diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..472bad7c3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# Bash needs lf even on windows/cygwin +*.st eol=lf +*.sh eol=lf + +# st files are always text, even if they contain invalid encoded chars +*.st text diff + +# GitHub is unable to properly detect Smalltalk code, so help it a bit +*.st linguist-language=Smalltalk diff --git a/Jenkinsfile b/Jenkinsfile index cd2d2fcdd..7f55193ec 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,11 +3,11 @@ pipeline { stages { stage('Build gtoolkit') { when { expression { - env.BRANCH_NAME.toString().equals('master') && (env.TAG_NAME == null) + env.BRANCH_NAME.toString().equals('main') && (env.TAG_NAME == null) } } steps { - build(job: '../gtoolkit/master', wait: false) + build(job: '../gtoolkit/main', wait: false) } } } diff --git a/src/.properties b/src/.properties index ad0471ded..1bcd6c431 100644 --- a/src/.properties +++ b/src/.properties @@ -1,3 +1,4 @@ { + #version : #1.0, #format : #tonel } \ No newline at end of file diff --git a/src/BaselineOfGToolkitCoder/BaselineOfGToolkitCoder.class.st b/src/BaselineOfGToolkitCoder/BaselineOfGToolkitCoder.class.st index 2bc695ae7..8cabac53a 100644 --- a/src/BaselineOfGToolkitCoder/BaselineOfGToolkitCoder.class.st +++ b/src/BaselineOfGToolkitCoder/BaselineOfGToolkitCoder.class.st @@ -31,26 +31,55 @@ Class { { #category : #baseline } BaselineOfGToolkitCoder >> baseline: spec [ - spec for: #'common' do: [ - spec - baseline: 'GToolkitBasicUtility' with: [ - spec repository: 'github://feenkcom/gtoolkit-utility/src' ]; - baseline: 'GToolkitAnnouncerUtility' with: [ - spec repository: 'github://feenkcom/gtoolkit-utility/src' ]; - baseline: 'GToolkitPager' with: [ - spec repository: 'github://feenkcom/gtoolkit-pager/src' ]. - spec - package: 'GToolkit-VariableBindings'; - package: 'GToolkit-Coder' with: [ - spec requires: #( 'GToolkitBasicUtility' 'GToolkitAnnouncerUtility' 'GToolkitPager' ). ]; - package: 'GToolkit-Coder-UI' with: [ - spec requires: #('GToolkit-Coder' 'GToolkit-VariableBindings' 'GToolkitBasicUtility' 'GToolkitPager' ). ]; - package: 'GToolkit-Coder-AddOns' with: [ - spec requires: #('GToolkit-Coder' 'GToolkit-Coder-UI'). ]; - package: 'GToolkit-Coder-Extensions' with: [ - spec requires: #('GToolkit-Coder' 'GToolkit-Coder-UI' 'GToolkit-Coder-AddOns'). ]; - package: 'GToolkit-Coder-Examples-SystemS1'; - package: 'GToolkit-Coder-Examples' with: [ - spec requires: #('GToolkit-Coder' 'GToolkit-Coder-UI' 'GToolkit-Coder-AddOns' 'GToolkit-Coder-Examples-SystemS1'). ]. ]. + spec + for: #common + do: [ spec + baseline: 'Futures' + with: [ spec repository: 'github://feenkcom/pharo-futures:main/src' ]; + baseline: 'GToolkitBasicUtility' + with: [ spec repository: 'github://feenkcom/gtoolkit-utility:main/src' ]; + baseline: 'GToolkitAnnouncerUtility' + with: [ spec repository: 'github://feenkcom/gtoolkit-utility:main/src' ]; + baseline: 'GToolkitNotifications' + with: [ spec repository: 'github://feenkcom/gtoolkit-notifications:main/src' ]; + baseline: 'GToolkitPager' + with: [ spec repository: 'github://feenkcom/gtoolkit-pager:main/src' ]; + baseline: 'GToolkitCompleter' + with: [ spec repository: 'github://feenkcom/gtoolkit-completer:main/src' ]; + baseline: 'GToolkit4Magritte' + with: [ spec repository: 'github://feenkcom/gt4magritte:main/src' ]; + baseline: 'GToolkitEditor' + with: [ spec repository: 'github://feenkcom/gtoolkit-editor:main/src' ]. + spec + package: 'GToolkit-VariableBindings'; + package: 'GToolkit-SearchFilters' with: [ spec requires: #('Futures') ]; + package: 'GToolkit-SearchFilters-PhlowTool' + with: [ spec requires: #('GToolkit-SearchFilters' 'GToolkitPager') ]; + package: 'GToolkit-Coder-StreamingCoders' + with: [ spec requires: #('Futures' 'GToolkit-SearchFilters') ]; + package: 'GToolkit-Coder-StreamingCoders-UI' + with: [ spec requires: #('Futures' 'GToolkit-Coder-StreamingCoders') ]; + package: 'GToolkit-Coder' + with: [ spec + requires: #('Futures' 'GToolkitBasicUtility' 'GToolkitAnnouncerUtility' 'GToolkitPager' 'GToolkitCompleter' 'GToolkit-SearchFilters') ]; + package: 'GToolkit-Coder-UI' + with: [ spec + requires: #('GToolkit-Coder' 'GToolkit-VariableBindings' 'GToolkitBasicUtility' 'GToolkitPager' 'GToolkitNotifications' 'GToolkit4Magritte' 'GToolkitEditor') ]; + package: 'GToolkit-Coder-AddOns' + with: [ spec requires: #('GToolkit-Coder' 'GToolkit-Coder-UI') ]; + package: 'GToolkit-Coder-Extensions' + with: [ spec requires: #('GToolkit-Coder' 'GToolkit-Coder-UI' 'GToolkit-Coder-AddOns') ]; + package: 'GToolkit-Coder-Examples-SystemS1'; + package: 'GToolkit-Coder-Examples' + with: [ spec + requires: #('GToolkit-Coder' 'GToolkit-Coder-UI' 'GToolkit-Coder-AddOns' 'GToolkit-Coder-Examples-SystemS1' 'GToolkit4Magritte') ]. + + self + forPharo13AndNewer: [] + forOlder: [ + spec package: 'GToolkit-Coder-Pharo12Extensions' with: [ + spec requires: #('GToolkit-Coder-Extensions') ] ] + + ] ] diff --git a/src/GToolkit-Coder-AddOns/BrEditorSearchTextMarkerContext.extension.st b/src/GToolkit-Coder-AddOns/BrEditorSearchTextMarkerContext.extension.st new file mode 100644 index 000000000..ee469c54d --- /dev/null +++ b/src/GToolkit-Coder-AddOns/BrEditorSearchTextMarkerContext.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #BrEditorSearchTextMarkerContext } + +{ #category : #'*GToolkit-Coder-AddOns' } +BrEditorSearchTextMarkerContext >> fromCoderViewModel: aCoderViewModel [ + self textSource: (GtTextualCoderViewModelSearchTextSource new textualCoderViewModel: aCoderViewModel) +] diff --git a/src/GToolkit-Coder-AddOns/GtAbstractRenameAction.class.st b/src/GToolkit-Coder-AddOns/GtAbstractRenameAction.class.st new file mode 100644 index 000000000..c2b1460a1 --- /dev/null +++ b/src/GToolkit-Coder-AddOns/GtAbstractRenameAction.class.st @@ -0,0 +1,295 @@ +Class { + #name : #GtAbstractRenameAction, + #superclass : #Object, + #traits : 'TGtAnnouncer', + #classTraits : 'TGtAnnouncer classTrait', + #instVars : [ + 'announcer', + 'validationBlock', + 'filter', + 'selectAll', + 'renameAttributes', + 'isInstalled', + 'originalLocations', + 'originalText', + 'allowAccept' + ], + #category : #'GToolkit-Coder-AddOns-Inline rename' +} + +{ #category : #actions } +GtAbstractRenameAction >> accept [ + self allowAccept ifFalse: [ ^ self ]. + self updateSource. + self uninstall. + self allowSave. + self announceEvent: #accept +] + +{ #category : #private } +GtAbstractRenameAction >> addRenameAttributes [ + self subclassResponsibility +] + +{ #category : #accessing } +GtAbstractRenameAction >> allowAccept [ + ^ allowAccept ifNil: [ true ] +] + +{ #category : #accessing } +GtAbstractRenameAction >> allowAccept: anObject [ + allowAccept := anObject +] + +{ #category : #private } +GtAbstractRenameAction >> allowSave [ +] + +{ #category : #private } +GtAbstractRenameAction >> announceEvent: aSymbol [ + ^ self announce: (GtRenameActionAnnouncement for: self type: aSymbol) +] + +{ #category : #announcer } +GtAbstractRenameAction >> announcer [ + ^ announcer ifNil: [ announcer := Announcer new ] +] + +{ #category : #actions } +GtAbstractRenameAction >> cancel [ + self uninstall. + self undoChanges. + self allowSave. + self announceEvent: #cancel +] + +{ #category : #actions } +GtAbstractRenameAction >> cancelFrom: anEditorElement [ + "Just announce cancel, because the controller then calls action cancel." + + self announceEvent: #cancel +] + +{ #category : #accessing } +GtAbstractRenameAction >> editor [ + ^ self editorAttribute editor +] + +{ #category : #accessing } +GtAbstractRenameAction >> editorAttribute [ + ^ renameAttributes notNil + ifTrue: [ renameAttributes + detect: [ :attr | attr isKindOf: GtRenameEditorAttribute ] + ifNone: [ ] ] +] + +{ #category : #private } +GtAbstractRenameAction >> filter: aTextEditorInputFilter [ + filter := aTextEditorInputFilter. + self primaryRenameAttribute + ifNotNil: [ :attr | attr filter: aTextEditorInputFilter ] +] + +{ #category : #'initialize-release' } +GtAbstractRenameAction >> forBinaryOrKeyword [ + validationBlock := [ :str | (self validateKeyword: str) or: [ self validateBinary: str ] ] +] + +{ #category : #'initialize-release' } +GtAbstractRenameAction >> forKeyword [ + validationBlock := [ :str | self validateKeyword: str ] +] + +{ #category : #'initialize-release' } +GtAbstractRenameAction >> forText [ + validationBlock := [ :str | true ]. + self filter: BrTextEditorNoInputFilter new +] + +{ #category : #'initialize-release' } +GtAbstractRenameAction >> forVariableOrUnaryMessage [ + validationBlock := [ :str | self validateVariable: str ]. + self filter: GtVariableInputFilter new +] + +{ #category : #initialization } +GtAbstractRenameAction >> initialize [ + super initialize. + originalLocations := #(). + isInstalled := false. + selectAll := false. + filter := BrTextEditorNoInputFilter new +] + +{ #category : #installation } +GtAbstractRenameAction >> install [ + originalLocations isEmpty + ifTrue: [ ^ self ]. + self preventSave. + self saveOriginalState. + self addRenameAttributes. + isInstalled := true. + self announceEvent: #install +] + +{ #category : #testing } +GtAbstractRenameAction >> isInstalled [ + ^ isInstalled +] + +{ #category : #testing } +GtAbstractRenameAction >> isRenameAttribute: anAttribute [ + ^ anAttribute isKindOf: GtRenamePreviewAttribute +] + +{ #category : #'private-validation' } +GtAbstractRenameAction >> isValid [ + validationBlock isNil + ifTrue: [ ^ true ]. + ^ validationBlock value: self newName +] + +{ #category : #accessing } +GtAbstractRenameAction >> locations: aCollectionOfIntervals [ + originalLocations := aCollectionOfIntervals asSortedCollection: [ :a :b | a first < b first ]. + self validateLocations +] + +{ #category : #actions } +GtAbstractRenameAction >> lostFocus [ + self isValid + ifTrue: [ self accept ] + ifFalse: [ self cancel ] +] + +{ #category : #accessing } +GtAbstractRenameAction >> newName [ + ^ renameAttributes first text asString +] + +{ #category : #accessing } +GtAbstractRenameAction >> originalName [ + ^ (originalText + copyFrom: originalLocations first first + to: originalLocations first last) asString +] + +{ #category : #installation } +GtAbstractRenameAction >> preventSave [ +] + +{ #category : #private } +GtAbstractRenameAction >> primaryRenameAttribute [ + renameAttributes isNil + ifTrue: [ ^ nil ]. + ^ renameAttributes + detect: [ :each | each isEditorAttribute ] + ifNone: [ nil ] +] + +{ #category : #actions } +GtAbstractRenameAction >> processReturnFor: renameEditor in: anEditorElement [ + self accept. + anEditorElement requestFocus +] + +{ #category : #private } +GtAbstractRenameAction >> removeAttributes [ + self subclassResponsibility +] + +{ #category : #actions } +GtAbstractRenameAction >> returnAccept [ + self isValid + ifFalse: [ ^ self ]. + self accept +] + +{ #category : #private } +GtAbstractRenameAction >> saveOriginalState [ + self subclassResponsibility +] + +{ #category : #accessing } +GtAbstractRenameAction >> selectAll [ + ^ selectAll +] + +{ #category : #accessing } +GtAbstractRenameAction >> selectAll: aBoolean [ + selectAll := aBoolean +] + +{ #category : #actions } +GtAbstractRenameAction >> tabAccept: forward [ + self isValid + ifFalse: [ ^ self ]. + self accept. + self + announceEvent: + (forward + ifTrue: [ #tab ] + ifFalse: [ #shiftTab ]) +] + +{ #category : #private } +GtAbstractRenameAction >> undoChanges [ + self subclassResponsibility +] + +{ #category : #installation } +GtAbstractRenameAction >> uninstall [ + self removeAttributes. + isInstalled := false. + self allowSave. + self announceEvent: #uninstall +] + +{ #category : #actions } +GtAbstractRenameAction >> updateName: blText [ + renameAttributes do: [ :each | each updateText: blText ]. + self announceEvent: #textUpdated +] + +{ #category : #private } +GtAbstractRenameAction >> updateSource [ + self subclassResponsibility +] + +{ #category : #'private-validation' } +GtAbstractRenameAction >> validateBinary: aString [ + aString isEmpty + ifTrue: [ ^ false ]. + ^ (RBScanner isSelector: aString) and: [ aString asSymbol isBinary ] +] + +{ #category : #'private-validation' } +GtAbstractRenameAction >> validateKeyword: aString [ + aString isEmpty + ifTrue: [ ^ false ]. + aString last = $: + ifFalse: [ ^ false ]. + ^ (RBScanner isSelector: aString) + and: [ aString asSymbol numArgs = 1 ] +] + +{ #category : #private } +GtAbstractRenameAction >> validateLocations [ + | lastLocation size | + originalLocations isEmpty + ifTrue: [ ^ self ]. + lastLocation := 0. + size := originalLocations first size. + originalLocations + do: [ :each | + each first <= lastLocation + ifTrue: [ self error: 'Cannot rename overlapping locations' ]. + lastLocation := each last. + each size ~= size + ifTrue: [ self error: 'Cannot rename items of different sizes' ] ] +] + +{ #category : #'private-validation' } +GtAbstractRenameAction >> validateVariable: aString [ + ^ RBScanner isVariable: aString +] diff --git a/src/GToolkit-Coder-AddOns/GtCoderAddOnsBuildContext.class.st b/src/GToolkit-Coder-AddOns/GtCoderAddOnsBuildContext.class.st new file mode 100644 index 000000000..0ae5780fe --- /dev/null +++ b/src/GToolkit-Coder-AddOns/GtCoderAddOnsBuildContext.class.st @@ -0,0 +1,21 @@ +" +My main purpose is to pass information while building coder addOns without having to add more method parameters. +" +Class { + #name : #GtCoderAddOnsBuildContext, + #superclass : #Object, + #instVars : [ + 'documentId' + ], + #category : #'GToolkit-Coder-AddOns-Support' +} + +{ #category : #accessing } +GtCoderAddOnsBuildContext >> documentId [ + ^ documentId +] + +{ #category : #accessing } +GtCoderAddOnsBuildContext >> documentId: anObject [ + documentId := anObject +] diff --git a/src/GToolkit-Coder-AddOns/GtCoderRenameActionAddAttributesSignal.class.st b/src/GToolkit-Coder-AddOns/GtCoderRenameActionAddAttributesSignal.class.st new file mode 100644 index 000000000..29f5f7ce1 --- /dev/null +++ b/src/GToolkit-Coder-AddOns/GtCoderRenameActionAddAttributesSignal.class.st @@ -0,0 +1,63 @@ +Class { + #name : #GtCoderRenameActionAddAttributesSignal, + #superclass : #GtCoderRenameActionSignal, + #instVars : [ + 'coderAttributes' + ], + #category : #'GToolkit-Coder-AddOns-Signals' +} + +{ #category : #accessing } +GtCoderRenameActionAddAttributesSignal >> coderAttributes [ + ^ coderAttributes +] + +{ #category : #accessing } +GtCoderRenameActionAddAttributesSignal >> coderAttributes: anObject [ + coderAttributes := anObject +] + +{ #category : #'gt - extensions' } +GtCoderRenameActionAddAttributesSignal >> gtExtraTextAttributesFor: aView [ + + coderAttributes ifNil: [ ^ aView empty ]. + ^ aView columnedList + title: 'Extra Text Attributes'; + priority: 0; + items: [ coderAttributes ]; + column: 'Document' + text: [ :each | + '{1}.{2}' + format: {each documentId ifNotNil: #textId ifNil: [ '-' ]. + each documentId ifNotNil: #textId ifNil: [ '-' ]} ] + width: 70; + column: 'Range' + text: [ :each | + '{1}..{2}' + format: {each startPosition ifNil: [ '-' ]. + each stopPosition ifNil: [ '-' ]} ] + width: 50; + column: 'Count' + text: [ :each | each textAttributes ifNotNil: #size ifNil: [ 0 ] ] + width: 50; + column: 'Attributes' + text: [ :each | + each textAttributes + ifNotNil: [ :aCollection | + String + streamContents: [ :aStream | + aCollection + do: [ :eachAttribute | eachAttribute gtDisplayOn: aStream ] + separatedBy: [ aStream nextPut: ', ' ] ] ] + ifNil: [ '' ] ] +] + +{ #category : #printing } +GtCoderRenameActionAddAttributesSignal >> printOneLineContentsOn: aStream [ + action ifNil: [ ^ super printOneLineContentsOn: aStream ]. + + aStream nextPutAll: '+A '. + action documentId gtDisplayOn: aStream. + aStream space. + action newSelector gtDisplayOn: aStream +] diff --git a/src/GToolkit-Coder-AddOns/GtCoderRenameActionRemoveAttributesSignal.class.st b/src/GToolkit-Coder-AddOns/GtCoderRenameActionRemoveAttributesSignal.class.st new file mode 100644 index 000000000..b4721e951 --- /dev/null +++ b/src/GToolkit-Coder-AddOns/GtCoderRenameActionRemoveAttributesSignal.class.st @@ -0,0 +1,63 @@ +Class { + #name : #GtCoderRenameActionRemoveAttributesSignal, + #superclass : #GtCoderRenameActionSignal, + #instVars : [ + 'coderAttributes' + ], + #category : #'GToolkit-Coder-AddOns-Signals' +} + +{ #category : #accessing } +GtCoderRenameActionRemoveAttributesSignal >> coderAttributes [ + ^ coderAttributes +] + +{ #category : #accessing } +GtCoderRenameActionRemoveAttributesSignal >> coderAttributes: anObject [ + coderAttributes := anObject +] + +{ #category : #'gt - extensions' } +GtCoderRenameActionRemoveAttributesSignal >> gtExtraTextAttributesFor: aView [ + + coderAttributes ifNil: [ ^ aView empty ]. + ^ aView columnedList + title: 'Extra Text Attributes'; + priority: 0; + items: [ coderAttributes ]; + column: 'Document' + text: [ :each | + '{1}.{2}' + format: {each documentId ifNotNil: #textId ifNil: [ '-' ]. + each documentId ifNotNil: #textId ifNil: [ '-' ]} ] + width: 70; + column: 'Range' + text: [ :each | + '{1}..{2}' + format: {each startPosition ifNil: [ '-' ]. + each stopPosition ifNil: [ '-' ]} ] + width: 50; + column: 'Count' + text: [ :each | each textAttributes ifNotNil: #size ifNil: [ 0 ] ] + width: 50; + column: 'Attributes' + text: [ :each | + each textAttributes + ifNotNil: [ :aCollection | + String + streamContents: [ :aStream | + aCollection + do: [ :eachAttribute | eachAttribute gtDisplayOn: aStream ] + separatedBy: [ aStream nextPut: ', ' ] ] ] + ifNil: [ '' ] ] +] + +{ #category : #printing } +GtCoderRenameActionRemoveAttributesSignal >> printOneLineContentsOn: aStream [ + action ifNil: [ ^ super printOneLineContentsOn: aStream ]. + + aStream nextPutAll: '-A '. + action documentId gtDisplayOn: aStream. + aStream space. + action newSelector gtDisplayOn: aStream +] diff --git a/src/GToolkit-Coder-AddOns/GtCoderRenameActionSignal.class.st b/src/GToolkit-Coder-AddOns/GtCoderRenameActionSignal.class.st new file mode 100644 index 000000000..134d1d554 --- /dev/null +++ b/src/GToolkit-Coder-AddOns/GtCoderRenameActionSignal.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderRenameActionSignal, + #superclass : #ContextStackSignal, + #instVars : [ + 'action' + ], + #category : #'GToolkit-Coder-AddOns-Signals' +} + +{ #category : #accessing } +GtCoderRenameActionSignal >> action [ + ^ action +] + +{ #category : #accessing } +GtCoderRenameActionSignal >> action: anObject [ + action := anObject +] diff --git a/src/GToolkit-Coder-AddOns/GtCoderViewModelRenameAction.class.st b/src/GToolkit-Coder-AddOns/GtCoderViewModelRenameAction.class.st new file mode 100644 index 000000000..2f17e8d81 --- /dev/null +++ b/src/GToolkit-Coder-AddOns/GtCoderViewModelRenameAction.class.st @@ -0,0 +1,217 @@ +Class { + #name : #GtCoderViewModelRenameAction, + #superclass : #GtAbstractRenameAction, + #instVars : [ + 'pharoSourceCoderViewModel', + 'documentId', + 'originalCursors', + 'coderAttributes', + 'hasRefactoringPreview', + 'requestInitialFocus' + ], + #category : #'GToolkit-Coder-AddOns-Inline rename' +} + +{ #category : #'instance creation' } +GtCoderViewModelRenameAction class >> locations: aCollectionOfIntervals textualCoderViewModel: aTextualCoderViewModel [ + ^ self new + locations: aCollectionOfIntervals; + pharoSourceCoderViewModel: aTextualCoderViewModel; + yourself +] + +{ #category : #'private - attributes' } +GtCoderViewModelRenameAction >> addRenameAttributes [ + | text cursorInLocation | + text := self pharoSourceCoderViewModel styledText + ifNil: [ self pharoSourceCoderViewModel sourceText ]. + + cursorInLocation := originalLocations + anySatisfy: [ :each | + originalCursors + anySatisfy: [ :eachCursor | eachCursor position between: each first - 1 and: each last ] ]. + + coderAttributes := originalLocations + collect: [ :each | + | attribute | + attribute := (cursorInLocation not + or: [ originalCursors + anySatisfy: [ :eachCursor | eachCursor position between: each first - 1 and: each last ] ]) + ifTrue: [ GtRenameEditorAttribute new + action: self; + selectAll: selectAll; + filter: filter; + coordinateFocus: hasRefactoringPreview; + requestInitialFocus: requestInitialFocus; + text: (text from: each first to: each last) ] + ifFalse: [ GtRenamePreviewAttribute new + action: self; + text: (text from: each first to: each last) ]. + cursorInLocation := true. + self pharoSourceCoderViewModel + addTextAttribute: attribute + from: each first + to: each last + documentId: documentId ]. + + renameAttributes := coderAttributes + flatCollect: [ :eachCoderAttribute | eachCoderAttribute textAttributes ]. + + self + assert: [ (renameAttributes select: [ :each | each isKindOf: GtRenameEditorAttribute ]) + size <= 1 ] + description: [ 'There must not be more than one editor attribute' ] +] + +{ #category : #'private - actions' } +GtCoderViewModelRenameAction >> allowSave [ + pharoSourceCoderViewModel allowSaveDueTo: self +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> documentId [ + ^ documentId +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> documentId: anObject [ + documentId := anObject +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> hasRefactoringPreview [ + ^ hasRefactoringPreview +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> hasRefactoringPreview: aBoolean [ + hasRefactoringPreview := aBoolean +] + +{ #category : #'initialize-release' } +GtCoderViewModelRenameAction >> initialize [ + super initialize. + coderAttributes := #(). + hasRefactoringPreview := false. + requestInitialFocus := true +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> lostFocus [ + hasRefactoringPreview ifTrue: [ ^ self ]. + ^ super lostFocus +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> pharoSourceCoderViewModel [ + ^ pharoSourceCoderViewModel +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> pharoSourceCoderViewModel: anObject [ + pharoSourceCoderViewModel := anObject +] + +{ #category : #'private - actions' } +GtCoderViewModelRenameAction >> preventSave [ + pharoSourceCoderViewModel preventSaveDueTo: self +] + +{ #category : #'private - attributes' } +GtCoderViewModelRenameAction >> removeAttributes [ + GtCoderRenameActionRemoveAttributesSignal new + action: self; + coderAttributes: coderAttributes; + emit. + + self pharoSourceCoderViewModel removeAllCoderTextAttributes: coderAttributes +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> requestInitialFocus [ + ^ requestInitialFocus +] + +{ #category : #accessing } +GtCoderViewModelRenameAction >> requestInitialFocus: aBoolean [ + requestInitialFocus := aBoolean +] + +{ #category : #private } +GtCoderViewModelRenameAction >> saveOriginalState [ + originalText := self pharoSourceCoderViewModel sourceText copy. + originalCursors := self pharoSourceCoderViewModel cursors copy +] + +{ #category : #private } +GtCoderViewModelRenameAction >> undoChanges [ + self pharoSourceCoderViewModel cursors: originalCursors +] + +{ #category : #installation } +GtCoderViewModelRenameAction >> uninstall [ + super uninstall. + pharoSourceCoderViewModel focused: true +] + +{ #category : #private } +GtCoderViewModelRenameAction >> updateCursorLocation [ + | sizeDifference offset | + sizeDifference := self newName size - self originalName size. + offset := 0. + originalLocations + with: renameAttributes + do: [ :interval :attr | + attr isEditorAttribute + ifTrue: [ + | aNewCursorPosition | + + aNewCursorPosition := (interval first + offset + attr editorCursorLocation - 1 min: pharoSourceCoderViewModel sourceText size). + pharoSourceCoderViewModel moveCursorTo: aNewCursorPosition. + ^ self ]. + + offset := offset + sizeDifference ] +] + +{ #category : #private } +GtCoderViewModelRenameAction >> updateSource [ + self newName = self originalName + ifTrue: [ ^ self removeAttributes ]. + + self + updateSourceIn: self pharoSourceCoderViewModel + at: originalLocations + attributes: renameAttributes. + + self updateCursorLocation +] + +{ #category : #private } +GtCoderViewModelRenameAction >> updateSourceIn: aTextualSourceCoderViewModel at: intervals attributes: attributeCollection [ + self + updateText: aTextualSourceCoderViewModel sourceText + at: intervals + attributes: attributeCollection +] + +{ #category : #private } +GtCoderViewModelRenameAction >> updateText: text at: intervals attributes: attributeCollection [ + | newText index | + newText := '' asRopedText. + index := 1. + intervals + with: attributeCollection + do: [ :each :attr | + each first = 1 + ifTrue: [ newText := newText , attr text ] + ifFalse: [ newText := newText , (text copyFrom: index to: each first - 1) + , attr text ]. + index := each last + 1 ]. + index > text size + ifFalse: [ newText := newText , (text copyFrom: index to: text size) ]. + + self pharoSourceCoderViewModel + sourceText: newText + from: self + synchronously: false +] diff --git a/src/GToolkit-Coder-AddOns/GtCompositeRenameAction.class.st b/src/GToolkit-Coder-AddOns/GtCompositeRenameAction.class.st index 7af94fd91..6b39f2e77 100644 --- a/src/GToolkit-Coder-AddOns/GtCompositeRenameAction.class.st +++ b/src/GToolkit-Coder-AddOns/GtCompositeRenameAction.class.st @@ -10,13 +10,8 @@ Class { { #category : #private } GtCompositeRenameAction >> addLocations: intervals to: anEditor [ - self addLocations: intervals toText: anEditor text -] - -{ #category : #private } -GtCompositeRenameAction >> addLocations: intervals toText: ropedText [ otherTextIntervals - at: ropedText + at: anEditor put: (intervals asSortedCollection: [ :a :b | a first < b first ]) ] @@ -30,21 +25,19 @@ GtCompositeRenameAction >> addRenameAttributes [ GtCompositeRenameAction >> addRenameAttributesToOtherEditors [ | size | size := self originalName size. - size < 1 - ifTrue: [ ^ self ]. + size < 1 ifTrue: [ ^ self ]. self removeOtherAttributes. otherTextIntervals - keysAndValuesDo: [ :text :intervals | + keysAndValuesDo: [ :editor :intervals | otherTextAttributes - at: text - put: - (intervals + at: editor + put: (intervals collect: [ :each | | attribute | attribute := GtRenamePreviewAttribute new - action: self; - text: (text from: each first to: each last). - (text from: each first to: each last) attributes: {attribute}. + action: self; + text: (editor text from: each first to: each last). + (editor text from: each first to: each last) attributes: {attribute}. attribute ]) ] ] @@ -64,8 +57,7 @@ GtCompositeRenameAction >> removeAttributes [ { #category : #private } GtCompositeRenameAction >> removeOtherAttributes [ otherTextAttributes - keysDo: - [ :text | text clearAttributes: [ :each | self isRenameAttribute: each ] ] + keysDo: [ :editor | editor text clearAttributes: [ :each | self isRenameAttribute: each ] ] ] { #category : #private } @@ -83,14 +75,23 @@ GtCompositeRenameAction >> updateName: blText [ { #category : #private } GtCompositeRenameAction >> updateOtherEditors [ - self newName = self originalName - ifTrue: [ ^ self ]. + self newName = self originalName ifTrue: [ ^ self ]. otherTextIntervals - keysAndValuesDo: [ :text :intervals | - self - updateText: text - at: intervals - attributes: (otherTextAttributes at: text) ] + keysAndValuesDo: [ :editor :intervals | + | newText index | + newText := '' asRopedText. + index := 1. + intervals + with: (otherTextAttributes at: editor) + do: [ :each :attr | + each first = 1 + ifTrue: [ newText := newText , attr text ] + ifFalse: [ newText := newText , (editor text copyFrom: index to: each first - 1) + , attr text ]. + index := each last + 1 ]. + index > editor text size + ifFalse: [ newText := newText , (editor text copyFrom: index to: editor text size) ]. + editor text: newText ] ] { #category : #private } diff --git a/src/GToolkit-Coder-AddOns/GtDefineClassFixItAction.class.st b/src/GToolkit-Coder-AddOns/GtDefineClassFixItAction.class.st deleted file mode 100644 index f6e5105ba..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineClassFixItAction.class.st +++ /dev/null @@ -1,61 +0,0 @@ -Class { - #name : #GtDefineClassFixItAction, - #superclass : #GtFixItVariableNodeAction, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #private } -GtDefineClassFixItAction >> addCreateClassAttributeInEditor: aBrTextEditor [ - | text stencilBlock attribute adornmentAttribute | - - text := aBrTextEditor text. - stencilBlock := [ | element | - element := GtPharoCreateBehaviorElement new. - element - behaviorBlock: [ :cls | - text - clearAttributes: [ :each | each == attribute or: [ each == adornmentAttribute ] ]. - sourceCoder requestStyleSourceText ]. - element forClassDefinition. - element forClassName: node name value asSymbol. - sourceCoder package - ifNotNil: [ :package | element forPackage: package ]. - sourceCoder packageTag - ifNotNil: [ :tag | element forPackageTag: tag ]. - element addAptitude: BrShadowAptitude. - element background: Color white. - element margin: (BlInsets all: 10). - element - constraintsDo: [ :c | - c textFlow pushLine. - c horizontal matchParent ]. - element ]. - attribute := BrGlamorousTrangleExpanderTextAttribute new. - attribute isExpanded: true. - attribute - attributesCreatingBlock: [ adornmentAttribute := BrTextAdornmentDynamicAttribute new - beAppend; - stencil: stencilBlock ]. - text - attribute: attribute - from: self attributePosition - to: self attributePosition -] - -{ #category : #accessing } -GtDefineClassFixItAction >> description [ - ^ 'Create class ' , self nodeName -] - -{ #category : #accessing } -GtDefineClassFixItAction >> executeOn: anEditorElement [ - self clearFixItAttributeInEditor: anEditorElement editor. - self addCreateClassAttributeInEditor: anEditorElement editor. -] - -{ #category : #accessing } -GtDefineClassFixItAction >> id [ - - - ^ GtDefineClassFixItActionElementId -] diff --git a/src/GToolkit-Coder-AddOns/GtDefineClassFixItActionElementId.class.st b/src/GToolkit-Coder-AddOns/GtDefineClassFixItActionElementId.class.st deleted file mode 100644 index e36dc9ea5..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineClassFixItActionElementId.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtDefineClassFixItActionElementId, - #superclass : #GtFixItActionElementId, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #converting } -GtDefineClassFixItActionElementId >> asSymbol [ - ^ #'fixit-action--define-class' -] diff --git a/src/GToolkit-Coder-AddOns/GtDefineClassVariableFixItAction.class.st b/src/GToolkit-Coder-AddOns/GtDefineClassVariableFixItAction.class.st deleted file mode 100644 index e64de5fc6..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineClassVariableFixItAction.class.st +++ /dev/null @@ -1,22 +0,0 @@ -Class { - #name : #GtDefineClassVariableFixItAction, - #superclass : #GtFixItVariableNodeAction, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #accessing } -GtDefineClassVariableFixItAction >> description [ - ^ 'Add class variable ' , self nodeName , ' to class ', sourceCoder behavior name -] - -{ #category : #accessing } -GtDefineClassVariableFixItAction >> executeOn: anEditorElement [ - sourceCoder addClassVariable: node name -] - -{ #category : #accessing } -GtDefineClassVariableFixItAction >> id [ - - - ^ GtDefineClassVariableFixItActionElementId -] diff --git a/src/GToolkit-Coder-AddOns/GtDefineClassVariableFixItActionElementId.class.st b/src/GToolkit-Coder-AddOns/GtDefineClassVariableFixItActionElementId.class.st deleted file mode 100644 index 2e0abb920..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineClassVariableFixItActionElementId.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtDefineClassVariableFixItActionElementId, - #superclass : #GtFixItActionElementId, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #converting } -GtDefineClassVariableFixItActionElementId >> asSymbol [ - ^ #'fixit-action--define-class-variable' -] diff --git a/src/GToolkit-Coder-AddOns/GtDefineInstanceVariableFixItAction.class.st b/src/GToolkit-Coder-AddOns/GtDefineInstanceVariableFixItAction.class.st deleted file mode 100644 index dd573b023..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineInstanceVariableFixItAction.class.st +++ /dev/null @@ -1,22 +0,0 @@ -Class { - #name : #GtDefineInstanceVariableFixItAction, - #superclass : #GtFixItVariableNodeAction, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #accessing } -GtDefineInstanceVariableFixItAction >> description [ - ^ 'Add slot ' , self nodeName , ' in ', sourceCoder behavior name -] - -{ #category : #accessing } -GtDefineInstanceVariableFixItAction >> executeOn: anEditorElement [ - sourceCoder addInstanceVariable: self nodeName -] - -{ #category : #accessing } -GtDefineInstanceVariableFixItAction >> id [ - - - ^ GtDefineInstanceVariableFixItActionElementId -] diff --git a/src/GToolkit-Coder-AddOns/GtDefineInstanceVariableFixItActionElementId.class.st b/src/GToolkit-Coder-AddOns/GtDefineInstanceVariableFixItActionElementId.class.st deleted file mode 100644 index ebdacf66b..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineInstanceVariableFixItActionElementId.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtDefineInstanceVariableFixItActionElementId, - #superclass : #GtFixItActionElementId, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #converting } -GtDefineInstanceVariableFixItActionElementId >> asSymbol [ - ^ #'fixit-action--define-instance-variable' -] diff --git a/src/GToolkit-Coder-AddOns/GtDefineMethodFixItActionElementId.class.st b/src/GToolkit-Coder-AddOns/GtDefineMethodFixItActionElementId.class.st deleted file mode 100644 index 5de45f04b..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineMethodFixItActionElementId.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtDefineMethodFixItActionElementId, - #superclass : #GtFixItActionElementId, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #converting } -GtDefineMethodFixItActionElementId >> asSymbol [ - ^ #'fixit-action--define-method' -] diff --git a/src/GToolkit-Coder-AddOns/GtDefineTemporaryFixItAction.class.st b/src/GToolkit-Coder-AddOns/GtDefineTemporaryFixItAction.class.st deleted file mode 100644 index d362195ab..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineTemporaryFixItAction.class.st +++ /dev/null @@ -1,36 +0,0 @@ -Class { - #name : #GtDefineTemporaryFixItAction, - #superclass : #GtFixItVariableNodeAction, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #accessing } -GtDefineTemporaryFixItAction >> description [ - ^ 'Add temporary variable ' , self nodeName -] - -{ #category : #executing } -GtDefineTemporaryFixItAction >> executeOn: anEditorElement [ - | ast newSource location declaration | - - ast := sourceCoder rbAST. - newSource := sourceCoder currentSourceString. - ast body rightBar isNil - ifTrue: [ location := ast body start - 1. - declaration := '| ' , self nodeName , ' |' , String cr , String tab ] - ifFalse: [ location := ast body rightBar - 1. - declaration := ((newSource at: location) isSeparator - ifTrue: [ '' ] - ifFalse: [ ' ' ]) , self nodeName , ' ' ]. - - newSource := (newSource first: location) , declaration - , (newSource allButFirst: location). - sourceCoder currentSourceString: newSource -] - -{ #category : #accessing } -GtDefineTemporaryFixItAction >> id [ - - - ^ GtDefineTemporaryVariableFixItActionElementId -] diff --git a/src/GToolkit-Coder-AddOns/GtDefineTemporaryVariableFixItActionElementId.class.st b/src/GToolkit-Coder-AddOns/GtDefineTemporaryVariableFixItActionElementId.class.st deleted file mode 100644 index c86896cca..000000000 --- a/src/GToolkit-Coder-AddOns/GtDefineTemporaryVariableFixItActionElementId.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtDefineTemporaryVariableFixItActionElementId, - #superclass : #GtFixItActionElementId, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #converting } -GtDefineTemporaryVariableFixItActionElementId >> asSymbol [ - ^ #'fixit-action--define-temporary-variable' -] diff --git a/src/GToolkit-Coder-AddOns/GtExtractMethodReorderParametersAttribute.class.st b/src/GToolkit-Coder-AddOns/GtExtractMethodReorderParametersAttribute.class.st deleted file mode 100644 index 1c61bf158..000000000 --- a/src/GToolkit-Coder-AddOns/GtExtractMethodReorderParametersAttribute.class.st +++ /dev/null @@ -1,54 +0,0 @@ -Class { - #name : #GtExtractMethodReorderParametersAttribute, - #superclass : #BrTextAdornmentAttribute, - #instVars : [ - 'actionBlock', - 'icon' - ], - #category : #'GToolkit-Coder-AddOns-Extract method' -} - -{ #category : #'instance creation' } -GtExtractMethodReorderParametersAttribute class >> action: aBlock icon: anIcon [ - ^ self new - actionBlock: aBlock; - icon: anIcon; - yourself -] - -{ #category : #accessing } -GtExtractMethodReorderParametersAttribute >> actionBlock [ - ^ actionBlock -] - -{ #category : #accessing } -GtExtractMethodReorderParametersAttribute >> actionBlock: aBlock [ - actionBlock := aBlock -] - -{ #category : #accessing } -GtExtractMethodReorderParametersAttribute >> doAffect: aTBrTextEditorTextualPiece in: anEditorElement [ - ^ BlElement new - size: icon extent; - background: icon; - addEventHandlerOn: BlClickEvent do: [ :anEvent | actionBlock cull: anEvent ]; - yourself -] - -{ #category : #accessing } -GtExtractMethodReorderParametersAttribute >> icon [ - ^ icon -] - -{ #category : #accessing } -GtExtractMethodReorderParametersAttribute >> icon: anObject [ - icon := anObject -] - -{ #category : #'initialize-release' } -GtExtractMethodReorderParametersAttribute >> initialize [ - super initialize. - - self beAppend. - self beNotOverwritableByStyler -] diff --git a/src/GToolkit-Coder-AddOns/GtExtractMethodSelectorKeywordAttribute.class.st b/src/GToolkit-Coder-AddOns/GtExtractMethodSelectorKeywordAttribute.class.st deleted file mode 100644 index 5712b22df..000000000 --- a/src/GToolkit-Coder-AddOns/GtExtractMethodSelectorKeywordAttribute.class.st +++ /dev/null @@ -1,44 +0,0 @@ -Class { - #name : #GtExtractMethodSelectorKeywordAttribute, - #superclass : #BlTextAttribute, - #instVars : [ - 'index' - ], - #category : #'GToolkit-Coder-AddOns-Extract method' -} - -{ #category : #'instance creation' } -GtExtractMethodSelectorKeywordAttribute class >> keywordIndex: anInteger [ - ^ self new - index: anInteger; - yourself -] - -{ #category : #comparing } -GtExtractMethodSelectorKeywordAttribute >> equals: aTextAttribute [ - ^ self index = aTextAttribute index -] - -{ #category : #comparing } -GtExtractMethodSelectorKeywordAttribute >> hash [ - "Answer an integer value that is related to the identity of the receiver." - - ^ super hash bitXor: self index hash -] - -{ #category : #accessing } -GtExtractMethodSelectorKeywordAttribute >> index [ - ^ index -] - -{ #category : #accessing } -GtExtractMethodSelectorKeywordAttribute >> index: anInteger [ - index := anInteger -] - -{ #category : #initialization } -GtExtractMethodSelectorKeywordAttribute >> initialize [ - super initialize. - - self beNotOverwritableByStyler -] diff --git a/src/GToolkit-Coder-AddOns/GtFixItAction.class.st b/src/GToolkit-Coder-AddOns/GtFixItAction.class.st index d7da10cec..3dc96b4c0 100644 --- a/src/GToolkit-Coder-AddOns/GtFixItAction.class.st +++ b/src/GToolkit-Coder-AddOns/GtFixItAction.class.st @@ -3,7 +3,8 @@ Class { #superclass : #Object, #instVars : [ 'node', - 'sourceCoder' + 'sourceCoder', + 'autoSave' ], #category : #'GToolkit-Coder-AddOns-FixIt' } @@ -21,12 +22,29 @@ GtFixItAction >> attributePosition [ ^ node name stopPosition ] +{ #category : #accessing } +GtFixItAction >> autoSave [ + ^ autoSave ifNil: [ false ] +] + +{ #category : #accessing } +GtFixItAction >> autoSave: aBoolean [ + autoSave := aBoolean +] + +{ #category : #executing } +GtFixItAction >> basicExecuteOn: anEditorElement [ + self subclassResponsibility +] + { #category : #private } GtFixItAction >> clearFixItAttributeInEditor: aBrTextEditor [ | position | position := self attributePosition. - (aBrTextEditor text from: position to: position) - clearAttributes: [ :each | each isKindOf: GtFixItAttribute ] + aBrTextEditor text + clearAttributes: position + to: position + if: [ :each | each isKindOf: GtFixItAttribute ] ] { #category : #'api - action' } @@ -36,7 +54,12 @@ GtFixItAction >> description [ { #category : #executing } GtFixItAction >> executeOn: anEditorElement [ - self subclassResponsibility + self basicExecuteOn: anEditorElement. + self finishExecuteOn: anEditorElement +] + +{ #category : #executing } +GtFixItAction >> finishExecuteOn: anEditorElement [ ] { #category : #'api - action' } @@ -49,8 +72,10 @@ GtFixItAction >> id [ { #category : #private } GtFixItAction >> menuActionWithIndex: anIndex [ ^ GtCoderContextMenuAction new - action: [ :aCoderViewModel :anEvent :anEditorElement | + action: [ :aCoderViewModel :anEditorElement | aCoderViewModel selectNone. + "Pass coder view model alongside of the editor element" + self flag: #TODO. self executeOn: anEditorElement ]; title: self description; id: (self id indexed: anIndex) @@ -61,6 +86,14 @@ GtFixItAction >> node: aRBProgramNode [ node := aRBProgramNode ] +{ #category : #printing } +GtFixItAction >> printOn: aStream [ + super printOn: aStream. + + aStream parenthesize: [ + aStream << self description ] +] + { #category : #initialization } GtFixItAction >> sourceCoder: aCoder [ sourceCoder := aCoder diff --git a/src/GToolkit-Coder-AddOns/GtFixItAttribute.class.st b/src/GToolkit-Coder-AddOns/GtFixItAttribute.class.st index a5a6d4247..664697959 100644 --- a/src/GToolkit-Coder-AddOns/GtFixItAttribute.class.st +++ b/src/GToolkit-Coder-AddOns/GtFixItAttribute.class.st @@ -2,36 +2,157 @@ Class { #name : #GtFixItAttribute, #superclass : #BrTextAdornmentAttribute, #instVars : [ - 'fixItActionsBlock' + 'fixItActionsBlock', + 'descriptionBlock', + 'hoverMarker', + 'label', + 'elementId', + 'hoverHighlight', + 'model' ], #category : #'GToolkit-Coder-AddOns-FixIt' } +{ #category : #accessing } +GtFixItAttribute >> addHoveringStylingFor: anEditorElement to: anElement [ + hoverMarker ifNil: [ ^ self ]. + hoverHighlight ifNil: [ ^ self ]. + anElement + when: BlMouseEnterEvent + do: [ :anEvent | + anEditorElement addHighlight: hoverHighlight overMarker: hoverMarker ]. + anElement + when: BlMouseLeaveEvent + do: [ :anEvent | + anEditorElement removeHighlightsWithId: hoverHighlight id ] +] + +{ #category : #accessing } +GtFixItAttribute >> computeDescriptionTextInEditorElement: anEditorElement forFixItActions: aCollectionOfFixItActions [ + | text | + + text := (descriptionBlock + cull: anEditorElement + cull: aCollectionOfFixItActions) asRopedText + foreground: BrGlamorousColors disabledButtonTextColor. + + ^ BrEditor new + fitContent; + aptitude: BrGlamorousRegularEditorAptitude new + glamorousCodeSmallSize; + text: text. +] + +{ #category : #accessing } +GtFixItAttribute >> computeMenuItemsForFixItActions: aCollectionOfFixItActions [ + | actionsGroupedById | + actionsGroupedById := aCollectionOfFixItActions + groupedBy: [ :eachFixitAction | eachFixitAction id ]. + + ^ actionsGroupedById values + flatCollect: [ :fixitActionsPerId | + fixitActionsPerId collectWithIndex: [ :eachFixItAction :eachIndex | + eachFixItAction menuActionWithIndex: eachIndex ] ] +] + +{ #category : #accessing } +GtFixItAttribute >> description: aBlockReturningBlText [ + descriptionBlock := aBlockReturningBlText +] + { #category : #accessing } GtFixItAttribute >> doAffect: aTBrTextEditorTextualPiece in: anEditorElement [ - "Return an element that should affect (either append or replace) a provided text piece" - - - ^ GtFixItButton new - beTinySize; - label: 'Fix it'; - aptitude: - BrGlamorousButtonWithIconAptitude - BrGlamorousButtonExteriorAptitude - + - (BrGlamorousWithDropdownAptitude - handle: [ BrButton new - beTinySize; - aptitude: - BrGlamorousButtonWithIconAptitude - BrGlamorousButtonWithLabelTooltipAptitude - - BrGlamorousButtonExteriorAptitude; - icon: BrGlamorousVectorIcons repair; - yourself ] - content: [ GtCoderContextMenuContent new - editorElement: anEditorElement; - items: ((fixItActionsBlock value groupedBy: [ :eachFixitAction | eachFixitAction id ]) values - flatCollect: [ :fixitActionsPerId | fixitActionsPerId collectWithIndex: [ :eachFixItAction :eachIndex | eachFixItAction menuActionWithIndex: eachIndex ] ]) ]); - icon: BrGlamorousVectorIcons repair; - yourself + | button dropDownAptitude | + button := GtFixItButton new. + elementId ifNotNil: [ button id: elementId ]. + button beTinySize. + button label: (label ifNil: [ 'Fix it' ]). + model ifNotNil: [ :aModel | button fixItModel: aModel ]. + + dropDownAptitude := BrGlamorousWithExplicitDropdownAptitude + handle: [ | dropdownButton | + dropdownButton := BrButton new. + dropdownButton beTinySize. + dropdownButton + aptitude: BrGlamorousButtonWithIconAptitude - BrGlamorousButtonWithLabelTooltipAptitude2 + - BrGlamorousButtonExteriorAptitude. + dropdownButton icon: BrGlamorousVectorIcons repair. + self addHoveringStylingFor: anEditorElement to: dropdownButton. + dropdownButton ] + content: [ | element | + element := self dropDownElementFor: anEditorElement. + self addHoveringStylingFor: anEditorElement to: element. + element ]. + + dropDownAptitude withGainFocusOnShow. + + button + aptitude: BrGlamorousButtonWithIconAptitude - BrGlamorousButtonExteriorAptitude - BrGlamorousButtonWithLabelTooltipAptitude2; + addAptitude: dropDownAptitude; + addAptitude: (BrGlamorousWithExplicitTooltipAptitude text: (label ifNil: [ 'Fix it' ]) shortcut: BlKeyCombination primaryT). + button userData at: #fixItAttribute put: self. + button userData at: #dropDownAptitude put: dropDownAptitude. + button icon: BrGlamorousVectorIcons repair. + + self addHoveringStylingFor: anEditorElement to: button. + ^ button +] + +{ #category : #accessing } +GtFixItAttribute >> dropDownElementFor: anEditorElement [ + | pane menuElement computedFixItActions menuItems | + computedFixItActions := fixItActionsBlock value. + menuItems := self computeMenuItemsForFixItActions: computedFixItActions. + + pane := BrVerticalPane new. + pane fitContent. + + descriptionBlock + ifNotNil: [ | labelText | + labelText := self + computeDescriptionTextInEditorElement: anEditorElement + forFixItActions: computedFixItActions. + + pane addChild: labelText ]. + + menuItems ifNotEmpty: [ + menuElement := GtCoderContextMenuContent new + beFocusable; + editorElement: anEditorElement; + items: menuItems; + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination enter; + name: 'Perform fix'; + description: 'Perform a fix-it action'; + action: [ :aShortcutEvent :aShortcut | + aShortcutEvent consumed: true. + menuElement selectedItemDo: [ :eachGtCoderContextMenuAction | + eachGtCoderContextMenuAction action + cull: anEditorElement parent textualCoderViewModel + cull: anEditorElement + cull: aShortcutEvent. + menuElement fireEvent: BrDropdownHideWish new. + menuElement fireEvent: BrContextMenuHideWish new ] ]). + + pane + when: BrPopoverGainFocusWish + do: [ :anEvent | + menuElement + requestFocus; + selectOne: 1 ]. + + pane addChild: menuElement ]. + ^ pane +] + +{ #category : #accessing } +GtFixItAttribute >> elementId [ + ^ elementId +] + +{ #category : #accessing } +GtFixItAttribute >> elementId: anObject [ + elementId := anObject ] { #category : #initialization } @@ -40,8 +161,53 @@ GtFixItAttribute >> fixItActions: aCollection [ ] { #category : #accessing } +GtFixItAttribute >> hoverAttribute [ + ^ hoverHighlight +] + +{ #category : #accessing } +GtFixItAttribute >> hoverAttribute: aTextAttribute [ + hoverHighlight := aTextAttribute +] + +{ #category : #accessing } +GtFixItAttribute >> hoverMarker [ + ^ hoverMarker +] + +{ #category : #accessing } +GtFixItAttribute >> hoverMarker: aTextAttribute [ + hoverMarker := aTextAttribute +] + +{ #category : #initialization } GtFixItAttribute >> initialize [ super initialize. self beAppend ] + +{ #category : #accessing } +GtFixItAttribute >> label [ + ^ label +] + +{ #category : #accessing } +GtFixItAttribute >> label: aString [ + label := aString +] + +{ #category : #testing } +GtFixItAttribute >> mayHaveExternalReferences [ + ^ true +] + +{ #category : #accessing } +GtFixItAttribute >> model [ + ^ model +] + +{ #category : #accessing } +GtFixItAttribute >> model: anObject [ + model := anObject +] diff --git a/src/GToolkit-Coder-AddOns/GtFixItButton.class.st b/src/GToolkit-Coder-AddOns/GtFixItButton.class.st index a3248280d..08a091399 100644 --- a/src/GToolkit-Coder-AddOns/GtFixItButton.class.st +++ b/src/GToolkit-Coder-AddOns/GtFixItButton.class.st @@ -1,5 +1,45 @@ Class { #name : #GtFixItButton, #superclass : #BrButton, + #instVars : [ + 'fixItModel' + ], #category : #'GToolkit-Coder-AddOns-FixIt' } + +{ #category : #accessing } +GtFixItButton >> fixItModel [ + ^ fixItModel +] + +{ #category : #accessing } +GtFixItButton >> fixItModel: anObject [ + self + assert: [ anObject isNotNil ] + description: [ 'FixIt model must be non-nil' ]. + + fixItModel = anObject ifTrue: [ ^ self ]. + + fixItModel := anObject. + self onFixItModelChanged +] + +{ #category : #hooks } +GtFixItButton >> onFixItModelChanged [ + fixItModel + when: GtFixItAdviceModelShowDropdownAnnouncement + send: #onGtFixItAdviceModelShowDropdownAnnouncement: + to: self +] + +{ #category : #'private - event handling' } +GtFixItButton >> onGtFixItAdviceModelShowDropdownAnnouncement: anAnnouncement [ + self fixItModel = anAnnouncement model ifFalse: [ ^ self ]. + + BlTaskAction enqueueElement: self action: [ self updateShowFixItDropdown ] +] + +{ #category : #'private - updating' } +GtFixItButton >> updateShowFixItDropdown [ + self dispatchEvent: (BrDropdownShowWish new anchor: self) +] diff --git a/src/GToolkit-Coder-AddOns/GtFixItVariableNodeAction.class.st b/src/GToolkit-Coder-AddOns/GtFixItVariableNodeAction.class.st deleted file mode 100644 index 95ffed4f5..000000000 --- a/src/GToolkit-Coder-AddOns/GtFixItVariableNodeAction.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtFixItVariableNodeAction, - #superclass : #GtFixItAction, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #'api - node' } -GtFixItVariableNodeAction >> nodeName [ - ^ node variableName -] diff --git a/src/GToolkit-Coder-AddOns/GtRenameAction.class.st b/src/GToolkit-Coder-AddOns/GtRenameAction.class.st index e61cebb63..9087f237d 100644 --- a/src/GToolkit-Coder-AddOns/GtRenameAction.class.st +++ b/src/GToolkit-Coder-AddOns/GtRenameAction.class.st @@ -1,19 +1,9 @@ Class { #name : #GtRenameAction, - #superclass : #Object, - #traits : 'TGtAnnouncer', - #classTraits : 'TGtAnnouncer classTrait', + #superclass : #GtAbstractRenameAction, #instVars : [ - 'announcer', 'textElement', - 'originalLocations', - 'originalText', - 'originalCursorPosition', - 'isInstalled', - 'renameAttributes', - 'selectAll', - 'validationBlock', - 'filter' + 'originalCursorPosition' ], #category : #'GToolkit-Coder-AddOns-Inline rename' } @@ -26,191 +16,47 @@ GtRenameAction class >> locations: aCollectionOfIntervals element: aTextEditorEl yourself ] -{ #category : #actions } -GtRenameAction >> accept [ - self updateSource. - self uninstall. - self allowSave. - self announceEvent: #accept -] - { #category : #private } GtRenameAction >> addRenameAttributes [ | text cursorInLocation | text := self text. cursorInLocation := originalLocations - anySatisfy: - [ :each | originalCursorPosition between: each first - 1 and: each last ]. + anySatisfy: [ :each | originalCursorPosition between: each first - 1 and: each last ]. renameAttributes := originalLocations - collect: [ :each | - | attribute | - attribute := (cursorInLocation not - or: [ originalCursorPosition between: each first - 1 and: each last ]) - ifTrue: [ GtRenameEditorAttribute new - action: self; - selectAll: selectAll; - filter: filter; - cursorLocation: originalCursorPosition - each first + 1; - text: (text from: each first to: each last) ] - ifFalse: [ GtRenamePreviewAttribute new - action: self; - text: (text from: each first to: each last) ]. - cursorInLocation := true. - (text from: each first to: each last) attributes: {attribute}. - attribute ] + collect: [ :each | + | attribute | + attribute := (cursorInLocation not + or: [ originalCursorPosition between: each first - 1 and: each last ]) + ifTrue: [ GtRenameEditorAttribute new + action: self; + selectAll: selectAll; + filter: filter; + coordinateFocus: self shouldCoordinateFocus; + cursorLocation: originalCursorPosition - each first + 1; + text: (text from: each first to: each last) ] + ifFalse: [ GtRenamePreviewAttribute new + action: self; + text: (text from: each first to: each last) ]. + cursorInLocation := true. + (text from: each first to: each last) attributes: {attribute}. + attribute ] ] { #category : #'private - actions' } GtRenameAction >> allowSave [ - textElement textualCoderViewModel allowSaveDueTo: self -] - -{ #category : #private } -GtRenameAction >> announceEvent: aSymbol [ - ^ self announce: (GtRenameActionAnnouncement for: self type: aSymbol) -] - -{ #category : #announcer } -GtRenameAction >> announcer [ - ^ announcer ifNil: [ announcer := Announcer new ] -] - -{ #category : #actions } -GtRenameAction >> cancel [ - self uninstall. - self undoChanges. - self allowSave. - self announceEvent: #cancel -] - -{ #category : #accessing } -GtRenameAction >> editor [ - ^ self editorAttribute editor -] - -{ #category : #accessing } -GtRenameAction >> editorAttribute [ - ^ renameAttributes notNil - ifTrue: [ renameAttributes - detect: [ :attr | attr isKindOf: GtRenameEditorAttribute ] - ifNone: [ ] ] -] - -{ #category : #private } -GtRenameAction >> filter: aTextEditorInputFilter [ - filter := aTextEditorInputFilter. - self primaryRenameAttribute - ifNotNil: [ :attr | attr filter: aTextEditorInputFilter ] -] - -{ #category : #'initialize-release' } -GtRenameAction >> forBinaryOrKeyword [ - validationBlock := [ :str | (self validateKeyword: str) or: [ self validateBinary: str ] ] -] - -{ #category : #'initialize-release' } -GtRenameAction >> forKeyword [ - validationBlock := [ :str | self validateKeyword: str ] -] - -{ #category : #'initialize-release' } -GtRenameAction >> forVariableOrUnaryMessage [ - validationBlock := [ :str | self validateVariable: str ]. - self filter: BrTextEditorAlphaNumericInputFilter new -] - -{ #category : #'initialize-release' } -GtRenameAction >> initialize [ - super initialize. - originalLocations := #(). - isInstalled := false. - selectAll := false. - filter := BrTextEditorNoInputFilter new -] - -{ #category : #installation } -GtRenameAction >> install [ - originalLocations isEmpty - ifTrue: [ ^ self ]. - self preventSave. - self saveOriginalState. - self addRenameAttributes. - isInstalled := true. - self announceEvent: #install -] - -{ #category : #testing } -GtRenameAction >> isInstalled [ - ^ isInstalled -] - -{ #category : #testing } -GtRenameAction >> isRenameAttribute: anAttribute [ - ^ anAttribute isKindOf: GtRenamePreviewAttribute -] - -{ #category : #'private-validation' } -GtRenameAction >> isValid [ - validationBlock isNil - ifTrue: [ ^ true ]. - ^ validationBlock value: self newName -] - -{ #category : #accessing } -GtRenameAction >> locations: aCollectionOfIntervals [ - originalLocations := aCollectionOfIntervals asSortedCollection: [ :a :b | a first < b first ]. - self validateLocations -] - -{ #category : #actions } -GtRenameAction >> lostFocus [ - self isValid - ifTrue: [ self accept ] - ifFalse: [ self cancel ] -] - -{ #category : #accessing } -GtRenameAction >> newName [ - ^ renameAttributes first text asString -] - -{ #category : #accessing } -GtRenameAction >> originalName [ - ^ (originalText - copyFrom: originalLocations first first - to: originalLocations first last) asString + (textElement respondsTo: #textualCoderViewModel) + ifTrue: [ textElement textualCoderViewModel allowSaveDueTo: self ] ] { #category : #'private - actions' } GtRenameAction >> preventSave [ - textElement textualCoderViewModel preventSaveDueTo: self -] - -{ #category : #private } -GtRenameAction >> primaryRenameAttribute [ - renameAttributes isNil - ifTrue: [ ^ nil ]. - ^ renameAttributes - detect: [ :each | each isEditorAttribute ] - ifNone: [ nil ] -] - -{ #category : #private } -GtRenameAction >> removeAttributeHandlers [ - renameAttributes do: [ :each | each uninstallEventHandlers ] + (textElement respondsTo: #textualCoderViewModel) + ifTrue: [ textElement textualCoderViewModel preventSaveDueTo: self ] ] { #category : #private } GtRenameAction >> removeAttributes [ - self text clearAttributes: [ :each | self isRenameAttribute: each ]. - self removeAttributeHandlers -] - -{ #category : #actions } -GtRenameAction >> returnAccept [ - self isValid - ifFalse: [ ^ self ]. - self accept + self text clearAttributes: [ :each | self isRenameAttribute: each ] ] { #category : #private } @@ -221,26 +67,9 @@ GtRenameAction >> saveOriginalState [ originalCursorPosition := self textEditor cursors first position ] -{ #category : #accessing } -GtRenameAction >> selectAll [ - ^ selectAll -] - -{ #category : #accessing } -GtRenameAction >> selectAll: anObject [ - selectAll := anObject -] - -{ #category : #actions } -GtRenameAction >> tabAccept: forward [ - self isValid - ifFalse: [ ^ self ]. - self accept. - self - announceEvent: - (forward - ifTrue: [ #tab ] - ifFalse: [ #shiftTab ]) +{ #category : #private } +GtRenameAction >> shouldCoordinateFocus [ + ^ false ] { #category : #accessing } @@ -265,19 +94,16 @@ GtRenameAction >> textElement: aTextEditorElement [ { #category : #private } GtRenameAction >> undoChanges [ - self textElement - text: originalText; - onTextModified. + textElement text: originalText. + (textElement respondsTo: #onTextModified) + ifTrue: [ textElement onTextModified ]. self textEditor moveCursorTo: originalCursorPosition ] { #category : #installation } GtRenameAction >> uninstall [ - self removeAttributes. - isInstalled := false. - self allowSave. - self announceEvent: #uninstall. + super uninstall. textElement enqueueTask: (BlTaskAction new action: [ textElement requestFocus ]) ] @@ -299,12 +125,6 @@ GtRenameAction >> updateCursorLocation [ offset := offset + sizeDifference ] ] -{ #category : #actions } -GtRenameAction >> updateName: blText [ - renameAttributes do: [ :each | each updateText: blText ]. - self announceEvent: #textUpdated -] - { #category : #private } GtRenameAction >> updateSource [ self newName = self originalName @@ -331,54 +151,15 @@ GtRenameAction >> updateText: text at: intervals attributes: attributeCollection index := 1. intervals with: attributeCollection - do: [ :each :attr | + do: [ :each :attr | each first = 1 ifTrue: [ newText := newText , attr text ] - ifFalse: [ newText := newText , (text copyFrom: index to: each first - 1) - , attr text ]. + ifFalse: [ newText := newText , (text copyFrom: index to: each first - 1) , attr text ]. index := each last + 1 ]. index > text size ifFalse: [ newText := newText , (text copyFrom: index to: text size) ]. - textElement - text: newText; - onTextModified -] - -{ #category : #'private-validation' } -GtRenameAction >> validateBinary: aString [ - aString isEmpty - ifTrue: [ ^ false ]. - ^ (RBScanner isSelector: aString) and: [ aString asSymbol isBinary ] -] - -{ #category : #'private-validation' } -GtRenameAction >> validateKeyword: aString [ - aString isEmpty - ifTrue: [ ^ false ]. - aString last = $: - ifFalse: [ ^ false ]. - ^ (RBScanner isSelector: aString) - and: [ aString asSymbol numArgs = 1 ] -] - -{ #category : #private } -GtRenameAction >> validateLocations [ - | lastLocation size | - originalLocations isEmpty - ifTrue: [ ^ self ]. - lastLocation := 0. - size := originalLocations first size. - originalLocations - do: [ :each | - each first <= lastLocation - ifTrue: [ self error: 'Cannot rename overlapping locations' ]. - lastLocation := each last. - each size ~= size - ifTrue: [ self error: 'Cannot rename items of different sizes' ] ] -] - -{ #category : #'private-validation' } -GtRenameAction >> validateVariable: aString [ - ^ RBScanner isVariable: aString + textElement text: newText. + (textElement respondsTo: #onTextModified) + ifTrue: [ textElement onTextModified ] ] diff --git a/src/GToolkit-Coder-AddOns/GtRenameAction2.class.st b/src/GToolkit-Coder-AddOns/GtRenameAction2.class.st deleted file mode 100644 index 78f8563e9..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameAction2.class.st +++ /dev/null @@ -1,391 +0,0 @@ -Class { - #name : #GtRenameAction2, - #superclass : #Object, - #traits : 'TGtAnnouncer', - #classTraits : 'TGtAnnouncer classTrait', - #instVars : [ - 'announcer', - 'pharoSourceCoderViewModel', - 'originalLocations', - 'originalText', - 'isInstalled', - 'renameAttributes', - 'selectAll', - 'validationBlock', - 'filter', - 'originalCursors', - 'coderAttributes' - ], - #category : #'GToolkit-Coder-AddOns-Inline rename' -} - -{ #category : #'instance creation' } -GtRenameAction2 class >> locations: aCollectionOfIntervals textualCoderViewModel: aTextualCoderViewModel [ - ^ self new - locations: aCollectionOfIntervals; - pharoSourceCoderViewModel: aTextualCoderViewModel; - yourself -] - -{ #category : #actions } -GtRenameAction2 >> accept [ - self updateSource. - self uninstall. - self allowSave. - self announceEvent: #accept -] - -{ #category : #'private - attributes' } -GtRenameAction2 >> addRenameAttributes [ - | text cursorInLocation | - - text := self pharoSourceCoderViewModel sourceText. - - cursorInLocation := originalLocations - anySatisfy: [ :each | originalCursors anySatisfy: [ :eachCursor | eachCursor position between: each first - 1 and: each last ] ]. - - coderAttributes := originalLocations - collect: [ :each | - | attribute | - attribute := (cursorInLocation not - or: [ originalCursors anySatisfy: [ :eachCursor | eachCursor position between: each first - 1 and: each last ] ]) - ifTrue: [ - GtRenameEditorAttribute new - action: self; - selectAll: selectAll; - filter: filter; - text: (text from: each first to: each last) ] - ifFalse: [ - GtRenamePreviewAttribute new - action: self; - text: (text from: each first to: each last) ]. - cursorInLocation := true. - - self pharoSourceCoderViewModel - addTextAttribute: attribute - from: each first - to: each last ]. - - renameAttributes := coderAttributes flatCollect: [ :eachCoderAttribute | eachCoderAttribute textAttributes ]. - - self - assert: [ (renameAttributes select: [ :each | each isKindOf: GtRenameEditorAttribute ]) size <= 1 ] - description: [ 'There must not be more than one editor attribute' ] -] - -{ #category : #'private - actions' } -GtRenameAction2 >> allowSave [ - pharoSourceCoderViewModel allowSaveDueTo: self -] - -{ #category : #private } -GtRenameAction2 >> announceEvent: aSymbol [ - ^ self announce: (GtRenameActionAnnouncement for: self type: aSymbol) -] - -{ #category : #announcer } -GtRenameAction2 >> announcer [ - ^ announcer ifNil: [ announcer := Announcer new ] -] - -{ #category : #actions } -GtRenameAction2 >> cancel [ - self uninstall. - self undoChanges. - self allowSave. - self announceEvent: #cancel -] - -{ #category : #accessing } -GtRenameAction2 >> editor [ - ^ self editorAttribute editor -] - -{ #category : #accessing } -GtRenameAction2 >> editorAttribute [ - ^ renameAttributes notNil - ifTrue: [ renameAttributes - detect: [ :attr | attr isKindOf: GtRenameEditorAttribute ] - ifNone: [ ] ] -] - -{ #category : #private } -GtRenameAction2 >> filter: aTextEditorInputFilter [ - filter := aTextEditorInputFilter. - self primaryRenameAttribute - ifNotNil: [ :attr | attr filter: aTextEditorInputFilter ] -] - -{ #category : #'initialize-release' } -GtRenameAction2 >> forBinaryOrKeyword [ - validationBlock := [ :str | (self validateKeyword: str) or: [ self validateBinary: str ] ] -] - -{ #category : #'initialize-release' } -GtRenameAction2 >> forKeyword [ - validationBlock := [ :str | self validateKeyword: str ] -] - -{ #category : #'initialize-release' } -GtRenameAction2 >> forVariableOrUnaryMessage [ - validationBlock := [ :str | self validateVariable: str ]. - self filter: BrTextEditorAlphaNumericInputFilter new -] - -{ #category : #'initialize-release' } -GtRenameAction2 >> initialize [ - super initialize. - originalLocations := #(). - isInstalled := false. - selectAll := false. - filter := BrTextEditorNoInputFilter new. - - coderAttributes := #(). -] - -{ #category : #installation } -GtRenameAction2 >> install [ - originalLocations isEmpty - ifTrue: [ ^ self ]. - - self preventSave. - self saveOriginalState. - - self addRenameAttributes. - isInstalled := true. - - self announceEvent: #install -] - -{ #category : #testing } -GtRenameAction2 >> isInstalled [ - ^ isInstalled -] - -{ #category : #testing } -GtRenameAction2 >> isRenameAttribute: anAttribute [ - ^ anAttribute isKindOf: GtRenamePreviewAttribute -] - -{ #category : #'private-validation' } -GtRenameAction2 >> isValid [ - validationBlock isNil - ifTrue: [ ^ true ]. - ^ validationBlock value: self newName -] - -{ #category : #accessing } -GtRenameAction2 >> locations: aCollectionOfIntervals [ - originalLocations := aCollectionOfIntervals asSortedCollection: [ :a :b | a first < b first ]. - self validateLocations -] - -{ #category : #actions } -GtRenameAction2 >> lostFocus [ - self isValid - ifTrue: [ self accept ] - ifFalse: [ self cancel ] -] - -{ #category : #accessing } -GtRenameAction2 >> newName [ - ^ renameAttributes first text asString -] - -{ #category : #accessing } -GtRenameAction2 >> originalName [ - ^ (originalText - copyFrom: originalLocations first first - to: originalLocations first last) asString -] - -{ #category : #accessing } -GtRenameAction2 >> pharoSourceCoderViewModel [ - ^ pharoSourceCoderViewModel -] - -{ #category : #accessing } -GtRenameAction2 >> pharoSourceCoderViewModel: anObject [ - pharoSourceCoderViewModel := anObject -] - -{ #category : #'private - actions' } -GtRenameAction2 >> preventSave [ - pharoSourceCoderViewModel preventSaveDueTo: self -] - -{ #category : #'private - attributes' } -GtRenameAction2 >> primaryRenameAttribute [ - renameAttributes isNil - ifTrue: [ ^ nil ]. - ^ renameAttributes - detect: [ :each | each isEditorAttribute ] - ifNone: [ nil ] -] - -{ #category : #'private - attributes' } -GtRenameAction2 >> removeAttributeHandlers [ - renameAttributes do: [ :each | each uninstallEventHandlers ] -] - -{ #category : #'private - attributes' } -GtRenameAction2 >> removeAttributes [ - self pharoSourceCoderViewModel removeAllCoderTextAttributes: coderAttributes. - self removeAttributeHandlers -] - -{ #category : #actions } -GtRenameAction2 >> returnAccept [ - self isValid - ifFalse: [ ^ self ]. - self accept -] - -{ #category : #private } -GtRenameAction2 >> saveOriginalState [ - originalText := self pharoSourceCoderViewModel sourceText copy. - originalCursors := self pharoSourceCoderViewModel cursors copy -] - -{ #category : #accessing } -GtRenameAction2 >> selectAll [ - ^ selectAll -] - -{ #category : #accessing } -GtRenameAction2 >> selectAll: anObject [ - selectAll := anObject -] - -{ #category : #actions } -GtRenameAction2 >> tabAccept: forward [ - self isValid - ifFalse: [ ^ self ]. - self accept. - self - announceEvent: - (forward - ifTrue: [ #tab ] - ifFalse: [ #shiftTab ]) -] - -{ #category : #private } -GtRenameAction2 >> undoChanges [ - self pharoSourceCoderViewModel cursors: originalCursors -] - -{ #category : #installation } -GtRenameAction2 >> uninstall [ - self removeAttributes. - isInstalled := false. - self allowSave. - self announceEvent: #uninstall. - - pharoSourceCoderViewModel focused: true -] - -{ #category : #private } -GtRenameAction2 >> updateCursorLocation [ - | sizeDifference offset | - sizeDifference := self newName size - self originalName size. - offset := 0. - originalLocations - with: renameAttributes - do: [ :interval :attr | - attr isEditorAttribute - ifTrue: [ - | aNewCursorPosition | - - aNewCursorPosition := (interval first + offset + attr editorCursorLocation - 1 min: pharoSourceCoderViewModel sourceText size). - pharoSourceCoderViewModel moveCursorTo: aNewCursorPosition. - ^ self ]. - - offset := offset + sizeDifference ] -] - -{ #category : #actions } -GtRenameAction2 >> updateName: blText [ - renameAttributes do: [ :each | each updateText: blText ]. - self announceEvent: #textUpdated -] - -{ #category : #private } -GtRenameAction2 >> updateSource [ - self newName = self originalName - ifTrue: [ ^ self removeAttributes ]. - - self - updateSourceIn: self pharoSourceCoderViewModel - at: originalLocations - attributes: renameAttributes. - - self updateCursorLocation -] - -{ #category : #private } -GtRenameAction2 >> updateSourceIn: aTextualSourceCoderViewModel at: intervals attributes: attributeCollection [ - self - updateText: aTextualSourceCoderViewModel sourceText - at: intervals - attributes: attributeCollection -] - -{ #category : #private } -GtRenameAction2 >> updateText: text at: intervals attributes: attributeCollection [ - | newText index | - newText := '' asRopedText. - index := 1. - intervals - with: attributeCollection - do: [ :each :attr | - each first = 1 - ifTrue: [ newText := newText , attr text ] - ifFalse: [ newText := newText , (text copyFrom: index to: each first - 1) - , attr text ]. - index := each last + 1 ]. - index > text size - ifFalse: [ newText := newText , (text copyFrom: index to: text size) ]. - - self pharoSourceCoderViewModel - sourceText: newText - from: self - synchronously: true -] - -{ #category : #'private-validation' } -GtRenameAction2 >> validateBinary: aString [ - aString isEmpty - ifTrue: [ ^ false ]. - ^ (RBScanner isSelector: aString) and: [ aString asSymbol isBinary ] -] - -{ #category : #'private-validation' } -GtRenameAction2 >> validateKeyword: aString [ - aString isEmpty - ifTrue: [ ^ false ]. - aString last = $: - ifFalse: [ ^ false ]. - ^ (RBScanner isSelector: aString) - and: [ aString asSymbol numArgs = 1 ] -] - -{ #category : #private } -GtRenameAction2 >> validateLocations [ - | lastLocation size | - originalLocations isEmpty - ifTrue: [ ^ self ]. - lastLocation := 0. - size := originalLocations first size. - originalLocations - do: [ :each | - each first <= lastLocation - ifTrue: [ self error: 'Cannot rename overlapping locations' ]. - lastLocation := each last. - each size ~= size - ifTrue: [ self error: 'Cannot rename items of different sizes' ] ] -] - -{ #category : #'private-validation' } -GtRenameAction2 >> validateVariable: aString [ - ^ RBScanner isVariable: aString -] diff --git a/src/GToolkit-Coder-AddOns/GtRenameClassController.class.st b/src/GToolkit-Coder-AddOns/GtRenameClassController.class.st deleted file mode 100644 index b8ecc7d7b..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameClassController.class.st +++ /dev/null @@ -1,20 +0,0 @@ -Class { - #name : #GtRenameClassController, - #superclass : #GtRenameVariableController, - #category : #'GToolkit-Coder-AddOns-Inline rename' -} - -{ #category : #accessing } -GtRenameClassController >> refactoringName [ - ^ 'Rename class' -] - -{ #category : #accessing } -GtRenameClassController >> rename: oldNameString to: newNameString [ - | model | - model := self createModel. - ^ GtRBRenameClassWithCommentsRefactoring - model: model - rename: (Smalltalk at: oldNameString asSymbol) - to: newNameString -] diff --git a/src/GToolkit-Coder-AddOns/GtRenameClassVariableController.class.st b/src/GToolkit-Coder-AddOns/GtRenameClassVariableController.class.st deleted file mode 100644 index 7a24f5e27..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameClassVariableController.class.st +++ /dev/null @@ -1,23 +0,0 @@ -Class { - #name : #GtRenameClassVariableController, - #superclass : #GtRenameVariableController, - #category : #'GToolkit-Coder-AddOns-Inline rename' -} - -{ #category : #accessing } -GtRenameClassVariableController >> refactoringName [ - ^ 'Rename class variable' -] - -{ #category : #accessing } -GtRenameClassVariableController >> rename: oldName to: newName [ - | model class | - model := self createModel. - class := (model classFor: coder classOrMetaClass) instanceSide - whoDefinesClassVariable: oldName. - ^ RBRenameClassVariableRefactoring - model: model - rename: oldName - to: newName - in: class -] diff --git a/src/GToolkit-Coder-AddOns/GtRenameController.class.st b/src/GToolkit-Coder-AddOns/GtRenameController.class.st deleted file mode 100644 index 0e6549131..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameController.class.st +++ /dev/null @@ -1,150 +0,0 @@ -Class { - #name : #GtRenameController, - #superclass : #GtSourceCoderRefactoringController, - #instVars : [ - 'handlers', - 'renameAction', - 'node', - 'newName', - 'renameInterval', - 'completionAction' - ], - #category : #'GToolkit-Coder-AddOns-Inline rename' -} - -{ #category : #private } -GtRenameController >> addRefactoringChangesPreview [ - | aRefactoring aRenamePosition | - self removeRefactoringAttribute. - - aRefactoring := self refactoring. - aRefactoring - ifNil: [ ^ self cancelRefactoring ]. - - aRenamePosition := self renamePosition. - - self - addRefactoringChangesPreview: aRefactoring - at: aRenamePosition - whenComplete: [ - self removeRefactoringAttribute. - self completionAction value. - coder requestStyleSourceText. - self allowSave ] -] - -{ #category : #executing } -GtRenameController >> cancelRefactoring [ - self removeRefactoringAttribute. - self restoreText. - self allowSave -] - -{ #category : #accessing } -GtRenameController >> completionAction [ - ^ completionAction ifNil: [ [ ] ] -] - -{ #category : #accessing } -GtRenameController >> completionAction: aBlock [ - completionAction := aBlock -] - -{ #category : #executing } -GtRenameController >> createModel [ - | model | - model := RBClassModelFactory rbNamespace - onEnvironment: RBBrowserEnvironment new. - model name: self refactoringName. - coder isForMethod - ifTrue: [ ((model classFor: coder behavior) sourceCodeFor: coder selector) - ~= originalSource asString - ifTrue: [ (model classFor: coder behavior) - compile: originalSource asString - classified: coder protocol ] ]. - ^ model -] - -{ #category : #accessing } -GtRenameController >> cursorPositionDo: aBlock [ - self sourceEditor cursors - do: [ :eachCursor | ^ aBlock value: (eachCursor position + 1 min: self sourceEditor text size) ] -] - -{ #category : #executing } -GtRenameController >> installRenameAction [ - renameAction := GtRenameAction - locations: { renameInterval } - element: sourceElement. - - renameAction selectAll: true. - - self preventSave. - - renameAction - when: GtRenameActionAnnouncement - do: [ :ann | - (#(#textUpdated #accept) includes: ann eventType) - ifTrue: [ self updateName: renameAction newName ]. - (#(#tab #shiftTab) includes: ann eventType) - ifTrue: [ self tab: ann eventType = #tab ]. - ann eventType = #cancel - ifTrue: [ self cancelRefactoring ] ]. - - self installValidation. - renameAction install -] - -{ #category : #executing } -GtRenameController >> installRenamer [ - self subclassResponsibility -] - -{ #category : #accessing } -GtRenameController >> installValidation [ - renameAction forVariableOrUnaryMessage -] - -{ #category : #accessing } -GtRenameController >> newName [ - ^ newName -] - -{ #category : #accessing } -GtRenameController >> originalName [ - ^ self subclassResponsibility -] - -{ #category : #accessing } -GtRenameController >> originalNode [ - - - ^ node -] - -{ #category : #accessing } -GtRenameController >> originalNode: aGtPharoProgramNode [ - node := aGtPharoProgramNode -] - -{ #category : #private } -GtRenameController >> renamePosition [ - ^ (renameAction isNil or: [ renameAction isInstalled ]) - ifTrue: [ renameInterval last ] - ifFalse: [ renameInterval last + self newName size - self originalName size ] -] - -{ #category : #executing } -GtRenameController >> safelyExecute [ - self installRenamer -] - -{ #category : #private } -GtRenameController >> tab: forward [ -] - -{ #category : #private } -GtRenameController >> updateName: aString [ - newName := aString. - self addRefactoringChangesPreview -] diff --git a/src/GToolkit-Coder-AddOns/GtRenameEditor.class.st b/src/GToolkit-Coder-AddOns/GtRenameEditor.class.st index 684bf5573..4b75a8bbd 100644 --- a/src/GToolkit-Coder-AddOns/GtRenameEditor.class.st +++ b/src/GToolkit-Coder-AddOns/GtRenameEditor.class.st @@ -3,3 +3,14 @@ Class { #superclass : #BrEditableLabel, #category : #'GToolkit-Coder-AddOns-Inline rename' } + +{ #category : #accessing } +GtRenameEditor >> initialize [ + super initialize. + self userData at: #isRefactoringEditor put: true. + self + when: BlFocusEvent + do: [ :anEvent | + self viewModel mode class = BrTextEditorReadonlyWithoutSelectionMode + ifTrue: [ self switchToEditor ] ] +] diff --git a/src/GToolkit-Coder-AddOns/GtRenameEditorAttribute.class.st b/src/GToolkit-Coder-AddOns/GtRenameEditorAttribute.class.st index 43ef15f75..9beefee37 100644 --- a/src/GToolkit-Coder-AddOns/GtRenameEditorAttribute.class.st +++ b/src/GToolkit-Coder-AddOns/GtRenameEditorAttribute.class.st @@ -5,14 +5,38 @@ Class { 'cursorLocation', 'selectAll', 'filter', - 'editorShortcuts' + 'requestInitialFocus', + 'styler', + 'coordinateFocus', + 'completionStrategy', + 'completionController' ], #category : #'GToolkit-Coder-AddOns-Inline rename' } { #category : #accessing } GtRenameEditorAttribute >> additionalAttributes [ - ^ #() + ^ {BlFontFamilyAttribute named: 'Source Code Pro'} +] + +{ #category : #accessing } +GtRenameEditorAttribute >> completionStrategy [ + ^ completionStrategy +] + +{ #category : #accessing } +GtRenameEditorAttribute >> completionStrategy: anObject [ + completionStrategy := anObject +] + +{ #category : #accessing } +GtRenameEditorAttribute >> coordinateFocus [ + ^ coordinateFocus +] + +{ #category : #accessing } +GtRenameEditorAttribute >> coordinateFocus: aBoolean [ + coordinateFocus := aBoolean ] { #category : #accessing } @@ -22,31 +46,73 @@ GtRenameEditorAttribute >> cursorLocation: anInteger [ { #category : #accessing } GtRenameEditorAttribute >> doAffect: aTBrTextEditorTextualPiece in: anEditorElement [ + | handlers | renameEditor := GtRenameEditor new - fitContent; - aptitude: (BrGlamorousEditableLabelAptitude new defaultBackground: Color transparent; minWidth: 0); - padding: (BlInsets left: 2 right: 2); - margin: (BlInsets top: 0 left: -2 bottom: 0 right: -2); - inputFilter: filter; - switchToEditor; - text: text; - requestFocus. + fitContent; + aptitude: (BrGlamorousEditableLabelAptitude new + defaultBackground: Color transparent; + defaultBorder: (BlBorder paint: BrGlamorousColors editorBorderColor); + minWidth: 0); + padding: (BlInsets left: 2 right: 2); + margin: (BlInsets + top: 0 + left: -2 + bottom: 0 + right: -2); + inputFilter: filter; + text: text. + styler ifNotNil: [ renameEditor styler: styler ]. + completionStrategy + ifNotNil: [ completionController := GtCompletionController + on: renameEditor + strategy: completionStrategy. + completionController closeOnEmptyCompletion: false. + completionController install ]. + requestInitialFocus + ifTrue: [ renameEditor + switchToEditor; + requestFocus ] + ifFalse: [ renameEditor switchToLabel ]. + + coordinateFocus ifTrue: [ self setupArrowKeysOn: anEditorElement ]. + + handlers := {BrTextEditorDeletedEvent. + BrTextEditorInsertedEvent} + collect: [ :each | BlEventHandler on: each do: [ :event | self renameAction cancelFrom: anEditorElement ] ]. + renameEditor + when: BlElementAddedToSceneGraphEvent + do: [ :anEvent | handlers do: [ :each | anEditorElement editor addEventHandler: each ] ]; + when: BlElementRemovedFromSceneGraphEvent + do: [ :anEvent | handlers do: [ :each | anEditorElement editor removeEventHandler: each ] ]. + + renameEditor + whenKey: BlKeyCombination escape + editorDo: [ :aShortcutEvent | self renameAction cancelFrom: anEditorElement ]; + whenKey: BlKeyCombination builder shift tab build + editorDo: [ :aShortcutEvent | + (self focusNext: false in: anEditorElement) + ifFalse: [ self renameAction tabAccept: false ] ]; + whenKey: BlKeyCombination tab + editorDo: [ :aShortcutEvent | + (self focusNext: true in: anEditorElement) + ifFalse: [ self renameAction tabAccept: true ] ]; + whenKey: BlKeyCombination enter + editorDo: [ :aShortcutEvent | self renameAction processReturnFor: renameEditor in: anEditorElement ]. + + renameEditor when: BrEditorCancelWish do: [ :event | self renameAction cancelFrom: anEditorElement ]. renameEditor editor when: BrTextEditorModifiedEvent do: [ :event | renameAction updateName: renameEditor editor text ]. - renameEditor - when: BlFocusOutEvent - do: [ :event | self updateFocus: event ]. - - self flag: #TODO. - "renameEditor editor moveCursorTo: (cursorLocation min: renameEditor editor text size)." + renameEditor when: BlFocusOutEvent do: [ :event | self updateFocus: event ]. - selectAll - ifTrue: [ renameEditor editor selecter all select ]. + requestInitialFocus + ifTrue: [ renameEditor editor + selectNone; + moveCursorTo: ((cursorLocation ifNil: [ SmallInteger maxVal ]) + min: renameEditor editor text size) ]. + self selectAllOnCreate ifTrue: [ renameEditor editor selecter all select ]. - renameEditor editor addEditorShortcuts: self editorShortcuts. - ^ renameEditor ] @@ -57,24 +123,6 @@ GtRenameEditorAttribute >> editorCursorLocation [ ^ text size ] -{ #category : #private } -GtRenameEditorAttribute >> editorShortcuts [ - ^ editorShortcuts - ifNil: [ editorShortcuts := { - (BlShortcutWithAction new - combination: BlKeyCombination escape; - action: [ :aShortcutEvent | self renameAction cancel ]). - (BlShortcutWithAction new - combination: BlKeyCombination builder shift tab build; - action: [ :aShortcutEvent | self renameAction tabAccept: false ]). - (BlShortcutWithAction new - combination: BlKeyCombination tab; - action: [ :aShortcutEvent | self renameAction tabAccept: true ]). - (BlShortcutWithAction new - combination: BlKeyCombination return; - action: [ :aShortcutEvent | self renameAction accept ])} ] -] - { #category : #accessing } GtRenameEditorAttribute >> filter: aTextEditorInputFilter [ filter := aTextEditorInputFilter. @@ -82,13 +130,41 @@ GtRenameEditorAttribute >> filter: aTextEditorInputFilter [ ifTrue: [ renameEditor inputFilter: aTextEditorInputFilter ] ] +{ #category : #accessing } +GtRenameEditorAttribute >> focusNext: aBoolean in: anEditorElement [ + | first last previous | + coordinateFocus ifFalse: [ ^ false ]. + anEditorElement + allChildrenBreadthFirstDo: [ :each | + (each userData at: #isRefactoringEditor ifAbsent: [ false ]) + ifTrue: [ first ifNil: [ first := each ]. + last := each. + (aBoolean not and: [ each == renameEditor and: [ previous notNil ] ]) + ifTrue: [ previous requestFocus. + ^ true ]. + (aBoolean and: [ previous == renameEditor ]) + ifTrue: [ each requestFocus. + ^ true ]. + previous := each ] ]. + aBoolean + ifTrue: [ first + ifNotNil: [ first requestFocus. + ^ true ] ] + ifFalse: [ last + ifNotNil: [ last requestFocus. + ^ true ] ]. + ^ false +] + { #category : #initialization } GtRenameEditorAttribute >> initialize [ super initialize. filter := BrTextEditorNoInputFilter new. selectAll := false. - self cache: self newPooledCache + self cache: self newPooledCache. + requestInitialFocus := true. + coordinateFocus := false ] { #category : #testing } @@ -106,6 +182,16 @@ GtRenameEditorAttribute >> renameAction [ ^ renameAction ] +{ #category : #accessing } +GtRenameEditorAttribute >> requestInitialFocus [ + ^ requestInitialFocus +] + +{ #category : #accessing } +GtRenameEditorAttribute >> requestInitialFocus: aBoolean [ + requestInitialFocus := aBoolean +] + { #category : #accessing } GtRenameEditorAttribute >> selectAll [ ^ selectAll @@ -117,9 +203,50 @@ GtRenameEditorAttribute >> selectAll: aBoolean [ ] { #category : #accessing } -GtRenameEditorAttribute >> uninstallEventHandlers [ - renameEditor notNil - ifTrue: [ renameEditor editor removeEditorShortcuts: self editorShortcuts ] +GtRenameEditorAttribute >> selectAllOnCreate [ + ^self selectAll +] + +{ #category : #private } +GtRenameEditorAttribute >> setupArrowKeysOn: anEditorElement [ + renameEditor + whenKey: BlKeyCombination arrowRight + editorDo: [ :aShortcutEvent | + | cursors editor | + editor := aShortcutEvent source editor. + cursors := editor cursors positions. + (cursors includes: editor text size) + ifTrue: [ aShortcutEvent consumed: (self focusNext: true in: anEditorElement) ] + ifFalse: [ cursors notEmpty + ifTrue: [ editor selectNone. + editor moveCursorTo: cursors first + 1 ] ] ]. + renameEditor + whenKey: BlKeyCombination arrowLeft + editorDo: [ :aShortcutEvent | + | cursors editor | + editor := aShortcutEvent source editor. + cursors := editor cursors positions. + (cursors includes: 0) + ifTrue: [ aShortcutEvent consumed: (self focusNext: false in: anEditorElement) ] + ifFalse: [ cursors notEmpty + ifTrue: [ editor selectNone. + editor moveCursorTo: cursors first - 1 ] ] ]. + renameEditor + whenKey: BlKeyCombination arrowDown + editorDo: [ :aShortcutEvent | aShortcutEvent consumed: (self focusNext: true in: anEditorElement) ]. + renameEditor + whenKey: BlKeyCombination arrowUp + editorDo: [ :aShortcutEvent | aShortcutEvent consumed: (self focusNext: false in: anEditorElement) ] +] + +{ #category : #accessing } +GtRenameEditorAttribute >> styler [ + ^ styler +] + +{ #category : #accessing } +GtRenameEditorAttribute >> styler: aBlTextStyler [ + styler := aBlTextStyler ] { #category : #accessing } diff --git a/src/GToolkit-Coder-AddOns/GtRenameInstanceVariableController.class.st b/src/GToolkit-Coder-AddOns/GtRenameInstanceVariableController.class.st deleted file mode 100644 index 985dc47e1..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameInstanceVariableController.class.st +++ /dev/null @@ -1,23 +0,0 @@ -Class { - #name : #GtRenameInstanceVariableController, - #superclass : #GtRenameVariableController, - #category : #'GToolkit-Coder-AddOns-Inline rename' -} - -{ #category : #accessing } -GtRenameInstanceVariableController >> refactoringName [ - ^ 'Rename instance variable' -] - -{ #category : #accessing } -GtRenameInstanceVariableController >> rename: oldName to: newName [ - | model class | - model := self createModel. - class := (model classFor: coder behavior) - whoDefinesInstanceVariable: oldName. - ^ RBRenameInstanceVariableRefactoring - model: model - rename: oldName - to: newName - in: class -] diff --git a/src/GToolkit-Coder-AddOns/GtRenameMethodController.class.st b/src/GToolkit-Coder-AddOns/GtRenameMethodController.class.st deleted file mode 100644 index 14e73e596..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameMethodController.class.st +++ /dev/null @@ -1,152 +0,0 @@ -Class { - #name : #GtRenameMethodController, - #superclass : #GtRenameController, - #instVars : [ - 'selector', - 'someImplementor', - 'newSelectorParts', - 'selectorIndex', - 'startIndex', - 'previewIndex' - ], - #category : #'GToolkit-Coder-AddOns-Inline rename' -} - -{ #category : #executing } -GtRenameMethodController >> installRenamer [ - self - cursorPositionDo: [ :position | - | message | - selector := self originalNode selector. - self someImplementor isNil - ifTrue: [ ^ self ]. - - startIndex := self originalNode selectorParts first startPosition. - newSelectorParts := selector keywords. - message := self originalNode. - message selectorParts - keysAndValuesDo: [ :i :eachSmaCCToken | - (eachSmaCCToken sourceInterval includes: position) - ifTrue: [ - selectorIndex := i. - self installRenamerOn: message ] ] ] -] - -{ #category : #executing } -GtRenameMethodController >> installRenamerOn: aGtPharoMessageNode [ - previewIndex := node selectorParts last stopPosition. - newName := newSelectorParts at: selectorIndex. - renameInterval := (aGtPharoMessageNode selectorParts at: selectorIndex) sourceInterval. - self installRenameAction -] - -{ #category : #accessing } -GtRenameMethodController >> installValidation [ - | argCount | - argCount := selector numArgs. - argCount = 0 - ifTrue: [ ^ super installValidation ]. - argCount = 1 - ifTrue: [ renameAction forBinaryOrKeyword ] - ifFalse: [ renameAction forKeyword ] -] - -{ #category : #accessing } -GtRenameMethodController >> newName [ - ^ self newSelectorName -] - -{ #category : #private } -GtRenameMethodController >> newNode [ - coder astAwait nodesDo: [ :each | - ((each isMessage or: [ each isMethod or: [ each isMethodPattern ] ]) - and: [ each selectorParts first startPosition = startIndex ]) - ifTrue: [ ^ each ] ]. - ^ nil -] - -{ #category : #private } -GtRenameMethodController >> newSelectorName [ - selector numArgs = 0 - ifTrue: [ ^ newName asSymbol ]. - ^ (String - streamContents: - [ :stream | newSelectorParts do: [ :each | stream nextPutAll: each ] ]) - asSymbol -] - -{ #category : #accessing } -GtRenameMethodController >> originalName [ - ^ selector -] - -{ #category : #private } -GtRenameMethodController >> refactoring [ - | implementor newSelector model | - implementor := self someImplementor. - implementor isNil - ifTrue: [ ^ nil ]. - newSelector := self newSelectorName. - newSelector = selector - ifTrue: [ ^ nil ]. - model := self createModel. - ^ (GtCoderRenameMethodRefactoring - model: model - renameMethod: selector - in: (model classFor: implementor) - to: newSelector - permutation: (1 to: selector numArgs)) - methodCoder: coder -] - -{ #category : #accessing } -GtRenameMethodController >> refactoringName [ - ^ 'Rename method' -] - -{ #category : #accessing } -GtRenameMethodController >> renamePosition [ - self newNode - ifNotNil: [ :message | ^ message selectorParts last stopPosition ]. - ^ (renameAction isNil or: [ renameAction isInstalled ]) - ifTrue: [ previewIndex ] - ifFalse: [ self originalNode selectorParts last stopPosition + self newName size - - self originalName size ] -] - -{ #category : #private } -GtRenameMethodController >> someImplementor [ - ^ someImplementor - ifNil: [ (SystemNavigation default allImplementorsOf: selector) - ifEmpty: [ nil ] - ifNotEmpty: [ :impls | someImplementor := impls anyOne methodClass ] ] -] - -{ #category : #private } -GtRenameMethodController >> tab: forward [ - self originalNode arguments size < 2 - ifTrue: [ ^ self ]. - forward - ifTrue: [ selectorIndex := selectorIndex + 1. - selectorIndex > self originalNode arguments size - ifTrue: [ selectorIndex := 1 ] ] - ifFalse: [ selectorIndex := selectorIndex - 1. - selectorIndex = 0 - ifTrue: [ selectorIndex := self originalNode arguments size ] ]. - self newNode - ifNotNil: [ :message | - self removeRefactoringAttribute. - self sourceEditor - moveCursorTo: (message selectorParts at: selectorIndex) stopPosition. - self installRenamerOn: message. - newSelectorParts = selector keywords - ifFalse: [ self addRefactoringChangesPreview ] ] -] - -{ #category : #private } -GtRenameMethodController >> updateName: aString [ - - "we should update selector parts before updating the name, otherwise the refactoring will be canceled" - newSelectorParts at: selectorIndex put: aString. - super updateName: aString -] diff --git a/src/GToolkit-Coder-AddOns/GtRenamePreviewAttribute.class.st b/src/GToolkit-Coder-AddOns/GtRenamePreviewAttribute.class.st index 5130a0425..9cf125a9d 100644 --- a/src/GToolkit-Coder-AddOns/GtRenamePreviewAttribute.class.st +++ b/src/GToolkit-Coder-AddOns/GtRenamePreviewAttribute.class.st @@ -31,7 +31,7 @@ GtRenamePreviewAttribute >> doAffect: aTBrTextEditorTextualPiece in: anEditorEle constraintsDo: [ :c | c horizontal fitContent. c vertical fitContent ]. - editor := BrTextEditor new + editor := BrTextEditorModel new text: text; yourself. @@ -66,9 +66,9 @@ GtRenamePreviewAttribute >> text [ GtRenamePreviewAttribute >> text: blText [ text := blText asString asRopedText. originalAttributes isNil - ifTrue: [ originalAttributes := blText isEmpty - ifTrue: [ #() ] - ifFalse: [ blText attributesAt: 1 ] ]. + ifTrue: + [ originalAttributes := (blText isEmpty ifTrue: [ #() ] ifFalse: [ blText attributesAt: 1 ]) + reject: [ :each | each isEventHandler ] ]. text attributes: originalAttributes , self additionalAttributes ] diff --git a/src/GToolkit-Coder-AddOns/GtRenameVariableController.class.st b/src/GToolkit-Coder-AddOns/GtRenameVariableController.class.st deleted file mode 100644 index e306322d0..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameVariableController.class.st +++ /dev/null @@ -1,44 +0,0 @@ -Class { - #name : #GtRenameVariableController, - #superclass : #GtRenameController, - #category : #'GToolkit-Coder-AddOns-Inline rename' -} - -{ #category : #accessing } -GtRenameVariableController >> addRefactoringChangesPreview [ - super addRefactoringChangesPreview. - self styleRenamedVariable -] - -{ #category : #executing } -GtRenameVariableController >> installRenamer [ - newName := self originalNode source. - renameInterval := self originalNode sourceInterval. - - self installRenameAction -] - -{ #category : #accessing } -GtRenameVariableController >> originalName [ - ^ self originalNode source -] - -{ #category : #accessing } -GtRenameVariableController >> refactoring [ - self originalName = self newName - ifTrue: [ ^ nil ]. - ^ self rename: self originalName to: self newName -] - -{ #category : #accessing } -GtRenameVariableController >> rename: oldName to: newName [ - ^ self subclassResponsibility -] - -{ #category : #accessing } -GtRenameVariableController >> styleRenamedVariable [ - (self sourceText - from: renameInterval first - to: self renamePosition) - attributes: {(BlTextForegroundAttribute paint: Color gray) } -] diff --git a/src/GToolkit-Coder-AddOns/GtRenameVariableFixItAction.class.st b/src/GToolkit-Coder-AddOns/GtRenameVariableFixItAction.class.st deleted file mode 100644 index 2a533651e..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameVariableFixItAction.class.st +++ /dev/null @@ -1,34 +0,0 @@ -Class { - #name : #GtRenameVariableFixItAction, - #superclass : #GtFixItVariableNodeAction, - #instVars : [ - 'newName' - ], - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #accessing } -GtRenameVariableFixItAction >> description [ - ^ 'Use ' , newName , ' instead of ' , self nodeName -] - -{ #category : #accessing } -GtRenameVariableFixItAction >> executeOn: anEditorElement [ - | newSource | - newSource := sourceCoder currentSourceString. - newSource := (newSource first: self startPosition - 1) , newName - , (newSource allButFirst: self stopPosition). - sourceCoder currentSourceString: newSource -] - -{ #category : #accessing } -GtRenameVariableFixItAction >> id [ - - - ^ GtRenameVariableFixItActionElementId -] - -{ #category : #initialization } -GtRenameVariableFixItAction >> newName: aString [ - newName := aString -] diff --git a/src/GToolkit-Coder-AddOns/GtRenameVariableFixItActionElementId.class.st b/src/GToolkit-Coder-AddOns/GtRenameVariableFixItActionElementId.class.st deleted file mode 100644 index edc10139b..000000000 --- a/src/GToolkit-Coder-AddOns/GtRenameVariableFixItActionElementId.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtRenameVariableFixItActionElementId, - #superclass : #GtFixItActionElementId, - #category : #'GToolkit-Coder-AddOns-FixIt' -} - -{ #category : #converting } -GtRenameVariableFixItActionElementId >> asSymbol [ - ^ #'fixit-action--rename-variable' -] diff --git a/src/GToolkit-Coder-AddOns/GtSourceCoder.extension.st b/src/GToolkit-Coder-AddOns/GtSourceCoder.extension.st new file mode 100644 index 000000000..fe188255c --- /dev/null +++ b/src/GToolkit-Coder-AddOns/GtSourceCoder.extension.st @@ -0,0 +1,54 @@ +Extension { #name : #GtSourceCoder } + +{ #category : #'*GToolkit-Coder-AddOns' } +GtSourceCoder >> createLabel: aString description: description [ + ^ aString asRopedText glamorousRegularFont + , ((' ' , description) asRopedText + glamorousCodeFont; + foreground: Color gray; + glamorousCodeTinySize) +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtSourceCoder >> extractableNodesAt: anIndex [ + "Return a collection of extractable nodes that intersect a given interval" + + + ^ self + nodeAt: anIndex + ifFound: [ :aNode | + aNode isSequence + ifTrue: [ aNode statements select: [ :eachNode | eachNode intersectsInterval: (anIndex to: anIndex) ] ] + ifFalse: [ { aNode isMessage ifTrue: [ aNode parent ] ifFalse: [ aNode ] } ] ] + ifNone: [ #() ] +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtSourceCoder >> extractableNodesFrom: aCoderViewModel [ + | allSelections allCursors | + + allSelections := aCoderViewModel selection allSelections. + allCursors := aCoderViewModel cursors allCursors. + + ^ allSelections size = 1 + ifTrue: [ self extractableNodesWithin: (allSelections first from + 1 + to: allSelections first to) ] + ifFalse: [ allCursors size = 1 + ifTrue: [ self extractableNodesAt: allCursors first position ] + ifFalse: [ #() ] ] +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtSourceCoder >> extractableNodesWithin: anInterval [ + "Return a collection of extractable nodes that intersect a given interval" + + + ^ self + nodeWithin: anInterval + ifFound: [ :aNode | + aNode isSequence + ifTrue: [ aNode statements + select: [ :eachNode | eachNode intersectsInterval: anInterval ] ] + ifFalse: [ {aNode} ] ] + ifNone: [ #() ] +] diff --git a/src/GToolkit-Coder-AddOns/GtSourceCoderInlineRenameShortcut.class.st b/src/GToolkit-Coder-AddOns/GtSourceCoderInlineRenameShortcut.class.st index ae5bc710b..e2f8390da 100644 --- a/src/GToolkit-Coder-AddOns/GtSourceCoderInlineRenameShortcut.class.st +++ b/src/GToolkit-Coder-AddOns/GtSourceCoderInlineRenameShortcut.class.st @@ -22,10 +22,18 @@ GtSourceCoderInlineRenameShortcut >> name [ ] { #category : #evaluation } -GtSourceCoderInlineRenameShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ - - self - forEditor: aBrTextEditor - selectionIntervalDo: [ :aSelectionInterval | aGtSourceCoder renameAt: aSelectionInterval last in: aBrEditorElement ] - orCursorStringPositionDo: [ :aCursorTextPosition | aGtSourceCoder renameAt: aCursorTextPosition in: aBrEditorElement ] +GtSourceCoderInlineRenameShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString :aSelectionInterval | + aSourceCoderViewModel coder + renameAt: aSelectionInterval last + in: aSourceCoderViewModel + documentId: aSourceCoderViewModel documentId + for: aBrEditorElement ] + orCursorPositionDo: [ :aCursorTextPosition | + aSourceCoderViewModel coder + renameAt: aCursorTextPosition + in: aSourceCoderViewModel + documentId: aSourceCoderViewModel documentId + for: aBrEditorElement ] ] diff --git a/src/GToolkit-Coder-AddOns/GtSourceCoderRefactoringController.class.st b/src/GToolkit-Coder-AddOns/GtSourceCoderRefactoringController.class.st index 4f115e762..60b078761 100644 --- a/src/GToolkit-Coder-AddOns/GtSourceCoderRefactoringController.class.st +++ b/src/GToolkit-Coder-AddOns/GtSourceCoderRefactoringController.class.st @@ -8,72 +8,96 @@ Class { #name : #GtSourceCoderRefactoringController, #superclass : #Object, #instVars : [ - 'coder', - 'sourceElement', - 'originalSource' + 'originalSource', + 'sourceCoderViewModel', + 'refactoringPreviewAttributes', + 'element', + 'documentId' ], #category : #'GToolkit-Coder-AddOns-! Core' } -{ #category : #'private - attributes' } -GtSourceCoderRefactoringController >> addRefactoringChangesPreview: aRefactoring at: aTextPosition [ - self - addRefactoringChangesPreview: aRefactoring - at: aTextPosition - whenComplete: nil +{ #category : #private } +GtSourceCoderRefactoringController >> allowSave [ + self sourceCoderViewModel allowSaveDueTo: self ] -{ #category : #'private - attributes' } -GtSourceCoderRefactoringController >> addRefactoringChangesPreview: aRefactoring at: aTextPosition whenComplete: aBlock [ - self removeRefactoringAttribute. - aRefactoring isNil - ifTrue: [ ^ self ]. - - (self sourceText from: aTextPosition to: aTextPosition) - attributes: { - (GtRefactoringChangesAttribute new - refactoring: aRefactoring; - completionAction: aBlock) } +{ #category : #accessing } +GtSourceCoderRefactoringController >> cancelRefactoring [ + self cleanUp. + self restoreText ] -{ #category : #private } -GtSourceCoderRefactoringController >> allowSave [ - self sourceElement textualCoderViewModel allowSaveDueTo: self +{ #category : #accessing } +GtSourceCoderRefactoringController >> cleanUp [ + self removeRefactoringAttribute. + self allowSave ] { #category : #accessing } -GtSourceCoderRefactoringController >> coder [ - ^ coder +GtSourceCoderRefactoringController >> documentId [ + ^ documentId ] { #category : #accessing } -GtSourceCoderRefactoringController >> coder: methodCoder [ - coder := methodCoder +GtSourceCoderRefactoringController >> documentId: anObject [ + documentId := anObject ] { #category : #executing } GtSourceCoderRefactoringController >> execute [ - [ - self saveSource. - self safelyExecute ] - on: Error - do: [ :ex | - self refactoringFailed. - NonInteractiveTranscript stderr - nextPutAll: ('[{1}] {2}' format: { self class name . ex asString }); - cr. - - (ex signalerContext stack first: 20) do: [ :eachStackEntry | + [ self saveSource. + self safelyExecute ] + on: Error + do: [ :ex | + self refactoringFailed. + (ex isKindOf: RBRefactoringError) + ifTrue: [ self inform: self refactoringName , ': ' , ex messageText ] + ifFalse: [ self informException: ex. NonInteractiveTranscript stderr - nextPutAll: eachStackEntry printString; - cr ]. - - ex return ] + nextPutAll: ('[{1}] {2}' + format: {self class name. + ex asString}); + cr. + + (ex signalerContext stack first: 20) + do: [ :eachStackEntry | + NonInteractiveTranscript stderr + nextPutAll: eachStackEntry printString; + cr ] ]. + self cancelRefactoring. + ex return ] +] + +{ #category : #executing } +GtSourceCoderRefactoringController >> executeIn: anElement [ + element := anElement. + ^ self execute +] + +{ #category : #accessing } +GtSourceCoderRefactoringController >> inform: aString [ + element ifNil: [ ^ super inform: aString ]. + element + showNotification: (GtNotificationMessage new + showNotifications; + message: aString) +] + +{ #category : #accessing } +GtSourceCoderRefactoringController >> informException: anException [ + element + showNotification: (GtObjectNotificationMessage new + message: ('[{1}] {2}' + format: {anException class name. + anException messageText}); + object: anException copy freeze; + showNotifications) ] { #category : #private } GtSourceCoderRefactoringController >> preventSave [ - self sourceElement textualCoderViewModel preventSaveDueTo: self + self sourceCoderViewModel preventSaveDueTo: self ] { #category : #accessing } @@ -92,8 +116,10 @@ GtSourceCoderRefactoringController >> refactoringName [ { #category : #'private - attributes' } GtSourceCoderRefactoringController >> removeRefactoringAttribute [ - self sourceEditor text - clearAttributes: [ :each | each class = GtRefactoringChangesAttribute ] + refactoringPreviewAttributes ifNil: [ ^ self ]. + self sourceCoderViewModel + removeCoderTextAttributes: refactoringPreviewAttributes. + refactoringPreviewAttributes := nil ] { #category : #private } @@ -112,28 +138,26 @@ GtSourceCoderRefactoringController >> saveSource [ ] { #category : #private } -GtSourceCoderRefactoringController >> setText: aStringOrText [ - self sourceElement - text: aStringOrText asRopedText; - onTextModified +GtSourceCoderRefactoringController >> setText: aStringOrText [ + self sourceCoderViewModel + sourceText: aStringOrText asRopedText + from: self + synchronously: true ] { #category : #accessing } -GtSourceCoderRefactoringController >> sourceEditor [ - ^ self sourceElement editor -] +GtSourceCoderRefactoringController >> sourceCoderViewModel [ + -{ #category : #accessing } -GtSourceCoderRefactoringController >> sourceElement [ - ^ sourceElement + ^ sourceCoderViewModel ] -{ #category : #accessing } -GtSourceCoderRefactoringController >> sourceElement: textElement [ - sourceElement := textElement +{ #category : #initialization } +GtSourceCoderRefactoringController >> sourceCoderViewModel: aSourceCoderViewModel [ + sourceCoderViewModel := aSourceCoderViewModel ] { #category : #accessing } GtSourceCoderRefactoringController >> sourceText [ - ^ self sourceEditor text + ^ self sourceCoderViewModel sourceText copy ] diff --git a/src/GToolkit-Coder-AddOns/GtTextualCoder.extension.st b/src/GToolkit-Coder-AddOns/GtTextualCoder.extension.st index bf6ff786c..9485597c7 100644 --- a/src/GToolkit-Coder-AddOns/GtTextualCoder.extension.st +++ b/src/GToolkit-Coder-AddOns/GtTextualCoder.extension.st @@ -1,16 +1,157 @@ Extension { #name : #GtTextualCoder } +{ #category : #'*GToolkit-Coder-AddOns' } +GtTextualCoder >> addContextMenuItemFrom: shortcut group: menuGroup withId: menuId to: coderAddOns [ + + ^ coderAddOns + addContextMenuItem: shortcut name + group: menuGroup + action: [ :aCoderViewModel :anEditorElement | + shortcut + performOnEditor: anEditorElement editor + element: anEditorElement + dueTo: nil ] + id: menuId + shortcutKey: shortcut combinationForPlatform gtDisplayString +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtTextualCoder >> addContextMenuItemFrom: shortcut withId: menuId to: coderAddOns [ + + ^ coderAddOns + addContextMenuItem: shortcut name + action: [ :aCoderViewModel :anEditorElement | + shortcut + performOnEditor: anEditorElement editor + element: anEditorElement + dueTo: nil ] + id: menuId + shortcutKey: shortcut combinationForPlatform gtDisplayString +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtTextualCoder >> addContextMenuItemWithNoShortcutFrom: shortcut group: menuGroup withId: menuId to: coderAddOns [ + + ^ coderAddOns + addContextMenuItem: shortcut name + group: menuGroup + action: [ :aCoderViewModel :anEditorElement | + shortcut + performOnEditor: anEditorElement editor + element: anEditorElement + dueTo: nil ] + id: menuId +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtTextualCoder >> addContextMenuItemWithNoShortcutFrom: shortcut withId: menuId to: coderAddOns [ + ^ coderAddOns + addContextMenuItem: shortcut name + action: [ :aCoderViewModel :anEditorElement | + shortcut + performOnEditor: anEditorElement editor + element: anEditorElement + dueTo: nil ] + id: menuId +] + { #category : #'*GToolkit-Coder-AddOns' } GtTextualCoder >> addCopyCutPasteContextMenuAddOnsAst: anAst to: coderAddOns [ - - coderAddOns - addContextMenuItem: 'Cut' - action: [ :aCoderViewModel :aClickEvent :anEditorElement | anEditorElement editor cutSelected ] - id: GtTextualCoderCutContextMenuItemId; - addContextMenuItem: 'Copy' - action: [ :aCoderViewModel :aClickEvent :anEditorElement | anEditorElement editor copySelected ] - id: GtTextualCoderCopyContextMenuItemId; - addContextMenuItem: 'Paste' - action: [ :aCoderViewModel :aClickEvent :anEditorElement | anEditorElement editor paste ] - id: GtTextualCoderPasteContextMenuItemId + + (self + addContextMenuItemFrom: BrEditorShortcut cut + group: BrMenuItemGroupConfiguration editing + withId: GtTextualCoderCutContextMenuItemId + to: coderAddOns) ifNotNil: [ :anAction | anAction priority: 1000.01 ]. + (self + addContextMenuItemFrom: BrEditorShortcut copy + group: BrMenuItemGroupConfiguration editing + withId: GtTextualCoderCopyContextMenuItemId + to: coderAddOns) ifNotNil: [ :anAction | anAction priority: 1000.02 ]. + (self + addContextMenuItemFrom: BrEditorShortcut paste + group: BrMenuItemGroupConfiguration editing + withId: GtTextualCoderPasteContextMenuItemId + to: coderAddOns) ifNotNil: [ :anAction | anAction priority: 1000.03 ] +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtTextualCoder >> addExplicitContextMenu: aString block: aBlock to: coderAddOns [ + + ^ coderAddOns addExplicitContextMenu: aString block: aBlock +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtTextualCoder >> initializeSearchTextAddOns: addOns viewModel: aCoderViewModel [ + GtTextualCoderSearchTextSettings isEnabledInCoder ifFalse: [ ^ self ]. + + addOns + addMainAction: (GtCoderCustomAction new + title: 'Search'; + stencil: [ BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + icon: BrGlamorousVectorIcons search; + id: GtCoderSearchActionId; + label: 'Search'; + beTinySize; + when: BrDropdownIsHidden + do: [ :anEvent | aCoderViewModel announce: GtSourceCoderViewModelSearchTextCancelRequested new ]; + addAptitude: (BrGlamorousWithExplicitDropdownAptitude new + in: [ :theAptitude | + aCoderViewModel weak + when: GtSourceCoderViewModelSearchTextRequested + send: #show + to: theAptitude ]; + withVisibleWidgetBoundsRelocator; + withContainerPermanentVisibilityUpdater; + menuContainerDo: [ :aContainer | + aContainer layout + areaBuilders: {BlSteppedLayoutAreaBuilder dropdownBottomRight. + BlSteppedLayoutAreaBuilder dropdownBottomLeft. + BlSteppedLayoutAreaBuilder allToBottom. + BlSteppedLayoutAreaBuilder allToRight. + BlSteppedLayoutAreaBuilder allToLeft. + BlSteppedLayoutAreaBuilder dropdownUpRight. + BlSteppedLayoutAreaBuilder dropdownUpLeft. + BlSteppedLayoutAreaBuilder allToUp} ]; + handle: [ BrButton new + aptitude: BrGlamorousButtonWithIconAptitude - BrGlamorousButtonExteriorAptitude + - BrGlamorousButtonWithLabelTooltipAptitude2; + icon: BrGlamorousVectorIcons search; + beTinySize ] + content: [ :anExplicitMenu | + | anElement | + anElement := GtTextualCoderSearchTextElement new + textualCoderViewModel: aCoderViewModel; + editorDo: [ :anEditor | + anEditor requestFocus. + aCoderViewModel selection isEmpty ifFalse: [ + | text | + text := (aCoderViewModel sourceText + copyFrom: aCoderViewModel selection from + 1 + to: aCoderViewModel selection to) asString. + anEditor doBlockSoon: [ + (anEditor inserter) + atCursor; + string: text; + insert. + (anEditor selecter) + all; + select ] ] ]; + hFitContentLimited; + constraintsDo: [ :c | + c minWidth: 300. + c frame horizontal alignCenter. + c frame vertical alignCenter ]. + BrFrame new + fitContentLimited; + padding: (BlInsets all: 5); + addChild: anElement ]) ]) +] + +{ #category : #'*GToolkit-Coder-AddOns' } +GtTextualCoder >> initializeShortcuts: addOns [ + super initializeShortcuts: addOns. + + addOns addShortcut: GtSourceCoderSearchTextShortcut new ] diff --git a/src/GToolkit-Coder-AddOns/GtVariableInputFilter.class.st b/src/GToolkit-Coder-AddOns/GtVariableInputFilter.class.st new file mode 100644 index 000000000..33f748862 --- /dev/null +++ b/src/GToolkit-Coder-AddOns/GtVariableInputFilter.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtVariableInputFilter, + #superclass : #BrTextEditorInputFilter, + #category : #'GToolkit-Coder-AddOns-Inline rename' +} + +{ #category : #'as yet unclassified' } +GtVariableInputFilter >> filter: aString [ + ^ aString select: [ :each | each isAlphaNumeric or: [ each = $_ ] ] +] diff --git a/src/GToolkit-Coder-Examples-SystemS1/GtSystemS1AnotherClassC3.class.st b/src/GToolkit-Coder-Examples-SystemS1/GtSystemS1AnotherClassC3.class.st index abca81c81..c50cc321f 100644 --- a/src/GToolkit-Coder-Examples-SystemS1/GtSystemS1AnotherClassC3.class.st +++ b/src/GToolkit-Coder-Examples-SystemS1/GtSystemS1AnotherClassC3.class.st @@ -15,6 +15,7 @@ GtSystemS1AnotherClassC3 class >> isDeprecated [ { #category : #example } GtSystemS1AnotherClassC3 >> methodWithExample [ + ^ self class new ] diff --git a/src/GToolkit-Coder-Examples/GtBehaviorCoderDummyClass.class.st b/src/GToolkit-Coder-Examples/GtBehaviorCoderDummyClass.class.st index ca12acc6e..f8f660d19 100644 --- a/src/GToolkit-Coder-Examples/GtBehaviorCoderDummyClass.class.st +++ b/src/GToolkit-Coder-Examples/GtBehaviorCoderDummyClass.class.st @@ -73,3 +73,8 @@ GtBehaviorCoderDummyClass >> instVarC: anObject [ GtBehaviorCoderDummyClass >> newMethod [ ^ 4 ] + +{ #category : #'tests - some' } +GtBehaviorCoderDummyClass >> overwrittenMethodFromTrait [ + "overwritten" +] diff --git a/src/GToolkit-Coder-Examples/GtCoderByScripterExamples.class.st b/src/GToolkit-Coder-Examples/GtCoderByScripterExamples.class.st index f5240c3d0..fd3c947b6 100644 --- a/src/GToolkit-Coder-Examples/GtCoderByScripterExamples.class.st +++ b/src/GToolkit-Coder-Examples/GtCoderByScripterExamples.class.st @@ -3,5 +3,5 @@ Class { #superclass : #Object, #traits : 'TCoderByScripterExamples', #classTraits : 'TCoderByScripterExamples classTrait', - #category : #'GToolkit-Coder-Examples-By scripter' + #category : #'GToolkit-Coder-Examples-Scripter' } diff --git a/src/GToolkit-Coder-Examples/GtCoderClassesHierarchyTreeExamples.class.st b/src/GToolkit-Coder-Examples/GtCoderClassesHierarchyTreeExamples.class.st index c3a899fca..a1a18c1e9 100644 --- a/src/GToolkit-Coder-Examples/GtCoderClassesHierarchyTreeExamples.class.st +++ b/src/GToolkit-Coder-Examples/GtCoderClassesHierarchyTreeExamples.class.st @@ -7,50 +7,67 @@ Class { { #category : #'instance creation' } GtCoderClassesHierarchyTreeExamples >> fixedHierarchyForSequenceableCollectionClass [ + | aHierarchyTree aSequenceableCollectionTree | - - aHierarchyTree := GtCoderClassesHierarchyTree hierarchyForClass: SequenceableCollection. + aHierarchyTree := GtCoderClassesHierarchyTree + hierarchyForClass: SequenceableCollection. aSequenceableCollectionTree := aHierarchyTree first first first at: 1. - - self assert: aSequenceableCollectionTree rootClass equals: SequenceableCollection. + + self + assert: aSequenceableCollectionTree rootClass + equals: SequenceableCollection. self assert: aSequenceableCollectionTree subclassTrees isEmpty. self assert: aSequenceableCollectionTree size equals: 0. - + ^ aHierarchyTree ] { #category : #'instance creation' } GtCoderClassesHierarchyTreeExamples >> fromCollectionClasses [ + | aHierarchyTree | - aHierarchyTree := GtCoderClassesHierarchyTree - fromClasses: { Collection. Array . SequenceableCollection . OrderedCollection . Set . Dictionary . OrderedDictionary }. - - self assert: aHierarchyTree size equals: 1. - self assert: aHierarchyTree first classes asSet equals: { Set . SequenceableCollection . OrderedDictionary . Dictionary } asSet. - self assert: (((aHierarchyTree sort: [:a :b | a name < b name ]) first at: 3) classes asSet = { Array . OrderedCollection } asSet). - + fromClasses: {Collection. + Array. + SequenceableCollection. + OrderedCollection. + Set. + Dictionary. + OrderedDictionary}. + + self assert: aHierarchyTree size equals: 4. + self + assert: aHierarchyTree classes asSet + equals: {Set. + Array. + Collection. + Dictionary} asSet. + self + assert: ((aHierarchyTree sort: [ :a :b | a name < b name ]) at: 2) classes asSet + = {OrderedDictionary. + SequenceableCollection} asSet. + ^ aHierarchyTree ] { #category : #'instance creation' } GtCoderClassesHierarchyTreeExamples >> growingHierarchyForArrayedCollectionClass [ + | aHierarchyTree aSequenceableCollectionTree anArrayedCollection | - aHierarchyTree := self growingHierarchyForSequenceableCollectionClass. aSequenceableCollectionTree := aHierarchyTree first first first at: 1. - anArrayedCollection := aSequenceableCollectionTree currentSubclassTrees - detect: [ :each | each rootClass = ArrayedCollection ]. - + anArrayedCollection := aSequenceableCollectionTree currentSubclassTrees + detect: [ :each | each rootClass = ArrayedCollection ]. + self assert: anArrayedCollection currentSubclassTrees isEmpty. - self assert: anArrayedCollection currentSize equals: 0. + self assert: anArrayedCollection currentSize equals: 0. self assert: anArrayedCollection subclassTrees size > 0. self assert: anArrayedCollection size > 0. self assert: anArrayedCollection currentSubclassTrees size > 0. - self assert: anArrayedCollection currentSize > 0. + self assert: anArrayedCollection currentSize > 0. ^ aHierarchyTree ] @@ -58,36 +75,41 @@ GtCoderClassesHierarchyTreeExamples >> growingHierarchyForArrayedCollectionClass { #category : #'instance creation' } GtCoderClassesHierarchyTreeExamples >> growingHierarchyForSequenceableCollectionClass [ + | aHierarchyTree aSequenceableCollectionTree | - aHierarchyTree := self hierarchyForSequenceableCollectionClass. aSequenceableCollectionTree := aHierarchyTree first first first at: 1. - - self assert: aSequenceableCollectionTree rootClass equals: SequenceableCollection. + + self + assert: aSequenceableCollectionTree rootClass + equals: SequenceableCollection. self assert: aSequenceableCollectionTree currentSubclassTrees isEmpty. self assert: aSequenceableCollectionTree currentSize equals: 0. self assert: aSequenceableCollectionTree size > 0. - + self assert: aSequenceableCollectionTree subclassTrees size > 0. self assert: aSequenceableCollectionTree size > 0. self assert: aSequenceableCollectionTree currentSubclassTrees size > 0. self assert: aSequenceableCollectionTree currentSize > 0. - + ^ aHierarchyTree ] { #category : #'instance creation' } GtCoderClassesHierarchyTreeExamples >> hierarchyForSequenceableCollectionClass [ + | aHierarchyTree | - - aHierarchyTree := GtCoderGrowingClassesHierarchyTree hierarchyForClass: SequenceableCollection. - + aHierarchyTree := GtCoderGrowingClassesHierarchyTree + hierarchyForClass: SequenceableCollection. + self assert: aHierarchyTree size equals: 1. - self assert: aHierarchyTree first classes equals: { Object }. - self assert: (aHierarchyTree first at: 1) classes equals: { Collection }. - self assert: (aHierarchyTree first first at: 1) classes equals: { SequenceableCollection }. - + self assert: aHierarchyTree first classes equals: {Object}. + self assert: (aHierarchyTree first at: 1) classes equals: {Collection}. + self + assert: (aHierarchyTree first first at: 1) classes + equals: {SequenceableCollection}. + ^ aHierarchyTree ] diff --git a/src/GToolkit-Coder-Examples/GtCoderExampleStateElementExamples.class.st b/src/GToolkit-Coder-Examples/GtCoderExampleStateElementExamples.class.st index 23f713c74..573fd3597 100644 --- a/src/GToolkit-Coder-Examples/GtCoderExampleStateElementExamples.class.st +++ b/src/GToolkit-Coder-Examples/GtCoderExampleStateElementExamples.class.st @@ -25,6 +25,7 @@ GtCoderExampleStateElementExamples >> assert: anElement backgroundColor: aColor { #category : #accessing } GtCoderExampleStateElementExamples >> basicExampleStatusElement [ + | anElement | anElement := GtCoderExampleStateElement new. self assert: anElement example isNil. @@ -35,47 +36,46 @@ GtCoderExampleStateElementExamples >> basicExampleStatusElement [ { #category : #accessing } GtCoderExampleStateElementExamples >> errorExample [ - ^ (self class >>#three) gtExample asExampleWithResult + + ^ (self class >> #three) gtExample asExampleWithResult ] { #category : #accessing } GtCoderExampleStateElementExamples >> exampleStateElement_Error [ + | anElement anExample | anElement := self basicExampleStatusElement. anExample := self errorExample. anElement example: anExample. - + self assert: anElement example equals: anExample. - BlUseAsyncFeatures disableDuring: [ - anExample run ]. + BlUseAsyncFeatures disableDuring: [ anExample run ]. self assert: anElement exampleResult isNotNil. self assert: anElement exampleResult isError. - self - assert: anElement - backgroundColor: GtFilterExampleErrorState default color. + self assert: anElement backgroundColor: GtFilterExampleErrorState default color. ^ anElement ] { #category : #accessing } GtCoderExampleStateElementExamples >> exampleStateElement_Failure [ + | anElement anExample | anElement := self basicExampleStatusElement. anExample := self failingExample. anElement example: anExample. - + self assert: anElement example equals: anExample. - BlUseAsyncFeatures disableDuring: [ - anExample run ]. + BlUseAsyncFeatures disableDuring: [ anExample run ]. self assert: anElement exampleResult isNotNil. self assert: anElement exampleResult isFailure. - self - assert: anElement + self + assert: anElement backgroundColor: GtFilterExampleFailureState default color. ^ anElement ] @@ -83,14 +83,15 @@ GtCoderExampleStateElementExamples >> exampleStateElement_Failure [ { #category : #accessing } GtCoderExampleStateElementExamples >> exampleStateElement_NotExecuted [ + | anElement anExample | anElement := self basicExampleStatusElement. anExample := self successExample. anElement example: anExample. self assert: anElement example equals: anExample. self assert: anElement exampleResult isNil. - self - assert: anElement + self + assert: anElement backgroundColor: GtFilterExampleNotExecutedState default color. ^ anElement ] @@ -98,15 +99,15 @@ GtCoderExampleStateElementExamples >> exampleStateElement_NotExecuted [ { #category : #accessing } GtCoderExampleStateElementExamples >> exampleStateElement_Success [ + | anElement anExample | anElement := self exampleStateElement_NotExecuted. anExample := anElement example. - BlUseAsyncFeatures disableDuring: [ - anExample run ]. + BlUseAsyncFeatures disableDuring: [ anExample run ]. self assert: anElement exampleResult isNotNil. self assert: anElement exampleResult isSuccess. - self - assert: anElement + self + assert: anElement backgroundColor: GtFilterExampleSuccessState default color. ^ anElement ] @@ -114,19 +115,22 @@ GtCoderExampleStateElementExamples >> exampleStateElement_Success [ { #category : #accessing } GtCoderExampleStateElementExamples >> failingExample [ - ^ (self class >>#two) gtExample asExampleWithResult + + ^ (self class >> #two) gtExample asExampleWithResult ] { #category : #accessing } GtCoderExampleStateElementExamples >> one [ + ^ 1 ] { #category : #accessing } GtCoderExampleStateElementExamples >> successExample [ - ^ (self class >>#one) gtExample asExampleWithResult + + ^ (self class >> #one) gtExample asExampleWithResult ] { #category : #accessing } diff --git a/src/GToolkit-Coder-Examples/GtCoderExamplesEventsRecorder.class.st b/src/GToolkit-Coder-Examples/GtCoderExamplesEventsRecorder.class.st index 5bf09e3e4..f469ab39c 100644 --- a/src/GToolkit-Coder-Examples/GtCoderExamplesEventsRecorder.class.st +++ b/src/GToolkit-Coder-Examples/GtCoderExamplesEventsRecorder.class.st @@ -17,6 +17,18 @@ GtCoderExamplesEventsRecorder >> first [ ^ self events first ] +{ #category : #'gt-extensions' } +GtCoderExamplesEventsRecorder >> gtEventsFor: aView [ + + + self events ifNil: [ ^ aView empty ]. + + ^ aView list + title: 'Events'; + priority: 1; + items: [ self events ] +] + { #category : #initialization } GtCoderExamplesEventsRecorder >> initialize [ super initialize. diff --git a/src/GToolkit-Coder-Examples/GtCoderNavigationClassesHierarchyListElementExamples.class.st b/src/GToolkit-Coder-Examples/GtCoderNavigationClassesHierarchyListElementExamples.class.st index 0f3de236e..8523b6cd3 100644 --- a/src/GToolkit-Coder-Examples/GtCoderNavigationClassesHierarchyListElementExamples.class.st +++ b/src/GToolkit-Coder-Examples/GtCoderNavigationClassesHierarchyListElementExamples.class.st @@ -7,7 +7,7 @@ Class { { #category : #'instance creation' } GtCoderNavigationClassesHierarchyListElementExamples >> fromCollectionClasses [ - - ^ GtCoderNavigationClassesHierarchyTreeElement + + ^ GtCoderClassesTreeElement fromClasses: #'Collections-Abstract' asPackage definedClasses ] diff --git a/src/GToolkit-Coder-Examples/GtCoderNavigationClassesListElementExamples.class.st b/src/GToolkit-Coder-Examples/GtCoderNavigationClassesListElementExamples.class.st deleted file mode 100644 index 14e424e88..000000000 --- a/src/GToolkit-Coder-Examples/GtCoderNavigationClassesListElementExamples.class.st +++ /dev/null @@ -1,13 +0,0 @@ -Class { - #name : #GtCoderNavigationClassesListElementExamples, - #superclass : #Object, - #category : #'GToolkit-Coder-Examples-Navigation' -} - -{ #category : #'instance creation' } -GtCoderNavigationClassesListElementExamples >> fromCollectionClasses [ - - - ^ GtCoderNavigationClassesListElement - fromClasses: #'Collections-Abstract' asPackage definedClasses -] diff --git a/src/GToolkit-Coder-Examples/GtCoderNavigationIndexStep.class.st b/src/GToolkit-Coder-Examples/GtCoderNavigationIndexStep.class.st new file mode 100644 index 000000000..8b477fc5e --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtCoderNavigationIndexStep.class.st @@ -0,0 +1,56 @@ +Class { + #name : #GtCoderNavigationIndexStep, + #superclass : #BlDevScripterToolActionStep, + #category : #'GToolkit-Coder-Examples-Scripter' +} + +{ #category : #'steps - interactions' } +GtCoderNavigationIndexStep >> assertSelectedMethod: aCompiledMethod [ + ^ self assert + label: ('Assert selected method: {1}' format: {aCompiledMethod selector}); + satisfies: [ :aLabel | aLabel states isSelected ]; + onParentStepTarget: self; + onThisOrBreadthFirstChildOfKind: GtCoderMethodsGroupedListElement; + // GtCoderNavigationCompiledMethodElement; + @ [ :each | each compiledMethod = aCompiledMethod ] +] + +{ #category : #accessing } +GtCoderNavigationIndexStep >> defaultLabel [ + ^ 'Coder navigation' +] + +{ #category : #'steps - interactions' } +GtCoderNavigationIndexStep >> selectMethods: someCompiledMethods [ + ^ someCompiledMethods size = 1 + ifTrue: [ self click + label: ('Select method: {1}' format: {someCompiledMethods first selector}); + onParentStepTarget: self; + onThisOrBreadthFirstChildOfKind: GtCoderMethodsGroupedListElement; + // BrLabel; + @ [ :eachLabel | eachLabel text asString = someCompiledMethods first selector ] ] + ifFalse: [ self + substeps2: ('Select {1} methods' format: {someCompiledMethods size}) + do: [ :aStep | + aStep click + label: ('Select the first method: {1}' format: {someCompiledMethods first selector}); + onParentStepTarget: aStep; + onThisOrBreadthFirstChildOfKind: GtCoderMethodsGroupedListElement; + // GtCoderNavigationCompiledMethodElement; + @ [ :eachLabel | eachLabel compiledMethod = someCompiledMethods first ]. + + aStep click + label: 'Select the other methods'; + modifiers: (BlKeyModifiers primary shift: true); + onParentStepTarget: aStep; + onThisOrBreadthFirstChildOfKind: GtCoderMethodsGroupedListElement; + // GtCoderNavigationCompiledMethodElement; + @ [ :each | each compiledMethod = someCompiledMethods last ]. + + aStep assert + label: 'Assert list selection'; + value: [ :aList | aList selectedItems asArray ] + equals: someCompiledMethods asArray; + onParentStepTarget: aStep; + onThisOrBreadthFirstChildOfKind: GtCoderMethodsGroupedListElement ] ] +] diff --git a/src/GToolkit-Coder-Examples/GtCoderNavigationPackagesListElementExamples.class.st b/src/GToolkit-Coder-Examples/GtCoderNavigationPackagesListElementExamples.class.st index 0c2226454..7d4af3c9c 100644 --- a/src/GToolkit-Coder-Examples/GtCoderNavigationPackagesListElementExamples.class.st +++ b/src/GToolkit-Coder-Examples/GtCoderNavigationPackagesListElementExamples.class.st @@ -7,6 +7,6 @@ Class { { #category : #'instance creation' } GtCoderNavigationPackagesListElementExamples >> fromAllPackages [ - - ^ GtCoderNavigationPackagesTreeElement fromPackages: RPackageOrganizer default packages + + ^ GtCoderPackagesTreeElement fromPackages: self packageOrganizer packages ] diff --git a/src/GToolkit-Coder-Examples/GtCoderShortcutsExplanation.class.st b/src/GToolkit-Coder-Examples/GtCoderShortcutsExplanation.class.st deleted file mode 100644 index e02a1dfc6..000000000 --- a/src/GToolkit-Coder-Examples/GtCoderShortcutsExplanation.class.st +++ /dev/null @@ -1,26 +0,0 @@ -" -1. What are the editor shortcuts? - -What shortcuts are availabe in a method coder? - -{{gtExample:GtMethodCoderExamples>>#emptyMethodCoder|previewShow=##gtLiveFor:|previewExpanded=|noCode=|previewHeight=100}} - -These ones: - -{{gtExample:GtMethodCoderExamples>>#emptyMethodCoder|previewShow=#gtViewKeybindingsFor:|previewExpanded=|noCode=}} - -And what about a Playground snippet? - -{{gtExample:GtPlaygroundPharoSnippetExamples>>#pharoSnippet|previewShow=#gtLiveFor:|previewHeight=100|noCode=}} - -These ones. - -{{gtExample:GtPlaygroundPharoSnippetExamples>>#pharoSnippet|previewShow=#gtKeybindingsFor:|previewHeight=400|noCode=}} - - -" -Class { - #name : #GtCoderShortcutsExplanation, - #superclass : #Object, - #category : #'GToolkit-Coder-Examples-! Explanation' -} diff --git a/src/GToolkit-Coder-Examples/GtCoderUncategorizedDummyClass.class.st b/src/GToolkit-Coder-Examples/GtCoderUncategorizedDummyClass.class.st index 936ed931b..08253121e 100644 --- a/src/GToolkit-Coder-Examples/GtCoderUncategorizedDummyClass.class.st +++ b/src/GToolkit-Coder-Examples/GtCoderUncategorizedDummyClass.class.st @@ -12,12 +12,19 @@ Class { { #category : #examples } GtCoderUncategorizedDummyClass >> checkUncategorized [ + | aPackage aPackageTag | - aPackage := self class package. - aPackageTag := aPackage classTags detect: [ :eachTag | eachTag name = aPackage name ]. - - self assert: aPackage name equals: aPackageTag name. - + aPackageTag := aPackage tags + detect: [ :eachTag | eachTag isRoot ]. + + self assert: aPackageTag isRoot. + self assert: aPackage name equals: 'GToolkit-Coder-Examples'. + self + forPharo12AndNewer: [ + self assert: aPackageTag name equals: 'Uncategorized'.] + forPharo11: [ + self assert: aPackage name equals: aPackageTag name ]. + ^ aPackageTag ] diff --git a/src/GToolkit-Coder-Examples/GtDiffBuilderExamples.class.st b/src/GToolkit-Coder-Examples/GtDiffBuilderExamples.class.st new file mode 100644 index 000000000..9725cacc0 --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtDiffBuilderExamples.class.st @@ -0,0 +1,424 @@ +Class { + #name : #GtDiffBuilderExamples, + #superclass : #Object, + #category : #'GToolkit-Coder-Examples-Diff' +} + +{ #category : #examples } +GtDiffBuilderExamples >> byteArrayDiff [ + + + | originalByteArray modifiedByteArray | + originalByteArray := (1 to: 64) + collect: [ :_ | 256 atRandom - 1 ] + as: ByteArray. + modifiedByteArray := originalByteArray copy. + 8 + timesRepeat: [ modifiedByteArray at: modifiedByteArray size atRandom put: 256 atRandom - 1 ]. + ^ GtDiffBuilder + computeDifferencesFrom: originalByteArray + to: modifiedByteArray + using: GtNullDiffSplitter new +] + +{ #category : #examples } +GtDiffBuilderExamples >> changeInMiddleAndDeletionAtEnd [ + + + | differences from to change | + from := String cr join: ((1 to: 10) collect: #asWords). + to := String cr + join: (((1 to: 10) collect: #asWords) + at: 2 put: 'two plus'; + allButLast). + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter ignoringEOLChars. + differences := change changes. + self assert: differences size equals: 2. + self assert: differences first isReplacement. + self assert: differences first deletedObjects equals: 'two'. + self assert: differences first insertedObjects equals: 'two plus'. + self assert: differences second isDeletion. + self assert: differences second deletedObjects equals: 'ten'. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> deletedLineChanges [ + + + | differences from to change | + to := self exampleMethod. + from := self exampleMethodWithAdditions. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 4. + self assert: (differences allSatisfy: #isReplacement). + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> deletedPharoChanges [ + + + | differences from to change | + to := self exampleMethod. + from := self exampleMethodWithAdditions. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtSmaCCDiffSplitter forPharo. + differences := change changes. + self assert: differences size equals: 6. + self assert: (differences allSatisfy: #isDeletion). + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> deletedWordChanges [ + + + | differences from to change | + to := self exampleMethod. + from := self exampleMethodWithAdditions. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtCharacterGroupDiffSplitter words. + differences := change changes. + self assert: differences size equals: 6. + self assert: (differences allSatisfy: #isDeletion). + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> deletionsAtBeginningExample [ + + + | differences from to change | + from := '1' , String cr , '2' , String lf , '3'. + to := from allButFirst: 2. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 1. + self assert: differences first isDeletion. + self assert: differences first startIndex equals: 1. + self assert: differences first stopIndex equals: 2. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> deletionsAtEndExample [ + + + | differences from to change | + from := '1' , String cr , '2' , String lf , '3'. + to := from allButLast. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 1. + self assert: differences first isDeletion. + self assert: differences first startIndex equals: from size. + self assert: differences first stopIndex equals: from size. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> deletionsInMiddle [ + + + | differences from to change | + from := String cr join: ((1 to: 9) collect: #printString). + to := String cr join: ((1 to: 4) , (7 to: 9) collect: #printString). + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 1. + self assert: differences first isDeletion. + self assert: differences first startIndex equals: 9. + self assert: differences first stopIndex equals: 12. + self assert: differences first newIndex equals: 9. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> exampleMethod [ + ^ '1 a.\2 a.\3 a.\4 a.\5 a.\6 a.\7 a.\8 a.\9 a.\10 a.' withCRs +] + +{ #category : #examples } +GtDiffBuilderExamples >> exampleMethodWithAdditions [ + ^ '1 a b.\2 a.\3 a.\4 a b.\5 a.\6 a b.\6.1 a.\7 b a.\8 a.\9 a.\10 a b.' + withCRs +] + +{ #category : #examples } +GtDiffBuilderExamples >> insertedLineChanges [ + + + | differences from to change | + from := self exampleMethod. + to := self exampleMethodWithAdditions. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 4. + self assert: (differences allSatisfy: #isReplacement). + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> insertedPharoChanges [ + + + | differences from to change | + from := self exampleMethod. + to := self exampleMethodWithAdditions. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtSmaCCDiffSplitter forPharo. + differences := change changes. + self assert: differences size equals: 6. + self assert: (differences allSatisfy: #isInsertion). + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> insertedWordChanges [ + + + | differences from to change | + from := self exampleMethod. + to := self exampleMethodWithAdditions. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtCharacterGroupDiffSplitter words. + differences := change changes. + self assert: differences size equals: 6. + self assert: (differences allSatisfy: #isInsertion). + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> insertionsAtBeginningExample [ + + + | differences from to change | + to := '1' , String cr , '2' , String lf , '3'. + from := to allButFirst: 2. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 1. + self assert: differences first isInsertion. + self assert: differences first startIndex equals: 1. + self assert: differences first stopIndex equals: 2. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> insertionsAtEndExample [ + + + | differences from to change | + to := '1' , String cr , '2' , String lf , '3'. + from := to allButLast. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 1. + self assert: differences first isInsertion. + self assert: differences first startIndex equals: to size. + self assert: differences first stopIndex equals: to size. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> insertionsInMiddle [ + + + | differences from to change | + to := String cr join: ((1 to: 9) collect: #printString). + from := String cr join: ((1 to: 4) , (7 to: 9) collect: #printString). + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 1. + self assert: differences first isInsertion. + self assert: differences first startIndex equals: 9. + self assert: differences first stopIndex equals: 12. + self assert: differences first originalIndex equals: 9. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> multipleChanges [ + + + | differences from to change | + from := String cr join: ((0 to: 9 by: 3) collect: #printString). + to := String cr join: ((0 to: 9 by: 2) collect: #printString). + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 2. + self assert: differences first isReplacement. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> multipleObjectChanges [ + + + | differences from to deletedObjects insertedObjects index change | + from := 0 to: 1000 by: 2. + to := 0 to: 1000 by: 7. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtNullDiffSplitter new. + differences := change changes. + deletedObjects := OrderedCollection new. + insertedObjects := OrderedCollection new. + differences + do: [ :each | + each deletedObjects ifNotNil: [ :objs | deletedObjects addAll: objs ]. + each insertedObjects ifNotNil: [ :objs | insertedObjects addAll: objs ] ]. + index := 1. + from + do: [ :each | + (to includes: each) + ifTrue: [ self assert: (deletedObjects includes: each) not ] + ifFalse: [ self assert: (deletedObjects at: index) equals: each. + index := index + 1 ] ]. + index := 1. + to + do: [ :each | + (from includes: each) + ifTrue: [ self assert: (insertedObjects includes: each) not ] + ifFalse: [ self assert: (insertedObjects at: index) equals: each. + index := index + 1 ] ]. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> noDifferencesExample [ + + + | differences from to change | + from := '1' , String cr , '2' , String lf , '3'. + to := from copy. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 0. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> replacementInMiddle [ + + + | differences from to change | + from := String cr join: ((1 to: 4) , (6 to: 9) collect: #printString). + to := String cr join: ((1 to: 5) , (7 to: 9) collect: #printString). + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: GtLineDiffSplitter new. + differences := change changes. + self assert: differences size equals: 1. + self assert: differences first isReplacement. + self assert: differences first deletionChange startIndex equals: 9. + self assert: differences first deletionChange stopIndex equals: 10. + self assert: differences first insertionChange startIndex equals: 9. + self assert: differences first insertionChange stopIndex equals: 10. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> reversedTokenChanges [ + + + | differences from to change scannerClass | + scannerClass := self class environment at: #GtPharoScanner ifAbsent: [ ^ nil ]. + to := 'method: arg ^self inject: arg into: [:sum :each | sum + each]'. + from := 'method: arg1 + ^ self + inject: arg1 + into: [:sum :each | (sum + each)]'. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: (GtSmaCCDiffSplitter new scannerClass: scannerClass). + differences := change changes. + self assert: differences size equals: 4. + self assert: differences first isReplacement. + self assert: differences first deletionChange objects equals: 'arg1'. + self assert: differences first insertionChange objects equals: 'arg'. + self assert: differences second isReplacement. + self assert: differences second deletionChange objects equals: 'arg1'. + self assert: differences second insertionChange objects equals: 'arg'. + self assert: differences third isDeletion. + self assert: differences third objects equals: '('. + self assert: differences fourth isDeletion. + self assert: differences fourth objects equals: ')'. + ^ change +] + +{ #category : #examples } +GtDiffBuilderExamples >> tokenChanges [ + + + | differences from to change scannerClass | + scannerClass := self class environment at: #GtPharoScanner ifAbsent: [ ^ nil ]. + from := 'method: arg ^self inject: arg into: [:sum :each | sum + each]'. + to := 'method: arg1 + ^ self + inject: arg1 + into: [:sum :each | (sum + each)]'. + change := GtDiffBuilder + computeDifferencesFrom: from + to: to + using: (GtSmaCCDiffSplitter new scannerClass: scannerClass). + differences := change changes. + self assert: differences size equals: 4. + self assert: differences first isReplacement. + self assert: differences first deletionChange objects equals: 'arg'. + self assert: differences first insertionChange objects equals: 'arg1'. + self assert: differences second isReplacement. + self assert: differences second deletionChange objects equals: 'arg'. + self assert: differences second insertionChange objects equals: 'arg1'. + self assert: differences third isInsertion. + self assert: differences third objects equals: '('. + self assert: differences fourth isInsertion. + self assert: differences fourth objects equals: ')'. + ^ change +] diff --git a/src/GToolkit-Coder-Examples/GtDiffElementExamples.class.st b/src/GToolkit-Coder-Examples/GtDiffElementExamples.class.st new file mode 100644 index 000000000..660eaa624 --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtDiffElementExamples.class.st @@ -0,0 +1,158 @@ +Class { + #name : #GtDiffElementExamples, + #superclass : #Object, + #category : #'GToolkit-Coder-Examples-Diff' +} + +{ #category : #examples } +GtDiffElementExamples >> changeElementExample [ + + + ^ (GtDiffElement onChange: self exampleChange) showButtons +] + +{ #category : #examples } +GtDiffElementExamples >> changeElementScrollsToFirstDiff [ + + + | scripter | + scripter := BlScripter new. + scripter extent: 800 @ 800. + scripter + element: ((GtDiffElement onChange: self exampleChange) + matchParent; + showButtons; + yourself). + scripter + checkStep: [ :s | + s + label: 'Check has scrolled'; + // GtDiffButtonsId; + // #label; + do: [ :each | self assert: (each text asString beginsWith: '1/') ] ]. + scripter + checkStep: [ :s | + s + label: 'Can scroll text up'; + // GtDiffOldTextId; + do: [ :each | self assert: each canScrollUp ] ]. + ^ scripter +] + +{ #category : #examples } +GtDiffElementExamples >> changeElementWithoutScroll [ + + + | scripter | + scripter := BlScripter new. + scripter extent: 800 @ 800. + scripter + element: ((GtDiffElement onChangeWithoutScrolling: self exampleChange) + matchParent; + showButtons; + yourself). + scripter + checkStep: [ :s | + s + label: 'Check has scrolled'; + // GtDiffButtonsId; + // #label; + do: [ :each | self assert: (each text asString beginsWith: '0/') ] ]. + scripter + checkStep: [ :s | + s + label: 'Can scroll text up'; + // GtDiffOldTextId; + do: [ :each | self assert: each canScrollUp not ] ]. + ^ scripter +] + +{ #category : #examples } +GtDiffElementExamples >> changeExampleFlat [ + + + ^ self changeElementExample aptitude: GtDiffFlatAptitude +] + +{ #category : #examples } +GtDiffElementExamples >> changeExampleShadow [ + + + ^ self changeElementExample aptitude: GtDiffShadowAptitude +] + +{ #category : #examples } +GtDiffElementExamples >> exampleChange [ + + + ^ GtDiffBuilder + computeDifferencesFrom: self originalString + to: self newString + using: GtCharacterGroupDiffSplitter words ignoreWhitespace +] + +{ #category : #examples } +GtDiffElementExamples >> exampleTextDiff [ + + + ^ TextDiffBuilder from: self originalString to: self newString +] + +{ #category : #examples } +GtDiffElementExamples >> newString [ + + + ^ String + streamContents: [ :str | + 1 + to: 1000 + by: 6 + do: [ :i | + str + print: i; + cr; + print: i + 1; + cr; + print: i + 2; + cr ] ] +] + +{ #category : #examples } +GtDiffElementExamples >> originalString [ + + + ^ String + streamContents: [ :str | + 1 + to: 1000 + by: 5 + do: [ :i | + str + print: i; + cr; + print: i + 1; + cr; + print: i + 2; + cr ] ] +] + +{ #category : #examples } +GtDiffElementExamples >> textDiffElementExample [ + + + ^ (GtDiffElement onDiff: self exampleTextDiff) showButtons +] + +{ #category : #examples } +GtDiffElementExamples >> textDiffExampleFlat [ + + + ^ self textDiffElementExample aptitude: GtDiffFlatAptitude +] + +{ #category : #examples } +GtDiffElementExamples >> textDiffExampleShadow [ + + + ^ self textDiffElementExample aptitude: GtDiffShadowAptitude +] diff --git a/src/GToolkit-Coder-Examples/GtDiffSplitterExamples.class.st b/src/GToolkit-Coder-Examples/GtDiffSplitterExamples.class.st new file mode 100644 index 000000000..abdd5bcdc --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtDiffSplitterExamples.class.st @@ -0,0 +1,135 @@ +Class { + #name : #GtDiffSplitterExamples, + #superclass : #Object, + #category : #'GToolkit-Coder-Examples-Diff' +} + +{ #category : #'null splitter' } +GtDiffSplitterExamples >> characterSplitterExample [ + + + ^ self compareNullSplits: '1234' +] + +{ #category : #'null splitter' } +GtDiffSplitterExamples >> compareNullSplits: aCollection [ + | splits values | + values := aCollection , aCollection reversed. + splits := GtNullDiffSplitter new split: values. + self assert: values size = splits size. + 1 + to: splits size + do: [ :i | + self assert: (splits at: i) equals: (splits at: splits size - i + 1). + self + assert: (splits at: i) hash + equals: (splits at: splits size - i + 1) hash. + self assert: (splits at: i) object = (values at: i) ]. + ^ splits +] + +{ #category : #'token splitter' } +GtDiffSplitterExamples >> javascriptDiffSplitter [ + + + | splits scannerClass splitter | + scannerClass := self class environment at: #JSScanner ifAbsent: [ ^ nil ]. + splitter := GtSmaCCDiffSplitter new scannerClass: scannerClass. + splits := splitter split: '/*1*/for(i=0; i<10; i++/*2*/) console.log(i);//3'. + #('/*1*/' 'for' '(' 'i' '=' '0' ';' 'i' '<' '10' ';' 'i' '++' '/*2*/' ')' 'console' '.' 'log' '(' 'i' ')' ';' '//3') + doWithIndex: [ :each :i | self assert: (splits at: i) object equals: each ]. + ^ splits +] + +{ #category : #lines } +GtDiffSplitterExamples >> lineIgnoringEOLSplitterExample [ + + + | splitter splits string | + string := '1' , String crlf , '2' , String cr , '3' , String lf , '4'. + splitter := GtLineDiffSplitter ignoringEOLChars. + splits := splitter split: string. + self assert: splits size = 4. + self assert: (splits at: 1) object equals: '1'. + self assert: (splits at: 4) object equals: '4'. + ^ splits +] + +{ #category : #lines } +GtDiffSplitterExamples >> lineWithEOLSplitterExample [ + + + | splitter splits string | + string := '1' , String crlf , '2' , String cr , '3' , String lf , '4'. + splitter := GtLineDiffSplitter new + includeEOLChars: true; + yourself. + splits := splitter split: string. + self assert: splits size = 4. + self assert: (splits at: 1) object equals: '1' , String crlf. + self assert: (splits at: 2) object equals: '2' , String cr. + self assert: (splits at: 3) object equals: '3' , String lf. + self assert: (splits at: 4) object equals: '4'. + ^ splits +] + +{ #category : #lines } +GtDiffSplitterExamples >> lineWithoutEOLSplitterExample [ + + + | splitter splits string | + string := '1' , String crlf , '1' , String cr , '1' , String lf , '1'. + splitter := GtLineDiffSplitter new + includeEOLChars: false; + yourself. + splits := splitter split: string. + self assert: splits size = 4. + self assert: splits splits asSet size equals: 1. + self assert: (splits at: 1) object equals: '1'. + ^ splits +] + +{ #category : #'null splitter' } +GtDiffSplitterExamples >> objectSplitterExample [ + + + ^ self + compareNullSplits: {1. + 'foo'. + Object new. + #(1 2 3). + 1.2} +] + +{ #category : #'character groups' } +GtDiffSplitterExamples >> wordIgnoreWhitespaceSplitter [ + + + | splits words | + words := #('This ' 'is ' 'a ' 'test' '. + ' '123' ',' '45'). + splits := GtCharacterGroupDiffSplitter words ignoreWhitespace + split: ('' join: words). + self assert: splits size equals: words size. + 1 + to: words size + do: [ :i | self assert: (splits at: i) object equals: (words at: i) trim ]. + ^ splits +] + +{ #category : #'character groups' } +GtDiffSplitterExamples >> wordSplitter [ + + + | splits words | + words := #('This' 'is' 'a' 'test'). + splits := GtCharacterGroupDiffSplitter words split: (' ' join: words). + self assert: splits size equals: words size * 2 - 1. + 1 + to: words size + do: [ :i | self assert: (splits at: i * 2 - 1) object equals: (words at: i) trim ]. + 1 + to: words size - 1 + do: [ :i | self assert: (splits at: i * 2) object equals: ' ' ]. + ^ splits +] diff --git a/src/GToolkit-Coder-Examples/GtFilterDescriptorExamples.class.st b/src/GToolkit-Coder-Examples/GtFilterDescriptorExamples.class.st new file mode 100644 index 000000000..b246dfa9c --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtFilterDescriptorExamples.class.st @@ -0,0 +1,79 @@ +Class { + #name : #GtFilterDescriptorExamples, + #superclass : #Object, + #category : #'GToolkit-Coder-Examples-Filters' +} + +{ #category : #examples } +GtFilterDescriptorExamples >> filterDescriptorOffersCompletionValues [ + + + | aFilterDescriptor | + aFilterDescriptor := self newFilterDescriptor + offerCompletionValues: true; + emptyDefaultValue: 'Lorem'; + completion: [ GtStreamedBlockCompletionStrategy new + streamingBlock: [ :aText | + (String loremIpsum piecesCutWhere: [ :each :next | next = Character space ]) + asAsyncStream ] ]. + + self assert: aFilterDescriptor name equals: 'No name'. + self assert: aFilterDescriptor order equals: 1. + self + assert: (aFilterDescriptor completion isKindOf: GtStreamedBlockCompletionStrategy). + self assert: aFilterDescriptor valueIsRequired. + self assert: aFilterDescriptor isDefault. + self assert: aFilterDescriptor emptyDefaultValue equals: 'Lorem'. + self assert: aFilterDescriptor showAsDefaultWhenEmpty. + self assert: aFilterDescriptor shouldOfferCompletionValues. + + self + assert: (aFilterDescriptor newFilterWithValue: '') + equals: GtSearchNullFilter new. + + ^ aFilterDescriptor +] + +{ #category : #examples } +GtFilterDescriptorExamples >> filterDescriptorValueNotRequired [ + + + | aFilterDescriptor | + aFilterDescriptor := self newFilterDescriptor valueNotRequired. + + self assert: aFilterDescriptor name equals: 'No name'. + self assert: aFilterDescriptor order equals: 1. + self assert: aFilterDescriptor completion equals: nil. + self assert: aFilterDescriptor valueIsRequired not. + self assert: aFilterDescriptor isDefault. + self assert: aFilterDescriptor emptyDefaultValue equals: nil. + self assert: aFilterDescriptor showAsDefaultWhenEmpty. + + self + assert: (aFilterDescriptor newFilterWithValue: '') + equals: GtSearchNullFilter new. + + ^ aFilterDescriptor +] + +{ #category : #examples } +GtFilterDescriptorExamples >> newFilterDescriptor [ + + + | aFilterDescriptor | + aFilterDescriptor := GtFilterDescriptor new. + + self assert: aFilterDescriptor name equals: 'No name'. + self assert: aFilterDescriptor order equals: 1. + self assert: aFilterDescriptor completion equals: nil. + self assert: aFilterDescriptor valueIsRequired. + self assert: aFilterDescriptor isDefault. + self assert: aFilterDescriptor emptyDefaultValue equals: nil. + self assert: aFilterDescriptor showAsDefaultWhenEmpty not. + + self + assert: (aFilterDescriptor newFilterWithValue: '') + equals: GtSearchNullFilter new. + + ^ aFilterDescriptor +] diff --git a/src/GToolkit-Coder-Examples/GtFilterElementByScripterExamples.class.st b/src/GToolkit-Coder-Examples/GtFilterElementByScripterExamples.class.st new file mode 100644 index 000000000..2907b4406 --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtFilterElementByScripterExamples.class.st @@ -0,0 +1,65 @@ +Class { + #name : #GtFilterElementByScripterExamples, + #superclass : #Object, + #traits : 'TBlDevScripterExamples', + #classTraits : 'TBlDevScripterExamples classTrait', + #category : #'GToolkit-Coder-Examples-Filters - New' +} + +{ #category : #examples } +GtFilterElementByScripterExamples >> booleanElement [ + + + | aScripter | + aScripter := self + scripterWithModel: [ GtFilterModelExamples new booleanModel ] + element: [ :aModel | aModel asFilterLabeledElement ]. + + ^ aScripter +] + +{ #category : #examples } +GtFilterElementByScripterExamples >> itemsModel [ + + + | aScripter | + aScripter := self + scripterWithModel: [ GtFilterModelExamples new itemsModel ] + element: [ :aModel | GtFilterItemsElement new filtersViewModel: aModel asFiltersViewModel ]. + + + + ^ aScripter +] + +{ #category : #examples } +GtFilterElementByScripterExamples >> shortListElement [ + + + | aScripter | + aScripter := self + scripterWithModel: [ GtFilterModelExamples new shortListModel ] + element: [ :aModel | aModel asFilterLabeledElement ]. + + ^ aScripter +] + +{ #category : #examples } +GtFilterElementByScripterExamples >> textElement [ + + + | aScripter | + aScripter := self + scripterWithModel: [ GtFilterModelExamples new textModel ] + element: [ :aModel | aModel asFilterLabeledElement ]. + + aScripter editor + click; + selectAll; + typeText: 'Class'; + assertTextEqualsTo: 'Class'; + // GtFilterSettingsId; + play. + + ^ aScripter +] diff --git a/src/GToolkit-Coder-Examples/GtFilterModelExamples.class.st b/src/GToolkit-Coder-Examples/GtFilterModelExamples.class.st new file mode 100644 index 000000000..d1bab504d --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtFilterModelExamples.class.st @@ -0,0 +1,612 @@ +Class { + #name : #GtFilterModelExamples, + #superclass : #Object, + #category : #'GToolkit-Coder-Examples-Filters - New' +} + +{ #category : #examples } +GtFilterModelExamples >> booleanModel [ + + + | aModel | + aModel := GtFilterBooleanModel new. + aModel switchedOn: true. + aModel name: 'Trait methods'. + self assert: aModel isSwitchedOn. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> invariableModel [ + + + | aModel | + aModel := GtFilterInvariableModel new. + aModel name: 'Stable tests'. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModel [ + + + | aModel | + aModel := GtFilterItemsModel new. + aModel + items: {self textModel. + self shortListModel. + self booleanModel. + self invariableModel. + self toggleModel}. + self assert: aModel items size equals: 5. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithAddingShortListModelWithSelectedValue [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := GtFilterItemsModel new. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := self shortListModel. + self assert: (aFilterModel selectedItem ifSome: [ true ] ifNone: [ false ]). + + aFiltersModel addFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 1. + self assert: someAnnouncements size equals: 1. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithAddingShortListModelWithoutSelectedValue [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := GtFilterItemsModel new. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := self shortListModel. + aFilterModel selectedItem: GtFilterModelNoItem default. + self assert: (aFilterModel selectedItem ifSome: [ false ] ifNone: [ true ]). + + aFiltersModel addFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 1. + self assert: someAnnouncements size equals: 0. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithAddingTextModelWithEmptyText [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := GtFilterItemsModel new. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := self textModel. + aFilterModel text: ''. + self assert: aFilterModel text isEmpty. + + aFiltersModel addFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 1. + self assert: someAnnouncements size equals: 0. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithAddingTextModelWithNonEmptyText [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := GtFilterItemsModel new. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := self textModel. + self assert: aFilterModel text isEmpty not. + + aFiltersModel addFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 1. + self assert: someAnnouncements size equals: 1. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithAddingToggleModelWithSelectedValue [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := GtFilterItemsModel new. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := self toggleModel. + self assert: (aFilterModel selectedItem ifSome: [ true ] ifNone: [ false ]). + + aFiltersModel addFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 1. + self assert: someAnnouncements size equals: 1. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithAddingToggleModelWithoutSelectedValue [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := GtFilterItemsModel new. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := self toggleModel. + aFilterModel selectedItem: GtFilterModelNoItem default. + self assert: (aFilterModel selectedItem ifSome: [ false ] ifNone: [ true ]). + + aFiltersModel addFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 1. + self assert: someAnnouncements size equals: 0. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithRemovingShortListModelWithSelectedValue [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := self itemsModelWithAddingShortListModelWithSelectedValue. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := aFiltersModel items first. + self assert: (aFilterModel selectedItem ifSome: [ true ] ifNone: [ false ]). + + aFiltersModel removeFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 0. + self assert: someAnnouncements size equals: 1. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithRemovingShortListModelWithoutSelectedValue [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := self itemsModelWithAddingShortListModelWithoutSelectedValue. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := aFiltersModel items first. + self assert: (aFilterModel selectedItem ifSome: [ false ] ifNone: [ true ]). + + aFiltersModel removeFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 0. + self assert: someAnnouncements size equals: 0. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithRemovingTextModelWithEmptyText [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := self itemsModelWithAddingTextModelWithEmptyText. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := aFiltersModel items first. + self assert: aFilterModel text isEmpty. + + aFiltersModel removeFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 0. + self assert: someAnnouncements size equals: 0. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithRemovingTextModelWithNonEmptyText [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := self itemsModelWithAddingTextModelWithNonEmptyText. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := aFiltersModel items first. + self assert: aFilterModel text isEmpty not. + + aFiltersModel removeFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 0. + self assert: someAnnouncements size equals: 1. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithRemovingToggleModelWithSelectedValue [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := self itemsModelWithAddingToggleModelWithSelectedValue. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := aFiltersModel items first. + self assert: (aFilterModel selectedItem ifSome: [ true ] ifNone: [ false ]). + + aFiltersModel removeFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 0. + self assert: someAnnouncements size equals: 1. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> itemsModelWithRemovingToggleModelWithoutSelectedValue [ + + + | aFiltersModel aFilterModel someAnnouncements | + someAnnouncements := OrderedCollection new. + aFiltersModel := self itemsModelWithAddingToggleModelWithoutSelectedValue. + aFiltersModel weak + when: GtFiltersModelUpdated + send: #add: + to: someAnnouncements. + + aFilterModel := aFiltersModel items first. + self assert: (aFilterModel selectedItem ifSome: [ false ] ifNone: [ true ]). + + aFiltersModel removeFilterModel: aFilterModel. + + self assert: aFiltersModel items size equals: 0. + self assert: someAnnouncements size equals: 0. + + ^ aFiltersModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModel [ + + + | aModel | + aModel := GtFilterShortListModel new. + aModel + items: {'instance'. + 'class'}. + aModel selectedItem: 'class'. + aModel name: 'Side'. + self + assert: aModel itemsFuture wait + equals: ({'instance'. + 'class'} collect: #asFilterModelItem). + self assert: aModel selectedItem equals: 'class' asFilterModelItem. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModelParameter [ + + + | aModel | + aModel := GtFilterShortListModelParameter new + updater: [ :aFilter :anItem | anItem ifSome: [ :aValue | anItem onCreateFilter: aFilter ] ifNone: [ ] ]; + items: [ {GtFilterModelPluggableItem new + label: 'readers and writers'; + filterUpdater: [ :aFilter | + aFilter includeReaders: true. + aFilter includeWriters: true ]. + GtFilterModelPluggableItem new + label: 'readers only'; + filterUpdater: [ :aFilter | + aFilter includeReaders: true. + aFilter includeWriters: false ]. + GtFilterModelPluggableItem new + label: 'writers only'; + filterUpdater: [ :aFilter | + aFilter includeReaders: false. + aFilter includeWriters: true ]} ]; + selectFirstItem; + displayAllItems. + self assert: aModel itemsFuture wait size equals: 3. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModelPluggableItems [ + + + | aModel allItems aCollection anIconBuilder aLabelBuilder | + aModel := GtFilterShortListModel new. + + aCollection := {Array. + OrderedCollection. + TGtAnnouncer. + Object. + ProtoObject}. + anIconBuilder := [ :aClass | + aClass gtSystemIconName + ifNotNil: [ :anIconName | aClass gtSafeIconNamed: anIconName ] ]. + aLabelBuilder := [ :aClass | aClass name ]. + allItems := GtFilterModelPluggableItem + forCollection: aCollection + icon: anIconBuilder + label: aLabelBuilder. + + aModel items: allItems. + aModel selectFirstItem. + aModel name: 'Classes'. + + self + assert: aModel selectedItem + equals: (GtFilterModelPluggableItem new + object: Array; + icon: anIconBuilder; + label: aLabelBuilder). + self assert: aModel selectedItem icon isNotNil. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModelPluggableItemsWithIconAndLabelBuilders [ + + + | aModel aCollection anIconBuilder aLabelBuilder | + aModel := GtFilterShortListModel new. + + aCollection := {Array. + OrderedCollection. + TGtAnnouncer. + Object. + ProtoObject}. + anIconBuilder := [ :aClass | + aClass gtSystemIconName + ifNotNil: [ :anIconName | aClass gtSafeIconNamed: anIconName ] ]. + aLabelBuilder := [ :aClass | aClass name ]. + + aModel + items: aCollection + icon: anIconBuilder + label: aLabelBuilder. + aModel selectFirstItem. + aModel name: 'Classes'. + + self + assert: aModel selectedItem + equals: (GtFilterModelPluggableItem new + object: Array; + icon: anIconBuilder; + label: aLabelBuilder). + self assert: aModel selectedItem icon isNotNil. + self assert: aModel selectedItem label equals: 'Array'. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModelPluggableItemsWithoutIconBuilder [ + + + | aModel aCollection aLabelBuilder | + aModel := GtFilterShortListModel new. + + aCollection := {Array. + OrderedCollection. + TGtAnnouncer. + Object. + ProtoObject}. + aLabelBuilder := [ :aClass | '+ ' , aClass name ]. + + aModel items: aCollection label: aLabelBuilder. + aModel selectFirstItem. + aModel name: 'Classes'. + + self + assert: aModel selectedItem + equals: (GtFilterModelPluggableItem new + object: Array; + label: aLabelBuilder). + self assert: aModel selectedItem icon isNil. + self assert: aModel selectedItem label equals: '+ Array'. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModelPluggableItemsWithoutLabelBuilder [ + + + | aModel aCollection anIconBuilder | + aModel := GtFilterShortListModel new. + + aCollection := {Array. + OrderedCollection. + TGtAnnouncer. + Object. + ProtoObject}. + anIconBuilder := [ :aClass | + aClass gtSystemIconName + ifNotNil: [ :anIconName | aClass gtSafeIconNamed: anIconName ] ]. + + aModel items: aCollection icon: anIconBuilder. + aModel selectFirstItem. + aModel name: 'Classes'. + + self + assert: aModel selectedItem + equals: (GtFilterModelPluggableItem new + object: Array; + icon: anIconBuilder). + self assert: aModel selectedItem icon isNotNil. + self assert: aModel selectedItem label equals: 'Array'. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModelWithIcons [ + + + | aModel | + aModel := GtFilterShortListModel new. + aModel + items: {GtFilterModelClassItem new itemClass: Array. + GtFilterModelClassItem new itemClass: OrderedCollection. + GtFilterModelClassItem new itemClass: TGtAnnouncer. + GtFilterModelClassItem new itemClass: Object. + GtFilterModelClassItem new itemClass: ProtoObject}. + aModel selectFirstItem. + aModel name: 'Classes'. + self + assert: aModel itemsFuture wait + equals: {GtFilterModelClassItem new itemClass: Array. + GtFilterModelClassItem new itemClass: OrderedCollection. + GtFilterModelClassItem new itemClass: TGtAnnouncer. + GtFilterModelClassItem new itemClass: Object. + GtFilterModelClassItem new itemClass: ProtoObject}. + self + assert: aModel selectedItem + equals: (GtFilterModelClassItem new itemClass: Array). + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModelWithParameter [ + + + | aModel | + aModel := self shortListModel. + aModel parameter: self shortListModelParameter. + self assert: aModel parameters size equals: 1. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> shortListModelWithParameter_changeParameterValue [ + + + | aModel aCollection aParameter anAnnouncement | + aModel := self shortListModelWithParameter. + + aCollection := OrderedCollection new. + aModel weak + when: GtFilterModelAnnouncement + send: #add: + to: aCollection. + + aParameter := aModel parameters first. + aParameter selectedItem: aParameter itemsFuture wait second. + self assert: aCollection size equals: 1. + anAnnouncement := aCollection first. + self assert: anAnnouncement class equals: GtFilterModelParameterUpdated. + self assert: anAnnouncement changesFilteredResult. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> textModel [ + + + | aModel | + aModel := GtFilterTextModel new. + aModel text: 'Object'. + aModel name: 'Methods up to'. + self assert: (aModel text equals: 'Object' asRopedText). + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> toggleModel [ + + + | aModel | + aModel := GtFilterToggleModel new. + aModel name: 'Side'. + aModel + items: {GtFilterModelInstanceSideItem new. + GtFilterModelClassSideItem new}. + aModel selectFirstItem. + + ^ aModel +] + +{ #category : #examples } +GtFilterModelExamples >> toggleModelWithIcons [ + + + | aModel | + aModel := GtFilterToggleModel new. + aModel name: 'Origin'. + aModel + items: {GtFilterModelTraitOriginItem new. + GtFilterModelClassOriginItem new}. + aModel selectFirstItem. + + ^ aModel +] diff --git a/src/GToolkit-Coder-Examples/GtFilterStubSampleA.class.st b/src/GToolkit-Coder-Examples/GtFilterStubSampleA.class.st index aa174778d..ec75f1a6e 100644 --- a/src/GToolkit-Coder-Examples/GtFilterStubSampleA.class.st +++ b/src/GToolkit-Coder-Examples/GtFilterStubSampleA.class.st @@ -13,3 +13,8 @@ GtFilterStubSampleA >> anotherMethodReferencingSampleBAndSendingNew [ GtFilterStubSampleA >> methodReferencingSampleB [ ^ GtFilterStubSampleB ] + +{ #category : #'as yet unclassified' } +GtFilterStubSampleA >> uncategoriedMethodForFilter [ + "This method should remain uncategoried" +] diff --git a/src/GToolkit-Coder-Examples/GtFilterTagElementExamples.class.st b/src/GToolkit-Coder-Examples/GtFilterTagElementExamples.class.st new file mode 100644 index 000000000..87d64eb60 --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtFilterTagElementExamples.class.st @@ -0,0 +1,416 @@ +Class { + #name : #GtFilterTagElementExamples, + #superclass : #Object, + #category : #'GToolkit-Coder-Examples-Filters' +} + +{ #category : #examples } +GtFilterTagElementExamples >> availableFilterDescriptors [ + ^ { + GtFilterDescriptor + creator: GtSearchNullFilter new + named: 'Filter with value' + order: 1. + + GtFilterDescriptor new + creator: GtSearchNullFilter new; + named: 'Filter without value'; + order: 2; + valueNotRequired. + + GtFilterDescriptor + creator: GtSearchNullFilter new + named: 'Filter C' + order: 3. + + GtFilterDescriptor + creator: GtSearchNullFilter new + named: 'Filter D' + order: 4. + } +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterOffersCompletionValues [ + + + ^ GtFilterDescriptorExamples new filterDescriptorOffersCompletionValues + named: 'Words' +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagCompletionOffersValues [ + + + | tagElement filterDescriptor | + filterDescriptor := self filterOffersCompletionValues. + + tagElement := GtFilterTagElement new + descriptor: filterDescriptor; + availableFilterDescriptors: self availableFilterDescriptors. + + self + assert: (tagElement query // GtFilterTagLabelId) anyOne text asString + equals: 'Words'. "there is no editor" "self assert: (tagElement query // GtFilterTagEditorId) isEmpty. + + self assert: tagElement isValid." + + ^ tagElement +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueNotRequired [ + + + | tagElement filterDescriptor | + filterDescriptor := self filterValueNotRequired. + + tagElement := GtFilterTagElement new + descriptor: filterDescriptor; + availableFilterDescriptors: self availableFilterDescriptors. + + self + assert: (tagElement query // GtFilterTagLabelId) anyOne text asString + equals: 'Default without value'. "there is no editor" + self assert: (tagElement query // GtFilterTagEditorId) isEmpty. + + self assert: tagElement isValid. + + ^ tagElement +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueNotRequiredByScripter [ + + + | scripter | + scripter := BlScripter new + extent: 500 @ 400; + element: self filterTagValueNotRequired. + + scripter + assertStep: [ :s | + s + label: 'Does not have focus by default'; + satisfies: [ :anElement | anElement hasFocus not ] ]. + + scripter + clickStep: [ :s | + s + label: 'Click on label to request focus'; + id: GtFilterTagLabelId ]. + + scripter + assertStep: [ :s | + s + label: 'Has focus after clicking on label'; + satisfies: [ :anElement | anElement hasFocus ] ]. + + scripter + clickStep: [ :s | + s + label: 'Click again on the label to open a dropdown'; + id: GtFilterTagLabelId ]. + + scripter + clickStep: [ :s | + s + label: 'Select filter without value'; + onSpaceRoot; + id: (GtFilterTagPickerItemId indexed: 2) ]. + + scripter + assertStep: [ :s | + s + label: 'Check that replace wish was fired'; + event: GtFilterTagReplaceWish; + value: [ :anEvent | anEvent filterDescriptor name ] + equals: [ 'Default without value' ]; + value: [ :anEvent | anEvent newFilterDescriptor name ] + equals: [ 'Filter without value' ] ]. + + scripter + assertStep: [ :s | + s + label: 'Tag still has focus'; + satisfies: [ :anElement | anElement hasFocus ] ]. + + scripter shortcut + label: 'Press backspace to remove the tag'; + combination: BlKeyCombination backspace; + play. + + scripter + assertStep: [ :s | + s + label: 'Check that remove wish was fired'; + event: GtFilterTagRemoveWish; + value: [ :anEvent | anEvent filterDescriptor name ] + equals: [ 'Filter without value' ] ]. + + ^ scripter +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueRequiredWithDefault [ + + + | aTagElement | + aTagElement := GtFilterTagElement new + descriptor: self filterValueRequiredWithDefault; + availableFilterDescriptors: self availableFilterDescriptors. + + ^ aTagElement +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueRequiredWithDefaultByScripter [ + + + | scripter | + scripter := BlScripter new + extent: 500 @ 400; + element: self filterTagValueRequiredWithDefault. + + scripter assertStep: [ :s | s satisfies: [ :anElement | anElement isValid ] ]. + + scripter + assertStep: [ :s | s satisfies: [ :anElement | anElement hasFocus not ] ]. + + scripter + assertStep: [ :s | + s + label: 'Check that the default value is set in the editor'; + id: GtFilterTagEditorId; + value: [ :anEditor | anEditor text asString ] equals: 'Default' ]. + + scripter + assertStep: [ :s | + s + label: 'Check that name of the filter is correct in the label'; + id: GtFilterTagLabelId; + value: [ :aLabel | aLabel text asString ] equals: 'Default with value' ]. + + ^ scripter +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueRequiredWithDefaultByScripterRemoveText [ + + + | scripter | + scripter := self filterTagValueRequiredWithDefaultByScripter. + + scripter clickStep: [ :s | s id: GtFilterTagEditorId ]. + + scripter shortcut + id: GtFilterTagEditorId; + combination: BlKeyCombination primaryA; + play. + + scripter + typeStep: [ :s | + s + id: GtFilterTagEditorId; + text: '' ]. + + scripter + assertStep: [ :s | + s + label: 'Check that the new value is typed in the editor'; + id: GtFilterTagEditorId; + value: [ :anEditor | anEditor text asString ] equals: '' ]. + + scripter assertStep: [ :s | s satisfies: [ :anEditor | anEditor isValid not ] ]. + + scripter shortcut + label: 'Accept changes with enter shortcut'; + id: GtFilterTagEditorId; + combination: BlKeyCombination enter; + play. + + scripter assertStep: [ :s | s eventNotFired: GtFilterTagUpdateWish ]. + + ^ scripter +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueRequiredWithDefaultByScripterTypeAndAcceptWithClickingAway [ + + + | scripter | + scripter := self filterTagValueRequiredWithDefaultByScripter. + + scripter clickStep: [ :s | s id: GtFilterTagEditorId ]. + + scripter + assertStep: [ :s | + s + id: GtFilterTagEditorId; + satisfies: [ :anEditor | anEditor hasFocus ] ]. + + scripter shortcut + id: GtFilterTagEditorId; + combination: BlKeyCombination primaryA; + play. + + scripter + typeStep: [ :s | + s + id: GtFilterTagEditorId; + text: ' New value ' ]. + + scripter + assertStep: [ :s | + s + label: 'Check that the new value is typed in the editor'; + id: GtFilterTagEditorId; + value: [ :anEditor "intentionally added spaces to test trimming" | anEditor text asString ] + equals: ' New value ' ]. + + scripter + assertStep: [ :s | + s + id: GtFilterTagEditorId; + satisfies: [ :anEditor | anEditor hasFocus ] ]. + + scripter clickStep: [ :s | s id: GtFilterTagLabelId ]. + + scripter + assertStep: [ :s | + s + id: GtFilterTagEditorId; + satisfies: [ :anEditor | anEditor hasFocus not ] ]. + + scripter + assertStep: [ :s | + s + label: 'Check that update wish was fired'; + event: GtFilterTagUpdateWish; + value: [ :anEvent | anEvent filterDescriptor name ] + equals: [ 'Default with value' ]; + value: [ :anEvent | anEvent newValue ] equals: [ 'New value' ] ]. + + ^ scripter +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueRequiredWithDefaultByScripterTypeAndAcceptWithShortcut [ + + + | scripter | + scripter := self filterTagValueRequiredWithDefaultByScripter. + + scripter clickStep: [ :s | s id: GtFilterTagEditorId ]. + + scripter + assertStep: [ :s | + s + id: GtFilterTagEditorId; + satisfies: [ :anEditor | anEditor hasFocus ] ]. + + scripter shortcut + id: GtFilterTagEditorId; + combination: BlKeyCombination primaryA; + play. + + scripter + typeStep: [ :s | + s + id: GtFilterTagEditorId; + text: ' New value ' ]. + + scripter + assertStep: [ :s | + s + label: 'Check that the new value is typed in the editor'; + id: GtFilterTagEditorId; + value: [ :anEditor "intentionally added spaces to test trimming" | anEditor text asString ] + equals: ' New value ' ]. + + scripter shortcut + label: 'Accept changes with enter shortcut'; + id: GtFilterTagEditorId; + combination: BlKeyCombination enter; + play. + + scripter + assertStep: [ :s | + s + label: 'Check that update wish was fired'; + event: GtFilterTagUpdateWish; + value: [ :anEvent | anEvent filterDescriptor name ] + equals: [ 'Default with value' ]; + value: [ :anEvent | anEvent newValue ] equals: [ 'New value' ] ]. + + ^ scripter +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueRequiredWithoutDefault [ + + + | aTagElement | + aTagElement := GtFilterTagElement new + descriptor: self filterValueRequiredWithoutDefault; + availableFilterDescriptors: self availableFilterDescriptors. + + ^ aTagElement +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterTagValueRequiredWithoutDefaultByScripter [ + + + | scripter | + scripter := BlScripter new + extent: 500 @ 400; + element: self filterTagValueRequiredWithoutDefault. + + scripter assertStep: [ :s | s satisfies: [ :anElement | anElement isValid ] ]. + + scripter + assertStep: [ :s | + s + label: 'Does not have focus by default'; + satisfies: [ :anElement | anElement hasFocus not ] ]. + + scripter + assertStep: [ :s | + s + id: GtFilterTagEditorId; + satisfies: [ :anEditor | anEditor text isEmpty ] ]. + + scripter + assertStep: [ :s | + s + id: GtFilterTagLabelId; + value: [ :aLabel | aLabel text asString ] equals: 'Default with value' ]. + + ^ scripter +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterValueNotRequired [ + + + ^ GtFilterDescriptorExamples new filterDescriptorValueNotRequired + named: 'Default without value' +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterValueRequiredWithDefault [ + + + ^ GtFilterDescriptorExamples new newFilterDescriptor + named: 'Default with value'; + emptyDefaultValue: 'Default' +] + +{ #category : #examples } +GtFilterTagElementExamples >> filterValueRequiredWithoutDefault [ + + + ^ GtFilterDescriptorExamples new newFilterDescriptor + named: 'Default with value' +] diff --git a/src/GToolkit-Coder-Examples/GtFiltersElementExamples.class.st b/src/GToolkit-Coder-Examples/GtFiltersElementExamples.class.st new file mode 100644 index 000000000..7e10cd4b2 --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtFiltersElementExamples.class.st @@ -0,0 +1,140 @@ +Class { + #name : #GtFiltersElementExamples, + #superclass : #Object, + #category : #'GToolkit-Coder-Examples-Filters' +} + +{ #category : #examples } +GtFiltersElementExamples >> emptyFiltersElement [ + + + | aFiltersElement | + aFiltersElement := GtFiltersElement new. + + self assert: (aFiltersElement query // GtFiltersAddButtonId) isNotEmpty. + + ^ aFiltersElement +] + +{ #category : #examples } +GtFiltersElementExamples >> filterDescriptors [ + ^ { + GtFilterDescriptor new + creator: GtSearchNullFilter new; + named: 'Filter with value'; + order: 1; + emptyDefaultValue: 'value'. + + GtFilterDescriptor new + creator: GtSearchNullFilter new; + named: 'Filter without value'; + order: 2; + valueNotRequired. + + GtFilterDescriptor new + creator: GtSearchNullFilter new; + named: 'Filter with empty default value'; + order: 3. + + GtFilterDescriptor + creator: GtSearchNullFilter new + named: 'Filter D' + order: 4. + } +] + +{ #category : #examples } +GtFiltersElementExamples >> filtersElementWithDescriptors [ + + + | filtersElement scripter | + filtersElement := self emptyFiltersElement. + filtersElement descriptors: self filterDescriptors. + + scripter := BlScripter new element: filtersElement. + + scripter + assertStep: [ :s | + s + label: 'Check how many default filters there are'; + value: [ :anElement | (anElement query // GtFilterTagElement) all size ] + equals: 2 ]. + + ^ scripter +] + +{ #category : #examples } +GtFiltersElementExamples >> filtersElementWithDescriptorsBuildFilters [ + + + | scripter | + scripter := self filtersElementWithDescriptors. + scripter element + when: GtFiltersChangedEvent + do: [ :anEvent | + | aFiltersElement theFilters | + aFiltersElement := anEvent currentTarget. + + theFilters := aFiltersElement currentFilters. + aFiltersElement + buildFilters: [ theFilters + do: [ :eachFilterAndValue | + aFiltersElement + addFilterForDescriptor: eachFilterAndValue key + andValue: eachFilterAndValue value ] ] ]. + + scripter clickStep: [ :s | s id: GtFiltersAddButtonId ]. + + scripter + assertStep: [ :s | + s + // GtFilterTagElement; + @ 3; + satisfies: [ :aTagElement | aTagElement hasFocus ] ]. + + scripter assertStep: [ :s | s eventNotFired: GtFiltersChangedEvent ]. + + scripter shortcut + // GtFilterTagElement; + @ 3; + // GtFilterTagEditorId; + combination: BlKeyCombination enter; + play. + + scripter + assertStep: [ :s | + s + label: 'Make sure that the filters changed event is fired only once'; + eventFired: GtFiltersChangedEvent times: 1 ]. + + ^ scripter +] + +{ #category : #examples } +GtFiltersElementExamples >> filtersElementWithDescriptorsReplaceFilterWithTheSameFilter [ + + + | scripter | + scripter := self filtersElementWithDescriptors. + + scripter + clickStep: [ :s | + s + // GtFilterTagElement; + @ 1; + // GtFilterTagPickerId ]. + + scripter + clickStep: [ :s | + s + onSpaceRoot; + id: (GtFilterTagPickerItemId indexed: 1) ]. + + scripter + assertStep: [ :s | + s + label: 'Replacing filter with the same filter should not emit anything'; + eventFired: GtFiltersChangedEvent times: 0 ]. + + ^ scripter +] diff --git a/src/GToolkit-Coder-Examples/GtIntersectionFilterExamples.class.st b/src/GToolkit-Coder-Examples/GtIntersectionFilterExamples.class.st index 698fc2562..3b9a91ba8 100644 --- a/src/GToolkit-Coder-Examples/GtIntersectionFilterExamples.class.st +++ b/src/GToolkit-Coder-Examples/GtIntersectionFilterExamples.class.st @@ -7,25 +7,25 @@ Class { { #category : #'as yet unclassified' } GtIntersectionFilterExamples >> intersectionDifferentFilters [ + | filter1 filter2 intersection | - filter1 := #gtExample gtPragmas. filter2 := #filter gtReferences. intersection := filter1 & filter2. self assert: (intersection matches: thisContext method). - ^ intersection. + ^ intersection ] { #category : #'as yet unclassified' } GtIntersectionFilterExamples >> intersectionSameFilter [ + | filter intersection | - filter := #gtExample gtPragmas. intersection := filter & filter. - + self assert: intersection size equals: filter size. self assert: (intersection matches: thisContext method). - - ^ intersection. + + ^ intersection ] diff --git a/src/GToolkit-Coder-Examples/GtMagritteModelExamples.extension.st b/src/GToolkit-Coder-Examples/GtMagritteModelExamples.extension.st new file mode 100644 index 000000000..e4331d14f --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtMagritteModelExamples.extension.st @@ -0,0 +1,87 @@ +Extension { #name : #GtMagritteModelExamples } + +{ #category : #'*GToolkit-Coder-Examples' } +GtMagritteModelExamples >> simpleBaselineDescription [ + + + ^ MAPriorityContainer + withAll: {MAStringDescription new + accessor: (MADictionaryAccessor key: #baseline); + beAlwaysEditable; + blocCompletion: [ GtClassesCompletionStrategy new ]; + beRequired; + addCondition: [ :aValue | aValue beginsWith: 'BaselineOf' ] + labelled: 'Baseline name must begin with BaselineOf'; + addCondition: [ :aValue | (Smalltalk globals includesKey: aValue asSymbol) not ] + labelled: 'Baseline name must be a non-existent class'; + comment: 'Baseline name'} +] + +{ #category : #'*GToolkit-Coder-Examples' } +GtMagritteModelExamples >> simpleBaselinesDescription [ + + + ^ MAPriorityContainer + withAll: {MAToManyRelationDescription new + label: 'Baselines'; + classes: {String}; + accessor: (MADictionaryAccessor key: #baselines); + blocListStencil: [ :aMemento :aDescription :aForm | + | aTagger | + aTagger := BrTagger new. + aTagger + hMatchParent; + vFitContent. + aForm hMatchParent. + aTagger + aptitude: (BrGlamorousTaggerEditableAptitude new + tagLabel: [ :aTag | + | aLabel | + aLabel := BrEditableLabel new + text: aTag name; + aptitude: (BrGlamorousEditableLabelAptitude new + glamorousCodeFont; + defaultForeground: Color black; + fontSize: 10). + + (GtCompletionController + on: aLabel + strategy: GtClassesCompletionStrategy new) install. + + aLabel ]). + aTagger + when: BrTaggerAddTagRequest + do: [ :aRequest | + | aValue | + aValue := (aTagger tags collect: #name) + add: aRequest tag name; + yourself. + GtMagritteBuilderUtility + write: aValue + using: aDescription + memento: aMemento + element: aTagger ]. + aTagger + when: BrTaggerRemoveTagRequest + do: [ :aRequest | + | aValue | + aValue := (aTagger tags collect: #name) + remove: aRequest tag name; + yourself. + GtMagritteBuilderUtility + write: aValue + using: aDescription + memento: aMemento + element: aTagger ]. + aTagger + withAsyncSinkDo: [ :anElementSink | + anElementSink + sink: AsyncPeekSink new; + whenUpdate: [ :theTagger :aSink | + | theValues theTexts | + theValues := aSink value currentValue. + theTexts := theValues + collect: [ :each | aDescription displayStringFor: each ]. + theTagger namedTags: theTexts ]. + (aMemento readObservableValueUsing: aDescription) observe: anElementSink ] ]} +] diff --git a/src/GToolkit-Coder-Examples/GtMagritteViewModelExamples.extension.st b/src/GToolkit-Coder-Examples/GtMagritteViewModelExamples.extension.st new file mode 100644 index 000000000..5805c8f77 --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtMagritteViewModelExamples.extension.st @@ -0,0 +1,75 @@ +Extension { #name : #GtMagritteViewModelExamples } + +{ #category : #'*GToolkit-Coder-Examples' } +GtMagritteViewModelExamples >> asyncViewModelWithSimpleBaseline [ + + + | aViewModel anObject aDescription | + anObject := self simpleBaselineObject. + aDescription := self simpleBaselineDescription. + aViewModel := GtMagritteViewModel forObject: anObject description: aDescription. + + self assert: aViewModel mementoFuture isAsyncFuture. + + ^ aViewModel +] + +{ #category : #'*GToolkit-Coder-Examples' } +GtMagritteViewModelExamples >> asyncViewModelWithSimpleBaselines [ + + + | aViewModel anObject aDescription | + anObject := GtMagritteModelExamples new simpleBaselinesObject. + aDescription := GtMagritteModelExamples new simpleBaselinesDescription. + aViewModel := GtMagritteViewModel forObject: anObject description: aDescription. + + self assert: aViewModel mementoFuture isAsyncFuture. + + ^ aViewModel +] + +{ #category : #'*GToolkit-Coder-Examples' } +GtMagritteViewModelExamples >> elementWithSimpleBaseline [ + + + | aViewModel anElement | + aViewModel := self asyncViewModelWithSimpleBaseline. + anElement := aViewModel asElement. + + self assert: anElement isNotNil. + + ^ anElement +] + +{ #category : #'*GToolkit-Coder-Examples' } +GtMagritteViewModelExamples >> simpleBaselineDescription [ + + + ^ GtMagritteModelExamples new simpleBaselineDescription +] + +{ #category : #'*GToolkit-Coder-Examples' } +GtMagritteViewModelExamples >> viewModelWithBaselines [ + + + | aViewModel anObject aDescription | + anObject := GtMagritteModelExamples new simpleBaselinesObject. + aDescription := GtMagritteModelExamples new simpleBaselinesDescription. + aViewModel := GtMagritteViewModel forObject: anObject description: aDescription. + + self assert: aViewModel mementoFuture isAsyncFuture. + + ^ aViewModel +] + +{ #category : #'*GToolkit-Coder-Examples' } +GtMagritteViewModelExamples >> viewModelWithBaselinesWithoutSaveOrCancelButton [ + + + | aViewModel | + aViewModel := self viewModelWithBaselines. + + aViewModel actions: #(). + + ^ aViewModel +] diff --git a/src/GToolkit-Coder-Examples/GtSearchNullFilterExamples.class.st b/src/GToolkit-Coder-Examples/GtSearchNullFilterExamples.class.st index 98a0acbf1..083453e17 100644 --- a/src/GToolkit-Coder-Examples/GtSearchNullFilterExamples.class.st +++ b/src/GToolkit-Coder-Examples/GtSearchNullFilterExamples.class.st @@ -7,12 +7,13 @@ Class { { #category : #'as yet unclassified' } GtSearchNullFilterExamples >> nullSearchFilter [ + | filter | filter := GtSearchNullFilter new. - + self assert: (filter matches: thisContext method) not. self assert: (filter matches: 'GToolkit-Coder' asPackage) not. - self assert: (filter isEmpty). - - ^ filter. + self assert: filter isEmpty. + + ^ filter ] diff --git a/src/GToolkit-Coder-Examples/GtSyncScrollRangesExamples.class.st b/src/GToolkit-Coder-Examples/GtSyncScrollRangesExamples.class.st new file mode 100644 index 000000000..ac48e411e --- /dev/null +++ b/src/GToolkit-Coder-Examples/GtSyncScrollRangesExamples.class.st @@ -0,0 +1,316 @@ +Class { + #name : #GtSyncScrollRangesExamples, + #superclass : #Object, + #category : #'GToolkit-Coder-Examples-Diff' +} + +{ #category : #accessing } +GtSyncScrollRangesExamples >> deletedLineChanges [ + + + | diff ranges fromLines toLines changes | + diff := GtDiffBuilderExamples new deletedLineChanges. + ranges := GtSyncScrollRanges createFromChange: diff. + toLines := #(#(1 1) #(4 1) #(6 2) #(10 1)). + fromLines := #(#(1 1) #(4 1) #(6 3) #(11 1)). + changes := ranges ranges select: #isDifference. + self assert: changes size equals: fromLines size. + changes + doWithIndex: [ :each :i | + self assert: each leftFirst + 1 equals: (fromLines at: i) first. + self assert: each leftLast equals: each leftFirst + (fromLines at: i) last. + self assert: each rightFirst + 1 equals: (toLines at: i) first. + self assert: each rightLast equals: each rightFirst + (toLines at: i) last ]. + ^ ranges +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> deletedPharoChanges [ + + + | diff ranges fromLines toLines changes | + diff := GtDiffBuilderExamples new deletedPharoChanges. + ranges := GtSyncScrollRanges createFromChange: diff. + toLines := #(#(1 1) #(4 1) #(6 2) #(10 1)). + fromLines := #(#(1 1) #(4 1) #(6 3) #(11 1)). + changes := ranges ranges select: #isDifference. + self assert: changes size equals: fromLines size. + changes + doWithIndex: [ :each :i | + self assert: each leftFirst + 1 equals: (fromLines at: i) first. + self assert: each leftLast equals: each leftFirst + (fromLines at: i) last. + self assert: each rightFirst + 1 equals: (toLines at: i) first. + self assert: each rightLast equals: each rightFirst + (toLines at: i) last ]. + ^ ranges +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> deletedWordChanges [ + + + | diff ranges fromLines toLines changes | + diff := GtDiffBuilderExamples new deletedWordChanges. + ranges := GtSyncScrollRanges createFromChange: diff. + toLines := #(#(1 1) #(4 1) #(6 2) #(10 1)). + fromLines := #(#(1 1) #(4 1) #(6 3) #(11 1)). + changes := ranges ranges select: #isDifference. + self assert: changes size equals: fromLines size. + changes + doWithIndex: [ :each :i | + self assert: each leftFirst + 1 equals: (fromLines at: i) first. + self assert: each leftLast equals: each leftFirst + (fromLines at: i) last. + self assert: each rightFirst + 1 equals: (toLines at: i) first. + self assert: each rightLast equals: each rightFirst + (toLines at: i) last ]. + ^ ranges +] + +{ #category : #examples } +GtSyncScrollRangesExamples >> diffWithEmptyLines [ + + + | ranges | + ranges := GtSyncScrollRanges + createFromChange: (GtDiffBuilder + computeDifferencesFrom: '1\\3\4\5\' withCRs + to: '1\2\3\\5\' withCRs + using: (GtLineDiffSplitter new includeEOLChars: false)). + self validateNextLeftChangeIn: ranges equals: #(2 4). + self validatePreviousRightChangeIn: ranges equals: #(2 4). + ^ ranges +] + +{ #category : #examples } +GtSyncScrollRangesExamples >> example [ + + + ^ GtSyncScrollRanges createFromChange: self exampleDiff +] + +{ #category : #examples } +GtSyncScrollRangesExamples >> exampleDiff [ + + + ^ GtDiffBuilder + computeDifferencesFrom: self originalString + to: self newString + using: GtCharacterGroupDiffSplitter words +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> insertedLineChanges [ + + + | diff ranges fromLines toLines changes | + diff := GtDiffBuilderExamples new insertedLineChanges. + ranges := GtSyncScrollRanges createFromChange: diff. + fromLines := #(#(1 1) #(4 1) #(6 2) #(10 1)). + toLines := #(#(1 1) #(4 1) #(6 3) #(11 1)). + changes := ranges ranges select: #isDifference. + self assert: changes size equals: fromLines size. + changes + doWithIndex: [ :each :i | + self assert: each leftFirst + 1 equals: (fromLines at: i) first. + self assert: each leftLast equals: each leftFirst + (fromLines at: i) last. + self assert: each rightFirst + 1 equals: (toLines at: i) first. + self assert: each rightLast equals: each rightFirst + (toLines at: i) last ]. + ^ ranges +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> insertedPharoChanges [ + + + | diff ranges fromLines toLines changes | + diff := GtDiffBuilderExamples new insertedPharoChanges. + ranges := GtSyncScrollRanges createFromChange: diff. + fromLines := #(#(1 1) #(4 1) #(6 2) #(10 1)). + toLines := #(#(1 1) #(4 1) #(6 3) #(11 1)). + changes := ranges ranges select: #isDifference. + self assert: changes size equals: fromLines size. + changes + doWithIndex: [ :each :i | + self assert: each leftFirst + 1 equals: (fromLines at: i) first. + self assert: each leftLast equals: each leftFirst + (fromLines at: i) last. + self assert: each rightFirst + 1 equals: (toLines at: i) first. + self assert: each rightLast equals: each rightFirst + (toLines at: i) last ]. + ^ ranges +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> insertedWordChanges [ + + + | diff ranges fromLines toLines changes | + diff := GtDiffBuilderExamples new insertedWordChanges. + ranges := GtSyncScrollRanges createFromChange: diff. + fromLines := #(#(1 1) #(4 1) #(6 2) #(10 1)). + toLines := #(#(1 1) #(4 1) #(6 3) #(11 1)). + changes := ranges ranges select: #isDifference. + self assert: changes size equals: fromLines size. + changes + doWithIndex: [ :each :i | + self assert: each leftFirst + 1 equals: (fromLines at: i) first. + self assert: each leftLast equals: each leftFirst + (fromLines at: i) last. + self assert: each rightFirst + 1 equals: (toLines at: i) first. + self assert: each rightLast equals: each rightFirst + (toLines at: i) last ]. + ^ ranges +] + +{ #category : #examples } +GtSyncScrollRangesExamples >> multilineCode [ + + + | change ranges | + change := GtDiffBuilderExamples new tokenChanges. + ranges := GtSyncScrollRanges createFromChange: change. + self assert: ranges ranges size equals: 1. + ^ ranges +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> multipleChanges [ + + + | diff ranges | + diff := GtDiffBuilderExamples new multipleChanges. + ranges := GtSyncScrollRanges createFromChange: diff. + ranges ranges + inject: {0. + 0} + into: [ :last :each | + self assert: last first equals: each leftFirst. + self assert: last last equals: each rightFirst. + {each leftLast. + each rightLast} ]. + ^ ranges +] + +{ #category : #private } +GtSyncScrollRangesExamples >> newString [ + + + ^ '1 +4 +5 +6 +7 +8 +9' +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> nextPreviousLeftChange [ + + + | diff ranges | + diff := GtDiffBuilderExamples new deletionsInMiddle. + ranges := GtSyncScrollRanges createFromChange: diff. + self validateNextLeftChangeIn: ranges equals: #(1 5 5). + self validatePreviousLeftChangeIn: ranges equals: #(6 5 5). + ^ ranges +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> nextPreviousMultipleChanges [ + + + | ranges | + ranges := self multipleChanges. + self validateNextLeftChangeIn: ranges equals: #(1 2 4). + self validateNextRightChangeIn: ranges equals: #(1 2 5). + ^ ranges +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> nextPreviousRightChange [ + + + | diff ranges | + diff := GtDiffBuilderExamples new insertionsInMiddle. + ranges := GtSyncScrollRanges createFromChange: diff. + self validateNextRightChangeIn: ranges equals: #(1 5 5). + self validatePreviousRightChangeIn: ranges equals: #(6 5 5). + ^ ranges +] + +{ #category : #private } +GtSyncScrollRangesExamples >> originalString [ + + + ^ '1 +2 +3 +5 +6 +7 +9 +10' +] + +{ #category : #examples } +GtSyncScrollRangesExamples >> reversedMultilineCode [ + + + | change ranges | + change := GtDiffBuilderExamples new reversedTokenChanges. + ranges := GtSyncScrollRanges createFromChange: change. + self assert: ranges ranges size equals: 1. + ^ ranges +] + +{ #category : #examples } +GtSyncScrollRangesExamples >> textDiffExample [ + + + ^ GtSyncScrollRanges + createFromDiff: (TextDiffBuilder from: self originalString to: self newString) +] + +{ #category : #examples } +GtSyncScrollRangesExamples >> textDiffNextChange [ + + + | ranges | + ranges := self textDiffExample. + self validateNextLeftChangeIn: ranges equals: #(1 2 7 8 2). + self validatePreviousLeftChangeIn: ranges equals: #(8 7 2 8). + self validateNextRightChangeIn: ranges equals: #(1 2 6 8 2). + self validatePreviousRightChangeIn: ranges equals: #(8 8 6 2 8). + ^ ranges +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> validateNextLeftChangeIn: ranges equals: stops [ + | stream | + stream := ReadStream on: stops. + ranges selectRange: (ranges rangeForLeft: stream next). + [ stream atEnd ] + whileFalse: [ self assert: ranges selectNext leftFirst + 1 equals: stream next ] +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> validateNextRightChangeIn: ranges equals: stops [ + | stream | + stream := ReadStream on: stops. + ranges selectRange: (ranges rangeForRight: stream next). + [ stream atEnd ] + whileFalse: [ self assert: ranges selectNext rightFirst + 1 equals: stream next ] +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> validatePreviousLeftChangeIn: ranges equals: stops [ + | stream | + stream := ReadStream on: stops. + ranges selectRange: (ranges rangeForLeft: stream next). + [ stream atEnd ] + whileFalse: [ self assert: ranges selectPrevious leftFirst + 1 equals: stream next ] +] + +{ #category : #accessing } +GtSyncScrollRangesExamples >> validatePreviousRightChangeIn: ranges equals: stops [ + | stream | + stream := ReadStream on: stops. + ranges selectRange: (ranges rangeForRight: stream next). + [ stream atEnd ] + whileFalse: [ self assert: ranges selectPrevious rightFirst + 1 equals: stream next ] +] diff --git a/src/GToolkit-Coder-Examples/GtUnionFilterExamples.class.st b/src/GToolkit-Coder-Examples/GtUnionFilterExamples.class.st index f95783abf..f91028f0d 100644 --- a/src/GToolkit-Coder-Examples/GtUnionFilterExamples.class.st +++ b/src/GToolkit-Coder-Examples/GtUnionFilterExamples.class.st @@ -7,22 +7,24 @@ Class { { #category : #examples } GtUnionFilterExamples >> unionDifferentMethods [ + | filter1 filter2 union | - filter1 := thisContext method selector gtImplementors. + filter1 := #unionDifferentMethods gtImplementors. filter2 := #foo gtImplementors. union := filter1 | filter2. self assert: union size = (filter1 size + filter2 size). - self assert: (union matches: thisContext method). + self assert: (union matches: GtUnionFilterExamples>>#unionDifferentMethods). ^ union ] { #category : #examples } GtUnionFilterExamples >> unionNullFilter [ + | filter union | filter := thisContext method selector gtImplementors. union := GtSearchNullFilter new | filter | filter. - self assert: union size = filter size. + self assert: union size equals: filter size. self assert: (union matches: thisContext method). ^ union ] @@ -30,6 +32,7 @@ GtUnionFilterExamples >> unionNullFilter [ { #category : #examples } GtUnionFilterExamples >> unionSameFilters [ + | filter union | filter := thisContext method selector gtImplementors. union := filter | filter. diff --git a/src/GToolkit-Coder-Examples/RBRenameClassWithCommentsRefactoringExamples.class.st b/src/GToolkit-Coder-Examples/RBRenameClassWithCommentsRefactoringExamples.class.st index f92ece769..4f5a1f577 100644 --- a/src/GToolkit-Coder-Examples/RBRenameClassWithCommentsRefactoringExamples.class.st +++ b/src/GToolkit-Coder-Examples/RBRenameClassWithCommentsRefactoringExamples.class.st @@ -17,17 +17,17 @@ Class { RBRenameClassWithCommentsRefactoringExamples >> renameClassComment [ "This comment with RBRenameClassWithCommentsRefactoringExamples should be renamed" - + | model refactoring cls newName | model := RBNamespace new. cls := model classFor: RBRenameClassWithCommentsRefactoringExamples. self assert: cls comment = self class comment. newName := #RenamedExample. refactoring := GtRBRenameClassWithCommentsRefactoring - model: model - rename: cls - to: newName. + model: model + rename: cls name + to: newName. refactoring primitiveExecute. self assert: cls comment ~= self class comment. self class comment lines @@ -35,14 +35,9 @@ RBRenameClassWithCommentsRefactoringExamples >> renameClassComment [ do: [ :orig :new | (orig includesSubstring: ' not ') ifTrue: [ self assert: orig = new ] - ifFalse: [ self - assert: (orig copyReplaceAll: self class name with: newName) = new ] ]. + ifFalse: [ self assert: (orig copyReplaceAll: self class name with: newName) = new ] ]. self - assert: - (cls methodFor: thisContext method selector) source - = - (thisContext method sourceCode - copyReplaceAll: self class name - with: newName). + assert: (cls methodFor: thisContext method selector) source + = (thisContext method sourceCode copyReplaceAll: self class name with: newName). ^ model "This should also be renamed since it contains RBRenameClassWithCommentsRefactoringExamples" ] diff --git a/src/GToolkit-Coder-Examples/TBlDevScripterActionStep.extension.st b/src/GToolkit-Coder-Examples/TBlDevScripterActionStep.extension.st new file mode 100644 index 000000000..89238a415 --- /dev/null +++ b/src/GToolkit-Coder-Examples/TBlDevScripterActionStep.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #TBlDevScripterActionStep } + +{ #category : #'*GToolkit-Coder-Examples' } +TBlDevScripterActionStep >> coderNavigationIndex [ + ^ self addStep: GtCoderNavigationIndexStep new referenceSender +] diff --git a/src/GToolkit-Coder-Examples/TCoderByScripterExamples.trait.st b/src/GToolkit-Coder-Examples/TCoderByScripterExamples.trait.st index c97357663..a3293b480 100644 --- a/src/GToolkit-Coder-Examples/TCoderByScripterExamples.trait.st +++ b/src/GToolkit-Coder-Examples/TCoderByScripterExamples.trait.st @@ -1,37 +1,62 @@ Trait { #name : #TCoderByScripterExamples, - #category : #'GToolkit-Coder-Examples-By scripter' + #category : #'GToolkit-Coder-Examples-Scripter' } { #category : #utilities } TCoderByScripterExamples >> openAndClickOnContextMenuItemFor: aScripterOrStep id: aBlElementId [ - - aScripterOrStep secondaryClick - label: 'Open context menu'; - onChildNamed: GtSourceCoderEditorId. - - aScripterOrStep wait forEmptySpaceTaskQueue. - - aScripterOrStep mouseMoveOver - label: 'Hover ', aBlElementId asSymbol; + + aScripterOrStep secondaryClickStep: [:s | + s label: 'Open context menu'; + // GtSourceCoderEditorId]. + + aScripterOrStep do + label: 'Scroll to ', aBlElementId asSymbol; + block: [ :aList | + (aList isKindOf: BrUngroupedList) + ifTrue: [ + aList scrollToItemSuchThat: [ :each | + each id asBlocElementId = aBlElementId asBlocElementId ] ] + ifFalse: [ + | aQuery anAttemptCount | + anAttemptCount := 0. + [ + | isVisible | + aQuery := aList query // aBlElementId asBlocElementId. + isVisible := aQuery isEmpty not and: [ + aQuery anyOne isVisibleFullyInParent: aList ]. + anAttemptCount < 5 and: [ isVisible not ] + ] whileTrue: [ + aList scrollBy: 0 @ 100. + anAttemptCount := anAttemptCount + 1. + ]. + self + assert: aQuery isEmpty not + description: [ 'Grouped list {2} does not include {2} element id' + format: { aList. aBlElementId } ] + ] ]; onSpaceRoot; - id: aBlElementId. + // BrMenuSteppedElement; + onThisOrBreadthFirstChildOfKind: BrList; + play. - aScripterOrStep wait forEmptySpaceTaskQueue. - - aScripterOrStep click - label: 'Click ', aBlElementId asSymbol; + aScripterOrStep mouseMoveOverStep: [:s | + s label: 'Hover ' , aBlElementId asSymbol; onSpaceRoot; - onChildNamed: aBlElementId. + // BrMenuSteppedElement; + // aBlElementId]. - aScripterOrStep wait forEmptySpaceTaskQueue. - aScripterOrStep syncContinue + aScripterOrStep clickStep: [:s | + s label: 'Click ' , aBlElementId asSymbol; + onSpaceRoot; + // BrMenuSteppedElement; + // aBlElementId] ] { #category : #'examples - scripter' } TCoderByScripterExamples >> scripter [ | scripter | - scripter := BlDevScripter new. + scripter := BlScripter new. self assert: scripter events isEmpty. @@ -46,32 +71,51 @@ TCoderByScripterExamples >> scripterForBlock: aBlock [ aScripter substep: 'Initialize Coder Model and UI' do: [ :aStep | aStep set label: 'Initialize Coder Model'; - model: aBlock. + model: aBlock; + play. aStep set label: 'Initialize Coder UI'; element: [ :aCoder | aCoder asElement ]; - onModel ]. - - aScripter wait forEmptySpaceTaskQueue. - ^ aScripter syncContinue + onModel; + play ]. + + ^ aScripter +] + +{ #category : #utilities } +TCoderByScripterExamples >> scripterInPagerForBlock: aBlock [ + + | aScripter | + aScripter := self scripter. + aScripter substep: 'Initialize Coder Model and UI' do: [ :aStep | + aStep set + label: 'Initialize Coder Model'; + model: aBlock; + play. + aStep set + label: 'Initialize Coder UI'; + element: [ :aCoder | + GtPagerSettings usedPager createWrappedOn: aCoder asElement ]; + onModel; + play ]. + + ^ aScripter ] { #category : #utilities } TCoderByScripterExamples >> selectTextFor: aScripterOrStep from: aStartPosition to: aToPosition andCheck: aSelectedString [ - aScripterOrStep do - label: 'Select part of the method'; - action: [ :aSourceEditor | + + aScripterOrStep doStep: [:s | + s label: 'Select part of the method'; + // GtSourceCoderEditorId; + action: [ :aSourceEditor | aSourceEditor selecter from: aStartPosition to: aToPosition; - select ]; - onChildNamed: GtSourceCoderEditorId. + select ]]. - aScripterOrStep wait forEmptySpaceTaskQueue. - - aScripterOrStep check - label: 'Check selected text'; - value: [ :aSourceEditor | aSourceEditor editor selectedText asString ] equals: aSelectedString; - onChildNamed: GtSourceCoderEditorId. - - aScripterOrStep syncContinue + aScripterOrStep assertStep: [:s | + s label: 'Assert selected text'; + // GtSourceCoderEditorId; + value: [ :aSourceEditor | aSourceEditor editor selectedText asString ] + equals: aSelectedString] ] diff --git a/src/GToolkit-Coder-Examples/TGtBehaviorCoderDummyTrait.trait.st b/src/GToolkit-Coder-Examples/TGtBehaviorCoderDummyTrait.trait.st index 7bd4e3531..0f1106a92 100644 --- a/src/GToolkit-Coder-Examples/TGtBehaviorCoderDummyTrait.trait.st +++ b/src/GToolkit-Coder-Examples/TGtBehaviorCoderDummyTrait.trait.st @@ -7,3 +7,11 @@ Trait { ], #category : #'GToolkit-Coder-Examples-Dummies' } + +{ #category : #'tests - some' } +TGtBehaviorCoderDummyTrait >> methodFromTrait [ +] + +{ #category : #'tests - some' } +TGtBehaviorCoderDummyTrait >> overwrittenMethodFromTrait [ +] diff --git a/src/GToolkit-Coder-Extensions/Behavior.extension.st b/src/GToolkit-Coder-Extensions/Behavior.extension.st index 0b0dddf58..1c521199e 100644 --- a/src/GToolkit-Coder-Extensions/Behavior.extension.st +++ b/src/GToolkit-Coder-Extensions/Behavior.extension.st @@ -2,88 +2,20 @@ Extension { #name : #Behavior } { #category : #'*GToolkit-Coder-Extensions' } Behavior >> gtBrowse [ - ^ (GtCoder forClass: self) openInPager maximized + ^ BlSpace new + withSceneDriller; + inPager: [ GtClassCoderTool forClass: self ]; + title: self name; + icon: BrGlamorousVectorIcons browse; + show ] { #category : #'*GToolkit-Coder-Extensions' } Behavior >> gtBrowseFrom: anElement [ - ^ ((GtCoder forClass: self) - openInPagerFrom: anElement) - maximized -] - -{ #category : #'*GToolkit-Coder-Extensions' } -Behavior >> gtCoderExamplesFor: aView context: aDictionary [ - - | anExamplesClassName | - - anExamplesClassName := (self name , 'Examples') asSymbol. - - ^ self environment - at: anExamplesClassName - ifPresent: [ :aClass | - | methodsCoder | - methodsCoder := GtClassMethodsCoder forClass: aClass. - aView explicit - priority: 50; - title: 'Related examples'; - actionButtonIcon: BrGlamorousVectorIcons add - action: [ methodsCoder addNewCoder ]; - stencil: [ methodsCoder asElement ]; - actionButtonIcon: BrGlamorousIcons browse - tooltip: 'Browse ', aClass name - action: [ :aButton | - aButton phlow spawnTool: (GtClassCoderTool forClass: aClass) ] ] - ifAbsent: [ aView empty ] -] - -{ #category : #'*GToolkit-Coder-Extensions' } -Behavior >> gtCoderMethodsFor: aView context: aPhlowContext [ - - | aMethodsCoderUIModel aMethodsCoder | - - aMethodsCoderUIModel := self methodsCoderFromContext: aPhlowContext. - aMethodsCoder := aMethodsCoderUIModel coder. - - ^ aView explicit - priority: 10; - title: 'Methods'; - disableAsync; - actionDropdownButtonIcon: BrGlamorousVectorIcons filter - tooltip: 'Filter' - content: [ :element | aMethodsCoder filterDropDown: element ]; - actionButtonDo: [ :aButtonAction | - aButtonAction - icon: BrGlamorousVectorIcons add; - tooltip: 'Add new method'; - action: [ aMethodsCoderUIModel addNewCoder ]; - name: GtPharoBehaviorCoder addNewMethodButtonName ]; - actionDropdownButtonIcon: BrGlamorousVectorIcons robot - tooltip: 'Generate' - content: [ :element | aMethodsCoder codeGeneratorsDropDown: element for: aMethodsCoderUIModel ]; - actionStencil: - [ self runAllExamplesButtonForMethodsCoder: aMethodsCoder ]; - stencil: [ aMethodsCoderUIModel asElement ] -] - -{ #category : #'*GToolkit-Coder-Extensions' } -Behavior >> methodsCoderFromContext: aPhlowContext [ - - - ^ aPhlowContext - optionAt: #behaviorCoder - ifPresent: [ :aBehaviorCoderUIModel | aBehaviorCoderUIModel methodsCoderUIModel ] - ifAbsentPut: [ (GtClassMethodsCoder forClass: self) asCoderUIModel ] -] - -{ #category : #'*GToolkit-Coder-Extensions' } -Behavior >> runAllExamplesButtonForMethodsCoder: aMethodsCoder [ - ^ BrButton new - addAptitude: - (GtFilterRunExampleButtonFourStateIconAptitude new coder: aMethodsCoder); - addAptitude: BrGlamorousButtonWithIconAptitude - BrGlamorousButtonWithLabelTooltipAptitude; - addAptitude: (GtFilterRunExampleButtonTooltipAptitude new coder: aMethodsCoder); - icon: BrGlamorousVectorIcons play; - label: 'Run All Examples'; - action: [ :element | aMethodsCoder exampler runExamplesFrom: element ] + ^ BlSpace new + withCommonSettings; + inPager: [ self gtDefaultInspectorTool ]; + title: self name; + icon: BrGlamorousVectorIcons browse; + showFrom: anElement ] diff --git a/src/GToolkit-Coder-Extensions/BlLocalVersionAcceptVersionButtonId.class.st b/src/GToolkit-Coder-Extensions/BlLocalVersionAcceptVersionButtonId.class.st new file mode 100644 index 000000000..d20202052 --- /dev/null +++ b/src/GToolkit-Coder-Extensions/BlLocalVersionAcceptVersionButtonId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #BlLocalVersionAcceptVersionButtonId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-Extensions' +} + +{ #category : #converting } +BlLocalVersionAcceptVersionButtonId >> asSymbol [ + ^ #'local-version-element--accept-change' +] diff --git a/src/GToolkit-Coder-Extensions/Class.extension.st b/src/GToolkit-Coder-Extensions/Class.extension.st deleted file mode 100644 index 2ba2a1b2b..000000000 --- a/src/GToolkit-Coder-Extensions/Class.extension.st +++ /dev/null @@ -1,31 +0,0 @@ -Extension { #name : #Class } - -{ #category : #'*GToolkit-Coder-Extensions' } -Class >> gtExamplesMapFor: aView context: aPhlowContext [ - - - | aMethodsCoderUIModel aMethodsCoder | - aMethodsCoderUIModel := self methodsCoderFromContext: aPhlowContext. - aMethodsCoder := aMethodsCoderUIModel coder. - aMethodsCoder exampler hasExamples - ifFalse: [ ^ aView empty ]. - ^ aView forward - title: 'Examples map'; - priority: 11; - object: [ | allExamples | - allExamples := Array - streamContents: [ :aStream | - aMethodsCoder exampler - allExecutableExampleCodersDo: [ :aCoder :anExample | aStream nextPut: anExample ] ]. - GtExampleGroup withAll: allExamples ]; - view: #gtMapFor:; - actionStencilDo: [ :anExplicitAction | - anExplicitAction - priority: 80; - stencil: [ self runAllExamplesButtonForMethodsCoder: aMethodsCoder ] ]; - updateWhen: - GtCodersCoderAdded , GtCodersCoderRemoved , GtCodersCodersChanged - , GtCoderSourceCodeChanged - in: [ aMethodsCoder ]; - addAutoUpdate: GtPhlowUpdateViewOnAddedToScene -] diff --git a/src/GToolkit-Coder-Extensions/CompiledMethod.extension.st b/src/GToolkit-Coder-Extensions/CompiledMethod.extension.st index 6dcd26072..e40257b8c 100644 --- a/src/GToolkit-Coder-Extensions/CompiledMethod.extension.st +++ b/src/GToolkit-Coder-Extensions/CompiledMethod.extension.st @@ -2,16 +2,32 @@ Extension { #name : #CompiledMethod } { #category : #'*GToolkit-Coder-Extensions' } CompiledMethod >> gtBrowse [ - ^ (GtCoder forMethod: self) - openInPager - maximized + ^ BlSpace new + withSceneDriller; + inPager: [ GtMethodCoderTool compiledMethod: self ]; + title: self methodClass name; + icon: BrGlamorousVectorIcons browse; + show ] { #category : #'*GToolkit-Coder-Extensions' } CompiledMethod >> gtBrowseFrom: anElement [ - ^ ((GtCoder forMethod: self) - openInPagerFrom: anElement) - maximized + ^ BlSpace new + withCommonSettings; + inPager: [ GtMethodCoderTool compiledMethod: self ]; + title: self methodClass name; + icon: BrGlamorousVectorIcons browse; + showFrom: anElement +] + +{ #category : #'*GToolkit-Coder-Extensions' } +CompiledMethod >> gtChangeRecords [ + self sourcePointer ifNil: [ ^ #() ]. + + ^ SourceFiles + changeRecordsFrom: self sourcePointer + className: self methodClass instanceSide name + isMeta: self methodClass isMeta ] { #category : #'*GToolkit-Coder-Extensions' } @@ -19,32 +35,30 @@ CompiledMethod >> gtDisplayOn: aStream [ aStream print: self methodClass; nextPutAll: '>>'; store: self selector. ] +{ #category : #'*GToolkit-Coder-Extensions' } +CompiledMethod >> gtLocalVersions [ + | changeRecords | + changeRecords := self gtChangeRecords. + ^ changeRecords withIndexCollect: [ :aChange :anIndex | + GtLocalVersion + change: aChange + previousChange: (changeRecords at: anIndex + 1 ifAbsent: [nil]) + currentMethod: self ] +] + { #category : #'*GToolkit-Coder-Extensions' } CompiledMethod >> gtLocalVersionsFor: aView [ - ^ aView columnedList + + self sourcePointer ifNil: [ ^ aView empty ]. + + ^ aView list title: 'Local versions'; - priority: 20; + priority: 10; items: [ - SourceFiles - changeRecordsFrom: self sourcePointer - className: self methodClass name - isMeta: self methodClass isMeta ]; - column: 'Timestamp' text: #timeStamp width: 200; - column: 'Change' translated - icon: [ :eachItem | - BrExpander new - vFitContent; - hMatchParent; - collapse; - margin: (BlInsets all: 10); - aptitude: GtCoderExpanderAptitude new; - header: [ BrLabel new aptitude: BrGlamorousLabelAptitude; text: eachItem gtDisplayString ]; - content: [ | container | - container := BrFrame new - vExact: 400; - hMatchParent; - aptitude: BrGlamorousWithVerticalResizerAptitude; - addChild: ((eachItem gtSourceFor: GtPhlowEmptyView new) asElement) matchParent ] ] - weight: 1 + self gtLocalVersions ]; + itemStencil: [ GtLocalVersionElement new ]; + itemDataBinder: [ :aLocalVersionElement :aLocalVersion | + aLocalVersionElement + localVersion: aLocalVersion ] ] diff --git a/src/GToolkit-Coder-Extensions/GtCoderAction.extension.st b/src/GToolkit-Coder-Extensions/GtCoderAction.extension.st index 02c2f66b7..5cc0e7275 100644 --- a/src/GToolkit-Coder-Extensions/GtCoderAction.extension.st +++ b/src/GToolkit-Coder-Extensions/GtCoderAction.extension.st @@ -1,9 +1,9 @@ Extension { #name : #GtCoderAction } { #category : #'*GToolkit-Coder-Extensions' } -GtCoderAction >> gtViewActionSourceCodeFor: aView [ +GtCoderAction >> gtSourceCodeFor: aView [ ^ self action - ifNil: [ aView ] + ifNil: [ aView empty ] ifNotNil: [ self action gtSourceCodeFor: aView ] ] diff --git a/src/GToolkit-Coder-Extensions/GtCoderModel.extension.st b/src/GToolkit-Coder-Extensions/GtCoderModel.extension.st index 6e235777a..3855beb0e 100644 --- a/src/GToolkit-Coder-Extensions/GtCoderModel.extension.st +++ b/src/GToolkit-Coder-Extensions/GtCoderModel.extension.st @@ -22,7 +22,7 @@ GtCoderModel >> gtLiveFor: aView [ ^ aView explicit title: 'Live'; priority: 5; - stencil: [ self asElement matchParent ] + stencil: [ self asElement vFitContentLimited ] ] { #category : #'*GToolkit-Coder-Extensions' } diff --git a/src/GToolkit-Coder-Extensions/GtLocalVersion.class.st b/src/GToolkit-Coder-Extensions/GtLocalVersion.class.st new file mode 100644 index 000000000..dfbf5017d --- /dev/null +++ b/src/GToolkit-Coder-Extensions/GtLocalVersion.class.st @@ -0,0 +1,119 @@ +Class { + #name : #GtLocalVersion, + #superclass : #Object, + #instVars : [ + 'previousChangeRecord', + 'currentMethod', + 'changeRecord' + ], + #category : #'GToolkit-Coder-Extensions' +} + +{ #category : #'instance creation' } +GtLocalVersion class >> change: aChangeRecord previousChange: aPreviousChangeRecord currentMethod: aMethod [ + ^ self new + initializeWithChange: aChangeRecord + previousChange: aPreviousChangeRecord + currentMethod: aMethod +] + +{ #category : #accessing } +GtLocalVersion >> currentSourceCodeInImage [ + ^ currentMethod + ifNil: [ '' ] + ifNotNil: [ :aMethod | + aMethod sourceCode ] +] + +{ #category : #'gt - extensions' } +GtLocalVersion >> gtActionLoadVersionFor: anAction [ + + + ^ changeRecord gtActionLoadVersionFor: anAction +] + +{ #category : #'gt - extensions' } +GtLocalVersion >> gtViewDiffWithInImageCodeFor: aView [ + + + ^ aView explicit + title: 'Diff (current)'; + priority: 10; + stencil: [ + | change styler | + + change := GtDiffBuilder + computeDifferencesFrom: self sourceCode + to: self currentSourceCodeInImage + using: GtSmaCCDiffSplitter forPharo. + + styler := GtCodeDiffRBTextStyler new. + + GtDiffElementWithLabelStencil new + fromLabelText: 'Version'; + toLabelText: 'Image'; + change: change; + styler: styler ] +] + +{ #category : #'gt - extensions' } +GtLocalVersion >> gtViewDiffWithPreviousFor: aView [ + + + ^ aView explicit + title: 'Diff'; + priority: 5; + stencil: [ + | change styler | + + change := GtDiffBuilder + computeDifferencesFrom: self previousSourceCode + to: self sourceCode + using: GtSmaCCDiffSplitter forPharo. + + styler := GtCodeDiffRBTextStyler new. + + GtDiffElementWithLabelStencil new + fromLabelText: 'Previous'; + toLabelText: 'Changed'; + change: change; + styler: styler ] +] + +{ #category : #initialization } +GtLocalVersion >> initializeWithChange: aChangeRecord previousChange: aPreviousChangeRecord currentMethod: aMethod [ + changeRecord := aChangeRecord. + previousChangeRecord := aPreviousChangeRecord. + currentMethod := aMethod. +] + +{ #category : #actions } +GtLocalVersion >> loadChange [ + ^ changeRecord asMCMethodDefinition load +] + +{ #category : #accessing } +GtLocalVersion >> previousSourceCode [ + ^ previousChangeRecord + ifNil: [ '' ] + ifNotNil: [ :aChangeRecord | + aChangeRecord sourceCode ] +] + +{ #category : #accessing } +GtLocalVersion >> printOn: aStream [ + super printOn: aStream . + + aStream parenthesize: [ + changeRecord printOn: aStream ] +] + +{ #category : #accessing } +GtLocalVersion >> sourceCode [ + ^ changeRecord sourceCode +] + +{ #category : #accessing } +GtLocalVersion >> timeStamp [ + ^ changeRecord timeStamp +] diff --git a/src/GToolkit-Coder-Extensions/GtLocalVersionElement.class.st b/src/GToolkit-Coder-Extensions/GtLocalVersionElement.class.st new file mode 100644 index 000000000..ae601ddec --- /dev/null +++ b/src/GToolkit-Coder-Extensions/GtLocalVersionElement.class.st @@ -0,0 +1,133 @@ +Class { + #name : #GtLocalVersionElement, + #superclass : #BrVerticalFlow, + #instVars : [ + 'localVersion', + 'expander', + 'header', + 'content' + ], + #category : #'GToolkit-Coder-Extensions' +} + +{ #category : #accessing } +GtLocalVersionElement >> content [ + + ^ content +] + +{ #category : #accessing } +GtLocalVersionElement >> content: anObject [ + + content := anObject +] + +{ #category : #accessing } +GtLocalVersionElement >> expander [ + + ^ expander +] + +{ #category : #accessing } +GtLocalVersionElement >> expander: anObject [ + + expander := anObject +] + +{ #category : #accessing } +GtLocalVersionElement >> header [ + + ^ header +] + +{ #category : #accessing } +GtLocalVersionElement >> header: anObject [ + + header := anObject +] + +{ #category : #accessing } +GtLocalVersionElement >> initialize [ + super initialize. + + self hMatchParent vFitContent padding: (BlInsets all: 2). + + self initializeHeader. + self initializeContent. + self initializeExpander. + + self addChild: self expander +] + +{ #category : #accessing } +GtLocalVersionElement >> initializeContent [ + content := BrVerticalFlow new matchParent +] + +{ #category : #accessing } +GtLocalVersionElement >> initializeExpander [ + expander := BrExpander new hMatchParent vFitContent + aptitude: GtCoderExpanderAptitude new; + header: [ self header ]; + content: [ self content ] +] + +{ #category : #accessing } +GtLocalVersionElement >> initializeHeader [ + header := BrHorizontalFlow new matchParent +] + +{ #category : #accessing } +GtLocalVersionElement >> localVersion: aLocalVersion [ + localVersion := aLocalVersion. + self updateView +] + +{ #category : #accessing } +GtLocalVersionElement >> updateContent [ + | currentContent previousContent change styler | + previousContent := localVersion previousSourceCode. + currentContent := localVersion sourceCode. + + content removeChildren. + + change := GtDiffBuilder + computeDifferencesFrom: previousContent + to: currentContent + using: GtSmaCCDiffSplitter forPharo. + styler := GtCodeDiffRBTextStyler new. + + content + addChild: (BrLabel new + aptitude: BrGlamorousLabelAptitude; + text: 'Version at ' , localVersion timeStamp printToSeconds); + addChild: (GtDiffElementWithLabelStencil new + fromLabelText: 'Previous'; + toLabelText: 'Changed'; + change: change; + styler: styler) create; + addChild: (BrFrame new + fitContent; + padding: (BlInsets left: 5); + addChild: (BrButton new + icon: BrGlamorousVectorIcons accept; + id:BlLocalVersionAcceptVersionButtonId; + label: 'Use this version'; + aptitude: BrGlamorousButtonWithIconAptitude; + action: [ :aButton | localVersion loadChange ])) +] + +{ #category : #accessing } +GtLocalVersionElement >> updateHeader [ + header removeChildren. + header + addChild: (BrLabel new + aptitude: BrGlamorousLabelAptitude; + text: 'Version at ' , localVersion timeStamp printToSeconds) +] + +{ #category : #accessing } +GtLocalVersionElement >> updateView [ + self updateHeader. + self updateContent. +] diff --git a/src/GToolkit-Coder-Extensions/GtSearchFilter.extension.st b/src/GToolkit-Coder-Extensions/GtSearchFilter.extension.st deleted file mode 100644 index b4b5ba62e..000000000 --- a/src/GToolkit-Coder-Extensions/GtSearchFilter.extension.st +++ /dev/null @@ -1,58 +0,0 @@ -Extension { #name : #GtSearchFilter } - -{ #category : #'*GToolkit-Coder-Extensions' } -GtSearchFilter class >> gtHierarchyFor: aView [ - - - ^ aView mondrian - title: 'Hierarchy'; - priority: 10; - painting: [ :m | - m nodes - stencil: [ :eachClass | - | anExpander text | - text := (eachClass = GtSearchFilter - ifTrue: ['Filter'] - ifFalse: [ - (eachClass name gtRemovePrefix: 'GtSearch') - gtRemoveSuffix: 'Filter' ]) - asRopedText - glamorousRegularFont; - fontSize: 18. - anExpander := BrExpander new. - anExpander vFitContent; hExact: 300. - anExpander aptitude: GtCoderExpanderAptitude new. - anExpander header: [ BlTextElement new - padding: (BlInsets left: 5 right: 5); - text: text ]. - anExpander content: [ - | container methods | - container := BlElement new - layout: BlLinearLayout vertical; - constraintsDo: [ :c | c vertical fitContent. c horizontal fitContent]. - container addChild: (BlTextElement new - padding: (BlInsets left: 5 right: 5); - text: text). - methods := eachClass = GtSearchFilter - ifTrue: [ (GtSearchFilter methods select: #isOverridden) ] - ifFalse: [ eachClass methods select: [:method | - method gtIsOverridingUpTo: GtSearchFilter ] ]. - methods do: [:aMethod | - container addChild: ((GtPharoMethodCoder forMethod: aMethod) asElement)]. - container ]. - anExpander margin: (BlInsets all: 10). - ]; - with: self withAllSubclasses. - m edges - stencil: [ :each | - BlLineElement new - zIndex: -1; - border: (BlBorder paint: (Color gray alpha: 0.5) width: 1); - fromHead: (BlArrowheadTriangle new scale: 4; background: Color white; - border: (BlBorder paint: (Color gray alpha: 0.5) width: 1))]; - fromRightCenter; - toLeftCenter; - connectFrom: #superclass. - m layout explicit: (GtGraphHorizontalTreeLayout new layered horizontalGap: 20; verticalGap: 10). - ] -] diff --git a/src/GToolkit-Coder-Extensions/GtSourceCoderShortcut.extension.st b/src/GToolkit-Coder-Extensions/GtSourceCoderShortcut.extension.st index 3c70a865f..48bfcc954 100644 --- a/src/GToolkit-Coder-Extensions/GtSourceCoderShortcut.extension.st +++ b/src/GToolkit-Coder-Extensions/GtSourceCoderShortcut.extension.st @@ -1,11 +1,13 @@ Extension { #name : #GtSourceCoderShortcut } { #category : #'*GToolkit-Coder-Extensions' } -GtSourceCoderShortcut >> gtSourceCodeFor: aView [ +GtSourceCoderShortcut >> gtSourceCodeFor: aView [ - ^ aView forward title: 'Source'; - object: [ self class >> #performOnEditor:element:coder:dueTo: ]; + object: [ + (self class includesSelector: #performOnEditor:element:coderViewModel:dueTo:) + ifTrue: [ self class >> #performOnEditor:element:coderViewModel:dueTo: ] + ifFalse: [ self class >> #performOnEditor:element:coder:dueTo:requesterObject: ] ]; view: #gtSourceFor: ] diff --git a/src/GToolkit-Coder-Extensions/Object.extension.st b/src/GToolkit-Coder-Extensions/Object.extension.st new file mode 100644 index 000000000..71e1a4c3f --- /dev/null +++ b/src/GToolkit-Coder-Extensions/Object.extension.st @@ -0,0 +1,21 @@ +Extension { #name : #Object } + +{ #category : #'*GToolkit-Coder-Extensions' } +Object >> gtBrowse [ + ^ BlSpace new + withSceneDriller; + inPager: [ GtObjectCoderTool forObject: self ]; + title: self gtDisplayString; + icon: BrGlamorousVectorIcons browse; + show +] + +{ #category : #'*GToolkit-Coder-Extensions' } +Object >> gtBrowseFrom: anElement [ + ^ BlSpace new + withSceneDriller; + inPager: [ GtObjectCoderTool forObject: self ]; + title: self gtDisplayString; + icon: BrGlamorousVectorIcons browse; + showFrom: anElement +] diff --git a/src/GToolkit-Coder-Extensions/Package.extension.st b/src/GToolkit-Coder-Extensions/Package.extension.st new file mode 100644 index 000000000..96e0f8953 --- /dev/null +++ b/src/GToolkit-Coder-Extensions/Package.extension.st @@ -0,0 +1,108 @@ +Extension { #name : #Package } + +{ #category : #'*GToolkit-Coder-Extensions' } +Package >> gtAllowedDependenciesFor: aView [ + + (self packageManifestOrNil + ifNil: [ false ] + ifNotNil: [ :aPackage | + aPackage class canPerform: #mustOnlyDependOn ]) ifFalse: [ ^ aView empty ] . + + ^ aView list + title: 'Allowed Dependencies'; + priority: 50; + items: [ + self packageManifestOrNil + ifNil: [ #() ] + ifNotNil: [ :aPackage | + (aPackage class canPerform: #mustOnlyDependOn) + ifTrue: [ aPackage mustOnlyDependOn collect: #asPackage ] + ifFalse: [ #() ] ] ]; + itemText: [ :aPackage | aPackage name ] +] + +{ #category : #'*GToolkit-Coder-Extensions' } +Package >> gtBaselinesFor: aView [ + + + ^ aView forward + title: 'References'; + tooltip: 'References to the package name'; + priority: 30; + object: [ self name gtStringLiteralCaseSensitiveMatch | self name gtReferences ]; + view: #gtItemsFor: +] + +{ #category : #'*GToolkit-Coder-Extensions' } +Package >> gtBrowse [ + ^ BlSpace new + withSceneDriller; + inPager: [ GtCoderElement forPackage: self ]; + title: self packageName; + icon: BrGlamorousVectorIcons browse; + show +] + +{ #category : #'*GToolkit-Coder-Extensions' } +Package >> gtBrowseFrom: anElement [ + ^ BlSpace new + withCommonSettings; + inPager: [ self gtDefaultInspectorTool ]; + title: self packageName; + icon: BrGlamorousVectorIcons browse; + showFrom: anElement +] + +{ #category : #'*GToolkit-Coder-Extensions' } +Package >> gtDefinedClassesFor: aView context: aPhlowContext [ + + (aPhlowContext isKindOf: GtPhlowExecutionContext) ifFalse: [ ^ aView empty ]. + aPhlowContext hasPackageCoder ifFalse: [ ^ aView empty ]. + ^ aView explicit + priority: 1; + title: 'Classes'; + disableAsync; + stencil: [ aPhlowContext packageCoder classesCoder asElement ] +] + +{ #category : #'*GToolkit-Coder-Extensions' } +Package >> gtDefinedTagsFor: aView context: aPhlowContext [ + + (aPhlowContext isKindOf: GtPhlowExecutionContext) ifFalse: [ ^ aView empty ]. + aPhlowContext hasPackageCoder ifFalse: [ ^ aView empty ]. + ^ aView explicit + priority: 10.5; + title: 'Tags'; + disableAsync; + stencil: [ + BrSimpleList new + itemStencil: [ GtPackageTagCardElement new ]; + itemDataBinder: [ :eachTagCard :eachTagCoder | + eachTagCard coder: eachTagCoder ]; + items: (aPhlowContext packageCoder packageTagsCoder coders + asSortedCollection: [ :a :b | a packageTagName < b packageTagName ]); + addEventFilterOn: BlClickEvent do: [ :anEvent | anEvent currentTarget requestFocus ] ] +] + +{ #category : #'*GToolkit-Coder-Extensions' } +Package >> gtPackageCommentViewFor: aView context: aPhlowContext [ + + (aPhlowContext isKindOf: GtPhlowExecutionContext) ifFalse: [ ^ aView empty ]. + aPhlowContext hasPackageCoder ifFalse: [ ^ aView empty ]. + + ^ aView textEditor + priority: 5; + title: 'Comment'; + text: [ aPhlowContext packageCoder package packageComment ]; + actionButtonIcon: BrGlamorousVectorIcons accept + tooltip: 'Save package comment' + action: [ :aButton | + aButton phlow firstParentWithViewContent + ifNotNil: [ :anElement | + anElement phlow firstChildWithViewContent + ifNotNil: [ :aChild | + aChild + childWithId: #editor + ifFound: [ :anEditor | aPhlowContext packageCoder package packageComment: anEditor text asString ] + ifNone: [ ] ] ] ] +] diff --git a/src/GToolkit-Coder-Extensions/PackageTag.extension.st b/src/GToolkit-Coder-Extensions/PackageTag.extension.st new file mode 100644 index 000000000..f7b98bc02 --- /dev/null +++ b/src/GToolkit-Coder-Extensions/PackageTag.extension.st @@ -0,0 +1,11 @@ +Extension { #name : #PackageTag } + +{ #category : #'*GToolkit-Coder-Extensions' } +PackageTag >> gtBrowseFrom: anElement [ + ^ BlSpace new + withCommonSettings; + inPager: [ self gtDefaultInspectorTool ]; + title: self name; + icon: BrGlamorousVectorIcons browse; + showFrom: anElement +] diff --git a/src/GToolkit-Coder-Extensions/ProtoObject.extension.st b/src/GToolkit-Coder-Extensions/ProtoObject.extension.st index 71438cf91..a68be5d74 100644 --- a/src/GToolkit-Coder-Extensions/ProtoObject.extension.st +++ b/src/GToolkit-Coder-Extensions/ProtoObject.extension.st @@ -2,12 +2,18 @@ Extension { #name : #ProtoObject } { #category : #'*GToolkit-Coder-Extensions' } ProtoObject >> gtBrowse [ - ^ (GtCoder forObject: self) openInPager maximized + ^ BlSpace new + withSceneDriller; + inPager: [ GtObjectCoderTool forObject: self ]; + icon: BrGlamorousVectorIcons browse; + show ] { #category : #'*GToolkit-Coder-Extensions' } ProtoObject >> gtBrowseFrom: anElement [ - ^ ((GtCoder forObject: self) - openInPagerFrom: anElement) - maximized + ^ BlSpace new + withCommonSettings; + inPager: [ GtObjectCoderTool forObject: self ]; + icon: BrGlamorousVectorIcons browse; + showFrom: anElement ] diff --git a/src/GToolkit-Coder-Extensions/RGMethodDefinition.extension.st b/src/GToolkit-Coder-Extensions/RGMethodDefinition.extension.st new file mode 100644 index 000000000..2cdad1093 --- /dev/null +++ b/src/GToolkit-Coder-Extensions/RGMethodDefinition.extension.st @@ -0,0 +1,15 @@ +Extension { #name : #RGMethodDefinition } + +{ #category : #'*GToolkit-Coder-Extensions' } +RGMethodDefinition >> gtLocalVersionsFor: aView [ + + + + self isDefined ifFalse: [ ^aView empty]. + + ^aView forward + title: 'Local versions'; + object: [ self compiledMethod ]; + priority: 10; + view: #gtLocalVersionsFor: +] diff --git a/src/GToolkit-Coder-Extensions/RPackage.extension.st b/src/GToolkit-Coder-Extensions/RPackage.extension.st deleted file mode 100644 index f1d0fd8bf..000000000 --- a/src/GToolkit-Coder-Extensions/RPackage.extension.st +++ /dev/null @@ -1,84 +0,0 @@ -Extension { #name : #RPackage } - -{ #category : #'*GToolkit-Coder-Extensions' } -RPackage >> gtBaselinesFor: aView [ - - ^ aView explicit - title: 'References'; - priority: 30; - stencil: [ self name gtSubstringLiteralCaseSensitiveMatch ] -] - -{ #category : #'*GToolkit-Coder-Extensions' } -RPackage >> gtBrowse [ - ^ ((GtCoder forPackage: self) - openInPager) maximized -] - -{ #category : #'*GToolkit-Coder-Extensions' } -RPackage >> gtBrowseFrom: anElement [ - ^ ((GtCoder forPackage: self) - openInPagerFrom: anElement) maximized -] - -{ #category : #'*GToolkit-Coder-Extensions' } -RPackage >> gtDefinedClassesFor: aView context: aPhlowContext [ - - (aPhlowContext isKindOf: GtPhlowExecutionContext) ifFalse: [ ^ aView empty ]. - aPhlowContext hasPackageCoder ifFalse: [ ^ aView empty ]. - ^ aView explicit - priority: 10; - title: 'Classes'; - disableAsync; - stencil: [ aPhlowContext packageCoder classesCoder asElement ] -] - -{ #category : #'*GToolkit-Coder-Extensions' } -RPackage >> gtDefinedTagsFor: aView context: aPhlowContext [ - - (aPhlowContext isKindOf: GtPhlowExecutionContext) ifFalse: [ ^ aView empty ]. - aPhlowContext hasPackageCoder ifFalse: [ ^ aView empty ]. - ^ aView explicit - priority: 10.5; - title: 'Tags'; - disableAsync; - stencil: [ - BrSimpleList new - itemStencil: [ GtPackageTagCardElement new ]; - itemDataBinder: [ :eachTagCard :eachTagCoder | - eachTagCard coder: eachTagCoder ]; - items: (aPhlowContext packageCoder packageTagsCoder coders - asSortedCollection: [ :a :b | a packageTagName < b packageTagName ]); - addEventFilterOn: BlClickEvent do: [ :anEvent | anEvent currentTarget requestFocus ] ] -] - -{ #category : #'*GToolkit-Coder-Extensions' } -RPackage >> gtDependenciesMapFor: aView [ - - ^ aView mondrian - title: 'Dependencies Map'; - priority: 10; - painting: [ :mondrian | - mondrian nodes - shape: [ :each | - BrLabel new - text: each name asRopedText; - aptitude: BrGlamorousLabelAptitude - ]; - with: (self gtPackagesUsed, {self}). - mondrian edges - shape: [ - BlParabollaArcElement new - zIndex: 0; - curvatureFraction: 0.1; - border: (BlBorder paint: (Color gray alpha: 0.1) width:2 ); - toHead: (BlArrowheadSimpleArrow new - border: (BlBorder builder paint: (Color gray alpha: 0.1); width:2; build)) ]; - fromRightCenter; - toLeftCenter; - connectFrom: #yourself - toAll: [ :each | |usedPackages | - usedPackages := each gtPackagesUsed]. - mondrian layout custom: (GtGraphHorizontalDominanceTreeLayout new). - ] -] diff --git a/src/GToolkit-Coder-Pharo-Bubbles/Behavior.extension.st b/src/GToolkit-Coder-Pharo-Bubbles/Behavior.extension.st deleted file mode 100644 index ee197a358..000000000 --- a/src/GToolkit-Coder-Pharo-Bubbles/Behavior.extension.st +++ /dev/null @@ -1,29 +0,0 @@ -Extension { #name : #Behavior } - -{ #category : #'*GToolkit-Coder-Pharo-Bubbles' } -Behavior >> gtCoderProtocolsBubblesFor: aView context: aPhlowContext [ - - | aBehaviorCoderViewModel | - - aBehaviorCoderViewModel := aPhlowContext - optionAt: #behaviorCoder - ifPresent: [ :aBehaviorCoderUIModel | aBehaviorCoderUIModel ] - ifAbsentPut: [ (GtBehaviorCoder forClass: self) asCoderUIModel ]. - - ^ aView explicit - priority: 11; - title: 'Protocols'; - disableAsync; - stencil: [ GtPharoBehaviorBubblesElement new - pharoBehaviorCoderViewModel: aBehaviorCoderViewModel ]; - actionButtonIcon: BrGlamorousIcons zoomin - tooltip: 'Zoom in' - action: [ :aButton :aTab | aTab viewContentElement zoomIn ]; - actionButtonIcon: BrGlamorousIcons zoomout - tooltip: 'Zoom out' - action: [ :aButton :aTab | aTab viewContentElement zoomOut ]; - actionButtonIcon: BrGlamorousIcons zoomtofit - tooltip: 'Zoom to fit' - action: [ :aButton :aTab | aTab viewContentElement zoomToFit ]; - actionUpdateButton -] diff --git a/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorBubblesElement.class.st b/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorBubblesElement.class.st deleted file mode 100644 index 098502f17..000000000 --- a/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorBubblesElement.class.st +++ /dev/null @@ -1,221 +0,0 @@ -Class { - #name : #GtPharoBehaviorBubblesElement, - #superclass : #BlElement, - #traits : 'TGtWithPharoBehaviorCoderViewModel', - #classTraits : 'TGtWithPharoBehaviorCoderViewModel classTrait', - #instVars : [ - 'canvas', - 'zoomToProtocolHandler' - ], - #category : #'GToolkit-Coder-Pharo-Bubbles' -} - -{ #category : #private } -GtPharoBehaviorBubblesElement >> canvasBoundingBox [ - - | aBoundingBox | - - canvas hasChildren - ifFalse: [ ^ BlBounds new asRectangle ]. - - aBoundingBox := nil. - - canvas childrenDo: [ :aChild | - aBoundingBox - ifNil: [ aBoundingBox := aChild bounds inParent bounds ] - ifNotNil: [ aBoundingBox merge: aChild bounds inParent bounds ] ]. - - ^ aBoundingBox expanded asRectangle -] - -{ #category : #'hooks - layout' } -GtPharoBehaviorBubblesElement >> dispatchLayoutDone [ - super dispatchLayoutDone. - - (canvas layout isKindOf: BlBasicLayout) - ifTrue: [ ^ self ]. - - canvas layout: BlBasicLayout new. - - self zoomToFit -] - -{ #category : #initialization } -GtPharoBehaviorBubblesElement >> initialize [ - super initialize. - - self constraintsDo: [ :c | - c horizontal matchParent. - c vertical matchParent ]. - - self beInSeparateCompositionLayer. - self background: BrGlamorousColors backdropColor. - self addLook: BrGlamorousMaximizerFullscreenHostAptitude new. - - canvas := BlCanvassableElement new - constraintsDo: [ :c | - c horizontal matchParent. - c vertical matchParent ]. - canvas addEventHandler: BlSlideHandler new. - canvas addEventHandler: BlScrollSlideHandler new. - canvas layout: (BlOnceLayout on: (GtGraphGridLayout new gapSize: 30)). - - canvas - when: BlCanvassableZoomLevelChanged - do: [ :anEvent | self updateForZoomLevel: anEvent currentTarget zoomLevel ]. - - zoomToProtocolHandler := BlEventHandler - on: BlClickEvent - do: [ :anEvent | - anEvent consumed: true. - self zoomInTo: anEvent currentTarget parent ]. - - self addChild: canvas -] - -{ #category : #'api - pharo behavior coder view model' } -GtPharoBehaviorBubblesElement >> onPharoBehaviorCoderViewModelChanged [ - "Is sent when a new pharoBehaviorCoder view model is assigned to the element. - Note: #onPharoBehaviorCoderViewModelChanged is sent before #subscribeToPharoBehaviorCoderViewModel - which means that if you perform any operation that triggers an announcement it will be ignored because the receiver - didn't get a chance to subscribe to any announcement. Override #onPostPharoBehaviorCoderViewModelChanged if you - wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" - | theProtocols | - - theProtocols := self pharoBehaviorCoderViewModel protocols sorted. - - canvas removeChildren. - canvas addChildren: (theProtocols collect: [ :eachProtocol | - | eachBehaviorProtocolCoder eachBehaviorProtocolCoderViewModel | - eachBehaviorProtocolCoder := GtPharoBehaviorProtocolCoder - forBehavior: self pharoBehaviorCoderViewModel behavior - protocol: eachProtocol. - - eachBehaviorProtocolCoderViewModel := GtPharoBehaviorProtocolCoderViewModel new - coderModel: eachBehaviorProtocolCoder. - - GtPharoBehaviorProtocolBubbleElement new - pharoBehaviorProtocolCoderViewModel: eachBehaviorProtocolCoderViewModel; - addEventHandler: BlPullHandler new ]) -] - -{ #category : #private } -GtPharoBehaviorBubblesElement >> startZoomAnimation: anAnimation [ - anAnimation onFinishedDo: [ - (self query // BlElement) all - do: [ :eachCard | eachCard invalidate ] ]. - - self addAnimation: anAnimation -] - -{ #category : #private } -GtPharoBehaviorBubblesElement >> updateForZoomLevel: aZoomLevel [ - (self query // GtPharoBehaviorProtocolBubbleElement) all - do: [ :eachCard | - eachCard childrenDo: [ :eachChild | - aZoomLevel > 0.5 - ifTrue: [ eachChild visibility: BlVisibility visible ] - ifFalse: [ eachChild visibility: BlVisibility hidden ]. - aZoomLevel > 0.5 - ifTrue: [ - eachCard - childWithId: #protocolLabelPreview - ifFound: [ :anElement | - anElement removeFromParent. - anElement removeEventHandler: zoomToProtocolHandler ] - ifNone: [ ] ] - ifFalse: [ - eachCard - childWithId: #protocolLabelPreview - ifFound: [ :anElement | anElement visibility: BlVisibility visible ] - ifNone: [ - eachCard addChild: ((BrLabel new - padding: (BlInsets all: 2); - look: BrGlamorousLabelLook; - text: eachCard pharoBehaviorProtocolCoderViewModel protocol) asScalableElement - id: #protocolLabelPreview; - fitAll; - addEventHandler: zoomToProtocolHandler; - constraintsDo: [ :c | - c ignoreByLayout ]) ] ] ] ] -] - -{ #category : #'api - zooming' } -GtPharoBehaviorBubblesElement >> zoomIn [ - self startZoomAnimation: (BlNumberTransition new - onStepDo: [ :eachLevel | canvas zoomLevel: eachLevel ]; - from: canvas zoomLevel; - to: canvas zoomLevel + canvas zoomStep; - duration: 200 milliSeconds) -] - -{ #category : #'api - zooming' } -GtPharoBehaviorBubblesElement >> zoomInTo: anElement [ - | anElementBounds anElementExtent aNewZoomLevel aMoveDelta zoomAnimation relocateAnimations | - - anElementBounds := anElement bounds inParent: self. - anElementExtent := anElementBounds extent. - (anElementExtent x isZero or: [ anElementExtent y isZero ]) - ifTrue: [ ^ self ]. - - aNewZoomLevel := 1. - - aMoveDelta := (canvas localPointToChildren: canvas bounds inLocal center) - (canvas localPointToChildren: anElementBounds center). - - zoomAnimation := BlNumberTransition new - target: canvas; - onStepDo: [ :eachLevel :aCanvas | aCanvas zoomLevel: eachLevel ]; - from: canvas zoomLevel; - to: aNewZoomLevel; - duration: 500 milliSeconds. - - relocateAnimations := canvas children accountedByLayout collect: [ :eachElement | - BlNumberTransition new - target: eachElement; - onStepDo: [ :eachPoint :aTarget | aTarget relocate: eachPoint ]; - from: eachElement constraints position; - to: eachElement constraints position + aMoveDelta; - duration: 200 milliSeconds ]. - - self startZoomAnimation: (BlSequentialAnimation with: { BlParallelAnimation with: relocateAnimations . zoomAnimation }). -] - -{ #category : #'api - zooming' } -GtPharoBehaviorBubblesElement >> zoomOut [ - self startZoomAnimation: (BlNumberTransition new - onStepDo: [ :eachLevel | canvas zoomLevel: eachLevel ]; - from: canvas zoomLevel; - to: canvas zoomLevel - canvas zoomStep; - duration: 200 milliSeconds) -] - -{ #category : #'api - zooming' } -GtPharoBehaviorBubblesElement >> zoomToFit [ - | theChildrenBoundingBox theChildrenExtent aNewZoomLevel aMoveDelta zoomAnimation relocateAnimations | - - theChildrenBoundingBox := self canvasBoundingBox. - theChildrenExtent := theChildrenBoundingBox extent. - (theChildrenExtent x isZero or: [ theChildrenExtent y isZero ]) - ifTrue: [ ^ self ]. - - aNewZoomLevel := ((canvas extent / theChildrenExtent) min - 0.05) min: 1. - - aMoveDelta := (canvas localPointToChildren: canvas bounds inLocal center) - (theChildrenBoundingBox center). - - zoomAnimation := BlNumberTransition new - target: canvas; - onStepDo: [ :eachLevel :aCanvas | aCanvas zoomLevel: eachLevel ]; - from: canvas zoomLevel; - to: aNewZoomLevel; - duration: 500 milliSeconds. - - relocateAnimations := canvas children accountedByLayout collect: [ :eachElement | - BlNumberTransition new - target: eachElement; - onStepDo: [ :eachPoint :aTarget | aTarget relocate: eachPoint ]; - from: eachElement constraints position; - to: eachElement constraints position + aMoveDelta; - duration: 200 milliSeconds ]. - - self startZoomAnimation: (BlSequentialAnimation with: { zoomAnimation . BlParallelAnimation with: relocateAnimations }). -] diff --git a/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorProtocolBubbleElement.class.st b/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorProtocolBubbleElement.class.st deleted file mode 100644 index 83d0c65ad..000000000 --- a/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorProtocolBubbleElement.class.st +++ /dev/null @@ -1,130 +0,0 @@ -Class { - #name : #GtPharoBehaviorProtocolBubbleElement, - #superclass : #BrHorizontalPane, - #traits : 'TGtWithPharoBehaviorProtocolCoderViewModel', - #classTraits : 'TGtWithPharoBehaviorProtocolCoderViewModel classTrait', - #instVars : [ - 'protocolLabel', - 'methodsContainer' - ], - #category : #'GToolkit-Coder-Pharo-Bubbles' -} - -{ #category : #initialization } -GtPharoBehaviorProtocolBubbleElement >> createMethodsContainer [ - ^ BrVerticalPane new - hExact: 200; - vFitContent; - padding: (BlInsets all: 4) -] - -{ #category : #initialization } -GtPharoBehaviorProtocolBubbleElement >> createProtocolLabel [ - ^ BrLabel new - beLargeSize; - look: BrGlamorousLabelLook -] - -{ #category : #'private - instance creation' } -GtPharoBehaviorProtocolBubbleElement >> createProtocolMethodRowFor: aPharoMethodsCoderViewModel index: anIndex [ - | tooltipLook | - - tooltipLook := BrGlamorousWithTooltipLook - content: [ - aPharoMethodsCoderViewModel expanded: true. - GtSourceCoderEditorElement new - in: [ :anEditorElement | anEditorElement editor beParagraphBased ]; - textualCoderViewModel: aPharoMethodsCoderViewModel; - vFitContent; - hExact: 400 ]. - tooltipLook - showDelay: 0 seconds; - hideDelay: 0 seconds; - attachToLeftAndRight; - onPrimaryHover. - - ^ BrLabel new - hMatchParent; - id: (GtPharoBehaviorProtocolBubbleMethodHeaderElementId indexed: anIndex); - text: aPharoMethodsCoderViewModel selector; - look: BrGlamorousLabelLook + tooltipLook + (BrStyleCommonLook new - hovered: [ :s | s background: (Color gray alpha: 0.2) ]); - when: BlClickEvent do: [ :anEvent | - | aContainer aCoderElement | - anEvent consumed: true. - - aContainer := self. - aContainer removeChildNamed: #'bubble--editor'. - - aPharoMethodsCoderViewModel expanded: true. - aCoderElement := aPharoMethodsCoderViewModel asElement. - aCoderElement hExact: 500. - aCoderElement vFitContent. - - aContainer addChild: (aCoderElement id: #'bubble--editor'). - self requestStyle ] -] - -{ #category : #initialization } -GtPharoBehaviorProtocolBubbleElement >> initialize [ - super initialize. - - self - background: (Color white); - addLook: BrShadowLook new beLarge; - addLook: BrGlamorousWithMaximizerAptitude new; - geometry: (BlRoundedRectangleGeometry cornerRadius: 5); - beInSeparateCompositionLayer; - fitContent. - - protocolLabel := self createProtocolLabel. - protocolLabel constraintsDo: [ :c | c linear horizontal alignCenter ]. - methodsContainer := self createMethodsContainer. - - methodsContainer addChild: protocolLabel. - - self addChild: methodsContainer. - - self addLook: (BrLayoutResizerLook new - vFitContentToFitContent: [ :aBlock | - self - childWithId: #'bubble--editor' - ifFound: aBlock - ifNone: [ ] ]; - hFitContent: [ :aBlock | - self - childWithId: #'bubble--editor' - ifFound: aBlock - ifNone: [ ] ] - toExact: 500; - exactToMatchParent: [ :aBlock | - self - childWithId: #'bubble--editor' - ifFound: aBlock - ifNone: [ ] ]; - matchParentToMatchParent: [ :aBlock | - self - childWithId: #'bubble--editor' - ifFound: aBlock - ifNone: [ ] ]) -] - -{ #category : #'api - pharo behavior protocol coder view model' } -GtPharoBehaviorProtocolBubbleElement >> onPharoBehaviorProtocolCoderViewModelChanged [ - "Is sent when a new pharoBehaviorProtocolCoder view model is assigned to the element. - Note: #onPharoBehaviorProtocolCoderViewModelChanged is sent before #subscribeToPharoBehaviorProtocolCoderViewModel - which means that if you perform any operation that triggers an announcement it will be ignored because the receiver - didn't get a chance to subscribe to any announcement. Override #onPostPharoBehaviorProtocolCoderViewModelChanged if you - wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" - - protocolLabel text: self pharoBehaviorProtocolCoderViewModel protocol. - - methodsContainer removeChildren: (methodsContainer query // GtPharoBehaviorProtocolBubbleMethodHeaderElementId) all. - methodsContainer addChildren: (self pharoBehaviorProtocolCoderViewModel methodViewModels - collectWithIndex: [ :eachMethodViewModel :anIndex | self createProtocolMethodRowFor: eachMethodViewModel index: anIndex ]). - - ((self pharoBehaviorProtocolCoderViewModel protocol beginsWith: 'private') - or: [ (self pharoBehaviorProtocolCoderViewModel protocol beginsWith: '*') ]) - ifTrue: [ self background: BrGlamorousColors backdropColor ] - ifFalse: [ self background: Color white ] -] diff --git a/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorProtocolBubbleMethodHeaderElementId.class.st b/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorProtocolBubbleMethodHeaderElementId.class.st deleted file mode 100644 index a95c1fc83..000000000 --- a/src/GToolkit-Coder-Pharo-Bubbles/GtPharoBehaviorProtocolBubbleMethodHeaderElementId.class.st +++ /dev/null @@ -1,13 +0,0 @@ -" -A method header in the list of methods in the protocol bubble -" -Class { - #name : #GtPharoBehaviorProtocolBubbleMethodHeaderElementId, - #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-Pharo-Bubbles' -} - -{ #category : #converting } -GtPharoBehaviorProtocolBubbleMethodHeaderElementId >> asSymbol [ - ^ #'behavior-protocol-bubble--method-header' -] diff --git a/src/GToolkit-Coder-Pharo-Bubbles/package.st b/src/GToolkit-Coder-Pharo-Bubbles/package.st deleted file mode 100644 index f8fa9d7f8..000000000 --- a/src/GToolkit-Coder-Pharo-Bubbles/package.st +++ /dev/null @@ -1 +0,0 @@ -Package { #name : #'GToolkit-Coder-Pharo-Bubbles' } diff --git a/src/GToolkit-Coder/RBArrayNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBArrayNode.extension.st similarity index 83% rename from src/GToolkit-Coder/RBArrayNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBArrayNode.extension.st index 8cf8c00b2..89799b2fa 100644 --- a/src/GToolkit-Coder/RBArrayNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBArrayNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBArrayNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBArrayNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. periods diff --git a/src/GToolkit-Coder/RBAssignmentNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBAssignmentNode.extension.st similarity index 76% rename from src/GToolkit-Coder/RBAssignmentNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBAssignmentNode.extension.st index 932ce03eb..ed0f89c9c 100644 --- a/src/GToolkit-Coder/RBAssignmentNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBAssignmentNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBAssignmentNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBAssignmentNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. assignment ifNotNil: [ assignment := assignment + anInteger ] diff --git a/src/GToolkit-Coder/RBBlockNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBBlockNode.extension.st similarity index 85% rename from src/GToolkit-Coder/RBBlockNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBBlockNode.extension.st index 9d93e4028..226d9fbb4 100644 --- a/src/GToolkit-Coder/RBBlockNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBBlockNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBBlockNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBBlockNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. colons ifNotNil: [ colons := colons collect: [ :f | f + anInteger ] ]. diff --git a/src/GToolkit-Coder/RBCascadeNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBCascadeNode.extension.st similarity index 83% rename from src/GToolkit-Coder/RBCascadeNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBCascadeNode.extension.st index acb9718b5..dbc45527b 100644 --- a/src/GToolkit-Coder/RBCascadeNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBCascadeNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBCascadeNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBCascadeNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. (messages size - 1) timesRepeat: [ self receiver gtMoveBy: 0 - anInteger ]. diff --git a/src/GToolkit-Coder/RBLiteralNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBLiteralNode.extension.st similarity index 79% rename from src/GToolkit-Coder/RBLiteralNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBLiteralNode.extension.st index 78ecb4692..8cd3aa22c 100644 --- a/src/GToolkit-Coder/RBLiteralNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBLiteralNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBLiteralNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBLiteralNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. start ifNotNil: [ start := start + anInteger ]. diff --git a/src/GToolkit-Coder/RBMessageNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBMessageNode.extension.st similarity index 80% rename from src/GToolkit-Coder/RBMessageNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBMessageNode.extension.st index 7dfab62f7..2948a29c7 100644 --- a/src/GToolkit-Coder/RBMessageNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBMessageNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBMessageNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBMessageNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. keywordsPositions diff --git a/src/GToolkit-Coder/RBMethodNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBMethodNode.extension.st similarity index 80% rename from src/GToolkit-Coder/RBMethodNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBMethodNode.extension.st index 74564e5e0..68255990c 100644 --- a/src/GToolkit-Coder/RBMethodNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBMethodNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBMethodNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBMethodNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. keywordsPositions diff --git a/src/GToolkit-Coder/RBParseErrorNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBParseErrorNode.extension.st similarity index 75% rename from src/GToolkit-Coder/RBParseErrorNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBParseErrorNode.extension.st index 89fc9c541..c48083e0e 100644 --- a/src/GToolkit-Coder/RBParseErrorNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBParseErrorNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBParseErrorNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBParseErrorNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. start ifNotNil: [ start := start + anInteger ] diff --git a/src/GToolkit-Coder/RBPragmaNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBPragmaNode.extension.st similarity index 85% rename from src/GToolkit-Coder/RBPragmaNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBPragmaNode.extension.st index 963884f68..6c07af9f6 100644 --- a/src/GToolkit-Coder/RBPragmaNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBPragmaNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBPragmaNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBPragmaNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. keywordsPositions diff --git a/src/GToolkit-Coder/RBProgramNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBProgramNode.extension.st similarity index 72% rename from src/GToolkit-Coder/RBProgramNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBProgramNode.extension.st index 62eb9d6ae..aade28bc9 100644 --- a/src/GToolkit-Coder/RBProgramNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBProgramNode.extension.st @@ -1,17 +1,17 @@ Extension { #name : #RBProgramNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBProgramNode >> gtMoveAllBy: anInteger [ self gtMoveBy: anInteger. self children do: [ :e | e gtMoveAllBy: anInteger ] ] -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBProgramNode >> gtMoveBy: anInteger [ self comments do: [ :f | f with: f contents at: f start + anInteger ] ] -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBProgramNode >> withAllParentsDo: aBlock [ | node | node := self. diff --git a/src/GToolkit-Coder/RBSequenceNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBSequenceNode.extension.st similarity index 84% rename from src/GToolkit-Coder/RBSequenceNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBSequenceNode.extension.st index 2e06f438e..9b2017b2b 100644 --- a/src/GToolkit-Coder/RBSequenceNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBSequenceNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBSequenceNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBSequenceNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. periods ifNotNil: [ periods := periods collect: [ :f | f + anInteger ] ]. diff --git a/src/GToolkit-Coder/RBValueNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBValueNode.extension.st similarity index 78% rename from src/GToolkit-Coder/RBValueNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBValueNode.extension.st index 3d8f74ea1..3edb5348d 100644 --- a/src/GToolkit-Coder/RBValueNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBValueNode.extension.st @@ -1,6 +1,6 @@ Extension { #name : #RBValueNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBValueNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. parentheses ifNotNil: [ parentheses := parentheses collect: [ :f | f + anInteger ] ]. diff --git a/src/GToolkit-Coder/RBVariableNode.extension.st b/src/GToolkit-Coder-Pharo12Extensions/RBVariableNode.extension.st similarity index 68% rename from src/GToolkit-Coder/RBVariableNode.extension.st rename to src/GToolkit-Coder-Pharo12Extensions/RBVariableNode.extension.st index aa4a6fd38..df4a4d3ea 100644 --- a/src/GToolkit-Coder/RBVariableNode.extension.st +++ b/src/GToolkit-Coder-Pharo12Extensions/RBVariableNode.extension.st @@ -1,11 +1,11 @@ Extension { #name : #RBVariableNode } -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBVariableNode >> gtIsLocal [ ^ (self whoDefines: self name) notNil ] -{ #category : #'*GToolkit-Coder' } +{ #category : #'*GToolkit-Coder-Pharo12Extensions' } RBVariableNode >> gtMoveBy: anInteger [ super gtMoveBy: anInteger. start ifNotNil: [ start := start + anInteger ] diff --git a/src/GToolkit-Coder-Pharo12Extensions/package.st b/src/GToolkit-Coder-Pharo12Extensions/package.st new file mode 100644 index 000000000..19410f66d --- /dev/null +++ b/src/GToolkit-Coder-Pharo12Extensions/package.st @@ -0,0 +1 @@ +Package { #name : #'GToolkit-Coder-Pharo12Extensions' } diff --git a/src/GToolkit-Coder-StreamingCoders-UI/GtFilteredCodersViewModel.class.st b/src/GToolkit-Coder-StreamingCoders-UI/GtFilteredCodersViewModel.class.st new file mode 100644 index 000000000..729b21078 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/GtFilteredCodersViewModel.class.st @@ -0,0 +1,86 @@ +Class { + #name : #GtFilteredCodersViewModel, + #superclass : #GtStreamingCodersViewModel, + #instVars : [ + 'highlighter' + ], + #category : #'GToolkit-Coder-StreamingCoders-UI-Coder' +} + +{ #category : #'api - accessing' } +GtFilteredCodersViewModel >> addOrReplaceFilter: aFilter [ + streamingCodersModel addOrReplaceFilter: aFilter +] + +{ #category : #'api - accessing' } +GtFilteredCodersViewModel >> addOrReplaceFilters: aCollectionOfFilters [ + streamingCodersModel addOrReplaceFilters: aCollectionOfFilters +] + +{ #category : #'api - accessing' } +GtFilteredCodersViewModel >> additionalFilters [ + + + ^ streamingCodersModel additionalFilters +] + +{ #category : #'api - accessing' } +GtFilteredCodersViewModel >> additionalFilters: aCollectionOfFilter [ + "Change the additional filters of the coders model" + + streamingCodersModel additionalFilters: aCollectionOfFilter +] + +{ #category : #'api - accessing' } +GtFilteredCodersViewModel >> compositeFilter [ + + + ^ streamingCodersModel compositeFilter +] + +{ #category : #'api - accessing' } +GtFilteredCodersViewModel >> highlighter [ + ^ highlighter +] + +{ #category : #'private - notifying' } +GtFilteredCodersViewModel >> notifyFilterChanged [ + self announce: GtFilteredCodersViewModelFilterChanged new +] + +{ #category : #'private - event handling' } +GtFilteredCodersViewModel >> onCodersStreamChanged: anAnnouncement [ + super onCodersStreamChanged: anAnnouncement. + + ((anAnnouncement isDueTo: GtFilteredCodersFilterReason) + or: [ anAnnouncement isDueTo: GtFilteredCodersAdditionalFiltersReason ]) + ifFalse: [ ^ self ]. + + self onFilterChanged +] + +{ #category : #'private - event handling' } +GtFilteredCodersViewModel >> onFilterChanged [ + self updateFiltersHighlighter. + self notifyFilterChanged +] + +{ #category : #'api - streaming coders model' } +GtFilteredCodersViewModel >> onStreamingCodersModelChanged [ + "Is sent when a new streamingCodersModel is assigned to the receiver. + Note: #onStreamingCodersModelChanged is sent before #subscribeToStreamingCodersModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostStreamingCodersModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + >#onObjectChangedTemplate'> + super onStreamingCodersModelChanged. + + self updateFiltersHighlighter +] + +{ #category : #'private - update' } +GtFilteredCodersViewModel >> updateFiltersHighlighter [ + highlighter := streamingCodersModel compositeFilter highlighter. + coderViewModels valuesDo: [ :eachCoderViewModel | + eachCoderViewModel highlighter: highlighter ] +] diff --git a/src/GToolkit-Coder-StreamingCoders-UI/GtFilteredCodersViewModelFilterChanged.class.st b/src/GToolkit-Coder-StreamingCoders-UI/GtFilteredCodersViewModelFilterChanged.class.st new file mode 100644 index 000000000..bb84d5138 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/GtFilteredCodersViewModelFilterChanged.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilteredCodersViewModelFilterChanged, + #superclass : #Announcement, + #category : #'GToolkit-Coder-StreamingCoders-UI-Events' +} diff --git a/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModel.class.st b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModel.class.st new file mode 100644 index 000000000..ee36fd875 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModel.class.st @@ -0,0 +1,181 @@ +Class { + #name : #GtStreamingCodersViewModel, + #superclass : #Object, + #traits : 'TGtWithStreamingCodersModel', + #classTraits : 'TGtWithStreamingCodersModel classTrait', + #instVars : [ + 'announcer', + 'coderViewModels', + 'coderViewModelsStream', + 'pendingCommands', + 'scrollTarget', + 'mutex' + ], + #category : #'GToolkit-Coder-StreamingCoders-UI-Coder' +} + +{ #category : #'api - announcer' } +GtStreamingCodersViewModel >> announce: anAnnouncement [ + >#announceTemplate'> + announcer ifNotNil: [ :anAnnouncer | anAnnouncer announce: anAnnouncement ]. +] + +{ #category : #'api - accessing' } +GtStreamingCodersViewModel >> coderViewModelsStream [ + + + ^ coderViewModelsStream asyncSimilarCopy +] + +{ #category : #'api - commands' } +GtStreamingCodersViewModel >> enqueueCommand: aViewModelCommand [ + mutex critical: [ + | theApplicableCoderViewModels | + + theApplicableCoderViewModels := OrderedCollection new. + + coderViewModels valuesDo: [ :eachCoderViewModel | + (aViewModelCommand applicableTo: eachCoderViewModel) + ifTrue: [ theApplicableCoderViewModels add: eachCoderViewModel ] ]. + + theApplicableCoderViewModels + ifEmpty: [ pendingCommands add: aViewModelCommand ] + ifNotEmpty: [ theApplicableCoderViewModels do: [ :eachCoderViewModel | aViewModelCommand applyOn: eachCoderViewModel ] ] ] +] + +{ #category : #'api - scrolling' } +GtStreamingCodersViewModel >> hasScrollTarget [ + ^ scrollTarget notNil +] + +{ #category : #initialization } +GtStreamingCodersViewModel >> initialize [ + super initialize. + + mutex := Mutex new. + coderViewModels := AsyncSharedDictionary new. + pendingCommands := OrderedCollection new. + coderViewModelsStream := AsyncEmptyStream new +] + +{ #category : #'instance creation' } +GtStreamingCodersViewModel >> newCoderViewModelFor: aCoderModel [ + ^ aCoderModel asCoderViewModel +] + +{ #category : #'private - notifying' } +GtStreamingCodersViewModel >> notifyCoderViewModelsStreamChanged [ + self announce: GtStreamingCodersViewModelStreamChanged new +] + +{ #category : #'private - event handling' } +GtStreamingCodersViewModel >> onCodersStreamChanged: anAnnouncement [ + (anAnnouncement isDueTo: GtStreamingCodersAddedReason) + ifTrue: [ self privateFetchCoderViewModelFor: anAnnouncement dueTo coder ]. + + self updateCoderViewModelsStream +] + +{ #category : #'api - streaming coders model' } +GtStreamingCodersViewModel >> onStreamingCodersModelChanged [ + "Is sent when a new streamingCodersModel is assigned to the receiver. + Note: #onStreamingCodersModelChanged is sent before #subscribeToStreamingCodersModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostStreamingCodersModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + >#onObjectChangedTemplate'> + + self updateCoderViewModelsStream +] + +{ #category : #'private - accessing' } +GtStreamingCodersViewModel >> privateCoderViewModelsStream: anAsyncStream [ + coderViewModelsStream := anAsyncStream cached. + self notifyCoderViewModelsStreamChanged +] + +{ #category : #'private - accessing' } +GtStreamingCodersViewModel >> privateFetchCoderViewModelFor: aCoderModel [ + ^ mutex critical: [ + coderViewModels + at: aCoderModel + ifAbsentPut: [ + | aCoderViewModel theApplicableCommands | + aCoderViewModel := self newCoderViewModelFor: aCoderModel. + + theApplicableCommands := pendingCommands select: [ :eachCommand | eachCommand applicableTo: aCoderViewModel ]. + pendingCommands removeAll: theApplicableCommands. + theApplicableCommands do: [ :eachCommand | eachCommand applyOn: aCoderViewModel ]. + + aCoderViewModel ] ] +] + +{ #category : #'api - scrolling' } +GtStreamingCodersViewModel >> scrollTarget [ + ^ scrollTarget +] + +{ #category : #'api - scrolling' } +GtStreamingCodersViewModel >> scrollToTarget: aGtStreamingCoderViewModelScrollTarget [ + scrollTarget := aGtStreamingCoderViewModelScrollTarget. + self announce: (GtStreamingCodersViewModelScrollTargetChanged new scrollTarget: scrollTarget) +] + +{ #category : #'api - streaming coders model' } +GtStreamingCodersViewModel >> subscribeToStreamingCodersModel [ + "Is sent after a new streamingCodersModel is assigned to the receiver. + It is required to unsubscribe from the previously subscribed objects by implementing + #unsubscribeFromStreamingCodersModel if the receiver subscribes to them" + + >#subscribeToObjectTemplate'> + + streamingCodersModel + when: GtStreamingCodersStreamChanged + send: #onCodersStreamChanged: + to: self +] + +{ #category : #'api - announcer' } +GtStreamingCodersViewModel >> unsubscribe: anObject [ + "Unsubscribe all subscriptions of anObject from the receiver" + + >#unsubscribeTemplate'> + ^ announcer ifNotNil: [ :anAnnouncer | anAnnouncer unsubscribe: anObject ] +] + +{ #category : #'api - streaming coders model' } +GtStreamingCodersViewModel >> unsubscribeFromStreamingCodersModel [ + "Is sent before a new streamingCodersModel is assigned to the receiver. + Objects that subscribe to streamingCodersModel are required to implement this method." + + >#unsubscribeFromObjectTemplate'> + + streamingCodersModel unsubscribe: self +] + +{ #category : #'private - updating' } +GtStreamingCodersViewModel >> updateCoderViewModelsStream [ + mutex critical: [ + self privateCoderViewModelsStream: (streamingCodersModel codersStream map: [ :eachCoder | self privateFetchCoderViewModelFor: eachCoder ]) ] +] + +{ #category : #'api - announcer' } +GtStreamingCodersViewModel >> when: anAnnouncementClass do: aBlock [ + >#whenDoTemplate'> + ^ (announcer ifNil: [ announcer := Announcer new ]) + when: anAnnouncementClass do: aBlock +] + +{ #category : #'api - announcer' } +GtStreamingCodersViewModel >> when: anAnnouncementClass do: aBlock for: aSubscriber [ + >#whenDoForTemplate'> + ^ (announcer ifNil: [ announcer := Announcer new ]) + when: anAnnouncementClass do: aBlock for: aSubscriber +] + +{ #category : #'api - announcer' } +GtStreamingCodersViewModel >> when: anAnnouncementClass send: aSelector to: anObject [ + >#whenSendToTemplate'> + ^ (announcer ifNil: [ announcer := Announcer new ]) weak + when: anAnnouncementClass send: aSelector to: anObject +] diff --git a/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelCommand.class.st b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelCommand.class.st new file mode 100644 index 000000000..15f7a1122 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelCommand.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtStreamingCodersViewModelCommand, + #superclass : #Object, + #category : #'GToolkit-Coder-StreamingCoders-UI-Utilities' +} + +{ #category : #'api - testing' } +GtStreamingCodersViewModelCommand >> applicableTo: aCoderViewModel [ + ^ false +] + +{ #category : #'api - testing' } +GtStreamingCodersViewModelCommand >> applyOn: aCoderViewModel [ + +] diff --git a/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelScrollTarget.class.st b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelScrollTarget.class.st new file mode 100644 index 000000000..4d60564d6 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelScrollTarget.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtStreamingCodersViewModelScrollTarget, + #superclass : #Object, + #category : #'GToolkit-Coder-StreamingCoders-UI-Utilities' +} + +{ #category : #'api - testing' } +GtStreamingCodersViewModelScrollTarget >> applicableTo: aCoderViewModel [ + ^ false +] + +{ #category : #printing } +GtStreamingCodersViewModelScrollTarget >> printTargetNameOn: aStream [ + aStream nextPutAll: '' +] diff --git a/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelScrollTargetChanged.class.st b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelScrollTargetChanged.class.st new file mode 100644 index 000000000..969f85ba0 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelScrollTargetChanged.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtStreamingCodersViewModelScrollTargetChanged, + #superclass : #Announcement, + #instVars : [ + 'scrollTarget' + ], + #category : #'GToolkit-Coder-StreamingCoders-UI-Events' +} + +{ #category : #accessing } +GtStreamingCodersViewModelScrollTargetChanged >> scrollTarget [ + + ^ scrollTarget +] + +{ #category : #accessing } +GtStreamingCodersViewModelScrollTargetChanged >> scrollTarget: anObject [ + + scrollTarget := anObject +] diff --git a/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelStreamChanged.class.st b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelStreamChanged.class.st new file mode 100644 index 000000000..147cb619f --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/GtStreamingCodersViewModelStreamChanged.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtStreamingCodersViewModelStreamChanged, + #superclass : #Announcement, + #category : #'GToolkit-Coder-StreamingCoders-UI-Events' +} diff --git a/src/GToolkit-Coder-StreamingCoders-UI/ManifestGToolkitCoderStreamingCodersUI.class.st b/src/GToolkit-Coder-StreamingCoders-UI/ManifestGToolkitCoderStreamingCodersUI.class.st new file mode 100644 index 000000000..0afc4869a --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/ManifestGToolkitCoderStreamingCodersUI.class.st @@ -0,0 +1,27 @@ +" +Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser +" +Class { + #name : #ManifestGToolkitCoderStreamingCodersUI, + #superclass : #PackageManifest, + #category : #'GToolkit-Coder-StreamingCoders-UI-Manifest' +} + +{ #category : #accessing } +ManifestGToolkitCoderStreamingCodersUI class >> mustOnlyDependOn [ + ^ { + 'Announcements-Core'. + 'Collections-Sequenceable'. + 'Futures'. + 'GToolkit-Coder-StreamingCoders'. + 'Kernel' + } +] + +{ #category : #accessing } +ManifestGToolkitCoderStreamingCodersUI class >> shouldDependOn [ + ^ { + 'Futures'. + 'GToolkit-Coder-StreamingCoders'. + } +] diff --git a/src/GToolkit-Coder-StreamingCoders-UI/TGtWithStreamingCodersViewModel.trait.st b/src/GToolkit-Coder-StreamingCoders-UI/TGtWithStreamingCodersViewModel.trait.st new file mode 100644 index 000000000..a90adfe19 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/TGtWithStreamingCodersViewModel.trait.st @@ -0,0 +1,84 @@ +Trait { + #name : #TGtWithStreamingCodersViewModel, + #instVars : [ + 'streamingCodersViewModel' + ], + #category : #'GToolkit-Coder-StreamingCoders-UI-Coder' +} + +{ #category : #'api - streaming coders view model' } +TGtWithStreamingCodersViewModel >> hasStreamingCodersViewModel [ + "Return a true if nil is assigned to the receiver, false otherwise" + + + >#hasObjectTemplate'> + + ^ streamingCodersViewModel notNil +] + +{ #category : #'api - streaming coders view model' } +TGtWithStreamingCodersViewModel >> onPostStreamingCodersViewModelChanged [ + "I am an optional hook method that is sent after #subscribeToStreamingCodersViewModel. + I do nothing by default but allow users to perform update operations when a receiver object is already + subscribed to announcements." + >#onPostObjectChangedTemplate'> +] + +{ #category : #'api - streaming coders view model' } +TGtWithStreamingCodersViewModel >> onStreamingCodersViewModelChanged [ + "Is sent when a new streamingCodersViewModel is assigned to the receiver. + Note: #onStreamingCodersViewModelChanged is sent before #subscribeToStreamingCodersViewModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostStreamingCodersViewModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + >#onObjectChangedTemplate'> +] + +{ #category : #'api - streaming coders view model' } +TGtWithStreamingCodersViewModel >> streamingCodersViewModel [ + "Return a not-null streamingCodersViewModel assigned to the receiver" + + >#objectGetterTemplate'> + self + assert: [ streamingCodersViewModel notNil ] + description: [ 'streamingCodersViewModel should be initialized' ]. + + ^ streamingCodersViewModel +] + +{ #category : #'api - streaming coders view model' } +TGtWithStreamingCodersViewModel >> streamingCodersViewModel: aStreamingCodersViewModel [ + "Set a not-null streamingCodersViewModel assigned to the receiver" + + >#objectSetterTemplate'> + self + assert: [ aStreamingCodersViewModel notNil ] + description: [ 'streamingCodersViewModel must not be nil' ]. + + streamingCodersViewModel == aStreamingCodersViewModel + ifTrue: [ ^ self ]. + + streamingCodersViewModel ifNotNil: [ self unsubscribeFromStreamingCodersViewModel ]. + streamingCodersViewModel := aStreamingCodersViewModel. + + self onStreamingCodersViewModelChanged. + self subscribeToStreamingCodersViewModel. + self onPostStreamingCodersViewModelChanged +] + +{ #category : #'api - streaming coders view model' } +TGtWithStreamingCodersViewModel >> subscribeToStreamingCodersViewModel [ + "Is sent after a new streamingCodersViewModel is assigned to the receiver. + It is required to unsubscribe from the previously subscribed objects by implementing + #unsubscribeFromStreamingCodersViewModel if the receiver subscribes to them" + + >#subscribeToObjectTemplate'> +] + +{ #category : #'api - streaming coders view model' } +TGtWithStreamingCodersViewModel >> unsubscribeFromStreamingCodersViewModel [ + "Is sent before a new streamingCodersViewModel is assigned to the receiver. + Objects that subscribe to streamingCodersViewModel are required to implement this method." + + >#unsubscribeFromObjectTemplate'> +] diff --git a/src/GToolkit-Coder-StreamingCoders-UI/package.st b/src/GToolkit-Coder-StreamingCoders-UI/package.st new file mode 100644 index 000000000..f5a86f2e6 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders-UI/package.st @@ -0,0 +1 @@ +Package { #name : #'GToolkit-Coder-StreamingCoders-UI' } diff --git a/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersAdditionalFiltersReason.class.st b/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersAdditionalFiltersReason.class.st new file mode 100644 index 000000000..8853786b8 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersAdditionalFiltersReason.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilteredCodersAdditionalFiltersReason, + #superclass : #GtStreamingCodersStreamChangedReason, + #category : #'GToolkit-Coder-StreamingCoders-Events' +} diff --git a/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersFilterReason.class.st b/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersFilterReason.class.st new file mode 100644 index 000000000..8dd968095 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersFilterReason.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilteredCodersFilterReason, + #superclass : #GtStreamingCodersStreamChangedReason, + #category : #'GToolkit-Coder-StreamingCoders-Events' +} diff --git a/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersModel.class.st b/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersModel.class.st new file mode 100644 index 000000000..14cfa5605 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtFilteredCodersModel.class.st @@ -0,0 +1,146 @@ +Class { + #name : #GtFilteredCodersModel, + #superclass : #GtStreamingCodersModel, + #instVars : [ + 'filter', + 'additionalFilters', + 'compositeFilter' + ], + #category : #'GToolkit-Coder-StreamingCoders-Coder' +} + +{ #category : #'instance creation' } +GtFilteredCodersModel class >> forFilter: aGtSearchFilter [ + ^ self new filter: aGtSearchFilter +] + +{ #category : #'api - accessing' } +GtFilteredCodersModel >> addOrReplaceFilter: aFilter [ + "Add or replace one additional filter. + A filter of the same class is replaced by the given filter." + + | newFilters isChanged | + isChanged := false. + newFilters := self additionalFilters + collect: [ :eachFilter | + eachFilter filterType = aFilter filterType + ifTrue: [ + isChanged := true. + aFilter ] + ifFalse: [ + eachFilter ] ]. + + isChanged ifFalse: [ + newFilters := newFilters , {aFilter} ]. + self additionalFilters: newFilters +] + +{ #category : #'api - accessing' } +GtFilteredCodersModel >> addOrReplaceFilters: aCollectionOfFilters [ + "Add or replace given additional filters. + Each filter of the same filter type (class) is replaced by the given filters." + + | newFilters isChanged usedFilters | + isChanged := false. + usedFilters := OrderedCollection new. + newFilters := self additionalFilters + collect: [ :existingFilter | + aCollectionOfFilters + detect: [ :givenFilter | + existingFilter filterType = givenFilter filterType ] + ifFound: [ :givenFilter | + isChanged := true. + usedFilters add: givenFilter. + givenFilter ] + ifNone: [ + existingFilter ] ]. + + newFilters := newFilters , (aCollectionOfFilters copyWithoutAll: usedFilters). + self additionalFilters: newFilters +] + +{ #category : #'api - accessing' } +GtFilteredCodersModel >> additionalFilters [ + + + ^ additionalFilters +] + +{ #category : #'api - accessing' } +GtFilteredCodersModel >> additionalFilters: aCollectionOfFilters [ + | aNewCollection | + aNewCollection := aCollectionOfFilters asArray. + additionalFilters = aNewCollection ifTrue: [ ^ self ]. + mutex critical: [ + additionalFilters := aNewCollection. + compositeFilter := self + createCombinedFilterFor: filter + andAdditionalFilters: additionalFilters. + self refreshItemsStreamDueTo: GtFilteredCodersAdditionalFiltersReason new ]. +] + +{ #category : #'api - accessing' } +GtFilteredCodersModel >> compositeFilter [ + "I am combination of: + - the main filter (see the `filter` method), and + - additional filters (see the `additionalFilters` method). + If `filter` is kind of input filter, `compositeFilter` is kind of output filter." + + + ^ compositeFilter +] + +{ #category : #'private - instance creation' } +GtFilteredCodersModel >> createCombinedFilterFor: aMainSearchFilter andAdditionalFilters: aCollectionOfFilters [ + "Given a main search filter and a potentially empty collection of additional filters, + create a composition of those filters" + + | aCompositeFilter | + + aCompositeFilter := aCollectionOfFilters + inject: nil + into: [ :sum :each | sum ifNil: [ each ] ifNotNil: [ sum & each ] ]. + + aCompositeFilter := aCompositeFilter + ifNil: [ aMainSearchFilter ] + ifNotNil: [ aMainSearchFilter & aCompositeFilter ]. + + ^ aCompositeFilter +] + +{ #category : #'api - accessing' } +GtFilteredCodersModel >> filter [ + "I am a main filter whose items may be futher filtered using additional filters" + + + ^ filter +] + +{ #category : #'api - accessing' } +GtFilteredCodersModel >> filter: aGtSearchFilter [ + mutex critical: [ + filter := aGtSearchFilter. + compositeFilter := self + createCombinedFilterFor: filter + andAdditionalFilters: additionalFilters. + self refreshItemsStreamDueTo: GtFilteredCodersFilterReason new ] +] + +{ #category : #initialization } +GtFilteredCodersModel >> initialize [ + super initialize. + + filter := GtSearchNullFilter new. + additionalFilters := #(). + compositeFilter := GtSearchNullFilter new. +] + +{ #category : #'private - instance creation' } +GtFilteredCodersModel >> newItemsStream [ + ^ compositeFilter asAsyncStream +] + +{ #category : #'api - accessing' } +GtFilteredCodersModel >> requesterContextDo: aBlock [ + ^ filter requesterContextDo: aBlock +] diff --git a/src/GToolkit-Coder-StreamingCoders/GtFinishedBulkUpdateReason.class.st b/src/GToolkit-Coder-StreamingCoders/GtFinishedBulkUpdateReason.class.st new file mode 100644 index 000000000..9aeb3f09a --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtFinishedBulkUpdateReason.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFinishedBulkUpdateReason, + #superclass : #GtStreamingCodersStreamChangedReason, + #category : #'GToolkit-Coder-StreamingCoders-Events' +} diff --git a/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersAddedReason.class.st b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersAddedReason.class.st new file mode 100644 index 000000000..3bb56fc0e --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersAddedReason.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtStreamingCodersAddedReason, + #superclass : #GtStreamingCodersStreamChangedReason, + #instVars : [ + 'coder' + ], + #category : #'GToolkit-Coder-StreamingCoders-Events' +} + +{ #category : #accessing } +GtStreamingCodersAddedReason >> coder [ + + ^ coder +] + +{ #category : #accessing } +GtStreamingCodersAddedReason >> coder: anObject [ + + coder := anObject +] diff --git a/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersModel.class.st b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersModel.class.st new file mode 100644 index 000000000..20914f28b --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersModel.class.st @@ -0,0 +1,186 @@ +Class { + #name : #GtStreamingCodersModel, + #superclass : #Object, + #instVars : [ + 'announcer', + 'coders', + 'itemsStream', + 'codersStream', + 'mutex' + ], + #category : #'GToolkit-Coder-StreamingCoders-Coder' +} + +{ #category : #'api - announcer' } +GtStreamingCodersModel >> announce: anAnnouncement [ + >#announceTemplate'> + announcer ifNotNil: [ :anAnnouncer | anAnnouncer announce: anAnnouncement ]. +] + +{ #category : #'api - converting' } +GtStreamingCodersModel >> asAsyncStream [ + + + ^ self codersStream +] + +{ #category : #'api - accessing' } +GtStreamingCodersModel >> codersStream [ + "Return a stream of coders" + + + ^ codersStream asyncSimilarCopy +] + +{ #category : #initialization } +GtStreamingCodersModel >> initialize [ + super initialize. + + mutex := Mutex new. + coders := Dictionary new. + itemsStream := AsyncEmptyStream new. + codersStream := AsyncEmptyStream new +] + +{ #category : #'api - accessing' } +GtStreamingCodersModel >> itemsStream [ + "Return a stream of items backing the coder" + + + ^ itemsStream asyncSimilarCopy +] + +{ #category : #'private - accessing' } +GtStreamingCodersModel >> itemsStream: anAsyncStream dueTo: aReasonObject [ + "Change the items stream to a given one due to a provided reason. + Please not that the new stream must not be the same stream as an existing #itemsStream" + + self + assert: [ itemsStream ~~ anAsyncStream ] + description: [ 'Must not pass the same stream as the current one' ]. + + mutex critical: [ + itemsStream := anAsyncStream cached. + codersStream := itemsStream + map: [ :eachItem | self newCoderFor: eachItem ] + key: [ :eachItem | self newCoderCacheKeyFor: eachItem ] + cache: coders ]. + + self notifyStreamChangedDueTo: aReasonObject +] + +{ #category : #'private - instance creation' } +GtStreamingCodersModel >> newCoderCacheKeyFor: anObject [ + ^ anObject +] + +{ #category : #'private - instance creation' } +GtStreamingCodersModel >> newCoderFor: anObject [ + ^ self subclassResponsibility +] + +{ #category : #'private - instance creation' } +GtStreamingCodersModel >> newItemsStream [ + ^ self subclassResponsibility +] + +{ #category : #'private - notifying' } +GtStreamingCodersModel >> notifyStreamChangedDueTo: aReasonObject [ + self announce: (GtStreamingCodersStreamChanged new dueTo: aReasonObject) +] + +{ #category : #'private - actions' } +GtStreamingCodersModel >> privateAddCoderFor: aNewItem [ + | anAddedCoder | + + anAddedCoder := self privateFetchCoderFor: aNewItem. + self refreshItemsStreamDueTo: (GtStreamingCodersAddedReason new coder: anAddedCoder) +] + +{ #category : #'private - instance creation' } +GtStreamingCodersModel >> privateFetchCoderFor: anObject [ + ^ mutex critical: [ + coders + at: (self newCoderCacheKeyFor: anObject) + ifAbsentPut: [ self newCoderFor: anObject ] ] +] + +{ #category : #'private - actions' } +GtStreamingCodersModel >> privateRemoveCoderFor: aRemovedItem [ + | aRemovedCoder | + + mutex critical: [ + aRemovedCoder := coders + removeKey: (self newCoderCacheKeyFor: aRemovedItem) + ifAbsent: [ nil ] ]. + + self refreshItemsStreamDueTo: (GtStreamingCodersRemovedReason new coder: aRemovedCoder) +] + +{ #category : #'private - actions' } +GtStreamingCodersModel >> privateSwapCodersFor: aFirstItem and: aSecondItem [ + | aFirstKey aSecondKey aFirstSwappedCoder aSecondSwappedCoder | + + aFirstKey := self newCoderCacheKeyFor: aFirstItem. + aSecondKey := self newCoderCacheKeyFor: aSecondItem. + + mutex critical: [ + coders + at: aFirstKey + ifPresent: [ :aFirstCoder | + aFirstSwappedCoder := aFirstCoder. + coders + at: aSecondKey + ifPresent: [ :aSecondCoder | + aSecondSwappedCoder := aSecondCoder. + coders at: aSecondKey put: aFirstCoder. + coders at: aFirstKey put: aSecondCoder ] + ifAbsent: [ coders at: aSecondKey put: aFirstCoder. ] ] + ifAbsent: [ + coders + at: aSecondKey + ifPresent: [ :aSecondCoder | + aSecondSwappedCoder := aSecondCoder. + coders at: aFirstKey put: aSecondCoder ] ] ]. + + "the operation changes the order of coders, therefore we must update the stream". + self refreshItemsStreamDueTo: (GtStreamingCodersSwappedReason new + coderA: aFirstSwappedCoder; + coderB: aSecondSwappedCoder) +] + +{ #category : #'private - accessing' } +GtStreamingCodersModel >> refreshItemsStreamDueTo: aReasonObject [ + self + itemsStream: self newItemsStream + dueTo: aReasonObject +] + +{ #category : #'api - announcer' } +GtStreamingCodersModel >> unsubscribe: anObject [ + "Unsubscribe all subscriptions of anObject from the receiver" + + >#unsubscribeTemplate'> + ^ announcer ifNotNil: [ :anAnnouncer | anAnnouncer unsubscribe: anObject ] +] + +{ #category : #'api - announcer' } +GtStreamingCodersModel >> when: anAnnouncementClass do: aBlock [ + >#whenDoTemplate'> + ^ (announcer ifNil: [ announcer := Announcer new ]) + when: anAnnouncementClass do: aBlock +] + +{ #category : #'api - announcer' } +GtStreamingCodersModel >> when: anAnnouncementClass do: aBlock for: aSubscriber [ + >#whenDoForTemplate'> + ^ (announcer ifNil: [ announcer := Announcer new ]) + when: anAnnouncementClass do: aBlock for: aSubscriber +] + +{ #category : #'api - announcer' } +GtStreamingCodersModel >> when: anAnnouncementClass send: aSelector to: anObject [ + >#whenSendToTemplate'> + ^ (announcer ifNil: [ announcer := Announcer new ]) weak + when: anAnnouncementClass send: aSelector to: anObject +] diff --git a/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersRemovedReason.class.st b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersRemovedReason.class.st new file mode 100644 index 000000000..d14599b6e --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersRemovedReason.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtStreamingCodersRemovedReason, + #superclass : #GtStreamingCodersStreamChangedReason, + #instVars : [ + 'coder' + ], + #category : #'GToolkit-Coder-StreamingCoders-Events' +} + +{ #category : #accessing } +GtStreamingCodersRemovedReason >> coder [ + + ^ coder +] + +{ #category : #accessing } +GtStreamingCodersRemovedReason >> coder: anObject [ + + coder := anObject +] diff --git a/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersStreamChanged.class.st b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersStreamChanged.class.st new file mode 100644 index 000000000..bece762b9 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersStreamChanged.class.st @@ -0,0 +1,25 @@ +Class { + #name : #GtStreamingCodersStreamChanged, + #superclass : #Announcement, + #instVars : [ + 'dueTo' + ], + #category : #'GToolkit-Coder-StreamingCoders-Events' +} + +{ #category : #accessing } +GtStreamingCodersStreamChanged >> dueTo [ + + ^ dueTo +] + +{ #category : #accessing } +GtStreamingCodersStreamChanged >> dueTo: anObject [ + + dueTo := anObject +] + +{ #category : #accessing } +GtStreamingCodersStreamChanged >> isDueTo: aReasonClass [ + ^ dueTo isKindOf: aReasonClass +] diff --git a/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersStreamChangedReason.class.st b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersStreamChangedReason.class.st new file mode 100644 index 000000000..d2cd4b3c6 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersStreamChangedReason.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtStreamingCodersStreamChangedReason, + #superclass : #Object, + #category : #'GToolkit-Coder-StreamingCoders-Events' +} diff --git a/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersSwappedReason.class.st b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersSwappedReason.class.st new file mode 100644 index 000000000..a58d947f9 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/GtStreamingCodersSwappedReason.class.st @@ -0,0 +1,33 @@ +Class { + #name : #GtStreamingCodersSwappedReason, + #superclass : #GtStreamingCodersStreamChangedReason, + #instVars : [ + 'coderA', + 'coderB' + ], + #category : #'GToolkit-Coder-StreamingCoders-Events' +} + +{ #category : #accessing } +GtStreamingCodersSwappedReason >> coderA [ + + ^ coderA +] + +{ #category : #accessing } +GtStreamingCodersSwappedReason >> coderA: anObject [ + + coderA := anObject +] + +{ #category : #accessing } +GtStreamingCodersSwappedReason >> coderB [ + + ^ coderB +] + +{ #category : #accessing } +GtStreamingCodersSwappedReason >> coderB: anObject [ + + coderB := anObject +] diff --git a/src/GToolkit-Coder-StreamingCoders/ManifestGToolkitCoderStreamingCoders.class.st b/src/GToolkit-Coder-StreamingCoders/ManifestGToolkitCoderStreamingCoders.class.st new file mode 100644 index 000000000..c73917b25 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/ManifestGToolkitCoderStreamingCoders.class.st @@ -0,0 +1,20 @@ +" +Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser +" +Class { + #name : #ManifestGToolkitCoderStreamingCoders, + #superclass : #PackageManifest, + #category : #'GToolkit-Coder-StreamingCoders-Manifest' +} + +{ #category : #accessing } +ManifestGToolkitCoderStreamingCoders class >> mustOnlyDependOn [ + ^ { + 'Announcements-Core'. + 'Collections-Unordered'. + 'Collections-Sequenceable'. + 'Futures'. + 'GToolkit-SearchFilters'. + 'Kernel' + } +] diff --git a/src/GToolkit-Coder-StreamingCoders/TGtWithStreamingCodersModel.trait.st b/src/GToolkit-Coder-StreamingCoders/TGtWithStreamingCodersModel.trait.st new file mode 100644 index 000000000..edf48d3af --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/TGtWithStreamingCodersModel.trait.st @@ -0,0 +1,84 @@ +Trait { + #name : #TGtWithStreamingCodersModel, + #instVars : [ + 'streamingCodersModel' + ], + #category : #'GToolkit-Coder-StreamingCoders-Coder' +} + +{ #category : #'api - streaming coders model' } +TGtWithStreamingCodersModel >> hasStreamingCodersModel [ + "Return a true if nil is assigned to the receiver, false otherwise" + + + >#hasObjectTemplate'> + + ^ streamingCodersModel notNil +] + +{ #category : #'api - streaming coders model' } +TGtWithStreamingCodersModel >> onPostStreamingCodersModelChanged [ + "I am an optional hook method that is sent after #subscribeToStreamingCodersModel. + I do nothing by default but allow users to perform update operations when a receiver object is already + subscribed to announcements." + >#onPostObjectChangedTemplate'> +] + +{ #category : #'api - streaming coders model' } +TGtWithStreamingCodersModel >> onStreamingCodersModelChanged [ + "Is sent when a new streamingCodersModel is assigned to the receiver. + Note: #onStreamingCodersModelChanged is sent before #subscribeToStreamingCodersModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostStreamingCodersModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + >#onObjectChangedTemplate'> +] + +{ #category : #'api - streaming coders model' } +TGtWithStreamingCodersModel >> streamingCodersModel [ + "Return a not-null streamingCodersModel assigned to the receiver" + + >#objectGetterTemplate'> + self + assert: [ streamingCodersModel notNil ] + description: [ 'streamingCodersModel should be initialized' ]. + + ^ streamingCodersModel +] + +{ #category : #'api - streaming coders model' } +TGtWithStreamingCodersModel >> streamingCodersModel: aStreamingCodersModel [ + "Set a not-null streamingCodersModel assigned to the receiver" + + >#objectSetterTemplate'> + self + assert: [ aStreamingCodersModel notNil ] + description: [ 'streamingCodersModel must not be nil' ]. + + streamingCodersModel == aStreamingCodersModel + ifTrue: [ ^ self ]. + + streamingCodersModel ifNotNil: [ self unsubscribeFromStreamingCodersModel ]. + streamingCodersModel := aStreamingCodersModel. + + self onStreamingCodersModelChanged. + self subscribeToStreamingCodersModel. + self onPostStreamingCodersModelChanged +] + +{ #category : #'api - streaming coders model' } +TGtWithStreamingCodersModel >> subscribeToStreamingCodersModel [ + "Is sent after a new streamingCodersModel is assigned to the receiver. + It is required to unsubscribe from the previously subscribed objects by implementing + #unsubscribeFromStreamingCodersModel if the receiver subscribes to them" + + >#subscribeToObjectTemplate'> +] + +{ #category : #'api - streaming coders model' } +TGtWithStreamingCodersModel >> unsubscribeFromStreamingCodersModel [ + "Is sent before a new streamingCodersModel is assigned to the receiver. + Objects that subscribe to streamingCodersModel are required to implement this method." + + >#unsubscribeFromObjectTemplate'> +] diff --git a/src/GToolkit-Coder-StreamingCoders/package.st b/src/GToolkit-Coder-StreamingCoders/package.st new file mode 100644 index 000000000..e57076b14 --- /dev/null +++ b/src/GToolkit-Coder-StreamingCoders/package.st @@ -0,0 +1 @@ +Package { #name : #'GToolkit-Coder-StreamingCoders' } diff --git a/src/GToolkit-Coder-UI/Behavior.extension.st b/src/GToolkit-Coder-UI/Behavior.extension.st new file mode 100644 index 000000000..8fd0dad74 --- /dev/null +++ b/src/GToolkit-Coder-UI/Behavior.extension.st @@ -0,0 +1,65 @@ +Extension { #name : #Behavior } + +{ #category : #'*GToolkit-Coder-UI' } +Behavior >> gtClassActionsDropdownFor: anAction [ + + + ^ anAction dropdown + tooltip: 'Class actions'; + icon: BrGlamorousVectorIcons hamburger; + id: GtCoderClassActionsDropdownButtonId; + priority: 100; + menuItemsForObject: self fromTarget: GtCoderClassTarget +] + +{ #category : #'*GToolkit-Coder-UI' } +Behavior >> gtDisplayTopSidebarIndexToggleFor: anAction [ + "Previous dropdown solution: Behavior>>#gtHierarchyButtonFor:" + + + ^ anAction toggle + tooltip: 'Show Package and Class Hierarchies'; + icon: BrGlamorousVectorIcons tree; + id: GtCoderClassHierarchyButtonId; + priority: 3; + isActivated: [ :aToggle :aTargetElement | + aTargetElement isAttachedToSceneGraph + ifTrue: [ | anEvent | + anEvent := GtCoderSidebarVisibilityWish new. + aTargetElement fireEvent: anEvent. + anEvent isVisible ] + ifFalse: [ BlTaskAction + enqueueElement: aToggle + action: [ | anEvent | + anEvent := GtCoderSidebarVisibilityWish new. + aToggle fireEvent: anEvent. + aToggle activated: anEvent isVisible ]. + GtCoderSettings defaultDetailState = GtPhlowToolDetailState detailed ] ]; + activatedAction: [ :aButton | aButton fireEvent: GtCoderDisplayTopSidebarWish new ]; + deactivatedAction: [ :aButton | aButton fireEvent: GtCoderHideSidebarWish new ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Behavior >> gtHierarchyButtonFor: anAction [ + "New implementation: Behavior>>#gtDisplayTopSidebarIndexToggleFor:" + + "" + ^ anAction dropdown + tooltip: 'Show Package and Class Hierarchies'; + icon: BrGlamorousVectorIcons tree; + id: GtCoderClassHierarchyButtonId; + priority: 3; + preferredExtentFrom: GtPharoCoderHierarchyDropdownConfiguration; + content: [ :aButton :aTargetElement :anExplicitMenu | + | aNavigationModel | + aNavigationModel := aButton phlow firstParentCoderNavigationModel. + BlElement new + size: GtPharoCoderHierarchyDropdownConfiguration preferredExtent; + addChild: ((GtCoderNavigationTabsStencil new + pragmaName: #gtCoderDropdownNavigation; + navigationModel: aNavigationModel; + asElement) background: Color white); + addAptitude: (BrGlamorousPopoverPinnableAptitude new + withAllPinActions; + menuModel: anExplicitMenu) ] +] diff --git a/src/GToolkit-Coder-UI/BlockClosure.extension.st b/src/GToolkit-Coder-UI/BlockClosure.extension.st new file mode 100644 index 000000000..e1f79141a --- /dev/null +++ b/src/GToolkit-Coder-UI/BlockClosure.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #BlockClosure } + +{ #category : #'*GToolkit-Coder-UI' } +BlockClosure >> asFilterModelItemsBuilder [ + ^ GtFilterModelValuableItemsBuilder new valuable: self +] diff --git a/src/GToolkit-Coder-UI/BrGlamorousMenuElementBuilder.extension.st b/src/GToolkit-Coder-UI/BrGlamorousMenuElementBuilder.extension.st new file mode 100644 index 000000000..af4cdf942 --- /dev/null +++ b/src/GToolkit-Coder-UI/BrGlamorousMenuElementBuilder.extension.st @@ -0,0 +1,15 @@ +Extension { #name : #BrGlamorousMenuElementBuilder } + +{ #category : #'*GToolkit-Coder-UI' } +BrGlamorousMenuElementBuilder >> visitTextualCoderMenu: aMenuModel [ + | aCoderViewModel aCoderElement | + aMenuModel coderViewModelStencil ifNil: [ ^ nil ]. + aCoderViewModel := aMenuModel coderViewModelStencil create. + aCoderViewModel ifNil: [ ^ nil ]. + + aCoderElement := aCoderViewModel asExpandedOnlyElement. + aCoderElement addAptitude: (BrGlamorousPopoverDefiningMethodAptitude new + menuModel: aMenuModel). + + ^ aCoderElement +] diff --git a/src/GToolkit-Coder-UI/BrGlamorousMenuItemTypeBuilder.extension.st b/src/GToolkit-Coder-UI/BrGlamorousMenuItemTypeBuilder.extension.st new file mode 100644 index 000000000..823139a34 --- /dev/null +++ b/src/GToolkit-Coder-UI/BrGlamorousMenuItemTypeBuilder.extension.st @@ -0,0 +1,15 @@ +Extension { #name : #BrGlamorousMenuItemTypeBuilder } + +{ #category : #'*GToolkit-Coder-UI' } +BrGlamorousMenuItemTypeBuilder >> visitCoderMenuActionItem: aMenuModel [ + ^ self + visitMenuLabeledItem: aMenuModel + withElementClass: GtCoderMenuActionItemElement +] + +{ #category : #'*GToolkit-Coder-UI' } +BrGlamorousMenuItemTypeBuilder >> visitCoderSubmenuItem: aMenuModel [ + ^ self + visitMenuLabeledItem: aMenuModel + withElementClass: GtCoderMenuSubmenuItemElement +] diff --git a/src/GToolkit-Coder-UI/Class.extension.st b/src/GToolkit-Coder-UI/Class.extension.st new file mode 100644 index 000000000..38690d6c5 --- /dev/null +++ b/src/GToolkit-Coder-UI/Class.extension.st @@ -0,0 +1,247 @@ +Extension { #name : #Class } + +{ #category : #'*GToolkit-Coder-UI' } +Class >> asFilterModelItem [ + ^ GtFilterModelClassItem new itemClass: self +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexBehaviorDefinitionFor: anAction [ + + ^ anAction dropdown + priority: 3; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons edit; + label: ('{1} definition' format: {self gtCoderTypeName asCamelCase}); + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration modification; + menuItemPinSubmenu; + content: [ :aButton :aTargetElement :anExplicitMenu | + | aForm aFormElement | + aForm := self isTrait + ifTrue: [ GtTraitUpdateForm onTrait: self ] + ifFalse: [ GtClassUpdateForm onClass: self ]. + aFormElement := aForm asElement. + BrFrame new + addChild: aFormElement; + padding: (BlInsets all: GtRefactoringsConstants contextMenuPadding); + hExact: 400; + vFitContentLimited; + background: aButton theme default contentBackground; + addAptitude: (BrLayoutResizerAptitude new + exactToMatchParent: aFormElement; + matchParentToMatchParent: aFormElement; + fitContentToFitContent: aFormElement; + fitContentLimitedToFitContentLimited: aFormElement); + addAptitude: (BrGlamorousPopoverPinnableAptitude new + withLeftAndRightResizers; + withAllPinActions; + menuModel: anExplicitMenu) ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexBrowseClassFor: anAction [ + + ^ anAction button + priority: 1; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons browse; + label: ('Browse {1}' format: {self gtCoderTypeName}); + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | aButton phlow spawnObject: self instanceSide ]; + primaryModifierAction: [ :aButton | self gtBrowseFrom: aButton ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexBrowseClassesReferencesFor: anAction context: aPhlowContext [ + + aPhlowContext coderSelectedClasses ifEmpty: [ ^ anAction noAction ]. + + ^ anAction button + priority: 2; + target: GtCoderClassesTarget; + icon: BrGlamorousVectorIcons empty; + label: 'Browse references'; + menuItemPreview: ('{1} classes' format: {aPhlowContext coderSelectedClasses size}); + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | + | someClasses aFilter | + someClasses := aPhlowContext coderSelectedClasses. + aFilter := someClasses allButFirst + inject: someClasses first gtReferences + into: [ :aSumFilter :aClass | aSumFilter | aClass gtReferences ]. + + aButton phlow spawnObject: aFilter ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexBrowseDefiningClassFor: anAction context: aPhlowContext [ + + | aCompiledMethod | + aCompiledMethod := aPhlowContext + coderCompiledMethodIfPresent: [ :theCompiledMethod | aCompiledMethod := theCompiledMethod ] + ifAbsent: [ nil ]. + aCompiledMethod ifNil: [ ^ anAction noAction ]. + aCompiledMethod methodClass = self ifTrue: [ ^ anAction noAction ]. + + ^ anAction button + priority: 2; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons browse; + label: ('Browse defining {1}' format: {aCompiledMethod methodClass gtCoderTypeName}); + menuItemPreview: aCompiledMethod methodClass name; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | + aButton phlow spawnTool: (GtMethodCoderTool compiledMethod: aCompiledMethod) ]; + primaryModifierAction: [ :aButton | + aCompiledMethod gtBrowseFrom: aButton ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexBrowseReferencesFor: anAction [ + + ^ anAction button + priority: 2; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons empty; + label: 'Browse references'; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | aButton phlow spawnObject: self instanceSide gtReferences ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexCopyClassNameFor: anAction [ + + ^ anAction button + priority: 9; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons clipboard; + label: ('Copy {1} name' format: {self gtCoderTypeName}); + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration editing; + action: [ Clipboard clipboardText: self name ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexCopyClassNameForLepiterFor: anAction [ + + ^ anAction button + priority: 10; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons clipboard; + label: 'Copy lepiter link'; + menuItemPreview: [ '\{\{gtClass:{1}\}\}' format: {self name} ]; + menuItemGroup: BrMenuItemGroupConfiguration editing; + action: [ Clipboard clipboardText: ('\{\{gtClass:{1}\}\}' format: {self name}) ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexNewSubclassFor: anAction [ + + ^ anAction dropdown + priority: 3; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons add; + label: (self isTrait ifFalse: [ 'New subclass' ] ifTrue: [ 'New trait user' ]); + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration modification; + menuItemPinSubmenu; + content: [ :aButton :aTargetElement :anExplicitMenu | + | aChild | + aChild := aButton phlow + firstParentCoderNavigationModelIfPresent: [ :aNavigationModel | + GtCoderNavigationTabsStencil new + pragmaName: #gtCoderDropdownNavigation; + navigationModel: aNavigationModel; + classOfInterest: self; + gtCreationInterface: aButton ] + ifAbsent: [ BrLabel new + aptitude: BrGlamorousLabelAptitude new; + text: 'Currently not available in this context menu'; + constraintsDo: [ :c | + c linear horizontal alignCenter. + c linear vertical alignCenter ] ]. + + BrVerticalPane new + exact: 400 @ 300; + addChild: aChild; + background: aButton theme default contentBackground; + addAptitude: (BrGlamorousPopoverPinnableAptitude new + withLeftAndRightResizers; + withAllPinActions; + menuModel: anExplicitMenu) ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexRemoveClassFor: anAction [ + + ^ anAction dropdown + priority: 5; + target: GtCoderClassTarget; + id: #'coder--context-menu-remove-class'; + icon: BrGlamorousVectorIcons bin; + label: ('Remove {1}' format: {self gtCoderTypeName}); + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration removal; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + GtCoderRemoveClassPreviewStencil new + classToRemove: self instanceSide; + menuModel: anExplicitMenu; + asElement ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexRemoveClassesFor: anAction context: aPhlowContext [ + + aPhlowContext coderSelectedClasses ifEmpty: [ ^ anAction noAction ]. + + ^ anAction dropdown + priority: 5; + target: GtCoderClassesTarget; + icon: BrGlamorousVectorIcons empty; + label: 'Remove classes'; + menuItemPreview: ('{1} classes' format: {aPhlowContext coderSelectedClasses size}); + menuItemGroup: BrMenuItemGroupConfiguration removal; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | someClasses | + someClasses := aPhlowContext coderSelectedClasses. + GtCoderRemoveClassesPreviewStencil new + classesToRemove: someClasses; + menuModel: anExplicitMenu; + asElement ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderSidebarIndexRenameClassFor: anAction [ + + ^ anAction dropdown + priority: 4; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons empty; + label: ('Rename {1}' format: {self gtCoderTypeName}); + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithInputViewModel new + refactoringTitle: ('Rename {1}' format: {self gtCoderTypeName}); + targetName: self name; + inputLabel: ('New {1} name:' format: {self gtCoderTypeName}); + initialText: self name; + refactoringWithInput: [ :anInput | GtRBRenameClassRefactoring rename: self name to: anInput ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithInputElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Class >> gtCoderTypeName [ + ^ 'class' +] diff --git a/src/GToolkit-Coder-UI/ClassVariable.extension.st b/src/GToolkit-Coder-UI/ClassVariable.extension.st new file mode 100644 index 000000000..f10099ee2 --- /dev/null +++ b/src/GToolkit-Coder-UI/ClassVariable.extension.st @@ -0,0 +1,221 @@ +Extension { #name : #ClassVariable } + +{ #category : #'*GToolkit-Coder-UI' } +ClassVariable >> gtBrowseReferencesActionFor: anAction context: aPhlowContext [ + + + ^ anAction button + label: 'Browse references'; + priority: 10; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | + aButton phlow + spawnObject: (GtSearchClassVariableReferenceFilter + forClassAndSubclasses: self owningClass + andVariable: self name) ] +] + +{ #category : #'*GToolkit-Coder-UI' } +ClassVariable >> gtCreateAccessorsActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Create accessors'; + priority: 22; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Create accessors'; + targetName: ('{1}''s {2} class variable' + format: {aSelectedClass name. + self name}); + confirmationLabel: ('Create accessors {1}' format: {self name}); + refactoringWithConfirmation: [ RBCreateAccessorsForVariableRefactoring + variable: self name + class: aSelectedClass + classVariable: true ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +ClassVariable >> gtPushDownActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Push down'; + priority: 21; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Push down'; + targetName: ('{1}''s {2} class variable' + format: {aSelectedClass name. + self name}); + confirmationLabel: ('Push down {1}' format: {self name}); + refactoringWithConfirmation: [ RBPushDownClassVariableRefactoring variable: self name class: aSelectedClass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +ClassVariable >> gtPushUpActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Push up'; + priority: 20; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Push up'; + targetName: ('{1}''s {2} class variable' + format: {aSelectedClass name. + self name}); + confirmationLabel: ('Push up {1}' format: {self name}); + refactoringWithConfirmation: [ RBPullUpClassVariableRefactoring + variable: self name + class: aSelectedClass superclass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +ClassVariable >> gtRemoveActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Remove'; + priority: 40; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration removal; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | element change button | + element := BrVerticalPane new fitContent. + element + addChild: (BrLabel new + margin: (BlInsets + top: 10 + bottom: 0 + left: 10 + right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: ('Remove ' , self name) asRopedText). + element + addChild: (BrAsyncWidget new + fitContent; + stencil: [ | pane references | + pane := BrVerticalPane new. + pane fitContent. + references := (GtSearchClassVariableReferenceFilter + forClassAndSubclasses: aSelectedClass + andVariable: self name) size. + references > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (references printString , ' reference' + , (references > 1 ifTrue: [ 's' ] ifFalse: [ '' ])) asRopedText) ]. + pane ]). + change := RBRemoveClassVariableRefactoring + variable: self name + class: aSelectedClass. + button := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + beSmallSize; + margin: (BlInsets + top: 10 + bottom: 10 + left: 10 + right: 10); + icon: BrGlamorousVectorIcons remove; + label: 'Remove'; + action: [ anExplicitMenu hideAll. + change execute ]. + element addChild: button as: #removeButton. + element ] +] + +{ #category : #'*GToolkit-Coder-UI' } +ClassVariable >> gtRenameActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Rename'; + priority: 25; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithInputViewModel new + refactoringTitle: 'Rename'; + targetName: ('{1}''s {2} class variable' + format: {aSelectedClass name. + self name}); + inputLabel: 'New slot name:'; + refactoringWithInput: [ :anInput | + RBRenameClassVariableRefactoring + rename: self name + to: anInput + in: self owningClass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithInputElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] diff --git a/src/GToolkit-Coder-UI/Collection.extension.st b/src/GToolkit-Coder-UI/Collection.extension.st new file mode 100644 index 000000000..76aec61f8 --- /dev/null +++ b/src/GToolkit-Coder-UI/Collection.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #Collection } + +{ #category : #'*GToolkit-Coder-UI' } +Collection >> asFilterModelItemsBuilder [ + ^ GtFilterModelExplicitItemsBuilder new items: self +] diff --git a/src/GToolkit-Coder-UI/GtAbstractCoderContextMenuContent.class.st b/src/GToolkit-Coder-UI/GtAbstractCoderContextMenuContent.class.st new file mode 100644 index 000000000..5dbb86f82 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtAbstractCoderContextMenuContent.class.st @@ -0,0 +1,45 @@ +Class { + #name : #GtAbstractCoderContextMenuContent, + #superclass : #BrSimpleList, + #category : #'GToolkit-Coder-UI-Basic' +} + +{ #category : #accessing } +GtAbstractCoderContextMenuContent >> initialize [ + super initialize. + self + stencil: self menuItemStencil; + padding: (BlInsets + top: 8 + right: 10 + bottom: 8 + left: 10); + hFitContentLimited; + vFitContentLimited +] + +{ #category : #accessing } +GtAbstractCoderContextMenuContent >> itemLabelFor: labelText [ + ^ BrLabel new + aptitude: BrGlamorousLabelAptitude; + text: labelText; + hMatchParent; + yourself +] + +{ #category : #accessing } +GtAbstractCoderContextMenuContent >> itemPadding [ + ^ BlInsets top: 2 bottom: 2 +] + +{ #category : #accessing } +GtAbstractCoderContextMenuContent >> menuItemStencil [ + self subclassResponsibility +] + +{ #category : #accessing } +GtAbstractCoderContextMenuContent >> selectionAptitude [ + ^ BrStyleCommonAptitude new + selected: [ :aStyle | aStyle background: self theme item selectedColor ]; + hovered: [ :aStyle | aStyle background: self theme item selectedColor ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationModelElement.class.st b/src/GToolkit-Coder-UI/GtAbstractCoderElement.class.st similarity index 58% rename from src/GToolkit-Coder-UI/GtCoderNavigationModelElement.class.st rename to src/GToolkit-Coder-UI/GtAbstractCoderElement.class.st index a8c5bd952..bb113726d 100644 --- a/src/GToolkit-Coder-UI/GtCoderNavigationModelElement.class.st +++ b/src/GToolkit-Coder-UI/GtAbstractCoderElement.class.st @@ -1,13 +1,16 @@ " I am an abstract class. -I provide basic {{gtMethod:GtCoderNavigationModelElement>>#navigationModel:|label=navigation}} accessors, model {{gtMethod:GtCoderNavigationModelElement>>#subscribeToNavigationModel|label=subscriptons}}, and model {{gtMethod:GtCoderNavigationModelElement>>#onNavigationModelChanged|label=instance changes}}. -See my {{gtClass:GtCoderNavigationModelElement|expanded|show=#gtSubclassesFor:}} for concrete implementations: +I am an {{gtClass:BlElement}}. +I provide basic {{gtClass:TBrLayoutResizable}} methods. +See my {{gtClass:GtCoderElement|expanded|show=#gtSubclassesFor:}} for concrete implementations: " Class { - #name : #GtCoderNavigationModelElement, - #superclass : #GtCoderElement, + #name : #GtAbstractCoderElement, + #superclass : #BlElement, + #traits : 'TBrLayoutResizable', + #classTraits : 'TBrLayoutResizable classTrait', #instVars : [ 'navigationModel' ], @@ -15,7 +18,7 @@ Class { } { #category : #'private - asserting' } -GtCoderNavigationModelElement >> assertNavigationModel: aGtCoderNavigationModel [ +GtAbstractCoderElement >> assertNavigationModel: aGtCoderNavigationModel [ self assert: [ aGtCoderNavigationModel isNotNil ] description: [ 'Navigation model must be non-nil' ]. @@ -26,20 +29,20 @@ GtCoderNavigationModelElement >> assertNavigationModel: aGtCoderNavigationModel ] { #category : #defaults } -GtCoderNavigationModelElement >> defaultNavigationModel [ +GtAbstractCoderElement >> defaultNavigationModel [ ^ GtCoderNavigationModel null ] { #category : #initialization } -GtCoderNavigationModelElement >> initialize [ +GtAbstractCoderElement >> initialize [ super initialize. + self matchParent. navigationModel := self defaultNavigationModel. - self initializeListeners. - + self initializeListeners ] { #category : #initialization } -GtCoderNavigationModelElement >> initializeListeners [ +GtAbstractCoderElement >> initializeListeners [ self when: GtCoderToReplace do: [ :anEvent | self replaceFrom: anEvent ]. @@ -54,15 +57,14 @@ GtCoderNavigationModelElement >> initializeListeners [ ] { #category : #'api - accessing' } -GtCoderNavigationModelElement >> navigationModel [ +GtAbstractCoderElement >> navigationModel [ ^ navigationModel ] { #category : #'api - accessing' } -GtCoderNavigationModelElement >> navigationModel: aGtCoderNavigationModel [ +GtAbstractCoderElement >> navigationModel: aGtCoderNavigationModel [ navigationModel = aGtCoderNavigationModel ifTrue: [ ^ self ]. - self assertNavigationModel: aGtCoderNavigationModel. self unsubscribeFromNavigationModel. navigationModel := aGtCoderNavigationModel. self subscribeToNavigationModel. @@ -70,42 +72,41 @@ GtCoderNavigationModelElement >> navigationModel: aGtCoderNavigationModel [ ] { #category : #'private - hooks' } -GtCoderNavigationModelElement >> onNavigationModelChanged [ +GtAbstractCoderElement >> onNavigationModelChanged [ "Subclasses can react to navigation model changes." ] { #category : #'api - updating' } -GtCoderNavigationModelElement >> pushCoder: aCoderOrCoders [ - self assertNavigationModel: self navigationModel. +GtAbstractCoderElement >> pushCoder: aCoderOrCoders [ self navigationModel selectCoder: aCoderOrCoders ] { #category : #'private - event handling' } -GtCoderNavigationModelElement >> replaceFrom: aGtCoderToReplaceEvent [ +GtAbstractCoderElement >> replaceFrom: aGtCoderToReplaceEvent [ aGtCoderToReplaceEvent consumed: true. self pushCoder: aGtCoderToReplaceEvent coder ] { #category : #'private - event handling' } -GtCoderNavigationModelElement >> spawnFrom: aGtCoderToSpawnEvent [ +GtAbstractCoderElement >> spawnFrom: aGtCoderToSpawnEvent [ aGtCoderToSpawnEvent consumed: true. self phlow spawnTool: (GtReadyCoderTool coder: (aGtCoderToSpawnEvent coder)) ] { #category : #'private - event handling' } -GtCoderNavigationModelElement >> spawnInSpaceFrom: aGtCoderToSpawnInSpaceEvent [ +GtAbstractCoderElement >> spawnInSpaceFrom: aGtCoderToSpawnInSpaceEvent [ aGtCoderToSpawnInSpaceEvent consumed: true. ] { #category : #'private - subscriptions' } -GtCoderNavigationModelElement >> subscribeToNavigationModel [ +GtAbstractCoderElement >> subscribeToNavigationModel [ "Subclasses can subscribe to the navigation model" ] { #category : #'private - subscriptions' } -GtCoderNavigationModelElement >> unsubscribeFromNavigationModel [ +GtAbstractCoderElement >> unsubscribeFromNavigationModel [ self navigationModel = GtCoderNavigationModel null ifTrue: [ ^ self ]. self navigationModel unsubscribe: self. ] diff --git a/src/GToolkit-Coder-UI/GtAbstractRemovePreviewStencil.class.st b/src/GToolkit-Coder-UI/GtAbstractRemovePreviewStencil.class.st new file mode 100644 index 000000000..44c27ba7b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtAbstractRemovePreviewStencil.class.st @@ -0,0 +1,119 @@ +Class { + #name : #GtAbstractRemovePreviewStencil, + #superclass : #BrStencil, + #instVars : [ + 'anElement' + ], + #category : #'GToolkit-Coder-UI-Utilities' +} + +{ #category : #accessing } +GtAbstractRemovePreviewStencil >> anElement [ + ^ anElement +] + +{ #category : #accessing } +GtAbstractRemovePreviewStencil >> anElement: element [ + ^ anElement := element +] + +{ #category : #accessing } +GtAbstractRemovePreviewStencil >> createPreviewContainerForItemNamed: itemName withReferences: references [ + | referencesButton container stillReferencedLabel noReferencesLabel waitingLabel | + noReferencesLabel := self noReferencesLabel. + noReferencesLabel + text: itemName asRopedText bold , ' is not referenced' asRopedText. + waitingLabel := self waitingLabel. + stillReferencedLabel := self stillReferencedLabelFor: itemName. + referencesButton := self referencesButtonFor: references. + + container := BrHorizontalPane new + fitContent; + withAsyncSinkDo: [ :anElementSink | + anElementSink + sink: AsyncCounterSink new; + whenPending: [ :aContainer :aSink | + aSink count > 0 + ifTrue: [ waitingLabel visibility: BlVisibility gone. + stillReferencedLabel visibility: BlVisibility visible. + referencesButton visibility: BlVisibility visible ]. + + referencesButton + label: (String + streamContents: [ :aStream | + aStream + print: aSink count; + space; + nextPutAll: ('place' asPluralBasedOn: aSink count); + nextPutAll: '...' ]) ]; + whenSuccess: [ :aContainer :aSink | + waitingLabel visibility: BlVisibility gone. + + aSink count > 0 + ifTrue: [ stillReferencedLabel visibility: BlVisibility visible. + referencesButton visibility: BlVisibility visible ] + ifFalse: [ stillReferencedLabel visibility: BlVisibility gone. + referencesButton visibility: BlVisibility gone. + noReferencesLabel visibility: BlVisibility visible ]. + + referencesButton + label: (String + streamContents: [ :aStream | + aStream + print: aSink count; + space; + nextPutAll: ('place' asPluralBasedOn: aSink count) ]) ] ]. + + container asyncSink forwardStream: references asAsyncStream. + + container + addChildren: {waitingLabel. + stillReferencedLabel visibility: BlVisibility gone. + referencesButton visibility: BlVisibility gone. + noReferencesLabel visibility: BlVisibility gone}. + ^ container +] + +{ #category : #accessing } +GtAbstractRemovePreviewStencil >> noReferencesLabel [ + | noReferencesLabel | + noReferencesLabel := BrLabel new + margin: (BlInsets all: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: 'Remove?' asRopedText. + ^ noReferencesLabel +] + +{ #category : #accessing } +GtAbstractRemovePreviewStencil >> referencesButtonFor: references [ + ^ BrButton new + aptitude: BrGlamorousLinkSquaredButtonWithLabelAptitude new glamorousCodeSmallSize; + beSmallSize; + margin: (BlInsets + top: 10 + left: 2 + bottom: 10 + right: 10); + label: '0 methods'; + action: [ self anElement phlow spawnObject: references ] +] + +{ #category : #accessing } +GtAbstractRemovePreviewStencil >> stillReferencedLabelFor: name [ + ^ BrLabel new + margin: (BlInsets + top: 10 + left: 10 + bottom: 10 + right: 0); + aptitude: BrGlamorousLabelAptitude new; + text: name asRopedText bold , ' is still referenced in ' asRopedText +] + +{ #category : #accessing } +GtAbstractRemovePreviewStencil >> waitingLabel [ + ^ BrLabel new + aptitude: BrGlamorousLabelAptitude new italic; + margin: (BlInsets all: 10); + text: 'Searching references...' +] diff --git a/src/GToolkit-Coder-UI/GtBehaviorCoderActionsElement.class.st b/src/GToolkit-Coder-UI/GtBehaviorCoderActionsElement.class.st new file mode 100644 index 000000000..8bcf10400 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtBehaviorCoderActionsElement.class.st @@ -0,0 +1,160 @@ +Class { + #name : #GtBehaviorCoderActionsElement, + #superclass : #GtCoderActionsElement, + #instVars : [ + 'behaviorCoderViewModel', + 'toolbarElement', + 'previewsElement', + 'addOnsElementFuture' + ], + #category : #'GToolkit-Coder-UI-Coder - Basic' +} + +{ #category : #accessing } +GtBehaviorCoderActionsElement >> addPreviews [ + previewsElement addChildren: + (self coderViewModel previews + collect: [ :aGtCoderPreview | + | addOnPreviewElement | + addOnPreviewElement := aGtCoderPreview stencil asElement . + aGtCoderPreview dataBinder + element: addOnPreviewElement; + coderViewModel: self behaviorCoderViewModel; + build. + + addOnPreviewElement]) +] + +{ #category : #accessing } +GtBehaviorCoderActionsElement >> addToolbarActions [ + toolbarElement addItems: + (self coderViewModel mainActions + collect: [ :aGtCoderAction | + self flag: 'Temporary hack. Coder should to Phlow actions'. + aGtCoderAction buildElementIn: self ]) +] + +{ #category : #accessing } +GtBehaviorCoderActionsElement >> behaviorCoderViewModel [ + ^ behaviorCoderViewModel +] + +{ #category : #accessing } +GtBehaviorCoderActionsElement >> behaviorCoderViewModel: aCoderViewModel [ + + behaviorCoderViewModel ifNotNil: [ self unsubscribeFromBehaviourCoderViewModel ]. + + behaviorCoderViewModel := aCoderViewModel. + + self subscribeToBehaviorCoderViewModel. + self onBehaviourCoderViewModelChanged +] + +{ #category : #accessing } +GtBehaviorCoderActionsElement >> coderViewModel [ + ^ self behaviorCoderViewModel +] + +{ #category : #accessing } +GtBehaviorCoderActionsElement >> coderViewModel: aCoderViewModel [ + self behaviorCoderViewModel: aCoderViewModel +] + +{ #category : #initialization } +GtBehaviorCoderActionsElement >> initialize [ + super initialize. + + self layout: BlLinearLayout horizontal. + self padding: (BlInsets empty). + self constraintsDo: [ :c | + c horizontal fitContent. + c vertical fitContent ]. + + previewsElement := BrHorizontalPane new + fitContent; + alignCenterLeft; + cellSpacing: 2. + + toolbarElement := self newToolbarElement + labeled: 'Main toolbar'. + + addOnsElementFuture := (BrAsyncElementFuture on: self) + executionConfiguration: (GtSingleCoderViewModel behaviourAddOnsExecutionConfiguration); + whenSuccess: [ :anEditorElement :theAddOns | + self behaviorCoderViewModel onAddOnsChanged: theAddOns ]. + + self addChildren: { previewsElement. toolbarElement } +] + +{ #category : #'building - widgets' } +GtBehaviorCoderActionsElement >> newToolbarElement [ + + + ^ BrToolbar new + aptitude: (BrGlamorousToolbarAptitude new + spacing: 0); + padding: (BlInsets empty); + margin: (BlInsets empty) +] + +{ #category : #callbacks } +GtBehaviorCoderActionsElement >> onBehaviourCoderViewModelChanged [ + self updatePreviews. + self updateToolbarActions. + addOnsElementFuture future: self behaviorCoderViewModel addOnsFuture +] + +{ #category : #'event handling' } +GtBehaviorCoderActionsElement >> onViewModelRecomputeAddOns: aRecomputeAddOnRequest [ + aRecomputeAddOnRequest coderViewModel == self behaviorCoderViewModel + ifFalse: [ ^ self ]. + + addOnsElementFuture future: self behaviorCoderViewModel addOnsFuture +] + +{ #category : #updating } +GtBehaviorCoderActionsElement >> removePreviews [ + previewsElement removeChildren +] + +{ #category : #updating } +GtBehaviorCoderActionsElement >> removeToolbarActions [ + toolbarElement numberOfItems + timesRepeat: [ toolbarElement removeItemAt: 1 ] +] + +{ #category : #subscriptions } +GtBehaviorCoderActionsElement >> subscribeToBehaviorCoderViewModel [ + self behaviorCoderViewModel weak + when: GtPharoBehaviorCoderViewModelAddonsChangedAnnouncement + send: #updateContent + to: self; + when: GtCoderViewModelRecomputeAddOnRequest + send: #onViewModelRecomputeAddOns: + to: self +] + +{ #category : #subscriptions } +GtBehaviorCoderActionsElement >> unsubscribeFromBehaviourCoderViewModel [ + self behaviorCoderViewModel unsubscribe: self +] + +{ #category : #updating } +GtBehaviorCoderActionsElement >> updateContent [ + self enqueueTask: (BlTaskAction new + action: [ + self updatePreviews. + self updateToolbarActions ]) +] + +{ #category : #updating } +GtBehaviorCoderActionsElement >> updatePreviews [ + self removePreviews. + self addPreviews +] + +{ #category : #updating } +GtBehaviorCoderActionsElement >> updateToolbarActions [ + self removeToolbarActions. + self addToolbarActions +] diff --git a/src/GToolkit-Coder-UI/GtBehaviorCoderBehaviorNameApplyPreviewId.class.st b/src/GToolkit-Coder-UI/GtBehaviorCoderBehaviorNameApplyPreviewId.class.st index 811cd432e..a76836e53 100644 --- a/src/GToolkit-Coder-UI/GtBehaviorCoderBehaviorNameApplyPreviewId.class.st +++ b/src/GToolkit-Coder-UI/GtBehaviorCoderBehaviorNameApplyPreviewId.class.st @@ -6,7 +6,7 @@ A button that appears when behavior name is modified to open a popup with the re Class { #name : #GtBehaviorCoderBehaviorNameApplyPreviewId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtBehaviorCoderBehaviorNameId.class.st b/src/GToolkit-Coder-UI/GtBehaviorCoderBehaviorNameId.class.st index 04cd3e513..96d362ded 100644 --- a/src/GToolkit-Coder-UI/GtBehaviorCoderBehaviorNameId.class.st +++ b/src/GToolkit-Coder-UI/GtBehaviorCoderBehaviorNameId.class.st @@ -6,7 +6,7 @@ An editable label with behavior's name Class { #name : #GtBehaviorCoderBehaviorNameId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtBehaviorDefinitionCoderUIModel.class.st b/src/GToolkit-Coder-UI/GtBehaviorDefinitionCoderUIModel.class.st deleted file mode 100644 index 334250827..000000000 --- a/src/GToolkit-Coder-UI/GtBehaviorDefinitionCoderUIModel.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtBehaviorDefinitionCoderUIModel, - #superclass : #GtSingleCoderViewModel, - #category : #'GToolkit-Coder-UI-Coder - Behavior-Definition Model' -} - -{ #category : #accessing } -GtBehaviorDefinitionCoderUIModel >> elementClass [ - ^ GtPharoBehaviorDefinitionCoderElement -] diff --git a/src/GToolkit-Coder-UI/GtBehaviorDefinitionCoderViewModel.class.st b/src/GToolkit-Coder-UI/GtBehaviorDefinitionCoderViewModel.class.st new file mode 100644 index 000000000..0a461fee3 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtBehaviorDefinitionCoderViewModel.class.st @@ -0,0 +1,35 @@ +Class { + #name : #GtBehaviorDefinitionCoderViewModel, + #superclass : #GtSingleCoderViewModel, + #category : #'GToolkit-Coder-UI-Coder - Behavior-Definition Model' +} + +{ #category : #accessing } +GtBehaviorDefinitionCoderViewModel >> acceptChanges [ + self coder acceptChanges +] + +{ #category : #accessing } +GtBehaviorDefinitionCoderViewModel >> discardChanges [ + self coder discardChanges +] + +{ #category : #accessing } +GtBehaviorDefinitionCoderViewModel >> elementClass [ + ^ GtPharoBehaviorDefinitionCoderElement +] + +{ #category : #accessing } +GtBehaviorDefinitionCoderViewModel >> onClassChanged: anAnnouncment [ + self announce: anAnnouncment +] + +{ #category : #accessing } +GtBehaviorDefinitionCoderViewModel >> subscribeToCoderModel [ + super subscribeToCoderModel. + + self coderModel weak + when: GtCoderClassChanged + send: #onClassChanged: + to: self +] diff --git a/src/GToolkit-Coder-UI/GtCategoryToggleAptitude.class.st b/src/GToolkit-Coder-UI/GtCategoryToggleAptitude.class.st deleted file mode 100644 index 2548de648..000000000 --- a/src/GToolkit-Coder-UI/GtCategoryToggleAptitude.class.st +++ /dev/null @@ -1,20 +0,0 @@ -Class { - #name : #GtCategoryToggleAptitude, - #superclass : #GtCoderToggleAptitude, - #category : #'GToolkit-Coder-UI-Looks' -} - -{ #category : #accessing } -GtCategoryToggleAptitude >> initialize [ - super initialize. - self - addChangeProperty: #(widget layout) - with: [ BlLinearLayout horizontal alignCenter ]. - self - addChangeProperty: #(widget constraints horizontal resizer) - with: BlLayoutResizer fitContent. - self - addChangeProperty: #(widget constraints vertical resizer) - with: [ BlLayoutResizer exact: 16 ]. - self addChangeProperty: #(widget padding) with: [ BlInsets all: 6 ] -] diff --git a/src/GToolkit-Coder-UI/GtClassBreadcrumbToggleAptitude.class.st b/src/GToolkit-Coder-UI/GtClassBreadcrumbToggleAptitude.class.st deleted file mode 100644 index a09a9f4ce..000000000 --- a/src/GToolkit-Coder-UI/GtClassBreadcrumbToggleAptitude.class.st +++ /dev/null @@ -1,25 +0,0 @@ -Class { - #name : #GtClassBreadcrumbToggleAptitude, - #superclass : #BrAptitude, - #category : #'GToolkit-Coder-UI-Looks' -} - -{ #category : #accessing } -GtClassBreadcrumbToggleAptitude >> initialize [ - super initialize. - self - addChangeProperty: #(widget layout) - with: [ BlLinearLayout horizontal alignCenter ]. - self - addChangeProperty: #(widget constraints horizontal resizer) - with: BlLayoutResizer fitContent. - - self add: (BrGlamorousLabelAptitude new - foreground: Color black; - yourself). - - self add: (BrToggleAptitude new - // #label; - activated: [ :aStyle | aStyle foreground: Color black ]; - deactivated: [ :aStyle | aStyle foreground: Color gray ]) -] diff --git a/src/GToolkit-Coder-UI/GtClassCardElement.class.st b/src/GToolkit-Coder-UI/GtClassCardElement.class.st index e4139861a..58601bf76 100644 --- a/src/GToolkit-Coder-UI/GtClassCardElement.class.st +++ b/src/GToolkit-Coder-UI/GtClassCardElement.class.st @@ -7,7 +7,7 @@ Class { { #category : #'building ui' } GtClassCardElement >> detailsLabel [ | comment | - comment := coder theClass organization comment. + comment := coder theClass gtComment. comment size > 100 ifTrue: [ comment := (comment first: 100) , '...' ]. comment := (comment replaceAllRegex: '[[:space:]]+' with: String space) diff --git a/src/GToolkit-Coder-UI/GtClassCoderTool.class.st b/src/GToolkit-Coder-UI/GtClassCoderTool.class.st index 41c37ed97..7e804f62a 100644 --- a/src/GToolkit-Coder-UI/GtClassCoderTool.class.st +++ b/src/GToolkit-Coder-UI/GtClassCoderTool.class.st @@ -1,6 +1,8 @@ Class { #name : #GtClassCoderTool, #superclass : #GtCoderTool, + #traits : 'TGtPhlowWithSelfObjectHolder', + #classTraits : 'TGtPhlowWithSelfObjectHolder classTrait', #instVars : [ 'observedClass' ], @@ -12,6 +14,13 @@ GtClassCoderTool class >> forClass: aClass [ ^ self new observedClass: aClass ] +{ #category : #'instance creation' } +GtClassCoderTool class >> forClass: aClass selfObjectHolder: aSelfObjectHolder [ + ^ self new + observedClass: aClass; + selfObjectHolder: aSelfObjectHolder +] + { #category : #'instance creation' } GtClassCoderTool class >> observedClass: aClass [ ^ self new observedClass: aClass @@ -19,7 +28,9 @@ GtClassCoderTool class >> observedClass: aClass [ { #category : #converting } GtClassCoderTool >> newCoder [ - ^ GtCoder forClass: self observedClass instanceSide + ^ self + ifSelfObject: [ :aSelfObject | GtCoderElement forObject: aSelfObject ] + ifNone: [ GtCoderElement forClass: self observedClass instanceSide ] ] { #category : #accessing } @@ -37,3 +48,8 @@ GtClassCoderTool >> observedClass [ GtClassCoderTool >> observedClass: anObject [ observedClass := anObject ] + +{ #category : #'api - accessing' } +GtClassCoderTool >> title [ + ^ observedClass ifNil: [ super name ] ifNotNil: [ :aClass | aClass name ] +] diff --git a/src/GToolkit-Coder-UI/GtClassesCoderUIModel.class.st b/src/GToolkit-Coder-UI/GtClassesCoderViewModel.class.st similarity index 67% rename from src/GToolkit-Coder-UI/GtClassesCoderUIModel.class.st rename to src/GToolkit-Coder-UI/GtClassesCoderViewModel.class.st index 6201e1c55..035ee8e25 100644 --- a/src/GToolkit-Coder-UI/GtClassesCoderUIModel.class.st +++ b/src/GToolkit-Coder-UI/GtClassesCoderViewModel.class.st @@ -1,10 +1,10 @@ Class { - #name : #GtClassesCoderUIModel, + #name : #GtClassesCoderViewModel, #superclass : #GtMultipleCodersViewModel, #category : #'GToolkit-Coder-UI-Coder - Classes Model' } { #category : #accessing } -GtClassesCoderUIModel >> elementClass [ +GtClassesCoderViewModel >> elementClass [ ^ GtClassesElement ] diff --git a/src/GToolkit-Coder-UI/GtClassesElement.class.st b/src/GToolkit-Coder-UI/GtClassesElement.class.st index 44575b807..7f73294d6 100644 --- a/src/GToolkit-Coder-UI/GtClassesElement.class.st +++ b/src/GToolkit-Coder-UI/GtClassesElement.class.st @@ -16,7 +16,7 @@ GtClassesElement >> buildContainer [ itemStencil: [ GtClassCardElement new ]; itemDataBinder: [ :eachClassCard :eachClassCoder | eachClassCard coder: eachClassCoder ]; items: (coder coders asSortedCollection: [ :a :b | a theClass name < b theClass name ]); - addEventFilterOn: BlClickEvent do: [ :anEvent | self requestFocus ]. + addEventFilterOn: BlClickEvent do: [ :anEvent | anEvent currentTarget requestFocus ]. self addChild: aClassList ] diff --git a/src/GToolkit-Coder-UI/GtCodeDefinitionEntityViewer.class.st b/src/GToolkit-Coder-UI/GtCodeDefinitionEntityViewer.class.st index 560417a36..724ae068a 100644 --- a/src/GToolkit-Coder-UI/GtCodeDefinitionEntityViewer.class.st +++ b/src/GToolkit-Coder-UI/GtCodeDefinitionEntityViewer.class.st @@ -40,6 +40,11 @@ GtCodeDefinitionEntityViewer >> definition: anObject [ definition := anObject ] +{ #category : #accessing } +GtCodeDefinitionEntityViewer >> definitionsDo: aBlock [ + aBlock cull: self definition +] + { #category : #initialization } GtCodeDefinitionEntityViewer >> initialize [ super initialize. @@ -86,6 +91,19 @@ GtCodeDefinitionEntityViewer >> when: aBlEvent do: aBlock [ eventHandlers add: (aBlEvent -> aBlock) ] +{ #category : #interactions } +GtCodeDefinitionEntityViewer >> whenAltClickDo: aBlock [ + "aBlock is in form [ :anEvent :aCodeDefinition ]" + + self + when: BlClickEvent + do: [ :anEvent :aDefinition | + anEvent modifiers isAlt + ifTrue: [ + anEvent consumed: true. + aBlock cull: anEvent cull: aDefinition ] ] +] + { #category : #interactions } GtCodeDefinitionEntityViewer >> whenClickDo: aBlock [ "I do not react to primary+click" diff --git a/src/GToolkit-Coder-UI/GtCodeDefinitionPluralEditor.class.st b/src/GToolkit-Coder-UI/GtCodeDefinitionPluralEditor.class.st deleted file mode 100644 index 588d4bf17..000000000 --- a/src/GToolkit-Coder-UI/GtCodeDefinitionPluralEditor.class.st +++ /dev/null @@ -1,135 +0,0 @@ -Class { - #name : #GtCodeDefinitionPluralEditor, - #superclass : #GtCodeDefinitionPluralViewer, - #instVars : [ - 'addSelector', - 'renameSelector', - 'removeSelector' - ], - #category : #'GToolkit-Coder-UI-Behaviour - Editor / Reader' -} - -{ #category : #accessing } -GtCodeDefinitionPluralEditor >> addSelector [ - ^ addSelector -] - -{ #category : #accessing } -GtCodeDefinitionPluralEditor >> addSelector: anObject [ - addSelector := anObject -] - -{ #category : #'api - instantiation' } -GtCodeDefinitionPluralEditor >> create [ - - | aDefinition anOwner theInitialNames addAction removeAction renameAction aTagger | - - aDefinition := self definition. - anOwner := self owner. - - theInitialNames := aDefinition perform: getSelector withEnoughArguments: { anOwner }. - - addAction := [ :aNewTag | - aNewTag name - ifNotEmpty: [ aDefinition perform: addSelector withEnoughArguments: { aNewTag name . anOwner } ] ]. - - removeAction := [ :aTag | aDefinition perform: removeSelector withEnoughArguments: { aTag name . anOwner } ]. - - renameAction := [ :aTag :aNewName | - aNewName trimmed - ifEmpty: [ removeAction value: aTag name ] - ifNotEmpty: [ :aTrimmedName | - aDefinition perform: renameSelector withEnoughArguments: { aTag name . aTrimmedName . anOwner } ] ]. - - aTagger := BrTagger new - aptitude: (BrGlamorousTaggerEditableAptitude new - margin: self margin; - tagLabel: [ :aTaggerTag | - | anEditableLabel eachLabelDefinition | - - eachLabelDefinition := aTaggerTag ifNotNil: [ - modelSelector ifNotNil: [ :aSelector | - aTaggerTag name - ifEmpty: [ nil ] - ifNotEmpty: [ :aTagName | aDefinition perform: aSelector withEnoughArguments: { aTaggerTag name } ] ] ]. - - anEditableLabel := BrEditableLabel new - text: aTaggerTag name; - styler: styler; - inputFilter: inputFilter; - aptitude: lookStencil create; - yourself. - - interactions do: [ :eachAssociation | - anEditableLabel - whenKey: eachAssociation key - labelDo: [ :aShortcutEvent :aShortcut | - aShortcutEvent consumed: true. - eachLabelDefinition - ifNotNil: [ eachAssociation value cull: aShortcutEvent cull: eachLabelDefinition ] ] ]. - - eventHandlers do: [ :eachAssociation | - anEditableLabel - when: eachAssociation key - do: [ :anEvent | - anEvent currentTarget isReadOnly - ifTrue: [ - eachLabelDefinition - ifNotNil: [ eachAssociation value cull: anEvent cull: eachLabelDefinition ] ] ] ]. - - completionStencil ifNotNil: [ :aStencil | - (GtCompletionController - on: anEditableLabel - strategy: aStencil create) install ]. - - anEditableLabel ]; - in: [ :aLook | separatorStencil ifNotNil: [ aLook separator: separatorStencil ] ]; - yourself); - namedTags: theInitialNames; - when: BrTaggerAddTagRequest do: [ :aRequest | addAction value: aRequest tag ]; - when: BrTaggerRemoveTagRequest do: [ :aRequest | removeAction value: aRequest tag ]; - when: BrTaggerRenameTagRequest do: [ :aRequest | renameAction value: aRequest tag value: aRequest newName ]. - - self containerName - ifNotNil: [ :aContainerName | aTagger containerName: aContainerName ]. - - aDefinition - when: addAnnouncement - do: [ :anEvent | - (anOwner isNil or: [ anEvent owner = anOwner ]) - ifTrue: [ aTagger addTagNamed: anEvent name ] ]. - - aDefinition - when: removeAnnouncement - do: [ :anEvent | - (anOwner isNil or: [ anEvent owner = anOwner ]) - ifTrue: [ aTagger removeTagNamed: anEvent name ] ]. - - aDefinition - when: renameAnnouncement - do: [ :anEvent | - (anOwner isNil or: [ anEvent owner = anOwner ]) - ifTrue: [ aTagger renameTagNamed: anEvent oldName to: anEvent newName ] ]. - - ^ aTagger -] - -{ #category : #accessing } -GtCodeDefinitionPluralEditor >> removeSelector [ - ^ removeSelector -] - -{ #category : #accessing } -GtCodeDefinitionPluralEditor >> removeSelector: anObject [ - removeSelector := anObject -] - -{ #category : #accessing } -GtCodeDefinitionPluralEditor >> renameSelector [ - ^ renameSelector -] - -{ #category : #accessing } -GtCodeDefinitionPluralEditor >> renameSelector: anObject [ - renameSelector := anObject -] diff --git a/src/GToolkit-Coder-UI/GtCodeDefinitionSingleEditor.class.st b/src/GToolkit-Coder-UI/GtCodeDefinitionSingleEditor.class.st index 777794c53..15da55291 100644 --- a/src/GToolkit-Coder-UI/GtCodeDefinitionSingleEditor.class.st +++ b/src/GToolkit-Coder-UI/GtCodeDefinitionSingleEditor.class.st @@ -75,7 +75,8 @@ GtCodeDefinitionSingleEditor >> create [ when: renameAnnouncement do: [ :anEvent | (anOwner isNil or: [ anEvent owner = anOwner ]) - ifTrue: [ anEditableLabel text: anEvent newName ] ] ]. + ifTrue: [ anEditableLabel text: anEvent newName ] ] + for: self ]. ^ anEditableLabel ] diff --git a/src/GToolkit-Coder-UI/GtCodeDefinitionSingleReader.class.st b/src/GToolkit-Coder-UI/GtCodeDefinitionSingleReader.class.st index 1b2c9c574..a1c76ead2 100644 --- a/src/GToolkit-Coder-UI/GtCodeDefinitionSingleReader.class.st +++ b/src/GToolkit-Coder-UI/GtCodeDefinitionSingleReader.class.st @@ -18,7 +18,6 @@ GtCodeDefinitionSingleReader >> create [ fitContent; aptitude: lookStencil create; margin: self margin; - focusability: BlFocusability none; text: theInitialName. self containerName @@ -42,7 +41,8 @@ GtCodeDefinitionSingleReader >> create [ when: renameAnnouncement do: [ :anEvent | (anOwner isNil or: [ anEvent owner = anOwner ]) - ifTrue: [ aLabel text: anEvent newName ] ] ]. + ifTrue: [ aLabel text: anEvent newName ] ] + for: self ]. ^ aLabel ] diff --git a/src/GToolkit-Coder-UI/GtCodeDefinitionViewer.class.st b/src/GToolkit-Coder-UI/GtCodeDefinitionViewer.class.st index be2c688b8..de215aed3 100644 --- a/src/GToolkit-Coder-UI/GtCodeDefinitionViewer.class.st +++ b/src/GToolkit-Coder-UI/GtCodeDefinitionViewer.class.st @@ -25,6 +25,11 @@ GtCodeDefinitionViewer >> buildSectionLabel: aSectionName [ constraintsDo: [ :c | c grid horizontal alignLeft ] ] +{ #category : #accessing } +GtCodeDefinitionViewer >> definitionsDo: aBlock [ + self subclassResponsibility +] + { #category : #initialization } GtCodeDefinitionViewer >> initialize [ super initialize. diff --git a/src/GToolkit-Coder-UI/GtCodeDiffBuilder.class.st b/src/GToolkit-Coder-UI/GtCodeDiffBuilder.class.st new file mode 100644 index 000000000..a786cd700 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCodeDiffBuilder.class.st @@ -0,0 +1,31 @@ +Class { + #name : #GtCodeDiffBuilder, + #superclass : #TextDiffBuilder, + #instVars : [ + 'isMethod' + ], + #category : #'GToolkit-Coder-UI-Diff' +} + +{ #category : #initialization } +GtCodeDiffBuilder >> initialize [ + super initialize. + isMethod := true +] + +{ #category : #accessing } +GtCodeDiffBuilder >> isMethod [ + ^ isMethod +] + +{ #category : #accessing } +GtCodeDiffBuilder >> isMethod: aBoolean [ + isMethod := aBoolean +] + +{ #category : #'instance creation' } +GtCodeDiffBuilder >> newStyler [ + ^ GtCodeDiffRBTextStyler new + isForWorkspace: isMethod not; + yourself +] diff --git a/src/GToolkit-Coder-UI/GtCodeDiffRBTextStyler.class.st b/src/GToolkit-Coder-UI/GtCodeDiffRBTextStyler.class.st new file mode 100644 index 000000000..a164c1cf8 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCodeDiffRBTextStyler.class.st @@ -0,0 +1,23 @@ +Class { + #name : #GtCodeDiffRBTextStyler, + #superclass : #BrRBTextStyler, + #category : #'GToolkit-Coder-UI-Diff' +} + +{ #category : #private } +GtCodeDiffRBTextStyler >> resolveStyleFor: aVariableNode [ + | value | + value := super resolveStyleFor: aVariableNode. + ^ value = #invalid + ifTrue: [ #default ] + ifFalse: [ value ] +] + +{ #category : #private } +GtCodeDiffRBTextStyler >> resolveStyleOpenBracket: aVariableNode [ + | value | + value := super resolveStyleOpenBracket: aVariableNode. + ^ value = #invalid + ifTrue: [ #default ] + ifFalse: [ value ] +] diff --git a/src/GToolkit-Coder-UI/GtCoder.class.st b/src/GToolkit-Coder-UI/GtCoder.class.st index 2e5e95736..452cab948 100644 --- a/src/GToolkit-Coder-UI/GtCoder.class.st +++ b/src/GToolkit-Coder-UI/GtCoder.class.st @@ -1,162 +1,8 @@ " -I am the entrance point into the coder as a tool. To instantiate me, use one of the following class-side methods: -- {{gtMethod:GtCoder class>>#forObject:|label=#selector}}, -- {{gtMethod:GtCoder class>>#forClass:|label=#selector}}, -- {{gtMethod:GtCoder class>>#forMethod:|label=#selector}}, -- {{gtMethod:GtCoder class>>#forPackage:|label=#selector}}, -- {{gtMethod:GtCoder class>>#new|label=#selector}}. -1. # Example - -{{gtExample:GtCoderElementExamples>>#coderElementWithObject|previewExpanded|codeExpanded=false|previewHeight=700}} - - +This class is deprecated. You should use GtCoderElement instead " Class { #name : #GtCoder, - #superclass : #GtCoderNavigationModelElement, - #traits : 'TGtPagerPageInstantiator + TGtPhlowToolDetailAdjustable', - #classTraits : 'TGtPagerPageInstantiator classTrait + TGtPhlowToolDetailAdjustable classTrait', - #instVars : [ - 'sidebarElement', - 'contentElement', - 'toolbarElement', - 'titleNotifier' - ], - #category : #'GToolkit-Coder-UI-! Core' + #superclass : #GtCoderElement, + #category : #'GToolkit-Coder-UI-Deprecated' } - -{ #category : #'private - accessing' } -GtCoder >> contentElement [ - - ^ contentElement -] - -{ #category : #initialization } -GtCoder >> initialize [ - super initialize. - self initializeTitleNotifier. - self addInteractiveModelState: GtPhlowToolDetailModel new. - self initializeContentElement. - self initializeSidebarElement. - self initializeToolbarElement. - self initializeListeners. - - self layout: BlLinearLayout horizontal. - - self addChild: self sidebarElement as: #sidebar. - self addChild: self contentElement as: #content. - self addChild: self toolbarElement as: #toolbar. - - self addAptitude: (BrLayoutResizerAptitude inherit). -] - -{ #category : #initialization } -GtCoder >> initializeContentElement [ - contentElement := GtCoderPlaygroundElement new. -] - -{ #category : #initialization } -GtCoder >> initializeSidebarElement [ - sidebarElement := GtPhlowHorizontalSidebarElement new beLeft. - self addAptitude: (GtPhlowToolDetailAptitude new - normal: [ :theInstance | sidebarElement hide ]; - detailed: [ :theInstance | sidebarElement show ]). -] - -{ #category : #initialization } -GtCoder >> initializeTitleNotifier [ - titleNotifier := GtPhlowTitleIconAndLabelNotifier new - shortLabel: [ self shortTitle ifEmpty: [ 'Coder' asRopedText ] ]; - icon: BrGlamorousVectorIcons emphasizedBrowse. - self addEventHandler: titleNotifier. -] - -{ #category : #initialization } -GtCoder >> initializeToolbarElement [ - toolbarElement := GtCoderToolbarElement new - constraintsDo: [ :c | - c ignoreByLayout. - c ignored horizontal alignRight. - c ignored vertical alignTop ]. -] - -{ #category : #'private - hooks' } -GtCoder >> onNavigationModelChanged [ - super onNavigationModelChanged. - self contentElement navigationModel: self navigationModel. - self toolbarElement navigationModel: self navigationModel. - self updateSidebarContent. - self updateTabLabel -] - -{ #category : #'private - announcement handling' } -GtCoder >> onNavigationModelSubjectChange [ - self enqueueTask: (BlTaskAction new action: [ - self updateTabLabel ]) -] - -{ #category : #opening } -GtCoder >> pagerWindowTitle [ - - ^ self shortTitle -] - -{ #category : #'private - accessing' } -GtCoder >> shortTitle [ - ^ (GtPhlowTitleLabelBuilder - longLabel: self subjectTitle) - build -] - -{ #category : #'private - accessing' } -GtCoder >> sidebarElement [ - - ^ sidebarElement -] - -{ #category : #'private - accessing' } -GtCoder >> subjectTitle [ - - self navigationModel selectedClassDo: [ :aClass | ^ aClass name ]. - self navigationModel selectedTagDo: [ :aPackageTag | ^ aPackageTag packageName, '-', aPackageTag name ]. - self navigationModel selectedPackageDo: [ :aPackage | ^ aPackage name ]. - ^ nil -] - -{ #category : #'private - subscriptions' } -GtCoder >> subscribeToNavigationModel [ - super subscribeToNavigationModel. - self navigationModel weak - when: GtCoderNavigationPackagesSelected, - GtCoderNavigationPackageSelected, - GtCoderNavigationPackageRenamed, - GtCoderNavigationPackageTagSelected, - GtCoderNavigationClassSelected, - GtCoderNavigationClassRenamed, - GtCoderNavigationClassModified - send: #onNavigationModelSubjectChange to: self -] - -{ #category : #'private - accessing' } -GtCoder >> titleNotifier [ - - ^ titleNotifier -] - -{ #category : #'private - accessing' } -GtCoder >> toolbarElement [ - - ^ toolbarElement -] - -{ #category : #'private - updating' } -GtCoder >> updateSidebarContent [ - self sidebarElement - contentStencil: (GtCoderNavigationTabsStencil new - navigationModel: self navigationModel) -] - -{ #category : #'private - updating' } -GtCoder >> updateTabLabel [ - self titleNotifier notifyIfFocused -] diff --git a/src/GToolkit-Coder-UI/GtCoder2.class.st b/src/GToolkit-Coder-UI/GtCoder2.class.st deleted file mode 100644 index 9a75f6bb2..000000000 --- a/src/GToolkit-Coder-UI/GtCoder2.class.st +++ /dev/null @@ -1,148 +0,0 @@ -Class { - #name : #GtCoder2, - #superclass : #BlElement, - #traits : 'TGtPagerPageInstantiator + TGtPhlowToolDetailAdjustable + TGtWithCoderToolViewModel + TBrLayoutResizable', - #classTraits : 'TGtPagerPageInstantiator classTrait + TGtPhlowToolDetailAdjustable classTrait + TGtWithCoderToolViewModel classTrait + TBrLayoutResizable classTrait', - #instVars : [ - 'coderContainer', - 'goBackButton', - 'navigationBreacrumb', - 'navigationSidebar' - ], - #category : #'GToolkit-Coder-UI-! Core' -} - -{ #category : #initialization } -GtCoder2 >> createCoderContainer [ - "A container that contains an actual current coder element. The content of the container - is replaced every time a coder is pushed or popped" - - ^ BrVerticalPane new - matchParent -] - -{ #category : #initialization } -GtCoder2 >> createGoBackButton [ - ^ BrButton new - aptitude: BrGlamorousButtonWithLabelAptitude; - label: 'Go back'; - action: [ self coderToolViewModel popCoderViewModel ] -] - -{ #category : #initialization } -GtCoder2 >> createNavigationBreadcrumb [ - ^ BrHorizontalPane new - hMatchParent; - vFitContent -] - -{ #category : #initialization } -GtCoder2 >> createNavigationSidebar [ - ^ GtPhlowHorizontalSidebarElement new beLeft -] - -{ #category : #initialization } -GtCoder2 >> initialize [ - super initialize. - - self layout: BlLinearLayout horizontal. - self matchParent. - - navigationSidebar := self createNavigationSidebar. - - goBackButton := self createGoBackButton. - navigationBreacrumb := self createNavigationBreadcrumb. - navigationBreacrumb addChild: goBackButton. - - coderContainer := self createCoderContainer. - - self addChildren: { - navigationSidebar. - - BrVerticalPane new - matchParent; - addChildren: { - navigationBreacrumb. - coderContainer } - }. - - self addInteractiveModelState: GtPhlowToolDetailModel new. - self addAptitude: (GtPhlowToolDetailAptitude new - normal: [ :theInstance | navigationSidebar hide ]; - detailed: [ :theInstance | navigationSidebar show ]). - - self - when: GtCoderToReplace - do: [ :anEvent | - anEvent consumed: true. - self coderToolViewModel pushCoderViewModel: anEvent coder asCoderViewModel ]. - - -] - -{ #category : #initialization } -GtCoder2 >> navigationStencil: aStencil [ - navigationSidebar contentStencil: aStencil -] - -{ #category : #'api - coder tool view model' } -GtCoder2 >> onCoderToolViewModelChanged [ - "Is sent when a new coderTool view model is assigned to the element. - Note: #onCoderToolViewModelChanged is sent before #subscribeToCoderToolViewModel - which means that if you perform any operation that triggers an announcement it will be ignored because the receiver - didn't get a chance to subscribe to any announcement. Override #onPostCoderToolViewModelChanged if you - wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" - - self updateGoBackButton. - self rebuildTopCoderElement -] - -{ #category : #'private - event handling' } -GtCoder2 >> onViewModelCoderPopped: anAnnouncement [ - self updateGoBackButton. - self rebuildTopCoderElement. -] - -{ #category : #'private - event handling' } -GtCoder2 >> onViewModelCoderPushed: anAnnouncement [ - self updateGoBackButton. - self rebuildTopCoderElement -] - -{ #category : #opening } -GtCoder2 >> pagerWindowTitle [ - - ^ self shortTitle -] - -{ #category : #'private - updating' } -GtCoder2 >> rebuildTopCoderElement [ - coderContainer removeChildren. - - self coderToolViewModel hasCoderViewModel - ifTrue: [ coderContainer addChild: self coderToolViewModel currentCoderViewModel asElement ] -] - -{ #category : #'api - coder tool view model' } -GtCoder2 >> subscribeToCoderToolViewModel [ - "Is sent after a new coderTool view model is assigned to the element. - It is required to unsubscribe from the view model or domain model by implementing - #unsubscribeFromCoderToolViewModel if elements subscribe to them" - - self coderToolViewModel weak - when: GtCoderToolViewModelCoderPushed send: #onViewModelCoderPushed: to: self; - when: GtCoderToolViewModelCoderPopped send: #onViewModelCoderPopped: to: self -] - -{ #category : #'api - coder tool view model' } -GtCoder2 >> unsubscribeFromCoderToolViewModel [ - "Is sent before a new coderTool view model is assigned to the element. - Elements that subscribe to coderTool view model in domain model are required to implement this methods." - - self coderToolViewModel unsubscribe: self -] - -{ #category : #'private - updating' } -GtCoder2 >> updateGoBackButton [ - goBackButton enabled: self coderToolViewModel canGoBack -] diff --git a/src/GToolkit-Coder-UI/GtCoderActionsElement.class.st b/src/GToolkit-Coder-UI/GtCoderActionsElement.class.st index 7931ca22d..943dda993 100644 --- a/src/GToolkit-Coder-UI/GtCoderActionsElement.class.st +++ b/src/GToolkit-Coder-UI/GtCoderActionsElement.class.st @@ -1,45 +1,17 @@ Class { #name : #GtCoderActionsElement, #superclass : #BlElement, - #traits : 'TGtWithTextualCoderViewModel', - #classTraits : 'TGtWithTextualCoderViewModel classTrait', - #instVars : [ - 'mainToolbar', - 'contextToolbar', - 'separator', - 'editor' - ], #category : #'GToolkit-Coder-UI-Coder - Basic' } -{ #category : #private } -GtCoderActionsElement >> addContextToolbarActions [ - contextToolbar addItems: (self textualCoderViewModel contextActions collect: [ :aGtCoderAction | self newButtonForAction: aGtCoderAction ]). - separator - visibility: - (contextToolbar hasItems - ifTrue: [ BlVisibility visible ] - ifFalse: [ BlVisibility gone ]) -] - -{ #category : #private } -GtCoderActionsElement >> addMainToolbarActions [ - mainToolbar - addItems: - (self textualCoderViewModel mainActions - collect: [ :aGtCoderAction | - self flag: 'Temporary hack. Coder should to Phlow actions'. - aGtCoderAction buildElementIn: self ]) -] - { #category : #accessing } -GtCoderActionsElement >> coderUIModel [ - ^ self textualCoderViewModel +GtCoderActionsElement >> coderViewModel [ + self subclassResponsibility ] { #category : #accessing } -GtCoderActionsElement >> coderUIModel: aCoderUIModel [ - self textualCoderViewModel: aCoderUIModel +GtCoderActionsElement >> coderViewModel: aCoderViewModel [ + self subclassResponsibility ] { #category : #'private - event handling' } @@ -69,53 +41,95 @@ GtCoderActionsElement >> handleButton: aButtonElement action: aGtCoderAction onE GtCoderExecutionContextVariable element: aButtonElement do: [ aButtonElement - enqueueTask: - (BlTaskAction new + enqueueTask: (BlTaskAction new action: [ [ aGtCoderAction action - glamourValueWithArgs: - {self textualCoderViewModel. - aButtonElement} ] + gtValueWithArgs: {self coderViewModel. + aButtonElement. + anEvent} ] ensure: [ self enqueueEnableButton: aButtonElement action: aGtCoderAction ] ]) ] ] -{ #category : #initialization } -GtCoderActionsElement >> initialize [ - super initialize. - - self layout: BlLinearLayout horizontal. - self padding: (BlInsets empty). - self constraintsDo: [ :c | - c horizontal matchParent. - c vertical fitContent ]. - - mainToolbar := self newToolbar labeled: 'Main toolbar'. - - separator := BlElement new - background: (Color gray alpha: 0.2); - margin: (BlInsets all: 5); - constraintsDo: [ :c | - c horizontal exact: 1. - c vertical matchParent ]. +{ #category : #'instance creation' } +GtCoderActionsElement >> initializeAltClick: aGtCoderAction element: anElement [ + aGtCoderAction allowAltClick ifFalse: [ ^ self ]. - contextToolbar := self newToolbar labeled: 'Context toolbar'. + anElement viewModel + when: BlClickEvent + do: [ :anEvent | + anEvent modifiers isSoftAltOnly + ifTrue: [ anEvent consumed: true. + anEvent target phlow spawnObject: aGtCoderAction actionDefinition ] ] +] - self addChildren: { mainToolbar . separator . contextToolbar } +{ #category : #'instance creation' } +GtCoderActionsElement >> initializePrimaryClick: aGtCoderAction element: button [ + aGtCoderAction allowPrimaryClick + ifTrue: [ button viewModel + when: BlClickEvent + do: [ :anEvent | + anEvent modifiers isPrimaryModifier + ifTrue: [ button viewModel clickEvent: anEvent ] ]. + button viewModel + when: BlMouseEnterEvent + do: [ :anEvent | + anEvent consumed: true. + anEvent modifiers isPrimaryModifier + ifTrue: [ anEvent target mouseCursor: BlCopyCursor new ] + ifFalse: [ anEvent target mouseCursor: nil ]. + anEvent target requestFocus ] ] ] { #category : #'instance creation' } GtCoderActionsElement >> newButtonForAction: aGtCoderAction [ - ^ BrButton new - icon: aGtCoderAction icon asElement; - beTinySize; - label: aGtCoderAction title; - aptitude: BrGlamorousButtonWithIconAptitude; - in: [ :aButtonElement | aButtonElement id: aGtCoderAction id ]; - action: [ :aButtonElement :aButtonModel :anEvent | - self - handleButton: aButtonElement - action: aGtCoderAction - onEvent: anEvent ] + | button | + button := BrButton new + icon: aGtCoderAction icon asElement; + beTinySize; + label: aGtCoderAction title; + constraintsDo: [ :c | c linear vertical alignCenter ]; + aptitude: BrGlamorousButtonWithIconAptitude; + in: [ :aButtonElement | aButtonElement id: aGtCoderAction id ]; + action: [ :aButtonElement :aButtonModel :anEvent | + self + handleButton: aButtonElement + action: aGtCoderAction + onEvent: anEvent ]. + aGtCoderAction isDisabled ifTrue: [ + self disableButton: button action: aGtCoderAction ]. + + self initializeAltClick: aGtCoderAction element: button. + self initializePrimaryClick: aGtCoderAction element: button. + ^ button +] + +{ #category : #'instance creation' } +GtCoderActionsElement >> newDropButtonDefaultMenuForAction: aGtCoderAction [ + | titleElement containerElement classCompleter | + containerElement := BlElement new + layout: BlLinearLayout vertical; + constraintsDo: [ :c | + c horizontal exact: 200. + c vertical fitContent ]. + titleElement := BrEditorElement new + aptitude: BrGlamorousInputFieldSpacingAptitude; + editor: (BrTextEditorModel new text: '' asRopedText); + requestFocus; + constraintsDo: [ :c | + c margin: (BlInsets top: 1). + c horizontal matchParent. + c vertical fitContent ]. + classCompleter := GtCompletionController + on: titleElement + strategy: GtCoderClassWithPackageCompletionStrategy new. + classCompleter install. + classCompleter announcer + when: GtCompleterCompletionAccepted + do: [ :event | + aGtCoderAction action value: titleElement editor text asString asClass. + titleElement fireEvent: BrDropdownHideWish new ]. + containerElement addChild: titleElement. + ^ containerElement ] { #category : #'instance creation' } @@ -126,132 +140,140 @@ GtCoderActionsElement >> newDropButtonForAction: aGtCoderAction [ aGtCoderAction title ifNotNil: [ button label: aGtCoderAction title asRopedText ]. - aGtCoderAction icon - ifNotNil: [ button icon: aGtCoderAction icon asElement ]. - + aGtCoderAction icon ifNotNil: [ button icon: aGtCoderAction icon asElement ]. + aContentStencil := aGtCoderAction stencil - ifNil: [ | classCompleter containerElement titleElement | - containerElement := BlElement new - layout: BlLinearLayout vertical; - constraintsDo: [ :c | - c horizontal exact: 200. - c vertical fitContent ]. - titleElement := BrEditorElement new - aptitude: BrGlamorousInputFieldSpacingAptitude; - editor: (BrTextEditor new text: '' asRopedText); - requestFocus; - constraintsDo: [ :c | - c margin: (BlInsets top: 1). - c horizontal matchParent. - c vertical fitContent ]. - classCompleter := GtCompletionController - on: titleElement - strategy: GtCoderClassWithPackageCompletionStrategy new. - classCompleter install. - classCompleter announcer - when: GtCompleterCompletionAccepted - do: [ :event | - aGtCoderAction action value: titleElement editor text asString asClass. - titleElement fireEvent: BrDropdownHideWish new ]. - containerElement addChild: titleElement. - containerElement ]. + ifNil: [ self newDropButtonDefaultMenuForAction: aGtCoderAction ]. + + self initializeAltClick: aGtCoderAction element: button. ^ button beTinySize; - aptitude: BrGlamorousButtonWithIconAptitude + - (BrGlamorousWithDropdownAptitude - handle: [ | aButton | - aButton := BrButton new. - aGtCoderAction title - ifNotNil: [ aButton label: aGtCoderAction title asRopedText ]. - aGtCoderAction icon - ifNotNil: [ aButton icon: aGtCoderAction icon asElement ]. - aButton - beSmallSize; - aptitude: - BrGlamorousButtonWithIconAptitude - BrGlamorousButtonWithLabelTooltipAptitude - - BrGlamorousButtonExteriorAptitude; - padding: BlInsets empty; - margin: BlInsets empty; - yourself ] - content: aContentStencil); + aptitude: BrGlamorousButtonWithIconAptitude + + (BrGlamorousWithExplicitDropdownAptitude + handle: [ | aButton | + aButton := BrButton new. + aGtCoderAction title + ifNotNil: [ aButton label: aGtCoderAction title asRopedText ]. + aGtCoderAction icon + ifNotNil: [ aButton icon: aGtCoderAction icon asElement ]. + aButton + beSmallSize; + aptitude: BrGlamorousButtonWithIconAptitude - BrGlamorousButtonWithLabelTooltipAptitude2 + - BrGlamorousButtonExteriorAptitude; + padding: BlInsets empty; + margin: BlInsets empty; + yourself ] + content: aContentStencil); yourself ] { #category : #'instance creation' } -GtCoderActionsElement >> newToolbar [ - +GtCoderActionsElement >> newDropButtonMenuForAction: aGtCoderAction [ + + | button aMenuStencil | + button := BrButton new. - ^ BrToolbar new - aptitude: (BrGlamorousToolbarAptitude new spacing: 4); - padding: (BlInsets left: -4) -] + aGtCoderAction title + ifNotNil: [ button label: aGtCoderAction title asRopedText ]. + aGtCoderAction icon ifNotNil: [ button icon: aGtCoderAction icon asElement ]. -{ #category : #'api - textual coder view model' } -GtCoderActionsElement >> onPostTextualCoderViewModelChanged [ - "I am an optional hook method that is sent after #subscribeToTextualCoderViewModel. - I do nothing by default but allow users to perform update operations when a receiver object is already - subscribed to announcements." - - self textualCoderViewModel ensureAddOns -] + aMenuStencil := aGtCoderAction menuBlock + ifNil: [ [ BrMenuExplicit new + stencil: [ self newDropButtonDefaultMenuForAction: aGtCoderAction ] ] ]. -{ #category : #'api - textual coder view model' } -GtCoderActionsElement >> onTextualCoderViewModelChanged [ - "Is sent when a new textualCoder view model is assigned to the element. - Note: #onTextualCoderViewModelChanged is sent before #subscribeToTextualCoderViewModel - which means that if you perform any operation that triggers an announcement it will be ignored because the receiver - didn't get a chance to subscribe to any announcement. Override #onPostTextualCoderViewModelChanged if you - wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + self initializeAltClick: aGtCoderAction element: button. - self removeMainToolbarActions. - self removeContextToolbarActions. - self addMainToolbarActions. - self addContextToolbarActions + ^ button + beTinySize; + aptitude: BrGlamorousButtonWithIconAptitude + + (BrGlamorousWithExplicitDropdownAptitude + handle: [ | aButton | + aButton := BrButton new. + aGtCoderAction title + ifNotNil: [ aButton label: aGtCoderAction title asRopedText ]. + aGtCoderAction icon + ifNotNil: [ aButton icon: aGtCoderAction icon asElement ]. + aButton + beSmallSize; + aptitude: BrGlamorousButtonWithIconAptitude - BrGlamorousButtonWithLabelTooltipAptitude2 + - BrGlamorousButtonExteriorAptitude; + padding: BlInsets empty; + margin: BlInsets empty; + yourself ] + menu: aMenuStencil); + yourself ] -{ #category : #private } -GtCoderActionsElement >> removeContextToolbarActions [ - contextToolbar numberOfItems timesRepeat: [ contextToolbar removeItemAt: 1 ] -] +{ #category : #'instace creation' } +GtCoderActionsElement >> newErrorButtonForAction: aGtCoderAction exception: anException [ + | aFrozenException aButton | + aFrozenException := GtSystemUtility freeze: anException. -{ #category : #private } -GtCoderActionsElement >> removeMainToolbarActions [ - mainToolbar numberOfItems timesRepeat: [ mainToolbar removeItemAt: 1 ] -] + aButton := BrButton new + beTinySize; + aptitude: BrGlamorousButtonWithIconAndLabelAptitude; + addAptitude: (BrGlamorousWithExplicitTooltipAptitude text: anException printString); + label: 'Exception'; + icon: BrGlamorousVectorIcons debug; + action: [ :theButton | + theButton phlow + spawnObject: {aGtCoderAction. + aFrozenException} ]. -{ #category : #'api - textual coder view model' } -GtCoderActionsElement >> subscribeToTextualCoderViewModel [ - "Is sent after a new textualCoder view model is assigned to the element. - It is required to unsubscribe from the view model or domain model by implementing - #unsubscribeFromTextualCoderViewModel if elements subscribe to them" + self initializeAltClick: aGtCoderAction element: aButton. - self textualCoderViewModel weak - when: GtTextualCoderViewModelMainActionsChanged send: #updateMainToolbar to: self; - when: GtTextualCoderViewModelContextActionsChanged send: #updateContextToolbar to: self + ^ aButton ] -{ #category : #'api - textual coder view model' } -GtCoderActionsElement >> unsubscribeFromTextualCoderViewModel [ - "Is sent before a new textualCoder view model is assigned to the element. - Elements that subscribe to textualCoder view model in domain model are required to implement this methods." - - self textualCoderViewModel unsubscribe: self -] +{ #category : #'instance creation' } +GtCoderActionsElement >> newLocateDebuggerButtonForAction: aGtCoderAction [ + + | button anAptitude | + button := BrButton new + icon: aGtCoderAction icon asElement; + beTinySize; + label: aGtCoderAction title; + id: aGtCoderAction id; + action: [ :aButtonElement :aButtonModel :anEvent | + self + handleButton: aButtonElement + action: aGtCoderAction + onEvent: anEvent ]. -{ #category : #'private - event handling' } -GtCoderActionsElement >> updateContextToolbar [ - self enqueueTask: - (BlTaskAction new - action: [ - self removeContextToolbarActions. - self addContextToolbarActions ]) + anAptitude := BrGlamorousButtonWithIconAndLabelAptitude new. + anAptitude + findActor: BrGlamorousButtonExteriorAptitude + ifFound: [ :anExteriorAptitude | + anExteriorAptitude + "backgroundPaint: BrGlamorousColors errorBackgroundColor;" + "borderPaint: BrGlamorousColors errorBorderColor" ] + ifNone: [ ]. + anAptitude + findActor: BrGlamorousButtonLabelAptitude + ifFound: [ :aLabelAptitude | + "aLabelAptitude + defaultColor: BrGlamorousColors errorBackgroundColor; + hoveredColor: BrGlamorousColors errorBorderColor; + pressedColor: BrGlamorousColors errorBorderColor; + selectedColor: BrGlamorousColors errorBackgroundColor" ] + ifNone: [ "ignore" ]. + button aptitude: anAptitude. + + self initializeAltClick: aGtCoderAction element: button. + self initializePrimaryClick: aGtCoderAction element: button. + ^ button ] -{ #category : #'private - event handling' } -GtCoderActionsElement >> updateMainToolbar [ - self enqueueTask: (BlTaskAction new - action: [ - self removeMainToolbarActions. - self addMainToolbarActions ]) +{ #category : #'instance creation' } +GtCoderActionsElement >> newToggleForAction: aGtCoderAction [ + + ^ BrToggle new + aptitude: BrGlamorousButtonWithIconAptitude + BrGlamorousToggleBackgroundAptitude; + icon: aGtCoderAction icon asElement; + beTinySize; + label: aGtCoderAction title; + in: [ :aToggle | self initializeAltClick: aGtCoderAction element: aToggle ]; + id: aGtCoderAction id; + yourself ] diff --git a/src/GToolkit-Coder-UI/GtCoderActivatableAction.extension.st b/src/GToolkit-Coder-UI/GtCoderActivatableAction.extension.st index 7393539ad..b7ce031f6 100644 --- a/src/GToolkit-Coder-UI/GtCoderActivatableAction.extension.st +++ b/src/GToolkit-Coder-UI/GtCoderActivatableAction.extension.st @@ -5,10 +5,10 @@ GtCoderActivatableAction >> buildElementIn: aCoderActionsElement [ | aButton | aButton := aCoderActionsElement newButtonForAction: self. - aButton enabled: (enabledBlock cull: aCoderActionsElement coderUIModel). + aButton enabled: (enabledBlock cull: aCoderActionsElement coderViewModel). updateAnnouncement ifNotNil: [ - aCoderActionsElement coderUIModel announcerUIModel weak + aCoderActionsElement coderViewModel announcer weak when: updateAnnouncement send: #enabled: to: aButton ]. ^ aButton diff --git a/src/GToolkit-Coder-UI/GtCoderAddOnCreatedSignal.class.st b/src/GToolkit-Coder-UI/GtCoderAddOnCreatedSignal.class.st new file mode 100644 index 000000000..0be87ef57 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderAddOnCreatedSignal.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtCoderAddOnCreatedSignal, + #superclass : #GtCoderAddOnsComputationSignal, + #instVars : [ + 'addOnClass', + 'addOnSelector' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #accessing } +GtCoderAddOnCreatedSignal >> addOnClass [ + ^ addOnClass +] + +{ #category : #accessing } +GtCoderAddOnCreatedSignal >> addOnClass: anObject [ + addOnClass := anObject +] + +{ #category : #accessing } +GtCoderAddOnCreatedSignal >> addOnSelector [ + ^ addOnSelector +] + +{ #category : #accessing } +GtCoderAddOnCreatedSignal >> addOnSelector: anObject [ + addOnSelector := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderAddOnEventsCollector.class.st b/src/GToolkit-Coder-UI/GtCoderAddOnEventsCollector.class.st new file mode 100644 index 000000000..30aad1c5c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderAddOnEventsCollector.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtCoderAddOnEventsCollector, + #superclass : #GtBeaconEventsCollector, + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #'as yet unclassified' } +GtCoderAddOnEventsCollector >> createDefaultEventsGrouper [ + ^ GtCoderAddOnEventsGrouper new +] + +{ #category : #'as yet unclassified' } +GtCoderAddOnEventsCollector >> relevantSignalType [ + ^ GtCoderAddOnsComputationSignal +] diff --git a/src/GToolkit-Coder-UI/GtCoderAddOnEventsGrouper.class.st b/src/GToolkit-Coder-UI/GtCoderAddOnEventsGrouper.class.st new file mode 100644 index 000000000..a97f64e2c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderAddOnEventsGrouper.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderAddOnEventsGrouper, + #superclass : #GtBeaconEventsGrouper, + #category : #'GToolkit-Coder-UI-Signals' +} diff --git a/src/GToolkit-Coder-UI/GtCoderAddOnSignal.class.st b/src/GToolkit-Coder-UI/GtCoderAddOnSignal.class.st new file mode 100644 index 000000000..faad995e2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderAddOnSignal.class.st @@ -0,0 +1,31 @@ +Class { + #name : #GtCoderAddOnSignal, + #superclass : #BeaconSignal, + #instVars : [ + 'coderViewModel' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #testing } +GtCoderAddOnSignal class >> gtNormalOperationSignal [ + "Answer a Boolean indicating whether this signal is generated as part of normal operations. + See {{gtMethod:BeaconSignal class>>gtNormalOperationSignal}} for a description" + + ^ true. +] + +{ #category : #accessing } +GtCoderAddOnSignal >> coderViewModel [ + ^ coderViewModel +] + +{ #category : #accessing } +GtCoderAddOnSignal >> coderViewModel: anObject [ + coderViewModel := anObject +] + +{ #category : #'as yet unclassified' } +GtCoderAddOnSignal >> signalTypeLabel [ + ^ self class name asString +] diff --git a/src/GToolkit-Coder-UI/GtCoderAddOnsComputationFinishedSignal.class.st b/src/GToolkit-Coder-UI/GtCoderAddOnsComputationFinishedSignal.class.st new file mode 100644 index 000000000..9babba8ce --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderAddOnsComputationFinishedSignal.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderAddOnsComputationFinishedSignal, + #superclass : #GtCoderAddOnsComputationSignal, + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #'as yet unclassified' } +GtCoderAddOnsComputationFinishedSignal >> isEndSignalType [ + ^ true +] diff --git a/src/GToolkit-Coder-UI/GtCoderAddOnsComputationSignal.class.st b/src/GToolkit-Coder-UI/GtCoderAddOnsComputationSignal.class.st new file mode 100644 index 000000000..7a5dc048a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderAddOnsComputationSignal.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtCoderAddOnsComputationSignal, + #superclass : #GtCoderAddOnSignal, + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #'as yet unclassified' } +GtCoderAddOnsComputationSignal class >> eventType [ + ^ GtCoderAddOnsComputedEvent +] + +{ #category : #'as yet unclassified' } +GtCoderAddOnsComputationSignal >> isEndSignalType [ + ^ false +] + +{ #category : #'as yet unclassified' } +GtCoderAddOnsComputationSignal >> isStartSignalType [ + ^ false +] diff --git a/src/GToolkit-Coder-UI/GtCoderAddOnsComputationStartedSignal.class.st b/src/GToolkit-Coder-UI/GtCoderAddOnsComputationStartedSignal.class.st new file mode 100644 index 000000000..35be6399c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderAddOnsComputationStartedSignal.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderAddOnsComputationStartedSignal, + #superclass : #GtCoderAddOnsComputationSignal, + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #'as yet unclassified' } +GtCoderAddOnsComputationStartedSignal >> isStartSignalType [ + ^ true +] diff --git a/src/GToolkit-Coder-UI/GtCoderAddOnsComputedEvent.class.st b/src/GToolkit-Coder-UI/GtCoderAddOnsComputedEvent.class.st new file mode 100644 index 000000000..58d6b0515 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderAddOnsComputedEvent.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderAddOnsComputedEvent, + #superclass : #GtBeaconLogEvent, + #category : #'GToolkit-Coder-UI-Signals' +} diff --git a/src/GToolkit-Coder-UI/GtCoderBrowseCoderButtonId.class.st b/src/GToolkit-Coder-UI/GtCoderBrowseCoderButtonId.class.st new file mode 100644 index 000000000..c6e2f3460 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderBrowseCoderButtonId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderBrowseCoderButtonId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #accessing } +GtCoderBrowseCoderButtonId >> asSymbol [ + ^ #'context-action--browse-coder' +] diff --git a/src/GToolkit-Coder-UI/GtCoderClassActionsDropdownButtonId.class.st b/src/GToolkit-Coder-UI/GtCoderClassActionsDropdownButtonId.class.st new file mode 100644 index 000000000..a20a18e8a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderClassActionsDropdownButtonId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderClassActionsDropdownButtonId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #accessing } +GtCoderClassActionsDropdownButtonId >> asSymbol [ + ^ #'context-action--class-actions' +] diff --git a/src/GToolkit-Coder-UI/GtCoderClassHierarchyButtonId.class.st b/src/GToolkit-Coder-UI/GtCoderClassHierarchyButtonId.class.st new file mode 100644 index 000000000..bf3d4c37e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderClassHierarchyButtonId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderClassHierarchyButtonId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #accessing } +GtCoderClassHierarchyButtonId >> asSymbol [ + ^ #'context-action--class-hierarchy' +] diff --git a/src/GToolkit-Coder-UI/GtCoderClassTarget.class.st b/src/GToolkit-Coder-UI/GtCoderClassTarget.class.st new file mode 100644 index 000000000..47c35b611 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderClassTarget.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderClassTarget, + #superclass : #GtPhlowActionUniqueTarget, + #category : #'GToolkit-Coder-UI-Phlow Targets' +} diff --git a/src/GToolkit-Coder-UI/GtCoderClassesTarget.class.st b/src/GToolkit-Coder-UI/GtCoderClassesTarget.class.st new file mode 100644 index 000000000..3f794806a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderClassesTarget.class.st @@ -0,0 +1,10 @@ +" +I represent a target for several selected classes, e.g., in the coder sidebar class list. +{{gtClass:GtPhlowAction}} can access the list of classes using {{gtMethod:GtPhlowContext>>#coderSelectedClasses}}. + +" +Class { + #name : #GtCoderClassesTarget, + #superclass : #GtPhlowActionUniqueTarget, + #category : #'GToolkit-Coder-UI-Phlow Targets' +} diff --git a/src/GToolkit-Coder-UI/GtCoderClassesTreeElement.class.st b/src/GToolkit-Coder-UI/GtCoderClassesTreeElement.class.st new file mode 100644 index 000000000..e94a09367 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderClassesTreeElement.class.st @@ -0,0 +1,337 @@ +" +#Classes tree element + +I show a list of classes grouping them based on the inheritance hierarchy and sorting alphabetically. +It is a building block of the navigation UI in Coder. + +{{gtExample:GtCoderNavigationClassesHierarchyListElementExamples>>#fromCollectionClasses|previewShow=#gtLiveFor:|noCode|previewHeight=180}} +" +Class { + #name : #GtCoderClassesTreeElement, + #superclass : #BrSimpleTree, + #traits : 'TGtCoderNavigationWithContextMenu', + #classTraits : 'TGtCoderNavigationWithContextMenu classTrait', + #instVars : [ + 'classesTree' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #'instance creation' } +GtCoderClassesTreeElement class >> fromClasses: aCollectionOfClasses [ + ^ self new initializeWithClasses: aCollectionOfClasses +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> bind: aClassHierarchyTree element: aClassElement index: anIndexInTree [ + aClassElement + id: (GtCoderNavigationClassesTreeElementId indexed: anIndexInTree). + + aClassElement classHierarchyTree: aClassHierarchyTree +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> browseReferencesTo: aClass [ + self phlow spawnObject: (aClass gtReferences) +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> browseSeveralReferencesTo: someClasses [ + | aFilter | + aFilter := someClasses allButFirst + inject: someClasses first gtReferences + into: [ :aSumFilter :aClass | aSumFilter | aClass gtReferences ]. + + self phlow spawnObject: aFilter +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> buildRemoveClassLabelFor: aClass [ + | labelText | + labelText := 'Remove class ' , aClass name. + ^ BrLabel new + margin: (BlInsets all: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: labelText asRopedText +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> buildRemoveSeveralClassesLabelFor: someClasses [ + | labelText | + labelText := 'Remove {1} classes' format: { someClasses size }. + ^ BrLabel new + margin: (BlInsets all: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: labelText asRopedText +] + +{ #category : #testing } +GtCoderClassesTreeElement >> containsClass: aClass [ + "Return true if the classes tree contains a given class, false otherwise" + ^ classesTree containsClass: aClass +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> contextMenuFor: aClass [ + | someClasses | + someClasses := self selectedItems collect: #rootClass. + + ^ (someClasses size < 2 or: [ (someClasses includes: aClass) not ]) + ifTrue: [ self contextMenuForOneItem: aClass ] + ifFalse: [ self contextMenuForSeveralItems: someClasses ] +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> contextMenuForOneItem: aClass [ + + ^ GtCoderClassTarget menuItemsForObject: aClass hostElement: self +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> contextMenuForSeveralItems: someClasses [ + + ^ GtCoderClassesTarget + menuItemsForObject: (GtPharoClassGroup withAll: someClasses) + hostElement: self +] + +{ #category : #'private - drag and drop' } +GtCoderClassesTreeElement >> createClassDragHandler [ + ^ BlDragHandler new + liftItem: [ :aGtCoderClassesTreeItemElement | + | someClasses currentClass | + currentClass := aGtCoderClassesTreeItemElement theClass. + someClasses := self selectedItems collect: #rootClass. + (someClasses size > 1 and: [ someClasses includes: currentClass ]) + ifTrue: [ someClasses + collect: [ :each | + BlDragItem new + sourceElement: aGtCoderClassesTreeItemElement; + domainObject: each; + stencil: [ :aDragItem | self createSeveralDraggedClassesItem: someClasses ] ] ] + ifFalse: [ BlDragItem new + sourceElement: aGtCoderClassesTreeItemElement; + domainObject: currentClass; + stencil: [ :aDragItem | self createDraggedMethodItem: aDragItem ] ] ] +] + +{ #category : #'private - drag and drop' } +GtCoderClassesTreeElement >> createClassDropHandler [ + ^ BlDropHandler new + acceptItemsSuchThat: [ :aDragItem :aGtCoderClassesTreeItemElement | + aDragItem domainObject isCompiledMethod + and: [ aGtCoderClassesTreeItemElement theClass ~= aDragItem domainObject methodClass ] ]; + whenDroppedDo: [ :anItemsDroppedEvent | self onDropCompiledMethodsOnClass: anItemsDroppedEvent ]; + whenDragEnteredDo: [ :anItemsDraggedOverEvent | + anItemsDraggedOverEvent mayAcceptItems + ifTrue: [ anItemsDraggedOverEvent currentTarget + effect: (BlOverlayAboveEffect new paint: (Color gray alpha: 0.2)) ] ]; + whenDragLeftDo: [ :anItemsLeftEvent | anItemsLeftEvent currentTarget effect: BlNullEffect new ] +] + +{ #category : #'private - drag and drop' } +GtCoderClassesTreeElement >> createDraggedMethodItem: aDragItem [ + ^ BrLabel new + aptitude: (BrGlamorousLabelAptitude new padding: (BlInsets all: 5)) + BrShadowAptitude; + beSmallSize; + opacity: 0.85; + margin: (BlInsets all: 5); + background: Color white; + beFocusable; + requestFocus; + geometry: (BlRoundedRectangleGeometry cornerRadius: 5); + fitContent; + text: aDragItem domainObject printString +] + +{ #category : #'private - context menu' } +GtCoderClassesTreeElement >> createLabel: aString description: description [ + ^ aString asRopedText glamorousRegularFont + , ((' ' , description) asRopedText + glamorousCodeFont; + foreground: Color gray; + glamorousCodeTinySize) +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> createNodeElement [ + ^ GtCoderClassesTreeItemElement new + addEventHandler: self createClassDropHandler; + addEventHandler: self createClassDragHandler; + in: [ :aWidget | + aWidget + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude + menu: [ self contextMenuFor: aWidget theClass ]) ]; + yourself +] + +{ #category : #'private - drag and drop' } +GtCoderClassesTreeElement >> createSeveralDraggedClassesItem: someClassHierarchyTrees [ + ^ BrLabel new + aptitude: (BrGlamorousLabelAptitude new padding: (BlInsets all: 5)) + BrShadowAptitude; + beSmallSize; + opacity: 0.85; + margin: (BlInsets all: 5); + background: Color white; + beFocusable; + requestFocus; + geometry: (BlRoundedRectangleGeometry cornerRadius: 5); + fitContent; + text: ('{1} classes' format: {someClassHierarchyTrees size}) +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> initialize [ + super initialize. + + self rowStencil: BrGlamorousSimpleTreeSelectableRowElementStencilBuilder new. + + self + nodeStencil: [ self createNodeElement ]; + nodeDataBinder: [ :aClassElement :aClassHierarchyTree :aTreeNode | + self + bind: aClassHierarchyTree + element: aClassElement + index: aTreeNode indexInTree ]. + self + when: BrSelectionDoubleClicked + do: [ :evt | self initializeWithHierachyForClass: self selectedClass ] +] + +{ #category : #'api - initialization' } +GtCoderClassesTreeElement >> initializeWithClasses: aCollectionOfClasses [ + | newTree | + newTree := (GtCoderClassesHierarchyTree fromClasses: aCollectionOfClasses) + sortByClassName. + + newTree = classesTree ifTrue: [ ^ self ]. + classesTree := newTree. + self + items: classesTree subclassTrees + lazy: [ :eachTree | eachTree subclassTrees ]. + self expandAll +] + +{ #category : #'api - initialization' } +GtCoderClassesTreeElement >> initializeWithHierachyForClass: aClass [ + classesTree := (GtCoderGrowingClassesHierarchyTree hierarchyForClass: aClass) + sortByClassName. + self + items: classesTree subclassTrees + lazy: [ :eachTree | eachTree subclassTrees ]. + self expandAll. + + self viewModel root + allChildrenNodesBreadthFirstDo: [ :treeNode | + treeNode ensureChildrenNodes. + aClass = treeNode value rootClass + ifTrue: [ self selectOne: treeNode indexInTree. + self scrollToIndex: treeNode indexInTree. + ^ self ] ] +] + +{ #category : #'private - context menu' } +GtCoderClassesTreeElement >> newSubclassOf: aClass [ + | dropdown tabGroup | + tabGroup := self + withAllParentsDetect: [ :each | each class == BrTabGroup ] + ifFound: #yourself + ifNone: [ ^self ]. + dropdown := tabGroup childWithId: GtNavigationNewDropdownId. + dropdown dispatchEvent: BrDropdownShowWish new. +] + +{ #category : #'private - drag and drop' } +GtCoderClassesTreeElement >> onDropCompiledMethodsOnClass: anItemsDroppedEvent [ + | aClass aModel compositeChange| + + aClass := anItemsDroppedEvent currentTarget theClass. + + aModel := RBNamespace new. + + anItemsDroppedEvent items do: [ :eachDragItem | + | eachCompiledMethod aTargetClass | + + eachCompiledMethod := eachDragItem domainObject. + + aTargetClass := eachCompiledMethod isClassSide + ifTrue: [ aClass classSide ] + ifFalse: [ aClass instanceSide ]. + + eachCompiledMethod methodClass = aTargetClass + ifFalse: [ + eachDragItem shouldCopy + ifFalse: [ + aModel + removeMethod: eachCompiledMethod selector + from: eachCompiledMethod methodClass ]. + + aModel + compile: eachCompiledMethod sourceCode + in: aTargetClass + classified: eachCompiledMethod protocol ] ]. + + compositeChange := aModel changes. + compositeChange execute +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> removeClassSubmenuFor: aClass [ + ^ BrMenuExplicit new + stencil: [ :anExplicitMenu | + GtCoderRemoveClassPreviewStencil new + classToRemove: aClass; + menuModel: anExplicitMenu; + asElement ] +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> removeSeveralClassesSubmenuFor: someClasses [ + ^ BrMenuExplicit new + stencil: [ :aMenuExplicit | + GtCoderRemoveClassesPreviewStencil new + classesToRemove: someClasses; + menuModel: aMenuExplicit; + asElement ] +] + +{ #category : #initialization } +GtCoderClassesTreeElement >> renameClassSubmenuFor: aClass [ + | submenu | + submenu := BrMenuExplicit new. + submenu + stencil: [ :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithInputViewModel new + refactoringTitle: 'Rename class'; + targetName: aClass name; + inputLabel: 'New class name:'; + initialText: aClass name; + refactoringWithInput: [ :anInput | GtRBRenameClassRefactoring rename: aClass name to: anInput ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: self action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: self. + GtRefactoringsPreviewWithInputElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ]. + ^ submenu +] + +{ #category : #'api - selection' } +GtCoderClassesTreeElement >> selectedClass [ + + self selectedNodeDo: [ :aNode | ^ aNode value rootClass ]. + ^ nil +] + +{ #category : #'api - selection' } +GtCoderClassesTreeElement >> selectedIndice [ + "Return selected indice or zero" + + self selectedIndices + ifNotEmpty: [ :theIndices | + (theIndices first between: 1 and: self viewModel itemCount) + ifTrue: [ ^ theIndices first ] ]. + ^ 0 +] diff --git a/src/GToolkit-Coder-UI/GtCoderClassesTreeItemElement.class.st b/src/GToolkit-Coder-UI/GtCoderClassesTreeItemElement.class.st new file mode 100644 index 000000000..5f9128af5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderClassesTreeItemElement.class.st @@ -0,0 +1,76 @@ +Class { + #name : #GtCoderClassesTreeItemElement, + #superclass : #BrHorizontalPane, + #instVars : [ + 'theClass', + 'classNameLabel', + 'classIconContainer', + 'currentIconName' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #accessing } +GtCoderClassesTreeItemElement >> classHierarchyTree: aClassHierarchyTree [ + | aNewIconName anIcon anIconForm | + (self children size = 2 + and: [ self children first = classIconContainer + and: [ self children last = classNameLabel ] ]) + ifFalse: [ self removeChildren. + self + addChildren: {classIconContainer. + classNameLabel} ]. + + theClass := aClassHierarchyTree ifNotNil: #rootClass. + + theClass + ifNil: [ classNameLabel text: '' asRopedText ] + ifNotNil: [ | aText | + aText := theClass gtDisplayText asRopedText. + (theClass isAbstract or: [ theClass hasAbstractMethods ]) + ifTrue: [ aText italic ]. + theClass isDeprecated + ifTrue: [ aText append: ' (deprecated)' asRopedText italic. + aText lineThrough ]. + classNameLabel text: aText ]. + + aNewIconName := theClass gtSystemIconName. + aNewIconName = currentIconName ifTrue: [ ^ self ]. + + aNewIconName ifNil: [ classIconContainer visibility: BlVisibility hidden ]. + anIconForm := theClass gtSafeIconNamed: aNewIconName. + anIcon := anIconForm asElement. + + classIconContainer removeChildren. + classIconContainer addChild: anIcon. + currentIconName := aNewIconName +] + +{ #category : #initialization } +GtCoderClassesTreeItemElement >> createClassNameLabel [ + ^ BrLabel new + hMatchParent; + vFitContent; + beSmallSize; + aptitude: GtCoderNavigationTreeLabelAptitude +] + +{ #category : #initialization } +GtCoderClassesTreeItemElement >> initialize [ + super initialize. + + self hMatchParent. + self vFitContent. + self alignCenterLeft. + + classIconContainer := BrFrame new + fitContent; + margin: (BlInsets right: 2). + classNameLabel := self createClassNameLabel. + self addChildren: { classIconContainer . classNameLabel } +] + +{ #category : #accessing } +GtCoderClassesTreeItemElement >> theClass [ + ^ theClass +] diff --git a/src/GToolkit-Coder-UI/GtCoderCodeExecutor.class.st b/src/GToolkit-Coder-UI/GtCoderCodeExecutor.class.st index 60025d99c..b851c64bd 100644 --- a/src/GToolkit-Coder-UI/GtCoderCodeExecutor.class.st +++ b/src/GToolkit-Coder-UI/GtCoderCodeExecutor.class.st @@ -1,5 +1,5 @@ " -I {{gtMethod:GtCoderCodeExecutor>>#execute|label=#selector}} a Pharo code provided by {{gtClass:GtCoderUIModel}}. +I {{gtMethod:GtCoderCodeExecutor>>#execute|label=#selector}} a Pharo code provided by {{gtClass:GtPharoSourceCoderViewModel}}. If a code is selected, I execute the selection. If there is no selection, I execute all code: - {{gtMethod:GtCoderCodeExecutor class>>#doIt}} - {{gtMethod:GtCoderCodeExecutor class>>#doIt}} @@ -16,6 +16,13 @@ Class { #category : #'GToolkit-Coder-UI-Execution' } +{ #category : #'api - instance creation' } +GtCoderCodeExecutor class >> compileIt [ + "Execute a code that creates or updates a class" + + ^ self new compileIt +] + { #category : #'api - instance creation' } GtCoderCodeExecutor class >> doIt [ "Execute a code (and do not display the execution result)" @@ -28,6 +35,25 @@ GtCoderCodeExecutor class >> doItAndGo [ ^ self new doItAndGo ] +{ #category : #'api - instance creation' } +GtCoderCodeExecutor class >> doItAndGoAsynchronous [ + "Execute a code block asynchronously and display the execution result" + + ^ self new doItAndGoAsynchronous +] + +{ #category : #'api - instance creation' } +GtCoderCodeExecutor class >> doItAndGoSerialized [ + "Execute a code and display the execution result" + ^ self new doItAndGoSerialized +] + +{ #category : #'api - instance creation' } +GtCoderCodeExecutor class >> doItAsynchronous [ + "Execute code asynchronously (and do not display the execution result)" + ^ self new doItAsynchronous +] + { #category : #'api - instance creation' } GtCoderCodeExecutor class >> playAll [ "Execute a method code (and do not display the execution result)" @@ -42,19 +68,26 @@ GtCoderCodeExecutor class >> playAllAndInspect [ { #category : #'private - accessing' } GtCoderCodeExecutor >> coder [ - ^ self coderUIModel coder + ^ self coderViewModel coder ] { #category : #'api - accessing' } -GtCoderCodeExecutor >> coderUIModel [ +GtCoderCodeExecutor >> coderViewModel [ ^ coderUIModel ] { #category : #'api - accessing' } -GtCoderCodeExecutor >> coderUIModel: anObject [ +GtCoderCodeExecutor >> coderViewModel: anObject [ coderUIModel := anObject ] +{ #category : #initialization } +GtCoderCodeExecutor >> compileIt [ + "Execute a code that creates or updates a class" + + action := #compileIt +] + { #category : #initialization } GtCoderCodeExecutor >> doIt [ "Execute a code (and do not display the execution result)" @@ -69,6 +102,27 @@ GtCoderCodeExecutor >> doItAndGo [ action := #doItAndGo ] +{ #category : #initialization } +GtCoderCodeExecutor >> doItAndGoAsynchronous [ + "Execute a code block asynchronously and display the execution result" + + action := #doItAndGoAsynchronous +] + +{ #category : #initialization } +GtCoderCodeExecutor >> doItAndGoSerialized [ + "Execute a code and display the execution result" + + action := #doItAndGoSerialized +] + +{ #category : #initialization } +GtCoderCodeExecutor >> doItAsynchronous [ + "Execute code asynchronously (and do not display the execution result)" + + action := #doItAsynchronous +] + { #category : #'api - accessing' } GtCoderCodeExecutor >> element [ ^ element @@ -76,27 +130,19 @@ GtCoderCodeExecutor >> element [ { #category : #accessing } GtCoderCodeExecutor >> element: anElement [ - element := anElement -] + self + assert: [ anElement isKindOf: BlElement ] + description: [ 'A code executor must be created in a context of an element' ]. -{ #category : #'api - accessing' } -GtCoderCodeExecutor >> event [ - self flag: 'I should not keep events, but rather #element'. - self halt. - ^ nil -] - -{ #category : #'api - accessing' } -GtCoderCodeExecutor >> event: anObject [ - self halt. - self flag: 'I should not receive events, but rather #element:'. + element := anElement ] { #category : #'api - execution' } GtCoderCodeExecutor >> execute [ - GtCoderExecutionContextVariable + ^ GtCoderExecutionContextVariable element: self element - do: [ self coderUIModel perform: action ] + do: [ + self coderViewModel perform: action ] ] { #category : #initialization } diff --git a/src/GToolkit-Coder-UI/GtCoderContentElement.class.st b/src/GToolkit-Coder-UI/GtCoderContentElement.class.st index d565cb873..ee548de18 100644 --- a/src/GToolkit-Coder-UI/GtCoderContentElement.class.st +++ b/src/GToolkit-Coder-UI/GtCoderContentElement.class.st @@ -1,5 +1,5 @@ " -I display a {{gtClass:GtBehaviorCoder}}, {{gtClass:GtPackageTagCoder}}, {{gtClass:GtPackageCoder}}, and {{gtClass:GtCodersModel}} coders. +I display a {{gtClass:GtPharoBehaviorCoder}}, {{gtClass:GtPharoPackageTagCoder}}, {{gtClass:GtPackageCoder}}, and {{gtClass:GtCodersModel}} coders. 1. # Example {{gtExample:GtCoderElementExamples>>#contentElementWithClass|previewExpanded|codeExpanded=false|previewHeight=700}} @@ -8,7 +8,7 @@ I display a {{gtClass:GtBehaviorCoder}}, {{gtClass:GtPackageTagCoder}}, {{gtClas " Class { #name : #GtCoderContentElement, - #superclass : #GtCoderNavigationModelElement, + #superclass : #GtAbstractCoderElement, #category : #'GToolkit-Coder-UI-Basic' } @@ -23,7 +23,9 @@ GtCoderContentElement >> initialize [ super initialize. self layout: BlFrameLayout new. - self addAptitude: (BrLayoutResizerAptitude inherit). + self + addAptitude: (BrLayoutResizerAptitude new + common: [ self children ]) ] { #category : #'hooks - children' } @@ -42,11 +44,14 @@ GtCoderContentElement >> onRemovedFromSceneGraph [ GtCoderContentElement >> subscribeToNavigationModel [ super subscribeToNavigationModel. self navigationModel weak - when: GtCoderNavigationSelectionAnnouncement send: #updateCoder: to: self. + when: GtCoderNavigationCoderChanged send: #updateCoder: to: self. ] { #category : #'private - event handling' } GtCoderContentElement >> updateCoder: anAnnouncement [ self removeChildren. - self addChild: anAnnouncement coder asElement. + self + addChild: (anAnnouncement coder asElement navigationModelForToolbar: self navigationModel). + + self requestStyle ] diff --git a/src/GToolkit-Coder-UI/GtCoderContextMenuAction.extension.st b/src/GToolkit-Coder-UI/GtCoderContextMenuAction.extension.st new file mode 100644 index 000000000..f4275862b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderContextMenuAction.extension.st @@ -0,0 +1,15 @@ +Extension { #name : #GtCoderContextMenuAction } + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderContextMenuAction >> asBrMenuItemForCoderElement: aTextualCoderEditorElement [ + | aMenuItem | + aMenuItem := GtCoderMenuActionItem new + textualCoderEditorElement: aTextualCoderEditorElement; + coderContextMenuAction: self; + disableIf: [ self isDisabled ]; + yourself. + + self allowAltClick ifTrue: [ aMenuItem definition: [ self actionDefinition ] ]. + + ^ aMenuItem +] diff --git a/src/GToolkit-Coder-UI/GtCoderContextMenuContent.class.st b/src/GToolkit-Coder-UI/GtCoderContextMenuContent.class.st index 792914f45..445ba7675 100644 --- a/src/GToolkit-Coder-UI/GtCoderContextMenuContent.class.st +++ b/src/GToolkit-Coder-UI/GtCoderContextMenuContent.class.st @@ -1,22 +1,18 @@ +" +I represent a Coder context menu. +#Examples +The following example shows a case during which a context menu height does not fit and a scrollbar is displayed: {{gtExample:GtPharoCoderByScripterExamples>>#displayPharoMethodContextMenuExtentIn800x345Space | previewExpanded=true | codeExpanded=false | previewHeight=500}} + +" Class { #name : #GtCoderContextMenuContent, - #superclass : #BrSimpleList, + #superclass : #GtAbstractCoderContextMenuContent, #instVars : [ 'editorElement' ], #category : #'GToolkit-Coder-UI-Basic' } -{ #category : #defaults } -GtCoderContextMenuContent >> defaultContextMenuActionName [ - ^ #contextMenuAction -] - -{ #category : #defaults } -GtCoderContextMenuContent >> defaultLabelChildName [ - ^ #label -] - { #category : #'api - accessing' } GtCoderContextMenuContent >> editorElement [ @@ -32,46 +28,100 @@ GtCoderContextMenuContent >> editorElement: anEditorElement [ ] { #category : #initialization } -GtCoderContextMenuContent >> initialize [ - super initialize. - self - stencil: [ :eachGtCoderContextMenuAction | - | element | - element := BrHorizontalPane new - hMatchParent; - vFitContent; - addAptitude: - (BrStyleCommonAptitude new - hovered: - [ :aStyle | aStyle background: self theme button defaultBorderColor ]); - padding: (BlInsets all: 5); +GtCoderContextMenuContent >> menuItemStencil [ + ^ [ :eachGtCoderContextMenuAction :aRowIndex | + | element labelText | + element := BrHorizontalPane new + id: (GtCoderContextMenuItemId indexed: aRowIndex); + hMatchParent; + vFitContent; + alignCenter; + addAptitude: self selectionAptitude; + padding: self itemPadding. + + labelText := eachGtCoderContextMenuAction title asRopedText . + eachGtCoderContextMenuAction isEnabled + ifFalse: [ labelText := labelText + foreground: BrGlamorousColors disabledButtonTextColor ]. + element addChild: (self itemLabelFor: labelText). + + eachGtCoderContextMenuAction isEnabled + ifTrue: [ + element when: BlClickEvent - do: [ :anEvent | + do: [ :anEvent | anEvent consumed: true. eachGtCoderContextMenuAction action - cull: self editorElement textualCoderViewModel - cull: anEvent - cull: self editorElement. + cull: self textualCoderViewModel + cull: self editorElement + cull: anEvent. self fireEvent: BrDropdownHideWish new. - self fireEvent: BrContextMenuHideWish new ]; - addChild: - (BrLabel new - aptitude: BrGlamorousLabelAptitude; - text: eachGtCoderContextMenuAction title; - yourself). - eachGtCoderContextMenuAction hoverAction - ifNotNil: [ :hover | - element - when: BlMouseEnterEvent - do: [ :anEvent | hover cull: self editorElement cull: anEvent ] ]. - eachGtCoderContextMenuAction leaveAction - ifNotNil: [ :leave | - element - when: BlMouseLeaveEvent - do: [ :anEvent | leave cull: self editorElement cull: anEvent ] ]. - eachGtCoderContextMenuAction name - ifNotNil: [ :anActionName | element containerName: anActionName ]. - element ]; - hFitContent; - vFitContent + self fireEvent: BrContextMenuHideWish new ] ]. + ((eachGtCoderContextMenuAction shortcutKey respondsTo: #isEmpty) + and: [ eachGtCoderContextMenuAction shortcutKey isEmpty not ]) + ifTrue: [ element + addChild: (BrHorizontalPane new + fitContent; + geometry: (BlRoundedRectangleGeometry cornerRadius: 4); + border: (BlBorder paint: Color gray width: 1); + padding: (BlInsets all: 1); + margin: (BlInsets left: 10); + addChild: (BrLabel new + aptitude: BrGlamorousLabelAptitude; + text: (eachGtCoderContextMenuAction shortcutKey asRopedText + fontName: 'Source Code Pro'; + foreground: Color gray; + fontSize: 10); + hFitContent; + yourself); + yourself) ]. + eachGtCoderContextMenuAction hoverAction + ifNotNil: [ :hover | + element + when: BlMouseEnterEvent + do: [ :anEvent | + hover cull: self textualCoderViewModel cull: anEvent ] ]. + eachGtCoderContextMenuAction leaveAction + ifNotNil: [ :leave | + element + when: BlMouseLeaveEvent + do: [ :anEvent | + leave cull: self textualCoderViewModel cull: anEvent ] ]. + eachGtCoderContextMenuAction name + ifNotNil: [ :anActionName | element containerName: anActionName ]. + + eachGtCoderContextMenuAction isEnabled ifTrue: [ + element + when: BlMouseEnterEvent + do: [ :anEvent | self selectOne: aRowIndex ]; + when: BlMouseLeaveEvent + do: [ :anEvent | self deselectOne: aRowIndex ] ]. + + element ] +] + +{ #category : #'api - accessing' } +GtCoderContextMenuContent >> textualCoderViewModel [ + "We add a context menu to the CoderElement, not to the editor element" + + self flag: #TODO. + + "There is a hack in: + - `GtSmaCCGrammarCoder>>#addMenuItemsElement` adds itself as an editor element, and + - implements `GtSmaCCGrammarCoder>>#textualCoderViewModel`. + - It is called from `SmaCCParser class>>#gtGrammarFor:context:`. + + It is not covered by any example. + + How to reproduce: + - browse LineParser class, + - in the `Grammar` tab click on the plus button, + - select Production from the dropdown menu. + + Note: it was causing DNU because the coder model does not understands parent. + See: https://github.com/feenkcom/gtoolkit/issues/4924" + + ^ (editorElement respondsTo: #textualCoderViewModel) + ifTrue: [ editorElement textualCoderViewModel ] + ifFalse: [ editorElement parent textualCoderViewModel ] ] diff --git a/src/GToolkit-Coder-UI/GtCoderContextMenuItemId.class.st b/src/GToolkit-Coder-UI/GtCoderContextMenuItemId.class.st new file mode 100644 index 000000000..43c6e5bcc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderContextMenuItemId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderContextMenuItemId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Basic' +} + +{ #category : #converting } +GtCoderContextMenuItemId >> asSymbol [ + ^ #'coder-context-menu--item' +] diff --git a/src/GToolkit-Coder-UI/GtCoderCustomAction.extension.st b/src/GToolkit-Coder-UI/GtCoderCustomAction.extension.st new file mode 100644 index 000000000..78847f42b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderCustomAction.extension.st @@ -0,0 +1,11 @@ +Extension { #name : #GtCoderCustomAction } + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderCustomAction >> buildElementIn: aCoderActionsElement [ + | anElement | + anElement := stencil asStencil asElement. + + aCoderActionsElement initializeAltClick: self element: anElement. + + ^ anElement +] diff --git a/src/GToolkit-Coder-UI/GtCoderDebugSessionInSpaceEvaluationStatus.class.st b/src/GToolkit-Coder-UI/GtCoderDebugSessionInSpaceEvaluationStatus.class.st new file mode 100644 index 000000000..629819f3f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDebugSessionInSpaceEvaluationStatus.class.st @@ -0,0 +1,132 @@ +" +I am a {{gtClass:GtCoderEvaluationStatus}}. +I represent an evaluation status for a source code that was executed and resulted to an unhandled exception. +The corresponding process is opened in a debugger. +" +Class { + #name : #GtCoderDebugSessionInSpaceEvaluationStatus, + #superclass : #GtCoderEvaluationStatus, + #instVars : [ + 'debugSession', + 'debugger', + 'sourceInterval', + 'sourceString', + 'evaluatedCode' + ], + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #converting } +GtCoderDebugSessionInSpaceEvaluationStatus >> asDebugSessionInSpaceEvaluationStatusFromAnnouncement: anAnnouncement [ + ^ GtCoderDebugSessionInSpaceEvaluationStatus new + debugSession: anAnnouncement releasedSession; + debugger: anAnnouncement debugger; + sourceInterval: self sourceInterval; + sourceString: self sourceString +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> debugSession [ + ^ debugSession +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> debugSession: anObject [ + debugSession := anObject +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> debugger [ + ^ debugger +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> debugger: anObject [ + debugger := anObject +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> debuggerSpace [ + + ^ GtCoderDebuggerSpaceFinder new + debugSession: self debugSession; + find; + space +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> evaluatedCode [ + + ^ evaluatedCode ifNil: [ + evaluatedCode := GtSourceCoderNoEvaluatedCode default ] +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> evaluatedCode: anObject [ + evaluatedCode := anObject +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> evaluatedCodeContext [ + "Return a stack context that corresponds to a given evaluated code." + + + self debugSession process ifNil: [ ^ nil ]. + + ^ self evaluatedCode findRelevantContextInStack: self debugSession context stack +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> evaluatedCodeInterval [ + "Return an interval that corresponds to a given evaluated code." + + + ^ GtCoderEvaluatedCodeIntervalFinder new + debugSession: self debugSession; + context: self evaluatedCodeContext; + evaluatedCode: self evaluatedCode; + sourceString: self sourceString; + find +] + +{ #category : #testing } +GtCoderDebugSessionInSpaceEvaluationStatus >> hasDebugSessionInSpace [ + ^ true +] + +{ #category : #'api - hooks' } +GtCoderDebugSessionInSpaceEvaluationStatus >> mayTerminateSourceCoderViewModel: aViewModel sharedDebugSessionOfStatus: anEvaluationStatus [ + "We want to keep the debug session, since we are about to open it in a full window." + + +] + +{ #category : #actions } +GtCoderDebugSessionInSpaceEvaluationStatus >> requestDebuggerSpaceClose [ + ^ self debuggerSpace ifNotNil: #requestClose +] + +{ #category : #'api - hooks' } +GtCoderDebugSessionInSpaceEvaluationStatus >> sourceCoderViewModel: aViewModel evaluationStatusChangedTo: aNewStatus [ + aNewStatus mayCloseSourceCoderViewModel: aViewModel debuggerSpaceOfStatus: self +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> sourceInterval [ + ^ sourceInterval +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> sourceInterval: anObject [ + sourceInterval := anObject +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> sourceString [ + ^ sourceString +] + +{ #category : #accessing } +GtCoderDebugSessionInSpaceEvaluationStatus >> sourceString: anObject [ + sourceString := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderDebugSessionMatcher.class.st b/src/GToolkit-Coder-UI/GtCoderDebugSessionMatcher.class.st new file mode 100644 index 000000000..9b920e01c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDebugSessionMatcher.class.st @@ -0,0 +1,26 @@ +Class { + #name : #GtCoderDebugSessionMatcher, + #superclass : #GtCoderDebuggerMatcher, + #instVars : [ + 'debugSession' + ], + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #accessing } +GtCoderDebugSessionMatcher >> debugSession [ + ^ debugSession +] + +{ #category : #accessing } +GtCoderDebugSessionMatcher >> debugSession: anObject [ + debugSession := anObject +] + +{ #category : #testing } +GtCoderDebugSessionMatcher >> isMoldableDebugger: anElement [ + | anElementDebugSession | + anElementDebugSession := anElement debugSession. + ^ anElementDebugSession isNotNil + and: [ anElementDebugSession interruptedProcess == debugSession interruptedProcess ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderDebuggerInterruptedProcessMatcher.class.st b/src/GToolkit-Coder-UI/GtCoderDebuggerInterruptedProcessMatcher.class.st new file mode 100644 index 000000000..07d438aec --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDebuggerInterruptedProcessMatcher.class.st @@ -0,0 +1,26 @@ +Class { + #name : #GtCoderDebuggerInterruptedProcessMatcher, + #superclass : #GtCoderDebuggerMatcher, + #instVars : [ + 'process' + ], + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #testing } +GtCoderDebuggerInterruptedProcessMatcher >> isMoldableDebugger: anElement [ + | anElementDebugSession | + anElementDebugSession := anElement debugSession. + ^ anElementDebugSession isNotNil + and: [ anElementDebugSession interruptedProcess == process ] +] + +{ #category : #accessing } +GtCoderDebuggerInterruptedProcessMatcher >> process [ + ^ process +] + +{ #category : #accessing } +GtCoderDebuggerInterruptedProcessMatcher >> process: anObject [ + process := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderDebuggerMatcher.class.st b/src/GToolkit-Coder-UI/GtCoderDebuggerMatcher.class.st new file mode 100644 index 000000000..b2291f2ed --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDebuggerMatcher.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderDebuggerMatcher, + #superclass : #Object, + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #testing } +GtCoderDebuggerMatcher >> isMoldableDebugger: anElement [ + ^ false +] diff --git a/src/GToolkit-Coder-UI/GtCoderDebuggerSpaceFinder.class.st b/src/GToolkit-Coder-UI/GtCoderDebuggerSpaceFinder.class.st new file mode 100644 index 000000000..fa0becf51 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDebuggerSpaceFinder.class.st @@ -0,0 +1,55 @@ +" +I find a {{gtClass:BlSpace}} in which a {{gtClass:GtMoldableDebugger}} for a given {{gtClass:Process}} is opened. +Main methods: +- {{gtMethod:GtCoderDebuggerSpaceFinder >> #process: | label=#selector}} sets the {{gtClass:Process}} for which a corresponding {{gtClass:BlSpace}} should be found, +- {{gtMethod:GtCoderDebuggerSpaceFinder >> #find | label=#selector}} searches the corresponding {{gtClass:BlSpace}}, +- {{gtMethod:GtCoderDebuggerSpaceFinder >> #space | label=#selector}} returns the found {{gtClass:BlSpace}} or `nil`. + +" +Class { + #name : #GtCoderDebuggerSpaceFinder, + #superclass : #Object, + #instVars : [ + 'space', + 'debuggerMatcher' + ], + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #accessing } +GtCoderDebuggerSpaceFinder >> debugSession: aDebugSession [ + debuggerMatcher := GtCoderDebugSessionMatcher new debugSession: aDebugSession +] + +{ #category : #finding } +GtCoderDebuggerSpaceFinder >> find [ + + BlParallelUniverse all + do: [ :eachUniverse | + eachUniverse spaces + do: [ :eachSpace | + | aQuery | + aQuery := eachSpace root query // GtMoldableDebugger. + aQuery isEmpty + ifFalse: [ (debuggerMatcher isMoldableDebugger: aQuery anyOne) + ifTrue: [ ^ space := eachSpace ] ] ] ]. + + ^ space +] + +{ #category : #initialization } +GtCoderDebuggerSpaceFinder >> initialize [ + super initialize. + debuggerMatcher := GtCoderDebuggerMatcher new +] + +{ #category : #accessing } +GtCoderDebuggerSpaceFinder >> process: aProcess [ + debuggerMatcher := GtCoderDebuggerInterruptedProcessMatcher new process: aProcess +] + +{ #category : #accessing } +GtCoderDebuggerSpaceFinder >> space [ + + ^ space +] diff --git a/src/GToolkit-Coder-UI/GtCoderDeprecatedTextAttributeAddedSignal.class.st b/src/GToolkit-Coder-UI/GtCoderDeprecatedTextAttributeAddedSignal.class.st new file mode 100644 index 000000000..7cdbbfcca --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDeprecatedTextAttributeAddedSignal.class.st @@ -0,0 +1,37 @@ +Class { + #name : #GtCoderDeprecatedTextAttributeAddedSignal, + #superclass : #ContextStackSignal, + #instVars : [ + 'coderTextAttributes', + 'deprecatedAttribute' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #accessing } +GtCoderDeprecatedTextAttributeAddedSignal >> coderTextAttributes [ + ^ coderTextAttributes +] + +{ #category : #accessing } +GtCoderDeprecatedTextAttributeAddedSignal >> coderTextAttributes: anObject [ + coderTextAttributes := anObject +] + +{ #category : #accessing } +GtCoderDeprecatedTextAttributeAddedSignal >> deprecatedAttribute [ + ^ deprecatedAttribute +] + +{ #category : #accessing } +GtCoderDeprecatedTextAttributeAddedSignal >> deprecatedAttribute: anObject [ + deprecatedAttribute := anObject +] + +{ #category : #printing } +GtCoderDeprecatedTextAttributeAddedSignal >> printOneLineContentsOn: stream [ + stream + nextPutAll: '! '; + print: deprecatedAttribute; + space +] diff --git a/src/GToolkit-Coder-UI/GtCoderDisplayTopSidebarWish.class.st b/src/GToolkit-Coder-UI/GtCoderDisplayTopSidebarWish.class.st new file mode 100644 index 000000000..6613ea1ff --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDisplayTopSidebarWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderDisplayTopSidebarWish, + #superclass : #BrWish, + #category : #'GToolkit-Coder-UI-Basic' +} diff --git a/src/GToolkit-Coder-UI/GtCoderDocumentIdDifferentSignal.class.st b/src/GToolkit-Coder-UI/GtCoderDocumentIdDifferentSignal.class.st new file mode 100644 index 000000000..383db0544 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDocumentIdDifferentSignal.class.st @@ -0,0 +1,28 @@ +Class { + #name : #GtCoderDocumentIdDifferentSignal, + #superclass : #GtCoderDocumentIdSignal, + #instVars : [ + 'originalDocumentId', + 'receivedDocumentId' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #'api - text' } +GtCoderDocumentIdDifferentSignal >> originalDocumentId: aDocumentId [ + originalDocumentId := aDocumentId +] + +{ #category : #printing } +GtCoderDocumentIdDifferentSignal >> printOneLineContentsOn: aStream [ + aStream + nextPutAll: 'Original: '; + print: originalDocumentId; + nextPutAll: ', received: '; + print: receivedDocumentId +] + +{ #category : #'api - text' } +GtCoderDocumentIdDifferentSignal >> receivedDocumentId: aDocumentId [ + receivedDocumentId := aDocumentId +] diff --git a/src/GToolkit-Coder-UI/GtCoderDocumentIdIsNilSignal.class.st b/src/GToolkit-Coder-UI/GtCoderDocumentIdIsNilSignal.class.st new file mode 100644 index 000000000..91a62267d --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDocumentIdIsNilSignal.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtCoderDocumentIdIsNilSignal, + #superclass : #GtCoderDocumentIdSignal, + #instVars : [ + 'originalDocumentId' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #'api - text' } +GtCoderDocumentIdIsNilSignal >> originalDocumentId: aDocumentId [ + originalDocumentId := aDocumentId +] + +{ #category : #printing } +GtCoderDocumentIdIsNilSignal >> printOneLineContentsOn: aStream [ + aStream + nextPutAll: 'Original: '; + print: originalDocumentId +] diff --git a/src/GToolkit-Coder-UI/GtCoderDocumentIdSignal.class.st b/src/GToolkit-Coder-UI/GtCoderDocumentIdSignal.class.st new file mode 100644 index 000000000..14f7de8b5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDocumentIdSignal.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderDocumentIdSignal, + #superclass : #ContextStackSignal, + #category : #'GToolkit-Coder-UI-Signals' +} diff --git a/src/GToolkit-Coder-UI/GtCoderDocumentIdUpdatedSignal.class.st b/src/GToolkit-Coder-UI/GtCoderDocumentIdUpdatedSignal.class.st new file mode 100644 index 000000000..272aaf733 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDocumentIdUpdatedSignal.class.st @@ -0,0 +1,48 @@ +Class { + #name : #GtCoderDocumentIdUpdatedSignal, + #superclass : #GtCoderDocumentIdSignal, + #instVars : [ + 'texturalCoderViewModel', + 'oldDocumentId', + 'newDocumentId' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #accessing } +GtCoderDocumentIdUpdatedSignal >> newDocumentId [ + ^ newDocumentId +] + +{ #category : #accessing } +GtCoderDocumentIdUpdatedSignal >> newDocumentId: anObject [ + newDocumentId := anObject +] + +{ #category : #accessing } +GtCoderDocumentIdUpdatedSignal >> oldDocumentId [ + ^ oldDocumentId +] + +{ #category : #accessing } +GtCoderDocumentIdUpdatedSignal >> oldDocumentId: anObject [ + oldDocumentId := anObject +] + +{ #category : #printing } +GtCoderDocumentIdUpdatedSignal >> printOneLineContentsOn: aStream [ + aStream + print: newDocumentId; + nextPutAll: ', old is '; + print: oldDocumentId +] + +{ #category : #accessing } +GtCoderDocumentIdUpdatedSignal >> texturalCoderViewModel [ + ^ texturalCoderViewModel +] + +{ #category : #accessing } +GtCoderDocumentIdUpdatedSignal >> texturalCoderViewModel: anObject [ + texturalCoderViewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderDropDownMenuAction.extension.st b/src/GToolkit-Coder-UI/GtCoderDropDownMenuAction.extension.st new file mode 100644 index 000000000..28b584d7e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderDropDownMenuAction.extension.st @@ -0,0 +1,25 @@ +Extension { #name : #GtCoderDropDownMenuAction } + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderDropDownMenuAction >> asBrMenuItemForCoderElement: aTextualCoderEditorElement [ + | aMenuItem aSubMenu | + aSubMenu := self menuBlock + cull: aTextualCoderEditorElement textualCoderViewModel + cull: aTextualCoderEditorElement. + aSubMenu ifNil: [ ^ nil ]. + + aMenuItem := GtCoderMenuSubmenuItem new + textualCoderEditorElement: aTextualCoderEditorElement; + coderContextMenuAction: self; + disableIf: [ self isDisabled ]; + submenu: aSubMenu. + + self allowAltClick ifTrue: [ aMenuItem definition: [ self actionDefinition ] ]. + + ^ aMenuItem +] + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderDropDownMenuAction >> buildElementIn: aCoderActionsElement [ + ^ aCoderActionsElement newDropButtonMenuForAction: self +] diff --git a/src/GToolkit-Coder-UI/GtCoderDropDownWithPreviewAction.extension.st b/src/GToolkit-Coder-UI/GtCoderDropDownWithPreviewAction.extension.st index c2697730e..048dbab9c 100644 --- a/src/GToolkit-Coder-UI/GtCoderDropDownWithPreviewAction.extension.st +++ b/src/GToolkit-Coder-UI/GtCoderDropDownWithPreviewAction.extension.st @@ -2,12 +2,18 @@ Extension { #name : #GtCoderDropDownWithPreviewAction } { #category : #'*GToolkit-Coder-UI' } GtCoderDropDownWithPreviewAction >> buildElementIn: aCoderActionsElement [ - ^ GtPreviewChangeButton new + | aButton | + aButton := GtPreviewChangeButton new + id: self id; icon: self icon; label: self title; changeAction: self changeAction; changeStencil: (self changeStencil isBlock ifTrue: [ self changeStencil asStencil arguments: {aCoderActionsElement} ] - ifFalse: [ self changeStencil ]) + ifFalse: [ self changeStencil ]). + + aCoderActionsElement initializeAltClick: self element: aButton. + + ^ aButton ] diff --git a/src/GToolkit-Coder-UI/GtCoderElement.class.st b/src/GToolkit-Coder-UI/GtCoderElement.class.st index 2b2328b70..b40b8067e 100644 --- a/src/GToolkit-Coder-UI/GtCoderElement.class.st +++ b/src/GToolkit-Coder-UI/GtCoderElement.class.st @@ -1,22 +1,326 @@ " -I am an abstract class. -I am an {{gtClass:BlElement}}. -I provide basic {{gtClass:TBrLayoutResizable}} methods. -See my {{gtClass:GtCoderElement|expanded|show=#gtSubclassesFor:}} for concrete implementations: +I am the entrance point into the coder as a tool. To instantiate me, use one of the following class-side methods: +- {{gtMethod:GtCoderElement class>>#forObject:|label=#selector}}, +- {{gtMethod:GtCoderElement class>>#forClass:|label=#selector}}, +- {{gtMethod:GtCoderElement class>>#forMethod:|label=#selector}}, +- {{gtMethod:GtCoderElement class>>#forPackage:|label=#selector}}, +- {{gtMethod:GtCoderElement class>>#new|label=#selector}}. +1. # Example + +{{gtExample:GtCoderElementExamples>>#coderElementWithObject|previewExpanded|codeExpanded=false|previewHeight=700}} " Class { #name : #GtCoderElement, - #superclass : #BlElement, - #traits : 'TBrLayoutResizable', - #classTraits : 'TBrLayoutResizable classTrait', - #category : #'GToolkit-Coder-UI-Basic' + #superclass : #GtAbstractCoderElement, + #traits : 'TGtPagerPageInstantiator + TGtPhlowToolDetailAdjustable', + #classTraits : 'TGtPagerPageInstantiator classTrait + TGtPhlowToolDetailAdjustable classTrait', + #instVars : [ + 'sidebarElement', + 'contentElement', + 'titleNotifier' + ], + #category : #'GToolkit-Coder-UI-! Core' } +{ #category : #'instance creation' } +GtCoderElement class >> defaultSidebarWidth [ + ^ 1.0 +] + +{ #category : #'private - accessing' } +GtCoderElement >> contentElement [ + + ^ contentElement +] + +{ #category : #'private - accessing' } +GtCoderElement >> contentElement: anElement [ + + contentElement := anElement +] + +{ #category : #'instance creation' } +GtCoderElement >> createPlaygroundElement [ + ^ GtCoderPlaygroundElement new +] + +{ #category : #'instance creation' } +GtCoderElement >> createSidebarElement [ + ^ GtPhlowHorizontalSidebarElement new + beLeft; + resizerStrategy: (BrResizerElementLength weight: self class defaultSidebarWidth); + contentStencil: (GtCoderNavigationTabsStencil new navigationModel: self navigationModel); + addAptitude: (GtPhlowToolDetailAptitude new + left: [ :aStyle | + aStyle + do: [ :aWidget | + aWidget userData + at: #leftSidebarResizerStrategy + ifPresent: [ :aResizerStrategy | aWidget resizerStrategy: aResizerStrategy ] + ifAbsentPut: [ aWidget + resizerStrategy: (BrResizerElementLength weight: self class defaultSidebarWidth) ]. + aWidget beLeft. + aWidget userData + at: #leftSidebarIsCollapsed + ifPresent: [ :aBoolean | aBoolean ifTrue: [ aWidget collapse ] ] + ifAbsent: [ ] ] + after: [ :aWidget | + aWidget userData at: #leftSidebarResizerStrategy put: aWidget resizerStrategy. + aWidget userData at: #leftSidebarIsCollapsed put: aWidget isCollapsed ] ]; + top: [ :aStyle | + aStyle + do: [ :aWidget | + aWidget userData + at: #topSidebarResizerStrategy + ifPresent: [ :aResizerStrategy | aWidget resizerStrategy: aResizerStrategy ] + ifAbsentPut: [ aWidget + resizerStrategy: (BrResizerElementLength weight: 0.5) ]. + aWidget beTop. + aWidget userData + at: #topSidebarIsCollapsed + ifPresent: [ :aBoolean | aBoolean ifTrue: [ aWidget collapse ] ] + ifAbsent: [ ] ] + after: [ :aWidget | + aWidget userData at: #topSidebarResizerStrategy put: aWidget resizerStrategy. + aWidget userData at: #topSidebarIsCollapsed put: aWidget isCollapsed ] ]) +] + +{ #category : #'instance creation' } +GtCoderElement >> createTitleNotifier [ + ^ GtPhlowTitleIconAndLabelNotifier new + shortLabel: [ self shortTitle ifEmpty: [ 'Coder' asRopedText ] ]; + icon: BrGlamorousVectorIcons emphasizedBrowse +] + +{ #category : #accessing } +GtCoderElement >> disablePlayPage [ + contentElement disablePlayPage +] + +{ #category : #'private - updating' } +GtCoderElement >> hideSidebar [ + sidebarElement ifNil: [ ^ self ]. + sidebarElement + hide; + removeFromParent +] + { #category : #initialization } GtCoderElement >> initialize [ super initialize. + + contentElement := self createPlaygroundElement. + + titleNotifier := self createTitleNotifier. + + self + layout: BlLinearLayout horizontal; + addEventHandler: titleNotifier; + addAptitude: (GtPhlowToolDetailAptitude new + normal: [ :aStyle | aStyle do: [ :aWidget | aWidget hideSidebar ] ]; + detailed: [ :aStyle | aStyle do: [ :aWidget | aWidget showSidebar ] ]; + left: [ :aStyle | + aStyle do: [ :aWidget | aWidget layout: BlLinearLayout horizontal ]. + aStyle + do: [ :aWidget | + aWidget sidebarElement + ifNotNil: [ :anElement | anElement states addState: GtPhlowToolDetailPositionState left ] ] + after: [ :aWidget | + aWidget sidebarElement + ifNotNil: [ :anElement | anElement states removeState: GtPhlowToolDetailPositionState left ] ] ]; + top: [ :aStyle | + aStyle do: [ :aWidget | aWidget layout: BlLinearLayout vertical ]. + aStyle + do: [ :aWidget | + aWidget sidebarElement + ifNotNil: [ :anElement | anElement states addState: GtPhlowToolDetailPositionState top ] ] + after: [ :aWidget | + aWidget sidebarElement + ifNotNil: [ :anElement | anElement states removeState: GtPhlowToolDetailPositionState top ] ] ]). + + self addChild: contentElement as: #content. + + self phlow beViewContent. + self phlow + accessTool: (GtPhlowToolClassType forClass: GtCoderElement) + viewModel: #navigationModel + object: #navigationModel. + + GtCoderSettings defaultDetailState + ifNotNil: [ :aState | self states addState: aState ]. + GtCoderSettings defaultDetailPositionState + ifNotNil: [ :aState | self states addState: aState ]. + + self + when: GtCoderSidebarVisibilityWish + do: [ :anEvent | self onGtCoderSidebarVisibilityWish: anEvent ]. + self + when: GtCoderDisplayTopSidebarWish + do: [ :anEvent | self onGtCoderDisplayTopSidebarWish: anEvent ]. + self + when: GtCoderHideSidebarWish + do: [ :anEvent | self onGtCoderHideSidebarWish: anEvent ]. + self + when: GtNavigationMethodSelectionWish + do: [ :anEvent | self onGtNavigationMethodSelectionWish: anEvent ]. + + self + addAptitude: (BrLayoutResizerAptitude new + common: [ contentElement ]; + common: [ sidebarElement ifNil: [ #() ] ]) +] + +{ #category : #'private - event handling' } +GtCoderElement >> onGtCoderDisplayTopSidebarWish: anEvent [ + anEvent consumed: true. + self states addState: GtPhlowToolDetailPositionState top. + self states addState: GtPhlowToolDetailState detailed. + self requestStyle +] + +{ #category : #'private - event handling' } +GtCoderElement >> onGtCoderHideSidebarWish: anEvent [ + anEvent consumed: true. + self states removeState: GtPhlowToolDetailPositionState top. + self states removeState: GtPhlowToolDetailState normal. + self requestStyle +] + +{ #category : #'private - event handling' } +GtCoderElement >> onGtCoderSidebarVisibilityWish: anEvent [ + anEvent consumed: true. + + self states + stateLike: GtPhlowToolDetailState detailed + ifFound: [ :aState | + anEvent isVisible: GtPhlowToolDetailState detailed = aState ] + ifNone: [ anEvent isVisible: false ] +] + +{ #category : #initialization } +GtCoderElement >> onGtNavigationMethodSelectionWish: anEvent [ + "Another method is selected in a methods coder, + let's select the same method in the navigation model." + + | aCompiledMethod | + anEvent consumed: true. + + anEvent source == self ifTrue: [ ^ self ]. + + aCompiledMethod := anEvent method. + self navigationModel ifNil: [ ^ self ]. - self matchParent + self navigationModel + selectedMethod: aCompiledMethod + source: (anEvent source ifNil: [ self ]) +] + +{ #category : #'private - hooks' } +GtCoderElement >> onNavigationModelChanged [ + super onNavigationModelChanged. + self contentElement navigationModel: self navigationModel. + self updateSidebarContent. + self updateTabLabel +] + +{ #category : #'private - announcement handling' } +GtCoderElement >> onNavigationModelSubjectChange [ + self enqueueTask: (BlTaskAction new action: [ + self updateTabLabel ]) +] + +{ #category : #opening } +GtCoderElement >> pagerWindowTitle [ + + ^ self shortTitle +] + +{ #category : #accessing } +GtCoderElement >> repository [ + self navigationModel hasSelectedClass ifTrue: [ + ^ self navigationModel selectedClass repository ]. + self navigationModel hasSelectedPackage ifTrue: [ + ^ self navigationModel selectedPackage repository ]. + + ^ nil +] + +{ #category : #filtering } +GtCoderElement >> selectMethodProtocolNamed: aMethodProtocolName [ + "(GtCoder forClass: GtPhlowProtoView) selectMethodProtocolNamed: #decorating" + | classProtocols | + classProtocols := navigationModel selectedClass gtProtocols. + classProtocols + detect: [ :aProtocol | aProtocol name = aMethodProtocolName ] + ifFound: [ :aProtocol | self navigationModel selectProtocol: aProtocol source: self ] + ifNone: [ "ignore" ] +] + +{ #category : #'private - accessing' } +GtCoderElement >> shortTitle [ + ^ (GtPhlowTitleLabelBuilder + longLabel: self subjectTitle) + build +] + +{ #category : #'private - updating' } +GtCoderElement >> showSidebar [ + sidebarElement ifNil: [ sidebarElement := self createSidebarElement ]. + sidebarElement show. + + (sidebarElement hasParent: self) + ifTrue: [ + ^ self ]. + + self addChildFirst: sidebarElement. + sidebarElement requestStyle +] + +{ #category : #accessing } +GtCoderElement >> sidebarElement [ + + ^ sidebarElement +] + +{ #category : #'private - accessing' } +GtCoderElement >> subjectTitle [ + + self navigationModel selectedClassDo: [ :aClass | ^ aClass name ]. + self navigationModel selectedTagDo: [ :aPackageTag | ^ aPackageTag packageName, '-', aPackageTag name ]. + self navigationModel selectedPackageDo: [ :aPackage | ^ aPackage name ]. + ^ nil +] + +{ #category : #'private - subscriptions' } +GtCoderElement >> subscribeToNavigationModel [ + super subscribeToNavigationModel. + self navigationModel weak + when: GtCoderNavigationPackagesSelected, + GtCoderNavigationPackageSelected, + GtCoderNavigationPackageRenamed, + GtCoderNavigationPackageTagSelected, + GtCoderNavigationPackageTagRenamed, + GtCoderNavigationClassSelected, + GtCoderNavigationClassRenamed, + GtCoderNavigationClassModified + send: #onNavigationModelSubjectChange to: self +] + +{ #category : #'private - accessing' } +GtCoderElement >> titleNotifier [ + + ^ titleNotifier +] + +{ #category : #'private - updating' } +GtCoderElement >> updateSidebarContent [ + sidebarElement ifNil: [ ^ self ]. + sidebarElement contentStencil: (GtCoderNavigationTabsStencil new navigationModel: self navigationModel) +] + +{ #category : #'private - updating' } +GtCoderElement >> updateTabLabel [ + self titleNotifier notifyIfFocused ] diff --git a/src/GToolkit-Coder-UI/GtCoderElementId.class.st b/src/GToolkit-Coder-UI/GtCoderElementId.class.st index bd621ea93..eae0f0478 100644 --- a/src/GToolkit-Coder-UI/GtCoderElementId.class.st +++ b/src/GToolkit-Coder-UI/GtCoderElementId.class.st @@ -5,7 +5,7 @@ Class { #name : #GtCoderElementId, #superclass : #BlElementUniqueId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #testing } diff --git a/src/GToolkit-Coder-UI/GtCoderElementStencil.class.st b/src/GToolkit-Coder-UI/GtCoderElementStencil.class.st new file mode 100644 index 000000000..76ada0318 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderElementStencil.class.st @@ -0,0 +1,41 @@ +" +I am a {{gtClass:BrStencil}} that creates a default coder element or customized one, based on users configuration using {{gtMethod:GtCoderElementStencil class>>#defaultStencil:}}. +I am an abstract class. See my {{gtClass:GtCoderElementStencil | show=#gtSubclassesFor: | expanded = true | label=subclasses}} +" +Class { + #name : #GtCoderElementStencil, + #superclass : #BrStencil, + #classInstVars : [ + 'defaultStencil' + ], + #category : #'GToolkit-Coder-UI-Coder - Source' +} + +{ #category : #clearing } +GtCoderElementStencil class >> clearDefaultStencil [ + defaultStencil := nil +] + +{ #category : #accessing } +GtCoderElementStencil class >> defaultStencil [ + + ^ defaultStencil +] + +{ #category : #accessing } +GtCoderElementStencil class >> defaultStencil: aStencil [ + defaultStencil := aStencil asStencil +] + +{ #category : #'api - instantiation' } +GtCoderElementStencil >> create [ + ^ self class defaultStencil + ifNil: [ self defaultElement] + ifNotNil: [ :aStencil | aStencil create ] +] + +{ #category : #'api - instantiation' } +GtCoderElementStencil >> defaultElement [ + + ^ self subclassResponsibility +] diff --git a/src/GToolkit-Coder-UI/GtCoderEmbeddedDebugSessionEvaluationStatus.class.st b/src/GToolkit-Coder-UI/GtCoderEmbeddedDebugSessionEvaluationStatus.class.st new file mode 100644 index 000000000..f1d56983a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderEmbeddedDebugSessionEvaluationStatus.class.st @@ -0,0 +1,203 @@ +" +I am a {{gtClass:GtCoderEvaluationStatus}}. +I represent an evaluation status for a source code that was executed and resulted to an unhandled exception. The corresponding process is suspended and waiting for a user action, e.g., opening in a debugger. + +" +Class { + #name : #GtCoderEmbeddedDebugSessionEvaluationStatus, + #superclass : #GtCoderEvaluationStatus, + #instVars : [ + 'debugSession', + 'exception', + 'sourceInterval', + 'sourceString', + 'sharedDebugSession', + 'evaluatedCode' + ], + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #comparing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + self class = anObject class ifFalse: [ ^ false ]. + + ^ self debugSession = anObject debugSession + and: [ self sharedDebugSession = anObject sharedDebugSession + and: [ self exception = anObject exception + and: [ self evaluatedCode = anObject evaluatedCode + and: [ self sourceInterval = anObject sourceInterval + and: [ self sourceString = anObject sourceString ] ] ] ] ] +] + +{ #category : #converting } +GtCoderEmbeddedDebugSessionEvaluationStatus >> asDebugSessionInSpaceEvaluationStatusFromAnnouncement: anAnnouncement [ + ^ GtCoderDebugSessionInSpaceEvaluationStatus new + debugSession: anAnnouncement releasedSession; + debugger: anAnnouncement debugger; + sourceInterval: self sourceInterval; + sourceString: self sourceString; + evaluatedCode: self evaluatedCode +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> debugSession [ + ^ debugSession +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> debugSession: anObject [ + debugSession := anObject +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> evaluatedCode [ + + ^ evaluatedCode ifNil: [ + evaluatedCode := GtSourceCoderNoEvaluatedCode default ] +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> evaluatedCode: anObject [ + evaluatedCode := anObject +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> evaluatedCodeContext [ + "Return a stack context that corresponds to a given evaluated code." + + + self sharedDebugSession + sessionDo: [ :aSession | ^ self evaluatedCode findRelevantContextInStack: aSession context stack ]. + ^ nil +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> evaluatedCodeInterval [ + "Return an interval that corresponds to a given evaluated code." + + + ^ GtCoderEvaluatedCodeIntervalFinder new + debugSession: self debugSession; + context: self evaluatedCodeContext; + evaluatedCode: self evaluatedCode; + sourceString: self sourceString; + find +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> exception [ + ^ exception +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> exception: anObject [ + exception := anObject +] + +{ #category : #printing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> gtDisplayOn: stream [ + stream nextPutAll: 'Embedded debugger('. + stream print: exception. + stream nextPut: $) +] + +{ #category : #'gt - extensions' } +GtCoderEmbeddedDebugSessionEvaluationStatus >> gtEvaluatedSource [ + | aText | + aText := self sourceString asRopedText glamorousCodeFontAndSize. + + self evaluatedCodeInterval + ifNotNil: [ :aContextInterval | + | aContextMin aContextMax | + aContextMin := aContextInterval first min: aText size. + aContextMax := aContextInterval last min: aText size. + (aText from: aContextMin to: aContextMax) + highlight: BrGlamorousColors debuggerTextHighlightColor ]. + + ^ aText +] + +{ #category : #'gt - extensions' } +GtCoderEmbeddedDebugSessionEvaluationStatus >> gtEvaluatedSourceFor: aView [ + + sourceString ifNil: [ ^ aView empty ]. + + ^ aView text + title: 'Evaluated source'; + priority: 20; + text: [ self gtEvaluatedSource ] +] + +{ #category : #'api - hooks' } +GtCoderEmbeddedDebugSessionEvaluationStatus >> handleStatusChangedAnnouncement: anAnnouncement sourceCoderElement: anElement [ + "I request to display a debug session notification. This method must be called + from an element, listening to GtSourceCoderViewModelEvaluationStatusChanged." + + + anAnnouncement isNotificationHandled ifTrue: [ ^ self ]. + anAnnouncement isNotificationHandled: true. + + GtCoderEvaluationStatusShowNotificationSignal new + evaluationStatus: self; + announcement: anAnnouncement; + sourceCoderElement: anElement; + emit. + + anElement + showNotification: (GtNotificationDebugSession new debugSession: self sharedDebugSession) +] + +{ #category : #testing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> hasSharedDebugSession [ + ^ true +] + +{ #category : #comparing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> hash [ + ^ self class hash + bitXor: (self debugSession hash + bitXor: (self sharedDebugSession hash + bitXor: (self exception hash + bitXor: (self evaluatedCode hash + bitXor: (self sourceInterval hash + bitXor: (self sourceString hash )))))) +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> sharedDebugSession [ + ^ sharedDebugSession +] + +{ #category : #'private - announcement handling' } +GtCoderEmbeddedDebugSessionEvaluationStatus >> sharedDebugSession: aSharedDebugSession [ + sharedDebugSession := aSharedDebugSession +] + +{ #category : #'api - hooks' } +GtCoderEmbeddedDebugSessionEvaluationStatus >> sourceCoderViewModel: aViewModel evaluationStatusChangedTo: aNewStatus [ + aNewStatus + mayTerminateSourceCoderViewModel: aViewModel + sharedDebugSessionOfStatus: self +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> sourceInterval [ + ^ sourceInterval +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> sourceInterval: anObject [ + sourceInterval := anObject +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> sourceString [ + ^ sourceString +] + +{ #category : #accessing } +GtCoderEmbeddedDebugSessionEvaluationStatus >> sourceString: anObject [ + sourceString := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderEvaluatedCodeIntervalFinder.class.st b/src/GToolkit-Coder-UI/GtCoderEvaluatedCodeIntervalFinder.class.st new file mode 100644 index 000000000..1b318f633 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderEvaluatedCodeIntervalFinder.class.st @@ -0,0 +1,68 @@ +Class { + #name : #GtCoderEvaluatedCodeIntervalFinder, + #superclass : #Object, + #instVars : [ + 'debugSession', + 'context', + 'sourceString', + 'evaluatedCode' + ], + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #accessing } +GtCoderEvaluatedCodeIntervalFinder >> context [ + + ^ context +] + +{ #category : #accessing } +GtCoderEvaluatedCodeIntervalFinder >> context: aContext [ + context := aContext +] + +{ #category : #accessing } +GtCoderEvaluatedCodeIntervalFinder >> debugSession [ + + ^ debugSession +] + +{ #category : #accessing } +GtCoderEvaluatedCodeIntervalFinder >> debugSession: anObject [ + debugSession := anObject +] + +{ #category : #accessing } +GtCoderEvaluatedCodeIntervalFinder >> evaluatedCode [ + + ^ evaluatedCode +] + +{ #category : #accessing } +GtCoderEvaluatedCodeIntervalFinder >> evaluatedCode: anObject [ + evaluatedCode := anObject +] + +{ #category : #finding } +GtCoderEvaluatedCodeIntervalFinder >> find [ + "Return an interval that corresponds to a given evaluated code." + + + debugSession ifNil: [ ^ nil ]. + debugSession process ifNil: [ ^ nil ]. + context ifNil: [ ^ nil ]. + context isDead ifTrue: [ ^ nil ]. + + ^ self debugSession pcRangeForContext: context +] + +{ #category : #accessing } +GtCoderEvaluatedCodeIntervalFinder >> sourceString [ + + ^ sourceString +] + +{ #category : #accessing } +GtCoderEvaluatedCodeIntervalFinder >> sourceString: anObject [ + sourceString := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderEvaluationResultAttributeElementId.class.st b/src/GToolkit-Coder-UI/GtCoderEvaluationResultAttributeElementId.class.st new file mode 100644 index 000000000..55c0324e1 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderEvaluationResultAttributeElementId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderEvaluationResultAttributeElementId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtCoderEvaluationResultAttributeElementId >> asSymbol [ + ^ #'coder-evaluate-result-attribute-element' +] diff --git a/src/GToolkit-Coder-UI/GtCoderEvaluationStatus.class.st b/src/GToolkit-Coder-UI/GtCoderEvaluationStatus.class.st new file mode 100644 index 000000000..c8870aac5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderEvaluationStatus.class.st @@ -0,0 +1,55 @@ +" +I represent an evaluation status of a source code. +I am used by {{gtClass:GtSourceCoderViewModel}}. +I am an abstract class. +See my subclasses for concrete evaluation statuses: {{gtClass:GtCoderEvaluationStatus | expanded=true | show=#gtSubclassesFor:}} +" +Class { + #name : #GtCoderEvaluationStatus, + #superclass : #Object, + #traits : 'TGtOptions', + #classTraits : 'TGtOptions classTrait', + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #testing } +GtCoderEvaluationStatus class >> isAbstract [ + ^ self name = #GtCoderEvaluationStatus +] + +{ #category : #converting } +GtCoderEvaluationStatus >> asDebugSessionInSpaceEvaluationStatusFromAnnouncement: anAnnouncement [ + ^ GtCoderNoEvaluationStatus default +] + +{ #category : #'api - hooks' } +GtCoderEvaluationStatus >> handleStatusChangedAnnouncement: anAnnouncement sourceCoderElement: anElement [ + +] + +{ #category : #testing } +GtCoderEvaluationStatus >> hasDebugSessionInSpace [ + ^ false +] + +{ #category : #testing } +GtCoderEvaluationStatus >> hasSharedDebugSession [ + ^ false +] + +{ #category : #'api - hooks' } +GtCoderEvaluationStatus >> mayCloseSourceCoderViewModel: aViewModel debuggerSpaceOfStatus: anEvaluationStatus [ +] + +{ #category : #'api - hooks' } +GtCoderEvaluationStatus >> mayTerminateSourceCoderViewModel: aViewModel sharedDebugSessionOfStatus: anEvaluationStatus [ + "Usually, we want to terminate a debug session of a previous evaluation status." + + anEvaluationStatus sharedDebugSession unsubscribe: aViewModel. + anEvaluationStatus sharedDebugSession terminate +] + +{ #category : #'api - hooks' } +GtCoderEvaluationStatus >> sourceCoderViewModel: aViewModel evaluationStatusChangedTo: aNewStatus [ + +] diff --git a/src/GToolkit-Coder-UI/GtCoderEvaluationStatusChangedSignal.class.st b/src/GToolkit-Coder-UI/GtCoderEvaluationStatusChangedSignal.class.st new file mode 100644 index 000000000..1b0f2dd8c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderEvaluationStatusChangedSignal.class.st @@ -0,0 +1,57 @@ +Class { + #name : #GtCoderEvaluationStatusChangedSignal, + #superclass : #ContextStackSignal, + #instVars : [ + 'viewModel', + 'oldStatus', + 'newStatus' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #testing } +GtCoderEvaluationStatusChangedSignal class >> gtNormalOperationSignal [ + "Answer a Boolean indicating whether this signal is generated as part of normal operations. + See {{gtMethod:BeaconSignal class>>gtNormalOperationSignal}} for a description" + + ^ true. +] + +{ #category : #accessing } +GtCoderEvaluationStatusChangedSignal >> newStatus [ + ^ newStatus +] + +{ #category : #accessing } +GtCoderEvaluationStatusChangedSignal >> newStatus: anObject [ + newStatus := anObject +] + +{ #category : #accessing } +GtCoderEvaluationStatusChangedSignal >> oldStatus [ + ^ oldStatus +] + +{ #category : #accessing } +GtCoderEvaluationStatusChangedSignal >> oldStatus: anObject [ + oldStatus := anObject +] + +{ #category : #printing } +GtCoderEvaluationStatusChangedSignal >> printOneLineContentsOn: stream [ + stream print: viewModel identityHash. + stream nextPutAll: ', '. + oldStatus gtDisplayOn: stream. + stream nextPutAll: ' -> '. + newStatus gtDisplayOn: stream +] + +{ #category : #accessing } +GtCoderEvaluationStatusChangedSignal >> viewModel [ + ^ viewModel +] + +{ #category : #accessing } +GtCoderEvaluationStatusChangedSignal >> viewModel: anObject [ + viewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderEvaluationStatusShowNotificationSignal.class.st b/src/GToolkit-Coder-UI/GtCoderEvaluationStatusShowNotificationSignal.class.st new file mode 100644 index 000000000..3f1bf441a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderEvaluationStatusShowNotificationSignal.class.st @@ -0,0 +1,48 @@ +Class { + #name : #GtCoderEvaluationStatusShowNotificationSignal, + #superclass : #ContextStackSignal, + #instVars : [ + 'evaluationStatus', + 'announcement', + 'sourceCoderElement' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #testing } +GtCoderEvaluationStatusShowNotificationSignal class >> gtNormalOperationSignal [ + "Answer a Boolean indicating whether this signal is generated as part of normal operations. + See {{gtMethod:BeaconSignal class>>gtNormalOperationSignal}} for a description" + + ^ true. +] + +{ #category : #accessing } +GtCoderEvaluationStatusShowNotificationSignal >> announcement [ + ^ announcement +] + +{ #category : #accessing } +GtCoderEvaluationStatusShowNotificationSignal >> announcement: anObject [ + announcement := anObject +] + +{ #category : #accessing } +GtCoderEvaluationStatusShowNotificationSignal >> evaluationStatus [ + ^ evaluationStatus +] + +{ #category : #accessing } +GtCoderEvaluationStatusShowNotificationSignal >> evaluationStatus: anObject [ + evaluationStatus := anObject +] + +{ #category : #accessing } +GtCoderEvaluationStatusShowNotificationSignal >> sourceCoderElement [ + ^ sourceCoderElement +] + +{ #category : #accessing } +GtCoderEvaluationStatusShowNotificationSignal >> sourceCoderElement: anObject [ + sourceCoderElement := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderExampleStateElement.class.st b/src/GToolkit-Coder-UI/GtCoderExampleStateElement.class.st index 6e25ce974..02ffe5514 100644 --- a/src/GToolkit-Coder-UI/GtCoderExampleStateElement.class.st +++ b/src/GToolkit-Coder-UI/GtCoderExampleStateElement.class.st @@ -1,16 +1,16 @@ " I am an {{gtClass:BlElement}}. I am an icon that represents an {{gtClass:GtExampleWithResult}} result state. -1. # Not Executed State +#Not Executed State {{gtExample:GtCoderExampleStateElementExamples>>#exampleStateElement_NotExecuted|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=50}} -1. # Success State +#Success State {{gtExample:GtCoderExampleStateElementExamples>>#exampleStateElement_Success|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=50}} -1. # Failure State +#Failure State {{gtExample:GtCoderExampleStateElementExamples>>#exampleStateElement_Failure|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=50}} -1. # Error State +#Error State {{gtExample:GtCoderExampleStateElementExamples>>#exampleStateElement_Error|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=50}} @@ -69,7 +69,7 @@ GtCoderExampleStateElement >> exampleResult [ { #category : #'private - accessing' } GtCoderExampleStateElement >> exampleState [ - ^ GtFilterExampleState default stateFromExample: self example + ^ GtExampleState default stateFromExample: self example ] { #category : #'private - testing' } @@ -87,13 +87,12 @@ GtCoderExampleStateElement >> initialize [ self layout: BlLinearLayout horizontal; - geometry: BlCircle new; + geometry: BlSquareGeometry new; margin: (BlInsets all: 1); - exact: 8@8. + exact: 12@12. self initializeInteractiveLook. self initializeTooltipLook. - self addAptitude: BrLayoutResizerAptitude inherit. ] { #category : #initialization } @@ -105,7 +104,7 @@ GtCoderExampleStateElement >> initializeInteractiveLook [ { #category : #initialization } GtCoderExampleStateElement >> initializeTooltipLook [ self addAptitude: - (BrGlamorousWithTooltipAptitude2 content: [ + (BrGlamorousWithExplicitTooltipAptitude content: [ self newTooltipContent ]) ] @@ -120,9 +119,16 @@ GtCoderExampleStateElement >> initializeViewModels [ { #category : #'private - instance creation' } GtCoderExampleStateElement >> newInspectorWithExampleResult [ - ^ ((GtInspector forObject: self tooltipObject) - exact: 400@400) - asScalableElement size: 200@200. + + | anElement | + anElement := self exampleResult + ifNotNil: [ :anExampleResult | + self example asGtExampleResultPreviewElement ] + ifNil: [ + ((GtInspector forObject: self example) exact: + 400 @ 400) asScalableElement size: 200 @ 200 ]. + + ^ anElement ] { #category : #'private - instance creation' } @@ -176,9 +182,8 @@ GtCoderExampleStateElement >> newTooltipContent [ | aContainer | aContainer := BrFrame new fitContent; - when: GtPhlowObjectToSpawn do: [ :anEvent | self fireEvent: (GtPhlowObjectToSpawn new - object: anEvent object; - sourceElement: anEvent target) ]. + when: GtPhlowObjectToSpawn do: [ :anEvent | + self phlow spawnPreviousEvent: anEvent ]. aContainer addChild: (self example hasResult ifTrue: [ self newInspectorWithExampleResult ] ifFalse: [ self newNotExecutedTooltipContent ]). @@ -221,9 +226,10 @@ GtCoderExampleStateElement >> playExampleFrom: aButton [ { #category : #'private - actions' } GtCoderExampleStateElement >> spawnExampleFrom: anElement [ - self exampleDo: [ :anExample | + + self exampleDo: [ :anExample | anExample hasResult ifTrue: [ - anElement phlow spawnObject: anExample result returnValueOrExampleException ] ] + anElement phlow spawnObject: anExample result returnValue ] ] ] { #category : #'private - accessing' } @@ -234,18 +240,12 @@ GtCoderExampleStateElement >> styleLook [ { #category : #'private - subscriptions' } GtCoderExampleStateElement >> subscribeToExample [ - self example announcer + self example announcer weak when: GtExampleExecuted send: #onExampleExecuted: to: self. ] -{ #category : #'private - accessing' } -GtCoderExampleStateElement >> tooltipObject [ - self exampleResult ifNotNil: [ :aResult | ^ aResult returnValueOrExampleException ]. - ^ self example -] - { #category : #'private - subscriptions' } GtCoderExampleStateElement >> unsubscribeFromExample [ self example ifNil: [ ^ self ]. @@ -261,10 +261,6 @@ GtCoderExampleStateElement >> updateIcon [ { #category : #'private - updating' } GtCoderExampleStateElement >> updateStyleLook [ - "the following nil check can be removed anytime later. - It is useful for a couple of days until the next image build (instead of update)." - self styleLook ifNil: [ ^ self ]. - self styleLook default: [ :aStyle | aStyle background: self exampleState color ]; hovered: [ :aStyle | aStyle background: self exampleState color darker ]. diff --git a/src/GToolkit-Coder-UI/GtCoderExecutionContextVariable.class.st b/src/GToolkit-Coder-UI/GtCoderExecutionContextVariable.class.st deleted file mode 100644 index 26c4e21d2..000000000 --- a/src/GToolkit-Coder-UI/GtCoderExecutionContextVariable.class.st +++ /dev/null @@ -1,50 +0,0 @@ -" -I am a {{gtClass:DynamicVariable}}. -I hold a {{gtMethod:BlSpace>>id}} as a {{gtClass:Process}} specific value. -I am used to pass an execution context information that can be requested, for example to display a user information. -1. # Usage -## Initializing Execution Context - -To initialize execution context value using a {{gtClass:BlElement}}, use {{gtMethod:GtCoderExecutionContextVariable class>>#element:do:|label=#selector|expanded=true}} class-side method: -1. ## Obtaining Execution Context - -To obtain the execution context, use {{gtMethod:GtCoderExecutionContextVariable class>>#spaceDo:ifClosed:|label=#selector|expanded=true}} class-side method: - - -" -Class { - #name : #GtCoderExecutionContextVariable, - #superclass : #DynamicVariable, - #category : #'GToolkit-Coder-UI-Basic' -} - -{ #category : #'api - execution' } -GtCoderExecutionContextVariable class >> element: anElement do: aBlock [ - "Initialize a Space ID using the element and evaluate the block" - | aSpaceId | - aSpaceId := self newSpaceIdFromElement: anElement. - ^ self - value: aSpaceId - during: aBlock -] - -{ #category : #testing } -GtCoderExecutionContextVariable class >> isInheritable [ - ^true -] - -{ #category : #'private - instance creation' } -GtCoderExecutionContextVariable class >> newSpaceIdFromElement: anElement [ - anElement spaceDo: [ :aSpace | - ^ aSpace id ]. - ^ nil -] - -{ #category : #'api - execution' } -GtCoderExecutionContextVariable class >> spaceDo: aSpaceBlock ifClosed: aClosedBlock [ - self value ifNotNil: [ :aSpaceId | - BlSpace - spaceWithId: aSpaceId - do: [ :aSpace | ^ aSpaceBlock cull: aSpace ] ]. - ^ aClosedBlock value -] diff --git a/src/GToolkit-Coder-UI/GtCoderExpanderShadowAptitude.class.st b/src/GToolkit-Coder-UI/GtCoderExpanderShadowAptitude.class.st index 29b32f44d..a7b139607 100644 --- a/src/GToolkit-Coder-UI/GtCoderExpanderShadowAptitude.class.st +++ b/src/GToolkit-Coder-UI/GtCoderExpanderShadowAptitude.class.st @@ -1,16 +1,37 @@ Class { #name : #GtCoderExpanderShadowAptitude, - #superclass : #BrStyleCommonAptitude, + #superclass : #BrLazyStyleCommonAptitude, + #classVars : [ + 'IsWithPermanentShadows' + ], #category : #'GToolkit-Coder-UI-Looks' } +{ #category : #settings } +GtCoderExpanderShadowAptitude class >> isWithPermanentShadows [ + ^ IsWithPermanentShadows ifNil: [ IsWithPermanentShadows := false ] +] + +{ #category : #settings } +GtCoderExpanderShadowAptitude class >> isWithPermanentShadows: aBoolean [ + "Set to true if you want to display shadows around all expanded method coders. + Set to false if you want to display shadows only around focused and hovered expanded method coders." + + IsWithPermanentShadows := aBoolean +] + { #category : #initialization } GtCoderExpanderShadowAptitude >> initialize [ + | aHoverState | super initialize. + aHoverState := self class isWithPermanentShadows + ifTrue: [ (self state hovered or: (self state expanded & self state unfocused)) ] + ifFalse: [ self state hovered and: self state unfocused ]. + self - when: (self state expanded & self state focused) + when: self state focused style: [ :aStyle | aStyle effect: BrGlamorousFocusedShadowEffect new ]; - when: (self state expanded & self state unfocused) + when: aHoverState style: [ :aStyle | aStyle effect: BrGlamorousShadowEffect new ] ] diff --git a/src/GToolkit-Coder-UI/GtCoderExpanderWithoutShadowAptitude.class.st b/src/GToolkit-Coder-UI/GtCoderExpanderWithoutShadowAptitude.class.st index 8ecdb796a..b31b54e58 100644 --- a/src/GToolkit-Coder-UI/GtCoderExpanderWithoutShadowAptitude.class.st +++ b/src/GToolkit-Coder-UI/GtCoderExpanderWithoutShadowAptitude.class.st @@ -63,8 +63,7 @@ GtCoderExpanderWithoutShadowAptitude >> defaultBorderPaint [ { #category : #initialization } GtCoderExpanderWithoutShadowAptitude >> defaultFocusedBorderPaint [ - ^ (self theme default primaryBorderColor alpha: 0.4) - gtOpaqueColorOnWhite + ^ self theme default primaryBorderColor ] { #category : #accessing } @@ -81,7 +80,6 @@ GtCoderExpanderWithoutShadowAptitude >> expandedBorderWidth: aNumber [ { #category : #initialization } GtCoderExpanderWithoutShadowAptitude >> initialize [ - | aTriangle | super initialize. borderWidth := 0. @@ -92,9 +90,7 @@ GtCoderExpanderWithoutShadowAptitude >> initialize [ borderPaint := self defaultBorderPaint. borderFocusedPaint := self defaultFocusedBorderPaint. - aTriangle := self newTriangle. toggle := self newToggle. - toggle addChild: aTriangle. sidebar := self newSidebar. sidebar addChild: toggle. @@ -103,20 +99,17 @@ GtCoderExpanderWithoutShadowAptitude >> initialize [ padding: self padding; layout: BlLinearLayout vertical. - self add: (BrStyleCommonAptitude new + self add: (BrLazyStyleCommonAptitude new + default: [ :aStyle :aTheme | + aStyle background: aTheme default contentBackground ]; collapsed: [ :aStyle | aStyle border: (BlBorder paint: self borderPaint width: self borderWidth) ]; expanded: [ :aStyle | - aStyle background: Color white. aStyle border: (BlBorder paint: self borderPaint width: self expandedBorderWidth) ]; - focused: [ :aStyle | (BlBorder paint: self borderFocusedPaint width: self expandedBorderWidth) ]). + focused: [ :aStyle | + aStyle border: (BlBorder paint: self borderFocusedPaint width: self expandedBorderWidth) ]). - self add: (BrStyleCommonAptitude new - @ aTriangle; - collapsed: [ :aStyle | aStyle geometry: self newCollapsedTriangleGeometry ]; - expanded: [ :aStyle | aStyle geometry: self newExpandedTriangleGeometry ]). - - self add: (BrStyleCommonAptitude new + self add: (BrLazyStyleCommonAptitude new @ sidebar; default: [ :aStyle | aStyle background: self borderPaint ]; focused: [ :aStyle | aStyle background: self borderFocusedPaint ]). @@ -133,7 +126,7 @@ GtCoderExpanderWithoutShadowAptitude >> initialize [ { #category : #'private - instance creation' } GtCoderExpanderWithoutShadowAptitude >> newCollapsedTriangleGeometry [ - ^ BlPolygon vertices: { + ^ BlPolygonGeometry vertices: { (0 @ 0). (8 @ 4). (0 @ 8) @@ -142,7 +135,7 @@ GtCoderExpanderWithoutShadowAptitude >> newCollapsedTriangleGeometry [ { #category : #'private - instance creation' } GtCoderExpanderWithoutShadowAptitude >> newExpandedTriangleGeometry [ - ^ BlPolygon vertices: { + ^ BlPolygonGeometry vertices: { (0 @ 0). (8 @ 0). (4 @ 8) diff --git a/src/GToolkit-Coder-UI/GtCoderExplicitContextMenuItemAction.extension.st b/src/GToolkit-Coder-UI/GtCoderExplicitContextMenuItemAction.extension.st new file mode 100644 index 000000000..d9719d912 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderExplicitContextMenuItemAction.extension.st @@ -0,0 +1,17 @@ +Extension { #name : #GtCoderExplicitContextMenuItemAction } + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderExplicitContextMenuItemAction >> asBrMenuItemForCoderElement: aTextualCoderEditorElement [ + + | aMenuItem | + aMenuItem := menuItemBlock + cull: aTextualCoderEditorElement + cull: aTextualCoderEditorElement textualCoderViewModel + cull: self. + + aMenuItem ifNotNil: [ + self allowAltClick ifTrue: [ aMenuItem definition: [ self actionDefinition ] ]. + aMenuItem disableIf: [ self isDisabled ] ]. + + ^ aMenuItem +] diff --git a/src/GToolkit-Coder-UI/GtCoderHideSidebarWish.class.st b/src/GToolkit-Coder-UI/GtCoderHideSidebarWish.class.st new file mode 100644 index 000000000..d96b5573f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderHideSidebarWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderHideSidebarWish, + #superclass : #BrWish, + #category : #'GToolkit-Coder-UI-Basic' +} diff --git a/src/GToolkit-Coder-UI/GtCoderIncomingEvaluationStatusSignal.class.st b/src/GToolkit-Coder-UI/GtCoderIncomingEvaluationStatusSignal.class.st new file mode 100644 index 000000000..4fc9d8b68 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderIncomingEvaluationStatusSignal.class.st @@ -0,0 +1,57 @@ +Class { + #name : #GtCoderIncomingEvaluationStatusSignal, + #superclass : #ContextStackSignal, + #instVars : [ + 'viewModel', + 'existingStatus', + 'newStatus' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #testing } +GtCoderIncomingEvaluationStatusSignal class >> gtNormalOperationSignal [ + "Answer a Boolean indicating whether this signal is generated as part of normal operations. + See {{gtMethod:BeaconSignal class>>gtNormalOperationSignal}} for a description" + + ^ true. +] + +{ #category : #accessing } +GtCoderIncomingEvaluationStatusSignal >> existingStatus [ + ^ existingStatus +] + +{ #category : #accessing } +GtCoderIncomingEvaluationStatusSignal >> existingStatus: anObject [ + existingStatus := anObject +] + +{ #category : #accessing } +GtCoderIncomingEvaluationStatusSignal >> newStatus [ + ^ newStatus +] + +{ #category : #accessing } +GtCoderIncomingEvaluationStatusSignal >> newStatus: anObject [ + newStatus := anObject +] + +{ #category : #printing } +GtCoderIncomingEvaluationStatusSignal >> printOneLineContentsOn: stream [ + stream print: viewModel identityHash. + stream nextPutAll: ', '. + existingStatus gtDisplayOn: stream. + stream nextPutAll: ' -> '. + newStatus gtDisplayOn: stream +] + +{ #category : #accessing } +GtCoderIncomingEvaluationStatusSignal >> viewModel [ + ^ viewModel +] + +{ #category : #accessing } +GtCoderIncomingEvaluationStatusSignal >> viewModel: anObject [ + viewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderListEmbeddedRenameEditorElement.class.st b/src/GToolkit-Coder-UI/GtCoderListEmbeddedRenameEditorElement.class.st new file mode 100644 index 000000000..9c2db2cb2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderListEmbeddedRenameEditorElement.class.st @@ -0,0 +1,158 @@ +Class { + #name : #GtCoderListEmbeddedRenameEditorElement, + #superclass : #BrHorizontalPane, + #instVars : [ + 'itemToRename', + 'ghostText', + 'editorElement', + 'renameAction', + 'endRenameAction' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #initialization } +GtCoderListEmbeddedRenameEditorElement >> createAcceptButton [ + ^ BrButton new + beTinySize; + margin: (BlInsets left: 5 right: 2); + aptitude: (BrGlamorousButtonRectangularAptitude paddingScale: 0.5) + + BrGlamorousButtonIconAptitude + + (BrGlamorousButtonFlatExteriorAptitude new + backgroundPaint: BrGlamorousColors neutralBackgroundColor; + hoveredBackgroundPaint: BrGlamorousColors secondaryHeaderBackgroundColor) + + BrGlamorousButtonWithLabelTooltipAptitude2; + icon: BrGlamorousVectorIcons accept; + label: 'Rename protocol'; + action: [ self privatePerformRename ] +] + +{ #category : #initialization } +GtCoderListEmbeddedRenameEditorElement >> createCancelButton [ + ^ BrButton new + beTinySize; + margin: (BlInsets left: 2 right: 2); + aptitude: (BrGlamorousButtonRectangularAptitude paddingScale: 0.5) + + BrGlamorousButtonIconAptitude + + (BrGlamorousButtonFlatExteriorAptitude new + backgroundPaint: BrGlamorousColors neutralBackgroundColor; + hoveredBackgroundPaint: BrGlamorousColors secondaryHeaderBackgroundColor) + + BrGlamorousButtonWithLabelTooltipAptitude2; + icon: BrGlamorousVectorIcons cancel; + label: 'Cancel'; + action: [ self privateEndRename ] +] + +{ #category : #'private - actions' } +GtCoderListEmbeddedRenameEditorElement >> createCompleterStrategy [ + ^ self subclassResponsibility +] + +{ #category : #initialization } +GtCoderListEmbeddedRenameEditorElement >> createEditorElement [ + | aEditorElement aCompleter | + + aEditorElement := BrEditor new + aptitude: BrGlamorousRegularEditorAptitude new glamorousRegularSmallSize + + BrGlamorousInputFieldSpacingAptitude; + beMode: BrTextEditorEditableSingleLineMode new; + geometry: (BlRoundedRectangleGeometry cornerRadius: 4); + padding: (BlInsets + top: 2 + left: 5 + bottom: 2 + right: 5); + hMatchParent; + vFitContent; + text: ''; + requestFocus. + + aCompleter := GtCompletionController + on: aEditorElement + strategy: self createCompleterStrategy. + + aCompleter install. + + aEditorElement + addEditorShortcut: (BlShortcutWithAction new + combination: BlKeyCombination escape; + action: [ :anEvent | self privateEndRename ]). + + aEditorElement + addEditorShortcut: (BlShortcutWithAction new + combination: BlKeyCombination enter; + action: [ :anEvent | self privatePerformRename ]). + + ^ aEditorElement +] + +{ #category : #initialization } +GtCoderListEmbeddedRenameEditorElement >> createGhostTestAttribute [ + ^ BrGhostTextAttribute + for: (ghostText asRopedText glamorousFormEditorCodeFontAndSize + foreground: Color lightGray) +] + +{ #category : #accessing } +GtCoderListEmbeddedRenameEditorElement >> endRenameAction: aBlock [ + endRenameAction := aBlock +] + +{ #category : #initialization } +GtCoderListEmbeddedRenameEditorElement >> initialize [ + | anAcceptButton aCancelButton | + super initialize. + + self alignCenterLeft; + hMatchParent; + vFitContent. + + ghostText := 'new name'. + renameAction := [ :anItemToRename :aNewName | ]. + endRenameAction := [ :anItemToRename | ]. + + editorElement := self createEditorElement. + anAcceptButton := self createAcceptButton. + aCancelButton := self createCancelButton. + + self addChildren: { editorElement . anAcceptButton . aCancelButton }. +] + +{ #category : #'private - actions' } +GtCoderListEmbeddedRenameEditorElement >> itemLabelOf: anItemToRename [ + ^ self subclassResponsibility +] + +{ #category : #accessing } +GtCoderListEmbeddedRenameEditorElement >> itemToRename: anItemToRename [ + | anItemText | + + itemToRename := anItemToRename. + anItemText := (self itemLabelOf: itemToRename) asRopedText attribute: self createGhostTestAttribute. + + editorElement text: anItemText. + editorElement navigator moveToEnd apply. + editorElement selecter all select. +] + +{ #category : #'private - actions' } +GtCoderListEmbeddedRenameEditorElement >> privateEndRename [ + endRenameAction cull: itemToRename +] + +{ #category : #'private - actions' } +GtCoderListEmbeddedRenameEditorElement >> privatePerformRename [ + "Performs a rename protocol action and updates UI removing a protocol editor" + | aNewName | + + aNewName := editorElement text asString trimmed. + + renameAction cull: itemToRename cull: aNewName. + + self privateEndRename +] + +{ #category : #accessing } +GtCoderListEmbeddedRenameEditorElement >> renameAction: aBlock [ + renameAction := aBlock +] diff --git a/src/GToolkit-Coder-UI/GtCoderLocateDebuggerAction.class.st b/src/GToolkit-Coder-UI/GtCoderLocateDebuggerAction.class.st new file mode 100644 index 000000000..6fb0c03bf --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderLocateDebuggerAction.class.st @@ -0,0 +1,49 @@ +Class { + #name : #GtCoderLocateDebuggerAction, + #superclass : #GtCoderAction, + #instVars : [ + 'weakSpace' + ], + #category : #'GToolkit-Coder-UI-Coder - Addons' +} + +{ #category : #comparing } +GtCoderLocateDebuggerAction >> = anObject [ + ^ super = anObject and: [ self space = anObject space ] +] + +{ #category : #building } +GtCoderLocateDebuggerAction >> buildElementIn: aCoderActionsElement [ + ^ aCoderActionsElement newLocateDebuggerButtonForAction: self +] + +{ #category : #comparing } +GtCoderLocateDebuggerAction >> hash [ + ^ super hash bitXor: self space hash +] + +{ #category : #initialization } +GtCoderLocateDebuggerAction >> initialize [ + super initialize. + + self + icon: BrGlamorousVectorIcons debug; + title: 'Locate debugger'; + action: [ :aCoderViewModel :aButtonElement :anEvent | + self locateDebuggerInSpace ] +] + +{ #category : #'private - event handling' } +GtCoderLocateDebuggerAction >> locateDebuggerInSpace [ + ^ self space ifNotNil: [ :theSpace | theSpace toFront ] +] + +{ #category : #accessing } +GtCoderLocateDebuggerAction >> space [ + ^ weakSpace ifNotNil: [ :aWeak | aWeak at: 1 ] +] + +{ #category : #accessing } +GtCoderLocateDebuggerAction >> weakSpace: anObject [ + weakSpace := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderMenuActionItem.class.st b/src/GToolkit-Coder-UI/GtCoderMenuActionItem.class.st new file mode 100644 index 000000000..b6bd19ccc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderMenuActionItem.class.st @@ -0,0 +1,56 @@ +Class { + #name : #GtCoderMenuActionItem, + #superclass : #BrMenuActionItem, + #instVars : [ + 'textualCoderEditorElement' + ], + #category : #'GToolkit-Coder-UI-Coder - Addons' +} + +{ #category : #visiting } +GtCoderMenuActionItem >> acceptVisitor: aVisitor [ + ^ aVisitor visitCoderMenuActionItem: self +] + +{ #category : #accessing } +GtCoderMenuActionItem >> coderContextMenuAction [ + + ^ self action +] + +{ #category : #accessing } +GtCoderMenuActionItem >> coderContextMenuAction: aCoderContextMenuAction [ + self action: aCoderContextMenuAction. + self id: aCoderContextMenuAction id. + aCoderContextMenuAction group ifNotNil: [ :aGroup | + self group: aGroup ]. + aCoderContextMenuAction shortcutKey + ifNotEmpty: [ self label: aCoderContextMenuAction title shortcut: aCoderContextMenuAction shortcutKey ] + ifEmpty: [ self label: aCoderContextMenuAction title ] +] + +{ #category : #accessing } +GtCoderMenuActionItem >> hoverAction [ + ^ self coderContextMenuAction hoverAction +] + +{ #category : #accessing } +GtCoderMenuActionItem >> leaveAction [ + ^ self coderContextMenuAction leaveAction +] + +{ #category : #accessing } +GtCoderMenuActionItem >> textualCoderEditorElement [ + + ^ textualCoderEditorElement +] + +{ #category : #accessing } +GtCoderMenuActionItem >> textualCoderEditorElement: aCoderElement [ + textualCoderEditorElement := aCoderElement +] + +{ #category : #accessing } +GtCoderMenuActionItem >> textualCoderViewModel [ + ^ self textualCoderEditorElement textualCoderViewModel +] diff --git a/src/GToolkit-Coder-UI/GtCoderMenuActionItemElement.class.st b/src/GToolkit-Coder-UI/GtCoderMenuActionItemElement.class.st new file mode 100644 index 000000000..b473e0a13 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderMenuActionItemElement.class.st @@ -0,0 +1,46 @@ +Class { + #name : #GtCoderMenuActionItemElement, + #superclass : #BrMenuActionItemElement, + #category : #'GToolkit-Coder-UI-Coder - Addons' +} + +{ #category : #'event handling' } +GtCoderMenuActionItemElement >> leaveOnEvent: anEvent [ + | aMenuModel | + aMenuModel := self menuModel. + aMenuModel ifNil: [ ^ self ]. + aMenuModel leaveAction ifNotNil: [ :aLeaveBlock | + aLeaveBlock cull: aMenuModel textualCoderViewModel cull: anEvent ] +] + +{ #category : #'event handling' } +GtCoderMenuActionItemElement >> onClick: anEvent element: anElement model: aButtonModel [ + | aMenuModel aCoderAction | + aMenuModel := self menuModel. + aMenuModel ifNil: [ ^ self ]. + self leaveOnEvent: anEvent. + aCoderAction := aMenuModel coderContextMenuAction. + aCoderAction ifNil: [ ^ self ]. + aCoderAction action ifNotNil: [ :anAction | + anAction gtValueWithArgs: { + aMenuModel textualCoderEditorElement textualCoderViewModel. + aMenuModel textualCoderEditorElement. + anEvent } ]. + aMenuModel hideOnClick ifTrue: [ aMenuModel hideAll ] +] + +{ #category : #'event handling' } +GtCoderMenuActionItemElement >> onMouseEnterEvent: anEvent [ + | aMenuModel | + super onMouseEnterEvent: anEvent. + aMenuModel := self menuModel. + aMenuModel ifNil: [ ^ self ]. + aMenuModel hoverAction ifNotNil: [ :aHoverBlock | + aHoverBlock cull: aMenuModel textualCoderViewModel cull: anEvent ] +] + +{ #category : #'event handling' } +GtCoderMenuActionItemElement >> onMouseLeaveEvent: anEvent [ + super onMouseLeaveEvent: anEvent. + self leaveOnEvent: anEvent +] diff --git a/src/GToolkit-Coder-UI/GtCoderMenuSubmenuItem.class.st b/src/GToolkit-Coder-UI/GtCoderMenuSubmenuItem.class.st new file mode 100644 index 000000000..154b4ed83 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderMenuSubmenuItem.class.st @@ -0,0 +1,56 @@ +Class { + #name : #GtCoderMenuSubmenuItem, + #superclass : #BrMenuSubmenuItem, + #instVars : [ + 'textualCoderEditorElement' + ], + #category : #'GToolkit-Coder-UI-Coder - Addons' +} + +{ #category : #visiting } +GtCoderMenuSubmenuItem >> acceptVisitor: aVisitor [ + ^ aVisitor visitCoderSubmenuItem: self +] + +{ #category : #accessing } +GtCoderMenuSubmenuItem >> coderContextMenuAction [ + + ^ self action +] + +{ #category : #accessing } +GtCoderMenuSubmenuItem >> coderContextMenuAction: aCoderContextMenuAction [ + self action: aCoderContextMenuAction. + self id: aCoderContextMenuAction id. + aCoderContextMenuAction group ifNotNil: [ :aGroup | + self group: aGroup ]. + aCoderContextMenuAction shortcutKey + ifNotEmpty: [ self label: aCoderContextMenuAction title shortcut: aCoderContextMenuAction shortcutKey ] + ifEmpty: [ self label: aCoderContextMenuAction title ] +] + +{ #category : #accessing } +GtCoderMenuSubmenuItem >> hoverAction [ + ^ self coderContextMenuAction hoverAction +] + +{ #category : #accessing } +GtCoderMenuSubmenuItem >> leaveAction [ + ^ self coderContextMenuAction leaveAction +] + +{ #category : #accessing } +GtCoderMenuSubmenuItem >> textualCoderEditorElement [ + + ^ textualCoderEditorElement +] + +{ #category : #accessing } +GtCoderMenuSubmenuItem >> textualCoderEditorElement: aCoderElement [ + textualCoderEditorElement := aCoderElement +] + +{ #category : #accessing } +GtCoderMenuSubmenuItem >> textualCoderViewModel [ + ^ self textualCoderEditorElement textualCoderViewModel +] diff --git a/src/GToolkit-Coder-UI/GtCoderMenuSubmenuItemElement.class.st b/src/GToolkit-Coder-UI/GtCoderMenuSubmenuItemElement.class.st new file mode 100644 index 000000000..1a840d38b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderMenuSubmenuItemElement.class.st @@ -0,0 +1,46 @@ +Class { + #name : #GtCoderMenuSubmenuItemElement, + #superclass : #BrMenuSubmenuItemElement, + #category : #'GToolkit-Coder-UI-Coder - Addons' +} + +{ #category : #'event handling' } +GtCoderMenuSubmenuItemElement >> leaveOnEvent: anEvent [ + | aMenuModel | + aMenuModel := self menuModel. + aMenuModel ifNil: [ ^ self ]. + aMenuModel leaveAction ifNotNil: [ :aLeaveBlock | + aLeaveBlock cull: aMenuModel textualCoderViewModel cull: anEvent ] +] + +{ #category : #'event handling' } +GtCoderMenuSubmenuItemElement >> onClick: anEvent element: anElement model: aButtonModel [ + | aMenuModel aCoderAction | + aMenuModel := self menuModel. + aMenuModel ifNil: [ ^ self ]. + self leaveOnEvent: anEvent. + aCoderAction := aMenuModel coderContextMenuAction. + aCoderAction ifNil: [ ^ self ]. + aCoderAction action ifNotNil: [ :anAction | + anAction gtValueWithArgs: { + aMenuModel textualCoderEditorElement textualCoderViewModel. + aMenuModel textualCoderEditorElement. + anEvent } ]. + aMenuModel hideOnClick ifTrue: [ aMenuModel hideAll ] +] + +{ #category : #'event handling' } +GtCoderMenuSubmenuItemElement >> onMouseEnterEvent: anEvent [ + | aMenuModel | + super onMouseEnterEvent: anEvent. + aMenuModel := self menuModel. + aMenuModel ifNil: [ ^ self ]. + aMenuModel hoverAction ifNotNil: [ :aHoverBlock | + aHoverBlock cull: aMenuModel textualCoderViewModel cull: anEvent ] +] + +{ #category : #'event handling' } +GtCoderMenuSubmenuItemElement >> onMouseLeaveEvent: anEvent [ + super onMouseLeaveEvent: anEvent. + self leaveOnEvent: anEvent +] diff --git a/src/GToolkit-Coder-UI/GtCoderMethodSelectedSourceEvent.class.st b/src/GToolkit-Coder-UI/GtCoderMethodSelectedSourceEvent.class.st new file mode 100644 index 000000000..29a5f16b2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderMethodSelectedSourceEvent.class.st @@ -0,0 +1,25 @@ +" +I am a helper event to indicate whether a selection happened due to a user click or changes in a model. +In other words, {{gtClass:GtCoderMethodsGroupedListElement}} does nothing if a selection is triggered from its {{gtClass:GtCoderNavigationModel}}. + +I am instantiated in {{gtMethod:GtCoderMethodsGroupedListElement>>#onMethodSelected:}} and checked in {{gtMethod:GtCoderMethodsGroupedListElement>>#onMethodsListSelectionChanged:}}. + +" +Class { + #name : #GtCoderMethodSelectedSourceEvent, + #superclass : #BlEvent, + #instVars : [ + 'originalSource' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #accessing } +GtCoderMethodSelectedSourceEvent >> originalSource [ + ^ originalSource +] + +{ #category : #accessing } +GtCoderMethodSelectedSourceEvent >> originalSource: anObject [ + originalSource := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderMethodTarget.class.st b/src/GToolkit-Coder-UI/GtCoderMethodTarget.class.st new file mode 100644 index 000000000..fa3057bc7 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderMethodTarget.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderMethodTarget, + #superclass : #GtPhlowActionUniqueTarget, + #category : #'GToolkit-Coder-UI-Phlow Targets' +} diff --git a/src/GToolkit-Coder-UI/GtCoderMethodsGroupedListElement.class.st b/src/GToolkit-Coder-UI/GtCoderMethodsGroupedListElement.class.st new file mode 100644 index 000000000..1b81685f7 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderMethodsGroupedListElement.class.st @@ -0,0 +1,643 @@ +Class { + #name : #GtCoderMethodsGroupedListElement, + #superclass : #BrGroupedList, + #traits : 'TGtCoderNavigationWithContextMenu', + #classTraits : 'TGtCoderNavigationWithContextMenu classTrait', + #instVars : [ + 'navigationModel', + 'methodGroups', + 'addOnsCache', + 'methodItemElementStencil' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #'private - insance creation' } +GtCoderMethodsGroupedListElement >> bindCompiledMethod: eachCompiledMethod withElement: element [ + element userData at: #method put: eachCompiledMethod. + + element compiledMethod: eachCompiledMethod +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> browseHierarchyImplementorsOf: aSymbol [ + self phlow spawnObject: aSymbol gtImplementors & self hierarchyFilter +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> browseHierarchyReferencesOf: aSymbol [ + self phlow spawnObject: aSymbol gtSenders & self hierarchyFilter +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> browseImplementorsOf: aSymbol [ + self phlow spawnObject: (GtSearchImplementorsFilter selector: aSymbol) +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> browseReferencesOf: aSymbol [ + self phlow spawnObject: (GtSearchReferencesFilter literal: aSymbol) +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> browseSeveralHierarchyImplementorsOf: aCollectionOfSymbols [ + | aFilter | + aFilter := aCollectionOfSymbols allButFirst + inject: aCollectionOfSymbols first gtImplementors & self hierarchyFilter + into: [ :filter :symbol | filter | (symbol gtImplementors & self hierarchyFilter) ]. + + self phlow spawnObject: aFilter +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> browseSeveralHierarchyReferencesOf: aCollectionOfSymbols [ + | aFilter | + aFilter := aCollectionOfSymbols allButFirst + inject: aCollectionOfSymbols first gtSenders & self hierarchyFilter + into: [ :filter :symbol | filter | (symbol gtSenders & self hierarchyFilter) ]. + + self phlow spawnObject: aFilter +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> browseSeveralImplementorsOf: aCollectionOfSymbols [ + | aFilter | + aFilter := aCollectionOfSymbols allButFirst + inject: (GtSearchImplementorsFilter selector: aCollectionOfSymbols first) + into: [ :filter :symbol | filter | (GtSearchImplementorsFilter selector: symbol) ]. + + self phlow spawnObject: aFilter +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> browseSeveralReferencesOf: aCollectionOfSymbols [ + | aFilter | + aFilter := aCollectionOfSymbols allButFirst + inject: (GtSearchReferencesFilter literal: aCollectionOfSymbols first) + into: [ :filter :symbol | filter | (GtSearchReferencesFilter literal: symbol) ]. + + self phlow spawnObject: aFilter. +] + +{ #category : #'private - insance creation' } +GtCoderMethodsGroupedListElement >> buildMethodItem [ + + | aLabel | + aLabel := self createBasicMethodItemElement. + aLabel addOnsCache: addOnsCache. + + aLabel + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude new + withGainFocusOnShow; + handleSubmenu: [ | someItems currentMethod | + currentMethod := aLabel userData at: #method. + someItems := self selectedItems. + (someItems size > 1 and: [ someItems identityIncludes: currentMethod ]) + ifTrue: [ self contextMenuForSeveralItems: someItems in: aLabel ] + ifFalse: [ self contextMenuForOneItem: currentMethod in: aLabel ] ]). + + aLabel addEventHandler: self createMethodDragHandler. + + ^ aLabel +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> contextMenuForOneItem: item in: anElement [ + ^ GtCoderMethodTarget menuItemsForObject: item hostElement: anElement +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> contextMenuForSeveralItems: someItems in: anElement [ + ^ GtCoderMethodsTarget + menuItemsForObject: (GtPharoCompiledMethodGroup withAll: someItems) + hostElement: anElement +] + +{ #category : #'private - insance creation' } +GtCoderMethodsGroupedListElement >> createBasicMethodItemElement [ + ^ methodItemElementStencil asElement +] + +{ #category : #'private - insance creation' } +GtCoderMethodsGroupedListElement >> createDraggedMethodItem: aDragItem [ + + ^ BrLabel new + aptitude: (BrGlamorousLabelAptitude new padding: (BlInsets all: 5)) + BrShadowAptitude; + beSmallSize; + opacity: 0.85; + margin: (BlInsets all: 5); + background: Color white; + beFocusable; + requestFocus; + geometry: (BlRoundedRectangleGeometry cornerRadius: 5); + fitContent; + text: aDragItem domainObject printString +] + +{ #category : #'private - insance creation' } +GtCoderMethodsGroupedListElement >> createMethodDragHandler [ + ^ BlDragHandler new + enableCopy; + liftItem: [ :aLabelElement | + | someItems currentMethod | + currentMethod := aLabelElement userData at: #method. + someItems := self selectedItems. + (someItems size > 1 and: [ someItems identityIncludes: currentMethod ]) + ifTrue: [ + someItems collect: [ :each | + BlDragItem new + sourceElement: aLabelElement; + domainObject: each; + stencil: [ :aDragItem | self createSeveralDraggedMethodsItem: someItems ] ] ] + ifFalse: [ + BlDragItem new + sourceElement: aLabelElement; + domainObject: (aLabelElement userData at: #method); + stencil: [ :aDragItem | self createDraggedMethodItem: aDragItem ] ] ] +] + +{ #category : #'private - insance creation' } +GtCoderMethodsGroupedListElement >> createMethodGroups [ + | classGroup instanceGroup | + instanceGroup := BrGroup new + domainObject: 'instance side'; + stream: #() asAsyncStream; + itemStencil: [ self buildMethodItem ]; + itemDataBinder: [ :element :eachCompiledMethod | + self bindCompiledMethod: eachCompiledMethod withElement: element ]; + shouldShowWithoutItems: false. + classGroup := instanceGroup copy domainObject: 'class side'. + ^ {instanceGroup. + classGroup} +] + +{ #category : #'private - insance creation' } +GtCoderMethodsGroupedListElement >> createSeveralDraggedMethodsItem: aCollection [ + ^ BrLabel new + aptitude: (BrGlamorousLabelAptitude new padding: (BlInsets all: 5)) + BrShadowAptitude; + beSmallSize; + opacity: 0.85; + margin: (BlInsets all: 5); + background: Color white; + beFocusable; + requestFocus; + geometry: (BlRoundedRectangleGeometry cornerRadius: 5); + fitContent; + text: ('{1} methods' format: {aCollection size}) +] + +{ #category : #'private - actions' } +GtCoderMethodsGroupedListElement >> hierarchyFilter [ + ^ (GtSearchInheritedMethodsFilter forClass: self selectedClass) withSuperclasses + withSubclasses +] + +{ #category : #initialization } +GtCoderMethodsGroupedListElement >> initialize [ + super initialize. + + addOnsCache := GtCoderNavigationCompiledMethodsAddOnsCache new subscribeToSystem. + methodItemElementStencil := GtCoderNavigationMethodItemElementStencil new. + + self + padding: (BlInsets left: 5 right: 10); + matchParent; + headerElementStencil: [ BrLabel new + beSmallSize; + aptitude: (BrGlamorousLabelAptitude new foreground: Color gray) ]; + headerDataBinder: [ :label :each | label text: each domainObject asRopedText ]. + + methodGroups := self createMethodGroups. + self groups: methodGroups. + + self + when: BrSelectionChanged + do: [ :anEvent | self onMethodsListSelectionChanged: anEvent ]. + self + when: BrWidgetPermanentlyRemovedEvent + do: [ :anEvent | self onBrWidgetPermanentlyRemovedEvent: anEvent ]. +] + +{ #category : #accessing } +GtCoderMethodsGroupedListElement >> methodItemElementStencil: aStencil [ + methodItemElementStencil := aStencil asStencil +] + +{ #category : #accessing } +GtCoderMethodsGroupedListElement >> methodList [ + ^ methodGroups flatCollect: [ :grp | grp itemsProvider currentItems ] +] + +{ #category : #accessing } +GtCoderMethodsGroupedListElement >> navigationModel [ + ^ navigationModel +] + +{ #category : #accessing } +GtCoderMethodsGroupedListElement >> navigationModel: anObject [ + navigationModel == anObject + ifTrue: [ ^ self ]. + + self unsubscribeFromNavigationModel. + navigationModel := anObject. + self subscribeToNavigationModel. + + self updateMethodList +] + +{ #category : #initialization } +GtCoderMethodsGroupedListElement >> onBrWidgetPermanentlyRemovedEvent: anEvent [ + addOnsCache ifNotNil: #unsubscribeFromSystem +] + +{ #category : #'event handling - selection' } +GtCoderMethodsGroupedListElement >> onMethodSelected: anAnnouncement [ + | aMethod aGroup | + anAnnouncement source = self ifTrue: [ ^ self ]. + + aMethod := anAnnouncement method. + (self selectedItems includes: aMethod) ifTrue: [ ^ self ]. + + aGroup := aMethod methodClass isInstanceSide + ifTrue: [ methodGroups first ] + ifFalse: [ methodGroups second ]. + + self + group: aGroup + itemSuchThat: [ :eachItem | eachItem = aMethod ] + ifFound: [ :anIndex :isSynchronous | + self selectOne: anIndex dueTo: (GtCoderMethodSelectedSourceEvent new + source: self; + originalSource: anAnnouncement source) ] + ifNone: [ ]. +] + +{ #category : #'event handling - selection' } +GtCoderMethodsGroupedListElement >> onMethodsListSelectionChanged: anEvent [ + | theIndices allSelectedItems aSource | + (anEvent isNotNil and: [ anEvent sourceEvent isKindOf: GtCoderMethodSelectedSourceEvent ]) + ifTrue: [ + "This element triggered the selection change, ignore the event." + anEvent sourceEvent source == self ifTrue: [ ^ self ]. + + aSource := anEvent sourceEvent originalSource ifNil: [ self ] ] + ifFalse: [ aSource := self ]. + + theIndices := self selectedIndices ifEmpty: [ ^ self ]. + (theIndices first between: 1 and: self viewModel entityCount) + ifFalse: [ ^ self ]. + (theIndices last between: 1 and: self viewModel entityCount) + ifFalse: [ ^ self ]. + + allSelectedItems := self selectedItems. + allSelectedItems ifEmpty: [ ^ self ]. + allSelectedItems size = 1 + ifTrue: [ self navigationModel selectMethod: allSelectedItems anyOne source: aSource ] + ifFalse: [ self navigationModel selectMethods: allSelectedItems source: aSource ] +] + +{ #category : #'event handling - selection' } +GtCoderMethodsGroupedListElement >> onMethodsSelected: anAnnouncement [ + +] + +{ #category : #'event handling' } +GtCoderMethodsGroupedListElement >> onMethodsToShowChanged: anAnnouncement [ + self updateMethodList +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> pushDownMethodSubmenuFor: aMethod [ + | submenu | + submenu := BrMenuExplicit new. + + submenu + stencil: [ :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Push down'; + targetName: ('{1}>>#{2}' + format: {aMethod methodClass. + aMethod selector}); + confirmationLabel: ('Push down {1}' format: {aMethod selector}); + refactoringWithConfirmation: [ GtRBPushDownMethodRefactoring + pushDown: {aMethod selector} + from: (self + forPharo12AndNewer: [ aMethod methodClass name ] + forPharo11: [ aMethod methodClass ]) ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: self action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: self. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ]. + + ^ submenu +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> pushDownSeveralMethodsSubmenuFor: aCollectionOfMethods [ + | submenu | + submenu := BrMenuExplicit new. + + submenu + stencil: [ :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Push down'; + targetName: ('{1} methods' format: {aCollectionOfMethods size}); + confirmationLabel: ('Push down {1} methods' format: {aCollectionOfMethods size}); + refactoringsWithConfirmation: [ | aGroupByClass aModel aCollectionOfRefactorings | + aGroupByClass := aCollectionOfMethods groupedBy: #methodClass. + aModel := Smalltalk createRbNamespace + onEnvironment: GtRBPushDownMethodRefactoring new defaultEnvironment. + aCollectionOfRefactorings := aGroupByClass + collect: [ :someMethods | + GtRBPushDownMethodRefactoring new + model: aModel; + pushDown: (someMethods collect: #selector) + from: (self + forPharo12AndNewer: [ someMethods anyOne methodClass name ] + forPharo11: [ someMethods anyOne methodClass ]) ] + as: Array. + aModel name: ('Push down {1} methods' format: {aCollectionOfMethods size}). + aCollectionOfRefactorings ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: self action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: self. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ]. + + ^ submenu +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> pushUpMethodSubmenuFor: aMethod [ + | submenu | + submenu := BrMenuExplicit new. + + submenu + stencil: [ :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Push up'; + targetName: ('{1}>>#{2}' + format: {aMethod methodClass. + aMethod selector}); + confirmationLabel: ('Push up {1}' format: {aMethod selector}); + refactoringWithConfirmation: [ GtPushUpMethodRefactoring pullUp: {aMethod selector} from: aMethod methodClass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: self action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: self. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ]. + + ^ submenu +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> pushUpSeveralMethodsSubmenuFor: aCollectionOfMethods [ + | submenu | + submenu := BrMenuExplicit new. + + submenu + stencil: [ :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Push up'; + targetName: ('{1} methods' format: {aCollectionOfMethods size}); + confirmationLabel: ('Push up {1} methods' format: {aCollectionOfMethods size}); + refactoringsWithConfirmation: [ | aGroupByClass aModel aCollectionOfRefactorings | + aGroupByClass := aCollectionOfMethods groupedBy: #methodClass. + aModel := Smalltalk createRbNamespace + onEnvironment: GtPushUpMethodRefactoring new defaultEnvironment. + aCollectionOfRefactorings := aGroupByClass + collect: [ :someMethods | + GtPushUpMethodRefactoring new + model: aModel; + pullUp: (someMethods collect: #selector) + from: someMethods anyOne methodClass ] + as: Array. + aModel name: ('Push up {1} methods' format: {aCollectionOfMethods size}). + aCollectionOfRefactorings ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: self action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: self. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ]. + + ^ submenu +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> removeMethodSubmenuFor: aMethod [ + | submenu | + submenu := BrMenuExplicit new. + ^ submenu + stencil: [ | element change button | + element := BrVerticalPane new fitContent. + element + addChild: (BrLabel new + margin: (BlInsets + top: 10 + bottom: 0 + left: 10 + right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: ('Remove ' , aMethod selector) asRopedText). + element + addChild: (BrAsyncWidget new + fitContent; + stencil: [ | pane references | + pane := BrVerticalPane new. + pane fitContent. + references := (GtPharoIndex current sendersOf: aMethod selector) size. + references > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (references printString , ' reference' + , (references > 1 ifTrue: [ 's' ] ifFalse: [ '' ])) asRopedText) ]. + pane ]). + change := RBRemoveMethodChange + remove: aMethod selector + from: aMethod methodClass. + button := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + beSmallSize; + margin: (BlInsets + top: 10 + bottom: 10 + left: 10 + right: 10); + icon: BrGlamorousVectorIcons remove; + label: 'Remove'; + action: [ submenu hideAll. + change execute ]. + element addChild: button as: #removeButton. + element ] +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> removeSeveralMethodsSubmenuFor: aCollectionOfMethods [ + | submenu | + submenu := BrMenuExplicit new. + ^ submenu + stencil: [ | element changes change button | + element := BrVerticalPane new fitContent. + element + addChild: (BrLabel new + margin: (BlInsets + top: 10 + bottom: 0 + left: 10 + right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: ('Remove {1} methods' format: {aCollectionOfMethods size}) asRopedText). + element + addChild: (BrAsyncWidget new + fitContent; + stencil: [ | pane references | + pane := BrVerticalPane new. + pane fitContent. + references := aCollectionOfMethods + inject: 0 + into: [ :sum :method | sum + (GtPharoIndex current sendersOf: method selector) size ]. + references > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (references printString , ' reference' + , (references > 1 ifTrue: [ 's' ] ifFalse: [ '' ])) asRopedText) ]. + pane ]). + + + changes := aCollectionOfMethods collect: [ :eachMethod | + RBRemoveMethodChange + remove: eachMethod selector + from: eachMethod methodClass ]. + + change := RBCompositeRefactoryChange new + name: ('Remove {1} methods' format: { aCollectionOfMethods size }); + changes: changes; + yourself. + + button := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + beSmallSize; + margin: (BlInsets + top: 10 + bottom: 10 + left: 10 + right: 10); + icon: BrGlamorousVectorIcons remove; + label: 'Remove'; + action: [ submenu hideAll. + change execute ]. + element addChild: button as: #removeButton. + element ] +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> renameMethodSubmenuFor: aMethod [ + | submenu | + submenu := BrMenuExplicit new. + + submenu + stencil: [ :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithInputViewModel new + refactoringTitle: 'Rename method'; + targetName: ('{1}>>#{2}' + format: {aMethod methodClass. + aMethod selector}); + inputLabel: 'New method name:'; + initialText: aMethod selector; + refactoringWithInput: [ :anInput | + GtRBRenameMethodRefactoring + renameMethod: aMethod selector + in: aMethod methodClass + to: anInput + permutation: (1 to: aMethod selector numArgs) ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: self action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: self. + GtRefactoringsPreviewWithInputElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ]. + + ^ submenu +] + +{ #category : #accessing } +GtCoderMethodsGroupedListElement >> selectedClass [ + ^ navigationModel selectedClass +] + +{ #category : #'private - context menu' } +GtCoderMethodsGroupedListElement >> selectedMethod [ + ^ navigationModel selectedMethod +] + +{ #category : #'private - subscriptions' } +GtCoderMethodsGroupedListElement >> subscribeToNavigationModel [ + | subscriptions | + + subscriptions := { + GtCoderNavigationMethodsToShowChanged -> #onMethodsToShowChanged:. + GtCoderNavigationMethodSelected -> #onMethodSelected:. + GtCoderNavigationMethodsSelected -> #onMethodsSelected:. + }. + + subscriptions + do: [ :sub | + navigationModel weak + when: sub key + send: sub value + to: self ] +] + +{ #category : #'private - subscriptions' } +GtCoderMethodsGroupedListElement >> unsubscribeFromNavigationModel [ + navigationModel ifNotNil: [ :aModel | aModel unsubscribe: self ] +] + +{ #category : #'updating lists' } +GtCoderMethodsGroupedListElement >> updateMethodList [ + self + inUIProcessDo: [ self navigationModel + ifNotNil: [ self updateMethodListWith: self navigationModel methodsToShow ] ] +] + +{ #category : #'updating lists' } +GtCoderMethodsGroupedListElement >> updateMethodListWith: aCollectionOfCompiledMethods [ + | instMethStream classMethStream instanceMethods classMethods | + self deselectAll. + + instanceMethods := aCollectionOfCompiledMethods + select: [ :each | each methodClass isInstanceSide ]. + + classMethods := aCollectionOfCompiledMethods + select: [ :each | each methodClass isClassSide ]. + + instMethStream := instanceMethods asSortedCollection: GtMethodsSortFunction new. + + classMethStream := classMethods asSortedCollection: GtMethodsSortFunction new. + + methodGroups + with: {instMethStream. + classMethStream} + do: [ :grp :str | grp items: str ]. + + self groups: methodGroups +] diff --git a/src/GToolkit-Coder-UI/GtCoderMethodsTarget.class.st b/src/GToolkit-Coder-UI/GtCoderMethodsTarget.class.st new file mode 100644 index 000000000..ab601b4b7 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderMethodsTarget.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderMethodsTarget, + #superclass : #GtPhlowActionUniqueTarget, + #category : #'GToolkit-Coder-UI-Phlow Targets' +} diff --git a/src/GToolkit-Coder-UI/GtCoderModel.extension.st b/src/GToolkit-Coder-UI/GtCoderModel.extension.st index e11830871..793cdf6b7 100644 --- a/src/GToolkit-Coder-UI/GtCoderModel.extension.st +++ b/src/GToolkit-Coder-UI/GtCoderModel.extension.st @@ -1,29 +1,29 @@ Extension { #name : #GtCoderModel } -{ #category : #'*GToolkit-Coder-UI' } -GtCoderModel >> asCoderUIModel [ - ^ self subclassResponsibility -] - { #category : #'*GToolkit-Coder-UI' } GtCoderModel >> asCoderViewModel [ - ^ self asCoderUIModel + ^ self subclassResponsibility ] { #category : #'*GToolkit-Coder-UI' } GtCoderModel >> asElement [ - ^ self asCoderUIModel asElement + ^ self asCoderViewModel asElement ] { #category : #'*GToolkit-Coder-UI' } GtCoderModel >> asExpandedOnlyElement [ "Create an element for just the expanded coder without expander" - ^ GtExpandedOnlyCoderElement new - coderUIModel: self asCoderUIModel; + ^ GtSourceCoderExpandedOnlyElement new + textualCoderViewModel: self asCoderViewModel; yourself ] +{ #category : #'*GToolkit-Coder-UI' } +GtCoderModel >> asStencil [ + ^ self +] + { #category : #'*GToolkit-Coder-UI' } GtCoderModel >> coderLook [ @@ -37,11 +37,53 @@ GtCoderModel >> coderLook: aLookOrBlock [ ] { #category : #'*GToolkit-Coder-UI' } -GtCoderModel >> defaultCoderLook [ - ^ nil +GtCoderModel >> create [ + ^ self asElement ] { #category : #'*GToolkit-Coder-UI' } GtCoderModel >> elementClass [ ^ GtExpandableSourceCoderElement ] + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderModel class >> gtAstCoderAddOnsFor: aView context: aPhlowContext [ + + ^ aView forward + title: 'Coder Addons'; + tooltip: 'The methods that extend the Coder with AddOns'; + priority: 45; + object: [ (GtSearchMethodsInClassFilter new + forClass: self; + includeSuperclass: true) + & (self astExtensionsPragma gtPragmas | #initializeAddOns: gtImplementors + | #initializeAddOns:viewModel: gtImplementors) ]; + view: #gtItemsFor:; + actionButtonIcon: BrGlamorousVectorIcons inspect + tooltip: 'Inspect Addons' + action: [ :aButton | + aButton phlow + spawnObject: (GtSearchMethodsInClassFilter new + forClass: self; + includeSuperclass: true) & self astExtensionsPragma gtPragmas ] +] + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderModel class >> gtContextMenuAddOnsFor: aView [ + + + ^ aView forward + title: 'Menu addons'; + tooltip: 'The methods that extend the Coder with context menu items'; + priority: 45.1; + object: [ (GtSearchMethodsInClassFilter new + forClass: self; + includeSuperclass: true) & self contextMenuAddOnsPragma gtPragmas ]; + view: #gtItemsFor:; + actionButtonIcon: BrGlamorousVectorIcons inspect + tooltip: 'Inspect Addons' + action: [ :aButton | + aButton phlow spawnObject: (GtSearchMethodsInClassFilter new + forClass: self; + includeSuperclass: true) & self contextMenuAddOnsPragma gtPragmas ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationBasicClassHierarchyElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationBasicClassHierarchyElement.class.st new file mode 100644 index 000000000..ae4a0dfc0 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationBasicClassHierarchyElement.class.st @@ -0,0 +1,120 @@ +Class { + #name : #GtCoderNavigationBasicClassHierarchyElement, + #superclass : #GtCoderNavigationElement, + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #initialization } +GtCoderNavigationBasicClassHierarchyElement >> headerLabel [ + | label | + label := BrLabel new. + label + aptitude: (BrGlamorousLabelAptitude new + fontSize: 12; + foreground: Color gray). + ^ label +] + +{ #category : #initialization } +GtCoderNavigationBasicClassHierarchyElement >> initializeElement [ + | pane1 | + pane1 := BrVerticalPane new + addAptitude: BrGlamorousWithHorizontalResizerAptitude; + matchParent; + addChildren: {self headerLabel text: 'Classes'. + classesList}. + + self addChild: pane1 +] + +{ #category : #initialization } +GtCoderNavigationBasicClassHierarchyElement >> initializeLayout [ + self layout: BlLinearLayout horizontal. + self + constraintsDo: [ :c | + c horizontal matchParent. + c vertical matchParent ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicClassHierarchyElement >> onClassDeselected: aGtCoderNavigationClassDeselected [ + self deselectClass: aGtCoderNavigationClassDeselected theClass +] + +{ #category : #'event handling' } +GtCoderNavigationBasicClassHierarchyElement >> onClassListSelectionChanged [ + | anIndex aSelectedItem theIndices | + supressListChanges ifTrue: [ ^ self ]. + theIndices := classesList selectedIndices. + theIndices ifEmpty: [ ^ self ]. + anIndex := theIndices first. + (anIndex between: 1 and: classesList viewModel itemCount) ifFalse: [ ^ self ]. + aSelectedItem := (classesList viewModel itemAt: anIndex) value rootClass. + self + suppressListChangeEventsDuring: [ self navigationModel selectClass: aSelectedItem ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicClassHierarchyElement >> onClassSelected: anAnnouncement [ + | aSelectedClass | + + aSelectedClass := anAnnouncement theClass. + (classesList containsClass: aSelectedClass) + ifFalse: [ self updateClassList ]. + self selectClass: aSelectedClass +] + +{ #category : #'event handling' } +GtCoderNavigationBasicClassHierarchyElement >> onClassesToShowChanged: anAnnouncement [ + supressListChanges ifTrue: [ ^ self ]. + self inUIProcessDo: [ self updateClassList ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicClassHierarchyElement >> onNavigationModelChanged [ + super onNavigationModelChanged. + self subscribeToClassList. +] + +{ #category : #'updating lists' } +GtCoderNavigationBasicClassHierarchyElement >> updateClassList [ + self hasNavigationModel ifFalse: [ ^ self ]. + navigationModel hasSelectedClass + ifTrue: [ self updateClassListsWith: navigationModel selectedClass ] + ifFalse: [ self emptyClassList ] +] + +{ #category : #'updating lists' } +GtCoderNavigationBasicClassHierarchyElement >> updateClassListsWith: aClass [ + | withRootClasses lastIndex isWithAllSuperclasses | + "Check that all its root classes are in the classesList (and therefore visible)." + lastIndex := nil. + withRootClasses := aClass withAllSuperclasses. + isWithAllSuperclasses := withRootClasses + allSatisfy: [ :eachClass | + | currentIndex newIndex | + currentIndex := lastIndex. + newIndex := nil. + classesList viewModel + indexOfSuchThat: [ :eachTree | eachTree rootClass = eachClass ] + do: [ :eachIndex | newIndex := eachIndex ]. + lastIndex := newIndex. + currentIndex + ifNil: [ newIndex isNotNil ] + ifNotNil: [ newIndex isNotNil and: [ currentIndex > newIndex ] ] ]. + + "If a selected item is the same as the requested class, we keep the existing hierarchy items. + Like this, for example, we do not lose siblings when we drag-and-drop methods arround." + (isWithAllSuperclasses + and: [ classesList selectedItems + anySatisfy: [ :eachClassTree | eachClassTree rootClass = aClass ] ]) + ifTrue: [ ^ self ]. + + classesList initializeWithHierachyForClass: aClass +] + +{ #category : #'updating lists' } +GtCoderNavigationBasicClassHierarchyElement >> updateContent [ + self updateClassList. + self updateSelectedClass +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationBasicPackagesTagsClassesElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationBasicPackagesTagsClassesElement.class.st new file mode 100644 index 000000000..e741b10bc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationBasicPackagesTagsClassesElement.class.st @@ -0,0 +1,450 @@ +Class { + #name : #GtCoderNavigationBasicPackagesTagsClassesElement, + #superclass : #GtCoderNavigationElement, + #instVars : [ + 'packagesList', + 'classesLabel' + ], + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #'api - package updates' } +GtCoderNavigationBasicPackagesTagsClassesElement >> addPackage: aPackage [ + | aSelectedPackageOrTag | + + packagesList selectedNodeDo: [ :aNode | + aSelectedPackageOrTag := aNode value ]. + + self updatePackageLists. + aSelectedPackageOrTag ifNotNil: [ + self selectPackageOrTag: aSelectedPackageOrTag ]. +] + +{ #category : #'api - package updates' } +GtCoderNavigationBasicPackagesTagsClassesElement >> addPackage: aPackage tag: aPackageTag [ + | aSelectedPackageOrTag | + + packagesList selectedNodeDo: [ :aNode | + aSelectedPackageOrTag := aNode value ]. + + self updatePackageLists. + aSelectedPackageOrTag ifNotNil: [ + self selectPackageOrTag: aSelectedPackageOrTag ]. +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> deselectPackages [ + packagesList deselectAll +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> expandPackage: aRPackage [ + packagesList viewModel root + allChildrenNodesBreadthFirstDo: [ :eachTreeNode | + eachTreeNode value = aRPackage + ifTrue: [ eachTreeNode expand ] ] +] + +{ #category : #'private - testing' } +GtCoderNavigationBasicPackagesTagsClassesElement >> hasPackageTagsIn: aRPackage [ + + | noTags noExtensions | + noTags := (aRPackage tags size = 1 and: [ aRPackage tags anyOne name = aRPackage name ]). + noExtensions := aRPackage extendedClasses isEmpty. + ^ noTags not or: [ noExtensions not ] +] + +{ #category : #initialization } +GtCoderNavigationBasicPackagesTagsClassesElement >> headerLabel [ + | label | + label := BrLabel new. + label + aptitude: (BrGlamorousLabelAptitude new + fontSize: 12; + foreground: Color gray). + ^ label +] + +{ #category : #'showing / hiding' } +GtCoderNavigationBasicPackagesTagsClassesElement >> hideClassList [ + classesLabel visibility: BlVisibility hidden. +] + +{ #category : #'showing / hiding' } +GtCoderNavigationBasicPackagesTagsClassesElement >> hideOrShowClassList [ + classesList maxSelectionIndex isZero + ifTrue: [ self hideClassList ] + ifFalse: [ self showClassList ] +] + +{ #category : #initialization } +GtCoderNavigationBasicPackagesTagsClassesElement >> initializeContent [ + super initializeContent. + packagesList := GtCoderPackagesTreeElement new + padding: (BlInsets right: 10). +] + +{ #category : #initialization } +GtCoderNavigationBasicPackagesTagsClassesElement >> initializeElement [ + | pane1 pane2 | + super initializeElement. + pane1 := BrVerticalPane new + addAptitude: BrGlamorousWithHorizontalResizerAptitude; + matchParent; + addChildren: {self headerLabel text: 'Packages'. + packagesList}. + pane2 := BrVerticalPane new + addAptitude: BrGlamorousWithHorizontalResizerAptitude; + matchParent; + addChildren: {classesLabel := self headerLabel text: 'Classes'. + classesList}. + self addChildren: { pane1. pane2 } +] + +{ #category : #initialization } +GtCoderNavigationBasicPackagesTagsClassesElement >> initializeLayout [ + super initializeLayout. + self layout: BlLinearLayout new beHorizontal. + self + constraintsDo: [ :c | + c horizontal matchParent. + c vertical matchParent ] +] + +{ #category : #'event handling - selection' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onClassDeselected: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self deselectClass: anAnnouncement theClass ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onClassRemoved: anAnnouncement [ + | aPackageOrTag | + self + inUIProcessDo: [ aPackageOrTag := self selectedPackageOrTag. + packagesList deselectAll. + self selectPackage: aPackageOrTag ] +] + +{ #category : #'event handling - selection' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onClassSelected: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self selectClasses: anAnnouncement selectedClasses ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageListSelectionChanged [ + | anIndex aSelectedItem theIndices | + supressListChanges ifTrue: [ ^ self ]. + theIndices := packagesList selectedIndices. + theIndices ifEmpty: [ ^ self ]. + anIndex := theIndices first. + (anIndex between: 1 and: packagesList viewModel itemCount) ifFalse: [ ^ self ]. + aSelectedItem := (packagesList viewModel itemAt: anIndex) value. + (aSelectedItem isPharoPackageModel) + ifTrue: [ self navigationModel selectPackage: aSelectedItem ] + ifFalse: [ self navigationModel selectPackageTag: aSelectedItem ]. + self showClassList. + self deselectClasses +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageRegistered: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self addPackage: anAnnouncement package ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageRenamed: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self + renamePackage: anAnnouncement package + oldName: anAnnouncement oldName + newName: anAnnouncement newName ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageSelected: anAnnouncer [ + self inUIProcessDo: [ self suppressListChangeEventsDuring: [ self selectPackage: anAnnouncer package ] ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageTagAdded: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self addPackage: anAnnouncement package tag: anAnnouncement tag ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageTagRemoved: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self removePackage: anAnnouncement package tag: anAnnouncement tag ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageTagRenamed: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self + renamePackageTag: anAnnouncement package + oldName: anAnnouncement oldName + newName: anAnnouncement newName ] +] + +{ #category : #'event handling - selection' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageTagSelected: anAnnouncer [ + self + inUIProcessDo: [ self + suppressListChangeEventsDuring: [ self selectPackage: anAnnouncer package tag: anAnnouncer tag. + "self deselectClasses" ] ] +] + +{ #category : #'event handling' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackageUnregistered: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self removePackage: anAnnouncement package ] +] + +{ #category : #'event handling - selection' } +GtCoderNavigationBasicPackagesTagsClassesElement >> onPackagesSelected: anAnnouncer [ + self + inUIProcessDo: [ self + suppressListChangeEventsDuring: [ self deselectPackages. + self deselectClasses ] ] +] + +{ #category : #'api - package updates' } +GtCoderNavigationBasicPackagesTagsClassesElement >> removePackage: aPackage [ + | aSelectedPackageOrTag anIndex | + anIndex := packagesList viewModel indexOf: aPackage. + anIndex > 0 ifFalse: [ ^ self ]. + aSelectedPackageOrTag := self selectedPackageOrTag. + + self updatePackageLists. + self + reselectPackageOrTag: aSelectedPackageOrTag + afterRemovalOfPackageNamed: aPackage name +] + +{ #category : #'api - package updates' } +GtCoderNavigationBasicPackagesTagsClassesElement >> removePackage: aPackage tag: aPackageTagName [ + | aSelectedPackageOrTag anIndex | + anIndex := packagesList viewModel indexOf: aPackage. + anIndex > 0 ifFalse: [ ^ self ]. + packagesList selectedNodeDo: [ :aNode | + aSelectedPackageOrTag := aNode value ]. + self updatePackageLists. + self reselectPackageOrTag: aSelectedPackageOrTag afterRemovalOfTagNamed: aPackageTagName. +] + +{ #category : #'api - package updates' } +GtCoderNavigationBasicPackagesTagsClassesElement >> renamePackage: aPackage oldName: anOldName newName: aNewName [ + | aSelectedPackageOrTag | + + packagesList selectedNodeDo: [ :aNode | + aSelectedPackageOrTag := aNode value ]. + + self updatePackageLists. + aSelectedPackageOrTag ifNotNil: [ + self selectPackageOrTag: aSelectedPackageOrTag ]. +] + +{ #category : #'api - package updates' } +GtCoderNavigationBasicPackagesTagsClassesElement >> renamePackageTag: aPackageTag oldName: anOldName newName: aNewName [ + | aSelectedPackageOrTag | + + packagesList selectedNodeDo: [ :aNode | + aSelectedPackageOrTag := aNode value ]. + + self updatePackageLists. + aSelectedPackageOrTag ifNotNil: [ + self selectPackageOrTag: aSelectedPackageOrTag ]. +] + +{ #category : #'api - package reselections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> reselectPackageOrTag: aSelectedPackageOrTag afterRemovalOfPackageNamed: aRemovedPackageOrTagName [ + aSelectedPackageOrTag ifNotNil: [ + self deselectPackages. + aSelectedPackageOrTag name = aRemovedPackageOrTagName + ifTrue: [ + self hideClassList ] + ifFalse: [ + self selectPackageOrTag: aSelectedPackageOrTag ] ] +] + +{ #category : #'api - package reselections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> reselectPackageOrTag: aSelectedPackageOrTag afterRemovalOfTagNamed: aRemovedTagName [ + aSelectedPackageOrTag + ifNotNil: [ self deselectPackages. + (aSelectedPackageOrTag name sameContentAs: aRemovedTagName) + ifTrue: [ self expandPackage: aSelectedPackageOrTag package. + self hideClassList ] + ifFalse: [ self selectPackageOrTag: aSelectedPackageOrTag ] ] +] + +{ #category : #'api - class selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectClass: aClass inPackage: aPackage tag: aPackageTag [ + self selectedPackage ~= aPackage + ifTrue: [ self selectPackage: aPackage tag: aPackageTag ]. + + self selectClass: aClass. + self updateProtocolList. + self updateSlotList +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectPackage: aPackage [ + | aPreviousIndex | + + self expandPackage: aPackage. + aPreviousIndex := packagesList selectedIndice. + + packagesList deselectAll. + packagesList viewModel + indexOf: aPackage + do: [ :aNewIndex | + packagesList + selectOne: aNewIndex; + scrollToIndex: + (self + scrollIndexFromPrevious: aPreviousIndex + current: aNewIndex + max: packagesList viewModel itemCount) ]. +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectPackage: aPackage tag: aPackageTag [ + | aPreviousIndex | + (self hasPackageTagsIn: aPackage) ifFalse: [ ^ self selectPackage: aPackage ]. + aPackageTag isNil ifTrue: [ ^ self selectPackage: aPackage ]. + + packagesList deselectAll. + self expandPackage: aPackage. + + aPreviousIndex := packagesList selectedIndice. + packagesList viewModel + indexOf: aPackageTag + do: [ :aNewIndex | + packagesList + selectOne: aNewIndex; + scrollToIndex: (self + scrollIndexFromPrevious: aPreviousIndex + current: aNewIndex + max: packagesList viewModel itemCount) ] +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectPackageOrTag: aPackageOrTag [ + (aPackageOrTag isPharoPackageModel) + ifTrue: [ self selectPackage: aPackageOrTag ] + ifFalse: [ self selectPackage: aPackageOrTag package tag: aPackageOrTag ] +] + +{ #category : #'api - class selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectedClass [ + + ^ classesList selectedClass +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectedPackage [ + + ^ packagesList selectedPackage +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectedPackageNodeDo: aBlock [ + ^ packagesList selectedPackageNodeDo: aBlock +] + +{ #category : #'api - package updates' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectedPackageOrTag [ + | aSelectedPackageOrTag | + packagesList selectedNodeDo: [ :aNode | aSelectedPackageOrTag := aNode value ]. + ^ aSelectedPackageOrTag +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectedPackageTag [ + + ^ packagesList selectedPackageTag +] + +{ #category : #'api - package selections' } +GtCoderNavigationBasicPackagesTagsClassesElement >> selectedPackageTagNodeDo: aBlock [ + ^ packagesList selectedPackageTagNodeDo: aBlock +] + +{ #category : #'showing / hiding' } +GtCoderNavigationBasicPackagesTagsClassesElement >> showClassList [ + classesList visibility: BlVisibility visible. + classesLabel visibility: BlVisibility visible +] + +{ #category : #subscriptions } +GtCoderNavigationBasicPackagesTagsClassesElement >> subscribeToContent [ + super subscribeToContent. + self subscribeToPackageList. + self subscribeToClassList. +] + +{ #category : #subscriptions } +GtCoderNavigationBasicPackagesTagsClassesElement >> subscribeToNavigationModel [ + | subscriptions | + self hasNavigationModel ifFalse: [ ^ self ]. + super subscribeToNavigationModel. + subscriptions := { + GtCoderNavigationPackagesSelected -> #onPackagesSelected:. + GtCoderNavigationPackageSelected -> #onPackageSelected:. + GtCoderNavigationPackageTagSelected -> #onPackageTagSelected:. + GtCoderNavigationPackageRegistered -> #onPackageRegistered:. + GtCoderNavigationPackageUnregistered -> #onPackageUnregistered:. + GtCoderNavigationPackageRenamed -> #onPackageRenamed:. + GtCoderNavigationPackageTagAdded -> #onPackageTagAdded:. + GtCoderNavigationPackageTagRemoved -> #onPackageTagRemoved:. + GtCoderNavigationPackageTagRenamed -> #onPackageTagRenamed:. + "Announcement when items selected" + "GtCoderNavigationClassSelected -> #onClassSelected:. <--- defined in the superclass" + "Announcements when we need to update items in the lists" + GtCodersFiltersChanged -> #onMethodsCoderFiltersChanged:. + }. + subscriptions + do: [ :sub | + navigationModel weak + when: sub key + send: sub value + to: self ] +] + +{ #category : #subscriptions } +GtCoderNavigationBasicPackagesTagsClassesElement >> subscribeToPackageList [ + packagesList + when: BrSelectionChanged + do: [ :anEvent | self onPackageListSelectionChanged ] +] + +{ #category : #'updating lists' } +GtCoderNavigationBasicPackagesTagsClassesElement >> updateClassList [ + self hasNavigationModel + ifFalse: [ ^ self ]. + + classesList initializeWithClasses: navigationModel classesToShow. + + navigationModel selectedClasses + ifNotNil: [ :aCollectionOfClasses | self selectClasses: aCollectionOfClasses ] +] + +{ #category : #'updating lists' } +GtCoderNavigationBasicPackagesTagsClassesElement >> updateContent [ + self updatePackageLists. + self updateClassList. +] + +{ #category : #'updating lists' } +GtCoderNavigationBasicPackagesTagsClassesElement >> updatePackageLists [ + self hasNavigationModel + ifFalse: [ ^ self ]. + + packagesList initializeWithPackages: navigationModel packagesToShow. + + navigationModel selectedPackage + ifNotNil: [ :aPackage | self selectPackage: aPackage tag: navigationModel selectedTag ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationClassHierachyElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationClassHierachyElement.class.st deleted file mode 100644 index f1760be15..000000000 --- a/src/GToolkit-Coder-UI/GtCoderNavigationClassHierachyElement.class.st +++ /dev/null @@ -1,108 +0,0 @@ -Class { - #name : #GtCoderNavigationClassHierachyElement, - #superclass : #GtCoderNavigationElement, - #category : #'GToolkit-Coder-UI-Navigation' -} - -{ #category : #initialization } -GtCoderNavigationClassHierachyElement >> initializeElement [ - super initializeElement. - self addChild: classesList -] - -{ #category : #initialization } -GtCoderNavigationClassHierachyElement >> initializeLayout [ - super initializeLayout. - self layout: BlLinearLayout vertical. - self constraintsDo: [ :c | - c horizontal matchParent. - c vertical matchParent ]. -] - -{ #category : #subscriptions } -GtCoderNavigationClassHierachyElement >> onClassModified: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self - updateClass: anAnnouncement theClass - inPackage: anAnnouncement package - tag: anAnnouncement tag ] -] - -{ #category : #subscriptions } -GtCoderNavigationClassHierachyElement >> onClassRenamed: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self - renameClass: anAnnouncement theClass - oldName: anAnnouncement oldName - newName: anAnnouncement newName - inPackage: anAnnouncement package - tag: anAnnouncement tag ] -] - -{ #category : #'event handling' } -GtCoderNavigationClassHierachyElement >> onClassSelected: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self updateClassListsWith: anAnnouncement theClass. - self selectClass: anAnnouncement theClass ]. -] - -{ #category : #'event handling' } -GtCoderNavigationClassHierachyElement >> onPackageSelected: anAnnouncer [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self emptyClassList. - self deselectClasses. ] -] - -{ #category : #'event handling' } -GtCoderNavigationClassHierachyElement >> onPackageTagSelected: anAnnouncer [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self emptyClassList. - self deselectClasses. ]. -] - -{ #category : #'event handling' } -GtCoderNavigationClassHierachyElement >> onPackagesSelected: anAnnouncer [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self emptyClassList. - self deselectClasses. ] -] - -{ #category : #subscriptions } -GtCoderNavigationClassHierachyElement >> subscribeToContent [ - super subscribeToContent. - self subscribeToClassList. -] - -{ #category : #subscriptions } -GtCoderNavigationClassHierachyElement >> subscribeToNavigationModel [ - self hasNavigationModel ifFalse: [ ^ self ]. - super subscribeToNavigationModel. - navigationModel when: GtCoderNavigationPackagesSelected send: #onPackagesSelected: to: self. - navigationModel when: GtCoderNavigationPackageSelected send: #onPackageSelected: to: self. - navigationModel when: GtCoderNavigationPackageTagSelected send: #onPackageTagSelected: to: self. -] - -{ #category : #'updating lists' } -GtCoderNavigationClassHierachyElement >> updateClassLists [ - self hasNavigationModel ifFalse: [ ^ self ]. - navigationModel hasSelectedClass - ifTrue: [ self updateClassListsWith: navigationModel selectedClass ] - ifFalse: [ self emptyClassList ]. -] - -{ #category : #'updating lists' } -GtCoderNavigationClassHierachyElement >> updateClassListsWith: aClass [ - classesList initializeWithHierachyForClass: aClass. -] - -{ #category : #'updating lists' } -GtCoderNavigationClassHierachyElement >> updateContent [ - self updateClassLists. - self updateSelectedClass -] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationClassHierarchyElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationClassHierarchyElement.class.st new file mode 100644 index 000000000..24a60ac2c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationClassHierarchyElement.class.st @@ -0,0 +1,70 @@ +" +I show a list of classes grouping them based on the inheritance hierarchy and sorting alphabetically. + + +" +Class { + #name : #GtCoderNavigationClassHierarchyElement, + #superclass : #GtCoderNavigationBasicClassHierarchyElement, + #instVars : [ + 'methodsList', + 'protocolsList', + 'slotsList' + ], + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #initialization } +GtCoderNavigationClassHierarchyElement >> initialize [ + | protocolsSlotsTabGroup pane2 pane3 | + super initialize. + + protocolsList := GtCoderProtocolsGroupedListElement new. + slotsList := GtCoderSlotsGroupedListElement new. + methodsList := GtCoderMethodsGroupedListElement multiSelection. + + protocolsSlotsTabGroup := BrTabGroup new + aptitude: BrGlamorousTabGroupProportionalAptitude - BrTabGroupSplitterAptitude; + addTab: (BrTab new + hMatchParent; + aptitude: GtProtocolSlotTabAptitude new; + label: 'Categories' asRopedText glamorousCodeSmallSize; + stencil: [ protocolsList ]); + addTab: (BrTab new + hMatchParent; + aptitude: GtProtocolSlotTabAptitude new; + label: 'Slots' asRopedText glamorousCodeSmallSize; + stencil: [ slotsList ]). + + pane2 := BrVerticalPane new + addAptitude: BrGlamorousWithHorizontalResizerAptitude; + matchParent; + addChildren: { + protocolsSlotsTabGroup }. + + pane3 := BrVerticalPane new + matchParent; + addChildren: { + self headerLabel text: 'Methods'. + methodsList}. + + self + addChildren: { + pane2. + pane3} + +] + +{ #category : #'event handling' } +GtCoderNavigationClassHierarchyElement >> onNavigationModelChanged [ + "subclasses may perform actions on navigation model changes" + super onNavigationModelChanged. + + protocolsList navigationModel: self navigationModel. + protocolsList updateProtocolListWith: self navigationModel protocolsToShow. + self navigationModel selectedProtocol + ifNotNil: [ :aProtocol | protocolsList selectProtocol: aProtocol ]. + + slotsList navigationModel: self navigationModel. + methodsList navigationModel: self navigationModel +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationClassesHierarchyTreeElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationClassesHierarchyTreeElement.class.st deleted file mode 100644 index 8cf7b93b2..000000000 --- a/src/GToolkit-Coder-UI/GtCoderNavigationClassesHierarchyTreeElement.class.st +++ /dev/null @@ -1,90 +0,0 @@ -" -I show a list of classes grouping them based on the inheritance hierarchy and sorting alphabetically - - -" -Class { - #name : #GtCoderNavigationClassesHierarchyTreeElement, - #superclass : #BrSimpleTree, - #instVars : [ - 'classesTree' - ], - #category : #'GToolkit-Coder-UI-Navigation' -} - -{ #category : #'instance creation' } -GtCoderNavigationClassesHierarchyTreeElement class >> fromClasses: aCollectionOfClasses [ - ^ self new initializeWithClasses: aCollectionOfClasses -] - -{ #category : #'private - ui' } -GtCoderNavigationClassesHierarchyTreeElement >> expandClass: aClass [ - | aClassDepth | - aClassDepth := 0. - aClass - allSuperclassesDo: [ :eachClass | aClassDepth := aClassDepth + 1 ]. - self expandUpTo: aClassDepth + 1 -] - -{ #category : #initialization } -GtCoderNavigationClassesHierarchyTreeElement >> initialize [ - super initialize. - - self rowStencil: BrGlamorousSimpleTreeSelectableRowElementStencilBuilder new. - - self - nodeStencil: [ BrLabel new - beSmallSize; - aptitude: BrGlamorousLabelAptitude ]; - nodeDataBinder: [ :aClassElement :aClassHierarchyTree | - aClassElement - text: - (aClassHierarchyTree - ifNil: [ '' asRopedText ] - ifNotNil: [ | aClass aText | - aClass := aClassHierarchyTree rootClass. - aText := aClass gtDisplayText asRopedText. - (aClass isAbstract or: [ aClass hasAbstractMethods ]) - ifTrue: [ aText italic ]. - aClass isDeprecated - ifTrue: [ aText append: ' (deprecated)' asRopedText italic ]. - aText ]) ] -] - -{ #category : #'api - initialization' } -GtCoderNavigationClassesHierarchyTreeElement >> initializeWithClasses: aCollectionOfClasses [ - classesTree := (GtCoderClassesHierarchyTree - fromClasses: aCollectionOfClasses) sortByClassName. - self - items: classesTree subclassTrees - lazy: [ :eachTree | eachTree subclassTrees ]. - self expandAll -] - -{ #category : #'api - initialization' } -GtCoderNavigationClassesHierarchyTreeElement >> initializeWithHierachyForClass: aClass [ - classesTree := (GtCoderGrowingClassesHierarchyTree - hierarchyForClass: aClass) sortByClassName. - self - items: classesTree subclassTrees - lazy: [ :eachTree | eachTree subclassTrees ]. - self expandAll -] - -{ #category : #'api - selection' } -GtCoderNavigationClassesHierarchyTreeElement >> selectedClass [ - - self selectedNodeDo: [ :aNode | ^ aNode value rootClass ]. - ^ nil -] - -{ #category : #'api - selection' } -GtCoderNavigationClassesHierarchyTreeElement >> selectedIndice [ - "Return selected indice or zero" - - self selectedIndices - ifNotEmpty: [ :theIndices | - (theIndices first between: 1 and: self viewModel itemCount) - ifTrue: [ ^ theIndices first ] ]. - ^ 0 -] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationClassesListElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationClassesListElement.class.st deleted file mode 100644 index 89057a611..000000000 --- a/src/GToolkit-Coder-UI/GtCoderNavigationClassesListElement.class.st +++ /dev/null @@ -1,79 +0,0 @@ -" -I show a simple list of classes sorting alphabetically - - -" -Class { - #name : #GtCoderNavigationClassesListElement, - #superclass : #BrSimpleList, - #traits : 'TGtCoderNavigationClassesHelper', - #classTraits : 'TGtCoderNavigationClassesHelper classTrait', - #category : #'GToolkit-Coder-UI-Navigation' -} - -{ #category : #'instance creation' } -GtCoderNavigationClassesListElement class >> fromClasses: aCollectionOfClasses [ - ^ self new initializeWithClasses: aCollectionOfClasses -] - -{ #category : #'private - ui' } -GtCoderNavigationClassesListElement >> buildClassLabel: aClass [ - | aLook aText | - aLook := (BrGlamorousLabelAptitude new fontSize: 12). - aText := aClass name. - - (self isAbstractClass: aClass) - ifTrue: [ aLook italic ]. - - (self isDeprecatedClass: aClass) - ifTrue: [ - aText := aText, ' (deprecated)'. - Transcript show: aText; cr. - aLook bold ]. - - ^ BrLabel new - aptitude: aLook; - text: aText. -] - -{ #category : #'private - ui' } -GtCoderNavigationClassesListElement >> buildClassRow: aClass index: aRowIndex list: aListElement [ - ^ BrWidgetContainer new - layout: BlLinearLayout horizontal; - aptitude: - BrHorizontalPaneAptitude new - + - (BrStyleCommonAptitude new - default: - [ :aStyle | aStyle background: self theme item deselectedColor ]; - hovered: - [ :aStyle | aStyle background: self theme item hoveredColor ]; - selected: - [ :aStyle | aStyle background: self theme item selectedColor ]; - pressed: - [ :aStyle | aStyle background: self theme item pressedColor ]; - focused: [ :aStyle | - aStyle - border: (BlBorder paint: self theme item focusedBorderColor width: 1) ]); - when: BlMouseDownEvent - do: [ :anEvent | - anEvent consumed: true. - aListElement selectOne: aRowIndex dueTo: anEvent. - aListElement requestFocus ]; - hMatchParent; - vFitContent; - padding: (BlInsets left: 3); - addChild: (self buildClassLabel: aClass) -] - -{ #category : #initialization } -GtCoderNavigationClassesListElement >> initialize [ - super initialize. - - self stencil: [ :eachClass :eachIndex :aListElement | self buildClassRow: eachClass index: eachIndex list: aListElement ] -] - -{ #category : #initialization } -GtCoderNavigationClassesListElement >> initializeWithClasses: aCollectionOfClasses [ - self items: (aCollectionOfClasses sorted: [ :classA :classB | classA name < classB name ]) -] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationClassesTreeElementId.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationClassesTreeElementId.class.st new file mode 100644 index 000000000..eccf4e0bd --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationClassesTreeElementId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderNavigationClassesTreeElementId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #accessing } +GtCoderNavigationClassesTreeElementId >> asSymbol [ + ^ #'coder-sidebar--classes-hierarchy-tree-item' +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationCompiledMethodElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationCompiledMethodElement.class.st new file mode 100644 index 000000000..3537ae1f5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationCompiledMethodElement.class.st @@ -0,0 +1,130 @@ +Class { + #name : #GtCoderNavigationCompiledMethodElement, + #superclass : #BlElement, + #traits : 'TBrLayoutResizable', + #classTraits : 'TBrLayoutResizable classTrait', + #instVars : [ + 'label', + 'compiledMethod', + 'labelElement', + 'addOnsElement', + 'addOnsCache' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #'api - initialization' } +GtCoderNavigationCompiledMethodElement >> addOnsCache: anAddOnsCache [ + addOnsCache := anAddOnsCache +] + +{ #category : #'api - compiled method' } +GtCoderNavigationCompiledMethodElement >> compiledMethod [ + ^ compiledMethod +] + +{ #category : #'api - compiled method' } +GtCoderNavigationCompiledMethodElement >> compiledMethod: aCompiledMethod [ + + self assert: [ aCompiledMethod isNotNil ]. + + compiledMethod = aCompiledMethod ifTrue: [ ^ self ]. + + compiledMethod := aCompiledMethod. + self onCompiledMethodChanged. +] + +{ #category : #initialization } +GtCoderNavigationCompiledMethodElement >> defaultLayout [ + ^ BlLinearLayout horizontal +] + +{ #category : #initialization } +GtCoderNavigationCompiledMethodElement >> initialize [ + super initialize. + + self initializeLabelElement. + self initializeAddOnsElement. + + self addChild: labelElement. + self addChild: addOnsElement. + + self addAptitude: BrGlamorousListItemAptitude. + + self + hMatchParent; + vFitContent +] + +{ #category : #initialization } +GtCoderNavigationCompiledMethodElement >> initializeAddOnsElement [ + addOnsElement := BrHorizontalPane new + id: #addOnsContainer; + cellSpacing: 2; + alignCenterLeft; + fitContent; + withAsyncFutureDo: [ :anElementFuture | + anElementFuture + executionConfiguration: GtSingleCoderViewModel secondaryMethodAddOnsExecutionConfiguration; + whenPending: [ :theContainer | + theContainer removeChildren ]; + whenError: [ :theContainer :anError | + self updateAddOnElements: { + anError asDebuggableElement + hFitContentLimited; + vFitContent } ]; + whenSuccess: [ :theContainer :someElements | + self updateAddOnElements: someElements ] ] +] + +{ #category : #initialization } +GtCoderNavigationCompiledMethodElement >> initializeLabelElement [ + labelElement := BrLabel new. + labelElement + hFitContent; + vFitContent; + beSmallSize; + aptitude: GtCoderNavigationTreeLabelAptitude +] + +{ #category : #'api - compiled method' } +GtCoderNavigationCompiledMethodElement >> onCompiledMethodChanged [ + self updateElement +] + +{ #category : #'private - updating' } +GtCoderNavigationCompiledMethodElement >> updateAddOnElements: addOnElements [ + (addOnElements allSatisfy: [ :eachElement | eachElement parent == addOnsElement ]) + ifTrue: [ ^ self ]. + + addOnElements do: #removeFromParent. + addOnsElement + removeChildren; + addChildren: addOnElements +] + +{ #category : #'private - updating' } +GtCoderNavigationCompiledMethodElement >> updateAddonsElement [ + | aFuture | + addOnsCache ifNil: [ ^ self ]. + + GtCoderNavigationCompiledMethodsAddOnsCache isEnabled ifFalse: [ ^ self ]. + + aFuture := addOnsCache elementsForCompiledMethod: compiledMethod. + addOnsElement asyncFuture future: aFuture +] + +{ #category : #'private - updating' } +GtCoderNavigationCompiledMethodElement >> updateElement [ + self updateLabelElement. + self updateAddonsElement. +] + +{ #category : #'private - updating' } +GtCoderNavigationCompiledMethodElement >> updateLabelElement [ + | aSelector | + aSelector := compiledMethod selector asRopedText. + compiledMethod isDeprecated ifTrue: [ aSelector lineThrough ]. + + labelElement text: aSelector +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationCompiledMethodsAddOnsCache.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationCompiledMethodsAddOnsCache.class.st new file mode 100644 index 000000000..24fa1c8ad --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationCompiledMethodsAddOnsCache.class.st @@ -0,0 +1,109 @@ +Class { + #name : #GtCoderNavigationCompiledMethodsAddOnsCache, + #superclass : #Object, + #instVars : [ + 'elementCache', + 'coderViewModelCache' + ], + #classVars : [ + 'IsEnabled' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #settings } +GtCoderNavigationCompiledMethodsAddOnsCache class >> disable [ + IsEnabled := false +] + +{ #category : #settings } +GtCoderNavigationCompiledMethodsAddOnsCache class >> enable [ + IsEnabled := true +] + +{ #category : #settings } +GtCoderNavigationCompiledMethodsAddOnsCache class >> isEnabled [ + ^ IsEnabled ifNil: [ IsEnabled := true ] +] + +{ #category : #private } +GtCoderNavigationCompiledMethodsAddOnsCache >> addOnsFutureForCompiledMethod: aCompiledMethod [ + + | coderViewModel | + coderViewModel := coderViewModelCache + at: aCompiledMethod + ifPresent: [ :aCoderViewModel | aCoderViewModel ] + ifAbsentPut: [ + | newCoder | + newCoder := GtSourceCoder forPharoMethod: aCompiledMethod. + newCoder asCoderViewModel ]. + + ^ coderViewModel +] + +{ #category : #accessing } +GtCoderNavigationCompiledMethodsAddOnsCache >> elementsForCompiledMethod: aCompiledMethod [ + + ^ elementCache + at: aCompiledMethod + ifPresent: [ :someElements | someElements asAsyncFuture ] + ifAbsent: [ | aCoderViewModel someElements | + aCoderViewModel := self + addOnsFutureForCompiledMethod: aCompiledMethod. + + aCoderViewModel addOnsFuture + map: [ :addOns | + someElements := addOns previews + collect: [ :eachAddOn | + | eachAddOnElement | + eachAddOnElement := eachAddOn stencil asElement. + eachAddOn dataBinder + element: eachAddOnElement; + coderViewModel: aCoderViewModel; + build. + eachAddOnElement ]. + + elementCache at: aCompiledMethod put: someElements. + + someElements ] ] +] + +{ #category : #initialization } +GtCoderNavigationCompiledMethodsAddOnsCache >> initialize [ + super initialize. + + elementCache := AsyncSharedWeakIdentityKeyDictionary new. + coderViewModelCache := AsyncSharedWeakIdentityKeyDictionary new. +] + +{ #category : #'event handling' } +GtCoderNavigationCompiledMethodsAddOnsCache >> onClassAddedOrRemoved: anAnnouncement [ + elementCache + keysAndValuesRemove: [ :eachCompiledMethod :someElements | + (eachCompiledMethod methodClass inheritsFrom: anAnnouncement classAffected) + or: [ anAnnouncement classAffected inheritsFrom: eachCompiledMethod methodClass ] ] +] + +{ #category : #'event handling' } +GtCoderNavigationCompiledMethodsAddOnsCache >> onMethodAddedOrRemoved: anAnnouncement [ + elementCache + keysAndValuesRemove: [ :eachCompiledMethod :someElements | + (eachCompiledMethod methodClass inheritsFrom: anAnnouncement classAffected) + or: [ anAnnouncement classAffected inheritsFrom: eachCompiledMethod methodClass ] ] +] + +{ #category : #initialization } +GtCoderNavigationCompiledMethodsAddOnsCache >> subscribeToSystem [ + SystemAnnouncer uniqueInstance weak + when: MethodAdded , MethodRemoved, MethodModified + send: #onMethodAddedOrRemoved: + to: self; + when: ClassAdded , ClassRemoved + send: #onClassAddedOrRemoved: + to: self +] + +{ #category : #initialization } +GtCoderNavigationCompiledMethodsAddOnsCache >> unsubscribeFromSystem [ + SystemAnnouncer uniqueInstance unsubscribe: self +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationContextMenuContent.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationContextMenuContent.class.st new file mode 100644 index 000000000..64d8a85d8 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationContextMenuContent.class.st @@ -0,0 +1,26 @@ +Class { + #name : #GtCoderNavigationContextMenuContent, + #superclass : #GtAbstractCoderContextMenuContent, + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #accessing } +GtCoderNavigationContextMenuContent >> menuItemStencil [ + ^ [ :e | + BrHorizontalPane new + hMatchParent; + vFitContent; + alignCenter; + aptitude: self selectionAptitude; + padding: self itemPadding; + when: BlClickEvent + do: [ e value cull: self anchor. + self fireEvent: (BrContextMenuHideWish new + anchor: self anchor; + yourself). + self fireEvent: (BrDropdownHideWish new + anchor: self anchor; + yourself) ]; + addChild: (self itemLabelFor: e key); + yourself ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationElement.class.st index 448c306e8..69e1bbe9c 100644 --- a/src/GToolkit-Coder-UI/GtCoderNavigationElement.class.st +++ b/src/GToolkit-Coder-UI/GtCoderNavigationElement.class.st @@ -3,7 +3,6 @@ Class { #superclass : #BlElement, #instVars : [ 'navigationModel', - 'supressNavigationChanges', 'supressListChanges', 'classesList' ], @@ -17,6 +16,16 @@ GtCoderNavigationElement >> assertNavigationModel: aGtCoderNavigationPackagesTag description: [ 'Navigation model must be non-nil' ]. ] +{ #category : #'private-accessing' } +GtCoderNavigationElement >> classesList [ + ^classesList +] + +{ #category : #'api - class selections' } +GtCoderNavigationElement >> deselectClass: aClass [ + classesList deselectAll +] + { #category : #'api - class selections' } GtCoderNavigationElement >> deselectClasses [ classesList deselectAll @@ -37,15 +46,14 @@ GtCoderNavigationElement >> hasNavigationModel [ GtCoderNavigationElement >> initialize [ super initialize. supressListChanges := false. - supressNavigationChanges := false. self initializeLayout. self initializeContent. - self initializeElement. + self initializeElement ] { #category : #initialization } GtCoderNavigationElement >> initializeContent [ - classesList := GtCoderNavigationClassesHierarchyTreeElement new. + classesList := GtCoderClassesTreeElement multiSelection. ] { #category : #initialization } @@ -67,78 +75,85 @@ GtCoderNavigationElement >> navigationModel [ { #category : #accessing } GtCoderNavigationElement >> navigationModel: aGtCoderNavigationPackagesTagsClassesModel [ - self assertNavigationModel: aGtCoderNavigationPackagesTagsClassesModel. self unsubscribeFromNavigationModel. navigationModel := aGtCoderNavigationPackagesTagsClassesModel. self onNavigationModelChanged. ] -{ #category : #'hooks - children' } -GtCoderNavigationElement >> onAddedToSceneGraph [ - super onAddedToSceneGraph. - self updateContent. - self subscribeToNavigationModel. - self subscribeToContent. +{ #category : #'event handling' } +GtCoderNavigationElement >> onClassAdded: anAnnouncement [ + self + suppressListChangeEventsDuring: [ self updateClassList ] +] + +{ #category : #'event handling' } +GtCoderNavigationElement >> onClassDeselected: anAnnouncement [ + self subclassResponsibility ] { #category : #'event handling' } GtCoderNavigationElement >> onClassListSelectionChanged [ - | anIndex aSelectedItem theIndices | + | theIndices someClasses | supressListChanges ifTrue: [ ^ self ]. theIndices := classesList selectedIndices. theIndices ifEmpty: [ ^ self ]. - anIndex := theIndices first. - (anIndex between: 1 and: classesList viewModel itemCount) + (theIndices first between: 1 and: classesList viewModel itemCount) ifFalse: [ ^ self ]. - aSelectedItem := (classesList viewModel itemAt: anIndex) value rootClass. - self navigationModel selectClass: aSelectedItem + (theIndices last between: 1 and: classesList viewModel itemCount) + ifFalse: [ ^ self ]. + someClasses := classesList selectedNodes + collect: [ :each | each value rootClass ]. + someClasses ifEmpty: [ ^ self ]. + someClasses size = 1 + ifTrue: [ self navigationModel selectClass: someClasses anyOne ] + ifFalse: [ self navigationModel selectClasses: someClasses ] ] { #category : #'event handling' } GtCoderNavigationElement >> onClassModified: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self - updateClass: anAnnouncement theClass - inPackage: anAnnouncement package - tag: anAnnouncement tag ] + BlTaskAction + enqueueElement: self + action: [ self + suppressListChangeEventsDuring: [ self + updateClass: anAnnouncement theClass + inPackage: anAnnouncement package + tag: anAnnouncement tag ] ] +] + +{ #category : #'event handling' } +GtCoderNavigationElement >> onClassRemoved: anAnnouncement [ + self subclassResponsibility ] { #category : #'event handling' } GtCoderNavigationElement >> onClassRenamed: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self - renameClass: anAnnouncement theClass - oldName: anAnnouncement oldName - newName: anAnnouncement newName - inPackage: anAnnouncement package - tag: anAnnouncement tag ] + "self + inUIProcessDo: [ self + suppressListChangeEventsDuring: [ self + renameClass: anAnnouncement theClass + oldName: anAnnouncement oldName + newName: anAnnouncement newName + inPackage: anAnnouncement package + tag: anAnnouncement tag ] ]" ] { #category : #'event handling' } GtCoderNavigationElement >> onClassSelected: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self - selectClass: anAnnouncement theClass - inPackage: anAnnouncement package - tag: anAnnouncement tag ]. + self subclassResponsibility +] + +{ #category : #'event handling' } +GtCoderNavigationElement >> onClassesToShowChanged: anAnnouncement [ + "Refactorings that are executed from a non-UI process should schedule an update task" + self inUIProcessDo: [ self updateClassList ] ] { #category : #'event handling' } GtCoderNavigationElement >> onNavigationModelChanged [ "subclasses may perform actions on navigation model changes" - self isAttachedToSceneGraph ifFalse: [ ^ self ]. - self subscribeToNavigationModel. self updateContent. -] - -{ #category : #'hooks - children' } -GtCoderNavigationElement >> onRemovedFromSceneGraph [ - super onRemovedFromSceneGraph. - self unsuscribeFromContent. - self unsubscribeFromNavigationModel. + self subscribeToNavigationModel. + self subscribeToContent. ] { #category : #'api - class updates' } @@ -148,7 +163,7 @@ GtCoderNavigationElement >> renameClass: aClass oldName: anOldName newName: aNew indexOfSuchThat: [ :aTree | aTree rootClass = aClass ] do: [ :aTree | - self updateClassLists. + self updateClassList. self updateSelectedClass ]. ] @@ -169,7 +184,7 @@ GtCoderNavigationElement >> scrollIndexFromPrevious: aPreviousIndex current: aNe { #category : #'api - class selections' } GtCoderNavigationElement >> selectClass: aClass [ - | aPreviousIndex | + | aPreviousIndex | aPreviousIndex := classesList selectedIndice. classesList viewModel indexOfSuchThat: [ :each | each rootClass = aClass ] @@ -183,6 +198,44 @@ GtCoderNavigationElement >> selectClass: aClass [ max: classesList viewModel itemCount) ] ] +{ #category : #'api - class selections' } +GtCoderNavigationElement >> selectClasses: aCollectionOfClasses [ + | aPreviousIndex newIndexes | + aPreviousIndex := classesList selectedIndice. + + newIndexes := Array + new: aCollectionOfClasses size + streamContents: [ :aStream | + aCollectionOfClasses + do: [ :eachClass | + classesList viewModel + indexOfSuchThat: [ :each | each rootClass = eachClass ] + do: [ :aNewIndex | aStream nextPut: aNewIndex ] ] ]. + + newIndexes + ifEmpty: [ classesList deselectAll. + ^ self ]. + + newIndexes size = 1 + ifTrue: [ classesList + selectOne: newIndexes anyOne; + scrollToIndex: (self + scrollIndexFromPrevious: aPreviousIndex + current: newIndexes anyOne + max: classesList viewModel itemCount). + ^ self ]. + + newIndexes sort: [ :a :b | a < b ]. + classesList deselectAll. + newIndexes + do: [ :aNewIndex | classesList addToSelection: aNewIndex dueTo: nil ]. + classesList + scrollToIndex: (self + scrollIndexFromPrevious: aPreviousIndex + current: newIndexes anyOne + max: classesList viewModel itemCount) +] + { #category : #subscriptions } GtCoderNavigationElement >> subscribeToClassList [ classesList @@ -196,10 +249,23 @@ GtCoderNavigationElement >> subscribeToContent [ { #category : #subscriptions } GtCoderNavigationElement >> subscribeToNavigationModel [ + | subscriptions | self hasNavigationModel ifFalse: [ ^ self ]. - navigationModel when: GtCoderNavigationClassSelected send: #onClassSelected: to: self. - navigationModel when: GtCoderNavigationClassRenamed send: #onClassRenamed: to: self. - navigationModel when: GtCoderNavigationClassModified send: #onClassModified: to: self. + + subscriptions := { + GtCoderNavigationClassSelected -> #onClassSelected:. + GtCoderNavigationClassDeselected -> #onClassDeselected:. + GtCoderNavigationClassRenamed -> #onClassRenamed:. + GtCoderNavigationClassModified -> #onClassModified:. + GtCoderNavigationClassesToShowChanged -> #onClassesToShowChanged:. + }. + + subscriptions + do: [ :sub | + navigationModel weak + when: sub key + send: sub value + to: self ] ] { #category : #subscriptions } @@ -210,14 +276,6 @@ GtCoderNavigationElement >> suppressListChangeEventsDuring: aBlock [ aBlock ensure: [ supressListChanges := anOldValue ]. ] -{ #category : #subscriptions } -GtCoderNavigationElement >> suppressNavigationChangeEventsDuring: aBlock [ - | anOldValue | - anOldValue := supressNavigationChanges. - supressNavigationChanges := true. - aBlock ensure: [ supressNavigationChanges := anOldValue ]. -] - { #category : #subscriptions } GtCoderNavigationElement >> unsubscribeFromNavigationModel [ self hasNavigationModel ifFalse: [ ^ self ]. @@ -235,12 +293,12 @@ GtCoderNavigationElement >> updateClass: aClass inPackage: aPackage tag: aTag [ indexOfSuchThat: [ :aTree | aTree rootClass = aClass ] do: [ :aTree | - self updateClassLists. + self updateClassList. self updateSelectedClass ]. ] { #category : #'updating lists' } -GtCoderNavigationElement >> updateClassLists [ +GtCoderNavigationElement >> updateClassList [ "Update class list" ] @@ -251,6 +309,5 @@ GtCoderNavigationElement >> updateContent [ { #category : #'updating lists' } GtCoderNavigationElement >> updateSelectedClass [ self hasNavigationModel ifFalse: [ ^ self ]. - navigationModel selectedClassDo: [ :aClass | - self selectClass: aClass ]. + navigationModel selectedClassDo: [ :aClass | self selectClass: aClass ] ] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationMethodItemElementStencil.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationMethodItemElementStencil.class.st new file mode 100644 index 000000000..1381ff8f2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationMethodItemElementStencil.class.st @@ -0,0 +1,33 @@ +" +I create a basic method item element used by {{gtClass:GtCoderMethodsGroupedListElement}}. +Such method item elements are created in the {{gtMethod:GtCoderMethodsGroupedListElement>>#buildMethodItem}} method. +Users can set their own stencil using {{gtMethod:GtCoderNavigationMethodItemElementStencil class>>#defaultStencil:}}. + +" +Class { + #name : #GtCoderNavigationMethodItemElementStencil, + #superclass : #BrStencil, + #classVars : [ + 'DefaultStencil' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #accessing } +GtCoderNavigationMethodItemElementStencil class >> defaultStencil [ + ^ DefaultStencil +] + +{ #category : #accessing } +GtCoderNavigationMethodItemElementStencil class >> defaultStencil: aStencil [ + "Users can customize their default navigation method item element" + + DefaultStencil := aStencil asStencil +] + +{ #category : #'api - instantiation' } +GtCoderNavigationMethodItemElementStencil >> create [ + ^ DefaultStencil + ifNil: [ GtCoderNavigationCompiledMethodElement new ] + ifNotNil: [ :aStencil | aStencil create ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationMethodProtocolListElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationMethodProtocolListElement.class.st deleted file mode 100644 index 786325b1e..000000000 --- a/src/GToolkit-Coder-UI/GtCoderNavigationMethodProtocolListElement.class.st +++ /dev/null @@ -1,65 +0,0 @@ -Class { - #name : #GtCoderNavigationMethodProtocolListElement, - #superclass : #BrSimpleList, - #category : #'GToolkit-Coder-UI-Navigation' -} - -{ #category : #'instance creation' } -GtCoderNavigationMethodProtocolListElement class >> fromClass: aClass [ - ^ self new initializeWithClass: aClass -] - -{ #category : #'private - ui' } -GtCoderNavigationMethodProtocolListElement >> buildProtocolLabel: aProtocol [ - ^ BrLabel new - aptitude: BrGlamorousLabelAptitude; - beSmallSize; - text: aProtocol name -] - -{ #category : #'private - ui' } -GtCoderNavigationMethodProtocolListElement >> buildProtocolRow: aProtocol index: aRowIndex list: aListElement [ - ^ BrWidgetContainer new - layout: BlLinearLayout horizontal; - aptitude: - BrHorizontalPaneAptitude new - + - (BrStyleCommonAptitude new - default: - [ :aStyle | aStyle background: self theme item deselectedColor ]; - hovered: - [ :aStyle | aStyle background: self theme item hoveredColor ]; - selected: - [ :aStyle | aStyle background: self theme item selectedColor ]; - pressed: - [ :aStyle | aStyle background: self theme item pressedColor ]; - focused: [ :aStyle | - aStyle - border: (BlBorder paint: self theme item focusedBorderColor width: 1) ]); - when: BlMouseDownEvent - do: [ :anEvent | - anEvent consumed: true. - aListElement selectOne: aRowIndex dueTo: anEvent. - aListElement requestFocus ]; - hMatchParent; - vFitContent; - padding: (BlInsets left: 3); - addChild: (self buildProtocolLabel: aProtocol) -] - -{ #category : #initialization } -GtCoderNavigationMethodProtocolListElement >> initialize [ - super initialize. - - self stencil: [ :eachProtocol :eachIndex :aListElement | self buildProtocolRow: eachProtocol index: eachIndex list: aListElement ] -] - -{ #category : #initialization } -GtCoderNavigationMethodProtocolListElement >> initializeWithClass: aClass [ - self initializeWithProtocols: aClass organization allProtocols -] - -{ #category : #initialization } -GtCoderNavigationMethodProtocolListElement >> initializeWithProtocols: aCollection [ - self items: (aCollection sorted: [ :protocolA :protocolB | protocolA name < protocolB name ]) -] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationPackagesTagsClassesElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationPackagesTagsClassesElement.class.st index 85f938cf5..79c82556c 100644 --- a/src/GToolkit-Coder-UI/GtCoderNavigationPackagesTagsClassesElement.class.st +++ b/src/GToolkit-Coder-UI/GtCoderNavigationPackagesTagsClassesElement.class.st @@ -1,475 +1,154 @@ Class { #name : #GtCoderNavigationPackagesTagsClassesElement, - #superclass : #GtCoderNavigationElement, + #superclass : #GtCoderNavigationBasicPackagesTagsClassesElement, + #traits : 'TGtCoderNavigationWithContextMenu', + #classTraits : 'TGtCoderNavigationWithContextMenu classTrait', #instVars : [ - 'packagesList', - 'classesLabel', 'methodProtocolsList', - 'classAndMethodProtocolList', - 'protocolLabel', - 'coderFilterChangesSubscription' + 'methodsLabel', + 'methodGroupList', + 'slotsGroup', + 'slotsGroupList' ], #category : #'GToolkit-Coder-UI-Navigation' } -{ #category : #'api - package updates' } -GtCoderNavigationPackagesTagsClassesElement >> addPackage: aPackage [ - | aSelectedPackageOrTag | - - packagesList selectedNodeDo: [ :aNode | - aSelectedPackageOrTag := aNode value ]. - - self updatePackageLists. - aSelectedPackageOrTag ifNotNil: [ - self selectPackageOrTag: aSelectedPackageOrTag ]. -] - -{ #category : #'api - package updates' } -GtCoderNavigationPackagesTagsClassesElement >> addPackage: aPackage tag: aPackageTag [ - | aSelectedPackageOrTag | - - packagesList selectedNodeDo: [ :aNode | - aSelectedPackageOrTag := aNode value ]. - - self updatePackageLists. - aSelectedPackageOrTag ifNotNil: [ - self selectPackageOrTag: aSelectedPackageOrTag ]. -] - -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> deselectPackages [ - packagesList deselectAll -] - -{ #category : #'updating lists' } -GtCoderNavigationPackagesTagsClassesElement >> emptyMethodProtocolList [ - methodProtocolsList initializeWithProtocols: #(). +{ #category : #initialization } +GtCoderNavigationPackagesTagsClassesElement >> buildCategorySlotTabs [ + | aTabGroup | + aTabGroup := BrTabGroup new + aptitude: BrGlamorousTabGroupProportionalAptitude - BrTabGroupSplitterAptitude; + addTab: (BrTab new + hMatchParent; + aptitude: GtProtocolSlotTabAptitude new; + label: 'Categories' asRopedText glamorousCodeSmallSize; + stencil: [ methodProtocolsList ]); + addTab: (BrTab new + id: #slots; + hMatchParent; + aptitude: GtProtocolSlotTabAptitude new; + label: 'Slots' asRopedText glamorousCodeSmallSize; + stencil: [ slotsGroupList ]). + + ^ aTabGroup ] -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> expandPackage: aRPackage [ - packagesList viewModel root - allChildrenNodesBreadthFirstDo: [ :eachTreeNode | - eachTreeNode value = aRPackage - ifTrue: [ eachTreeNode expand ] ] +{ #category : #initialization } +GtCoderNavigationPackagesTagsClassesElement >> buildGroupList [ + ^ BrGroupedList new + padding: (BlInsets left: 5 right: 10); + matchParent; + headerElementStencil: [ BrLabel new + beSmallSize; + aptitude: (BrGlamorousLabelAptitude new foreground: Color gray) ]; + headerDataBinder: [ :label :each | label text: each domainObject asRopedText ] ] -{ #category : #'private - testing' } -GtCoderNavigationPackagesTagsClassesElement >> hasPackageTagsIn: aRPackage [ - - | noTags noExtensions | - noTags := (aRPackage classTags size = 1 and: [ aRPackage classTags anyOne name = aRPackage name ]). - noExtensions := aRPackage extendedClasses isEmpty. - ^ noTags not or: [ noExtensions not ] +{ #category : #initialization } +GtCoderNavigationPackagesTagsClassesElement >> buildMethodGroupList [ + ^ GtCoderMethodsGroupedListElement multiSelection ] -{ #category : #'showing / hiding' } -GtCoderNavigationPackagesTagsClassesElement >> hideClassList [ - classAndMethodProtocolList visibility: BlVisibility gone. - classesLabel visibility: BlVisibility gone. - self layout columnCount: 1. +{ #category : #initialization } +GtCoderNavigationPackagesTagsClassesElement >> buildProtocolGroupList [ + ^ GtCoderProtocolsGroupedListElement new ] -{ #category : #'showing / hiding' } -GtCoderNavigationPackagesTagsClassesElement >> hideOrShowClassList [ - classesList maxSelectionIndex isZero - ifTrue: [ self hideClassList ] - ifFalse: [ self showClassList ] +{ #category : #initialization } +GtCoderNavigationPackagesTagsClassesElement >> buildSlotsGroupList [ + ^ GtCoderSlotsGroupedListElement new ] { #category : #initialization } GtCoderNavigationPackagesTagsClassesElement >> initializeContent [ super initializeContent. - packagesList := GtCoderNavigationPackagesTreeElement new. - - methodProtocolsList := GtCoderNavigationMethodProtocolListElement new - matchParent. - - classAndMethodProtocolList := BrVerticalPane new - matchParent; - addChild: classesList; - addChild: (protocolLabel := BrLabel new - aptitude: (BrGlamorousLabelAptitude new fontSize: 12; foreground: Color gray); - text: 'Categories'); - addChild: methodProtocolsList. + methodProtocolsList := self buildProtocolGroupList. + slotsGroupList := self buildSlotsGroupList. + methodGroupList := self buildMethodGroupList ] { #category : #initialization } GtCoderNavigationPackagesTagsClassesElement >> initializeElement [ + | pane3 pane4 | super initializeElement. - self addChildren: { - BrLabel new - aptitude: (BrGlamorousLabelAptitude new fontSize: 12; foreground: Color gray); - text: 'Packages'. - - classesLabel := BrLabel new - aptitude: (BrGlamorousLabelAptitude new fontSize: 12; foreground: Color gray); - text: 'Classes'. - - packagesList. - classAndMethodProtocolList. - } + pane3 := BrVerticalPane new + addAptitude: BrGlamorousWithHorizontalResizerAptitude; + matchParent; + addChildren: {self buildCategorySlotTabs}. + + pane4 := BrVerticalPane new + matchParent; + addChildren: {methodsLabel := self headerLabel text: 'Methods'. + methodGroupList}. + self + addChildren: {pane3. + pane4} ] -{ #category : #initialization } -GtCoderNavigationPackagesTagsClassesElement >> initializeLayout [ - super initializeLayout. - self layout: (BlGridLayout horizontal columnCount: 2; cellSpacing: 5). - self constraintsDo: [ :c | - c horizontal matchParent. - c vertical matchParent ]. +{ #category : #'private - accessing' } +GtCoderNavigationPackagesTagsClassesElement >> methodProtocolsList [ + ^ methodProtocolsList ] { #category : #'event handling' } GtCoderNavigationPackagesTagsClassesElement >> onMethodsCoderFiltersChanged: aGtCodersFiltersChanged [ aGtCodersFiltersChanged source = self ifTrue: [ ^ self ]. - - methodProtocolsList selectedItemDo: [ :aSelectedItem | - | aCategoryFilter | - aCategoryFilter := GtSearchMethodCategoryFilter forCategory: aSelectedItem name. - - aGtCodersFiltersChanged filters do: [ :eachFilter | - eachFilter = aCategoryFilter ifTrue: [ - ^ self ] ] ]. - - aGtCodersFiltersChanged filters do: [ :eachFilter | - eachFilter class = GtSearchMethodCategoryFilter ifTrue: [ - methodProtocolsList items - doWithIndex: [ :aProtocol :anIndex | - (aProtocol name = eachFilter category) ifTrue: [ - self suppressListChangeEventsDuring: [ - methodProtocolsList selectOne: anIndex ]. - ^ self ] ] ] ]. - - self suppressListChangeEventsDuring: [ - methodProtocolsList deselectAll ]. -] - -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackageListSelectionChanged [ - | anIndex aSelectedItem theIndices | - supressListChanges ifTrue: [ ^ self ]. - theIndices := packagesList selectedIndices. - theIndices ifEmpty: [ ^ self ]. - anIndex := theIndices first. - (anIndex between: 1 and: packagesList viewModel itemCount) - ifFalse: [ ^ self ]. - aSelectedItem := (packagesList viewModel itemAt: anIndex) value. - (aSelectedItem isKindOf: RPackage) - ifTrue: [ self navigationModel selectPackage: aSelectedItem ] - ifFalse: [ self navigationModel selectTag: aSelectedItem ] -] - -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackageRegistered: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self addPackage: anAnnouncement package ] -] - -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackageRenamed: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self - renamePackage: anAnnouncement package - oldName: anAnnouncement oldName - newName: anAnnouncement newName ] -] - -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackageSelected: anAnnouncer [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self selectPackage: anAnnouncer package. - self deselectClasses. ] -] - -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackageTagAdded: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self addPackage: anAnnouncement package tag: anAnnouncement tag ] -] - -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackageTagRemoved: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self removePackage: anAnnouncement package tag: anAnnouncement tag ] -] + methodProtocolsList + selectedItemDo: [ :aSelectedItem | + | aCategoryFilter | + aCategoryFilter := GtSearchMethodCategoryFilter + forCategory: aSelectedItem name. -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackageTagSelected: anAnnouncer [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self - selectPackage: anAnnouncer package - tag: anAnnouncer tag. - self deselectClasses. ]. -] + aGtCodersFiltersChanged filters + do: [ :eachFilter | eachFilter = aCategoryFilter ifTrue: [ ^ self ] ] ]. -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackageUnregistered: anAnnouncement [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self removePackage: anAnnouncement package ] -] + aGtCodersFiltersChanged filters + do: [ :eachFilter | + eachFilter class = GtSearchMethodCategoryFilter + ifTrue: [ methodProtocolsList items + doWithIndex: [ :aProtocol :anIndex | + aProtocol name = eachFilter category + ifTrue: [ self + suppressListChangeEventsDuring: [ methodProtocolsList selectOne: anIndex ]. + ^ self ] ] ] ]. -{ #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onPackagesSelected: anAnnouncer [ - supressNavigationChanges ifTrue: [ ^ self ]. - self suppressListChangeEventsDuring: [ - self deselectPackages. - self deselectClasses. ] + self suppressListChangeEventsDuring: [ methodProtocolsList deselectAll ] ] { #category : #'event handling' } -GtCoderNavigationPackagesTagsClassesElement >> onProtocolListSelectionChanged [ - | anIndex aSelectedItem theIndices | - supressListChanges ifTrue: [ ^ self ]. - theIndices := methodProtocolsList selectedIndices. - theIndices ifEmpty: [ ^ self ]. - anIndex := theIndices first. - (anIndex between: 1 and: methodProtocolsList viewModel itemCount) - ifFalse: [ ^ self ]. - aSelectedItem := (methodProtocolsList viewModel itemAt: anIndex) value. - self navigationModel selectMethodProtocol: aSelectedItem source: self. -] +GtCoderNavigationPackagesTagsClassesElement >> onNavigationModelChanged [ + methodProtocolsList navigationModel: self navigationModel. + slotsGroupList navigationModel: self navigationModel. + methodGroupList navigationModel: self navigationModel. -{ #category : #'api - package updates' } -GtCoderNavigationPackagesTagsClassesElement >> removePackage: aPackage [ - | aSelectedPackageOrTag anIndex | - - anIndex := packagesList viewModel indexOf: aPackage. - anIndex > 0 ifFalse: [ ^ self ]. - - packagesList selectedNodeDo: [ :aNode | - aSelectedPackageOrTag := aNode value ]. - - self updatePackageLists. - self reselectPackageOrTag: aSelectedPackageOrTag afterRemovalOfPackageNamed: aPackage name. + super onNavigationModelChanged ] -{ #category : #'api - package updates' } -GtCoderNavigationPackagesTagsClassesElement >> removePackage: aPackage tag: aPackageTagName [ - | aSelectedPackageOrTag anIndex | - anIndex := packagesList viewModel indexOf: aPackage. - anIndex > 0 ifFalse: [ ^ self ]. - packagesList selectedNodeDo: [ :aNode | - aSelectedPackageOrTag := aNode value ]. - self updatePackageLists. - self reselectPackageOrTag: aSelectedPackageOrTag afterRemovalOfTagNamed: aPackageTagName. -] - -{ #category : #'api - package updates' } -GtCoderNavigationPackagesTagsClassesElement >> renamePackage: aPackage oldName: anOldName newName: aNewName [ - | aSelectedPackageOrTag | - - packagesList selectedNodeDo: [ :aNode | - aSelectedPackageOrTag := aNode value ]. - - self updatePackageLists. - aSelectedPackageOrTag ifNotNil: [ - self selectPackageOrTag: aSelectedPackageOrTag ]. -] - -{ #category : #'api - package reselections' } -GtCoderNavigationPackagesTagsClassesElement >> reselectPackageOrTag: aSelectedPackageOrTag afterRemovalOfPackageNamed: aRemovedPackageOrTagName [ - aSelectedPackageOrTag ifNotNil: [ - self deselectPackages. - aSelectedPackageOrTag name = aRemovedPackageOrTagName - ifTrue: [ - self hideClassList ] - ifFalse: [ - self selectPackageOrTag: aSelectedPackageOrTag ] ] -] - -{ #category : #'api - package reselections' } -GtCoderNavigationPackagesTagsClassesElement >> reselectPackageOrTag: aSelectedPackageOrTag afterRemovalOfTagNamed: aRemovedTagName [ - aSelectedPackageOrTag ifNotNil: [ - self deselectPackages. - aSelectedPackageOrTag name = aRemovedTagName - ifTrue: [ - self expandPackage: aSelectedPackageOrTag package. - self hideClassList. ] - ifFalse: [ - self selectPackageOrTag: aSelectedPackageOrTag ] ] -] - -{ #category : #'api - class selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectClass: aClass inPackage: aPackage tag: aPackageTag [ - (self selectedPackage ~= aPackage - or: [ self selectedPackageTag ~= aPackageTag ]) - ifTrue: [ self selectPackage: aPackage tag: aPackageTag ]. - self selectClass: aClass -] - -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectPackage: aPackage [ - | aPreviousIndex | - aPreviousIndex := packagesList selectedIndice. - packagesList viewModel - indexOf: aPackage - do: [ :aNewIndex | - packagesList - selectOne: aNewIndex; - scrollToIndex: - (self - scrollIndexFromPrevious: aPreviousIndex - current: aNewIndex - max: packagesList viewModel itemCount) ]. - self updateClassLists. - self hideOrShowClassList -] - -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectPackage: aPackage tag: aPackageTag [ - | aPreviousIndex | - (self hasPackageTagsIn: aPackage) - ifFalse: [ ^ self selectPackage: aPackage ]. - self expandPackage: aPackage. - aPreviousIndex := packagesList selectedIndice. - packagesList viewModel - indexOf: aPackageTag - do: [ :aNewIndex | - packagesList - selectOne: aNewIndex; - scrollToIndex: - (self - scrollIndexFromPrevious: aPreviousIndex - current: aNewIndex - max: packagesList viewModel itemCount) ]. - self updateClassLists. - self hideOrShowClassList -] - -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectPackageOrTag: aPackageOrTag [ - (aPackageOrTag isKindOf: RPackage) - ifTrue: [ self selectPackage: aPackageOrTag ] - ifFalse: [ self selectPackage: aPackageOrTag package tag: aPackageOrTag ] -] - -{ #category : #'api - class selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectedClass [ - - ^ classesList selectedClass -] - -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectedPackage [ - - ^ packagesList selectedPackage -] - -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectedPackageNodeDo: aBlock [ - ^ packagesList selectedPackageNodeDo: aBlock -] - -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectedPackageTag [ - - ^ packagesList selectedPackageTag -] - -{ #category : #'api - package selections' } -GtCoderNavigationPackagesTagsClassesElement >> selectedPackageTagNodeDo: aBlock [ - ^ packagesList selectedPackageTagNodeDo: aBlock -] - -{ #category : #'showing / hiding' } -GtCoderNavigationPackagesTagsClassesElement >> showClassList [ - classAndMethodProtocolList visibility: BlVisibility visible. - classesLabel visibility: BlVisibility visible. - self layout columnCount: 2. -] - -{ #category : #subscriptions } -GtCoderNavigationPackagesTagsClassesElement >> subscribeToContent [ - super subscribeToContent. - self subscribeToPackageList. - self subscribeToClassList. - self subscribeToProtocolList. -] - -{ #category : #subscriptions } -GtCoderNavigationPackagesTagsClassesElement >> subscribeToNavigationModel [ - self hasNavigationModel ifFalse: [ ^ self ]. - super subscribeToNavigationModel. - navigationModel when: GtCoderNavigationPackagesSelected send: #onPackagesSelected: to: self. - navigationModel when: GtCoderNavigationPackageSelected send: #onPackageSelected: to: self. - navigationModel when: GtCoderNavigationPackageTagSelected send: #onPackageTagSelected: to: self. - navigationModel when: GtCoderNavigationPackageRegistered send: #onPackageRegistered: to: self. - navigationModel when: GtCoderNavigationPackageUnregistered send: #onPackageUnregistered: to: self. - navigationModel when: GtCoderNavigationPackageRenamed send: #onPackageRenamed: to: self. - navigationModel when: GtCoderNavigationPackageTagAdded send: #onPackageTagAdded: to: self. - navigationModel when: GtCoderNavigationPackageTagRemoved send: #onPackageTagRemoved: to: self. - navigationModel when: GtCodersFiltersChanged send: #onMethodsCoderFiltersChanged: to: self. -] - -{ #category : #subscriptions } -GtCoderNavigationPackagesTagsClassesElement >> subscribeToPackageList [ - packagesList - when: BrSelectionChanged - do: [ :anEvent | self onPackageListSelectionChanged ]. -] - -{ #category : #subscriptions } -GtCoderNavigationPackagesTagsClassesElement >> subscribeToProtocolList [ - methodProtocolsList - when: BrSelectionChanged - do: [ :anEvent | self onProtocolListSelectionChanged ]. -] - -{ #category : #'updating lists' } -GtCoderNavigationPackagesTagsClassesElement >> updateClassLists [ - self hasNavigationModel ifFalse: [ ^ self ]. - classesList initializeWithClasses: navigationModel classesToShow. - - navigationModel hasSelectedClass - ifTrue: [ - | methodProtocols | - methodProtocols := navigationModel selectedClass organization protocols. - methodProtocolsList initializeWithProtocols: methodProtocols. - protocolLabel visibility: BlVisibility visible. - methodProtocolsList visibility: BlVisibility visible. ] - ifFalse: [ - methodProtocolsList initializeWithProtocols: Array empty. - protocolLabel visibility: BlVisibility gone. - methodProtocolsList visibility: BlVisibility gone. ] +{ #category : #'private - accessing' } +GtCoderNavigationPackagesTagsClassesElement >> selectedProtocol [ + ^ self navigationModel selectedProtocol ] { #category : #'updating lists' } -GtCoderNavigationPackagesTagsClassesElement >> updateContent [ - self updatePackageAndClassLists. - self updateSelectedPackageAndTag. - self updateSelectedClass. - self hideOrShowClassList +GtCoderNavigationPackagesTagsClassesElement >> updateProtocolList [ + methodProtocolsList updateProtocolsList ] { #category : #'updating lists' } -GtCoderNavigationPackagesTagsClassesElement >> updatePackageAndClassLists [ - self updatePackageLists. - self updateClassLists. -] - -{ #category : #'updating lists' } -GtCoderNavigationPackagesTagsClassesElement >> updatePackageLists [ +GtCoderNavigationPackagesTagsClassesElement >> updateSelections [ self hasNavigationModel ifFalse: [ ^ self ]. - packagesList initializeWithPackages: navigationModel packagesToShow. + navigationModel hasSelectedPackage + ifTrue: [ navigationModel hasSelectedClass + ifTrue: [ self + selectClass: navigationModel selectedClass + inPackage: navigationModel selectedPackage + tag: navigationModel selectedTag ] + ifFalse: [ self + selectPackage: navigationModel selectedPackage + tag: navigationModel selectedTag ] ] ] { #category : #'updating lists' } -GtCoderNavigationPackagesTagsClassesElement >> updateSelectedPackageAndTag [ - self hasNavigationModel ifFalse: [ ^ self ]. - navigationModel hasSelectedPackage - ifTrue: [ navigationModel hasSelectedTag - ifTrue: [ - self - selectPackage: navigationModel selectedPackage - tag: navigationModel selectedTag ] ] - ifFalse: [ - self selectPackage: navigationModel selectedPackage ]. +GtCoderNavigationPackagesTagsClassesElement >> updateSlotList [ + slotsGroupList updateSlotList ] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationPackagesTreeElement.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationPackagesTreeElement.class.st deleted file mode 100644 index 0be67b322..000000000 --- a/src/GToolkit-Coder-UI/GtCoderNavigationPackagesTreeElement.class.st +++ /dev/null @@ -1,108 +0,0 @@ -" -I show a simple list of packages sorting alphabetically - - -" -Class { - #name : #GtCoderNavigationPackagesTreeElement, - #superclass : #BrSimpleTree, - #category : #'GToolkit-Coder-UI-Navigation' -} - -{ #category : #'instance creation' } -GtCoderNavigationPackagesTreeElement class >> fromPackages: aCollectionOfPackages [ - ^ self new initializeWithPackages: aCollectionOfPackages -] - -{ #category : #'private - enumeration' } -GtCoderNavigationPackagesTreeElement >> childrenForPackageOrTag: aPackageOrTag [ - | theTags anExtension | - (aPackageOrTag isKindOf: RPackage) ifFalse: [ ^ #() ]. - theTags := aPackageOrTag classTags asOrderedCollection. - theTags withIndexDo: [ :eachTag :eachIndex | eachTag name = aPackageOrTag name ifTrue: [ - theTags at: eachIndex put: (GtCoderPackageUncategorizedTag forPackageTag: eachTag) ] ]. - theTags sort: [ :tagA :tagB | self isTag: tagA lessThan: tagB ]. - - anExtension := GtCoderPackageExtentionTag forPackage: aPackageOrTag. - anExtension hasExtendedClasses ifTrue: [ - theTags addLast: anExtension ]. - "Do not return an only tag of the same name as the package" - (theTags size = 1 and: [ - theTags anyOne class = GtCoderPackageUncategorizedTag ]) ifTrue: [ ^ #() ]. - - ^ theTags -] - -{ #category : #initialization } -GtCoderNavigationPackagesTreeElement >> initialize [ - super initialize. - - self rowStencil: BrGlamorousSimpleTreeSelectableRowElementStencilBuilder new. - - self nodeStencil: [ - BrLabel new - beSmallSize; - aptitude: BrGlamorousListLabelAptitude ]; - nodeDataBinder: [ :aPackageElement :aPackage | - aPackageElement - text: (aPackage - ifNil: [ '' ] - ifNotNil: [ aPackage name ]) ] -] - -{ #category : #'api - initialization' } -GtCoderNavigationPackagesTreeElement >> initializeWithPackages: aCollectionOfPackages [ - | theSortedPackages | - theSortedPackages := aCollectionOfPackages asArray - sorted: - [ :packageA :packageB | packageA name asLowercase < packageB name asLowercase ]. - self - items: theSortedPackages - lazy: - [ :eachPackageOrTag | self childrenForPackageOrTag: eachPackageOrTag ] -] - -{ #category : #'private - testing' } -GtCoderNavigationPackagesTreeElement >> isTag: tagA lessThan: tagB [ - tagA class = GtCoderPackageUncategorizedTag ifTrue: [ ^ true ]. - tagB class = GtCoderPackageUncategorizedTag ifTrue: [ ^ false ]. - ^ tagA name < tagB name - -] - -{ #category : #'api - selection' } -GtCoderNavigationPackagesTreeElement >> selectedIndice [ - "Return selected indice or zero" - - self selectedIndices - ifNotEmpty: [ :theIndices | - (theIndices first between: 1 and: self viewModel itemCount) - ifTrue: [ ^ theIndices first ] ]. - ^ 0 -] - -{ #category : #'api - selection' } -GtCoderNavigationPackagesTreeElement >> selectedPackage [ - - ^ self selectedPackageNodeDo: #value -] - -{ #category : #'api - selection' } -GtCoderNavigationPackagesTreeElement >> selectedPackageNodeDo: aBlock [ - self selectedNodeDo: [ :aNode | aNode depth = 0 - ifTrue: [ ^ aBlock cull: aNode ] ]. - ^ nil -] - -{ #category : #'api - selection' } -GtCoderNavigationPackagesTreeElement >> selectedPackageTag [ - - ^ self selectedPackageTagNodeDo: #value -] - -{ #category : #'api - selection' } -GtCoderNavigationPackagesTreeElement >> selectedPackageTagNodeDo: aBlock [ - self selectedNodeDo: [ :aNode | aNode depth = 1 - ifTrue: [ ^ aBlock cull: aNode ] ]. - ^ nil -] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationTabsStencil.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationTabsStencil.class.st index 0a32a2f91..abc8d03fb 100644 --- a/src/GToolkit-Coder-UI/GtCoderNavigationTabsStencil.class.st +++ b/src/GToolkit-Coder-UI/GtCoderNavigationTabsStencil.class.st @@ -3,24 +3,86 @@ Class { #superclass : #BrStencil, #instVars : [ 'navigations', - 'navigationModel' + 'navigationModel', + 'formCache', + 'pragmaName', + 'classOfInterest', + 'packageOfInterest', + 'packageTagOfInterest' + ], + #classVars : [ + 'DisplayFullDropdownNavigation' ], #category : #'GToolkit-Coder-UI-Navigation' } +{ #category : #configuration } +GtCoderNavigationTabsStencil class >> beBasicDropdownNavigation [ + DisplayFullDropdownNavigation := false +] + +{ #category : #configuration } +GtCoderNavigationTabsStencil class >> beFullDropdownNavigation [ + DisplayFullDropdownNavigation := true +] + +{ #category : #initialization } +GtCoderNavigationTabsStencil class >> initialize [ + self beFullDropdownNavigation +] + +{ #category : #testing } +GtCoderNavigationTabsStencil class >> isFullDropdownNavigation [ + ^ DisplayFullDropdownNavigation ifNil: [ true ] +] + +{ #category : #'gt-extensions' } +GtCoderNavigationTabsStencil >> addButtonsTo: explicitView [ + explicitView + actionStencil: [ :aButton | + GtSpotterDropdownButtonStencil new + valuable: (GtCoderSpotterStart new navigationModelBlock: [ self navigationModel ]); + tooltip: 'Search Code'; + objectActOn: [ :anActOnEvent :anItem :theButton | + | acted | + acted := false. + (anItem isPharoPackageModel) + ifTrue: [ self navigationModel selectPackage: anItem. + acted := true ]. + (anItem isKindOf: ClassDescription) + ifTrue: [ self navigationModel selectClass: anItem. + acted := true ]. + (anItem isKindOf: CompiledMethod) + ifTrue: [ self navigationModel selectMethod: anItem. + acted := true ]. + acted ifTrue: [ anActOnEvent beActed ] ]; + id: GtNavigationSpotterDropdownId; + asElement ] + tooltip: 'Search Code'. + explicitView + actionDropdownButtonIcon: BrGlamorousVectorIcons add + tooltip: 'Add class or package' + content: [ :aDropdown | + | element | + element := BlElement new. + element + constraintsDo: [ :c | + c horizontal exact: 400. + c vertical exact: 300 ]. + element addChild: (self gtCreationInterface: aDropdown). + aDropdown + when: GtNavigationDropDownCreateWish + do: [ :aWish | element children first selectTabWithLabelString: aWish tabName ]. + element ] + id: GtNavigationNewDropdownId +] + { #category : #adding } GtCoderNavigationTabsStencil >> addNavigation: aNavigationElement [ self assertNavigationView: aNavigationElement. self navigations: (self navigations copyWith: aNavigationElement) ] -{ #category : #asserting } -GtCoderNavigationTabsStencil >> assertNavigationModel: aGtCoderNavigationPackagesTagsClassesModel [ - self - assert: [ aGtCoderNavigationPackagesTagsClassesModel isNotNil ] - description: [ 'Navigation model must be non-nil' ]. -] - { #category : #asserting } GtCoderNavigationTabsStencil >> assertNavigationView: aNavigationView [ self @@ -28,6 +90,38 @@ GtCoderNavigationTabsStencil >> assertNavigationView: aNavigationView [ description: [ 'Navigation must be kind of ', GtPhlowView name ]. ] +{ #category : #navigation } +GtCoderNavigationTabsStencil >> classOfInterest [ + ^ classOfInterest +] + +{ #category : #'as yet unclassified' } +GtCoderNavigationTabsStencil >> classOfInterest: aClass [ + "Consider this class instead of the navigation model selected class." + + classOfInterest := aClass +] + +{ #category : #navigation } +GtCoderNavigationTabsStencil >> consideredClassDo: aBlock [ + self classOfInterest ifNotNil: [ :aClass | ^ aBlock value: aClass ]. + ^ navigationModel selectedClassDo: aBlock +] + +{ #category : #navigation } +GtCoderNavigationTabsStencil >> consideredPackageDo: aBlock [ + self packageOfInterest ifNotNil: [ :aPackage | ^ aBlock value: aPackage ]. + self classOfInterest ifNotNil: [ :aClass | ^ aBlock value: aClass package ]. + ^ navigationModel selectedPackageDo: aBlock +] + +{ #category : #navigation } +GtCoderNavigationTabsStencil >> consideredPackageTagDo: aBlock [ + self packageTagOfInterest ifNotNil: [ :aPackageTag | ^ aBlock value: aPackageTag ]. + self classOfInterest ifNotNil: [ :aClass | ^ aBlock value: aClass packageTag ]. + ^ navigationModel selectedTagDo: aBlock +] + { #category : #'api - instantiation' } GtCoderNavigationTabsStencil >> create [ @@ -35,7 +129,7 @@ GtCoderNavigationTabsStencil >> create [ views: self navigations; asElementDo: [ :aTabGroup | aTabGroup - background: BrGlamorousColors neutralBackgroundColor; + background: BlTheme default default navigationBackground; matchParent ] ] @@ -44,29 +138,144 @@ GtCoderNavigationTabsStencil >> defaultNavigations [ ^ GtPhlowViewsCollector new fromObject: self; - pragmaName: #gtCoderNavigation; + pragmaName: self pragmaName; collect ] +{ #category : #defaults } +GtCoderNavigationTabsStencil >> defaultPragmaName [ + ^ #gtCoderNavigation +] + +{ #category : #accessing } +GtCoderNavigationTabsStencil >> formCache [ + ^ formCache +] + +{ #category : #accessing } +GtCoderNavigationTabsStencil >> formCache: anObject [ + formCache := anObject +] + +{ #category : #'gt-extensions' } +GtCoderNavigationTabsStencil >> gtClassDropdownHierarchyFor: aView [ + + | explicitView | + self navigationModel ifNil: [ ^ aView empty ]. + explicitView := aView explicit + title: 'Class Hierarchy'; + priority: 2; + stencil: [ self isFullDropdownNavigation + ifTrue: [ GtCoderNavigationClassHierarchyElement new + navigationModel: self navigationModel ] + ifFalse: [ GtCoderNavigationBasicClassHierarchyElement new + navigationModel: self navigationModel ] ]. + self addButtonsTo: explicitView. + ^ explicitView +] + { #category : #'gt-extensions' } GtCoderNavigationTabsStencil >> gtClassHierarchyFor: aView [ + | explicitView | self navigationModel ifNil: [ ^ aView empty ]. - ^ aView explicit - title: 'Class Hierarchy'; - priority: 2; - stencil: [ GtCoderNavigationClassHierachyElement new navigationModel: self navigationModel ] + explicitView := aView explicit + title: 'Class Hierarchy'; + priority: 2; + stencil: [ GtCoderNavigationClassHierarchyElement new + navigationModel: self navigationModel ]. + self addButtonsTo: explicitView. + ^ explicitView +] + +{ #category : #'gt-extensions' } +GtCoderNavigationTabsStencil >> gtCreationInterface: anElement [ + | contentTabs tabClasses | + contentTabs := BrTabGroup new. + contentTabs aptitude: BrGlamorousTabGroupAptitude new. + contentTabs + constraintsDo: [ :c | + c horizontal matchParent. + c vertical matchParent ]. + tabClasses := (GtCreationForm allSubclasses + select: [ :aClass | + (aClass isAbstract or: [ aClass hasAbstractMethods ]) not + and: [ aClass isAdditionForm ] ]) + asSortedCollection: [ :a :b | a priority < b priority ]. + tabClasses + do: [ :eachTabClass | + contentTabs + addTab: (BrTab new + aptitude: BrGlamorousTabAptitude new; + label: eachTabClass componentName; + addAptitude: (GtPhlowBrowseDefinitionAptitude new definition: [ eachTabClass ]); + stencil: [ (self formCache + at: eachTabClass + ifAbsentPut: [ | form | + form := eachTabClass new + onReset: [ BlTaskAction + enqueueElement: anElement + action: [ anElement fireEvent: BrDropdownHideWish new. + self formCache removeKey: eachTabClass ] ]. + self consideredClassDo: [ :aClass | form fillFormWithClass: aClass ]. + form hasPackageAndTag + ifTrue: [ self + consideredPackageDo: [ :aPackage | form packageAndTagName packageName: aPackage name ]. + self + consideredPackageTagDo: [ :aTag | + aTag isPharoPackageTagModel + ifTrue: [ aTag isRoot ifFalse: [ form packageAndTagName tagName: aTag name ] ] + ifFalse: [ aTag isUncategorized ifFalse: [ form packageAndTagName tagName: aTag name ] ] ] ]. + ((GtCreationFormWidget forForm: form) + onAccept: [ :anInstance | + BlTaskAction + enqueueElement: anElement + action: [ form selectInNavigationModel: navigationModel anInstance: anInstance. + anElement fireEvent: BrDropdownHideWish new ]. + self formCache removeKey: eachTabClass ifAbsent: [ ] ]) asElement ]) + removeFromParent ]) ]. + ^ contentTabs +] + +{ #category : #'gt-extensions' } +GtCoderNavigationTabsStencil >> gtPackagesTagsClassesDropdownFor: aView [ + + | explicitView | + self navigationModel ifNil: [ ^ aView empty ]. + explicitView := aView explicit + title: 'Package Hierarchy'; + priority: 1; + stencil: [ self isFullDropdownNavigation + ifTrue: [ GtCoderNavigationPackagesTagsClassesElement new + navigationModel: self navigationModel ] + ifFalse: [ GtCoderNavigationBasicPackagesTagsClassesElement new + navigationModel: self navigationModel ] ]. + self addButtonsTo: explicitView. + ^ explicitView ] { #category : #'gt-extensions' } GtCoderNavigationTabsStencil >> gtPackagesTagsClassesFor: aView [ + | explicitView | self navigationModel ifNil: [ ^ aView empty ]. - ^ aView explicit - title: 'Package Hierarchy'; - priority: 1; - stencil: [ GtCoderNavigationPackagesTagsClassesElement new - navigationModel: self navigationModel ] + explicitView := aView explicit + title: 'Package Hierarchy'; + priority: 1; + stencil: [ GtCoderNavigationPackagesTagsClassesElement new + navigationModel: self navigationModel ]. + self addButtonsTo: explicitView. + ^ explicitView +] + +{ #category : #accessing } +GtCoderNavigationTabsStencil >> initialize [ + formCache := Dictionary new. +] + +{ #category : #testing } +GtCoderNavigationTabsStencil >> isFullDropdownNavigation [ + ^ self class isFullDropdownNavigation ] { #category : #accessing } @@ -78,7 +287,6 @@ GtCoderNavigationTabsStencil >> navigationModel [ { #category : #accessing } GtCoderNavigationTabsStencil >> navigationModel: aGtCoderNavigationPackagesTagsClassesModel [ - self assertNavigationModel: aGtCoderNavigationPackagesTagsClassesModel. navigationModel := aGtCoderNavigationPackagesTagsClassesModel. ] @@ -91,3 +299,37 @@ GtCoderNavigationTabsStencil >> navigations [ GtCoderNavigationTabsStencil >> navigations: anArrayOfPhlowViews [ navigations := anArrayOfPhlowViews ] + +{ #category : #navigation } +GtCoderNavigationTabsStencil >> packageOfInterest [ + ^ packageOfInterest +] + +{ #category : #navigation } +GtCoderNavigationTabsStencil >> packageOfInterest: aPackage [ + "Consider this package instead of the navigation model selected package." + + packageOfInterest := aPackage +] + +{ #category : #navigation } +GtCoderNavigationTabsStencil >> packageTagOfInterest [ + ^ packageTagOfInterest +] + +{ #category : #navigation } +GtCoderNavigationTabsStencil >> packageTagOfInterest: aPackageTag [ + "Consider this package tag instead of the navigation model selected package tag." + + packageTagOfInterest := aPackageTag +] + +{ #category : #accessing } +GtCoderNavigationTabsStencil >> pragmaName [ + ^ pragmaName ifNil: [ pragmaName := self defaultPragmaName ] +] + +{ #category : #accessing } +GtCoderNavigationTabsStencil >> pragmaName: anObject [ + pragmaName := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderNavigationTreeLabelAptitude.class.st b/src/GToolkit-Coder-UI/GtCoderNavigationTreeLabelAptitude.class.st new file mode 100644 index 000000000..10d4c3783 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNavigationTreeLabelAptitude.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtCoderNavigationTreeLabelAptitude, + #superclass : #BrGlamorousLabelAptitude, + #category : #'GToolkit-Coder-UI-Looks' +} + +{ #category : #initialization } +GtCoderNavigationTreeLabelAptitude >> initialize [ + super initialize. + + self foreground: self theme label navigationTextForeground +] diff --git a/src/GToolkit-Coder-UI/GtCoderNoEvaluationStatus.class.st b/src/GToolkit-Coder-UI/GtCoderNoEvaluationStatus.class.st new file mode 100644 index 000000000..f4f5ac062 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNoEvaluationStatus.class.st @@ -0,0 +1,17 @@ +" +I am a {{gtClass:GtCoderEvaluationStatus}}. +I represent an evaluation status for a source code that was not executed. + +" +Class { + #name : #GtCoderNoEvaluationStatus, + #superclass : #GtCoderEvaluationStatus, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Coders - Evaluation' +} + +{ #category : #printing } +GtCoderNoEvaluationStatus >> gtDisplayOn: stream [ + stream nextPutAll: 'No evaluation' +] diff --git a/src/GToolkit-Coder-UI/GtCoderNotUserSnippetState.class.st b/src/GToolkit-Coder-UI/GtCoderNotUserSnippetState.class.st new file mode 100644 index 000000000..d178a4f4e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderNotUserSnippetState.class.st @@ -0,0 +1,17 @@ +Class { + #name : #GtCoderNotUserSnippetState, + #superclass : #GtCoderUserSnippetEvaluationState, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Coder - Source Model' +} + +{ #category : #printing } +GtCoderNotUserSnippetState >> gtDisplayOn: stream [ + stream nextPutAll: 'No user snippet' +] + +{ #category : #accessing } +GtCoderNotUserSnippetState >> isUserSnippet [ + ^ false +] diff --git a/src/GToolkit-Coder-UI/GtCoderPackageTagTarget.class.st b/src/GToolkit-Coder-UI/GtCoderPackageTagTarget.class.st new file mode 100644 index 000000000..594cbf473 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderPackageTagTarget.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderPackageTagTarget, + #superclass : #GtPhlowActionUniqueTarget, + #category : #'GToolkit-Coder-UI-Phlow Targets' +} diff --git a/src/GToolkit-Coder-UI/GtCoderPackageTarget.class.st b/src/GToolkit-Coder-UI/GtCoderPackageTarget.class.st new file mode 100644 index 000000000..e919d0c86 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderPackageTarget.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderPackageTarget, + #superclass : #GtPhlowActionUniqueTarget, + #category : #'GToolkit-Coder-UI-Phlow Targets' +} diff --git a/src/GToolkit-Coder-UI/GtCoderPackagesTarget.class.st b/src/GToolkit-Coder-UI/GtCoderPackagesTarget.class.st new file mode 100644 index 000000000..fe68c85b2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderPackagesTarget.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderPackagesTarget, + #superclass : #GtPhlowActionUniqueTarget, + #category : #'GToolkit-Coder-UI-Phlow Targets' +} diff --git a/src/GToolkit-Coder-UI/GtCoderPackagesTreeElement.class.st b/src/GToolkit-Coder-UI/GtCoderPackagesTreeElement.class.st new file mode 100644 index 000000000..c9f2274a2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderPackagesTreeElement.class.st @@ -0,0 +1,276 @@ +" +I show a simple list of packages sorting alphabetically + + +" +Class { + #name : #GtCoderPackagesTreeElement, + #superclass : #BrSimpleTree, + #traits : 'TGtCoderNavigationWithContextMenu', + #classTraits : 'TGtCoderNavigationWithContextMenu classTrait', + #instVars : [ + 'packageToRename', + 'renamingEditor' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #'instance creation' } +GtCoderPackagesTreeElement class >> fromPackages: aCollectionOfPackages [ + ^ self new initializeWithPackages: aCollectionOfPackages +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> buildRemovePackageLabelFor: aPackage [ + | labelText | + labelText := 'Remove package <1s>' expandMacrosWith: aPackage name. + ^ BrLabel new + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: labelText asRopedText +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> buildRemoveTagLabelFor: aTag [ + | labelText | + labelText := 'Remove package tag <1s>' expandMacrosWith: aTag name. + ^ BrLabel new + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: labelText asRopedText +] + +{ #category : #'private - enumeration' } +GtCoderPackagesTreeElement >> childrenForPackageOrTag: aPackageOrTag [ + | theTags anExtension | + (aPackageOrTag isPharoPackageModel) ifFalse: [ ^ #() ]. + + theTags := aPackageOrTag tags asOrderedCollection. + theTags withIndexDo: [ :eachTag :eachIndex | + eachTag isRoot ifTrue: [ + theTags + at: eachIndex + put: (GtCoderPackageUncategorizedTag forPackageTag: eachTag) ] ]. + theTags sort: [ :tagA :tagB | self isTag: tagA lessThan: tagB ]. + + anExtension := GtCoderPackageExtensionTag forPackage: aPackageOrTag. + anExtension hasExtendedClasses ifTrue: [ + theTags addLast: anExtension ]. + + "Do not return an only root tag that contains all classes" + (theTags size = 1 and: [ + theTags anyOne class = GtCoderPackageUncategorizedTag ]) ifTrue: [ ^ #() ]. + + ^ theTags +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> contextMenuFor: aPackageOrTag [ + + ^ aPackageOrTag isPharoPackageModel + ifTrue: [ GtCoderPackageTarget menuItemsForObject: aPackageOrTag hostElement: self ] + ifFalse: [ GtCoderPackageTagTarget menuItemsForObject: aPackageOrTag hostElement: self ] +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> createPackageOrTagDropHandler [ + ^ BlDropHandler new + acceptItemsSuchThat: [ :aDragItem :aGtCoderPackagesTreeItemElement | + aDragItem domainObject isClassOrTrait + and: [ (aGtCoderPackagesTreeItemElement packageOrTag + isKindOf: GtCoderPackageExtensionTag) not + and: [ | aPackageOrTag | + aPackageOrTag := aDragItem domainObject package + tagOf: aDragItem domainObject. + aPackageOrTag isRoot + ifTrue: [ aPackageOrTag := GtCoderPackageUncategorizedTag forPackageTag: aPackageOrTag ]. + + aPackageOrTag isRoot + ifFalse: [ aGtCoderPackagesTreeItemElement packageOrTag ~= aPackageOrTag ] + ifTrue: [ aGtCoderPackagesTreeItemElement packageOrTag ~= aPackageOrTag + and: [ aPackageOrTag package ~= aGtCoderPackagesTreeItemElement packageOrTag ] ] ] ] ]; + whenDroppedDo: [ :anItemsDroppedEvent | self onDropClassesOnPackageOrTag: anItemsDroppedEvent ]; + whenDragEnteredDo: [ :anItemsDraggedOverEvent | + anItemsDraggedOverEvent mayAcceptItems + ifTrue: [ anItemsDraggedOverEvent currentTarget + effect: (BlOverlayAboveEffect new paint: (Color gray alpha: 0.2)) ] ]; + whenDragLeftDo: [ :anItemsLeftEvent | anItemsLeftEvent currentTarget effect: BlNullEffect new ] +] + +{ #category : #'private - context menu' } +GtCoderPackagesTreeElement >> createRenamePackageForm: aPackageOrTag [ + ^ GtCoderPackagesTreeRenameEditor new + itemToRename: aPackageOrTag; + renameAction: [ :anItemToRename :aNewName | + (anItemToRename isPharoPackageModel) + ifTrue: [ + (GtRBRenamePackageRefactoring + from: anItemToRename name + to: aNewName) + execute ]. + (anItemToRename isPharoPackageTagModel) + ifTrue: [ + (GtRBRenamePackageTagRefactoring + packageName: anItemToRename package name + from: anItemToRename name + to: aNewName) + execute ] ]; + endRenameAction: [ self privateEndRenamePackage ] +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> demotePackage: aPackageOrTag inElement: elem [ + self notYetImplemented +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> initialize [ + super initialize. + + self rowStencil: BrGlamorousSimpleTreeSelectableRowElementStencilBuilder new. + + self + nodeType: [ :aTypeFactory :aTreeNode | aTreeNode value = packageToRename ifTrue: [ #rename ] ifFalse: [ #display ] ]; + nodeStencil: [ :anItemType | + anItemType = #rename + ifTrue: [ BrFrame new + id: #'coder--package-rename'; + hMatchParent; + vFitContent ] + ifFalse: [ | anItemElement | + anItemElement := GtCoderPackagesTreeItemElement new. + anItemElement + hMatchParent; + vFitContent; + addEventHandler: self createPackageOrTagDropHandler; + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude + menu: [ self contextMenuFor: anItemElement packageOrTag ]) ] ]; + nodeDataBinder: [ :aPackageElement :aPackage | + aPackageElement id asSymbol = #'coder--package-rename' + ifTrue: [ aPackageElement removeChildren. + renamingEditor + ifNil: [ renamingEditor := self createRenamePackageForm: aPackage ]. + aPackageElement addChild: renamingEditor ] + ifFalse: [ aPackageElement packageOrTag: aPackage ] ] +] + +{ #category : #'api - initialization' } +GtCoderPackagesTreeElement >> initializeWithPackages: aCollectionOfPackages [ + | theSortedPackages | + theSortedPackages := aCollectionOfPackages asArray + sorted: + [ :packageA :packageB | packageA name asLowercase < packageB name asLowercase ]. + self + items: theSortedPackages + lazy: + [ :eachPackageOrTag | self childrenForPackageOrTag: eachPackageOrTag ] +] + +{ #category : #'private - testing' } +GtCoderPackagesTreeElement >> isTag: tagA lessThan: tagB [ + tagA class = GtCoderPackageUncategorizedTag ifTrue: [ ^ true ]. + tagB class = GtCoderPackageUncategorizedTag ifTrue: [ ^ false ]. + ^ tagA name < tagB name + +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> newPackage [ + | dropdown tabGroup | + tabGroup := self + withAllParentsDetect: [ :each | each class == BrTabGroup ] + ifFound: #yourself + ifNone: [ ^self ]. + dropdown := tabGroup childWithId: GtNavigationNewDropdownId. + dropdown dispatchEvent: BrDropdownShowWish new. + dropdown dispatchEvent: (GtNavigationDropDownCreateWish showTab: GtPackageCreationForm). +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> newTagFor: aPackage inElement: elem [ + self notYetImplemented +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> onDropClassesOnPackageOrTag: anItemsDroppedEvent [ + | aPackageOrTag aModel compositeChange someRefactorings aPromise | + aPackageOrTag := anItemsDroppedEvent currentTarget packageOrTag. + aModel := Smalltalk createRbNamespace + onEnvironment: GtRBPushDownMethodRefactoring new defaultEnvironment. + + someRefactorings := anItemsDroppedEvent items + collect: [ :eachDragItem | + | eachClass | + eachClass := eachDragItem domainObject. + + GtRBMoveClassToPackageRefactoring + model: aModel + move: eachClass name + to: aPackageOrTag categoryName ]. + + someRefactorings do: #primitiveExecute. + compositeChange := aModel changes. + + "We use 1 threshold to keep coders with the same selected package." + aPromise := compositeChange gtExecuteWithNotificationThreshold: 1. + self enqueueTask: (BlPromiseTask new promise: aPromise). +] + +{ #category : #'private - context menu' } +GtCoderPackagesTreeElement >> privateEndRenamePackage [ + "Removes package editing UI" + + packageToRename := nil. + renamingEditor := nil. + self viewModel notifyChildrenChanged +] + +{ #category : #initialization } +GtCoderPackagesTreeElement >> promoteTag: aPackageOrTag inElement: elem [ + self notYetImplemented +] + +{ #category : #'private - context menu' } +GtCoderPackagesTreeElement >> requestRenamePackage: aPackageOrTag [ + "An entrance point to the rename package action. + Presents a UI to rename a given package" + + packageToRename := aPackageOrTag. + renamingEditor := nil. + self viewModel notifyChildrenChanged +] + +{ #category : #'api - selection' } +GtCoderPackagesTreeElement >> selectedIndice [ + "Return selected indice or zero" + + self selectedIndices + ifNotEmpty: [ :theIndices | + (theIndices first between: 1 and: self viewModel itemCount) + ifTrue: [ ^ theIndices first ] ]. + ^ 0 +] + +{ #category : #'api - selection' } +GtCoderPackagesTreeElement >> selectedPackage [ + + ^ self selectedPackageNodeDo: #value +] + +{ #category : #'api - selection' } +GtCoderPackagesTreeElement >> selectedPackageNodeDo: aBlock [ + self selectedNodeDo: [ :aNode | aNode depth = 0 + ifTrue: [ ^ aBlock cull: aNode ] ]. + ^ nil +] + +{ #category : #'api - selection' } +GtCoderPackagesTreeElement >> selectedPackageTag [ + + ^ self selectedPackageTagNodeDo: #value +] + +{ #category : #'api - selection' } +GtCoderPackagesTreeElement >> selectedPackageTagNodeDo: aBlock [ + self selectedNodeDo: [ :aNode | aNode depth = 1 + ifTrue: [ ^ aBlock cull: aNode ] ]. + ^ nil +] diff --git a/src/GToolkit-Coder-UI/GtCoderPackagesTreeItemElement.class.st b/src/GToolkit-Coder-UI/GtCoderPackagesTreeItemElement.class.st new file mode 100644 index 000000000..a2c9def32 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderPackagesTreeItemElement.class.st @@ -0,0 +1,43 @@ +Class { + #name : #GtCoderPackagesTreeItemElement, + #superclass : #BrHorizontalPane, + #instVars : [ + 'packageOrTag', + 'label' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #initialization } +GtCoderPackagesTreeItemElement >> initialize [ + super initialize. + + label := BrLabel new + matchParent; + text: ''; + beSmallSize; + aptitude: (GtCoderNavigationTreeLabelAptitude new padding: (BlInsets + top: 2 + bottom: 2 + left: 1 + right: 1)). + + self addChild: label +] + +{ #category : #accessing } +GtCoderPackagesTreeItemElement >> packageOrTag [ + ^ packageOrTag +] + +{ #category : #accessing } +GtCoderPackagesTreeItemElement >> packageOrTag: aPackageOrTag [ + packageOrTag = aPackageOrTag + ifTrue: [ ^ self ]. + + packageOrTag := aPackageOrTag. + + packageOrTag + ifNil: [ label text: '' ] + ifNotNil: [ label text: packageOrTag name ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderPackagesTreeRenameEditor.class.st b/src/GToolkit-Coder-UI/GtCoderPackagesTreeRenameEditor.class.st new file mode 100644 index 000000000..c05c60deb --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderPackagesTreeRenameEditor.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtCoderPackagesTreeRenameEditor, + #superclass : #GtCoderListEmbeddedRenameEditorElement, + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #'as yet unclassified' } +GtCoderPackagesTreeRenameEditor >> createCompleterStrategy [ + ^ GtMethodProtocolCompletionStrategy new +] + +{ #category : #'as yet unclassified' } +GtCoderPackagesTreeRenameEditor >> itemLabelOf: aPackage [ + ^ aPackage name +] diff --git a/src/GToolkit-Coder-UI/GtCoderPlaygroundElement.class.st b/src/GToolkit-Coder-UI/GtCoderPlaygroundElement.class.st index 3bf778695..5609d2e3d 100644 --- a/src/GToolkit-Coder-UI/GtCoderPlaygroundElement.class.st +++ b/src/GToolkit-Coder-UI/GtCoderPlaygroundElement.class.st @@ -1,5 +1,5 @@ " -I display {{gtClass:GtCoderContentElement}} and {{gtClass:GtPageElement}}. +I display {{gtClass:GtCoderContentElement}} and an embedded playground. 1. # Example {{gtExample:GtCoderElementExamples>>#playgroundElementWithClass|previewExpanded|codeExpanded=false|previewHeight=700}} @@ -8,11 +8,10 @@ I display {{gtClass:GtCoderContentElement}} and {{gtClass:GtPageElement}}. " Class { #name : #GtCoderPlaygroundElement, - #superclass : #GtCoderNavigationModelElement, + #superclass : #GtAbstractCoderElement, #instVars : [ 'contentElement', - 'playPageElement', - 'playPage' + 'playgroundElement' ], #category : #'GToolkit-Coder-UI-Basic' } @@ -23,15 +22,15 @@ GtCoderPlaygroundElement >> contentElement [ ^ contentElement ] -{ #category : #defaults } -GtCoderPlaygroundElement >> defaultPlayPage [ - ^ GtPlayPage newDefault. +{ #category : #accessing } +GtCoderPlaygroundElement >> disablePlayPage [ + self removeChild: self playPageElement ] { #category : #initialization } GtCoderPlaygroundElement >> initialize [ super initialize. - playPage := self defaultPlayPage. + self initializeContentElement. self initializePlayPageElement. @@ -40,7 +39,9 @@ GtCoderPlaygroundElement >> initialize [ self addChild: self contentElement as: #content. self addChild: self playPageElement as: #playPage. - self addAptitude: (BrLayoutResizerAptitude inherit). + self + addAptitude: (BrLayoutResizerAptitude new + common: [ contentElement ]) ] { #category : #initialization } @@ -50,23 +51,21 @@ GtCoderPlaygroundElement >> initializeContentElement [ { #category : #initialization } GtCoderPlaygroundElement >> initializePlayPageElement [ - | aStencil | - aStencil := GtPlayPageStencil new - playPage: self playPage; - receiver: self receiver. - playPageElement := GtPhlowVerticalSidebarElement new - beBottom; - show; - collapse; - background: self theme status neutralBackgroundColor; - contentStencil: aStencil + playgroundElement := (GtInspector environment + at: GtInspector embeddedPlaygroundName) new. + + playgroundElement collapse. + playgroundElement objectHolder: self objectHolder +] + +{ #category : #'private - accessing' } +GtCoderPlaygroundElement >> objectHolder [ + ^ GtInspectorObject new object: self receiver ] { #category : #'private - hooks' } GtCoderPlaygroundElement >> onClassSelected: anEvent [ - self playPage receiver: anEvent theClass. - self playPage xDocLoadSavedContent. - + self updatePlayground ] { #category : #'private - hooks' } @@ -78,32 +77,23 @@ GtCoderPlaygroundElement >> onNavigationModelChanged [ { #category : #'private - hooks' } GtCoderPlaygroundElement >> onPackageSelected: anEvent [ - self playPage receiver: anEvent package. - self playPage xDocLoadSavedContent. + self inUIProcessDo: [ self updatePlayground ] ] { #category : #'private - hooks' } GtCoderPlaygroundElement >> onPackageTagSelected: anEvent [ - self playPage receiver: anEvent tag. - self playPage xDocLoadSavedContent. + self inUIProcessDo: [ self updatePlayground ] ] { #category : #'private - hooks' } GtCoderPlaygroundElement >> onPackagesSelected: anEvent [ - self playPage receiver: Smalltalk. - self playPage xDocLoadSavedContent. -] - -{ #category : #'private - accessing' } -GtCoderPlaygroundElement >> playPage [ - - ^ playPage + self inUIProcessDo: [ self updatePlayground ] ] { #category : #'private - accessing' } GtCoderPlaygroundElement >> playPageElement [ - ^ playPageElement + ^ playgroundElement ] { #category : #'private - accessing' } @@ -126,6 +116,5 @@ GtCoderPlaygroundElement >> subscribeToNavigationModel [ { #category : #'private - updating' } GtCoderPlaygroundElement >> updatePlayground [ - self playPage receiver: self receiver. - self playPage xDocLoadSavedContent. + self playPageElement objectHolder: self objectHolder ] diff --git a/src/GToolkit-Coder-UI/GtCoderPrintStringAttribute.class.st b/src/GToolkit-Coder-UI/GtCoderPrintStringAttribute.class.st index efa002593..1614397cb 100644 --- a/src/GToolkit-Coder-UI/GtCoderPrintStringAttribute.class.st +++ b/src/GToolkit-Coder-UI/GtCoderPrintStringAttribute.class.st @@ -24,7 +24,7 @@ GtCoderPrintStringAttribute class >> forString: aString [ { #category : #private } GtCoderPrintStringAttribute >> actionButtonLook [ ^ BrGlamorousButtonIconAptitude + BrGlamorousButtonLayoutAptitude + (BrStyleCommonAptitude new - default: [ :aStyle | aStyle geometry: BlCircle new ]; + default: [ :aStyle | aStyle geometry: BlCircleGeometry new ]; hovered: [ :aStyle | aStyle background: BrGlamorousColors neutralBackgroundColor darker ]; pressed: [ :aStyle | aStyle background: BrGlamorousColors neutralBackgroundColor darker darker ]) ] @@ -104,49 +104,67 @@ GtCoderPrintStringAttribute >> copyFromEditorElement: anEditorElement [ { #category : #accessing } GtCoderPrintStringAttribute >> doAffect: aTBrTextEditorTextualPiece in: anEditorElement [ | hamburgerButton | - hamburgerButton := nil. - + ^ BrLabel new - aptitude: (BrGlamorousLabelAptitude new glamorousCodeFont labelDo: [ :aLabel | - aLabel addAptitude: (BrGlamorousWithTooltipAptitude new contentStencil: [ self buildTooltip ]) ]); + aptitude: (BrGlamorousLabelAptitude new glamorousCodeFont + labelDo: [ :aLabel | + aLabel + addAptitude: (BrGlamorousWithExplicitTooltipAptitude content: [ self buildTooltip ]) ]); beTinySize; + id: GtCoderPrintStringElementId; text: object printString; background: BrGlamorousColors neutralBackgroundColor; - margin: (BlInsets top: 0 left: 4 bottom: 0 right: 4); - padding: (BlInsets top: 2 left: 4 bottom: 2 right: 4); + margin: (BlInsets + top: 0 + left: 4 + bottom: 0 + right: 4); + padding: (BlInsets + top: 2 + left: 4 + bottom: 2 + right: 4); geometry: (BlRoundedRectangleGeometry cornerRadius: 2); addAptitude: (BrStyleCommonAptitude new - focused: [ :aStyle | aStyle border: (BlBorder paint: (BrGlamorousColors focusedEditorBorderColor alpha: 0.75) width: 1) ]); + focused: [ :aStyle | + aStyle + border: (BlBorder + paint: (BrGlamorousColors focusedEditorBorderColor alpha: 0.75) + width: 1) ]); requestFocus; addShortcut: (BlShortcutWithAction new - combination: BlKeyCombination primaryC; - action: [ self copyFromEditorElement: anEditorElement ]); + combination: BlKeyCombination primaryC; + action: [ self copyFromEditorElement: anEditorElement ]); addShortcut: (BlShortcutWithAction new - combination: BlKeyCombination primaryI; - action: [ :aShortcutEvent | self spawnFromElement: aShortcutEvent currentTarget ]); + combination: BlKeyCombination primaryI; + action: [ :aShortcutEvent | self spawnFromElement: aShortcutEvent currentTarget ]); addShortcut: (BlShortcutWithAction new - combination: BlKeyCombination return; - action: [ self insertIntoTheEditorElement: anEditorElement ]); + combination: BlKeyCombination return; + action: [ self insertIntoTheEditorElement: anEditorElement ]); addShortcut: (BlShortcutWithAction new - combination: BlKeyCombination backspace; - action: [ self removeFromEditorElement: anEditorElement ]); - when: BlMouseDownEvent do: [ :anEvent | - anEvent consumed: true. - anEvent currentTarget requestFocus ]; - when: BlElementRemovedFromParentEvent do: [ :anEvent | - anEditorElement requestFocus ]; + combination: BlKeyCombination backspace; + action: [ self removeFromEditorElement: anEditorElement ]); + when: BlMouseDownEvent + do: [ :anEvent | + anEvent consumed: true. + anEvent currentTarget requestFocus ]; + when: BlElementRemovedFromParentEvent + do: [ :anEvent | anEditorElement requestFocus ]; addChild: (hamburgerButton := BrButton new - aptitude: self actionButtonLook + (BrGlamorousWithDropdownAptitude - handle: [ BrButton new - aptitude: BrGlamorousButtonIconAptitude + BrGlamorousButtonLayoutAptitude; + aptitude: self actionButtonLook + + (BrGlamorousWithExplicitDropdownAptitude + handle: [ BrButton new + aptitude: BrGlamorousButtonIconAptitude + BrGlamorousButtonLayoutAptitude; + icon: BrGlamorousVectorIcons hamburger; + beSmallSize; + exact: 16 @ 16 ] + content: [ self + buildMenuFor: hamburgerButton + fromEditorElement: anEditorElement ]); icon: BrGlamorousVectorIcons hamburger; - beSmallSize; - exact: 16@16 ] - content: [ self buildMenuFor: hamburgerButton fromEditorElement: anEditorElement ]); - icon: BrGlamorousVectorIcons hamburger; - beTinySize; - exact: 12@12) + beTinySize; + exact: 12 @ 12) ] { #category : #initialization } diff --git a/src/GToolkit-Coder-UI/GtCoderPrintStringElementId.class.st b/src/GToolkit-Coder-UI/GtCoderPrintStringElementId.class.st new file mode 100644 index 000000000..1c3fd9836 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderPrintStringElementId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderPrintStringElementId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Coder - Source Attributes' +} + +{ #category : #converting } +GtCoderPrintStringElementId >> asSymbol [ + ^ #'coder-print-string-result' +] diff --git a/src/GToolkit-Coder-UI/GtCoderProtocolsGroupHeaderElement.class.st b/src/GToolkit-Coder-UI/GtCoderProtocolsGroupHeaderElement.class.st new file mode 100644 index 000000000..24b15fd7d --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderProtocolsGroupHeaderElement.class.st @@ -0,0 +1,151 @@ +Class { + #name : #GtCoderProtocolsGroupHeaderElement, + #superclass : #BrHorizontalPane, + #instVars : [ + 'targetClass', + 'addProtocolButton', + 'protocolLabel' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #'as yet unclassified' } +GtCoderProtocolsGroupHeaderElement >> addProtocolFrom: aProtocolEditor [ + self + addProtocolNamed: aProtocolEditor text asString trimmed + in: targetClass. + + ^ aProtocolEditor fireEvent: BrDropdownHideWish new +] + +{ #category : #'as yet unclassified' } +GtCoderProtocolsGroupHeaderElement >> addProtocolNamed: aProtocolName in: aClass [ + | refactoring | + + aProtocolName ifEmpty: [ ^ self ]. + aClass ifNil: [ ^ self ]. + + refactoring := RBAddProtocolChange + addProtocolNamed: aProtocolName + in: aClass. + + refactoring execute +] + +{ #category : #'as yet unclassified' } +GtCoderProtocolsGroupHeaderElement >> createAddProtocolButton [ + | aButtonStencil aPopUp | + aButtonStencil := [ BrButton new + beTinySize; + aptitude: (BrGlamorousButtonRectangularAptitude paddingScale: 0) + + BrGlamorousButtonIconAptitude; + icon: BrGlamorousVectorIcons add ] asStencil. + + aPopUp := BrGlamorousWithExplicitDropdownAptitude + handle: aButtonStencil + content: [ :aButton | self createNewProtocolForm ]. + + ^ aButtonStencil asElement + addAptitude: (BrGlamorousButtonFlatExteriorAptitude new + backgroundPaint: (Color gray alpha: 0.1)); + addAptitude: BrGlamorousButtonWithLabelTooltipAptitude2; + addAptitude: aPopUp; + label: 'Add new protocol' +] + +{ #category : #'as yet unclassified' } +GtCoderProtocolsGroupHeaderElement >> createNewProtocolForm [ + | aContainer aProtocolEditor aDefaultText aProtocolCompleter anAcceptButton | + aDefaultText := '' asRopedText + attribute: (BrGhostTextAttribute + for: ('new protocol name' asRopedText glamorousFormEditorCodeFontAndSize + foreground: Color lightGray)). + + aContainer := BrHorizontalPane new + padding: (BlInsets + top: 6 + right: 2 + bottom: 6 + left: 2); + cellSpacing: 6; + alignCenterLeft; + fitContent. + + aProtocolEditor := BrEditor new + aptitude: BrGlamorousRegularEditorAptitude new glamorousRegularSmallSize + + BrGlamorousInputFieldSpacingAptitude; + beMode: BrTextEditorEditableSingleLineMode new; + geometry: (BlRoundedRectangleGeometry cornerRadius: 4); + vFitContent; + hFitContent; + constraintsDo: [ :c | c minWidth: 150 ]; + text: aDefaultText; + requestFocus. + + aProtocolCompleter := GtCompletionController + on: aProtocolEditor + strategy: GtMethodProtocolCompletionStrategy new. + + aProtocolCompleter install. + + aProtocolEditor + addEditorShortcut: (BlShortcutWithAction new + combination: BlKeyCombination escape; + action: [ :anEvent | aProtocolEditor fireEvent: BrDropdownHideWish new ]). + + aProtocolEditor + addEditorShortcut: (BlShortcutWithAction new + combination: BlKeyCombination enter; + action: [ :anEvent | self addProtocolFrom: aProtocolEditor ]). + + aContainer addChild: aProtocolEditor. + + anAcceptButton := BrButton new + beSmallSize; + aptitude: (BrGlamorousButtonRectangularAptitude paddingScale: 0.5) + + BrGlamorousButtonIconAptitude + + (BrGlamorousButtonFlatExteriorAptitude new + backgroundPaint: (Color gray alpha: 0.1)) + + BrGlamorousButtonWithLabelTooltipAptitude2; + icon: BrGlamorousVectorIcons accept; + label: 'Add protocol'; + action: [ self addProtocolFrom: aProtocolEditor ]. + + aContainer addChild: anAcceptButton. + + ^ aContainer +] + +{ #category : #'as yet unclassified' } +GtCoderProtocolsGroupHeaderElement >> initialize [ + super initialize. + + self + alignCenterLeft; + hMatchParent; + vFitContent. + + protocolLabel := BrLabel new + beSmallSize + padding: (BlInsets top: 2 bottom: 2); + aptitude: (BrGlamorousLabelAptitude new foreground: Color gray). + + addProtocolButton := self createAddProtocolButton. + self addChildren: { protocolLabel . addProtocolButton } +] + +{ #category : #'as yet unclassified' } +GtCoderProtocolsGroupHeaderElement >> protocolsGroupName: aGroupName targetClass: aClass [ + protocolLabel text: aGroupName asRopedText. + + targetClass := nil. + aClass ifNotNil: [ + aGroupName = 'instance side' + ifTrue: [ targetClass := aClass instanceSide ]. + aGroupName = 'class side' + ifTrue: [ targetClass := aClass classSide ] ]. + + targetClass + ifNil: [ addProtocolButton visibility: BlVisibility gone ] + ifNotNil: [ addProtocolButton visibility: BlVisibility visible ]. +] diff --git a/src/GToolkit-Coder-UI/GtCoderProtocolsGroupRenameEditor.class.st b/src/GToolkit-Coder-UI/GtCoderProtocolsGroupRenameEditor.class.st new file mode 100644 index 000000000..e904dd569 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderProtocolsGroupRenameEditor.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtCoderProtocolsGroupRenameEditor, + #superclass : #GtCoderListEmbeddedRenameEditorElement, + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #'as yet unclassified' } +GtCoderProtocolsGroupRenameEditor >> createCompleterStrategy [ + ^ GtMethodProtocolCompletionStrategy new +] + +{ #category : #'as yet unclassified' } +GtCoderProtocolsGroupRenameEditor >> itemLabelOf: aProtocol [ + ^ aProtocol name +] diff --git a/src/GToolkit-Coder-UI/GtCoderProtocolsGroupedListElement.class.st b/src/GToolkit-Coder-UI/GtCoderProtocolsGroupedListElement.class.st new file mode 100644 index 000000000..bf846c8e5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderProtocolsGroupedListElement.class.st @@ -0,0 +1,494 @@ +" +#Navigation protocol list + +Displays a list of instance and class side protocols as a grouped list with an ability to drag and drop method on a protocol to move or copy a method there. + + +" +Class { + #name : #GtCoderProtocolsGroupedListElement, + #superclass : #BrGroupedList, + #traits : 'TGtCoderNavigationWithContextMenu', + #classTraits : 'TGtCoderNavigationWithContextMenu classTrait', + #instVars : [ + 'protocolGroups', + 'navigationModel', + 'protocolToRename', + 'renamingEditor' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #'private - refactorings' } +GtCoderProtocolsGroupedListElement >> autoCategorizeProtocol: aPharoProtocol [ + | classifier allMethods| + + allMethods := aPharoProtocol methodSelectors collect: [ :selector| + aPharoProtocol protocolClass >> selector ]. + + classifier := MethodClassifier new. + classifier classifyAll: allMethods +] + +{ #category : #'private - instance creation' } +GtCoderProtocolsGroupedListElement >> bindProtocol: item toElement: element [ + | itemText | + + itemText := item name asRopedText , ' ' asRopedText + , (item allMethods size asRopedText + foreground: self theme button disabledTextColor). + + element userData at: #protocol put: item. + ^ element text: itemText +] + +{ #category : #'private - instance creation' } +GtCoderProtocolsGroupedListElement >> contextMenuFor: aPharoProtocol [ + | menu | + menu := BrMenuItems new. + aPharoProtocol name = Protocol unclassified + ifTrue: [ menu + addItemLabel: (self createLabel: 'Auto-categorize methods' description: '') + action: [ self autoCategorizeProtocol: aPharoProtocol ] ]. + aPharoProtocol canBeRenamed + ifTrue: [ menu + addItem: (BrMenuSubmenuItem new + label: (self createLabel: 'Rename' description: aPharoProtocol name); + submenu: (self renameProtocolSubmenuFor: aPharoProtocol); + bePinSubmenuAction) ]. + aPharoProtocol canBeRemoved + ifTrue: [ menu + addItem: (BrMenuSubmenuItem new + label: (self createLabel: 'Remove' description: aPharoProtocol name); + submenu: (self removeProtocolSubmenuFor: aPharoProtocol)) ]. + ^ menu +] + +{ #category : #'private - instance creation' } +GtCoderProtocolsGroupedListElement >> createProtocolDropHandler [ + ^ BlDropHandler new + acceptItemsSuchThat: [ :aDragItem :aBrLabel | + aDragItem domainObject isCompiledMethod + and: [ aBrLabel userData + at: #protocol + ifPresent: [ :aProtocol | + aProtocol isVirtualProtocol not + and: [ aProtocol name ~= aDragItem domainObject protocol ] ] + ifAbsent: [ false ] ] ]; + whenDroppedDo: [ :anItemsDroppedEvent | self onDropCompiledMethodsOnProtocol: anItemsDroppedEvent ]; + whenDragEnteredDo: [ :anItemsDraggedOverEvent | + anItemsDraggedOverEvent mayAcceptItems + ifTrue: [ anItemsDraggedOverEvent currentTarget + effect: (BlOverlayAboveEffect new paint: (Color gray alpha: 0.2)) ] ]; + whenDragLeftDo: [ :anItemsLeftEvent | anItemsLeftEvent currentTarget effect: BlNullEffect new ] +] + +{ #category : #'private - instance creation' } +GtCoderProtocolsGroupedListElement >> createProtocolGroups [ + | virtualGroup instanceGroup classGroup | + + virtualGroup := BrGroup new + domainObject: 'virtual'; + items: #(); + itemType: [ :aTypeFactory :eachPharoProtocol | + eachPharoProtocol = protocolToRename + ifTrue: [ #rename ] + ifFalse: [ #display ] ]; + itemStencil: [ :anItemType | + anItemType = #rename + ifTrue: [ + BrFrame new + id: #'coder--protocol-rename'; + hMatchParent; + vFitContent ] + ifFalse: [ self createProtocolLabelElement ] ]; + itemDataBinder: [ :element :item | + element id asSymbol = #'coder--protocol-rename' + ifTrue: [ + element removeChildren. + renamingEditor ifNil: [ renamingEditor := self createRenameProtocolForm: item ]. + element addChild: renamingEditor ] + ifFalse: [ self bindProtocol: item toElement: element ] ]; + shouldShowWithoutItems: false. + + instanceGroup := virtualGroup copy domainObject: 'instance side'. + classGroup := virtualGroup copy domainObject: 'class side'. + ^ { + virtualGroup. + instanceGroup. + classGroup } +] + +{ #category : #'private - instance creation' } +GtCoderProtocolsGroupedListElement >> createProtocolLabelElement [ + "An element that represents a protocol within a list of protocols" + + + | aLabel aLabelAptitude | + aLabel := BrLabel new. + + aLabelAptitude := GtCoderNavigationTreeLabelAptitude new + padding: (BlInsets + top: 2 + bottom: 2 + left: 1 + right: 1); + add: BrGlamorousListItemAptitude; + add: (BrGlamorousWithExplicitContextMenuAptitude + menu: [ self contextMenuFor: (aLabel userData at: #protocol) ]). + + ^ aLabel + hMatchParent; + vFitContent; + beSmallSize; + beFocusable; + aptitude: aLabelAptitude; + addEventHandler: self createProtocolDropHandler +] + +{ #category : #'private - instance creation' } +GtCoderProtocolsGroupedListElement >> createRenameProtocolForm: aPharoProtocol [ + ^ GtCoderProtocolsGroupRenameEditor new + itemToRename: aPharoProtocol; + renameAction: [ :anItemToRename :aNewName | + self navigationModel + renameProtocol: anItemToRename + to: aNewName ]; + endRenameAction: [ self privateEndRenameProtocol ] +] + +{ #category : #'api - list' } +GtCoderProtocolsGroupedListElement >> deselectProtocol [ + self deselectAll +] + +{ #category : #'private - selection' } +GtCoderProtocolsGroupedListElement >> detectProtocolIndex: aGtPharoProtocol inGroups: aCollectionOfArrayOfProtocols [ + | aSelectionIndex | + + aGtPharoProtocol ifNil: [ ^ 0 ]. + + aSelectionIndex := 0. + + aCollectionOfArrayOfProtocols detect: [ :eachGroup | + eachGroup + ifEmpty: [ false ] + ifNotEmpty: [ + | anIndexInGroup | + "add 1 for the header" + aSelectionIndex := aSelectionIndex + 1. + anIndexInGroup := eachGroup + detectIndex: [ :eachProtocol | eachProtocol = aGtPharoProtocol ] + ifNone: [ 0 ]. + + anIndexInGroup > 0 + ifTrue: [ + aSelectionIndex := aSelectionIndex + anIndexInGroup. + true ] + ifFalse: [ + aSelectionIndex := aSelectionIndex + eachGroup size. + false + ] + ]. + ] + ifNone: [ ^ 0 ]. + + ^ aSelectionIndex +] + +{ #category : #initialization } +GtCoderProtocolsGroupedListElement >> initialize [ + super initialize. + + self + padding: (BlInsets left: 5 right: 10); + matchParent; + headerElementStencil: [ GtCoderProtocolsGroupHeaderElement new ]; + headerDataBinder: [ :header :each | + header + protocolsGroupName: each domainObject + targetClass: self navigationModel selectedClass ]. + + self when: BrSelectionChanged do: [ + self privateEndRenameProtocol. + self onProtocolListSelectionChanged ]. + + protocolGroups := self createProtocolGroups. + self groups: protocolGroups +] + +{ #category : #accessing } +GtCoderProtocolsGroupedListElement >> navigationModel [ + ^ navigationModel +] + +{ #category : #accessing } +GtCoderProtocolsGroupedListElement >> navigationModel: anObject [ + navigationModel == anObject + ifTrue: [ ^ self ]. + + self unsubscribeFromNavigationModel. + navigationModel := anObject. + self subscribeToNavigationModel. + + self updateProtocolsList +] + +{ #category : #'private - refactorings' } +GtCoderProtocolsGroupedListElement >> onDropCompiledMethodsOnProtocol: anItemsDroppedEvent [ + | aProtocol aProtocolClass aModel compositeChange | + aProtocol := anItemsDroppedEvent currentTarget userData at: #protocol. + aProtocolClass := aProtocol protocolClass. + + aModel := RBNamespace new. + + anItemsDroppedEvent items + do: [ :eachDragItem | + | aTargetProtocolClass eachCompiledMethod | + eachCompiledMethod := eachDragItem domainObject. + aTargetProtocolClass := aProtocolClass. + + eachCompiledMethod methodClass = aTargetProtocolClass + ifTrue: [ + aProtocol name = Protocol unclassified + ifTrue: [ + "To circumvent Pharo's behavior we first + remove a method and then add it as unclassified back" + aModel + removeMethod: eachCompiledMethod selector + from: eachCompiledMethod methodClass. + + aModel + compile: eachCompiledMethod sourceCode + in: aTargetProtocolClass + classified: aProtocol name ] + ifFalse: [ + "Pharo doesn't allow to change method's protocol + to `as yet unclassified` if that method's was + already classified" + aModel + selector: eachCompiledMethod selector + in: aTargetProtocolClass + classified: aProtocol name ] ] + ifFalse: [ + eachDragItem shouldCopy + ifFalse: [ + aModel + removeMethod: eachCompiledMethod selector + from: eachCompiledMethod methodClass ]. + aModel + compile: eachCompiledMethod sourceCode + in: aTargetProtocolClass + classified: aProtocol name ] ]. + + compositeChange := aModel changes. + + compositeChange execute +] + +{ #category : #'event handling' } +GtCoderProtocolsGroupedListElement >> onProtocolDeselected: anAnnouncement [ + self deselectProtocol +] + +{ #category : #'event handling' } +GtCoderProtocolsGroupedListElement >> onProtocolListSelectionChanged [ + | aSelectedItem | + aSelectedItem := self selectedProtocol. + aSelectedItem ifNil: [ ^ self ]. + + self navigationModel + ifNotNil: [ :aModel | aModel selectProtocol: aSelectedItem source: self ] +] + +{ #category : #'event handling' } +GtCoderProtocolsGroupedListElement >> onProtocolSelected: anAnnouncement [ + self selectProtocol: anAnnouncement protocol +] + +{ #category : #'event handling' } +GtCoderProtocolsGroupedListElement >> onProtocolsToShowChanged: anAnnouncement [ + self updateProtocolsList +] + +{ #category : #'private - refactorings' } +GtCoderProtocolsGroupedListElement >> privateEndRenameProtocol [ + "Removes protocol editing UI" + + protocolToRename := nil. + renamingEditor := nil. + self dispatchEvent: (BrItemsProviderItemsChangedEvent new + viewModel: self viewModel; + isSynchronous: true) +] + +{ #category : #'private - refactorings' } +GtCoderProtocolsGroupedListElement >> removeProtocolSubmenuFor: aGtPharoProtocol [ + | submenu | + submenu := BrMenuExplicit new. + ^ submenu + stencil: [ | element change button | + element := BrVerticalPane new fitContent. + element + addChild: (BrLabel new + aptitude: BrGlamorousLabelAptitude new glamorousRegularFontAndSize; + text: (self createLabel: 'Remove ' description: aGtPharoProtocol name); + margin: (BlInsets + top: 10 + bottom: 0 + left: 10 + right: 10)). + element + addChild: (BrAsyncWidget new + fitContent; + stencil: [ | pane methods | + pane := BrVerticalPane new. + pane fitContent. + methods := aGtPharoProtocol methodSelectors size. + methods > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (methods printString , ' defined class' + , (methods > 1 ifTrue: [ 'es' ] ifFalse: [ '' ])) asRopedText) ]. + pane ]). + change := RBRemoveProtocolChange + removeProtocolNamed: aGtPharoProtocol name + in: aGtPharoProtocol protocolClass. + button := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + beSmallSize; + margin: (BlInsets + top: 10 + bottom: 10 + left: 10 + right: 10); + icon: BrGlamorousVectorIcons remove; + label: 'Remove'; + action: [ submenu hideAll. + change execute ]. + element addChild: button as: #removeButton. + element ] +] + +{ #category : #'private - refactorings' } +GtCoderProtocolsGroupedListElement >> renameProtocolSubmenuFor: aGtPharoProtocol [ + ^ BrMenuExplicit new + stencil: [ :anExplicitMenu | + | aViewModel | + anExplicitMenu id: #'coder--context-menu-rename-protocol-form'. + + aViewModel := GtRefactoringsWithInputViewModel new + refactoringTitle: 'Rename method protocol'; + targetName: aGtPharoProtocol name; + inputLabel: 'New method protocol name:'; + initialText: aGtPharoProtocol name; + refactoringWithInput: [ :anInput | + GtRBRenameProtocolRefactoring + renameProtocol: aGtPharoProtocol name + in: aGtPharoProtocol protocolClass + to: anInput ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: self action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: self. + GtRefactoringsPreviewWithInputElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'api - list' } +GtCoderProtocolsGroupedListElement >> selectProtocol: aGtPharoProtocol [ + | selectionIndex | + + selectionIndex := self + detectProtocolIndex: aGtPharoProtocol + inGroups: (protocolGroups collect: [ :eachGroup | eachGroup itemsProvider currentItems ]). + + selectionIndex > 0 + ifTrue: [ self selectOne: selectionIndex ] + ifFalse: [ self deselectAll ] +] + +{ #category : #'api - list' } +GtCoderProtocolsGroupedListElement >> selectedProtocol [ + + | theIndices anIndex | + + theIndices := self selectedIndices ifEmpty: [ ^nil]. + anIndex := theIndices first. + (anIndex between: 1 and: self viewModel entityCount) + ifFalse: [ ^ nil ]. + ^ (self viewModel entityAt: anIndex) value object +] + +{ #category : #'private - subscriptions' } +GtCoderProtocolsGroupedListElement >> subscribeToNavigationModel [ + | subscriptions | + + subscriptions := { + GtCoderNavigationProtocolsToShowChanged -> #onProtocolsToShowChanged:. + GtCoderNavigationProtocolSelected -> #onProtocolSelected:. + GtCoderNavigationProtocolDeselected -> #onProtocolDeselected:. + }. + + subscriptions + do: [ :sub | + navigationModel weak + when: sub key + send: sub value + to: self ] +] + +{ #category : #subscriptions } +GtCoderProtocolsGroupedListElement >> unsubscribeFromNavigationModel [ + navigationModel ifNotNil: [ :aModel | aModel unsubscribe: self ] +] + +{ #category : #'private - updating' } +GtCoderProtocolsGroupedListElement >> updateProtocolListWith: theGtPharoProtocols [ + | virtualStream instStream classStream virtualGtPharoProtocols instanceGtPharoProtocols classGtPharoProtocols groupItems | + + virtualGtPharoProtocols := theGtPharoProtocols + select: [ :eachPharoProtocol | eachPharoProtocol isVirtualProtocol ]. + + instanceGtPharoProtocols := theGtPharoProtocols + select: [ :eachPharoProtocol | eachPharoProtocol isVirtualProtocol not and: [ eachPharoProtocol isClassSide not ] ]. + + classGtPharoProtocols := theGtPharoProtocols + select: [ :eachPharoProtocol | eachPharoProtocol isVirtualProtocol not and: [ eachPharoProtocol isClassSide ] ]. + + virtualStream := virtualGtPharoProtocols + asSortedCollection: [ :a :b | a name < b name ]. + + instStream := instanceGtPharoProtocols + asSortedCollection: [ :a :b | a name < b name ]. + + classStream := classGtPharoProtocols + asSortedCollection: [ :a :b | a name < b name ]. + + groupItems := { + virtualStream. + instStream. + classStream}. + + protocolGroups + with: { + virtualStream. + instStream. + classStream} + do: [ :grp :str | grp items: str ]. + + self groups: protocolGroups +] + +{ #category : #'private - updating' } +GtCoderProtocolsGroupedListElement >> updateProtocolsList [ + self + inUIProcessDo: [ self updateProtocolListWith: self navigationModel protocolsToShow. + + self navigationModel selectedProtocol + ifNil: [ self deselectProtocol ] + ifNotNil: [ :aProtocol | self selectProtocol: aProtocol ] ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderRemoveClassPreviewStencil.class.st b/src/GToolkit-Coder-UI/GtCoderRemoveClassPreviewStencil.class.st new file mode 100644 index 000000000..09d333dc2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderRemoveClassPreviewStencil.class.st @@ -0,0 +1,111 @@ +Class { + #name : #GtCoderRemoveClassPreviewStencil, + #superclass : #BrStencil, + #instVars : [ + 'classToRemove', + 'menuModel' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #accessing } +GtCoderRemoveClassPreviewStencil >> classToRemove [ + ^ classToRemove +] + +{ #category : #accessing } +GtCoderRemoveClassPreviewStencil >> classToRemove: anObject [ + classToRemove := anObject +] + +{ #category : #'api - instantiation' } +GtCoderRemoveClassPreviewStencil >> create [ + | element change button | + element := BrVerticalPane new fitContent. + element + addChild: (self newRemoveClassLabel + margin: (BlInsets + top: 10 + bottom: 0 + left: 10 + right: 10)). + element addChild: self newReferencesAsyncElement. + change := RBRemoveClassChange remove: self classToRemove. + button := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + beSmallSize; + margin: (BlInsets + top: 10 + bottom: 10 + left: 10 + right: 10); + icon: BrGlamorousVectorIcons bin; + label: 'Remove'; + action: [ self menuModel ifNotNil: #hideAll. + change execute ]. + element addChild: button as: #removeButton. + ^ element +] + +{ #category : #accessing } +GtCoderRemoveClassPreviewStencil >> menuModel [ + ^ menuModel +] + +{ #category : #accessing } +GtCoderRemoveClassPreviewStencil >> menuModel: anObject [ + menuModel := anObject +] + +{ #category : #private } +GtCoderRemoveClassPreviewStencil >> newReferencesAsyncElement [ + ^ BrAsyncWidget new + fitContent; + stencil: [ self newReferencesContentElement ] +] + +{ #category : #private } +GtCoderRemoveClassPreviewStencil >> newReferencesContentElement [ + | pane users references subclasses | + references := (GtPharoIndex current + globalVariableReferencesTo: self classToRemove binding) size. + subclasses := self classToRemove allSubclasses size. + users := self classToRemove isTrait + ifFalse: [ 0 ] + ifTrue: [ self classToRemove users size ]. + pane := BrVerticalPane new. + pane fitContent. + references > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (references printString , ' reference' + , (references > 1 ifTrue: [ 's' ] ifFalse: [ '' ])) asRopedText) ]. + subclasses > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (subclasses printString , ' subclass' + , (subclasses > 1 ifTrue: [ 'es' ] ifFalse: [ '' ])) asRopedText) ]. + + users > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (users printString , ' user' , (users > 1 ifTrue: [ 's' ] ifFalse: [ '' ])) + asRopedText) ]. + ^ pane +] + +{ #category : #private } +GtCoderRemoveClassPreviewStencil >> newRemoveClassLabel [ + | labelText | + labelText := 'Remove {1} {2}' format: { self classToRemove gtCoderTypeName. self classToRemove name }. + ^ BrLabel new + margin: (BlInsets all: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: labelText asRopedText +] diff --git a/src/GToolkit-Coder-UI/GtCoderRemoveClassesPreviewStencil.class.st b/src/GToolkit-Coder-UI/GtCoderRemoveClassesPreviewStencil.class.st new file mode 100644 index 000000000..431f854fa --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderRemoveClassesPreviewStencil.class.st @@ -0,0 +1,95 @@ +Class { + #name : #GtCoderRemoveClassesPreviewStencil, + #superclass : #BrStencil, + #instVars : [ + 'classesToRemove', + 'menuModel' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #accessing } +GtCoderRemoveClassesPreviewStencil >> classesToRemove [ + ^ classesToRemove +] + +{ #category : #accessing } +GtCoderRemoveClassesPreviewStencil >> classesToRemove: anObject [ + classesToRemove := anObject +] + +{ #category : #'as yet unclassified' } +GtCoderRemoveClassesPreviewStencil >> create [ + | element change button | + element := BrVerticalPane new fitContent. + element + addChild: (self newRemoveSeveralClassesLabel + margin: (BlInsets + top: 10 + bottom: 0 + left: 10 + right: 10)). + element + addChild: (BrAsyncWidget new + fitContent; + stencil: [ | pane references subclasses | + references := self classesToRemove + sumNumbers: [ :eachClass | (GtPharoIndex current globalVariableReferencesTo: eachClass binding) size ]. + subclasses := self classesToRemove + sumNumbers: [ :eachClass | (eachClass allSubclasses difference: self classesToRemove) size ]. + pane := BrVerticalPane new. + pane fitContent. + references > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (references printString , ' reference' + , (references > 1 ifTrue: [ 's' ] ifFalse: [ '' ])) asRopedText) ]. + subclasses > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (subclasses printString , ' subclass' + , (subclasses > 1 ifTrue: [ 'es' ] ifFalse: [ '' ])) asRopedText) ]. + pane ]). + change := RBCompositeRefactoryChange new + name: ('Remove {1} classes' format: {self classesToRemove size}); + changes: (self classesToRemove + collect: [ :eachClass | RBRemoveClassChange remove: eachClass ]). + button := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + beSmallSize; + margin: (BlInsets + top: 10 + bottom: 10 + left: 10 + right: 10); + icon: BrGlamorousVectorIcons remove; + label: 'Remove'; + action: [ self menuModel ifNotNil: #hideAll. + change execute ]. + element addChild: button as: #removeButton. + ^ element +] + +{ #category : #accessing } +GtCoderRemoveClassesPreviewStencil >> menuModel [ + ^ menuModel +] + +{ #category : #accessing } +GtCoderRemoveClassesPreviewStencil >> menuModel: anObject [ + menuModel := anObject +] + +{ #category : #private } +GtCoderRemoveClassesPreviewStencil >> newRemoveSeveralClassesLabel [ + | labelText | + labelText := 'Remove {1} classes' format: {self classesToRemove size}. + ^ BrLabel new + margin: (BlInsets all: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: labelText asRopedText +] diff --git a/src/GToolkit-Coder-UI/GtCoderRunTestAction.extension.st b/src/GToolkit-Coder-UI/GtCoderRunTestAction.extension.st new file mode 100644 index 000000000..b12a44df2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderRunTestAction.extension.st @@ -0,0 +1,16 @@ +Extension { #name : #GtCoderRunTestAction } + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderRunTestAction >> buildElementIn: aCoderActionsElement [ + ^BlElement new + viewModel: BrWidgetModel new; + layout: BlLinearLayout horizontal; + constraintsDo: [:c | + c horizontal fitContent. + c vertical fitContent]; + addChild: (GtPharoMethodTestCaseStateElement new + bindToCoderViewModel: aCoderActionsElement coderViewModel; + constraintsDo: [:c | + c linear vertical alignCenter ]); + addChild: (aCoderActionsElement newButtonForAction: self) +] diff --git a/src/GToolkit-Coder-UI/GtCoderSearchActionId.class.st b/src/GToolkit-Coder-UI/GtCoderSearchActionId.class.st new file mode 100644 index 000000000..191b3191b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderSearchActionId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderSearchActionId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtCoderSearchActionId >> asSymbol [ + ^ #'main-action--search' +] diff --git a/src/GToolkit-Coder-UI/GtCoderSettings.class.st b/src/GToolkit-Coder-UI/GtCoderSettings.class.st new file mode 100644 index 000000000..9704d5145 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderSettings.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GtCoderSettings, + #superclass : #Object, + #classVars : [ + 'DefaultDetailPositionState', + 'DefaultDetailState' + ], + #category : #'GToolkit-Coder-UI-! Core' +} + +{ #category : #settings } +GtCoderSettings class >> defaultDetailPositionState [ + + ^ DefaultDetailPositionState +] + +{ #category : #settings } +GtCoderSettings class >> defaultDetailState [ + ^ DefaultDetailState +] + +{ #category : #settings } +GtCoderSettings class >> displayDetails [ + DefaultDetailState := GtPhlowToolDetailState detailed +] + +{ #category : #settings } +GtCoderSettings class >> hideDetails [ + DefaultDetailState := nil +] + +{ #category : #settings } +GtCoderSettings class >> leftPosition [ + DefaultDetailPositionState := nil +] + +{ #category : #settings } +GtCoderSettings class >> topPosition [ + DefaultDetailPositionState := GtPhlowToolDetailPositionState top +] diff --git a/src/GToolkit-Coder-UI/GtCoderSidebarVisibilityWish.class.st b/src/GToolkit-Coder-UI/GtCoderSidebarVisibilityWish.class.st new file mode 100644 index 000000000..98bfc0883 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderSidebarVisibilityWish.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderSidebarVisibilityWish, + #superclass : #BrWish, + #instVars : [ + 'isVisible' + ], + #category : #'GToolkit-Coder-UI-Basic' +} + +{ #category : #accessing } +GtCoderSidebarVisibilityWish >> isVisible [ + ^ isVisible ifNil: [ false ] +] + +{ #category : #accessing } +GtCoderSidebarVisibilityWish >> isVisible: anObject [ + isVisible := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderSlotsGroupedListElement.class.st b/src/GToolkit-Coder-UI/GtCoderSlotsGroupedListElement.class.st new file mode 100644 index 000000000..cfb52b4e4 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderSlotsGroupedListElement.class.st @@ -0,0 +1,178 @@ +Class { + #name : #GtCoderSlotsGroupedListElement, + #superclass : #BrGroupedList, + #traits : 'TGtCoderNavigationWithContextMenu', + #classTraits : 'TGtCoderNavigationWithContextMenu classTrait', + #instVars : [ + 'slotsGroup', + 'navigationModel' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #initialization } +GtCoderSlotsGroupedListElement >> computeSlotItemText: aSlot [ + | slotText | + + slotText := aSlot name asRopedText. + self navigationModel selectedClass = aSlot definingClass instanceSide + ifFalse: [ slotText, ((' ({1})' format: { aSlot definingClass name }) asRopedText foreground: Color gray) ]. + + ^ slotText +] + +{ #category : #initialization } +GtCoderSlotsGroupedListElement >> createSlotGroups [ + | classSlotGroup instanceGroup classVarGroup | + instanceGroup := BrGroup new + domainObject: 'instance-side slots'; + stream: #() asAsyncStream; + itemStencil: [ BrHorizontalPane new + aptitude: BrGlamorousListItemAptitude; + hMatchParent; + vFitContent ]; + itemDataBinder: [ :element :item | + | label slotText | + element removeChildren. + label := BrLabel new + hMatchParent; + vFitContent; + beSmallSize; + aptitude: GtCoderNavigationTreeLabelAptitude. + + slotText := self computeSlotItemText: item. + + label text: slotText. + label + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude + menu: [ | aPhlowContext | + aPhlowContext := GtPhlowContext new + coderSelectedClass: self selectedClass; + behavior: self selectedClass. + navigationModel + coderDo: [ :aCoder | + (aCoder isKindOf: GtPharoBehaviorCoder) + ifTrue: [ aPhlowContext selfObjectHolder: aCoder objectHolder ] ]. + GtCoderVariableTarget + menuItemsForObject: item + inContext: aPhlowContext + hostElement: element ]). + element addChild: label ]; + shouldShowWithoutItems: false. + classSlotGroup := instanceGroup copy domainObject: 'class-side slots'. + classVarGroup := instanceGroup copy domainObject: 'class vars'. + ^ {instanceGroup. + classSlotGroup. + classVarGroup} +] + +{ #category : #initialization } +GtCoderSlotsGroupedListElement >> initialize [ + super initialize. + + self + padding: (BlInsets left: 5 right: 10); + matchParent; + headerElementStencil: [ BrLabel new + beSmallSize; + aptitude: (BrGlamorousLabelAptitude new foreground: Color gray) ]; + headerDataBinder: [ :label :each | label text: each domainObject asRopedText ]. + + slotsGroup := self createSlotGroups. + self groups: slotsGroup. + + self + when: BrSelectionChanged + do: [ :anEvent | self onSlotsListSelectionChanged ] +] + +{ #category : #accessing } +GtCoderSlotsGroupedListElement >> navigationModel [ + ^ navigationModel +] + +{ #category : #accessing } +GtCoderSlotsGroupedListElement >> navigationModel: anObject [ + navigationModel == anObject + ifTrue: [ ^ self ]. + + self unsubscribeFromNavigationModel. + navigationModel := anObject. + self subscribeToNavigationModel. + + self updateSlotList +] + +{ #category : #'event handling - selection' } +GtCoderSlotsGroupedListElement >> onSlotSelected: anAnnouncement [ + +] + +{ #category : #'event handling - selection' } +GtCoderSlotsGroupedListElement >> onSlotsListSelectionChanged [ + | theIndices anIndex aSelectedItem | + + theIndices := self selectedIndices ifEmpty: [ ^ self ]. + anIndex := theIndices first. + (anIndex between: 1 and: self viewModel entityCount) + ifFalse: [ ^ self ]. + aSelectedItem := (self viewModel entityAt: anIndex) value object. + self navigationModel selectSlot: aSelectedItem source: self +] + +{ #category : #'event handling' } +GtCoderSlotsGroupedListElement >> onSlotsToShowChanged: anAnnouncement [ + self updateSlotList +] + +{ #category : #accessing } +GtCoderSlotsGroupedListElement >> selectedClass [ + ^ navigationModel selectedClass +] + +{ #category : #'private - subscriptions' } +GtCoderSlotsGroupedListElement >> subscribeToNavigationModel [ + | subscriptions | + + subscriptions := { + GtCoderNavigationSlotsToShowChanged -> #onSlotsToShowChanged:. + GtCoderNavigationSlotSelected -> #onSlotSelected:. + }. + + subscriptions + do: [ :sub | + navigationModel weak + when: sub key + send: sub value + to: self ] +] + +{ #category : #subscriptions } +GtCoderSlotsGroupedListElement >> unsubscribeFromNavigationModel [ + navigationModel ifNotNil: [ :aModel | aModel unsubscribe: self ] +] + +{ #category : #'updating lists' } +GtCoderSlotsGroupedListElement >> updateSlotList [ + | slotsToShow newSlotGroups instanceSideSlots classSideSlots staticVars | + self navigationModel ifNil: [ ^ self ]. + + self deselectAll. + + slotsToShow := self navigationModel slotsToShow. + instanceSideSlots := slotsToShow + select: [ :eachSlot | eachSlot isInstanceVariable and: [ eachSlot owningClass isClassSide not ] ]. + + classSideSlots := slotsToShow + select: [ :eachSlot | eachSlot isInstanceVariable and: [ eachSlot owningClass isClassSide ] ]. + + staticVars := slotsToShow select: [ :eachSlot | eachSlot isClassVariable ]. + + newSlotGroups := {instanceSideSlots. + classSideSlots. + staticVars} + collect: [ :each | each asSortedCollection: [ :a :b | a name < b name ] ]. + + slotsGroup with: newSlotGroups do: [ :grp :str | grp items: str ]. + self groups: slotsGroup +] diff --git a/src/GToolkit-Coder-UI/GtCoderSpotterStart.class.st b/src/GToolkit-Coder-UI/GtCoderSpotterStart.class.st index 5f79aa89d..b3304d3dc 100644 --- a/src/GToolkit-Coder-UI/GtCoderSpotterStart.class.st +++ b/src/GToolkit-Coder-UI/GtCoderSpotterStart.class.st @@ -8,88 +8,28 @@ Class { } { #category : #searching } -GtCoderSpotterStart >> gtSpotterForClassMethodsInCurrentClassFor: aStep [ +GtCoderSpotterStart >> gtSpotterForClassesFor: aSearch [ - self navigationModel hasSelectedClass - ifFalse: [ ^ self ]. - aStep listProcessor - priority: 2; - title: 'Class methods'; - allCandidates: [ self navigationModel selectedClass classSide methods ]; - filter: GtFilterSubstring; - wantsToDisplayOnEmptyQuery: true -] - -{ #category : #searching } -GtCoderSpotterStart >> gtSpotterForClassesFor: aStep [ - - aStep listProcessor + ^ aSearch list priority: 10; - allCandidates: [ Smalltalk allClassesAndTraits ]; + items: [ Smalltalk allClassesAndTraits ]; title: 'Classes'; - filter: GtFilterSubstring; - itemIcon: #systemIcon; - keyBinding: $b meta; + filterBySubstring; wantsToDisplayOnEmptyQuery: self navigationModel hasSelectedClass not ] { #category : #searching } -GtCoderSpotterStart >> gtSpotterForInstanceMethodsInCurrentClassFor: aStep [ - - self navigationModel hasSelectedClass - ifFalse: [ ^ self ]. - aStep listProcessor - priority: 1; - title: 'Instance methods'; - allCandidates: [ self navigationModel selectedClass instanceSide methods ]; - filter: GtFilterSubstring; - wantsToDisplayOnEmptyQuery: true -] - -{ #category : #searching } -GtCoderSpotterStart >> gtSpotterForMessagesFor: aStep [ +GtCoderSpotterStart >> gtSpotterForPackagesFor: aSearch [ - aStep listProcessor - priority: 35; - title: 'Messages'; - filter: GtNullFilter - item: - [ :filter :context | GTSelector substring: context textTrimmed filter: filter ]; - actLogic: [ :each :step :spotterElement | - (each name asSymbol gtImplementors) - gtSpotterActDefault: step - from: spotterElement ]; - wantsToDisplayOnEmptyQuery: false -] - -{ #category : #searching } -GtCoderSpotterStart >> gtSpotterForPackagesFor: aStep [ - - aStep listProcessor + ^ aSearch list priority: 20; - allCandidates: [ RPackageOrganizer default packages ]; + items: [ self packageOrganizer packages ]; title: 'Packages'; itemName: [ :package | package name ]; - itemIcon: [ Smalltalk ui icons iconNamed: #packageIcon ]; - filter: GtFilterSubstring; + filterBySubstring; wantsToDisplayOnEmptyQuery: self navigationModel hasSelectedPackage not ] -{ #category : #searching } -GtCoderSpotterStart >> gtSpotterImplementorsFor: aStep [ - - self flag: #specialFilter. - aStep listProcessor - priority: 30; - title: 'Implementors'; - filter: GtFilterImplementor - item: [ :filter :context | - SystemNavigation default - allBehaviorsDo: [ :class | class localMethods do: filter ] ]; - keyBinding: $m meta; - wantsToDisplayOnEmptyQuery: false -] - { #category : #accessing } GtCoderSpotterStart >> navigationModel [ ^ navigationModelBlock value diff --git a/src/GToolkit-Coder-UI/GtCoderTextAttributesAddedDocumentIdIsNilSignal.class.st b/src/GToolkit-Coder-UI/GtCoderTextAttributesAddedDocumentIdIsNilSignal.class.st new file mode 100644 index 000000000..87e6ba3e6 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderTextAttributesAddedDocumentIdIsNilSignal.class.st @@ -0,0 +1,82 @@ +Class { + #name : #GtCoderTextAttributesAddedDocumentIdIsNilSignal, + #superclass : #GtCoderDocumentIdSignal, + #instVars : [ + 'editorElement', + 'receivedText', + 'startPosition', + 'endPosition', + 'coderTextAttributes' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> coderTextAttributes [ + ^ coderTextAttributes +] + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> coderTextAttributes: aGtTextualCoderTextAttributes [ + coderTextAttributes := aGtTextualCoderTextAttributes +] + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> editorElement [ + ^ editorElement +] + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> editorElement: anObject [ + editorElement := anObject +] + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> endPosition [ + ^ endPosition +] + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> endPosition: anObject [ + endPosition := anObject +] + +{ #category : #views } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> gtTextAttributesViewFor: aView [ + + ^ aView columnedList + title: 'Attributes'; + priority: 0; + items: [ self textAttributes ifNil: [ #() ] ]; + column: 'Index' + text: [ :eachItem :eachIndex | eachIndex asRopedText foreground: Color gray ] + width: 45; + column: 'Attribute' text: [ :each | each gtDisplayString ] +] + +{ #category : #printing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> printOneLineContentsOn: aStream [ + aStream + nextPut: $[; + print: startPosition; + nextPut: $,; + print: endPosition; + nextPut: $]; + space; + print: self textAttributes +] + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> startPosition [ + ^ startPosition +] + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> startPosition: anObject [ + startPosition := anObject +] + +{ #category : #accessing } +GtCoderTextAttributesAddedDocumentIdIsNilSignal >> textAttributes [ + ^ coderTextAttributes ifNotNil: #textAttributes +] diff --git a/src/GToolkit-Coder-UI/GtCoderTextAttributesDocumentIdIsNilSignal.class.st b/src/GToolkit-Coder-UI/GtCoderTextAttributesDocumentIdIsNilSignal.class.st new file mode 100644 index 000000000..1da9e9ea9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderTextAttributesDocumentIdIsNilSignal.class.st @@ -0,0 +1,92 @@ +Class { + #name : #GtCoderTextAttributesDocumentIdIsNilSignal, + #superclass : #GtCoderDocumentIdSignal, + #instVars : [ + 'editorElement', + 'receivedText', + 'startPosition', + 'endPosition', + 'coderTextAttributes' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> coderTextAttributes [ + ^ coderTextAttributes +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> coderTextAttributes: aGtTextualCoderTextAttributes [ + coderTextAttributes := aGtTextualCoderTextAttributes +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> editorElement [ + ^ editorElement +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> editorElement: anObject [ + editorElement := anObject +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> endPosition [ + ^ endPosition +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> endPosition: anObject [ + endPosition := anObject +] + +{ #category : #views } +GtCoderTextAttributesDocumentIdIsNilSignal >> gtTextAttributesViewFor: aView [ + + ^ aView columnedList + title: 'Attributes'; + priority: 0; + items: [ self textAttributes ifNil: [ #() ] ]; + column: 'Index' + text: [ :eachItem :eachIndex | eachIndex asRopedText foreground: Color gray ] + width: 45; + column: 'Attribute' text: [ :each | each gtDisplayString ] +] + +{ #category : #printing } +GtCoderTextAttributesDocumentIdIsNilSignal >> printOneLineContentsOn: aStream [ + aStream + nextPut: $[; + print: startPosition; + nextPut: $,; + print: endPosition; + nextPut: $]; + space; + print: self textAttributes +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> receivedText [ + ^ receivedText +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> receivedText: anObject [ + receivedText := anObject +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> startPosition [ + ^ startPosition +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> startPosition: anObject [ + startPosition := anObject +] + +{ #category : #accessing } +GtCoderTextAttributesDocumentIdIsNilSignal >> textAttributes [ + ^ coderTextAttributes ifNotNil: #textAttributes +] diff --git a/src/GToolkit-Coder-UI/GtCoderToSpawnInSpace.class.st b/src/GToolkit-Coder-UI/GtCoderToSpawnInSpace.class.st index 8bb5a94bd..8b3919aae 100644 --- a/src/GToolkit-Coder-UI/GtCoderToSpawnInSpace.class.st +++ b/src/GToolkit-Coder-UI/GtCoderToSpawnInSpace.class.st @@ -1,5 +1,5 @@ " -I am fired by inner coders to tell {{gtClass:GtCoder}} to spawn a new coder in a new ${class:BlSpace} +I am fired by inner coders to tell {{gtClass:GtCoder}} to spawn a new coder in a new {{gtClass:BlSpace}}. " diff --git a/src/GToolkit-Coder-UI/GtCoderToggleAction.extension.st b/src/GToolkit-Coder-UI/GtCoderToggleAction.extension.st new file mode 100644 index 000000000..39624b6b4 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderToggleAction.extension.st @@ -0,0 +1,22 @@ +Extension { #name : #GtCoderToggleAction } + +{ #category : #'*GToolkit-Coder-UI' } +GtCoderToggleAction >> buildElementIn: aCoderActionsElement [ + | button | + button := aCoderActionsElement newToggleForAction: self. + toggleModel + when: BrToggleActivatedEvent do: [ :evt | button activate ]; + when: BrToggleDeactivatedEvent do: [ :evt | button deactivate ]. + + button + activated: toggleModel isActivated; + when: BrToggleActivatedEvent + do: [ :evt | + toggleModel activate. + activateBlock value: evt ]; + when: BrToggleDeactivatedEvent + do: [ :evt | + toggleModel deactivate. + deactivateBlock value: evt ]. + ^ button +] diff --git a/src/GToolkit-Coder-UI/GtCoderToggleAptitude.class.st b/src/GToolkit-Coder-UI/GtCoderToggleAptitude.class.st deleted file mode 100644 index 86f662daf..000000000 --- a/src/GToolkit-Coder-UI/GtCoderToggleAptitude.class.st +++ /dev/null @@ -1,45 +0,0 @@ -Class { - #name : #GtCoderToggleAptitude, - #superclass : #BrMaterialToggleBackgroundAptitude, - #category : #'GToolkit-Coder-UI-Looks' -} - -{ #category : #initialization } -GtCoderToggleAptitude >> defaultNormalBackground [ - ^ Color transparent -] - -{ #category : #initialization } -GtCoderToggleAptitude >> initialize [ - | interactiveLook | - super initialize. - self flag: 'Working around a toggled display issue'. - interactiveLook := self looks - detect: [ :each | each isKindOf: BrInteractiveAptitude ] - ifNone: [ nil ]. - interactiveLook notNil - ifTrue: [ interactiveLook - style: [ :aStyler | - aStyler default: [ :aWidget | self updateActivatedBackground ]. - aStyler - pressed: - [ :aWidget | aWidget background: self defaultCheckedBackground slightlyLighter ] ] ]. - self - add: BrMaterialRoundedAptitude new; - add: BrTextLabelAptitude new. - self - addChange: - (BrLookChange new - up: [ :e | - self flag: 'Working around a toggled display issue'. - self updateActivatedBackground ]; - down: [ :e | ]; - yourself) -] - -{ #category : #private } -GtCoderToggleAptitude >> updateActivatedBackground [ - self widget isActivated - ifTrue: [ self toggleActivated ] - ifFalse: [ self toggleDeactivated ] -] diff --git a/src/GToolkit-Coder-UI/GtCoderTool.class.st b/src/GToolkit-Coder-UI/GtCoderTool.class.st index 44dad7343..5ef046eae 100644 --- a/src/GToolkit-Coder-UI/GtCoderTool.class.st +++ b/src/GToolkit-Coder-UI/GtCoderTool.class.st @@ -8,7 +8,7 @@ Class { GtCoderTool >> asElementDo: aOneArgBlock [ "Create a tool element and execute the block." - ^ aOneArgBlock cull: self newCoder asPagerPageElement + ^ aOneArgBlock cull: self newCoder ] { #category : #'api - converting' } diff --git a/src/GToolkit-Coder-UI/GtCoderToolbarElement.class.st b/src/GToolkit-Coder-UI/GtCoderToolbarElement.class.st index 37df61b05..af34b8594 100644 --- a/src/GToolkit-Coder-UI/GtCoderToolbarElement.class.st +++ b/src/GToolkit-Coder-UI/GtCoderToolbarElement.class.st @@ -8,79 +8,30 @@ I display a {{gtClass:BrToolbar}}. " Class { #name : #GtCoderToolbarElement, - #superclass : #GtCoderNavigationModelElement, + #superclass : #GtAbstractCoderElement, #instVars : [ 'toolbarElement' ], #category : #'GToolkit-Coder-UI-Basic' } -{ #category : #'gt-extensions' } -GtCoderToolbarElement >> addClassTab: look [ - - ^ BrTab new - aptitude: BrGlamorousTabAptitude new; - label: 'Class'; - stencil: [ | element | - element := GtPharoCreateBehaviorElement new. - element - behaviorBlock: [ :cls | - look hide. - navigationModel selectClass: cls ]. - element forClassDefinition. - navigationModel - selectedPackageDo: [ :package | element forPackage: package ]. - navigationModel - selectedTagDo: [ :tag | element forPackageTag: tag ]. - element ] -] - -{ #category : #'gt-extensions' } -GtCoderToolbarElement >> addPackageTab: look [ - - ^ BrTab new - aptitude: BrGlamorousTabAptitude new; - label: 'Package'; - stencil: [ | element | - element := GtPharoCreatePackageElement new. - element - packageBlock: [ :pkg :tag | - look hide. - tag isNil - ifTrue: [ self navigationModel selectPackage: pkg ] - ifFalse: [ self navigationModel selectPackageTag: tag ] ]. - self navigationModel - selectedPackageDo: [ :package | element forPackage: package ]. - element ] -] - -{ #category : #'gt-extensions' } -GtCoderToolbarElement >> addTraitTab: look [ - - ^ BrTab new - aptitude: BrGlamorousTabAptitude new; - label: 'Trait'; - stencil: [ | element | - element := GtPharoCreateBehaviorElement new. - element - behaviorBlock: [ :cls | - look hide. - self navigationModel selectClass: cls ]. - element forTraitDefinition. - self navigationModel - selectedPackageDo: [ :package | element forPackage: package ]. - self navigationModel - selectedTagDo: [ :tag | element forPackageTag: tag ]. - element ] -] - -{ #category : #'private - actions' } -GtCoderToolbarElement >> browseFrom: anElement [ - "Do we want to share the same coder model?" - self navigationModel coderDo: [ :aCoder | - ((GtCoder forCoder: aCoder asNewCoderModelWithSameSubject) - openInPagerFrom: anElement) - maximized ] +{ #category : #initialization } +GtCoderToolbarElement >> addCustomButtons [ + | classToUse | + classToUse := self navigationModel hasSelectedClass + ifTrue: [ self navigationModel selectedClass ] + ifFalse: [ self navigationModel hasSelectedPackage + ifTrue: [ self navigationModel selectedPackage ] + ifFalse: [ self class ] ]. + (GtPhlowClassActionsCollector new + pragmaName: #gtClassAction; + from: classToUse class; + to: Behavior; + collect) + do: [ :each | + each + asElement: [ :actionElement | self toolbarElement addItem: actionElement ] + withHostElement: self ] ] { #category : #initialization } @@ -100,111 +51,13 @@ GtCoderToolbarElement >> initialize [ { #category : #initialization } GtCoderToolbarElement >> initializeToolbarElement [ toolbarElement := BrToolbar new - aptitude: BrGlamorousToolbarAptitude new; - padding: - (BlInsets - top: 10 - left: 0 - bottom: 0 - right: 5); - addItem: self newBrowseButton; - addItem: self newSpotterButton; - addItem: self newHierarchyButton; - addItem: self newAddButton; - yourself -] - -{ #category : #'private - instance creation' } -GtCoderToolbarElement >> newAddButton [ - | look | - ^ BrButton new - label: 'Add class or package'; - aptitude: - BrGlamorousButtonWithIconAptitude - + - (look := BrGlamorousWithDropdownAptitude - handle: [ BrButton new - aptitude: - BrGlamorousButtonWithIconAptitude - BrGlamorousButtonWithLabelTooltipAptitude - - BrGlamorousButtonExteriorAptitude; - icon: BrGlamorousVectorIcons add; - yourself ] - content: [ | element | - element := BlElement new. - element - constraintsDo: [ :c | - c horizontal exact: 400. - c vertical exact: 300 ]. - element addChild: (self newAddInterface: look). - element ]); - icon: BrGlamorousVectorIcons add; - yourself -] - -{ #category : #'private - instance creation' } -GtCoderToolbarElement >> newAddInterface: look [ - | contentTabs tabMethods | - contentTabs := BrTabGroup new. - contentTabs aptitude: BrGlamorousTabGroupAptitude new. - contentTabs - constraintsDo: [ :c | - c horizontal matchParent. - c vertical matchParent ]. - tabMethods := (Pragma allNamed: #gtCreateComponentTab: in: self class) - asSortedCollection: [ :a :b | a arguments first < b arguments first ]. - tabMethods - do: - [ :each | contentTabs addTab: (self perform: each method selector with: look) ]. - ^ contentTabs -] - -{ #category : #'private - instance creation' } -GtCoderToolbarElement >> newBrowseButton [ - ^ BrButton new - aptitude: BrGlamorousButtonWithIconAptitude; - label: 'Browse in another world tab'; - icon: BrGlamorousVectorIcons emphasizedBrowse; - action: [ :aButton | self browseFrom: aButton ] -] - -{ #category : #'private - instance creation' } -GtCoderToolbarElement >> newHierarchyButton [ - ^ BrButton new - label: 'Show Package and Class Hierarchies'; - aptitude: BrGlamorousButtonWithIconAptitude + (BrGlamorousWithDropdownAptitude - handle: [ - BrButton new - aptitude: BrGlamorousButtonWithIconAptitude - BrGlamorousButtonWithLabelTooltipAptitude - BrGlamorousButtonExteriorAptitude; - icon: BrGlamorousIcons tree; - yourself ] - content: [ - BlElement new - size: 400@400; - addChild: ((GtCoderNavigationTabsStencil new - navigationModel: navigationModel; - asElement) background: Color white) ]); - icon: BrGlamorousIcons tree asElement; - yourself + aptitude: BrGlamorousToolbarAptitude new; + padding: (BlInsets right: 5). ] -{ #category : #'private - instance creation' } -GtCoderToolbarElement >> newSpotterButton [ - - - ^ GtSpotterDropdownButtonStencil new - valuable: (GtCoderSpotterStart new navigationModelBlock: [self navigationModel]); - tooltip: 'Search Code'; - actOn: [ :anActOnEvent :anItem :theButton | - | acted | - acted := false. - (anItem isKindOf: RPackage) ifTrue: [ - self navigationModel selectPackage: anItem. acted := true ]. - (anItem isKindOf: ClassDescription) ifTrue: [ - self navigationModel selectClass: anItem. acted := true ]. - (anItem isKindOf: CompiledMethod) ifTrue: [ - self navigationModel selectMethod: anItem. acted := true ]. - acted ifTrue: [ anActOnEvent beActed ] ]; - asElement. +{ #category : #initialization } +GtCoderToolbarElement >> onNavigationModelChanged [ + self updateCustomButtons ] { #category : #accessing } @@ -212,3 +65,9 @@ GtCoderToolbarElement >> toolbarElement [ ^ toolbarElement ] + +{ #category : #initialization } +GtCoderToolbarElement >> updateCustomButtons [ + toolbarElement removeChildren. + self addCustomButtons +] diff --git a/src/GToolkit-Coder-UI/GtCoderUIConstants.class.st b/src/GToolkit-Coder-UI/GtCoderUIConstants.class.st new file mode 100644 index 000000000..b0b698f5e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderUIConstants.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtCoderUIConstants, + #superclass : #Object, + #category : #'GToolkit-Coder-UI-Utilities' +} + +{ #category : #initialization } +GtCoderUIConstants class >> actionsToolbarHeight [ + "Coder toobar items are computed later and the coder element height adopted accordingly. + In case of list of coders, it does not behave a best way, + because the list layout: + - first, computes position (ehgith) of coders without the toolbar action items, + - second, scrolls to a specific coder item, + - third, recomputes the coders height. + As a consequence, the position of the item users + wanted to scroll to, might be significantly shifted. + As a workaround, we explicitly set (a minimum) toolbar height." + + ^ 16 +] diff --git a/src/GToolkit-Coder-UI/GtCoderUIUtility.class.st b/src/GToolkit-Coder-UI/GtCoderUIUtility.class.st new file mode 100644 index 000000000..e8bc4b56c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderUIUtility.class.st @@ -0,0 +1,33 @@ +Class { + #name : #GtCoderUIUtility, + #superclass : #Object, + #category : #'GToolkit-Coder-UI-Utilities' +} + +{ #category : #'api - instance creation' } +GtCoderUIUtility class >> exampleExecutionToolbarFor: anExampleWithResult [ + + ^ BrToolbar new + aptitude: BrGlamorousToolbarAptitude new; + addItem: (BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + icon: BrGlamorousVectorIcons play; + label: 'Play'; + action: [ :aButton | anExampleWithResult run ]; + beTinySize); + addItem: (BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + icon: BrGlamorousVectorIcons playinspect; + label: 'Play and Inspect'; + action: [ :aButton | + anExampleWithResult run. + aButton phlow spawnObject: anExampleWithResult result ]; + beTinySize); + addChild: (BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + icon: BrGlamorousVectorIcons debug; + label: 'Debug'; + action: [ :aButton | + anExampleWithResult openingDebugger result ]; + beTinySize) +] diff --git a/src/GToolkit-Coder-UI/GtCoderUserSnippetDynamicVariable.class.st b/src/GToolkit-Coder-UI/GtCoderUserSnippetDynamicVariable.class.st new file mode 100644 index 000000000..4b4299927 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderUserSnippetDynamicVariable.class.st @@ -0,0 +1,32 @@ +Class { + #name : #GtCoderUserSnippetDynamicVariable, + #superclass : #DynamicVariable, + #category : #'GToolkit-Coder-UI-Coder - Source Model' +} + +{ #category : #accessing } +GtCoderUserSnippetDynamicVariable class >> value: aSnippetEvaluationState [ + ^ self soleInstance value: aSnippetEvaluationState +] + +{ #category : #accessing } +GtCoderUserSnippetDynamicVariable class >> valueAtProcess: aProcess [ + ^ self soleInstance valueAtProcess: aProcess +] + +{ #category : #accessing } +GtCoderUserSnippetDynamicVariable >> default [ + ^ GtCoderNotUserSnippetState default +] + +{ #category : #accessing } +GtCoderUserSnippetDynamicVariable >> value: aSnippetEvaluationState [ + | activeProcess | + activeProcess := Processor activeProcess. + ^ activeProcess psValueAt: index put: aSnippetEvaluationState +] + +{ #category : #accessing } +GtCoderUserSnippetDynamicVariable >> valueAtProcess: aProcess [ + ^ (aProcess psValueAt: index) ifNil: [ self default ] +] diff --git a/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationConfiguration.class.st b/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationConfiguration.class.st new file mode 100644 index 000000000..ed09f30df --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationConfiguration.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderUserSnippetEvaluationConfiguration, + #superclass : #Object, + #category : #'GToolkit-Coder-UI-Coder - Source Model' +} + +{ #category : #accessing } +GtCoderUserSnippetEvaluationConfiguration class >> handledErrors [ + ^ GtCoderEvaluationUnhandledError , Halt, Warning +] diff --git a/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationExceptionState.class.st b/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationExceptionState.class.st new file mode 100644 index 000000000..d23c9ab78 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationExceptionState.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtCoderUserSnippetEvaluationExceptionState, + #superclass : #GtCoderUserSnippetEvaluationState, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Coder - Source Model' +} + +{ #category : #printing } +GtCoderUserSnippetEvaluationExceptionState >> gtDisplayOn: stream [ + stream nextPutAll: 'Snippet with exception' +] diff --git a/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationRunningState.class.st b/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationRunningState.class.st new file mode 100644 index 000000000..cfff18b1f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationRunningState.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtCoderUserSnippetEvaluationRunningState, + #superclass : #GtCoderUserSnippetEvaluationState, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Coder - Source Model' +} + +{ #category : #printing } +GtCoderUserSnippetEvaluationRunningState >> gtDisplayOn: stream [ + stream nextPutAll: 'Running snippet' +] diff --git a/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationState.class.st b/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationState.class.st new file mode 100644 index 000000000..f955b25f9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderUserSnippetEvaluationState.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtCoderUserSnippetEvaluationState, + #superclass : #Object, + #category : #'GToolkit-Coder-UI-Coder - Source Model' +} + +{ #category : #testing } +GtCoderUserSnippetEvaluationState >> isUserSnippet [ + ^ true +] diff --git a/src/GToolkit-Coder-UI/GtCoderVariableTarget.class.st b/src/GToolkit-Coder-UI/GtCoderVariableTarget.class.st new file mode 100644 index 000000000..40e6f2879 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderVariableTarget.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderVariableTarget, + #superclass : #GtPhlowActionUniqueTarget, + #category : #'GToolkit-Coder-UI-Phlow Targets' +} diff --git a/src/GToolkit-Coder-UI/GtCoderViewModelRecomputeAddOnRequest.class.st b/src/GToolkit-Coder-UI/GtCoderViewModelRecomputeAddOnRequest.class.st new file mode 100644 index 000000000..c9a1370b4 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderViewModelRecomputeAddOnRequest.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderViewModelRecomputeAddOnRequest, + #superclass : #Announcement, + #instVars : [ + 'coderViewModel' + ], + #category : #'GToolkit-Coder-UI-Coder - Basic' +} + +{ #category : #accessing } +GtCoderViewModelRecomputeAddOnRequest >> coderViewModel [ + ^ coderViewModel +] + +{ #category : #accessing } +GtCoderViewModelRecomputeAddOnRequest >> coderViewModel: anObject [ + coderViewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtCoderViewModelRequester.class.st b/src/GToolkit-Coder-UI/GtCoderViewModelRequester.class.st new file mode 100644 index 000000000..4d9f2beff --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCoderViewModelRequester.class.st @@ -0,0 +1,53 @@ +Class { + #name : #GtCoderViewModelRequester, + #superclass : #Object, + #traits : 'TGtCoderRequesterObject', + #classTraits : 'TGtCoderRequesterObject classTrait', + #instVars : [ + 'coderViewModel' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Model' +} + +{ #category : #accessing } +GtCoderViewModelRequester >> coderViewModel [ + ^ coderViewModel +] + +{ #category : #accessing } +GtCoderViewModelRequester >> coderViewModel: anObject [ + coderViewModel := anObject +] + +{ #category : #testing } +GtCoderViewModelRequester >> isCoderViewModel: aCoderViewModel [ + ^ aCoderViewModel = self coderViewModel +] + +{ #category : #testing } +GtCoderViewModelRequester >> isUndefinedOrCoderViewModel: aCoderViewModel [ + ^ self isCoderViewModel: aCoderViewModel +] + +{ #category : #'api - notifying' } +GtCoderViewModelRequester >> notifyShowDebuggerRequest: aDebugSession dueTo: anException sourceString: aSourceString sourceInterval: aSourceInterval evaluationInfo: anEvaluationInfo [ + "Return true if announcement was handled (and debugger displayed in some way). + Return false otherwise." + + ^ self coderViewModel + notifyShowDebuggerRequest: aDebugSession + dueTo: anException + sourceString: aSourceString + sourceInterval: aSourceInterval + evaluationInfo: anEvaluationInfo +] + +{ #category : #accessing } +GtCoderViewModelRequester >> selfObject [ + ^ self coderViewModel selfObject +] + +{ #category : #accessing } +GtCoderViewModelRequester >> selfObjectHolder [ + ^ self coderViewModel selfObjectHolder +] diff --git a/src/GToolkit-Coder-UI/GtCodersModel.extension.st b/src/GToolkit-Coder-UI/GtCodersModel.extension.st index b144255a3..b5c6fb74d 100644 --- a/src/GToolkit-Coder-UI/GtCodersModel.extension.st +++ b/src/GToolkit-Coder-UI/GtCodersModel.extension.st @@ -1,11 +1,11 @@ Extension { #name : #GtCodersModel } { #category : #'*GToolkit-Coder-UI' } -GtCodersModel >> asCoderUIModel [ +GtCodersModel >> asCoderViewModel [ ^ self subclassResponsibility ] { #category : #'*GToolkit-Coder-UI' } GtCodersModel >> asElement [ - ^ self asCoderUIModel asElement + ^ self asCoderViewModel asElement ] diff --git a/src/GToolkit-Coder-UI/GtCollapsedCoderElementStencil.class.st b/src/GToolkit-Coder-UI/GtCollapsedCoderElementStencil.class.st new file mode 100644 index 000000000..ccd6a19c2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCollapsedCoderElementStencil.class.st @@ -0,0 +1,17 @@ +" +I create a basic collapsed coder element used by {{gtClass:GtCoderMethodsGroupedListElement}}. +Such collapsed coder element is created in the {{gtMethod:GtExpandableSourceCoderElement>>#newCollapsedElement}} method. +Users can set their own stencil using {{gtMethod:GtCollapsedCoderElementStencil class>>#defaultStencil:}}. + +" +Class { + #name : #GtCollapsedCoderElementStencil, + #superclass : #GtCoderElementStencil, + #category : #'GToolkit-Coder-UI-Coder - Source' +} + +{ #category : #'api - instantiation' } +GtCollapsedCoderElementStencil >> defaultElement [ + + ^ GtSourceCoderCollapsedContentElement new +] diff --git a/src/GToolkit-Coder-UI/GtCompositeDiffChange.extension.st b/src/GToolkit-Coder-UI/GtCompositeDiffChange.extension.st new file mode 100644 index 000000000..a875fbe50 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCompositeDiffChange.extension.st @@ -0,0 +1,8 @@ +Extension { #name : #GtCompositeDiffChange } + +{ #category : #'*GToolkit-Coder-UI' } +GtCompositeDiffChange >> asElement [ + ^ (GtDiffElement onChange: self) + addAptitude: GtDiffFlatAptitude; + showButtons +] diff --git a/src/GToolkit-Coder-UI/GtContextCoderVariableAptitude.class.st b/src/GToolkit-Coder-UI/GtContextCoderVariableAptitude.class.st deleted file mode 100644 index 357fdfacf..000000000 --- a/src/GToolkit-Coder-UI/GtContextCoderVariableAptitude.class.st +++ /dev/null @@ -1,73 +0,0 @@ -Class { - #name : #GtContextCoderVariableAptitude, - #superclass : #BrAptitude, - #instVars : [ - 'methodCoder' - ], - #category : #'GToolkit-Coder-UI-Looks' -} - -{ #category : #initialization } -GtContextCoderVariableAptitude >> initialize [ - super initialize. - self - addChange: - (BrLookChange new - up: [ :e | - self widget - childNamed: #bodyElement - ifFound: [ :element | element addChild: self variablesElement ] - ifNone: [ ] ]; - down: [ :e | ]; - yourself) -] - -{ #category : #accessing } -GtContextCoderVariableAptitude >> methodCoder [ - ^ methodCoder -] - -{ #category : #accessing } -GtContextCoderVariableAptitude >> methodCoder: contextCoder [ - methodCoder := contextCoder -] - -{ #category : #private } -GtContextCoderVariableAptitude >> variables [ - | vars | - vars := OrderedCollection new. - methodCoder tempNamesAndValuesDo: [ :name :value | vars add: {name. 'temporary'. value} ]. - methodCoder instanceVariableNamesAndValuesDo: [ :name :value | vars add: {name. 'instance'. value} ]. - vars sort: [ :a :b | a first < b first ]. - vars addFirst: {'self'. 'self'. methodCoder receiver}. - methodCoder stackVariableNamesAndValuesDo: [ :name :value | vars add: {name. 'stack'. value} ]. - ^ vars -] - -{ #category : #initialization } -GtContextCoderVariableAptitude >> variablesElement [ - | variablesElement | - variablesElement := BrColumnedList new. - variablesElement - addEventHandler: - (GtPhlowListToSelectHandler new - transformation: [ :each | each last ]). - variablesElement column - title: 'Variable'; - stencil: [ :each | - BrLabel new - aptitude: (BrGlamorousLabelAptitude new foreground: Color black); - text: each first asString; - hMatchParent; - vMatchParent ]. - variablesElement column - title: 'Value'; - stencil: [ :each | - BrLabel new - aptitude: (BrGlamorousLabelAptitude new foreground: Color black); - text: (each last printStringLimitedTo: 50); - hMatchParent; - vMatchParent ]. - variablesElement display: self variables. - ^ variablesElement -] diff --git a/src/GToolkit-Coder-UI/GtCreateElement.class.st b/src/GToolkit-Coder-UI/GtCreateElement.class.st deleted file mode 100644 index 9b545e2ca..000000000 --- a/src/GToolkit-Coder-UI/GtCreateElement.class.st +++ /dev/null @@ -1,45 +0,0 @@ -Class { - #name : #GtCreateElement, - #superclass : #BlElement, - #traits : 'TBrLayoutResizable', - #classTraits : 'TBrLayoutResizable classTrait', - #instVars : [ - 'definition' - ], - #category : #'GToolkit-Coder-UI-Behaviour' -} - -{ #category : #'private - instance creation' } -GtCreateElement >> buildSectionLabel: aSectionName [ - - ^ BrLabel new - aptitude: (BrGlamorousLabelAptitude new glamorousRegularFontAndSize foreground: Color gray; fontSize: 12); - text: aSectionName, ':'; - focusability: BlFocusability none; - margin: (BlInsets top: 5 right: 2); - constraintsDo: [ :c | c grid horizontal alignLeft ] -] - -{ #category : #'private - ui' } -GtCreateElement >> buttonMargin [ - ^ BlInsets top: 3 left: 0 bottom: 3 right: 5 -] - -{ #category : #'private - ui' } -GtCreateElement >> editableLabelLook [ - ^ BrGlamorousEditableLabelAptitude new - glamorousCodeFont; - defaultForeground: Color black; - fontSize: 10 -] - -{ #category : #initialization } -GtCreateElement >> initialize [ - super initialize. - self layout: BlFlowLayout vertical. - self margin: (BlInsets all: 5). - self - constraintsDo: [ :c | - c horizontal exact: 400. - c vertical fitContent ] -] diff --git a/src/GToolkit-Coder-UI/GtCreationForm.class.st b/src/GToolkit-Coder-UI/GtCreationForm.class.st new file mode 100644 index 000000000..ee2c87cd2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtCreationForm.class.st @@ -0,0 +1,323 @@ +Class { + #name : #GtCreationForm, + #superclass : #Object, + #instVars : [ + 'onAccept', + 'packageAndTagName', + 'onElementCreated', + 'onReset' + ], + #category : #'GToolkit-Coder-UI-Forms - Models' +} + +{ #category : #accessing } +GtCreationForm class >> componentName [ + ^ self subclassResponsibility +] + +{ #category : #accessing } +GtCreationForm class >> isAdditionForm [ + ^ true +] + +{ #category : #accessing } +GtCreationForm class >> priority [ + ^ self subclassResponsibility +] + +{ #category : #converting } +GtCreationForm >> asElement [ + | viewModel | + viewModel := self asGtMagritteViewModel focusFirstInputField. + self onElementCreated + ifNotNil: [ :aCallback | viewModel onElementCreated: aCallback ]. + ^ viewModel asElement +] + +{ #category : #callbacks } +GtCreationForm >> commit [ + ^ self subclassResponsibility +] + +{ #category : #callbacks } +GtCreationForm >> commitPackage [ + | aPackage | + (self packageOrganizer includesPackageNamed: packageAndTagName packageName) + ifTrue: [ + aPackage := self packageOrganizer + packageNamed: packageAndTagName packageName. + ^ packageAndTagName tagName + ifEmpty: [ aPackage ] + ifNotEmpty: [ :aTagName | + aPackage + tagNamed: aTagName + ifAbsent: [ aPackage addClassTag: aTagName ] ] ] + ifFalse: [ + aPackage := (Package named: packageAndTagName packageName). + self + forPharo12AndNewer: [ + self class packageOrganizer addPackage: aPackage] + forPharo11: [ + aPackage register ]. + ^ packageAndTagName tagName + ifEmpty: [ aPackage ] + ifNotEmpty: [ :aTagName | aPackage addClassTag: aTagName ] ] +] + +{ #category : #'api - initialization' } +GtCreationForm >> fillFormWithClass: aClass [ + +] + +{ #category : #accessing } +GtCreationForm >> ghostTextFor: aString [ + ^ BrGhostTextAttribute + for: (aString asRopedText glamorousFormEditorCodeFontAndSize foreground: Color lightGray) +] + +{ #category : #accessing } +GtCreationForm >> hasPackageAndTag [ + ^ true +] + +{ #category : #initialization } +GtCreationForm >> initialize [ + packageAndTagName := GtPackageAndTagName new +] + +{ #category : #magritte } +GtCreationForm >> magritteAcceptAction [ + + ^ super magritteAcceptAction + label: 'Create'; + onSuccessCallback: (GtMagritteCallback new + action: [ :aModel :aButton :aMemento :aDescription | + [ | aClass | + aClass := self secureCommit. + onAccept ifNotNil: [ onAccept value: aClass ] ] + on: Error do: #debug ]); + beEnabledOnValidOverallStatus +] + +{ #category : #magritte } +GtCreationForm >> magritteCancelAction [ + + ^ super magritteCancelAction + onSuccessCallback: (GtMagritteCallback new + action: [ :aModel :aButton :aMemento :aDescription | + [ onReset ifNotNil: [ onReset value ] ] + on: Error do: #debug ]); + beAlwaysEnabled +] + +{ #category : #accessing } +GtCreationForm >> onAccept [ + + ^ onAccept +] + +{ #category : #accessing } +GtCreationForm >> onAccept: aBlock [ + + onAccept := aBlock +] + +{ #category : #accessing } +GtCreationForm >> onElementCreated [ + ^ onElementCreated +] + +{ #category : #accessing } +GtCreationForm >> onElementCreated: aBlock [ + onElementCreated := aBlock +] + +{ #category : #accessing } +GtCreationForm >> onReset [ + ^ onReset +] + +{ #category : #accessing } +GtCreationForm >> onReset: aBlock [ + onReset := aBlock +] + +{ #category : #accessing } +GtCreationForm >> packageAndTagDescription [ + + ^ GtPackageAndTagDescription new + label: 'Package'; + priority: 3; + accessor: #packageAndTagName; + editorAptitude: [ BrGlamorousRegularEditorAptitude new glamorousFormEditorCodeFontAndSize ]; + labelAptitude: [ BrGlamorousLabelAptitude new glamorousFormLabelStyle ]; + beRequired +] + +{ #category : #accessing } +GtCreationForm >> packageAndTagName [ + + ^ packageAndTagName +] + +{ #category : #accessing } +GtCreationForm >> packageAndTagName: anObject [ + + packageAndTagName := anObject +] + +{ #category : #callbacks } +GtCreationForm >> secureCommit [ + "Subclasses may use a mutex to prevent race conditions." + + ^ self commit +] + +{ #category : #accessing } +GtCreationForm >> selectInNavigationModel: aNavigationModel anInstance: anInstance [ + aNavigationModel selectPackage: anInstance package. + + (anInstance package tags reject: [ :aTag | aTag isRoot]) + ifNotEmpty: [ + anInstance packageTag ifNotNil: [ :aPackageTag | + aPackageTag isRoot ifFalse: [ + anInstance packageTagName ifNotNil: [ :aPackageTagName | + aNavigationModel + selectPackageTag: (anInstance package + tagNamed: aPackageTagName) ] ] ] ]. + + aNavigationModel selectClass: anInstance +] + +{ #category : #accessing } +GtCreationForm >> taggerStencilWithCompletion: aCompletionStrategy contextMenuContentBlock: aContextMenuContentBlock [ + ^ self + taggerStencilWithCompletion: aCompletionStrategy + contextMenuContentBlock: aContextMenuContentBlock + contextMenuBlock: nil +] + +{ #category : #accessing } +GtCreationForm >> taggerStencilWithCompletion: aCompletionStrategy contextMenuContentBlock: aContextMenuContentBlock contextMenuBlock: aContextMenuBlock [ + "aContextMenuContentBlock returns an element. + aContextMenuBlock returns a BrMenu or nil." + + ^ [ :aMemento :aDescription :aForm | + | aTagger aTaggerAptitude | + aTagger := BrTagger new hFitContentLimited. + aForm hMatchParent. + aTagger + padding: (BlInsets top: -2 bottom: 5); + clipChildren: false; + aptitude: (aTaggerAptitude := GtCreationEditableTaggerAptitude new + margin: (BlInsets + top: 0 + bottom: 3 + left: 0 + right: 5); + tagLabel: [ :aTag | + | aLabel | + aLabel := BrEditor new fitContent + text: (aDescription displayStringFor: aTag name); + aptitude: (BrGlamorousEditableLabelAptitude new glamorousFormEditorCodeFontAndSize + defaultForeground: Color black); + addShortcut: (BlShortcutWithAction new + name: 'Move to previous form item'; + description: 'Moves to the previous form item. If none is found, we cycle back to the last.'; + combination: BlKeyCombination shiftTab; + action: [ :anEvent | + BlFocusFinder new + direction: BlFocusSearchDirectionBackward new; + root: aForm; + referenceElement: anEvent currentTarget; + nextFocusDo: #requestFocus ifNone: [ ] ]); + addShortcut: (BlShortcutWithAction new + name: 'Move to next form item'; + description: 'Moves to the next form item. If none is found, we cycle back to the first.'; + combination: (BlKeyCombination tab or: BlKeyCombination enter); + action: [ :anEvent | + BlFocusFinder new + direction: BlFocusSearchDirectionForward new; + root: aForm; + referenceElement: anEvent currentTarget; + nextFocusDo: #requestFocus ifNone: [ ] ]). + + aDescription blocShortcuts + ifNotNil: [ :aBlockReturningCollection | + aBlockReturningCollection value + do: [ :aShortcut | aLabel addShortcut: aShortcut ] ]. + + aCompletionStrategy + ifNotNil: [ (GtCompletionController on: aLabel strategy: aCompletionStrategy) install ]. + aContextMenuBlock + ifNil: [ aContextMenuContentBlock + ifNotNil: [ aLabel + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude + content: [ aContextMenuContentBlock + value: aTag name + value: aLabel + value: aMemento ]) ] ] + ifNotNil: [ aLabel + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude + menu: [ aContextMenuBlock + value: aTag name + value: aLabel + value: aMemento ]) ]. + aLabel ]). + aTagger + when: BrTaggerAddTagRequest + do: [ :aRequest | + | aValue | + aRequest currentTarget addTag: aRequest tag. + aValue := aTagger tags collect: #name thenSelect: #isNotEmpty. + + GtMagritteBuilderUtility + write: aValue asArray + using: aDescription + memento: aMemento + element: aTagger ]. + aTagger + when: BrTaggerRenameTagRequest + do: [ :aRequest | + (aRequest tag name = aRequest newName) not + ifTrue: [ | aValue | + aRequest currentTarget renameTag: aRequest tag to: aRequest newName. + aValue := aTagger tags collect: #name. + + GtMagritteBuilderUtility + write: aValue asArray + using: aDescription + memento: aMemento + element: aTagger ] ]. + aTagger + when: BrTaggerRemoveTagRequest + do: [ :aRequest | + | aValue | + aRequest currentTarget removeTag: aRequest tag. + aValue := aTagger tags collect: #name. + + GtMagritteBuilderUtility + write: aValue asArray + using: aDescription + memento: aMemento + element: aTagger ]. + aTagger + withAsyncSinkDo: [ :anElementSink | + anElementSink + sink: AsyncPeekSink new; + whenUpdate: [ :theTagger :aSink | + | theValues | + (theTagger tags isEmpty or: [ aSink value isChanged not ]) + ifTrue: [ theValues := aSink value currentValue. + theTagger namedTags: theValues ] ]. + (aMemento readObservableValueUsing: aDescription) observe: anElementSink ]. + GtMagritteBuilderUtility + newValidationContainerWith: aTagger + memento: aMemento + using: aDescription ] +] + +{ #category : #accessing } +GtCreationForm >> toolFor: anInstance [ + ^ GtClassCoderTool forClass: anInstance +] diff --git a/src/GToolkit-Coder-UI/GtDiffButtonsId.class.st b/src/GToolkit-Coder-UI/GtDiffButtonsId.class.st new file mode 100644 index 000000000..e26475a93 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtDiffButtonsId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtDiffButtonsId, + #superclass : #GtDiffElementId, + #category : #'GToolkit-Coder-UI-Diff' +} + +{ #category : #converting } +GtDiffButtonsId >> asSymbol [ + ^ #'diff--button-bar' +] diff --git a/src/GToolkit-Coder-UI/GtDiffElement.class.st b/src/GToolkit-Coder-UI/GtDiffElement.class.st index 21c84931a..a218c31d8 100644 --- a/src/GToolkit-Coder-UI/GtDiffElement.class.st +++ b/src/GToolkit-Coder-UI/GtDiffElement.class.st @@ -7,115 +7,82 @@ Class { 'leftElement', 'rightElement', 'syncScrollRanges', - 'diff', 'outlines', - 'separatorElement' + 'separatorElement', + 'styler', + 'buttonsElement', + 'progressLabel' ], #category : #'GToolkit-Coder-UI-Diff' } -{ #category : #examples } -GtDiffElement class >> example [ - - - ^ self on: self exampleDiff -] - -{ #category : #examples } -GtDiffElement class >> exampleDiff [ - - ^ TextDiffBuilder from: self originalString to: self newString -] - -{ #category : #examples } -GtDiffElement class >> exampleFlat [ - - - ^ (self on: self exampleDiff) aptitude: GtDiffFlatAptitude +{ #category : #'instance creation' } +GtDiffElement class >> on: anObject [ + self + deprecated: 'Please use onDiff: instead.' + transformWith: '`@receiver on: `@arg' -> '`@receiver onDiff: `@arg'. + ^ self onDiff: anObject ] -{ #category : #examples } -GtDiffElement class >> exampleShadow [ - - - ^ (self on: self exampleDiff) aptitude: GtDiffShadowAptitude +{ #category : #'instance creation' } +GtDiffElement class >> onChange: aCompositeDiffChange [ + ^ self new + change: aCompositeDiffChange; + scrollNext; + yourself ] -{ #category : #examples } -GtDiffElement class >> newString [ - - ^ String - streamContents: [ :str | - 1 to: 1000 by: 6 do: [ :i | - str - print: i; - cr; - print: i + 1; - cr; - print: i + 2; - cr ] ] +{ #category : #'instance creation' } +GtDiffElement class >> onChangeWithoutScrolling: aCompositeDiffChange [ + ^ self new + change: aCompositeDiffChange; + yourself ] { #category : #'instance creation' } -GtDiffElement class >> on: aDiffBuilder [ +GtDiffElement class >> onDiff: aDiffBuilder [ ^ self new diff: aDiffBuilder; + scrollNext; yourself ] -{ #category : #examples } -GtDiffElement class >> originalString [ - - ^ String - streamContents: [ :str | - 1 to: 1000 by: 5 do: [ :i | - str - print: i; - cr; - print: i + 1; - cr; - print: i + 2; - cr ] ] +{ #category : #'instance creation' } +GtDiffElement class >> onDiffWithoutScrolling: aDiffBuilder [ + ^ self new + diff: aDiffBuilder; + scrollNext; + yourself ] -{ #category : #private } -GtDiffElement >> buildInterface [ - syncScrollRanges := GtSyncScrollRanges createFromDiff: diff. - - leftElement text: self previousRopedText. - diff newStyler ifNotNil: [ :aStyler | leftElement styler: aStyler ]. - - rightElement text: self newRopedText. - diff newStyler ifNotNil: [ :aStyler | rightElement styler: aStyler ]. - - self updateOutlines +{ #category : #initialize } +GtDiffElement >> change: aCompositeDiffChange [ + | text | + text := aCompositeDiffChange from asRopedText glamorousCodeFont. + aCompositeDiffChange applyAttributesToInput: text. + leftElement text: text. + text := aCompositeDiffChange to asRopedText glamorousCodeFont. + aCompositeDiffChange applyAttributesToOutput: text. + rightElement text: text. + syncScrollRanges := GtSyncScrollRanges createFromChange: aCompositeDiffChange. + self updateOutlines. + self updateProgressLabel ] { #category : #initialize } GtDiffElement >> diff: aDiffBuilder [ - diff := aDiffBuilder. - self buildInterface + syncScrollRanges := GtSyncScrollRanges createFromDiff: aDiffBuilder. + leftElement text: aDiffBuilder previousRopedText. + rightElement text: aDiffBuilder newRopedText. + aDiffBuilder newStyler ifNotNil: [ :aStyler | self styler: aStyler ]. + self updateOutlines. + self updateProgressLabel ] { #category : #'event handling' } GtDiffElement >> drawMeAndChildrenOnSpartaCanvas: aCanvas [ - | stroke fill | super drawMeAndChildrenOnSpartaCanvas: aCanvas. - stroke := aCanvas stroke - alpha: 0.2; - paint: Color black; - width: 1. - fill := aCanvas fill - alpha: 0.2; - paint: Color yellow. - outlines - do: [ :each | - fill - path: (each pathOnSpartaCanvas: aCanvas of: self); - draw. - stroke - path: (each pathOnSpartaCanvas: aCanvas of: self); - draw ] + self paintOnCanvas: aCanvas ] { #category : #initialize } @@ -123,46 +90,97 @@ GtDiffElement >> editorLook [ ^ BrGlamorousCodeEditorAptitude ] +{ #category : #accessing } +GtDiffElement >> hideButtons [ + buttonsElement visibility: BlVisibility visible +] + { #category : #initialize } GtDiffElement >> initialize [ + | gridLayout comparisonElement | super initialize. - - self layout: BlLinearLayout horizontal. + + gridLayout := BlLinearLayout vertical. + self layout: gridLayout. self matchParent. - - self beNormalSize. - outlines := #(). - + self beNormalSize. + + self + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination builder primary arrowDown build; + action: [ self scrollNext ]). + self + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination builder primary arrowUp build; + action: [ self scrollPrevious ]). + + outlines := Dictionary new. + + buttonsElement := self newButtonsElement. + buttonsElement id: GtDiffButtonsId. + buttonsElement visibility: BlVisibility gone. + buttonsElement + addChildren: {self newPreviousChangeButton. + self newProgressLabel. + self newNextChangeButton}. + leftElement := self newTextElement - id: GtDiffOldTextId; - when: BlElementScrolledEvent do: [ :e | self leftScrolled ]. + id: GtDiffOldTextId; + when: BlElementScrolledEvent do: [ :e | self leftScrolled ]. + leftElement editor + addEventHandler: (BlEventHandler + on: BrTextEditorCursorMovedEvent + do: [ :e | self leftCursorChanged ]); + addEventHandler: (BlEventHandler + on: BrTextEditorCursorAddedEvent + do: [ :e | self leftCursorChanged ]). rightElement := self newTextElement - id: GtDiffNewTextId; - when: BlElementScrolledEvent do: [ :e | self rightScrolled ]. - - separatorElement := self newSeparatorElement - id: GtDiffSeparatorId. - - self addChildren: { - leftElement. - separatorElement. - rightElement - }. + id: GtDiffNewTextId; + when: BlElementScrolledEvent do: [ :e | self rightScrolled ]. + rightElement editor + addEventHandler: (BlEventHandler + on: BrTextEditorCursorMovedEvent + do: [ :e | self rightCursorChanged ]); + addEventHandler: (BlEventHandler + on: BrTextEditorCursorAddedEvent + do: [ :e | self rightCursorChanged ]). + + comparisonElement := BrHorizontalPane new + hMatchParent; + vFitContentLimited; + addChildren: {leftElement. self newSeparatorElement id: GtDiffSeparatorId. rightElement}. + + self + addChildren: {buttonsElement . comparisonElement} ] { #category : #'event handling' } GtDiffElement >> leftBottomFor: anInteger [ - | range element | + | range element index parentBounds | range := self leftRange. - anInteger < range first - ifTrue: [ ^ 0 ]. - anInteger > range last - ifTrue: [ ^ self height ]. - element := (leftElement children at: anInteger - range first + 1). - ^ (element bounds bottom + leftElement padding top) - max: 0 + parentBounds := leftElement bounds inParent inParent. + range isEmpty ifTrue: [ ^ parentBounds top + leftElement padding top ]. + index := (anInteger max: range first) min: range last. + element := leftElement children at: index - range first + 1. + ^ ((anInteger < range first + ifTrue: [ element bounds top ] + ifFalse: [ element bounds bottom ]) + leftElement bounds inParent inParent top + max: parentBounds top) min: parentBounds bottom +] + +{ #category : #'event handling' } +GtDiffElement >> leftCursorChanged [ + | newPosition leftLine range | + leftLine := self leftLineForCursor ifNil: [ self leftRange first ]. + range := syncScrollRanges rangeForLeft: leftLine. + syncScrollRanges selectRange: range. + newPosition := syncScrollRanges rightLineFor: leftLine. + (self rightRange includes: newPosition) + ifFalse: [ rightElement scrollToPosition: newPosition ]. + self updateProgressLabel. + self invalidate ] { #category : #accessing } @@ -170,27 +188,24 @@ GtDiffElement >> leftEditorLook: aLook [ leftElement aptitude: aLook ] +{ #category : #'event handling' } +GtDiffElement >> leftLineForCursor [ + leftElement editor cursors + do: [ :each | ^ leftElement text asString lineNumberCorrespondingToIndex: each position + 1 ]. + ^ nil +] + { #category : #private } GtDiffElement >> leftRange [ | first | - first := (leftElement instVarNamed: 'layoutPositionsRange') - ifNil: [ 1 ] - ifNotNil: [ :interval | interval first ]. + first := (leftElement findMinMaxChildLayoutPositions + ifNil: [ 1 ] + ifNotNil: [ :interval | interval first ]) max: 1. ^ first to: first + leftElement children size - 1 ] { #category : #'event handling' } GtDiffElement >> leftScrolled [ - | newPosition | - newPosition := syncScrollRanges rightLineFor: self leftRange first. - - (self rightRange includes: newPosition) - ifTrue: [ | offset | - offset := rightElement children first bounds position y. - 1 to: newPosition - self rightRange first do: [ :i | offset := offset + (rightElement children at: i) height ]. - "rightElement scrollBy: 0@offset" ] - ifFalse: [ "rightElement scrollToPosition: newPosition" ]. - self updateOutlines ] @@ -201,27 +216,83 @@ GtDiffElement >> leftStyler: aStyler [ { #category : #'event handling' } GtDiffElement >> leftTopFor: anInteger [ - | range element | + | range element index | range := self leftRange. - anInteger < range first - ifTrue: [ ^ 0 ]. - anInteger > range last - ifTrue: [ ^ self height ]. - element := (leftElement children at: anInteger - range first + 1). - ^ (element bounds top + leftElement padding top) - max: 0 + range isEmpty + ifTrue: [ ^ leftElement bounds inParent inParent top + leftElement padding top ]. + index := (anInteger max: range first) min: range last. + element := leftElement children at: index - range first + 1. + ^ (anInteger > range last + ifTrue: [ element bounds bottom ] + ifFalse: [ element bounds top ]) + leftElement bounds inParent inParent top + max: leftElement bounds inParent inParent top ] -{ #category : #private } -GtDiffElement >> newRopedText [ - ^ diff newRopedText +{ #category : #accessing } +GtDiffElement >> moveButtonsToBottom [ + buttonsElement removeFromParent. + buttonsElement margin: (BlInsets top: 5). + self addChild: buttonsElement. + self showButtons +] + +{ #category : #'event handling' } +GtDiffElement >> moveCursorIn: textEditor toLine: anInteger [ + | string index | + string := textEditor text asString. + index := (string intervalOfLine: anInteger) first - 1. + index < 0 + ifTrue: [ index := anInteger <= 0 ifTrue: [ 0 ] ifFalse: [ string size ] ]. + textEditor editor moveCursorTo: index +] + +{ #category : #initialize } +GtDiffElement >> newButtonsElement [ + ^ BrHorizontalPane new + vFitContent; + hMatchParent; + margin: (BlInsets bottom: 5); + alignCenter; + constraintsDo: [ :c | c grid horizontal span: 3 ] +] + +{ #category : #initialize } +GtDiffElement >> newNextChangeButton [ + ^ BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + fitContent; + beTinySize; + margin: (BlInsets left: 5); + icon: BrGlamorousVectorIcons down; + label: 'Next change'; + action: [ :aButton | self scrollNext ] +] + +{ #category : #initialize } +GtDiffElement >> newPreviousChangeButton [ + ^ BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + fitContent; + beTinySize; + icon: BrGlamorousVectorIcons up; + label: 'Previous change'; + action: [ :aButton | self scrollPrevious ] +] + +{ #category : #initialize } +GtDiffElement >> newProgressLabel [ + ^ progressLabel := BrLabel new + fitContent; + alignCenter; + margin: (BlInsets left: 5); + aptitude: BrGlamorousLabelAptitude ] { #category : #initialize } GtDiffElement >> newSeparatorElement [ ^ BlElement new constraintsDo: [ :c | - c vertical matchParent. + c vertical exact: 1. c horizontal exact: 0 ] yourself ] @@ -229,16 +300,25 @@ GtDiffElement >> newSeparatorElement [ { #category : #initialize } GtDiffElement >> newTextElement [ ^ BrEditor new - focusability: BlFocusability none; beReadOnlyWithSelection; - matchParent; + hMatchParent; + vFitContentLimited; aptitude: self editorLook ] { #category : #'geometry hooks' } -GtDiffElement >> notifyExtentChanged [ - super notifyExtentChanged. - outlines := #(). +GtDiffElement >> onExtentChanged [ + super onExtentChanged. + + outlines := Dictionary new. + self updateOutlines +] + +{ #category : #'geometry hooks' } +GtDiffElement >> onLayout: aBounds context: aBlElementBoundsUpdateContext [ + super onLayout: aBounds context: aBlElementBoundsUpdateContext. + + outlines := Dictionary new. self updateOutlines ] @@ -249,8 +329,8 @@ GtDiffElement >> outlineFor: aSyncScrollRange [ leftBottom := self leftBottomFor: aSyncScrollRange leftLast. rightTop := self rightTopFor: aSyncScrollRange rightFirst + 1. rightBottom := self rightBottomFor: aSyncScrollRange rightLast. - leftBounds := leftElement bounds. - rightBounds := rightElement bounds. + leftBounds := leftElement bounds inParent inParent. + rightBounds := rightElement bounds inParent inParent. vertices := OrderedCollection new: 9. vertices add: leftBounds left @ leftTop; @@ -262,25 +342,71 @@ GtDiffElement >> outlineFor: aSyncScrollRange [ add: leftBounds right @ leftBottom; add: leftBounds left @ leftBottom; add: leftBounds left @ leftTop. - ^ BlPolyline vertices: vertices + ^ BlPolylineGeometry vertices: vertices ] -{ #category : #private } -GtDiffElement >> previousRopedText [ - ^ diff previousRopedText +{ #category : #'event handling' } +GtDiffElement >> paintMeAndChildrenOn: aCompositorPainter offset: anOffset [ + | aCanvas | + super paintMeAndChildrenOn: aCompositorPainter offset: anOffset. + + aCanvas := aCompositorPainter canvas. + + aCanvas transform + by: [ :t | t translateBy: anOffset ] + during: [ self paintOnCanvas: aCanvas ] +] + +{ #category : #'event handling' } +GtDiffElement >> paintOnCanvas: aCanvas [ + | stroke selectedStroke fill | + stroke := aCanvas stroke + alpha: 0.2; + paint: Color black; + width: 1. + selectedStroke := aCanvas stroke + alpha: 0.6; + paint: BrGlamorousColors primaryBorderColor; + width: 2. + fill := aCanvas fill + alpha: 0.2; + paint: Color yellow. + ^ outlines + keysAndValuesDo: [ :range :each | + fill + path: (each pathOnSpartaCanvas: aCanvas of: self); + draw. + (range isSelected ifTrue: [ selectedStroke ] ifFalse: [ stroke ]) + path: (each pathOnSpartaCanvas: aCanvas of: self); + draw ] ] { #category : #'event handling' } GtDiffElement >> rightBottomFor: anInteger [ - | range element | + | range element index parentBounds | range := self rightRange. - anInteger < range first - ifTrue: [ ^ 0 ]. - anInteger > range last - ifTrue: [ ^ self height ]. - element := (rightElement children at: anInteger - range first + 1). - ^ (element bounds bottom + rightElement padding top) - max: 0 + parentBounds := rightElement bounds inParent inParent. + range isEmpty ifTrue: [ ^ parentBounds top + rightElement padding top ]. + index := (anInteger max: range first) min: range last. + element := rightElement children at: index - range first + 1. + ^ ((anInteger < range first + ifTrue: [ element bounds top ] + ifFalse: [ element bounds bottom ]) + + rightElement bounds inParent inParent top max: parentBounds top) + min: parentBounds bottom +] + +{ #category : #'event handling' } +GtDiffElement >> rightCursorChanged [ + | newPosition rightLine range | + rightLine := self rightLineForCursor ifNil: [ self rightRange first ]. + range := syncScrollRanges rangeForRight: rightLine. + syncScrollRanges selectRange: range. + newPosition := syncScrollRanges leftLineFor: rightLine. + (self leftRange includes: newPosition) + ifFalse: [ leftElement scrollToPosition: newPosition ]. + self updateProgressLabel. + self invalidate ] { #category : #accessing } @@ -288,27 +414,24 @@ GtDiffElement >> rightEditorLook: aLook [ rightElement aptitude: aLook ] +{ #category : #'event handling' } +GtDiffElement >> rightLineForCursor [ + rightElement editor cursors + do: [ :each | ^ rightElement text asString lineNumberCorrespondingToIndex: each position + 1 ]. + ^ nil +] + { #category : #private } GtDiffElement >> rightRange [ | first | - first := (rightElement instVarNamed: 'layoutPositionsRange') - ifNil: [ 1 ] - ifNotNil: [ :interval | interval first ]. + first := (rightElement findMinMaxChildLayoutPositions + ifNil: [ 1 ] + ifNotNil: [ :interval | interval first ]) max: 1. ^ first to: first + rightElement children size - 1 ] { #category : #'event handling' } GtDiffElement >> rightScrolled [ - | newPosition | - newPosition := syncScrollRanges leftLineFor: self rightRange first. - - (self leftRange includes: newPosition) - ifTrue: [ | offset | - offset := leftElement children first bounds position y. - 1 to: newPosition - self leftRange first do: [ :i | offset := offset + (leftElement children at: i) height ]. - "leftElement scrollBy: 0@ offset" ] - ifFalse: [ "leftElement scrollToPosition: newPosition" ]. - self updateOutlines ] @@ -319,27 +442,74 @@ GtDiffElement >> rightStyler: aStyler [ { #category : #'event handling' } GtDiffElement >> rightTopFor: anInteger [ - | range element | + | range element index | range := self rightRange. - anInteger < range first - ifTrue: [ ^ 0 ]. - anInteger > range last - ifTrue: [ ^ self height ]. - element := (rightElement children at: anInteger - range first + 1). - ^ (element bounds top + rightElement padding top) - max: 0 + range isEmpty ifTrue: [ ^ rightElement bounds inParent inParent top + rightElement padding top ]. + index := (anInteger max: range first) min: range last. + element := rightElement children at: index - range first + 1. + ^ (anInteger > range last + ifTrue: [ element bounds bottom ] + ifFalse: [ element bounds top ]) + rightElement bounds inParent inParent top + max: rightElement bounds inParent inParent top +] + +{ #category : #'event handling' } +GtDiffElement >> scrollNext [ + | range | + range := syncScrollRanges selectNext. + range ifNil: [ ^ self ]. + leftElement scrollToPosition: range leftFirst + 1. + rightElement scrollToPosition: range rightFirst + 1. + self updateProgressLabel +] + +{ #category : #'event handling' } +GtDiffElement >> scrollPrevious [ + | range | + range := syncScrollRanges selectPrevious. + range ifNil: [ ^ self ]. + leftElement scrollToPosition: range leftFirst + 1. + rightElement scrollToPosition: range rightFirst + 1. + self updateProgressLabel +] + +{ #category : #accessing } +GtDiffElement >> showButtons [ + buttonsElement visibility: BlVisibility visible +] + +{ #category : #accessing } +GtDiffElement >> styler [ + ^ styler +] + +{ #category : #accessing } +GtDiffElement >> styler: anObject [ + styler := anObject. + styler + ifNotNil: [ leftElement styler: styler. + rightElement styler: styler ] ] { #category : #'event handling' } GtDiffElement >> updateOutlines [ | ranges | - outlines := OrderedCollection new. + outlines := Dictionary new. ranges := syncScrollRanges rangesForLeft: self leftRange andRight: self rightRange. ranges do: [ :each | each isDifference - ifTrue: [ outlines add: (self outlineFor: each) ] ]. + ifTrue: [ outlines at: each put: (self outlineFor: each) ] ]. self invalidate ] + +{ #category : #initialize } +GtDiffElement >> updateProgressLabel [ + | progress | + progress := syncScrollRanges selectedProgress. + progressLabel + text: (progress first printString , '/' , progress last printString) asRopedText + glamorousRegularFont +] diff --git a/src/GToolkit-Coder-UI/GtDiffElementWithLabelStencil.class.st b/src/GToolkit-Coder-UI/GtDiffElementWithLabelStencil.class.st new file mode 100644 index 000000000..d2ec0d173 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtDiffElementWithLabelStencil.class.st @@ -0,0 +1,142 @@ +Class { + #name : #GtDiffElementWithLabelStencil, + #superclass : #BrStencil, + #instVars : [ + 'fromLabelText', + 'toLabelText', + 'shouldHideDiff', + 'change', + 'styler' + ], + #category : #'GToolkit-Coder-UI-Diff' +} + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> change [ + ^ change +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> change: aGtCompositeDiffChange [ + change := aGtCompositeDiffChange +] + +{ #category : #'api - instantiation' } +GtDiffElementWithLabelStencil >> create [ + | headerElement diffContainer | + + headerElement := BrHorizontalPane new + hMatchParent; + vFitContent. + + headerElement + addChild: self createFromLabel; + addChild: self createToLabel. + + diffContainer := self createDiffContainer. + + ^ BrVerticalPane new + matchParent; + addChild: headerElement; + addChild: diffContainer asScrollableElement +] + +{ #category : #'api - instantiation' } +GtDiffElementWithLabelStencil >> createDiffContainer [ + | diffContainer | + diffContainer := BrVerticalPane new + hMatchParent; + vFitContentLimited. + + self shouldHideDiff ifFalse: [ + diffContainer addChild: self createPanesDiffElement ]. + + ^ diffContainer +] + +{ #category : #'building - widgets' } +GtDiffElementWithLabelStencil >> createFromLabel [ + ^ BrLabel new + margin: (BlInsets left: 9); + aptitude: BrGlamorousLabelAptitude; + text: (self fromLabelText asRopedText glamorousCodeSmallSize + foreground: BrGlamorousColors defaultButtonTextColor); + hMatchParent +] + +{ #category : #'building - widgets' } +GtDiffElementWithLabelStencil >> createPanesDiffElement [ + | diffWrapper | + diffWrapper := BrVerticalPane new. " + diffWrapper margin: (BlInsets all: 3)." + diffWrapper hMatchParent. + diffWrapper vFitContentLimited. + + diffWrapper + addChild: ((GtEpiceaDiffElement onChange: self change) + aptitude: GtDiffFlatAptitude; + styler: styler; + vFitContentLimited; + beSmallSize; + moveButtonsToBottom). + + ^ diffWrapper +] + +{ #category : #'building - widgets' } +GtDiffElementWithLabelStencil >> createToLabel [ + ^ BrLabel new + margin: (BlInsets left: 16); + aptitude: BrGlamorousLabelAptitude; + text: (self toLabelText asRopedText glamorousCodeSmallSize + foreground: BrGlamorousColors defaultButtonTextColor); + hMatchParent +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> fromLabelText [ + ^ fromLabelText +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> fromLabelText: aString [ + + fromLabelText := aString +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> hideDiff [ + shouldHideDiff := true +] + +{ #category : #testing } +GtDiffElementWithLabelStencil >> shouldHideDiff [ + ^ shouldHideDiff ifNil: [ false ] +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> shouldHideDiff: anObject [ + + shouldHideDiff := anObject +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> styler [ + ^ styler +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> styler: anObject [ + styler := anObject +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> toLabelText [ + ^ toLabelText +] + +{ #category : #accessing } +GtDiffElementWithLabelStencil >> toLabelText: aString [ + + toLabelText := aString +] diff --git a/src/GToolkit-Coder-UI/GtDiffFlatAptitude.class.st b/src/GToolkit-Coder-UI/GtDiffFlatAptitude.class.st index 9a737c959..c999829a5 100644 --- a/src/GToolkit-Coder-UI/GtDiffFlatAptitude.class.st +++ b/src/GToolkit-Coder-UI/GtDiffFlatAptitude.class.st @@ -9,31 +9,51 @@ GtDiffFlatAptitude >> initialize [ super initialize. self add: (BrLayoutResizerAptitude new - inherit; inherit: GtDiffOldTextId; inherit: GtDiffNewTextId). + self add: (BrSizeAdjustmentAptitude new + normal: [ :aStyle | aStyle padding: (BlInsets all: 10) ]; + small: [ :aStyle | aStyle padding: (BlInsets all: 8) ]; + tiny: [ :aStyle | aStyle padding: (BlInsets all: 6) ]; + mini: [ :aStyle | aStyle padding: (BlInsets all: 6) ]). + + self add: (self newSeparatorLook // GtDiffSeparatorId). + self add: (self newTextLook // GtDiffOldTextId). self add: (self newTextLook // GtDiffNewTextId) ] +{ #category : #accessing } +GtDiffFlatAptitude >> newSeparatorLook [ + ^ BrSizeAdjustmentAptitude new + normal: [ :aStyle | aStyle hExact: 40 ]; + small: [ :aStyle | aStyle hExact: 25 ]; + tiny: [ :aStyle | aStyle hExact: 20 ]; + mini: [ :aStyle | aStyle hExact: 10 ] +] + { #category : #'instance creation' } GtDiffFlatAptitude >> newTextLook [ ^ BrSizeAdjustmentAptitude new normal: [ :aStyle | aStyle padding: (BlInsets all: 10); - do: [ :aWidget | aWidget aptitude glamorousCodeSize ] ]; + do: [ :aWidget | aWidget aptitude glamorousCodeSize ] + after: [ ] ]; small: [ :aStyle | aStyle padding: (BlInsets all: 6); - do: [ :aWidget | aWidget aptitude glamorousCodeSmallSize ] ]; + do: [ :aWidget | aWidget aptitude glamorousCodeSmallSize ] + after: [ ] ]; tiny: [ :aStyle | aStyle padding: (BlInsets all: 4); - do: [ :aWidget | aWidget aptitude glamorousCodeTinySize ] ]; + do: [ :aWidget | aWidget aptitude glamorousCodeTinySize ] + after: [ ] ]; mini: [ :aStyle | aStyle padding: (BlInsets all: 2); - do: [ :aWidget | aWidget aptitude glamorousCodeMiniSize ] ] + do: [ :aWidget | aWidget aptitude glamorousCodeMiniSize ] + after: [ ] ] ] diff --git a/src/GToolkit-Coder-UI/GtDiffShadowAptitude.class.st b/src/GToolkit-Coder-UI/GtDiffShadowAptitude.class.st index 18341f689..613ad39cf 100644 --- a/src/GToolkit-Coder-UI/GtDiffShadowAptitude.class.st +++ b/src/GToolkit-Coder-UI/GtDiffShadowAptitude.class.st @@ -9,7 +9,6 @@ GtDiffShadowAptitude >> initialize [ super initialize. self add: (BrLayoutResizerAptitude new - inherit; inherit: GtDiffOldTextId; inherit: GtDiffNewTextId). @@ -53,17 +52,21 @@ GtDiffShadowAptitude >> newTextLook [ normal: [ :aStyle | aStyle padding: (BlInsets all: 10); - do: [ :aWidget | aWidget aptitude glamorousCodeSize ] ]; + do: [ :aWidget | aWidget aptitude glamorousCodeSize ] + after: [ ] ]; small: [ :aStyle | aStyle padding: (BlInsets all: 6); - do: [ :aWidget | aWidget aptitude glamorousCodeSmallSize ] ]; + do: [ :aWidget | aWidget aptitude glamorousCodeSmallSize ] + after: [ ] ]; tiny: [ :aStyle | aStyle padding: (BlInsets all: 4); - do: [ :aWidget | aWidget aptitude glamorousCodeTinySize ] ]; + do: [ :aWidget | aWidget aptitude glamorousCodeTinySize ] + after: [ ] ]; mini: [ :aStyle | aStyle padding: (BlInsets all: 2); - do: [ :aWidget | aWidget aptitude glamorousCodeMiniSize ] ] + do: [ :aWidget | aWidget aptitude glamorousCodeMiniSize ] + after: [ ] ] ] diff --git a/src/GToolkit-Coder-UI/GtExampleResult.extension.st b/src/GToolkit-Coder-UI/GtExampleResult.extension.st new file mode 100644 index 000000000..955b4ee17 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtExampleResult.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #GtExampleResult } + +{ #category : #'*GToolkit-Coder-UI' } +GtExampleResult >> asGtExampleResultPreviewElementFor: anExample [ + ^ returnValue asGtExampleResultPreviewElementFor: anExample +] diff --git a/src/GToolkit-Coder-UI/GtExampleWithResult.extension.st b/src/GToolkit-Coder-UI/GtExampleWithResult.extension.st new file mode 100644 index 000000000..24c7deff6 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtExampleWithResult.extension.st @@ -0,0 +1,7 @@ +Extension { #name : #GtExampleWithResult } + +{ #category : #'*GToolkit-Coder-UI' } +GtExampleWithResult >> asGtExampleResultPreviewElement [ + + ^ self result asGtExampleResultPreviewElementFor: self +] diff --git a/src/GToolkit-Coder-UI/GtExpandableCoderViewModel.class.st b/src/GToolkit-Coder-UI/GtExpandableCoderViewModel.class.st new file mode 100644 index 000000000..e1fb72a52 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtExpandableCoderViewModel.class.st @@ -0,0 +1,167 @@ +Class { + #name : #GtExpandableCoderViewModel, + #superclass : #GtSingleCoderViewModel, + #instVars : [ + 'expanded', + 'shouldHaveHeader', + 'hasFocus' + ], + #category : #'GToolkit-Coder-UI-Coder - Basic' +} + +{ #category : #converting } +GtExpandableCoderViewModel >> asExpandedOnlyElement [ + "Create an element for just the expanded coder without expander" + + ^ GtSourceCoderExpandedOnlyElement new + textualCoderViewModel: self asCoderViewModel; + yourself +] + +{ #category : #'api - expansion' } +GtExpandableCoderViewModel >> collapse [ + self expanded: false +] + +{ #category : #'api - text' } +GtExpandableCoderViewModel >> collapsedTextPromise [ + "Return a text that should be displayed in the collapsed state" + + + ^ coderModel collapsedTextPromise +] + +{ #category : #'private - addons' } +GtExpandableCoderViewModel >> computeHeaderContextMenuAstAddOns [ + + | aCoderModel newAddOns pragmas | + aCoderModel := self coderModel. + pragmas := aCoderModel + pragmasNamed: aCoderModel class contextHeaderMenuAddOnsPragma + inHierarchy: aCoderModel class. + newAddOns := aCoderModel newAddOns. + pragmas + do: [ :eachPragma | + [ aCoderModel + perform: eachPragma methodSelector + withEnoughArguments: {aCoderModel selector. + newAddOns. + self} ] + on: Error + do: [ :anError | + "emit as a beacon signal" + anError emit. + NonInteractiveTranscript stderr + nextPut: $[; + print: eachPragma method printString; + nextPut: $]; + space; + print: anError; + cr ] ]. + ^ newAddOns +] + +{ #category : #'api - expansion' } +GtExpandableCoderViewModel >> expand [ + self expanded: true +] + +{ #category : #'api - expansion' } +GtExpandableCoderViewModel >> expanded [ + ^ expanded +] + +{ #category : #'api - expansion' } +GtExpandableCoderViewModel >> expanded: aBoolean [ + self expanded: aBoolean from: self +] + +{ #category : #'api - expansion' } +GtExpandableCoderViewModel >> expanded: aBoolean from: aSourceObject [ + expanded = aBoolean + ifTrue: [ ^ self ]. + + expanded := aBoolean. + self notifyExpansionChanged: expanded from: aSourceObject +] + +{ #category : #'api - focus' } +GtExpandableCoderViewModel >> focused [ + + + ^ hasFocus +] + +{ #category : #'api - focus' } +GtExpandableCoderViewModel >> focused: aBoolean [ + self focused: aBoolean from: self +] + +{ #category : #'api - focus' } +GtExpandableCoderViewModel >> focused: aBoolean from: aSourceObject [ + hasFocus = aBoolean + ifTrue: [ ^ self ]. + + hasFocus := aBoolean. + self notifyFocusChanged: hasFocus from: aSourceObject. + + codersUIModel ifNotNil: [ :theCoders | + aBoolean + ifTrue: [ theCoders focusCoderUIModel: self ] + ifFalse: [ theCoders unfocusCoderUIModel: self ] ] +] + +{ #category : #'api - header' } +GtExpandableCoderViewModel >> headerElementClass [ + "Return an class of an element that should represent a header of the coder. + The used element must implement {{gtClass:TGtWithTextualCoderViewModel}} trait." + + + ^ nil +] + +{ #category : #initialization } +GtExpandableCoderViewModel >> initialize [ + super initialize. + expanded := true. + hasFocus := false. + shouldHaveHeader := true +] + +{ #category : #'private - notifying' } +GtExpandableCoderViewModel >> notifyExpansionChanged: aBoolean from: aSourceObject [ + self + announce: (GtExpandableCoderViewModelExpansionChanged new + textualCoderViewModel: self; + expanded: aBoolean; + source: aSourceObject) +] + +{ #category : #'private - notifying' } +GtExpandableCoderViewModel >> notifyFocusChanged: aBoolean from: aSourceObject [ + self announcer announce: (GtTextualCoderViewModelFocusChanged new + focused: aBoolean; + source: aSourceObject) +] + +{ #category : #'api - header' } +GtExpandableCoderViewModel >> shouldHaveHeader: aBoolean [ + shouldHaveHeader := aBoolean +] + +{ #category : #'api - header' } +GtExpandableCoderViewModel >> wantsHeader [ + + + ^ shouldHaveHeader and: [ self headerElementClass notNil ] +] + +{ #category : #'api - header' } +GtExpandableCoderViewModel >> withHeader [ + self shouldHaveHeader: true +] + +{ #category : #'api - header' } +GtExpandableCoderViewModel >> withoutHeader [ + self shouldHaveHeader: false +] diff --git a/src/GToolkit-Coder-UI/GtExpandableCoderViewModelExpansionChanged.class.st b/src/GToolkit-Coder-UI/GtExpandableCoderViewModelExpansionChanged.class.st new file mode 100644 index 000000000..4b5a12265 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtExpandableCoderViewModelExpansionChanged.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtExpandableCoderViewModelExpansionChanged, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'expanded', + 'source' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' +} + +{ #category : #accessing } +GtExpandableCoderViewModelExpansionChanged >> expanded [ + ^ expanded +] + +{ #category : #accessing } +GtExpandableCoderViewModelExpansionChanged >> expanded: aBoolean [ + expanded := aBoolean +] + +{ #category : #accessing } +GtExpandableCoderViewModelExpansionChanged >> source [ + ^ source +] + +{ #category : #accessing } +GtExpandableCoderViewModelExpansionChanged >> source: anObject [ + source := anObject +] diff --git a/src/GToolkit-Coder-UI/GtExpandableSourceCoderElement.class.st b/src/GToolkit-Coder-UI/GtExpandableSourceCoderElement.class.st index cd9827d6a..05677185c 100644 --- a/src/GToolkit-Coder-UI/GtExpandableSourceCoderElement.class.st +++ b/src/GToolkit-Coder-UI/GtExpandableSourceCoderElement.class.st @@ -1,14 +1,31 @@ Class { #name : #GtExpandableSourceCoderElement, #superclass : #BrExpander, + #traits : 'TGtWithTextualCoderViewModel', + #classTraits : 'TGtWithTextualCoderViewModel classTrait', #instVars : [ 'collapsedElement', 'expandedElement', - 'coderUIModel' + 'modificationIndicator', + 'collapsedElementStencil', + 'expandedElementStencil' + ], + #classVars : [ + 'DefaultExpandedElementStencilClass' ], #category : #'GToolkit-Coder-UI-Coder - Source' } +{ #category : #accessing } +GtExpandableSourceCoderElement class >> defaultExpandedElementStencilClass [ + ^ DefaultExpandedElementStencilClass +] + +{ #category : #accessing } +GtExpandableSourceCoderElement class >> defaultExpandedElementStencilClass: anObject [ + DefaultExpandedElementStencilClass := anObject +] + { #category : #private } GtExpandableSourceCoderElement >> addCodersCoderLook: aSourceCoder to: anElement [ aSourceCoder coderLook @@ -21,76 +38,67 @@ GtExpandableSourceCoderElement >> asVerticallyResizableDo: aBlock [ ] { #category : #private } -GtExpandableSourceCoderElement >> assignCollapsedCoder: aSourceCoder to: aCollapsedElement [ - aCollapsedElement coderUIModel: aSourceCoder. +GtExpandableSourceCoderElement >> assignCollapsedCoder: aTextualCoderViewModel to: aCollapsedElement [ + aCollapsedElement textualCoderViewModel: aTextualCoderViewModel. "Initialize look just once" aCollapsedElement aptitude ifNil: [ - self addCodersCoderLook: aSourceCoder to: aCollapsedElement. - aCollapsedElement addAptitude: GtSourceCoderCollapsedTextAndExampleAptitude ] + self addCodersCoderLook: aTextualCoderViewModel to: aCollapsedElement. + "aCollapsedElement addAptitude: GtSourceCoderCollapsedAddOnsAptitude" ] ] { #category : #private } -GtExpandableSourceCoderElement >> assignExpandedCoder: aSourceCoder to: anExpandedElement [ - anExpandedElement coderUIModel: aSourceCoder. +GtExpandableSourceCoderElement >> assignExpandedCoder: aTextualCoderViewModel to: anExpandedElement [ + anExpandedElement textualCoderViewModel: aTextualCoderViewModel. "Initialize look just once" anExpandedElement aptitude ifNil: [ - self addCodersCoderLook: aSourceCoder to: anExpandedElement. - anExpandedElement addAptitude: GtSourceCoderEditorAptitude ] + self addCodersCoderLook: aTextualCoderViewModel to: anExpandedElement ] ] { #category : #accessing } -GtExpandableSourceCoderElement >> coderUIModel [ - - self - assert: [ coderUIModel isNotNil ] - description: [ 'Coder is not set!' ]. - - ^ coderUIModel +GtExpandableSourceCoderElement >> coderViewModel [ + + ^ self textualCoderViewModel ] { #category : #accessing } -GtExpandableSourceCoderElement >> coderUIModel: aCoderUIModel [ - self - assert: [ aCoderUIModel isNotNil ] - description: [ 'Coder must not be nil' ]. - - coderUIModel == aCoderUIModel - ifTrue: [ ^ self ]. +GtExpandableSourceCoderElement >> coderViewModel: aCoderViewModel [ + self textualCoderViewModel: aCoderViewModel +] - coderUIModel - ifNotNil: [ :aPreviousCoder | - aPreviousCoder unsubscribe: self. - aPreviousCoder coderModel unsubscribe: self ]. +{ #category : #accessing } +GtExpandableSourceCoderElement >> collapsedElementStencil: anObject [ + collapsedElementStencil := anObject +] - coderUIModel := aCoderUIModel. +{ #category : #'private - instance creation' } +GtExpandableSourceCoderElement >> createBasicCollapsedElement [ + ^ collapsedElementStencil asElement +] - coderUIModel expanded - ifTrue: [ expandedElement - ifNotNil: [ :anElement | self assignExpandedCoder: aCoderUIModel to: anElement ]. - collapsedElement - ifNotNil: [ :anElement | self markDirty: anElement as: true ] ] - ifFalse: [ collapsedElement - ifNotNil: - [ :anElement | self assignCollapsedCoder: aCoderUIModel to: anElement ]. - expandedElement - ifNotNil: [ :anElement | self markDirty: anElement as: true ] ]. +{ #category : #'private - instance creation' } +GtExpandableSourceCoderElement >> createBasicExpandedElement [ + ^ expandedElementStencil asElement +] - coderUIModel focused - ifFalse: [ self loseFocus ]. +{ #category : #initialization } +GtExpandableSourceCoderElement >> defaultCollapsedElementStencil [ + ^ GtCollapsedCoderElementStencil new +] - self expanded: coderUIModel expanded. +{ #category : #initialization } +GtExpandableSourceCoderElement >> defaultExpandedElementStencil [ + ^ self class defaultExpandedElementStencilClass + ifNil: [ GtExpandedCoderElementStencil new ] + ifNotNil: [ :aClass | aClass new ] +] - coderUIModel coder subscribeToSystem. - coderUIModel coder announcer weak - when: GtCoderRequestFocus send: #requestFocusAsyncronously to: self; - when: GtCoderMethodRemoved send: #onMethodRemoved: to: self. - - coderUIModel weak - when: GtTextualCoderViewModelExpansionChanged send: #onViewModelExpansionChanged: to: self +{ #category : #accessing } +GtExpandableSourceCoderElement >> expandedElementStencil: anObject [ + expandedElementStencil := anObject ] { #category : #accessing } @@ -101,30 +109,45 @@ GtExpandableSourceCoderElement >> gtAllShortcutsFor: aView [ ^ aView columnedList title: 'All shortcuts' translated; priority: 10; - items: [ self shortcuts , coderUIModel addOns shortcuts ]; - column: 'Key' item: [ :each | each combination gtDisplayString ]; - column: 'Action' item: [ :each | each action asString ] + items: [ self shortcuts , textualCoderViewModel addOns wait shortcuts ]; + column: 'Key' text: [ :each | each combination gtDisplayString ]; + column: 'Action' text: [ :each | each action asString ] ] { #category : #initialization } GtExpandableSourceCoderElement >> initialize [ super initialize. + collapsedElementStencil := self defaultCollapsedElementStencil. + expandedElementStencil := self defaultExpandedElementStencil. + self vFitContent; hMatchParent; margin: (BlInsets all: 4); - aptitude: GtCoderExpanderAptitude; + aptitude: (GtCoderExpanderAptitude new padding: BlInsets empty); beInSingleCompositionLayer. self states withExpansion. - self when: BrExpandedEvent do: [ self onExpanded ]. - self when: BrCollapsedEvent do: [ self onCollapsed ]. + self when: BrExpandedEvent do: [ :anEvent | anEvent target onExpanded ]. + self when: BrCollapsedEvent do: [ :anEvent | anEvent target onCollapsed ]. self header: [ collapsedElement := self newCollapsedElement ]; - content: [ expandedElement := self newExpandedElement ] + content: [ expandedElement := self newExpandedElement ]. + + self + when: BlClickEvent + do: [ :anEvent | + self isCollapsed + ifTrue: [ anEvent consumed: true. + self expand. + self textualCoderViewModel focused: true ] ]. + + modificationIndicator := self newModificationIndicator. + modificationIndicator visibility: BlVisibility gone. + self addChild: modificationIndicator ] { #category : #private } @@ -139,34 +162,39 @@ GtExpandableSourceCoderElement >> markDirty: anElement as: aBoolean [ anElement userData at: #coderDirty put: aBoolean ] -{ #category : #private } +{ #category : #'private - instance creation' } GtExpandableSourceCoderElement >> newCollapsedElement [ | aCollapsedElement | - aCollapsedElement := GtSourceCoderCollapsedContentElement new. + aCollapsedElement := self createBasicCollapsedElement. - coderUIModel + textualCoderViewModel ifNotNil: [ :aCoder | self assignCollapsedCoder: aCoder to: aCollapsedElement ]. ^ aCollapsedElement ] -{ #category : #private } +{ #category : #'private - instance creation' } GtExpandableSourceCoderElement >> newExpandedElement [ | anExpandedElement | - anExpandedElement := GtSourceCoderExpandedContentElement new. + anExpandedElement := self createBasicExpandedElement. - coderUIModel + textualCoderViewModel ifNotNil: [ :aCoder | self assignExpandedCoder: aCoder to: anExpandedElement ]. ^ anExpandedElement ] +{ #category : #'private - instance creation' } +GtExpandableSourceCoderElement >> newModificationIndicator [ + ^ GtSourceCoderModificationIndicator new withVerticalLeftIgnoredLayout +] + { #category : #private } GtExpandableSourceCoderElement >> onCollapsed [ self beInSingleCompositionLayer. - coderUIModel ifNotNil: [ :aCoderUIModel | - aCoderUIModel expanded: false. + textualCoderViewModel ifNotNil: [ :aCoderUIModel | + aCoderUIModel expanded: false from: self. (collapsedElement notNil and: [ self isDirty: collapsedElement ]) ifTrue: [ self assignCollapsedCoder: aCoderUIModel to: collapsedElement ] ] ] @@ -175,19 +203,43 @@ GtExpandableSourceCoderElement >> onCollapsed [ GtExpandableSourceCoderElement >> onExpanded [ self beInSeparateCompositionLayer. - coderUIModel ifNotNil: [ :aCoderUIModel | - aCoderUIModel expanded: true. + textualCoderViewModel ifNotNil: [ :aCoderUIModel | + aCoderUIModel expanded: true from: self. (expandedElement notNil and: [ self isDirty: expandedElement ]) ifTrue: [ self assignExpandedCoder: aCoderUIModel to: expandedElement ] ] ] -{ #category : #events } -GtExpandableSourceCoderElement >> onMethodRemoved: anAnnouncement [ +{ #category : #'api - textual coder view model' } +GtExpandableSourceCoderElement >> onTextualCoderViewModelChanged [ + "Is sent when a new textualCoder view model is assigned to the element. + Note: #onTextualCoderViewModelChanged is sent before #subscribeToTextualCoderViewModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostTextualCoderViewModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + + modificationIndicator textualCoderViewModel: self textualCoderViewModel. + + self updateElement ] { #category : #'private - event handling' } GtExpandableSourceCoderElement >> onViewModelExpansionChanged: anAnnouncement [ - self expanded: anAnnouncement expanded + anAnnouncement source == self + ifTrue: [ ^ self ]. + self hasTextualCoderViewModel + ifFalse: [ ^ self ]. + anAnnouncement textualCoderViewModel = self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ self expanded: anAnnouncement expanded ] +] + +{ #category : #'private - event handling' } +GtExpandableSourceCoderElement >> onViewModelTextChanged: anAnnouncement [ + "Can be removed" + self updateModificationIndicator ] { #category : #'focus requesting' } @@ -195,13 +247,51 @@ GtExpandableSourceCoderElement >> requestFocus [ self childNamed: #editor ifFound: [ :anEditorElement | anEditorElement requestFocus ] - ifNone: [ super requestFocus ] + ifNone: [ super requestFocus ] ] -{ #category : #'focus requesting' } -GtExpandableSourceCoderElement >> requestFocusAsyncronously [ - ^ self - enqueueTask: - (BlTaskAction new - action: [ self requestFocus ]) +{ #category : #'api - textual coder view model' } +GtExpandableSourceCoderElement >> subscribeToTextualCoderViewModel [ + "Is sent after a new textualCoder view model is assigned to the element. + It is required to unsubscribe from the view model or domain model by implementing + #unsubscribeFromTextualCoderViewModel if elements subscribe to them" + + textualCoderViewModel weak + when: GtExpandableCoderViewModelExpansionChanged + send: #onViewModelExpansionChanged: + to: self +] + +{ #category : #'api - textual coder view model' } +GtExpandableSourceCoderElement >> unsubscribeFromTextualCoderViewModel [ + "Is sent before a new textualCoder view model is assigned to the element. + Elements that subscribe to textualCoder view model in domain model are required to implement this methods." + + "textualCoderViewModel coder unsubscribeFromSystem." + textualCoderViewModel unsubscribe: self +] + +{ #category : #'private - update' } +GtExpandableSourceCoderElement >> updateElement [ + textualCoderViewModel expanded + ifTrue: [ expandedElement + ifNotNil: [ :anElement | self assignExpandedCoder: textualCoderViewModel to: anElement ]. + collapsedElement + ifNotNil: [ :anElement | self markDirty: anElement as: true ] ] + ifFalse: [ collapsedElement + ifNotNil: + [ :anElement | self assignCollapsedCoder: textualCoderViewModel to: anElement ]. + expandedElement + ifNotNil: [ :anElement | self markDirty: anElement as: true ] ]. + + textualCoderViewModel focused + ifFalse: [ self loseFocus ]. + + self expanded: textualCoderViewModel expanded. + self updateModificationIndicator +] + +{ #category : #'private - update' } +GtExpandableSourceCoderElement >> updateModificationIndicator [ + "can be removed" ] diff --git a/src/GToolkit-Coder-UI/GtExpandedCoderElementStencil.class.st b/src/GToolkit-Coder-UI/GtExpandedCoderElementStencil.class.st new file mode 100644 index 000000000..b3dd266dd --- /dev/null +++ b/src/GToolkit-Coder-UI/GtExpandedCoderElementStencil.class.st @@ -0,0 +1,11 @@ +Class { + #name : #GtExpandedCoderElementStencil, + #superclass : #GtCoderElementStencil, + #category : #'GToolkit-Coder-UI-Coder - Source' +} + +{ #category : #'api - instantiation' } +GtExpandedCoderElementStencil >> defaultElement [ + + ^ GtSourceCoderExpandedContentElement new +] diff --git a/src/GToolkit-Coder-UI/GtExpandedCoderElementWithRustTextEditorStencil.class.st b/src/GToolkit-Coder-UI/GtExpandedCoderElementWithRustTextEditorStencil.class.st new file mode 100644 index 000000000..79c9182e2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtExpandedCoderElementWithRustTextEditorStencil.class.st @@ -0,0 +1,17 @@ +Class { + #name : #GtExpandedCoderElementWithRustTextEditorStencil, + #superclass : #GtCoderElementStencil, + #category : #'GToolkit-Coder-UI-Coder - Source' +} + +{ #category : #'api - instantiation' } +GtExpandedCoderElementWithRustTextEditorStencil >> defaultElement [ + + ^ GtSourceCoderExpandedContentElement new + initializeEditorElement: (GtTextEditorElement new + defaultFontName: 'Source Code Pro'; + defaultFontSize: 14; + defaultForeground: Color black; + in: [ :anEditor | anEditor privateEditorHandle enableInMemoryCommandsRecorder ]; + yourself) +] diff --git a/src/GToolkit-Coder-UI/GtExpandedOnlyCoderElement.class.st b/src/GToolkit-Coder-UI/GtExpandedOnlyCoderElement.class.st index d7775bb5c..9b1979f10 100644 --- a/src/GToolkit-Coder-UI/GtExpandedOnlyCoderElement.class.st +++ b/src/GToolkit-Coder-UI/GtExpandedOnlyCoderElement.class.st @@ -8,6 +8,13 @@ Class { #category : #'GToolkit-Coder-UI-Coder - Source' } +{ #category : #testing } +GtExpandedOnlyCoderElement class >> isDeprecated [ + "Use GtSourceCoderExpandedOnlyElement instead" + + ^ true +] + { #category : #adding } GtExpandedOnlyCoderElement >> addCodersCoderLook: aSourceCoder to: anElement [ aSourceCoder coderLook @@ -23,40 +30,36 @@ GtExpandedOnlyCoderElement >> asVerticallyResizableDo: aBlock [ GtExpandedOnlyCoderElement >> coder: aCoderUIModel [ self - deprecated: 'Use #coderUIModel: instead.' - transformWith: '`@receiver coder: `@arg' -> '`@receiver coderUIModel: `@arg'. - - ^ self coderUIModel: aCoderUIModel + deprecated: 'Use #coderViewModel: instead.' + transformWith: '`@receiver coder: `@arg' -> '`@receiver coderViewModel: `@arg'. + + ^ self coderViewModel: aCoderUIModel ] { #category : #accessing } -GtExpandedOnlyCoderElement >> coderUIModel [ +GtExpandedOnlyCoderElement >> coderViewModel [ ^ coderUIModel ] { #category : #accessing } -GtExpandedOnlyCoderElement >> coderUIModel: aCoderUIModel [ +GtExpandedOnlyCoderElement >> coderViewModel: aCoderViewModel [ self - assert: [ aCoderUIModel isNotNil ] + assert: [ aCoderViewModel isNotNil ] description: [ 'Coder must not be nil' ]. - coderUIModel == aCoderUIModel ifTrue: [ ^ self ]. + coderUIModel == aCoderViewModel ifTrue: [ ^ self ]. coderUIModel ifNotNil: [ coderUIModel coder announcer unsubscribe: self. - coderUIModel := aCoderUIModel. - expandedElement coderUIModel: aCoderUIModel. + coderUIModel := aCoderViewModel. + expandedElement coderViewModel: aCoderViewModel. ^ self ]. - coderUIModel := aCoderUIModel. - expandedElement := self newExpandedElement: aCoderUIModel. + coderUIModel := aCoderViewModel. + expandedElement := self newExpandedElement: aCoderViewModel. self addChild: expandedElement. self addAptitude: (BrLayoutResizerAptitude new inherit: expandedElement). - aCoderUIModel expanded: true. - aCoderUIModel coder announcer weak - when: GtCoderRequestFocus - send: #requestFocusAsyncronously - to: self + aCoderViewModel expanded: true ] { #category : #initialization } @@ -72,9 +75,9 @@ GtExpandedOnlyCoderElement >> initialize [ { #category : #'instance creation' } GtExpandedOnlyCoderElement >> newExpandedElement: aCoderModel [ ^ GtSourceCoderExpandedContentElement new - coderUIModel: aCoderModel; + textualCoderViewModel: aCoderModel; in: [ :anElement | self addCodersCoderLook: aCoderModel to: anElement ]; - addAptitude: GtSourceCoderEditorAptitude; + showScrollbars; yourself ] @@ -85,11 +88,3 @@ GtExpandedOnlyCoderElement >> requestFocus [ ifFound: [ :anEditorElement | anEditorElement requestFocus ] ifNone: [ super requestFocus ] ] - -{ #category : #'focus requesting' } -GtExpandedOnlyCoderElement >> requestFocusAsyncronously [ - ^ self - enqueueTask: - (BlTaskAction new - action: [ self requestFocus ]) -] diff --git a/src/GToolkit-Coder-UI/GtFilterBooleanModel.class.st b/src/GToolkit-Coder-UI/GtFilterBooleanModel.class.st new file mode 100644 index 000000000..59407489b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterBooleanModel.class.st @@ -0,0 +1,58 @@ +Class { + #name : #GtFilterBooleanModel, + #superclass : #GtFilterModel, + #instVars : [ + 'switchedOn' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterBooleanModel >> filterViewModelClass [ + ^ GtFilterBooleanViewModel +] + +{ #category : #'initialization ' } +GtFilterBooleanModel >> initialize [ + super initialize. + switchedOn := false +] + +{ #category : #testing } +GtFilterBooleanModel >> isSwitchedOff [ + ^ self isSwitchedOn not +] + +{ #category : #testing } +GtFilterBooleanModel >> isSwitchedOn [ + ^ switchedOn +] + +{ #category : #'private - notifying' } +GtFilterBooleanModel >> notifySwitchChanged [ + self + announce: (GtFilterBooleanModelSwitchChanged new + model: self; + switchedOn: self isSwitchedOn) +] + +{ #category : #accessing } +GtFilterBooleanModel >> selectedValue [ + "Return a filter value, e.g., selected item, input text." + + ^ self switchedOn +] + +{ #category : #accessing } +GtFilterBooleanModel >> switchedOn [ + + ^ switchedOn +] + +{ #category : #accessing } +GtFilterBooleanModel >> switchedOn: aBoolean [ + self switchedOn = aBoolean ifTrue: [ ^ self ]. + + switchedOn := aBoolean. + self notifySwitchChanged +] diff --git a/src/GToolkit-Coder-UI/GtFilterBooleanModelSwitchChanged.class.st b/src/GToolkit-Coder-UI/GtFilterBooleanModelSwitchChanged.class.st new file mode 100644 index 000000000..c22d925d4 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterBooleanModelSwitchChanged.class.st @@ -0,0 +1,36 @@ +Class { + #name : #GtFilterBooleanModelSwitchChanged, + #superclass : #GtFilterModelAnnouncement, + #instVars : [ + 'switchedOn' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #testing } +GtFilterBooleanModelSwitchChanged >> changesFilteredResult [ + "Indicates whether it changes a result of filtered items, e.g., list of method coders." + + ^ true +] + +{ #category : #testing } +GtFilterBooleanModelSwitchChanged >> isOff [ + ^ self switchedOn not +] + +{ #category : #testing } +GtFilterBooleanModelSwitchChanged >> isOn [ + ^ self switchedOn +] + +{ #category : #accessing } +GtFilterBooleanModelSwitchChanged >> switchedOn [ + + ^ switchedOn +] + +{ #category : #accessing } +GtFilterBooleanModelSwitchChanged >> switchedOn: aBoolean [ + switchedOn := aBoolean +] diff --git a/src/GToolkit-Coder-UI/GtFilterBooleanSettingsElement.class.st b/src/GToolkit-Coder-UI/GtFilterBooleanSettingsElement.class.st new file mode 100644 index 000000000..4568a1519 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterBooleanSettingsElement.class.st @@ -0,0 +1,59 @@ +Class { + #name : #GtFilterBooleanSettingsElement, + #superclass : #GtFilterSettingsElement, + #instVars : [ + 'switcherElement' + ], + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #initialization } +GtFilterBooleanSettingsElement >> initialize [ + super initialize. + self initializeSwitcherElement. + self addChild: switcherElement as: #switcher +] + +{ #category : #initialization } +GtFilterBooleanSettingsElement >> initializeSwitcherElement [ + switcherElement := BrCheckbox new + fitContent; + aptitude: BrGlamorousCheckboxAptitude; + whenCheckedDo: [ :anEvent | self onCheckedEvent: anEvent ]; + whenUncheckedDo: [ :anEvent | self onUncheckedEvent: anEvent ]. +] + +{ #category : #'event handling' } +GtFilterBooleanSettingsElement >> onCheckedEvent: anEvent [ +] + +{ #category : #'as yet unclassified' } +GtFilterBooleanSettingsElement >> onFilterViewModelChanged [ + super onFilterViewModelChanged. + + self updateSwitcherElement +] + +{ #category : #'event handling' } +GtFilterBooleanSettingsElement >> onSwitchChanged: anAnnouncement [ + BlTaskAction enqueueElement: self action: [ self updateSwitcherElement ] +] + +{ #category : #'event handling' } +GtFilterBooleanSettingsElement >> onUncheckedEvent: anEvent [ +] + +{ #category : #'api - filter view model' } +GtFilterBooleanSettingsElement >> subscribeToFilterViewModel [ + super subscribeToFilterViewModel. + + self filterViewModel weak + when: GtFilterBooleanViewModelSwitchChanged + send: #onSwitchChanged: + to: self +] + +{ #category : #'private - updating' } +GtFilterBooleanSettingsElement >> updateSwitcherElement [ + switcherElement checked: self filterViewModel isSwitchedOn +] diff --git a/src/GToolkit-Coder-UI/GtFilterBooleanViewModel.class.st b/src/GToolkit-Coder-UI/GtFilterBooleanViewModel.class.st new file mode 100644 index 000000000..867ef6086 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterBooleanViewModel.class.st @@ -0,0 +1,34 @@ +Class { + #name : #GtFilterBooleanViewModel, + #superclass : #GtFilterViewModel, + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #accessing } +GtFilterBooleanViewModel >> filterElementClass [ + ^ GtFilterBooleanSettingsElement +] + +{ #category : #testing } +GtFilterBooleanViewModel >> isSwitchedOff [ + ^ self filterModel isSwitchedOff +] + +{ #category : #testing } +GtFilterBooleanViewModel >> isSwitchedOn [ + ^ self filterModel isSwitchedOn +] + +{ #category : #'event handling' } +GtFilterBooleanViewModel >> onSwitchChanged: anAnnouncement [ + self announce: (GtFilterBooleanViewModelSwitchChanged new viewModel: self) +] + +{ #category : #'api - filter model' } +GtFilterBooleanViewModel >> subscribeToFilterModel [ + super subscribeToFilterModel. + self filterModel weak + when: GtFilterBooleanModelSwitchChanged + send: #onSwitchChanged: + to: self +] diff --git a/src/GToolkit-Coder-UI/GtFilterBooleanViewModelSwitchChanged.class.st b/src/GToolkit-Coder-UI/GtFilterBooleanViewModelSwitchChanged.class.st new file mode 100644 index 000000000..15b3eeadc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterBooleanViewModelSwitchChanged.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtFilterBooleanViewModelSwitchChanged, + #superclass : #GtFilterViewModelAnnouncement, + #instVars : [ + 'switchedOn' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #testing } +GtFilterBooleanViewModelSwitchChanged >> isOff [ + ^ self switchedOn not +] + +{ #category : #testing } +GtFilterBooleanViewModelSwitchChanged >> isOn [ + ^ self switchedOn +] + +{ #category : #'as yet unclassified' } +GtFilterBooleanViewModelSwitchChanged >> switchedOn [ + + ^ switchedOn +] + +{ #category : #'as yet unclassified' } +GtFilterBooleanViewModelSwitchChanged >> switchedOn: aBoolean [ + switchedOn := aBoolean +] diff --git a/src/GToolkit-Coder-UI/GtFilterDefaultSearchFilters.class.st b/src/GToolkit-Coder-UI/GtFilterDefaultSearchFilters.class.st new file mode 100644 index 000000000..075c0e7ef --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterDefaultSearchFilters.class.st @@ -0,0 +1,124 @@ +Class { + #name : #GtFilterDefaultSearchFilters, + #superclass : #Object, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #instVars : [ + 'coderSearchFilters', + 'filterSearchFilters' + ], + #classInstVars : [ + 'uniqueInstance' + ], + #category : #'GToolkit-Coder-UI-Filters - Support' +} + +{ #category : #'add / remove' } +GtFilterDefaultSearchFilters class >> addCoderSearchFilter: aSearchFilter [ + self default addCoderSearchFilter: aSearchFilter +] + +{ #category : #'add / remove' } +GtFilterDefaultSearchFilters class >> addCoderSearchFilters: aCollectionOfSearchFilters [ + self default addCoderSearchFilters: aCollectionOfSearchFilters +] + +{ #category : #'add / remove' } +GtFilterDefaultSearchFilters class >> addFilterSearchFilter: aSearchFilter [ + self default addFilterSearchFilter: aSearchFilter +] + +{ #category : #'add / remove' } +GtFilterDefaultSearchFilters class >> addFilterSearchFilters: aCollectionOfSearchFilters [ + self default addFilterSearchFilters: aCollectionOfSearchFilters +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters class >> coderSearchFilters [ + ^ self default coderSearchFilters +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters class >> coderSearchFilters: aCollectionOfSearchFilters [ + self default coderSearchFilters: aCollectionOfSearchFilters +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters class >> filterSearchFilters [ + ^ self default filterSearchFilters +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters class >> filterSearchFilters: aCollectionOfSearchFilters [ + self default filterSearchFilters: aCollectionOfSearchFilters +] + +{ #category : #'add / remove' } +GtFilterDefaultSearchFilters >> addCoderSearchFilter: aSearchFilter [ + coderSearchFilters := self coderSearchFilters copyWith: aSearchFilter +] + +{ #category : #'add / remove' } +GtFilterDefaultSearchFilters >> addCoderSearchFilters: aCollectionOfSearchFilters [ + coderSearchFilters := self coderSearchFilters copyWithAll: aCollectionOfSearchFilters +] + +{ #category : #'add / remove' } +GtFilterDefaultSearchFilters >> addFilterSearchFilter: aSearchFilter [ + filterSearchFilters := self filterSearchFilters copyWith: aSearchFilter +] + +{ #category : #'add / remove' } +GtFilterDefaultSearchFilters >> addFilterSearchFilters: aCollectionOfSearchFilters [ + filterSearchFilters := self filterSearchFilters copyWithAll: aCollectionOfSearchFilters +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters >> coderSearchFilters [ + ^ coderSearchFilters ifNil: [ coderSearchFilters := self collectCoderSearchFilters ] +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters >> coderSearchFilters: aCollectionOfSearchFilters [ + coderSearchFilters := aCollectionOfSearchFilters +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters >> collectCoderSearchFilters [ + ^ self pragmaCoderSearchFilters + collect: [ :eachPragma | self perform: eachPragma methodSelector ] +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters >> collectFilterSearchFilters [ + ^ self pragmaFilterSearchFilters + collect: [ :eachPragma | self perform: eachPragma methodSelector ] +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters >> filterSearchFilters [ + ^ filterSearchFilters ifNil: [ filterSearchFilters := self collectFilterSearchFilters ] +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters >> filterSearchFilters: aCollectionOfSearchFilters [ + filterSearchFilters := aCollectionOfSearchFilters +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters >> pragmaCoderSearchFilters [ + ^ Pragma + allNamed: #gtCoderSearchFilter: + from: self class + to: GtFilterDefaultSearchFilters + sortedByArgument: 1 +] + +{ #category : #accessing } +GtFilterDefaultSearchFilters >> pragmaFilterSearchFilters [ + ^ Pragma + allNamed: #gtFilterSearchFilter: + from: self class + to: GtFilterDefaultSearchFilters + sortedByArgument: 1 +] diff --git a/src/GToolkit-Coder-UI/GtFilterDescriptor.class.st b/src/GToolkit-Coder-UI/GtFilterDescriptor.class.st index e0e92263b..b473ed9ce 100644 --- a/src/GToolkit-Coder-UI/GtFilterDescriptor.class.st +++ b/src/GToolkit-Coder-UI/GtFilterDescriptor.class.st @@ -2,8 +2,8 @@ I describe how a {{gtClass:GtSearchMethodsFilter}} should look and behave in a UI. I am used to build {{gtClass:GtFilterTagElement}} instances inside of {{gtClass:GtFiltersElement}}. For more details: -- {{gtMethod:GtMethodsCoderElement>>#buildFilter}} is a code that inittialize {{gtClass:GtFiltersElement}} using instances of myself, -- {{gtMethod:GtSearchMethodsFilter class>>#filterDescriptorFor:|show=#gtImplementorsFor:}} displays a list of various definitions of myself. +- {{gtMethod:GtPharoMethodsCoderElement>>#buildFilter}} is a code that inittialize {{gtClass:GtFiltersElement}} using instances of myself, +- {{gtMethod:GtSearchFilter class>>#filterDescriptorFor:|show=#gtImplementorsFor:}} displays a list of various definitions of myself. See {{gtClass:GtFiltersElement}} for an example. @@ -14,20 +14,22 @@ Class { #instVars : [ 'name', 'order', + 'creationBlock', + 'isDefault', + 'completionBlock', 'completion', 'emptyDefaultValue', - 'creationBlock', 'valueIsRequired', - 'isDefault' + 'offerCompletionValues' ], #category : #'GToolkit-Coder-UI-Filters' } -{ #category : #'instace creation' } +{ #category : #'instance creation' } GtFilterDescriptor class >> creator: aBlock named: aString order: anInteger [ ^ self new - creationBlock: aBlock; - name: aString; + creator: aBlock; + named: aString; order: anInteger; yourself ] @@ -35,8 +37,8 @@ GtFilterDescriptor class >> creator: aBlock named: aString order: anInteger [ { #category : #'instance creation' } GtFilterDescriptor class >> creator: aBlock named: aString order: anInteger completion: completionStrategy [ ^ self new - creationBlock: aBlock; - name: aString; + creator: aBlock; + named: aString; order: anInteger; completion: completionStrategy; yourself @@ -45,46 +47,95 @@ GtFilterDescriptor class >> creator: aBlock named: aString order: anInteger comp { #category : #'instance creation' } GtFilterDescriptor class >> creator: aBlock named: aString order: anInteger completion: completionStrategy emptyDefaultValue: defaultValueString [ ^ self new - creationBlock: aBlock; - name: aString; + creator: aBlock; + named: aString; order: anInteger; completion: completionStrategy; emptyDefaultValue: defaultValueString; yourself ] -{ #category : #'initialize-release' } +{ #category : #comparing } +GtFilterDescriptor >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + ^ self class = anObject class + and: [ self name = anObject name + and: [ self order = anObject order + and: [ self emptyDefaultValue = anObject emptyDefaultValue + and: [ self valueIsRequired = anObject valueIsRequired ] ] ] ] +] + +{ #category : #'api - configuration' } GtFilterDescriptor >> beNotDefault [ "Do not display this particular descriptor filter as a default filter" + isDefault := false ] { #category : #accessing } GtFilterDescriptor >> completion [ - ^ completion + ^ completion ifNil: [ completion := (completionBlock ifNil: [ self defaultCompletion ]) value ] ] -{ #category : #accessing } -GtFilterDescriptor >> completion: completionStrategy [ - completion := completionStrategy +{ #category : #'api - configuration' } +GtFilterDescriptor >> completion: completionStrategyBlock [ + completionBlock := completionStrategyBlock ] { #category : #accessing } +GtFilterDescriptor >> creationBlock [ + ^ creationBlock ifNil: [ self defaultCreator ] +] + +{ #category : #'api - configuration' } GtFilterDescriptor >> creationBlock: aBlock [ + + "We deprecated #creationBlock: in favor of #creator: to keep the instance side api be as close as possible to fluent class side api" + self + deprecated: 'Please use creator: instead.' + transformWith: + '`@receiver creationBlock: `@statements1' + -> '`@receiver creator: `@statements1'. + + self creator: aBlock +] + +{ #category : #'api - configuration' } +GtFilterDescriptor >> creator: aBlock [ creationBlock := aBlock ] +{ #category : #initialization } +GtFilterDescriptor >> defaultCompletion [ + ^ [ nil ] +] + +{ #category : #initialization } +GtFilterDescriptor >> defaultCreator [ + ^ [ :aString | GtSearchNullFilter new ] +] + +{ #category : #initialization } +GtFilterDescriptor >> defaultName [ + ^ 'No name' +] + +{ #category : #initialization } +GtFilterDescriptor >> defaultOrder [ + ^ 1 +] + { #category : #accessing } GtFilterDescriptor >> emptyDefaultValue [ ^ emptyDefaultValue ] -{ #category : #accessing } +{ #category : #'api - configuration' } GtFilterDescriptor >> emptyDefaultValue: aString [ emptyDefaultValue := aString ] -{ #category : #'gt-extensions' } +{ #category : #'gt - extensions' } GtFilterDescriptor >> gtCompletionsFor: aView [ self completion ifNil: [ ^ aView empty ]. @@ -96,34 +147,120 @@ GtFilterDescriptor >> gtCompletionsFor: aView [ view: #gtCompletionsFor: ] -{ #category : #'initialize-release' } +{ #category : #'gt - extensions' } +GtFilterDescriptor >> gtInfoFor: aView [ + + + ^ aView columnedList + title: 'Info'; + priority: 1; + items: [ { + 'Name' -> (name ifNil: [ 'Unspecified (default: {1})' format: { self defaultName printString } ]). + 'Order' -> (order ifNil: [ 'Unspecified (default: {1})' format: { self defaultOrder printString } ]). + 'Filter creator' -> (creationBlock ifNil: [ 'Unspecified (default: {1})' format: { self defaultCreator } ]). + 'Completion' -> (completionBlock ifNil: [ 'Unspecified (default: {1})' format: { self defaultCompletion } ]). + 'Value required' -> valueIsRequired. + 'Is default' -> isDefault. + 'Default value when empty' -> (emptyDefaultValue + ifNotNil: [ :aValue | aValue printString ] + ifNil: [ 'Unspecified (default: {1})' format: { nil } ]). + 'Show default value when empty' -> self showAsDefaultWhenEmpty. + 'Offers user completion items' -> self shouldOfferCompletionValues + } ]; + column: 'Property' text: #key; + column: 'Value' text: #value; + send: #value; + actionUpdateButton +] + +{ #category : #'gt - extensions' } +GtFilterDescriptor >> gtViewFilterBlockFor: aView [ + + + creationBlock ifNil: [ ^ aView empty ]. + + ^ aView forward + title: 'Filter creation block'; + object: [ creationBlock ]; + view: #gtSourceCodeFor: +] + +{ #category : #comparing } +GtFilterDescriptor >> hash [ + ^ self class hash hashMultiply bitXor: self name hash +] + +{ #category : #initialization } GtFilterDescriptor >> initialize [ super initialize. + valueIsRequired := true. isDefault := true. + offerCompletionValues := false +] + +{ #category : #testing } +GtFilterDescriptor >> isDefault [ + "Return true if the filter should be applied as a default" + + ^ isDefault +] + +{ #category : #testing } +GtFilterDescriptor >> isValueValid: aValueString [ + "Return true if a given value is valid for this filter descriptor, false otherwise. + Empty values are valid only when the empty default value is empty too or is unspecified" + + + ^ self valueIsRequired not or: [ + aValueString isNotEmpty or: [ + aValueString = (self emptyDefaultValue ifNil: [ '' ]) ] ] ] { #category : #accessing } GtFilterDescriptor >> name [ - ^ name + "Return the name of the filter (or a default one if not specified) to be displayed in the list of the filters." + + + ^ name ifNil: [ self defaultName ] ] -{ #category : #accessing } +{ #category : #'api - configuration' } GtFilterDescriptor >> name: aString [ + "We deprecated #name: in favor of #named: to keep the instance side api be as close as possible to fluent class side api" + self + deprecated: 'Please use named: instead.' + transformWith: + '`@receiver name: `@statements1' + -> '`@receiver named: `@statements1'. + + self named: aString +] + +{ #category : #'api - configuration' } +GtFilterDescriptor >> named: aString [ name := aString ] { #category : #filters } GtFilterDescriptor >> newFilterWithValue: aString [ - ^ creationBlock value: aString + ^ self creationBlock cull: aString +] + +{ #category : #'api - configuration' } +GtFilterDescriptor >> offerCompletionValues: aBoolean [ + offerCompletionValues := aBoolean ] { #category : #accessing } GtFilterDescriptor >> order [ - ^ order + "The order of the filter in the list of all filters." + + + ^ order ifNil: [ self defaultOrder ] ] -{ #category : #accessing } +{ #category : #'api - configuration' } GtFilterDescriptor >> order: anInteger [ order := anInteger ] @@ -139,17 +276,27 @@ GtFilterDescriptor >> printOn: aStream [ nextPutAll: ')' ] +{ #category : #testing } +GtFilterDescriptor >> shouldOfferCompletionValues [ + ^ offerCompletionValues +] + { #category : #testing } GtFilterDescriptor >> showAsDefaultWhenEmpty [ - ^ isDefault and: [ emptyDefaultValue notNil ] + ^ isDefault and: [ valueIsRequired not or: [ emptyDefaultValue notNil ] ] ] { #category : #testing } GtFilterDescriptor >> valueIsRequired [ + "Return true if the filter requires an input value, false otherwise. + Filters that require a value should be rendered with a text input field + allowing users to enter the value." + + ^ valueIsRequired ] -{ #category : #'initialize-release' } +{ #category : #'api - configuration' } GtFilterDescriptor >> valueNotRequired [ valueIsRequired := false ] diff --git a/src/GToolkit-Coder-UI/GtFilterEditWish.class.st b/src/GToolkit-Coder-UI/GtFilterEditWish.class.st new file mode 100644 index 000000000..da4c99ca5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterEditWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterEditWish, + #superclass : #BrWish, + #category : #'GToolkit-Coder-UI-Filters - Events' +} diff --git a/src/GToolkit-Coder-UI/GtFilterElementId.class.st b/src/GToolkit-Coder-UI/GtFilterElementId.class.st new file mode 100644 index 000000000..901ae5d10 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterElementId.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterElementId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} diff --git a/src/GToolkit-Coder-UI/GtFilterExcludingFocusFinder.class.st b/src/GToolkit-Coder-UI/GtFilterExcludingFocusFinder.class.st new file mode 100644 index 000000000..69b9e5baa --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterExcludingFocusFinder.class.st @@ -0,0 +1,36 @@ +Class { + #name : #GtFilterExcludingFocusFinder, + #superclass : #BlFocusFinder, + #instVars : [ + 'excludedElement' + ], + #category : #'GToolkit-Coder-UI-Filters - Support' +} + +{ #category : #accessing } +GtFilterExcludingFocusFinder >> excludedElement [ + ^ excludedElement +] + +{ #category : #accessing } +GtFilterExcludingFocusFinder >> excludedElement: anObject [ + excludedElement := anObject +] + +{ #category : #'api - finder' } +GtFilterExcludingFocusFinder >> focusableCandidates [ + | theFocusCandidates someFocusCandidates | + theFocusCandidates := BlFocusFinderCandidates new. + someFocusCandidates := super focusableCandidates. + someFocusCandidates + do: [ :eachCandidate | + | toExclude | + toExclude := false. + eachCandidate == excludedElement + ifTrue: [ eachCandidate isFocused ifFalse: [ toExclude := true ] ] + ifFalse: [ eachCandidate allParentsDo: [ :eachParent | + eachParent == excludedElement + ifTrue: [ toExclude := true ] ] ]. + toExclude ifFalse: [ theFocusCandidates add: eachCandidate ] ]. + ^ theFocusCandidates +] diff --git a/src/GToolkit-Coder-UI/GtFilterFocusFirstPartWish.class.st b/src/GToolkit-Coder-UI/GtFilterFocusFirstPartWish.class.st new file mode 100644 index 000000000..48dc929d7 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterFocusFirstPartWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterFocusFirstPartWish, + #superclass : #BrWish, + #category : #'GToolkit-Coder-UI-Filters - Events' +} diff --git a/src/GToolkit-Coder-UI/GtFilterFocusIntendedPartAfterAddingWish.class.st b/src/GToolkit-Coder-UI/GtFilterFocusIntendedPartAfterAddingWish.class.st new file mode 100644 index 000000000..008aabca0 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterFocusIntendedPartAfterAddingWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterFocusIntendedPartAfterAddingWish, + #superclass : #BrWish, + #category : #'GToolkit-Coder-UI-Filters - Events' +} diff --git a/src/GToolkit-Coder-UI/GtFilterFocusNextFilterWish.class.st b/src/GToolkit-Coder-UI/GtFilterFocusNextFilterWish.class.st new file mode 100644 index 000000000..4c7cd6e9b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterFocusNextFilterWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterFocusNextFilterWish, + #superclass : #BrWish, + #category : #'GToolkit-Coder-UI-Filters - Events' +} diff --git a/src/GToolkit-Coder-UI/GtFilterFocusNextPartWish.class.st b/src/GToolkit-Coder-UI/GtFilterFocusNextPartWish.class.st new file mode 100644 index 000000000..3a01289c0 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterFocusNextPartWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterFocusNextPartWish, + #superclass : #BrWish, + #category : #'GToolkit-Coder-UI-Filters - Events' +} diff --git a/src/GToolkit-Coder-UI/GtFilterFocusWholeWish.class.st b/src/GToolkit-Coder-UI/GtFilterFocusWholeWish.class.st new file mode 100644 index 000000000..40eeb7f29 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterFocusWholeWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterFocusWholeWish, + #superclass : #BrWish, + #category : #'GToolkit-Coder-UI-Filters - Events' +} diff --git a/src/GToolkit-Coder-UI/GtFilterInvariableModel.class.st b/src/GToolkit-Coder-UI/GtFilterInvariableModel.class.st new file mode 100644 index 000000000..8b19a50da --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterInvariableModel.class.st @@ -0,0 +1,11 @@ +Class { + #name : #GtFilterInvariableModel, + #superclass : #GtFilterModel, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterInvariableModel >> filterViewModelClass [ + + ^ GtFilterInvariableViewModel +] diff --git a/src/GToolkit-Coder-UI/GtFilterInvariableSettingsElement.class.st b/src/GToolkit-Coder-UI/GtFilterInvariableSettingsElement.class.st new file mode 100644 index 000000000..a48ea01b2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterInvariableSettingsElement.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterInvariableSettingsElement, + #superclass : #GtFilterSettingsElement, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} diff --git a/src/GToolkit-Coder-UI/GtFilterInvariableViewModel.class.st b/src/GToolkit-Coder-UI/GtFilterInvariableViewModel.class.st new file mode 100644 index 000000000..b6f109e21 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterInvariableViewModel.class.st @@ -0,0 +1,11 @@ +Class { + #name : #GtFilterInvariableViewModel, + #superclass : #GtFilterViewModel, + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #accessing } +GtFilterInvariableViewModel >> filterElementClass [ + + ^ GtFilterInvariableSettingsElement +] diff --git a/src/GToolkit-Coder-UI/GtFilterItemId.class.st b/src/GToolkit-Coder-UI/GtFilterItemId.class.st new file mode 100644 index 000000000..aa9d46dfc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterItemId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterItemId, + #superclass : #GtFilterElementId, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #converting } +GtFilterItemId >> asSymbol [ + ^ #'filter--item' +] diff --git a/src/GToolkit-Coder-UI/GtFilterItemsChangedSignal.class.st b/src/GToolkit-Coder-UI/GtFilterItemsChangedSignal.class.st new file mode 100644 index 000000000..6e957d089 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterItemsChangedSignal.class.st @@ -0,0 +1,53 @@ +Class { + #name : #GtFilterItemsChangedSignal, + #superclass : #GtFilterSignal, + #instVars : [ + 'incomingItems', + 'previousItems' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #'gt - extensions' } +GtFilterItemsChangedSignal >> gtIncomingItemsFor: aView [ + + ^ aView columnedList + title: 'Incoming items'; + priority: -1; + items: [ self incomingItems ]; + column: 'Name' text: #yourself weight: 4; + column: 'Was used' text: [ :anItem | self previousItems identityIncludes: anItem ]; + column: 'Is similar' text: [ :anItem | self previousItems includes: anItem ] +] + +{ #category : #'gt - extensions' } +GtFilterItemsChangedSignal >> gtPreviousItemsFor: aView [ + + ^ aView columnedList + title: 'Previous items'; + priority: 0; + items: [ self previousItems ]; + column: 'Name' text: #yourself weight: 4; + column: 'Is reused' text: [ :anItem | self incomingItems identityIncludes: anItem ]; + column: 'Is similar' text: [ :anItem | self incomingItems includes: anItem ] +] + +{ #category : #accessing } +GtFilterItemsChangedSignal >> incomingItems [ + ^ incomingItems +] + +{ #category : #accessing } +GtFilterItemsChangedSignal >> incomingItems: anObject [ + incomingItems := anObject +] + +{ #category : #accessing } +GtFilterItemsChangedSignal >> previousItems [ + ^ previousItems +] + +{ #category : #accessing } +GtFilterItemsChangedSignal >> previousItems: anObject [ + previousItems := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterItemsElement.class.st b/src/GToolkit-Coder-UI/GtFilterItemsElement.class.st new file mode 100644 index 000000000..acf40f079 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterItemsElement.class.st @@ -0,0 +1,468 @@ +Class { + #name : #GtFilterItemsElement, + #superclass : #BlElement, + #traits : 'TBrLayoutResizable + TGtWithFiltersViewModel', + #classTraits : 'TBrLayoutResizable classTrait + TGtWithFiltersViewModel classTrait', + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #'private - updating' } +GtFilterItemsElement >> addElementForFilterViewModel: aFilterViewModel index: anIndex [ + | anElement | + anElement := aFilterViewModel asFilterLabeledElement + id: (GtFilterNameId indexed: anIndex); + margin: (BlInsets left: 3 right: 3); + constraintsDo: [ :c | + c flow vertical alignCenter. + c flow horizontal alignLeft ]; + when: GtFilterFocusNextFilterWish + do: [ :anEvent | self onFocusNextFilterWish: anEvent ]. + + self addNavigationShortcutsTo: anElement. + self addRemoveButtonIconTo: anElement. + + anElement + addShortcut: (BlShortcutWithAction new + name: 'Remove filter'; + combination: (BlKeyCombination backspace or: BlKeyCombination delete); + overrideChildren: false; + action: [ :anEvent | + self + onRemoveFilterViewModelOfElement: anEvent currentTarget + from: anEvent currentTarget ]). + + self addChild: anElement at: anIndex. + + ^ anElement +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> addItemElementForViewModel: aFilterViewModel [ + | anIndex anElement | + anIndex := self filtersViewModel items + detectIndex: [ :each | each == aFilterViewModel ] + ifNone: [ ^ self ]. + + anElement := self addElementForFilterViewModel: aFilterViewModel index: anIndex. + anElement fireEvent: GtFilterFocusIntendedPartAfterAddingWish new +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> addNavigationShortcutsTo: anElement [ + anElement + addShortcut: (BlShortcutWithAction new + name: 'Move to next filter'; + description: 'Moves to the next filter. If none is found, we cycle back to the first.'; + combination: (BlKeyCombination tab or: BlKeyCombination arrowRight); + overrideChildren: false; + action: [ :anEvent | + BlFocusFinder new + direction: BlFocusSearchDirectionForward new; + root: self; + referenceElement: anEvent currentTarget; + nextFocusDo: #requestFocus ifNone: [ ] ]); + addShortcut: (BlShortcutWithAction new + name: 'Move to previous form item'; + description: 'Moves to the previous filter. If none is found, we cycle back to the last.'; + combination: (BlKeyCombination shiftTab or: BlKeyCombination arrowLeft); + overrideChildren: false; + action: [ :anEvent | + BlFocusFinder new + direction: BlFocusSearchDirectionBackward new; + root: self; + referenceElement: anEvent currentTarget; + nextFocusDo: #requestFocus ifNone: [ ] ]); + addShortcut: (BlShortcutWithAction new + name: 'Move to upper filter'; + description: 'Moves to the upper filter. If none is found, we stay in the current.'; + combination: (BlKeyCombination arrowUp); + overrideChildren: false; + action: [ :anEvent | + BlFocusFinder new + up; + root: self; + referenceElement: anEvent currentTarget; + nextFocusDo: #requestFocus ifNone: [ ] ]); + addShortcut: (BlShortcutWithAction new + name: 'Move to lower filter'; + description: 'Moves to the lower filter. If none is found, we stay in the current.'; + combination: (BlKeyCombination arrowDown); + overrideChildren: false; + action: [ :anEvent | + BlFocusFinder new + down; + root: self; + referenceElement: anEvent currentTarget; + nextFocusDo: #requestFocus ifNone: [ ] ]); + addShortcut: (BlShortcutWithAction new + name: 'Edit filter'; + combination: BlKeyCombination enter; + overrideChildren: false; + action: [ :anEvent | self onEditItemsEvent: anEvent ]) +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> addNewFilterElement [ + self addChild: self newAddFilterButton as: GtFiltersAddId +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> addRemoveButtonIconTo: anElement [ + | aRemoveButton aKeyMap | + aKeyMap := 'Backspace | Delete'. + anElement clipChildren: false. + aRemoveButton := self newRemoveButton. + aRemoveButton visibility: BlVisibility gone. + aRemoveButton addAptitude: (BrGlamorousWithExplicitTooltipAptitude + text: 'Remove' + shortcut: aKeyMap). + aRemoveButton action: [ :aButton | + self onRemoveFilterViewModelOfElement: anElement from: aButton ]. + anElement + when: BlMouseEnterEvent + do: [ :anEvent | aRemoveButton visibility: BlVisibility visible ]. + anElement + when: BlMouseLeaveEvent + do: [ :anEvent | aRemoveButton visibility: BlVisibility gone ]. + anElement addChild: aRemoveButton +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> availableFilters [ + ^ self hasFiltersViewModel + ifTrue: [ self filtersViewModel availableFilters + ifEmpty: [ {GtFilterVirtualEmptyModel default} ] ] + ifFalse: [ {GtFilterVirtualEmptyModel default} ] +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> createDropdownContent [ + | aListElement | + aListElement := BrSimpleList new. + ^ aListElement + vFitContentLimited; + hFitContentLimited; + itemStencil: [ :anItemType | self createItemElementForListElement: aListElement ]; + itemDataBinder: [ :anItemElement :anItemObject :anItemIndex | + anItemElement id: (GtFilterItemId indexed: anItemIndex). + anItemElement userData at: GtFilterModel put: anItemObject. + (anItemElement childAt: 1) text: anItemObject name. + anItemElement ]; + itemDataUnbinder: [ :anItemElement :anItemObject :anItemIndex | + anItemElement id: nil. + anItemElement userData removeKey: GtFilterModel ifAbsent: [ "ignore" ]. + (anItemElement childAt: 1) text: ''. + anItemElement ]; + when: BlElementAddedToSceneGraphEvent + do: [ :anEvent | self onListElementAddedToSceneGraph: anEvent ]; + addShortcut: (BlShortcutWithAction new + name: 'Add selected filter'; + description: 'Add selected filter'; + combination: BlKeyCombination builder enter build; + action: [ :aShortcutEvent :aShortcut | self onAddSelectedItemEvent: aShortcutEvent ]); + withAsyncFutureDo: [ :anElementFuture | + anElementFuture + whenPending: [ :theListElement | + theListElement items: { GtFilterVirtualWaitingModel default } ]; + whenError: [ :theListElement :anError | + theListElement items: { GtFilterVirtualExceptionModel new exception: anError } ]; + whenSuccess: [ :theListElement :aCollection | + theListElement items: aCollection ]; + future: [ self availableFilters ] asAsyncFuture ] +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> createItemElementForListElement: aListElement [ + self + assert: [ aListElement isNotNil ] + description: [ 'List element must be non-nil' ]. + + ^ BrHorizontalPane new + hMatchParent; + vFitContent; + padding: (BlInsets all: 5); + addAptitude: (BrStyleCommonAptitude new + default: [ :s | s background: Color transparent ]; + hovered: [ :s | s background: self theme button defaultBorderColor ]; + selected: [ :s | s background: self theme button defaultBorderColor ]); + addChild: (BrLabel new + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont glamorousCodeSmallSize); + when: BlClickEvent do: [ :anEvent | self onItemMouseClickEvent: anEvent ]; + when: BlMouseEnterEvent + do: [ :anEvent | self onItemMouseEnterEvent: anEvent listElement: aListElement ] +] + +{ #category : #initialization } +GtFilterItemsElement >> defaultLayout [ + ^ BlFlowLayout horizontal +] + +{ #category : #'event handling' } +GtFilterItemsElement >> focusFirstItemElementInList: aListElement [ + aListElement requestFocus. + aListElement selectFirst. + aListElement scrollToSelection +] + +{ #category : #initialization } +GtFilterItemsElement >> initialize [ + super initialize. + self hFitContentLimited. + self vFitContent. + self padding: (BlInsets top: 5 right: 2 bottom: 2 left: 1). + self clipChildren: false. + self initializeEventHandlers. + self initializeShortcuts. +] + +{ #category : #initialization } +GtFilterItemsElement >> initializeEventHandlers [ + +] + +{ #category : #initialization } +GtFilterItemsElement >> initializeShortcuts [ + self + addShortcut: (BlShortcutWithAction new + name: 'Add new filter'; + combination: (BlKeyCombination builder + shift; + primary; + s; + build); + action: [ :anEvent | self onAddNewFilterShortcutEvent: anEvent ]) +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> newAddFilterButton [ + ^ self newAddFilterHandleElement + id: GtFiltersAddButtonId; + addAptitude: BrGlamorousButtonWithLabelTooltipAptitude2 new; + addAptitude: (BrStyleCommonAptitude new + default: [ :aStyle | + aStyle + border: BlBorder empty; + background: self theme status neutralBackgroundColor ]; + hovered: [ :aStyle | aStyle background: self theme status neutralBackgroundColor darker ]; + pressed: [ :aStyle | aStyle background: self theme status neutralBackgroundColor darker darker ]; + focused: [ :aStyle | aStyle border: (BlBorder paint: self theme editor focusedBorderColor width: 1) ]); + addAptitude: (BrGlamorousWithExplicitDropdownAptitude + handle: [ self newAddFilterHandleElement ] + content: [ self createDropdownContent ]); + margin: (BlInsets left: 2); + constraintsDo: [ :c | c flow vertical alignCenter ]; + beFocusable; + in: [ :anElement | self addNavigationShortcutsTo: anElement ]; + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination enter; + action: [ :anEvent | anEvent currentTarget dispatchEvent: BrDropdownShowWish new ]); + when: BrDropdownIsHidden + do: [ :anEvent | self onAddFilterDropdownIsHiddenEvent: anEvent ]; + when: GtFilterEditWish + do: [ :anEvent | anEvent currentTarget dispatchEvent: BrDropdownShowWish new ] +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> newAddFilterHandleElement [ + ^ BrButton new + aptitude: BrGlamorousButtonRectangularAptitude new; + addAptitude: BrGlamorousButtonIconAptitude new; + icon: BrGlamorousVectorIcons add; + label: 'Add Filter'; + beSmallSize; + hExact: 20 +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> newRemoveButton [ + ^ GtInspectorRoundButtonStencil mini asElement + icon: (BrPlusIconStencil close radius: 3) asElement; + zIndex: 10; + id: GtPagerClosePageButtonElementId; + constraintsDo: [ :c | + c ignoreByLayout. + c ignored horizontal alignCenterAt: 1.0. + c ignored vertical alignCenterAt: 0.0 ] +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> onAddFilterDropdownIsHiddenEvent: anEvent [ + anEvent currentTarget requestFocus +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onAddNewFilterShortcutEvent: anEvent [ + self hasChildren ifFalse: [ ^ self ]. + anEvent consumed: true. + self children last + requestFocus; + dispatchEvent: GtFilterEditWish new +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onAddSelectedItemEvent: aShortcutEvent [ + | anElementList | + anElementList := aShortcutEvent currentTarget. + anElementList + selectedItemDo: [ :aFilterModel :anIndex | + BlTaskAction + enqueueElement: self + action: [ anElementList fireEvent: BrDropdownHideWish new ]. + BlTaskAction + enqueueElement: self + action: [ self requestAddFilterModel: aFilterModel ] ] +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onEditItemsEvent: anEvent [ + anEvent currentTarget dispatchEvent: GtFilterEditWish new +] + +{ #category : #'api - filter view model' } +GtFilterItemsElement >> onFiltersViewModelChanged [ + self updateItemsElement +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onFocusNextFilterWish: anEvent [ + anEvent consumed: true. + BlTaskAction enqueueElement: self action: [ + GtFilterExcludingFocusFinder new + direction: BlFocusSearchDirectionForward new; + root: self; + referenceElement: anEvent currentTarget; + excludedElement: anEvent currentTarget; + nextFocusDo: [ :anElement | + anElement requestFocus ] ifNone: [ ] ] +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onItemAdded: anAnnouncement [ + BlTaskAction + enqueueElement: self + action: [ self addItemElementForViewModel: anAnnouncement item ] +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onItemMouseClickEvent: anEvent [ + | aModel | + anEvent consumed: true. + anEvent currentTarget fireEvent: BrDropdownHideWish new. + aModel := anEvent currentTarget userData at: GtFilterModel ifAbsent: [ nil ]. + self requestAddFilterModel: aModel +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onItemMouseEnterEvent: anEvent listElement: aListElement [ + "Select the hovered item" + + | anItemElement aFilter | + anItemElement := anEvent currentTarget. + aFilter := anItemElement userData at: GtFilterModel ifAbsent: [ nil ]. + aFilter ifNil: [ ^ self ]. + aListElement + itemSuchThat: [ :eachItem | eachItem == aFilter ] + ifFound: [ :anIndex | aListElement selectOne: anIndex ] +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onItemRemoved: anAnnouncement [ + BlTaskAction + enqueueElement: self + action: [ self removeItemElementForViewModel: anAnnouncement item ] +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onItemsChanged: anAnnouncement [ + BlTaskAction enqueueElement: self action: [ self updateItemsElement ] +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onListElementAddedToSceneGraph: anEvent [ + self focusFirstItemElementInList: anEvent currentTarget +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> onRemoveFilterViewModelOfElement: anElement from: aButton [ + anElement hasFilterViewModel ifFalse: [ ^ self ]. + self hasFiltersViewModel ifFalse: [ ^ self ]. + + self filtersViewModel removeFilterViewModel: anElement filterViewModel +] + +{ #category : #'event handling' } +GtFilterItemsElement >> onRemoveFilterViewModelWish: anEvent [ + anEvent filterViewModel ifNil: [ ^ self ]. + self hasFiltersViewModel ifFalse: [ ^ self ]. + + self filtersViewModel removeFilterViewModel: anEvent filterViewModel +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> removeItemElementForViewModel: aFilterViewModel [ + | aChildToFocus | + aChildToFocus := nil. + self + childrenDo: [ :aChild | + ((aChild id = GtFiltersAddButtonId uniqueInstance) not + and: [ aChild filterViewModel == aFilterViewModel ]) + ifTrue: [ | isChildFocused | + isChildFocused := aChild isFocused. + aChild removeFromParent. + isChildFocused + ifTrue: [ aChildToFocus + ifNotNil: [ aChildToFocus requestFocus ] + ifNil: [ self + childrenDo: [ :anotherChild | + anotherChild requestFocus. + ^ self ] ] ]. + ^ self ]. + aChildToFocus := aChild ] +] + +{ #category : #'event handling' } +GtFilterItemsElement >> requestAddFilterModel: aFilterModel [ + aFilterModel ifNil: [ ^ self ]. + aFilterModel isExceptionFilterModel ifTrue: [ + self phlow spawnObject: aFilterModel exception. + ^ self ]. + aFilterModel isVirtualFilterModel ifTrue: [ ^ self ]. + self hasFiltersViewModel ifFalse: [ ^ self ]. + + self filtersViewModel addFilterModel: aFilterModel +] + +{ #category : #'api - filter view model' } +GtFilterItemsElement >> subscribeToFiltersViewModel [ + self filtersViewModel weak + when: GtFiltersViewModelItemsChanged + send: #onItemsChanged: + to: self; + when: GtFiltersViewModelItemAdded + send: #onItemAdded: + to: self; + when: GtFiltersViewModelItemRemoved + send: #onItemRemoved: + to: self +] + +{ #category : #'api - filter view model' } +GtFilterItemsElement >> unsubscribeFromFiltersViewModel [ + self filtersViewModel unsubscribe: self +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> updateItemsElement [ + self removeChildren. + self filtersViewModel items withIndexDo: [ :eachFilterViewModel :anIndex | + self addElementForFilterViewModel: eachFilterViewModel index: anIndex ]. + self addNewFilterElement +] + +{ #category : #'private - updating' } +GtFilterItemsElement >> updateItemsElementDueTo: aReason [ + self removeChildren. + self filtersViewModel items withIndexDo: [ :eachFilterViewModel :anIndex | + self addElementForFilterViewModel: eachFilterViewModel index: anIndex ]. + self addNewFilterElement +] diff --git a/src/GToolkit-Coder-UI/GtFilterItemsModel.class.st b/src/GToolkit-Coder-UI/GtFilterItemsModel.class.st new file mode 100644 index 000000000..10b28e787 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterItemsModel.class.st @@ -0,0 +1,172 @@ +Class { + #name : #GtFilterItemsModel, + #superclass : #GtFiltersModel, + #instVars : [ + 'items' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'add / remove' } +GtFilterItemsModel >> addFilterModel: aFilterModel [ + | aCollection | + self + assert: [ (self items identityIncludes: aFilterModel) not ] + description: [ 'Filter model can be added only once: {1}' format: {aFilterModel} ]. + + aCollection := (self items copyWith: aFilterModel). + + GtFilterItemsChangedSignal new + incomingItems: aCollection; + previousItems: items; + emit. + + items := aCollection. + self subscribeToItem: aFilterModel. + self notifyItemAdded: aFilterModel. + + aFilterModel changesFilteredResult ifFalse: [ ^ self ]. + self notifyFiltersUpdated. +] + +{ #category : #accessing } +GtFilterItemsModel >> filtersViewModelClass [ + + ^ GtFiltersItemsViewModel +] + +{ #category : #'gt - extensions' } +GtFilterItemsModel >> gtItemsFor: aView [ + + ^ aView columnedList + title: 'Items'; + items: [ self items ifNil: [ #() ] ]; + column: 'Index' + text: [ :_ :anIndex | anIndex ] + width: 40; + column: 'Name' text: #yourself; + column: 'Selected value' text: #selectedValue; + actionUpdateButton +] + +{ #category : #initialization } +GtFilterItemsModel >> initialize [ + super initialize. + items := Array empty +] + +{ #category : #accessing } +GtFilterItemsModel >> items [ + + ^ items +] + +{ #category : #accessing } +GtFilterItemsModel >> items: aCollection [ + GtFilterItemsModelComparator new + incomingItems: aCollection; + existingItems: items; + ifEqual: [ ^ self ]. + + GtFilterItemsChangedSignal new + incomingItems: aCollection; + previousItems: items; + emit. + + self unsubscribeFromItems. + items := aCollection. + self subscribeToItems. + self notifyItemsChanged. + self notifyFiltersUpdated. +] + +{ #category : #'private - notifying' } +GtFilterItemsModel >> notifyFiltersUpdated [ + "This announcement must be delivered only if the update changes search filter results. + For example, we do not want to announce it if an added or removed filter model + does not have a selected value." + + self announce: (GtFiltersModelUpdated new model: self) +] + +{ #category : #'private - notifying' } +GtFilterItemsModel >> notifyItemAdded: aFilterModel [ + self + announce: (GtFiltersModelItemAdded new + model: self; + item: aFilterModel) +] + +{ #category : #'private - notifying' } +GtFilterItemsModel >> notifyItemRemoved: aFilterModel [ + self + announce: (GtFiltersModelItemRemoved new + model: self; + item: aFilterModel) +] + +{ #category : #'private - notifying' } +GtFilterItemsModel >> notifyItemsChanged [ + self + announce: (GtFiltersModelItemsChanged new + model: self; + items: self items) +] + +{ #category : #'event handling' } +GtFilterItemsModel >> onFilterModelAnnouncement: anAnnouncement [ + self assert: [ (self items identityIncludes: anAnnouncement model) ]. + + anAnnouncement changesFilteredResult ifFalse: [ ^ self ]. + self notifyFiltersUpdated +] + +{ #category : #'add / remove' } +GtFilterItemsModel >> removeFilterModel: aFilterModel [ + | aCollection | + self + assert: [ self items identityIncludes: aFilterModel ] + description: [ 'Filter model is missing and therefore cannot be removed: {1}' + format: {aFilterModel} ]. + + aCollection := self items reject: [ :each | each == aFilterModel ]. + + GtFilterItemsChangedSignal new + incomingItems: aCollection; + previousItems: items; + emit. + + self unsubscribeFromItem: aFilterModel. + items := aCollection. + self notifyItemRemoved: aFilterModel. + + aFilterModel changesFilteredResult ifFalse: [ ^ self ]. + self notifyFiltersUpdated. +] + +{ #category : #private } +GtFilterItemsModel >> subscribeToItem: aFilterModel [ + (aFilterModel hasSubscriber: self) ifTrue: [ ^ self ]. + + aFilterModel weak + when: GtFilterModelAnnouncement + send: #onFilterModelAnnouncement: + to: self +] + +{ #category : #private } +GtFilterItemsModel >> subscribeToItems [ + self items do: [ :eachItem | + self subscribeToItem: eachItem ] +] + +{ #category : #private } +GtFilterItemsModel >> unsubscribeFromItem: aFilterModel [ + aFilterModel unsubscribe: self +] + +{ #category : #private } +GtFilterItemsModel >> unsubscribeFromItems [ + self items do: [ :eachItem | + self unsubscribeFromItem: eachItem ] +] diff --git a/src/GToolkit-Coder-UI/GtFilterItemsModelComparator.class.st b/src/GToolkit-Coder-UI/GtFilterItemsModelComparator.class.st new file mode 100644 index 000000000..feb6a5cdc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterItemsModelComparator.class.st @@ -0,0 +1,39 @@ +Class { + #name : #GtFilterItemsModelComparator, + #superclass : #Object, + #instVars : [ + 'incomingItems', + 'existingItems' + ], + #category : #'GToolkit-Coder-UI-Filters - Support' +} + +{ #category : #accessing } +GtFilterItemsModelComparator >> existingItems [ + ^ existingItems +] + +{ #category : #accessing } +GtFilterItemsModelComparator >> existingItems: anObject [ + existingItems := anObject +] + +{ #category : #comparing } +GtFilterItemsModelComparator >> ifEqual: aBlock [ + existingItems size = incomingItems size ifFalse: [ ^ self ]. + + existingItems with: incomingItems do: [ :anExisting :anIncoming | + (anExisting equals: anIncoming) ifFalse: [ ^ self ] ]. + + ^ aBlock value +] + +{ #category : #accessing } +GtFilterItemsModelComparator >> incomingItems [ + ^ incomingItems +] + +{ #category : #accessing } +GtFilterItemsModelComparator >> incomingItems: anObject [ + incomingItems := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterLabelElement.class.st b/src/GToolkit-Coder-UI/GtFilterLabelElement.class.st new file mode 100644 index 000000000..d22043fab --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterLabelElement.class.st @@ -0,0 +1,201 @@ +Class { + #name : #GtFilterLabelElement, + #superclass : #BlElement, + #traits : 'TBrLayoutResizable + TGtWithFilterViewModel', + #classTraits : 'TBrLayoutResizable classTrait + TGtWithFilterViewModel classTrait', + #instVars : [ + 'labelElement', + 'settingsContainer' + ], + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #'private - updating' } +GtFilterLabelElement >> addSettingsElement: anElement [ + settingsContainer addChild: anElement as: GtFilterSettingsId +] + +{ #category : #initialization } +GtFilterLabelElement >> defaultLayout [ + ^ BlLinearLayout horizontal +] + +{ #category : #initialization } +GtFilterLabelElement >> initialize [ + super initialize. + self fitContent. + self beFocusable. + self beInSingleCompositionLayer. + + self initializeLabelElement. + self initializeSettingsContainer. + + self initializeStyling. + self initializeEventHandlers. + self initializeShortcuts +] + +{ #category : #initialization } +GtFilterLabelElement >> initializeEventHandlers [ + self when: BlClickEvent do: [ :anEvent | self onClickEvent: anEvent ]. + self when: GtFilterEditWish do: [ :anEvent | self onEditWish: anEvent ]. + self when: GtFilterFocusIntendedPartAfterAddingWish do: [ :anEvent | + self onFocusFirstPartWish: anEvent ]. + self when: GtFilterFocusWholeWish do: [ :anEvent | self onFocusWholeWish: anEvent ]. + self when: GtFilterFocusNextPartWish do: [ :anEvent | self onFocusNextPartdWish: anEvent ] +] + +{ #category : #initialization } +GtFilterLabelElement >> initializeLabelElement [ + labelElement := BrLabel new + id: GtFilterTagLabelId; + text: 'Filter'; + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont glamorousCodeSmallSize; + vFitContent; + beSmallSize; + constraintsDo: [ :c | c linear vertical alignCenter ] +] + +{ #category : #initialization } +GtFilterLabelElement >> initializeSettingsContainer [ + settingsContainer := BrHorizontalPane new + hFitContentLimited; + vFitContent; + margin: (BlInsets all: 2); + constraintsDo: [ :c | + c linear vertical alignCenter. + c minHeight: 20 ] +] + +{ #category : #initialization } +GtFilterLabelElement >> initializeShortcuts [ + +] + +{ #category : #initialization } +GtFilterLabelElement >> initializeStyling [ + self + addAptitude: (BrStyleCommonAptitude new + default: [ :aStyle | + aStyle geometry: (BlRoundedRectangleGeometry cornerRadius: 4). + aStyle background: self theme status neutralBackgroundColor ]; + hovered: [ :aStyle | aStyle background: self theme status neutralBackgroundColor darker ]; + focused: [ :aStyle | aStyle background: self theme editor focusedBorderColor lighter lighter ]). + + self + addAptitude: (BrStyleCommonAptitude new + // GtFilterTagLabelId / #label; + default: [ :aStyle | aStyle foreground: Color black ]; + focused: [ :aStyle | aStyle foreground: Color white ]) +] + +{ #category : #'event handling' } +GtFilterLabelElement >> onClickEvent: anEvent [ + anEvent consumed: true. + anEvent modifiers isSoftAltOnly ifFalse: [ + self requestFocus. + ^ self ]. + + GtFilterMethodDefinitionSpawner new + filterElement: self; + spawn +] + +{ #category : #'event handling' } +GtFilterLabelElement >> onEditWish: anEvent [ + self + settingsElementDo: [ :anElement | anElement dispatchEvent: GtFilterEditWish new ] +] + +{ #category : #'api - filter view model' } +GtFilterLabelElement >> onFilterViewModelChanged [ + self updateElement +] + +{ #category : #'event handling' } +GtFilterLabelElement >> onFocusFirstPartWish: anEvent [ + anEvent consumed: true. + self + settingsElementDo: [ :aSettingsElement | + | aWish | + aWish := GtFilterFocusFirstPartWish new. + aSettingsElement dispatchEvent: aWish. + aWish isConsumed ifFalse: [ self requestFocus ] ]. + + true ifTrue: [ ^ self ]. + self requestFocus. + BlTaskAction + enqueueElement: self + action: [ BlFocusFinder new + right; + root: settingsContainer; + referenceElement: settingsContainer; + nextFocusDo: [ :anElement | anElement requestFocus ] + ifNone: [ self requestFocus ] ] +] + +{ #category : #'event handling' } +GtFilterLabelElement >> onFocusNextPartdWish: anEvent [ + anEvent consumed: true. + self requestFocus. + self fireEvent: GtFilterFocusNextFilterWish new +] + +{ #category : #'event handling' } +GtFilterLabelElement >> onFocusWholeWish: anEvent [ + anEvent consumed: true. + self requestFocus +] + +{ #category : #'event handling' } +GtFilterLabelElement >> onLabelChanged: anAnnouncement [ + BlTaskAction enqueueElement: self action: [ self updateLabelElement ] +] + +{ #category : #'private - updating' } +GtFilterLabelElement >> removeSettingsElement [ + settingsContainer removeChildren +] + +{ #category : #accessing } +GtFilterLabelElement >> settingsElementDo: aBlock [ + | aChild | + aChild := settingsContainer children at: 1 ifAbsent: [ ^ self ]. + aBlock cull: aChild +] + +{ #category : #'api - filter view model' } +GtFilterLabelElement >> subscribeToFilterViewModel [ + self filterViewModel weak + when: GtFilterViewModelLabelChanged + send: #onLabelChanged: + to: self +] + +{ #category : #'api - filter view model' } +GtFilterLabelElement >> unsubscribeFromFilterViewModel [ + self filterViewModel unsubscribe: self +] + +{ #category : #'private - updating' } +GtFilterLabelElement >> updateElement [ + self updateLabelElement. + self updateSettingsContainer. +] + +{ #category : #'private - updating' } +GtFilterLabelElement >> updateLabelElement [ + labelElement text: self filterViewModel label +] + +{ #category : #'private - updating' } +GtFilterLabelElement >> updateSettingsContainer [ + | anElement | + self removeSettingsElement. + anElement := self filterViewModel asUserSettingsElement. + anElement ifNil: [ ^ self ]. + anElement constraintsDo: [ :c | + c linear vertical alignCenter. + c frame vertical alignCenter ]. + self addSettingsElement: anElement +] diff --git a/src/GToolkit-Coder-UI/GtFilterMethodDefinitionSpawner.class.st b/src/GToolkit-Coder-UI/GtFilterMethodDefinitionSpawner.class.st new file mode 100644 index 000000000..35538a765 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterMethodDefinitionSpawner.class.st @@ -0,0 +1,47 @@ +" +I find and spawn {{gtClass:GtFilterModel}} compiled method definitions. +" +Class { + #name : #GtFilterMethodDefinitionSpawner, + #superclass : #Object, + #instVars : [ + 'filterElement' + ], + #category : #'GToolkit-Coder-UI-Filters - Support' +} + +{ #category : #accessing } +GtFilterMethodDefinitionSpawner >> filterElement [ + ^ filterElement +] + +{ #category : #accessing } +GtFilterMethodDefinitionSpawner >> filterElement: anElement [ + filterElement := anElement +] + +{ #category : #'api - spawning' } +GtFilterMethodDefinitionSpawner >> methodDefinition [ + + | aSearchFilter aMethod aClass | + filterElement hasFilterViewModel ifFalse: [ ^ nil ]. + + aSearchFilter := filterElement filterViewModel filterModel asSearchFilter. + aSearchFilter ifNil: [ ^ nil ]. + + aClass := aSearchFilter class classSide. + aMethod := aClass lookupSelector: #filterDescriptor2For:. + (aMethod isNil + or: [ aMethod = (GtSearchFilter class >> #filterDescriptor2For:) ]) + ifTrue: [ aMethod := aClass lookupSelector: #globalFilterDescriptor2 ]. + + ^ aMethod +] + +{ #category : #'api - spawning' } +GtFilterMethodDefinitionSpawner >> spawn [ + filterElement ifNil: [ ^ self ]. + + self methodDefinition ifNotNil: [ :aMethod | + filterElement phlow spawnTool: (GtMethodCoderTool compiledMethod: aMethod) ]. +] diff --git a/src/GToolkit-Coder-UI/GtFilterMethodsCoderAvailableFiltersBuilder.class.st b/src/GToolkit-Coder-UI/GtFilterMethodsCoderAvailableFiltersBuilder.class.st new file mode 100644 index 000000000..2589e7c2c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterMethodsCoderAvailableFiltersBuilder.class.st @@ -0,0 +1,33 @@ +Class { + #name : #GtFilterMethodsCoderAvailableFiltersBuilder, + #superclass : #Object, + #instVars : [ + 'coders' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #building } +GtFilterMethodsCoderAvailableFiltersBuilder >> availableFilters [ + + | availableFilters | + availableFilters := SortedCollection sortBlock: [ :a :b | a order < b order ]. + + coders ifNil: [ ^ #() ]. + + GtSearchMethodsFilter withAllSubclassesDo: [ :eachClass | + eachClass filterDescriptors2For: coders into: availableFilters ]. + + ^ availableFilters +] + +{ #category : #accessing } +GtFilterMethodsCoderAvailableFiltersBuilder >> coders [ + + ^ coders +] + +{ #category : #accessing } +GtFilterMethodsCoderAvailableFiltersBuilder >> coders: aMethodsCoder [ + coders := aMethodsCoder +] diff --git a/src/GToolkit-Coder-UI/GtFilterModel.class.st b/src/GToolkit-Coder-UI/GtFilterModel.class.st new file mode 100644 index 000000000..87cf01d0a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModel.class.st @@ -0,0 +1,268 @@ +Class { + #name : #GtFilterModel, + #superclass : #Object, + #traits : 'TGtAnnouncer', + #classTraits : 'TGtAnnouncer classTrait', + #instVars : [ + 'announcer', + 'order', + 'creator', + 'name', + 'labelBuilder', + 'isDefault' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #comparing } +GtFilterModel >> = anObject [ + "I compare similarity of two filter models. + I must not compare selected items. For such cases, use #equals:." + + self == anObject ifTrue: [ ^ true ]. + + ^ self class = anObject class + and: [ self name = anObject name + and: [ self order = anObject order ] ] +] + +{ #category : #announcer } +GtFilterModel >> announcer [ + + ^ announcer ifNil: [ announcer := Announcer new ] +] + +{ #category : #converting } +GtFilterModel >> asFilterLabeledElement [ + + ^ self filterLabelElementClass new filterViewModel: self asFilterViewModel +] + +{ #category : #converting } +GtFilterModel >> asFilterViewModel [ + + ^ self filterViewModelClass new filterModel: self +] + +{ #category : #converting } +GtFilterModel >> asSearchFilter [ + + ^ creator cull: self selectedValue cull: self +] + +{ #category : #converting } +GtFilterModel >> asUserParametersElement [ + + ^ self asFilterViewModel asUserParametersElement +] + +{ #category : #converting } +GtFilterModel >> asUserSettingsElement [ + + ^ self asFilterViewModel asUserSettingsElement +] + +{ #category : #initialization } +GtFilterModel >> beDefault [ + isDefault := true +] + +{ #category : #initialization } +GtFilterModel >> beNotDefault [ + isDefault := false +] + +{ #category : #testing } +GtFilterModel >> changesFilteredResult [ + "Indicates whether it changes a result of filtered items, e.g., list of method coders." + + + ^ true +] + +{ #category : #accessing } +GtFilterModel >> creator [ + ^ creator +] + +{ #category : #accessing } +GtFilterModel >> creator: anObject [ + creator := anObject +] + +{ #category : #initialization } +GtFilterModel >> defaultFilterName [ + | aToIndex aFromIndex | + aToIndex := (self class name size - 5 max: 1). + aFromIndex := 9 min: aToIndex. + ^ self class name copyFrom: aFromIndex to: aToIndex +] + +{ #category : #initialization } +GtFilterModel >> defaultFilterOrder [ + ^ 50 +] + +{ #category : #comparing } +GtFilterModel >> equals: anObject [ + self = anObject ifFalse: [ ^ false ]. + + ^ self selectedValue = anObject selectedValue +] + +{ #category : #accessing } +GtFilterModel >> filterLabelElementClass [ + + ^ labelBuilder labelElementClass +] + +{ #category : #accessing } +GtFilterModel >> filterViewModelClass [ + + ^ labelBuilder labelElementClass +] + +{ #category : #'gt - extensions' } +GtFilterModel >> gtLiveFor: aView [ + + ^ aView explicit + title: 'Live'; + stencil: [ self asFilterLabeledElement margin: (BlInsets all: 10) ] +] + +{ #category : #comparing } +GtFilterModel >> hash [ + ^ self class hash + bitXor: (self name hash + bitXor: self order hash) +] + +{ #category : #initialization } +GtFilterModel >> initialize [ + super initialize. + labelBuilder := GtFilterModelImplicitPrefixLabel new model: self. + isDefault := false. +] + +{ #category : #testing } +GtFilterModel >> isDefaultFilterModel [ + ^ isDefault +] + +{ #category : #testing } +GtFilterModel >> isExceptionFilterModel [ + ^ false +] + +{ #category : #testing } +GtFilterModel >> isVirtualFilterModel [ + ^ false +] + +{ #category : #accessing } +GtFilterModel >> label [ + "Return filter label" + + + ^ labelBuilder label +] + +{ #category : #accessing } +GtFilterModel >> label: aString [ + self + deprecated: 'Use name: instead' + transformWith: '`@receiver label: `@argument' -> '`@receiver name: `@argument'. + self name: aString +] + +{ #category : #accessing } +GtFilterModel >> name [ + "Return filter name" + + + ^ name ifNil: [ name := self defaultFilterName ] +] + +{ #category : #accessing } +GtFilterModel >> name: aString [ + "Set filter name. + The name is used in a filter selection (dropdown) list." + + name = aString ifTrue: [ ^ self ]. + + name := aString. + self notifyLabelChanged +] + +{ #category : #accessing } +GtFilterModel >> named: aString [ + self name: aString +] + +{ #category : #'private - notifying' } +GtFilterModel >> notifyLabelChanged [ + self + announce: (GtFilterModelLabelChanged new + model: self; + label: self label) +] + +{ #category : #accessing } +GtFilterModel >> order [ + ^ order ifNil: [ order := self defaultFilterOrder ] +] + +{ #category : #accessing } +GtFilterModel >> order: anObject [ + order := anObject +] + +{ #category : #accessing } +GtFilterModel >> prefixLabel: aString [ + "Display a filter label before a setting widget." + + | aLabel | + aLabel := GtFilterModelExplicitPrefixLabel new label: aString. + labelBuilder = aLabel ifTrue: [ ^ self ]. + + labelBuilder := aLabel. + self notifyLabelChanged +] + +{ #category : #printing } +GtFilterModel >> printDetailsOn: aStream [ + aStream + nextPutAll: self name asString; + nextPutAll: ': '; + print: (self selectedValue ifNotNil: [ :aValue | aValue asFilterModelItem itemValue ]); + nextPutAll: ', order: '; + print: self order +] + +{ #category : #printing } +GtFilterModel >> printOn: aStream [ + super printOn: aStream. + + aStream nextPut: $(. + self printDetailsOn: aStream. + aStream nextPut: $) +] + +{ #category : #accessing } +GtFilterModel >> selectedValue [ + "Return a filter value, e.g., selected item, input text." + + ^ nil +] + +{ #category : #accessing } +GtFilterModel >> suffixLabel: aString [ + "Display a filter label after a setting widget." + + | aLabel | + aLabel := GtFilterModelExplicitSuffixLabel new label: aString. + labelBuilder = aLabel ifTrue: [ ^ self ]. + + labelBuilder := aLabel. + self notifyLabelChanged +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelAbstractMethodItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelAbstractMethodItem.class.st new file mode 100644 index 000000000..7ac9c02e9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelAbstractMethodItem.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtFilterModelAbstractMethodItem, + #superclass : #GtFilterModelMethodDefinitionItem, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterModelAbstractMethodItem >> isSubclassResponsibilityMethod [ + ^ true +] + +{ #category : #accessing } +GtFilterModelAbstractMethodItem >> label [ + ^ 'abstract' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelAllItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelAllItem.class.st new file mode 100644 index 000000000..2515907c2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelAllItem.class.st @@ -0,0 +1,49 @@ +Class { + #name : #GtFilterModelAllItem, + #superclass : #GtFilterModelItem, + #instVars : [ + 'label' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #factory } +GtFilterModelAllItem class >> default [ + ^ self new +] + +{ #category : #factory } +GtFilterModelAllItem class >> methodsLabel [ + ^ self new label: 'All methods' +] + +{ #category : #factory } +GtFilterModelAllItem class >> pragmasLabel [ + ^ self new label: 'All pragmas' +] + +{ #category : #accessing } +GtFilterModelAllItem >> ifSome: aBlock ifNone: aNoneBlock [ + ^ aNoneBlock value +] + +{ #category : #initialization } +GtFilterModelAllItem >> initialize [ + super initialize. + label := 'All' +] + +{ #category : #accessing } +GtFilterModelAllItem >> label [ + ^ label +] + +{ #category : #factory } +GtFilterModelAllItem >> label: aString [ + label := aString +] + +{ #category : #convenience } +GtFilterModelAllItem >> withAll: aCollection [ + ^ Array with: self withAll: aCollection +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelAllItemsSelector.class.st b/src/GToolkit-Coder-UI/GtFilterModelAllItemsSelector.class.st new file mode 100644 index 000000000..3787c87d5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelAllItemsSelector.class.st @@ -0,0 +1,17 @@ +Class { + #name : #GtFilterModelAllItemsSelector, + #superclass : #GtFilterModelItemsSelector, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelAllItemsSelector >> items [ + + ^ self filterModel items +] + +{ #category : #accessing } +GtFilterModelAllItemsSelector >> itemsFuture [ + + ^ self filterModel itemsFuture +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelAnnouncement.class.st b/src/GToolkit-Coder-UI/GtFilterModelAnnouncement.class.st new file mode 100644 index 000000000..a0deb2b53 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelAnnouncement.class.st @@ -0,0 +1,25 @@ +Class { + #name : #GtFilterModelAnnouncement, + #superclass : #Announcement, + #instVars : [ + 'model' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #testing } +GtFilterModelAnnouncement >> changesFilteredResult [ + "Indicates whether it changes a result of filtered items, e.g., list of method coders." + + ^ false +] + +{ #category : #accessing } +GtFilterModelAnnouncement >> model [ + ^ model +] + +{ #category : #accessing } +GtFilterModelAnnouncement >> model: anObject [ + model := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelClassItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelClassItem.class.st new file mode 100644 index 000000000..a218ab401 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelClassItem.class.st @@ -0,0 +1,55 @@ +Class { + #name : #GtFilterModelClassItem, + #superclass : #GtFilterModelItem, + #instVars : [ + 'itemClass' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelClassItem >> icon [ + | anIconName | + self itemClass ifNil: [ ^ nil ]. + + anIconName := self itemClass gtSystemIconName. + anIconName ifNil: [ ^ nil ]. + + ^ self itemClass gtSafeIconNamed: anIconName +] + +{ #category : #accessing } +GtFilterModelClassItem >> itemClass [ + ^ itemClass +] + +{ #category : #accessing } +GtFilterModelClassItem >> itemClass: anObject [ + itemClass := anObject +] + +{ #category : #accessing } +GtFilterModelClassItem >> itemSymbol [ + ^ self itemClass instanceSide name asSymbol +] + +{ #category : #accessing } +GtFilterModelClassItem >> itemValue [ + ^ self itemClass +] + +{ #category : #accessing } +GtFilterModelClassItem >> label [ + + ^ self itemClass name +] + +{ #category : #printing } +GtFilterModelClassItem >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + print: self itemClass; + nextPut: $) +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelClassOriginItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelClassOriginItem.class.st new file mode 100644 index 000000000..45197792f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelClassOriginItem.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtFilterModelClassOriginItem, + #superclass : #GtFilterModelMethodOriginItem, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelClassOriginItem >> icon [ + ^ self iconNamed: #class +] + +{ #category : #accessing } +GtFilterModelClassOriginItem >> label [ + ^ 'non-trait' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelClassSideItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelClassSideItem.class.st new file mode 100644 index 000000000..1bffc42a5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelClassSideItem.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterModelClassSideItem, + #superclass : #GtFilterModelSideItem, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelClassSideItem >> label [ + ^ 'class' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelComputingItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelComputingItem.class.st new file mode 100644 index 000000000..6db5c409c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelComputingItem.class.st @@ -0,0 +1,22 @@ +Class { + #name : #GtFilterModelComputingItem, + #superclass : #GtFilterModelItem, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelComputingItem >> ifSome: aBlock ifNone: aNoneBlock [ + ^ aNoneBlock value +] + +{ #category : #testing } +GtFilterModelComputingItem >> isVirtualFilterModelItem [ + ^ true +] + +{ #category : #accessing } +GtFilterModelComputingItem >> label [ + ^ 'computing...' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelDefinedMethodItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelDefinedMethodItem.class.st new file mode 100644 index 000000000..836d16590 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelDefinedMethodItem.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterModelDefinedMethodItem, + #superclass : #GtFilterModelMethodDefinitionItem, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterModelDefinedMethodItem >> label [ + ^ 'defined' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelEmptyItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelEmptyItem.class.st new file mode 100644 index 000000000..083baa23a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelEmptyItem.class.st @@ -0,0 +1,26 @@ +" +I am a {{gtClass:GtFilterModelItem}}. +I am used to indicate that there are no items in a list. +" +Class { + #name : #GtFilterModelEmptyItem, + #superclass : #GtFilterModelItem, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelEmptyItem >> ifSome: aBlock ifNone: aNoneBlock [ + ^ aNoneBlock value +] + +{ #category : #testing } +GtFilterModelEmptyItem >> isVirtualFilterModelItem [ + ^ true +] + +{ #category : #accessing } +GtFilterModelEmptyItem >> label [ + ^ 'empty list' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelEmptyItemsBuilder.class.st b/src/GToolkit-Coder-UI/GtFilterModelEmptyItemsBuilder.class.st new file mode 100644 index 000000000..c1be359bb --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelEmptyItemsBuilder.class.st @@ -0,0 +1,27 @@ +Class { + #name : #GtFilterModelEmptyItemsBuilder, + #superclass : #GtFilterModelItemsBuilder, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'api - instantiation' } +GtFilterModelEmptyItemsBuilder >> create [ + ^ Array empty +] + +{ #category : #'api - instantiation' } +GtFilterModelEmptyItemsBuilder >> createFuture [ + ^ Array empty asAsyncFuture +] + +{ #category : #accessing } +GtFilterModelEmptyItemsBuilder >> firstItemDo: aBlock [ + "do nothing" +] + +{ #category : #accessing } +GtFilterModelEmptyItemsBuilder >> lastItemDo: aBlock [ + "do nothing" +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelExampleResultStateItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelExampleResultStateItem.class.st new file mode 100644 index 000000000..e48ca528e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelExampleResultStateItem.class.st @@ -0,0 +1,46 @@ +Class { + #name : #GtFilterModelExampleResultStateItem, + #superclass : #GtFilterModelItem, + #instVars : [ + 'resultState' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelExampleResultStateItem >> icon [ + ^ BlElement new + size: 8 @ 8; + background: self resultState color; + geometry: BlRectangleGeometry new +] + +{ #category : #accessing } +GtFilterModelExampleResultStateItem >> itemValue [ + ^ self resultState +] + +{ #category : #accessing } +GtFilterModelExampleResultStateItem >> label [ + ^ self resultState label +] + +{ #category : #printing } +GtFilterModelExampleResultStateItem >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + print: (self resultState ifNotNil: #label); + nextPut: $) +] + +{ #category : #accessing } +GtFilterModelExampleResultStateItem >> resultState [ + ^ resultState +] + +{ #category : #accessing } +GtFilterModelExampleResultStateItem >> resultState: anObject [ + resultState := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelExceptionItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelExceptionItem.class.st new file mode 100644 index 000000000..117151df8 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelExceptionItem.class.st @@ -0,0 +1,78 @@ +" +I am displayed if there is an exception while computing model items. +For intenal use only. I must not be used as an expected model item. +" +Class { + #name : #GtFilterModelExceptionItem, + #superclass : #GtFilterModelItem, + #instVars : [ + 'exception' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'instance creation' } +GtFilterModelExceptionItem class >> freeze: anException [ + ^ self new exception: (GtSystemUtility freeze: anException) +] + +{ #category : #accessing } +GtFilterModelExceptionItem >> exception [ + ^ exception +] + +{ #category : #accessing } +GtFilterModelExceptionItem >> exception: anObject [ + exception := anObject +] + +{ #category : #'gt - extensions' } +GtFilterModelExceptionItem >> gtExceptionMessageFor: aView [ + + exception ifNil: [ ^ aView empty ]. + + ^ aView forward + title: 'Exception message'; + object: [ exception ]; + view: #gtMessageTextFor: +] + +{ #category : #'gt - extensions' } +GtFilterModelExceptionItem >> gtExceptionStackFor: aView [ + + exception ifNil: [ ^ aView empty ]. + + ^ aView forward + title: 'Exception stack'; + object: [ exception ]; + view: #gtLiveFor: +] + +{ #category : #accessing } +GtFilterModelExceptionItem >> icon [ + ^ BrGlamorousVectorIcons debug +] + +{ #category : #accessing } +GtFilterModelExceptionItem >> ifSome: aBlock ifNone: aNoneBlock [ + "Exception item is not expected in any model. + We therefore treat it as an item without a value. + Widgets can treat it differently using #isExceptionFilterModelItem." + + ^ aNoneBlock value +] + +{ #category : #testing } +GtFilterModelExceptionItem >> isExceptionFilterModelItem [ + ^ true +] + +{ #category : #accessing } +GtFilterModelExceptionItem >> label [ + "Return item label or nil" + + + ^ self exception + ifNotNil: [ :anException | anException printStringLimitedTo: 30 ] + ifNil: [ 'exception' ] +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelExplicitItemsBuilder.class.st b/src/GToolkit-Coder-UI/GtFilterModelExplicitItemsBuilder.class.st new file mode 100644 index 000000000..fa057a308 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelExplicitItemsBuilder.class.st @@ -0,0 +1,52 @@ +Class { + #name : #GtFilterModelExplicitItemsBuilder, + #superclass : #GtFilterModelItemsBuilder, + #instVars : [ + 'items' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #comparing } +GtFilterModelExplicitItemsBuilder >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + ^ self class = anObject class and: [ self items = anObject items ] +] + +{ #category : #'api - instantiation' } +GtFilterModelExplicitItemsBuilder >> create [ + ^ self items ifNil: [ Array empty ] +] + +{ #category : #'api - instantiation' } +GtFilterModelExplicitItemsBuilder >> createFuture [ + ^ self create asAsyncFuture +] + +{ #category : #accessing } +GtFilterModelExplicitItemsBuilder >> firstItemDo: aBlock [ + self items ifEmpty: [ ^ self ]. + aBlock cull: self items first +] + +{ #category : #comparing } +GtFilterModelExplicitItemsBuilder >> hash [ + ^ self class hash bitXor: self items hash +] + +{ #category : #accessing } +GtFilterModelExplicitItemsBuilder >> items [ + ^ items +] + +{ #category : #accessing } +GtFilterModelExplicitItemsBuilder >> items: aCollection [ + items := aCollection collect: #asFilterModelItem +] + +{ #category : #accessing } +GtFilterModelExplicitItemsBuilder >> lastItemDo: aBlock [ + self items ifEmpty: [ ^ self ]. + aBlock cull: self items last +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelExplicitPrefixLabel.class.st b/src/GToolkit-Coder-UI/GtFilterModelExplicitPrefixLabel.class.st new file mode 100644 index 000000000..1110750f3 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelExplicitPrefixLabel.class.st @@ -0,0 +1,30 @@ +Class { + #name : #GtFilterModelExplicitPrefixLabel, + #superclass : #GtFilterModelLabel, + #instVars : [ + 'label' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #comparing } +GtFilterModelExplicitPrefixLabel >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + ^ self class = anObject class and: [ self label = anObject label ] +] + +{ #category : #comparing } +GtFilterModelExplicitPrefixLabel >> hash [ + ^ self class hash bitXor: self label hash +] + +{ #category : #accessing } +GtFilterModelExplicitPrefixLabel >> label [ + ^ label +] + +{ #category : #accessing } +GtFilterModelExplicitPrefixLabel >> label: anObject [ + label := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelExplicitSuffixLabel.class.st b/src/GToolkit-Coder-UI/GtFilterModelExplicitSuffixLabel.class.st new file mode 100644 index 000000000..c59d8ad3e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelExplicitSuffixLabel.class.st @@ -0,0 +1,35 @@ +Class { + #name : #GtFilterModelExplicitSuffixLabel, + #superclass : #GtFilterModelLabel, + #instVars : [ + 'label' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #comparing } +GtFilterModelExplicitSuffixLabel >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + ^ self class = anObject class and: [ self label = anObject label ] +] + +{ #category : #comparing } +GtFilterModelExplicitSuffixLabel >> hash [ + ^ self class hash bitXor: self label hash +] + +{ #category : #accessing } +GtFilterModelExplicitSuffixLabel >> label [ + ^ label +] + +{ #category : #accessing } +GtFilterModelExplicitSuffixLabel >> label: anObject [ + label := anObject +] + +{ #category : #accessing } +GtFilterModelExplicitSuffixLabel >> labelElementClass [ + ^ GtFilterSuffixLabelElement +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelImplicitPrefixLabel.class.st b/src/GToolkit-Coder-UI/GtFilterModelImplicitPrefixLabel.class.st new file mode 100644 index 000000000..72ddee5ea --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelImplicitPrefixLabel.class.st @@ -0,0 +1,36 @@ +Class { + #name : #GtFilterModelImplicitPrefixLabel, + #superclass : #GtFilterModelLabel, + #instVars : [ + 'model' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #comparing } +GtFilterModelImplicitPrefixLabel >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + ^ self class = anObject class and: [ self model = anObject model ] +] + +{ #category : #comparing } +GtFilterModelImplicitPrefixLabel >> hash [ + ^ self class hash bitXor: self model hash +] + +{ #category : #'as yet unclassified' } +GtFilterModelImplicitPrefixLabel >> label [ + ^ self model name +] + +{ #category : #accessing } +GtFilterModelImplicitPrefixLabel >> model [ + + ^ model +] + +{ #category : #accessing } +GtFilterModelImplicitPrefixLabel >> model: aFilterModel [ + model := aFilterModel +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelInstanceSideItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelInstanceSideItem.class.st new file mode 100644 index 000000000..0e9e926e8 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelInstanceSideItem.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtFilterModelInstanceSideItem, + #superclass : #GtFilterModelSideItem, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterModelInstanceSideItem >> isInstanceSideMethod [ + ^ true +] + +{ #category : #accessing } +GtFilterModelInstanceSideItem >> label [ + ^ 'instance' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelItem.class.st new file mode 100644 index 000000000..aba2ba1df --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelItem.class.st @@ -0,0 +1,103 @@ +Class { + #name : #GtFilterModelItem, + #superclass : #Object, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #comparing } +GtFilterModelItem >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + self class = anObject class ifFalse: [ ^ false ]. + + ^ self label = anObject label +] + +{ #category : #converting } +GtFilterModelItem >> asFilterModelItem [ + ^ self +] + +{ #category : #comparing } +GtFilterModelItem >> hash [ + ^ self label hash +] + +{ #category : #accessing } +GtFilterModelItem >> icon [ + + ^ nil +] + +{ #category : #accessing } +GtFilterModelItem >> ifIcon: anIconBlock [ + ^ self + ifIconAndLabel: anIconBlock + ifIcon: anIconBlock + ifLabel: [ "ignore" ] +] + +{ #category : #accessing } +GtFilterModelItem >> ifIconAndLabel: anIconAndLabelBlock ifIcon: anIconBlock ifLabel: aLabelBlock [ + ^ self label + ifNil: [ + self icon + ifNil: [ "ignore" ] + ifNotNil: [ :anIcon | anIconBlock cull: anIcon ] ] + ifNotNil: [ :aLabel | + self icon + ifNil: [ aLabelBlock cull: aLabel ] + ifNotNil: [ :anIcon | anIconAndLabelBlock cull: anIcon cull: aLabel ] ] +] + +{ #category : #accessing } +GtFilterModelItem >> ifLabel: aLabelBlock [ + ^ self + ifIconAndLabel: [ :anIcon :aLabel | aLabelBlock cull: aLabel ] + ifIcon: [ "ignore" ] + ifLabel: aLabelBlock +] + +{ #category : #accessing } +GtFilterModelItem >> ifSome: aBlock [ + ^ self ifSome: aBlock ifNone: [ nil ] +] + +{ #category : #accessing } +GtFilterModelItem >> ifSome: aBlock ifNone: aNoneBlock [ + ^ aBlock cull: self itemValue cull: self +] + +{ #category : #testing } +GtFilterModelItem >> isExceptionFilterModelItem [ + ^ false +] + +{ #category : #testing } +GtFilterModelItem >> isVirtualFilterModelItem [ + ^ false +] + +{ #category : #accessing } +GtFilterModelItem >> itemValue [ + ^ self +] + +{ #category : #accessing } +GtFilterModelItem >> label [ + "Return item label or nil" + + + ^ self subclassResponsibility +] + +{ #category : #accessing } +GtFilterModelItem >> labelSize [ + + ^ self label ifNotNil: #size ifNil: [ 0 ] +] + +{ #category : #converting } +GtFilterModelItem >> onCreateFilter: aFilter [ + ^ self +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelItemIconStencilBuilder.class.st b/src/GToolkit-Coder-UI/GtFilterModelItemIconStencilBuilder.class.st new file mode 100644 index 000000000..89b5af25e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelItemIconStencilBuilder.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GtFilterModelItemIconStencilBuilder, + #superclass : #BrStencilBuilder, + #instVars : [ + 'object' + ], + #category : #'GToolkit-Coder-UI-Filters - Support' +} + +{ #category : #'api - instantiation' } +GtFilterModelItemIconStencilBuilder >> buildDefault [ + + | anIconName | + anIconName := self object gtIconName. + (anIconName isNil or: [ anIconName = Object gtIconName ]) ifTrue: [ ^ nil ]. + + ^ (self object gtSafeIconNamed: anIconName) ifNotNil: #asElement +] + +{ #category : #accessing } +GtFilterModelItemIconStencilBuilder >> object [ + ^ object +] + +{ #category : #accessing } +GtFilterModelItemIconStencilBuilder >> object: anObject [ + object := anObject +] + +{ #category : #'api - instantiation' } +GtFilterModelItemIconStencilBuilder >> paramsOn: aStencilExecutor [ + super paramsOn: aStencilExecutor. + + aStencilExecutor push: object +] + +{ #category : #initialization } +GtFilterModelItemIconStencilBuilder >> reset [ + object := nil +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelItemLabelStencilBuilder.class.st b/src/GToolkit-Coder-UI/GtFilterModelItemLabelStencilBuilder.class.st new file mode 100644 index 000000000..053f26fc8 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelItemLabelStencilBuilder.class.st @@ -0,0 +1,35 @@ +Class { + #name : #GtFilterModelItemLabelStencilBuilder, + #superclass : #BrStencilBuilder, + #instVars : [ + 'object' + ], + #category : #'GToolkit-Coder-UI-Filters - Support' +} + +{ #category : #'api - instantiation' } +GtFilterModelItemLabelStencilBuilder >> buildDefault [ + ^ object gtDisplayString +] + +{ #category : #accessing } +GtFilterModelItemLabelStencilBuilder >> object [ + ^ object +] + +{ #category : #accessing } +GtFilterModelItemLabelStencilBuilder >> object: anObject [ + object := anObject +] + +{ #category : #'api - instantiation' } +GtFilterModelItemLabelStencilBuilder >> paramsOn: aStencilExecutor [ + super paramsOn: aStencilExecutor. + + aStencilExecutor push: object +] + +{ #category : #initialization } +GtFilterModelItemLabelStencilBuilder >> reset [ + object := nil +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelItemsBuilder.class.st b/src/GToolkit-Coder-UI/GtFilterModelItemsBuilder.class.st new file mode 100644 index 000000000..7c07343ba --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelItemsBuilder.class.st @@ -0,0 +1,56 @@ +Class { + #name : #GtFilterModelItemsBuilder, + #superclass : #Object, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #converting } +GtFilterModelItemsBuilder >> asFilterModelItemsBuilder [ + ^ self +] + +{ #category : #'api - instantiation' } +GtFilterModelItemsBuilder >> create [ + + self deprecated: 'Use #createFuture instead'. + + ^ self subclassResponsibility +] + +{ #category : #'api - instantiation' } +GtFilterModelItemsBuilder >> createFuture [ + + ^ self subclassResponsibility +] + +{ #category : #initialization } +GtFilterModelItemsBuilder >> filterModel: aFilterModel [ + "Subclasses may use it to trigger item updates or other activities" + + +] + +{ #category : #accessing } +GtFilterModelItemsBuilder >> firstItemDo: aBlock [ + | aCollection | + aCollection := self create. + aCollection ifEmpty: [ ^ self ]. + aBlock cull: aCollection first +] + +{ #category : #accessing } +GtFilterModelItemsBuilder >> itemAt: anIndex do: aBlock [ + | aCollection | + aCollection := self create. + aCollection ifEmpty: [ ^ self ]. + aCollection size < anIndex ifTrue: [ ^ self ]. + aBlock cull: (aCollection at: anIndex) +] + +{ #category : #accessing } +GtFilterModelItemsBuilder >> lastItemDo: aBlock [ + | aCollection | + aCollection := self create. + aCollection ifEmpty: [ ^ self ]. + aBlock cull: aCollection last +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelItemsChanged.class.st b/src/GToolkit-Coder-UI/GtFilterModelItemsChanged.class.st new file mode 100644 index 000000000..b90fb690b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelItemsChanged.class.st @@ -0,0 +1,23 @@ +Class { + #name : #GtFilterModelItemsChanged, + #superclass : #GtFilterModelAnnouncement, + #instVars : [ + 'itemsBuilder' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterModelItemsChanged >> items [ + ^ itemsBuilder create +] + +{ #category : #accessing } +GtFilterModelItemsChanged >> itemsBuilder [ + ^ itemsBuilder +] + +{ #category : #accessing } +GtFilterModelItemsChanged >> itemsBuilder: aBuilder [ + itemsBuilder := aBuilder +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelItemsSelector.class.st b/src/GToolkit-Coder-UI/GtFilterModelItemsSelector.class.st new file mode 100644 index 000000000..1963acc14 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelItemsSelector.class.st @@ -0,0 +1,37 @@ +Class { + #name : #GtFilterModelItemsSelector, + #superclass : #Object, + #instVars : [ + 'filterModel' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelItemsSelector >> filterModel [ + ^ filterModel +] + +{ #category : #accessing } +GtFilterModelItemsSelector >> filterModel: anObject [ + filterModel := anObject +] + +{ #category : #accessing } +GtFilterModelItemsSelector >> items [ + + self deprecated: 'Use #createFuture instead'. + + ^ self subclassResponsibility +] + +{ #category : #accessing } +GtFilterModelItemsSelector >> itemsFuture [ + + ^ self subclassResponsibility +] + +{ #category : #accessing } +GtFilterModelItemsSelector >> selectedItem [ + ^ self filterModel selectedItem +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelItemsWithoutSelectedItemSelector.class.st b/src/GToolkit-Coder-UI/GtFilterModelItemsWithoutSelectedItemSelector.class.st new file mode 100644 index 000000000..39ab71ea9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelItemsWithoutSelectedItemSelector.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFilterModelItemsWithoutSelectedItemSelector, + #superclass : #GtFilterModelItemsSelector, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'as yet unclassified' } +GtFilterModelItemsWithoutSelectedItemSelector >> items [ + + ^ self filterModel items reject: [ :each | self selectedItem = each ] +] + +{ #category : #'as yet unclassified' } +GtFilterModelItemsWithoutSelectedItemSelector >> itemsFuture [ + + ^ self filterModel itemsFuture map: [ :anArray | + anArray reject: [ :each | self selectedItem = each ] ] +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelLabel.class.st b/src/GToolkit-Coder-UI/GtFilterModelLabel.class.st new file mode 100644 index 000000000..254e74120 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelLabel.class.st @@ -0,0 +1,16 @@ +Class { + #name : #GtFilterModelLabel, + #superclass : #Object, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelLabel >> label [ + + ^ self subclassResponsibility +] + +{ #category : #accessing } +GtFilterModelLabel >> labelElementClass [ + ^ GtFilterPrefixLabelElement +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelLabelChanged.class.st b/src/GToolkit-Coder-UI/GtFilterModelLabelChanged.class.st new file mode 100644 index 000000000..bc7a87d59 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelLabelChanged.class.st @@ -0,0 +1,28 @@ +Class { + #name : #GtFilterModelLabelChanged, + #superclass : #GtFilterModelAnnouncement, + #instVars : [ + 'label' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterModelLabelChanged >> label [ + ^ label +] + +{ #category : #accessing } +GtFilterModelLabelChanged >> label: anObject [ + label := anObject +] + +{ #category : #accessing } +GtFilterModelLabelChanged >> name [ + ^ label +] + +{ #category : #accessing } +GtFilterModelLabelChanged >> name: aString [ + label := aString +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelMethodDefinitionItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelMethodDefinitionItem.class.st new file mode 100644 index 000000000..63f13f114 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelMethodDefinitionItem.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtFilterModelMethodDefinitionItem, + #superclass : #GtFilterModelItem, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterModelMethodDefinitionItem >> isSubclassResponsibilityMethod [ + ^ false +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelMethodOriginItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelMethodOriginItem.class.st new file mode 100644 index 000000000..d6a0b90c8 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelMethodOriginItem.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtFilterModelMethodOriginItem, + #superclass : #GtFilterModelItem, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterModelMethodOriginItem >> isTraitOrigin [ + ^ false +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelNoItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelNoItem.class.st new file mode 100644 index 000000000..0d3f3e613 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelNoItem.class.st @@ -0,0 +1,17 @@ +Class { + #name : #GtFilterModelNoItem, + #superclass : #GtFilterModelItem, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelNoItem >> ifSome: aBlock ifNone: aNoneBlock [ + ^ aNoneBlock value +] + +{ #category : #accessing } +GtFilterModelNoItem >> label [ + ^ 'none' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelObjectItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelObjectItem.class.st new file mode 100644 index 000000000..742367016 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelObjectItem.class.st @@ -0,0 +1,48 @@ +Class { + #name : #GtFilterModelObjectItem, + #superclass : #GtFilterModelItem, + #instVars : [ + 'object' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelObjectItem >> icon [ + | anIconName | + anIconName := self object gtIconName. + (anIconName isNil or: [ anIconName = Object gtIconName ]) ifTrue: [ ^ nil ]. + + ^ self object gtSafeIconNamed: anIconName +] + +{ #category : #accessing } +GtFilterModelObjectItem >> itemValue [ + ^ self object +] + +{ #category : #accessing } +GtFilterModelObjectItem >> label [ + + ^ self object gtDisplayString +] + +{ #category : #accessing } +GtFilterModelObjectItem >> object [ + ^ object +] + +{ #category : #accessing } +GtFilterModelObjectItem >> object: anObject [ + object := anObject +] + +{ #category : #printing } +GtFilterModelObjectItem >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + print: self object; + nextPut: $) +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelParameter.class.st b/src/GToolkit-Coder-UI/GtFilterModelParameter.class.st new file mode 100644 index 000000000..4310a6729 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelParameter.class.st @@ -0,0 +1,80 @@ +Class { + #name : #GtFilterModelParameter, + #superclass : #Object, + #traits : 'TGtAnnouncer', + #classTraits : 'TGtAnnouncer classTrait', + #instVars : [ + 'updater', + 'announcer' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #announcer } +GtFilterModelParameter >> announcer [ + + ^ announcer ifNil: [ announcer := Announcer new ] +] + +{ #category : #converting } +GtFilterModelParameter >> asFilterViewModel [ + + ^ self filterViewModelClass new filterModel: self +] + +{ #category : #converting } +GtFilterModelParameter >> asUserParametersElement [ + + ^ self asFilterViewModel asUserParametersElement +] + +{ #category : #testing } +GtFilterModelParameter >> changesFilteredResult [ + "Indicates whether it changes a result of filtered items, e.g., list of method coders." + + + ^ false +] + +{ #category : #accessing } +GtFilterModelParameter >> filterViewModelClass [ + ^ self subclassResponsibility +] + +{ #category : #'gt - extensions' } +GtFilterModelParameter >> gtLiveFor: aView [ + + ^ aView explicit + title: 'Live'; + stencil: [ self asUserParametersElement margin: (BlInsets all: 10) ] +] + +{ #category : #converting } +GtFilterModelParameter >> onCreateFilter: aFilter [ + updater ifNil: [ ^ self ]. + updater cull: aFilter cull: self selectedValue cull: self. +] + +{ #category : #printing } +GtFilterModelParameter >> printDetailsOn: aStream [ + aStream + print: (self selectedValue ifNotNil: [ :aValue | aValue asFilterModelItem itemValue ]) +] + +{ #category : #accessing } +GtFilterModelParameter >> selectedValue [ + "Return a filter value, e.g., selected item, input text." + + ^ nil +] + +{ #category : #accessing } +GtFilterModelParameter >> updater [ + ^ updater +] + +{ #category : #accessing } +GtFilterModelParameter >> updater: aBlock [ + + updater := aBlock +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelParameterUpdated.class.st b/src/GToolkit-Coder-UI/GtFilterModelParameterUpdated.class.st new file mode 100644 index 000000000..da6086503 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelParameterUpdated.class.st @@ -0,0 +1,31 @@ +" +I am announced when a parameter is is changed, implying new {{gtClass:GtSearchFilter}} results. +" +Class { + #name : #GtFilterModelParameterUpdated, + #superclass : #GtFilterModelAnnouncement, + #instVars : [ + 'originalAnnouncement' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #testing } +GtFilterModelParameterUpdated >> changesFilteredResult [ + ^ self originalAnnouncement changesFilteredResult +] + +{ #category : #accessing } +GtFilterModelParameterUpdated >> originalAnnouncement [ + ^ originalAnnouncement +] + +{ #category : #accessing } +GtFilterModelParameterUpdated >> originalAnnouncement: anAnnouncement [ + originalAnnouncement := anAnnouncement +] + +{ #category : #accessing } +GtFilterModelParameterUpdated >> parameterModel [ + self originalAnnouncement ifNotNil: #model +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelPluggableItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelPluggableItem.class.st new file mode 100644 index 000000000..3b35db2c0 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelPluggableItem.class.st @@ -0,0 +1,141 @@ +Class { + #name : #GtFilterModelPluggableItem, + #superclass : #GtFilterModelItem, + #instVars : [ + 'object', + 'iconStencilBuilder', + 'labelStencilBuilder', + 'filterUpdater' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'instance creation' } +GtFilterModelPluggableItem class >> forCollection: aCollection icon: anIconStencilBuilder [ + ^ aCollection + collect: [ :eachObject | + self new + object: eachObject; + icon: anIconStencilBuilder ] +] + +{ #category : #'instance creation' } +GtFilterModelPluggableItem class >> forCollection: aCollection icon: anIconStencilBuilder label: aLabelStencilBuilder [ + ^ aCollection + collect: [ :eachObject | + self new + object: eachObject; + icon: anIconStencilBuilder; + label: aLabelStencilBuilder ] +] + +{ #category : #'instance creation' } +GtFilterModelPluggableItem class >> forCollection: aCollection label: aLabelStencilBuilder [ + ^ aCollection + collect: [ :eachObject | + self new + object: eachObject; + label: aLabelStencilBuilder ] +] + +{ #category : #'instance creation' } +GtFilterModelPluggableItem class >> forObject: anObject icon: anIconStencilBuilder [ + ^ self new + object: anObject; + icon: anIconStencilBuilder +] + +{ #category : #'instance creation' } +GtFilterModelPluggableItem class >> forObject: anObject icon: anIconStencilBuilder label: aLabelStencilBuilder [ + ^ self new + object: anObject; + icon: anIconStencilBuilder; + label: aLabelStencilBuilder +] + +{ #category : #'instance creation' } +GtFilterModelPluggableItem class >> forObject: anObject label: aLabelStencilBuilder [ + ^ self new + object: anObject; + label: aLabelStencilBuilder +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> filterUpdater [ + ^ filterUpdater +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> filterUpdater: aBlock [ + "Set a filter block that is called when a filter model is updated and new filter created. + It receives two arguments: [ :aFilter :anItem | ]." + + filterUpdater := aBlock +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> icon [ + + ^ iconStencilBuilder + object: self object; + build +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> icon: aStencilBuilder [ + iconStencilBuilder := aStencilBuilder + asStencilBuilder: GtFilterModelItemIconStencilBuilder +] + +{ #category : #initialization } +GtFilterModelPluggableItem >> initialize [ + super initialize. + iconStencilBuilder := GtFilterModelItemIconStencilBuilder new. + labelStencilBuilder := GtFilterModelItemLabelStencilBuilder new. + filterUpdater := [ :aFilter :anItem | "do nothing" ] +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> itemValue [ + ^ self object +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> label [ + + ^ labelStencilBuilder + object: self object; + build +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> label: aStencilBuilder [ + labelStencilBuilder := aStencilBuilder + asStencilBuilder: GtFilterModelItemLabelStencilBuilder +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> object [ + ^ object +] + +{ #category : #accessing } +GtFilterModelPluggableItem >> object: anObject [ + object := anObject +] + +{ #category : #converting } +GtFilterModelPluggableItem >> onCreateFilter: aFilter [ + filterUpdater ifNil: [ ^ self ]. + filterUpdater cull: aFilter cull: self +] + +{ #category : #printing } +GtFilterModelPluggableItem >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + print: self object; + nextPut: $) +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelPluggableItemsBuilder.class.st b/src/GToolkit-Coder-UI/GtFilterModelPluggableItemsBuilder.class.st new file mode 100644 index 000000000..613771f73 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelPluggableItemsBuilder.class.st @@ -0,0 +1,72 @@ +Class { + #name : #GtFilterModelPluggableItemsBuilder, + #superclass : #GtFilterModelItemsBuilder, + #instVars : [ + 'iconStencilBuilder', + 'labelStencilBuilder', + 'itemsBuilder' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'api - instantiation' } +GtFilterModelPluggableItemsBuilder >> create [ + + ^ self itemsBuilder create + collect: [ :eachItem | + GtFilterModelPluggableItem new + object: eachItem itemValue; + icon: self iconStencilBuilder; + label: self labelStencilBuilder ] +] + +{ #category : #'api - instantiation' } +GtFilterModelPluggableItemsBuilder >> createFuture [ + ^ self itemsBuilder createFuture flatten + map: [ :anArray | + anArray collect: [ :eachItem | + GtFilterModelPluggableItem new + object: eachItem itemValue; + icon: self iconStencilBuilder; + label: self labelStencilBuilder ] ] +] + +{ #category : #accessing } +GtFilterModelPluggableItemsBuilder >> iconStencilBuilder [ + ^ iconStencilBuilder +] + +{ #category : #accessing } +GtFilterModelPluggableItemsBuilder >> iconStencilBuilder: aStencilBuilder [ + iconStencilBuilder := aStencilBuilder + asStencilBuilder: GtFilterModelItemIconStencilBuilder +] + +{ #category : #initialization } +GtFilterModelPluggableItemsBuilder >> initialize [ + super initialize. + iconStencilBuilder := GtFilterModelItemIconStencilBuilder new. + labelStencilBuilder := GtFilterModelItemLabelStencilBuilder new +] + +{ #category : #accessing } +GtFilterModelPluggableItemsBuilder >> itemsBuilder [ + + ^ itemsBuilder +] + +{ #category : #accessing } +GtFilterModelPluggableItemsBuilder >> itemsBuilder: anObject [ + itemsBuilder := anObject +] + +{ #category : #accessing } +GtFilterModelPluggableItemsBuilder >> labelStencilBuilder [ + ^ labelStencilBuilder +] + +{ #category : #accessing } +GtFilterModelPluggableItemsBuilder >> labelStencilBuilder: aStencilBuilder [ + labelStencilBuilder := aStencilBuilder + asStencilBuilder: GtFilterModelItemLabelStencilBuilder +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelSelectedItemChanged.class.st b/src/GToolkit-Coder-UI/GtFilterModelSelectedItemChanged.class.st new file mode 100644 index 000000000..8b7073c12 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelSelectedItemChanged.class.st @@ -0,0 +1,25 @@ +Class { + #name : #GtFilterModelSelectedItemChanged, + #superclass : #GtFilterModelAnnouncement, + #instVars : [ + 'selectedItem' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #testing } +GtFilterModelSelectedItemChanged >> changesFilteredResult [ + "Indicates whether it changes a result of filtered items, e.g., list of method coders." + + ^ true +] + +{ #category : #accessing } +GtFilterModelSelectedItemChanged >> selectedItem [ + ^ selectedItem +] + +{ #category : #accessing } +GtFilterModelSelectedItemChanged >> selectedItem: anObject [ + selectedItem := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelSideItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelSideItem.class.st new file mode 100644 index 000000000..8d1f78f11 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelSideItem.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtFilterModelSideItem, + #superclass : #GtFilterModelItem, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterModelSideItem >> isInstanceSideMethod [ + ^ false +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelStringItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelStringItem.class.st new file mode 100644 index 000000000..7b569a52d --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelStringItem.class.st @@ -0,0 +1,65 @@ +Class { + #name : #GtFilterModelStringItem, + #superclass : #GtFilterModelItem, + #instVars : [ + 'string', + 'iconStencil' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #comparing } +GtFilterModelStringItem >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + self class = anObject class ifFalse: [ ^ false ]. + + ^ self string sameContentAs: anObject string +] + +{ #category : #comparing } +GtFilterModelStringItem >> hash [ + ^ self class hash bitXor: self string hash +] + +{ #category : #accessing } +GtFilterModelStringItem >> icon [ + + ^ iconStencil ifNotNil: #asElement +] + +{ #category : #accessing } +GtFilterModelStringItem >> icon: anElementStencil [ + iconStencil := anElementStencil +] + +{ #category : #accessing } +GtFilterModelStringItem >> itemValue [ + ^ self string +] + +{ #category : #accessing } +GtFilterModelStringItem >> label [ + + ^ self string +] + +{ #category : #printing } +GtFilterModelStringItem >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + nextPutAll: self string asString; + nextPut: $) +] + +{ #category : #accessing } +GtFilterModelStringItem >> string [ + ^ string +] + +{ #category : #accessing } +GtFilterModelStringItem >> string: aString [ + string := aString +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelTestCaseResultStateItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelTestCaseResultStateItem.class.st new file mode 100644 index 000000000..afaa46b82 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelTestCaseResultStateItem.class.st @@ -0,0 +1,46 @@ +Class { + #name : #GtFilterModelTestCaseResultStateItem, + #superclass : #GtFilterModelItem, + #instVars : [ + 'resultState' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelTestCaseResultStateItem >> icon [ + ^ BlElement new + background: self resultState color; + border: self resultState border; + size: 8 @ 8 +] + +{ #category : #accessing } +GtFilterModelTestCaseResultStateItem >> itemValue [ + ^ self resultState +] + +{ #category : #accessing } +GtFilterModelTestCaseResultStateItem >> label [ + ^ self resultState label +] + +{ #category : #printing } +GtFilterModelTestCaseResultStateItem >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + print: (self resultState ifNotNil: #label); + nextPut: $) +] + +{ #category : #accessing } +GtFilterModelTestCaseResultStateItem >> resultState [ + ^ resultState +] + +{ #category : #accessing } +GtFilterModelTestCaseResultStateItem >> resultState: anObject [ + resultState := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelTraitOriginItem.class.st b/src/GToolkit-Coder-UI/GtFilterModelTraitOriginItem.class.st new file mode 100644 index 000000000..2f3228021 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelTraitOriginItem.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtFilterModelTraitOriginItem, + #superclass : #GtFilterModelMethodOriginItem, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterModelTraitOriginItem >> icon [ + ^ self iconNamed: #trait +] + +{ #category : #testing } +GtFilterModelTraitOriginItem >> isTraitOrigin [ + ^ true +] + +{ #category : #accessing } +GtFilterModelTraitOriginItem >> label [ + ^ 'trait' +] diff --git a/src/GToolkit-Coder-UI/GtFilterModelValuableItemsBuilder.class.st b/src/GToolkit-Coder-UI/GtFilterModelValuableItemsBuilder.class.st new file mode 100644 index 000000000..04340a8cc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterModelValuableItemsBuilder.class.st @@ -0,0 +1,41 @@ +Class { + #name : #GtFilterModelValuableItemsBuilder, + #superclass : #GtFilterModelItemsBuilder, + #instVars : [ + 'valuable' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #comparing } +GtFilterModelValuableItemsBuilder >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + ^ self class = anObject class and: [ self valuable = anObject valuable ] +] + +{ #category : #'api - instantiation' } +GtFilterModelValuableItemsBuilder >> create [ + ^ self valuable value collect: #asFilterModelItem +] + +{ #category : #'api - instantiation' } +GtFilterModelValuableItemsBuilder >> createFuture [ + ^ self valuable asAsyncFuture flatten + map: [ :anArray | anArray collect: #asFilterModelItem ] +] + +{ #category : #comparing } +GtFilterModelValuableItemsBuilder >> hash [ + ^ self class hash bitXor: self valuable hash +] + +{ #category : #accessing } +GtFilterModelValuableItemsBuilder >> valuable [ + ^ valuable +] + +{ #category : #accessing } +GtFilterModelValuableItemsBuilder >> valuable: aValuable [ + valuable := aValuable +] diff --git a/src/GToolkit-Coder-UI/GtFilterNameId.class.st b/src/GToolkit-Coder-UI/GtFilterNameId.class.st new file mode 100644 index 000000000..489d206ef --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterNameId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterNameId, + #superclass : #GtFilterElementId, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #converting } +GtFilterNameId >> asSymbol [ + ^ #'filter--name' +] diff --git a/src/GToolkit-Coder-UI/GtFilterParametersElement.class.st b/src/GToolkit-Coder-UI/GtFilterParametersElement.class.st new file mode 100644 index 000000000..dcb9a1604 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterParametersElement.class.st @@ -0,0 +1,31 @@ +Class { + #name : #GtFilterParametersElement, + #superclass : #GtFilterSettingsElement, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #'api - filter view model' } +GtFilterParametersElement >> onFilterViewModelChanged [ + super onFilterViewModelChanged. + self updateElement +] + +{ #category : #'private - updating' } +GtFilterParametersElement >> updateElement [ + | someParameters someElements | + someParameters := self filterViewModel parameters. + + self removeChildren. + + someParameters + ifEmpty: [ + self visibility: BlVisibility gone. + ^ self ] + ifNotEmpty: [ + self visibility: BlVisibility visible ]. + + true ifTrue: [ ^ self ]. + + someElements := someParameters collect: #asUserParametersElement. + self addChildren: someElements +] diff --git a/src/GToolkit-Coder-UI/GtFilterPrefixLabelElement.class.st b/src/GToolkit-Coder-UI/GtFilterPrefixLabelElement.class.st new file mode 100644 index 000000000..5d0c0c0cc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterPrefixLabelElement.class.st @@ -0,0 +1,32 @@ +Class { + #name : #GtFilterPrefixLabelElement, + #superclass : #GtFilterLabelElement, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #initialization } +GtFilterPrefixLabelElement >> initialize [ + super initialize. + self padding: (BlInsets top: 0 right: 0 bottom: 0 left: 2). + + self addChild: labelElement. + self addChild: settingsContainer as: #container +] + +{ #category : #'private - updating' } +GtFilterPrefixLabelElement >> updateSettingsContainer [ + | someParameters someElements | + super updateSettingsContainer. + + someParameters := self filterViewModel parameters. + + someParameters ifEmpty: [ ^ self ]. + + someElements := someParameters collect: #asUserParametersElement. + someElements do: [ :eachElement | + eachElement constraintsDo: [ :c | + c linear vertical alignCenter. + c frame vertical alignCenter ] ]. + + settingsContainer addChildren: someElements. +] diff --git a/src/GToolkit-Coder-UI/GtFilterRunExampleButtonAptitude.class.st b/src/GToolkit-Coder-UI/GtFilterRunExampleButtonAptitude.class.st deleted file mode 100644 index 1a13eeb06..000000000 --- a/src/GToolkit-Coder-UI/GtFilterRunExampleButtonAptitude.class.st +++ /dev/null @@ -1,182 +0,0 @@ -" -I am an abstract class. -I am a {{gtClass:BrLook}}. -I provide common behavior to update a {{gtClass:BrButton}} visual apperance based on {{gtClass:GtMethodsCoder}} and {{gtClass:GtExampleWithResult}} changes. See my {{gtMethod:GtFilterRunExampleButtonLook>>#subscribeToCoder|label=subscriptions}} for more details. -Look at my {{gtClass:GtFilterRunExampleButtonLook|show=#gtSubclassesFor:|expanded=true|height=150}} for more details and examples: - - -" -Class { - #name : #GtFilterRunExampleButtonAptitude, - #superclass : #BrAptitude, - #instVars : [ - 'coder', - 'mutex', - 'isUpdateRequested' - ], - #category : #'GToolkit-Coder-UI-Filters' -} - -{ #category : #'private - updating' } -GtFilterRunExampleButtonAptitude >> beUpdateRequestedIfDoneEarlierDo: aBlock [ - mutex critical: [ - self isUpdateRequested ifTrue: aBlock. - isUpdateRequested := true. ] -] - -{ #category : #'api - accessing' } -GtFilterRunExampleButtonAptitude >> coder [ - - ^ coder -] - -{ #category : #'api - accessing' } -GtFilterRunExampleButtonAptitude >> coder: aGtFilteredMethodsCoder [ - coder = aGtFilteredMethodsCoder ifTrue: [ ^ self ]. - self unsubscribeFromCoder. - coder := aGtFilteredMethodsCoder. - self subscribeToCoder. - self onCoderChanged. -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonAptitude >> coderDo: aBlock [ - self coder ifNotNil: aBlock -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonAptitude >> enqueueUpdate [ - self widgetAndCoderDo: [ :aWidget :aCoder | - aWidget enqueueTask: (BlTaskAction new action: [ - self updateNow ]) ]. -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonAptitude >> exampler [ - - ^ self coder exampler -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonAptitude >> examplerDo: aBlock [ - self coderDo: [ :aCoder | - aBlock cull: aCoder exampler ] -] - -{ #category : #initialization } -GtFilterRunExampleButtonAptitude >> initialize [ - super initialize. - isUpdateRequested := false. - mutex := Mutex new. -] - -{ #category : #initialization } -GtFilterRunExampleButtonAptitude >> initializeIconElement: anElement [ - anElement - geometry: BlCircle new; - margin: (BlInsets all: 1); - size: 8 @ 8. -] - -{ #category : #'private - testing' } -GtFilterRunExampleButtonAptitude >> isUpdateRequested [ - - ^ isUpdateRequested -] - -{ #category : #'api - hooks' } -GtFilterRunExampleButtonAptitude >> onAttachedTo: anElement [ - super onAttachedTo: anElement. - - self postponeUpdate. -] - -{ #category : #'private - hooks' } -GtFilterRunExampleButtonAptitude >> onCoderChanged [ - "We expect that the Coder is changed just once before the look is attached." - self beUpdateRequestedIfDoneEarlierDo: [ ^ self ]. - self updateNow. -] - -{ #category : #'private - announcement handling' } -GtFilterRunExampleButtonAptitude >> onCoderChanged: aGtCodersCodersChanged [ - self postponeUpdate. -] - -{ #category : #'private - announcement handling' } -GtFilterRunExampleButtonAptitude >> onExampleExecuted: aGtCoderExampleExecuted [ - self postponeUpdate. -] - -{ #category : #'api - hooks' } -GtFilterRunExampleButtonAptitude >> onUninstalledIn: anElement [ - super onUninstalledIn: anElement. - self resetUpdateRequested. -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonAptitude >> postponeUpdate [ - self widgetAndCoderDo: [ :aWidget :aCoder | - self beUpdateRequestedIfDoneEarlierDo: [ ^ self ]. - BlUseAsyncFeatures - ifEnabledDo: [ - BlTaskAtQueue default - schedule: [ self enqueueUpdate ] - at: self postponedTime ] - otherwise: [ - self updateNow ] ] -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonAptitude >> postponedTime [ - | aNow | - self widgetDo: [ :aWidget | - aNow := aWidget space - ifNotNil: [ :aSpace | aSpace time now ] - ifNil: [ BlTime real now ] ]. - aNow ifNil: [ aNow := BlTime real now ]. - ^ aNow + 300 milliSeconds. -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonAptitude >> resetUpdateRequested [ - mutex critical: [ - isUpdateRequested := false ]. -] - -{ #category : #'private - subscriptions' } -GtFilterRunExampleButtonAptitude >> subscribeToCoder [ - self coder weak - when: GtCoderExampleExecuted - send: #onExampleExecuted: - to: self. - self coder weak - when: GtCodersCoderAdded , GtCodersCoderRemoved , GtCodersCodersChanged, GtCoderSourceCodeChanged - send: #onCoderChanged: - to: self -] - -{ #category : #'private - subscriptions' } -GtFilterRunExampleButtonAptitude >> unsubscribeFromCoder [ - self coderDo: [ :aCoder | - aCoder unsubscribe: self ]. -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonAptitude >> updateNow [ - self resetUpdateRequested. - self updateWidget. -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonAptitude >> updateWidget [ - "Subclasses may perform update actions. - I should be called from a UI process." -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonAptitude >> widgetAndCoderDo: aTwoArgBlock [ - self widgetDo: [ :aWidget | - self coderDo: [ :aCoder | - aTwoArgBlock cull: aWidget cull: aCoder ] ] -] diff --git a/src/GToolkit-Coder-UI/GtFilterRunExampleButtonFourStateIconAptitude.class.st b/src/GToolkit-Coder-UI/GtFilterRunExampleButtonFourStateIconAptitude.class.st deleted file mode 100644 index 4ac76c7ad..000000000 --- a/src/GToolkit-Coder-UI/GtFilterRunExampleButtonFourStateIconAptitude.class.st +++ /dev/null @@ -1,152 +0,0 @@ -" -I am a {{gtClass:GtFilterRunExampleButtonIconLook}}. -I display a colored icon that represents an {{gtClass:GtExampleWithResult}} execution and result state. -If there are examples with different execution states, I display several colors. -1. # Examples - -The following examples show example status icons. -1. ## All States - -I display all states at once as follows: -{{gtExample:GtFilterRunExampleButtonLookExamples>>#fourIconLookButton_WithoutCoder|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=65}} -1. ## Not Executed Examples - -If all examples have same state, only one icon (color) is displayed: {{gtExample:GtFilterRunExampleButtonLookExamples>>#fourIconLookButton_NotExecuted|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=65}} -1. ## Examples With Two Execution States - - -{{gtExample:GtFilterRunExampleButtonLookExamples>>#fourIconLookButton_SuccessAndNotExecutedStates|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=65}} - - -" -Class { - #name : #GtFilterRunExampleButtonFourStateIconAptitude, - #superclass : #GtFilterRunExampleButtonIconAptitude, - #instVars : [ - 'fourIconElement', - 'failureIconElement', - 'errorIconElement', - 'successIconElement', - 'neutralIconElement' - ], - #category : #'GToolkit-Coder-UI-Filters' -} - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonFourStateIconAptitude >> errorIconElement [ - ^ errorIconElement -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonFourStateIconAptitude >> failureIconElement [ - ^ failureIconElement -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonFourStateIconAptitude >> fourIconElement [ - ^ fourIconElement -] - -{ #category : #initialization } -GtFilterRunExampleButtonFourStateIconAptitude >> initialize [ - super initialize. - self initializeFourStateIconElement. - self initializeNeutralIconElement. - self initializeErrorIconElement. - self initializeFailureIconElement. - self initializeSuccessIconElement. - - self fourIconElement addChild: self successIconElement as: #success. - self fourIconElement addChild: self failureIconElement as: #failure. - self fourIconElement addChild: self errorIconElement as: #error. - self fourIconElement addChild: self neutralIconElement as: #neutral. - - self addChangeAddChildAs: #(content fourStateExample) with: [ self fourIconElement ]. -] - -{ #category : #initialization } -GtFilterRunExampleButtonFourStateIconAptitude >> initializeErrorIconElement [ - errorIconElement := BlElement new - background: self newErrorBackground. - self initializeIconElement: errorIconElement. -] - -{ #category : #initialization } -GtFilterRunExampleButtonFourStateIconAptitude >> initializeExampleElement [ - super initializeExampleElement. - self exampleElement visibility: BlVisibility gone. -] - -{ #category : #initialization } -GtFilterRunExampleButtonFourStateIconAptitude >> initializeFailureIconElement [ - failureIconElement := BlElement new - background: self newFailureBackground. - self initializeIconElement: failureIconElement -] - -{ #category : #initialization } -GtFilterRunExampleButtonFourStateIconAptitude >> initializeFourStateIconElement [ - fourIconElement := BlElement new - layout: (BlGridLayout new - columnCount: 2; - cellSpacing: 0); - background: BlBackground transparent; - constraintsDo: [ :c | - c horizontal fitContent. - c vertical fitContent ]. -] - -{ #category : #initialization } -GtFilterRunExampleButtonFourStateIconAptitude >> initializeNeutralIconElement [ - neutralIconElement := BlElement new - background: self newNotExecutedBackground. - self initializeIconElement: neutralIconElement. -] - -{ #category : #initialization } -GtFilterRunExampleButtonFourStateIconAptitude >> initializeSuccessIconElement [ - successIconElement := BlElement new - background: self newSuccessBackground. - self initializeIconElement: successIconElement -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonFourStateIconAptitude >> neutralIconElement [ - ^ neutralIconElement -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonFourStateIconAptitude >> successIconElement [ - ^ successIconElement -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonFourStateIconAptitude >> updateFourIconElement [ - | isNeutralVisible isErrorVisible isFailureVisible isSuccessVisible | - self updateIconElement: self neutralIconElement status: (isNeutralVisible := self exampler hasNotExecutedExamples). - self updateIconElement: self errorIconElement status: (isErrorVisible := self exampler hasErrorExamples). - self updateIconElement: self failureIconElement status: (isFailureVisible := self exampler hasFailureExamples). - self updateIconElement: self successIconElement status: (isSuccessVisible := self exampler hasSuccessExamples). - isNeutralVisible asBit + isErrorVisible asBit + isFailureVisible asBit + isSuccessVisible asBit = 1 - ifTrue: [ - self fourIconElement visibility: BlVisibility gone. - self exampleElement visibility: BlVisibility visible ] - ifFalse: [ - self fourIconElement visibility: BlVisibility visible. - self exampleElement visibility: BlVisibility gone ] -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonFourStateIconAptitude >> updateIconElement: anElement status: aBoolean [ - aBoolean - ifTrue: [ - anElement visibility: BlVisibility visible ] - ifFalse: [ - anElement visibility: BlVisibility hidden ]. -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonFourStateIconAptitude >> updateWidget [ - super updateWidget. - self updateFourIconElement. -] diff --git a/src/GToolkit-Coder-UI/GtFilterRunExampleButtonIconAptitude.class.st b/src/GToolkit-Coder-UI/GtFilterRunExampleButtonIconAptitude.class.st deleted file mode 100644 index 31db54c42..000000000 --- a/src/GToolkit-Coder-UI/GtFilterRunExampleButtonIconAptitude.class.st +++ /dev/null @@ -1,110 +0,0 @@ -" -I am a {{gtClass:GtFilterRunExampleButtonLook}}. -I display a colored icon that represent an {{gtClass:GtExampleWithResult}} execution and result state. -1. # Examples - -The following examples shows a colored icon as a part of a button in four possible states. -1. ## Not Executed Examples - -{{gtExample:GtFilterRunExampleButtonLookExamples>>#oneIconLookButton_SkipNoTest|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=65}} -1. ## Examples With Successful Execution - -{{gtExample:GtFilterRunExampleButtonLookExamples>>#oneIconLookButton_Success|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=65}} -1. ## Examples With Not Satisfying Assertions - -{{gtExample:GtFilterRunExampleButtonLookExamples>>#oneIconLookButton_Failure|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=65}} -1. ## Examples With Execution Errors - -{{gtExample:GtFilterRunExampleButtonLookExamples>>#oneIconLookButton_Error|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=65}} - - -" -Class { - #name : #GtFilterRunExampleButtonIconAptitude, - #superclass : #GtFilterRunExampleButtonAptitude, - #instVars : [ - 'exampleElement' - ], - #category : #'GToolkit-Coder-UI-Filters' -} - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonIconAptitude >> exampleElement [ - - ^ exampleElement -] - -{ #category : #initialization } -GtFilterRunExampleButtonIconAptitude >> initialize [ - super initialize. - self initializeExampleElement. - - self addChangeAddChildAs: #(content example) with: [ self exampleElement ]. -] - -{ #category : #initialization } -GtFilterRunExampleButtonIconAptitude >> initializeExampleElement [ - exampleElement := BlElement new - background: self newNotExecutedBackground. - self initializeIconElement: exampleElement. -] - -{ #category : #'private - instance creation' } -GtFilterRunExampleButtonIconAptitude >> newBackground [ - self coder exampler hasNotExecutedExamples ifTrue: [ - ^ self newNotExecutedBackground ]. - self coder exampler hasErrorExamples ifTrue: [ - ^ self newErrorBackground ]. - self coder exampler hasFailureExamples ifTrue: [ - ^ self newFailureBackground ]. - self coder exampler hasSuccessExamples ifTrue: [ - ^ self newSuccessBackground ]. - ^ self newNotExecutedBackground. -] - -{ #category : #'private - instance creation' } -GtFilterRunExampleButtonIconAptitude >> newErrorBackground [ - ^ GtFilterExampleErrorState default color -] - -{ #category : #'private - instance creation' } -GtFilterRunExampleButtonIconAptitude >> newFailureBackground [ - ^ GtFilterExampleFailureState default color -] - -{ #category : #'private - instance creation' } -GtFilterRunExampleButtonIconAptitude >> newNotExecutedBackground [ - ^ GtFilterExampleNotExecutedState default color -] - -{ #category : #'private - instance creation' } -GtFilterRunExampleButtonIconAptitude >> newSuccessBackground [ - ^ GtFilterExampleSuccessState default color -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonIconAptitude >> updateButtonStatus [ - "I must be called in a UI process". - self widgetDo: [ :aWidget | - self examplerDo: [ :anExampler | - | aStatus | - aStatus := anExampler hasSelectedExecutableExamples. - aWidget dispatchEvent: (BrEnablementWish enabled: aStatus). - anExampler hasExamples - ifFalse: [ aWidget visibility: BlVisibility gone ] - ifTrue: [ aWidget visibility: BlVisibility visible] ] ] -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonIconAptitude >> updateExampleStatus [ - "I must be called in a UI process". - self exampleElement - background: self newBackground -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonIconAptitude >> updateWidget [ - super updateWidget. - self updateExampleStatus. - self updateButtonStatus. -] diff --git a/src/GToolkit-Coder-UI/GtFilterRunExampleButtonTooltipAptitude.class.st b/src/GToolkit-Coder-UI/GtFilterRunExampleButtonTooltipAptitude.class.st deleted file mode 100644 index aa2d4c06c..000000000 --- a/src/GToolkit-Coder-UI/GtFilterRunExampleButtonTooltipAptitude.class.st +++ /dev/null @@ -1,108 +0,0 @@ -Class { - #name : #GtFilterRunExampleButtonTooltipAptitude, - #superclass : #GtFilterRunExampleButtonAptitude, - #instVars : [ - 'tooltipContent' - ], - #category : #'GToolkit-Coder-UI-Filters' -} - -{ #category : #initialization } -GtFilterRunExampleButtonTooltipAptitude >> initialize [ - super initialize. - self initializeTooltipContent. - - self add: (BrGlamorousWithTooltipAptitude content: [self initializeTooltipContent]). -] - -{ #category : #initialization } -GtFilterRunExampleButtonTooltipAptitude >> initializeTooltipContent [ - tooltipContent := BrColumnedList new. - tooltipContent - items: GtFilterExampleState default allStates; - padding: (BlInsets all: 10); - fitContent. - - tooltipContent - addEventHandlerOn: BrSelectionChanged do: [ :anEvent | - anEvent selectedDo: [ :theIndices | - | aWidgetElement aSelectedObject | - - aWidgetElement := anEvent currentTarget. - aSelectedObject := aWidgetElement viewModel itemAt: theIndices first. - - self coder - setFilters: { - ((GtSearchExamplesFilter new - resultState: aSelectedObject) - coder: self coder)} - source: self - ] ]. - - tooltipContent column - title: 'Icon'; - cellStencil: [ - | anElement | - anElement := BlElement new - background: BlBackground transparent. - self initializeIconElement: anElement. - anElement ]; - dataBinder: [ :anElement :anItem | - anElement background: anItem color ]; - width: 10. - - tooltipContent column - title: 'Name'; - cellStencil: [ - BrLabel new - aptitude: BrGlamorousLabelAptitude ]; - dataBinder: [ :anElement :anItem | - anElement text: anItem label ]; - width: 100. - - tooltipContent column - title: 'Amount'; - cellStencil: [ - BrLabel new - aptitude: BrGlamorousLabelAptitude; - fitContent ]; - dataBinder: [ :anElement :anItem | - | aTotal aTotalString aTotalText | - self examplerDo: [ :anExampler | - aTotal := anItem numberOfExamplesFor: anExampler. - aTotalString := aTotal asString, (' example' asPluralBasedOn: aTotal) ]. - aTotalString ifNil: [ aTotalString := '? examples' ]. - aTotalText := aTotalString asRopedText foreground: Color gray. - anElement text: aTotalText ]. - ^ tooltipContent -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonTooltipAptitude >> listItems [ - | states | - states := GtFilterExampleState default allStates. - ^ states collect: [ :eachState | - eachState labelWithAmountFor: self coder exampler ]. -] - -{ #category : #'private - accessing' } -GtFilterRunExampleButtonTooltipAptitude >> tooltipContent [ - - ^ tooltipContent -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonTooltipAptitude >> updateList [ - | statusesToDisplay | - statusesToDisplay := GtFilterExampleState default allStates reject: [ :eachStatus | - (eachStatus numberOfExamplesFor: self exampler) isZero ]. - - self tooltipContent - items: statusesToDisplay -] - -{ #category : #'private - updating' } -GtFilterRunExampleButtonTooltipAptitude >> updateWidget [ - super updateWidget. - self updateList. -] diff --git a/src/GToolkit-Coder-UI/GtFilterSelectableItemModel.class.st b/src/GToolkit-Coder-UI/GtFilterSelectableItemModel.class.st new file mode 100644 index 000000000..c146d2101 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterSelectableItemModel.class.st @@ -0,0 +1,38 @@ +Class { + #name : #GtFilterSelectableItemModel, + #superclass : #GtFilterModel, + #traits : 'TGtWithFilterModelParameters + TGtWithSelectableItem', + #classTraits : 'TGtWithFilterModelParameters classTrait + TGtWithSelectableItem classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterSelectableItemModel class >> isAbstract [ + ^ self name = #GtFilterSelectableItemModel +] + +{ #category : #testing } +GtFilterSelectableItemModel >> changesFilteredResult [ + "Indicates whether it changes a result of filtered items, e.g., list of method coders." + + + ^ self selectedItem + ifSome: [ true ] + ifNone: [ self parameters anySatisfy: #changesFilteredResult ] +] + +{ #category : #'private - notifying' } +GtFilterSelectableItemModel >> notifyItemsChanged [ + self + announce: (GtFilterModelItemsChanged new + model: self; + itemsBuilder: self itemsBuilder) +] + +{ #category : #'private - notifying' } +GtFilterSelectableItemModel >> notifySelectedItemChanged [ + self + announce: (GtFilterModelSelectedItemChanged new + model: self; + selectedItem: self selectedItem) +] diff --git a/src/GToolkit-Coder-UI/GtFilterSelectableItemModelParameter.class.st b/src/GToolkit-Coder-UI/GtFilterSelectableItemModelParameter.class.st new file mode 100644 index 000000000..e62766342 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterSelectableItemModelParameter.class.st @@ -0,0 +1,23 @@ +Class { + #name : #GtFilterSelectableItemModelParameter, + #superclass : #GtFilterModelParameter, + #traits : 'TGtWithSelectableItem', + #classTraits : 'TGtWithSelectableItem classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'private - notifying' } +GtFilterSelectableItemModelParameter >> notifyItemsChanged [ + self + announce: (GtFilterModelItemsChanged new + model: self; + itemsBuilder: self itemsBuilder) +] + +{ #category : #'private - notifying' } +GtFilterSelectableItemModelParameter >> notifySelectedItemChanged [ + self + announce: (GtFilterModelSelectedItemChanged new + model: self; + selectedItem: self selectedItem) +] diff --git a/src/GToolkit-Coder-UI/GtFilterSelectableItemViewModel.class.st b/src/GToolkit-Coder-UI/GtFilterSelectableItemViewModel.class.st new file mode 100644 index 000000000..bea937acb --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterSelectableItemViewModel.class.st @@ -0,0 +1,61 @@ +Class { + #name : #GtFilterSelectableItemViewModel, + #superclass : #GtFilterViewModel, + #traits : 'TGtFilterViewModelParameters', + #classTraits : 'TGtFilterViewModelParameters classTrait', + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #accessing } +GtFilterSelectableItemViewModel >> items: aCollection [ + self filterModel items: aCollection +] + +{ #category : #accessing } +GtFilterSelectableItemViewModel >> itemsFuture [ + + ^ self filterModel itemsFuture +] + +{ #category : #accessing } +GtFilterSelectableItemViewModel >> name [ + ^ self filterModel name +] + +{ #category : #'event handling' } +GtFilterSelectableItemViewModel >> onItemsChanged: anAnnouncement [ + self + announce: (GtFilterViewModelItemsChanged new + viewModel: self; + itemsBuilder: anAnnouncement itemsBuilder) +] + +{ #category : #'event handling' } +GtFilterSelectableItemViewModel >> onSelectedItemChanged: anAnnouncement [ + self + announce: (GtFilterViewModelSelectedItemChanged new + viewModel: self; + selectedItem: anAnnouncement selectedItem) +] + +{ #category : #accessing } +GtFilterSelectableItemViewModel >> selectedItem [ + ^ self filterModel selectedItem +] + +{ #category : #accessing } +GtFilterSelectableItemViewModel >> selectedItem: anItem [ + self filterModel selectedItem: anItem +] + +{ #category : #'api - filter model' } +GtFilterSelectableItemViewModel >> subscribeToFilterModel [ + super subscribeToFilterModel. + self filterModel weak + when: GtFilterModelItemsChanged + send: #onItemsChanged: + to: self; + when: GtFilterModelSelectedItemChanged + send: #onSelectedItemChanged: + to: self +] diff --git a/src/GToolkit-Coder-UI/GtFilterSettingsElement.class.st b/src/GToolkit-Coder-UI/GtFilterSettingsElement.class.st new file mode 100644 index 000000000..4423faade --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterSettingsElement.class.st @@ -0,0 +1,30 @@ +Class { + #name : #GtFilterSettingsElement, + #superclass : #BlElement, + #traits : 'TGtWithFilterViewModel + TBrLayoutResizable', + #classTraits : 'TGtWithFilterViewModel classTrait + TBrLayoutResizable classTrait', + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #accessing } +GtFilterSettingsElement class >> futureExecutionConfiguration [ + ^ AsyncFutureExecutionConfiguration new + customGroup: #CoderFilter; + lowPriority +] + +{ #category : #initialization } +GtFilterSettingsElement >> defaultLayout [ + ^ BlLinearLayout horizontal +] + +{ #category : #initialization } +GtFilterSettingsElement >> initialize [ + super initialize. + self fitContent +] + +{ #category : #'api - filter view model' } +GtFilterSettingsElement >> unsubscribeFromFilterViewModel [ + self filterViewModel unsubscribe: self +] diff --git a/src/GToolkit-Coder-UI/GtFilterSettingsId.class.st b/src/GToolkit-Coder-UI/GtFilterSettingsId.class.st new file mode 100644 index 000000000..cde60b544 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterSettingsId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterSettingsId, + #superclass : #GtFilterElementId, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #converting } +GtFilterSettingsId >> asSymbol [ + ^ #'filter--settings' +] diff --git a/src/GToolkit-Coder-UI/GtFilterShortListModel.class.st b/src/GToolkit-Coder-UI/GtFilterShortListModel.class.st new file mode 100644 index 000000000..f4cee0813 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterShortListModel.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtFilterShortListModel, + #superclass : #GtFilterSelectableItemModel, + #traits : 'TGtWithItemsSelector', + #classTraits : 'TGtWithItemsSelector classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterShortListModel >> filterViewModelClass [ + ^ GtFilterShortListViewModel +] diff --git a/src/GToolkit-Coder-UI/GtFilterShortListModelParameter.class.st b/src/GToolkit-Coder-UI/GtFilterShortListModelParameter.class.st new file mode 100644 index 000000000..5f21c3b1c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterShortListModelParameter.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtFilterShortListModelParameter, + #superclass : #GtFilterSelectableItemModelParameter, + #traits : 'TGtWithItemsSelector', + #classTraits : 'TGtWithItemsSelector classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterShortListModelParameter >> filterViewModelClass [ + ^ GtFilterShortListViewModel +] diff --git a/src/GToolkit-Coder-UI/GtFilterShortListSettingsElement.class.st b/src/GToolkit-Coder-UI/GtFilterShortListSettingsElement.class.st new file mode 100644 index 000000000..cf869e02a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterShortListSettingsElement.class.st @@ -0,0 +1,415 @@ +Class { + #name : #GtFilterShortListSettingsElement, + #superclass : #GtFilterSettingsElement, + #instVars : [ + 'buttonElement', + 'buttonAptitude', + 'listElement', + 'handleElement' + ], + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #initialization } +GtFilterShortListSettingsElement >> defaultItemIcon [ + ^ BrFrame new exact: 0 @ 0 +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> defaultItemLabel [ + ^ '' +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> dispatchShowItemsWish [ + buttonElement dispatchEvent: BrDropdownShowWish new +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> focusFirstItemElementInList: aListElement [ + aListElement requestFocus. + aListElement selectFirst. + aListElement selectedItemDo: [ :anItem | + (self filterViewModel selectedItem = anItem) + ifTrue: [ aListElement selectNext ] ]. + aListElement scrollToSelection +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> handleElementDo: aBlock [ + (handleElement at: 1) ifNotNil: aBlock +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> initialize [ + super initialize. + handleElement := nil asWeakReference. + listElement := nil asWeakReference. + self initializeButtonElement. + self addChild: buttonElement as: #button. + self initializeEventHandlers. + self initializeShortcuts. +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> initializeButtonElement [ + buttonElement := self newButtonElement + aptitude: BrGlamorousButtonWithIconAndLabelAptitude; + addAptitude: BrGlamorousButtonIconDropdownAptitude; + addAptitude: (BrGlamorousWithExplicitDropdownAptitude + handle: [ self newHandleElement ] + content: [ self newContentElement ] + do: [ :theAptitude | + theAptitude + withContainerDownUpDropdown; + when: BrDropdownIsHidden + do: [ :anEvent | self onDropdownIsHiddenEvent: anEvent ] ]) +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> initializeEventHandlers [ + self when: GtFilterEditWish do: [ :anEvent | self onEditWish: anEvent ] +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> initializeShortcuts [ + self + addShortcut: (BlShortcutWithAction new + name: 'Display items'; + combination: (BlKeyCombination space or: BlKeyCombination enter); + overrideChildren: false; + action: [ :anEvent | self onDisplayItemsEvent: anEvent ]) +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> interruptListElementFuture [ + self listElementDo: [ :anElement | + anElement withAsyncFutureDo: [ :anElementFuture | + anElementFuture + cancelHard; + interrupt ] ] +] + +{ #category : #accessing } +GtFilterShortListSettingsElement >> listElementDo: aBlock [ + (listElement at: 1) ifNotNil: aBlock +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> newButtonElement [ + ^ BrButton new + beSmallSize; + label: self defaultItemLabel; + icon: self defaultItemIcon; + fitContent +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> newContentElement [ + | aListElement | + self + listElementDo: [ :anElement | + anElement removeFromParent. + ^ anElement ]. + + aListElement := BrSimpleList new + fitContentLimited; + itemStencil: [ :anItemType :aListWidget | self newItemElement ]; + itemDataBinder: [ :anItemElement :anItemObject :anItemIndex | + anItemElement disabled: (anItemObject isVirtualFilterModelItem or: [ + anItemObject = self filterViewModel selectedItem ]). + anItemElement + label: (anItemObject label ifNil: [ self defaultItemLabel ]); + icon: (anItemObject icon ifNil: [ self defaultItemIcon ]); + id: (GtFilterItemId indexed: anItemIndex); + model: anItemObject ]; + itemDataUnbinder: [ :anItemElement :anItemObject :anItemIndex | + anItemElement + label: self defaultItemLabel; + icon: self defaultItemIcon; + id: nil; + model: nil ]; + withAsyncFutureDo: [ :anElementFuture | + anElementFuture + executionConfiguration: (self class futureExecutionConfiguration); + whenPending: [ :theListElement | + theListElement items: { GtFilterModelComputingItem default } ]; + whenError: [ :theListElement :anError | + theListElement items: { (GtFilterModelExceptionItem freeze: anError) } ]; + whenSuccess: [ :theListElement :aCollection | + self updateListElement: theListElement items: aCollection ] ]; + when: BlElementAddedToSceneGraphEvent + do: [ :anEvent | self onListElementAddedToSceneGraph: anEvent ]; + addShortcut: (BlShortcutWithAction new + name: 'Pick selected filter item'; + description: 'Pick selected item as the filter selected item'; + combination: BlKeyCombination builder enter build; + action: [ :aShortcutEvent :aShortcut | self onPickSelectedItemEvent: aShortcutEvent ]); + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination builder arrowDown build; + action: [ self selectNextItemInList: aListElement ]); + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination builder arrowUp build; + action: [ self selectPreviousItemInList: aListElement ])"; + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination builder primary home build; + action: [ self selectFirst; scrollToSelection ]); + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination builder primary end build; + action: [ self selectLast; scrollToSelection ] )". + + + aListElement finalizationRegistry + add: aListElement + finalizer: (ObjectFinalizer new + receiver: self; + selector: #onListElementFinalization; + arguments: #()). + + self updateListElement: aListElement. + listElement at: 1 put: aListElement. + ^ aListElement +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> newHandleElement [ + | aHandle | + self + handleElementDo: [ :anElement | + anElement removeFromParent. + ^ anElement ]. + aHandle := self newButtonElement + disable; + aptitude: BrGlamorousButtonRectangularAptitude; + addAptitude: BrGlamorousButtonIconAptitude; + addAptitude: BrGlamorousButtonLabelAptitude; + addAptitude: BrGlamorousButtonIconDropdownAptitude. + + self updateHandleElement: aHandle. + handleElement at: 1 put: aHandle. + ^ aHandle +] + +{ #category : #initialization } +GtFilterShortListSettingsElement >> newItemElement [ + ^ self newButtonElement + aptitude: BrGlamorousButtonRectangularAptitude; + addAptitude: BrGlamorousButtonIconAptitude; + addAptitude: BrGlamorousButtonLabelAptitude; + hMatchParent; + alignCenterLeft; + label: 'item'; + action: [ :aButton | self onItemClick: aButton ]; + when: BlMouseEnterEvent + do: [ :anEvent | self onItemMouseEnterEvent: anEvent ] +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onDisplayItemsEvent: anEvent [ + anEvent consumed: true. + self dispatchShowItemsWish +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onDropdownIsHiddenEvent: anEvent [ + self fireEvent: GtFilterFocusWholeWish new +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onEditWish: anEvent [ + anEvent consumed: true. + self dispatchShowItemsWish +] + +{ #category : #'api - filter view model' } +GtFilterShortListSettingsElement >> onFilterViewModelChanged [ + super onFilterViewModelChanged. + self updateElement +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onFiltersViewModelItemRemoved: anAnnouncement [ + self interruptListElementFuture +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onItemClick: aButton [ + self hasFilterViewModel ifFalse: [ ^ self ]. + self updateSelectedItem: aButton model from: aButton. +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onItemMouseEnterEvent: anEvent [ + "Select the hovered item" + + | aButton aFilterItem | + aButton := anEvent currentTarget. + aFilterItem := aButton model. + aFilterItem ifNil: [ ^ self ]. + self + listElementDo: [ :aListElement | + aListElement + itemSuchThat: [ :eachItem | eachItem == aFilterItem ] + ifFound: [ :anIndex | aListElement selectOne: anIndex ] ] +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onItemsChanged: anAnnouncement [ + BlTaskAction enqueueElement: self action: [ self updateElement ] +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onListElementAddedToSceneGraph: anEvent [ + self focusFirstItemElementInList: anEvent currentTarget +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onListElementFinalization [ + self interruptListElementFuture +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onPickSelectedItemEvent: aShortcutEvent [ + | anElementList | + anElementList := aShortcutEvent currentTarget. + anElementList selectedItemDo: [ :anItem :anIndex | + self updateSelectedItem: anItem from: anElementList ] +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> onSelectedItemChanged: anAnnouncement [ + BlTaskAction enqueueElement: self action: [ self updateElement ] +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> selectNextItemInList: aListElement [ + | aCurrentSelection | + aCurrentSelection := aListElement selectedIndices. + aListElement selectNext. + aListElement selectedItemDo: [ :anItem | + (self filterViewModel selectedItem = anItem) + ifTrue: [ aListElement selectNext ] ]. + aListElement selectedItemDo: [ :anItem | + (self filterViewModel selectedItem = anItem) + ifTrue: [ + aListElement selectPrevious. + ^ self ] ]. + + aListElement scrollToSelection +] + +{ #category : #'event handling' } +GtFilterShortListSettingsElement >> selectPreviousItemInList: aListElement [ + | aCurrentSelection | + aCurrentSelection := aListElement selectedIndices. + aListElement selectPrevious. + aListElement selectedItemDo: [ :anItem | + (self filterViewModel selectedItem = anItem) + ifTrue: [ aListElement selectPrevious ] ]. + aListElement selectedItemDo: [ :anItem | + (self filterViewModel selectedItem = anItem) + ifTrue: [ + aListElement selectNext. + ^ self ] ]. + + aListElement scrollToSelection +] + +{ #category : #'api - filter view model' } +GtFilterShortListSettingsElement >> subscribeToFilterViewModel [ + super subscribeToFilterViewModel. + self filterViewModel weak + when: GtFilterViewModelItemsChanged + send: #onItemsChanged: + to: self; + when: GtFilterViewModelSelectedItemChanged + send: #onSelectedItemChanged: + to: self; + when: GtFiltersViewModelItemRemoved + send: #onFiltersViewModelItemRemoved: + to: self. +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateButtonElement [ + self updateHandleElement: buttonElement +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateElement [ + self updateButtonElement. + self updateHandleElement. + self updateListElement. +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateHandleElement [ + self handleElementDo: [ :anElement | + self updateHandleElement: anElement ]. +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateHandleElement: anElement [ + self hasFilterViewModel ifFalse: [ ^ self ]. + + self filterViewModel selectedItem + ifIconAndLabel: [ :anIcon :aLabel | + anElement icon: anIcon. + anElement label: aLabel ] + ifIcon: [ :anIcon | + anElement icon: anIcon. + anElement label: self defaultItemLabel ] + ifLabel: [ :aLabel | + anElement icon: self defaultItemIcon. + anElement label: aLabel ] +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateListElement [ + self listElementDo: [ :anElement | self updateListElement: anElement ] +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateListElement: anElement [ + self hasFilterViewModel ifFalse: [ ^ self ]. + + anElement withAsyncFutureDo: [ :anElementFuture | + anElementFuture future: self filterViewModel someItemsFuture asAsyncFuture ]. +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateListElement: theListElement items: aCollection [ + aCollection + ifEmpty: [ theListElement items: {GtFilterModelEmptyItem default} ] + ifNotEmpty: [ theListElement items: aCollection ] +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateListElementOldWay: anElement [ + "We use this method to demo a UI blocking issue. + See https://github.com/feenkcom/gtoolkit/issues/4327" + + | aCollection | + self hasFilterViewModel ifFalse: [ ^ self ]. + + aCollection := self filterViewModel someItems. + self updateListElement: anElement items: aCollection +] + +{ #category : #'private - updating' } +GtFilterShortListSettingsElement >> updateSelectedItem: aFilterModelItem from: anElement [ + aFilterModelItem ifNil: [ ^ self ]. + aFilterModelItem isVirtualFilterModelItem ifTrue: [ ^ self ]. + aFilterModelItem isExceptionFilterModelItem ifTrue: [ + anElement phlow spawnObject: aFilterModelItem. + ^ self ]. + + self hasFilterViewModel ifFalse: [ ^ self ]. + aFilterModelItem = self filterViewModel selectedItem ifTrue: [ ^ self ]. + BlTaskAction + enqueueElement: self + action: [ anElement fireEvent: BrDropdownHideWish new ]. + BlTaskAction + enqueueElement: self + action: [ self filterViewModel selectedItem: aFilterModelItem ] +] diff --git a/src/GToolkit-Coder-UI/GtFilterShortListViewModel.class.st b/src/GToolkit-Coder-UI/GtFilterShortListViewModel.class.st new file mode 100644 index 000000000..df565c598 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterShortListViewModel.class.st @@ -0,0 +1,24 @@ +Class { + #name : #GtFilterShortListViewModel, + #superclass : #GtFilterSelectableItemViewModel, + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #accessing } +GtFilterShortListViewModel >> filterElementClass [ + ^ GtFilterShortListSettingsElement +] + +{ #category : #accessing } +GtFilterShortListViewModel >> someItems [ + + ^ self filterModel someItems +] + +{ #category : #accessing } +GtFilterShortListViewModel >> someItemsFuture [ + "Return an async future that computes list items" + + + ^ self filterModel someItemsFuture +] diff --git a/src/GToolkit-Coder-UI/GtFilterSignal.class.st b/src/GToolkit-Coder-UI/GtFilterSignal.class.st new file mode 100644 index 000000000..9f00ce89b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterSignal.class.st @@ -0,0 +1,13 @@ +Class { + #name : #GtFilterSignal, + #superclass : #ContextStackSignal, + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #testing } +GtFilterSignal class >> gtNormalOperationSignal [ + "Answer a Boolean indicating whether this signal is generated as part of normal operations. + See {{gtMethod:BeaconSignal class>>gtNormalOperationSignal}} for a description" + + ^ true. +] diff --git a/src/GToolkit-Coder-UI/GtFilterStep.class.st b/src/GToolkit-Coder-UI/GtFilterStep.class.st new file mode 100644 index 000000000..63423edbb --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterStep.class.st @@ -0,0 +1,137 @@ +Class { + #name : #GtFilterStep, + #superclass : #BlDevScripterToolActionStep, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #'steps - checks' } +GtFilterStep >> assertFilterModelAtIndex: anIndex itemsCountIs: anItemsCount [ + ^ self assert + label: ('Assert filter model no. {1} has {2} {3}' + format: {anIndex. + anItemsCount. + 'item' asPluralBasedOn: anItemsCount}); + referenceSender; + onParentStepTarget: self; + value: [ :aFilterItemsElement | (aFilterItemsElement filtersViewModel items at: anIndex) itemsFuture wait size ] + equals: anItemsCount +] + +{ #category : #'steps - checks' } +GtFilterStep >> assertFilterModelAtIndex: anIndex itemsCountIsAtLeast: anItemsCount [ + ^ self assert + label: ('Assert filter model no. {1} has at least {2} {3}' + format: {anIndex. + anItemsCount. + 'item' asPluralBasedOn: anItemsCount}); + referenceSender; + onParentStepTarget: self; + satisfies: [ :aFilterItemsElement | + (aFilterItemsElement filtersViewModel items at: anIndex) itemsFuture wait size + >= anItemsCount ] + description: [ :aFilterItemsElement | + 'Filter at position {1} must have at least {2} {3}, but have {4}' + format: {anIndex. + anItemsCount. + 'item' asPluralBasedOn: anItemsCount. + (aFilterItemsElement filtersViewModel items at: anIndex) itemsFuture wait size} ] +] + +{ #category : #'steps - checks' } +GtFilterStep >> assertFilterModelAtIndex: anIndex selectedValueIs: aValue [ + ^ self assert + label: ('Assert filter model no. {1} has selected item: {2}' + format: {anIndex. + aValue}); + referenceSender; + onParentStepTarget: self; + value: [ :aFilterItemsElement | (aFilterItemsElement filtersViewModel items at: anIndex) selectedValue ] + equals: aValue +] + +{ #category : #'steps - checks' } +GtFilterStep >> assertFilterModelNamed: aFilterName [ + ^ self assert + label: ('Assert filter model with name {1} exists' format: {aFilterName}); + referenceSender; + onParentStepTarget: self; + onChildFromBlock: [ :aFilterItemsElement | aFilterItemsElement filtersViewModel items ]; + anySatisfy: [ :aFilterViewModel | aFilterViewModel name = aFilterName ] + description: [ :someFilterViewModels | + 'Filter with name {1} is missing: {2}' + format: {aFilterName. + someFilterViewModels} ] +] + +{ #category : #'steps - checks' } +GtFilterStep >> assertFilterModelNamed: aFilterName selectedValueIs: aValue [ + ^ self assert + label: ('Assert filter model with name {1} has selected item: {2}' + format: {aFilterName. aValue}); + referenceSender; + onParentStepTarget: self; + onChildFromBlock: [ :aFilterItemsElement | aFilterItemsElement filtersViewModel items ]; + anySatisfy: [ :aFilterViewModel | aFilterViewModel name = aFilterName and: [ + aFilterViewModel selectedValue = aValue ] ] + description: [ :someFilterViewModels | + 'Filter with name {1} and {2} selected value is missing: {3}.' + format: {aFilterName. aValue. someFilterViewModels} ] +] + +{ #category : #'steps - checks' } +GtFilterStep >> assertFilterModelsCountIs: anItemsCount [ + ^ self assert + label: ('Assert filter models count is {1}' format: {anItemsCount}); + referenceSender; + onParentStepTarget: self; + value: [ :aFilterItemsElement | (aFilterItemsElement filtersViewModel items size) ] + equals: anItemsCount +] + +{ #category : #'steps - interactions' } +GtFilterStep >> clickOnAddFilter [ + ^ self click + referenceSender; + // GtFiltersAddId +] + +{ #category : #'steps - interactions' } +GtFilterStep >> clickOnDropdownFilterItem: aFilterItem [ + ^ self click + label: ('Click on dropdown item: {1}' format: { aFilterItem }); + referenceSender; + onSpaceRoot; + onDropdown: true; + // #scrollable; + onChildFromBlock: [ :anElement | anElement children detect: [ :each | each viewModel model = aFilterItem ] ] +] + +{ #category : #'steps - interactions' } +GtFilterStep >> clickOnDropdownFilterItemAtIndex: anIndex [ + ^ self click + label: ('Click on dropdown item at index {1}' format: { anIndex }); + referenceSender; + onSpaceRoot; + // BrAnchoredOuterContainer; + // (GtFilterItemId indexed: anIndex) +] + +{ #category : #'steps - interactions' } +GtFilterStep >> clickOnDropdownFilterItemLabeled: aLabel [ + ^ self click + label: ('Click on dropdown item with label {1}' format: {aLabel}); + referenceSender; + onSpaceRoot; + onDropdown: true; + // #scrollable; + onChildFromBlock: [ :anElement | anElement children detect: [ :each | each children last text asString = aLabel ] ] +] + +{ #category : #'steps - interactions' } +GtFilterStep >> clickOnFilterSettingsAtIndex: anIndex [ + ^ self click + label: ('Click on filter settings at index {1}' format: { anIndex }); + referenceSender; + // (GtFilterNameId indexed: anIndex); + // GtFilterSettingsId +] diff --git a/src/GToolkit-Coder-UI/GtFilterSuffixLabelElement.class.st b/src/GToolkit-Coder-UI/GtFilterSuffixLabelElement.class.st new file mode 100644 index 000000000..810b6cea2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterSuffixLabelElement.class.st @@ -0,0 +1,14 @@ +Class { + #name : #GtFilterSuffixLabelElement, + #superclass : #GtFilterLabelElement, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #'as yet unclassified' } +GtFilterSuffixLabelElement >> initialize [ + super initialize. + self padding: (BlInsets top: 0 right: 2 bottom: 0 left: 0). + + self addChild: settingsContainer as: #container. + self addChild: labelElement +] diff --git a/src/GToolkit-Coder-UI/GtFilterTagEditorId.class.st b/src/GToolkit-Coder-UI/GtFilterTagEditorId.class.st new file mode 100644 index 000000000..2daecb758 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTagEditorId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterTagEditorId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Filters' +} + +{ #category : #converting } +GtFilterTagEditorId >> asSymbol [ + ^ #'filter-tag--editor' +] diff --git a/src/GToolkit-Coder-UI/GtFilterTagElement.class.st b/src/GToolkit-Coder-UI/GtFilterTagElement.class.st index 260994524..65751d02d 100644 --- a/src/GToolkit-Coder-UI/GtFilterTagElement.class.st +++ b/src/GToolkit-Coder-UI/GtFilterTagElement.class.st @@ -2,234 +2,410 @@ I am a {{gtClass:BlElement}}. I display one filter in a chain of filters. I am used by {{gtClass:GtFiltersElement}}. -1. # Element Example - -This is how I look like: {{gtExample:GtFiltersElementExamples>>#filterTagElement|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=70}} -1. # Coder Example - -The following example shows a coder with default filters element that you can change: {{gtExample:GtFiltersElementExamples>>#coderWithFiltersElement|codeExpanded=false|previewExpanded=true}} - " Class { #name : #GtFilterTagElement, #superclass : #BrHorizontalPane, #instVars : [ - 'descriptor', + 'filterDescriptor', + 'availableFilterDescriptors', 'valueString', - 'filterElement' + 'filterElement', + 'filterPickerHandle', + 'filterLabel' ], #category : #'GToolkit-Coder-UI-Filters' } -{ #category : #actions } +{ #category : #'private - accessing' } GtFilterTagElement >> activateEditor [ self - childNamed: #editableLabel - ifFound: [ :label | - label - requestFocus; - switchToEditor ] - ifNone: [ (self childNamed: #dropDown) requestFocus ] + childWithId: GtFilterTagEditorId + ifFound: [ :anEditor | anEditor requestFocus ] + ifNone: [ filterLabel requestFocus ] ] -{ #category : #private } +{ #category : #'private - accessing' } GtFilterTagElement >> applyDescriptor: aDescriptor [ self descriptor: aDescriptor. self activateEditor. - filterElement applyFilters -] - -{ #category : #elements } -GtFilterTagElement >> buildTagElements [ - self removeChildren. - self addChild: self createDropDown as: #dropDown. - descriptor valueIsRequired - ifTrue: [ self addChild: self createEditableLabel as: #editableLabel ] -] - -{ #category : #elements } -GtFilterTagElement >> createDropDown [ - | button dropDownLook | - button := self createDropDownButton. - dropDownLook := BrGlamorousWithDropdownAptitude - handle: [ self createDropDownButton ] - content: [ BrSimpleList new - items: filterElement descriptors; - stencil: [ :each | - | element | - element := BlElement new. - element - viewModel: - (BrInteractiveModel new - withHover; - yourself); - aptitude: - (BrInteractiveCommonAptitude new - default: [ :f | f background: Color transparent ]; - hovered: [ :f | f background: self theme button defaultBorderColor ]; - yourself); - layout: BlLinearLayout horizontal; - constraintsDo: [ :c | - c horizontal matchParent. - c vertical fitContent ]; - padding: (BlInsets all: 5); - addEventHandlerOn: BlClickEvent - do: [ button label: each name. - dropDownLook hide. - self applyDescriptor: each ]; - addChild: - (BrLabel new - aptitude: - BrGlamorousLabelAptitude new glamorousRegularFont - glamorousCodeSmallSize; - text: each name; - yourself). - element ]; - hFitContent; - vFitContent ]. - button addAptitude: dropDownLook. - button - addAptitude: - (BrInteractiveCommonAptitude new - default: [ button background: Color transparent ]; - hovered: [ button background: - self theme status neutralBackgroundColor darker ]). - ^ button -] - -{ #category : #elements } -GtFilterTagElement >> createDropDownButton [ - | button | - button := BrButton new. - button - aptitude: - BrTextLabelAptitude new glamorousRegularFont glamorousCodeSmallSize - + BrGlamorousButtonIconAptitude + BrGlamorousButtonLayoutAptitude. - button vExact: 20. - button padding: (BlInsets left: 4 right: 2). - button label: self descriptor name. - button icon: BrGlamorousVectorIcons downwards. - button beSmallSize. - ^ button -] - -{ #category : #elements } + + aDescriptor valueIsRequired + ifFalse: [ self applyFiltersDueTo: #applyDescriptor ] +] + +{ #category : #'private - instance creation' } +GtFilterTagElement >> applyFiltersDueTo: aReason [ + filterElement + ifNil: [ ^ self ]. + + filterElement applyFiltersDueTo: aReason +] + +{ #category : #'api - accessing' } +GtFilterTagElement >> availableFilterDescriptors [ + "Tag element allows users to change the filter to another one. + Therefore each filter tag element should be able to get a list of all possible descriptors" + + + ^ availableFilterDescriptors +] + +{ #category : #'api - accessing' } +GtFilterTagElement >> availableFilterDescriptors: aCollectionOfFilterDescriptors [ + availableFilterDescriptors := aCollectionOfFilterDescriptors +] + +{ #category : #'api - accessing' } +GtFilterTagElement >> createAndAddCompletionValuesDropdown [ + | aDefaultValue | + + aDefaultValue := filterDescriptor emptyDefaultValue ifNil: [ '' ]. + + self addChild: (self createFilterLabel + id: GtFilterTagLabelId; + text: aDefaultValue; + addAptitude: (BrGlamorousWithExplicitDropdownAptitude + handle: [ + BrHorizontalPane new + fitContent; + addChildren: { + self createFilterLabel text: aDefaultValue. + self createFilterPickerHandle. + } ] + content: [ self createCompletionValuesDropdownContent ])). + self addChild: self createFilterPickerHandle. + +] + +{ #category : #'api - accessing' } +GtFilterTagElement >> createAndAddFilterValueEditor [ + | aDefaultValue | + aDefaultValue := filterDescriptor emptyDefaultValue ifNil: [ '' ]. + + self + childWithId: GtFilterTagEditorId + ifFound: [ :anEditor | anEditor text: aDefaultValue ] + ifNone: [ self addChild: (self createEditableLabel text: aDefaultValue) ]. + + self + childWithId: GtFilterTagEditorId + ifFound: [ :anEditor | + filterDescriptor completion + ifNotNil: [ :strategy | + (GtCompletionController on: anEditor strategy: strategy) install ] + ifNil: [ + (GtCompletionController on: anEditor strategy: GtCompletionStrategy new) install ] ] +] + +{ #category : #'private - instance creation' } +GtFilterTagElement >> createCompletionValuesDropdownContent [ + ^ BrSimpleList new + fitContent; + stencil: [ :eachCompletionAction :eachIndex | + BrHorizontalPane new + id: (GtFilterTagPickerItemId indexed: eachIndex); + hMatchParent; + vFitContent; + padding: (BlInsets all: 5); + aptitude: (BrStyleCommonAptitude new + default: [ :s | s background: Color transparent ]; + hovered: [ :s | s background: self theme button defaultBorderColor ]; + yourself); + when: BlClickEvent + do: [ :anEvent | + anEvent consumed: true. + anEvent currentTarget fireEvent: BrDropdownHideWish new. + self + requestUpdateFilter: filterDescriptor + value: eachCompletionAction displayText asString trimmed ]; + addChild: (BrLabel new + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont glamorousCodeSmallSize; + text: eachCompletionAction displayText; + yourself); + yourself ]; + stream: (filterDescriptor completion + ifNil: [ AsyncEmptyStream new ] + ifNotNil: [ :aCompletion | + (aCompletion + completionResultFor: '' + at: 0 + requested: true) asAsyncStream ]); + yourself +] + +{ #category : #'private - instance creation' } +GtFilterTagElement >> createDropdownContent [ + ^ BrSimpleList new + vFitContentLimited; + hFitContent; + stencil: [ :eachFilterDescriptor :eachIndex | + BrHorizontalPane new + id: (GtFilterTagPickerItemId indexed: eachIndex); + hMatchParent; + vFitContent; + padding: (BlInsets all: 5); + aptitude: (BrStyleCommonAptitude new + default: [ :s | s background: Color transparent ]; + hovered: [ :s | s background: self theme button defaultBorderColor ]; + yourself); + when: BlClickEvent + do: [ :anEvent | + anEvent consumed: true. + anEvent currentTarget fireEvent: BrDropdownHideWish new. + self + requestReplaceFilter: filterDescriptor + with: eachFilterDescriptor ]; + addChild: (BrLabel new + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont glamorousCodeSmallSize; + text: eachFilterDescriptor name; + yourself); + yourself ]; + items: self availableFilterDescriptors; + yourself +] + +{ #category : #'private - instance creation' } GtFilterTagElement >> createEditableLabel [ - | label | - label := BrEditableLabel new. - label - aptitude: - BrGlamorousEditableLabelAptitude new glamorousRegularFont - glamorousRegularSmallSize. - label text: (valueString ifNil: [ '' ]). - label - padding: - (BlInsets - top: 2 - left: 4 - bottom: 2 - right: 4). - label - when: BrEditorAcceptWish - do: [ :aWish | - valueString := aWish text asString trim. - (label parent == self and: [ valueString isEmpty ]) - ifTrue: [ self removeFromParent ]. - filterElement applyFilters ]. - label - when: BrEditorCancelWish - do: [ :aWish | - (label parent == self and: [ self isValid not ]) - ifTrue: [ self removeFromParent ]. - filterElement applyFilters ]. - label - whenKey: BlKeyCombination backspace - labelDo: [ :aShortcutEvent :aShortcut | - self removeFromParent. - filterElement applyFilters ]. - descriptor completion - ifNotNil: - [ :strategy | (GtCompletionController on: label strategy: strategy) install ]. - ^ label -] - -{ #category : #accessing } + | anEditor | + + anEditor := BrEditor new + beMode: BrTextEditorEditableSingleLineMode new; + fitContent; + id: GtFilterTagEditorId; + aptitude: BrGlamorousRegularEditorAptitude new glamorousRegularFont glamorousCodeSmallSize; + background: Color white; + geometry: (BlRoundedRectangleGeometry cornerRadius: 2); + margin: (BlInsets top: 2 left: 0 bottom: 2 right: 3); + padding: (BlInsets top: 1 left: 2 bottom: 1 right: 2); + constraintsDo: [ :c | c minWidth: 16 ]; + text: ''. + + anEditor addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination enter; + action: [ :anEvent | + self + requestUpdateFilter: filterDescriptor + value: anEvent currentTarget text asString trimmed ]). + + anEditor when: BlFocusOutEvent do: [ :anEvent | + anEvent isDueToRemoval + ifFalse: [ + self + requestUpdateFilter: filterDescriptor + value: anEvent currentTarget text asString trimmed ] ]. + + ^ anEditor +] + +{ #category : #'private - instance creation' } +GtFilterTagElement >> createFilterLabel [ + ^ BrLabel new + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont glamorousCodeSmallSize; + vFitContent; + beSmallSize +] + +{ #category : #'private - instance creation' } +GtFilterTagElement >> createFilterLabelHandle [ + ^ self createFilterLabel + id: GtFilterTagLabelId; + addAptitude: (BrGlamorousWithExplicitDropdownAptitude + handle: [ + BrHorizontalPane new + fitContent; + addChildren: { + self createFilterLabel text: filterDescriptor name. + self createFilterPickerHandle + } ] + content: [ self createDropdownContent ]) doNotShowOnClick +] + +{ #category : #'private - instance creation' } +GtFilterTagElement >> createFilterPickerHandle [ + ^ BrButton new + id: GtFilterTagPickerId; + aptitude: (BrGlamorousButtonCircularAptitude paddingScale: 0 heightScale: 0.75) + + BrGlamorousButtonFlatExteriorAptitude + + BrGlamorousButtonIconAptitude; + icon: BrGlamorousVectorIcons downwards; + beNormalSize +] + +{ #category : #'api - accessing' } GtFilterTagElement >> descriptor [ - ^ descriptor + ^ filterDescriptor ] -{ #category : #accessing } +{ #category : #'api - accessing' } GtFilterTagElement >> descriptor: aFilterDescriptor [ - descriptor := aFilterDescriptor. - valueString := ''. - self buildTagElements + filterDescriptor := aFilterDescriptor. + + filterLabel text: filterDescriptor name. + + filterDescriptor valueIsRequired + ifTrue: [ + filterDescriptor shouldOfferCompletionValues + ifTrue: [ self createAndAddCompletionValuesDropdown ] + ifFalse: [ self createAndAddFilterValueEditor ] ] + ifFalse: [ + self + childWithId: GtFilterTagEditorId + ifFound: [ :anEditor | anEditor removeFromParent ] ] ] -{ #category : #'initialize-release' } +{ #category : #initialization } GtFilterTagElement >> emptyDefaultValueLabel [ - ^ descriptor emptyDefaultValue + ^ filterDescriptor emptyDefaultValue ] -{ #category : #accessing } +{ #category : #'api - accessing' } GtFilterTagElement >> filter: aGtMethodsFilter [ - filterElement := aGtMethodsFilter + filterElement := aGtMethodsFilter. + self availableFilterDescriptors: filterElement descriptors ] -{ #category : #'initialize-release' } +{ #category : #initialization } GtFilterTagElement >> initialize [ super initialize. - self fitContent. - self margin: (BlInsets all: 3). + + availableFilterDescriptors := #(). + self - addAptitude: - (BrStyleCommonAptitude new - default: [ :aStyle | - aStyle geometry: (BlRoundedRectangleGeometry cornerRadius: 4). - aStyle background: self theme status neutralBackgroundColor. - aStyle border: BlBorder empty ]; - hovered: [ :aStyle | - aStyle background: - self theme status neutralBackgroundColor darker ]) + fitContent; + alignCenterLeft; + beInSingleCompositionLayer; + beFocusable. + + self addAptitude: + (BrStyleCommonAptitude new + default: [ :aStyle | + aStyle geometry: (BlRoundedRectangleGeometry cornerRadius: 4). + aStyle background: self theme status neutralBackgroundColor ]; + hovered: [ :aStyle | + aStyle background: self theme status neutralBackgroundColor darker ]; + focused: [ :aStyle | + aStyle background: self theme editor focusedBorderColor lighter lighter ]). + + filterLabel := self createFilterLabelHandle. + filterLabel margin: (BlInsets top: 2 right: 0 bottom: 2 left: 4). + filterPickerHandle := self createFilterPickerHandle. + filterPickerHandle margin: (BlInsets top: 1 right: 4). + + self addChildren: { + filterLabel. + filterPickerHandle + }. + + self addAptitude: (BrStyleCommonAptitude new + // #label; + default: [ :aStyle | aStyle foreground: Color black ]; + focused: [ :aStyle | aStyle foreground: Color white ]). + + self addAptitude: (BrStyleCommonAptitude new + // #icon; + default: [ :aStyle | aStyle background: Color black ]; + focused: [ :aStyle | aStyle background: Color white ]). + + self when: BlClickEvent do: [ :anEvent | + anEvent consumed: true. + self hasFocus + ifTrue: [ + filterLabel dispatchEvent: (BrDropdownShowWish new) ]. + self requestFocus ]. + + filterPickerHandle + when: BlClickEvent + do: [ :anEvent | filterLabel dispatchEvent: BrDropdownShowWish new ]. + + self addShortcut: (BlShortcutWithAction new + combination: (BlKeyCombination backspace); + action: [ self requestRemoveFilter: filterDescriptor ]) ] { #category : #testing } GtFilterTagElement >> isDefaultAllFilter [ ^ self descriptor showAsDefaultWhenEmpty - and: [ valueString isEmpty - and: - [ (self childNamed: #editableLabel) text asString = self emptyDefaultValueLabel ] ] + and: [ + self + childWithId: GtFilterTagEditorId + ifFound: [ :aLabel | aLabel text asString = self emptyDefaultValueLabel ] + ifNone: [ true ] ] ] { #category : #testing } -GtFilterTagElement >> isValid [ - ^ descriptor valueIsRequired not or: [ self valueString notEmpty ] +GtFilterTagElement >> isValid [ + ^ filterDescriptor isValueValid: self valueString ] -{ #category : #'initialize-release' } +{ #category : #initialization } GtFilterTagElement >> makeDefaultFilter [ - valueString := ''. self setLabelText: self emptyDefaultValueLabel ] -{ #category : #private } +{ #category : #'private - actions' } +GtFilterTagElement >> requestRemoveFilter: aFilterDescriptor [ + "Is sent by filter tag when it requests to remove a filter" + | aWish | + + self dispatchEvent: (aWish := GtFilterTagRemoveWish new + filterDescriptor: aFilterDescriptor). + + self removeFromParent. + self applyFiltersDueTo: aWish +] + +{ #category : #'private - actions' } +GtFilterTagElement >> requestReplaceFilter: aFilterDescriptor with: aNewFilterDescriptor [ + "Is sent by filter tag when it requests to replace itself with a new filter" + | aWish | + + aFilterDescriptor = aNewFilterDescriptor + ifTrue: [ ^ self ]. + + self descriptor: aNewFilterDescriptor. + self activateEditor. + + self dispatchEvent: (aWish := GtFilterTagReplaceWish new + filterDescriptor: aFilterDescriptor; + newFilterDescriptor: aNewFilterDescriptor). + + aNewFilterDescriptor valueIsRequired + ifFalse: [ self applyFiltersDueTo: aWish ] +] + +{ #category : #'private - actions' } +GtFilterTagElement >> requestUpdateFilter: aFilterDescriptor value: aString [ + "Is sent by filter tag when it requests to update the filter with a new value" + | aWish | + + (aFilterDescriptor isValueValid: aString) + ifFalse: [ ^ self ]. + + self dispatchEvent: (aWish := GtFilterTagUpdateWish new + filterDescriptor: aFilterDescriptor; + newValue: aString). + + self applyFiltersDueTo: aWish +] + +{ #category : #'private - accessing' } GtFilterTagElement >> setLabelText: aString [ self - childNamed: #editableLabel - ifFound: [ :label | - label text deleteAll. - aString notEmpty - ifTrue: [ label text - append: aString asRopedText glamorousRegularFont glamorousRegularSmallSize ] ] + childWithId: GtFilterTagEditorId + ifFound: [ :label | label text: aString ] ] -{ #category : #accessing } +{ #category : #'api - accessing' } GtFilterTagElement >> valueString [ - ^ valueString + ^ self + childWithId: GtFilterTagEditorId + ifFound: [ :anEditor | anEditor text asString ] + ifNone: [ '' ] ] -{ #category : #accessing } +{ #category : #'api - accessing' } GtFilterTagElement >> valueString: aString [ - valueString := aString. self setLabelText: aString ] diff --git a/src/GToolkit-Coder-UI/GtFilterTagLabelId.class.st b/src/GToolkit-Coder-UI/GtFilterTagLabelId.class.st new file mode 100644 index 000000000..134d53398 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTagLabelId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterTagLabelId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Filters' +} + +{ #category : #converting } +GtFilterTagLabelId >> asSymbol [ + ^ #'filter-tag--label' +] diff --git a/src/GToolkit-Coder-UI/GtFilterTagPickerId.class.st b/src/GToolkit-Coder-UI/GtFilterTagPickerId.class.st new file mode 100644 index 000000000..583deedf3 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTagPickerId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterTagPickerId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Filters' +} + +{ #category : #converting } +GtFilterTagPickerId >> asSymbol [ + ^ #'filter-tag--picker' +] diff --git a/src/GToolkit-Coder-UI/GtFilterTagPickerItemId.class.st b/src/GToolkit-Coder-UI/GtFilterTagPickerItemId.class.st new file mode 100644 index 000000000..a5538b2a3 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTagPickerItemId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFilterTagPickerItemId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Filters' +} + +{ #category : #converting } +GtFilterTagPickerItemId >> asSymbol [ + ^ #'filter-tag--picker-item' +] diff --git a/src/GToolkit-Coder-UI/GtFilterTagRemoveWish.class.st b/src/GToolkit-Coder-UI/GtFilterTagRemoveWish.class.st new file mode 100644 index 000000000..3a354f5e7 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTagRemoveWish.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtFilterTagRemoveWish, + #superclass : #GtFilterTagWish, + #category : #'GToolkit-Coder-UI-Filters' +} diff --git a/src/GToolkit-Coder-UI/GtFilterTagReplaceWish.class.st b/src/GToolkit-Coder-UI/GtFilterTagReplaceWish.class.st new file mode 100644 index 000000000..784564aa5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTagReplaceWish.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtFilterTagReplaceWish, + #superclass : #GtFilterTagWish, + #instVars : [ + 'newFilterDescriptor' + ], + #category : #'GToolkit-Coder-UI-Filters' +} + +{ #category : #accessing } +GtFilterTagReplaceWish >> newFilterDescriptor [ + + ^ newFilterDescriptor +] + +{ #category : #accessing } +GtFilterTagReplaceWish >> newFilterDescriptor: anObject [ + + newFilterDescriptor := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterTagUpdateWish.class.st b/src/GToolkit-Coder-UI/GtFilterTagUpdateWish.class.st new file mode 100644 index 000000000..61ecd35d2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTagUpdateWish.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtFilterTagUpdateWish, + #superclass : #GtFilterTagWish, + #instVars : [ + 'newValue' + ], + #category : #'GToolkit-Coder-UI-Filters' +} + +{ #category : #accessing } +GtFilterTagUpdateWish >> newValue [ + + ^ newValue +] + +{ #category : #accessing } +GtFilterTagUpdateWish >> newValue: anObject [ + + newValue := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterTagWish.class.st b/src/GToolkit-Coder-UI/GtFilterTagWish.class.st new file mode 100644 index 000000000..86369cf60 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTagWish.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtFilterTagWish, + #superclass : #BlEvent, + #instVars : [ + 'filterDescriptor' + ], + #category : #'GToolkit-Coder-UI-Filters' +} + +{ #category : #accessing } +GtFilterTagWish >> filterDescriptor [ + + ^ filterDescriptor +] + +{ #category : #accessing } +GtFilterTagWish >> filterDescriptor: anObject [ + + filterDescriptor := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterTextModel.class.st b/src/GToolkit-Coder-UI/GtFilterTextModel.class.st new file mode 100644 index 000000000..4d4fe7fc4 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTextModel.class.st @@ -0,0 +1,100 @@ +Class { + #name : #GtFilterTextModel, + #superclass : #GtFilterModel, + #instVars : [ + 'text', + 'inputFilter', + 'completionBuilder' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterTextModel >> changesFilteredResult [ + "Indicates whether it changes a result of filtered items, e.g., list of method coders." + + + ^ self text isEmpty ifFalse: [ true ] ifTrue: [ false ] +] + +{ #category : #accessing } +GtFilterTextModel >> completion [ + + ^ completionBuilder value +] + +{ #category : #accessing } +GtFilterTextModel >> completion: aCompletionBuilder [ + completionBuilder := aCompletionBuilder +] + +{ #category : #accessing } +GtFilterTextModel >> completionDo: aBlock [ + self completion ifNotNil: aBlock +] + +{ #category : #accessing } +GtFilterTextModel >> filterViewModelClass [ + ^ GtFilterTextViewModel +] + +{ #category : #initialization } +GtFilterTextModel >> initialize [ + super initialize. + text := BlText empty. + inputFilter := BrTextEditorNoLineBreakInputFilter new. +] + +{ #category : #accessing } +GtFilterTextModel >> inputFilter [ + + ^ inputFilter +] + +{ #category : #accessing } +GtFilterTextModel >> inputFilter: anInputFilter [ + inputFilter = anInputFilter ifTrue: [ ^ self ]. + + inputFilter := anInputFilter. + self notifyInputFilterChanged. +] + +{ #category : #'private - notifying' } +GtFilterTextModel >> notifyInputFilterChanged [ + self + announce: (GtFilterTextModelInputFilterChanged new + model: self; + inputFilter: self inputFilter) +] + +{ #category : #'private - notifying' } +GtFilterTextModel >> notifyTextChanged [ + self + announce: (GtFilterTextModelTextChanged new + model: self; + text: self text) +] + +{ #category : #accessing } +GtFilterTextModel >> selectedValue [ + "Return a filter value, e.g., selected item, input text." + + ^ self text asString +] + +{ #category : #accessing } +GtFilterTextModel >> text [ + + ^ text +] + +{ #category : #accessing } +GtFilterTextModel >> text: aText [ + | aNewText | + aText ifNil: [ ^ self ]. + aNewText := aText asRopedText. + (self text equals: aNewText) ifTrue: [ ^ self ]. + + text := aNewText. + self notifyTextChanged. +] diff --git a/src/GToolkit-Coder-UI/GtFilterTextModelInputFilterChanged.class.st b/src/GToolkit-Coder-UI/GtFilterTextModelInputFilterChanged.class.st new file mode 100644 index 000000000..763e6120e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTextModelInputFilterChanged.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFilterTextModelInputFilterChanged, + #superclass : #GtFilterModelAnnouncement, + #instVars : [ + 'inputFilter' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterTextModelInputFilterChanged >> inputFilter [ + ^ inputFilter +] + +{ #category : #accessing } +GtFilterTextModelInputFilterChanged >> inputFilter: anObject [ + inputFilter := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterTextModelTextChanged.class.st b/src/GToolkit-Coder-UI/GtFilterTextModelTextChanged.class.st new file mode 100644 index 000000000..c32599d38 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTextModelTextChanged.class.st @@ -0,0 +1,25 @@ +Class { + #name : #GtFilterTextModelTextChanged, + #superclass : #GtFilterModelAnnouncement, + #instVars : [ + 'text' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #testing } +GtFilterTextModelTextChanged >> changesFilteredResult [ + "Indicates whether it changes a result of filtered items, e.g., list of method coders." + + ^ true +] + +{ #category : #accessing } +GtFilterTextModelTextChanged >> text [ + ^ text +] + +{ #category : #accessing } +GtFilterTextModelTextChanged >> text: anObject [ + text := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterTextSettingsElement.class.st b/src/GToolkit-Coder-UI/GtFilterTextSettingsElement.class.st new file mode 100644 index 000000000..4a5a32c6f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTextSettingsElement.class.st @@ -0,0 +1,237 @@ +Class { + #name : #GtFilterTextSettingsElement, + #superclass : #GtFilterSettingsElement, + #instVars : [ + 'editorElement', + 'completionController' + ], + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #initialization } +GtFilterTextSettingsElement >> initialize [ + super initialize. + self initializeEditorElement. + self addChild: editorElement as: #editor. + self initializeEventHandlers. +] + +{ #category : #initialization } +GtFilterTextSettingsElement >> initializeEditorElement [ + editorElement := BrEditor new + hFitContentLimited; + vFitContent; + beMode: BrTextEditorEditableSingleLineMode new; + background: self theme default contentBackground; + aptitude: BrGlamorousRegularEditorAptitude new + glamorousRegularSmallSize; + text: ''; + padding: (BlInsets top: 1 right: 2 bottom: 1 left: 2); + constraintsDo: [ :c | c minWidth: 20 ]; + addShortcut: (BlShortcutWithAction new + name: 'Accept new text'; + description: 'Accept new text and store it into the filter model'; + combination: BlKeyCombination enter; + action: [ :anEvent | + self onTextEditorModificationAccepted: anEvent ]); + addShortcut: (BlShortcutWithAction new + name: 'Move cursor left or focus current filter'; + combination: (BlKeyCombination arrowLeft); + action: [ :anEvent | + self onTextEditorArrowLeftEvent: anEvent ]); + addShortcut: (BlShortcutWithAction new + name: 'Move cursor right or focus next filter'; + combination: (BlKeyCombination arrowRight); + action: [ :anEvent | + self onTextEditorArrowRightEvent: anEvent ]); + addShortcut: (BlShortcutWithAction new + name: 'Focus previous filter'; + combination: (BlKeyCombination shiftTab); + action: [ :anEvent | + self onTextEditorShiftTabEvent: anEvent ]); + addShortcut: (BlShortcutWithAction new + name: 'Focus next filter'; + combination: (BlKeyCombination tab); + action: [ :anEvent | + self onTextEditorTabEvent: anEvent ]); + addShortcut: (BlShortcutWithAction new + name: 'Delete text or focus current filter'; + combination: (BlKeyCombination backspace); + action: [ :anEvent | + self onTextEditorDeleteEvent: anEvent ]); + when: BlFocusInEvent do: [ :anEvent | + anEvent currentTarget selecter all select ]; + when: BlFocusOutEvent do: [ :anEvent | + anEvent currentTarget deselecter all deselect. + anEvent isDueToRemoval ifFalse: [ + self onTextEditorModificationAccepted: anEvent ] ] +] + +{ #category : #initialization } +GtFilterTextSettingsElement >> initializeEventHandlers [ + self when: GtFilterEditWish do: [ :anEvent | self onEditWish: anEvent ]. + self + when: GtFilterFocusFirstPartWish + do: [ :anEvent | self onFocusFirstPartWish: anEvent ] +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onEditWish: anEvent [ + anEvent consumed: true. + editorElement requestFocus +] + +{ #category : #'api - filter view model' } +GtFilterTextSettingsElement >> onFilterViewModelChanged [ + super onFilterViewModelChanged. + + self updateEditorElement +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onFocusFirstPartWish: anEvent [ + anEvent consumed: true. + editorElement requestFocus +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onInputFilterChanged: anAnnouncement [ + BlTaskAction + enqueueElement: self + action: [ self updateEditorElementInputFilter ] +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onTextChanged: anAnnouncement [ + BlTaskAction + enqueueElement: self + action: [ self updateEditorElementText ] +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onTextEditorArrowLeftEvent: anEvent [ + | anElement anEditor | + anElement := anEvent currentTarget. + anEditor := anElement editor. + anEvent consumed: true. + (anEditor cursors positions includes: 0) + ifTrue: [ self fireEvent: GtFilterFocusWholeWish new. + ^ self ]. + + anEditor selection isEmpty + ifTrue: [ anEditor navigator + moveLeft; + apply ] + ifFalse: [ anEditor deselecter + all; + deselect ] +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onTextEditorArrowRightEvent: anEvent [ + | anElement anEditor | + anElement := anEvent currentTarget. + anEditor := anElement editor. + anEvent consumed: true. + (anEditor cursors positions includes: anEditor text size) + ifTrue: [ self fireEvent: GtFilterFocusNextPartWish new. + ^ self ]. + + anEditor selection isEmpty + ifTrue: [ anEditor navigator + moveRight; + apply ] + ifFalse: [ anEditor deselecter + all; + deselect ] +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onTextEditorDeleteEvent: anEvent [ + | anElement anEditor | + anElement := anEvent currentTarget. + anEditor := anElement editor. + anEvent consumed: true. + (anEditor cursors positions includes: 0) + ifTrue: [ self fireEvent: GtFilterFocusWholeWish new. + ^ self ]. + + anEditor deleter + selected; + oneBeforeCursorIfOutsideOfSelection; + surroundings: anEditor surroundMap; + delete +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onTextEditorModificationAccepted: anEvent [ + self hasFilterViewModel ifFalse: [ ^ self ]. + anEvent consumed: true. + self filterViewModel + text: (editorElement text copy trimmer + bothSeparators; + trimmed) +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onTextEditorShiftTabEvent: anEvent [ + anEvent consumed: true. + self fireEvent: GtFilterFocusWholeWish new +] + +{ #category : #'event handling' } +GtFilterTextSettingsElement >> onTextEditorTabEvent: anEvent [ + anEvent consumed: true. + self fireEvent: GtFilterFocusNextPartWish new +] + +{ #category : #'api - filter view model' } +GtFilterTextSettingsElement >> subscribeToFilterViewModel [ + super subscribeToFilterViewModel. + + self filterViewModel weak + when: GtFilterTextViewModelTextChanged + send: #onTextChanged: + to: self; + when: GtFilterTextViewModelInputFilterChanged + send: #onInputFilterChanged: + to: self +] + +{ #category : #'private - updating' } +GtFilterTextSettingsElement >> updateEditorCompletionController [ + completionController ifNotNil: [ :aController | + completionController := nil. + aController uninstall ]. + + self filterViewModel completionDo: [ :aStrategy | + completionController := GtCompletionController on: editorElement strategy: aStrategy. + completionController install ] +] + +{ #category : #'private - updating' } +GtFilterTextSettingsElement >> updateEditorElement [ + self updateEditorElementInputFilter. + self updateEditorElementText. + self updateEditorCompletionController. +] + +{ #category : #'private - updating' } +GtFilterTextSettingsElement >> updateEditorElementInputFilter [ + (editorElement editor inputFilter = self filterViewModel inputFilter) + ifTrue: [ ^ self ]. + + editorElement inputFilter: self filterViewModel inputFilter +] + +{ #category : #'private - updating' } +GtFilterTextSettingsElement >> updateEditorElementText [ + | aText aTrimmedText | + aText := self filterViewModel text. + (editorElement text equals: aText) ifTrue: [ ^ self ]. + + aTrimmedText := aText trimmer + bothSeparators; + trimmed. + editorElement text: aTrimmedText +] diff --git a/src/GToolkit-Coder-UI/GtFilterTextViewModel.class.st b/src/GToolkit-Coder-UI/GtFilterTextViewModel.class.st new file mode 100644 index 000000000..49b3f001a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTextViewModel.class.st @@ -0,0 +1,60 @@ +Class { + #name : #GtFilterTextViewModel, + #superclass : #GtFilterViewModel, + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #accessing } +GtFilterTextViewModel >> completionDo: aBlock [ + self hasFilterModel ifFalse: [ ^ self ]. + self filterModel completionDo: aBlock +] + +{ #category : #accessing } +GtFilterTextViewModel >> filterElementClass [ + ^ GtFilterTextSettingsElement +] + +{ #category : #testing } +GtFilterTextViewModel >> inputFilter [ + + ^ self filterModel inputFilter +] + +{ #category : #'event handling' } +GtFilterTextViewModel >> onInputFilterChanged: anAnnouncement [ + self + announce: (GtFilterTextViewModelInputFilterChanged new + viewModel: self; + inputFilter: anAnnouncement inputFilter) +] + +{ #category : #'event handling' } +GtFilterTextViewModel >> onTextChanged: anAnnouncement [ + self + announce: (GtFilterTextViewModelTextChanged new + viewModel: self; + text: anAnnouncement text) +] + +{ #category : #'api - filter model' } +GtFilterTextViewModel >> subscribeToFilterModel [ + super subscribeToFilterModel. + self filterModel weak + when: GtFilterTextModelTextChanged + send: #onTextChanged: + to: self; + when: GtFilterTextModelInputFilterChanged + send: #onInputFilterChanged: + to: self +] + +{ #category : #testing } +GtFilterTextViewModel >> text [ + ^ self filterModel text +] + +{ #category : #testing } +GtFilterTextViewModel >> text: aText [ + self filterModel text: aText +] diff --git a/src/GToolkit-Coder-UI/GtFilterTextViewModelInputFilterChanged.class.st b/src/GToolkit-Coder-UI/GtFilterTextViewModelInputFilterChanged.class.st new file mode 100644 index 000000000..4f27b197d --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTextViewModelInputFilterChanged.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFilterTextViewModelInputFilterChanged, + #superclass : #GtFilterViewModelAnnouncement, + #instVars : [ + 'inputFilter' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterTextViewModelInputFilterChanged >> inputFilter [ + ^ inputFilter +] + +{ #category : #accessing } +GtFilterTextViewModelInputFilterChanged >> inputFilter: anObject [ + inputFilter := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterTextViewModelTextChanged.class.st b/src/GToolkit-Coder-UI/GtFilterTextViewModelTextChanged.class.st new file mode 100644 index 000000000..f12f490e5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterTextViewModelTextChanged.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFilterTextViewModelTextChanged, + #superclass : #GtFilterViewModelAnnouncement, + #instVars : [ + 'text' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterTextViewModelTextChanged >> text [ + ^ text +] + +{ #category : #accessing } +GtFilterTextViewModelTextChanged >> text: anObject [ + text := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterToggleBackgroundAptitude.class.st b/src/GToolkit-Coder-UI/GtFilterToggleBackgroundAptitude.class.st new file mode 100644 index 000000000..32bbd2f48 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterToggleBackgroundAptitude.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtFilterToggleBackgroundAptitude, + #superclass : #BrToggleAptitude, + #category : #'GToolkit-Coder-UI-Filters - Support' +} + +{ #category : #initialization } +GtFilterToggleBackgroundAptitude >> initialize [ + super initialize. + self + activated: [ :aStyle | + aStyle background: self theme button defaultBorderColor lighter. + aStyle + border: (BlBorder paint: Color white width: 1) ] +] diff --git a/src/GToolkit-Coder-UI/GtFilterToggleGroupAptitude.class.st b/src/GToolkit-Coder-UI/GtFilterToggleGroupAptitude.class.st new file mode 100644 index 000000000..b3abc92b9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterToggleGroupAptitude.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtFilterToggleGroupAptitude, + #superclass : #BrToggleGroupAptitude, + #category : #'GToolkit-Coder-UI-Filters - Support' +} + +{ #category : #initialization } +GtFilterToggleGroupAptitude >> initialize [ + super initialize. + self + addChangeProperty: #(widget layout) + with: [ BlLinearLayout horizontal cellSpacing: 0 ]. + self + addChangeProperty: #(widget geometry) + with: [ BlRoundedRectangleGeometry cornerRadius: 3 ]. + self + addChangeProperty: #(widget background) + with: + [ self theme default contentBackground ]. +] diff --git a/src/GToolkit-Coder-UI/GtFilterToggleModel.class.st b/src/GToolkit-Coder-UI/GtFilterToggleModel.class.st new file mode 100644 index 000000000..f128903d6 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterToggleModel.class.st @@ -0,0 +1,11 @@ +Class { + #name : #GtFilterToggleModel, + #superclass : #GtFilterSelectableItemModel, + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterToggleModel >> filterViewModelClass [ + + ^ GtFilterToggleViewModel +] diff --git a/src/GToolkit-Coder-UI/GtFilterToggleSettingsElement.class.st b/src/GToolkit-Coder-UI/GtFilterToggleSettingsElement.class.st new file mode 100644 index 000000000..de7081475 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterToggleSettingsElement.class.st @@ -0,0 +1,261 @@ +Class { + #name : #GtFilterToggleSettingsElement, + #superclass : #GtFilterSettingsElement, + #instVars : [ + 'toggleGroupElement' + ], + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #initialization } +GtFilterToggleSettingsElement >> initialize [ + super initialize. + self initializeToggleGroupElement. + + self addChild: toggleGroupElement as: #toggles. + self initializeEventHandlers. + self initializeShortcuts +] + +{ #category : #initialization } +GtFilterToggleSettingsElement >> initializeEventHandlers [ + self when: GtFilterEditWish do: [ :anEvent | self onEditWish: anEvent ]. + self + when: GtFilterFocusFirstPartWish + do: [ :anEvent | self onFocusFirstPartWish: anEvent ] +] + +{ #category : #initialization } +GtFilterToggleSettingsElement >> initializeShortcuts [ +] + +{ #category : #initialization } +GtFilterToggleSettingsElement >> initializeToggleGroupElement [ + toggleGroupElement := BrToggleGroup new + aptitude: GtFilterToggleGroupAptitude new; + when: BrToggleActivatedEvent + do: [ :anEvent | self onToggleActivatedEvent: anEvent ]; + withAsyncFutureDo: [ :anElementFuture | + anElementFuture + executionConfiguration: (self class futureExecutionConfiguration); + whenPending: [ :theToggleGroup | + self + updateToggleElement: theToggleGroup + items: { GtFilterModelComputingItem default } ]; + whenError: [ :theToggleGroup :anError | + self + updateToggleElement: theToggleGroup + items: { GtFilterModelExceptionItem freeze: anError } ]; + whenSuccess: [ :theToggleGroup :aCollection | + self updateToggleElement: theToggleGroup items: aCollection ] ] +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> interruptToggleGroupFuture [ + toggleGroupElement withAsyncFutureDo: [ :anElementFuture | + anElementFuture + cancelHard; + interrupt ] +] + +{ #category : #'private - updating' } +GtFilterToggleSettingsElement >> newAptitudeForItem: anItem [ + | anAptitude | + anAptitude := anItem + ifIconAndLabel: [ BrGlamorousToggleWithIconAndLabelAptitude new ] + ifIcon: [ BrGlamorousToggleWithIconAptitude new ] + ifLabel: [ BrGlamorousToggleWithLabelAptitude new ]. + + anAptitude - BrGlamorousToggleBackgroundAptitude. + anAptitude + GtFilterToggleBackgroundAptitude. + + ^ anAptitude +] + +{ #category : #'private - updating' } +GtFilterToggleSettingsElement >> newToggleForItem: aFilterItem index: anIndex [ + | aToggle | + aToggle := BrToggle new. + aToggle id: (GtFilterItemId indexed: anIndex). + aToggle beFocusable. + aToggle beSmallSize. + aToggle viewModel model: aFilterItem. + aFilterItem == self filterViewModel selectedItem ifTrue: [ aToggle activate ]. + aFilterItem ifIcon: [ :anIcon | aToggle icon: anIcon ]. + aFilterItem ifLabel: [ :aLabel | aToggle label: aLabel ]. + aToggle aptitude: (self newAptitudeForItem: aFilterItem). + aToggle addAptitude: (BrStyleCommonAptitude new + // #label; + default: [ :aStyle | aStyle foreground: self theme button defaultTextColor ]; + focused: [ :aStyle | aStyle foreground: self theme button hoveredTextColor ]). + aToggle + when: BlMouseEnterEvent + do: [ :anEvent | self onToggleMouseEnterEvent: anEvent ]. + + self registerNavigationShortcutsForToggle: aToggle. + ^ aToggle +] + +{ #category : #'private - updating' } +GtFilterToggleSettingsElement >> onEditWish: anEvent [ + anEvent consumed: true. + self requestToggleFocus +] + +{ #category : #'api - filter view model' } +GtFilterToggleSettingsElement >> onFilterViewModelChanged [ + super onFilterViewModelChanged. + self updateElement +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> onFiltersViewModelItemRemoved: anAnnouncement [ + BlTaskAction enqueueElement: self action: [ self interruptToggleGroupFuture ] +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> onFocusFirstPartWish: anEvent [ + anEvent consumed: true. + self requestToggleFocus +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> onItemsChanged: anAnnouncement [ + BlTaskAction enqueueElement: self action: [ self updateElement ] +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> onSelectedItemChanged: anAnnouncement [ + BlTaskAction enqueueElement: self action: [ self updateActiveToggle ] +] + +{ #category : #initialization } +GtFilterToggleSettingsElement >> onToggleActivatedEvent: anEvent [ + self hasFilterViewModel ifFalse: [ ^ self ]. + anEvent toggle model ifNotNil: [ :anItem | + self filterViewModel selectedItem: anItem ] +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> onToggleEnterShortcutEvent: anEvent [ + self hasFilterViewModel ifFalse: [ ^ self ]. + anEvent consumed: true. + anEvent currentTarget activate. +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> onToggleMouseEnterEvent: anEvent [ + "Change focused if another toggle is focused." + + | aHoveredToggle aResult | + anEvent consumed: true. + aResult := toggleGroupElement viewModel toggles + anySatisfy: [ :eachToggle | + | isToggleFocused | + isToggleFocused := false. + eachToggle widgetDo: [ :aWidget | isToggleFocused := aWidget isFocused ]. + isToggleFocused ]. + aResult ifFalse: [ ^ self ]. + + aHoveredToggle := anEvent currentTarget. + aHoveredToggle requestFocus +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> onToggleMoveLeftShortcutEvent: anEvent [ + anEvent consumed: true. + BlFocusFinder new + left; + root: toggleGroupElement; + referenceElement: anEvent currentTarget; + nextFocusDo: #requestFocus + ifNone: [ self fireEvent: GtFilterFocusWholeWish new ] +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> onToggleMoveRightShortcutEvent: anEvent [ + anEvent consumed: true. + BlFocusFinder new + right; + root: toggleGroupElement; + referenceElement: anEvent currentTarget; + nextFocusDo: #requestFocus + ifNone: [ self fireEvent: GtFilterFocusNextPartWish new ] +] + +{ #category : #'private - updating' } +GtFilterToggleSettingsElement >> registerNavigationShortcutsForToggle: aToggle [ + aToggle + addShortcut: (BlShortcutWithAction new + name: 'Select filter item'; + description: 'Accept new filter item and store it into the filter model'; + combination: BlKeyCombination enter; + action: [ :anEvent | self onToggleEnterShortcutEvent: anEvent ]); + addShortcut: (BlShortcutWithAction new + name: 'Move previous filter item'; + combination: (BlKeyCombination arrowLeft or: BlKeyCombination shiftTab); + action: [ :anEvent | self onToggleMoveLeftShortcutEvent: anEvent ]); + addShortcut: (BlShortcutWithAction new + name: 'Move next filter item'; + combination: (BlKeyCombination arrowRight or: BlKeyCombination tab); + action: [ :anEvent | self onToggleMoveRightShortcutEvent: anEvent ]) +] + +{ #category : #'event handling' } +GtFilterToggleSettingsElement >> requestToggleFocus [ + toggleGroupElement viewModel hasActivatedToggle + ifTrue: [ toggleGroupElement activatedToggle requestFocus ] + ifFalse: [ toggleGroupElement viewModel toggles + ifNotEmpty: [ :aCollection | aCollection first widgetDo: [ :aWidget | + aWidget requestFocus ] ] ] +] + +{ #category : #'api - filter view model' } +GtFilterToggleSettingsElement >> subscribeToFilterViewModel [ + super subscribeToFilterViewModel. + self filterViewModel weak + when: GtFilterViewModelItemsChanged + send: #onItemsChanged: + to: self; + when: GtFilterViewModelSelectedItemChanged + send: #onSelectedItemChanged: + to: self; + when: GtFiltersViewModelItemRemoved + send: #onFiltersViewModelItemRemoved: + to: self. +] + +{ #category : #'private - updating' } +GtFilterToggleSettingsElement >> updateActiveToggle [ + | aSelectedItem | + aSelectedItem := self filterViewModel selectedItem. + toggleGroupElement activatedToggle viewModel model == aSelectedItem + ifTrue: [ ^ self ]. + toggleGroupElement viewModel toggles + do: [ :eachToggle | + eachToggle viewModel model == aSelectedItem + ifTrue: [ eachToggle activate ] ] +] + +{ #category : #'private - updating' } +GtFilterToggleSettingsElement >> updateElement [ + self updateToggles +] + +{ #category : #initialization } +GtFilterToggleSettingsElement >> updateToggleElement: aToggleGroup items: aCollection [ + aToggleGroup removeToggles. + + aCollection doWithIndex: [ :eachItem :anIndex | + | aToggle | + aToggle := self newToggleForItem: eachItem index: anIndex. + aToggleGroup addToggle: aToggle ] +] + +{ #category : #'private - updating' } +GtFilterToggleSettingsElement >> updateToggles [ + self hasFilterViewModel ifFalse: [ ^ self ]. + + toggleGroupElement withAsyncFutureDo: [ :anElementFuture | + anElementFuture future: self filterViewModel itemsFuture asAsyncFuture ]. +] diff --git a/src/GToolkit-Coder-UI/GtFilterToggleViewModel.class.st b/src/GToolkit-Coder-UI/GtFilterToggleViewModel.class.st new file mode 100644 index 000000000..4f399a695 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterToggleViewModel.class.st @@ -0,0 +1,11 @@ +Class { + #name : #GtFilterToggleViewModel, + #superclass : #GtFilterSelectableItemViewModel, + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #accessing } +GtFilterToggleViewModel >> filterElementClass [ + + ^ GtFilterToggleSettingsElement +] diff --git a/src/GToolkit-Coder-UI/GtFilterViewModel.class.st b/src/GToolkit-Coder-UI/GtFilterViewModel.class.st new file mode 100644 index 000000000..57865dd9e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterViewModel.class.st @@ -0,0 +1,96 @@ +Class { + #name : #GtFilterViewModel, + #superclass : #Object, + #traits : 'TGtWithFilterModel + TGtAnnouncer', + #classTraits : 'TGtWithFilterModel classTrait + TGtAnnouncer classTrait', + #instVars : [ + 'announcer' + ], + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #announcer } +GtFilterViewModel >> announcer [ + + ^ announcer ifNil: [ announcer := Announcer new ] +] + +{ #category : #converting } +GtFilterViewModel >> asFilterLabeledElement [ + + ^ self filterLabelElementClass new filterViewModel: self +] + +{ #category : #converting } +GtFilterViewModel >> asUserParametersElement [ + + ^ self filterElementClass new filterViewModel: self +] + +{ #category : #converting } +GtFilterViewModel >> asUserSettingsElement [ + + ^ self filterElementClass new filterViewModel: self +] + +{ #category : #accessing } +GtFilterViewModel >> filterElementClass [ + + ^ self subclassResponsibility +] + +{ #category : #accessing } +GtFilterViewModel >> filterLabelElementClass [ + + ^ self hasFilterModel + ifTrue: [ self filterModel filterLabelElementClass ] + ifFalse: [ GtFilterPrefixLabelElement ] +] + +{ #category : #accessing } +GtFilterViewModel >> label [ + + ^ self filterModel label +] + +{ #category : #'event handling' } +GtFilterViewModel >> onLabelChanged: anAnnouncement [ + self + announce: (GtFilterViewModelLabelChanged new + viewModel: self; + label: anAnnouncement label) +] + +{ #category : #accessing } +GtFilterViewModel >> parameters [ + + ^ #() +] + +{ #category : #printing } +GtFilterViewModel >> printOn: aStream [ + super printOn: aStream. + + self hasFilterModel ifFalse: [ ^ self ]. + aStream nextPut: $(. + self filterModel printDetailsOn: aStream. + aStream nextPut: $) +] + +{ #category : #accessing } +GtFilterViewModel >> selectedValue [ + ^ self filterModel selectedValue +] + +{ #category : #'api - filter model' } +GtFilterViewModel >> subscribeToFilterModel [ + self filterModel weak + when: GtFilterModelLabelChanged + send: #onLabelChanged: + to: self +] + +{ #category : #'api - filter model' } +GtFilterViewModel >> unsubscribeFromFilterModel [ + self filterModel unsubscribe: self +] diff --git a/src/GToolkit-Coder-UI/GtFilterViewModelAnnouncement.class.st b/src/GToolkit-Coder-UI/GtFilterViewModelAnnouncement.class.st new file mode 100644 index 000000000..bd073fe9f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterViewModelAnnouncement.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFilterViewModelAnnouncement, + #superclass : #Announcement, + #instVars : [ + 'viewModel' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterViewModelAnnouncement >> viewModel [ + ^ viewModel +] + +{ #category : #accessing } +GtFilterViewModelAnnouncement >> viewModel: anObject [ + viewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterViewModelItemsChanged.class.st b/src/GToolkit-Coder-UI/GtFilterViewModelItemsChanged.class.st new file mode 100644 index 000000000..9dee42b41 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterViewModelItemsChanged.class.st @@ -0,0 +1,23 @@ +Class { + #name : #GtFilterViewModelItemsChanged, + #superclass : #GtFilterViewModelAnnouncement, + #instVars : [ + 'itemsBuilder' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterViewModelItemsChanged >> items [ + ^ itemsBuilder create +] + +{ #category : #accessing } +GtFilterViewModelItemsChanged >> itemsBuilder [ + ^ itemsBuilder +] + +{ #category : #accessing } +GtFilterViewModelItemsChanged >> itemsBuilder: aBuilder [ + itemsBuilder := aBuilder +] diff --git a/src/GToolkit-Coder-UI/GtFilterViewModelLabelChanged.class.st b/src/GToolkit-Coder-UI/GtFilterViewModelLabelChanged.class.st new file mode 100644 index 000000000..3bc31e835 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterViewModelLabelChanged.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFilterViewModelLabelChanged, + #superclass : #GtFilterViewModelAnnouncement, + #instVars : [ + 'label' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterViewModelLabelChanged >> label [ + ^ label +] + +{ #category : #accessing } +GtFilterViewModelLabelChanged >> label: anObject [ + label := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterViewModelSelectedItemChanged.class.st b/src/GToolkit-Coder-UI/GtFilterViewModelSelectedItemChanged.class.st new file mode 100644 index 000000000..36ad4bed7 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterViewModelSelectedItemChanged.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFilterViewModelSelectedItemChanged, + #superclass : #GtFilterViewModelAnnouncement, + #instVars : [ + 'selectedItem' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFilterViewModelSelectedItemChanged >> selectedItem [ + ^ selectedItem +] + +{ #category : #accessing } +GtFilterViewModelSelectedItemChanged >> selectedItem: anObject [ + selectedItem := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFilterVirtualEmptyModel.class.st b/src/GToolkit-Coder-UI/GtFilterVirtualEmptyModel.class.st new file mode 100644 index 000000000..ffc7e52b3 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterVirtualEmptyModel.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtFilterVirtualEmptyModel, + #superclass : #GtFilterModel, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #classInstVars : [ + 'uniqueInstance' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterVirtualEmptyModel >> isVirtualFilterModel [ + ^ true +] + +{ #category : #accessing } +GtFilterVirtualEmptyModel >> name [ + ^ 'empty' +] diff --git a/src/GToolkit-Coder-UI/GtFilterVirtualExceptionModel.class.st b/src/GToolkit-Coder-UI/GtFilterVirtualExceptionModel.class.st new file mode 100644 index 000000000..b6697c3f2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterVirtualExceptionModel.class.st @@ -0,0 +1,36 @@ +Class { + #name : #GtFilterVirtualExceptionModel, + #superclass : #GtFilterModel, + #instVars : [ + 'exception' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +GtFilterVirtualExceptionModel >> exception [ + ^ exception +] + +{ #category : #accessing } +GtFilterVirtualExceptionModel >> exception: anException [ + | anExceptionCopy | + anExceptionCopy := GtSystemUtility freeze: anException. + + exception := anExceptionCopy +] + +{ #category : #testing } +GtFilterVirtualExceptionModel >> isExceptionFilterModel [ + ^ true +] + +{ #category : #testing } +GtFilterVirtualExceptionModel >> isVirtualFilterModel [ + ^ true +] + +{ #category : #accessing } +GtFilterVirtualExceptionModel >> name [ + ^ 'exception' +] diff --git a/src/GToolkit-Coder-UI/GtFilterVirtualWaitingModel.class.st b/src/GToolkit-Coder-UI/GtFilterVirtualWaitingModel.class.st new file mode 100644 index 000000000..efdd9fc52 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFilterVirtualWaitingModel.class.st @@ -0,0 +1,17 @@ +Class { + #name : #GtFilterVirtualWaitingModel, + #superclass : #GtFilterModel, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #testing } +GtFilterVirtualWaitingModel >> isVirtualFilterModel [ + ^ true +] + +{ #category : #accessing } +GtFilterVirtualWaitingModel >> name [ + ^ 'rendering...' +] diff --git a/src/GToolkit-Coder-UI/GtFiltersAddButtonId.class.st b/src/GToolkit-Coder-UI/GtFiltersAddButtonId.class.st new file mode 100644 index 000000000..caa59cc59 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersAddButtonId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFiltersAddButtonId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Filters' +} + +{ #category : #converting } +GtFiltersAddButtonId >> asSymbol [ + ^ #'filter--add-button' +] diff --git a/src/GToolkit-Coder-UI/GtFiltersAddId.class.st b/src/GToolkit-Coder-UI/GtFiltersAddId.class.st new file mode 100644 index 000000000..08e395983 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersAddId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtFiltersAddId, + #superclass : #GtFilterElementId, + #category : #'GToolkit-Coder-UI-Filters - Widgets' +} + +{ #category : #converting } +GtFiltersAddId >> asSymbol [ + ^ #'filter--add' +] diff --git a/src/GToolkit-Coder-UI/GtFiltersChangedEvent.class.st b/src/GToolkit-Coder-UI/GtFiltersChangedEvent.class.st index f1d469053..a3bafafa9 100644 --- a/src/GToolkit-Coder-UI/GtFiltersChangedEvent.class.st +++ b/src/GToolkit-Coder-UI/GtFiltersChangedEvent.class.st @@ -2,7 +2,8 @@ Class { #name : #GtFiltersChangedEvent, #superclass : #BlEvent, #instVars : [ - 'filterElement' + 'filterElement', + 'reason' ], #category : #'GToolkit-Coder-UI-Filters' } @@ -16,3 +17,15 @@ GtFiltersChangedEvent >> filterElement [ GtFiltersChangedEvent >> filterElement: anElement [ filterElement := anElement ] + +{ #category : #accessing } +GtFiltersChangedEvent >> reason [ + + ^ reason +] + +{ #category : #accessing } +GtFiltersChangedEvent >> reason: anObject [ + + reason := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFiltersElement.class.st b/src/GToolkit-Coder-UI/GtFiltersElement.class.st index 07fccb762..2fb5093bf 100644 --- a/src/GToolkit-Coder-UI/GtFiltersElement.class.st +++ b/src/GToolkit-Coder-UI/GtFiltersElement.class.st @@ -1,14 +1,6 @@ " I am a {{gtClass:BlElement}}. -I build a filter in {{gtClass:GtCoderElement}}, see {{gtMethod:GtMethodsCoderElement>>#buildFilter|label=#selector}}. -1. # Element Example - -The following example shows how I look like: {{gtExample:GtFiltersElementExamples>>#filtersElement|codeExpanded=false|previewExpanded=true|previewShow=#gtLiveFor:|previewHeight=70}} -1. # Coder Example - -The following example shows a coder with default filters element that you can change: {{gtExample:GtFiltersElementExamples>>#coderWithFiltersElement|codeExpanded=false|previewExpanded=true}} - - +I build a filter in {{gtClass:GtCoderElement}}, see {{gtMethod:GtPharoMethodsCoderElement>>#buildFilter|label=#selector}}. " Class { #name : #GtFiltersElement, @@ -19,77 +11,115 @@ Class { #category : #'GToolkit-Coder-UI-Filters' } -{ #category : #accessing } -GtFiltersElement >> addEmptyDefault [ - | filterElement defaultDescriptor | - self childrenCount = 1 - ifFalse: [ ^ self ]. - defaultDescriptor := self descriptors - detect: [ :each | each showAsDefaultWhenEmpty ] - ifNone: [ ^ self ]. - filterElement := GtFilterTagElement new. - filterElement filter: self. - filterElement descriptor: defaultDescriptor. - filterElement makeDefaultFilter. - self addChild: filterElement at: self childrenCount +{ #category : #private } +GtFiltersElement >> addAllDefaults [ + | filterElement defaultDescriptors | + + defaultDescriptors := self descriptors select: [ :each | each showAsDefaultWhenEmpty ]. + defaultDescriptors + do: [ :defaultDescriptor | + filterElement := self createFilterTagFor: defaultDescriptor. + filterElement makeDefaultFilter. + + self addChild: filterElement at: self childrenCount ] ] { #category : #accessing } GtFiltersElement >> addFilterForDescriptor: aFilterDescriptor andValue: aString [ | element | - element := GtFilterTagElement new. - element filter: self. - element descriptor: aFilterDescriptor. + element := self createFilterTagFor: aFilterDescriptor. aFilterDescriptor valueIsRequired ifTrue: [ element valueString: aString ]. self addChild: element at: self childrenCount ] -{ #category : #'api - actions' } +{ #category : #private } GtFiltersElement >> applyFilters [ - self fireEvent: (GtFiltersChangedEvent new filterElement: self). - self addEmptyDefault + self applyFiltersDueTo: #unknown ] -{ #category : #accessing } +{ #category : #private } +GtFiltersElement >> applyFiltersDueTo: aReason [ + self fireEvent: (GtFiltersChangedEvent new filterElement: self; reason: aReason) +] + +{ #category : #private } +GtFiltersElement >> areAllDefaults [ + ^ self childrenCount > 1 + and: [ (1 to: self childrenCount - 1) + allSatisfy: [ :index | (self childAt: index) isDefaultAllFilter ] ] +] + +{ #category : #'api - filters' } +GtFiltersElement >> asIntersectionFilter [ + | compositeFilter | + + compositeFilter := nil. + + self + filtersDo: [ :descriptor :value | + compositeFilter + ifNil: [ compositeFilter := descriptor newFilterWithValue: value ] + ifNotNil: [ compositeFilter := compositeFilter & (descriptor newFilterWithValue: value) ] ]. + ^ compositeFilter +] + +{ #category : #'api - filters' } GtFiltersElement >> buildFilters: aBlock [ self clearFilters. - aBlock value. - self addEmptyDefault + aBlock value ] -{ #category : #accessing } +{ #category : #'api - filters' } GtFiltersElement >> clearFilters [ + "Remove all filters" + [ self childrenCount > 1 ] whileTrue: [ self removeChildAt: 1 ] ] -{ #category : #'private - actions' } +{ #category : #'private - instance creation' } +GtFiltersElement >> createFilterTagFor: aFilterDescription [ + ^ GtFilterTagElement new + descriptor: aFilterDescription; + margin: (BlInsets all: 2); + filter: self +] + +{ #category : #private } GtFiltersElement >> createNewTag [ | tag | - self descriptors isEmptyOrNil ifTrue: [ ^ self ]. - (self childrenCount > 1 - and: [ (self childAt: self childrenCount - 1) isDefaultAllFilter ]) - ifTrue: [ self removeChildAt: self childrenCount - 1 ]. - tag := GtFilterTagElement new. - tag filter: self. - tag descriptor: self descriptors first. - self addChild: tag at: self childrenCount. - tag activateEditor + self descriptors + ifNotEmpty: [ :items | + "self areAllDefaults ifTrue: [ self clearFilters ]." + tag := self createFilterTagFor: items first. + self addChild: tag at: self childrenCount. + tag activateEditor ] +] + +{ #category : #'api - filters' } +GtFiltersElement >> currentFilters [ + ^ Array streamContents: [ :aStream | self filtersDo: [ :eachFilter :eachValue | aStream nextPut: (eachFilter -> eachValue) ] ] ] { #category : #accessing } GtFiltersElement >> descriptors [ - ^ descriptors + ^ (descriptors isBlock ifTrue: [ descriptors value ] ifFalse: [ descriptors ]) + ifNil: [ #() ] ] { #category : #accessing } GtFiltersElement >> descriptors: aCollection [ descriptors := aCollection. - self addEmptyDefault + + BlFrameTelemetry + time: [ 'Add default filter tag' ] + during: [ self addAllDefaults ] ] -{ #category : #accessing } +{ #category : #'api - filters' } GtFiltersElement >> filtersDo: aBlock [ + "Iterate over all valid filters" + 1 to: self childrenCount - 1 do: [ :i | | filterTagElement | filterTagElement := self childAt: i. @@ -99,37 +129,43 @@ GtFiltersElement >> filtersDo: aBlock [ value: filterTagElement valueString ] ] ] +{ #category : #'gt - extensions' } +GtFiltersElement >> gtViewDefaultDescriptorsFor: aView [ + + + ^ aView columnedList + title: 'Descriptors'; + items: [ self descriptors ]; + column: 'Name' text: [ :aDescriptor | aDescriptor name ]; + column: 'Order' text: [ :aDescriptor | aDescriptor order ]; + column: 'Is Default' text: [ :aDescriptor | aDescriptor isDefault ]; + column: 'Is Required' text: [ :aDescriptor | aDescriptor valueIsRequired ]; + column: 'Empty Value' text: [ :aDescriptor | aDescriptor emptyDefaultValue ]; + column: 'Show Empty Default' text: [ :aDescriptor | aDescriptor showAsDefaultWhenEmpty ] +] + { #category : #initialization } GtFiltersElement >> initialize [ super initialize. self hMatchParent. self vFitContent. - self addChild: self newAddTagButton as: #newButton + self addChild: self newAddTagButton ] { #category : #'private - instance creation' } GtFiltersElement >> newAddTagButton [ ^ BrButton new - aptitude: - BrGlamorousButtonRectangularAptitude new - + BrGlamorousButtonIconAptitude new - + BrGlamorousButtonWithLabelTooltipAptitude new - + - (BrInteractiveCommonAptitude new - default: [ :aWidget | - aWidget + id: GtFiltersAddButtonId; + aptitude: BrGlamorousButtonRectangularAptitude new + BrGlamorousButtonIconAptitude new + + BrGlamorousButtonWithLabelTooltipAptitude2 new + + (BrStyleCommonAptitude new + default: [ :aStyle | + aStyle border: BlBorder empty; background: self theme status neutralBackgroundColor ]; - hovered: [ :aWidget | - aWidget background: - self theme status neutralBackgroundColor darker ]; - pressed: [ :aWidget | - aWidget - background: - self theme status neutralBackgroundColor darker darker ]; - focused: [ :aWidget | - aWidget - border: (BlBorder paint: self theme editor focusedBorderColor width: 1) ]); + hovered: [ :aStyle | aStyle background: self theme status neutralBackgroundColor darker ]; + pressed: [ :aStyle | aStyle background: self theme status neutralBackgroundColor darker darker ]; + focused: [ :aStyle | aStyle border: (BlBorder paint: self theme editor focusedBorderColor width: 1) ]); icon: BrGlamorousVectorIcons add; label: 'Add Filter'; margin: (BlInsets all: 2); @@ -137,8 +173,7 @@ GtFiltersElement >> newAddTagButton [ hExact: 20; constraintsDo: [ :c | c flow vertical alignCenter ]; action: [ :aButton | self createNewTag ]; - addShortcut: - (BlShortcutWithAction new - combination: BlKeyCombination return; + addShortcut: (BlShortcutWithAction new + combination: BlKeyCombination enter; action: [ :anEvent | self createNewTag ]) ] diff --git a/src/GToolkit-Coder-UI/GtFiltersItemsViewModel.class.st b/src/GToolkit-Coder-UI/GtFiltersItemsViewModel.class.st new file mode 100644 index 000000000..c0cd4f46d --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersItemsViewModel.class.st @@ -0,0 +1,90 @@ +Class { + #name : #GtFiltersItemsViewModel, + #superclass : #GtFiltersViewModel, + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #accessing } +GtFiltersItemsViewModel >> filtersElementClass [ + ^ GtFilterItemsElement +] + +{ #category : #accessing } +GtFiltersItemsViewModel >> items [ + + ^ self filtersModel items collect: [ :eachFilterModel | + self ensureFilterViewModelFor: eachFilterModel ] +] + +{ #category : #'api - filter model' } +GtFiltersItemsViewModel >> onItemAdded: anAnnouncement [ + | aViewModel | + aViewModel := self ensureFilterViewModelFor: anAnnouncement item. + self + announce: (GtFiltersViewModelItemAdded new + viewModel: self; + item: aViewModel). + aViewModel + announce: (GtFiltersViewModelItemAdded new + viewModel: self; + item: aViewModel) +] + +{ #category : #'api - filter model' } +GtFiltersItemsViewModel >> onItemRemoved: anAnnouncement [ + | aViewModel | + aViewModel := self removeOldFilterViewModelFor: anAnnouncement item. + aViewModel ifNil: [ ^ self ]. + self + announce: (GtFiltersViewModelItemRemoved new + viewModel: self; + item: aViewModel). + aViewModel + announce: (GtFiltersViewModelItemRemoved new + viewModel: self; + item: aViewModel) +] + +{ #category : #'event handling' } +GtFiltersItemsViewModel >> onItemsChanged: anAnnouncement [ + self removeOldFilterViewModelsFor: anAnnouncement items. + self + announce: (GtFiltersViewModelItemsChanged new + viewModel: self; + items: self items) +] + +{ #category : #'event handling' } +GtFiltersItemsViewModel >> removeOldFilterViewModelFor: aFilterModel [ + ^ filterViewModels removeKey: aFilterModel ifAbsent: [ nil ] +] + +{ #category : #'event handling' } +GtFiltersItemsViewModel >> removeOldFilterViewModelsFor: aCollectionOfFilterModels [ + "Remove filter view models of removed filter models. + New filter view models are added on #items call." + + | toRemove | + toRemove := filterViewModels keys + reject: [ :each | aCollectionOfFilterModels identityIncludes: each ]. + toRemove do: [ :eachFilterModel | self removeOldFilterViewModelFor: eachFilterModel ] +] + +{ #category : #'api - filter model' } +GtFiltersItemsViewModel >> subscribeToFiltersModel [ + self filtersModel weak + when: GtFiltersModelItemsChanged + send: #onItemsChanged: + to: self; + when: GtFiltersModelItemAdded + send: #onItemAdded: + to: self; + when: GtFiltersModelItemRemoved + send: #onItemRemoved: + to: self +] + +{ #category : #'api - filter model' } +GtFiltersItemsViewModel >> unsubscribeFromFiltersModel [ + self filtersModel unsubscribe: self +] diff --git a/src/GToolkit-Coder-UI/GtFiltersModel.class.st b/src/GToolkit-Coder-UI/GtFiltersModel.class.st new file mode 100644 index 000000000..1d9f826cd --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersModel.class.st @@ -0,0 +1,66 @@ +Class { + #name : #GtFiltersModel, + #superclass : #Object, + #traits : 'TGtAnnouncer', + #classTraits : 'TGtAnnouncer classTrait', + #instVars : [ + 'announcer', + 'availableFiltersBuilder' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'add / remove' } +GtFiltersModel >> addFilterModel: aFilterModel [ + self subclassResponsibility +] + +{ #category : #announcer } +GtFiltersModel >> announcer [ + + ^ announcer ifNil: [ announcer := Announcer new ] +] + +{ #category : #converting } +GtFiltersModel >> asFiltersElement [ + + ^ self asFiltersViewModel asFiltersElement +] + +{ #category : #converting } +GtFiltersModel >> asFiltersViewModel [ + + ^ self filtersViewModelClass new filtersModel: self +] + +{ #category : #accessing } +GtFiltersModel >> availableFilters [ + "Return a collection of available filters" + + + ^ availableFiltersBuilder ifNotNil: #availableFilters ifNil: [ #() ] +] + +{ #category : #accessing } +GtFiltersModel >> availableFiltersBuilder [ + ^ availableFiltersBuilder +] + +{ #category : #accessing } +GtFiltersModel >> availableFiltersBuilder: aBuilder [ + availableFiltersBuilder := aBuilder +] + +{ #category : #accessing } +GtFiltersModel >> filtersViewModelClass [ + + ^ self subclassResponsibility +] + +{ #category : #'gt - extensions' } +GtFiltersModel >> gtLiveFor: aView [ + + ^ aView explicit + title: 'Live'; + stencil: [ self asFiltersElement margin: (BlInsets all: 10) ] +] diff --git a/src/GToolkit-Coder-UI/GtFiltersModelAnnouncement.class.st b/src/GToolkit-Coder-UI/GtFiltersModelAnnouncement.class.st new file mode 100644 index 000000000..48d2cb888 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersModelAnnouncement.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFiltersModelAnnouncement, + #superclass : #Announcement, + #instVars : [ + 'model' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFiltersModelAnnouncement >> model [ + ^ model +] + +{ #category : #accessing } +GtFiltersModelAnnouncement >> model: anObject [ + model := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFiltersModelItemAdded.class.st b/src/GToolkit-Coder-UI/GtFiltersModelItemAdded.class.st new file mode 100644 index 000000000..04956181e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersModelItemAdded.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFiltersModelItemAdded, + #superclass : #GtFiltersModelAnnouncement, + #instVars : [ + 'item' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFiltersModelItemAdded >> item [ + ^ item +] + +{ #category : #accessing } +GtFiltersModelItemAdded >> item: anObject [ + item := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFiltersModelItemRemoved.class.st b/src/GToolkit-Coder-UI/GtFiltersModelItemRemoved.class.st new file mode 100644 index 000000000..162b39f9d --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersModelItemRemoved.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFiltersModelItemRemoved, + #superclass : #GtFiltersModelAnnouncement, + #instVars : [ + 'item' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFiltersModelItemRemoved >> item [ + ^ item +] + +{ #category : #accessing } +GtFiltersModelItemRemoved >> item: anObject [ + item := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFiltersModelItemsChanged.class.st b/src/GToolkit-Coder-UI/GtFiltersModelItemsChanged.class.st new file mode 100644 index 000000000..fbb2c7bc2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersModelItemsChanged.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFiltersModelItemsChanged, + #superclass : #GtFiltersModelAnnouncement, + #instVars : [ + 'items' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFiltersModelItemsChanged >> items [ + ^ items +] + +{ #category : #accessing } +GtFiltersModelItemsChanged >> items: anObject [ + items := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFiltersModelUpdated.class.st b/src/GToolkit-Coder-UI/GtFiltersModelUpdated.class.st new file mode 100644 index 000000000..4b30fe224 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersModelUpdated.class.st @@ -0,0 +1,10 @@ +" +I announce that a {{gtClass:GtFiltersModel}} changed. +I am announced only if an update changes search filter results. +In other words, I must not be announced if an added or removed filter model does not have a selected value. +" +Class { + #name : #GtFiltersModelUpdated, + #superclass : #GtFiltersModelAnnouncement, + #category : #'GToolkit-Coder-UI-Filters - Events' +} diff --git a/src/GToolkit-Coder-UI/GtFiltersViewModel.class.st b/src/GToolkit-Coder-UI/GtFiltersViewModel.class.st new file mode 100644 index 000000000..f4613d92b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersViewModel.class.st @@ -0,0 +1,69 @@ +Class { + #name : #GtFiltersViewModel, + #superclass : #Object, + #traits : 'TGtWithFiltersModel + TGtAnnouncer', + #classTraits : 'TGtWithFiltersModel classTrait + TGtAnnouncer classTrait', + #instVars : [ + 'announcer', + 'filterViewModels' + ], + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #'add / remove' } +GtFiltersViewModel >> addFilterModel: aFilterModel [ + self hasFiltersModel ifFalse: [ ^ self ]. + + ^ self filtersModel addFilterModel: aFilterModel +] + +{ #category : #announcer } +GtFiltersViewModel >> announcer [ + + ^ announcer ifNil: [ announcer := Announcer new ] +] + +{ #category : #converting } +GtFiltersViewModel >> asFiltersElement [ + ^ self filtersElementClass new filtersViewModel: self +] + +{ #category : #accessing } +GtFiltersViewModel >> availableFilters [ + "Return a collection of available filters" + + + ^ self filtersModel availableFilters +] + +{ #category : #private } +GtFiltersViewModel >> createFilterViewModelFor: aFilterModel [ + ^ aFilterModel asFilterViewModel +] + +{ #category : #private } +GtFiltersViewModel >> ensureFilterViewModelFor: aFilterModel [ + ^ filterViewModels + at: aFilterModel + ifPresent: [ :aFilterViewModel | aFilterViewModel ] + ifAbsentPut: [ self createFilterViewModelFor: aFilterModel ] +] + +{ #category : #accessing } +GtFiltersViewModel >> filtersElementClass [ + ^ self subclassResponsibility +] + +{ #category : #initialization } +GtFiltersViewModel >> initialize [ + super initialize. + filterViewModels := IdentityDictionary new +] + +{ #category : #'add / remove' } +GtFiltersViewModel >> removeFilterViewModel: aFilterViewModel [ + aFilterViewModel hasFilterModel ifFalse: [ ^ self ]. + + self hasFiltersModel ifFalse: [ ^ self ]. + self filtersModel removeFilterModel: aFilterViewModel filterModel +] diff --git a/src/GToolkit-Coder-UI/GtFiltersViewModelAnnouncement.class.st b/src/GToolkit-Coder-UI/GtFiltersViewModelAnnouncement.class.st new file mode 100644 index 000000000..80a58a941 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersViewModelAnnouncement.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFiltersViewModelAnnouncement, + #superclass : #Announcement, + #instVars : [ + 'viewModel' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFiltersViewModelAnnouncement >> viewModel [ + ^ viewModel +] + +{ #category : #accessing } +GtFiltersViewModelAnnouncement >> viewModel: anObject [ + viewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFiltersViewModelItemAdded.class.st b/src/GToolkit-Coder-UI/GtFiltersViewModelItemAdded.class.st new file mode 100644 index 000000000..da24108b2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersViewModelItemAdded.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFiltersViewModelItemAdded, + #superclass : #GtFiltersViewModelAnnouncement, + #instVars : [ + 'item' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFiltersViewModelItemAdded >> item [ + ^ item +] + +{ #category : #accessing } +GtFiltersViewModelItemAdded >> item: anObject [ + item := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFiltersViewModelItemRemoved.class.st b/src/GToolkit-Coder-UI/GtFiltersViewModelItemRemoved.class.st new file mode 100644 index 000000000..d373e737c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersViewModelItemRemoved.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFiltersViewModelItemRemoved, + #superclass : #GtFiltersViewModelAnnouncement, + #instVars : [ + 'item' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFiltersViewModelItemRemoved >> item [ + ^ item +] + +{ #category : #accessing } +GtFiltersViewModelItemRemoved >> item: anObject [ + item := anObject +] diff --git a/src/GToolkit-Coder-UI/GtFiltersViewModelItemsChanged.class.st b/src/GToolkit-Coder-UI/GtFiltersViewModelItemsChanged.class.st new file mode 100644 index 000000000..7e69dd3d8 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtFiltersViewModelItemsChanged.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtFiltersViewModelItemsChanged, + #superclass : #GtFiltersViewModelAnnouncement, + #instVars : [ + 'items' + ], + #category : #'GToolkit-Coder-UI-Filters - Events' +} + +{ #category : #accessing } +GtFiltersViewModelItemsChanged >> items [ + ^ items +] + +{ #category : #accessing } +GtFiltersViewModelItemsChanged >> items: anObject [ + items := anObject +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderContextPCRangeChanged.class.st b/src/GToolkit-Coder-UI/GtMethodCoderContextPCRangeChanged.class.st deleted file mode 100644 index 33b8926c6..000000000 --- a/src/GToolkit-Coder-UI/GtMethodCoderContextPCRangeChanged.class.st +++ /dev/null @@ -1,18 +0,0 @@ -Class { - #name : #GtMethodCoderContextPCRangeChanged, - #superclass : #Announcement, - #instVars : [ - 'pcRange' - ], - #category : #'GToolkit-Coder-UI-Coder - Method Events' -} - -{ #category : #accessing } -GtMethodCoderContextPCRangeChanged >> pcRange [ - ^ pcRange -] - -{ #category : #accessing } -GtMethodCoderContextPCRangeChanged >> pcRange: anObject [ - pcRange := anObject -] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderCopyActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderCopyActionId.class.st new file mode 100644 index 000000000..04b123254 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtMethodCoderCopyActionId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtMethodCoderCopyActionId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtMethodCoderCopyActionId >> asSymbol [ + ^ #'main-action--copy' +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderDebugExampleActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderDebugExampleActionId.class.st index b83ab61de..f4a02d0c4 100644 --- a/src/GToolkit-Coder-UI/GtMethodCoderDebugExampleActionId.class.st +++ b/src/GToolkit-Coder-UI/GtMethodCoderDebugExampleActionId.class.st @@ -6,7 +6,7 @@ A button to debug an example Class { #name : #GtMethodCoderDebugExampleActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtMethodCoderDiscardChangesActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderDiscardChangesActionId.class.st index 36443a415..fed352a23 100644 --- a/src/GToolkit-Coder-UI/GtMethodCoderDiscardChangesActionId.class.st +++ b/src/GToolkit-Coder-UI/GtMethodCoderDiscardChangesActionId.class.st @@ -6,7 +6,7 @@ A button to discard changes Class { #name : #GtMethodCoderDiscardChangesActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtMethodCoderExtractMethodContextMenuItemId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderExtractMethodContextMenuItemId.class.st index d63d0e0e3..b03a39b66 100644 --- a/src/GToolkit-Coder-UI/GtMethodCoderExtractMethodContextMenuItemId.class.st +++ b/src/GToolkit-Coder-UI/GtMethodCoderExtractMethodContextMenuItemId.class.st @@ -6,7 +6,7 @@ A context menu action to extract a method Class { #name : #GtMethodCoderExtractMethodContextMenuItemId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtMethodCoderExtractPlaygroundActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderExtractPlaygroundActionId.class.st new file mode 100644 index 000000000..ce6b281e5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtMethodCoderExtractPlaygroundActionId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtMethodCoderExtractPlaygroundActionId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtMethodCoderExtractPlaygroundActionId >> asSymbol [ + ^ #'context-action--extract-playground' +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderInspectActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderInspectActionId.class.st new file mode 100644 index 000000000..d83bc061a --- /dev/null +++ b/src/GToolkit-Coder-UI/GtMethodCoderInspectActionId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtMethodCoderInspectActionId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtMethodCoderInspectActionId >> asSymbol [ + ^ #'main-action--inspect' +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderOverriddenAmountId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderOverriddenAmountId.class.st new file mode 100644 index 000000000..311696883 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtMethodCoderOverriddenAmountId.class.st @@ -0,0 +1,13 @@ +" +A label or button that represents an amount of methods that override a given method +" +Class { + #name : #GtMethodCoderOverriddenAmountId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtMethodCoderOverriddenAmountId >> asSymbol [ + ^ #'method-coder--overridden-amount' +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderOverridesAmountId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderOverridesAmountId.class.st new file mode 100644 index 000000000..072d6d7c5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtMethodCoderOverridesAmountId.class.st @@ -0,0 +1,13 @@ +" +A label or button that represents an amount of methods that a given method overrides +" +Class { + #name : #GtMethodCoderOverridesAmountId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtMethodCoderOverridesAmountId >> asSymbol [ + ^ #'method-coder--overrides-amount' +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderOverridingOverriddenId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderOverridingOverriddenId.class.st new file mode 100644 index 000000000..dc789ec6e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtMethodCoderOverridingOverriddenId.class.st @@ -0,0 +1,13 @@ +" +A buttonthat shows if a method is overriding one in the superclass or is overridden in one of the subclasses +" +Class { + #name : #GtMethodCoderOverridingOverriddenId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtMethodCoderOverridingOverriddenId >> asSymbol [ + ^ #'method-coder--overriding-overridden-button' +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderPlayAndInspectExampleActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderPlayAndInspectExampleActionId.class.st index 9be320ab0..893f876c5 100644 --- a/src/GToolkit-Coder-UI/GtMethodCoderPlayAndInspectExampleActionId.class.st +++ b/src/GToolkit-Coder-UI/GtMethodCoderPlayAndInspectExampleActionId.class.st @@ -6,7 +6,7 @@ A button to play and inspect an example Class { #name : #GtMethodCoderPlayAndInspectExampleActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtMethodCoderPlayExampleActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderPlayExampleActionId.class.st index d9474b39c..cb81ebdc6 100644 --- a/src/GToolkit-Coder-UI/GtMethodCoderPlayExampleActionId.class.st +++ b/src/GToolkit-Coder-UI/GtMethodCoderPlayExampleActionId.class.st @@ -6,7 +6,7 @@ A button to play an example Class { #name : #GtMethodCoderPlayExampleActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtMethodCoderRemoveActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderRemoveActionId.class.st new file mode 100644 index 000000000..000fbfdf6 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtMethodCoderRemoveActionId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtMethodCoderRemoveActionId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtMethodCoderRemoveActionId >> asSymbol [ + ^ #'main-action--remove' +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderSaveActionId.class.st b/src/GToolkit-Coder-UI/GtMethodCoderSaveActionId.class.st index 4f6c0d0b3..549350dc6 100644 --- a/src/GToolkit-Coder-UI/GtMethodCoderSaveActionId.class.st +++ b/src/GToolkit-Coder-UI/GtMethodCoderSaveActionId.class.st @@ -6,7 +6,7 @@ A button to save a method Class { #name : #GtMethodCoderSaveActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtMethodCoderSaved.class.st b/src/GToolkit-Coder-UI/GtMethodCoderSaved.class.st new file mode 100644 index 000000000..e77fbc3dc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtMethodCoderSaved.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GtMethodCoderSaved, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'method', + 'previousMethod' + ], + #category : #'GToolkit-Coder-UI-Coder - Method Events' +} + +{ #category : #testing } +GtMethodCoderSaved >> isSameMethod [ + previousMethod ifNil: [ ^ false ]. + method ifNil: [ ^ false ]. + + ^ method methodClass = previousMethod methodClass + and: [ method selector = previousMethod selector ] +] + +{ #category : #accessing } +GtMethodCoderSaved >> method [ + + ^ method +] + +{ #category : #accessing } +GtMethodCoderSaved >> method: aCompiledMethod [ + + method := aCompiledMethod +] + +{ #category : #accessing } +GtMethodCoderSaved >> previousMethod [ + ^ previousMethod +] + +{ #category : #accessing } +GtMethodCoderSaved >> previousMethod: aCompiledMethodOrNil [ + previousMethod := aCompiledMethodOrNil +] diff --git a/src/GToolkit-Coder-UI/GtMethodCoderTool.class.st b/src/GToolkit-Coder-UI/GtMethodCoderTool.class.st index e667f7935..d5f0fd34d 100644 --- a/src/GToolkit-Coder-UI/GtMethodCoderTool.class.st +++ b/src/GToolkit-Coder-UI/GtMethodCoderTool.class.st @@ -1,6 +1,8 @@ Class { #name : #GtMethodCoderTool, #superclass : #GtCoderTool, + #traits : 'TGtPhlowWithSelfObjectHolder', + #classTraits : 'TGtPhlowWithSelfObjectHolder classTrait', #instVars : [ 'compiledMethod' ], @@ -12,6 +14,13 @@ GtMethodCoderTool class >> compiledMethod: aMethod [ ^ self new compiledMethod: aMethod ] +{ #category : #'instance creation' } +GtMethodCoderTool class >> compiledMethod: aMethod selfObjectHolder: aSelfObjectHolder [ + ^ self new + compiledMethod: aMethod; + selfObjectHolder: aSelfObjectHolder +] + { #category : #accessing } GtMethodCoderTool >> compiledMethod [ ^ compiledMethod @@ -24,7 +33,10 @@ GtMethodCoderTool >> compiledMethod: anObject [ { #category : #converting } GtMethodCoderTool >> newCoder [ - ^ GtCoder forMethod: self compiledMethod + ^ self + ifSelfObject: [ :aSelfObject | + GtCoderElement forObject: aSelfObject method: self compiledMethod ] + ifNone: [ GtCoderElement forMethod: self compiledMethod ] ] { #category : #accessing } diff --git a/src/GToolkit-Coder-UI/GtMultipleCodersViewModel.class.st b/src/GToolkit-Coder-UI/GtMultipleCodersViewModel.class.st index d8feac7a2..f98e06d98 100644 --- a/src/GToolkit-Coder-UI/GtMultipleCodersViewModel.class.st +++ b/src/GToolkit-Coder-UI/GtMultipleCodersViewModel.class.st @@ -18,7 +18,7 @@ GtMultipleCodersViewModel >> announcer [ ] { #category : #converting } -GtMultipleCodersViewModel >> asCoderUIModel [ +GtMultipleCodersViewModel >> asCoderViewModel [ ^ self ] @@ -44,7 +44,7 @@ GtMultipleCodersViewModel >> coder: aGtCodersModel [ { #category : #updating } GtMultipleCodersViewModel >> coderUIModelFor: aCoder [ - ^ aCoder asCoderUIModel + ^ aCoder asCoderViewModel ] { #category : #accessing } diff --git a/src/GToolkit-Coder-UI/GtNavigationDropDownCreateWish.class.st b/src/GToolkit-Coder-UI/GtNavigationDropDownCreateWish.class.st new file mode 100644 index 000000000..1c83c34a4 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtNavigationDropDownCreateWish.class.st @@ -0,0 +1,23 @@ +Class { + #name : #GtNavigationDropDownCreateWish, + #superclass : #BrWish, + #instVars : [ + 'tabName' + ], + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #accessing } +GtNavigationDropDownCreateWish class >> showTab: aCreationForm [ + ^self new tabName: aCreationForm componentName +] + +{ #category : #accessing } +GtNavigationDropDownCreateWish >> tabName [ + ^ tabName +] + +{ #category : #accessing } +GtNavigationDropDownCreateWish >> tabName: aString [ + tabName := aString +] diff --git a/src/GToolkit-Coder-UI/GtNavigationMethodSelectionWish.class.st b/src/GToolkit-Coder-UI/GtNavigationMethodSelectionWish.class.st new file mode 100644 index 000000000..dbd862621 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtNavigationMethodSelectionWish.class.st @@ -0,0 +1,24 @@ +" +I let to know that another method is selected in a {{gtMethod:GtPharoStreamingMethodsCoderElement}} and that this new selection should be reflected in a corresponding {{gtClass:GtCoderMethodsGroupedListElement | label=sidebar index method list}}. + +To make it happen, I am handled by {{gtMethod:GtCoderElement>>#onGtNavigationMethodSelectionWish: | label=#methodClass}}, followed by a {{gtMethod:GtCoderNavigationModel>>#selectedMethod:source:}} call. + +" +Class { + #name : #GtNavigationMethodSelectionWish, + #superclass : #BrWish, + #instVars : [ + 'method' + ], + #category : #'GToolkit-Coder-UI-Navigation - Helpers' +} + +{ #category : #accessing } +GtNavigationMethodSelectionWish >> method [ + ^ method +] + +{ #category : #accessing } +GtNavigationMethodSelectionWish >> method: anObject [ + method := anObject +] diff --git a/src/GToolkit-Coder-UI/GtNavigationNewDropdownId.class.st b/src/GToolkit-Coder-UI/GtNavigationNewDropdownId.class.st new file mode 100644 index 000000000..596246335 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtNavigationNewDropdownId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtNavigationNewDropdownId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #accessing } +GtNavigationNewDropdownId >> asSymbol [ + ^#'navigation-newDropdown' +] diff --git a/src/GToolkit-Coder-UI/GtNavigationSpotterDropdownId.class.st b/src/GToolkit-Coder-UI/GtNavigationSpotterDropdownId.class.st new file mode 100644 index 000000000..a6eadc1c8 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtNavigationSpotterDropdownId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtNavigationSpotterDropdownId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #accessing } +GtNavigationSpotterDropdownId >> asSymbol [ + ^#'navigation-spotter' +] diff --git a/src/GToolkit-Coder-UI/GtNoCoderRequester.extension.st b/src/GToolkit-Coder-UI/GtNoCoderRequester.extension.st new file mode 100644 index 000000000..d6cc88e5d --- /dev/null +++ b/src/GToolkit-Coder-UI/GtNoCoderRequester.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #GtNoCoderRequester } + +{ #category : #'*GToolkit-Coder-UI' } +GtNoCoderRequester >> isUndefinedOrCoderViewModel: aCoderViewModel [ + ^ true +] diff --git a/src/GToolkit-Coder-UI/GtNotificationDebugSession.class.st b/src/GToolkit-Coder-UI/GtNotificationDebugSession.class.st new file mode 100644 index 000000000..1dda88c64 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtNotificationDebugSession.class.st @@ -0,0 +1,67 @@ +Class { + #name : #GtNotificationDebugSession, + #superclass : #GtNotificationEvent, + #instVars : [ + 'debugSession' + ], + #category : #'GToolkit-Coder-UI-DebugSession' +} + +{ #category : #'private - asserting' } +GtNotificationDebugSession >> assertDebugSession: aDebugSession [ + self + assert: [ aDebugSession isNotNil ] + description: [ 'Debug session must be non-nil' ]. + self + assert: [ aDebugSession isKindOf: GtSharedDebugSession ] + description: [ 'Debug session must be a ', + GtSharedDebugSession name, ' instance, instead of a ', + aDebugSession class name, ' instance' ] +] + +{ #category : #accessing } +GtNotificationDebugSession >> debugSession [ + + ^ debugSession +] + +{ #category : #accessing } +GtNotificationDebugSession >> debugSession: aDebugSession [ + self assertDebugSession: aDebugSession. + debugSession ifNotNil: [ self unsubscribeFromDebugSession ]. + debugSession := aDebugSession. + self subscribeToDebugSession +] + +{ #category : #'api - accessing' } +GtNotificationDebugSession >> elementClass [ + ^ GtNotificationDebugSessionElement +] + +{ #category : #'private - announcement handling' } +GtNotificationDebugSession >> onSharedDebugSessionAnnouncement: anAnnouncement [ + self debugSession unsubscribe: self. + self requestRemoval +] + +{ #category : #'api - hooks' } +GtNotificationDebugSession >> removedFrom: aNotifications [ + "Subclasses can react to notification removal" + + super removedFrom: aNotifications. + self unsubscribeFromDebugSession. + debugSession terminate +] + +{ #category : #'private - subscriptions' } +GtNotificationDebugSession >> subscribeToDebugSession [ + debugSession weak + when: GtSharedDebugSessionAnnouncement + send: #onSharedDebugSessionAnnouncement: + to: self +] + +{ #category : #'private - subscriptions' } +GtNotificationDebugSession >> unsubscribeFromDebugSession [ + debugSession unsubscribe: self +] diff --git a/src/GToolkit-Coder-UI/GtNotificationDebugSessionElement.class.st b/src/GToolkit-Coder-UI/GtNotificationDebugSessionElement.class.st new file mode 100644 index 000000000..13befe232 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtNotificationDebugSessionElement.class.st @@ -0,0 +1,247 @@ +Class { + #name : #GtNotificationDebugSessionElement, + #superclass : #GtNotificationEventElement, + #instVars : [ + 'textEditor', + 'toolbarElement', + 'borderElement', + 'stackListElement' + ], + #category : #'GToolkit-Coder-UI-DebugSession' +} + +{ #category : #'private - accessing' } +GtNotificationDebugSessionElement >> actOnElementDetached [ + super actOnElementDetached. + self clearContent. +] + +{ #category : #'private - accessing' } +GtNotificationDebugSessionElement >> borderElement [ + + ^ borderElement +] + +{ #category : #'private - hooks' } +GtNotificationDebugSessionElement >> clearContent [ + self stackListElement items: #(). + self textEditor text: ''. +] + +{ #category : #'private - actions' } +GtNotificationDebugSessionElement >> debug [ + self debugSessionDo: [ :aSession | + aSession debug. + self dismiss ] +] + +{ #category : #'private - accessing' } +GtNotificationDebugSessionElement >> debugSessionDo: aBlock [ + self + notificationDo: [ :aNotification | aNotification debugSession ifNotNil: aBlock ] +] + +{ #category : #initialization } +GtNotificationDebugSessionElement >> defaultLayout [ + ^ BlLinearLayout vertical alignCenterRight +] + +{ #category : #initialization } +GtNotificationDebugSessionElement >> initialize [ + super initialize. + self initializeBorderElement. + self initializeTextEditor. + self initializeStackList. + self initializeToolbarElement. + + self addChild: self borderElement as: #border. + + self borderElement containerDo: [ :aContainer | + aContainer alignCenterRight. + aContainer addChild: self textEditor as: #label. + aContainer addChild: self stackListElement as: #stack. + aContainer addChild: self toolbarElement as: #toolbar ]. + + self addAptitude: (BrLayoutResizerAptitude new + hInherit; + vAnyToFitContent; + hInherit: self borderElement; + vAnyToFitContent: self borderElement). + + self borderElement addAptitude: (BrLayoutResizerAptitude new + hInherit: self textEditor; + vAnyToFitContent: self textEditor; + hInherit: self stackListElement; + anyToFitContent: self toolbarElement). +] + +{ #category : #initialization } +GtNotificationDebugSessionElement >> initializeBorderElement [ + borderElement := GtNotificationBorderElement new + borderDo: [ :anElement | + anElement + border: (BlBorder + paint: self theme status errorBackgroundColor + width: 1); + padding: (BlInsets all: 0) ]; + withCloseButtonAction: [ self terminate ] +] + +{ #category : #initialization } +GtNotificationDebugSessionElement >> initializeStackList [ + stackListElement := BrSimpleList new + hMatchParent; + vExact: 50; + padding: (BlInsets top: 5 right: 9 bottom: 10 left: 9); + itemType: [ :anItemTypeFactory :anItemObject :anItemIndex | GtDebugSession ]; + itemStencil: [ :anEventElementClass :aListWidget | + BrLabel new + aptitude: (BrGlamorousLabelAptitude new + glamorousRegularFont; + glamorousRegularSmallSize); + hMatchParent; + vFitContent ]; + itemDataBinder: [ :aLabel :aContext :anItemIndex | + aLabel text: (self textForContext: aContext) ] +] + +{ #category : #initialization } +GtNotificationDebugSessionElement >> initializeTextEditor [ + textEditor := BrEditor new + aptitude: + (BrGlamorousRegularEditorAptitude new + glamorousRegularFont; + glamorousRegularSmallSize); + padding: + (BlInsets + top: 5 + right: 10 + bottom: 5 + left: 10); + background: self theme status errorBackgroundColor; + hMatchParent; + vFitContent; + beReadOnlyWithSelection +] + +{ #category : #initialization } +GtNotificationDebugSessionElement >> initializeToolbarElement [ + | aDebugButton | + toolbarElement := BrToolbar new + aptitude: BrGlamorousToolbarAptitude; + padding: (BlInsets top: 5 right: 10 bottom: 10 left: 10); + fitContent. + + aDebugButton := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + beSmallSize; + icon: BrGlamorousVectorIcons debug; + label: 'Debug'; + action: [ self debug ]. + + self toolbarElement + addItem: aDebugButton +] + +{ #category : #'private - instance creation' } +GtNotificationDebugSessionElement >> newTextDescribingSession: aSession [ + ^ String + streamContents: [ :aStream | + | aString | + aString := aSession name asString. + aString size > 72 ifTrue: [ aString := aString first: 72 ]. + aStream nextPutAll: aString ] +] + +{ #category : #'private - announcement handling' } +GtNotificationDebugSessionElement >> onDebugSessionDebugged: anAnnouncement [ + self enqueueTask: (BlTaskAction new action: [ + self dismiss ]) +] + +{ #category : #'private - announcement handling' } +GtNotificationDebugSessionElement >> onDebugSessionTerminated: anAnnouncement [ + self enqueueTask: (BlTaskAction new action: [ + self dismiss ]) +] + +{ #category : #'private - hooks' } +GtNotificationDebugSessionElement >> onNotificationChanged [ + super onNotificationChanged. + self updateElement. +] + +{ #category : #'private - accessing' } +GtNotificationDebugSessionElement >> stackListElement [ + + ^ stackListElement +] + +{ #category : #'private - subscriptions' } +GtNotificationDebugSessionElement >> subscribeToNotification [ + super subscribeToNotification. + self debugSessionDo: [ :aSession | + aSession weak + when: GtSharedDebugSessionDebuggedAnnouncement + send: #onDebugSessionDebugged: + to: self; + when: GtSharedDebugSessionTerminatedAnnouncement + send: #onDebugSessionTerminated: + to: self ] +] + +{ #category : #'private - actions' } +GtNotificationDebugSessionElement >> terminate [ + self debugSessionDo: [ :aSession | + aSession terminate. + self dismiss. + ]. +] + +{ #category : #accessing } +GtNotificationDebugSessionElement >> textEditor [ + + ^ textEditor +] + +{ #category : #'private - instance creation' } +GtNotificationDebugSessionElement >> textForContext: aContext [ + ^ aContext printString +] + +{ #category : #'private - accessing' } +GtNotificationDebugSessionElement >> toolbarElement [ + + ^ toolbarElement +] + +{ #category : #'private - subscriptions' } +GtNotificationDebugSessionElement >> unsubscribeFromNotification [ + super unsubscribeFromNotification. + self debugSessionDo: [ :aSession | + aSession unsubscribe: self ] +] + +{ #category : #'private - updating' } +GtNotificationDebugSessionElement >> updateElement [ + self debugSessionDo: [ :aSession | + self updateTextEditor. + self updateStackList. ] +] + +{ #category : #'private - updating' } +GtNotificationDebugSessionElement >> updateStackList [ + self debugSessionDo: [ :aSession | + aSession interruptedContext + ifNotNil: [ :anInterruptedContext | + anInterruptedContext stack ifNotNil: [ :aStack | + self stackListElement items: aStack ] ] ] +] + +{ #category : #'private - updating' } +GtNotificationDebugSessionElement >> updateTextEditor [ + self debugSessionDo: [ :aSession | + | aText | + aText := self newTextDescribingSession: aSession. + self textEditor text: aText ] +] diff --git a/src/GToolkit-Coder-UI/GtObjectCoderTool.class.st b/src/GToolkit-Coder-UI/GtObjectCoderTool.class.st new file mode 100644 index 000000000..613902a91 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtObjectCoderTool.class.st @@ -0,0 +1,45 @@ +Class { + #name : #GtObjectCoderTool, + #superclass : #GtCoderTool, + #instVars : [ + 'object' + ], + #category : #'GToolkit-Coder-UI-Tools' +} + +{ #category : #'instance creation' } +GtObjectCoderTool class >> forObject: anObject [ + ^ self new object: anObject +] + +{ #category : #converting } +GtObjectCoderTool >> newCoder [ + ^ GtCoderElement forObject: self object +] + +{ #category : #accessing } +GtObjectCoderTool >> object [ + ^ object +] + +{ #category : #accessing } +GtObjectCoderTool >> object: anObject [ + object := anObject +] + +{ #category : #'api - accessing' } +GtObjectCoderTool >> title [ + "Return an object name" + + + ^ [ self object gtDisplayString ] + onErrorDo: [ :anError | + [ | anotherTitle | + anotherTitle := self object className. + + '{1}{2}''s error: {3}' + format: {anotherTitle first isVowel ifTrue: [ 'an ' ] ifFalse: [ 'a ' ]. + anotherTitle. + anError gtDisplayString} ] + onErrorDo: [ :anotherError | anError messageText ] ] +] diff --git a/src/GToolkit-Coder-UI/GtPackageCoderPackageNameId.class.st b/src/GToolkit-Coder-UI/GtPackageCoderPackageNameId.class.st index 0365834e4..6abcfb05e 100644 --- a/src/GToolkit-Coder-UI/GtPackageCoderPackageNameId.class.st +++ b/src/GToolkit-Coder-UI/GtPackageCoderPackageNameId.class.st @@ -6,7 +6,7 @@ An editable label with package name Class { #name : #GtPackageCoderPackageNameId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtPackageCoderTool.class.st b/src/GToolkit-Coder-UI/GtPackageCoderTool.class.st index 3773f9dbc..28d04f4b8 100644 --- a/src/GToolkit-Coder-UI/GtPackageCoderTool.class.st +++ b/src/GToolkit-Coder-UI/GtPackageCoderTool.class.st @@ -8,13 +8,22 @@ Class { } { #category : #'instance creation' } -GtPackageCoderTool class >> package: aPackage [ +GtPackageCoderTool class >> forPackage: aPackage [ ^ self new package: aPackage ] +{ #category : #'instance creation' } +GtPackageCoderTool class >> package: aPackage [ + "Should be removed since it overrides core method." + + self deprecated: 'Use #forPackage: instead'. + + ^ self forPackage: aPackage +] + { #category : #converting } GtPackageCoderTool >> newCoder [ - ^ GtCoder forPackage: self package + ^ GtCoderElement forPackage: self package ] { #category : #accessing } diff --git a/src/GToolkit-Coder-UI/GtPackageTagCoderTool.class.st b/src/GToolkit-Coder-UI/GtPackageTagCoderTool.class.st new file mode 100644 index 000000000..d0acc6531 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtPackageTagCoderTool.class.st @@ -0,0 +1,37 @@ +Class { + #name : #GtPackageTagCoderTool, + #superclass : #GtCoderTool, + #instVars : [ + 'packageTag' + ], + #category : #'GToolkit-Coder-UI-Tools' +} + +{ #category : #'instace creation' } +GtPackageTagCoderTool class >> forPackageTag: aPackageTag [ + ^ self new packageTag: aPackageTag +] + +{ #category : #'instace creation' } +GtPackageTagCoderTool class >> packageTag: aPackageTag [ + "Should be removed since it overrides core method." + + self deprecated: 'Use #forPackageTag: instead'. + + ^ self forPackageTag: aPackageTag +] + +{ #category : #accessing } +GtPackageTagCoderTool >> newCoder [ + ^ GtCoderElement forPackageTag: self packageTag +] + +{ #category : #accessing } +GtPackageTagCoderTool >> packageTag [ + ^ packageTag +] + +{ #category : #accessing } +GtPackageTagCoderTool >> packageTag: aPackageTag [ + packageTag := aPackageTag +] diff --git a/src/GToolkit-Coder-UI/GtPackagesCoderElement.class.st b/src/GToolkit-Coder-UI/GtPackagesCoderElement.class.st index f79705d17..db14af3cb 100644 --- a/src/GToolkit-Coder-UI/GtPackagesCoderElement.class.st +++ b/src/GToolkit-Coder-UI/GtPackagesCoderElement.class.st @@ -1,6 +1,8 @@ Class { #name : #GtPackagesCoderElement, #superclass : #BlElement, + #traits : 'TGtWithCoderToolbar', + #classTraits : 'TGtWithCoderToolbar classTrait', #instVars : [ 'packagesCoder', 'contentPane' @@ -10,14 +12,23 @@ Class { { #category : #'private - ui' } GtPackagesCoderElement >> buildContentPane [ + | container | + container := BlElement new + layout: BlLinearLayout vertical; + addChild: self makeLabel; + constraintsDo: [ :c | + c horizontal matchParent. + c vertical matchParent ]. + contentPane := BlElement new. contentPane layout: BlLinearLayout horizontal. - contentPane padding: (BlInsets all: 5). + contentPane padding: (BlInsets top: 0 left: 5 bottom: 5 right: 5). contentPane constraintsDo: [ :c | c horizontal matchParent. c vertical matchParent ]. - ^ contentPane + + ^ container addChild: contentPane ] { #category : #'private - ui' } @@ -29,7 +40,7 @@ GtPackagesCoderElement >> buildPackageList [ items: (packagesCoder coders asSortedCollection: [ :a :b | a packageName < b packageName ]); - addEventFilterOn: BlClickEvent do: [ :anEvent | self requestFocus ] + addEventFilterOn: BlClickEvent do: [ :anEvent | anEvent currentTarget requestFocus ] ] { #category : #'private - ui' } @@ -43,18 +54,20 @@ GtPackagesCoderElement >> buildPackagesElement [ c horizontal matchParent. c vertical matchParent ]. element - addChild: - (BrLabel new - margin: (BlInsets left: 10); - aptitude: (BrGlamorousLabelAptitude new bold glamorousRegularFont fontSize: 18); - text: packagesCoder name). + addChild: (BrHorizontalPane new + constraintsDo: [ :c | + c horizontal matchParent. + c vertical fitContent ]; + addChild: (BrLabel new + margin: (BlInsets left: 10); + aptitude: (BrGlamorousLabelAptitude new bold glamorousRegularFont fontSize: 18); + text: packagesCoder name); + addChild: self toolbarElement). element - addChild: - (BrLabel new + addChild: (BrLabel new margin: (BlInsets left: 10); beSmallSize; - aptitude: - (BrGlamorousLabelAptitude new glamorousRegularFont + aptitude: (BrGlamorousLabelAptitude new glamorousRegularFont foreground: self theme button defaultTextColor); text: packagesCoder coders size printString , ' Packages'). element addChild: self buildPackageList. @@ -90,6 +103,7 @@ GtPackagesCoderElement >> codersUIModel: aPackagesCoder [ { #category : #'initialize-release' } GtPackagesCoderElement >> initialize [ super initialize. + self initializeToolbarElement. self constraintsDo: [ :c | c horizontal matchParent. @@ -98,3 +112,13 @@ GtPackagesCoderElement >> initialize [ self when: BlClickEvent do: [ self requestFocus ]. self addChild: self buildContentPane. ] + +{ #category : #'private - ui' } +GtPackagesCoderElement >> makeLabel [ + ^ BrLabel new + aptitude: (BrGlamorousLabelAptitude new glamorousRegularFontAndSize + foreground: Color gray; + fontSize: 12); + text: 'Packages'; + padding: (BlInsets top: 5 left: 20) +] diff --git a/src/GToolkit-Coder-UI/GtPackagesCoderUIModel.class.st b/src/GToolkit-Coder-UI/GtPackagesCoderViewModel.class.st similarity index 67% rename from src/GToolkit-Coder-UI/GtPackagesCoderUIModel.class.st rename to src/GToolkit-Coder-UI/GtPackagesCoderViewModel.class.st index ba5943dbf..69c52039b 100644 --- a/src/GToolkit-Coder-UI/GtPackagesCoderUIModel.class.st +++ b/src/GToolkit-Coder-UI/GtPackagesCoderViewModel.class.st @@ -1,10 +1,10 @@ Class { - #name : #GtPackagesCoderUIModel, + #name : #GtPackagesCoderViewModel, #superclass : #GtMultipleCodersViewModel, #category : #'GToolkit-Coder-UI-Coder - Packages Model' } { #category : #accessing } -GtPackagesCoderUIModel >> elementClass [ +GtPackagesCoderViewModel >> elementClass [ ^ GtPackagesCoderElement ] diff --git a/src/GToolkit-Coder-UI/GtPharoCoderHierarchyDropdownConfiguration.class.st b/src/GToolkit-Coder-UI/GtPharoCoderHierarchyDropdownConfiguration.class.st new file mode 100644 index 000000000..3bdd9f07f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtPharoCoderHierarchyDropdownConfiguration.class.st @@ -0,0 +1,56 @@ +Class { + #name : #GtPharoCoderHierarchyDropdownConfiguration, + #superclass : #Object, + #classInstVars : [ + 'preferredExtent' + ], + #category : #'GToolkit-Coder-UI' +} + +{ #category : #cleanup } +GtPharoCoderHierarchyDropdownConfiguration class >> cleanUp: aggressive [ + aggressive ifTrue: [ preferredExtent := nil ] +] + +{ #category : #accessing } +GtPharoCoderHierarchyDropdownConfiguration class >> defaultPreferredExtent [ + ^ 800 @ 400 +] + +{ #category : #accessing } +GtPharoCoderHierarchyDropdownConfiguration class >> gtConfiguration [ + ^ {GtPhlowOverviewItem new + name: 'Preferred extent'; + value: self preferredExtent} +] + +{ #category : #accessing } +GtPharoCoderHierarchyDropdownConfiguration class >> gtConfigurationFor: aView [ + + + ^ aView columnedList + title: 'Configuration'; + priority: 40; + actionUpdateButton; + items: [ self gtConfiguration ]; + column: 'Name' text: #name; + column: 'Value' text: #description; + send: #value +] + +{ #category : #accessing } +GtPharoCoderHierarchyDropdownConfiguration class >> preferredExtent [ + ^ preferredExtent ifNil: [ preferredExtent := self defaultPreferredExtent ] +] + +{ #category : #accessing } +GtPharoCoderHierarchyDropdownConfiguration class >> preferredExtent: anObject [ + preferredExtent := anObject +] + +{ #category : #accessing } +GtPharoCoderHierarchyDropdownConfiguration class >> preferredExtentOrNil [ + "Just for testing purposes." + + ^ preferredExtent +] diff --git a/src/GToolkit-Coder-UI/GtPharoRemoveClassPreviewStencil.class.st b/src/GToolkit-Coder-UI/GtPharoRemoveClassPreviewStencil.class.st new file mode 100644 index 000000000..73e438140 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtPharoRemoveClassPreviewStencil.class.st @@ -0,0 +1,25 @@ +Class { + #name : #GtPharoRemoveClassPreviewStencil, + #superclass : #GtAbstractRemovePreviewStencil, + #instVars : [ + 'classToRemove' + ], + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #initialization } +GtPharoRemoveClassPreviewStencil >> anElement: element [ + anElement := element +] + +{ #category : #initialization } +GtPharoRemoveClassPreviewStencil >> classToRemove: aClass [ + classToRemove := aClass +] + +{ #category : #accessing } +GtPharoRemoveClassPreviewStencil >> create [ + ^ self + createPreviewContainerForItemNamed: classToRemove name + withReferences: classToRemove gtReferences | (GtSearchSubclassesFilter forClass: classToRemove) +] diff --git a/src/GToolkit-Coder-UI/GtPhlowAction.extension.st b/src/GToolkit-Coder-UI/GtPhlowAction.extension.st new file mode 100644 index 000000000..ed89b9b78 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtPhlowAction.extension.st @@ -0,0 +1,11 @@ +Extension { #name : #GtPhlowAction } + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowAction >> asNavigationElement: aBlock withHostElement: aTargetElement [ + + ^ self + asElement: [ :anElement | + anElement states addState: BrSizeAdjustmentState mini. + aBlock value: anElement ] + withHostElement: aTargetElement +] diff --git a/src/GToolkit-Coder-UI/GtPhlowContext.extension.st b/src/GToolkit-Coder-UI/GtPhlowContext.extension.st new file mode 100644 index 000000000..b969b0fa9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtPhlowContext.extension.st @@ -0,0 +1,50 @@ +Extension { #name : #GtPhlowContext } + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowContext >> coderCompiledMethod: aCompiledMethod [ + self optionAt: #coderCompiledMethod put: aCompiledMethod +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowContext >> coderCompiledMethodIfPresent: aPresentBlock ifAbsent: anAbsentBlock [ + ^ self + optionAt: #coderCompiledMethod + ifPresent: aPresentBlock + ifAbsent: anAbsentBlock +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowContext >> coderSelectedClass: aClass [ + self optionAt: #coderSelectedClass put: aClass +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowContext >> coderSelectedClassIfPresent: aPresentBlock ifAbsent: anAbsentBlock [ + ^ self + optionAt: #coderSelectedClass + ifPresent: aPresentBlock + ifAbsent: anAbsentBlock +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowContext >> coderSelectedClasses [ + ^ self optionAt: #coderSelectedClasses ifAbsent: [ #() ] +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowContext >> coderSelectedClasses: aCollectionOfClasses [ + self optionAt: #coderSelectedClasses put: aCollectionOfClasses +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowContext >> coderViewModel: aCoderViewModel [ + self optionAt: #coderViewModel: put: aCoderViewModel +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowContext >> coderViewModelIfPresent: aPresentBlock ifAbsent: anAbsentBlock [ + ^ self + optionAt: #coderViewModel + ifPresent: aPresentBlock + ifAbsent: anAbsentBlock +] diff --git a/src/GToolkit-Coder-UI/GtPhlowElementContext.extension.st b/src/GToolkit-Coder-UI/GtPhlowElementContext.extension.st new file mode 100644 index 000000000..a3a6b98e2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtPhlowElementContext.extension.st @@ -0,0 +1,64 @@ +Extension { #name : #GtPhlowElementContext } + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowElementContext >> coder [ + "Use #firstParentCoder instead" + + + ^ self firstParentCoder +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowElementContext >> coderNavigationModel [ + "Use #firstParentCoderNavigationModel instead" + + + ^ self firstParentCoderNavigationModel +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowElementContext >> firstParentCoder [ + + ^ self + firstParentCoderIfPresent: [ :parent | parent ] + ifAbsent: [ self error: 'Couldn''t find a coder parent' ] +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowElementContext >> firstParentCoderIfPresent: aPresentBlock ifAbsent: anAbsentBlock [ + + ^ self element + allParentsDetect: [ :parent | parent isKindOf: GtCoderElement ] + ifFound: aPresentBlock + ifNone: anAbsentBlock +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowElementContext >> firstParentCoderNavigationModel [ + + | aCoder aNavigationModel | + aCoder := self firstParentCoder. + aNavigationModel := aCoder navigationModel. + + self + assert: [ aNavigationModel isNotNil ] + description: [ 'Coder navigation model must be non-nil' ]. + + ^ aNavigationModel +] + +{ #category : #'*GToolkit-Coder-UI' } +GtPhlowElementContext >> firstParentCoderNavigationModelIfPresent: aPresentBlock ifAbsent: anAbsentBlock [ + + ^ self + firstParentCoderIfPresent: [ :aCoder | + | aNavigationModel | + aNavigationModel := aCoder navigationModel. + + self + assert: [ aNavigationModel isNotNil ] + description: [ 'Coder navigation model must be non-nil' ]. + + aPresentBlock value: aNavigationModel ] + ifAbsent: anAbsentBlock +] diff --git a/src/GToolkit-Coder-UI/GtPlaygroundEvaluatedCodeButtonAttribute.class.st b/src/GToolkit-Coder-UI/GtPlaygroundEvaluatedCodeButtonAttribute.class.st new file mode 100644 index 000000000..c19dd36ff --- /dev/null +++ b/src/GToolkit-Coder-UI/GtPlaygroundEvaluatedCodeButtonAttribute.class.st @@ -0,0 +1,124 @@ +Class { + #name : #GtPlaygroundEvaluatedCodeButtonAttribute, + #superclass : #BrTextAdornmentAttribute, + #instVars : [ + 'result', + 'action', + 'coder', + 'evaluationInterval' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Attributes' +} + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> action [ + ^ action +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> action: aBlock [ + action := aBlock +] + +{ #category : #initialization } +GtPlaygroundEvaluatedCodeButtonAttribute >> affect: aTBrTextEditorTextualPiece in: anEditorElement [ + + | aButton hoverAttribute | + aButton := BrButton new. + hoverAttribute := BlTextHighlightAttribute + paint: BrGlamorousColors textHighlightColor. + ^ aButton + aptitude: (BrLazyStyleCommonAptitude new + default: [ :aStyle | aStyle background: BrGlamorousColors defaultButtonBorderColor ]; + hovered: [ :aStyle | aStyle background: BrGlamorousColors hoveredButtonBorderColor ]) + + ((BrGlamorousWithExplicitTooltipAptitude + content: [ | aContainer | + aContainer := BlElement new + layout: BlFrameLayout new; + constraintsDo: [ :c | + c horizontal fitContent. + c vertical fitContent ]; + when: GtPhlowObjectToSpawn + do: [ :anEvent | + aButton phlow spawnPreviousEvent: anEvent ]. + self playgroundElementIn: aContainer ]) showDelay: 200 milliSeconds); + size: 8 @ 8; + geometry: BlEllipseGeometry new; + margin: (BlInsets left: 2 right: 2); + action: [ :aButtonElement :aButtonModel :anEvent | self clickEvent: anEvent from: aButtonElement ]; + when: BlMouseEnterEvent + do: [ :anEvent | + evaluationInterval + ifNotNil: [ (anEditorElement text + from: (evaluationInterval first min: anEditorElement text size) + to: (evaluationInterval last min: anEditorElement text size)) + attribute: hoverAttribute ] ]; + when: BlMouseLeaveEvent + do: [ :anEvent | anEditorElement text clearAttributes: [ :each | each == hoverAttribute ] ]; + yourself +] + +{ #category : #'event handling' } +GtPlaygroundEvaluatedCodeButtonAttribute >> clickEvent: anEvent from: aButton [ + self action cull: aButton cull: anEvent +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> coder [ + ^ coder +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> coder: anObject [ + coder := anObject +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> evaluationInterval [ + ^ evaluationInterval +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> evaluationInterval: anObject [ + evaluationInterval := anObject +] + +{ #category : #initialization } +GtPlaygroundEvaluatedCodeButtonAttribute >> initialize [ + super initialize. + self beAppend. + action := [ :aButton :anEvent | + aButton phlow + spawnObject: self result + withDestination: self spawnDestination ] +] + +{ #category : #testing } +GtPlaygroundEvaluatedCodeButtonAttribute >> mayHaveExternalReferences [ + ^ true +] + +{ #category : #initialization } +GtPlaygroundEvaluatedCodeButtonAttribute >> playgroundElementIn: aContainer [ + ^ (self result gtViewsFor: GtPhlowEmptyView new) + asElementDo: [ :anInspectorElement | + aContainer + addChild: ((anInspectorElement exact: 400 @ 400) asScalableElement size: 200 @ 200) ] +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> result [ + ^ result +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> result: anObjectOrBlock [ + result := anObjectOrBlock +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeButtonAttribute >> spawnDestination [ + ^ self coder + ifNil: [ GtPhlowSpawnDesiredDestination defaultDestination ] + ifNotNil: [ self coder spawnDestination ] +] diff --git a/src/GToolkit-Coder-UI/GtPlaygroundEvaluatedCodeViewButtonAttribute.class.st b/src/GToolkit-Coder-UI/GtPlaygroundEvaluatedCodeViewButtonAttribute.class.st new file mode 100644 index 000000000..a853f21d2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtPlaygroundEvaluatedCodeViewButtonAttribute.class.st @@ -0,0 +1,36 @@ +Class { + #name : #GtPlaygroundEvaluatedCodeViewButtonAttribute, + #superclass : #GtPlaygroundEvaluatedCodeButtonAttribute, + #instVars : [ + 'view' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Attributes' +} + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeViewButtonAttribute >> defaultView [ + ^ #gtViewsFor: +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeViewButtonAttribute >> initialize [ + super initialize. + + self view: self defaultView +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeViewButtonAttribute >> playgroundElementIn: aContainer [ + ^ (self result perform: self view withArguments: {GtPhlowEmptyView new}) + asElementDo: [ :anInspectorElement | aContainer addChild: (anInspectorElement asScalableElement size: 200 @ 200) ] +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeViewButtonAttribute >> view [ + ^ view +] + +{ #category : #accessing } +GtPlaygroundEvaluatedCodeViewButtonAttribute >> view: anObject [ + view := anObject +] diff --git a/src/GToolkit-Coder-UI/GtPreviewChangeButton.class.st b/src/GToolkit-Coder-UI/GtPreviewChangeButton.class.st index f267ab131..8b8ed673f 100644 --- a/src/GToolkit-Coder-UI/GtPreviewChangeButton.class.st +++ b/src/GToolkit-Coder-UI/GtPreviewChangeButton.class.st @@ -32,7 +32,7 @@ GtPreviewChangeButton >> buildDropDownElement [ changeAction cull: aButtonElement cull: aButtonModel cull: anEvent. aButtonElement fireEvent: BrDropdownHideWish new ]; yourself. - element addChild: unloadButton. + element addChild: unloadButton as: #'change-button-action'. ^ element ] @@ -64,10 +64,10 @@ GtPreviewChangeButton >> initialize [ self beTinySize. self aptitude: BrGlamorousButtonWithIconAptitude new + - (BrGlamorousWithDropdownAptitude + (BrGlamorousWithExplicitDropdownAptitude handle: [ BrButton new - icon: BrGlamorousIcons empty; + icon: BrGlamorousVectorIcons empty; beTinySize; aptitude: BrGlamorousButtonRectangularAptitude new + BrGlamorousButtonIconAptitude new ] content: [ self buildDropDownElement ]) diff --git a/src/GToolkit-Coder-UI/GtProtocolSlotTabAptitude.class.st b/src/GToolkit-Coder-UI/GtProtocolSlotTabAptitude.class.st new file mode 100644 index 000000000..300a1b43c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtProtocolSlotTabAptitude.class.st @@ -0,0 +1,45 @@ +Class { + #name : #GtProtocolSlotTabAptitude, + #superclass : #BrTabAptitude, + #instVars : [ + 'bar' + ], + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #accessing } +GtProtocolSlotTabAptitude >> initialize [ + super initialize. + self add: (BrGlamorousTextLabelAptitude new foreground: Color gray). + self add: BrGlamorousTabActionbarAptitude new. + self add: BrLayoutAlignmentAptitude new. + self + addChangeProperty: #(widget layout) + with: [ BlLinearLayout horizontal ]. + bar := BlElement new + visibility: BlVisibility hidden; + background: self theme default primaryBorderColor; + constraintsDo: [ :c | + c ignoreByLayout. + c ignored vertical alignBottom. + c vertical exact: 1. + c horizontal matchParent ]. + self + add: + (BrStyleCommonAptitude new + default: [ :aStyle | + aStyle + padding: + (BlInsets + top: 2 + left: 12 + bottom: 2 + right: 12) ]). + self + add: + (BrStyleCommonAptitude new + @ bar; + deselected: [ :aStyle | aStyle hidden ]; + selected: [ :aStyle | aStyle visible ]). + self addChangeAddChildAs: #(content bar) with: [ bar ] +] diff --git a/src/GToolkit-Coder-UI/GtRBAddPoolVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/GtRBAddPoolVariableRefactoring.extension.st deleted file mode 100644 index b96c948fc..000000000 --- a/src/GToolkit-Coder-UI/GtRBAddPoolVariableRefactoring.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : #GtRBAddPoolVariableRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -GtRBAddPoolVariableRefactoring >> gtDescription [ - - - ^ ('Add' asRopedText glamorousRoundedBackground) - append: ' pool named ' asRopedText; - append: variableName asRopedText glamorousRoundedBackground; - append: ' to ' asRopedText; - append: class name asRopedText -] diff --git a/src/GToolkit-Coder-UI/GtRBAddTraitUsageRefactoring.extension.st b/src/GToolkit-Coder-UI/GtRBAddTraitUsageRefactoring.extension.st deleted file mode 100644 index e382afddd..000000000 --- a/src/GToolkit-Coder-UI/GtRBAddTraitUsageRefactoring.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : #GtRBAddTraitUsageRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -GtRBAddTraitUsageRefactoring >> gtDescription [ - - - ^ ('Add' asRopedText glamorousRoundedBackground) - append: ' trait usage ' asRopedText; - append: traitName asRopedText glamorousRoundedBackground; - append: ' to ' asRopedText; - append: class name asRopedText -] diff --git a/src/GToolkit-Coder-UI/GtRBChangeSuperclassRefactoring.extension.st b/src/GToolkit-Coder-UI/GtRBChangeSuperclassRefactoring.extension.st deleted file mode 100644 index 09f61b996..000000000 --- a/src/GToolkit-Coder-UI/GtRBChangeSuperclassRefactoring.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : #GtRBChangeSuperclassRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -GtRBChangeSuperclassRefactoring >> gtDescription [ - - - ^ ('Change' asRopedText glamorousRoundedBackground) - append: ' superclass of ' asRopedText; - append: className asRopedText; - append: ' to ' asRopedText; - append: newSuperclass name asRopedText glamorousRoundedBackground -] diff --git a/src/GToolkit-Coder-UI/GtRBRemovePoolVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/GtRBRemovePoolVariableRefactoring.extension.st deleted file mode 100644 index c4b8628ad..000000000 --- a/src/GToolkit-Coder-UI/GtRBRemovePoolVariableRefactoring.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : #GtRBRemovePoolVariableRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -GtRBRemovePoolVariableRefactoring >> gtDescription [ - - - ^ ('Remove' asRopedText glamorousRoundedBackground) - append: ' pool named ' asRopedText; - append: variableName asRopedText glamorousRoundedBackground; - append: ' from ' asRopedText; - append: class name asRopedText -] diff --git a/src/GToolkit-Coder-UI/GtRBRemoveTraitUsageRefactoring.extension.st b/src/GToolkit-Coder-UI/GtRBRemoveTraitUsageRefactoring.extension.st deleted file mode 100644 index c52704dd8..000000000 --- a/src/GToolkit-Coder-UI/GtRBRemoveTraitUsageRefactoring.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : #GtRBRemoveTraitUsageRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -GtRBRemoveTraitUsageRefactoring >> gtDescription [ - - - ^ ('Remove' asRopedText glamorousRoundedBackground) - append: ' trait usage ' asRopedText; - append: traitName asRopedText glamorousRoundedBackground; - append: ' from ' asRopedText; - append: class name asRopedText -] diff --git a/src/GToolkit-Coder-UI/GtReadyCoderTool.class.st b/src/GToolkit-Coder-UI/GtReadyCoderTool.class.st index e39023197..be6d4c02b 100644 --- a/src/GToolkit-Coder-UI/GtReadyCoderTool.class.st +++ b/src/GToolkit-Coder-UI/GtReadyCoderTool.class.st @@ -16,7 +16,9 @@ GtReadyCoderTool class >> coder: aCoder [ GtReadyCoderTool >> asElementDo: aOneArgBlock [ "Create a tool element and execute the block." - ^ aOneArgBlock cull: (GtCoder forCoder: self coder) asPagerPageElement + | anElement | + anElement := GtCoderElement forCoder: self coder asNewCoderModelWithSameSubject. + ^ aOneArgBlock cull: anElement ] { #category : #accessing } @@ -54,3 +56,9 @@ GtReadyCoderTool >> name [ GtReadyCoderTool >> tabLook [ ^ BrGlamorousTabSwitcherWithIconAptitude ] + +{ #category : #'api - accessing' } +GtReadyCoderTool >> title [ + + ^ self coder ifNotNil: #coderName ifNil: [ super title ] +] diff --git a/src/GToolkit-Coder-UI/GtRefactoringsPreviewAcceptId.class.st b/src/GToolkit-Coder-UI/GtRefactoringsPreviewAcceptId.class.st index 1ba93a5fb..036a9b650 100644 --- a/src/GToolkit-Coder-UI/GtRefactoringsPreviewAcceptId.class.st +++ b/src/GToolkit-Coder-UI/GtRefactoringsPreviewAcceptId.class.st @@ -6,7 +6,7 @@ A button to apply refactorings from a preview dropdown Class { #name : #GtRefactoringsPreviewAcceptId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtSearchBinaryFilter.extension.st b/src/GToolkit-Coder-UI/GtSearchBinaryFilter.extension.st new file mode 100644 index 000000000..790873798 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSearchBinaryFilter.extension.st @@ -0,0 +1,12 @@ +Extension { #name : #GtSearchBinaryFilter } + +{ #category : #'*GToolkit-Coder-UI' } +GtSearchBinaryFilter >> highlighter [ + ^ left highlighter + ifNil: [ right highlighter ] + ifNotNil: [ :lh | + right highlighter + ifNil: [ lh ] + ifNotNil: + [ :rh | GtCompositeHighlighter forHighlighters: (Array with: lh with: rh) ] ] +] diff --git a/src/GToolkit-Coder-UI/GtSearchFilter.extension.st b/src/GToolkit-Coder-UI/GtSearchFilter.extension.st new file mode 100644 index 000000000..e819e1c04 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSearchFilter.extension.st @@ -0,0 +1,24 @@ +Extension { #name : #GtSearchFilter } + +{ #category : #'*GToolkit-Coder-UI' } +GtSearchFilter >> asCoder [ + ^ self result asCoder +] + +{ #category : #'*GToolkit-Coder-UI' } +GtSearchFilter >> asElement [ + ^ self asCoder asElement +] + +{ #category : #'*GToolkit-Coder-UI' } +GtSearchFilter >> gtExamples [ + | examples | + examples := OrderedCollection new. + self contents do: [ :each | examples addAll: each gtExamples ]. + ^ GtExampleGroup withAll: examples +] + +{ #category : #'*GToolkit-Coder-UI' } +GtSearchFilter >> highlighter [ + ^ nil +] diff --git a/src/GToolkit-Coder-UI/GtSingleCoderViewModel.class.st b/src/GToolkit-Coder-UI/GtSingleCoderViewModel.class.st index 290a0a97b..768150780 100644 --- a/src/GToolkit-Coder-UI/GtSingleCoderViewModel.class.st +++ b/src/GToolkit-Coder-UI/GtSingleCoderViewModel.class.st @@ -2,20 +2,83 @@ 1. Coder UI Model Wraps {{gtClass:GtCoderModel}} and optionally adds UI related api and state. Coder UI Model should be passed to any Coder UI element instead of the {{gtClass:GtCoderModel}} - +##Add-ons +Coders are extensible using a concept of an add-on. " Class { #name : #GtSingleCoderViewModel, #superclass : #Object, - #traits : 'TGtAnnouncer + TGtWithCoderModel', - #classTraits : 'TGtAnnouncer classTrait + TGtWithCoderModel classTrait', + #traits : 'TGtAnnouncer + TGtOptions', + #classTraits : 'TGtAnnouncer classTrait + TGtOptions classTrait', #instVars : [ 'announcer', - 'codersUIModel' + 'codersUIModel', + 'coderModel', + 'extraAddOns', + 'addOnPromise', + 'currentAddOns', + 'addOnsCache' ], #category : #'GToolkit-Coder-UI-Coder - Basic' } +{ #category : #accessing } +GtSingleCoderViewModel class >> addOnsExecutionConfiguration [ + ^ self methodAddOnsExecutionConfiguration +] + +{ #category : #accessing } +GtSingleCoderViewModel class >> behaviourAddOnsExecutionConfiguration [ + ^ AsyncFutureExecutionConfiguration new + customGroup: #ClassCoderAddOn; + lowPriority +] + +{ #category : #accessing } +GtSingleCoderViewModel class >> commonExecutionConfiguration [ + ^ GtCoderModel commonExecutionConfiguration +] + +{ #category : #accessing } +GtSingleCoderViewModel class >> methodAddOnsExecutionConfiguration [ + ^ AsyncFutureExecutionConfiguration new + customGroup: #MethodCoderAddOn; + maxAmountOfWorkers: 2; + defaultPriority +] + +{ #category : #accessing } +GtSingleCoderViewModel class >> secondaryMethodAddOnsExecutionConfiguration [ + ^ AsyncFutureExecutionConfiguration new + customGroup: #SecondaryMethodCoderAddOn; + maxAmountOfWorkers: 1; + lowPriority +] + +{ #category : #'api - shortcuts' } +GtSingleCoderViewModel >> addMainAction: aGtCoderAction [ + "Add an extra main action add-on independent from the dynamically computed add-ons" + + extraAddOns addMainAction: aGtCoderAction +] + +{ #category : #'api - add-ons' } +GtSingleCoderViewModel >> addOns [ + + + ^ self addOnsFuture await: self class methodAddOnsExecutionConfiguration +] + +{ #category : #'api - add-ons' } +GtSingleCoderViewModel >> addOnsFuture [ + "Return a future that will be resolved to coder addons" + + + ^ AsyncCachedFuture + forFuture: self computeAddOnsFuture + cache: addOnsCache +] + { #category : #accessing } GtSingleCoderViewModel >> announcer [ ^ announcer ifNil: [ announcer := Announcer new ] @@ -23,18 +86,22 @@ GtSingleCoderViewModel >> announcer [ { #category : #accessing } GtSingleCoderViewModel >> announcerUIModel [ + self + deprecated: 'Use #announcer instead' + transformWith: '`@receiver announcerUIModel' -> '`@receiver announcer'. + ^ self announcer ] { #category : #converting } -GtSingleCoderViewModel >> asCoderUIModel [ +GtSingleCoderViewModel >> asCoderViewModel [ ^ self ] { #category : #converting } GtSingleCoderViewModel >> asElement [ ^ self elementClass new - coderUIModel: self; + coderViewModel: self; yourself ] @@ -53,6 +120,33 @@ GtSingleCoderViewModel >> coderLook [ ^ self coder coderLook ] +{ #category : #'api - coder model' } +GtSingleCoderViewModel >> coderModel [ + "Return a not-null coder model assigned to the receiver view model" + self + assert: [ coderModel notNil ] + description: [ 'coder model should be initialized' ]. + + ^ coderModel +] + +{ #category : #'api - coder model' } +GtSingleCoderViewModel >> coderModel: aCoderModel [ + "Set a not-null coder domain model assigned to the receiver view model" + self + assert: [ aCoderModel notNil ] + description: [ 'coder model must not be nil' ]. + + coderModel == aCoderModel + ifTrue: [ ^ self ]. + + coderModel ifNotNil: [ self unsubscribeFromCoderModel ]. + coderModel := aCoderModel. + + self onCoderModelChanged. + self subscribeToCoderModel +] + { #category : #accessing } GtSingleCoderViewModel >> coders [ ^ self coder coders @@ -68,11 +162,28 @@ GtSingleCoderViewModel >> codersUIModel: anObject [ codersUIModel := anObject ] +{ #category : #'private - addons' } +GtSingleCoderViewModel >> computeAddOnsFuture [ + + + ^ self subclassResponsibility +] + +{ #category : #converting } +GtSingleCoderViewModel >> createPhlowContext [ + ^ GtPhlowContext new coderViewModel: self +] + { #category : #accessing } GtSingleCoderViewModel >> elementClass [ ^ self subclassResponsibility ] +{ #category : #accessing } +GtSingleCoderViewModel >> evaluationResult [ + ^ nil +] + { #category : #'gt-extension' } GtSingleCoderViewModel >> gtLiveFor: aView [ @@ -84,6 +195,14 @@ GtSingleCoderViewModel >> gtLiveFor: aView [ view: #gtLiveFor: ] +{ #category : #'api - coder model' } +GtSingleCoderViewModel >> hasCoder [ + "Return a true if coder model is assigned to the receiver, false otherwise" + + + ^ coderModel notNil +] + { #category : #accessing } GtSingleCoderViewModel >> hasFocus [ @@ -105,20 +224,62 @@ GtSingleCoderViewModel >> hasFocus: aBoolean [ self focused: aBoolean ] +{ #category : #initialization } +GtSingleCoderViewModel >> initialize [ + super initialize. + + addOnsCache := AsyncFutureCache new +] + { #category : #testing } GtSingleCoderViewModel >> isModified [ ^ self coder isModified ] +{ #category : #'private - event handling' } +GtSingleCoderViewModel >> onAddOnsChanged: theAddOns [ + "Is sent by Coder Element from a UI thread when new add-ons are computed" + + currentAddOns := theAddOns +] + { #category : #'api - coder model' } GtSingleCoderViewModel >> onCoderModelChanged [ "Is sent when a new coder model is assigned to the view model" ] -{ #category : #accessing } -GtSingleCoderViewModel >> programCounterRange [ - "This is workwound util the method coder context had a dedicated element. - Now the element for displaying a coder is shared between all types of coders." +{ #category : #'api - coder model' } +GtSingleCoderViewModel >> onCoderRecomputeAddOnsRequest: aCoderAddOnsUpdateRequest [ + self requestUpdateAddOns +] - ^ nil +{ #category : #'api - add-ons' } +GtSingleCoderViewModel >> requestUpdateAddOns [ + addOnsCache resetCache. + self announcer announce: (GtCoderViewModelRecomputeAddOnRequest new coderViewModel: self) +] + +{ #category : #'api - coder model' } +GtSingleCoderViewModel >> subscribeToCoderModel [ + "Is sent after a new coder model is assigned to the view model. + It is required to unsubscribe from the domain model by implementing + #unsubscribeFromCoderModel if view model subscribes to them" + + self coderModel subscribeToSystem. + + self coderModel announcer weak + when: GtCoderAddOnsUpdateRequest + send: #onCoderRecomputeAddOnsRequest: + to: self +] + +{ #category : #'api - coder model' } +GtSingleCoderViewModel >> unsubscribeFromCoderModel [ + "Is sent before a new coder model is assigned to the view model. + View models that subscribe to coder model are required to implement this methods" + + self coderModel announcer unsubscribe: self + + "unsubscribing from system here is not good, what if there are multiple view models on the same coder model?" + "self coderModel unsubscribeFromSystem" ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoder.extension.st b/src/GToolkit-Coder-UI/GtSourceCoder.extension.st new file mode 100644 index 000000000..551fbe11f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoder.extension.st @@ -0,0 +1,23 @@ +Extension { #name : #GtSourceCoder } + +{ #category : #'*GToolkit-Coder-UI' } +GtSourceCoder >> notifyShowDebuggerRequest: aDebugSession dueTo: anException sourceString: aSourceString sourceInterval: aSourceInterval evaluationInfo: anEvaluationInfo [ + "Return true if announcement was handled (and debugger displayed in some way). + Return false otherwise." + + + | anAnnouncement aSharedDebugSession | + aSharedDebugSession := GtSharedDebugSession new + session: aDebugSession. + anAnnouncement := GtCoderShowDebuggerRequest new + coder: self; + debugSession: aDebugSession; + sharedDebugSession: aSharedDebugSession; + exception: anException; + sourceString: aSourceString; + sourceInterval: aSourceInterval; + evaluationInfo: anEvaluationInfo. + self announce: anAnnouncement. + + ^ anAnnouncement isDelivered +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderActionsElement.class.st b/src/GToolkit-Coder-UI/GtSourceCoderActionsElement.class.st new file mode 100644 index 000000000..b2a30e806 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderActionsElement.class.st @@ -0,0 +1,186 @@ +Class { + #name : #GtSourceCoderActionsElement, + #superclass : #GtCoderActionsElement, + #traits : 'TGtWithTextualCoderViewModel', + #classTraits : 'TGtWithTextualCoderViewModel classTrait', + #instVars : [ + 'mainToolbar', + 'contextToolbar', + 'separator', + 'editor' + ], + #category : #'GToolkit-Coder-UI-Coder - Basic' +} + +{ #category : #private } +GtSourceCoderActionsElement >> addContextToolbarActions [ + contextToolbar addItems: (self textualCoderViewModel contextActions collect: [ :aGtCoderAction | + [ aGtCoderAction buildElementIn: self ] + on: Error + do: [ :anException | self newErrorButtonForAction: aGtCoderAction exception: anException ] ]). + separator + visibility: + (contextToolbar hasItems + ifTrue: [ BlVisibility visible ] + ifFalse: [ BlVisibility gone ]) +] + +{ #category : #private } +GtSourceCoderActionsElement >> addMainToolbarActions [ + | mainActions coderModel | + + mainActions := self coderViewModel mainActions. + coderModel := self coderViewModel coderModel. + (coderModel isForMethod and: [ coderModel compiledMethod isNotNil and: [ coderModel compiledMethod isBigMethod ] ]) ifTrue: + [ mainActions := mainActions reject: + [ :action | action title = 'Save' ] ]. + mainToolbar + addItems: (mainActions + collect: [ :aGtCoderAction | + self flag: 'Temporary hack. Coder should to Phlow actions'. + [ aGtCoderAction buildElementIn: self ] + on: Error + do: [ :anException | self newErrorButtonForAction: aGtCoderAction exception: anException ] ]) +] + +{ #category : #accessing } +GtSourceCoderActionsElement >> coderViewModel [ + ^ self textualCoderViewModel +] + +{ #category : #accessing } +GtSourceCoderActionsElement >> coderViewModel: aCoderViewModel [ + self textualCoderViewModel: aCoderViewModel +] + +{ #category : #initialization } +GtSourceCoderActionsElement >> initialize [ + super initialize. + + self layout: BlLinearLayout horizontal. + self padding: (BlInsets empty). + self constraintsDo: [ :c | + c horizontal matchParent. + c vertical fitContent. + c minWidth: GtCoderUIConstants actionsToolbarHeight ]. + + mainToolbar := self newToolbar labeled: 'Main toolbar'. + + separator := BlElement new + background: (Color gray alpha: 0.2); + margin: (BlInsets all: 5); + constraintsDo: [ :c | + c horizontal exact: 1. + c vertical matchParent ]. + + contextToolbar := self newToolbar labeled: 'Context toolbar'. + + self addChildren: { mainToolbar . separator . contextToolbar } +] + +{ #category : #'instance creation' } +GtSourceCoderActionsElement >> newToolbar [ + + ^ BrToolbar new + aptitude: (BrGlamorousToolbarAptitude new spacing: 4); + padding: (BlInsets left: -4); + constraintsDo: [ :c | c linear vertical alignCenter ] +] + +{ #category : #'private - announcement handling' } +GtSourceCoderActionsElement >> onAboutToOpenDebugger: anAnnouncement [ + BlTaskAction + enqueueElement: self + action: [ self updateDebuggerIndicatorForProcess: anAnnouncement process ] +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderActionsElement >> onPostTextualCoderViewModelChanged [ + "I am an optional hook method that is sent after #subscribeToTextualCoderViewModel. + I do nothing by default but allow users to perform update operations when a receiver object is already + subscribed to announcements." +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderActionsElement >> onTextualCoderViewModelChanged [ + "Is sent when a new textualCoder view model is assigned to the element. + Note: #onTextualCoderViewModelChanged is sent before #subscribeToTextualCoderViewModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostTextualCoderViewModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + + self removeMainToolbarActions. + self removeContextToolbarActions. + self addMainToolbarActions. + self addContextToolbarActions +] + +{ #category : #private } +GtSourceCoderActionsElement >> removeContextToolbarActions [ + | aboutToRemoveElements | + aboutToRemoveElements := contextToolbar items. + contextToolbar removeAllItems. + BrWidgetPermanentlyRemovedEvent notifyElements: aboutToRemoveElements +] + +{ #category : #private } +GtSourceCoderActionsElement >> removeMainToolbarActions [ + | aboutToRemoveElements | + aboutToRemoveElements := mainToolbar items. + mainToolbar removeAllItems. + BrWidgetPermanentlyRemovedEvent notifyElements: aboutToRemoveElements +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderActionsElement >> subscribeToTextualCoderViewModel [ + "Is sent after a new textualCoder view model is assigned to the element. + It is required to unsubscribe from the view model or domain model by implementing + #unsubscribeFromTextualCoderViewModel if elements subscribe to them" + + self textualCoderViewModel weak + when: GtTextualCoderViewModelMainActionsChanged send: #updateMainToolbar to: self; + when: GtTextualCoderViewModelContextActionsChanged send: #updateContextToolbar to: self; + when: GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement send: #onAboutToOpenDebugger: to: self. +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderActionsElement >> unsubscribeFromTextualCoderViewModel [ + "Is sent before a new textualCoder view model is assigned to the element. + Elements that subscribe to textualCoder view model in domain model are required to implement this methods." + + self textualCoderViewModel unsubscribe: self +] + +{ #category : #'private - event handling' } +GtSourceCoderActionsElement >> updateContextToolbar [ + self enqueueTask: + (BlTaskAction new + action: [ + self removeContextToolbarActions. + self addContextToolbarActions ]) +] + +{ #category : #'private - updating' } +GtSourceCoderActionsElement >> updateDebuggerIndicatorForProcess: aProcess [ + | aSpace | + aSpace := GtCoderDebuggerSpaceFinder new + process: aProcess; + find; + space. + + GtSourceCoderProcessInDebuggerSignal new + process: aProcess; + space: aSpace; + emit. + + aSpace ifNil: [ ^ self ]. + self textualCoderViewModel addLocateDebuggerInSpaceAction: aSpace +] + +{ #category : #'private - event handling' } +GtSourceCoderActionsElement >> updateMainToolbar [ + self enqueueTask: (BlTaskAction new + action: [ + self removeMainToolbarActions. + self addMainToolbarActions ]) +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderAptitude.class.st b/src/GToolkit-Coder-UI/GtSourceCoderAptitude.class.st deleted file mode 100644 index 28838f24c..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderAptitude.class.st +++ /dev/null @@ -1,41 +0,0 @@ -Class { - #name : #GtSourceCoderAptitude, - #superclass : #BrAptitude, - #category : #'GToolkit-Coder-UI-Coder - Source Look' -} - -{ #category : #'private - accessing' } -GtSourceCoderAptitude >> coder [ - self - deprecated: 'Please use #sourceCoderUIModel instead' - transformWith: '`@receiver coder' -> '`@receiver sourceCoderUIModel'. - - ^ self sourceCoderUIModel -] - -{ #category : #initialization } -GtSourceCoderAptitude >> initializeListeners [ - super initializeListeners. - - self when: GtSourceCoderContentCoderChanged do: [ :anEvent | - anEvent sourceCoder - ifNotNil: [ :aSourceCoder | self onCoderChanged: aSourceCoder ] ] -] - -{ #category : #initialization } -GtSourceCoderAptitude >> initializeRequests [ - super initializeRequests. - - self request: GtSourceCoderContentCoderRequest new -] - -{ #category : #hooks } -GtSourceCoderAptitude >> onCoderChanged: aGtSourceCoder [ -] - -{ #category : #'private - accessing' } -GtSourceCoderAptitude >> sourceCoderUIModel [ - - - ^ (self request: GtSourceCoderContentCoderRequest oneWay) sourceCoder -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbAction.class.st b/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbAction.class.st deleted file mode 100644 index 3400cfa9d..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbAction.class.st +++ /dev/null @@ -1,22 +0,0 @@ -Class { - #name : #GtSourceCoderBreadcrumbAction, - #superclass : #Object, - #category : #'GToolkit-Coder-UI-Coder - Source Model' -} - -{ #category : #accessing } -GtSourceCoderBreadcrumbAction >> foreground [ - ^ BrGlamorousColors defaultButtonTextColor -] - -{ #category : #accessing } -GtSourceCoderBreadcrumbAction >> name [ - - - ^ self subclassResponsibility -] - -{ #category : #action } -GtSourceCoderBreadcrumbAction >> performSourceCoderActionFrom: anElement [ - self subclassResponsibility -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbSpawnBehaviorAction.class.st b/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbSpawnBehaviorAction.class.st deleted file mode 100644 index 78fbe736e..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbSpawnBehaviorAction.class.st +++ /dev/null @@ -1,56 +0,0 @@ -Class { - #name : #GtSourceCoderBreadcrumbSpawnBehaviorAction, - #superclass : #GtSourceCoderBreadcrumbAction, - #instVars : [ - 'methodBehavior' - ], - #category : #'GToolkit-Coder-UI-Coder - Source Model' -} - -{ #category : #accessing } -GtSourceCoderBreadcrumbSpawnBehaviorAction >> foreground [ - ^ self methodBehavior exists - ifTrue: [ BrGlamorousColors defaultButtonTextColor ] - ifFalse: [ BrGlamorousColors linkWithErrorColor ] -] - -{ #category : #accessing } -GtSourceCoderBreadcrumbSpawnBehaviorAction >> methodBehavior [ - ^ methodBehavior -] - -{ #category : #accessing } -GtSourceCoderBreadcrumbSpawnBehaviorAction >> methodBehavior: anObject [ - methodBehavior := anObject -] - -{ #category : #accessing } -GtSourceCoderBreadcrumbSpawnBehaviorAction >> name [ - - - ^ self methodBehavior - behaviorNameDo: [ :aName | - self methodBehavior exists - ifTrue: [ aName ] - ifFalse: [ aName, ' (Deleted)' ] ] - ifAbsent: [ '(Unspecified)' ] -] - -{ #category : #action } -GtSourceCoderBreadcrumbSpawnBehaviorAction >> performSourceCoderActionFrom: anElement [ - - self methodBehavior realBehaviorDo: [ :aBehavior | - anElement phlow spawnTool: (GtClassCoderTool observedClass: aBehavior). - ^ self ]. - - anElement phlow spawnTool: (GtInspectorTool forObject: self methodBehavior). -] - -{ #category : #printing } -GtSourceCoderBreadcrumbSpawnBehaviorAction >> printOn: aStream [ - aStream - nextPutAll: 'Spawn '; - nextPutAll: self name; - space; - nextPutAll: 'class' -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbSpawnPackageAction.class.st b/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbSpawnPackageAction.class.st deleted file mode 100644 index 7cb330911..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderBreadcrumbSpawnPackageAction.class.st +++ /dev/null @@ -1,43 +0,0 @@ -Class { - #name : #GtSourceCoderBreadcrumbSpawnPackageAction, - #superclass : #GtSourceCoderBreadcrumbAction, - #instVars : [ - 'package' - ], - #category : #'GToolkit-Coder-UI-Coder - Source Model' -} - -{ #category : #accessing } -GtSourceCoderBreadcrumbSpawnPackageAction >> name [ - - - ^ self package name -] - -{ #category : #accessing } -GtSourceCoderBreadcrumbSpawnPackageAction >> package [ - ^ package -] - -{ #category : #accessing } -GtSourceCoderBreadcrumbSpawnPackageAction >> package: aPackage [ - self - assert: [ aPackage notNil ] - description: [ 'Package must not be nil' ]. - - package := aPackage -] - -{ #category : #action } -GtSourceCoderBreadcrumbSpawnPackageAction >> performSourceCoderActionFrom: anElement [ - anElement phlow spawnTool: (GtPackageCoderTool package: self package) -] - -{ #category : #printing } -GtSourceCoderBreadcrumbSpawnPackageAction >> printOn: aStream [ - aStream - nextPutAll: 'Spawn '; - nextPutAll: self package name; - space; - nextPutAll: 'package' -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderBrowseBehaviorShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderBrowseBehaviorShortcut.class.st index 538701da5..678c75463 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderBrowseBehaviorShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderBrowseBehaviorShortcut.class.st @@ -22,10 +22,8 @@ GtSourceCoderBrowseBehaviorShortcut >> name [ ] { #category : #evaluation } -GtSourceCoderBrowseBehaviorShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ - - self - forEditor: aBrTextEditor - selectedStringDo: [ :aSelectedString | aGtSourceCoder browseBehaviorIn: aSelectedString ] - orCursorStringPositionDo: [ :aStringPosition | aGtSourceCoder browseBehaviorAt: aStringPosition ] +GtSourceCoderBrowseBehaviorShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent requesterObject: aRequester [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString | aGtSourceCoder browseBehaviorIn: aSelectedString requesterObject: aRequester ] + orCursorPositionDo: [ :aStringPosition | aGtSourceCoder browseBehaviorAt: aStringPosition requesterObject: aRequester ] ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderBrowseImplementorsShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderBrowseImplementorsShortcut.class.st index 516b415cf..510c3a861 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderBrowseImplementorsShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderBrowseImplementorsShortcut.class.st @@ -22,9 +22,14 @@ GtSourceCoderBrowseImplementorsShortcut >> name [ ] { #category : #evaluation } -GtSourceCoderBrowseImplementorsShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ - self - forEditor: aBrTextEditor - selectedStringDo: [ :aSelectedString | aGtSourceCoder browseImplementorsIn: aSelectedString ] - orCursorStringPositionDo: [ :aStringPosition | aGtSourceCoder browseImplementorsAt: aStringPosition ] +GtSourceCoderBrowseImplementorsShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent requesterObject: aRequester [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString | + aGtSourceCoder + browseImplementorsIn: aSelectedString + requesterObject: aRequester ] + orCursorPositionDo: [ :aStringPosition | + aGtSourceCoder + browseImplementorsAt: aStringPosition + requesterObject: aRequester ] ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderBrowseReferencesShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderBrowseReferencesShortcut.class.st index 63c597a99..4fd43c536 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderBrowseReferencesShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderBrowseReferencesShortcut.class.st @@ -22,9 +22,14 @@ GtSourceCoderBrowseReferencesShortcut >> name [ ] { #category : #evaluation } -GtSourceCoderBrowseReferencesShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ - self - forEditor: aBrTextEditor - selectedStringDo: [ :aSelectedString | aGtSourceCoder browseReferencesIn: aSelectedString ] - orCursorStringPositionDo: [ :aStringPosition | aGtSourceCoder browseReferencesAt: aStringPosition ] +GtSourceCoderBrowseReferencesShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent requesterObject: aRequester [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString | + aGtSourceCoder + browseReferencesIn: aSelectedString + requesterObject: aRequester ] + orCursorPositionDo: [ :aStringPosition | + aGtSourceCoder + browseReferencesAt: aStringPosition + requesterObject: aRequester ] ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedAddOnsId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderCollapsedAddOnsId.class.st new file mode 100644 index 000000000..ee4c46818 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderCollapsedAddOnsId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtSourceCoderCollapsedAddOnsId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #accessing } +GtSourceCoderCollapsedAddOnsId >> asSymbol [ + ^ 'source-coder--collapsed-addons' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedContentElement.class.st b/src/GToolkit-Coder-UI/GtSourceCoderCollapsedContentElement.class.st index ab0d2f037..cd681f60c 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedContentElement.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderCollapsedContentElement.class.st @@ -1,5 +1,140 @@ Class { #name : #GtSourceCoderCollapsedContentElement, #superclass : #GtSourceCoderContentElement, + #instVars : [ + 'label', + 'addOns', + 'content', + 'previews' + ], #category : #'GToolkit-Coder-UI-Coder - Source' } + +{ #category : #initialization } +GtSourceCoderCollapsedContentElement >> initialize [ + super initialize. + + content := BlElement new + layout: BlLinearLayout horizontal; + constraintsDo: [ :c | + c horizontal matchParent. + c vertical fitContent ]. + + label := self newLabel. + addOns := self newAddOnsContainer. + + previews := Dictionary new. + + content + addChildren: {label. + addOns}. + self addChild: content. + self + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude + menu: [ self createHeaderContextMenu ]) +] + +{ #category : #'instance creation' } +GtSourceCoderCollapsedContentElement >> newAddOnsContainer [ + ^ BrHorizontalPane new + matchParent; + alignCenterLeft; + withAsyncFutureDo: [ :anElementFuture | + anElementFuture + executionConfiguration: GtSingleCoderViewModel methodAddOnsExecutionConfiguration; + whenSuccess: [ :aContainer :theAddOns | + aContainer removeChildren. + theAddOns previews do: [ :eachAddOn | + | eachAddOnElement | + + eachAddOnElement := previews + at: eachAddOn id + ifAbsentPut: [ eachAddOn stencil asElement ]. + + eachAddOn dataBinder + element: eachAddOnElement; + coderViewModel: textualCoderViewModel; + build. + + aContainer addChild: eachAddOnElement ] ]; + whenError: [ :aContainer :anError | + aContainer removeChildren. + aContainer addChild: (BrLabel new aptitude: BrGlamorousLabelAptitude; text: anError printString) ]; + whenPending: [ :aContainer | ] ]; + withAsyncPromiseDo: [ :anElementPromise | + anElementPromise + whenSuccess: [ :aContainer :theAddOns | + aContainer removeChildren. + theAddOns previews do: [ :eachAddOn | + | eachAddOnElement | + + eachAddOnElement := previews + at: eachAddOn id + ifAbsentPut: [ eachAddOn stencil asElement ]. + + eachAddOn dataBinder + element: eachAddOnElement; + coderViewModel: textualCoderViewModel; + build. + + aContainer addChild: eachAddOnElement ] ]; + whenError: [ :aContainer :anError | + aContainer removeChildren. + aContainer addChild: (BrLabel new aptitude: BrGlamorousLabelAptitude; text: anError printString) ]; + whenPending: [ :aContainer | ] ] +] + +{ #category : #'instance creation' } +GtSourceCoderCollapsedContentElement >> newLabel [ + ^ BlAttributedTextElement new + id: GtSourceCoderCollapsedTextId; + editorMeasurement; + bold; + glamorousCodeFont; + foreground: self theme label defaultTextForeground; + withAsyncPromiseDo: [ :anElementPromise | + anElementPromise + whenSuccess: [ :aLabel :aText | aLabel text: aText asRopedText ]; + whenError: [ :aLabel :anError | aLabel text: anError printString asRopedText ]; + whenPending: [ :aLabel | aLabel text: '' asRopedText ] ] +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderCollapsedContentElement >> onTextualCoderViewModelChanged [ + super onTextualCoderViewModelChanged. + + self updateCollapsedTextLabel. + addOns removeChildren. + addOns asyncFuture future: textualCoderViewModel addOnsFuture +] + +{ #category : #'private - event handling' } +GtSourceCoderCollapsedContentElement >> onViewModelTextChanged: anAnnouncement [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + anAnnouncement textualCoderViewModel = self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction enqueueElement: self action: [ self updateCollapsedTextLabel ] +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderCollapsedContentElement >> subscribeToTextualCoderViewModel [ + super subscribeToTextualCoderViewModel. + + textualCoderViewModel weak + when: GtTextualCoderViewModelTextChanged + send: #onViewModelTextChanged: + to: self +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderCollapsedContentElement >> unsubscribeFromTextualCoderViewModel [ + super unsubscribeFromTextualCoderViewModel. + + textualCoderViewModel unsubscribe: self +] + +{ #category : #'private - update' } +GtSourceCoderCollapsedContentElement >> updateCollapsedTextLabel [ + label asyncPromise promise: textualCoderViewModel collapsedTextPromise +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextAndExampleAptitude.class.st b/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextAndExampleAptitude.class.st deleted file mode 100644 index e3ed820a5..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextAndExampleAptitude.class.st +++ /dev/null @@ -1,141 +0,0 @@ -" -I am a {{gtClass:BrLook}}. -I am a {{gtClass:GtSourceCoderLook}}. -I attach two labels: -1. a {{gtMethod:GtSourceCoderCollapsedTextAndExampleLook>>#methodLabel|label=method name}}, and -2. example {{gtMethod:GtSourceCoderCollapsedTextAndExampleLook>>#exampleElement|label=execution state}}. -1. # Examples - -If a method is an example, I display the method name and example result: {{gtExample:GtCoderCollapsedContentElementExamples>>#textAndExampleLook_withExampleMethod|codeExpanded=false|previewExpanded=true|previewHeight=150}} -If a method is ** an example, I omit displaying the example result: {{gtExample:GtCoderCollapsedContentElementExamples>>#textAndExampleLook_withRegularMethod|codeExpanded=false|previewExpanded=true|previewHeight=150}} - - -" -Class { - #name : #GtSourceCoderCollapsedTextAndExampleAptitude, - #superclass : #GtSourceCoderAptitude, - #instVars : [ - 'container', - 'methodLabel', - 'exampleElement' - ], - #category : #'GToolkit-Coder-UI-Coder - Source Look' -} - -{ #category : #'private - accessing' } -GtSourceCoderCollapsedTextAndExampleAptitude >> container [ - - ^ container -] - -{ #category : #'private - accessing' } -GtSourceCoderCollapsedTextAndExampleAptitude >> exampleElement [ - - ^ exampleElement -] - -{ #category : #'private - updating' } -GtSourceCoderCollapsedTextAndExampleAptitude >> hide [ - self exampleElement visibility: BlVisibility hidden -] - -{ #category : #initialization } -GtSourceCoderCollapsedTextAndExampleAptitude >> initialize [ - super initialize. - self initializeMethodLabel. - self initializeExampleElement. - self initializeContainer. - - self container - addChild: self methodLabel as: GtSourceCoderCollapsedTextId; - addChild: self exampleElement as: #exampleElement. - - self addChangeAddChildAs: #(content label) with: [ self container ] -] - -{ #category : #initialization } -GtSourceCoderCollapsedTextAndExampleAptitude >> initializeContainer [ - container := BrHorizontalPane new - fitContent; - alignCenterLeft -] - -{ #category : #initialization } -GtSourceCoderCollapsedTextAndExampleAptitude >> initializeExampleElement [ - exampleElement := GtCoderExampleStateElement new - margin: (BlInsets top: 4 left: 1 bottom: 1 right: 1). -] - -{ #category : #initialization } -GtSourceCoderCollapsedTextAndExampleAptitude >> initializeListeners [ - super initializeListeners. - - self when: BlClickEvent do: [ :anEvent | - anEvent consumed: true. - self sourceCoderUIModel - expanded: true; - focused: true ] -] - -{ #category : #initialization } -GtSourceCoderCollapsedTextAndExampleAptitude >> initializeMethodLabel [ - methodLabel := BrLabel new - aptitude: BrGlamorousLabelAptitude new editorMeasurement bold glamorousCodeFont -] - -{ #category : #'private - accessing' } -GtSourceCoderCollapsedTextAndExampleAptitude >> methodLabel [ - - ^ methodLabel -] - -{ #category : #hooks } -GtSourceCoderCollapsedTextAndExampleAptitude >> onCoderChanged: aGtSourceCoderUIModel [ - self widget coderUIModel - ifNotNil: [ :aPreviousCoder | aPreviousCoder coder announcer unsubscribe: self ]. - self updateLabelsFor: aGtSourceCoderUIModel coder. - aGtSourceCoderUIModel coder announcer weak - when: GtCoderCollapsedLabelChanged - send: #onCollapsedLabelChanged: - to: self. - aGtSourceCoderUIModel coder announcer weak - when: GtCoderExampleExecuted - send: #onExampleExecuted: - to: self -] - -{ #category : #'private - announcement handling' } -GtSourceCoderCollapsedTextAndExampleAptitude >> onCollapsedLabelChanged: anAnnouncement [ - self updateLabelsFor: anAnnouncement coder -] - -{ #category : #'private - announcement handling' } -GtSourceCoderCollapsedTextAndExampleAptitude >> onExampleExecuted: anAnnouncement [ - self updateExampleElementFor: anAnnouncement coder -] - -{ #category : #'private - updating' } -GtSourceCoderCollapsedTextAndExampleAptitude >> show [ - self exampleElement visibility: BlVisibility visible -] - -{ #category : #'private - updating' } -GtSourceCoderCollapsedTextAndExampleAptitude >> updateExampleElementFor: aSourceCoder [ - aSourceCoder canExecuteExample - ifFalse: [ self hide. - ^ self ]. - self show. - - self exampleElement example: aSourceCoder example -] - -{ #category : #'private - updating' } -GtSourceCoderCollapsedTextAndExampleAptitude >> updateLabelsFor: aSourceCoder [ - self updateMethodLabelFor: aSourceCoder. - self updateExampleElementFor: aSourceCoder. -] - -{ #category : #'private - updating' } -GtSourceCoderCollapsedTextAndExampleAptitude >> updateMethodLabelFor: aSourceCoder [ - self methodLabel text: aSourceCoder collapsedText -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextAptitude.class.st b/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextAptitude.class.st deleted file mode 100644 index d3ca3af72..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextAptitude.class.st +++ /dev/null @@ -1,54 +0,0 @@ -Class { - #name : #GtSourceCoderCollapsedTextAptitude, - #superclass : #GtSourceCoderAptitude, - #instVars : [ - 'label' - ], - #category : #'GToolkit-Coder-UI-Coder - Source Look' -} - -{ #category : #initialization } -GtSourceCoderCollapsedTextAptitude >> initialize [ - super initialize. - - label := self newLabel. - self addChangeAddChildAs: { #content . GtSourceCoderCollapsedTextId } with: [ label ]. -] - -{ #category : #accessing } -GtSourceCoderCollapsedTextAptitude >> initializeListeners [ - super initializeListeners. - - self when: BlClickEvent do: [ :anEvent | - anEvent consumed: true. - self sourceCoderUIModel - expanded: true; - focused: true ] -] - -{ #category : #'instance creation' } -GtSourceCoderCollapsedTextAptitude >> newLabel [ - ^ BrLabel new - aptitude: BrGlamorousLabelAptitude new editorMeasurement bold glamorousCodeFont -] - -{ #category : #initialization } -GtSourceCoderCollapsedTextAptitude >> onCoderChanged: aGtSourceCoderUIModel [ - self widget coderUIModel - ifNotNil: [ :aPreviousCoder | aPreviousCoder coder announcer unsubscribe: self ]. - self updateLabelFor: aGtSourceCoderUIModel coder. - aGtSourceCoderUIModel coder announcer weak - when: GtCoderCollapsedLabelChanged - send: #updateLabel: - to: self -] - -{ #category : #private } -GtSourceCoderCollapsedTextAptitude >> updateLabel: anAnnouncement [ - self updateLabelFor: anAnnouncement coder -] - -{ #category : #private } -GtSourceCoderCollapsedTextAptitude >> updateLabelFor: aSourceCoder [ - label text: aSourceCoder collapsedText -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextId.class.st index 4b97a5ab3..1c8a1e80e 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextId.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderCollapsedTextId.class.st @@ -6,7 +6,7 @@ A label that shows a collapsed text of the source coder Class { #name : #GtSourceCoderCollapsedTextId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderCompileItShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderCompileItShortcut.class.st new file mode 100644 index 000000000..3d9387276 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderCompileItShortcut.class.st @@ -0,0 +1,25 @@ +Class { + #name : #GtSourceCoderCompileItShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI-Shortcuts' +} + +{ #category : #accessing } +GtSourceCoderCompileItShortcut >> description [ + ^ 'Compile class or trait definition' +] + +{ #category : #initialization } +GtSourceCoderCompileItShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination primaryS. + name := 'Compile'. +] + +{ #category : #evaluation } +GtSourceCoderCompileItShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + GtCoderExecutionContextVariable + element: aBrEditorElement + do: [ aSourceCoderViewModel compileIt ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderContentCoderChanged.class.st b/src/GToolkit-Coder-UI/GtSourceCoderContentCoderChanged.class.st deleted file mode 100644 index 6c7c73941..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderContentCoderChanged.class.st +++ /dev/null @@ -1,18 +0,0 @@ -Class { - #name : #GtSourceCoderContentCoderChanged, - #superclass : #BrChangeEvent, - #instVars : [ - 'sourceCoder' - ], - #category : #'GToolkit-Coder-UI-Coder - Source Events' -} - -{ #category : #accessing } -GtSourceCoderContentCoderChanged >> sourceCoder [ - ^ sourceCoder -] - -{ #category : #accessing } -GtSourceCoderContentCoderChanged >> sourceCoder: anObject [ - sourceCoder := anObject -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderContentCoderRequest.class.st b/src/GToolkit-Coder-UI/GtSourceCoderContentCoderRequest.class.st deleted file mode 100644 index 3bb3e7875..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderContentCoderRequest.class.st +++ /dev/null @@ -1,10 +0,0 @@ -Class { - #name : #GtSourceCoderContentCoderRequest, - #superclass : #BrRequest, - #category : #'GToolkit-Coder-UI-Coder - Source Events' -} - -{ #category : #initialization } -GtSourceCoderContentCoderRequest >> responseClass [ - ^ GtSourceCoderContentCoderChanged -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderContentElement.class.st b/src/GToolkit-Coder-UI/GtSourceCoderContentElement.class.st index e72ba1d9c..698d74fc0 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderContentElement.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderContentElement.class.st @@ -1,8 +1,11 @@ Class { #name : #GtSourceCoderContentElement, #superclass : #BlElement, - #traits : 'TBrLayoutResizable', - #classTraits : 'TBrLayoutResizable classTrait', + #traits : 'TBrLayoutResizable + TGtWithTextualCoderViewModel', + #classTraits : 'TBrLayoutResizable classTrait + TGtWithTextualCoderViewModel classTrait', + #instVars : [ + 'header' + ], #category : #'GToolkit-Coder-UI-Coder - Source' } @@ -12,7 +15,7 @@ GtSourceCoderContentElement >> coder [ deprecated: 'Use #coderUIModel instead.' transformWith: '`@receiver coder' -> '`@receiver coderUIModel'. - ^ self coderUIModel + ^ self coderViewModel ] { #category : #accessing } @@ -22,22 +25,51 @@ GtSourceCoderContentElement >> coder: aCoder [ deprecated: 'Use #coderUIModel: instead.' transformWith: '`@receiver coder: `@arg' -> '`@receiver coderUIModel: `@arg'. - self coderUIModel: aCoder + self coderViewModel: aCoder ] { #category : #accessing } -GtSourceCoderContentElement >> coderUIModel [ +GtSourceCoderContentElement >> coderViewModel [ ^ self viewModel sourceCoder ] { #category : #accessing } -GtSourceCoderContentElement >> coderUIModel: aGtSourceCoderUIModel [ +GtSourceCoderContentElement >> coderViewModel: aGtSourceCoderViewModel [ - self telemetry - timeSync: [ 'Assign coder to {1}' format: { self class name } ] - during: [ self viewModel sourceCoder: aGtSourceCoderUIModel ] + BlFrameTelemetry + time: [ 'Assign coder to {1}' format: { self class name } ] + during: [ self textualCoderViewModel: aGtSourceCoderViewModel ] +] + +{ #category : #updating } +GtSourceCoderContentElement >> createHeaderContextMenu [ + "wait for the addons to be computed" + + | aMenu aMenuItemsPromise | + aMenu := BrMenuItems new. + aMenu beGroupedElementType. + aMenuItemsPromise := self textualCoderViewModel addOns asyncThen: + [:theAddOns | + "extra context menu items that depend on ast and view model state such as selection" + | theItems theContextMenuAstAddons theMenuItems | + theContextMenuAstAddons := self textualCoderViewModel + computeHeaderContextMenuAstAddOns. + theItems := theAddOns contextMenuActions + , theContextMenuAstAddons contextMenuActions. + theItems := theItems reject: [:e | e title isNil]. + theMenuItems := theItems + collect: [:eachAction | eachAction asBrMenuItemForCoderElement: self] + thenReject: #isNil. + self enqueueTask: (BlTaskAction new action: [aMenu items: theMenuItems])]. + self enqueueTask: (BlPromiseTask new promise: aMenuItemsPromise). + ^aMenu +] + +{ #category : #initialization } +GtSourceCoderContentElement >> defaultLayout [ + ^ BlLinearLayout vertical ] { #category : #accessing } @@ -48,18 +80,55 @@ GtSourceCoderContentElement >> gtAllShortcutsFor: aView [ title: 'All shortcuts' translated; priority: 10; items: [ self shortcuts , self coder addOns shortcuts ]; - column: 'Key' item: [ :each | each combination gtDisplayString ]; - column: 'Action' item: [ :each | each action asString ] + column: 'Key' text: [ :each | each combination gtDisplayString ]; + column: 'Action' text: [ :each | each action asString ] ] { #category : #initialization } GtSourceCoderContentElement >> initialize [ super initialize. self - layout: BlLinearLayout vertical; - margin: (BlInsets left: 5); + padding: (BlInsets top: 4 right: 4 bottom: 4 left: 10); hMatchParent; - vFitContentLimited. + vFitContentLimited +] + +{ #category : #initialization } +GtSourceCoderContentElement >> newHeaderElement [ + | pragmas headerClass | + pragmas := Pragma + allNamed: #coderBreadcrumb: + from: self textualCoderViewModel class + to: Object. + + pragmas sorted: [ :aPragma | aPragma arguments first ]. + headerClass := pragmas + ifEmpty: [ self textualCoderViewModel headerElementClass ] + ifNotEmpty: [ + self textualCoderViewModel perform: pragmas first methodSelector ]. + + ^ headerClass new + constraintsDo: [ :c | + c margin: (BlInsets top: 2). + c frame horizontal alignLeft. + c frame vertical alignCenter ]; + yourself +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderContentElement >> onTextualCoderViewModelChanged [ + "Is sent when a new textualCoder view model is assigned to the element. + Note: #onTextualCoderViewModelChanged is sent before #subscribeToTextualCoderViewModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostTextualCoderViewModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" - self viewModel: (GtSourceCoderContentModel new) + textualCoderViewModel wantsHeader + ifTrue: [ + header ifNil: [ + header := self newHeaderElement. + self addChildFirst: header ]. + header textualCoderViewModel: textualCoderViewModel. + header visibility: BlVisibility visible ] + ifFalse: [ header ifNotNil: [ :aHeader | aHeader visibility: BlVisibility gone ] ] ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderContentModel.class.st b/src/GToolkit-Coder-UI/GtSourceCoderContentModel.class.st deleted file mode 100644 index 663b16cb9..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderContentModel.class.st +++ /dev/null @@ -1,37 +0,0 @@ -Class { - #name : #GtSourceCoderContentModel, - #superclass : #BrWidgetModel, - #instVars : [ - 'sourceCoder' - ], - #category : #'GToolkit-Coder-UI-Coder - Source' -} - -{ #category : #initialization } -GtSourceCoderContentModel >> initializeListeners [ - super initializeListeners. - - self when: GtSourceCoderContentCoderRequest reply: [ :aResponse | aResponse sourceCoder: self sourceCoder ] -] - -{ #category : #'private - notifying' } -GtSourceCoderContentModel >> notifySourceCoderChanged: aGtSourceCoderUIModel [ - self dispatchEvent: (GtSourceCoderContentCoderChanged new sourceCoder: aGtSourceCoderUIModel) -] - -{ #category : #accessing } -GtSourceCoderContentModel >> sourceCoder [ - - - ^ sourceCoder -] - -{ #category : #accessing } -GtSourceCoderContentModel >> sourceCoder: aGtSourceCoderUIModel [ - self - assert: [ aGtSourceCoderUIModel isKindOf: GtSourceCoderViewModel ] - description: [ 'Should be a Source Coder UI Model' ]. - - sourceCoder := aGtSourceCoderUIModel. - self notifySourceCoderChanged: aGtSourceCoderUIModel -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDebugActionId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDebugActionId.class.st index f013da5de..09f7cfd63 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderDebugActionId.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderDebugActionId.class.st @@ -6,7 +6,7 @@ A button to debug a piece of code Class { #name : #GtSourceCoderDebugActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDebugShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDebugShortcut.class.st new file mode 100644 index 000000000..c628e594f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderDebugShortcut.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtSourceCoderDebugShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI-Shortcuts' +} + +{ #category : #accessing } +GtSourceCoderDebugShortcut >> description [ + ^ 'Opens a debugger on the evaluation of the whole source code or just the selection.' +] + +{ #category : #accessing } +GtSourceCoderDebugShortcut >> initialize [ + super initialize. + + combination := BlKeyCombinationBuilder new primary shift d build +] + +{ #category : #accessing } +GtSourceCoderDebugShortcut >> name [ + ^ 'Debug' +] + +{ #category : #accessing } +GtSourceCoderDebugShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + GtCoderExecutionContextVariable + element: aBrEditorElement + do: [ aSourceCoderViewModel debug ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItActionId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItActionId.class.st index f0bf39f33..bf48c822d 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderDoItActionId.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItActionId.class.st @@ -6,7 +6,7 @@ A button to evaluate a source code Class { #name : #GtSourceCoderDoItActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoActionId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoActionId.class.st index 7d65b3310..246afd151 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoActionId.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoActionId.class.st @@ -6,7 +6,7 @@ A button to evaluate and inspect the result Class { #name : #GtSourceCoderDoItAndGoActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoAsynchronousActionId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoAsynchronousActionId.class.st new file mode 100644 index 000000000..67c6338f7 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoAsynchronousActionId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtSourceCoderDoItAndGoAsynchronousActionId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderDoItAndGoAsynchronousActionId >> asSymbol [ + ^ #'source-coder--doit-and-go-asynchronous-action' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoSerializedActionId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoSerializedActionId.class.st new file mode 100644 index 000000000..ebbe0ae95 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndGoSerializedActionId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtSourceCoderDoItAndGoSerializedActionId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderDoItAndGoSerializedActionId >> asSymbol [ + ^ #'source-coder--doit-and-go-serialized-action' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectAsynchronousShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectAsynchronousShortcut.class.st new file mode 100644 index 000000000..a77b4dfb7 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectAsynchronousShortcut.class.st @@ -0,0 +1,34 @@ +Class { + #name : #GtSourceCoderDoItAndInspectAsynchronousShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI-Shortcuts' +} + +{ #category : #accessing } +GtSourceCoderDoItAndInspectAsynchronousShortcut >> description [ + ^ 'Evaluates asynchronously the whole source code or just the selection and inspects the result.' +] + +{ #category : #initialization } +GtSourceCoderDoItAndInspectAsynchronousShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination primaryG +] + +{ #category : #accessing } +GtSourceCoderDoItAndInspectAsynchronousShortcut >> name [ + ^ 'Evaluate and inspect (asynchronous)' +] + +{ #category : #evaluation } +GtSourceCoderDoItAndInspectAsynchronousShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + GtCoderExecutionContextVariable + element: aBrEditorElement + do: [ + aSourceCoderViewModel hasAsyncExecution + ifTrue: [ + self inform: 'Cannot start async execution: already running' ] + ifFalse: [ + aSourceCoderViewModel doItAndGoAsynchronous ] ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectSerializedShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectSerializedShortcut.class.st new file mode 100644 index 000000000..f18fb63c0 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectSerializedShortcut.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtSourceCoderDoItAndInspectSerializedShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI-Shortcuts' +} + +{ #category : #accessing } +GtSourceCoderDoItAndInspectSerializedShortcut >> description [ + ^ 'Evaluates the whole source code or just the selection and inspects the serialized result.' +] + +{ #category : #initialization } +GtSourceCoderDoItAndInspectSerializedShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination primaryShiftG +] + +{ #category : #accessing } +GtSourceCoderDoItAndInspectSerializedShortcut >> name [ + ^ 'Evaluate and inspect serialized' +] + +{ #category : #evaluation } +GtSourceCoderDoItAndInspectSerializedShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + GtCoderExecutionContextVariable + element: aBrEditorElement + do: [ aSourceCoderViewModel doItAndGoSerialized ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectShortcut.class.st index 61db5bcf4..2e2ea3d44 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItAndInspectShortcut.class.st @@ -13,12 +13,8 @@ GtSourceCoderDoItAndInspectShortcut >> description [ GtSourceCoderDoItAndInspectShortcut >> initialize [ super initialize. - combination := BlKeyCombination primaryG -] - -{ #category : #accessing } -GtSourceCoderDoItAndInspectShortcut >> name [ - ^ 'Do it and inspect' + combination := BlKeyCombination primaryG. + name := 'Evaluate and inspect'. ] { #category : #evaluation } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItAsynchronousActionId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItAsynchronousActionId.class.st new file mode 100644 index 000000000..1510f2954 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItAsynchronousActionId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtSourceCoderDoItAsynchronousActionId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderDoItAsynchronousActionId >> asSymbol [ + ^ #'source-coder--doit-asynchronous-action' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItAsynchronousShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItAsynchronousShortcut.class.st new file mode 100644 index 000000000..55e0b5021 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItAsynchronousShortcut.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtSourceCoderDoItAsynchronousShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI-Shortcuts' +} + +{ #category : #accessing } +GtSourceCoderDoItAsynchronousShortcut >> description [ + ^ 'Evaluates the whole source code (method) or just the selection.' +] + +{ #category : #initialization } +GtSourceCoderDoItAsynchronousShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination primaryD +] + +{ #category : #accessing } +GtSourceCoderDoItAsynchronousShortcut >> name [ + ^ 'Evaluate (asynchronous)' +] + +{ #category : #evaluation } +GtSourceCoderDoItAsynchronousShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + GtCoderExecutionContextVariable + element: aBrEditorElement + do: [ aSourceCoderViewModel doItAsynchronous ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderDoItShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderDoItShortcut.class.st index 2e67e9b95..b211840b4 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderDoItShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderDoItShortcut.class.st @@ -13,12 +13,8 @@ GtSourceCoderDoItShortcut >> description [ GtSourceCoderDoItShortcut >> initialize [ super initialize. - combination := BlKeyCombination primaryD -] - -{ #category : #accessing } -GtSourceCoderDoItShortcut >> name [ - ^ 'Do it' + combination := BlKeyCombination primaryD. + name := 'Evaluate'. ] { #category : #evaluation } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderEditorAptitude.class.st b/src/GToolkit-Coder-UI/GtSourceCoderEditorAptitude.class.st deleted file mode 100644 index 4a90dc94d..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderEditorAptitude.class.st +++ /dev/null @@ -1,50 +0,0 @@ -Class { - #name : #GtSourceCoderEditorAptitude, - #superclass : #GtSourceCoderAptitude, - #instVars : [ - 'sourceCoderUIModel', - 'editorElement', - 'interactions', - 'completion', - 'actions' - ], - #category : #'GToolkit-Coder-UI-Coder - Source Look' -} - -{ #category : #accessing } -GtSourceCoderEditorAptitude >> editorElement [ - - - ^ editorElement -] - -{ #category : #initialization } -GtSourceCoderEditorAptitude >> initialize [ - super initialize. - - editorElement := self newEditorElement. - actions := self newActionsElement. - - self addChangeAddChildAs: { #content . GtSourceCoderEditorId uniqueInstance asSymbol } with: [ editorElement ]. - self addChangeAddChildAs: #(content actions) with: [ actions ]. - - self add: (BrLayoutResizerAptitude new inherit: editorElement) -] - -{ #category : #'instance creation' } -GtSourceCoderEditorAptitude >> newActionsElement [ - ^ GtCoderActionsElement new - margin: (BlInsets top: 5); - yourself -] - -{ #category : #'instance creation' } -GtSourceCoderEditorAptitude >> newEditorElement [ - ^ GtSourceCoderEditorElement new -] - -{ #category : #hooks } -GtSourceCoderEditorAptitude >> onCoderChanged: aGtSourceCoderUIModel [ - actions coderUIModel: aGtSourceCoderUIModel. - editorElement textualCoderViewModel: aGtSourceCoderUIModel -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderEditorElement.class.st b/src/GToolkit-Coder-UI/GtSourceCoderEditorElement.class.st index 3b6d1b188..dc710bfef 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderEditorElement.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderEditorElement.class.st @@ -1,16 +1,70 @@ Class { #name : #GtSourceCoderEditorElement, #superclass : #GtTextualCoderEditorElement, - #category : #'GToolkit-Coder-UI-Coder - Textual' + #category : #'GToolkit-Coder-UI-Coder - Source' } +{ #category : #views } +GtSourceCoderEditorElement >> gtEditorTextAttributesFor: aView [ + + ^ aView forward + title: 'Text attributes'; + priority: 41; + object: [ self editor ]; + view: #gtTextAttributesFor: +] + +{ #category : #views } +GtSourceCoderEditorElement >> gtEditorTextIntervalsFor: aView [ + + ^ aView forward + title: 'Text intervals'; + priority: 42; + object: [ self editor ]; + view: #gtTextIntervalsFor: +] + +{ #category : #views } +GtSourceCoderEditorElement >> gtView1For: aView [ + + ^ aView forward + title: 'Text intervals'; + priority: 42; + object: [ self editor ]; + view: #gtTextIntervalsFor: +] + +{ #category : #initalization } +GtSourceCoderEditorElement >> initialize [ + super initialize. + self editor doubleClickWordClassifier: GtSmalltalkSourceCoder doubleClickClassifier. + self editor movementWordClassifier: GtSmalltalkSourceCoder wordMovementClassifier. +] + { #category : #'api - textual coder view model' } GtSourceCoderEditorElement >> onTextualCoderViewModelChanged [ super onTextualCoderViewModelChanged. self textualCoderViewModel evaluationResult ifNil: [ evaluationHighlighter hideResult ] - ifNotNil: [ :aResult | evaluationHighlighter displayResult: aResult ]. - self textualCoderViewModel programCounterRange - ifNotNil: [ :aRange | self highlightPCRangeForInterval: aRange ] + ifNotNil: [ :aResult | evaluationHighlighter displayResult: aResult ] +] + +{ #category : #registration } +GtSourceCoderEditorElement >> onViewModelEvaluationStatusChanged: anAnnouncement [ + self + enqueueTask: (BlTaskAction new + action: [ anAnnouncement evaluationStatus + handleStatusChangedAnnouncement: anAnnouncement + sourceCoderElement: self. + self styleText ]) +] + +{ #category : #registration } +GtSourceCoderEditorElement >> registerCoderViewModelAnnouncementsFor: aGtSourceCoderUIModel [ + super registerCoderViewModelAnnouncementsFor: aGtSourceCoderUIModel. + aGtSourceCoderUIModel weak + when: GtSourceCoderViewModelEvaluationStatusChanged + send: #onViewModelEvaluationStatusChanged: + to: self ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderEditorId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderEditorId.class.st index 789743391..5568befb4 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderEditorId.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderEditorId.class.st @@ -6,10 +6,10 @@ Text editor within the source coder Class { #name : #GtSourceCoderEditorId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } GtSourceCoderEditorId >> asSymbol [ - ^ #editor + ^ #'source-coder--editor' ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderEditorInnerId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderEditorInnerId.class.st new file mode 100644 index 000000000..2b5382c5c --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderEditorInnerId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtSourceCoderEditorInnerId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderEditorInnerId >> asSymbol [ + ^ #'source-coder--editor-inner' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderElement.class.st b/src/GToolkit-Coder-UI/GtSourceCoderElement.class.st new file mode 100644 index 000000000..b6742d1eb --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderElement.class.st @@ -0,0 +1,60 @@ +Class { + #name : #GtSourceCoderElement, + #superclass : #GtTextualCoderElement, + #category : #'GToolkit-Coder-UI-Coder - Source' +} + +{ #category : #views } +GtSourceCoderElement >> gtEditorTextAttributesFor: aView [ + + ^ aView forward + title: 'Text attributes'; + priority: 41; + object: [ self editor ]; + view: #gtTextAttributesFor: +] + +{ #category : #views } +GtSourceCoderElement >> gtEditorTextIntervalsFor: aView [ + + ^ aView forward + title: 'Text intervals'; + priority: 42; + object: [ self editor ]; + view: #gtTextIntervalsFor: +] + +{ #category : #initalization } +GtSourceCoderElement >> initialize [ + super initialize. + self editor doubleClickWordClassifier: GtSmalltalkSourceCoder doubleClickClassifier. + self editor movementWordClassifier: GtSmalltalkSourceCoder wordMovementClassifier. +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderElement >> onTextualCoderViewModelChanged [ + super onTextualCoderViewModelChanged. + + self textualCoderViewModel evaluationResult + ifNil: [ evaluationHighlighter hideResult ] + ifNotNil: [ :aResult | evaluationHighlighter displayResult: aResult ] +] + +{ #category : #registration } +GtSourceCoderElement >> onViewModelEvaluationStatusChanged: anAnnouncement [ + self + enqueueTask: (BlTaskAction new + action: [ anAnnouncement evaluationStatus + handleStatusChangedAnnouncement: anAnnouncement + sourceCoderElement: self. + self styleText ]) +] + +{ #category : #registration } +GtSourceCoderElement >> registerCoderViewModelAnnouncementsFor: aGtSourceCoderUIModel [ + super registerCoderViewModelAnnouncementsFor: aGtSourceCoderUIModel. + aGtSourceCoderUIModel weak + when: GtSourceCoderViewModelEvaluationStatusChanged + send: #onViewModelEvaluationStatusChanged: + to: self +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderEmbeddedExpanderToggleId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderEmbeddedExpanderToggleId.class.st new file mode 100644 index 000000000..d7c06d0ef --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderEmbeddedExpanderToggleId.class.st @@ -0,0 +1,13 @@ +" +An element to toggle an embedded expandable element within the text of the source coder. An examples can be an expandable method or a baseline name. +" +Class { + #name : #GtSourceCoderEmbeddedExpanderToggleId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderEmbeddedExpanderToggleId >> asSymbol [ + ^ #'source-coder--embedded-expander-toggle' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderErrorAttribute.class.st b/src/GToolkit-Coder-UI/GtSourceCoderErrorAttribute.class.st index 32087e6e4..be47aa561 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderErrorAttribute.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderErrorAttribute.class.st @@ -2,7 +2,9 @@ Class { #name : #GtSourceCoderErrorAttribute, #superclass : #BrTextAdornmentAttribute, #instVars : [ - 'string' + 'string', + 'openOnFirstShow', + 'clickAction' ], #category : #'GToolkit-Coder-UI-Coder - Source Attributes' } @@ -16,20 +18,76 @@ GtSourceCoderErrorAttribute class >> for: aString [ yourself ] +{ #category : #accessing } +GtSourceCoderErrorAttribute >> buildPopupIn: anEditorElement aptitude: aptitude [ + | pane text textElement | + pane := BrVerticalPane new. + pane id: GtSourceCoderErrorContentElementId. + pane fitContentLimited. + string lines + do: [ :line | + text := line asRopedText + glamorousCodeSmallSize. + textElement := BlTextElement new. + textElement text: text. + textElement padding: (BlInsets all: 10). + textElement beFocusable. + textElement + when: BlClickEvent + do: [ :event | + event currentTarget fireEvent: BrDropdownHideWish new. + clickAction cull: anEditorElement cull: aptitude ]. + {BlKeyCombination escape. + BlKeyCombination enter. + BlKeyCombination backspace} + do: [ :each | + textElement + addShortcut: (BlShortcutWithAction new + combination: each; + repeatable: false; + action: [ :aShortcutEvent :aShortcutAction | + aShortcutEvent currentTarget fireEvent: BrDropdownHideWish new. + anEditorElement requestFocus ]) ]. + textElement enqueueTask: [ textElement requestFocus ] asBlTask. + pane addChild: textElement ]. + ^ pane +] + +{ #category : #accessing } +GtSourceCoderErrorAttribute >> clickAction [ + ^ clickAction +] + +{ #category : #accessing } +GtSourceCoderErrorAttribute >> clickAction: aBlock [ + clickAction := aBlock +] + { #category : #accessing } GtSourceCoderErrorAttribute >> doAffect: aTBrTextEditorTextualPiece in: anEditorElement [ - | text | - - text := string asRopedText - foreground: BrGlamorousColors defaultButtonTextColor; - medium; - glamorousCodeFontAndSmallSize. - - ^ BlTextElement new - text: text; - background: (BrGlamorousColors errorBackgroundColor alpha: 0.4); - padding: (BlInsets top: 3 left: 0 bottom: 3 right: 0); - margin: (BlInsets left: 2 right: 2); + | button aptitude | + button := self errorButton. + button id: GtSourceCoderErrorButtonElementId. + button + addAptitude: (aptitude := BrGlamorousWithExplicitDropdownAptitude + handle: [ self errorButton ] + content: [ self buildPopupIn: anEditorElement aptitude: aptitude ] + containerDo: [ :aMenuContainer | aMenuContainer border: (BlBorder paint: BrGlamorousColors errorBackgroundColor) ]). + openOnFirstShow + ifTrue: [ button + enqueueTask: [ openOnFirstShow := false. + button dispatchEvent: BrDropdownShowWish new ] asBlTask ]. + + ^ button +] + +{ #category : #accessing } +GtSourceCoderErrorAttribute >> errorButton [ + ^ BrButton new + background: BrGlamorousColors errorBackgroundColor; + size: 16 @ 16; + border: (BlBorder paint: Color transparent width: 2); + geometry: BlEllipseGeometry new; yourself ] @@ -37,3 +95,20 @@ GtSourceCoderErrorAttribute >> doAffect: aTBrTextEditorTextualPiece in: anEditor GtSourceCoderErrorAttribute >> for: aString [ string := aString ] + +{ #category : #accessing } +GtSourceCoderErrorAttribute >> initialize [ + super initialize. + openOnFirstShow := true. + clickAction := [ :textElement :dropDownAptitude | ] +] + +{ #category : #accessing } +GtSourceCoderErrorAttribute >> openOnFirstShow [ + ^ openOnFirstShow +] + +{ #category : #accessing } +GtSourceCoderErrorAttribute >> openOnFirstShow: aBoolean [ + openOnFirstShow := aBoolean +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderErrorButtonElementId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderErrorButtonElementId.class.st new file mode 100644 index 000000000..df9d86344 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderErrorButtonElementId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtSourceCoderErrorButtonElementId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderErrorButtonElementId >> asSymbol [ + ^ #'coder--error-button' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderErrorContentElementId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderErrorContentElementId.class.st new file mode 100644 index 000000000..164b76255 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderErrorContentElementId.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtSourceCoderErrorContentElementId, + #superclass : #BlElementUniqueId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderErrorContentElementId >> asSymbol [ + ^ #'coder--error-content' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderErrorFixItAttribute.class.st b/src/GToolkit-Coder-UI/GtSourceCoderErrorFixItAttribute.class.st new file mode 100644 index 000000000..d9960e4f5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderErrorFixItAttribute.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GtSourceCoderErrorFixItAttribute, + #superclass : #GtSourceCoderErrorAttribute, + #instVars : [ + 'fixItAttribute' + ], + #category : #'GToolkit-Coder-UI-FixIt' +} + +{ #category : #'as yet unclassified' } +GtSourceCoderErrorFixItAttribute >> buildPopupIn: anEditorElement aptitude: aptitude [ + | element | + element := fixItAttribute dropDownElementFor: anEditorElement. + element id: #'error-fix-it'. + element beFocusable. + {BlKeyCombination escape. + "BlKeyCombination enter." + BlKeyCombination backspace} + do: [ :key | + element + addShortcut: (BlShortcutWithAction new + combination: key; + repeatable: false; + action: [ :aShortcutEvent :aShortcutAction | + aShortcutEvent currentTarget fireEvent: BrDropdownHideWish new. + anEditorElement requestFocus ]) ]. + element + enqueueTask: [ element dispatchEvent: BrPopoverGainFocusWish new ] asBlTask. + ^ element +] + +{ #category : #accessing } +GtSourceCoderErrorFixItAttribute >> fixItAttribute [ + ^ fixItAttribute +] + +{ #category : #accessing } +GtSourceCoderErrorFixItAttribute >> fixItAttribute: anObject [ + fixItAttribute := anObject +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderEvaluationHighlighter.class.st b/src/GToolkit-Coder-UI/GtSourceCoderEvaluationHighlighter.class.st index 1d9255d49..342c17d20 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderEvaluationHighlighter.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderEvaluationHighlighter.class.st @@ -7,35 +7,20 @@ Class { { #category : #'private - ui' } GtSourceCoderEvaluationHighlighter >> addAttributesFor: anEvaluationResult within: aTextInterval [ | theAttributes | - theAttributes := #(). - - anEvaluationResult isSuccess - ifTrue: [ - | aResultButtonAttribute | - - aResultButtonAttribute := GtPlaygroundEvaluatedCodeButtonAttribute new - result: anEvaluationResult value; - coder: anEvaluationResult sourceCoder; "Pass the coder to get the spawn destination" - beNotOverwritableByStyler. - (self text - from: aTextInterval last - to: aTextInterval last) attribute: aResultButtonAttribute. + anEvaluationResult isSuccess + ifTrue: [ | aResultButtonAttribute | + aResultButtonAttribute := GtPlaygroundEvaluatedCodeButtonAttribute new + result: anEvaluationResult value; + coder: anEvaluationResult sourceCoder; + evaluationInterval: aTextInterval; + beNotOverwritableByStyler. "Pass the coder to get the spawn destination" - theAttributes := { aResultButtonAttribute } ] - ifFalse: [ - anEvaluationResult isSyntaxError - ifTrue: [ - theAttributes := { - BlTextDecorationAttribute new underline color: Color red; beNotOverwritableByStyler. - (GtSourceCoderErrorAttribute for: 'Syntax error') beNotOverwritableByStyler - }. + (self text from: aTextInterval last to: aTextInterval last) + attribute: aResultButtonAttribute. - self text - attributes: theAttributes - from: aTextInterval first - to: aTextInterval last ] ]. + theAttributes := {aResultButtonAttribute} ]. ^ theAttributes ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderEvaluationResultDisplayer.class.st b/src/GToolkit-Coder-UI/GtSourceCoderEvaluationResultDisplayer.class.st index 60f2d3101..fe3664a70 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderEvaluationResultDisplayer.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderEvaluationResultDisplayer.class.st @@ -3,9 +3,9 @@ Class { #superclass : #Object, #instVars : [ 'editorElement', - 'updateRequester', 'evaluationResult', - 'evaluationAttributes' + 'evaluationAttributes', + 'updater' ], #category : #'GToolkit-Coder-UI-Coder - Source' } @@ -24,7 +24,7 @@ GtSourceCoderEvaluationResultDisplayer >> displayResult: anEvaluationResult [ evaluationResult = anEvaluationResult ifTrue: [ ^ self ]. evaluationResult := anEvaluationResult. - updateRequester requestUpdate + updater requestUpdate ] { #category : #'api - evaluation result' } @@ -47,7 +47,7 @@ GtSourceCoderEvaluationResultDisplayer >> editorElement [ { #category : #accessing } GtSourceCoderEvaluationResultDisplayer >> editorElement: anObject [ editorElement := anObject. - updateRequester element: editorElement + updater element: editorElement ] { #category : #accessing } @@ -60,7 +60,7 @@ GtSourceCoderEvaluationResultDisplayer >> hideResult [ evaluationResult ifNil: [ ^ self ]. evaluationResult := nil. - updateRequester requestUpdate + updater requestUpdate ] { #category : #'api - evaluation result' } @@ -78,8 +78,8 @@ GtSourceCoderEvaluationResultDisplayer >> initialize [ editorElement := nil. evaluationResult := nil. - updateRequester := GtPhlowUpdateRequester new. - updateRequester action: [ self primitiveUpdateResult ]. + updater := BrElementUpdater new. + updater action: [ self primitiveUpdateResult ]. evaluationAttributes := #() ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderExpandedContentElement.class.st b/src/GToolkit-Coder-UI/GtSourceCoderExpandedContentElement.class.st index 08034a3bc..a7563dc63 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderExpandedContentElement.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderExpandedContentElement.class.st @@ -1,5 +1,78 @@ Class { #name : #GtSourceCoderExpandedContentElement, #superclass : #GtSourceCoderContentElement, + #instVars : [ + 'editorElement', + 'actions', + 'tags' + ], #category : #'GToolkit-Coder-UI-Coder - Source' } + +{ #category : #accessing } +GtSourceCoderExpandedContentElement >> actions [ + ^ actions +] + +{ #category : #'focus requesting' } +GtSourceCoderExpandedContentElement >> focusTarget [ + ^ editorElement focusTarget +] + +{ #category : #initialization } +GtSourceCoderExpandedContentElement >> initialize [ + super initialize. + + editorElement := self newEditorElement. + actions := self newActionsElement. + + actions visibility: BlVisibility hidden. + + self + addAptitude: (BrStyleCommonAptitude new + when: BlElementState hovered & BlElementState focused not + style: [ :aStyle | + aStyle + do: [ :aWidget | actions visibility: BlVisibility visible ] + after: [ :aWidget | actions visibility: BlVisibility hidden ] ]; + focused: [ :aStyle | + aStyle + do: [ :aWidget | actions visibility: BlVisibility visible ] + after: [ :aWidget | actions visibility: BlVisibility hidden ] ]). + + self + addChildren: {editorElement. + actions}. + self addAptitude: (BrLayoutResizerAptitude new inherit: editorElement) +] + +{ #category : #initialization } +GtSourceCoderExpandedContentElement >> initializeEditorElement: anElement [ + editorElement initializeEditorElement: anElement +] + +{ #category : #'instance creation' } +GtSourceCoderExpandedContentElement >> newActionsElement [ + ^ GtSourceCoderActionsElement new + margin: (BlInsets top: 5); + id: #actions; + yourself +] + +{ #category : #'instance creation' } +GtSourceCoderExpandedContentElement >> newEditorElement [ + ^ GtSourceCoderElement new +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderExpandedContentElement >> onTextualCoderViewModelChanged [ + super onTextualCoderViewModelChanged. + + actions coderViewModel: textualCoderViewModel. + editorElement textualCoderViewModel: textualCoderViewModel +] + +{ #category : #initialization } +GtSourceCoderExpandedContentElement >> showScrollbars [ + editorElement showScrollbars +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderExpandedOnlyElement.class.st b/src/GToolkit-Coder-UI/GtSourceCoderExpandedOnlyElement.class.st new file mode 100644 index 000000000..60b72c808 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderExpandedOnlyElement.class.st @@ -0,0 +1,98 @@ +Class { + #name : #GtSourceCoderExpandedOnlyElement, + #superclass : #BlElement, + #traits : 'TBrLayoutResizable + TGtWithTextualCoderViewModel', + #classTraits : 'TBrLayoutResizable classTrait + TGtWithTextualCoderViewModel classTrait', + #instVars : [ + 'expandedElement', + 'modificationIndicator' + ], + #category : #'GToolkit-Coder-UI-Coder - Source' +} + +{ #category : #converting } +GtSourceCoderExpandedOnlyElement >> asVerticallyResizableDo: aBlock [ + ^ self +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderExpandedOnlyElement >> coderViewModel [ + ^ self textualCoderViewModel +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderExpandedOnlyElement >> coderViewModel: aViewModel [ + self textualCoderViewModel: aViewModel +] + +{ #category : #initialization } +GtSourceCoderExpandedOnlyElement >> defaultLayout [ + ^ BlLinearLayout vertical +] + +{ #category : #'focus requesting' } +GtSourceCoderExpandedOnlyElement >> focusTarget [ + ^ expandedElement focusTarget +] + +{ #category : #initialization } +GtSourceCoderExpandedOnlyElement >> initialize [ + super initialize. + self + hMatchParent; + vFitContent. + + expandedElement := self newExpandedElement. + modificationIndicator := self newModificationIndicator. + modificationIndicator visibility: BlVisibility gone. + + self addChild: expandedElement. + self addChild: modificationIndicator. + + self addAptitude: (BrLayoutResizerAptitude new common: expandedElement) +] + +{ #category : #initialization } +GtSourceCoderExpandedOnlyElement >> newExpandedElement [ + ^ GtSourceCoderExpandedContentElement new + showScrollbars; + yourself +] + +{ #category : #initialization } +GtSourceCoderExpandedOnlyElement >> newModificationIndicator [ + ^ GtSourceCoderModificationIndicator new + withVerticalLeftIgnoredLayout; + margin: (BlInsets all: 0) +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderExpandedOnlyElement >> onTextualCoderViewModelChanged [ + "Is sent when a new textualCoder view model is assigned to the element. + Note: #onTextualCoderViewModelChanged is sent before #subscribeToTextualCoderViewModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostTextualCoderViewModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + + self textualCoderViewModel expanded: true. + expandedElement textualCoderViewModel: self textualCoderViewModel. + modificationIndicator textualCoderViewModel: self textualCoderViewModel. + + self updateElement +] + +{ #category : #'private - updating' } +GtSourceCoderExpandedOnlyElement >> updateElement [ + self textualCoderViewModel coderLook + ifNotNil: [ :aNewAptitude | + expandedElement userData + at: #coderAptitude + ifPresent: [ :anOldAptitude | expandedElement removeAptitude: anOldAptitude ] + ifAbsent: [ ]. + + expandedElement addAptitude: aNewAptitude. + + expandedElement userData + at: #coderAptitude + put: aNewAptitude ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderExpanderToggleId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderExpanderToggleId.class.st index 6dfc7b3d1..17ea6be93 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderExpanderToggleId.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderExpanderToggleId.class.st @@ -1,12 +1,11 @@ " -An element to toggle an expander's state on click - - +An element to toggle {{gtClass:BrExpander}}'s state on click. In a context of a coder, an expander is a sidebar element that allows to collapse/expand a coder. +Do not confuse with an embedded expander within the text which is usually represented by a triangle. " Class { #name : #GtSourceCoderExpanderToggleId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderExtractMethodShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderExtractMethodShortcut.class.st index 4251d09e9..a754ccb7a 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderExtractMethodShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderExtractMethodShortcut.class.st @@ -22,6 +22,7 @@ GtSourceCoderExtractMethodShortcut >> name [ ] { #category : #evaluation } -GtSourceCoderExtractMethodShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ - aGtSourceCoder extractMethod: aBrEditorElement +GtSourceCoderExtractMethodShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + (GtExtractMethodToComponentController new + sourceCoderViewModel: aSourceCoderViewModel) executeIn: aBrEditorElement ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderExtractTempVarContextMenuItemId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderExtractTempVarContextMenuItemId.class.st new file mode 100644 index 000000000..cba04ee9b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderExtractTempVarContextMenuItemId.class.st @@ -0,0 +1,13 @@ +" +A context menu action to extract a temporary variable +" +Class { + #name : #GtSourceCoderExtractTempVarContextMenuItemId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderExtractTempVarContextMenuItemId >> asSymbol [ + ^ #'context-menu--extract-temp' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderFocusChanged.class.st b/src/GToolkit-Coder-UI/GtSourceCoderFocusChanged.class.st deleted file mode 100644 index b503e81cc..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderFocusChanged.class.st +++ /dev/null @@ -1,29 +0,0 @@ -Class { - #name : #GtSourceCoderFocusChanged, - #superclass : #Announcement, - #instVars : [ - 'source', - 'focused' - ], - #category : #'GToolkit-Coder-UI-Coder - Source Events' -} - -{ #category : #accessing } -GtSourceCoderFocusChanged >> focused [ - ^ focused -] - -{ #category : #accessing } -GtSourceCoderFocusChanged >> focused: anObject [ - focused := anObject -] - -{ #category : #accessing } -GtSourceCoderFocusChanged >> source [ - ^ source -] - -{ #category : #accessing } -GtSourceCoderFocusChanged >> source: anObject [ - source := anObject -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderFoldNoiseShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderFoldNoiseShortcut.class.st new file mode 100644 index 000000000..914794df6 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderFoldNoiseShortcut.class.st @@ -0,0 +1,37 @@ +Class { + #name : #GtSourceCoderFoldNoiseShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI-Shortcuts' +} + +{ #category : #accessing } +GtSourceCoderFoldNoiseShortcut >> description [ + ^ 'Folds instrumentation messages' +] + +{ #category : #initialization } +GtSourceCoderFoldNoiseShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination primaryShiftN +] + +{ #category : #accessing } +GtSourceCoderFoldNoiseShortcut >> name [ + ^ 'Fold Noise' +] + +{ #category : #evaluation } +GtSourceCoderFoldNoiseShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent requesterObject: aRequester [ + | folding | + folding := nil. + aBrTextEditor + styledTextAttributesDo: [ :someAttributes | + someAttributes + detect: [ :eachAttribute | + eachAttribute class == GtTextFolderAttribute + or: [ eachAttribute class == BrGlamorousFolderTextAttributeV2 ] ] + ifFound: [ :aFoundAttribute | + folding ifNil: [ folding := aFoundAttribute isFolded not ]. + aFoundAttribute isFolded: folding fromTextEditorElement: aBrEditorElement ] ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderFormatShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderFormatShortcut.class.st index 44509a72a..b2f11e18b 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderFormatShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderFormatShortcut.class.st @@ -22,6 +22,6 @@ GtSourceCoderFormatShortcut >> name [ ] { #category : #evaluation } -GtSourceCoderFormatShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ - aGtSourceCoder format +GtSourceCoderFormatShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent requesterObject: aRequester [ + aGtSourceCoder formatWithRequesterObject: aRequester ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderId.class.st new file mode 100644 index 000000000..e6b17bc23 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderId.class.st @@ -0,0 +1,13 @@ +" +An element that represents a source coder +" +Class { + #name : #GtSourceCoderId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtSourceCoderId >> asSymbol [ + ^ #'source-coder' +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderModificationIndicator.class.st b/src/GToolkit-Coder-UI/GtSourceCoderModificationIndicator.class.st new file mode 100644 index 000000000..deba2e5e5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderModificationIndicator.class.st @@ -0,0 +1,97 @@ +Class { + #name : #GtSourceCoderModificationIndicator, + #superclass : #BlElement, + #traits : 'TBrLayoutResizable + TGtWithTextualCoderViewModel', + #classTraits : 'TBrLayoutResizable classTrait + TGtWithTextualCoderViewModel classTrait', + #category : #'GToolkit-Coder-UI-Coder - Source' +} + +{ #category : #'api - visibility' } +GtSourceCoderModificationIndicator >> hide [ + self visibility: BlVisibility gone +] + +{ #category : #initialization } +GtSourceCoderModificationIndicator >> initialize [ + super initialize. + + self background: self theme status changesBackgroundColor. + self preventMeAndChildrenMouseEvents. + self id: GtTextualCoderModificationIndicatorId. + self + withAsyncPromiseDo: [ :anElementPromise | + anElementPromise + whenSuccess: [ :anIndicator :isModified | self onPromiseSuccess: isModified ]; + whenError: [ :anIndicator :anException | self onPromiseError: anException ] ] +] + +{ #category : #'private - event handling' } +GtSourceCoderModificationIndicator >> onPromiseError: anException [ +] + +{ #category : #'private - event handling' } +GtSourceCoderModificationIndicator >> onPromiseSuccess: isModified [ + isModified ifTrue: [ self show ] ifFalse: [ self hide ] +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderModificationIndicator >> onTextualCoderViewModelChanged [ + "Is sent when a new textualCoder view model is assigned to the element. + Note: #onTextualCoderViewModelChanged is sent before #subscribeToTextualCoderViewModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostTextualCoderViewModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + + self updateElement +] + +{ #category : #'private - event handling' } +GtSourceCoderModificationIndicator >> onViewModelTextChanged: anAnnouncement [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + anAnnouncement textualCoderViewModel = self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction enqueueElement: self action: [ self updateElement ] +] + +{ #category : #'api - visibility' } +GtSourceCoderModificationIndicator >> show [ + self visibility: BlVisibility visible +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderModificationIndicator >> subscribeToTextualCoderViewModel [ + "Is sent after a new textualCoder view model is assigned to the element. + It is required to unsubscribe from the view model or domain model by implementing + #unsubscribeFromTextualCoderViewModel if elements subscribe to them" + + self textualCoderViewModel weak + when: GtTextualCoderViewModelTextChanged + send: #onViewModelTextChanged: + to: self +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderModificationIndicator >> unsubscribeFromTextualCoderViewModel [ + "Is sent before a new textualCoder view model is assigned to the element. + Elements that subscribe to textualCoder view model in domain model are required to implement this methods." + + self textualCoderViewModel unsubscribe: self +] + +{ #category : #'api - textual coder view model' } +GtSourceCoderModificationIndicator >> updateElement [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + self asyncPromise promise: self textualCoderViewModel isTextModifiedPromise +] + +{ #category : #'api - initialization' } +GtSourceCoderModificationIndicator >> withVerticalLeftIgnoredLayout [ + self + constraintsDo: [ :c | + c ignoreByLayout. + c ignored horizontal alignLeft. + c horizontal exact: 5. + c vertical matchParent. + c margin: (BlInsets left: 8) ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderMoveStatementDownShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderMoveStatementDownShortcut.class.st new file mode 100644 index 000000000..395ca17b9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderMoveStatementDownShortcut.class.st @@ -0,0 +1,35 @@ +Class { + #name : #GtSourceCoderMoveStatementDownShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI' +} + +{ #category : #accessing } +GtSourceCoderMoveStatementDownShortcut >> description [ + ^ 'Moves a statement down in the statement list.' +] + +{ #category : #accessing } +GtSourceCoderMoveStatementDownShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination builder primary arrowDown build +] + +{ #category : #accessing } +GtSourceCoderMoveStatementDownShortcut >> name [ + ^ 'Move statement down' +] + +{ #category : #accessing } +GtSourceCoderMoveStatementDownShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString :aSelectionInterval | + aSourceCoderViewModel coder + moveStatementsOrCascadesDownWithin: aSelectionInterval + in: aSourceCoderViewModel ] + orCursorPositionDo: [ :aCursorTextPosition | + aSourceCoderViewModel coder + moveStatementOrCascadeDownAt: aCursorTextPosition + in: aSourceCoderViewModel ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderMoveStatementUpShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderMoveStatementUpShortcut.class.st new file mode 100644 index 000000000..12ee0523e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderMoveStatementUpShortcut.class.st @@ -0,0 +1,35 @@ +Class { + #name : #GtSourceCoderMoveStatementUpShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI' +} + +{ #category : #accessing } +GtSourceCoderMoveStatementUpShortcut >> description [ + ^ 'Moves a statement up in the statement list.' +] + +{ #category : #accessing } +GtSourceCoderMoveStatementUpShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination builder primary arrowUp build +] + +{ #category : #accessing } +GtSourceCoderMoveStatementUpShortcut >> name [ + ^ 'Move statement up' +] + +{ #category : #accessing } +GtSourceCoderMoveStatementUpShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString :aSelectionInterval | + aSourceCoderViewModel coder + moveStatementsOrCascadesUpWithin: aSelectionInterval + in: aSourceCoderViewModel ] + orCursorPositionDo: [ :aCursorTextPosition | + aSourceCoderViewModel coder + moveStatementOrCascadeUpAt: aCursorTextPosition + in: aSourceCoderViewModel ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderPlayAndInspectShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderPlayAndInspectShortcut.class.st index 783c294a3..69f3ac0d2 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderPlayAndInspectShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderPlayAndInspectShortcut.class.st @@ -22,11 +22,13 @@ GtSourceCoderPlayAndInspectShortcut >> name [ ] { #category : #evaluation } -GtSourceCoderPlayAndInspectShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ +GtSourceCoderPlayAndInspectShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent requesterObject: aRequester [ GtCoderExecutionContextVariable element: aBrEditorElement - do: [ - "only execute if there is selection" - aBrTextEditor hasSelection - ifTrue: [ GtSourceCoderDoItAndInspectShortcut new performDueTo: aShortcutEvent ] ] + do: [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString | + "only execute if there is selection" + GtSourceCoderDoItAndInspectShortcut new performDueTo: aShortcutEvent ] + orCursorPositionDo: [ :aStringPosition | ] ]. ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderPlayShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderPlayShortcut.class.st index c38d0de4e..cf5556d9a 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderPlayShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderPlayShortcut.class.st @@ -22,11 +22,13 @@ GtSourceCoderPlayShortcut >> name [ ] { #category : #evaluation } -GtSourceCoderPlayShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ +GtSourceCoderPlayShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent requesterObject: aRequester [ GtCoderExecutionContextVariable element: aBrEditorElement do: [ - "only execute if there is selection" - aBrTextEditor hasSelection - ifTrue: [ GtSourceCoderDoItShortcut new performDueTo: aShortcutEvent ] ] + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString | + "only execute if there is selection" + GtSourceCoderDoItShortcut new performDueTo: aShortcutEvent ] + orCursorPositionDo: [ :aStringPosition | ] ]. ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderPrintItShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderPrintItShortcut.class.st index 3a98a6016..a45f97e14 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderPrintItShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderPrintItShortcut.class.st @@ -13,12 +13,8 @@ GtSourceCoderPrintItShortcut >> description [ GtSourceCoderPrintItShortcut >> initialize [ super initialize. - combination := BlKeyCombination primaryP -] - -{ #category : #accessing } -GtSourceCoderPrintItShortcut >> name [ - ^ 'Print' + combination := BlKeyCombination primaryP. + name := 'Print'. ] { #category : #evaluation } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderProcessInDebuggerSignal.class.st b/src/GToolkit-Coder-UI/GtSourceCoderProcessInDebuggerSignal.class.st new file mode 100644 index 000000000..85828d8ce --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderProcessInDebuggerSignal.class.st @@ -0,0 +1,42 @@ +Class { + #name : #GtSourceCoderProcessInDebuggerSignal, + #superclass : #BeaconSignal, + #instVars : [ + 'process', + 'space' + ], + #category : #'GToolkit-Coder-UI-Signals' +} + +{ #category : #testing } +GtSourceCoderProcessInDebuggerSignal class >> gtNormalOperationSignal [ + "Answer a Boolean indicating whether this signal is generated as part of normal operations. + See {{gtMethod:BeaconSignal class>>gtNormalOperationSignal}} for a description" + + ^ true. +] + +{ #category : #printing } +GtSourceCoderProcessInDebuggerSignal >> printOneLineContentsOn: aStream [ + aStream print: space +] + +{ #category : #accessing } +GtSourceCoderProcessInDebuggerSignal >> process [ + ^ process +] + +{ #category : #accessing } +GtSourceCoderProcessInDebuggerSignal >> process: anObject [ + process := anObject +] + +{ #category : #accessing } +GtSourceCoderProcessInDebuggerSignal >> space [ + ^ space +] + +{ #category : #accessing } +GtSourceCoderProcessInDebuggerSignal >> space: anObject [ + space := anObject +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderProfileActionId.class.st b/src/GToolkit-Coder-UI/GtSourceCoderProfileActionId.class.st index 3be8509ce..1f3f2f54f 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderProfileActionId.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderProfileActionId.class.st @@ -1,12 +1,10 @@ " A button to profile a piece of code - - " Class { #name : #GtSourceCoderProfileActionId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderRequester.extension.st b/src/GToolkit-Coder-UI/GtSourceCoderRequester.extension.st new file mode 100644 index 000000000..be3f55c25 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderRequester.extension.st @@ -0,0 +1,14 @@ +Extension { #name : #GtSourceCoderRequester } + +{ #category : #'*GToolkit-Coder-UI' } +GtSourceCoderRequester >> notifyShowDebuggerRequest: aDebugSession dueTo: anException sourceString: aSourceString sourceInterval: aSourceInterval evaluationInfo: anEvaluationInfo [ + "Return true if announcement was handled (and debugger displayed in some way). + Return false otherwise." + + ^ self coder + notifyShowDebuggerRequest: aDebugSession + dueTo: anException + sourceString: aSourceString + sourceInterval: aSourceInterval + evaluationInfo: anEvaluationInfo +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderSaveShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderSaveShortcut.class.st index 8b228ef82..d71934b02 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderSaveShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderSaveShortcut.class.st @@ -23,5 +23,5 @@ GtSourceCoderSaveShortcut >> name [ { #category : #evaluation } GtSourceCoderSaveShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ - aSourceCoderViewModel save + aSourceCoderViewModel requestSave ] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderSearchTextShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderSearchTextShortcut.class.st new file mode 100644 index 000000000..2f339d057 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderSearchTextShortcut.class.st @@ -0,0 +1,27 @@ +Class { + #name : #GtSourceCoderSearchTextShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI-Shortcuts' +} + +{ #category : #accessing } +GtSourceCoderSearchTextShortcut >> description [ + ^ 'Search text' +] + +{ #category : #initialization } +GtSourceCoderSearchTextShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination primaryF +] + +{ #category : #accessing } +GtSourceCoderSearchTextShortcut >> name [ + ^ 'Search' +] + +{ #category : #evaluation } +GtSourceCoderSearchTextShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + aSourceCoderViewModel requestSearch +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderSelectStatementDownShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderSelectStatementDownShortcut.class.st new file mode 100644 index 000000000..f1e9a393b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderSelectStatementDownShortcut.class.st @@ -0,0 +1,35 @@ +Class { + #name : #GtSourceCoderSelectStatementDownShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI' +} + +{ #category : #accessing } +GtSourceCoderSelectStatementDownShortcut >> description [ + ^ 'Selects current statement or expands selection to next statement.' +] + +{ #category : #accessing } +GtSourceCoderSelectStatementDownShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination builder shift primary arrowDown build +] + +{ #category : #accessing } +GtSourceCoderSelectStatementDownShortcut >> name [ + ^ 'Selection statement down' +] + +{ #category : #accessing } +GtSourceCoderSelectStatementDownShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString :aSelectionInterval | + aSourceCoderViewModel coder + expandStatementSelection: aSelectionInterval + downIn: aSourceCoderViewModel ] + orCursorPositionDo: [ :aCursorTextPosition | + aSourceCoderViewModel coder + selectStatementOrCascadeAt: aCursorTextPosition + in: aSourceCoderViewModel ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderSelectStatementUpShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderSelectStatementUpShortcut.class.st new file mode 100644 index 000000000..93ed4854b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderSelectStatementUpShortcut.class.st @@ -0,0 +1,35 @@ +Class { + #name : #GtSourceCoderSelectStatementUpShortcut, + #superclass : #GtSourceCoderShortcut, + #category : #'GToolkit-Coder-UI' +} + +{ #category : #accessing } +GtSourceCoderSelectStatementUpShortcut >> description [ + ^ 'Selects current statement or expands selection to previous statement.' +] + +{ #category : #accessing } +GtSourceCoderSelectStatementUpShortcut >> initialize [ + super initialize. + + combination := BlKeyCombination builder shift primary arrowUp build +] + +{ #category : #accessing } +GtSourceCoderSelectStatementUpShortcut >> name [ + ^ 'Selection statement up' +] + +{ #category : #accessing } +GtSourceCoderSelectStatementUpShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coderViewModel: aSourceCoderViewModel dueTo: aShortcutEvent [ + aBrEditorElement + selectedStringAndIntervalDo: [ :aSelectedString :aSelectionInterval | + aSourceCoderViewModel coder + expandStatementSelection: aSelectionInterval + upIn: aSourceCoderViewModel ] + orCursorPositionDo: [ :aCursorTextPosition | + aSourceCoderViewModel coder + selectStatementOrCascadeAt: aCursorTextPosition + in: aSourceCoderViewModel ] +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderShortcut.class.st b/src/GToolkit-Coder-UI/GtSourceCoderShortcut.class.st index 0e7e4568c..49cf6a515 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderShortcut.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderShortcut.class.st @@ -6,13 +6,15 @@ Class { { #category : #private } GtSourceCoderShortcut >> forEditor: aBrTextEditor selectedStringDo: aSelectedTextBlock orCursorStringPositionDo: aCursorTextPositionBlock [ + self deprecated: 'Use #selectedStringDo:orCursorStringPositionDo: text editor element API instead'. + aBrTextEditor selectedText ifEmpty: [ aBrTextEditor hasCursors ifTrue: [ | aCursorPosition aTextPosition | aCursorPosition := aBrTextEditor cursors first position. - aTextPosition := (aCursorPosition + 1) min: aBrTextEditor text size. + aTextPosition := aCursorPosition min: aBrTextEditor text size max: 1. aCursorTextPositionBlock value: aTextPosition ] ] ifNotEmpty: [ :aSelectedText | aSelectedTextBlock value: aSelectedText asString ] @@ -20,6 +22,8 @@ GtSourceCoderShortcut >> forEditor: aBrTextEditor selectedStringDo: aSelectedTex { #category : #private } GtSourceCoderShortcut >> forEditor: aBrTextEditor selectionIntervalDo: aSelectionIntervalBlock orCursorStringPositionDo: aCursorTextPositionBlock [ + self deprecated: 'Use #selectedStringDo:orCursorStringPositionDo: text editor element API instead'. + aBrTextEditor hasSelection ifTrue: [ | aFromIndex aToIndex | @@ -31,13 +35,13 @@ GtSourceCoderShortcut >> forEditor: aBrTextEditor selectionIntervalDo: aSelectio | aCursorPosition aTextPosition | aCursorPosition := aBrTextEditor cursors first position. - aTextPosition := (aCursorPosition + 1) min: aBrTextEditor text size. + aTextPosition := aCursorPosition min: aBrTextEditor text size max: 1. aCursorTextPositionBlock value: aTextPosition ] ] ] { #category : #evaluation } -GtSourceCoderShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent [ +GtSourceCoderShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElement coder: aGtSourceCoder dueTo: aShortcutEvent requesterObject: aRequester [ self subclassResponsibility ] @@ -48,6 +52,7 @@ GtSourceCoderShortcut >> performOnEditor: aBrTextEditor element: aBrEditorElemen element: aBrEditorElement coder: aSourceCoderViewModel coder dueTo: aShortcutEvent + requesterObject: aSourceCoderViewModel ] { #category : #evaluation } diff --git a/src/GToolkit-Coder-UI/GtSourceCoderSpawnDestinationAptitude.class.st b/src/GToolkit-Coder-UI/GtSourceCoderSpawnDestinationAptitude.class.st deleted file mode 100644 index 7a40edf2a..000000000 --- a/src/GToolkit-Coder-UI/GtSourceCoderSpawnDestinationAptitude.class.st +++ /dev/null @@ -1,57 +0,0 @@ -" -I add to a coder a way to change the destination where inspected objects are shown. - -Each coder holds to its destination. Available destinations are modeled as subclasses of {{gtClass:GtPhlowSpawnDesiredDestination}} - - -" -Class { - #name : #GtSourceCoderSpawnDestinationAptitude, - #superclass : #GtSourceCoderAptitude, - #instVars : [ - 'switchButton' - ], - #category : #'GToolkit-Coder-UI-Coder - Source Look' -} - -{ #category : #initialization } -GtSourceCoderSpawnDestinationAptitude >> initialize [ - super initialize. - - switchButton := self newSwitchButton. - - self addChangeAddChildAs: #(content switchButton) with: [ switchButton ] -] - -{ #category : #'instance creation' } -GtSourceCoderSpawnDestinationAptitude >> newSwitchButton [ - ^ BrButton new - aptitude: BrGlamorousButtonWithIconAptitude - BrGlamorousButtonExteriorAptitude; - action: [ self switchSpawnDestination ]; - label: 'Change the place where inspected objects are shown.'; - constraintsDo: [ :c | - c ignoreByLayout. - c ignored horizontal alignRight. - c ignored vertical alignBottom ]; - zIndex: 10 -] - -{ #category : #hooks } -GtSourceCoderSpawnDestinationAptitude >> onCoderChanged: aGtSourceCoderUIModel [ - self switchButton - icon: aGtSourceCoderUIModel coder spawnDestination icon -] - -{ #category : #accessing } -GtSourceCoderSpawnDestinationAptitude >> switchButton [ - ^ switchButton -] - -{ #category : #'instance creation' } -GtSourceCoderSpawnDestinationAptitude >> switchSpawnDestination [ - | aSourceCoder | - - aSourceCoder := self sourceCoderUIModel coder. - aSourceCoder spawnDestination: aSourceCoder spawnDestination followingDestination. - self switchButton icon: aSourceCoder spawnDestination icon -] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderViewModel.class.st b/src/GToolkit-Coder-UI/GtSourceCoderViewModel.class.st index 653b1cbc5..00a53ccaf 100644 --- a/src/GToolkit-Coder-UI/GtSourceCoderViewModel.class.st +++ b/src/GToolkit-Coder-UI/GtSourceCoderViewModel.class.st @@ -2,11 +2,22 @@ Class { #name : #GtSourceCoderViewModel, #superclass : #GtTextualCoderViewModel, #instVars : [ - 'evaluationResult' + 'evaluationResult', + 'evaluationStatus' ], #category : #'GToolkit-Coder-UI-Coder - Source Model' } +{ #category : #converting } +GtSourceCoderViewModel >> asCoderRequesterObject [ + ^ GtCoderViewModelRequester new coderViewModel: self +] + +{ #category : #accessing } +GtSourceCoderViewModel >> asynchronusExecutionStrategy [ + ^ nil +] + { #category : #'api - actions' } GtSourceCoderViewModel >> debug [ ^ self selection isEmpty @@ -52,9 +63,11 @@ GtSourceCoderViewModel >> doIt: aTextInterval [ GtSourceCoderViewModel >> doIt: aTextInterval requestedBy: aRequesterObject [ "Evaluate source code within given text interval and return the result" - ^ self coderModel doItInContext: (self evaluationContext - from: aTextInterval first to: aTextInterval last; - requesterObject: aRequesterObject) + ^ self handleCoderEvaluationUnhandledErrorDuring: [ + self coderModel doItInContext: ( + self evaluationContext + from: aTextInterval first to: aTextInterval last; + requesterObject: aRequesterObject) ] ] { #category : #'api - actions' } @@ -64,13 +77,25 @@ GtSourceCoderViewModel >> doItAll [ ^ self doItAllRequestedBy: self ] +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAllAsynchronousRequestedBy: aRequesterObject [ + "Evaluate the whole source code independent from the selection and return the result" + + ^ self handleCoderEvaluationUnhandledErrorDuring: [ + self doItAsynchronousInContext: (self evaluationContext + all; + requesterObject: aRequesterObject) ] +] + { #category : #'api - actions' } GtSourceCoderViewModel >> doItAllRequestedBy: aRequesterObject [ "Evaluate the whole source code independent from the selection and return the result" + - ^ self coder doItInContext: (self evaluationContext - all; - requesterObject: aRequesterObject) + ^ self handleCoderEvaluationUnhandledErrorDuring: [ + self coder doItInContext: (self evaluationContext + all; + requesterObject: aRequesterObject) ] ] { #category : #'api - actions' } @@ -86,14 +111,137 @@ GtSourceCoderViewModel >> doItAndGo [ GtSourceCoderViewModel >> doItAndGo: aTextInterval [ "Evaluate a piece of source code within an interval and inspect a result." - ^ self coder doItAndGoInContext: (self evaluationContext from: aTextInterval first to: aTextInterval last) + ^ self handleCoderEvaluationUnhandledErrorDuring: [ + self coder doItAndGoInContext: ( + self evaluationContext + from: aTextInterval first + to: aTextInterval last) ] ] { #category : #'api - actions' } GtSourceCoderViewModel >> doItAndGoAll [ "Evaluate the whole source code and inspect the result" - ^ self coder doItAndGoInContext: self evaluationContext all + ^ self handleCoderEvaluationUnhandledErrorDuring: [ + self coder doItAndGoInContext: self evaluationContext all ] +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAndGoAllAsynchronous [ + "Evaluate the whole source code and inspect the result" + + ^ self handleCoderEvaluationUnhandledErrorDuring: [ + self doItAndGoAsynchronousInContext: (self evaluationContext + all; + yourself) ] +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAndGoAllSerialized [ + "Evaluate the whole source code and inspect the result" + + ^ self coder doItAndGoInContext: (self evaluationContext all + serializationStrategy: #GtRsrStonSerializationStrategy) +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAndGoAsynchronous [ + + ^ self selection isEmpty + ifTrue: [ self doItAndGoAllAsynchronous ] + ifFalse: [ self doItAndGoAsynchronous: self selectedTextInterval ] +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAndGoAsynchronous: aTextInterval [ + "Evaluate a piece of source code within an interval and inspect a result." + + ^ self handleCoderEvaluationUnhandledErrorDuring: [ + self doItAndGoAsynchronousInContext: ( + self evaluationContext + from: aTextInterval first to: aTextInterval last; + yourself) ] +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAndGoAsynchronousInContext: aGtSourceCoderEvaluationContext [ + | executionResult | + + aGtSourceCoderEvaluationContext + executionStrategy: self asynchronusExecutionStrategy. + executionResult := self coder + doItAndGoInContext: aGtSourceCoderEvaluationContext. + + executionResult isSyntaxError ifTrue: [ + "No update needed in case the result is a syntax error." + ^ executionResult ]. + + self updatWithAsyncExecution: executionResult value. + + ^ executionResult +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAndGoSerialized [ + "Evaluate selected source code or everything if there is nothing selected and inspect the result" + + ^ self selection isEmpty + ifTrue: [ self doItAndGoAllSerialized ] + ifFalse: [ self doItAndGoSerialized: self selectedTextInterval ] +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAndGoSerialized: aTextInterval [ + "Evaluate a piece of source code within an interval and inspect a result." + + ^ self coder doItAndGoInContext: ((self evaluationContext from: aTextInterval first to: aTextInterval last) + serializationStrategy: #GtRsrStonSerializationStrategy) +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAsynchronous [ + "Evaluate a piece of source code within a selection interval or everything if nothing is selected and return a result" + + + ^ self doItAsynchronousRequestedBy: self +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAsynchronous: aTextInterval requestedBy: aRequesterObject [ + "Evaluate source code within given text interval and return the result" + + ^ self handleCoderEvaluationUnhandledErrorDuring: [ + self doItAsynchronousInContext: ( + self evaluationContext + from: aTextInterval first to: aTextInterval last; + requesterObject: aRequesterObject) ] +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAsynchronousInContext: aGtSourceCoderEvaluationContext [ + | executionResult | + + aGtSourceCoderEvaluationContext + executionStrategy: self asynchronusExecutionStrategy. + executionResult := self coder + doItInContext: aGtSourceCoderEvaluationContext. + + executionResult isSyntaxError ifTrue: [ + "No update needed in case the result is a syntax error." + ^ executionResult ]. + + self updatWithAsyncExecution: executionResult value. + + ^ executionResult +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> doItAsynchronousRequestedBy: aRequesterObject [ + "Evaluate selected source code or everything if there is nothing selected and return the result" + + ^ self selection isEmpty + ifTrue: [ self doItAllAsynchronousRequestedBy: aRequesterObject ] + ifFalse: [ self doItAsynchronous: self selectedTextInterval requestedBy: aRequesterObject ] ] { #category : #'api - actions' } @@ -105,7 +253,7 @@ GtSourceCoderViewModel >> doItRequestedBy: aRequesterObject [ ifFalse: [ self doIt: self selectedTextInterval requestedBy: aRequesterObject ] ] -{ #category : #accessing } +{ #category : #'api - accessing' } GtSourceCoderViewModel >> elementClass [ ^ GtExpandableSourceCoderElement ] @@ -113,21 +261,176 @@ GtSourceCoderViewModel >> elementClass [ { #category : #'private - evaluation' } GtSourceCoderViewModel >> evaluationContext [ - ^ self coderModel evaluationContext - requesterObject: self + requesterObject: self asCoderRequesterObject ] -{ #category : #accessing } +{ #category : #'api - accessing' } GtSourceCoderViewModel >> evaluationResult [ ^ evaluationResult ] -{ #category : #accessing } +{ #category : #'api - accessing' } GtSourceCoderViewModel >> evaluationResult: anObject [ evaluationResult := anObject ] +{ #category : #accessing } +GtSourceCoderViewModel >> evaluationStatus [ + + ^ evaluationStatus +] + +{ #category : #'api - accessing' } +GtSourceCoderViewModel >> evaluationStatus: aNewStatus [ + | anOldStatus | + GtCoderIncomingEvaluationStatusSignal new + existingStatus: evaluationStatus; + newStatus: aNewStatus; + viewModel: self; + emit. + evaluationStatus = aNewStatus ifTrue: [ ^ self ]. + anOldStatus := evaluationStatus. + evaluationStatus := aNewStatus. + GtCoderEvaluationStatusChangedSignal new + oldStatus: anOldStatus; + newStatus: aNewStatus; + viewModel: self; + emit. + anOldStatus sourceCoderViewModel: self evaluationStatusChangedTo: aNewStatus. + self onEvaluationStatusChanged +] + +{ #category : #'gt-extension' } +GtSourceCoderViewModel >> gtInfo [ + ^ super gtInfo, (Array streamContents: [ :aStream | + aStream nextPut: #evaluationContext -> self evaluationContext. + aStream nextPut: #evaluationResult -> self evaluationResult. + aStream nextPut: #evaluationStatus -> self evaluationStatus. + aStream nextPut: #selectedTextInterval -> self selectedTextInterval ]) +] + +{ #category : #'private - event handling' } +GtSourceCoderViewModel >> handleCoderEvaluationUnhandledErrorDuring: aBlock [ + ^ [ GtCoderUserSnippetDynamicVariable + value: GtCoderUserSnippetEvaluationRunningState default + during: aBlock ] + on: GtCoderUserSnippetEvaluationConfiguration handledErrors + do: [ :anError | + GtCoderUserSnippetDynamicVariable value: GtCoderUserSnippetEvaluationExceptionState default . + self + notifyCoderEvaluationUnhandledError: anError + inProcess: Processor activeProcess. + anError pass ] +] + +{ #category : #initialization } +GtSourceCoderViewModel >> initialize [ + super initialize. + evaluationStatus := GtCoderNoEvaluationStatus default +] + +{ #category : #'private - notifying' } +GtSourceCoderViewModel >> notifyCoderEvaluationUnhandledError: anError inProcess: aProcess [ + self + announce: (GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement new + process: aProcess; + exception: anError) +] + +{ #category : #'private - notifying' } +GtSourceCoderViewModel >> notifyEvaluationStatusChanged [ + self + announce: (GtSourceCoderViewModelEvaluationStatusChanged new + textualCoderViewModel: self; + evaluationStatus: self evaluationStatus) +] + +{ #category : #'api - notifying' } +GtSourceCoderViewModel >> notifyShowDebuggerRequest: aDebugSession dueTo: anException sourceString: aSourceString sourceInterval: aSourceInterval evaluationInfo: anEvaluationInfo [ + "Return true if announcement was handled (and debugger displayed in some way). + Return false otherwise." + + + | aSharedDebugSession aStatus | + aSharedDebugSession := GtSharedDebugSession new session: aDebugSession. + aStatus := GtCoderEmbeddedDebugSessionEvaluationStatus new + debugSession: aDebugSession; + sharedDebugSession: aSharedDebugSession; + exception: anException; + evaluatedCode: anEvaluationInfo evaluatedCode; + sourceInterval: aSourceInterval; + sourceString: aSourceString. + + aSharedDebugSession weak + when: GtSharedDebugSessionDebuggedAnnouncement + send: #onDebugSessionDebuggedAnnouncement: + to: self; + when: GtSharedDebugSessionTerminatedAnnouncement + send: #onDebugSessionTerminatedAnnouncement: + to: self. + + self evaluationStatus: aStatus. + + ^ true +] + +{ #category : #'private - announcement handling' } +GtSourceCoderViewModel >> onDebugSessionDebuggedAnnouncement: anAnnouncement [ + | aStatus | + anAnnouncement session unsubscribe: self. + aStatus := self evaluationStatus + asDebugSessionInSpaceEvaluationStatusFromAnnouncement: anAnnouncement. + self evaluationStatus: aStatus +] + +{ #category : #'private - announcement handling' } +GtSourceCoderViewModel >> onDebugSessionTerminatedAnnouncement: anAnnouncement [ + anAnnouncement session unsubscribe: self. + self evaluationStatus: GtCoderNoEvaluationStatus default +] + +{ #category : #'private - hooks' } +GtSourceCoderViewModel >> onEvaluationStatusChanged [ + self notifyEvaluationStatusChanged. +] + +{ #category : #'private - announcement handling' } +GtSourceCoderViewModel >> onShowDebuggerRequest: anAnnouncement [ + | aStatus | + anAnnouncement isDelivered ifTrue: [ ^ self ]. + anAnnouncement beDelivered. + + aStatus := GtCoderEmbeddedDebugSessionEvaluationStatus new + debugSession: anAnnouncement debugSession; + sharedDebugSession: anAnnouncement sharedDebugSession; + exception: anAnnouncement exception; + sourceInterval: anAnnouncement sourceInterval; + sourceString: anAnnouncement sourceString. + + anAnnouncement sharedDebugSession weak + when: GtSharedDebugSessionDebuggedAnnouncement + send: #onDebugSessionDebuggedAnnouncement: + to: self; + when: GtSharedDebugSessionTerminatedAnnouncement + send: #onDebugSessionTerminatedAnnouncement: + to: self. + + self evaluationStatus: aStatus +] + +{ #category : #'private - event handling' } +GtSourceCoderViewModel >> onSourceCodeChanged: anSourceCodeChangedAnnouncement [ + super onSourceCodeChanged: anSourceCodeChangedAnnouncement. + self evaluationStatus: GtCoderNoEvaluationStatus default +] + +{ #category : #'private - event handling' } +GtSourceCoderViewModel >> onSourceCodeReplaced: aSourceCodeReplacedAnnouncement [ + super onSourceCodeReplaced: aSourceCodeReplacedAnnouncement. + self evaluationStatus: GtCoderNoEvaluationStatus default +] + { #category : #'api - actions' } GtSourceCoderViewModel >> printIt [ "Evaluate selected source code or everything if there is nothing selected and print the result" @@ -151,6 +454,30 @@ GtSourceCoderViewModel >> printItAll [ ^ self coder printItInContext: self evaluationContext all ] +{ #category : #'api - actions' } +GtSourceCoderViewModel >> requestSave [ + "Request the view model to start the saving process. We first send a corresponding announcement to + allow UI to intercept the saving request and show, for example, a confirmation dialog. If an announcement + was not consumed (= handled) proceed with the default save action" + | anAnnouncement | + + anAnnouncement := GtSourceCoderViewModelSaveRequested new. + self announce: anAnnouncement. + + anAnnouncement consumed + ifFalse: [ self save ] +] + +{ #category : #'api - actions' } +GtSourceCoderViewModel >> save [ + "Attempt to save the source code in a default context. For example in terms of a method, the changes would + be saved in the origin class of the method (trait, interface or superclass). + Return true if the source code was succesfully saved" + + + ^ false +] + { #category : #'private - evaluation' } GtSourceCoderViewModel >> selectedTextInterval [ @@ -160,3 +487,17 @@ GtSourceCoderViewModel >> selectedTextInterval [ anInterval := eachMonotoneSelection from + 1 to: eachMonotoneSelection to ]. ^ anInterval ] + +{ #category : #'api - coder model' } +GtSourceCoderViewModel >> subscribeToCoderModel [ + super subscribeToCoderModel. + + self coderModel weak + when: GtCoderShowDebuggerRequest + send: #onShowDebuggerRequest: + to: self +] + +{ #category : #updating } +GtSourceCoderViewModel >> updatWithAsyncExecution: aCoderExecutionResult [ +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement.class.st b/src/GToolkit-Coder-UI/GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement.class.st new file mode 100644 index 000000000..9b1500148 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'process', + 'exception' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' +} + +{ #category : #accessing } +GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement >> exception [ + ^ exception +] + +{ #category : #accessing } +GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement >> exception: anObject [ + exception := anObject +] + +{ #category : #accessing } +GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement >> process [ + ^ process +] + +{ #category : #accessing } +GtSourceCoderViewModelAboutToOpenDebuggerAnnouncement >> process: anObject [ + process := anObject +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderViewModelEvaluationStatusChanged.class.st b/src/GToolkit-Coder-UI/GtSourceCoderViewModelEvaluationStatusChanged.class.st new file mode 100644 index 000000000..9aff8aaf1 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderViewModelEvaluationStatusChanged.class.st @@ -0,0 +1,31 @@ +Class { + #name : #GtSourceCoderViewModelEvaluationStatusChanged, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'evaluationStatus', + 'isNotificationHandled' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Model' +} + +{ #category : #accessing } +GtSourceCoderViewModelEvaluationStatusChanged >> evaluationStatus [ + ^ evaluationStatus +] + +{ #category : #accessing } +GtSourceCoderViewModelEvaluationStatusChanged >> evaluationStatus: anObject [ + evaluationStatus := anObject +] + +{ #category : #accessing } +GtSourceCoderViewModelEvaluationStatusChanged >> isNotificationHandled [ + "Indicates, whether a user notification, e.g., debug session notification, was requested (and therefore displayed)." + + ^ isNotificationHandled ifNil: [ isNotificationHandled := false ] +] + +{ #category : #accessing } +GtSourceCoderViewModelEvaluationStatusChanged >> isNotificationHandled: aBoolean [ + isNotificationHandled := aBoolean +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderViewModelSaveRequested.class.st b/src/GToolkit-Coder-UI/GtSourceCoderViewModelSaveRequested.class.st new file mode 100644 index 000000000..10b053bd9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderViewModelSaveRequested.class.st @@ -0,0 +1,32 @@ +" +Is announced by the {{gtClass:GtSourceCoderViewModel}} when a save is requested. If the announcement is not consumed, the save will happen in a default context +" +Class { + #name : #GtSourceCoderViewModelSaveRequested, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'consumed' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Events' +} + +{ #category : #accessing } +GtSourceCoderViewModelSaveRequested >> consumed [ + + + ^ consumed +] + +{ #category : #accessing } +GtSourceCoderViewModelSaveRequested >> consumed: aBoolean [ + "Prevents the default save action, when set to true by the event handler" + + consumed := aBoolean +] + +{ #category : #initialization } +GtSourceCoderViewModelSaveRequested >> initialize [ + super initialize. + + consumed := false +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderViewModelSearchTextCancelRequested.class.st b/src/GToolkit-Coder-UI/GtSourceCoderViewModelSearchTextCancelRequested.class.st new file mode 100644 index 000000000..155de9c16 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderViewModelSearchTextCancelRequested.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtSourceCoderViewModelSearchTextCancelRequested, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'consumed' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Events' +} + +{ #category : #accessing } +GtSourceCoderViewModelSearchTextCancelRequested >> consumed [ + + + ^ consumed +] + +{ #category : #accessing } +GtSourceCoderViewModelSearchTextCancelRequested >> consumed: aBoolean [ + "Prevents the default save action, when set to true by the event handler" + + consumed := aBoolean +] + +{ #category : #initialization } +GtSourceCoderViewModelSearchTextCancelRequested >> initialize [ + super initialize. + + consumed := false +] diff --git a/src/GToolkit-Coder-UI/GtSourceCoderViewModelSearchTextRequested.class.st b/src/GToolkit-Coder-UI/GtSourceCoderViewModelSearchTextRequested.class.st new file mode 100644 index 000000000..4d43eba35 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtSourceCoderViewModelSearchTextRequested.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtSourceCoderViewModelSearchTextRequested, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'consumed' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Events' +} + +{ #category : #accessing } +GtSourceCoderViewModelSearchTextRequested >> consumed [ + + + ^ consumed +] + +{ #category : #accessing } +GtSourceCoderViewModelSearchTextRequested >> consumed: aBoolean [ + "Prevents the default save action, when set to true by the event handler" + + consumed := aBoolean +] + +{ #category : #initialization } +GtSourceCoderViewModelSearchTextRequested >> initialize [ + super initialize. + + consumed := false +] diff --git a/src/GToolkit-Coder-UI/GtSyncScrollRange.class.st b/src/GToolkit-Coder-UI/GtSyncScrollRange.class.st index edfd039d2..a3e561aec 100644 --- a/src/GToolkit-Coder-UI/GtSyncScrollRange.class.st +++ b/src/GToolkit-Coder-UI/GtSyncScrollRange.class.st @@ -6,7 +6,8 @@ Class { 'leftLast', 'rightFirst', 'rightLast', - 'isDifference' + 'isDifference', + 'isSelected' ], #category : #'GToolkit-Coder-UI-Diff' } @@ -18,6 +19,16 @@ GtSyncScrollRange class >> left: leftInteger right: rightInteger [ yourself ] +{ #category : #'instance creation' } +GtSyncScrollRange class >> leftFirst: leftFirst leftLast: leftLast rightFirst: rightFirst rightLast: rightLast [ + ^ self new + leftFirst: leftFirst; + leftLast: leftLast; + rightFirst: rightFirst; + rightLast: rightLast; + yourself +] + { #category : #syncing } GtSyncScrollRange >> advanceBoth [ self leftSize = self rightSize @@ -51,10 +62,26 @@ GtSyncScrollRange >> advanceRight [ yourself ] +{ #category : #accessing } +GtSyncScrollRange >> beDifferent [ + isDifference := true +] + +{ #category : #testing } +GtSyncScrollRange >> includesLeftLine: anInteger [ + ^ self leftFirst < anInteger and: [ anInteger <= self leftLast ] +] + +{ #category : #testing } +GtSyncScrollRange >> includesRightLine: anInteger [ + ^ self rightFirst < anInteger and: [ anInteger <= self rightLast ] +] + { #category : #'initialize-release' } GtSyncScrollRange >> initialize [ super initialize. isDifference := false. + isSelected := false. leftFirst := 0. leftLast := 0. rightFirst := 0. @@ -74,21 +101,61 @@ GtSyncScrollRange >> isDifference [ ^ isDifference ] +{ #category : #testing } +GtSyncScrollRange >> isLeftEmpty [ + ^ self leftSize = 0 +] + +{ #category : #testing } +GtSyncScrollRange >> isRightEmpty [ + ^ self rightSize = 0 +] + +{ #category : #testing } +GtSyncScrollRange >> isSelected [ + ^ isSelected +] + { #category : #accessing } GtSyncScrollRange >> leftFirst [ ^ leftFirst ] +{ #category : #accessing } +GtSyncScrollRange >> leftFirst: anInteger [ + leftFirst := anInteger +] + { #category : #accessing } GtSyncScrollRange >> leftLast [ ^ leftLast ] +{ #category : #accessing } +GtSyncScrollRange >> leftLast: anInteger [ + leftLast := anInteger +] + +{ #category : #accessing } +GtSyncScrollRange >> leftRangeDisplayString [ + ^ leftFirst printString , '-' , leftLast printString +] + { #category : #accessing } GtSyncScrollRange >> leftSize [ ^ leftLast - leftFirst ] +{ #category : #'initialize-release' } +GtSyncScrollRange >> markDifferent [ + isDifference := true +] + +{ #category : #'initialize-release' } +GtSyncScrollRange >> markSame [ + isDifference := false +] + { #category : #syncing } GtSyncScrollRange >> mergeWith: aRange [ leftFirst := leftFirst min: aRange leftFirst. @@ -128,12 +195,37 @@ GtSyncScrollRange >> rightFirst [ ^ rightFirst ] +{ #category : #accessing } +GtSyncScrollRange >> rightFirst: anInteger [ + rightFirst := anInteger +] + { #category : #accessing } GtSyncScrollRange >> rightLast [ ^ rightLast ] +{ #category : #accessing } +GtSyncScrollRange >> rightLast: anInteger [ + rightLast := anInteger +] + +{ #category : #accessing } +GtSyncScrollRange >> rightRangeDisplayString [ + ^ rightFirst printString , '-' , rightLast printString +] + { #category : #accessing } GtSyncScrollRange >> rightSize [ ^ rightLast - rightFirst ] + +{ #category : #accessing } +GtSyncScrollRange >> select [ + isSelected := true +] + +{ #category : #accessing } +GtSyncScrollRange >> unselect [ + isSelected := false +] diff --git a/src/GToolkit-Coder-UI/GtSyncScrollRanges.class.st b/src/GToolkit-Coder-UI/GtSyncScrollRanges.class.st index 196ebc1f7..885b95f34 100644 --- a/src/GToolkit-Coder-UI/GtSyncScrollRanges.class.st +++ b/src/GToolkit-Coder-UI/GtSyncScrollRanges.class.st @@ -8,88 +8,198 @@ Class { } { #category : #'instance creation' } -GtSyncScrollRanges class >> createFromDiff: aDiffBuilder [ +GtSyncScrollRanges class >> createFromChange: aCompositeChange [ | syncScroll | syncScroll := self new. - aDiffBuilder - patchSequenceDoIfMatch: [ :line | syncScroll advanceBoth ] - ifInsert: [ :line | syncScroll advanceRight ] - ifRemove: [ :line | syncScroll advanceLeft ]. - syncScroll mergeDifferences. + syncScroll buildFromChange: aCompositeChange. ^ syncScroll ] -{ #category : #examples } -GtSyncScrollRanges class >> example [ - - ^ self createFromDiff: self exampleDiff +{ #category : #'instance creation' } +GtSyncScrollRanges class >> createFromDiff: aDiffBuilder [ + | syncScroll | + syncScroll := self new. + syncScroll buildFromDiff: aDiffBuilder. + ^ syncScroll ] -{ #category : #examples } -GtSyncScrollRanges class >> exampleDiff [ - - ^ TextDiffBuilder from: self originalString to: self newString +{ #category : #'private-creation changes' } +GtSyncScrollRanges >> addDestinationLineChangesFrom: aCompositeChange to: changedLines [ + | lineNumber index lineNumbers | + lineNumber := 0. + index := 1. + aCompositeChange to + lineIndicesDo: [ :start :end :endWithCR | + [ | currentChange previousIndex | + previousIndex := index. + currentChange := aCompositeChange changes at: index. + currentChange insertionChange + ifNil: [ currentChange deletionChange + ifNotNil: [ :change | + change newIndex <= endWithCR + ifTrue: [ (changedLines at: currentChange) + add: lineNumber; + add: lineNumber. + index := index + 1 ] ] ] + ifNotNil: [ :change | + (change startIndex between: start and: endWithCR) + ifTrue: [ (changedLines at: currentChange) add: lineNumber ]. + (change stopIndex max: change startIndex) <= endWithCR + ifTrue: [ (changedLines at: currentChange) add: lineNumber + 1. + index := index + 1 ] ]. + index > aCompositeChange changes size ifTrue: [ ^ self ]. + previousIndex ~= index ] whileTrue. + lineNumber := lineNumber + 1 ]. + lineNumbers := changedLines at: aCompositeChange changes last. + [ lineNumbers size < 4 ] whileTrue: [ lineNumbers add: lineNumber ] ] -{ #category : #examples } -GtSyncScrollRanges class >> newString [ - - ^ '1 -4 -5 -6 -7 -8 -9' +{ #category : #'private-creation changes' } +GtSyncScrollRanges >> addOriginalLineChangesFrom: aCompositeChange to: changedLines [ + | lineNumber index | + lineNumber := 0. + index := 1. + aCompositeChange from + lineIndicesDo: [ :start :end :endWithCR | + [ | currentChange previousIndex | + previousIndex := index. + currentChange := aCompositeChange changes at: index. + currentChange deletionChange + ifNil: [ currentChange insertionChange + ifNotNil: [ :change | + change originalIndex <= endWithCR + ifTrue: [ changedLines + at: currentChange + ifAbsentPut: [ OrderedCollection with: lineNumber with: lineNumber ]. + index := index + 1 ] ] ] + ifNotNil: [ :change | + (change isEmpty and: [ change startIndex > endWithCR ]) + ifFalse: [ change startIndex <= endWithCR + ifTrue: [ changedLines + at: currentChange + ifAbsentPut: [ OrderedCollection with: lineNumber ] ]. + change stopIndex <= endWithCR + ifTrue: [ (changedLines + at: currentChange + ifAbsentPut: [ OrderedCollection with: lineNumber + 1 ]) + add: lineNumber + 1. + index := index + 1 ] ] ]. + index > aCompositeChange changes size ifTrue: [ ^ self ]. + previousIndex ~= index ] whileTrue. + lineNumber := lineNumber + 1 ]. + (changedLines + at: aCompositeChange changes last + ifAbsentPut: [ OrderedCollection with: lineNumber ]) add: lineNumber ] -{ #category : #examples } -GtSyncScrollRanges class >> originalString [ - - ^ '1 -2 -3 -5 -6 -7 -9 -10' +{ #category : #'private-creation changes' } +GtSyncScrollRanges >> addRanges: changedRanges fromChanges: aCompositeChange [ + | fromStart toStart fromEnd toEnd | + fromEnd := aCompositeChange from lineCount. + toEnd := aCompositeChange to lineCount. + fromStart := 0. + toStart := 0. + changedRanges + do: [ :each | + (ranges isEmpty and: [ each first = 0 and: [ each third = 0 ] ]) + ifFalse: [ ranges + add: (GtSyncScrollRange + leftFirst: fromStart + leftLast: each first + rightFirst: toStart + rightLast: each third) ]. + ranges + add: (GtSyncScrollRange + leftFirst: each first + leftLast: each second + rightFirst: each third + rightLast: each fourth) beDifferent. + fromStart := each second. + toStart := each fourth ]. + (fromStart + 1 > fromEnd and: [ toStart + 1 > toEnd ]) + ifFalse: [ ranges + add: (GtSyncScrollRange + leftFirst: fromStart + leftLast: fromEnd + rightFirst: toStart + rightLast: toEnd) ] ] -{ #category : #'private-creation' } +{ #category : #'private-creation diff' } GtSyncScrollRanges >> advanceBoth [ ranges last advanceBoth ifNotNil: [ :range | ranges add: range ] ] -{ #category : #'private-creation' } +{ #category : #'private-creation diff' } GtSyncScrollRanges >> advanceLeft [ ranges last advanceLeft ifNotNil: [ :range | ranges add: range ] ] -{ #category : #'private-creation' } +{ #category : #'private-creation diff' } GtSyncScrollRanges >> advanceRight [ ranges last advanceRight ifNotNil: [ :range | ranges add: range ] ] -{ #category : #'initialize-release' } -GtSyncScrollRanges >> initialize [ - super initialize. - ranges := OrderedCollection with: GtSyncScrollRange new +{ #category : #'private-creation changes' } +GtSyncScrollRanges >> buildFromChange: aCompositeChange [ + | changedLines changedRanges change | + ranges := OrderedCollection new. + changedLines := IdentityDictionary new. + change := aCompositeChange isLineBased + ifTrue: [ aCompositeChange ] + ifFalse: [ GtDiffBuilder + computeDifferencesFrom: aCompositeChange from + to: aCompositeChange to + using: GtLineDiffSplitter ignoringEOLChars ]. + change changes notEmpty + ifTrue: [ self addOriginalLineChangesFrom: change to: changedLines. + self addDestinationLineChangesFrom: change to: changedLines ]. + changedRanges := changedLines values + asSortedCollection: [ :a :b | a first < b first or: [ a first = b first and: [ a third < b third ] ] ]. + changedRanges := self mergeRanges: changedRanges. + self addRanges: changedRanges fromChanges: change +] + +{ #category : #'private-creation diff' } +GtSyncScrollRanges >> buildFromDiff: aDiffBuilder [ + ranges := OrderedCollection with: GtSyncScrollRange new. + aDiffBuilder + patchSequenceDoIfMatch: [ :line | self advanceBoth ] + ifInsert: [ :line | self advanceRight ] + ifRemove: [ :line | self advanceLeft ]. + self mergeDifferences +] + +{ #category : #inspecting } +GtSyncScrollRanges >> gtRangesViewFor: aView [ + + ^ aView columnedList + title: 'Ranges'; + items: [ self ranges ]; + column: 'Left' + text: [ :each | each leftRangeDisplayString asRopedText glamorousRegularFont ]; + column: 'Right' + text: [ :each | each rightRangeDisplayString asRopedText glamorousRegularFont ]; + column: 'Equal' + text: [ :each | each isDifference not asString asRopedText glamorousRegularFont ] ] { #category : #accessing } -GtSyncScrollRanges >> leftLineFor: rightIndex [ - | range | - range := ranges - detect: [ :each | rightIndex between: each rightFirst and: each rightLast ] - ifNone: [ ^ 0 ]. - range rightSize = 0 - ifTrue: [ ^ 0 ]. - ^ ((rightIndex asFloat - range rightFirst) / range rightSize - * range leftSize + range leftFirst) rounded -] - -{ #category : #'private-creation' } +GtSyncScrollRanges >> leftLineFor: anInteger [ + | range rightIndex | + rightIndex := self ranges notEmpty + ifTrue: [ (self ranges last rightLast min: anInteger) max: self ranges first rightFirst ] + ifFalse: [ anInteger ]. + range := self ranges + detect: [ :each | rightIndex between: each rightFirst and: each rightLast ] + ifNone: [ ^ 0 ]. + ^ ((range rightSize = 0 + ifTrue: [ 0 ] + ifFalse: [ (rightIndex asFloat - range rightFirst) / range rightSize ]) + * range leftSize + range leftFirst) rounded +] + +{ #category : #'private-creation diff' } GtSyncScrollRanges >> mergeDifferences [ | index current previous | index := ranges size. @@ -103,20 +213,114 @@ GtSyncScrollRanges >> mergeDifferences [ current := previous ] ] +{ #category : #'private-creation changes' } +GtSyncScrollRanges >> mergeRanges: changedRanges [ + | newRanges | + newRanges := OrderedCollection new. + changedRanges + do: [ :each | + (newRanges notEmpty + and: [ (newRanges last at: 2) >= (each at: 1) + or: [ (newRanges last at: 4) >= (each at: 3) ] ]) + ifTrue: [ | lastRange | + lastRange := newRanges last. + lastRange at: 2 put: ((each at: 2) max: (lastRange at: 2)). + lastRange at: 4 put: ((each at: 4) max: (lastRange at: 4)) ] + ifFalse: [ newRanges add: each ] ]. + ^ newRanges +] + +{ #category : #accessing } +GtSyncScrollRanges >> rangeForLeft: anInteger [ + ^ self ranges + detect: [ :each | each includesLeftLine: anInteger ] + ifNone: [ ] +] + +{ #category : #accessing } +GtSyncScrollRanges >> rangeForRight: anInteger [ + ^ self ranges + detect: [ :each | each includesRightLine: anInteger ] + ifNone: [ ] +] + +{ #category : #accessing } +GtSyncScrollRanges >> ranges [ + ^ ranges ifNil: [ #() ] +] + { #category : #accessing } GtSyncScrollRanges >> rangesForLeft: leftInterval andRight: rightInterval [ - ^ ranges + ^ self ranges select: [ :each | each overlapsLeft: leftInterval orRight: rightInterval ] ] { #category : #accessing } -GtSyncScrollRanges >> rightLineFor: leftIndex [ - | range | - range := ranges - detect: [ :each | leftIndex between: each leftFirst and: each leftLast ] - ifNone: [ ^ 0 ]. - range leftSize = 0 - ifTrue: [ ^ 0 ]. - ^ ((leftIndex asFloat - range leftFirst) / range leftSize - * range rightSize + range rightFirst) rounded +GtSyncScrollRanges >> rightLineFor: anInteger [ + | range leftIndex | + leftIndex := self ranges notEmpty + ifTrue: [ (self ranges last leftLast min: anInteger) max: self ranges first leftFirst ] + ifFalse: [ anInteger ]. + range := self ranges + detect: [ :each | leftIndex between: each leftFirst and: each leftLast ] + ifNone: [ ^ 0 ]. + ^ ((range leftSize = 0 + ifTrue: [ 0 ] + ifFalse: [ (leftIndex asFloat - range leftFirst) / range leftSize ]) + * range rightSize + range rightFirst) rounded +] + +{ #category : #accessing } +GtSyncScrollRanges >> selectNext [ + | found | + found := false. + self ranges isEmpty ifTrue: [ ^ nil ]. + self ranges + do: [ :each | + found + ifTrue: [ each isDifference ifTrue: [ ^ each select ] ] + ifFalse: [ found := each isSelected ]. + each unselect ]. + ^ (self ranges + detect: [ :each | each isDifference ] + ifNone: [ self ranges first ]) select +] + +{ #category : #accessing } +GtSyncScrollRanges >> selectPrevious [ + | found | + found := false. + self ranges isEmpty ifTrue: [ ^ nil ]. + self ranges + reverseDo: [ :each | + found + ifTrue: [ each isDifference ifTrue: [ ^ each select ] ] + ifFalse: [ found := each isSelected ]. + each unselect ]. + self ranges size + to: 1 + by: -1 + do: [ :i | + | range | + range := self ranges at: i. + range isDifference ifTrue: [ ^ range select ] ]. + ^ self ranges last select +] + +{ #category : #accessing } +GtSyncScrollRanges >> selectRange: aRange [ + self ranges do: [ :each | each unselect ]. + aRange ifNotNil: [ aRange select ] +] + +{ #category : #accessing } +GtSyncScrollRanges >> selectedProgress [ + | index | + index := {0. 0}. + self ranges + do: [ :each | + each isDifference + ifTrue: [ index at: 2 put: index last + 1. + each isSelected ifTrue: [ index at: 1 put: index last ] ] ]. + ^ index ] diff --git a/src/GToolkit-Coder-UI/GtTextCoder.extension.st b/src/GToolkit-Coder-UI/GtTextCoder.extension.st index 6f65a9f19..d2187b3e0 100644 --- a/src/GToolkit-Coder-UI/GtTextCoder.extension.st +++ b/src/GToolkit-Coder-UI/GtTextCoder.extension.st @@ -1,6 +1,6 @@ Extension { #name : #GtTextCoder } { #category : #'*GToolkit-Coder-UI' } -GtTextCoder >> asCoderUIModel [ +GtTextCoder >> asCoderViewModel [ ^ GtTextualCoderViewModel new coder: self ] diff --git a/src/GToolkit-Coder-UI/GtTextualCoder.extension.st b/src/GToolkit-Coder-UI/GtTextualCoder.extension.st deleted file mode 100644 index 53c367312..000000000 --- a/src/GToolkit-Coder-UI/GtTextualCoder.extension.st +++ /dev/null @@ -1,8 +0,0 @@ -Extension { #name : #GtTextualCoder } - -{ #category : #'*GToolkit-Coder-UI' } -GtTextualCoder >> breadcrumbActions [ - - - ^ #() -] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderCopyContextMenuItemId.class.st b/src/GToolkit-Coder-UI/GtTextualCoderCopyContextMenuItemId.class.st index 55acfa133..ec64e95df 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderCopyContextMenuItemId.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderCopyContextMenuItemId.class.st @@ -6,7 +6,7 @@ A context menu action to copy a text Class { #name : #GtTextualCoderCopyContextMenuItemId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtTextualCoderCutContextMenuItemId.class.st b/src/GToolkit-Coder-UI/GtTextualCoderCutContextMenuItemId.class.st index 6186f955a..3fa3fb43d 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderCutContextMenuItemId.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderCutContextMenuItemId.class.st @@ -6,7 +6,7 @@ A context menu action to cut text Class { #name : #GtTextualCoderCutContextMenuItemId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElement.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElement.class.st index 055ccf64e..8e9c53a40 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderEditorElement.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElement.class.st @@ -7,36 +7,112 @@ Class { 'completion', 'evaluationHighlighter', 'evaluationPrinter', - 'shortcuts' + 'shortcuts', + 'cursorsUpdater', + 'textUpdater', + 'addOnsElementFuture', + 'menuItemsElementFuture' ], #category : #'GToolkit-Coder-UI-Coder - Textual' } +{ #category : #private } +GtTextualCoderEditorElement >> addErrorAttribute: errorAttribute at: anInteger [ + | text position | + text := self editor text. + position := anInteger - 1 max: 0. + text size < position ifTrue: [ ^ self ]. + text clearAttributesOfClass: GtSourceCoderErrorAttribute. + text + attribute: errorAttribute + from: position + to: position. + self scrollToPosition: (text asString lineNumberCorrespondingToIndex: position) +] + +{ #category : #'hooks - children' } +GtTextualCoderEditorElement >> attachSpace [ + super attachSpace. + + GtTextualCoderEditorElementAttachedSpaceSignal new + element: self; + emit +] + { #category : #'api - textual coder view model' } -GtTextualCoderEditorElement >> coderUIModel: aTextualCoderViewModel [ +GtTextualCoderEditorElement >> coderViewModel: aTextualCoderViewModel [ self textualCoderViewModel: aTextualCoderViewModel ] +{ #category : #'private - updating' } +GtTextualCoderEditorElement >> computeTextForUpdateForTextChangedEvent: aGtTextualCoderViewModelTextChanged [ + "In case the editor has a text edit command we determine + the updated text by editing the text currently in the + editor. If no edit command is present in the event, + we take the text from the event. We use the edit command + to avoid setting the text from the event which is not + styled, and can cause flickerings." + + ^ aGtTextualCoderViewModelTextChanged hasTextEditCommand + ifTrue: [ + | editCommand| + editCommand := aGtTextualCoderViewModelTextChanged textEditCommand. + self editedTextBasedOnEditCommand: editCommand. ] + ifFalse: [ + aGtTextualCoderViewModelTextChanged text copy ] +] + { #category : #'instance creation' } -GtTextualCoderEditorElement >> createContextMenuContent [ +GtTextualCoderEditorElement >> createContextMenu [ "wait for the addons to be computed" - | theContextMenu | - - theContextMenu := GtCoderContextMenuContent new - editorElement: self. + + + | aMenu aMenuItemsFuture | + aMenu := BrMenuItems new. + aMenu beGroupedElementType. + + aMenuItemsFuture := self textualCoderViewModel addOnsFuture + map: [ :theAddOns | + | theItems theContextMenuAstAddons theMenuItems | + "extra context menu items that depend on ast and view model state such as selection" + theContextMenuAstAddons := self textualCoderViewModel + computeContextMenuAstAddOns. + theItems := theAddOns contextMenuActions, theContextMenuAstAddons contextMenuActions. + theItems := theItems reject: [ :e | e title isNil ]. + + theMenuItems := theItems + collect: [ :eachAction | eachAction asBrMenuItemForCoderElement: self ] + thenReject: #isNil. + + aMenu -> theMenuItems ]. + + menuItemsElementFuture future: aMenuItemsFuture. + + ^ aMenu +] + +{ #category : #initialization } +GtTextualCoderEditorElement >> defaultTextEditorMode [ + ^ BrTextEditorEditableCodeMode new +] + +{ #category : #'hooks - children' } +GtTextualCoderEditorElement >> detachSpace [ + super detachSpace. - self textualCoderViewModel addOnsAsyncDo: [ :theAddOns | - | theItems theContextMenuAstAddons | - - "extra context menu items that depend on ast and view model state such as selection" - theContextMenuAstAddons := self textualCoderViewModel computeContextMenuAstAddOns. - - theItems := theAddOns contextMenuActions, theAddOns mainActions, theContextMenuAstAddons contextMenuActions. - theItems := theItems reject: [ :e | e title isNil ]. - - theContextMenu enqueueTask: (BlTaskAction new action: [ theContextMenu items: theItems ]) ]. + GtTextualCoderEditorElementDetachedSpaceSignal new + element: self; + emit +] + +{ #category : #'private - updating' } +GtTextualCoderEditorElement >> editedTextBasedOnEditCommand: aTextEditCommand [ + | modifiedText| + "self haltOnce." - ^ theContextMenu + modifiedText := self text copy. + aTextEditCommand applyTextChangeOn: modifiedText. + ^ modifiedText ] { #category : #private } @@ -47,84 +123,129 @@ GtTextualCoderEditorElement >> hideHighlighters [ evaluationPrinter hideResultSynchronously. ] -{ #category : #private } -GtTextualCoderEditorElement >> highlightPCRangeForInterval: aSelectionInterval [ - self textualCoderViewModel isModified - ifTrue: [ ^ self ]. - self text - clearAttributes: [ :each | each class = BlTextDecorationAttribute ]. - (self text from: aSelectionInterval first to: aSelectionInterval last) - underlineDo: [ :anAttribute | - anAttribute - color: self theme status errorBackgroundColor; - thickness: 1.5; - beNotOverwritableByStyler ] -] - { #category : #initialization } GtTextualCoderEditorElement >> initialize [ super initialize. self - aptitude: BrGlamorousCodeEditorAptitude + (BrGlamorousWithContextMenuAptitude content: [ self createContextMenuContent ]); + aptitude: BrGlamorousCodeEditorAptitude; + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude menu: [ self createContextMenu ]); padding: BlInsets empty; hMatchParent; - vFitContent. + vFitContentLimited. - self editor - beEditableCode; - disableStyleTextWhenModified. + self id: GtSourceCoderEditorId. shortcuts := #(). completion := GtCompletionController on: self. evaluationHighlighter := GtSourceCoderEvaluationHighlighter new editorElement: self. evaluationPrinter := GtSourceCoderEvaluationPrinter new editorElement: self. - - self initializeListeners + + cursorsUpdater := BrElementUpdater new + element: self; + action: (MessageSend receiver: self selector: #privateUpdateCursors:announcement:). + + textUpdater := BrElementUpdater new + element: self; + action: (MessageSend receiver: self selector: #privateUpdateText:announcement:). + + addOnsElementFuture := (BrAsyncElementFuture on: self) + executionConfiguration: (GtSingleCoderViewModel methodAddOnsExecutionConfiguration); + whenSuccess: [ :anEditorElement :theAddOns | + self textualCoderViewModel onAddOnsChanged: theAddOns ]; + whenError: [ :anEditorElement :anError | + self onAddOnsError: anError ]. + + menuItemsElementFuture := (BrAsyncElementFuture on: self) + executionConfiguration: (GtSingleCoderViewModel methodAddOnsExecutionConfiguration); + whenSuccess: [ :anEditorElement :anAssoc | + anAssoc key items: anAssoc value ]; + whenError: [ :anEditorElement :anError | + self onContextMenuError: anError ]. + + self initializeListeners. + + self withAsyncPromiseDo: [ :anElementPromise | + anElementPromise + whenSuccess: [ :anEditorElement :anEditorState | anEditorElement privateUpdateEditorState: anEditorState ]; + whenError: [ :anEditorElement :anEditorState | ]; + whenPending: [ :anEditorElement | ] ] ] { #category : #initialization } GtTextualCoderEditorElement >> initializeListeners [ - self when: BlFocusInEvent do: [ :anEvent | - self textualCoderViewModel focused: true from: self ]. - self when: BlFocusOutEvent do: [ :anEvent | - "when elements are removed from the scene graph due to tab switching or scrolling they lose focus. - We are only interested when focus is lost explicitly via user interaction" - anEvent isDueToRemoval - ifFalse: [ self textualCoderViewModel focused: false from: self ] ]. + self + when: BlFocusInEvent + do: [ :anEvent | self onFocusInEvent: anEvent ]. + + self + when: BlFocusOutEvent + do: [ :anEvent | self onFocusOutEvent: anEvent ]. + + self editor + when: BrTextEditorHistoryChangedEvent + do: [ :anEvent | self onTextEditorHistoryChanged: anEvent ]. self editor - when: BrTextEditorModifiedEvent - do: [ :anEvent | self onTextModified ]. + when: BrTextEditorModificationEvent + do: [ :anEvent | self onTextModifiedDueToEvent: anEvent ]. self editor - when: BrTextEditorCursorChangedEvent - do: [ :anEvent | self textualCoderViewModel cursors: anEvent cursors from: self ]. + when: BrTextEditorCursorChangedEvent + do: [ :anEvent | self onTextEditorCursorChanged: anEvent ]. self editor - when: BrTextEditorSelectionChangedEvent - do: [ :anEvent | self textualCoderViewModel selection: anEvent selection from: self ]. + when: BrTextEditorSelectionChangedEvent + do: [ :anEvent | self onTextEditorSelectionChanged: anEvent ]. self editor when: BrTextEditorTextStyledEvent - do: [ :anEvent | self textualCoderViewModel styledText: anEvent styledText ] + do: [ :anEvent | self onTextStyled: anEvent ] +] + +{ #category : #initialization } +GtTextualCoderEditorElement >> onAddOnsError: anError [ + anError emit. + NonInteractiveTranscript stderr + show: '[GtTextualCoderEditorElement>>#onAddOnsError:] '; + show: anError; + newLine +] + +{ #category : #'hooks - children' } +GtTextualCoderEditorElement >> onAddedToSceneGraph [ + super onAddedToSceneGraph. + + GtTextualCoderEditorElementAddedToSceneGraphSignal new + element: self; + emit ] { #category : #'private - event handling' } GtTextualCoderEditorElement >> onCodeEvaluated: anEvaluationAnnouncement [ - evaluationHighlighter displayResult: anEvaluationAnnouncement evaluationResult. - evaluationPrinter hideResult + (anEvaluationAnnouncement requesterObject + isCoderViewModel: self textualCoderViewModel) ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ + evaluationHighlighter displayResult: anEvaluationAnnouncement evaluationResult. + evaluationPrinter hideResult ] ] { #category : #'private - event handling' } GtTextualCoderEditorElement >> onCoderParseError: aGtCoderParseError [ "A parse error can be notifying from a non-UI thread" - self enqueueTask: (BlTaskAction new action: [ - self - reportParseError: aGtCoderParseError errorMessage - at: aGtCoderParseError location ]) + (aGtCoderParseError requesterObject + isUndefinedOrCoderViewModel: self textualCoderViewModel) ifFalse: [ ^ self ]. + self + enqueueTask: [ aGtCoderParseError isUndeclaredError + ifTrue: [ self reportUndeclaredError: aGtCoderParseError ] + ifFalse: [ self + reportParseError: aGtCoderParseError errorMessage + at: aGtCoderParseError location ] ] asBlTask ] { #category : #'private - event handling' } @@ -134,47 +255,46 @@ GtTextualCoderEditorElement >> onCoderViewModelFocused: aBoolean [ ifFalse: [ self loseFocus ] ] -{ #category : #'private - event handling' } -GtTextualCoderEditorElement >> onContextPCRangeChanged: aContextPCRangeChangedAnnouncement [ - | selectionInterval | - - selectionInterval := aContextPCRangeChangedAnnouncement pcRange. - self highlightPCRangeForInterval: selectionInterval +{ #category : #initialization } +GtTextualCoderEditorElement >> onContextMenuError: anError [ + anError emit. + NonInteractiveTranscript stderr + show: '[GtTextualCoderEditorElement>>#onContextMenuError:] '; + show: anError; + newLine ] { #category : #'private - event handling' } -GtTextualCoderEditorElement >> onCursorsChanged: aCursorsChangedAnnouncement [ - "Is sent when the cursors change in the View Model. - May be sent from a non-UI thread" - - "if the source of the announcement is myself, do nothing to break the change cycle" - aCursorsChangedAnnouncement source = self - ifTrue: [ ^ self ]. +GtTextualCoderEditorElement >> onFocusChangedAnnouncement: anAnnouncement [ + anAnnouncement source == self ifTrue: [ ^ self ]. - "since it may be sent from a non-UI process make sure to wrap in the action" - self enqueueTask: (BlTaskAction new action: [ - self navigator - withoutResettingDesiredCoordinate; - removeAll; - addAll: aCursorsChangedAnnouncement cursors; - apply ]) + anAnnouncement source == self textualCoderViewModel ifFalse: [ ^ self ]. + + self + enqueueTask: (BlTaskAction new + action: [ self onCoderViewModelFocused: anAnnouncement focused ]) ] -{ #category : #'private - event handling' } -GtTextualCoderEditorElement >> onFocusChangedAnnouncement: anAnnouncement [ - self assertUIProcess. +{ #category : #initialization } +GtTextualCoderEditorElement >> onFocusInEvent: anEvent [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. - anAnnouncement source == self - ifTrue: [ ^ self ]. + self textualCoderViewModel focused: true from: self +] - anAnnouncement source == self textualCoderViewModel - ifFalse: [ ^ self ]. +{ #category : #initialization } +GtTextualCoderEditorElement >> onFocusOutEvent: anEvent [ + anEvent isDueToRemoval ifTrue: [ ^ self ]. + self hasTextualCoderViewModel ifFalse: [ ^ self ]. - self onCoderViewModelFocused: anAnnouncement focused + self textualCoderViewModel focused: false from: self ] { #category : #'private - event handling' } GtTextualCoderEditorElement >> onObjectSpawnRequest: anAnnouncement [ + (anAnnouncement requesterObject isCoderViewModel: self textualCoderViewModel) + ifFalse: [ ^ self ]. + self enqueueTask: (BlTaskAction new action: [ self phlow spawnObject: anAnnouncement object @@ -187,53 +307,62 @@ GtTextualCoderEditorElement >> onPostTextualCoderViewModelChanged [ I do nothing by default but allow users to perform update operations when a receiver object is already subscribed to announcements." - self textualCoderViewModel hasStyledText - ifTrue: [ ^ self ]. - - "the add-ons will not compute themselves unless we tell them to" - self textualCoderViewModel addOnsAsyncDo: [ :theAddOns | self onViewModelReadyToStyle ] + self textualCoderViewModel requestUpdateAddOns ] { #category : #'private - event handling' } GtTextualCoderEditorElement >> onPrintRequest: anEvaluationAnnouncement [ - evaluationHighlighter hideResult. - evaluationPrinter displayResult: anEvaluationAnnouncement evaluationResult. + (anEvaluationAnnouncement requesterObject isCoderViewModel: self textualCoderViewModel) + ifFalse: [ ^ self ]. + + BlTaskAction enqueueElement: self action: [ + evaluationHighlighter hideResult. + evaluationPrinter displayResult: anEvaluationAnnouncement evaluationResult ]. ] -{ #category : #'private - event handling' } -GtTextualCoderEditorElement >> onShowDebuggerRequest: aShowDebuggerAnnouncement [ - | sharedDebugSession anEvaluatedSource anEvaluatedInterval theSourceStartInText theSourceEndInText | - - evaluationHighlighter hideResult. - evaluationPrinter hideResult. - - sharedDebugSession := GtSharedDebugSession new - session: aShowDebuggerAnnouncement debugSession. - - self showNotification: (GtNotificationDebugSession new debugSession: sharedDebugSession). - - anEvaluatedSource := aShowDebuggerAnnouncement sourceString. - anEvaluatedInterval := aShowDebuggerAnnouncement sourceInterval. +{ #category : #'hooks - children' } +GtTextualCoderEditorElement >> onRemovedFromSceneGraph [ + super onRemovedFromSceneGraph. - theSourceStartInText := self text finder - caseSensitiveSubstring: anEvaluatedSource; - startAtPosition: anEvaluatedInterval first; - searchClosest. - - "what did we evaluate?" - theSourceStartInText isZero - ifTrue: [ ^ self ]. + GtTextualCoderEditorElementRemovedFromSceneGraphSignal new + element: self; + emit +] + +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> onScrollToCharacterPositionAnnouncement: anAnnouncement [ + self assertUIProcess. + BrEditorSearchTextScroller new + editorElement: self; + lineIndex: anAnnouncement lineIndex; + characterStartPosition: anAnnouncement characterPosition; + characterStopPosition: anAnnouncement characterPosition; + scroll +] - theSourceEndInText := (theSourceStartInText + anEvaluatedSource size - 1) min: self text size. +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> onSearchTextGetTextAndVisibleTextAnnouncement: anAnnouncement [ + self assertUIProcess. + anAnnouncement consumed ifTrue: [ ^ self ]. + anAnnouncement consumed: true. - self text - clearAttributes: [ :each | - { GtEmbeddedDebuggerAttribute } anySatisfy: [ :cls | each isKindOf: cls ] ]. + anAnnouncement text: self text. + anAnnouncement visibleText: self visibleText +] + +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> onSearchTextUpdateTextAnnouncement: anAnnouncement [ + | aResult | + self assertUIProcess. + anAnnouncement consumed ifTrue: [ ^ self ]. + anAnnouncement consumed: true. - (self text from: theSourceStartInText to: theSourceEndInText) - attribute: (GtEmbeddedDebuggerAttribute new - signaledException: aShowDebuggerAnnouncement exception; - debugSession: sharedDebugSession) + aResult := BrEditorSearchTextUpdater new + editorModel: self editor; + styledText: anAnnouncement styledText; + update. + + anAnnouncement isUpdated: aResult ] { #category : #'private - event handling' } @@ -243,9 +372,25 @@ GtTextualCoderEditorElement >> onStyleTextRequest: anEvent [ self enqueueTask: (BlTaskAction new action: [ self styleTextAndAfterDo: anEvent afterAction ]) ] -{ #category : #'private - event handling' } -GtTextualCoderEditorElement >> onStylersUpdated: anAnnouncement [ +{ #category : #initialization } +GtTextualCoderEditorElement >> onTextEditorCursorChanged: anEvent [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel cursors: anEvent cursors from: self +] + +{ #category : #initialization } +GtTextualCoderEditorElement >> onTextEditorHistoryChanged: anEvent [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel history: self editor history copy +] + +{ #category : #initialization } +GtTextualCoderEditorElement >> onTextEditorSelectionChanged: anEvent [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + self textualCoderViewModel selection: anEvent selection from: self ] { #category : #'private - event handling' } @@ -258,6 +403,38 @@ GtTextualCoderEditorElement >> onTextModified [ synchronously: true ] +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> onTextModifiedDueToEvent: aTextModifiedEvent [ + "Is sent when the text changes in the editor. Here we should synchronise the UI and the model. We also receive the event, which can contain the type of change." + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel + sourceText: self text + from: self + synchronously: true + dueToEvent: aTextModifiedEvent +] + +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> onTextStyled: anEvent [ + "An underlying editor styled the text" + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel styledText: anEvent styledText +] + +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> onTextualCoderViewModelAddHighlightRequest: anAnnouncement [ + anAnnouncement textualCoderViewModel = self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ self + addHighlight: anAnnouncement textHighlight + overRange: anAnnouncement textRange ] +] + { #category : #'api - textual coder view model' } GtTextualCoderEditorElement >> onTextualCoderViewModelChanged [ "Is sent when a new textualCoder view model is assigned to the element. @@ -267,31 +444,42 @@ GtTextualCoderEditorElement >> onTextualCoderViewModelChanged [ wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" self onCoderViewModelFocused: self textualCoderViewModel focused. - + "Setting text may change cursor and selection because text editor makes sure that they have valid values. That is why we create a snaphot of the editor's state which we will restore once new text is set" - self editor restoreState: self textualCoderViewModel asEditorState. - self editor model: self textualCoderViewModel. - + "Related to: https://github.com/feenkcom/gtoolkit/issues/3727" + self privateUpdateEditorState: self textualCoderViewModel asEditorState. + + self editor model: self textualCoderViewModel. + shortcuts := self textualCoderViewModel shortcuts copy. self editor addEditorShortcuts: shortcuts. - completion strategy: self textualCoderViewModel coderModel completionStrategy + completion strategy: self textualCoderViewModel completionStrategy. + + self textualCoderViewModel compositeStyler ifNotNil: [ :aStyler | + self styler: aStyler ] ] { #category : #'private - event handling' } -GtTextualCoderEditorElement >> onToolSpawnRequest: anAnnouncement [ - self enqueueTask: (BlTaskAction new action: [ - self phlow - spawnTool: anAnnouncement tool - withDestination: anAnnouncement spawnDestination ]) +GtTextualCoderEditorElement >> onTextualCoderViewModelRemoveHighlightsRequest: anAnnouncement [ + anAnnouncement textualCoderViewModel = self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ self removeHighlightsWithId: anAnnouncement id ] ] { #category : #'private - event handling' } -GtTextualCoderEditorElement >> onUpdateAddOnsRequest: anAnnouncement [ - +GtTextualCoderEditorElement >> onToolSpawnRequest: anAnnouncement [ + self enqueueTask: (BlTaskAction new action: [ + anAnnouncement requesterObject = self textualCoderViewModel ifTrue: [ + self phlow + spawnTool: anAnnouncement tool + withDestination: anAnnouncement spawnDestination ] ]) ] { #category : #'private - event handling' } @@ -303,42 +491,50 @@ GtTextualCoderEditorElement >> onViewModelCursorsChanged: aCursorsChangedAnnounc aCursorsChangedAnnouncement source = self ifTrue: [ ^ self ]. - aCursorsChangedAnnouncement source == self textualCoderViewModel + "do nothing if it comes from a different view model" + aCursorsChangedAnnouncement textualCoderViewModel == self textualCoderViewModel ifFalse: [ ^ self ]. "since it may be sent from a non-UI process make sure to wrap in the action" - self enqueueTask: (BlTaskAction new action: [ - self navigator - withoutResettingDesiredCoordinate; - removeAll; - addAll: aCursorsChangedAnnouncement cursors; - apply ]) + cursorsUpdater requestUpdate: aCursorsChangedAnnouncement ] { #category : #'private - event handling' } GtTextualCoderEditorElement >> onViewModelReadyToStyle [ - self enqueueTask: (BlTaskAction new action: [ self editor styleText ]) + self enqueueTask: (BlTaskAction new action: [ self requestManualStyling ]) +] + +{ #category : #registration } +GtTextualCoderEditorElement >> onViewModelRecomputeAddOns: aRecomputeAddOnRequest [ + aRecomputeAddOnRequest coderViewModel == self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ addOnsElementFuture future: self textualCoderViewModel addOnsFuture ] ] { #category : #'private - event handling' } GtTextualCoderEditorElement >> onViewModelSelectionChanged: aSelectionChangedAnnouncement [ "Is sent when the selection changes in the View Model. May be sent from a non-UI thread" - + "if the source of the announcement is myself, do nothing to break the change cycle" - aSelectionChangedAnnouncement source = self - ifTrue: [ ^ self ]. - + + aSelectionChangedAnnouncement source = self ifTrue: [ ^ self ]. + aSelectionChangedAnnouncement source == self textualCoderViewModel ifFalse: [ ^ self ]. - "since it may be sent from a non-UI process make sure to wrap in the action" - self enqueueTask: (BlTaskAction new action: [ - self deselecter all deselect. - self selecter - withoutCursorUpdate; - all: aSelectionChangedAnnouncement selection; - select ]) + BlTaskAction + enqueueElement: self + action: [ self deselecter all deselect. + self selecter + in: [ :aSelecter | + aSelectionChangedAnnouncement shouldUpdateCursor + ifFalse: [ aSelecter withoutCursorUpdate ] ]; + all: aSelectionChangedAnnouncement selection; + select ] ] { #category : #'private - event handling' } @@ -365,11 +561,23 @@ GtTextualCoderEditorElement >> onViewModelStylersChanged [ { #category : #'private - event handling' } GtTextualCoderEditorElement >> onViewModelTextAttributesAdded: anAnnouncement [ + self textualCoderViewModel = anAnnouncement textualCoderViewModel ifFalse: [ ^ self ]. + + anAnnouncement documentId ifNil: [ + GtCoderTextAttributesAddedDocumentIdIsNilSignal new + coderTextAttributes: anAnnouncement coderTextAttributes; + editorElement: self; + startPosition: anAnnouncement startPosition; + endPosition: anAnnouncement stopPosition; + emit ]. + self enqueueTask: (BlTaskAction new action: [ - anAnnouncement coderTextAttributes - applyOnEditorText: self text - from: anAnnouncement startPosition - to: anAnnouncement stopPosition ]) + anAnnouncement text characters = self text characters + ifTrue: [ + anAnnouncement coderTextAttributes + applyOnEditorText: self text + from: anAnnouncement startPosition + to: anAnnouncement stopPosition ] ]) ] { #category : #'private - event handling' } @@ -380,26 +588,96 @@ GtTextualCoderEditorElement >> onViewModelTextAttributesRemoved: anAnnouncement { #category : #'private - event handling' } GtTextualCoderEditorElement >> onViewModelTextChanged: aGtTextualCoderViewModelTextChanged [ + "Text changed in a view model and should be synchronised on the UI side. + There can be multiple sources of the text change: + - an editor element itself + - other sources that changed text through coder model api. + + When the source is a text editor element (`self`) we don't reset the text in the editor. + If an announcement comes from a different view model, we skip it." + + + "Do nothing if it comes from a different view model" + aGtTextualCoderViewModelTextChanged textualCoderViewModel == self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ self processViewModelTextChanged: aGtTextualCoderViewModelTextChanged ]. +] + +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> onViewModelTextEditCommandRequest: aGtTextualCoderViewModelTextEditCommandRequest [ + "This is sent by the view model on requests to change the text + in the editor by executing a command." + + BlTaskAction + enqueueElement: self + action: [ + aGtTextualCoderViewModelTextEditCommandRequest textEditCommand + applyOn: self editor ] +] + +{ #category : #'private - updating' } +GtTextualCoderEditorElement >> privateUpdateCursors: anEditorElement announcement: aCursorsChangedAnnouncement [ + + "do nothing if it comes from a different view model" + aCursorsChangedAnnouncement source == self textualCoderViewModel + ifFalse: [ ^ self ]. + + GtTextualCoderEditorElementUpdateCursorsSignal new + element: self; + emit. + + self navigator + withoutResettingDesiredCoordinate; + removeAll; + addAll: aCursorsChangedAnnouncement cursors; + apply +] + +{ #category : #'private - updating' } +GtTextualCoderEditorElement >> privateUpdateEditorState: anEditorState [ + GtTextualCoderEditorElementUpdateStateSignal new + element: self; + emit. + self editor restoreState: anEditorState +] + +{ #category : #'private - updating' } +GtTextualCoderEditorElement >> privateUpdateText: anEditorElement announcement: aGtTextualCoderViewModelTextChanged [ + | textForUpdate | + "Executed in a UI thread as a result of text being changed in the view model" + + "do nothing if it comes from a different view model" + aGtTextualCoderViewModelTextChanged textualCoderViewModel == self textualCoderViewModel + ifFalse: [ ^ self ]. + + GtTextualCoderEditorElementUpdateTextSignal new + element: self; + emit. + + "Determine the text that we should use to update the editor." + textForUpdate := self computeTextForUpdateForTextChangedEvent: aGtTextualCoderViewModelTextChanged. + + self text: textForUpdate. + self hideHighlighters. +] + +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> processViewModelTextChanged: aGtTextualCoderViewModelTextChanged [ aGtTextualCoderViewModelTextChanged source == self ifTrue: [ ^ self hideHighlighters ]. - + aGtTextualCoderViewModelTextChanged isSynchronous - ifTrue: [ - self text: aGtTextualCoderViewModelTextChanged text copy. - self hideHighlighters ] - ifFalse: [ self enqueueTask: (BlTaskAction new action: [ - self text: aGtTextualCoderViewModelTextChanged text copy. - self hideHighlighters ]) ] + ifTrue: [ self privateUpdateText: self announcement: aGtTextualCoderViewModelTextChanged ] + ifFalse: [ textUpdater requestUpdate: aGtTextualCoderViewModelTextChanged ] ] { #category : #registration } GtTextualCoderEditorElement >> registerCoderModelAnnouncementsFor: aCoderModel [ aCoderModel announcer weak - when: GtCoderAddOnsUpdateRequest - send: #onUpdateAddOnsRequest: - to: self; - when: GtCoderStylerChanged send: #onStylersUpdated: to: self; when: GtCoderStyleTextRequest send: #onStyleTextRequest: to: self; when: GtCoderParseError send: #onCoderParseError: to: self; when: GtCoderObjectSpawnRequest @@ -409,25 +687,24 @@ GtTextualCoderEditorElement >> registerCoderModelAnnouncementsFor: aCoderModel [ when: GtCoderEvaluationAnnouncement send: #onCodeEvaluated: to: self; - when: GtCoderPrintAnnouncement send: #onPrintRequest: to: self; - when: GtCoderShowDebuggerRequest - send: #onShowDebuggerRequest: - to: self + when: GtCoderPrintAnnouncement send: #onPrintRequest: to: self ] { #category : #registration } GtTextualCoderEditorElement >> registerCoderViewModelAnnouncementsFor: aGtSourceCoderUIModel [ aGtSourceCoderUIModel weak - when: GtMethodCoderContextPCRangeChanged - send: #onContextPCRangeChanged: + when: GtCoderViewModelRecomputeAddOnRequest + send: #onViewModelRecomputeAddOns: to: self; - when: GtSourceCoderFocusChanged + when: GtTextualCoderViewModelFocusChanged send: #onFocusChangedAnnouncement: to: self; - when: GtTextualCoderViewModelTextChanged send: #onViewModelTextChanged: to: self; + when: GtTextualCoderViewModelTextEditCommandRequestAnnouncement + send: #onViewModelTextEditCommandRequest: + to: self; when: GtTextualCoderViewModelCursorsChanged send: #onViewModelCursorsChanged: to: self; @@ -449,31 +726,56 @@ GtTextualCoderEditorElement >> registerCoderViewModelAnnouncementsFor: aGtSource when: GtTextualCoderViewModelTextAttributesRemoved send: #onViewModelTextAttributesRemoved: to: self; + when: GtTextualCoderViewModelAddHighlightRequest + send: #onTextualCoderViewModelAddHighlightRequest: + to: self; + when: GtTextualCoderViewModelRemoveHighlightsRequest + send: #onTextualCoderViewModelRemoveHighlightsRequest: + to: self; when: GtTextualCoderViewModelStyledTextChanged send: #onViewModelStyledTextChanged: - to: self. + to: self; + when: GtTextualCoderViewModelScrollToCharacterPositionAnnouncement + send: #onScrollToCharacterPositionAnnouncement: + to: self; + when: GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement + send: #onSearchTextGetTextAndVisibleTextAnnouncement: + to: self; + when: GtTextualCoderViewModelSearchTextUpdateTextAnnouncement + send: #onSearchTextUpdateTextAnnouncement: + to: self ] { #category : #private } GtTextualCoderEditorElement >> reportParseError: aString at: anInteger [ - | text position | + | error | + error := (aString endsWith: '->') + ifTrue: [ aString allButLast: 2 ] + ifFalse: [ aString ]. + self addErrorAttribute: (GtSourceCoderErrorAttribute for: error) at: anInteger +] - text := self editor text. - - position := anInteger - 1 max: 1. - text size < position - ifTrue: [ ^ self ]. - - text clearAttributesOfClass: GtSourceCoderErrorAttribute. - text - attribute: (GtSourceCoderErrorAttribute for: aString) - from: position - to: position +{ #category : #'private - event handling' } +GtTextualCoderEditorElement >> reportUndeclaredError: aGtCoderParseError [ + | rbNode interval gtNode ast errorAttribute | + rbNode := aGtCoderParseError node. + interval := rbNode sourceInterval. + ast := GtPharoParser + parse: rbNode methodNode source + startingAt: (rbNode methodNode isDoIt + ifTrue: [ GtPharoParser startingStateForDoItMethod ] + ifFalse: [ GtPharoParser startingStateForMethod ]). + gtNode := ast nodeForInterval: interval. + errorAttribute := GtUndeclaredVariableAdvice new + errorAttribute: gtNode + coderModel: aGtCoderParseError coder. + self addErrorAttribute: errorAttribute at: aGtCoderParseError location ] { #category : #private } -GtTextualCoderEditorElement >> styleText [ - self editor styleText +GtTextualCoderEditorElement >> showScrollbars [ + self + addAptitude: (BrGlamorousWithVerticalScrollbarAptitude new scrollableName: self id) ] { #category : #private } @@ -486,13 +788,25 @@ GtTextualCoderEditorElement >> subscribeToTextualCoderViewModel [ "Is sent after a new textualCoder view model is assigned to the element. It is required to unsubscribe from the view model or domain model by implementing #unsubscribeFromTextualCoderViewModel if elements subscribe to them" - - self registerCoderModelAnnouncementsFor: self textualCoderViewModel coderModel. + | coderModel | + + coderModel := self textualCoderViewModel coderModel. + self registerCoderModelAnnouncementsFor: coderModel. self registerCoderViewModelAnnouncementsFor: self textualCoderViewModel. + (coderModel isForMethod and: [ coderModel compiledMethod isNotNil ]) ifTrue: + [ coderModel compiledMethod isBigMethod ifTrue: + [ self beReadOnlyWithSelection ] ]. completion install ] +{ #category : #'editor - accessing' } +GtTextualCoderEditorElement >> text: aBlText [ + (self text equalsIgnoringAttributes: aBlText) + ifTrue: [ ^ self dataSource history disableDuring: [ super text: aBlText ] ]. + ^ super text: aBlText +] + { #category : #'api - textual coder view model' } GtTextualCoderEditorElement >> unsubscribeFromTextualCoderViewModel [ "Is sent before a new textualCoder view model is assigned to the element. diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementAddedToSceneGraphSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementAddedToSceneGraphSignal.class.st new file mode 100644 index 000000000..6892b3a5b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementAddedToSceneGraphSignal.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtTextualCoderEditorElementAddedToSceneGraphSignal, + #superclass : #GtTextualCoderEditorElementSignal, + #category : #'GToolkit-Coder-UI-Coder - Textual' +} diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementAttachedSpaceSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementAttachedSpaceSignal.class.st new file mode 100644 index 000000000..3b1feb1c3 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementAttachedSpaceSignal.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtTextualCoderEditorElementAttachedSpaceSignal, + #superclass : #GtTextualCoderEditorElementSignal, + #category : #'GToolkit-Coder-UI-Coder - Textual' +} diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementDetachedSpaceSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementDetachedSpaceSignal.class.st new file mode 100644 index 000000000..153fbb99e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementDetachedSpaceSignal.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtTextualCoderEditorElementDetachedSpaceSignal, + #superclass : #GtTextualCoderEditorElementSignal, + #category : #'GToolkit-Coder-UI-Coder - Textual' +} diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementRemovedFromSceneGraphSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementRemovedFromSceneGraphSignal.class.st new file mode 100644 index 000000000..c5a83fe1e --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementRemovedFromSceneGraphSignal.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtTextualCoderEditorElementRemovedFromSceneGraphSignal, + #superclass : #GtTextualCoderEditorElementSignal, + #category : #'GToolkit-Coder-UI-Coder - Textual' +} diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementSignal.class.st new file mode 100644 index 000000000..5ca1361ec --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementSignal.class.st @@ -0,0 +1,26 @@ +Class { + #name : #GtTextualCoderEditorElementSignal, + #superclass : #ContextStackSignal, + #instVars : [ + 'element' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual' +} + +{ #category : #testing } +GtTextualCoderEditorElementSignal class >> gtNormalOperationSignal [ + "Answer a Boolean indicating whether this signal is generated as part of normal operations. + Excluding these signals makes a good starting point for application specific logging, or logging unexpected signals" + + ^ true. +] + +{ #category : #accessing } +GtTextualCoderEditorElementSignal >> element [ + ^ element +] + +{ #category : #accessing } +GtTextualCoderEditorElementSignal >> element: anObject [ + element := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateCursorsSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateCursorsSignal.class.st new file mode 100644 index 000000000..265ecc031 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateCursorsSignal.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtTextualCoderEditorElementUpdateCursorsSignal, + #superclass : #GtTextualCoderEditorElementSignal, + #category : #'GToolkit-Coder-UI-Coder - Textual' +} diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateStateSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateStateSignal.class.st new file mode 100644 index 000000000..6a55632a5 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateStateSignal.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtTextualCoderEditorElementUpdateStateSignal, + #superclass : #GtTextualCoderEditorElementSignal, + #category : #'GToolkit-Coder-UI-Coder - Textual' +} diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateTextRequestedSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateTextRequestedSignal.class.st new file mode 100644 index 000000000..737ea184b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateTextRequestedSignal.class.st @@ -0,0 +1,24 @@ +Class { + #name : #GtTextualCoderEditorElementUpdateTextRequestedSignal, + #superclass : #GtTextualCoderEditorElementSignal, + #instVars : [ + 'text' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual' +} + +{ #category : #printing } +GtTextualCoderEditorElementUpdateTextRequestedSignal >> printOneLineContentsOn: aStream [ + + text gtDisplayOn: aStream. +] + +{ #category : #'private - event handling' } +GtTextualCoderEditorElementUpdateTextRequestedSignal >> text [ + ^ text +] + +{ #category : #'private - event handling' } +GtTextualCoderEditorElementUpdateTextRequestedSignal >> text: aBlText [ + text := aBlText +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateTextSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateTextSignal.class.st new file mode 100644 index 000000000..3a8075fcc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderEditorElementUpdateTextSignal.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtTextualCoderEditorElementUpdateTextSignal, + #superclass : #GtTextualCoderEditorElementSignal, + #category : #'GToolkit-Coder-UI-Coder - Textual' +} diff --git a/src/GToolkit-Coder-UI/GtTextualCoderElement.class.st b/src/GToolkit-Coder-UI/GtTextualCoderElement.class.st new file mode 100644 index 000000000..1d277258b --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderElement.class.st @@ -0,0 +1,995 @@ +Class { + #name : #GtTextualCoderElement, + #superclass : #BlElement, + #traits : 'TBlAssertUIProcess + TGtWithTextualCoderViewModel + TBrLayoutResizable', + #classTraits : 'TBlAssertUIProcess classTrait + TGtWithTextualCoderViewModel classTrait + TBrLayoutResizable classTrait', + #instVars : [ + 'completion', + 'evaluationHighlighter', + 'evaluationPrinter', + 'shortcuts', + 'cursorsUpdater', + 'textUpdater', + 'addOnsElementFuture', + 'editorElement', + 'menuItemsElementFuture' + ], + #classVars : [ + 'DefaultCompletionControllerClass' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual' +} + +{ #category : #accessing } +GtTextualCoderElement class >> defaultCompletionControllerClass [ + ^ DefaultCompletionControllerClass +] + +{ #category : #accessing } +GtTextualCoderElement class >> defaultCompletionControllerClass: aCompletionController [ + self + deprecated: 'It is not possible to set completion controller in the new editor element, +therefore such settings will not be supported.'. + + DefaultCompletionControllerClass := aCompletionController +] + +{ #category : #private } +GtTextualCoderElement >> addErrorAttribute: errorAttribute at: anInteger [ + | text position | + text := self editor text. + position := anInteger - 1 max: 0. + text size < position ifTrue: [ ^ self ]. + text clearAttributesOfClass: GtSourceCoderErrorAttribute. + text + attribute: errorAttribute + from: position + to: position. + self scrollToPosition: (text asString lineNumberCorrespondingToIndex: position) +] + +{ #category : #'hooks - children' } +GtTextualCoderElement >> attachSpace [ + super attachSpace. + + GtTextualCoderEditorElementAttachedSpaceSignal new + element: self; + emit +] + +{ #category : #'api - textual coder view model' } +GtTextualCoderElement >> coderViewModel: aTextualCoderViewModel [ + self textualCoderViewModel: aTextualCoderViewModel +] + +{ #category : #'private - updating' } +GtTextualCoderElement >> computeTextForUpdateForTextChangedEvent: aGtTextualCoderViewModelTextChanged [ + "In case the editor has a text edit command we determine + the updated text by editing the text currently in the + editor. If no edit command is present in the event, + we take the text from the event. We use the edit command + to avoid setting the text from the event which is not + styled, and can cause flickerings." + + ^ aGtTextualCoderViewModelTextChanged hasTextEditCommand + ifTrue: [ + | editCommand| + editCommand := aGtTextualCoderViewModelTextChanged textEditCommand. + self editedTextBasedOnEditCommand: editCommand. ] + ifFalse: [ + aGtTextualCoderViewModelTextChanged text copy ] +] + +{ #category : #'instance creation' } +GtTextualCoderElement >> createContextMenu [ + "wait for the addons to be computed" + + + | aMenu aMenuItemsFuture | + aMenu := BrMenuItems new. + aMenu beGroupedElementType. + + aMenuItemsFuture := self textualCoderViewModel addOnsFuture + map: [ :theAddOns | + | theItems theContextMenuAstAddons theMenuItems | + "extra context menu items that depend on ast and view model state such as selection" + theContextMenuAstAddons := self textualCoderViewModel + computeContextMenuAstAddOns. + theItems := theAddOns contextMenuActions, theContextMenuAstAddons contextMenuActions. + theItems := theItems reject: [ :e | e title isNil ]. + + theMenuItems := theItems + collect: [ :eachAction | eachAction asBrMenuItemForCoderElement: self ] + thenReject: #isNil. + + aMenu -> theMenuItems ]. + + menuItemsElementFuture future: aMenuItemsFuture. + + ^ aMenu +] + +{ #category : #TODO } +GtTextualCoderElement >> deleter [ + "This method should be deprecated and removed, instead add higher-level api methods" + self flag: #TODO. + + ^ editorElement deleter +] + +{ #category : #TODO } +GtTextualCoderElement >> deselecter [ + "This method should be deprecated and removed, instead add higher-level api methods" + self flag: #TODO. + + ^ editorElement deselecter +] + +{ #category : #'hooks - children' } +GtTextualCoderElement >> detachSpace [ + super detachSpace. + + GtTextualCoderEditorElementDetachedSpaceSignal new + element: self; + emit +] + +{ #category : #'private - updating' } +GtTextualCoderElement >> editedTextBasedOnEditCommand: aTextEditCommand [ + | modifiedText| + "self haltOnce." + + modifiedText := self text copy. + aTextEditCommand applyTextChangeOn: modifiedText. + ^ modifiedText +] + +{ #category : #TODO } +GtTextualCoderElement >> editor [ + "This method should be deprecated and removed, instead add higher-level api methods" + self flag: #TODO. + + ^ editorElement editor +] + +{ #category : #TODO } +GtTextualCoderElement >> editorElement [ + ^ editorElement +] + +{ #category : #'focus requesting' } +GtTextualCoderElement >> focusTarget [ + ^ editorElement +] + +{ #category : #private } +GtTextualCoderElement >> hideHighlighters [ + self text clearAttributes: [ :eachAttribute | + eachAttribute isKindOf: GtSourceCoderErrorAttribute ]. + evaluationHighlighter hideResultSynchronously. + evaluationPrinter hideResultSynchronously. +] + +{ #category : #initialization } +GtTextualCoderElement >> initialize [ + super initialize. + + editorElement := BrEditor new. + editorElement + beMode: BrTextEditorEditableCodeMode new; + id: GtSourceCoderEditorInnerId; + aptitude: BrGlamorousCodeEditorAptitude; + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude menu: [ self createContextMenu ]); + padding: BlInsets empty. + + self addChild: editorElement. + + self + layout: BlLinearLayout vertical; + addAptitude: (BrLayoutResizerAptitude new inherit: [ editorElement ]); + hMatchParent; + vFitContentLimited. + + self id: GtSourceCoderEditorId. + + shortcuts := #(). + self class defaultCompletionControllerClass ifNotNil: [ :aClass | + completion := aClass on: editorElement ]. + + evaluationHighlighter := GtSourceCoderEvaluationHighlighter new + editorElement: self. + evaluationPrinter := GtSourceCoderEvaluationPrinter new editorElement: self. + + cursorsUpdater := BrElementUpdater new + element: self; + action: (MessageSend receiver: self selector: #privateUpdateCursors:announcement:). + + textUpdater := BrElementUpdater new + element: self; + action: (MessageSend receiver: self selector: #privateUpdateText:announcement:). + + addOnsElementFuture := (BrAsyncElementFuture on: self) + executionConfiguration: GtSingleCoderViewModel methodAddOnsExecutionConfiguration; + whenSuccess: [ :anEditorElement :theAddOns | self textualCoderViewModel onAddOnsChanged: theAddOns ]; + whenError: [ :anEditorElement :anError | self onAddOnsError: anError ]. + + menuItemsElementFuture := (BrAsyncElementFuture on: self) + executionConfiguration: (GtSingleCoderViewModel methodAddOnsExecutionConfiguration); + whenSuccess: [ :anEditorElement :anAssoc | + anAssoc key items: anAssoc value ]; + whenError: [ :anEditorElement :anError | + self onContextMenuError: anError ]. + + self initializeListeners. + + self + withAsyncPromiseDo: [ :anElementPromise | + anElementPromise + whenSuccess: [ :anEditorElement :anEditorState | anEditorElement privateUpdateEditorState: anEditorState ]; + whenError: [ :anEditorElement :anEditorState | ]; + whenPending: [ :anEditorElement | ] ] +] + +{ #category : #initialization } +GtTextualCoderElement >> initializeEditorElement: aNewEditorElement [ + | anOldEditor | + self assert: [ aNewEditorElement isKindOf: GtTextEditorElement ]. + + anOldEditor := editorElement. + editorElement := aNewEditorElement. + self + replaceChild: anOldEditor + with: editorElement + as: GtSourceCoderEditorInnerId. + + aNewEditorElement + aptitude: BrGlamorousCodeEditorAptitude; + addAptitude: (BrGlamorousWithExplicitContextMenuAptitude menu: [ self createContextMenu ]); + padding: BlInsets empty. + + completion ifNotNil: [ :aCompletion | + aCompletion uninstall. + completion := nil ]. + + self initializeEditorListeners. + + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + self onTextualCoderViewModelChanged. + self onPostTextualCoderViewModelChanged +] + +{ #category : #initialization } +GtTextualCoderElement >> initializeEditorListeners [ + self editor + when: BrTextEditorTextReplacedEvent + do: [ :anEvent | self onTextEditorTextReplacedEvent: anEvent ]. + + self editor + when: BrTextEditorHistoryChangedEvent + do: [ :anEvent | self onTextEditorHistoryChanged: anEvent ]. + + self editor + when: BrTextEditorModificationEvent + do: [ :anEvent | self onTextModifiedDueToEvent: anEvent ]. + + self editor + when: BrTextEditorCursorChangedEvent + do: [ :anEvent | self onTextEditorCursorChanged: anEvent ]. + + self editor + when: BrTextEditorSelectionChangedEvent + do: [ :anEvent | self onTextEditorSelectionChanged: anEvent ]. + + self editor + when: BrTextEditorTextStyledEvent + do: [ :anEvent | self onTextStyled: anEvent ] +] + +{ #category : #initialization } +GtTextualCoderElement >> initializeListeners [ + self when: BlFocusInEvent do: [ :anEvent | self onFocusInEvent: anEvent ]. + self when: BlFocusOutEvent do: [ :anEvent | self onFocusOutEvent: anEvent ]. + self initializeEditorListeners +] + +{ #category : #TODO } +GtTextualCoderElement >> inserter [ + "This method should be deprecated and removed, instead add higher-level api methods" + self flag: #TODO. + + ^ editorElement inserter +] + +{ #category : #TODO } +GtTextualCoderElement >> navigator [ + "This method should be deprecated and removed, instead add higher-level api methods" + self flag: #TODO. + + ^ editorElement navigator +] + +{ #category : #initialization } +GtTextualCoderElement >> onAddOnsError: anError [ + anError emit. + NonInteractiveTranscript stderr + show: '[GtTextualCoderEditorElement>>#onAddOnsError:] '; + show: anError; + newLine +] + +{ #category : #'hooks - children' } +GtTextualCoderElement >> onAddedToSceneGraph [ + super onAddedToSceneGraph. + + GtTextualCoderEditorElementAddedToSceneGraphSignal new + element: self; + emit +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onCodeEvaluated: anEvaluationAnnouncement [ + (anEvaluationAnnouncement requesterObject + isCoderViewModel: self textualCoderViewModel) ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ + evaluationHighlighter displayResult: anEvaluationAnnouncement evaluationResult. + evaluationPrinter hideResult ] +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onCoderParseError: aGtCoderParseError [ + "A parse error can be notifying from a non-UI thread" + + (aGtCoderParseError requesterObject + isUndefinedOrCoderViewModel: self textualCoderViewModel) ifFalse: [ ^ self ]. + self + enqueueTask: [ aGtCoderParseError isUndeclaredError + ifTrue: [ self reportUndeclaredError: aGtCoderParseError ] + ifFalse: [ self + reportParseError: aGtCoderParseError errorMessage + at: aGtCoderParseError location ] ] asBlTask +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onCoderViewModelFocused: aBoolean [ + aBoolean + ifTrue: [ self requestFocus ] + ifFalse: [ self loseFocus ] +] + +{ #category : #initialization } +GtTextualCoderElement >> onContextMenuError: anError [ + anError emit. + NonInteractiveTranscript stderr + show: '[GtTextualCoderElement>>#onContextMenuError:] '; + show: anError; + newLine +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onFocusChangedAnnouncement: anAnnouncement [ + anAnnouncement source == self ifTrue: [ ^ self ]. + + anAnnouncement source == self textualCoderViewModel ifFalse: [ ^ self ]. + + self + enqueueTask: (BlTaskAction new + action: [ self onCoderViewModelFocused: anAnnouncement focused ]) +] + +{ #category : #initialization } +GtTextualCoderElement >> onFocusInEvent: anEvent [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel focused: true from: self +] + +{ #category : #initialization } +GtTextualCoderElement >> onFocusOutEvent: anEvent [ + anEvent isDueToRemoval ifTrue: [ ^ self ]. + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel focused: false from: self +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onObjectSpawnRequest: anAnnouncement [ + (anAnnouncement requesterObject isCoderViewModel: self textualCoderViewModel) + ifFalse: [ ^ self ]. + + self enqueueTask: (BlTaskAction new action: [ + self phlow + spawnObject: anAnnouncement object + withDestination: anAnnouncement spawnDestination ]) +] + +{ #category : #'api - textual coder view model' } +GtTextualCoderElement >> onPostTextualCoderViewModelChanged [ + "I am an optional hook method that is sent after #subscribeToTextualCoderViewModel. + I do nothing by default but allow users to perform update operations when a receiver object is already + subscribed to announcements." + + self textualCoderViewModel requestUpdateAddOns +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onPrintRequest: anEvaluationAnnouncement [ + (anEvaluationAnnouncement requesterObject isCoderViewModel: self textualCoderViewModel) + ifFalse: [ ^ self ]. + + BlTaskAction enqueueElement: self action: [ + evaluationHighlighter hideResult. + evaluationPrinter displayResult: anEvaluationAnnouncement evaluationResult ]. +] + +{ #category : #'hooks - children' } +GtTextualCoderElement >> onRemovedFromSceneGraph [ + super onRemovedFromSceneGraph. + + GtTextualCoderEditorElementRemovedFromSceneGraphSignal new + element: self; + emit +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onScrollToCharacterPositionAnnouncement: anAnnouncement [ + self assertUIProcess. + BrEditorSearchTextScroller new + editorElement: editorElement; + lineIndex: anAnnouncement lineIndex; + characterStartPosition: anAnnouncement characterPosition; + characterStopPosition: anAnnouncement characterPosition; + scroll +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onSearchTextGetTextAndVisibleTextAnnouncement: anAnnouncement [ + self assertUIProcess. + anAnnouncement consumed ifTrue: [ ^ self ]. + anAnnouncement consumed: true. + + anAnnouncement text: self text. + anAnnouncement visibleText: self visibleText +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onSearchTextUpdateTextAnnouncement: anAnnouncement [ + | aResult | + self assertUIProcess. + anAnnouncement consumed ifTrue: [ ^ self ]. + anAnnouncement consumed: true. + + aResult := BrEditorSearchTextUpdater new + editorModel: self editor; + styledText: anAnnouncement styledText; + update. + + anAnnouncement isUpdated: aResult +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onStyleTextRequest: anEvent [ + "onStyleTextRequest: may be sent from a non-UI thread" + + self enqueueTask: (BlTaskAction new action: [ self styleTextAndAfterDo: anEvent afterAction ]) +] + +{ #category : #initialization } +GtTextualCoderElement >> onTextEditorCursorChanged: anEvent [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel cursors: anEvent cursors from: self +] + +{ #category : #initialization } +GtTextualCoderElement >> onTextEditorHistoryChanged: anEvent [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel history: self editor history copy +] + +{ #category : #initialization } +GtTextualCoderElement >> onTextEditorSelectionChanged: anEvent [ + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel selection: anEvent selection from: self +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onTextEditorTextReplacedEvent: aTextReplacedEvent [ + "We assume that the text we have in the view model is same + as this event is a consequence that this element updated + its editor element in GtTextualCoderElement>>#onTextualCoderViewModelChanged. + For that reason we just update the document ID." + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel + documentId: aTextReplacedEvent documentId +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onTextModified [ + "Is sent when the text changes in the editor. Here we should synchronise the UI and the model." + + self textualCoderViewModel + sourceText: self text + from: self + synchronously: true +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onTextModifiedDueToEvent: aTextModifiedEvent [ + "Is sent when the text changes in the editor. Here we should synchronise the UI and the model. We also receive the event, which can contain the type of change." + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel + documentId: aTextModifiedEvent documentId; + sourceText: self text + from: self + synchronously: true + dueToEvent: aTextModifiedEvent +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onTextStyled: anEvent [ + "An underlying editor styled the text" + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + + self textualCoderViewModel styledText: anEvent styledText +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onTextualCoderViewModelAddHighlightRequest: anAnnouncement [ + anAnnouncement textualCoderViewModel = self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ editorElement + addHighlight: anAnnouncement textHighlight + overRange: anAnnouncement textRange ] +] + +{ #category : #'api - textual coder view model' } +GtTextualCoderElement >> onTextualCoderViewModelChanged [ + "Is sent when a new textualCoder view model is assigned to the element. + Note: #onTextualCoderViewModelChanged is sent before #subscribeToTextualCoderViewModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostTextualCoderViewModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + + | coderModel | + coderModel := self textualCoderViewModel coderModel. + (coderModel isForMethod and: [ coderModel compiledMethod isNotNil ]) ifTrue: + [ coderModel compiledMethod isBigMethod ifTrue: + [ editorElement beReadOnlyWithSelection ] ]. + + self onCoderViewModelFocused: self textualCoderViewModel focused. + + "Setting text may change cursor and selection because text editor makes sure + that they have valid values. That is why we create a snaphot of the editor's state + which we will restore once new text is set" + + "Related to: https://github.com/feenkcom/gtoolkit/issues/3727" + self privateUpdateEditorState: self textualCoderViewModel asEditorState. + + self editor model: self textualCoderViewModel. + + shortcuts := self textualCoderViewModel shortcuts copy. + self editor addEditorShortcuts: shortcuts. + + completion + ifNotNil: [ :aCompletion | + aCompletion strategy: self textualCoderViewModel completionStrategy ] + ifNil: [ editorElement completionStrategy: self textualCoderViewModel completionStrategy ]. + + self textualCoderViewModel compositeStyler ifNotNil: [ :aStyler | + self styler: aStyler ] +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onTextualCoderViewModelRemoveHighlightsRequest: anAnnouncement [ + anAnnouncement textualCoderViewModel = self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ editorElement removeHighlightsWithId: anAnnouncement id ] +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onToolSpawnRequest: anAnnouncement [ + self enqueueTask: (BlTaskAction new action: [ + anAnnouncement requesterObject = self textualCoderViewModel ifTrue: [ + self phlow + spawnTool: anAnnouncement tool + withDestination: anAnnouncement spawnDestination ] ]) +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelCursorsChanged: aCursorsChangedAnnouncement [ + "Is sent when the cursors changes in the View Model. + May be sent from a non-UI thread" + + "if the source of the announcement is myself, do nothing to break the change cycle" + aCursorsChangedAnnouncement source = self + ifTrue: [ ^ self ]. + + "do nothing if it comes from a different view model" + aCursorsChangedAnnouncement textualCoderViewModel == self textualCoderViewModel + ifFalse: [ ^ self ]. + + "since it may be sent from a non-UI process make sure to wrap in the action" + cursorsUpdater requestUpdate: aCursorsChangedAnnouncement +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelReadyToStyle [ + self enqueueTask: (BlTaskAction new action: [ self styleText ]) +] + +{ #category : #registration } +GtTextualCoderElement >> onViewModelRecomputeAddOns: aRecomputeAddOnRequest [ + aRecomputeAddOnRequest coderViewModel == self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ addOnsElementFuture future: self textualCoderViewModel addOnsFuture ] +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelSelectionChanged: aSelectionChangedAnnouncement [ + "Is sent when the selection changes in the View Model. + May be sent from a non-UI thread" + + "if the source of the announcement is myself, do nothing to break the change cycle" + + aSelectionChangedAnnouncement source = self ifTrue: [ ^ self ]. + + aSelectionChangedAnnouncement source == self textualCoderViewModel + ifFalse: [ ^ self ]. + + BlTaskAction + enqueueElement: self + action: [ self deselecter all deselect. + self selecter + in: [ :aSelecter | + aSelectionChangedAnnouncement shouldUpdateCursor + ifFalse: [ aSelecter withoutCursorUpdate ] ]; + all: aSelectionChangedAnnouncement selection; + select ] +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelShortcutsChanged [ + "Is sent when the shortcuts change in the View Model. + May be sent from a non-UI thread" + + "since it may be sent from a non-UI process make sure to wrap in the action" + self enqueueTask: (BlTaskAction new action: [ + self editor removeEditorShortcuts: shortcuts. + shortcuts := self textualCoderViewModel shortcuts copy. + self editor addEditorShortcuts: shortcuts ]) +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelStyledTextChanged: anAnnouncement [ +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelStylersChanged [ + self enqueueTask: (BlTaskAction new + action: [ self styler: self textualCoderViewModel compositeStyler ]) +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelTextAttributesAdded: anAnnouncement [ + self assert: [ anAnnouncement startPosition isZero not ]. + + self textualCoderViewModel = anAnnouncement textualCoderViewModel ifFalse: [ ^ self ]. + + anAnnouncement documentId ifNil: [ + GtCoderTextAttributesAddedDocumentIdIsNilSignal new + coderTextAttributes: anAnnouncement coderTextAttributes; + editorElement: self; + startPosition: anAnnouncement startPosition; + endPosition: anAnnouncement stopPosition; + emit ]. + + self enqueueTask: (BlTaskAction new action: [ + anAnnouncement coderTextAttributes + applyOnEditorElement: editorElement + receivedText: anAnnouncement text + from: anAnnouncement startPosition + to: anAnnouncement stopPosition ]) +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelTextAttributesRemoved: anAnnouncement [ + self textualCoderViewModel = anAnnouncement textualCoderViewModel ifFalse: [ ^ self ]. + + self enqueueTask: (BlTaskAction new action: [ + anAnnouncement coderTextAttributes removeFromEditorElement: editorElement ]) +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelTextChanged: aGtTextualCoderViewModelTextChanged [ + "Text changed in a view model and should be synchronised on the UI side. + There can be multiple sources of the text change: + - an editor element itself + - other sources that changed text through coder model api. + + When the source is a text editor element (`self`) we don't reset the text in the editor. + If an announcement comes from a different view model, we skip it." + + + "Do nothing if it comes from a different view model" + aGtTextualCoderViewModelTextChanged textualCoderViewModel == self textualCoderViewModel + ifFalse: [ ^ self ]. + + GtTextualCoderEditorElementUpdateTextRequestedSignal new + element: self; + text: aGtTextualCoderViewModelTextChanged text; + emit. + + BlTaskAction + enqueueElement: self + action: [ self processViewModelTextChanged: aGtTextualCoderViewModelTextChanged ]. +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> onViewModelTextEditCommandRequest: aGtTextualCoderViewModelTextEditCommandRequest [ + "This is sent by the view model on requests to change the text + in the editor by executing a command." + + BlTaskAction + enqueueElement: self + action: [ + aGtTextualCoderViewModelTextEditCommandRequest textEditCommand + applyOn: self editor ] +] + +{ #category : #'private - updating' } +GtTextualCoderElement >> privateUpdateCursors: anEditorElement announcement: aCursorsChangedAnnouncement [ + + "do nothing if it comes from a different view model" + aCursorsChangedAnnouncement source == self textualCoderViewModel + ifFalse: [ ^ self ]. + + GtTextualCoderEditorElementUpdateCursorsSignal new + element: self; + emit. + + self navigator + withoutResettingDesiredCoordinate; + removeAll; + addAll: aCursorsChangedAnnouncement cursors; + apply +] + +{ #category : #'private - updating' } +GtTextualCoderElement >> privateUpdateEditorState: anEditorState [ + GtTextualCoderEditorElementUpdateStateSignal new + element: self; + emit. + + self editor restoreState: anEditorState +] + +{ #category : #'private - updating' } +GtTextualCoderElement >> privateUpdateText: anEditorElement announcement: aGtTextualCoderViewModelTextChanged [ + | textForUpdate | + "Executed in a UI thread as a result of text being changed in the view model" + + "do nothing if it comes from a different view model" + aGtTextualCoderViewModelTextChanged textualCoderViewModel == self textualCoderViewModel + ifFalse: [ ^ self ]. + + GtTextualCoderEditorElementUpdateTextSignal new + element: self; + emit. + + "Determine the text that we should use to update the editor." + textForUpdate := self computeTextForUpdateForTextChangedEvent: aGtTextualCoderViewModelTextChanged. + + self text: textForUpdate. + self hideHighlighters. +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> processViewModelTextChanged: aGtTextualCoderViewModelTextChanged [ + aGtTextualCoderViewModelTextChanged source == self + ifTrue: [ ^ self hideHighlighters ]. + + aGtTextualCoderViewModelTextChanged isSynchronous + ifTrue: [ self privateUpdateText: self announcement: aGtTextualCoderViewModelTextChanged ] + ifFalse: [ textUpdater requestUpdate: aGtTextualCoderViewModelTextChanged ] +] + +{ #category : #registration } +GtTextualCoderElement >> registerCoderModelAnnouncementsFor: aCoderModel [ + aCoderModel announcer weak + when: GtCoderStyleTextRequest send: #onStyleTextRequest: to: self; + when: GtCoderParseError send: #onCoderParseError: to: self; + when: GtCoderObjectSpawnRequest + send: #onObjectSpawnRequest: + to: self; + when: GtCoderToolSpawnRequest send: #onToolSpawnRequest: to: self; + when: GtCoderEvaluationAnnouncement + send: #onCodeEvaluated: + to: self; + when: GtCoderPrintAnnouncement send: #onPrintRequest: to: self +] + +{ #category : #registration } +GtTextualCoderElement >> registerCoderViewModelAnnouncementsFor: aGtSourceCoderUIModel [ + aGtSourceCoderUIModel weak + when: GtCoderViewModelRecomputeAddOnRequest + send: #onViewModelRecomputeAddOns: + to: self; + when: GtTextualCoderViewModelFocusChanged + send: #onFocusChangedAnnouncement: + to: self; + when: GtTextualCoderViewModelTextChanged + send: #onViewModelTextChanged: + to: self; + when: GtTextualCoderViewModelTextEditCommandRequestAnnouncement + send: #onViewModelTextEditCommandRequest: + to: self; + when: GtTextualCoderViewModelCursorsChanged + send: #onViewModelCursorsChanged: + to: self; + when: GtTextualCoderViewModelSelectionChanged + send: #onViewModelSelectionChanged: + to: self; + when: GtTextualCoderViewModelStylersChanged + send: #onViewModelStylersChanged + to: self; + when: GtTextualCoderViewModelShortcutsChanged + send: #onViewModelShortcutsChanged + to: self; + when: GtTextualCoderViewModelReadyToRestyle + send: #onViewModelReadyToStyle + to: self; + when: GtTextualCoderViewModelTextAttributesAdded + send: #onViewModelTextAttributesAdded: + to: self; + when: GtTextualCoderViewModelTextAttributesRemoved + send: #onViewModelTextAttributesRemoved: + to: self; + when: GtTextualCoderViewModelAddHighlightRequest + send: #onTextualCoderViewModelAddHighlightRequest: + to: self; + when: GtTextualCoderViewModelRemoveHighlightsRequest + send: #onTextualCoderViewModelRemoveHighlightsRequest: + to: self; + when: GtTextualCoderViewModelStyledTextChanged + send: #onViewModelStyledTextChanged: + to: self; + when: GtTextualCoderViewModelScrollToCharacterPositionAnnouncement + send: #onScrollToCharacterPositionAnnouncement: + to: self; + when: GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement + send: #onSearchTextGetTextAndVisibleTextAnnouncement: + to: self; + when: GtTextualCoderViewModelSearchTextUpdateTextAnnouncement + send: #onSearchTextUpdateTextAnnouncement: + to: self +] + +{ #category : #private } +GtTextualCoderElement >> reportParseError: aString at: anInteger [ + | error | + error := (aString endsWith: '->') + ifTrue: [ aString allButLast: 2 ] + ifFalse: [ aString ]. + self addErrorAttribute: (GtSourceCoderErrorAttribute for: error) at: anInteger +] + +{ #category : #'private - event handling' } +GtTextualCoderElement >> reportUndeclaredError: aGtCoderParseError [ + | rbNode interval gtNode ast errorAttribute | + rbNode := aGtCoderParseError node. + interval := rbNode sourceInterval. + ast := GtPharoParser + parse: rbNode methodNode source + startingAt: (rbNode methodNode isDoIt + ifTrue: [ GtPharoParser startingStateForDoItMethod ] + ifFalse: [ GtPharoParser startingStateForMethod ]). + gtNode := ast nodeForInterval: interval. + errorAttribute := GtUndeclaredVariableAdvice new + errorAttribute: gtNode + coderModel: aGtCoderParseError coder. + self addErrorAttribute: errorAttribute at: aGtCoderParseError location +] + +{ #category : #private } +GtTextualCoderElement >> scrollToPosition: anObject [ + editorElement scrollToPosition: anObject +] + +{ #category : #TODO } +GtTextualCoderElement >> selecter [ + "This method should be deprecated and removed, instead add higher-level api methods" + self flag: #TODO. + + ^ editorElement selecter +] + +{ #category : #TODO } +GtTextualCoderElement >> selection [ + "This method should be deprecated and removed, instead add higher-level api methods" + self flag: #TODO. + + ^ editorElement selection +] + +{ #category : #private } +GtTextualCoderElement >> showScrollbars [ + self + addAptitude: (BrGlamorousWithVerticalScrollbarAptitude new scrollableName: editorElement id) +] + +{ #category : #private } +GtTextualCoderElement >> styleText [ + self editor requestManualStyling +] + +{ #category : #private } +GtTextualCoderElement >> styleTextAndAfterDo: aBlock [ + self editor styleTextAndAfterDo: aBlock +] + +{ #category : #TODO } +GtTextualCoderElement >> styler: anObject [ + editorElement styler: anObject +] + +{ #category : #'api - textual coder view model' } +GtTextualCoderElement >> subscribeToTextualCoderViewModel [ + "Is sent after a new textualCoder view model is assigned to the element. + It is required to unsubscribe from the view model or domain model by implementing + #unsubscribeFromTextualCoderViewModel if elements subscribe to them" + + self registerCoderModelAnnouncementsFor: self textualCoderViewModel coderModel. + self registerCoderViewModelAnnouncementsFor: self textualCoderViewModel. + + completion ifNotNil: #install +] + +{ #category : #TODO } +GtTextualCoderElement >> text [ + ^ editorElement text +] + +{ #category : #TODO } +GtTextualCoderElement >> text: aBlText [ + self text == aBlText ifTrue: [ ^ self ]. + + (self text equalsIgnoringAttributes: aBlText) + ifTrue: [ self editor history + disableDuring: [ editorElement text: aBlText ] ] + ifFalse: [ editorElement text: aBlText ] +] + +{ #category : #'api - textual coder view model' } +GtTextualCoderElement >> unsubscribeFromTextualCoderViewModel [ + "Is sent before a new textualCoder view model is assigned to the element. + Elements that subscribe to textualCoder view model in domain model are required to implement this methods." + + self textualCoderViewModel unsubscribe: self. + self textualCoderViewModel coderModel unsubscribe: self. + + completion + ifNotNil: #uninstall + ifNil: [ editorElement resetCompletionStrategy ]. + self editor removeEditorShortcuts: shortcuts +] + +{ #category : #TODO } +GtTextualCoderElement >> visibleText [ + ^ editorElement visibleText +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderMenu.class.st b/src/GToolkit-Coder-UI/GtTextualCoderMenu.class.st new file mode 100644 index 000000000..896881a39 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderMenu.class.st @@ -0,0 +1,39 @@ +Class { + #name : #GtTextualCoderMenu, + #superclass : #BrMenu, + #instVars : [ + 'coder', + 'coderViewModelStencil' + ], + #category : #'GToolkit-Coder-UI-Menu' +} + +{ #category : #visiting } +GtTextualCoderMenu >> acceptVisitor: aVisitor [ + ^ aVisitor visitTextualCoderMenu: self +] + +{ #category : #'api - initialization' } +GtTextualCoderMenu >> coder: aCoder [ + self coderViewModelStencil: [ aCoder asCoderViewModel ] asStencil +] + +{ #category : #'api - initialization' } +GtTextualCoderMenu >> coderStencil: aCoderStencil [ + self coderViewModelStencil: [ aCoderStencil asStencil create asCoderViewModel ] asStencil +] + +{ #category : #'api - initialization' } +GtTextualCoderMenu >> coderViewModel: aCoderViewModel [ + self coderViewModelStencil: [ aCoderViewModel ] asStencil +] + +{ #category : #accessing } +GtTextualCoderMenu >> coderViewModelStencil [ + ^ coderViewModelStencil +] + +{ #category : #'api - initialization' } +GtTextualCoderMenu >> coderViewModelStencil: aCoderViewModelStencil [ + coderViewModelStencil := aCoderViewModelStencil asStencil +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderModificationIndicatorId.class.st b/src/GToolkit-Coder-UI/GtTextualCoderModificationIndicatorId.class.st new file mode 100644 index 000000000..d78bc2e61 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderModificationIndicatorId.class.st @@ -0,0 +1,13 @@ +" +A bar that appears when a textual coder is modified +" +Class { + #name : #GtTextualCoderModificationIndicatorId, + #superclass : #GtCoderElementId, + #category : #'GToolkit-Coder-UI-Coder - Ids' +} + +{ #category : #converting } +GtTextualCoderModificationIndicatorId >> asSymbol [ + ^ #'textual-coder--modification-indicator' +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderPasteContextMenuItemId.class.st b/src/GToolkit-Coder-UI/GtTextualCoderPasteContextMenuItemId.class.st index a63874090..7bfe3a349 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderPasteContextMenuItemId.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderPasteContextMenuItemId.class.st @@ -6,7 +6,7 @@ A context menu action to paste a text Class { #name : #GtTextualCoderPasteContextMenuItemId, #superclass : #GtCoderElementId, - #category : #'GToolkit-Coder-UI-Coder - Basic' + #category : #'GToolkit-Coder-UI-Coder - Ids' } { #category : #converting } diff --git a/src/GToolkit-Coder-UI/GtTextualCoderSearchTextElement.class.st b/src/GToolkit-Coder-UI/GtTextualCoderSearchTextElement.class.st new file mode 100644 index 000000000..a738b2bbc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderSearchTextElement.class.st @@ -0,0 +1,200 @@ +Class { + #name : #GtTextualCoderSearchTextElement, + #superclass : #BrEditorSearchTextElement, + #traits : 'TGtWithTextualCoderViewModel', + #classTraits : 'TGtWithTextualCoderViewModel classTrait', + #instVars : [ + 'marker', + 'context' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model' +} + +{ #category : #initialization } +GtTextualCoderSearchTextElement >> initialize [ + super initialize. + self initializeMarker. + self initializeEventHandlers. + + self hFitContentLimited +] + +{ #category : #initialization } +GtTextualCoderSearchTextElement >> initializeEventHandlers [ + self + when: BrEditorSearchTextPatternWish + do: [ :anEvent | self onBrSearchTextPatternWish: anEvent ]. + self + when: BrEditorSearchTextNoPatternWish + do: [ :anEvent | self onBrSearchTextNoPatternWish: anEvent ]. + self + when: BrEditorSearchTextNextOccurenceWish + do: [ :anEvent | self onBrSearchTextNextOccurenceWish: anEvent ]. + self + when: BrEditorSearchTextPreviousOccurenceWish + do: [ :anEvent | self onBrSearchTextPreviousOccurenceWish: anEvent ]. + self + when: BrEditorSearchTextEndWish + do: [ :anEvent | self onBrSearchTextEndWish: anEvent ] +] + +{ #category : #initialization } +GtTextualCoderSearchTextElement >> initializeMarker [ + marker := BrEditorSearchTextMarker new + computation: BrEditorSearchTextAsyncFutureComputation newDefault. + + marker + when: BrEditorSearchTextFinishedAnnouncement + send: #onBrEditorSearchTextFinishedAnnouncement: + to: self +] + +{ #category : #accessing } +GtTextualCoderSearchTextElement >> markerContext [ + "For testing purposes only" + + + ^ context +] + +{ #category : #private } +GtTextualCoderSearchTextElement >> newMarkerContext [ + self hasTextualCoderViewModel ifFalse: [ ^ nil ]. + + ^ BrEditorSearchTextMarkerContext new + fromCoderViewModel: self textualCoderViewModel; + element: self +] + +{ #category : #'private - event management' } +GtTextualCoderSearchTextElement >> onBrEditorSearchTextFinishedAnnouncement: anAnnouncement [ + BlTaskAction + enqueueElement: self + action: [ self index: anAnnouncement selectedIndex total: anAnnouncement itemsCount ] +] + +{ #category : #initialization } +GtTextualCoderSearchTextElement >> onBrSearchTextEndWish: anEvent [ + | aContext aRange aSelection | + anEvent consumed: true. + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + aContext := context ifNil: [ ^ self ]. + context := nil. + + marker unmark: aContext. + aRange := aContext selectedRange. + aRange ifNil: [ ^ self ]. + + aSelection := BlCompositeSelection new select: aRange first - 1 to: aRange last. + self textualCoderViewModel + announce: (GtTextualCoderViewModelSelectionChanged new + shouldUpdateCursor: true; + selection: aSelection; + source: self textualCoderViewModel); + announce: (GtTextualCoderViewModelFocusChanged new + focused: true; + source: self textualCoderViewModel). + anEvent currentTarget fireEvent: BrDropdownHideWish new +] + +{ #category : #'private - event management' } +GtTextualCoderSearchTextElement >> onBrSearchTextNextOccurenceWish: anEvent [ + | aContext | + anEvent consumed: true. + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + aContext := context ifNil: [ ^ self ]. + + marker markNext: aContext. + self textualCoderViewModel + scrollToLine: aContext selectedLine + withCharacterPosition: aContext selectedStartPosition +] + +{ #category : #'private - event management' } +GtTextualCoderSearchTextElement >> onBrSearchTextNoPatternWish: anEvent [ + | aContext | + anEvent consumed: true. + + aContext := context. + context := nil. + aContext ifNil: [ ^ self ]. + + marker unmark: aContext +] + +{ #category : #'private - event management' } +GtTextualCoderSearchTextElement >> onBrSearchTextPatternWish: anEvent [ + | aContext | + anEvent consumed: true. + + aContext := self newMarkerContext. + aContext ifNil: [ ^ self ]. + + aContext pattern: anEvent pattern. + context := aContext. + marker mark: aContext +] + +{ #category : #'private - event management' } +GtTextualCoderSearchTextElement >> onBrSearchTextPreviousOccurenceWish: anEvent [ + | aContext | + anEvent consumed: true. + self hasTextualCoderViewModel ifFalse: [ ^ self ]. + aContext := context ifNil: [ ^ self ]. + + marker markPrevious: aContext. + self textualCoderViewModel + scrollToLine: aContext selectedLine + withCharacterPosition: aContext selectedStartPosition +] + +{ #category : #'private - event management' } +GtTextualCoderSearchTextElement >> onGtSourceCoderViewModelSearchTextCancelRequested: anAnnouncement [ + | aContext | + anAnnouncement consumed: true. + aContext := context. + aContext ifNil: [ ^ self ]. + marker unmark: aContext +] + +{ #category : #'private - event management' } +GtTextualCoderSearchTextElement >> onGtSourceCoderViewModelSearchTextRequested: anAnnouncement [ + anAnnouncement consumed ifTrue: [ ^ self ]. + anAnnouncement consumed: true. + + self editorDo: [ :anEditor | anEditor requestFocus ] +] + +{ #category : #'api - textual coder view model' } +GtTextualCoderSearchTextElement >> onTextualCoderViewModelChanged [ + "Is sent when a new textualCoder view model is assigned to the element. + Note: #onTextualCoderViewModelChanged is sent before #subscribeToTextualCoderViewModel + which means that if you perform any operation that triggers an announcement it will be ignored because the receiver + didn't get a chance to subscribe to any announcement. Override #onPostTextualCoderViewModelChanged if you + wish to perform an operation that requires an announcement, but keep in mind that this is not the most efficient way" + + +] + +{ #category : #'api - textual coder view model' } +GtTextualCoderSearchTextElement >> subscribeToTextualCoderViewModel [ + "Is sent after a new textualCoder view model is assigned to the element. + It is required to unsubscribe from the view model or domain model by implementing + #unsubscribeFromTextualCoderViewModel if elements subscribe to them" + + self textualCoderViewModel weak + when: GtSourceCoderViewModelSearchTextRequested + send: #onGtSourceCoderViewModelSearchTextRequested: + to: self; + when: GtSourceCoderViewModelSearchTextCancelRequested + send: #onGtSourceCoderViewModelSearchTextCancelRequested: + to: self +] + +{ #category : #'api - textual coder view model' } +GtTextualCoderSearchTextElement >> unsubscribeFromTextualCoderViewModel [ + "Is sent before a new textualCoder view model is assigned to the element. + Elements that subscribe to textualCoder view model in domain model are required to implement this methods." + + self textualCoderViewModel unsubscribe: self +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderSearchTextSettings.class.st b/src/GToolkit-Coder-UI/GtTextualCoderSearchTextSettings.class.st new file mode 100644 index 000000000..8842b4b03 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderSearchTextSettings.class.st @@ -0,0 +1,23 @@ +Class { + #name : #GtTextualCoderSearchTextSettings, + #superclass : #Object, + #classVars : [ + 'IsEnabledInCoder' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model' +} + +{ #category : #settings } +GtTextualCoderSearchTextSettings class >> disableInCoder [ + IsEnabledInCoder := false +] + +{ #category : #settings } +GtTextualCoderSearchTextSettings class >> enableInCoder [ + IsEnabledInCoder := true +] + +{ #category : #settings } +GtTextualCoderSearchTextSettings class >> isEnabledInCoder [ + ^ IsEnabledInCoder ifNil: [ true ] +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderSearchTextVisualizer.class.st b/src/GToolkit-Coder-UI/GtTextualCoderSearchTextVisualizer.class.st new file mode 100644 index 000000000..9ab8ed9a9 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderSearchTextVisualizer.class.st @@ -0,0 +1,62 @@ +Class { + #name : #GtTextualCoderSearchTextVisualizer, + #superclass : #BrEditorSearchTextVisualizer, + #instVars : [ + 'textualCoderViewModel', + 'actions' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model' +} + +{ #category : #accessing } +GtTextualCoderSearchTextVisualizer >> action: aBlock [ + "Add action this is executed on every finished styler. + The action is NOT executed in a UI process." + + actions := self actions copyWith: aBlock +] + +{ #category : #accessing } +GtTextualCoderSearchTextVisualizer >> actions [ + ^ actions ifNil: [ actions := Array empty ] +] + +{ #category : #accessing } +GtTextualCoderSearchTextVisualizer >> actions: anObject [ + actions := anObject +] + +{ #category : #'api - displaying' } +GtTextualCoderSearchTextVisualizer >> executeActionInContext: aBrEditorSearchTextVisualizerContext [ + self textualCoderViewModel ifNil: [ ^ self ]. + + self actions + do: [ :eachBlock | eachBlock cull: aBrEditorSearchTextVisualizerContext cull: self textualCoderViewModel ] +] + +{ #category : #'api - displaying' } +GtTextualCoderSearchTextVisualizer >> scrollContext: aBrEditorSearchTextVisualizerContext [ + self textualCoderViewModel ifNil: [ ^ self ]. + + self textualCoderViewModel + scrollToLine: aBrEditorSearchTextVisualizerContext selectedLine + withCharacterPosition: aBrEditorSearchTextVisualizerContext selectedStartPosition +] + +{ #category : #'api - displaying' } +GtTextualCoderSearchTextVisualizer >> selectionContext: aBrEditorSearchTextVisualizerContext [ + self textualCoderViewModel ifNil: [ ^ self ]. + + self scrollContext: aBrEditorSearchTextVisualizerContext. + self executeActionInContext: aBrEditorSearchTextVisualizerContext. +] + +{ #category : #accessing } +GtTextualCoderSearchTextVisualizer >> textualCoderViewModel [ + ^ textualCoderViewModel +] + +{ #category : #accessing } +GtTextualCoderSearchTextVisualizer >> textualCoderViewModel: anObject [ + textualCoderViewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderTextAttributes.class.st b/src/GToolkit-Coder-UI/GtTextualCoderTextAttributes.class.st index 50c089c50..2a1e6c236 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderTextAttributes.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderTextAttributes.class.st @@ -3,13 +3,67 @@ Class { #superclass : #Object, #instVars : [ 'markerAttribute', - 'textAttributes' + 'textAttributes', + 'textAttributeTags', + 'markerAttributeTag', + 'documentId', + 'startPosition', + 'stopPosition' ], #category : #'GToolkit-Coder-UI-Coder - Textual Model' } +{ #category : #'api - attribute' } +GtTextualCoderTextAttributes >> applyOnEditorElement: aTextEditorElement receivedText: aText from: aStartPosition to: anEndPosition [ + | attributesRange | + (aTextEditorElement isKindOf: BrEditorElement) + ifTrue: [ aText characters = aTextEditorElement text characters + ifTrue: [ self + applyOnEditorText: aTextEditorElement text + from: aStartPosition + to: anEndPosition ]. + ^ self ]. + + "We assume it is a GtTextEditorElement element. + I think the markerAttribute is simply ignored." + + self flag: #TODO. + "Investigate what to do when documentId is nil. Can it even be nil here?" + true + ifTrue: [ documentId ifNil: [ + GtCoderTextAttributesDocumentIdIsNilSignal new + coderTextAttributes: self; + editorElement: aTextEditorElement; + receivedText: aText; + startPosition: aStartPosition; + endPosition: anEndPosition; + emit. + ^ self ] ] + ifFalse: [ self assert: [ documentId isNotNil ] ]. + + "From where does (aStartPosition to: anEndPosition) come from?" + self flag: #TODO. + + attributesRange := BrTextCharacterRange + start: aStartPosition + end: anEndPosition + documentId: documentId. + + markerAttributeTag := aTextEditorElement + addAttribute: markerAttribute + overRange: attributesRange. + + textAttributeTags := textAttributes collect: [ :eachAttribute | + aTextEditorElement + addAttribute: eachAttribute + overRange: attributesRange ] +] + { #category : #'api - attribute' } GtTextualCoderTextAttributes >> applyOnEditorText: aText [ + "It is called from `GtTextualCoderViewModel>>#asEditorState` + and seems to be ok." + aText findAttribute: markerAttribute indicesDo: [ :aStartPosition :anEndPosition | @@ -18,6 +72,8 @@ GtTextualCoderTextAttributes >> applyOnEditorText: aText [ { #category : #'api - attribute' } GtTextualCoderTextAttributes >> applyOnEditorText: aText from: aStartPosition to: anEndPosition [ + "Can be used only on the BrEditorElement kind of editors. Not on GtTextEditorElement." + aText findAttribute: markerAttribute indicesDo: [ :aMarkerStartPosition :aMarkerEndPosition | @@ -26,6 +82,49 @@ GtTextualCoderTextAttributes >> applyOnEditorText: aText from: aStartPosition to aText attributes: { markerAttribute }, textAttributes from: aStartPosition to: anEndPosition ] +{ #category : #accessing } +GtTextualCoderTextAttributes >> documentId [ + ^ documentId +] + +{ #category : #accessing } +GtTextualCoderTextAttributes >> documentId: anObject [ + documentId := anObject +] + +{ #category : #'gt - extensions' } +GtTextualCoderTextAttributes >> gtAttributes [ + textAttributes ifNil: [ ^ #() ]. + ^ Array + new: textAttributes size + 1 + streamContents: [ :aStream | + aStream nextPut: markerAttributeTag -> markerAttribute. + textAttributes + withIndexDo: [ :anAttribute :anIndex | + | aTag | + aTag := textAttributeTags at: anIndex ifAbsent: [ nil ]. + aStream nextPut: aTag -> anAttribute ] ] +] + +{ #category : #'gt - extensions' } +GtTextualCoderTextAttributes >> gtAttributesFor: aView [ + + ^ aView columnedList + title: 'Attributes'; + items: [ self gtAttributes ]; + column: 'Tag' text: #key width: 60; + column: 'Attribute' text: #value; + send: #value +] + +{ #category : #'as yet unclassified' } +GtTextualCoderTextAttributes >> initialize [ + super initialize. + + markerAttributeTag := 0. + textAttributeTags := #() +] + { #category : #accessing } GtTextualCoderTextAttributes >> markerAttribute [ ^ markerAttribute @@ -36,17 +135,70 @@ GtTextualCoderTextAttributes >> markerAttribute: anObject [ markerAttribute := anObject ] +{ #category : #printing } +GtTextualCoderTextAttributes >> printOn: aStream [ + super printOn: aStream. + aStream + nextPut: $(; + print: documentId; + nextPut: $) +] + +{ #category : #'api - attribute' } +GtTextualCoderTextAttributes >> removeFromEditorElement: aTextEditorElement [ + (aTextEditorElement isKindOf: BrEditorElement) + ifTrue: [ self removeFromEditorText: aTextEditorElement text. + ^ self ]. + + "We assume it is a GtTextEditorElement element." + self flag: #TODO. + + textAttributeTags do: [ :eachTagToRemove | + aTextEditorElement removeAttributeTagged: eachTagToRemove overMarker: markerAttribute ]. + + aTextEditorElement removeAttributeTagged: markerAttributeTag +] + { #category : #'api - attribute' } GtTextualCoderTextAttributes >> removeFromEditorText: aText [ + "Can be used only on the BrEditorElement kind of editors. Not on GtTextEditorElement." + aText removeAttributes: { self markerAttribute }, self textAttributes ] +{ #category : #accessing } +GtTextualCoderTextAttributes >> startPosition [ + ^ startPosition +] + +{ #category : #accessing } +GtTextualCoderTextAttributes >> startPosition: anObject [ + startPosition := anObject +] + +{ #category : #accessing } +GtTextualCoderTextAttributes >> stopPosition [ + ^ stopPosition +] + +{ #category : #accessing } +GtTextualCoderTextAttributes >> stopPosition: anObject [ + stopPosition := anObject +] + { #category : #accessing } GtTextualCoderTextAttributes >> textAttributes [ ^ textAttributes ] { #category : #accessing } -GtTextualCoderTextAttributes >> textAttributes: anObject [ - textAttributes := anObject +GtTextualCoderTextAttributes >> textAttributes: aCollection [ + textAttributes := aCollection. + + aCollection do: [ :eachAttribute | + (eachAttribute isKindOf: BlTextHighlightAttribute) ifTrue: [ + GtCoderDeprecatedTextAttributeAddedSignal new + coderTextAttributes: self; + deprecatedAttribute: eachAttribute; + emit ] ] ] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModel.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModel.class.st index 157102688..a17fa511a 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderViewModel.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModel.class.st @@ -1,81 +1,51 @@ Class { #name : #GtTextualCoderViewModel, - #superclass : #GtSingleCoderViewModel, + #superclass : #GtExpandableCoderViewModel, #instVars : [ - 'addOns', - 'addOnMonitor', - 'addOnCommand', - 'addOnCommandBlock', - 'extraAddOns', 'stylers', 'mainActions', 'contextActions', 'contextMenuActions', 'shortcuts', - 'hasFocus', - 'expanded', 'cursors', 'selection', 'extraTextAttributes', - 'styledText' + 'styledText', + 'isTextModifiedPromise', + 'completionStrategy', + 'debuggerInSpace', + 'nodeAttributeMap', + 'history', + 'documentId' ], #category : #'GToolkit-Coder-UI-Coder - Textual Model' } -{ #category : #'api - add-ons' } -GtTextualCoderViewModel >> addOnsAsyncDo: aBlock [ - addOnMonitor critical: [ - addOnCommand ifNotNil: [ - | aUniqueSubscriber | - aUniqueSubscriber := Object new. - "the add-ons are being computed, subscribe to the ast changed announcement to wait until is finishes" - ^ self - when: GtTextualCoderViewModelAddOnsChanged - doOnce: [ :anAnnouncement | aBlock value: anAnnouncement addOns ] - for: aUniqueSubscriber ]. - - "the add-ons are already computed, return it directly" - addOns ifNotNil: [ :theAddOns | ^ aBlock value: theAddOns ]. - - addOnCommandBlock := [ - | theComputedAddOns | - - theComputedAddOns := self computeAddOns. - addOnMonitor critical: [ - addOns := theComputedAddOns. - addOnCommand := nil. - addOnCommandBlock := nil ]. - self onAddOnsChanged: theComputedAddOns. - aBlock value: theComputedAddOns. - self announce: (GtTextualCoderViewModelAddOnsChanged new addOns: theComputedAddOns) ]. - addOnCommand := addOnCommandBlock asBlTktTerminableCommand asBlTktLoggingCommand. - - BlUseAsyncFeatures - ifEnabledDo: [ BlTktWorkerProvider coderAddOnsPool schedule: addOnCommand ] - otherwise: [ addOnCommand execute ] ] +{ #category : #'api - text' } +GtTextualCoderViewModel >> addHighlight: aBrTextHighlight overRange: aBrTextRange [ + self + announce: (GtTextualCoderViewModelAddHighlightRequest new + textualCoderViewModel: self; + textHighlight: aBrTextHighlight; + textRange: aBrTextRange) ] { #category : #'api - add-ons' } -GtTextualCoderViewModel >> addOnsAwait [ - | theAddOns | - - theAddOns := nil. - self addOnsAwaitDo: [ :theComputedAddOns | theAddOns := theComputedAddOns ]. - ^ theAddOns +GtTextualCoderViewModel >> addLocateDebuggerInSpaceAction: aSpace [ + debuggerInSpace := aSpace asWeakReference. + + aSpace + addEventHandlerOn: BlSpaceClosedEvent + do: [ :anEvent | self onDebuggerSpaceCloseEvent: anEvent ]. + + self requestUpdateAddOns ] { #category : #'api - add-ons' } -GtTextualCoderViewModel >> addOnsAwaitDo: aBlock [ - | aSemaphore theAddOns | - - aSemaphore := Semaphore new. - theAddOns := nil. - self addOnsAsyncDo: [ :theComputedAddOns | - theAddOns := theComputedAddOns. - aSemaphore signal ]. - - aSemaphore wait: 5 seconds. - aBlock value: theAddOns +GtTextualCoderViewModel >> addOnsFuture [ + "Return a future that will be resolved to coder addons" + + ^ self computeAddOnsFuture ] { #category : #'api - shortcuts' } @@ -101,49 +71,146 @@ GtTextualCoderViewModel >> addStylers: aCollectionOfGtCoderStyler [ { #category : #'api - text' } GtTextualCoderViewModel >> addTextAttribute: aTextAttribute from: aStartPosition to: anEndPosition [ - + + self deprecated: 'Use addTextAttribute:from:to:documentId: instead.'. + + ^ self + addTextAttribute: aTextAttribute + from: aStartPosition + to: anEndPosition + documentId: documentId +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> addTextAttribute: aTextAttribute from: aStartPosition to: anEndPosition documentId: aDocumentId [ + + ^ self + addTextAttributes: {aTextAttribute} + from: aStartPosition + to: anEndPosition + documentId: aDocumentId +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> addTextAttribute: aTextAttribute onText: aText from: aStartPosition to: anEndPosition [ + + self deprecated: 'Use addTextAttribute:onText:from:to:documentId: instead'. + + ^ self + addTextAttribute: aTextAttribute + onText: aText + from: aStartPosition + to: anEndPosition + documentId: documentId +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> addTextAttribute: aTextAttribute onText: aText from: aStartPosition to: anEndPosition documentId: aDocumentId [ + ^ self - addTextAttributes: { aTextAttribute } + addTextAttributes: {aTextAttribute} + onText: aText from: aStartPosition to: anEndPosition + documentId: aDocumentId ] { #category : #'api - text' } GtTextualCoderViewModel >> addTextAttributes: aCollectionOfTextAttributes from: aStartPosition to: anEndPosition [ - - | aCurrentText aMarkerAttribute newCoderTextAttributes | + + + self deprecated: 'Use addTextAttributes:from:to:documentId: instead'. + + ^ self + addTextAttributes: aCollectionOfTextAttributes + from: aStartPosition + to: anEndPosition + documentId: documentId +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> addTextAttributes: aCollectionOfTextAttributes from: aStartPosition to: anEndPosition documentId: aDocumentId [ + + ^ self + addTextAttributes: aCollectionOfTextAttributes + onText: self sourceText + from: aStartPosition + to: anEndPosition + documentId: aDocumentId +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> addTextAttributes: aCollectionOfTextAttributes onText: aText from: aStartPosition to: anEndPosition documentId: aDocumentId [ + + | aMarkerAttribute newCoderTextAttributes | + + "If documentId is different, do nothing." + self flag: #TODO. + "documentId ifNotNil: [" + aDocumentId + ifNil: [ + GtCoderDocumentIdIsNilSignal new + originalDocumentId: documentId; + emit ] + ifNotNil: [ + aDocumentId = documentId ifFalse: [ + GtCoderDocumentIdDifferentSignal new + originalDocumentId: documentId; + receivedDocumentId: aDocumentId; + emit ] ] "]". aMarkerAttribute := BrTextInvisibleMarkerAttribute new beNotOverwritableByStyler. "we change the sourceText directly to not trigger the styler recomputation" - aCurrentText := self sourceText. - aCurrentText attribute: aMarkerAttribute from: aStartPosition to: anEndPosition. + aText attribute: aMarkerAttribute from: aStartPosition to: anEndPosition. + "Pass aDocumentId, once it circulates properly." + self flag: #TODO. newCoderTextAttributes := GtTextualCoderTextAttributes new markerAttribute: aMarkerAttribute; - textAttributes: aCollectionOfTextAttributes. + textAttributes: aCollectionOfTextAttributes; + documentId: (aDocumentId ifNil: [ documentId ]); + startPosition: aStartPosition; + stopPosition: anEndPosition. extraTextAttributes add: newCoderTextAttributes. self announce: (GtTextualCoderViewModelTextAttributesAdded new + textualCoderViewModel: self; coderTextAttributes: newCoderTextAttributes; + text: aText; startPosition: aStartPosition; stopPosition: anEndPosition). ^ newCoderTextAttributes ] +{ #category : #'api - styling' } +GtTextualCoderViewModel >> allSavedAttributesForStyler: aStyler [ + ^ (self nodeAttributeMapForStyler: aStyler) values +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> applyTextEditCommand: aTextEditCommand [ + self announce: (GtTextualCoderViewModelTextEditCommandRequestAnnouncement new + textualCoderViewModel: self; + textEditCommand: aTextEditCommand"; + updateStrategy: GtCoderUpdateStrategy new makeAsynchronous") +] + { #category : #converting } GtTextualCoderViewModel >> asEditorState [ + | anEditorText aMemento | - - anEditorText := self coderModel sourceText copy. + + anEditorText := self coderModel currentSourceText copy. self extraTextAttributes do: [ :eachCoderAttributes | eachCoderAttributes applyOnEditorText: anEditorText ]. aMemento := BrTextEditorCompositeMemento new addMemento: (BrTextEditorCursorsMemento new cursors: cursors copy); - addMemento: (BrTextEditorSelectionMemento new selection: selection copy). + addMemento: (BrTextEditorSelectionMemento new selection: selection copy); + addMemento: (BrTextEditorHistoryMemento new history: (history ifNil: [BrTextEditorHistory new]) copy). self hasStyledText ifTrue: [ @@ -165,12 +232,35 @@ GtTextualCoderViewModel >> astAsyncDo: aBlock [ { #category : #'api - coder model' } GtTextualCoderViewModel >> astAwait [ - ^ self coderModel astAwait + self + deprecated: 'Please use #astSync instead.' + transformWith: '`@receiver astAwait' -> '`@receiver astSync'. + + ^ self astSync +] + +{ #category : #'api - coder model' } +GtTextualCoderViewModel >> astSync [ + ^ self coderModel astSync +] + +{ #category : #'api - coder model' } +GtTextualCoderViewModel >> coderAstFuture [ + + ^ self coderModel coderAstFuture ] -{ #category : #'api - expansion' } -GtTextualCoderViewModel >> collapse [ - self expanded: false +{ #category : #accessing } +GtTextualCoderViewModel >> completionStrategy [ + + + ^ completionStrategy + ifNil: [ completionStrategy := self newCompletionStrategy ] +] + +{ #category : #accessing } +GtTextualCoderViewModel >> completionStrategy: aGtCompletionStrategy [ + completionStrategy := aGtCompletionStrategy ] { #category : #'api - stylers' } @@ -181,60 +271,90 @@ GtTextualCoderViewModel >> compositeStyler [ yourself ] -{ #category : #'private - addons' } -GtTextualCoderViewModel >> computeAddOns [ - | aCoderModel newAddOns pragmas theAst | - - aCoderModel := self coderModel. - - theAst := aCoderModel astAwait. - pragmas := aCoderModel - pragmasNamed: #gtAstCoderAddOns: - inHierarchy: aCoderModel class. +{ #category : #'api - add-ons' } +GtTextualCoderViewModel >> computeAddOnsFuture [ + "Return a future that will be resolved to coder addons" + + | aCoderModel | - newAddOns := aCoderModel newAddOns. - "extra addons" - newAddOns addAddOns: aCoderModel addOns. - aCoderModel initializeShortcuts: newAddOns. - aCoderModel initializeAddOns: newAddOns. - theAst ifNotNil: [ - pragmas reverseDo: [ :eachPragma | - [ aCoderModel - perform: eachPragma methodSelector - withEnoughArguments: { theAst . newAddOns . self } ] - on: Error - do: [ :anError | - "emit as a beacon signal" - anError emit. + aCoderModel := self coderModel. + ^ aCoderModel coderAstFuture map: [ :aCoderAst | + | theAst newAddOns pragmas | + + GtCoderAddOnsComputationStartedSignal new + coderViewModel: self; + emit. + + theAst := aCoderAst ast. + + pragmas := aCoderModel + pragmasNamed: GtCoderModel astExtensionsPragma + inHierarchy: aCoderModel class. + + newAddOns := aCoderModel newAddOns. + newAddOns ast: aCoderAst. + "extra addons" + newAddOns addAddOns: aCoderModel addOns. + aCoderModel initializeShortcuts: newAddOns. + aCoderModel initializeAddOns: newAddOns viewModel: self. + theAst ifNotNil: [ + pragmas reverseDo: [ :eachPragma | + [ - NonInteractiveTranscript stderr - nextPut: $[; - print: eachPragma method printString; - nextPut: $]; - space; - print: anError; - cr ] ] ]. - - "view model add-ons override coder model add-ons" - newAddOns addAddOns: extraAddOns. - newAddOns markAsUpdated. - ^ newAddOns + BlFrameTelemetry telemetryComputationStartSignal: eachPragma. + + aCoderModel + perform: eachPragma methodSelector + withEnoughArguments: { theAst . newAddOns . self }. + + BlFrameTelemetry telemetryComputationEndSignal: eachPragma. + + GtCoderAddOnCreatedSignal new + coderViewModel: self; + addOnClass: eachPragma methodClass; + addOnSelector: eachPragma methodSelector; + emit. ] + on: Error + do: [ :anError | + BlFrameTelemetry telemetryComputationEndSignal: eachPragma. + + "emit as a beacon signal" + anError emit. + + NonInteractiveTranscript stderr + nextPut: $[; + print: eachPragma method printString; + nextPut: $]; + space; + print: anError; + cr ] ] ]. + + "view model add-ons override coder model add-ons" + newAddOns addAddOns: extraAddOns. + newAddOns markAsUpdated. + + GtCoderAddOnsComputationFinishedSignal new + coderViewModel: self; + emit. + + newAddOns ] ] { #category : #'private - addons' } GtTextualCoderViewModel >> computeContextMenuAstAddOns [ + | aCoderModel newAddOns pragmas theAst | aCoderModel := self coderModel. - theAst := aCoderModel astAwait. + theAst := aCoderModel astSync. pragmas := aCoderModel - pragmasNamed: #gtCoderContextMenuAddOns: + pragmasNamed: aCoderModel class contextMenuAddOnsPragma inHierarchy: aCoderModel class. newAddOns := aCoderModel newAddOns. theAst ifNotNil: [ - pragmas reverseDo: [ :eachPragma | + pragmas do: [ :eachPragma | [ aCoderModel perform: eachPragma methodSelector withEnoughArguments: { theAst . newAddOns . self } ] @@ -270,6 +390,7 @@ GtTextualCoderViewModel >> contextActions: theContextActions [ { #category : #'api - add-ons' } GtTextualCoderViewModel >> contextMenuActions [ + ^ contextMenuActions ] @@ -304,32 +425,28 @@ GtTextualCoderViewModel >> cursors: aBrTextEditorCursor from: aSourceObject [ ] { #category : #accessing } -GtTextualCoderViewModel >> elementClass [ - ^ GtTextualCoderEditorElement -] - -{ #category : #'api - add-ons' } -GtTextualCoderViewModel >> ensureAddOns [ - self addOnsAsyncDo: [ :theAddOns | ] -] - -{ #category : #'api - expansion' } -GtTextualCoderViewModel >> expand [ - self expanded: true +GtTextualCoderViewModel >> documentId [ + ^ documentId ] -{ #category : #'api - expansion' } -GtTextualCoderViewModel >> expanded [ - ^ expanded +{ #category : #accessing } +GtTextualCoderViewModel >> documentId: anObject [ + | oldDocumentId | + documentId = anObject ifTrue: [ ^ self ]. + + oldDocumentId := documentId. + documentId := anObject. + + GtCoderDocumentIdUpdatedSignal new + texturalCoderViewModel: self; + oldDocumentId: oldDocumentId; + newDocumentId: anObject; + emit ] -{ #category : #'api - expansion' } -GtTextualCoderViewModel >> expanded: aBoolean [ - expanded = aBoolean - ifTrue: [ ^ self ]. - - expanded := aBoolean. - self notifyExpansionChanged: expanded +{ #category : #accessing } +GtTextualCoderViewModel >> elementClass [ + ^ GtTextualCoderElement ] { #category : #accessing } @@ -337,47 +454,23 @@ GtTextualCoderViewModel >> extraTextAttributes [ ^ extraTextAttributes ] -{ #category : #'api - focus' } -GtTextualCoderViewModel >> focused [ - - - ^ hasFocus -] - -{ #category : #'api - focus' } -GtTextualCoderViewModel >> focused: aBoolean [ - self focused: aBoolean from: self -] - -{ #category : #'api - focus' } -GtTextualCoderViewModel >> focused: aBoolean from: aSourceObject [ - hasFocus = aBoolean - ifTrue: [ ^ self ]. - - hasFocus := aBoolean. - self notifyFocusChanged: hasFocus from: aSourceObject. - - codersUIModel ifNotNil: [ :theCoders | - aBoolean - ifTrue: [ theCoders focusCoderUIModel: self ] - ifFalse: [ theCoders unfocusCoderUIModel: self ] ] -] - { #category : #'gt-extension' } GtTextualCoderViewModel >> gtInfo [ ^ Array streamContents: [ :aStream | aStream nextPut: (#coderModel -> self coderModel). aStream nextPut: (#cursors -> self cursors). aStream nextPut: (#selection -> self selection). - aStream nextPut: (#ast -> self astAwait). + aStream nextPut: (#addOns -> self addOns wait). + aStream nextPut: (#ast -> self astSync). aStream nextPut: (#stylers -> self stylers). aStream nextPut: (#mainActions -> self mainActions). aStream nextPut: (#contextActions -> self contextActions). aStream nextPut: (#contextMenuActions -> self contextMenuActions). aStream nextPut: (#shortcuts -> self shortcuts). - aStream nextPut: (#attributes -> self extraTextAttributes). + aStream nextPut: (#extraTextAttributes -> self extraTextAttributes). aStream nextPut: (#hasFocus -> self focused). - aStream nextPut: (#expanded -> self expanded) ] + aStream nextPut: (#expanded -> self expanded). + aStream nextPut: (#wantsHeader -> self wantsHeader) ] ] { #category : #'gt-extension' } @@ -390,7 +483,7 @@ GtTextualCoderViewModel >> gtInfoFor: aView [ items: [ self gtInfo ]; actionUpdateButtonTooltip: 'Refresh'; column: 'Property' - item: [ :eachItem :eachIndex | eachItem key asRopedText foreground: Color gray ] + text: [ :eachItem :eachIndex | eachItem key asRopedText foreground: Color gray ] width: 150; column: 'Value' text: [ :eachItem | eachItem value gtDisplayText ]; @@ -412,24 +505,54 @@ GtTextualCoderViewModel >> hasStyledText [ ^ styledText notNil ] +{ #category : #accessing } +GtTextualCoderViewModel >> history [ + ^ history +] + +{ #category : #accessing } +GtTextualCoderViewModel >> history: anObject [ + history := anObject +] + { #category : #initialization } GtTextualCoderViewModel >> initialize [ super initialize. - + extraAddOns := GtTextualCoderAddOns new. - addOnMonitor := Monitor new. stylers := #(). shortcuts := #(). mainActions := #(). contextActions := #(). contextMenuActions := #(). - + cursors := BrTextEditorMultipleCursor new. selection := BlCompositeSelection new. extraTextAttributes := OrderedCollection new. - - hasFocus := false. - expanded := true + nodeAttributeMap := Dictionary new. + history := BrTextEditorHistory new +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> isTextModifiedPromise [ + "Return a promise which is resolved to true if the text is modified, + false otherwise. Should be used together with {{gtClass:GtTextualCoderViewModelTextChanged}} to update + the modification status in the UI" + + + ^ isTextModifiedPromise + ifNil: [ isTextModifiedPromise := self coderModel isModifiedFuture + await: self class commonExecutionConfiguration ] +] + +{ #category : #'api - add-ons' } +GtTextualCoderViewModel >> locateDebuggerInSpaceAction [ + | aSpace | + debuggerInSpace ifNil: [ ^ nil ]. + aSpace := debuggerInSpace at: 1. + aSpace ifNil: [ ^ nil ]. + + ^ GtCoderLocateDebuggerAction new weakSpace: debuggerInSpace ] { #category : #'api - add-ons' } @@ -465,20 +588,21 @@ GtTextualCoderViewModel >> moveCursorTo: aCursorIndex [ self cursors: (BrTextEditorCursor at: aCursorIndex) ] -{ #category : #'private - notifying' } -GtTextualCoderViewModel >> notifyCursorsChanged: aBrTextEditorCursor from: aSourceObject [ - self announce: (GtTextualCoderViewModelCursorsChanged new cursors: aBrTextEditorCursor; source: aSourceObject) +{ #category : #accessing } +GtTextualCoderViewModel >> newCompletionStrategy [ + ^ GtCompletionStrategy new ] -{ #category : #'private - notifying' } -GtTextualCoderViewModel >> notifyExpansionChanged: aBoolean [ - self announce: (GtTextualCoderViewModelExpansionChanged new expanded: aBoolean) +{ #category : #accessing } +GtTextualCoderViewModel >> nodeAttributeMapForStyler: aStyler [ + ^ nodeAttributeMap at: aStyler class ifAbsentPut: [Dictionary new] ] { #category : #'private - notifying' } -GtTextualCoderViewModel >> notifyFocusChanged: aBoolean from: aSourceObject [ - self announcerUIModel announce: (GtSourceCoderFocusChanged new - focused: aBoolean; +GtTextualCoderViewModel >> notifyCursorsChanged: aBrTextEditorCursor from: aSourceObject [ + self announce: (GtTextualCoderViewModelCursorsChanged new + textualCoderViewModel: self; + cursors: aBrTextEditorCursor; source: aSourceObject) ] @@ -496,28 +620,39 @@ GtTextualCoderViewModel >> notifyStyledTextChanged [ styledText: self styledText). ] -{ #category : #'private - addons' } +{ #category : #'private - event handling' } GtTextualCoderViewModel >> onAddOnsChanged: theAddOns [ - "Is sent when new add-ons are computed" + "Is sent by Coder Element from a UI thread when new add-ons are computed" + | someContextActions | + super onAddOnsChanged: theAddOns. - self stylers: theAddOns stylers. self shortcuts: theAddOns shortcuts. self mainActions: theAddOns mainActions. - self contextActions: theAddOns contextActions. - self contextMenuActions: theAddOns contextMenuActions -] -{ #category : #'private - event handling' } -GtTextualCoderViewModel >> onAstChanged: anAstChangedAnnouncement [ + someContextActions := theAddOns contextActions. + self locateDebuggerInSpaceAction ifNotNil: [ :anAction | + someContextActions := someContextActions copyWith: anAction ]. + self contextActions: someContextActions. - "ast changed, the styled text may not be valid anymore" - self unsetStyledText. + self contextMenuActions: theAddOns contextMenuActions. + self stylers: theAddOns stylers +] - self announce: (GtTextualCoderViewModelAstChanged new ast: anAstChangedAnnouncement ast). +{ #category : #'api - coder model' } +GtTextualCoderViewModel >> onCoderModelChanged [ + super onCoderModelChanged +] - addOnMonitor critical: [ addOns := nil ]. +{ #category : #'api - add-ons' } +GtTextualCoderViewModel >> onDebuggerSpaceCloseEvent: anEvent [ + | aCurrentSpace | + debuggerInSpace ifNil: [ ^ self ]. + aCurrentSpace := debuggerInSpace at: 1. + aCurrentSpace ifNil: [ ^ self ]. - self addOnsAsyncDo: [ :theAddOns | self announce: GtTextualCoderViewModelReadyToRestyle new ] + aCurrentSpace = anEvent space ifFalse: [ ^ self ]. + debuggerInSpace := nil. + self requestUpdateAddOns ] { #category : #'api - styled text' } @@ -536,18 +671,38 @@ GtTextualCoderViewModel >> onPreviousStyledTextUnset: aPreviousStyledText [ { #category : #'private - event handling' } GtTextualCoderViewModel >> onSourceCodeChanged: anSourceCodeChangedAnnouncement [ - "source code changed, meaning that the styled text is no longer correct" self unsetStyledText. + + addOnPromise := nil. + isTextModifiedPromise := nil. + + GtTextualCoderViewModelTextChangedSignal new + documentId: documentId; + text: self coderModel currentSourceText; + textualCoderViewModel: self; + emit. self announce: (GtTextualCoderViewModelTextChanged new - text: self coderModel sourceText; - updateStrategy: anSourceCodeChangedAnnouncement updateStrategy). + textualCoderViewModel: self; + text: self coderModel currentSourceText; + updateStrategy: anSourceCodeChangedAnnouncement updateStrategy) +] + +{ #category : #'private - event handling' } +GtTextualCoderViewModel >> onSourceCodeReplaced: aSourceCodeReplacedAnnouncement [ + "source code replaced to a new one. It most likely means that the origin of + the source text has changed." +] - self coderModel terminateAstCommand. - self terminateAddOnsCommand. +{ #category : #printing } +GtTextualCoderViewModel >> printOn: aStream [ + super printOn: aStream. - self coderModel ensureAst + aStream + nextPut: $(; + print: documentId; + nextPut: $) ] { #category : #'api - text' } @@ -556,6 +711,13 @@ GtTextualCoderViewModel >> removeAllCoderTextAttributes: aCollectionOfGtTextualC self removeCoderTextAttributes: eachCoderTextAttribute ] ] +{ #category : #'api - refactorings' } +GtTextualCoderViewModel >> removeAttribute: anAttribute [ + | attributes | + attributes := self extraTextAttributes select: [ :each | each textAttributes includes: anAttribute ]. + self removeAllCoderTextAttributes: attributes +] + { #category : #'api - text' } GtTextualCoderViewModel >> removeCoderTextAttributes: aGtTextualCoderTextAttributes [ | aCurrentText | @@ -568,7 +730,55 @@ GtTextualCoderViewModel >> removeCoderTextAttributes: aGtTextualCoderTextAttribu aCurrentText := self sourceText. aCurrentText removeAttributes: { aGtTextualCoderTextAttributes markerAttribute }, aGtTextualCoderTextAttributes textAttributes. - self announce: (GtTextualCoderViewModelTextAttributesRemoved new coderTextAttributes: aGtTextualCoderTextAttributes) + self announce: (GtTextualCoderViewModelTextAttributesRemoved new + textualCoderViewModel: self; + coderTextAttributes: aGtTextualCoderTextAttributes) +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> removeHighlightsWithId: aStringId [ + self + announce: (GtTextualCoderViewModelRemoveHighlightsRequest new + textualCoderViewModel: self; + id: aStringId) +] + +{ #category : #'api - shortcuts' } +GtTextualCoderViewModel >> removeStylersOfClass: aGtCoderStylerClass [ + extraAddOns removeStylersOfClass: aGtCoderStylerClass +] + +{ #category : #'api - actions' } +GtTextualCoderViewModel >> requestSearch [ + "Request the view model to start the saving process. We first send a corresponding announcement to + allow UI to intercept the saving request and show, for example, a confirmation dialog. If an announcement + was not consumed (= handled) proceed with the default save action" + | anAnnouncement | + + anAnnouncement := GtSourceCoderViewModelSearchTextRequested new textualCoderViewModel: self. + self announce: anAnnouncement. +] + +{ #category : #'api - attribute state' } +GtTextualCoderViewModel >> resetNodeAttributeMapForStyler: aStyler [ + nodeAttributeMap at: aStyler class put: Dictionary new +] + +{ #category : #'api - attribute state' } +GtTextualCoderViewModel >> saveAttribute: folder forNode: aNode andStyler: aStyler [ + (self nodeAttributeMapForStyler: aStyler) at: aNode put: folder +] + +{ #category : #'api - scrolling' } +GtTextualCoderViewModel >> scrollToLine: aLineIndex withCharacterPosition: aCharacterPosition [ + aLineIndex ifNil: [ ^ self ]. + aCharacterPosition ifNil: [ ^ self ]. + + self + announce: (GtTextualCoderViewModelScrollToCharacterPositionAnnouncement new + textualCoderViewModel: self; + lineIndex: aLineIndex; + characterPosition: aCharacterPosition) ] { #category : #'api - selection' } @@ -626,28 +836,37 @@ GtTextualCoderViewModel >> shortcuts: aCollectionOfShortcuts [ GtTextualCoderViewModel >> shouldAddOnsBeUpdated [ "Return true if addons should be updated, false otherwise. When a Coder Model is attached to the corresponding Element that element should check if addons need to be updated and if it is the case start the update with the help of ${method:GtCoderModel>>#updateAddOnsFrom:}$" - + - ^ addOnMonitor critical: [ - addOns - ifNil: [ true ] - ifNotNil: [ :theAddOns | theAddOns shouldBeUpdated ] ] + ^ addOnPromise + ifNil: [ true asAsyncPromise ] + ifNotNil: [ :theAddOnsPromise | theAddOnsPromise then: [ :theAddOns | theAddOns shouldBeUpdated ] ] ] { #category : #'api - text' } GtTextualCoderViewModel >> sourceText [ - ^ self coderModel sourceText + ^ self coderModel currentSourceText +] + +{ #category : #'api - text' } +GtTextualCoderViewModel >> sourceText: aNewSourceText from: aSourceObject synchronously: isSynchronous [ + self + sourceText: aNewSourceText + from: aSourceObject + synchronously: isSynchronous + dueToEvent: nil ] { #category : #'api - text' } -GtTextualCoderViewModel >> sourceText: aNewSourceText from: aSourceObject synchronously: isSynchronous [ +GtTextualCoderViewModel >> sourceText: aNewSourceText from: aSourceObject synchronously: isSynchronous dueToEvent: aTextModifiedEvent [ self coderModel currentSourceText: aNewSourceText with: (GtCoderUpdateStrategy new source: aSourceObject; isSynchronous: isSynchronous) + dueTo: aTextModifiedEvent ] { #category : #'api - styled text' } @@ -697,7 +916,8 @@ GtTextualCoderViewModel >> stylers: theStylers [ "when stylers change we should reset the styled text, since there is no guarantee it will be a valid one" self unsetStyledText. - self announce: GtTextualCoderViewModelStylersChanged new + self announce: GtTextualCoderViewModelStylersChanged new. + self announce: GtTextualCoderViewModelReadyToRestyle new ] { #category : #'api - coder model' } @@ -705,22 +925,24 @@ GtTextualCoderViewModel >> subscribeToCoderModel [ super subscribeToCoderModel. self coderModel weak - when: GtCoderAstChanged - send: #onAstChanged: + when: GtCoderSourceCodeReplaced + send: #onSourceCodeReplaced: to: self. - + self coderModel weak when: GtCoderSourceCodeChanged send: #onSourceCodeChanged: to: self ] -{ #category : #'api - add-ons' } -GtTextualCoderViewModel >> terminateAddOnsCommand [ - addOnMonitor critical: [ - addOnCommand ifNotNil: [ :anAddOnsCommand | anAddOnsCommand terminate ]. - addOnCommand := nil. - addOnCommandBlock := nil ] +{ #category : #'api - text' } +GtTextualCoderViewModel >> textReplaced: aNewSourceText from: aSourceObject synchronously: isSynchronous dueToEvent: aTextModifiedEvent [ + self coderModel + textReplaced: aNewSourceText + with: (GtCoderUpdateStrategy new + source: aSourceObject; + isSynchronous: isSynchronous) + dueTo: aTextModifiedEvent ] { #category : #'api - styled text' } diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelAddHighlightRequest.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelAddHighlightRequest.class.st new file mode 100644 index 000000000..f0ebf28c6 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelAddHighlightRequest.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtTextualCoderViewModelAddHighlightRequest, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'textHighlight', + 'textRange' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' +} + +{ #category : #accessing } +GtTextualCoderViewModelAddHighlightRequest >> textHighlight [ + ^ textHighlight +] + +{ #category : #accessing } +GtTextualCoderViewModelAddHighlightRequest >> textHighlight: anObject [ + textHighlight := anObject +] + +{ #category : #accessing } +GtTextualCoderViewModelAddHighlightRequest >> textRange [ + ^ textRange +] + +{ #category : #accessing } +GtTextualCoderViewModelAddHighlightRequest >> textRange: anObject [ + textRange := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelAnnouncement.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelAnnouncement.class.st index 5958dfa76..0e4072cf0 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderViewModelAnnouncement.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelAnnouncement.class.st @@ -1,5 +1,18 @@ Class { #name : #GtTextualCoderViewModelAnnouncement, #superclass : #Announcement, + #instVars : [ + 'textualCoderViewModel' + ], #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' } + +{ #category : #accessing } +GtTextualCoderViewModelAnnouncement >> textualCoderViewModel [ + ^ textualCoderViewModel +] + +{ #category : #accessing } +GtTextualCoderViewModelAnnouncement >> textualCoderViewModel: anObject [ + textualCoderViewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelBehaviorChanged.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelBehaviorChanged.class.st new file mode 100644 index 000000000..a41b10fe2 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelBehaviorChanged.class.st @@ -0,0 +1,19 @@ +Class { + #name : #GtTextualCoderViewModelBehaviorChanged, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'newBehavior' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' +} + +{ #category : #accessing } +GtTextualCoderViewModelBehaviorChanged >> newBehavior [ + + ^ newBehavior +] + +{ #category : #accessing } +GtTextualCoderViewModelBehaviorChanged >> newBehavior: aBehavior [ + newBehavior := aBehavior +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelExpansionChanged.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelExpansionChanged.class.st deleted file mode 100644 index c338626c2..000000000 --- a/src/GToolkit-Coder-UI/GtTextualCoderViewModelExpansionChanged.class.st +++ /dev/null @@ -1,18 +0,0 @@ -Class { - #name : #GtTextualCoderViewModelExpansionChanged, - #superclass : #GtTextualCoderViewModelAnnouncement, - #instVars : [ - 'expanded' - ], - #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' -} - -{ #category : #accessing } -GtTextualCoderViewModelExpansionChanged >> expanded [ - ^ expanded -] - -{ #category : #accessing } -GtTextualCoderViewModelExpansionChanged >> expanded: aBoolean [ - expanded := aBoolean -] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelFocusChanged.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelFocusChanged.class.st new file mode 100644 index 000000000..7d7ac1992 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelFocusChanged.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtTextualCoderViewModelFocusChanged, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'source', + 'focused' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' +} + +{ #category : #accessing } +GtTextualCoderViewModelFocusChanged >> focused [ + ^ focused +] + +{ #category : #accessing } +GtTextualCoderViewModelFocusChanged >> focused: anObject [ + focused := anObject +] + +{ #category : #accessing } +GtTextualCoderViewModelFocusChanged >> source [ + ^ source +] + +{ #category : #accessing } +GtTextualCoderViewModelFocusChanged >> source: anObject [ + source := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelRemoveHighlightsRequest.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelRemoveHighlightsRequest.class.st new file mode 100644 index 000000000..f1d0808ca --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelRemoveHighlightsRequest.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtTextualCoderViewModelRemoveHighlightsRequest, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'id' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' +} + +{ #category : #accessing } +GtTextualCoderViewModelRemoveHighlightsRequest >> id [ + ^ id +] + +{ #category : #accessing } +GtTextualCoderViewModelRemoveHighlightsRequest >> id: anObject [ + id := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelScrollToCharacterPositionAnnouncement.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelScrollToCharacterPositionAnnouncement.class.st new file mode 100644 index 000000000..1df4f6d72 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelScrollToCharacterPositionAnnouncement.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtTextualCoderViewModelScrollToCharacterPositionAnnouncement, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'characterPosition', + 'lineIndex' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' +} + +{ #category : #accessing } +GtTextualCoderViewModelScrollToCharacterPositionAnnouncement >> characterPosition [ + ^ characterPosition +] + +{ #category : #accessing } +GtTextualCoderViewModelScrollToCharacterPositionAnnouncement >> characterPosition: anObject [ + characterPosition := anObject +] + +{ #category : #accessing } +GtTextualCoderViewModelScrollToCharacterPositionAnnouncement >> lineIndex [ + ^ lineIndex +] + +{ #category : #accessing } +GtTextualCoderViewModelScrollToCharacterPositionAnnouncement >> lineIndex: anObject [ + lineIndex := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement.class.st new file mode 100644 index 000000000..97e646c2f --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'consumed', + 'text', + 'visibleText' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Events' +} + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement >> consumed [ + ^ consumed ifNil: [ false ] +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement >> consumed: aBoolean [ + consumed := aBoolean +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement >> text [ + ^ text +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement >> text: anObject [ + text := anObject +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement >> visibleText [ + ^ visibleText +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement >> visibleText: anObject [ + visibleText := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextSource.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextSource.class.st new file mode 100644 index 000000000..a8cfb5c09 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextSource.class.st @@ -0,0 +1,84 @@ +Class { + #name : #GtTextualCoderViewModelSearchTextSource, + #superclass : #BrEditorSearchTextSource, + #instVars : [ + 'textualCoderViewModel' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model' +} + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextSource >> editorModel [ + "Return an editor model for which the found patterns, e.g, substrings, are marked" + + + ^ 'self subclassResponsibility' +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextSource >> element [ + "Return an element to which actions, e.g., background computation can be hooked" + + + ^ nil +] + +{ #category : #testing } +GtTextualCoderViewModelSearchTextSource >> hasText [ + + ^ self text isNotNil +] + +{ #category : #testing } +GtTextualCoderViewModelSearchTextSource >> hasVisibleText [ + ^ self visibleText isNotNil +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextSource >> text [ + "Return current editor text" + + + | anAnnouncement | + anAnnouncement := GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement new + textualCoderViewModel: textualCoderViewModel. + textualCoderViewModel announce: anAnnouncement. + ^ anAnnouncement text +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextSource >> textualCoderViewModel [ + ^ textualCoderViewModel +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextSource >> textualCoderViewModel: anObject [ + textualCoderViewModel := anObject +] + +{ #category : #updating } +GtTextualCoderViewModelSearchTextSource >> updateText: aStyledText [ + "Integrate new styled text to the existing editor's text. + Return true if the integration happened, false otherwise. + Note: Must be called from a UI process." + + + | anAnnouncement | + anAnnouncement := GtTextualCoderViewModelSearchTextUpdateTextAnnouncement new + textualCoderViewModel: textualCoderViewModel; + styledText: aStyledText. + textualCoderViewModel announce: anAnnouncement. + ^ anAnnouncement isUpdated +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextSource >> visibleText [ + "Return current editor visible text" + + + | anAnnouncement | + anAnnouncement := GtTextualCoderViewModelSearchTextGetTextAndVisibleTextAnnouncement new + textualCoderViewModel: textualCoderViewModel. + textualCoderViewModel announce: anAnnouncement. + ^ anAnnouncement visibleText +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextUpdateTextAnnouncement.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextUpdateTextAnnouncement.class.st new file mode 100644 index 000000000..324ee1717 --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelSearchTextUpdateTextAnnouncement.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GtTextualCoderViewModelSearchTextUpdateTextAnnouncement, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'consumed', + 'styledText', + 'isUpdated' + ], + #category : #'GToolkit-Coder-UI-Coder - Source Events' +} + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextUpdateTextAnnouncement >> consumed [ + ^ consumed ifNil: [ false ] +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextUpdateTextAnnouncement >> consumed: aBoolean [ + consumed := aBoolean +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextUpdateTextAnnouncement >> isUpdated [ + ^ isUpdated ifNil: [ false ] +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextUpdateTextAnnouncement >> isUpdated: aBoolean [ + isUpdated := aBoolean +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextUpdateTextAnnouncement >> styledText [ + ^ styledText +] + +{ #category : #accessing } +GtTextualCoderViewModelSearchTextUpdateTextAnnouncement >> styledText: anObject [ + styledText := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelSelectionChanged.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelSelectionChanged.class.st index ba9545d3c..1a8cc7fc0 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderViewModelSelectionChanged.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelSelectionChanged.class.st @@ -3,7 +3,8 @@ Class { #superclass : #GtTextualCoderViewModelAnnouncement, #instVars : [ 'source', - 'selection' + 'selection', + 'shouldUpdateCursor' ], #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' } @@ -18,6 +19,16 @@ GtTextualCoderViewModelSelectionChanged >> selection: anObject [ selection := anObject ] +{ #category : #accessing } +GtTextualCoderViewModelSelectionChanged >> shouldUpdateCursor [ + ^ shouldUpdateCursor ifNil: [ false ] +] + +{ #category : #accessing } +GtTextualCoderViewModelSelectionChanged >> shouldUpdateCursor: aBoolean [ + shouldUpdateCursor := aBoolean +] + { #category : #accessing } GtTextualCoderViewModelSelectionChanged >> source [ ^ source diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextAttributesAdded.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextAttributesAdded.class.st index a29458ab4..69df637c8 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextAttributesAdded.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextAttributesAdded.class.st @@ -4,7 +4,8 @@ Class { #instVars : [ 'coderTextAttributes', 'startPosition', - 'stopPosition' + 'stopPosition', + 'text' ], #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' } @@ -19,6 +20,11 @@ GtTextualCoderViewModelTextAttributesAdded >> coderTextAttributes: anObject [ coderTextAttributes := anObject ] +{ #category : #accessing } +GtTextualCoderViewModelTextAttributesAdded >> documentId [ + ^ self coderTextAttributes ifNotNil: #documentId +] + { #category : #accessing } GtTextualCoderViewModelTextAttributesAdded >> startPosition [ ^ startPosition @@ -38,3 +44,15 @@ GtTextualCoderViewModelTextAttributesAdded >> stopPosition [ GtTextualCoderViewModelTextAttributesAdded >> stopPosition: anObject [ stopPosition := anObject ] + +{ #category : #accessing } +GtTextualCoderViewModelTextAttributesAdded >> text [ + + ^ text +] + +{ #category : #accessing } +GtTextualCoderViewModelTextAttributesAdded >> text: anObject [ + + text := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextChanged.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextChanged.class.st index 12d5e1a7e..b12f2ac87 100644 --- a/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextChanged.class.st +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextChanged.class.st @@ -8,7 +8,13 @@ Class { #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' } -{ #category : #accessing } +{ #category : #testing } +GtTextualCoderViewModelTextChanged >> hasTextEditCommand [ + ^ self updateStrategy notNil and: [ + self updateStrategy hasTextEditCommand ] +] + +{ #category : #testing } GtTextualCoderViewModelTextChanged >> isSynchronous [ ^ self updateStrategy isSynchronous ] @@ -28,6 +34,12 @@ GtTextualCoderViewModelTextChanged >> text: anObject [ text := anObject ] +{ #category : #accessing } +GtTextualCoderViewModelTextChanged >> textEditCommand [ + ^ self updateStrategy ifNotNil: [ :anUpdateStrategy | + anUpdateStrategy textEditCommand ] +] + { #category : #accessing } GtTextualCoderViewModelTextChanged >> updateStrategy [ ^ updateStrategy diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextChangedSignal.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextChangedSignal.class.st new file mode 100644 index 000000000..01698d7cc --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextChangedSignal.class.st @@ -0,0 +1,48 @@ +Class { + #name : #GtTextualCoderViewModelTextChangedSignal, + #superclass : #ContextStackSignal, + #instVars : [ + 'text', + 'documentId', + 'textualCoderViewModel' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model' +} + +{ #category : #accessing } +GtTextualCoderViewModelTextChangedSignal >> documentId [ + ^ documentId +] + +{ #category : #accessing } +GtTextualCoderViewModelTextChangedSignal >> documentId: anObject [ + documentId := anObject +] + +{ #category : #printing } +GtTextualCoderViewModelTextChangedSignal >> printOneLineContentsOn: aStream [ + + documentId gtDisplayOn: aStream. + aStream space. + text gtDisplayOn: aStream. +] + +{ #category : #accessing } +GtTextualCoderViewModelTextChangedSignal >> text [ + ^ text +] + +{ #category : #accessing } +GtTextualCoderViewModelTextChangedSignal >> text: anObject [ + text := anObject +] + +{ #category : #accessing } +GtTextualCoderViewModelTextChangedSignal >> textualCoderViewModel [ + ^ textualCoderViewModel +] + +{ #category : #accessing } +GtTextualCoderViewModelTextChangedSignal >> textualCoderViewModel: anObject [ + textualCoderViewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextEditCommandRequestAnnouncement.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextEditCommandRequestAnnouncement.class.st new file mode 100644 index 000000000..ba0d20afb --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelTextEditCommandRequestAnnouncement.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtTextualCoderViewModelTextEditCommandRequestAnnouncement, + #superclass : #GtTextualCoderViewModelAnnouncement, + #instVars : [ + 'textEditCommand' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model - Events' +} + +{ #category : #accessing } +GtTextualCoderViewModelTextEditCommandRequestAnnouncement >> textEditCommand [ + ^ textEditCommand +] + +{ #category : #accessing } +GtTextualCoderViewModelTextEditCommandRequestAnnouncement >> textEditCommand: anObject [ + textEditCommand := anObject +] diff --git a/src/GToolkit-Coder-UI/GtTextualCoderViewModelWaitForDocumentIdRepeatedTask.class.st b/src/GToolkit-Coder-UI/GtTextualCoderViewModelWaitForDocumentIdRepeatedTask.class.st new file mode 100644 index 000000000..e73e7019d --- /dev/null +++ b/src/GToolkit-Coder-UI/GtTextualCoderViewModelWaitForDocumentIdRepeatedTask.class.st @@ -0,0 +1,102 @@ +Class { + #name : #GtTextualCoderViewModelWaitForDocumentIdRepeatedTask, + #superclass : #BlRepeatedTask, + #instVars : [ + 'textualCoderViewModel', + 'onUpdatedBlock', + 'onMissedBlock', + 'maxRepetitions', + 'counter' + ], + #category : #'GToolkit-Coder-UI-Coder - Textual Model' +} + +{ #category : #initialization } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> initialize [ + super initialize. + + onUpdatedBlock := [ :aTextualCoderViewModel :aDocumentId | ]. + onMissedBlock := [ :aTextualCoderViewModel | ]. + + "Wait delay * maxRepetitions time, + which is approximately 500ms * 144 = 72 seconds" + maxRepetitions := 144. + counter := 0. +] + +{ #category : #accessing } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> maxRepetitions [ + ^ maxRepetitions +] + +{ #category : #accessing } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> maxRepetitions: anInteger [ + maxRepetitions := anInteger +] + +{ #category : #private } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> missed [ + onMissedBlock ifNotNil: [ :aBlock | + aBlock cull: textualCoderViewModel ]. + self stop. +] + +{ #category : #hooks } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> onEnqueuedInElement: anElement [ + element == anElement + ifFalse: [ counter := 0 ]. + + super onEnqueuedInElement: anElement. +] + +{ #category : #accessing } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> onMissedBlock [ + + ^ onMissedBlock +] + +{ #category : #accessing } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> onMissedBlock: aBlock [ + + onMissedBlock := aBlock +] + +{ #category : #'api - running' } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> onRun [ + | documentId | + textualCoderViewModel ifNil: [ self missed. ^ self ]. + counter > maxRepetitions ifTrue: [ self missed. ^ self ]. + counter := counter + 1. + + documentId := textualCoderViewModel documentId ifNil: [ ^ self ]. + + onUpdatedBlock ifNotNil: [ :aBlock | + aBlock cull: textualCoderViewModel cull: documentId ]. + self stop +] + +{ #category : #accessing } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> onUpdatedBlock [ + ^ onUpdatedBlock +] + +{ #category : #accessing } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> onUpdatedBlock: aBlock [ + onUpdatedBlock := aBlock +] + +{ #category : #'api - running' } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> stop [ + super stop. + counter := 0. +] + +{ #category : #accessing } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> textualCoderViewModel [ + ^ textualCoderViewModel +] + +{ #category : #accessing } +GtTextualCoderViewModelWaitForDocumentIdRepeatedTask >> textualCoderViewModel: anObject [ + textualCoderViewModel := anObject +] diff --git a/src/GToolkit-Coder-UI/InstanceVariableSlot.extension.st b/src/GToolkit-Coder-UI/InstanceVariableSlot.extension.st new file mode 100644 index 000000000..b9d359462 --- /dev/null +++ b/src/GToolkit-Coder-UI/InstanceVariableSlot.extension.st @@ -0,0 +1,369 @@ +Extension { #name : #InstanceVariableSlot } + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtAbstractActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Abstract'; + priority: 23; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Abstract'; + targetName: ('{1}''s {2} inst. variable' + format: {aSelectedClass name. + self name}); + confirmationLabel: ('Abstract {1}' format: {self name}); + refactoringWithConfirmation: [ RBAbstractInstanceVariableRefactoring + variable: self name + class: aSelectedClass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtBrowseDefiningClassFor: anAction context: aPhlowContext [ + + self definingClass = self owningClass ifTrue: [ ^ anAction noAction ]. + + ^ anAction button + label: ('Browse defining {1}' format: {self definingClass gtCoderTypeName}); + priority: 6; + target: GtCoderVariableTarget; + menuItemPreview: [ self definingClass name ]; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | aButton phlow spawnObject: self definingClass ]; + primaryModifierAction: [ :aButton | self definingClass gtBrowseFrom: aButton ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtBrowseOwningClassFor: anAction context: aPhlowContext [ + + + ^ anAction button + label: 'Browse defining class'; + priority: 5; + target: GtCoderVariableTarget; + menuItemPreview: [ self owningClass name ]; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | aButton phlow spawnObject: self owningClass ]; + primaryModifierAction: [ :aButton | self owningClass gtBrowseFrom: aButton ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtBrowseReferencesActionFor: anAction context: aPhlowContext [ + + ^ anAction button + label: 'Browse references'; + priority: 10; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | + | aFilter | + aFilter := self owningClass isInstanceSide + ifTrue: [ GtSearchInstanceSlotReferenceFilter + forClassAndSubclasses: self owningClass + andVariable: self name ] + ifFalse: [ GtSearchClassSlotReferenceFilter + forClassAndSubclasses: self owningClass + andVariable: self name ]. + aButton phlow spawnObject: aFilter ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtCopySlotNameFor: anAction context: aPhlowContext [ + + + ^ anAction button + label: 'Copy slot name'; + icon: BrGlamorousVectorIcons clipboard; + priority: 5; + target: GtCoderVariableTarget; + menuItemPreview: [ self name ]; + menuItemGroup: BrMenuItemGroupConfiguration editing; + action: [ :aButton | Clipboard default clipboardText: self name asString ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtCopyValueTextFor: anAction context: aPhlowContext [ + + | aSelfObject | + aSelfObject := aPhlowContext selfObjectIfNone: [ ^ anAction noAction ]. + + ^ anAction button + label: 'Copy value text'; + icon: BrGlamorousVectorIcons clipboard; + priority: 6; + target: GtCoderVariableTarget; + menuItemPreview: [ (aSelfObject readSlot: self) gtDisplayText ]; + menuItemGroup: BrMenuItemGroupConfiguration editing; + action: [ :aButton | Clipboard default clipboardText: (aSelfObject readSlot: self) gtDisplayText ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtCreateAccessorsActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Create accessors'; + priority: 22; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Create accessors'; + targetName: ('{1}''s {2} inst. variable' + format: {aSelectedClass name. + self name}); + confirmationLabel: ('Create accessors {1}' format: {self name}); + refactoringWithConfirmation: [ RBCreateAccessorsForVariableRefactoring + instanceVariable: self name + class: aSelectedClass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtInspectObjectActionFor: anAction context: aPhlowContext [ + + | aSelfObject | + aSelfObject := aPhlowContext selfObjectIfNone: [ ^ anAction noAction ]. + + ^ anAction button + label: 'Inspect object'; + priority: 30; + target: GtCoderVariableTarget; + menuItemPreview: [ (aSelfObject readSlot: self) gtDisplayText ]; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | + aButton phlow spawnObject: (aSelfObject readSlot: self) ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtProtectSlotActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Protect'; + priority: 24; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Protect'; + targetName: ('{1}''s {2} inst. variable' + format: {aSelectedClass name. + self name}); + confirmationLabel: ('Protect {1}' format: {self name}); + refactoringWithConfirmation: [ RBProtectInstanceVariableRefactoring variable: self name class: aSelectedClass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtPushDownActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Push down'; + priority: 21; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Push down'; + targetName: ('{1}''s {2} inst. variable' + format: {aSelectedClass name. + self name}); + confirmationLabel: ('Push down {1}' format: {self name}); + refactoringWithConfirmation: [ GtPushDownInstanceVariableRefactoring + variable: self name + class: aSelectedClass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtPushUpActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Push up'; + priority: 20; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithConfirmationViewModel new + refactoringTitle: 'Push up'; + targetName: ('{1}''s {2} inst. variable' + format: {aSelectedClass name. + self name}); + confirmationLabel: ('Push up {1}' format: {self name}); + refactoringWithConfirmation: [ GtRBPullUpInstanceVariableRefactoring + variable: self name + class: aSelectedClass superclass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithConfirmationElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtRemoveActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Remove'; + priority: 40; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration removal; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | element change button | + element := BrVerticalPane new fitContent. + element + addChild: (BrLabel new + margin: (BlInsets + top: 10 + bottom: 0 + left: 10 + right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont; + text: ('Remove ' , self name) asRopedText). + element + addChild: (BrAsyncWidget new + fitContent; + stencil: [ | pane references | + pane := BrVerticalPane new. + pane fitContent. + references := (GtSearchInstanceSlotReferenceFilter + forClassAndSubclasses: aSelectedClass + andVariable: self name) size. + references > 0 + ifTrue: [ pane + addChild: (BrLabel new + margin: (BlInsets left: 10 right: 10); + aptitude: BrGlamorousLabelAptitude new glamorousRegularFont thin; + text: (references printString , ' reference' + , (references > 1 ifTrue: [ 's' ] ifFalse: [ '' ])) asRopedText) ]. + pane ]). + change := RBRemoveInstanceVariableChange + remove: self name + from: aSelectedClass. + button := BrButton new + aptitude: BrGlamorousButtonWithIconAptitude; + beSmallSize; + margin: (BlInsets + top: 10 + bottom: 10 + left: 10 + right: 10); + icon: BrGlamorousVectorIcons remove; + label: 'Remove'; + action: [ anExplicitMenu hideAll. + change execute ]. + element addChild: button as: #removeButton. + element ] +] + +{ #category : #'*GToolkit-Coder-UI' } +InstanceVariableSlot >> gtRenameActionFor: anAction context: aPhlowContext [ + + | aSelectedClass | + aSelectedClass := self owningClass. + aSelectedClass ifNil: [ ^ anAction noAction ]. + + ^ anAction dropdown + label: 'Rename'; + priority: 25; + target: GtCoderVariableTarget; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration refactoring; + menuItemPinSubmenu; + content: [ :anActionElement :aTargetElement :anExplicitMenu | + | aViewModel | + aViewModel := GtRefactoringsWithInputViewModel new + refactoringTitle: 'Rename'; + targetName: ('{1}''s {2} inst. variable' + format: {aSelectedClass name. + self name}); + inputLabel: 'New slot name:'; + refactoringWithInput: [ :anInput | + GtRenameInstanceVariableRefactoring + rename: self name + to: anInput + in: aSelectedClass ]; + afterAppliedBlock: [ BlTaskAction enqueueElement: aTargetElement action: [ anExplicitMenu hideAll ] ]; + menuModel: anExplicitMenu; + anchorElement: aTargetElement. + GtRefactoringsPreviewWithInputElement new + refactoringsViewModel: aViewModel; + beContextMenuElement; + bePinnable: anExplicitMenu ] +] diff --git a/src/GToolkit-Coder-UI/Object.extension.st b/src/GToolkit-Coder-UI/Object.extension.st new file mode 100644 index 000000000..9e7f4b630 --- /dev/null +++ b/src/GToolkit-Coder-UI/Object.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #Object } + +{ #category : #'*GToolkit-Coder-UI' } +Object >> asFilterModelItem [ + ^ GtFilterModelObjectItem new object: self +] diff --git a/src/GToolkit-Coder-UI/Package.extension.st b/src/GToolkit-Coder-UI/Package.extension.st new file mode 100644 index 000000000..efb2b9b1d --- /dev/null +++ b/src/GToolkit-Coder-UI/Package.extension.st @@ -0,0 +1,13 @@ +Extension { #name : #Package } + +{ #category : #'*GToolkit-Coder-UI' } +Package >> gtDefaultInspectorTool [ + ^ GtPhlowCompositeTool new + addTool: (GtPackageCoderTool forPackage: self); + addTool: super gtDefaultInspectorTool +] + +{ #category : #'*GToolkit-Coder-UI' } +Package >> gtSpawnCoder [ + ^ GtPackageCoderTool forPackage: self +] diff --git a/src/GToolkit-Coder-UI/PackageTag.extension.st b/src/GToolkit-Coder-UI/PackageTag.extension.st new file mode 100644 index 000000000..e78b150a9 --- /dev/null +++ b/src/GToolkit-Coder-UI/PackageTag.extension.st @@ -0,0 +1,13 @@ +Extension { #name : #PackageTag } + +{ #category : #'*GToolkit-Coder-UI' } +PackageTag >> gtDefaultInspectorTool [ + ^ GtPhlowCompositeTool new + addTool: (GtPackageTagCoderTool forPackageTag: self); + addTool: super gtDefaultInspectorTool +] + +{ #category : #'*GToolkit-Coder-UI' } +PackageTag >> gtSpawnCoder [ + ^ GtPackageTagCoderTool forPackageTag: self +] diff --git a/src/GToolkit-Coder-UI/RBAbstractInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBAbstractInstanceVariableRefactoring.extension.st new file mode 100644 index 000000000..a075c375d --- /dev/null +++ b/src/GToolkit-Coder-UI/RBAbstractInstanceVariableRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBAbstractInstanceVariableRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBAbstractInstanceVariableRefactoring >> gtDescription [ + ^ 'Abstract instance variable ' , variableName +] diff --git a/src/GToolkit-Coder-UI/RBAbstractVariablesRefactoring.extension.st b/src/GToolkit-Coder-UI/RBAbstractVariablesRefactoring.extension.st new file mode 100644 index 000000000..a131ee0eb --- /dev/null +++ b/src/GToolkit-Coder-UI/RBAbstractVariablesRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBAbstractVariablesRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBAbstractVariablesRefactoring >> gtDescription [ + ^ 'Abstract variables' +] diff --git a/src/GToolkit-Coder-UI/RBAddClassVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBAddClassVariableRefactoring.extension.st index 3afe79dd1..0cfa67e27 100644 --- a/src/GToolkit-Coder-UI/RBAddClassVariableRefactoring.extension.st +++ b/src/GToolkit-Coder-UI/RBAddClassVariableRefactoring.extension.st @@ -2,7 +2,7 @@ Extension { #name : #RBAddClassVariableRefactoring } { #category : #'*GToolkit-Coder-UI' } RBAddClassVariableRefactoring >> gtDescription [ - + ^ ('Add' asRopedText glamorousRoundedBackground) append: ' class variable ' asRopedText; diff --git a/src/GToolkit-Coder-UI/RBAddInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBAddInstanceVariableRefactoring.extension.st index c54c63196..0da544dc2 100644 --- a/src/GToolkit-Coder-UI/RBAddInstanceVariableRefactoring.extension.st +++ b/src/GToolkit-Coder-UI/RBAddInstanceVariableRefactoring.extension.st @@ -2,11 +2,11 @@ Extension { #name : #RBAddInstanceVariableRefactoring } { #category : #'*GToolkit-Coder-UI' } RBAddInstanceVariableRefactoring >> gtDescription [ - + ^ ('Add' asRopedText glamorousRoundedBackground) append: ' instance variable ' asRopedText; append: variableName asRopedText glamorousRoundedBackground; append: ' to ' asRopedText; - append: class printString asRopedText + append: self refactoredClassName asRopedText ] diff --git a/src/GToolkit-Coder-UI/RBAddParameterRefactoring.extension.st b/src/GToolkit-Coder-UI/RBAddParameterRefactoring.extension.st new file mode 100644 index 000000000..ce947770d --- /dev/null +++ b/src/GToolkit-Coder-UI/RBAddParameterRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBAddParameterRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBAddParameterRefactoring >> gtDescription [ + ^ 'Add parameter to ' , oldSelector +] diff --git a/src/GToolkit-Coder-UI/RBChildrenToSiblingsRefactoring.extension.st b/src/GToolkit-Coder-UI/RBChildrenToSiblingsRefactoring.extension.st new file mode 100644 index 000000000..e303fb5ac --- /dev/null +++ b/src/GToolkit-Coder-UI/RBChildrenToSiblingsRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBChildrenToSiblingsRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBChildrenToSiblingsRefactoring >> gtDescription [ + ^ 'Convert children of ' , className , ' to siblings' +] diff --git a/src/GToolkit-Coder-UI/RBExtractMethodRefactoring.extension.st b/src/GToolkit-Coder-UI/RBExtractMethodRefactoring.extension.st new file mode 100644 index 000000000..b61bf314d --- /dev/null +++ b/src/GToolkit-Coder-UI/RBExtractMethodRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBExtractMethodRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBExtractMethodRefactoring >> gtDescription [ + ^ 'Extract method from ' , class name , '>>' , selector +] diff --git a/src/GToolkit-Coder-UI/RBExtractMethodToComponentRefactoring.extension.st b/src/GToolkit-Coder-UI/RBExtractMethodToComponentRefactoring.extension.st new file mode 100644 index 000000000..c9623c0ef --- /dev/null +++ b/src/GToolkit-Coder-UI/RBExtractMethodToComponentRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBExtractMethodToComponentRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBExtractMethodToComponentRefactoring >> gtDescription [ + ^ 'Extract method to component from ' , class name , '>>' , selector +] diff --git a/src/GToolkit-Coder-UI/RBExtractToTemporaryRefactoring.extension.st b/src/GToolkit-Coder-UI/RBExtractToTemporaryRefactoring.extension.st new file mode 100644 index 000000000..9448cf0d9 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBExtractToTemporaryRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBExtractToTemporaryRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBExtractToTemporaryRefactoring >> gtDescription [ + ^ 'Extract expression to temporary ' , newVariableName +] diff --git a/src/GToolkit-Coder-UI/RBInlineAllSendersRefactoring.extension.st b/src/GToolkit-Coder-UI/RBInlineAllSendersRefactoring.extension.st new file mode 100644 index 000000000..ece6ab3dd --- /dev/null +++ b/src/GToolkit-Coder-UI/RBInlineAllSendersRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBInlineAllSendersRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBInlineAllSendersRefactoring >> gtDescription [ + ^ 'Inline all self sends of ' , selector +] diff --git a/src/GToolkit-Coder-UI/RBInlineMethodRefactoring.extension.st b/src/GToolkit-Coder-UI/RBInlineMethodRefactoring.extension.st new file mode 100644 index 000000000..0a9936b83 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBInlineMethodRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBInlineMethodRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBInlineMethodRefactoring >> gtDescription [ + ^ 'Inline method ' , class name , '>>' , sourceSelector +] diff --git a/src/GToolkit-Coder-UI/RBInlineParameterRefactoring.extension.st b/src/GToolkit-Coder-UI/RBInlineParameterRefactoring.extension.st new file mode 100644 index 000000000..5e8821532 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBInlineParameterRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBInlineParameterRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBInlineParameterRefactoring >> gtDescription [ + ^ 'Inline parameter ' , argument , ' in ' , oldSelector +] diff --git a/src/GToolkit-Coder-UI/RBInlineTemporaryRefactoring.extension.st b/src/GToolkit-Coder-UI/RBInlineTemporaryRefactoring.extension.st new file mode 100644 index 000000000..b7f5e81d1 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBInlineTemporaryRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBInlineTemporaryRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBInlineTemporaryRefactoring >> gtDescription [ + ^ 'Inline temporary variable in ' , class name , '>>' , selector +] diff --git a/src/GToolkit-Coder-UI/RBMoveMethodRefactoring.extension.st b/src/GToolkit-Coder-UI/RBMoveMethodRefactoring.extension.st new file mode 100644 index 000000000..24a02b3f8 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBMoveMethodRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBMoveMethodRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBMoveMethodRefactoring >> gtDescription [ + ^ 'Move method ' , class name , '>>' , selector +] diff --git a/src/GToolkit-Coder-UI/RBProtectInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBProtectInstanceVariableRefactoring.extension.st new file mode 100644 index 000000000..38f3378d5 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBProtectInstanceVariableRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBProtectInstanceVariableRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBProtectInstanceVariableRefactoring >> gtDescription [ + ^ 'Protect instance variable ' , variableName +] diff --git a/src/GToolkit-Coder-UI/RBPullUpClassVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBPullUpClassVariableRefactoring.extension.st new file mode 100644 index 000000000..ef386c07f --- /dev/null +++ b/src/GToolkit-Coder-UI/RBPullUpClassVariableRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBPullUpClassVariableRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBPullUpClassVariableRefactoring >> gtDescription [ + ^ 'Push up class variable ' , variableName +] diff --git a/src/GToolkit-Coder-UI/RBPullUpInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBPullUpInstanceVariableRefactoring.extension.st new file mode 100644 index 000000000..e365c9bf1 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBPullUpInstanceVariableRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBPullUpInstanceVariableRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBPullUpInstanceVariableRefactoring >> gtDescription [ + ^ 'Push up instance variable ' , variableName +] diff --git a/src/GToolkit-Coder-UI/RBPullUpMethodRefactoring.extension.st b/src/GToolkit-Coder-UI/RBPullUpMethodRefactoring.extension.st new file mode 100644 index 000000000..802a91f5c --- /dev/null +++ b/src/GToolkit-Coder-UI/RBPullUpMethodRefactoring.extension.st @@ -0,0 +1,11 @@ +Extension { #name : #RBPullUpMethodRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBPullUpMethodRefactoring >> gtDescription [ + ^ 'Push up method ' , self methodClassName , '>>' , selectors anyOne +] + +{ #category : #'*GToolkit-Coder-UI' } +RBPullUpMethodRefactoring >> methodClassName [ + ^ class name +] diff --git a/src/GToolkit-Coder-UI/RBPushDownClassVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBPushDownClassVariableRefactoring.extension.st new file mode 100644 index 000000000..2a51c9cd5 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBPushDownClassVariableRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBPushDownClassVariableRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBPushDownClassVariableRefactoring >> gtDescription [ + ^ 'Push down class variable ' , variableName +] diff --git a/src/GToolkit-Coder-UI/RBPushDownInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBPushDownInstanceVariableRefactoring.extension.st new file mode 100644 index 000000000..1a3fc75e3 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBPushDownInstanceVariableRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBPushDownInstanceVariableRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBPushDownInstanceVariableRefactoring >> gtDescription [ + ^ ('Push down instance variable ' , variableName) asRopedText +] diff --git a/src/GToolkit-Coder-UI/RBPushDownMethodRefactoring.extension.st b/src/GToolkit-Coder-UI/RBPushDownMethodRefactoring.extension.st new file mode 100644 index 000000000..96512e08b --- /dev/null +++ b/src/GToolkit-Coder-UI/RBPushDownMethodRefactoring.extension.st @@ -0,0 +1,11 @@ +Extension { #name : #RBPushDownMethodRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBPushDownMethodRefactoring >> gtDescription [ + ^ 'Push down method ' , self superclassName , '>>' , selectors anyOne +] + +{ #category : #'*GToolkit-Coder-UI' } +RBPushDownMethodRefactoring >> superclassName [ + ^ class name +] diff --git a/src/GToolkit-Coder-UI/RBRefactoring.extension.st b/src/GToolkit-Coder-UI/RBRefactoring.extension.st index 5dfe0ecf1..344d1d428 100644 --- a/src/GToolkit-Coder-UI/RBRefactoring.extension.st +++ b/src/GToolkit-Coder-UI/RBRefactoring.extension.st @@ -2,7 +2,12 @@ Extension { #name : #RBRefactoring } { #category : #'*GToolkit-Coder-UI' } RBRefactoring >> gtDescription [ - + ^ self class name ] + +{ #category : #'*GToolkit-Coder-UI' } +RBRefactoring >> gtDisplayOn: aStream [ + aStream nextPutAll: self gtDescription +] diff --git a/src/GToolkit-Coder-UI/RBRemoveClassVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBRemoveClassVariableRefactoring.extension.st deleted file mode 100644 index 515063afb..000000000 --- a/src/GToolkit-Coder-UI/RBRemoveClassVariableRefactoring.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : #RBRemoveClassVariableRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -RBRemoveClassVariableRefactoring >> gtDescription [ - - - ^ ('Remove' asRopedText glamorousRoundedBackground) - append: ' class variable ' asRopedText; - append: variableName asRopedText glamorousRoundedBackground; - append: ' from ' asRopedText; - append: class name asRopedText -] diff --git a/src/GToolkit-Coder-UI/RBRemoveInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBRemoveInstanceVariableRefactoring.extension.st deleted file mode 100644 index 54daab99c..000000000 --- a/src/GToolkit-Coder-UI/RBRemoveInstanceVariableRefactoring.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : #RBRemoveInstanceVariableRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -RBRemoveInstanceVariableRefactoring >> gtDescription [ - - - ^ ('Remove' asRopedText glamorousRoundedBackground) - append: ' instance variable ' asRopedText; - append: variableName asRopedText glamorousRoundedBackground; - append: ' from ' asRopedText; - append: class printString asRopedText -] diff --git a/src/GToolkit-Coder-UI/RBRemoveParameterRefactoring.extension.st b/src/GToolkit-Coder-UI/RBRemoveParameterRefactoring.extension.st new file mode 100644 index 000000000..abdba7ab7 --- /dev/null +++ b/src/GToolkit-Coder-UI/RBRemoveParameterRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBRemoveParameterRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBRemoveParameterRefactoring >> gtDescription [ + ^ 'Remove parameter ' , argument , ' from ' , class name , '>>' , oldSelector +] diff --git a/src/GToolkit-Coder-UI/RBRenameClassRefactoring.extension.st b/src/GToolkit-Coder-UI/RBRenameClassRefactoring.extension.st deleted file mode 100644 index 9e8c10631..000000000 --- a/src/GToolkit-Coder-UI/RBRenameClassRefactoring.extension.st +++ /dev/null @@ -1,12 +0,0 @@ -Extension { #name : #RBRenameClassRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -RBRenameClassRefactoring >> gtDescription [ - - - ^ ('Rename' asRopedText glamorousRoundedBackground) - append: ' class ' asRopedText; - append: className asRopedText; - append: ' to ' asRopedText; - append: newName asRopedText glamorousRoundedBackground -] diff --git a/src/GToolkit-Coder-UI/RBRenameClassVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBRenameClassVariableRefactoring.extension.st deleted file mode 100644 index fd3f38aa4..000000000 --- a/src/GToolkit-Coder-UI/RBRenameClassVariableRefactoring.extension.st +++ /dev/null @@ -1,14 +0,0 @@ -Extension { #name : #RBRenameClassVariableRefactoring } - -{ #category : #'*GToolkit-Coder-UI' } -RBRenameClassVariableRefactoring >> gtDescription [ - - - ^ ('Rename' asRopedText glamorousRoundedBackground) - append: ' class variable ' asRopedText; - append: variableName asRopedText glamorousRoundedBackground; - append: ' to ' asRopedText; - append: newName asRopedText glamorousRoundedBackground; - append: ' in ' asRopedText; - append: class name asRopedText -] diff --git a/src/GToolkit-Coder-UI/RBRenameInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBRenameInstanceVariableRefactoring.extension.st index 2b29bcf97..ef4b6209d 100644 --- a/src/GToolkit-Coder-UI/RBRenameInstanceVariableRefactoring.extension.st +++ b/src/GToolkit-Coder-UI/RBRenameInstanceVariableRefactoring.extension.st @@ -2,7 +2,7 @@ Extension { #name : #RBRenameInstanceVariableRefactoring } { #category : #'*GToolkit-Coder-UI' } RBRenameInstanceVariableRefactoring >> gtDescription [ - + ^ ('Rename' asRopedText glamorousRoundedBackground) append: ' instance variable ' asRopedText; diff --git a/src/GToolkit-Coder-UI/RBTemporaryToInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RBTemporaryToInstanceVariableRefactoring.extension.st new file mode 100644 index 000000000..f69b166bc --- /dev/null +++ b/src/GToolkit-Coder-UI/RBTemporaryToInstanceVariableRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RBTemporaryToInstanceVariableRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RBTemporaryToInstanceVariableRefactoring >> gtDescription [ + ^ 'Convert temporary ' , temporaryVariableName , ' to instance variable' +] diff --git a/src/GToolkit-Coder-UI/RBTransformation.extension.st b/src/GToolkit-Coder-UI/RBTransformation.extension.st index 2da892fec..a14ff6c18 100644 --- a/src/GToolkit-Coder-UI/RBTransformation.extension.st +++ b/src/GToolkit-Coder-UI/RBTransformation.extension.st @@ -2,7 +2,7 @@ Extension { #name : #RBTransformation } { #category : #'*GToolkit-Coder-UI' } RBTransformation >> gtDescription [ - + ^ self class name ] diff --git a/src/GToolkit-Coder-UI/RePullUpInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder-UI/RePullUpInstanceVariableRefactoring.extension.st new file mode 100644 index 000000000..db255da28 --- /dev/null +++ b/src/GToolkit-Coder-UI/RePullUpInstanceVariableRefactoring.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #RePullUpInstanceVariableRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RePullUpInstanceVariableRefactoring >> gtDescription [ + ^ 'Push up instance variable ' , variableName +] diff --git a/src/GToolkit-Coder-UI/RePullUpMethodRefactoring.extension.st b/src/GToolkit-Coder-UI/RePullUpMethodRefactoring.extension.st new file mode 100644 index 000000000..6fc2c7a0e --- /dev/null +++ b/src/GToolkit-Coder-UI/RePullUpMethodRefactoring.extension.st @@ -0,0 +1,11 @@ +Extension { #name : #RePullUpMethodRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +RePullUpMethodRefactoring >> gtDescription [ + ^ 'Push up method ' , self methodClassName , '>>' , selectors anyOne +] + +{ #category : #'*GToolkit-Coder-UI' } +RePullUpMethodRefactoring >> methodClassName [ + ^ class name +] diff --git a/src/GToolkit-Coder-UI/ReRefactoring.extension.st b/src/GToolkit-Coder-UI/ReRefactoring.extension.st new file mode 100644 index 000000000..94a2872cc --- /dev/null +++ b/src/GToolkit-Coder-UI/ReRefactoring.extension.st @@ -0,0 +1,13 @@ +Extension { #name : #ReRefactoring } + +{ #category : #'*GToolkit-Coder-UI' } +ReRefactoring >> gtDescription [ + + + ^ self class name +] + +{ #category : #'*GToolkit-Coder-UI' } +ReRefactoring >> gtDisplayOn: aStream [ + aStream nextPutAll: self gtDescription +] diff --git a/src/GToolkit-Coder-UI/ReTransformation.extension.st b/src/GToolkit-Coder-UI/ReTransformation.extension.st new file mode 100644 index 000000000..45b697c1d --- /dev/null +++ b/src/GToolkit-Coder-UI/ReTransformation.extension.st @@ -0,0 +1,8 @@ +Extension { #name : #ReTransformation } + +{ #category : #'*GToolkit-Coder-UI' } +ReTransformation >> gtDescription [ + + + ^ self class name +] diff --git a/src/GToolkit-Coder-UI/String.extension.st b/src/GToolkit-Coder-UI/String.extension.st new file mode 100644 index 000000000..c9da6ff92 --- /dev/null +++ b/src/GToolkit-Coder-UI/String.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #String } + +{ #category : #'*GToolkit-Coder-UI' } +String >> asFilterModelItem [ + ^ GtFilterModelStringItem new string: self +] diff --git a/src/GToolkit-Coder-UI/TBlDevScripterActionStep.extension.st b/src/GToolkit-Coder-UI/TBlDevScripterActionStep.extension.st new file mode 100644 index 000000000..c1e820772 --- /dev/null +++ b/src/GToolkit-Coder-UI/TBlDevScripterActionStep.extension.st @@ -0,0 +1,11 @@ +Extension { #name : #TBlDevScripterActionStep } + +{ #category : #'*GToolkit-Coder-UI' } +TBlDevScripterActionStep >> filterStep [ + ^ self + addStep: (GtFilterStep new + label: 'Filter'; + referenceSender; + onParentStepTarget: self; + onThisOrBreadthFirstChildOfKind: GtFilterItemsElement) +] diff --git a/src/GToolkit-Coder-UI/TBlDevScripterTarget.extension.st b/src/GToolkit-Coder-UI/TBlDevScripterTarget.extension.st index ac516b20c..675bdf71d 100644 --- a/src/GToolkit-Coder-UI/TBlDevScripterTarget.extension.st +++ b/src/GToolkit-Coder-UI/TBlDevScripterTarget.extension.st @@ -1,31 +1,11 @@ Extension { #name : #TBlDevScripterTarget } -{ #category : #'*GToolkit-Coder-UI' } -TBlDevScripterTarget >> onGtPharoSnippetDestinationButton [ - self - onChildNamed: #content; - onChildOfClass: GtSourceCoderExpandedContentElement; - onChildNamed: #switchButton -] - -{ #category : #'*GToolkit-Coder-UI' } -TBlDevScripterTarget >> onGtPharoSnippetEvaluateButton [ - self - "onChildOfClass: GtExpandableCoderElement;" - onChildNamed: #content; - onChildOfClass: GtSourceCoderExpandedContentElement; - onChildNamed: #actions; - onChildAt: 1; "BrToolbar" - "onChildOfClass: BrToolbar;" - onChildAt: 1 -] - { #category : #'*GToolkit-Coder-UI' } TBlDevScripterTarget >> onGtPharoSnippetPlayButton [ self - onChildNamed: #content; + id: #content; onChildOfClass: GtSourceCoderExpandedContentElement; - onChildNamed: #actions; + id: #actions; onChildAt: 1; "BrToolbar" onChildAt: 1 "BrButton" ] diff --git a/src/GToolkit-Coder-UI/TBrMenuModelVisitor.extension.st b/src/GToolkit-Coder-UI/TBrMenuModelVisitor.extension.st new file mode 100644 index 000000000..9085223be --- /dev/null +++ b/src/GToolkit-Coder-UI/TBrMenuModelVisitor.extension.st @@ -0,0 +1,16 @@ +Extension { #name : #TBrMenuModelVisitor } + +{ #category : #'*GToolkit-Coder-UI' } +TBrMenuModelVisitor >> visitCoderMenuActionItem: aMenuModel [ + ^ self visitMenuActionItem: aMenuModel +] + +{ #category : #'*GToolkit-Coder-UI' } +TBrMenuModelVisitor >> visitCoderSubmenuItem: aMenuModel [ + ^ self visitMenuSubmenuItem: aMenuModel +] + +{ #category : #'*GToolkit-Coder-UI' } +TBrMenuModelVisitor >> visitTextualCoderMenu: aMenuModel [ + ^ self visitMenu: aMenuModel +] diff --git a/src/GToolkit-Coder-UI/TGtCoderNavigationClassesHelper.trait.st b/src/GToolkit-Coder-UI/TGtCoderNavigationClassesHelper.trait.st deleted file mode 100644 index bc14dbd20..000000000 --- a/src/GToolkit-Coder-UI/TGtCoderNavigationClassesHelper.trait.st +++ /dev/null @@ -1,34 +0,0 @@ -Trait { - #name : #TGtCoderNavigationClassesHelper, - #category : #'GToolkit-Coder-UI-Navigation' -} - -{ #category : #'private - ui' } -TGtCoderNavigationClassesHelper >> buildClassLabel: aClass [ - | aLook aText | - aLook := (BrGlamorousLabelAptitude new fontSize: 12). - aText := aClass name. - - (self isAbstractClass: aClass) - ifTrue: [ aLook italic ]. - - (self isDeprecatedClass: aClass) - ifTrue: [ - aText := aText, ' (deprecated)'. - Transcript show: aText; cr. - aLook bold ]. - - ^ BrLabel new - aptitude: aLook; - text: aText. -] - -{ #category : #'private - testing' } -TGtCoderNavigationClassesHelper >> isAbstractClass: aClass [ - ^ aClass isAbstract or: [ aClass hasAbstractMethods ] -] - -{ #category : #'private - testing' } -TGtCoderNavigationClassesHelper >> isDeprecatedClass: aClass [ - ^ aClass isDeprecated -] diff --git a/src/GToolkit-Coder-UI/TGtCoderNavigationWithContextMenu.trait.st b/src/GToolkit-Coder-UI/TGtCoderNavigationWithContextMenu.trait.st new file mode 100644 index 000000000..2a42491f8 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtCoderNavigationWithContextMenu.trait.st @@ -0,0 +1,13 @@ +Trait { + #name : #TGtCoderNavigationWithContextMenu, + #category : #'GToolkit-Coder-UI-Navigation' +} + +{ #category : #'private - context menu' } +TGtCoderNavigationWithContextMenu >> createLabel: aString description: description [ + ^ aString asRopedText glamorousRegularFont + , ((' ' , description) asRopedText + glamorousCodeFont; + foreground: Color gray; + glamorousCodeTinySize) +] diff --git a/src/GToolkit-Coder-UI/TGtCoderRequesterObject.extension.st b/src/GToolkit-Coder-UI/TGtCoderRequesterObject.extension.st new file mode 100644 index 000000000..4bc533473 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtCoderRequesterObject.extension.st @@ -0,0 +1,11 @@ +Extension { #name : #TGtCoderRequesterObject } + +{ #category : #'*GToolkit-Coder-UI' } +TGtCoderRequesterObject >> isCoderViewModel: aCoderViewModel [ + ^ false +] + +{ #category : #'*GToolkit-Coder-UI' } +TGtCoderRequesterObject >> isUndefinedOrCoderViewModel: aCoderViewModel [ + ^ false +] diff --git a/src/GToolkit-Coder-UI/TGtCoderViewModelWithParentProperties.trait.st b/src/GToolkit-Coder-UI/TGtCoderViewModelWithParentProperties.trait.st new file mode 100644 index 000000000..7182ca7fa --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtCoderViewModelWithParentProperties.trait.st @@ -0,0 +1,61 @@ +Trait { + #name : #TGtCoderViewModelWithParentProperties, + #category : #'GToolkit-Coder-UI-Coder - Basic' +} + +{ #category : #accessing } +TGtCoderViewModelWithParentProperties >> parentCoderViewModel [ + ^ self + optionAt: #parentCoderViewModel + ifPresent: [ :aWeak | aWeak at: 1 ] + ifAbsent: [ nil ] +] + +{ #category : #accessing } +TGtCoderViewModelWithParentProperties >> parentCoderViewModel: aCoderViewModel [ + "In case of method coders expanded inside of another method coder, + the parent-method-coder view model is set, to be able to get an extra information." + + self + optionAt: #parentCoderViewModel + put: aCoderViewModel asWeakReference +] + +{ #category : #accessing } +TGtCoderViewModelWithParentProperties >> parentExample [ + + ^ self + optionAt: #parentExample + ifPresent: [ :aWeak | aWeak at: 1 ] + ifAbsent: [ nil ] +] + +{ #category : #accessing } +TGtCoderViewModelWithParentProperties >> parentExample: anExample [ + "In case of method coders expanded inside of another method coder, + the parent-method-coder example is set, to be able to highlight failing code + in those expanded method coders." + + self + optionAt: #parentExample + put: anExample asWeakReference +] + +{ #category : #accessing } +TGtCoderViewModelWithParentProperties >> parentTestCase [ + ^ self + optionAt: #parentTestCase + ifPresent: [ :aWeak | aWeak at: 1 ] + ifAbsent: [ nil ] +] + +{ #category : #accessing } +TGtCoderViewModelWithParentProperties >> parentTestCase: aTestCase [ + "In case of method coders expanded inside of another method coder, + the parent-method-coder test case is set, to be able to highlight failing code + in those expanded method coders." + + self + optionAt: #parentTestCase + put: aTestCase asWeakReference +] diff --git a/src/GToolkit-Coder-UI/TGtCoderWithSelfObjectHolder.trait.st b/src/GToolkit-Coder-UI/TGtCoderWithSelfObjectHolder.trait.st new file mode 100644 index 000000000..a31546861 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtCoderWithSelfObjectHolder.trait.st @@ -0,0 +1,71 @@ +Trait { + #name : #TGtCoderWithSelfObjectHolder, + #instVars : [ + 'selfObjectHolder' + ], + #category : #'GToolkit-Coder-UI-Coder - Basic' +} + +{ #category : #testing } +TGtCoderWithSelfObjectHolder classSide >> isDeprecated [ + "Use my TGtPhlowWithSelfObjectHolder instead" + + ^ true +] + +{ #category : #'api - self object' } +TGtCoderWithSelfObjectHolder >> hasSelfObject [ + ^ self ifSelfObject: [ :anObject | true ] ifNone: [ false ] +] + +{ #category : #'api - self object' } +TGtCoderWithSelfObjectHolder >> ifSelfObject: definedBlock ifNone: noneBlock [ + ^ self selfObjectHolder ifDefined: definedBlock ifNone: noneBlock +] + +{ #category : #'api - self object' } +TGtCoderWithSelfObjectHolder >> onSelfObjectHolderChanged [ + "Trait users can perform other actions using this hook." + + +] + +{ #category : #'api - self object' } +TGtCoderWithSelfObjectHolder >> selfObject [ + "Return an object that should be bound to `self` during code evaluation. + Favor using #ifSelfObject:ifNone: to distinguish between defined + self-objects and nil values." + + ^ self selfObjectHolder ifDefined: [ :anObject | anObject ] ifNone: [ nil ] +] + +{ #category : #'api - self object' } +TGtCoderWithSelfObjectHolder >> selfObject: anObject [ + "Assign an object that should be bound to `self` during code evaluation" + + self selfObjectHolder: (GtPhlowObject forValue: anObject) +] + +{ #category : #'api - self object' } +TGtCoderWithSelfObjectHolder >> selfObjectHolder [ + + ^ selfObjectHolder ifNil: [ + selfObjectHolder := GtPhlowUndefinedObject default ] +] + +{ #category : #'api - self object' } +TGtCoderWithSelfObjectHolder >> selfObjectHolder: aTGtCoderObject [ + self + assert: [ aTGtCoderObject isNotNil ] + description: [ 'selfObject holder must not be nil.' ]. + + self selfObjectHolder = aTGtCoderObject ifTrue: [ ^ self ]. + + selfObjectHolder := aTGtCoderObject. + self onSelfObjectHolderChanged +] + +{ #category : #'api - self object' } +TGtCoderWithSelfObjectHolder >> selfObjectIfNone: noneBlock [ + ^ self ifSelfObject: #yourself ifNone: noneBlock +] diff --git a/src/GToolkit-Coder-UI/TGtExampleResultValue.extension.st b/src/GToolkit-Coder-UI/TGtExampleResultValue.extension.st new file mode 100644 index 000000000..b8b302137 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtExampleResultValue.extension.st @@ -0,0 +1,8 @@ +Extension { #name : #TGtExampleResultValue } + +{ #category : #'*GToolkit-Coder-UI' } +TGtExampleResultValue >> asGtExampleResultPreviewElementFor: anExample [ + + ^ ((GtInspector forObject: self value) exact: 400 @ 400) + asScalableElement size: 200 @ 200 +] diff --git a/src/GToolkit-Coder-UI/TGtFilterViewModelParameters.trait.st b/src/GToolkit-Coder-UI/TGtFilterViewModelParameters.trait.st new file mode 100644 index 000000000..ff8a8cf4e --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtFilterViewModelParameters.trait.st @@ -0,0 +1,10 @@ +Trait { + #name : #TGtFilterViewModelParameters, + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #accessing } +TGtFilterViewModelParameters >> parameters [ + + ^ self filterModel parameters +] diff --git a/src/GToolkit-Coder-UI/TGtWithCoderModel.trait.st b/src/GToolkit-Coder-UI/TGtWithCoderModel.trait.st deleted file mode 100644 index ac140505c..000000000 --- a/src/GToolkit-Coder-UI/TGtWithCoderModel.trait.st +++ /dev/null @@ -1,62 +0,0 @@ -Trait { - #name : #TGtWithCoderModel, - #instVars : [ - 'coderModel' - ], - #category : #'GToolkit-Coder-UI-Coder - Basic' -} - -{ #category : #'api - coder model' } -TGtWithCoderModel >> coderModel [ - "Return a not-null coder model assigned to the receiver view model" - self - assert: [ coderModel notNil ] - description: [ 'coder model should be initialized' ]. - - ^ coderModel -] - -{ #category : #'api - coder model' } -TGtWithCoderModel >> coderModel: aCoderModel [ - "Set a not-null coder domain model assigned to the receiver view model" - self - assert: [ aCoderModel notNil ] - description: [ 'coder model must not be nil' ]. - - coderModel == aCoderModel - ifTrue: [ ^ self ]. - - coderModel ifNotNil: [ self unsubscribeFromCoderModel ]. - coderModel := aCoderModel. - - self onCoderModelChanged. - self subscribeToCoderModel -] - -{ #category : #'api - coder model' } -TGtWithCoderModel >> hasCoder [ - "Return a true if coder model is assigned to the receiver, false otherwise" - - - ^ coderModel notNil -] - -{ #category : #'api - coder model' } -TGtWithCoderModel >> onCoderModelChanged [ - "Is sent when a new coder model is assigned to the view model" - - self explicitRequirement -] - -{ #category : #'api - coder model' } -TGtWithCoderModel >> subscribeToCoderModel [ - "Is sent after a new coder model is assigned to the view model. - It is required to unsubscribe from the domain model by implementing - #unsubscribeFromCoderModel if view model subscribes to them" -] - -{ #category : #'api - coder model' } -TGtWithCoderModel >> unsubscribeFromCoderModel [ - "Is sent before a new coder model is assigned to the view model. - View models that subscribe to coder model are required to implement this methods" -] diff --git a/src/GToolkit-Coder-UI/TGtWithCoderToolbar.trait.st b/src/GToolkit-Coder-UI/TGtWithCoderToolbar.trait.st new file mode 100644 index 000000000..23c9abf1f --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtWithCoderToolbar.trait.st @@ -0,0 +1,26 @@ +Trait { + #name : #TGtWithCoderToolbar, + #instVars : [ + 'toolbarElement' + ], + #category : #'GToolkit-Coder-UI-Coder - Basic' +} + +{ #category : #accessing } +TGtWithCoderToolbar >> initializeToolbarElement [ + toolbarElement := GtCoderToolbarElement new + constraintsDo: [ :c | + c ignoreByLayout. + c ignored horizontal alignRight. + c ignored vertical alignTop ] +] + +{ #category : #accessing } +TGtWithCoderToolbar >> navigationModelForToolbar: aNavigationModel [ + self toolbarElement navigationModel: aNavigationModel +] + +{ #category : #accessing } +TGtWithCoderToolbar >> toolbarElement [ + ^ toolbarElement +] diff --git a/src/GToolkit-Coder-UI/TGtWithFilterModel.trait.st b/src/GToolkit-Coder-UI/TGtWithFilterModel.trait.st new file mode 100644 index 000000000..c4937d4b3 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtWithFilterModel.trait.st @@ -0,0 +1,42 @@ +Trait { + #name : #TGtWithFilterModel, + #instVars : [ + 'filterModel' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'api - filter model' } +TGtWithFilterModel >> filterModel [ + self + assert: [ filterModel isNotNil ] + description: [ 'Filter model must be defined' ]. + ^ filterModel +] + +{ #category : #'api - filter model' } +TGtWithFilterModel >> filterModel: aFilterModel [ + filterModel = aFilterModel ifTrue: [ ^ self ]. + + filterModel ifNotNil: [ self unsubscribeFromFilterModel ]. + filterModel := aFilterModel. + self onFilterModelChanged. + self subscribeToFilterModel +] + +{ #category : #'api - filter model' } +TGtWithFilterModel >> hasFilterModel [ + ^ filterModel isNotNil +] + +{ #category : #'api - filter model' } +TGtWithFilterModel >> onFilterModelChanged [ +] + +{ #category : #'api - filter model' } +TGtWithFilterModel >> subscribeToFilterModel [ +] + +{ #category : #'api - filter model' } +TGtWithFilterModel >> unsubscribeFromFilterModel [ +] diff --git a/src/GToolkit-Coder-UI/TGtWithFilterModelParameters.trait.st b/src/GToolkit-Coder-UI/TGtWithFilterModelParameters.trait.st new file mode 100644 index 000000000..beb566a57 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtWithFilterModelParameters.trait.st @@ -0,0 +1,65 @@ +Trait { + #name : #TGtWithFilterModelParameters, + #instVars : [ + 'parameters' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #parameters } +TGtWithFilterModelParameters >> addParameter: aFilterModelParameter [ + self parameters add: aFilterModelParameter. + self subscribeToParameter: aFilterModelParameter. +] + +{ #category : #converting } +TGtWithFilterModelParameters >> asSearchFilter [ + | aFilter | + aFilter := super asSearchFilter. + self parameters do: [ :eachParameter | + eachParameter onCreateFilter: aFilter ]. + ^ aFilter +] + +{ #category : #'event handling' } +TGtWithFilterModelParameters >> onParameterModelAnnouncement: anAnnouncement [ + anAnnouncement changesFilteredResult ifFalse: [ ^ self ]. + + self announce: (GtFilterModelParameterUpdated new + model: self; + originalAnnouncement: anAnnouncement) +] + +{ #category : #parameters } +TGtWithFilterModelParameters >> parameter: aFilterModelParameter [ + self parameters add: aFilterModelParameter. + self subscribeToParameter: aFilterModelParameter. +] + +{ #category : #parameters } +TGtWithFilterModelParameters >> parameterAt: anIndex do: aBlock [ + | aParameter | + aParameter := self parameters at: anIndex ifAbsent: [ nil ]. + aParameter ifNotNil: aBlock +] + +{ #category : #accessing } +TGtWithFilterModelParameters >> parameters [ + + ^ parameters ifNil: [ parameters := OrderedCollection new: 1 ] +] + +{ #category : #accessing } +TGtWithFilterModelParameters >> parameters: aCollection [ + parameters := aCollection. + aCollection do: [ :eachParameter | + self subscribeToParameter: eachParameter ] +] + +{ #category : #'private - subscriptions' } +TGtWithFilterModelParameters >> subscribeToParameter: aFilterModelParameter [ + aFilterModelParameter weak + when: GtFilterModelAnnouncement + send: #onParameterModelAnnouncement: + to: self +] diff --git a/src/GToolkit-Coder-UI/TGtWithFilterViewModel.trait.st b/src/GToolkit-Coder-UI/TGtWithFilterViewModel.trait.st new file mode 100644 index 000000000..1401f7e0c --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtWithFilterViewModel.trait.st @@ -0,0 +1,48 @@ +Trait { + #name : #TGtWithFilterViewModel, + #instVars : [ + 'filterViewModel' + ], + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #'api - filter view model' } +TGtWithFilterViewModel >> filterViewModel [ + self + assert: [ filterViewModel isNotNil ] + description: [ 'Filter view model must be defined' ]. + ^ filterViewModel +] + +{ #category : #'api - filter view model' } +TGtWithFilterViewModel >> filterViewModel: aFilterModel [ + filterViewModel = aFilterModel ifTrue: [ ^ self ]. + + filterViewModel ifNotNil: [ self unsubscribeFromFilterViewModel ]. + filterViewModel := aFilterModel. + self onFilterViewModelChanged. + self subscribeToFilterViewModel +] + +{ #category : #'api - filter view model' } +TGtWithFilterViewModel >> filterViewModelDo: aBlock [ + self hasFilterViewModel ifTrue: [ + aBlock cull: self filterViewModel ] +] + +{ #category : #'api - filter view model' } +TGtWithFilterViewModel >> hasFilterViewModel [ + ^ filterViewModel isNotNil +] + +{ #category : #'api - filter view model' } +TGtWithFilterViewModel >> onFilterViewModelChanged [ +] + +{ #category : #'api - filter view model' } +TGtWithFilterViewModel >> subscribeToFilterViewModel [ +] + +{ #category : #'api - filter view model' } +TGtWithFilterViewModel >> unsubscribeFromFilterViewModel [ +] diff --git a/src/GToolkit-Coder-UI/TGtWithFiltersModel.trait.st b/src/GToolkit-Coder-UI/TGtWithFiltersModel.trait.st new file mode 100644 index 000000000..eddecc534 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtWithFiltersModel.trait.st @@ -0,0 +1,47 @@ +Trait { + #name : #TGtWithFiltersModel, + #instVars : [ + 'filterModels' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #'api - filter model' } +TGtWithFiltersModel >> filtersModel [ + self + assert: [ filterModels isNotNil ] + description: [ 'Filters model must be defined' ]. + ^ filterModels +] + +{ #category : #'api - filter model' } +TGtWithFiltersModel >> filtersModel: aFiltersModel [ + filterModels = aFiltersModel ifTrue: [ ^ self ]. + + filterModels ifNotNil: [ self unsubscribeFromFiltersModel ]. + filterModels := aFiltersModel. + self onFiltersModelChanged. + self subscribeToFiltersModel +] + +{ #category : #'api - filter model' } +TGtWithFiltersModel >> filtersModelDo: aBlock [ + self hasFiltersModel ifTrue: [ aBlock cull: self filtersModel ] +] + +{ #category : #'api - filter model' } +TGtWithFiltersModel >> hasFiltersModel [ + ^ filterModels isNotNil +] + +{ #category : #'api - filter model' } +TGtWithFiltersModel >> onFiltersModelChanged [ +] + +{ #category : #'api - filter model' } +TGtWithFiltersModel >> subscribeToFiltersModel [ +] + +{ #category : #'api - filter model' } +TGtWithFiltersModel >> unsubscribeFromFiltersModel [ +] diff --git a/src/GToolkit-Coder-UI/TGtWithFiltersViewModel.trait.st b/src/GToolkit-Coder-UI/TGtWithFiltersViewModel.trait.st new file mode 100644 index 000000000..58f3b76bb --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtWithFiltersViewModel.trait.st @@ -0,0 +1,48 @@ +Trait { + #name : #TGtWithFiltersViewModel, + #instVars : [ + 'filtersViewModel' + ], + #category : #'GToolkit-Coder-UI-Filters - View Models' +} + +{ #category : #'api - filter view model' } +TGtWithFiltersViewModel >> filtersViewModel [ + self + assert: [ filtersViewModel isNotNil ] + description: [ 'Filters view model must be defined' ]. + ^ filtersViewModel +] + +{ #category : #'api - filter view model' } +TGtWithFiltersViewModel >> filtersViewModel: aFiltersModel [ + filtersViewModel = aFiltersModel ifTrue: [ ^ self ]. + + filtersViewModel ifNotNil: [ self unsubscribeFromFiltersViewModel ]. + filtersViewModel := aFiltersModel. + self onFiltersViewModelChanged. + self subscribeToFiltersViewModel +] + +{ #category : #'api - filter view model' } +TGtWithFiltersViewModel >> filtersViewModelDo: aBlock [ + self hasFiltersViewModel ifTrue: [ + aBlock cull: self filtersViewModel ] +] + +{ #category : #'api - filter view model' } +TGtWithFiltersViewModel >> hasFiltersViewModel [ + ^ filtersViewModel isNotNil +] + +{ #category : #'api - filter view model' } +TGtWithFiltersViewModel >> onFiltersViewModelChanged [ +] + +{ #category : #'api - filter view model' } +TGtWithFiltersViewModel >> subscribeToFiltersViewModel [ +] + +{ #category : #'api - filter view model' } +TGtWithFiltersViewModel >> unsubscribeFromFiltersViewModel [ +] diff --git a/src/GToolkit-Coder-UI/TGtWithItemsSelector.trait.st b/src/GToolkit-Coder-UI/TGtWithItemsSelector.trait.st new file mode 100644 index 000000000..ee3251dc0 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtWithItemsSelector.trait.st @@ -0,0 +1,51 @@ +Trait { + #name : #TGtWithItemsSelector, + #instVars : [ + 'itemsSelector' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #initialization } +TGtWithItemsSelector >> defaultItemsSelector [ + ^ GtFilterModelItemsWithoutSelectedItemSelector new filterModel: self +] + +{ #category : #initialization } +TGtWithItemsSelector >> displayAllItems [ + "Display all items, including the selected item in a dropdown list." + + self itemsSelector: (GtFilterModelAllItemsSelector new filterModel: self) +] + +{ #category : #initialization } +TGtWithItemsSelector >> displayItemsWithoutSelectedItem [ + self itemsSelector: (GtFilterModelItemsWithoutSelectedItemSelector new filterModel: self) +] + +{ #category : #accessing } +TGtWithItemsSelector >> itemsSelector [ + + ^ itemsSelector ifNil: [ itemsSelector := self defaultItemsSelector ] +] + +{ #category : #accessing } +TGtWithItemsSelector >> itemsSelector: anItemsSelector [ + itemsSelector := anItemsSelector +] + +{ #category : #accessing } +TGtWithItemsSelector >> someItems [ + "Return items that should be displayed in a list" + + + ^ self itemsSelector items +] + +{ #category : #accessing } +TGtWithItemsSelector >> someItemsFuture [ + "Return an async future that computes list items" + + + ^ self itemsSelector itemsFuture asAsyncFuture +] diff --git a/src/GToolkit-Coder-UI/TGtWithSelectableItem.trait.st b/src/GToolkit-Coder-UI/TGtWithSelectableItem.trait.st new file mode 100644 index 000000000..ec6ce0eb5 --- /dev/null +++ b/src/GToolkit-Coder-UI/TGtWithSelectableItem.trait.st @@ -0,0 +1,123 @@ +Trait { + #name : #TGtWithSelectableItem, + #instVars : [ + 'selectedItem', + 'itemsBuilder' + ], + #category : #'GToolkit-Coder-UI-Filters - Models' +} + +{ #category : #accessing } +TGtWithSelectableItem >> items [ + + self deprecated: 'Use #itemsFuture instead'. + + ^ itemsBuilder create +] + +{ #category : #accessing } +TGtWithSelectableItem >> items: anObject [ + self itemsBuilder: anObject +] + +{ #category : #accessing } +TGtWithSelectableItem >> items: anObject icon: anIconStencilBuilder [ + | aNewItemsBuilder aPluggableItemsBuilder | + aNewItemsBuilder := anObject asFilterModelItemsBuilder. + aPluggableItemsBuilder := GtFilterModelPluggableItemsBuilder new + itemsBuilder: aNewItemsBuilder; + iconStencilBuilder: anIconStencilBuilder. + self itemsBuilder: aPluggableItemsBuilder +] + +{ #category : #accessing } +TGtWithSelectableItem >> items: anObject icon: anIconStencilBuilder label: aLabelStencilBuilder [ + | aNewItemsBuilder aPluggableItemsBuilder | + aNewItemsBuilder := anObject asFilterModelItemsBuilder. + aPluggableItemsBuilder := GtFilterModelPluggableItemsBuilder new + itemsBuilder: aNewItemsBuilder; + iconStencilBuilder: anIconStencilBuilder; + labelStencilBuilder: aLabelStencilBuilder. + self itemsBuilder: aPluggableItemsBuilder +] + +{ #category : #accessing } +TGtWithSelectableItem >> items: anObject label: aLabelStencilBuilder [ + | aNewItemsBuilder aPluggableItemsBuilder | + aNewItemsBuilder := anObject asFilterModelItemsBuilder. + aPluggableItemsBuilder := GtFilterModelPluggableItemsBuilder new + itemsBuilder: aNewItemsBuilder; + labelStencilBuilder: aLabelStencilBuilder. + self itemsBuilder: aPluggableItemsBuilder +] + +{ #category : #accessing } +TGtWithSelectableItem >> itemsBuilder [ + + ^ itemsBuilder ifNil: [ + itemsBuilder := GtFilterModelEmptyItemsBuilder default ] +] + +{ #category : #accessing } +TGtWithSelectableItem >> itemsBuilder: anObject [ + | aNewItemsBuilder | + aNewItemsBuilder := anObject asFilterModelItemsBuilder. + self itemsBuilder = aNewItemsBuilder ifTrue: [ ^ self ]. + itemsBuilder := aNewItemsBuilder. + aNewItemsBuilder filterModel: self. + + self notifyItemsChanged +] + +{ #category : #accessing } +TGtWithSelectableItem >> itemsFuture [ + + ^ itemsBuilder createFuture +] + +{ #category : #'private - notifying' } +TGtWithSelectableItem >> notifyItemsChanged [ + self explicitRequirement +] + +{ #category : #'private - notifying' } +TGtWithSelectableItem >> notifySelectedItemChanged [ + self explicitRequirement +] + +{ #category : #initialization } +TGtWithSelectableItem >> selectFirstItem [ + self itemsBuilder firstItemDo: [ :anItem | self selectedItem: anItem ] +] + +{ #category : #initialization } +TGtWithSelectableItem >> selectItemAt: anIndex [ + self itemsBuilder itemAt: anIndex do: [ :anItem | self selectedItem: anItem ] +] + +{ #category : #initialization } +TGtWithSelectableItem >> selectLastItem [ + self itemsBuilder lastItemDo: [ :anItem | self selectedItem: anItem ] +] + +{ #category : #accessing } +TGtWithSelectableItem >> selectedItem [ + ^ selectedItem ifNil: [ selectedItem := GtFilterModelNoItem default ] +] + +{ #category : #accessing } +TGtWithSelectableItem >> selectedItem: anItem [ + | aNewItem | + aNewItem := anItem asFilterModelItem. + self selectedItem = aNewItem ifTrue: [ ^ self ]. + + selectedItem := aNewItem. + self notifySelectedItemChanged +] + +{ #category : #accessing } +TGtWithSelectableItem >> selectedValue [ + "Return a filter value, e.g., selected item, input text." + + ^ self selectedItem +] diff --git a/src/GToolkit-Coder-UI/TGtWithTextualCoderViewModel.trait.st b/src/GToolkit-Coder-UI/TGtWithTextualCoderViewModel.trait.st index 15a90478b..49082fbe9 100644 --- a/src/GToolkit-Coder-UI/TGtWithTextualCoderViewModel.trait.st +++ b/src/GToolkit-Coder-UI/TGtWithTextualCoderViewModel.trait.st @@ -58,13 +58,13 @@ TGtWithTextualCoderViewModel >> textualCoderViewModel: aTextualCoderViewModel [ textualCoderViewModel == aTextualCoderViewModel ifTrue: [ ^ self ]. - + textualCoderViewModel ifNotNil: [ self unsubscribeFromTextualCoderViewModel ]. textualCoderViewModel := aTextualCoderViewModel. self onTextualCoderViewModelChanged. self subscribeToTextualCoderViewModel. - self onPostTextualCoderViewModelChanged + self onPostTextualCoderViewModelChanged. ] { #category : #'api - textual coder view model' } diff --git a/src/GToolkit-Coder-UI/Trait.extension.st b/src/GToolkit-Coder-UI/Trait.extension.st new file mode 100644 index 000000000..b990fd17e --- /dev/null +++ b/src/GToolkit-Coder-UI/Trait.extension.st @@ -0,0 +1,21 @@ +Extension { #name : #Trait } + +{ #category : #'*GToolkit-Coder-UI' } +Trait >> gtCoderSidebarIndexBrowseTraitUsersFor: anAction [ + + self isTrait ifFalse: [ ^ anAction noAction ]. + + ^ anAction button + priority: 3; + target: GtCoderClassTarget; + icon: BrGlamorousVectorIcons empty; + label: 'Browse trait users'; + menuItemPreview: self name; + menuItemGroup: BrMenuItemGroupConfiguration navigation; + action: [ :aButton | aButton phlow spawnObject: (self instanceSide users asArray sort: [ :a | a name ] ascending) ] +] + +{ #category : #'*GToolkit-Coder-UI' } +Trait >> gtCoderTypeName [ + ^ 'trait' +] diff --git a/src/GToolkit-Coder-UI/UndefinedObject.extension.st b/src/GToolkit-Coder-UI/UndefinedObject.extension.st new file mode 100644 index 000000000..5cbef537a --- /dev/null +++ b/src/GToolkit-Coder-UI/UndefinedObject.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #UndefinedObject } + +{ #category : #'*GToolkit-Coder-UI' } +UndefinedObject >> asFilterModelItem [ + ^ GtFilterModelNoItem default +] diff --git a/src/GToolkit-Coder/Announcement.extension.st b/src/GToolkit-Coder/Announcement.extension.st index 31da0f484..a3a029977 100644 --- a/src/GToolkit-Coder/Announcement.extension.st +++ b/src/GToolkit-Coder/Announcement.extension.st @@ -3,15 +3,12 @@ Extension { #name : #Announcement } { #category : #'*GToolkit-Coder' } Announcement class >> gtAllKindOfReferencesFor: aView [ - ^ aView explicit + + ^ aView forward title: 'Announcement references'; tooltip: 'Class and subclass references'; priority: 41; - stencil: [ - | aFilter | - aFilter := self gtReferences. - self allSubclassesDo: [ :eachClass | - aFilter := aFilter | eachClass gtReferences ]. - aFilter ]; + object: [ self gtReferencesWithSubclasses ]; + view: #gtItemsFor:; actionUpdateButtonTooltip: 'Update reference list' ] diff --git a/src/GToolkit-Coder/Behavior.extension.st b/src/GToolkit-Coder/Behavior.extension.st new file mode 100644 index 000000000..738e3807f --- /dev/null +++ b/src/GToolkit-Coder/Behavior.extension.st @@ -0,0 +1,15 @@ +Extension { #name : #Behavior } + +{ #category : #'*GToolkit-Coder' } +Behavior >> gtBrowseFromButton: anElement [ + "Do we want to share the same coder model?" + + anElement phlow firstParentCoderNavigationModel + coderDo: [ :aCoder | + BlSpace new + withSceneDriller; + inPager: [ GtReadyCoderTool coder: aCoder asNewCoderModelWithSameSubject ]; + title: self printString; + icon: BrGlamorousVectorIcons browse; + showFrom: anElement ] +] diff --git a/src/GToolkit-Coder/BlTktWorkerProvider.extension.st b/src/GToolkit-Coder/BlTktWorkerProvider.extension.st deleted file mode 100644 index 6a5320efe..000000000 --- a/src/GToolkit-Coder/BlTktWorkerProvider.extension.st +++ /dev/null @@ -1,73 +0,0 @@ -Extension { #name : #BlTktWorkerProvider } - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider class >> coderAddOnsPool [ - - ^ self - serviceNamed: self coderAddOnsPoolName - ifMissing: [ self new coderAddOnsPool; service ] -] - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider >> coderAddOnsPool [ - "Coder addons computation: maxPoolSize: 2, priority: 30" - self - name: self class coderAddOnsPoolName; - priorityQueue; - poolMaxSize: 2; - nonUIPriority -] - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider class >> coderAddOnsPoolName [ - - ^ 'Coder addons' -] - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider >> coderAstPool [ - "Coder computation processing: maxPoolSize: 2, priority: 30" - self - name: self class coderAstPoolName; - priorityQueue; - poolMaxSize: 2; - nonUIPriority -] - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider class >> coderAstPool [ - - ^ self - serviceNamed: self coderAstPoolName - ifMissing: [ self new coderAstPool; service ] -] - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider class >> coderAstPoolName [ - - ^ 'Coder ast' -] - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider class >> coderPool [ - - ^ self - serviceNamed: self coderPoolName - ifMissing: [ self new coderPool; service ] -] - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider >> coderPool [ - "Coder computation processing: maxPoolSize: 1, priority: 30" - self - name: self class coderPoolName; - priorityQueue; - poolMaxSize: 1; - nonUIPriority -] - -{ #category : #'*GToolkit-Coder' } -BlTktWorkerProvider class >> coderPoolName [ - - ^ 'Coder updates' -] diff --git a/src/GToolkit-Coder/BrStencil.extension.st b/src/GToolkit-Coder/BrStencil.extension.st new file mode 100644 index 000000000..443f1d418 --- /dev/null +++ b/src/GToolkit-Coder/BrStencil.extension.st @@ -0,0 +1,12 @@ +Extension { #name : #BrStencil } + +{ #category : #'*GToolkit-Coder' } +BrStencil class >> gtInspectActionFor: anAction [ + + self isAbstract ifTrue: [ ^ anAction noAction ]. + ^ anAction button + icon: BrGlamorousVectorIcons playinspect; + tooltip: 'Inspect Stencil'; + priority: 10; + action: [ :aButton | aButton phlow spawnObject: self new ] +] diff --git a/src/GToolkit-Coder/Class.extension.st b/src/GToolkit-Coder/Class.extension.st deleted file mode 100644 index 6d4dbc34c..000000000 --- a/src/GToolkit-Coder/Class.extension.st +++ /dev/null @@ -1,11 +0,0 @@ -Extension { #name : #Class } - -{ #category : #'*GToolkit-Coder' } -Class >> gtInheritance [ - ^ GtPharoInheritance new subclass: self; superclass: self superclass -] - -{ #category : #'*GToolkit-Coder' } -Class >> gtTraitUsages [ - ^ self traits collect: [ :each | GtPharoTraitUsage new baseBehavior: self; usedTrait: each ] -] diff --git a/src/GToolkit-Coder/ClassDescription.extension.st b/src/GToolkit-Coder/ClassDescription.extension.st index c7ee1dafc..0e9e8eda9 100644 --- a/src/GToolkit-Coder/ClassDescription.extension.st +++ b/src/GToolkit-Coder/ClassDescription.extension.st @@ -1,5 +1,12 @@ Extension { #name : #ClassDescription } +{ #category : #'*GToolkit-Coder' } +ClassDescription >> gtDefaultInspectorTool [ + ^ GtPhlowCompositeTool new + addTool: (GtClassCoderTool forClass: self); + addTool: super gtDefaultInspectorTool +] + { #category : #'*GToolkit-Coder' } ClassDescription >> gtPackageScope [ ^ self package diff --git a/src/GToolkit-Coder/CompiledMethod.extension.st b/src/GToolkit-Coder/CompiledMethod.extension.st index cda7baa95..49c1bf106 100644 --- a/src/GToolkit-Coder/CompiledMethod.extension.st +++ b/src/GToolkit-Coder/CompiledMethod.extension.st @@ -4,13 +4,3 @@ Extension { #name : #CompiledMethod } CompiledMethod >> gtPackageScope [ ^ self package ] - -{ #category : #'*GToolkit-Coder' } -CompiledMethod >> gtReferencedClasses [ - "Return classes that are directly referenced by this method. - Compared to #referencedClasses I do not count a value of the ClassVariable as a reference" - - ^ self literals - select: [ :l | (l isKindOf: ClassVariable) not and: [ l value isClass ] ] - thenCollect: [:v | v value ]. -] diff --git a/src/GToolkit-Coder/DebugSession.extension.st b/src/GToolkit-Coder/DebugSession.extension.st index 487fed66d..8a109446d 100644 --- a/src/GToolkit-Coder/DebugSession.extension.st +++ b/src/GToolkit-Coder/DebugSession.extension.st @@ -3,7 +3,7 @@ Extension { #name : #DebugSession } { #category : #'*GToolkit-Coder' } DebugSession >> gtCreateModelForContext: aContext [ - ^ (GtDebugContext forContext: aContext) topContext: interruptedContext + ^ (GtDebugContext forContext: aContext) "topContext: interruptedContext" ] { #category : #'*GToolkit-Coder' } @@ -17,11 +17,10 @@ DebugSession >> gtRecompileMethodTo: text inContext: aContext notifying: aNotify aContext method isDoIt ifTrue: [ ^ false ]. (recompilationContext := (self gtCreateModelForContext: aContext) locateClosureHomeWithContent: text) ifNil: [ ^ false ]. + newMethod := (self gtCreateModelForContext: recompilationContext) recompileCurrentMethodTo: text notifying: aNotifyer. newMethod ifNil: [ ^ false ]. - newMethod isQuick - ifTrue: [ recompilationContext := self downInContext: recompilationContext. - recompilationContext jump: recompilationContext previousPc - recompilationContext pc ]. + (self isContextPostMortem: self interruptedContext) ifFalse: [ self rewindContextToMethod: newMethod fromContext: recompilationContext ]. diff --git a/src/GToolkit-Coder/GtBaselinesCompletionStrategy.class.st b/src/GToolkit-Coder/GtBaselinesCompletionStrategy.class.st new file mode 100644 index 000000000..fb3ac97c7 --- /dev/null +++ b/src/GToolkit-Coder/GtBaselinesCompletionStrategy.class.st @@ -0,0 +1,10 @@ +Class { + #name : #GtBaselinesCompletionStrategy, + #superclass : #GtClassesCompletionStrategy, + #category : #'GToolkit-Coder-Completion' +} + +{ #category : #accessing } +GtBaselinesCompletionStrategy >> candidateClasses [ + ^ super candidateClasses select: [:aClassName | aClassName beginsWith: 'BaselineOf' ] +] diff --git a/src/GToolkit-Coder/GtCharacterGroupDiffSplitter.class.st b/src/GToolkit-Coder/GtCharacterGroupDiffSplitter.class.st new file mode 100644 index 000000000..0e75b8958 --- /dev/null +++ b/src/GToolkit-Coder/GtCharacterGroupDiffSplitter.class.st @@ -0,0 +1,102 @@ +Class { + #name : #GtCharacterGroupDiffSplitter, + #superclass : #GtDiffSplitter, + #instVars : [ + 'groups', + 'shouldIgnoreWhitespace' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #'instance creation' } +GtCharacterGroupDiffSplitter class >> words [ + ^ self new + addGroup: #tokenish; + addGroup: #isLineBreak; + addGroup: #isSeparator; + yourself +] + +{ #category : #adding } +GtCharacterGroupDiffSplitter >> addGroup: aSymbolOrBlock [ + groups add: aSymbolOrBlock +] + +{ #category : #accessing } +GtCharacterGroupDiffSplitter >> classify: aCharacter [ + (shouldIgnoreWhitespace and: [ aCharacter isSeparator ]) ifTrue: [ ^ 0 ]. + ^ (1 to: groups size) + detect: [ :i | (groups at: i) value: aCharacter ] + ifNone: [ groups size + 1 ] +] + +{ #category : #'as yet unclassified' } +GtCharacterGroupDiffSplitter >> descriptionString [ + ^ 'by character groups (' , (', ' join: groups) , ')' +] + +{ #category : #initialization } +GtCharacterGroupDiffSplitter >> ignoreWhitespace [ + shouldIgnoreWhitespace := true +] + +{ #category : #initialization } +GtCharacterGroupDiffSplitter >> includeWhitespace [ + shouldIgnoreWhitespace := false +] + +{ #category : #initialization } +GtCharacterGroupDiffSplitter >> initialize [ + super initialize. + shouldIgnoreWhitespace := false. + groups := OrderedCollection new +] + +{ #category : #'as yet unclassified' } +GtCharacterGroupDiffSplitter >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + nextPutAll: (', ' join: groups); + nextPut: $) +] + +{ #category : #accessing } +GtCharacterGroupDiffSplitter >> split: aString from: start to: end into: aGtDiffSplits [ + | stream | + stream := ReadStream + on: aString + from: start + to: end. + self splitStream: stream into: aGtDiffSplits +] + +{ #category : #accessing } +GtCharacterGroupDiffSplitter >> split: aString into: aGtDiffSplits [ + self splitStream: aString readStream into: aGtDiffSplits +] + +{ #category : #accessing } +GtCharacterGroupDiffSplitter >> splitStream: stream into: aGtDiffSplits [ + | nextGroupId groupId start | + start := stream position + 1. + groupId := 0. + [ stream atEnd ] + whileFalse: [ nextGroupId := self classify: stream next. + nextGroupId ~= groupId + ifTrue: [ groupId = 0 + ifFalse: [ aGtDiffSplits + addSplit: (GtRangeDiffSplit + on: stream originalContents + from: start + to: stream position - 1) ]. + start := stream position. + groupId := nextGroupId ] ]. + groupId > 0 + ifTrue: [ aGtDiffSplits + addSplit: (GtRangeDiffSplit + on: stream originalContents + from: start + to: stream position) ] +] diff --git a/src/GToolkit-Coder/GtClassCoderMethodNavigationAnnouncement.class.st b/src/GToolkit-Coder/GtClassCoderMethodNavigationAnnouncement.class.st index 7cda787af..54694358a 100644 --- a/src/GToolkit-Coder/GtClassCoderMethodNavigationAnnouncement.class.st +++ b/src/GToolkit-Coder/GtClassCoderMethodNavigationAnnouncement.class.st @@ -2,7 +2,8 @@ Class { #name : #GtClassCoderMethodNavigationAnnouncement, #superclass : #Announcement, #instVars : [ - 'method' + 'method', + 'source' ], #category : #'GToolkit-Coder-Event' } @@ -16,3 +17,13 @@ GtClassCoderMethodNavigationAnnouncement >> method [ GtClassCoderMethodNavigationAnnouncement >> method: aMethod [ method := aMethod ] + +{ #category : #accessing } +GtClassCoderMethodNavigationAnnouncement >> source [ + ^ source +] + +{ #category : #accessing } +GtClassCoderMethodNavigationAnnouncement >> source: anObject [ + source := anObject +] diff --git a/src/GToolkit-Coder/GtClassCoderMethodsNavigationAnnouncement.class.st b/src/GToolkit-Coder/GtClassCoderMethodsNavigationAnnouncement.class.st new file mode 100644 index 000000000..9e0f3c1d4 --- /dev/null +++ b/src/GToolkit-Coder/GtClassCoderMethodsNavigationAnnouncement.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtClassCoderMethodsNavigationAnnouncement, + #superclass : #Announcement, + #instVars : [ + 'methods', + 'source' + ], + #category : #'GToolkit-Coder-Event' +} + +{ #category : #accessing } +GtClassCoderMethodsNavigationAnnouncement >> methods [ + ^ methods +] + +{ #category : #accessing } +GtClassCoderMethodsNavigationAnnouncement >> methods: anObject [ + methods := anObject +] + +{ #category : #accessing } +GtClassCoderMethodsNavigationAnnouncement >> source [ + ^ source +] + +{ #category : #accessing } +GtClassCoderMethodsNavigationAnnouncement >> source: anObject [ + source := anObject +] diff --git a/src/GToolkit-Coder/GtClassCoderSlotNavigationAnnouncement.class.st b/src/GToolkit-Coder/GtClassCoderSlotNavigationAnnouncement.class.st new file mode 100644 index 000000000..bece2402f --- /dev/null +++ b/src/GToolkit-Coder/GtClassCoderSlotNavigationAnnouncement.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtClassCoderSlotNavigationAnnouncement, + #superclass : #Announcement, + #instVars : [ + 'slot', + 'source' + ], + #category : #'GToolkit-Coder-Coders' +} + +{ #category : #'as yet unclassified' } +GtClassCoderSlotNavigationAnnouncement >> slot [ + ^ slot +] + +{ #category : #accessing } +GtClassCoderSlotNavigationAnnouncement >> slot: aSlot [ + slot := aSlot +] + +{ #category : #'as yet unclassified' } +GtClassCoderSlotNavigationAnnouncement >> source [ + ^ source +] + +{ #category : #accessing } +GtClassCoderSlotNavigationAnnouncement >> source: aSource [ + source := aSource +] diff --git a/src/GToolkit-Coder/GtClassesCompletionStrategy.class.st b/src/GToolkit-Coder/GtClassesCompletionStrategy.class.st index 3477de38e..4c3f06877 100644 --- a/src/GToolkit-Coder/GtClassesCompletionStrategy.class.st +++ b/src/GToolkit-Coder/GtClassesCompletionStrategy.class.st @@ -1,6 +1,6 @@ Class { #name : #GtClassesCompletionStrategy, - #superclass : #GtCompletionStrategy, + #superclass : #GtStreamedCompletionStrategy, #instVars : [ 'excludedClasses', 'candidateClasses' @@ -17,33 +17,32 @@ GtClassesCompletionStrategy >> candidateClasses [ ] { #category : #accessing } -GtClassesCompletionStrategy >> completionActionsFor: aText at: positionInteger max: maxInteger [ - | currentInput completionActions | +GtClassesCompletionStrategy >> completionActionStreamFor: aText at: positionInteger requested: aBoolean [ + | currentInput | currentInput := aText asString. - currentInput isEmpty - ifTrue: [ ^ #() ]. - completionActions := (self candidateClasses - first: maxInteger - startingWith: currentInput) - collect: [ :className | + currentInput isEmpty ifTrue: [ ^ #() asAsyncStream ]. + ^ (self candidateClasses asyncStreamStartingWith: currentInput) + collect: + [ :className | | deletion insertion | deletion := positionInteger < currentInput size - ifTrue: [ GtDeleteTextCompletionAction + ifTrue: + [ GtDeleteTextCompletionAction labeled: className from: positionInteger + 1 to: currentInput size ]. insertion := GtInsertTextCompletionAction - labeled: className + labeled: (self labelFor: className withSearch: currentInput) completion: (className allButFirst: positionInteger) position: positionInteger from: 1. deletion notNil - ifTrue: [ (GtCompositeCompletionAction labeled: className) + ifTrue: + [ (GtCompositeCompletionAction labeled: insertion displayText) addAction: deletion; addAction: insertion; yourself ] - ifFalse: [ insertion ] ]. - ^ completionActions + ifFalse: [ insertion ] ] ] { #category : #initialization } @@ -51,11 +50,6 @@ GtClassesCompletionStrategy >> excludeClasses: aCollectionOfClasses [ excludedClasses := aCollectionOfClasses asSet ] -{ #category : #testing } -GtClassesCompletionStrategy >> hasCompletionEntryFor: aString [ - ^ true -] - { #category : #testing } GtClassesCompletionStrategy >> hasCompletionsAt: anInteger in: aText [ ^ true diff --git a/src/GToolkit-Coder/GtCoderAction.class.st b/src/GToolkit-Coder/GtCoderAction.class.st index 87563f5e0..04d0573be 100644 --- a/src/GToolkit-Coder/GtCoderAction.class.st +++ b/src/GToolkit-Coder/GtCoderAction.class.st @@ -5,9 +5,16 @@ Class { 'title', 'icon', 'action', - 'id' + 'id', + 'shortcutKey', + 'allowPrimaryClick', + 'allowAltClick', + 'actionDefinition', + 'isEnabled', + 'properties', + 'priority' ], - #category : #'GToolkit-Coder' + #category : #'GToolkit-Coder-Coders - Addons' } { #category : #comparing } @@ -31,9 +38,62 @@ GtCoderAction >> action: anObject [ action := anObject ] +{ #category : #accessing } +GtCoderAction >> actionDefinition [ + "Return a method definition that defines the action" + + + ^ actionDefinition + ifNil: [ actionDefinition := self computeActionDefinition + ifNil: [ GtCoderActionNoDefinition default ] ] +] + +{ #category : #accessing } +GtCoderAction >> actionDefinitionFromBlock: aBlock [ + aBlock isClosure ifFalse: [ ^ self ]. + actionDefinition := GtCoderActionClosureDefinition new closure: aBlock +] + +{ #category : #testing } +GtCoderAction >> allowAltClick [ + ^ (allowAltClick ifNil: [ true ]) and: [ self actionDefinition isDefined ] +] + +{ #category : #accessing } +GtCoderAction >> allowPrimaryClick [ + ^ allowPrimaryClick ifNil: [ false ] +] + +{ #category : #accessing } +GtCoderAction >> allowPrimaryClick: anObject [ + allowPrimaryClick := anObject +] + +{ #category : #converting } +GtCoderAction >> asBrMenuItemForCoderElement: aTextualCoderEditorElement [ + ^ nil +] + +{ #category : #initialization } +GtCoderAction >> computeActionDefinition [ + ^ self action isClosure + ifTrue: [ GtCoderActionClosureDefinition new closure: self action ] + ifFalse: [ nil ] +] + +{ #category : #accessing } +GtCoderAction >> disableAction [ + isEnabled := false +] + +{ #category : #accessing } +GtCoderAction >> enableAction [ + isEnabled := true +] + { #category : #evaluating } -GtCoderAction >> glamourValueWithArgs: aSequenceOfArguments [ - ^ self action glamourValueWithArgs: aSequenceOfArguments +GtCoderAction >> gtValueWithArgs: anArray [ + ^ self action valueWithPossibleArgs: anArray ] { #category : #comparing } @@ -70,6 +130,16 @@ GtCoderAction >> id: anObject [ id := anObject ] +{ #category : #testing } +GtCoderAction >> isDisabled [ + ^ self isEnabled not +] + +{ #category : #testing } +GtCoderAction >> isEnabled [ + ^ isEnabled ifNil: [ true ] +] + { #category : #accessing } GtCoderAction >> leaveAction [ ^ nil @@ -94,6 +164,49 @@ GtCoderAction >> printOn: aStream [ nextPut: $) ] +{ #category : #accessing } +GtCoderAction >> priority [ + ^ priority ifNil: [ priority := 50 ] +] + +{ #category : #accessing } +GtCoderAction >> priority: aNumber [ + priority := aNumber +] + +{ #category : #'accessing properties' } +GtCoderAction >> properties [ + ^ properties ifNil: [ + properties := Dictionary new ] +] + +{ #category : #'accessing properties' } +GtCoderAction >> propertyAt: propName ifAbsent: aBlock [ + properties ifNil: [ ^aBlock value ]. + ^ self properties + at: propName + ifAbsent: aBlock +] + +{ #category : #'accessing properties' } +GtCoderAction >> propertyAt: propName put: propValue [ + ^ self properties + at: propName + ifAbsentPut: propValue +] + +{ #category : #accessing } +GtCoderAction >> shortcutKey [ + + ^ shortcutKey ifNil: [shortcutKey := ''] +] + +{ #category : #accessing } +GtCoderAction >> shortcutKey: aString [ + + shortcutKey := aString +] + { #category : #accessing } GtCoderAction >> title [ ^ title diff --git a/src/GToolkit-Coder/GtCoderActionClosureDefinition.class.st b/src/GToolkit-Coder/GtCoderActionClosureDefinition.class.st new file mode 100644 index 000000000..e4e92d4ce --- /dev/null +++ b/src/GToolkit-Coder/GtCoderActionClosureDefinition.class.st @@ -0,0 +1,42 @@ +Class { + #name : #GtCoderActionClosureDefinition, + #superclass : #GtCoderActionDefinition, + #instVars : [ + 'closure' + ], + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #accessing } +GtCoderActionClosureDefinition >> closure [ + ^ closure +] + +{ #category : #accessing } +GtCoderActionClosureDefinition >> closure: aBlock [ + closure := aBlock +] + +{ #category : #'gt - extensions' } +GtCoderActionClosureDefinition >> gtClosureVariablesFor: aView [ + + ^ aView forward + title: 'Variables'; + object: [ closure ]; + view: #gtVariablesFor: +] + +{ #category : #'gt - extensions' } +GtCoderActionClosureDefinition >> gtSourceCodeFor: aView [ + + ^ aView forward + title: 'Source code'; + priority: 1; + object: [ self closure ]; + view: #gtSourceCodeFor: +] + +{ #category : #testing } +GtCoderActionClosureDefinition >> isDefined [ + ^ self closure isNotNil +] diff --git a/src/GToolkit-Coder/GtCoderActionDefinition.class.st b/src/GToolkit-Coder/GtCoderActionDefinition.class.st new file mode 100644 index 000000000..7bd93b14d --- /dev/null +++ b/src/GToolkit-Coder/GtCoderActionDefinition.class.st @@ -0,0 +1,15 @@ +Class { + #name : #GtCoderActionDefinition, + #superclass : #Object, + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #'gt - extensions' } +GtCoderActionDefinition >> gtSourceCodeFor: aView [ + ^ aView empty +] + +{ #category : #testing } +GtCoderActionDefinition >> isDefined [ + ^ false +] diff --git a/src/GToolkit-Coder/GtCoderActionNoDefinition.class.st b/src/GToolkit-Coder/GtCoderActionNoDefinition.class.st new file mode 100644 index 000000000..d16150341 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderActionNoDefinition.class.st @@ -0,0 +1,7 @@ +Class { + #name : #GtCoderActionNoDefinition, + #superclass : #GtCoderActionDefinition, + #traits : 'TGtUniqueInstance', + #classTraits : 'TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-Coders - Addons' +} diff --git a/src/GToolkit-Coder/GtCoderActionSeparator.class.st b/src/GToolkit-Coder/GtCoderActionSeparator.class.st new file mode 100644 index 000000000..48cc4d7ba --- /dev/null +++ b/src/GToolkit-Coder/GtCoderActionSeparator.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtCoderActionSeparator, + #superclass : #GtCoderAction, + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #accessing } +GtCoderActionSeparator >> initialize [ + super initialize. + title := '-------------'. + action := [ ] +] diff --git a/src/GToolkit-Coder/GtCoderActivatableAction.class.st b/src/GToolkit-Coder/GtCoderActivatableAction.class.st index 8f056b856..0cae8b08b 100644 --- a/src/GToolkit-Coder/GtCoderActivatableAction.class.st +++ b/src/GToolkit-Coder/GtCoderActivatableAction.class.st @@ -5,7 +5,7 @@ Class { 'enabledBlock', 'updateAnnouncement' ], - #category : #'GToolkit-Coder' + #category : #'GToolkit-Coder-Coders - Addons' } { #category : #accessing } diff --git a/src/GToolkit-Coder/GtCoderAddOns.class.st b/src/GToolkit-Coder/GtCoderAddOns.class.st index eca30ee67..c5a088b3d 100644 --- a/src/GToolkit-Coder/GtCoderAddOns.class.st +++ b/src/GToolkit-Coder/GtCoderAddOns.class.st @@ -6,7 +6,9 @@ Class { 'mainActions', 'contextMenuActions', 'shortcuts', - 'updateRequested' + 'updateRequested', + 'previews', + 'contextMenuActionsSortedArray' ], #category : #'GToolkit-Coder-Coders - Addons' } @@ -16,7 +18,8 @@ GtCoderAddOns >> addAddOns: aGtCoderAddOns [ self addShortcuts: aGtCoderAddOns shortcuts. self addMainActions: aGtCoderAddOns mainActions. self addContextActions: aGtCoderAddOns contextActions. - self addContextMenuActions: aGtCoderAddOns contextMenuActions + self addContextMenuActions: aGtCoderAddOns contextMenuActions. + self addPreviews: aGtCoderAddOns previews. ] { #category : #'api - context actions' } @@ -48,14 +51,51 @@ GtCoderAddOns >> addContextAction: aTitleString icon: anIcon action: aBlock id: onlyNew: true ] +{ #category : #'api - context actions' } +GtCoderAddOns >> addContextAction: aTitleString icon: anIcon action: aBlock id: aBlElementId allowPrimaryClick: aBoolean [ + self + updateActionList: #contextActions + withAction: (GtCoderAction new + title: aTitleString; + icon: anIcon; + action: aBlock; + id: aBlElementId; + allowPrimaryClick: aBoolean) + onlyNew: true +] + { #category : #'api - context actions' } GtCoderAddOns >> addContextActions: aCollectionOfContextActions [ aCollectionOfContextActions do: [ :eachContextAction | self addContextAction: eachContextAction ] ] +{ #category : #'api - main actions' } +GtCoderAddOns >> addContextDropDownWithPreviewAction: aString icon: anIcon action: actionBlock stencil: stencilBlock [ + self + updateActionList: #contextActions + withAction: (GtCoderDropDownWithPreviewAction new + title: aString; + icon: anIcon; + changeAction: actionBlock; + changeStencil: stencilBlock) + onlyNew: true +] + +{ #category : #'api - main actions' } +GtCoderAddOns >> addContextMenu: aString icon: anIcon submenu: aBlock [ + | newAction | + newAction := GtCoderDropDownMenuAction new + title: aString; + icon: anIcon; + menu: aBlock. + self addContextMenuAction: newAction. + ^ newAction +] + { #category : #'api - context menu' } GtCoderAddOns >> addContextMenuAction: aContextMenuAction [ - contextMenuActions add: aContextMenuAction + contextMenuActions add: aContextMenuAction. + contextMenuActionsSortedArray := nil. ] { #category : #'api - context menu' } @@ -65,6 +105,7 @@ GtCoderAddOns >> addContextMenuActions: aCollectionOfContextMenuActions [ { #category : #'api - context menu' } GtCoderAddOns >> addContextMenuItem: aString action: aBlock [ + ^ self addContextMenuItem: aString hover: nil @@ -74,17 +115,123 @@ GtCoderAddOns >> addContextMenuItem: aString action: aBlock [ { #category : #'api - context menu' } GtCoderAddOns >> addContextMenuItem: aString action: aBlock id: aSymbol [ + + ^ self + addContextMenuItem: aString + hover: nil + leave: nil + action: aBlock + id: aSymbol +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString action: aBlock id: aSymbol shortcutKey: shortcutString [ + + ^ self + addContextMenuItem: aString + hover: nil + leave: nil + action: aBlock + id: aSymbol + shortcutKey: shortcutString +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString action: aBlock shortcutKey: shortcut [ + + ^ self + addContextMenuItem: aString + hover: nil + leave: nil + action: aBlock + shortcutKey: shortcut +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString group: menuGroup action: aBlock id: aSymbol [ + + ^ self + addContextMenuItem: aString + group: menuGroup + hover: nil + leave: nil + action: aBlock + id: aSymbol +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString group: menuGroup action: aBlock id: aSymbol shortcutKey: shortcutString [ + ^ self addContextMenuItem: aString + group: menuGroup hover: nil leave: nil action: aBlock id: aSymbol + shortcutKey: shortcutString +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString group: menuGroup hover: hoverBlock leave: leaveBlock action: aBlock [ + + ^ self + addContextMenuItem: aString + group: menuGroup + hover: hoverBlock + leave: leaveBlock + action: aBlock + id: nil +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString group: menuGroup hover: hoverBlock leave: leaveBlock action: aBlock id: aSymbol [ + + | newAction | + newAction := GtCoderContextMenuAction new + title: aString; + group: menuGroup; + action: aBlock; + hoverAction: hoverBlock; + leaveAction: leaveBlock; + id: aSymbol. + self addContextMenuAction: newAction. + ^ newAction +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString group: menuGroup hover: hoverBlock leave: leaveBlock action: aBlock id: aSymbol shortcutKey: shortcutString [ + + | newAction | + newAction := GtCoderContextMenuAction new + title: aString; + group: menuGroup; + action: aBlock; + hoverAction: hoverBlock; + leaveAction: leaveBlock; + id: aSymbol; + shortcutKey: shortcutString. + self addContextMenuAction: newAction. + ^ newAction +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString group: menuGroup hover: hoverBlock leave: leaveBlock action: aBlock shortcutKey: shortcutString [ + + ^ self + addContextMenuItem: aString + group: menuGroup + hover: hoverBlock + leave: leaveBlock + action: aBlock + id: nil + shortcutKey: shortcutString ] { #category : #'api - context menu' } GtCoderAddOns >> addContextMenuItem: aString hover: hoverBlock leave: leaveBlock action: aBlock [ - self + + ^ self addContextMenuItem: aString hover: hoverBlock leave: leaveBlock @@ -94,6 +241,7 @@ GtCoderAddOns >> addContextMenuItem: aString hover: hoverBlock leave: leaveBlock { #category : #'api - context menu' } GtCoderAddOns >> addContextMenuItem: aString hover: hoverBlock leave: leaveBlock action: aBlock id: aSymbol [ + | newAction | newAction := GtCoderContextMenuAction new title: aString; @@ -101,17 +249,81 @@ GtCoderAddOns >> addContextMenuItem: aString hover: hoverBlock leave: leaveBlock hoverAction: hoverBlock; leaveAction: leaveBlock; id: aSymbol. - self addContextMenuAction: newAction + self addContextMenuAction: newAction. + ^ newAction +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString hover: hoverBlock leave: leaveBlock action: aBlock id: aSymbol shortcutKey: shortcutString [ + + | newAction | + newAction := GtCoderContextMenuAction new + title: aString; + action: aBlock; + hoverAction: hoverBlock; + leaveAction: leaveBlock; + id: aSymbol; + shortcutKey: shortcutString. + self addContextMenuAction: newAction. + ^ newAction +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addContextMenuItem: aString hover: hoverBlock leave: leaveBlock action: aBlock shortcutKey: shortcutString [ + + ^ self + addContextMenuItem: aString + hover: hoverBlock + leave: leaveBlock + action: aBlock + id: nil + shortcutKey: shortcutString ] { #category : #'api - main actions' } -GtCoderAddOns >> addDropDownAction: aString icon: anIcon stencil: aBlock [ +GtCoderAddOns >> addDropDownAction: aString icon: anIcon content: aBlock [ | newAction | - newAction := GtCoderDropDownAction new + newAction := GtCoderDropDownMenuAction new title: aString; icon: anIcon; - stencil: aBlock. - mainActions add: newAction + content: aBlock. + mainActions add: newAction. + ^ newAction +] + +{ #category : #'api - main actions' } +GtCoderAddOns >> addDropDownAction: aString icon: anIcon items: aBlock [ + | newAction | + newAction := GtCoderDropDownMenuAction new + title: aString; + icon: anIcon; + items: aBlock. + mainActions add: newAction. + ^ newAction +] + +{ #category : #'api - main actions' } +GtCoderAddOns >> addDropDownAction: aString icon: anIcon menu: aBlock [ + | newAction | + newAction := GtCoderDropDownMenuAction new + title: aString; + icon: anIcon; + menu: aBlock. + mainActions add: newAction. + ^ newAction +] + +{ #category : #'api - main actions' } +GtCoderAddOns >> addDropDownAction: aString icon: anIcon stencil: aBlock [ + "Use #GtCoderAddOns>>#addDropDownAction:icon:content: instead." + + | newAction | + newAction := GtCoderDropDownAction new + title: aString; + icon: anIcon; + stencil: aBlock. + mainActions add: newAction. + ^ newAction ] { #category : #'api - main actions' } @@ -122,7 +334,32 @@ GtCoderAddOns >> addDropDownWithPreviewAction: aString icon: anIcon action: acti icon: anIcon; changeAction: actionBlock; changeStencil: stencilBlock. - mainActions add: newAction + mainActions add: newAction. + ^ newAction +] + +{ #category : #'api - main actions' } +GtCoderAddOns >> addDropDownWithPreviewAction: aString icon: anIcon action: actionBlock stencil: stencilBlock id: anId [ + | newAction | + newAction := GtCoderDropDownWithPreviewAction new + title: aString; + icon: anIcon; + changeAction: actionBlock; + changeStencil: stencilBlock; + id: anId. + mainActions add: newAction. + ^ newAction +] + +{ #category : #'api - context menu' } +GtCoderAddOns >> addExplicitContextMenu: aString block: aBlock [ + + | newAction | + newAction := GtCoderExplicitContextMenuItemAction new + title: aString; + menuItem: aBlock. + self addContextMenuAction: newAction. + ^ newAction ] { #category : #'api - main actions' } @@ -168,6 +405,37 @@ GtCoderAddOns >> addOrUpdateShortcut: aBlShortcut to: aCollection [ aCollection add: aBlShortcut ] +{ #category : #'api - previews' } +GtCoderAddOns >> addPreview: aCoderPreview [ + self previews add: aCoderPreview + +] + +{ #category : #'api - previews' } +GtCoderAddOns >> addPreview: aString stencil: aStencil [ + self addPreview: (GtCoderPreview new + id: aString; + title: aString; + stencil: [ BrFrame new fitContent ]; + dataBinder: [ :anElement :aViewModel | anElement removeChildren; addChild: aStencil asStencil asElement ]) +] + +{ #category : #'api - previews' } +GtCoderAddOns >> addPreview: aString stencil: aStencil dataBinder: aCoderPreviewDataBinder [ + self addPreview: (GtCoderPreview new + id: aString; + title: aString; + stencil: aStencil; + dataBinder: aCoderPreviewDataBinder) +] + +{ #category : #'api - previews' } +GtCoderAddOns >> addPreviews: aCollectionOfPreviews [ + + aCollectionOfPreviews do: [ :eachPreview | + self addPreview: eachPreview ] +] + { #category : #'api - shortcuts' } GtCoderAddOns >> addShortcut: aBlShortcut [ self addOrUpdateShortcut: aBlShortcut to: shortcuts @@ -185,71 +453,10 @@ GtCoderAddOns >> contextActions [ { #category : #'api - context menu' } GtCoderAddOns >> contextMenuActions [ - ^ contextMenuActions -] - -{ #category : #changes } -GtCoderAddOns >> differenceWith: aGtCoderAddOns [ - ^ Array streamContents: [ :aStream | self differenceWith: aGtCoderAddOns on: aStream ] -] - -{ #category : #changes } -GtCoderAddOns >> differenceWith: aGtCoderAddOns on: aStream [ - self - assert: [ self class = aGtCoderAddOns class ] - description: [ 'Coder AddOns type must be identical to compute the difference' ]. - - self mainActions = aGtCoderAddOns mainActions - ifFalse: [ aStream nextPut: GtCoderAddOnsMainActionsDifference new ]. - - self contextActions = aGtCoderAddOns contextActions - ifFalse: [ aStream nextPut: GtCoderAddOnsContextActionsDifference new ]. - - self contextMenuActions = aGtCoderAddOns contextMenuActions - ifFalse: [ aStream nextPut: GtCoderAddOnsContextMenuDifference new ]. - - self shortcuts = aGtCoderAddOns shortcuts - ifFalse: [ aStream nextPut: GtCoderAddOnsShortcutsDifference new ]. -] - -{ #category : #'gt-extensions' } -GtCoderAddOns >> gtViewContextActionsFor: aView [ - - ^ aView columnedList - title: 'Context actions' translated; - priority: 25; - items: [ self contextActions ]; - column: 'Title' - item: [ :anAction | anAction title ] - text: [ :title | title ifNil: [ '' ] ]; - column: 'Action' item: [ :anAction | anAction action ] -] - -{ #category : #'gt-extensions' } -GtCoderAddOns >> gtViewKeybindingsFor: aView [ - - self shortcuts ifEmpty: [ ^ aView empty ]. - ^ aView columnedList - title: 'Shortcuts' translated; - priority: 10; - items: [ self shortcuts asArray ]; - column: 'Name' item: [ :each | each name ifNil: [ each gtDisplayString ] ]; - column: 'Combination' item: [ :each | each combination gtDisplayString ] -] - -{ #category : #'gt-extensions' } -GtCoderAddOns >> gtViewMainActionsFor: aView [ - - ^ aView columnedList - title: 'Main actions' translated; - priority: 20; - items: [ self mainActions ]; - column: 'Title' - item: [ :anAction | anAction title ] - text: [ :title | title ifNil: [ '' ] ]; - column: 'Action' - item: [ :anAction | anAction action ] - weight: 3 + + ^ contextMenuActionsSortedArray ifNil: [ + contextMenuActionsSortedArray := contextMenuActions asArray + sort: [ :a | a priority ] ascending ] ] { #category : #'initialize-release' } @@ -258,7 +465,9 @@ GtCoderAddOns >> initialize [ contextActions := OrderedCollection new. mainActions := OrderedCollection new. contextMenuActions := OrderedCollection new. + contextMenuActionsSortedArray := nil. shortcuts := OrderedCollection new. + previews := OrderedCollection new. updateRequested := true ] @@ -278,7 +487,13 @@ GtCoderAddOns >> postCopy [ contextActions := contextActions copy. mainActions := mainActions copy. contextMenuActions := contextMenuActions copy. - shortcuts := shortcuts copy + shortcuts := shortcuts copy. + previews := previews copy. +] + +{ #category : #'api - previews' } +GtCoderAddOns >> previews [ + ^ previews ifNil: [ previews := OrderedCollection new ] ] { #category : #changes } diff --git a/src/GToolkit-Coder/GtCoderAddOnsContextActionsDifference.class.st b/src/GToolkit-Coder/GtCoderAddOnsContextActionsDifference.class.st deleted file mode 100644 index 8c16afb85..000000000 --- a/src/GToolkit-Coder/GtCoderAddOnsContextActionsDifference.class.st +++ /dev/null @@ -1,5 +0,0 @@ -Class { - #name : #GtCoderAddOnsContextActionsDifference, - #superclass : #GtCoderAddOnsDifference, - #category : #'GToolkit-Coder-Coders - Addons' -} diff --git a/src/GToolkit-Coder/GtCoderAddOnsContextMenuDifference.class.st b/src/GToolkit-Coder/GtCoderAddOnsContextMenuDifference.class.st deleted file mode 100644 index 45be73ab8..000000000 --- a/src/GToolkit-Coder/GtCoderAddOnsContextMenuDifference.class.st +++ /dev/null @@ -1,5 +0,0 @@ -Class { - #name : #GtCoderAddOnsContextMenuDifference, - #superclass : #GtCoderAddOnsDifference, - #category : #'GToolkit-Coder-Coders - Addons' -} diff --git a/src/GToolkit-Coder/GtCoderAddOnsDifference.class.st b/src/GToolkit-Coder/GtCoderAddOnsDifference.class.st deleted file mode 100644 index cf32e5771..000000000 --- a/src/GToolkit-Coder/GtCoderAddOnsDifference.class.st +++ /dev/null @@ -1,5 +0,0 @@ -Class { - #name : #GtCoderAddOnsDifference, - #superclass : #Object, - #category : #'GToolkit-Coder-Coders - Addons' -} diff --git a/src/GToolkit-Coder/GtCoderAddOnsMainActionsDifference.class.st b/src/GToolkit-Coder/GtCoderAddOnsMainActionsDifference.class.st deleted file mode 100644 index f374a4c7d..000000000 --- a/src/GToolkit-Coder/GtCoderAddOnsMainActionsDifference.class.st +++ /dev/null @@ -1,5 +0,0 @@ -Class { - #name : #GtCoderAddOnsMainActionsDifference, - #superclass : #GtCoderAddOnsDifference, - #category : #'GToolkit-Coder-Coders - Addons' -} diff --git a/src/GToolkit-Coder/GtCoderAddOnsShortcutsDifference.class.st b/src/GToolkit-Coder/GtCoderAddOnsShortcutsDifference.class.st deleted file mode 100644 index 3fa1cb6d8..000000000 --- a/src/GToolkit-Coder/GtCoderAddOnsShortcutsDifference.class.st +++ /dev/null @@ -1,5 +0,0 @@ -Class { - #name : #GtCoderAddOnsShortcutsDifference, - #superclass : #GtCoderAddOnsDifference, - #category : #'GToolkit-Coder-Coders - Addons' -} diff --git a/src/GToolkit-Coder/GtCoderAddOnsStylersDifference.class.st b/src/GToolkit-Coder/GtCoderAddOnsStylersDifference.class.st deleted file mode 100644 index 664d90eae..000000000 --- a/src/GToolkit-Coder/GtCoderAddOnsStylersDifference.class.st +++ /dev/null @@ -1,5 +0,0 @@ -Class { - #name : #GtCoderAddOnsStylersDifference, - #superclass : #GtCoderAddOnsDifference, - #category : #'GToolkit-Coder-Coders - Addons' -} diff --git a/src/GToolkit-Coder/GtCoderAnnouncer.class.st b/src/GToolkit-Coder/GtCoderAnnouncer.class.st index a9bef339a..8df2352c0 100644 --- a/src/GToolkit-Coder/GtCoderAnnouncer.class.st +++ b/src/GToolkit-Coder/GtCoderAnnouncer.class.st @@ -20,7 +20,7 @@ GtCoderAnnouncer >> announce: anAnnouncement [ GtCoderAnnouncer >> initialize [ super initialize. - monitor := Monitor new. + monitor := Mutex new. suppressedAnnouncements := #() ] @@ -29,6 +29,6 @@ GtCoderAnnouncer >> suppress: aCollectionOfAnnouncementClasses during: aBlock [ monitor critical: [ | aPreviousSuppressedAnnouncements | aPreviousSuppressedAnnouncements := suppressedAnnouncements copy. - suppressedAnnouncements := (suppressedAnnouncements, aCollectionOfAnnouncementClasses asCollection) asSet. + suppressedAnnouncements := (suppressedAnnouncements, aCollectionOfAnnouncementClasses asOrderedCollection) asSet. aBlock ensure: [ suppressedAnnouncements := aPreviousSuppressedAnnouncements ] ] ] diff --git a/src/GToolkit-Coder/GtCoderAst.class.st b/src/GToolkit-Coder/GtCoderAst.class.st new file mode 100644 index 000000000..2435aa6cf --- /dev/null +++ b/src/GToolkit-Coder/GtCoderAst.class.st @@ -0,0 +1,46 @@ +Class { + #name : #GtCoderAst, + #superclass : #Object, + #instVars : [ + 'ast', + 'epoch', + 'sourceString' + ], + #category : #'GToolkit-Coder-Ast' +} + +{ #category : #accessing } +GtCoderAst >> ast [ + + ^ ast +] + +{ #category : #accessing } +GtCoderAst >> ast: anObject [ + + ast := anObject +] + +{ #category : #accessing } +GtCoderAst >> epoch [ + + ^ epoch +] + +{ #category : #accessing } +GtCoderAst >> epoch: anObject [ + + epoch := anObject +] + +{ #category : #accessing } +GtCoderAst >> sourceString [ + + + ^ sourceString +] + +{ #category : #accessing } +GtCoderAst >> sourceString: aGtCoderSourceString [ + sourceString := aGtCoderSourceString +] diff --git a/src/GToolkit-Coder/GtCoderAstCompositeStyler.class.st b/src/GToolkit-Coder/GtCoderAstCompositeStyler.class.st index 0166d19a7..c3c4dde9a 100644 --- a/src/GToolkit-Coder/GtCoderAstCompositeStyler.class.st +++ b/src/GToolkit-Coder/GtCoderAstCompositeStyler.class.st @@ -7,25 +7,42 @@ Class { #category : #'GToolkit-Coder-Styler/Highlighter' } +{ #category : #'as yet unclassified' } +GtCoderAstCompositeStyler >> = anObject [ + ^self class == anObject class and: [self stylers = anObject stylers] +] + { #category : #'api - styling' } GtCoderAstCompositeStyler >> extraStyle: aText ast: theAst [ self stylers do: [ :eachStyler | BlTextStylerTelemetry - timeSync: [ 'Extra style using ', eachStyler class name ] + time: [ 'Extra style using ', eachStyler class name ] during: [ eachStyler extraStyle: aText ast: theAst ] ] ] +{ #category : #'as yet unclassified' } +GtCoderAstCompositeStyler >> hash [ + ^ self class hash xor: self stylers hash +] + { #category : #'api - styling' } GtCoderAstCompositeStyler >> style: aText ast: theAst [ self stylers do: [ :eachStyler | [ + GtCoderAstStyler telemetryComputationStartSignal: eachStyler. + BlTextStylerTelemetry - timeSync: [ 'Style using ', eachStyler class name ] - during: [ eachStyler style: aText ast: theAst ] ] + time: [ 'Style using ', eachStyler class name ] + during: [ eachStyler style: aText ast: theAst ]. + + GtCoderAstStyler telemetryComputationEndSignal: eachStyler. + ] on: Error do: [ :anError | + GtCoderAstStyler telemetryComputationEndSignal: eachStyler. + BlTextStylerTelemetry - timeSync: [ 'Handle an error in ', eachStyler class name ] + time: [ 'Handle an error in ', eachStyler class name ] during: [ anError emit. diff --git a/src/GToolkit-Coder/GtCoderAstSmaCCParserStyler.class.st b/src/GToolkit-Coder/GtCoderAstSmaCCParserStyler.class.st index 28449fdd6..a8e76fe2f 100644 --- a/src/GToolkit-Coder/GtCoderAstSmaCCParserStyler.class.st +++ b/src/GToolkit-Coder/GtCoderAstSmaCCParserStyler.class.st @@ -19,5 +19,5 @@ GtCoderAstSmaCCParserStyler >> smaccStyler: anObject [ { #category : #'api - styling' } GtCoderAstSmaCCParserStyler >> style: aText ast: theAst [ - self smaccStyler style: aText using: theAst + self smaccStyler style: aText ast: theAst ] diff --git a/src/GToolkit-Coder/GtCoderAstStyler.class.st b/src/GToolkit-Coder/GtCoderAstStyler.class.st index 1e77a1744..f061c7bae 100644 --- a/src/GToolkit-Coder/GtCoderAstStyler.class.st +++ b/src/GToolkit-Coder/GtCoderAstStyler.class.st @@ -7,6 +7,40 @@ Class { #category : #'GToolkit-Coder-Styler/Highlighter' } +{ #category : #'as yet unclassified' } +GtCoderAstStyler class >> primitiveTelemetryComputationSignal: isStart object: anObject process: aProcess [ + +] + +{ #category : #'as yet unclassified' } +GtCoderAstStyler class >> telemetryComputationEndSignal: anObject [ + self + primitiveTelemetryComputationSignal: false + object: anObject + process: Processor activeProcess +] + +{ #category : #'as yet unclassified' } +GtCoderAstStyler class >> telemetryComputationStartSignal: anObject [ + self + primitiveTelemetryComputationSignal: true + object: anObject + process: Processor activeProcess +] + +{ #category : #comparing } +GtCoderAstStyler >> = anObject [ + self class == anObject class ifFalse: [^false]. + (self coderViewModel isNil or: [anObject coderViewModel isNil]) ifTrue: [^true]. + ^self coderViewModel = anObject coderViewModel +] + +{ #category : #'as yet unclassified' } +GtCoderAstStyler >> anyCursorsInRange: textRange [ + ^ self coderViewModel cursors positions + anySatisfy: [ :each | textRange includes: each ] +] + { #category : #'api - styling' } GtCoderAstStyler >> attribute: aTextAttribute from: aStart to: anEnd [ self attributes: { aTextAttribute } from: aStart to: anEnd @@ -49,12 +83,39 @@ GtCoderAstStyler >> coderViewModel: anObject [ GtCoderAstStyler >> extraStyle: aText ast: theAst [ ] +{ #category : #'api - styling' } +GtCoderAstStyler >> from: aStart to: anEnd [ + ^ [ text from: aStart to: anEnd ] + on: Error + do: [ :anError | + anError emit. + + NonInteractiveTranscript stdout + nextPutAll: '['; + nextPutAll: self class name; + nextPutAll: '] '; + nextPutAll: 'Was not able to get subtext from '; + print: aStart; + nextPutAll: ' to '; + print: anEnd; + nextPutAll: ' on text of size '; + print: text size; + cr. + + '' asRopedText ]. +] + +{ #category : #comparing } +GtCoderAstStyler >> hash [ + ^self class hash +] + { #category : #private } GtCoderAstStyler >> privateStyle: aText [ | theAst | "Styling happens in a non-UI process, therefore it is fine to wait until AST is computed in a blocking way" - theAst := self coderViewModel coderModel astAwait. + theAst := self coderViewModel coderModel astSync. theAst ifNil: [ ^ aText ]. diff --git a/src/GToolkit-Coder/GtCoderBehaviorChanged.class.st b/src/GToolkit-Coder/GtCoderBehaviorChanged.class.st new file mode 100644 index 000000000..53ac9ef33 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderBehaviorChanged.class.st @@ -0,0 +1,19 @@ +Class { + #name : #GtCoderBehaviorChanged, + #superclass : #GtSourceCoderAnnouncement, + #instVars : [ + 'newBehavior' + ], + #category : #'GToolkit-Coder-Event' +} + +{ #category : #accessing } +GtCoderBehaviorChanged >> newBehavior [ + + ^ newBehavior +] + +{ #category : #accessing } +GtCoderBehaviorChanged >> newBehavior: aBehavior [ + newBehavior := aBehavior +] diff --git a/src/GToolkit-Coder/GtCoderClassAdded.class.st b/src/GToolkit-Coder/GtCoderClassAdded.class.st index 91a1e2180..dbe92b6f9 100644 --- a/src/GToolkit-Coder/GtCoderClassAdded.class.st +++ b/src/GToolkit-Coder/GtCoderClassAdded.class.st @@ -1,5 +1,5 @@ Class { #name : #GtCoderClassAdded, - #superclass : #GtCoderAnnouncement, + #superclass : #GtCoderClassChanged, #category : #'GToolkit-Coder-Event' } diff --git a/src/GToolkit-Coder/GtCoderRequestFocus.class.st b/src/GToolkit-Coder/GtCoderClassChanged.class.st similarity index 73% rename from src/GToolkit-Coder/GtCoderRequestFocus.class.st rename to src/GToolkit-Coder/GtCoderClassChanged.class.st index efb23f800..dc2ebbfd8 100644 --- a/src/GToolkit-Coder/GtCoderRequestFocus.class.st +++ b/src/GToolkit-Coder/GtCoderClassChanged.class.st @@ -1,5 +1,5 @@ Class { - #name : #GtCoderRequestFocus, + #name : #GtCoderClassChanged, #superclass : #GtCoderAnnouncement, #category : #'GToolkit-Coder-Event' } diff --git a/src/GToolkit-Coder/GtCoderClassRemoved.class.st b/src/GToolkit-Coder/GtCoderClassRemoved.class.st index 3adf9d27b..17008d57a 100644 --- a/src/GToolkit-Coder/GtCoderClassRemoved.class.st +++ b/src/GToolkit-Coder/GtCoderClassRemoved.class.st @@ -1,5 +1,5 @@ Class { #name : #GtCoderClassRemoved, - #superclass : #GtCoderAnnouncement, + #superclass : #GtCoderClassChanged, #category : #'GToolkit-Coder-Event' } diff --git a/src/GToolkit-Coder/GtCoderClassRenamed.class.st b/src/GToolkit-Coder/GtCoderClassRenamed.class.st index 86f32e793..7aee3bfd0 100644 --- a/src/GToolkit-Coder/GtCoderClassRenamed.class.st +++ b/src/GToolkit-Coder/GtCoderClassRenamed.class.st @@ -1,5 +1,5 @@ Class { #name : #GtCoderClassRenamed, - #superclass : #GtCoderAnnouncement, + #superclass : #GtCoderClassChanged, #category : #'GToolkit-Coder-Event' } diff --git a/src/GToolkit-Coder/GtCoderClassWithPackageCompletionStrategy.class.st b/src/GToolkit-Coder/GtCoderClassWithPackageCompletionStrategy.class.st index 13f8f5997..83af64e0d 100644 --- a/src/GToolkit-Coder/GtCoderClassWithPackageCompletionStrategy.class.st +++ b/src/GToolkit-Coder/GtCoderClassWithPackageCompletionStrategy.class.st @@ -1,6 +1,6 @@ Class { #name : #GtCoderClassWithPackageCompletionStrategy, - #superclass : #GtCompletionStrategy, + #superclass : #GtStreamedCompletionStrategy, #instVars : [ 'allPackages', 'allClasses' @@ -17,7 +17,7 @@ GtCoderClassWithPackageCompletionStrategy >> allClasses [ { #category : #accessing } GtCoderClassWithPackageCompletionStrategy >> allPackages [ ^ allPackages ifNil: [ - allPackages := GtPrefixTree withAll: RPackage organizer packageNames ] + allPackages := GtPrefixTree withAll: self packageOrganizer packageNames ] ] { #category : #accessing } @@ -26,45 +26,41 @@ GtCoderClassWithPackageCompletionStrategy >> classesIn: aPackage [ ] { #category : #accessing } -GtCoderClassWithPackageCompletionStrategy >> completionActionsFor: aText at: positionInteger max: maxInteger [ - | currentInput completionActions delimitedIndex | +GtCoderClassWithPackageCompletionStrategy >> completionActionStreamFor: aText at: positionInteger requested: aBoolean [ + | currentInput delimitedIndex | currentInput := aText asString. delimitedIndex := currentInput indexOf: $>. - delimitedIndex > 0 ifTrue: [ - | extractedPackageName partialClassName package | - extractedPackageName := currentInput copyFrom: 1 to: delimitedIndex - 1. - partialClassName := currentInput copyFrom: delimitedIndex + 1 to: currentInput size. - package := RPackage organizer - packageNamed: extractedPackageName - ifAbsent: [ ^ #() ]. - ^ ((self classesIn: package) first: maxInteger startingWith: partialClassName) collect: [ :className | - GtInsertTextCompletionAction - labeled: className - completion: (className allButFirst: partialClassName size) - position: positionInteger - from: positionInteger - partialClassName size + 1 ] ]. - completionActions := (self allClasses first: (maxInteger/2) asInteger startingWith: currentInput) collect: [ :className | - GtInsertTextCompletionAction - labeled: className - completion: (className allButFirst: currentInput size) - position: positionInteger - from: positionInteger - currentInput size + 1 ]. - completionActions addAll: ((self allPackages first: (maxInteger/2) asInteger startingWith: currentInput) collect: [ :packageName | - GtInsertTextCompletionAction - labeled: packageName - completion: (packageName allButFirst: currentInput size) , '>' - position: positionInteger - from: positionInteger - currentInput size + 1 ]). - - ^ completionActions - "(self class findPackageTagsMatching: completionString) do: [ :each | - completionActions add: (GtReplaceTextCompletionAction forText: each categoryName) ]." - -] - -{ #category : #testing } -GtCoderClassWithPackageCompletionStrategy >> hasCompletionEntryFor: aString [ - ^ true + delimitedIndex > 0 + ifTrue: + [ | extractedPackageName partialClassName package | + extractedPackageName := currentInput copyFrom: 1 to: delimitedIndex - 1. + partialClassName := currentInput copyFrom: delimitedIndex + 1 to: currentInput size. + package := self packageOrganizer packageNamed: extractedPackageName ifAbsent: [ ^ #() ]. + ^ ((self classesIn: package) asyncStreamStartingWith: partialClassName) + collect: + [ :className | + GtInsertTextCompletionAction + labeled: (self labelFor: className withSearch: partialClassName) + completion: (className allButFirst: partialClassName size) + position: positionInteger + from: positionInteger - partialClassName size + 1 ] ]. + ^ ((self allClasses asyncStreamStartingWith: currentInput) + collect: + [ :className | + GtInsertTextCompletionAction + labeled: (self labelFor: className withSearch: currentInput) + completion: (className allButFirst: currentInput size) + position: positionInteger + from: positionInteger - currentInput size + 1 ]) + merge: + ((self allPackages asyncStreamStartingWith: currentInput) + collect: + [ :packageName | + GtInsertTextCompletionAction + labeled: (self labelFor: packageName withSearch: currentInput) + completion: (packageName allButFirst: currentInput size) , '>' + position: positionInteger + from: positionInteger - currentInput size + 1 ]) ] { #category : #testing } diff --git a/src/GToolkit-Coder/GtCoderClassesHierarchyTree.class.st b/src/GToolkit-Coder/GtCoderClassesHierarchyTree.class.st index 9f6e51e19..47a61889a 100644 --- a/src/GToolkit-Coder/GtCoderClassesHierarchyTree.class.st +++ b/src/GToolkit-Coder/GtCoderClassesHierarchyTree.class.st @@ -10,25 +10,29 @@ Class { #superclass : #Object, #instVars : [ 'rootClass', - 'subclassTrees' + 'subclassTrees', + 'rootClassName' ], #category : #'GToolkit-Coder-Navigation - Model' } { #category : #factory } GtCoderClassesHierarchyTree class >> fromClasses: aCollectionOfClasses [ - | aRootItem theSetOfClasses theRootTrees theSubclassTrees | - + + | aRootItem theSetOfClasses classToTree | aRootItem := self new. - theSetOfClasses := aCollectionOfClasses asSet. - - theRootTrees := theSetOfClasses - reject: [ :eachClass | theSetOfClasses anySatisfy: [ :eachSuperclass | eachClass inheritsFrom: eachSuperclass ] ] - thenCollect: [ :eachRootClass | eachRootClass -> (theSetOfClasses select: [ :eachClass | eachClass inheritsFrom: eachRootClass ]) ]. - - theSubclassTrees := theRootTrees collect: [ :eachAssoc | (self fromClasses: eachAssoc value) rootClass: eachAssoc key ] as: Array. - aRootItem subclassTrees: theSubclassTrees. + classToTree := Dictionary new. + theSetOfClasses do: [ :each | + classToTree + at: each + put: (self new rootClass: each) ]. + theSetOfClasses do: [ :each | + classToTree + at: each superclass + ifPresent: [ :superTree | + superTree subclassTrees add: (classToTree at: each) ] + ifAbsent: [ aRootItem subclassTrees add: (classToTree at: each) ] ]. ^ aRootItem ] @@ -47,6 +51,14 @@ GtCoderClassesHierarchyTree class >> hierarchyForClass: aClass [ ^ aRootNode. ] +{ #category : #comparing } +GtCoderClassesHierarchyTree >> = anObject [ + ^ anObject class = self class + and: [ self rootClass = anObject rootClass + and: [ self rootClassName = anObject rootClassName + and: [ self subclassTrees = anObject subclassTrees ] ] ] +] + { #category : #accessing } GtCoderClassesHierarchyTree >> at: anIndex [ @@ -61,6 +73,14 @@ GtCoderClassesHierarchyTree >> classes [ ^ self subclassTrees collect: [ :eachTree | eachTree rootClass ] ] +{ #category : #testing } +GtCoderClassesHierarchyTree >> containsClass: aClass [ + rootClass = aClass + ifTrue: [ ^ true ]. + + ^ self subclassTrees anySatisfy: [ :eachTree | eachTree containsClass: aClass ] +] + { #category : #accessing } GtCoderClassesHierarchyTree >> first [ @@ -87,10 +107,15 @@ GtCoderClassesHierarchyTree >> hasRootClass [ ^ rootClass isNotNil ] +{ #category : #comparing } +GtCoderClassesHierarchyTree >> hash [ + ^ self rootClass hash bitXor: self subclassTrees hash +] + { #category : #initialization } GtCoderClassesHierarchyTree >> initialize [ super initialize. - subclassTrees := Array empty. + subclassTrees := OrderedCollection new ] { #category : #printing } @@ -116,7 +141,13 @@ GtCoderClassesHierarchyTree >> rootClass [ { #category : #accessing } GtCoderClassesHierarchyTree >> rootClass: aClass [ - rootClass := aClass + rootClass := aClass. + rootClassName := rootClass name +] + +{ #category : #accessing } +GtCoderClassesHierarchyTree >> rootClassName [ + ^ rootClassName ] { #category : #accessing } diff --git a/src/GToolkit-Coder/GtCoderCommentStyler.class.st b/src/GToolkit-Coder/GtCoderCommentStyler.class.st deleted file mode 100644 index 4b1bacdbd..000000000 --- a/src/GToolkit-Coder/GtCoderCommentStyler.class.st +++ /dev/null @@ -1,27 +0,0 @@ -Class { - #name : #GtCoderCommentStyler, - #superclass : #GtRBASTStyler, - #traits : 'TRBProgramNodeVisitor', - #classTraits : 'TRBProgramNodeVisitor classTrait', - #category : #'GToolkit-Coder-Styler/Highlighter' -} - -{ #category : #visiting } -GtCoderCommentStyler >> visitNode: aNode [ - | comments | - comments := aNode comments. - comments notEmpty - ifTrue: [ comments - do: [ :each | - | comment document | - "Hack to make sure we have the same indices for the part we are formatting" - comment := ((String new: each start withAll: Character cr) , each contents) asRopedText. - (text from: each start + 1 to: comment size) - attributes: {(GtCompletionStrategyAttribute new strategy: GtDocumentCompletionStrategy new)}. - document := GtDocumenter new. - document editor useSyncStylerStrategy. - document text: comment. - comment := document text. - each start + 1 to: comment size do: [ :i | (text from: i to: i) attributes: (comment attributesAt: i) ] ] ]. - ^ super visitNode: aNode -] diff --git a/src/GToolkit-Coder/GtCoderCompiledMethodSourceCode.class.st b/src/GToolkit-Coder/GtCoderCompiledMethodSourceCode.class.st index 2e5d3a0c7..6fbc82923 100644 --- a/src/GToolkit-Coder/GtCoderCompiledMethodSourceCode.class.st +++ b/src/GToolkit-Coder/GtCoderCompiledMethodSourceCode.class.st @@ -5,43 +5,11 @@ I represent a source code of a method defined by a class and a selector " Class { #name : #GtCoderCompiledMethodSourceCode, - #superclass : #GtCoderSourceCode, - #instVars : [ - 'compiledMethod' - ], - #category : #'GToolkit-Coder-Coders' + #superclass : #GtCoderCompiledMethodTextSource, + #category : #'GToolkit-Coder-Coders - Source Text' } -{ #category : #'api - accessing' } -GtCoderCompiledMethodSourceCode >> asAstCacheKey [ - ^ compiledMethod -] - -{ #category : #initialization } -GtCoderCompiledMethodSourceCode >> buildCollapsedText [ - - - ^ compiledMethod selector asRopedText -] - -{ #category : #initialization } -GtCoderCompiledMethodSourceCode >> buildSourceText [ - - - ^ compiledMethod sourceCode asRopedText -] - -{ #category : #accessing } -GtCoderCompiledMethodSourceCode >> compiledMethod [ - ^ compiledMethod -] - -{ #category : #accessing } -GtCoderCompiledMethodSourceCode >> compiledMethod: aCompiledMethod [ - compiledMethod := aCompiledMethod -] - { #category : #testing } -GtCoderCompiledMethodSourceCode >> isModified [ - ^ compiledMethod sourceCode ~= self sourceString +GtCoderCompiledMethodSourceCode class >> isDeprecated [ + ^ true ] diff --git a/src/GToolkit-Coder/GtCoderCompiledMethodTextSource.class.st b/src/GToolkit-Coder/GtCoderCompiledMethodTextSource.class.st new file mode 100644 index 000000000..7f542c421 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderCompiledMethodTextSource.class.st @@ -0,0 +1,30 @@ +Class { + #name : #GtCoderCompiledMethodTextSource, + #superclass : #GtCoderTextSource, + #instVars : [ + 'compiledMethod' + ], + #category : #'GToolkit-Coder-Coders - Source Text' +} + +{ #category : #accessing } +GtCoderCompiledMethodTextSource >> compiledMethod [ + + ^ compiledMethod +] + +{ #category : #accessing } +GtCoderCompiledMethodTextSource >> compiledMethod: anObject [ + + self critical: [ + compiledMethod := anObject. + originalSourceText := nil ] +] + +{ #category : #initialization } +GtCoderCompiledMethodTextSource >> computeOriginalSourceText [ + + ^ (compiledMethod isBigMethod + ifTrue: [ (compiledMethod limitedSourceCode), '...' ] + ifFalse: [ compiledMethod sourceCode ]) asRopedText +] diff --git a/src/GToolkit-Coder/GtCoderContextMenuAction.class.st b/src/GToolkit-Coder/GtCoderContextMenuAction.class.st index 0607d62c1..1a2346df8 100644 --- a/src/GToolkit-Coder/GtCoderContextMenuAction.class.st +++ b/src/GToolkit-Coder/GtCoderContextMenuAction.class.st @@ -3,11 +3,23 @@ Class { #superclass : #GtCoderAction, #instVars : [ 'hoverAction', - 'leaveAction' + 'leaveAction', + 'group' ], - #category : #'GToolkit-Coder' + #category : #'GToolkit-Coder-Coders - Addons' } +{ #category : #accessing } +GtCoderContextMenuAction >> group [ + + ^ group +] + +{ #category : #accessing } +GtCoderContextMenuAction >> group: aMenuItemGroupConfiguration [ + group := aMenuItemGroupConfiguration +] + { #category : #accessing } GtCoderContextMenuAction >> hoverAction [ ^ hoverAction diff --git a/src/GToolkit-Coder/GtCoderContextVariableStyler.class.st b/src/GToolkit-Coder/GtCoderContextVariableStyler.class.st deleted file mode 100644 index 7c34f8f10..000000000 --- a/src/GToolkit-Coder/GtCoderContextVariableStyler.class.st +++ /dev/null @@ -1,86 +0,0 @@ -Class { - #name : #GtCoderContextVariableStyler, - #superclass : #GtRBASTStyler, - #traits : 'TRBProgramNodeVisitor', - #classTraits : 'TRBProgramNodeVisitor classTrait', - #instVars : [ - 'methodCoder', - 'variables', - 'interval' - ], - #category : #'GToolkit-Coder-Styler/Highlighter' -} - -{ #category : #testing } -GtCoderContextVariableStyler >> hasValueForVariableNamed: aVariableName [ - ^ self - valueForVariableNamed: aVariableName - ifPresent: [ :value | true ] - ifAbsent: [ false ] -] - -{ #category : #initialization } -GtCoderContextVariableStyler >> initializeVariables [ - variables notNil - ifTrue: [ ^ self ]. - variables := Dictionary new. - methodCoder instanceVariableNodesDo: [ :aVariableNode | - variables at: aVariableNode key put: aVariableNode ]. - methodCoder temporaryVariableNodesDo: [ :aVariableNode | - variables at: aVariableNode key put: aVariableNode ]. - interval := methodCoder session pcRangeForContext: methodCoder context -] - -{ #category : #accessing } -GtCoderContextVariableStyler >> methodCoder [ - ^ methodCoder -] - -{ #category : #accessing } -GtCoderContextVariableStyler >> methodCoder: aMethodCoder [ - methodCoder := aMethodCoder -] - -{ #category : #styling } -GtCoderContextVariableStyler >> style: aText ast: ast [ - aText clearAttributes: [ :each | each isKindOf: GtPlaygroundEvaluatedCodeButtonAttribute ]. - self initializeVariables. - super style: aText ast: ast -] - -{ #category : #styling } -GtCoderContextVariableStyler >> styleVariableNode: aVariableNode [ - self subclassResponsibility -] - -{ #category : #accessing } -GtCoderContextVariableStyler >> valueForVariableNamed: aVariableName ifPresent: presentBlock [ - ^ self - valueForVariableNamed: aVariableName - ifPresent: presentBlock - ifAbsent: [ ^ self ] -] - -{ #category : #accessing } -GtCoderContextVariableStyler >> valueForVariableNamed: aVariableName ifPresent: presentBlock ifAbsent: absentBloc [ - ^ variables - at: aVariableName - ifPresent: [ :variableNode | - presentBlock value: variableNode value ] - ifAbsent: absentBloc -] - -{ #category : #visiting } -GtCoderContextVariableStyler >> visitArgumentNode: anArgumentNode [ - self styleVariableNode: anArgumentNode -] - -{ #category : #visiting } -GtCoderContextVariableStyler >> visitInstanceVariableNode: aSelfNode [ - self styleVariableNode: aSelfNode -] - -{ #category : #visiting } -GtCoderContextVariableStyler >> visitTemporaryNode: aNode [ - self styleVariableNode: aNode -] diff --git a/src/GToolkit-Coder/GtCoderCouldNotHandleEvalutionError.class.st b/src/GToolkit-Coder/GtCoderCouldNotHandleEvalutionError.class.st new file mode 100644 index 000000000..fb80abca2 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderCouldNotHandleEvalutionError.class.st @@ -0,0 +1,21 @@ +" +I am used to indicate that {{gtClass:GtCoderEvaluationUnhandledError}} error cannot be handled a standard way, e.g., opening a debugger window. When we simulate UI interactions using {{gtClass:BlScripter}}, we cannot open a debugger window as it suspends the Scripter's UI process and an example evaluator waits indefinitely for the example to finish. +" +Class { + #name : #GtCoderCouldNotHandleEvalutionError, + #superclass : #Error, + #instVars : [ + 'unhandledError' + ], + #category : #'GToolkit-Coder-Coders' +} + +{ #category : #accessing } +GtCoderCouldNotHandleEvalutionError >> unhandledError [ + ^ unhandledError +] + +{ #category : #accessing } +GtCoderCouldNotHandleEvalutionError >> unhandledError: anObject [ + unhandledError := anObject +] diff --git a/src/GToolkit-Coder/GtCoderCustomAction.class.st b/src/GToolkit-Coder/GtCoderCustomAction.class.st new file mode 100644 index 000000000..54b537fa5 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderCustomAction.class.st @@ -0,0 +1,27 @@ +Class { + #name : #GtCoderCustomAction, + #superclass : #GtCoderAction, + #instVars : [ + 'stencil' + ], + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #initialization } +GtCoderCustomAction >> computeActionDefinition [ + self stencil isClosure + ifTrue: [ ^ GtCoderActionClosureDefinition new closure: self stencil ]. + ^ nil +] + +{ #category : #accessing } +GtCoderCustomAction >> stencil [ + + ^ stencil +] + +{ #category : #accessing } +GtCoderCustomAction >> stencil: anObject [ + + stencil := anObject +] diff --git a/src/GToolkit-Coder/GtCoderDropDownAction.class.st b/src/GToolkit-Coder/GtCoderDropDownAction.class.st index 1885a4caf..b4c9399f5 100644 --- a/src/GToolkit-Coder/GtCoderDropDownAction.class.st +++ b/src/GToolkit-Coder/GtCoderDropDownAction.class.st @@ -1,12 +1,22 @@ +" +Use {{gtClass:GtCoderDropDownMenuAction}} instead. +" Class { #name : #GtCoderDropDownAction, #superclass : #GtCoderAction, #instVars : [ 'stencil' ], - #category : #'GToolkit-Coder' + #category : #'GToolkit-Coder-Coders - Addons' } +{ #category : #initialization } +GtCoderDropDownAction >> computeActionDefinition [ + self stencil isClosure + ifTrue: [ ^ GtCoderActionClosureDefinition new closure: self stencil ]. + ^ nil +] + { #category : #accessing } GtCoderDropDownAction >> stencil [ ^ stencil diff --git a/src/GToolkit-Coder/GtCoderDropDownMenuAction.class.st b/src/GToolkit-Coder/GtCoderDropDownMenuAction.class.st new file mode 100644 index 000000000..623ec40dd --- /dev/null +++ b/src/GToolkit-Coder/GtCoderDropDownMenuAction.class.st @@ -0,0 +1,55 @@ +Class { + #name : #GtCoderDropDownMenuAction, + #superclass : #GtCoderAction, + #instVars : [ + 'group', + 'menuBlock' + ], + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #initialization } +GtCoderDropDownMenuAction >> computeActionDefinition [ + self menuBlock isClosure + ifTrue: [ ^ GtCoderActionClosureDefinition new closure: self menuBlock ]. + ^ nil +] + +{ #category : #accessing } +GtCoderDropDownMenuAction >> content: aBlock [ + self actionDefinitionFromBlock: aBlock. + self menu: [ BrMenuExplicit new stencil: aBlock ] +] + +{ #category : #accessing } +GtCoderDropDownMenuAction >> group [ + + ^ group +] + +{ #category : #accessing } +GtCoderDropDownMenuAction >> group: aMenuItemGroupConfiguration [ + group := aMenuItemGroupConfiguration +] + +{ #category : #accessing } +GtCoderDropDownMenuAction >> items: aBlock [ + self actionDefinitionFromBlock: aBlock. + self + menu: [ BrMenuItems new + in: [ :theMenuItems | aBlock cull: theMenuItems ]; + yourself ] +] + +{ #category : #accessing } +GtCoderDropDownMenuAction >> menu: aBlock [ + "The block can have two arguments: [ :aTextualCoderViewModel :aTextualCoderEditorElement | ]. + The block returns BrMenu." + + menuBlock := aBlock +] + +{ #category : #accessing } +GtCoderDropDownMenuAction >> menuBlock [ + ^ menuBlock +] diff --git a/src/GToolkit-Coder/GtCoderDropDownWithPreviewAction.class.st b/src/GToolkit-Coder/GtCoderDropDownWithPreviewAction.class.st index ed2030f91..b9322aacd 100644 --- a/src/GToolkit-Coder/GtCoderDropDownWithPreviewAction.class.st +++ b/src/GToolkit-Coder/GtCoderDropDownWithPreviewAction.class.st @@ -5,7 +5,7 @@ Class { 'changeAction', 'changeStencil' ], - #category : #'GToolkit-Coder-GToolkit-Coder' + #category : #'GToolkit-Coder-Coders - Addons' } { #category : #accessing } @@ -27,3 +27,12 @@ GtCoderDropDownWithPreviewAction >> changeStencil [ GtCoderDropDownWithPreviewAction >> changeStencil: aBlock [ changeStencil := aBlock ] + +{ #category : #initialization } +GtCoderDropDownWithPreviewAction >> computeActionDefinition [ + self changeAction isClosure + ifTrue: [ ^ GtCoderActionClosureDefinition new closure: self changeAction ]. + self changeStencil isClosure + ifTrue: [ ^ GtCoderActionClosureDefinition new closure: self changeStencil ]. + ^ nil +] diff --git a/src/GToolkit-Coder/GtCoderEvaluationAnnouncement.class.st b/src/GToolkit-Coder/GtCoderEvaluationAnnouncement.class.st index e6782bd12..af726c8e0 100644 --- a/src/GToolkit-Coder/GtCoderEvaluationAnnouncement.class.st +++ b/src/GToolkit-Coder/GtCoderEvaluationAnnouncement.class.st @@ -19,21 +19,8 @@ GtCoderEvaluationAnnouncement >> evaluationResult: aGtSourceCoderEvaluationResul evaluationResult := aGtSourceCoderEvaluationResult ] -{ #category : #testing } -GtCoderEvaluationAnnouncement >> isRequestedByElementOrItsChild: anElement [ - "Return true if the evaluation ws requested by a given visual element or any of its children" - - - self requesterObject == anElement - ifTrue: [ ^ true ]. - - (self requesterObject isKindOf: BlElement) - ifFalse: [ ^ false ]. - - ^ self requesterObject hasParent: anElement -] - { #category : #accessing } GtCoderEvaluationAnnouncement >> requesterObject [ + ^ self evaluationResult requesterObject ] diff --git a/src/GToolkit-Coder/GtCoderEvaluationUnhandledError.class.st b/src/GToolkit-Coder/GtCoderEvaluationUnhandledError.class.st index 76718d91f..553b26e8f 100644 --- a/src/GToolkit-Coder/GtCoderEvaluationUnhandledError.class.st +++ b/src/GToolkit-Coder/GtCoderEvaluationUnhandledError.class.st @@ -4,7 +4,9 @@ Class { #instVars : [ 'sourceCoder', 'sourceString', - 'sourceInterval' + 'sourceInterval', + 'requesterObject', + 'evaluatedCode' ], #category : #'GToolkit-Coder-Coders' } @@ -16,20 +18,62 @@ GtCoderEvaluationUnhandledError >> debug [ UIManager default gtDebugProcess: Processor activeProcess context: self exception signalerContext - withEmbeddedDebuggerIn: sourceCoder + withEmbeddedDebuggerIn: requesterObject withSourceString: self sourceString withSourceInterval: self sourceInterval forException: self exception + evaluationInfo: (GtCoderEvaluationUnhandledErrorInformation new + requesterObject: self requesterObject; + evaluatedCode: self evaluatedCode; + sourceString: self sourceString; + sourceInterval: self sourceInterval; + sourceCoder: self sourceCoder) ] { #category : #'priv handling' } GtCoderEvaluationUnhandledError >> defaultAction [ + "We do not want to handle this exception if executed within a scripter. + The reason is that we do not want to open an embedded debuger as it stops + the Scripter's UI process and creates a new one. As a consequence, + an example execution is stopped and example runners (Jenkins, CI) is waiting + indefinitely." + - ^ UIManager default unhandledErrorDefaultAction: self + ^ BlDevScripterStepExecutionIndicator + ifScripter: [ GtCoderCouldNotHandleEvalutionError new + unhandledError: self; + signal ] + ifNone: [ UIManager default unhandledErrorDefaultAction: self ] +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledError >> evaluatedCode [ + + ^ evaluatedCode ifNil: [ + evaluatedCode := GtSourceCoderNoEvaluatedCode default ] +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledError >> evaluatedCode: aSourceCode [ + evaluatedCode := aSourceCode +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledError >> requesterObject [ + "Return an object that requested evaluation." + + + ^ requesterObject +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledError >> requesterObject: anObject [ + requesterObject := anObject ] { #category : #accessing } GtCoderEvaluationUnhandledError >> sourceCoder [ + ^ sourceCoder ] diff --git a/src/GToolkit-Coder/GtCoderEvaluationUnhandledErrorInformation.class.st b/src/GToolkit-Coder/GtCoderEvaluationUnhandledErrorInformation.class.st new file mode 100644 index 000000000..701e25a44 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderEvaluationUnhandledErrorInformation.class.st @@ -0,0 +1,71 @@ +" +I hold extra information that is passed by {{gtClass:GtCoderEvaluationUnhandledError}} to handle (debug) unhandled exceptions. +" +Class { + #name : #GtCoderEvaluationUnhandledErrorInformation, + #superclass : #Object, + #instVars : [ + 'requesterObject', + 'evaluatedCode', + 'sourceString', + 'sourceInterval', + 'sourceCoder' + ], + #category : #'GToolkit-Coder-Coders' +} + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> evaluatedCode [ + + ^ evaluatedCode ifNil: [ + evaluatedCode := GtSourceCoderNoEvaluatedCode default ] +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> evaluatedCode: anObject [ + evaluatedCode := anObject +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> requesterObject [ + "Return an object that requested evaluation." + + + ^ requesterObject +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> requesterObject: anObject [ + requesterObject := anObject +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> sourceCoder [ + + ^ sourceCoder +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> sourceCoder: anObject [ + sourceCoder := anObject +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> sourceInterval [ + ^ sourceInterval +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> sourceInterval: anObject [ + sourceInterval := anObject +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> sourceString [ + ^ sourceString +] + +{ #category : #accessing } +GtCoderEvaluationUnhandledErrorInformation >> sourceString: anObject [ + sourceString := anObject +] diff --git a/src/GToolkit-Coder/GtCoderExampleExecuted.class.st b/src/GToolkit-Coder/GtCoderExampleExecuted.class.st index 503ef45ed..21c4b3260 100644 --- a/src/GToolkit-Coder/GtCoderExampleExecuted.class.st +++ b/src/GToolkit-Coder/GtCoderExampleExecuted.class.st @@ -1,6 +1,6 @@ " I am announced when a Coder example is executed. -I am used by {{gtClass:GtMethodCoder}}, see {{gtMethod:GtMethodCoder>>#handleExampleExecuted:|expanded}}. +I am used by {{gtClass:GtPharoMethodCoder}}, see {{gtMethod:GtPharoMethodCoder>>#handleExampleExecuted:|expanded}}. " diff --git a/src/GToolkit-Coder/GtCoderExampler.class.st b/src/GToolkit-Coder/GtCoderExampler.class.st deleted file mode 100644 index 70b12c3c5..000000000 --- a/src/GToolkit-Coder/GtCoderExampler.class.st +++ /dev/null @@ -1,197 +0,0 @@ -" -I am an example executor. -I am used by {{gtClass:GtCodersModel}}, see: {{gtMethod:Behavior>>#gtCoderMethodsFor:context:}} for a usage. - - -" -Class { - #name : #GtCoderExampler, - #superclass : #Object, - #instVars : [ - 'coder' - ], - #category : #'GToolkit-Coder-Exampler' -} - -{ #category : #'api - instance creation' } -GtCoderExampler class >> coder: aCoder [ - ^ self new coder: aCoder -] - -{ #category : #'api - enumeration' } -GtCoderExampler >> allExampleCodersDo: aBlock [ - "iterate all coders with examples" - self subclassResponsibility -] - -{ #category : #'api - enumeration' } -GtCoderExampler >> allExecutableExampleCodersDo: aBlock [ - self allExampleCodersDo: [ :aCoder :anExample | - aCoder canExecuteExample ifTrue: [ - aBlock cull: aCoder cull: anExample ] ] -] - -{ #category : #'api - enumeration' } -GtCoderExampler >> allExecutableExampleCodersWithResultsDo: aBlock [ - self allExecutableExampleCodersDo: [ :aCoder :anExample | - aCoder exampleResult ifNotNil: [ :aResult | - aBlock cull: aCoder cull: anExample cull: aResult ] ] -] - -{ #category : #'api - accessing' } -GtCoderExampler >> coder [ - - ^ coder -] - -{ #category : #'api - initialization' } -GtCoderExampler >> coder: anObject [ - coder := anObject -] - -{ #category : #'api - accessing' } -GtCoderExampler >> coderFor: aCompiledMethod [ - - ^ self subclassResponsibility -] - -{ #category : #'api - testing' } -GtCoderExampler >> hasErrorExamples [ - - self allExecutableExampleCodersWithResultsDo: [ :eachCoder | - eachCoder exampleResult isError ifTrue: [ - ^ true ] ]. - ^ false -] - -{ #category : #'api - testing' } -GtCoderExampler >> hasExamples [ - self allExecutableExampleCodersDo: [ ^ true ]. - ^ false -] - -{ #category : #'api - testing' } -GtCoderExampler >> hasFailureExamples [ - - self allExecutableExampleCodersWithResultsDo: [ :eachCoder | - eachCoder exampleResult isFailure ifTrue: [ - ^ true ] ]. - ^ false -] - -{ #category : #'api - testing' } -GtCoderExampler >> hasNotExecutedExamples [ - - self allExecutableExampleCodersDo: [ :eachCoder | - eachCoder exampleResult ifNil: [ - ^ true ] ]. - ^ false -] - -{ #category : #'api - testing' } -GtCoderExampler >> hasSelectedExecutableExamples [ - self selectedExecutableExampleCodersDo: [ ^ true ]. - ^ false -] - -{ #category : #'api - testing' } -GtCoderExampler >> hasSuccessExamples [ - - self allExecutableExampleCodersWithResultsDo: [ :eachCoder | - eachCoder exampleResult isSuccess ifTrue: [ - ^ true ] ]. - ^ false -] - -{ #category : #'api - accessing' } -GtCoderExampler >> numberOfAllExamples [ - - | aTotal | - aTotal := 0. - self allExecutableExampleCodersDo: [ :eachCoder | - aTotal := aTotal + 1 ]. - ^ aTotal -] - -{ #category : #'api - accessing' } -GtCoderExampler >> numberOfErrorExamples [ - - | aTotal | - aTotal := 0. - self allExecutableExampleCodersWithResultsDo: [ :eachCoder | - eachCoder exampleResult isError ifTrue: [ - aTotal := aTotal + 1 ] ]. - ^ aTotal -] - -{ #category : #'api - accessing' } -GtCoderExampler >> numberOfFailureExamples [ - - | aTotal | - aTotal := 0. - self allExecutableExampleCodersWithResultsDo: [ :eachCoder | - eachCoder exampleResult isFailure ifTrue: [ - aTotal := aTotal + 1 ] ]. - ^ aTotal -] - -{ #category : #'api - accessing' } -GtCoderExampler >> numberOfNotExecutedExamples [ - - | aTotal | - aTotal := 0. - self allExecutableExampleCodersDo: [ :eachCoder | - eachCoder exampleResult ifNil: [ - aTotal := aTotal + 1 ] ]. - ^ aTotal -] - -{ #category : #'api - accessing' } -GtCoderExampler >> numberOfSuccessExamples [ - - | aTotal | - aTotal := 0. - self allExecutableExampleCodersWithResultsDo: [ :eachCoder | - eachCoder exampleResult isSuccess ifTrue: [ - aTotal := aTotal + 1 ] ]. - ^ aTotal -] - -{ #category : #'api - execution' } -GtCoderExampler >> runExamples [ - self selectedExecutableExampleCodersDo: [ :eachCoder | - (eachCoder isModified or: [ eachCoder example hasNoTestPragma ]) ifFalse: [ - eachCoder example run ] ] -] - -{ #category : #'api - execution' } -GtCoderExampler >> runExamplesFrom: anElement [ - self runExamples. -] - -{ #category : #'api - execution' } -GtCoderExampler >> runNoTestExamples [ - self selectedExecutableExampleCodersDo: [ :eachCoder | - (eachCoder isModified not and: [ eachCoder example hasNoTestPragma ]) ifTrue: [ - eachCoder example run ] ] -] - -{ #category : #'api - enumeration' } -GtCoderExampler >> selectedExampleCodersDo: aBlock [ - "iterate selected coders with examples" - self subclassResponsibility -] - -{ #category : #'api - enumeration' } -GtCoderExampler >> selectedExecutableExampleCodersDo: aBlock [ - self selectedExampleCodersDo: [ :aCoder :anExample | - aCoder canExecuteExample ifTrue: [ - aBlock cull: aCoder cull: anExample ] ] -] - -{ #category : #'api - enumeration' } -GtCoderExampler >> selectedExecutableExampleCodersWithResultsDo: aBlock [ - self selectedExecutableExampleCodersDo: [ :aCoder :anExample | - aCoder exampleResult ifNotNil: [ :aResult | - aBlock cull: aCoder cull: anExample cull: aResult ] ] -] diff --git a/src/GToolkit-Coder/GtCoderExplicitContextMenuItemAction.class.st b/src/GToolkit-Coder/GtCoderExplicitContextMenuItemAction.class.st new file mode 100644 index 000000000..fe349e364 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderExplicitContextMenuItemAction.class.st @@ -0,0 +1,28 @@ +Class { + #name : #GtCoderExplicitContextMenuItemAction, + #superclass : #GtCoderAction, + #instVars : [ + 'menuItemBlock', + 'group' + ], + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #accessing } +GtCoderExplicitContextMenuItemAction >> group [ + + ^ group +] + +{ #category : #accessing } +GtCoderExplicitContextMenuItemAction >> group: aMenuItemGroupConfiguration [ + group := aMenuItemGroupConfiguration +] + +{ #category : #accessing } +GtCoderExplicitContextMenuItemAction >> menuItem: aBlock [ + "Set block that creates a menu item, an instance of `BrMenuItem`. + The block have two arguments [ :aTextualCoderEditorElement :aTextualCoderViewModel :aCoderAction | ]" + + menuItemBlock := aBlock +] diff --git a/src/GToolkit-Coder/GtCoderExplicitSourceCode.class.st b/src/GToolkit-Coder/GtCoderExplicitSourceCode.class.st index acac72fea..24b846ac8 100644 --- a/src/GToolkit-Coder/GtCoderExplicitSourceCode.class.st +++ b/src/GToolkit-Coder/GtCoderExplicitSourceCode.class.st @@ -5,43 +5,11 @@ I represent an explicit source code that was provided as a string " Class { #name : #GtCoderExplicitSourceCode, - #superclass : #GtCoderSourceCode, - #instVars : [ - 'source' - ], - #category : #'GToolkit-Coder-Coders' + #superclass : #GtCoderExplicitStringSource, + #category : #'GToolkit-Coder-Coders - Source Text' } -{ #category : #'api - accessing' } -GtCoderExplicitSourceCode >> asAstCacheKey [ - ^ source -] - -{ #category : #initialization } -GtCoderExplicitSourceCode >> buildCollapsedText [ - - - ^ (self sourceString linesDo: [ :eachLine | eachLine trimmed ifNotEmpty: [ :aNonEmptyLine | ^ aNonEmptyLine ] ]) asRopedText -] - -{ #category : #initialization } -GtCoderExplicitSourceCode >> buildSourceText [ - - - ^ source asRopedText -] - -{ #category : #'api - testing' } -GtCoderExplicitSourceCode >> isModified [ - ^ self source ~= self sourceString -] - -{ #category : #accessing } -GtCoderExplicitSourceCode >> source [ - ^ source -] - -{ #category : #accessing } -GtCoderExplicitSourceCode >> source: aString [ - source := aString +{ #category : #testing } +GtCoderExplicitSourceCode class >> isDeprecated [ + ^ true ] diff --git a/src/GToolkit-Coder/GtCoderExplicitSourceText.class.st b/src/GToolkit-Coder/GtCoderExplicitSourceText.class.st index d0a50531f..f9b8c1cec 100644 --- a/src/GToolkit-Coder/GtCoderExplicitSourceText.class.st +++ b/src/GToolkit-Coder/GtCoderExplicitSourceText.class.st @@ -1,42 +1,10 @@ Class { #name : #GtCoderExplicitSourceText, - #superclass : #GtCoderSourceCode, - #instVars : [ - 'text' - ], - #category : #'GToolkit-Coder-Coders' + #superclass : #GtCoderExplicitTextSource, + #category : #'GToolkit-Coder-Coders - Source Text' } -{ #category : #'api - accessing' } -GtCoderExplicitSourceText >> asAstCacheKey [ - ^ text -] - -{ #category : #initialization } -GtCoderExplicitSourceText >> buildCollapsedText [ - - - ^ (self sourceString linesDo: [ :eachLine | eachLine trimmed ifNotEmpty: [ :aNonEmptyLine | ^ aNonEmptyLine ] ]) asRopedText -] - -{ #category : #initialization } -GtCoderExplicitSourceText >> buildSourceText [ - - - ^ text copy -] - -{ #category : #'api - testing' } -GtCoderExplicitSourceText >> isModified [ - ^ self text rope ~= self sourceText rope -] - -{ #category : #accessing } -GtCoderExplicitSourceText >> text [ - ^ text -] - -{ #category : #accessing } -GtCoderExplicitSourceText >> text: aStringOrText [ - text := aStringOrText asRopedText +{ #category : #testing } +GtCoderExplicitSourceText class >> isDeprecated [ + ^ true ] diff --git a/src/GToolkit-Coder/GtCoderExplicitStringSource.class.st b/src/GToolkit-Coder/GtCoderExplicitStringSource.class.st new file mode 100644 index 000000000..c7920968d --- /dev/null +++ b/src/GToolkit-Coder/GtCoderExplicitStringSource.class.st @@ -0,0 +1,27 @@ +Class { + #name : #GtCoderExplicitStringSource, + #superclass : #GtCoderTextSource, + #instVars : [ + 'source' + ], + #category : #'GToolkit-Coder-Coders - Source Text' +} + +{ #category : #initialization } +GtCoderExplicitStringSource >> computeOriginalSourceText [ + ^ source asRopedText +] + +{ #category : #accessing } +GtCoderExplicitStringSource >> source [ + + ^ source +] + +{ #category : #accessing } +GtCoderExplicitStringSource >> source: anObject [ + + self critical: [ + source := anObject. + originalSourceText := nil ] +] diff --git a/src/GToolkit-Coder/GtCoderExplicitTextSource.class.st b/src/GToolkit-Coder/GtCoderExplicitTextSource.class.st new file mode 100644 index 000000000..d3ca3ab9d --- /dev/null +++ b/src/GToolkit-Coder/GtCoderExplicitTextSource.class.st @@ -0,0 +1,26 @@ +Class { + #name : #GtCoderExplicitTextSource, + #superclass : #GtCoderTextSource, + #instVars : [ + 'text' + ], + #category : #'GToolkit-Coder-Coders - Source Text' +} + +{ #category : #initialization } +GtCoderExplicitTextSource >> computeOriginalSourceText [ + ^ text copy +] + +{ #category : #accessing } +GtCoderExplicitTextSource >> text [ + ^ text +] + +{ #category : #accessing } +GtCoderExplicitTextSource >> text: aStringOrText [ + + self critical: [ + text := aStringOrText asRopedText. + originalSourceText := nil ] +] diff --git a/src/GToolkit-Coder/GtCoderGrowingClassesHierarchyTree.class.st b/src/GToolkit-Coder/GtCoderGrowingClassesHierarchyTree.class.st index c70131c86..ef980586b 100644 --- a/src/GToolkit-Coder/GtCoderGrowingClassesHierarchyTree.class.st +++ b/src/GToolkit-Coder/GtCoderGrowingClassesHierarchyTree.class.st @@ -31,7 +31,7 @@ GtCoderGrowingClassesHierarchyTree >> gtFixedTreeFor: aView [ ^ aView tree - title: 'Fixed Classes'; + title: 'Fixed classes'; priority: 1; items: [ { self } ]; children: [ :anItem | anItem currentSubclassTrees ]; @@ -42,7 +42,7 @@ GtCoderGrowingClassesHierarchyTree >> gtFixedTreeFor: aView [ GtCoderGrowingClassesHierarchyTree >> gtTreeFor: aView [ ^ (super gtTreeFor: aView) - title: 'Growing Classes'; + title: 'Growing classes'; priority: 2 ] @@ -71,4 +71,5 @@ GtCoderGrowingClassesHierarchyTree >> updateSubclasses [ collect: [ :eachClass | self class new rootClass: eachClass ] as: Array. + self sortByClassName ] diff --git a/src/GToolkit-Coder/GtCoderModel.class.st b/src/GToolkit-Coder/GtCoderModel.class.st index d70d71d3e..7c6457524 100644 --- a/src/GToolkit-Coder/GtCoderModel.class.st +++ b/src/GToolkit-Coder/GtCoderModel.class.st @@ -5,7 +5,7 @@ Is an abstract model of a moldable Coder - a tool for creating and manipulating As a root entity in the hierarchy of coders {{gtClass:GtCoderModel}} do not make any assumptions on the way code is represented. In fact in contrast to {{gtClass:GtSourceCoder}} , it does not know anything about code. 1. # Moldability -Coder is designed to be easily adapted by the users in order to be used in various contexts through so called `add-ons` {{gtClass:GtCoderAddOns}}. Its subclasses can override {{gtMethod:GtCoderModel>>#addOnsClass}} to allow coder to have extra kinds of addons or {{gtMethod:GtCoderModel>>#initializeAddOns}} to customize the default add-ons. +Coder is designed to be easily adapted by the users in order to be used in various contexts through so called `add-ons` {{gtClass:GtCoderAddOns}}. Its subclasses can override {{gtMethod:GtCoderModel>>#addOnsClass}} to allow coder to have extra kinds of addons or {{gtMethod:GtCoderModel>>#initializeAddOns:}} to customize the default add-ons. 1. # Add-ons By default there are the following types of add-ons: @@ -25,7 +25,6 @@ Class { 'monitor', 'announcer', 'id', - 'suppressedAnnouncements', 'attributes', 'extraAddOns' ], @@ -35,6 +34,24 @@ Class { #category : #'GToolkit-Coder-Coders' } +{ #category : #addons } +GtCoderModel class >> astExtensionsPragma [ + ^ #gtAstCoderAddOns: +] + +{ #category : #accessing } +GtCoderModel class >> commonExecutionConfiguration [ + ^ AsyncFutureExecutionConfiguration new + customGroup: #Coder; + maxAmountOfWorkers: 2; + defaultPriority +] + +{ #category : #addons } +GtCoderModel class >> contextMenuAddOnsPragma [ + ^ #gtCoderContextMenuAddOns +] + { #category : #'class initialization' } GtCoderModel class >> initialize [ UniqueIdGenerator := BlUniqueIdGenerator new @@ -123,7 +140,7 @@ GtCoderModel >> contextActions [ { #category : #'api - actions' } GtCoderModel >> contextMenuActions [ - + ^ self addOns contextMenuActions ] @@ -143,6 +160,18 @@ GtCoderModel >> expanded: aBoolean [ self deprecated: 'The expansion state was moved to the ViewModel' ] +{ #category : #views } +GtCoderModel >> gtSystemSubscriptionsViewFor: aView [ + + ^ aView columnedList + title: 'System subscriptions'; + items: [ self systemSubscriptions ]; + column: 'Announcement' text: [ :each | each announcementClass ]; + column: 'Action' + text: [ :each | each action ] + weight: 2 +] + { #category : #accessing } GtCoderModel >> id [ ^ id @@ -153,20 +182,28 @@ GtCoderModel >> initialize [ super initialize. id := UniqueIdGenerator generateUniqueId. - monitor := Monitor new. - announcer := GtCoderAnnouncer new. - - self coderLook: self defaultCoderLook + monitor := Mutex new. + announcer := GtCoderAnnouncer new ] { #category : #initialization } GtCoderModel >> initializeAddOns: addOns [ ] +{ #category : #initialization } +GtCoderModel >> initializeAddOns: addOns viewModel: aCoderViewModel [ + self initializeAddOns: addOns +] + { #category : #initialization } GtCoderModel >> initializeShortcuts: addOns [ ] +{ #category : #subscriptions } +GtCoderModel >> isSubscribedToSystem [ + ^ SystemAnnouncer uniqueInstance hasSubscriber: self +] + { #category : #'api - actions' } GtCoderModel >> mainActions [ "Return a collection of main actions in the coder, for example save" @@ -180,6 +217,23 @@ GtCoderModel >> newAddOns [ ^ self addOnsClass new ] +{ #category : #private } +GtCoderModel >> pragmasNamed: aSymbol inHierarchy: aClass [ + | actions | + actions := OrderedCollection new. + aClass withAllSuperclassesDo: [ :each | + actions addAll: ((Pragma allNamed: aSymbol in: each) + reject: [ :eachPragma | actions anySatisfy: [ :otherPragma | + eachPragma methodSelector = otherPragma methodSelector ] ]) ]. + actions + sort: ( + [ :aPragma | aPragma numArgs isZero + ifFalse: [ aPragma arguments first ] + ifTrue: [ 50 ] ] ascending, + [ :aPragma | aPragma methodSelector ] ascending). + ^ actions +] + { #category : #elements } GtCoderModel >> previewElement [ "Return a preview element for the Spotter" @@ -195,21 +249,13 @@ GtCoderModel >> removeAttributeNamed: aSymbol [ attributes removeKey: aSymbol ifAbsent: [ ] ] -{ #category : #elements } -GtCoderModel >> requestFocus [ - self announce: (GtCoderRequestFocus new coder: self) -] - { #category : #'api - addons' } GtCoderModel >> requestUpdateAddOns [ "Request addons to update due to some environmental changes (similar to requesting text to restyle). It is a responsibility of the Coder's Element to start the actual addon update (${method:GtCoderModel>>#updateAddOnsFrom:}$) which may happen in a background thread, that is why we can not start the update directly - we must have a reference to the element" - "addOns - ifNil: [ ^ self ]. - self addOns requestUpdate. - self announce: (GtCoderAddOnsUpdateRequest new coder: self)" + self announce: (GtCoderAddOnsUpdateRequest new coder: self) ] { #category : #subscriptions } @@ -217,6 +263,15 @@ GtCoderModel >> subscribeToSystem [ "do nothing" ] +{ #category : #subscriptions } +GtCoderModel >> systemSubscriptions [ + ^ Array + streamContents: [ :aStream | + SystemAnnouncer uniqueInstance subscriptions + subscriptionsOf: self + do: [ :eachSubscription | aStream nextPut: eachSubscription ] ] +] + { #category : #subscriptions } GtCoderModel >> unsubscribeFromSystem [ SystemAnnouncer uniqueInstance unsubscribe: self diff --git a/src/GToolkit-Coder/GtCoderNavigationAnnouncement.class.st b/src/GToolkit-Coder/GtCoderNavigationAnnouncement.class.st index 9007cafd1..2584be645 100644 --- a/src/GToolkit-Coder/GtCoderNavigationAnnouncement.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationAnnouncement.class.st @@ -2,7 +2,8 @@ Class { #name : #GtCoderNavigationAnnouncement, #superclass : #Announcement, #instVars : [ - 'coder' + 'coder', + 'source' ], #category : #'GToolkit-Coder-Navigation - Events' } @@ -16,3 +17,13 @@ GtCoderNavigationAnnouncement >> coder [ GtCoderNavigationAnnouncement >> coder: aCoder [ coder := aCoder ] + +{ #category : #accessing } +GtCoderNavigationAnnouncement >> source [ + ^ source +] + +{ #category : #accessing } +GtCoderNavigationAnnouncement >> source: anObject [ + source := anObject +] diff --git a/src/GToolkit-Coder/GtCoderNavigationClassAnnoucement.class.st b/src/GToolkit-Coder/GtCoderNavigationClassAnnouncement.class.st similarity index 55% rename from src/GToolkit-Coder/GtCoderNavigationClassAnnoucement.class.st rename to src/GToolkit-Coder/GtCoderNavigationClassAnnouncement.class.st index 831bffbc4..1e9d4243c 100644 --- a/src/GToolkit-Coder/GtCoderNavigationClassAnnoucement.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationClassAnnouncement.class.st @@ -1,5 +1,5 @@ Class { - #name : #GtCoderNavigationClassAnnoucement, + #name : #GtCoderNavigationClassAnnouncement, #superclass : #GtCoderNavigationSystemChangesAnnouncement, #instVars : [ 'package', @@ -10,31 +10,31 @@ Class { } { #category : #accessing } -GtCoderNavigationClassAnnoucement >> package [ +GtCoderNavigationClassAnnouncement >> package [ ^ package ] { #category : #accessing } -GtCoderNavigationClassAnnoucement >> package: anObject [ +GtCoderNavigationClassAnnouncement >> package: anObject [ package := anObject ] { #category : #accessing } -GtCoderNavigationClassAnnoucement >> tag [ +GtCoderNavigationClassAnnouncement >> tag [ ^ tag ] { #category : #accessing } -GtCoderNavigationClassAnnoucement >> tag: anObject [ +GtCoderNavigationClassAnnouncement >> tag: anObject [ tag := anObject ] { #category : #accessing } -GtCoderNavigationClassAnnoucement >> theClass [ +GtCoderNavigationClassAnnouncement >> theClass [ ^ theClass ] { #category : #accessing } -GtCoderNavigationClassAnnoucement >> theClass: anObject [ +GtCoderNavigationClassAnnouncement >> theClass: anObject [ theClass := anObject ] diff --git a/src/GToolkit-Coder/GtCoderNavigationClassDeselected.class.st b/src/GToolkit-Coder/GtCoderNavigationClassDeselected.class.st new file mode 100644 index 000000000..adf9def3b --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationClassDeselected.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtCoderNavigationClassDeselected, + #superclass : #GtCoderNavigationSelectionAnnouncement, + #instVars : [ + 'theClass' + ], + #category : #'GToolkit-Coder-Navigation - Events' +} + +{ #category : #accessing } +GtCoderNavigationClassDeselected >> theClass [ + + + ^ theClass +] + +{ #category : #accessing } +GtCoderNavigationClassDeselected >> theClass: anObject [ + theClass := anObject +] diff --git a/src/GToolkit-Coder/GtCoderNavigationClassModified.class.st b/src/GToolkit-Coder/GtCoderNavigationClassModified.class.st index a17d542d9..3d303dd7a 100644 --- a/src/GToolkit-Coder/GtCoderNavigationClassModified.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationClassModified.class.st @@ -1,5 +1,5 @@ Class { #name : #GtCoderNavigationClassModified, - #superclass : #GtCoderNavigationClassAnnoucement, + #superclass : #GtCoderNavigationClassAnnouncement, #category : #'GToolkit-Coder-Navigation - Events' } diff --git a/src/GToolkit-Coder/GtCoderNavigationClassRenamed.class.st b/src/GToolkit-Coder/GtCoderNavigationClassRenamed.class.st index 5015ebbf4..efbc85a6e 100644 --- a/src/GToolkit-Coder/GtCoderNavigationClassRenamed.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationClassRenamed.class.st @@ -1,6 +1,6 @@ Class { #name : #GtCoderNavigationClassRenamed, - #superclass : #GtCoderNavigationClassAnnoucement, + #superclass : #GtCoderNavigationClassAnnouncement, #instVars : [ 'oldName', 'newName' diff --git a/src/GToolkit-Coder/GtCoderNavigationClassSelected.class.st b/src/GToolkit-Coder/GtCoderNavigationClassSelected.class.st index 20b0b864e..cf4b8eff5 100644 --- a/src/GToolkit-Coder/GtCoderNavigationClassSelected.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationClassSelected.class.st @@ -4,7 +4,8 @@ Class { #instVars : [ 'theClass', 'package', - 'tag' + 'tag', + 'selectedClasses' ], #category : #'GToolkit-Coder-Navigation - Events' } @@ -19,6 +20,16 @@ GtCoderNavigationClassSelected >> package: anObject [ package := anObject ] +{ #category : #accessing } +GtCoderNavigationClassSelected >> selectedClasses [ + ^ selectedClasses +] + +{ #category : #accessing } +GtCoderNavigationClassSelected >> selectedClasses: anObject [ + selectedClasses := anObject +] + { #category : #accessing } GtCoderNavigationClassSelected >> tag [ ^ tag diff --git a/src/GToolkit-Coder/GtCoderNavigationClassesToShowChanged.class.st b/src/GToolkit-Coder/GtCoderNavigationClassesToShowChanged.class.st new file mode 100644 index 000000000..7a64be207 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationClassesToShowChanged.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderNavigationClassesToShowChanged, + #superclass : #GtCoderNavigationItemsToShowAnnouncement, + #category : #'GToolkit-Coder-Navigation - Events' +} diff --git a/src/GToolkit-Coder/GtCoderNavigationCoderChanged.class.st b/src/GToolkit-Coder/GtCoderNavigationCoderChanged.class.st new file mode 100644 index 000000000..f8e9f2982 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationCoderChanged.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderNavigationCoderChanged, + #superclass : #GtCoderNavigationAnnouncement, + #category : #'GToolkit-Coder-Navigation - Events' +} diff --git a/src/GToolkit-Coder/GtCoderNavigationItemsToShowAnnouncement.class.st b/src/GToolkit-Coder/GtCoderNavigationItemsToShowAnnouncement.class.st new file mode 100644 index 000000000..102176509 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationItemsToShowAnnouncement.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderNavigationItemsToShowAnnouncement, + #superclass : #GtCoderNavigationAnnouncement, + #category : #'GToolkit-Coder-Navigation - Events' +} diff --git a/src/GToolkit-Coder/GtCoderNavigationMethodSelected.class.st b/src/GToolkit-Coder/GtCoderNavigationMethodSelected.class.st new file mode 100644 index 000000000..910944ad5 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationMethodSelected.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderNavigationMethodSelected, + #superclass : #GtCoderNavigationSelectionAnnouncement, + #instVars : [ + 'method' + ], + #category : #'GToolkit-Coder-Navigation - Events' +} + +{ #category : #accessing } +GtCoderNavigationMethodSelected >> method [ + ^ method +] + +{ #category : #accessing } +GtCoderNavigationMethodSelected >> method: aCompiledMethod [ + method := aCompiledMethod +] diff --git a/src/GToolkit-Coder/GtCoderNavigationMethodsSelected.class.st b/src/GToolkit-Coder/GtCoderNavigationMethodsSelected.class.st new file mode 100644 index 000000000..aa8e0f422 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationMethodsSelected.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderNavigationMethodsSelected, + #superclass : #GtCoderNavigationSelectionAnnouncement, + #instVars : [ + 'methods' + ], + #category : #'GToolkit-Coder-Navigation - Events' +} + +{ #category : #accessing } +GtCoderNavigationMethodsSelected >> methods [ + ^ methods +] + +{ #category : #accessing } +GtCoderNavigationMethodsSelected >> methods: anObject [ + methods := anObject +] diff --git a/src/GToolkit-Coder/GtCoderNavigationMethodsToShowChanged.class.st b/src/GToolkit-Coder/GtCoderNavigationMethodsToShowChanged.class.st new file mode 100644 index 000000000..12addf985 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationMethodsToShowChanged.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderNavigationMethodsToShowChanged, + #superclass : #GtCoderNavigationItemsToShowAnnouncement, + #category : #'GToolkit-Coder-Navigation - Events' +} diff --git a/src/GToolkit-Coder/GtCoderNavigationModel.class.st b/src/GToolkit-Coder/GtCoderNavigationModel.class.st index 7a6744b4f..432caf80e 100644 --- a/src/GToolkit-Coder/GtCoderNavigationModel.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationModel.class.st @@ -32,6 +32,12 @@ GtCoderNavigationModel >> hasSelectedPackage [ ^ self subclassResponsibility ] +{ #category : #'api - testing' } +GtCoderNavigationModel >> hasSelectedPackageOrClass [ + + ^ self hasSelectedPackage or: [ self hasSelectedClass ] +] + { #category : #'api - testing' } GtCoderNavigationModel >> hasSelectedTag [ @@ -45,7 +51,7 @@ GtCoderNavigationModel >> isNavigationModel [ { #category : #'api - accessing' } GtCoderNavigationModel >> packagesToShow [ - + ^ self subclassResponsibility ] @@ -55,7 +61,27 @@ GtCoderNavigationModel >> selectClass: aClass [ ] { #category : #'api - selection' } -GtCoderNavigationModel >> selectMethod: aCompiledMethod [ +GtCoderNavigationModel >> selectClasses: aCollectionOfClasses [ + self subclassResponsibility +] + +{ #category : #'api - selection' } +GtCoderNavigationModel >> selectMethod: aMethod [ + self selectMethod: aMethod source: self +] + +{ #category : #'api - selection' } +GtCoderNavigationModel >> selectMethod: aCompiledMethod source: aSource [ + self subclassResponsibility +] + +{ #category : #'api - selection' } +GtCoderNavigationModel >> selectMethods: aCollectionOfMethods [ + self selectMethods: aCollectionOfMethods source: self +] + +{ #category : #'api - selection' } +GtCoderNavigationModel >> selectMethods: aCollectionOfCompiledMethods source: aSource [ self subclassResponsibility ] @@ -69,6 +95,13 @@ GtCoderNavigationModel >> selectedClassDo: aBlock [ self subclassResponsibility ] +{ #category : #'api - selection' } +GtCoderNavigationModel >> selectedMethod: aCompiledMethod source: aSource [ + "Inform that another method was selected and should be reflected in the navigation model." + + self subclassResponsibility +] + { #category : #'api - selection' } GtCoderNavigationModel >> selectedPackageDo: aBlock [ self subclassResponsibility diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageAnnoucement.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageAnnouncement.class.st similarity index 57% rename from src/GToolkit-Coder/GtCoderNavigationPackageAnnoucement.class.st rename to src/GToolkit-Coder/GtCoderNavigationPackageAnnouncement.class.st index 9cf01c641..e189052fc 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageAnnoucement.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageAnnouncement.class.st @@ -1,5 +1,5 @@ Class { - #name : #GtCoderNavigationPackageAnnoucement, + #name : #GtCoderNavigationPackageAnnouncement, #superclass : #GtCoderNavigationSystemChangesAnnouncement, #instVars : [ 'package' @@ -8,16 +8,16 @@ Class { } { #category : #accessing } -GtCoderNavigationPackageAnnoucement class >> package: aRPackage [ +GtCoderNavigationPackageAnnouncement class >> package: aRPackage [ ^ self new package: aRPackage ] { #category : #accessing } -GtCoderNavigationPackageAnnoucement >> package [ +GtCoderNavigationPackageAnnouncement >> package [ ^ package ] { #category : #accessing } -GtCoderNavigationPackageAnnoucement >> package: anObject [ +GtCoderNavigationPackageAnnouncement >> package: anObject [ package := anObject ] diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageRegistered.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageRegistered.class.st index 0b0623a08..9d1c8ca00 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageRegistered.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageRegistered.class.st @@ -1,5 +1,5 @@ Class { #name : #GtCoderNavigationPackageRegistered, - #superclass : #GtCoderNavigationPackageAnnoucement, + #superclass : #GtCoderNavigationPackageAnnouncement, #category : #'GToolkit-Coder-Navigation - Events' } diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageRenamed.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageRenamed.class.st index 0f2f24da9..68c6523e0 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageRenamed.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageRenamed.class.st @@ -1,6 +1,6 @@ Class { #name : #GtCoderNavigationPackageRenamed, - #superclass : #GtCoderNavigationPackageAnnoucement, + #superclass : #GtCoderNavigationPackageAnnouncement, #instVars : [ 'oldName', 'newName' diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageSelected.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageSelected.class.st index 6a14f61df..9ea8e2aa2 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageSelected.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageSelected.class.st @@ -9,7 +9,7 @@ Class { { #category : #accessing } GtCoderNavigationPackageSelected >> package [ - + ^ package ] diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageTagAdded.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageTagAdded.class.st index 769a6d1a3..329eaf472 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageTagAdded.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageTagAdded.class.st @@ -1,5 +1,5 @@ Class { #name : #GtCoderNavigationPackageTagAdded, - #superclass : #GtCoderNavigationPackageTagAnnoucement, + #superclass : #GtCoderNavigationPackageTagAnnouncement, #category : #'GToolkit-Coder-Navigation - Events' } diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageTagAnnoucement.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageTagAnnouncement.class.st similarity index 55% rename from src/GToolkit-Coder/GtCoderNavigationPackageTagAnnoucement.class.st rename to src/GToolkit-Coder/GtCoderNavigationPackageTagAnnouncement.class.st index ca962cb13..7c26eabf8 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageTagAnnoucement.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageTagAnnouncement.class.st @@ -1,5 +1,5 @@ Class { - #name : #GtCoderNavigationPackageTagAnnoucement, + #name : #GtCoderNavigationPackageTagAnnouncement, #superclass : #GtCoderNavigationSystemChangesAnnouncement, #instVars : [ 'package', @@ -9,21 +9,21 @@ Class { } { #category : #accessing } -GtCoderNavigationPackageTagAnnoucement >> package [ +GtCoderNavigationPackageTagAnnouncement >> package [ ^ package ] { #category : #accessing } -GtCoderNavigationPackageTagAnnoucement >> package: anObject [ +GtCoderNavigationPackageTagAnnouncement >> package: anObject [ package := anObject ] { #category : #accessing } -GtCoderNavigationPackageTagAnnoucement >> tag [ +GtCoderNavigationPackageTagAnnouncement >> tag [ ^ tag ] { #category : #accessing } -GtCoderNavigationPackageTagAnnoucement >> tag: anObject [ +GtCoderNavigationPackageTagAnnouncement >> tag: anObject [ tag := anObject ] diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageTagRemoved.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageTagRemoved.class.st index acbe394e6..0a7a89189 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageTagRemoved.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageTagRemoved.class.st @@ -1,5 +1,5 @@ Class { #name : #GtCoderNavigationPackageTagRemoved, - #superclass : #GtCoderNavigationPackageTagAnnoucement, + #superclass : #GtCoderNavigationPackageTagAnnouncement, #category : #'GToolkit-Coder-Navigation - Events' } diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageTagRenamed.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageTagRenamed.class.st new file mode 100644 index 000000000..e285e9fe5 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationPackageTagRenamed.class.st @@ -0,0 +1,29 @@ +Class { + #name : #GtCoderNavigationPackageTagRenamed, + #superclass : #GtCoderNavigationPackageAnnouncement, + #instVars : [ + 'oldName', + 'newName' + ], + #category : #'GToolkit-Coder-Navigation - Events' +} + +{ #category : #accessing } +GtCoderNavigationPackageTagRenamed >> newName [ + ^ newName +] + +{ #category : #accessing } +GtCoderNavigationPackageTagRenamed >> newName: anObject [ + newName := anObject +] + +{ #category : #accessing } +GtCoderNavigationPackageTagRenamed >> oldName [ + ^ oldName +] + +{ #category : #accessing } +GtCoderNavigationPackageTagRenamed >> oldName: anObject [ + oldName := anObject +] diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageTagSelected.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageTagSelected.class.st index 16e2f45e6..feeb8913d 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageTagSelected.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageTagSelected.class.st @@ -20,7 +20,7 @@ GtCoderNavigationPackageTagSelected >> package: anObject [ { #category : #accessing } GtCoderNavigationPackageTagSelected >> tag [ - + ^ tag ] diff --git a/src/GToolkit-Coder/GtCoderNavigationPackageUnregistered.class.st b/src/GToolkit-Coder/GtCoderNavigationPackageUnregistered.class.st index 4285ca6a2..36f765c83 100644 --- a/src/GToolkit-Coder/GtCoderNavigationPackageUnregistered.class.st +++ b/src/GToolkit-Coder/GtCoderNavigationPackageUnregistered.class.st @@ -1,5 +1,5 @@ Class { #name : #GtCoderNavigationPackageUnregistered, - #superclass : #GtCoderNavigationPackageAnnoucement, + #superclass : #GtCoderNavigationPackageAnnouncement, #category : #'GToolkit-Coder-Navigation - Events' } diff --git a/src/GToolkit-Coder/GtCoderNavigationProtocolDeselected.class.st b/src/GToolkit-Coder/GtCoderNavigationProtocolDeselected.class.st new file mode 100644 index 000000000..fddd72440 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationProtocolDeselected.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderNavigationProtocolDeselected, + #superclass : #GtCoderNavigationSelectionAnnouncement, + #instVars : [ + 'protocol' + ], + #category : #'GToolkit-Coder-Navigation - Events' +} + +{ #category : #accessing } +GtCoderNavigationProtocolDeselected >> protocol [ + ^ protocol +] + +{ #category : #accessing } +GtCoderNavigationProtocolDeselected >> protocol: anObject [ + protocol := anObject +] diff --git a/src/GToolkit-Coder/GtCoderNavigationProtocolSelected.class.st b/src/GToolkit-Coder/GtCoderNavigationProtocolSelected.class.st new file mode 100644 index 000000000..e3a90f412 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationProtocolSelected.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderNavigationProtocolSelected, + #superclass : #GtCoderNavigationSelectionAnnouncement, + #instVars : [ + 'protocol' + ], + #category : #'GToolkit-Coder-Navigation - Events' +} + +{ #category : #accessing } +GtCoderNavigationProtocolSelected >> protocol [ + ^ protocol +] + +{ #category : #accessing } +GtCoderNavigationProtocolSelected >> protocol: anObject [ + protocol := anObject +] diff --git a/src/GToolkit-Coder/GtCoderNavigationProtocolsToShowChanged.class.st b/src/GToolkit-Coder/GtCoderNavigationProtocolsToShowChanged.class.st new file mode 100644 index 000000000..42ee72177 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationProtocolsToShowChanged.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderNavigationProtocolsToShowChanged, + #superclass : #GtCoderNavigationItemsToShowAnnouncement, + #category : #'GToolkit-Coder-Navigation - Events' +} diff --git a/src/GToolkit-Coder/GtCoderNavigationSlotSelected.class.st b/src/GToolkit-Coder/GtCoderNavigationSlotSelected.class.st new file mode 100644 index 000000000..e125112f8 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationSlotSelected.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderNavigationSlotSelected, + #superclass : #GtCoderNavigationSelectionAnnouncement, + #instVars : [ + 'slot' + ], + #category : #'GToolkit-Coder-Navigation - Events' +} + +{ #category : #accessing } +GtCoderNavigationSlotSelected >> slot [ + ^ slot +] + +{ #category : #accessing } +GtCoderNavigationSlotSelected >> slot: anObject [ + slot := anObject +] diff --git a/src/GToolkit-Coder/GtCoderNavigationSlotsToShowChanged.class.st b/src/GToolkit-Coder/GtCoderNavigationSlotsToShowChanged.class.st new file mode 100644 index 000000000..fc31aee59 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderNavigationSlotsToShowChanged.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderNavigationSlotsToShowChanged, + #superclass : #GtCoderNavigationItemsToShowAnnouncement, + #category : #'GToolkit-Coder-Navigation - Events' +} diff --git a/src/GToolkit-Coder/GtCoderNullNavigationModel.class.st b/src/GToolkit-Coder/GtCoderNullNavigationModel.class.st index 8836922ed..6600147fd 100644 --- a/src/GToolkit-Coder/GtCoderNullNavigationModel.class.st +++ b/src/GToolkit-Coder/GtCoderNullNavigationModel.class.st @@ -47,15 +47,29 @@ GtCoderNullNavigationModel >> selectClass: aClass [ ] { #category : #'api - selection' } -GtCoderNullNavigationModel >> selectMethod: aCompiledMethod [ +GtCoderNullNavigationModel >> selectClasses: aCollectionOfClasses [ "do nothing" ] +{ #category : #'api - selection' } +GtCoderNullNavigationModel >> selectMethod: aCompiledMethod source: aSource [ + "do nothing" + + +] + { #category : #'api - selection' } GtCoderNullNavigationModel >> selectMethodProtocol: aMethodProtocol [ "do nothing" ] +{ #category : #'api - selection' } +GtCoderNullNavigationModel >> selectMethods: aCollectionOfCompiledMethods source: aSource [ + "do nothing" + + +] + { #category : #'api - selection' } GtCoderNullNavigationModel >> selectPackage: aPackage [ "do nothing" @@ -66,6 +80,18 @@ GtCoderNullNavigationModel >> selectedClassDo: aBlock [ "do nothing" ] +{ #category : #'api - selection' } +GtCoderNullNavigationModel >> selectedMethod: aCompiledMethod source: aSource [ + "Inform that another method was selected and should be reflected in the navigation model. + + Do nothing." +] + +{ #category : #'as yet unclassified' } +GtCoderNullNavigationModel >> selectedPackage [ + "Do Nothing" +] + { #category : #'api - selection' } GtCoderNullNavigationModel >> selectedPackageDo: aBlock [ "do nothing" diff --git a/src/GToolkit-Coder/GtCoderObject.class.st b/src/GToolkit-Coder/GtCoderObject.class.st new file mode 100644 index 000000000..9d264db3c --- /dev/null +++ b/src/GToolkit-Coder/GtCoderObject.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtCoderObject, + #superclass : #GtPhlowObject, + #category : #'GToolkit-Coder-Object Holder' +} + +{ #category : #testing } +GtCoderObject class >> isDeprecated [ + "Use my superclass" + + ^ true +] diff --git a/src/GToolkit-Coder/GtCoderObjectSpawnRequest.class.st b/src/GToolkit-Coder/GtCoderObjectSpawnRequest.class.st index 767129d5c..8583c1a39 100644 --- a/src/GToolkit-Coder/GtCoderObjectSpawnRequest.class.st +++ b/src/GToolkit-Coder/GtCoderObjectSpawnRequest.class.st @@ -3,7 +3,8 @@ Class { #superclass : #GtSourceCoderAnnouncement, #instVars : [ 'object', - 'spawnDestination' + 'spawnDestination', + 'requesterObject' ], #category : #'GToolkit-Coder-Event' } @@ -18,6 +19,17 @@ GtCoderObjectSpawnRequest >> object: anObject [ object := anObject ] +{ #category : #accessing } +GtCoderObjectSpawnRequest >> requesterObject [ + + ^ requesterObject ifNil: [ GtNoCoderRequester uniqueInstance ] +] + +{ #category : #accessing } +GtCoderObjectSpawnRequest >> requesterObject: anObject [ + requesterObject := anObject asCoderRequesterObject +] + { #category : #accessing } GtCoderObjectSpawnRequest >> spawnDestination [ ^ spawnDestination diff --git a/src/GToolkit-Coder/GtCoderPackageExtensionTag.class.st b/src/GToolkit-Coder/GtCoderPackageExtensionTag.class.st new file mode 100644 index 000000000..587aa38ce --- /dev/null +++ b/src/GToolkit-Coder/GtCoderPackageExtensionTag.class.st @@ -0,0 +1,77 @@ +Class { + #name : #GtCoderPackageExtensionTag, + #superclass : #Object, + #instVars : [ + 'package' + ], + #category : #'GToolkit-Coder-Navigation - Model' +} + +{ #category : #'instance creation' } +GtCoderPackageExtensionTag class >> forPackage: aRPackage [ + ^ self new package: aRPackage. +] + +{ #category : #testing } +GtCoderPackageExtensionTag >> = anObject [ + ^ self class = anObject class + and: [ package = anObject package ] +] + +{ #category : #accessing } +GtCoderPackageExtensionTag >> categoryName [ + ^ self package categoryName +] + +{ #category : #accessing } +GtCoderPackageExtensionTag >> classes [ + + ^ self package extendedClasses asArray +] + +{ #category : #accessing } +GtCoderPackageExtensionTag >> gtTagName [ + ^ self name +] + +{ #category : #testing } +GtCoderPackageExtensionTag >> hasExtendedClasses [ + + ^ self package gtDoesExtendClasses +] + +{ #category : #testing } +GtCoderPackageExtensionTag >> hash [ + ^ package hash +] + +{ #category : #testing } +GtCoderPackageExtensionTag >> isRoot [ + ^false +] + +{ #category : #testing } +GtCoderPackageExtensionTag >> isUncategorized [ + ^false +] + +{ #category : #accessing } +GtCoderPackageExtensionTag >> name [ + ^ 'Extensions' +] + +{ #category : #accessing } +GtCoderPackageExtensionTag >> package [ + + ^ package +] + +{ #category : #accessing } +GtCoderPackageExtensionTag >> package: aRPackage [ + package := aRPackage +] + +{ #category : #accessing } +GtCoderPackageExtensionTag >> packageName [ + ^ self package name +] diff --git a/src/GToolkit-Coder/GtCoderPackageExtentionTag.class.st b/src/GToolkit-Coder/GtCoderPackageExtentionTag.class.st deleted file mode 100644 index 09eef426b..000000000 --- a/src/GToolkit-Coder/GtCoderPackageExtentionTag.class.st +++ /dev/null @@ -1,60 +0,0 @@ -Class { - #name : #GtCoderPackageExtentionTag, - #superclass : #Object, - #traits : 'TBlDebug', - #classTraits : 'TBlDebug classTrait', - #instVars : [ - 'package', - 'classes' - ], - #category : #'GToolkit-Coder-Navigation - Model' -} - -{ #category : #'instance creation' } -GtCoderPackageExtentionTag class >> forPackage: aRPackage [ - ^ self new package: aRPackage. -] - -{ #category : #accessing } -GtCoderPackageExtentionTag >> classes [ - - ^ classes -] - -{ #category : #accessing } -GtCoderPackageExtentionTag >> classes: anObject [ - classes := anObject -] - -{ #category : #testing } -GtCoderPackageExtentionTag >> hasExtendedClasses [ - - ^ self classes isNotEmpty -] - -{ #category : #accessing } -GtCoderPackageExtentionTag >> name [ - ^ 'Extensions' -] - -{ #category : #hooks } -GtCoderPackageExtentionTag >> onPackageChange [ - self classes: self package extendedClasses asArray -] - -{ #category : #accessing } -GtCoderPackageExtentionTag >> package [ - - ^ package -] - -{ #category : #accessing } -GtCoderPackageExtentionTag >> package: aRPackage [ - package := aRPackage. - self onPackageChange. -] - -{ #category : #accessing } -GtCoderPackageExtentionTag >> packageName [ - ^ self package name. -] diff --git a/src/GToolkit-Coder/GtCoderPackageUncategorizedTag.class.st b/src/GToolkit-Coder/GtCoderPackageUncategorizedTag.class.st index 6db4e74d5..5bb427fbf 100644 --- a/src/GToolkit-Coder/GtCoderPackageUncategorizedTag.class.st +++ b/src/GToolkit-Coder/GtCoderPackageUncategorizedTag.class.st @@ -1,8 +1,6 @@ Class { #name : #GtCoderPackageUncategorizedTag, #superclass : #Object, - #traits : 'TBlDebug', - #classTraits : 'TBlDebug classTrait', #instVars : [ 'packageTag' ], @@ -17,7 +15,12 @@ GtCoderPackageUncategorizedTag class >> forPackageTag: aRPackage [ { #category : #comparing } GtCoderPackageUncategorizedTag >> = anObject [ ^ (self class = anObject class and: [ self packageTagName = anObject packageTagName ]) or: [ - (anObject isKindOf: RPackageTag) and: [ self packageTagName = anObject name ] ] + (anObject isPharoPackageTagModel) and: [ self packageTagName = anObject name ] ] +] + +{ #category : #accessing } +GtCoderPackageUncategorizedTag >> categoryName [ + ^ packageTag categoryName ] { #category : #accessing } @@ -25,6 +28,21 @@ GtCoderPackageUncategorizedTag >> classes [ ^ self packageTag classes ] +{ #category : #accessing } +GtCoderPackageUncategorizedTag >> gtTagName [ + ^ packageTag name +] + +{ #category : #testing } +GtCoderPackageUncategorizedTag >> isRoot [ + ^ true +] + +{ #category : #accessing } +GtCoderPackageUncategorizedTag >> isUncategorized [ + ^ true +] + { #category : #accessing } GtCoderPackageUncategorizedTag >> name [ ^ 'Uncategorized' @@ -42,7 +60,7 @@ GtCoderPackageUncategorizedTag >> packageName [ { #category : #accessing } GtCoderPackageUncategorizedTag >> packageTag [ - + ^ packageTag ] diff --git a/src/GToolkit-Coder/GtCoderPackageUpdatedAnnouncement.class.st b/src/GToolkit-Coder/GtCoderPackageUpdatedAnnouncement.class.st index f0311324f..e7815f32e 100644 --- a/src/GToolkit-Coder/GtCoderPackageUpdatedAnnouncement.class.st +++ b/src/GToolkit-Coder/GtCoderPackageUpdatedAnnouncement.class.st @@ -1,5 +1,5 @@ " -I am sent by the {{gtClass:GtBehaviorCoder}} when the current package changes (means class is not selected and we have to remove the content from the class coder) +I am sent by the {{gtClass:GtPharoBehaviorCoder}} when the current package changes (means class is not selected and we have to remove the content from the class coder) " diff --git a/src/GToolkit-Coder/GtCoderParseError.class.st b/src/GToolkit-Coder/GtCoderParseError.class.st index 299047425..1686c770e 100644 --- a/src/GToolkit-Coder/GtCoderParseError.class.st +++ b/src/GToolkit-Coder/GtCoderParseError.class.st @@ -3,7 +3,9 @@ Class { #superclass : #GtSourceCoderAnnouncement, #instVars : [ 'errorMessage', - 'location' + 'location', + 'requesterObject', + 'exception' ], #category : #'GToolkit-Coder-Event' } @@ -18,6 +20,24 @@ GtCoderParseError >> errorMessage: anObject [ errorMessage := anObject ] +{ #category : #accessing } +GtCoderParseError >> exception [ + ^ exception +] + +{ #category : #accessing } +GtCoderParseError >> exception: anException [ + exception := anException +] + +{ #category : #testing } +GtCoderParseError >> isUndeclaredError [ + ^ self + forPharo12AndNewer: [ (errorMessage beginsWith: 'Undeclared variable') + or: [ errorMessage beginsWith: 'Undeclared temp' ] ] + forPharo11: [ exception isKindOf: OCUndeclaredVariableWarning ] +] + { #category : #accessing } GtCoderParseError >> location [ ^ location @@ -27,3 +47,23 @@ GtCoderParseError >> location [ GtCoderParseError >> location: anObject [ location := anObject ] + +{ #category : #accessing } +GtCoderParseError >> node [ + "The node in the exception is an RB node (Pharo 11)" + + ^ (self exception notNil and: [ self exception respondsTo: #node ]) + ifTrue: [ self exception node ] + ifFalse: [ coder rbAST ifNotNil: [ :node | node nodeForOffset: location ] ] +] + +{ #category : #accessing } +GtCoderParseError >> requesterObject [ + + ^ requesterObject +] + +{ #category : #accessing } +GtCoderParseError >> requesterObject: anObject [ + requesterObject := anObject asCoderRequesterObject +] diff --git a/src/GToolkit-Coder/GtCoderPreview.class.st b/src/GToolkit-Coder/GtCoderPreview.class.st new file mode 100644 index 000000000..2aa5621e7 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderPreview.class.st @@ -0,0 +1,80 @@ +Class { + #name : #GtCoderPreview, + #superclass : #Object, + #instVars : [ + 'id', + 'title', + 'stencil', + 'dataBinder' + ], + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #comparing } +GtCoderPreview >> = anObject [ + + "Answer whether the receiver and anObject represent the same object." + + self == anObject ifTrue: [ ^ true ]. + self class = anObject class ifFalse: [ ^ false ]. + ^ id = anObject id +] + +{ #category : #accessing } +GtCoderPreview >> dataBinder [ + + ^ dataBinder +] + +{ #category : #accessing } +GtCoderPreview >> dataBinder: aStencil [ + dataBinder := aStencil asStencilBuilder: GtCoderPreviewDataBinder +] + +{ #category : #comparing } +GtCoderPreview >> hash [ + + "Answer an integer value that is related to the identity of the receiver." + + ^ id hash +] + +{ #category : #accessing } +GtCoderPreview >> id [ + + ^ id +] + +{ #category : #accessing } +GtCoderPreview >> id: anObject [ + "Assign an action id which is used to uniquely identify an action" + + id := anObject +] + +{ #category : #accessing } +GtCoderPreview >> stencil [ + ^ stencil +] + +{ #category : #accessing } +GtCoderPreview >> stencil: aStencil [ + stencil := aStencil asStencil +] + +{ #category : #accessing } +GtCoderPreview >> stencilBuilder [ + ^ self stencil +] + +{ #category : #accessing } +GtCoderPreview >> title [ + + ^ title +] + +{ #category : #accessing } +GtCoderPreview >> title: aString [ + + title := aString +] diff --git a/src/GToolkit-Coder/GtCoderPreviewDataBinder.class.st b/src/GToolkit-Coder/GtCoderPreviewDataBinder.class.st new file mode 100644 index 000000000..10e5d4434 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderPreviewDataBinder.class.st @@ -0,0 +1,53 @@ +Class { + #name : #GtCoderPreviewDataBinder, + #superclass : #BrStencilBuilder, + #instVars : [ + 'element', + 'coderViewModel' + ], + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #'api - instantiation' } +GtCoderPreviewDataBinder >> buildDefault [ + + + ^ BlElement new size: 0@0 +] + +{ #category : #accessing } +GtCoderPreviewDataBinder >> coderViewModel [ + ^ coderViewModel +] + +{ #category : #accessing } +GtCoderPreviewDataBinder >> coderViewModel: aCoderViewModel [ + coderViewModel := aCoderViewModel +] + +{ #category : #accessing } +GtCoderPreviewDataBinder >> element [ + + ^ element +] + +{ #category : #accessing } +GtCoderPreviewDataBinder >> element: anObject [ + + element := anObject +] + +{ #category : #'api - instantiation' } +GtCoderPreviewDataBinder >> paramsOn: aStencilExecutor [ + super paramsOn: aStencilExecutor. + + aStencilExecutor + push: self element; + push: self coderViewModel +] + +{ #category : #initialization } +GtCoderPreviewDataBinder >> reset [ + element := nil. + coderViewModel := nil. +] diff --git a/src/GToolkit-Coder/GtCoderPrintAnnouncement.class.st b/src/GToolkit-Coder/GtCoderPrintAnnouncement.class.st index 76c508c8d..c74b6a6bd 100644 --- a/src/GToolkit-Coder/GtCoderPrintAnnouncement.class.st +++ b/src/GToolkit-Coder/GtCoderPrintAnnouncement.class.st @@ -18,3 +18,9 @@ GtCoderPrintAnnouncement >> evaluationResult [ GtCoderPrintAnnouncement >> evaluationResult: aGtSourceCoderEvaluationResult [ evaluationResult := aGtSourceCoderEvaluationResult ] + +{ #category : #accessing } +GtCoderPrintAnnouncement >> requesterObject [ + + ^ self evaluationResult requesterObject +] diff --git a/src/GToolkit-Coder/GtCoderProtocolChanged.class.st b/src/GToolkit-Coder/GtCoderProtocolChanged.class.st new file mode 100644 index 000000000..4181e11a7 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderProtocolChanged.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtCoderProtocolChanged, + #superclass : #GtSourceCoderAnnouncement, + #instVars : [ + 'newProtocol' + ], + #category : #'GToolkit-Coder-Event' +} + +{ #category : #accessing } +GtCoderProtocolChanged >> newProtocol [ + ^ newProtocol +] + +{ #category : #accessing } +GtCoderProtocolChanged >> newProtocol: anObject [ + newProtocol := anObject +] diff --git a/src/GToolkit-Coder/GtCoderRenameMethodRefactoring.class.st b/src/GToolkit-Coder/GtCoderRenameMethodRefactoring.class.st deleted file mode 100644 index ea923009c..000000000 --- a/src/GToolkit-Coder/GtCoderRenameMethodRefactoring.class.st +++ /dev/null @@ -1,27 +0,0 @@ -Class { - #name : #GtCoderRenameMethodRefactoring, - #superclass : #RBRenameMethodRefactoring, - #instVars : [ - 'methodCoder' - ], - #category : #'GToolkit-Coder-Refactoring' -} - -{ #category : #accessing } -GtCoderRenameMethodRefactoring >> methodCoder [ - ^ methodCoder -] - -{ #category : #accessing } -GtCoderRenameMethodRefactoring >> methodCoder: anObject [ - methodCoder := anObject -] - -{ #category : #transforming } -GtCoderRenameMethodRefactoring >> transform [ - self changes addChange: (GtCoderRenameMethodChange new - methodCoder: self methodCoder; - newSelector: newSelector; - oldSelector: oldSelector). - super transform. -] diff --git a/src/GToolkit-Coder/GtCoderRenameTemporaryRequestedAnnouncement.class.st b/src/GToolkit-Coder/GtCoderRenameTemporaryRequestedAnnouncement.class.st new file mode 100644 index 000000000..79db138c1 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderRenameTemporaryRequestedAnnouncement.class.st @@ -0,0 +1,30 @@ +Class { + #name : #GtCoderRenameTemporaryRequestedAnnouncement, + #superclass : #GtSourceCoderAnnouncement, + #instVars : [ + 'variableName', + 'interestedCoders' + ], + #category : #'GToolkit-Coder-Event' +} + +{ #category : #initialization } +GtCoderRenameTemporaryRequestedAnnouncement >> initialize [ + super initialize. + interestedCoders := OrderedCollection new +] + +{ #category : #accessing } +GtCoderRenameTemporaryRequestedAnnouncement >> interestedCoders [ + ^ interestedCoders +] + +{ #category : #accessing } +GtCoderRenameTemporaryRequestedAnnouncement >> variableName [ + ^ variableName +] + +{ #category : #accessing } +GtCoderRenameTemporaryRequestedAnnouncement >> variableName: aString [ + variableName := aString +] diff --git a/src/GToolkit-Coder/GtCoderRunTestAction.class.st b/src/GToolkit-Coder/GtCoderRunTestAction.class.st new file mode 100644 index 000000000..1c4fdbd00 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderRunTestAction.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtCoderRunTestAction, + #superclass : #GtCoderAction, + #category : #'GToolkit-Coder-Coders - Addons' +} diff --git a/src/GToolkit-Coder/GtCoderShowDebuggerRequest.class.st b/src/GToolkit-Coder/GtCoderShowDebuggerRequest.class.st index 595cf3c98..fff6e9f8f 100644 --- a/src/GToolkit-Coder/GtCoderShowDebuggerRequest.class.st +++ b/src/GToolkit-Coder/GtCoderShowDebuggerRequest.class.st @@ -5,11 +5,22 @@ Class { 'debugSession', 'exception', 'sourceString', - 'sourceInterval' + 'sourceInterval', + 'isDelivered', + 'sharedDebugSession', + 'evaluationInfo' ], #category : #'GToolkit-Coder-Event' } +{ #category : #accessing } +GtCoderShowDebuggerRequest >> beDelivered [ + self + assert: [ isDelivered not ] + description: [ 'I can be delivered only once' ]. + isDelivered := true +] + { #category : #accessing } GtCoderShowDebuggerRequest >> debugSession [ ^ debugSession @@ -20,6 +31,17 @@ GtCoderShowDebuggerRequest >> debugSession: anObject [ debugSession := anObject ] +{ #category : #accessing } +GtCoderShowDebuggerRequest >> evaluationInfo [ + + ^ evaluationInfo +] + +{ #category : #accessing } +GtCoderShowDebuggerRequest >> evaluationInfo: anObject [ + evaluationInfo := anObject +] + { #category : #accessing } GtCoderShowDebuggerRequest >> exception [ ^ exception @@ -30,6 +52,31 @@ GtCoderShowDebuggerRequest >> exception: anObject [ exception := anObject ] +{ #category : #initialization } +GtCoderShowDebuggerRequest >> initialize [ + super initialize. + isDelivered := false +] + +{ #category : #accessing } +GtCoderShowDebuggerRequest >> isDelivered [ + "Return true if the request was handled (and debugger displayed)." + + + ^ isDelivered ifNil: [ false ] +] + +{ #category : #accessing } +GtCoderShowDebuggerRequest >> sharedDebugSession [ + + ^ sharedDebugSession +] + +{ #category : #accessing } +GtCoderShowDebuggerRequest >> sharedDebugSession: aGtSharedDebugSession [ + sharedDebugSession := aGtSharedDebugSession +] + { #category : #accessing } GtCoderShowDebuggerRequest >> sourceInterval [ ^ sourceInterval diff --git a/src/GToolkit-Coder/GtCoderSourceCode.class.st b/src/GToolkit-Coder/GtCoderSourceCode.class.st deleted file mode 100644 index 1b8c25438..000000000 --- a/src/GToolkit-Coder/GtCoderSourceCode.class.st +++ /dev/null @@ -1,173 +0,0 @@ -" -I represent a source code of the {{gtClass:GtSourceCoder}}. For performance reasons the computation of the actual source code in the coder is done lazily. I am to provide a necessary abstraction to support it - - -" -Class { - #name : #GtCoderSourceCode, - #superclass : #Object, - #instVars : [ - 'monitor', - 'sourceText', - 'collapsedText' - ], - #category : #'GToolkit-Coder-Coders' -} - -{ #category : #'api - text' } -GtCoderSourceCode >> appendString: aString [ - self critical: [ self sourceText appendString: aString ] -] - -{ #category : #'api - text' } -GtCoderSourceCode >> appendText: aText [ - self critical: [ self sourceText append: aText ] -] - -{ #category : #'api - accessing' } -GtCoderSourceCode >> asAstCacheKey [ - ^ self subclassResponsibility -] - -{ #category : #'api - converting' } -GtCoderSourceCode >> asCompiledMethodSourceCode: aCompiledMethod [ - ^ (GtCoderCompiledMethodSourceCode new fromSourceCode: self) compiledMethod: aCompiledMethod -] - -{ #category : #'api - converting' } -GtCoderSourceCode >> asExplicitSourceCode: aString [ - ^ (GtCoderExplicitSourceCode new fromSourceCode: self) source: aString -] - -{ #category : #initialization } -GtCoderSourceCode >> buildCollapsedText [ - - - ^ self subclassResponsibility -] - -{ #category : #initialization } -GtCoderSourceCode >> buildSourceText [ - - - ^ self subclassResponsibility -] - -{ #category : #'api - accessing' } -GtCoderSourceCode >> collapsedText [ - - - ^ self critical: [ collapsedText ifNil: [ collapsedText := self buildCollapsedText ] ] -] - -{ #category : #'api - accessing' } -GtCoderSourceCode >> collapsedTextDo: aBlock [ - "Evaluate a given block with my source code if present" - - ^ self critical: [ collapsedText ifNotNil: aBlock ] -] - -{ #category : #'mutual exclusion' } -GtCoderSourceCode >> critical: aBlock [ - ^ monitor critical: aBlock -] - -{ #category : #'api - text' } -GtCoderSourceCode >> currentSourceString: aString [ - "Set a new source text" - - self critical: [ sourceText := aString asRopedText ] -] - -{ #category : #'api - text' } -GtCoderSourceCode >> currentSourceText: aText [ - "Set a new source text" - - self critical: [ sourceText := aText copyWithoutExternalReferences ] -] - -{ #category : #'api - text' } -GtCoderSourceCode >> delete: aFromIndex to: aToIndex [ - self sourceTextDo: [ :aText | aText delete: aFromIndex to: aToIndex ] -] - -{ #category : #initialization } -GtCoderSourceCode >> fromSourceCode: aGtCoderSourceCode [ - sourceText := nil. - collapsedText := nil. - - aGtCoderSourceCode sourceTextDo: [ :aSourceText | sourceText := aSourceText ]. - aGtCoderSourceCode collapsedTextDo: [ :aCollapsedText | collapsedText := aCollapsedText ]. -] - -{ #category : #'api - testing' } -GtCoderSourceCode >> hasCollapsedText [ - - - ^ self critical: [ collapsedText notNil ] -] - -{ #category : #'api - testing' } -GtCoderSourceCode >> hasSourceText [ - - - ^ self critical: [ sourceText notNil ] -] - -{ #category : #initialization } -GtCoderSourceCode >> initialize [ - super initialize. - - monitor := Monitor new -] - -{ #category : #'api - testing' } -GtCoderSourceCode >> isModified [ - ^ self subclassResponsibility -] - -{ #category : #'api - text' } -GtCoderSourceCode >> replaceFrom: aFromIndex to: aToIndex withString: aString [ - self critical: [ self sourceText replace: aFromIndex to: aToIndex withString: aString ] -] - -{ #category : #'api - text' } -GtCoderSourceCode >> resetCollapsedText [ - "Remove the cached collapsed text forcing it to be recomputed next time it is accessed" - - collapsedText := nil -] - -{ #category : #'api - text' } -GtCoderSourceCode >> resetSourceText [ - "Remove the cached source text forcing it to be recomputed next time it is accessed" - - sourceText := nil -] - -{ #category : #'api - accessing' } -GtCoderSourceCode >> sourceString [ - "Return a string representation of the current source text" - - - ^ self critical: [ self sourceText asString ] -] - -{ #category : #'api - accessing' } -GtCoderSourceCode >> sourceText [ - - - ^ self critical: [ sourceText ifNil: [ sourceText := self buildSourceText ] ] -] - -{ #category : #'api - accessing' } -GtCoderSourceCode >> sourceText: aText [ - self critical: [ sourceText := aText copyWithoutExternalReferences ] -] - -{ #category : #'api - accessing' } -GtCoderSourceCode >> sourceTextDo: aBlock [ - "Evaluate a given block with my source code if present" - - ^ self critical: [ sourceText ifNotNil: [ :aText | aBlock value: aText ] ] -] diff --git a/src/GToolkit-Coder/GtCoderSourceCodeChanged.class.st b/src/GToolkit-Coder/GtCoderSourceCodeChanged.class.st index 2b321f019..7a34a6439 100644 --- a/src/GToolkit-Coder/GtCoderSourceCodeChanged.class.st +++ b/src/GToolkit-Coder/GtCoderSourceCodeChanged.class.st @@ -7,7 +7,10 @@ Class { #name : #GtCoderSourceCodeChanged, #superclass : #GtSourceCoderAnnouncement, #instVars : [ - 'updateStragegy' + 'updateStragegy', + 'newText', + 'previousText', + 'reason' ], #category : #'GToolkit-Coder-Event' } @@ -18,11 +21,51 @@ GtCoderSourceCodeChanged >> isSynchronous [ and: [ self updateStrategy isSynchronous ] ] +{ #category : #accessing } +GtCoderSourceCodeChanged >> newText [ + + ^ newText +] + +{ #category : #accessing } +GtCoderSourceCodeChanged >> newText: aText [ + + newText := aText +] + +{ #category : #accessing } +GtCoderSourceCodeChanged >> previousText [ + ^ previousText +] + +{ #category : #accessing } +GtCoderSourceCodeChanged >> previousText: aText [ + previousText := aText +] + +{ #category : #accessing } +GtCoderSourceCodeChanged >> reason [ + ^ reason +] + +{ #category : #accessing } +GtCoderSourceCodeChanged >> reason: anObject [ + reason := anObject +] + { #category : #accessing } GtCoderSourceCodeChanged >> source [ ^ self updateStrategy source ] +{ #category : #accessing } +GtCoderSourceCodeChanged >> textEditCommand [ + ^ self reason ifNotNil: [ :aReason| + (aReason isKindOf: BrTextEditorModifiedEvent) + ifTrue: [ aReason editCommand ] + ifFalse: [ nil ] ] +] + { #category : #accessing } GtCoderSourceCodeChanged >> updateStrategy [ ^ updateStragegy diff --git a/src/GToolkit-Coder/GtCoderSourceCodeReplaced.class.st b/src/GToolkit-Coder/GtCoderSourceCodeReplaced.class.st new file mode 100644 index 000000000..982e187c2 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderSourceCodeReplaced.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtCoderSourceCodeReplaced, + #superclass : #GtSourceCoderAnnouncement, + #instVars : [ + 'sourceCode' + ], + #category : #'GToolkit-Coder-Event' +} + +{ #category : #accessing } +GtCoderSourceCodeReplaced >> sourceCode [ + + ^ sourceCode +] + +{ #category : #accessing } +GtCoderSourceCodeReplaced >> sourceCode: anObject [ + + sourceCode := anObject +] diff --git a/src/GToolkit-Coder/GtCoderSourceEpoch.class.st b/src/GToolkit-Coder/GtCoderSourceEpoch.class.st new file mode 100644 index 000000000..74849c67e --- /dev/null +++ b/src/GToolkit-Coder/GtCoderSourceEpoch.class.st @@ -0,0 +1,25 @@ +Class { + #name : #GtCoderSourceEpoch, + #superclass : #Object, + #instVars : [ + 'timestamp' + ], + #category : #'GToolkit-Coder-Coders - Source Text' +} + +{ #category : #accessing } +GtCoderSourceEpoch >> age [ + ^ DateAndTime now - timestamp +] + +{ #category : #initialization } +GtCoderSourceEpoch >> initialize [ + super initialize. + + timestamp := DateAndTime now +] + +{ #category : #accessing } +GtCoderSourceEpoch >> nextEpoch [ + ^ self class new +] diff --git a/src/GToolkit-Coder/GtCoderSourceString.class.st b/src/GToolkit-Coder/GtCoderSourceString.class.st new file mode 100644 index 000000000..c1f1644e7 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderSourceString.class.st @@ -0,0 +1,46 @@ +Class { + #name : #GtCoderSourceString, + #superclass : #Object, + #instVars : [ + 'sourceText', + 'string', + 'epoch' + ], + #category : #'GToolkit-Coder-Coders - Source Text' +} + +{ #category : #accessing } +GtCoderSourceString >> epoch [ + + ^ epoch +] + +{ #category : #accessing } +GtCoderSourceString >> epoch: anObject [ + epoch := anObject +] + +{ #category : #accessing } +GtCoderSourceString >> sourceText [ + + + ^ sourceText +] + +{ #category : #accessing } +GtCoderSourceString >> sourceText: aGtCoderSourceText [ + + sourceText := aGtCoderSourceText +] + +{ #category : #accessing } +GtCoderSourceString >> string [ + + ^ string +] + +{ #category : #accessing } +GtCoderSourceString >> string: anObject [ + + string := anObject +] diff --git a/src/GToolkit-Coder/GtCoderSourceText.class.st b/src/GToolkit-Coder/GtCoderSourceText.class.st new file mode 100644 index 000000000..b964cb7f4 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderSourceText.class.st @@ -0,0 +1,47 @@ +Class { + #name : #GtCoderSourceText, + #superclass : #Object, + #instVars : [ + 'text', + 'epoch' + ], + #category : #'GToolkit-Coder-Coders - Source Text' +} + +{ #category : #converting } +GtCoderSourceText >> asSourceString [ + + + ^ GtCoderSourceString new + sourceText: self; + string: text asString; + epoch: epoch +] + +{ #category : #accessing } +GtCoderSourceText >> epoch [ + ^ epoch +] + +{ #category : #initialization } +GtCoderSourceText >> initialize [ + super initialize. + + epoch := GtCoderSourceEpoch new +] + +{ #category : #accessing } +GtCoderSourceText >> text [ + + ^ text +] + +{ #category : #accessing } +GtCoderSourceText >> text: aBlText [ + self + assert: [ text isNil ] + description: [ 'Source text can not be modified' ]. + + text := aBlText. + epoch := epoch nextEpoch +] diff --git a/src/GToolkit-Coder/GtCoderStylerChanged.class.st b/src/GToolkit-Coder/GtCoderStylerChanged.class.st deleted file mode 100644 index 2eb792f81..000000000 --- a/src/GToolkit-Coder/GtCoderStylerChanged.class.st +++ /dev/null @@ -1,26 +0,0 @@ -" -1. Coder styler changed event -** - -Is announced by the {{gtClass:GtCoderModel}} when stylers change. - - -" -Class { - #name : #GtCoderStylerChanged, - #superclass : #GtSourceCoderAnnouncement, - #instVars : [ - 'stylers' - ], - #category : #'GToolkit-Coder-Event' -} - -{ #category : #accessing } -GtCoderStylerChanged >> stylers [ - ^ stylers -] - -{ #category : #accessing } -GtCoderStylerChanged >> stylers: anObject [ - stylers := anObject -] diff --git a/src/GToolkit-Coder/GtCoderTextSource.class.st b/src/GToolkit-Coder/GtCoderTextSource.class.st new file mode 100644 index 000000000..1ee9c2914 --- /dev/null +++ b/src/GToolkit-Coder/GtCoderTextSource.class.st @@ -0,0 +1,142 @@ +" +I represent a source code of the {{gtClass:GtTextualCoder}}. For performance reasons the computation of the actual source code in the coder is done lazily. I am to provide a necessary abstraction to support it +" +Class { + #name : #GtCoderTextSource, + #superclass : #Object, + #instVars : [ + 'monitor', + 'currentSourceText', + 'originalSourceText' + ], + #category : #'GToolkit-Coder-Coders - Source Text' +} + +{ #category : #'api - converting' } +GtCoderTextSource >> asCompiledMethodSourceCode: aCompiledMethod [ + ^ (GtCoderCompiledMethodTextSource new fromSourceCode: self) compiledMethod: aCompiledMethod +] + +{ #category : #initialization } +GtCoderTextSource >> computeOriginalSourceText [ + ^ self subclassResponsibility +] + +{ #category : #'mutual exclusion' } +GtCoderTextSource >> critical: aBlock [ + ^ monitor critical: aBlock +] + +{ #category : #'api - accessing' } +GtCoderTextSource >> currentSourceString [ + "Return a string representation of the current source text" + + + ^ self critical: [ self currentSourceText asSourceString ] +] + +{ #category : #'api - accessing' } +GtCoderTextSource >> currentSourceString: aString [ + "Set a new source text" + + self critical: [ currentSourceText := GtCoderSourceText new + text: aString asRopedText; + yourself ] +] + +{ #category : #'api - accessing' } +GtCoderTextSource >> currentSourceText [ + + + ^ self critical: [ currentSourceText ifNil: [ self originalSourceText ] ] +] + +{ #category : #'api - accessing' } +GtCoderTextSource >> currentSourceText: aText [ + "Set a new source text" + + self critical: [ + currentSourceText := GtCoderSourceText new + text: aText copyWithoutExternalReferences; + yourself ] +] + +{ #category : #initialization } +GtCoderTextSource >> fromSourceCode: aGtCoderTextSource [ + originalSourceText := aGtCoderTextSource privateGetOriginalSourceText. + currentSourceText := aGtCoderTextSource privateGetCurrentSourceText +] + +{ #category : #'api - testing' } +GtCoderTextSource >> hasSourceText [ + + + ^ originalSourceText notNil +] + +{ #category : #initialization } +GtCoderTextSource >> initialize [ + super initialize. + + monitor := Mutex new. + currentSourceText := nil. + originalSourceText := nil +] + +{ #category : #'api - testing' } +GtCoderTextSource >> isModified [ + ^ self critical: [ + currentSourceText + ifNil: [ ^ false ]. + self originalSourceText text characters ~= self currentSourceText text characters ] +] + +{ #category : #'api - accessing' } +GtCoderTextSource >> originalSourceText [ + ^ self + critical: [ originalSourceText + ifNil: [ originalSourceText := GtCoderSourceText new + text: self computeOriginalSourceText ] ] +] + +{ #category : #'private - accessing' } +GtCoderTextSource >> privateGetCurrentSourceText [ + ^ currentSourceText +] + +{ #category : #'private - accessing' } +GtCoderTextSource >> privateGetOriginalSourceText [ + ^ originalSourceText +] + +{ #category : #'api - accessing' } +GtCoderTextSource >> resetSourceText [ + "Remove the cached source text forcing it to be recomputed next time it is accessed" + + self critical: [ + originalSourceText := nil. + currentSourceText := nil ] +] + +{ #category : #'api - accessing' } +GtCoderTextSource >> sourceString [ + "Return a string representation of the current source text" + + + self + deprecated: 'Use #currentSourceString' + transformWith: '`@receiver sourceString' -> '`@receiver currentSourceString'. + + ^ self currentSourceString +] + +{ #category : #'api - accessing' } +GtCoderTextSource >> sourceText [ + + + self + deprecated: 'Use #currentSourceText' + transformWith: '`@receiver sourceText' -> '`@receiver currentSourceText'. + + ^ self currentSourceText +] diff --git a/src/GToolkit-Coder/GtCoderToggleAction.class.st b/src/GToolkit-Coder/GtCoderToggleAction.class.st new file mode 100644 index 000000000..9a6eea96b --- /dev/null +++ b/src/GToolkit-Coder/GtCoderToggleAction.class.st @@ -0,0 +1,46 @@ +Class { + #name : #GtCoderToggleAction, + #superclass : #GtCoderAction, + #instVars : [ + 'activateBlock', + 'deactivateBlock', + 'toggleModel' + ], + #category : #'GToolkit-Coder-Coders - Addons' +} + +{ #category : #accessing } +GtCoderToggleAction >> activateBlock: aBlock [ + activateBlock := aBlock +] + +{ #category : #initialization } +GtCoderToggleAction >> computeActionDefinition [ + activateBlock isClosure + ifTrue: [ ^ GtCoderActionClosureDefinition new closure: activateBlock ]. + deactivateBlock isClosure + ifTrue: [ ^ GtCoderActionClosureDefinition new closure: deactivateBlock ]. + ^ nil +] + +{ #category : #accessing } +GtCoderToggleAction >> deactivateBlock: aBlock [ + deactivateBlock := aBlock +] + +{ #category : #accessing } +GtCoderToggleAction >> initialize [ + super initialize. + activateBlock := deactivateBlock := [ :evt | ] +] + +{ #category : #accessing } +GtCoderToggleAction >> toggleModel [ + + ^ toggleModel +] + +{ #category : #accessing } +GtCoderToggleAction >> toggleModel: aToggleModel [ + toggleModel := aToggleModel +] diff --git a/src/GToolkit-Coder/GtCoderToolSpawnRequest.class.st b/src/GToolkit-Coder/GtCoderToolSpawnRequest.class.st index 3a372ebe5..2fece2058 100644 --- a/src/GToolkit-Coder/GtCoderToolSpawnRequest.class.st +++ b/src/GToolkit-Coder/GtCoderToolSpawnRequest.class.st @@ -3,11 +3,23 @@ Class { #superclass : #GtSourceCoderAnnouncement, #instVars : [ 'tool', - 'spawnDestination' + 'spawnDestination', + 'requesterObject' ], #category : #'GToolkit-Coder-Event' } +{ #category : #accessing } +GtCoderToolSpawnRequest >> requesterObject [ + + ^ requesterObject ifNil: [ GtNoCoderRequester uniqueInstance ] +] + +{ #category : #accessing } +GtCoderToolSpawnRequest >> requesterObject: anObject [ + requesterObject := anObject +] + { #category : #accessing } GtCoderToolSpawnRequest >> spawnDestination [ ^ spawnDestination diff --git a/src/GToolkit-Coder/GtCoderUndefinedObject.class.st b/src/GToolkit-Coder/GtCoderUndefinedObject.class.st new file mode 100644 index 000000000..e540739ca --- /dev/null +++ b/src/GToolkit-Coder/GtCoderUndefinedObject.class.st @@ -0,0 +1,12 @@ +Class { + #name : #GtCoderUndefinedObject, + #superclass : #GtPhlowUndefinedObject, + #category : #'GToolkit-Coder-Object Holder' +} + +{ #category : #testing } +GtCoderUndefinedObject class >> isDeprecated [ + "Use my superclass" + + ^ true +] diff --git a/src/GToolkit-Coder/GtCoderUpdateStrategy.class.st b/src/GToolkit-Coder/GtCoderUpdateStrategy.class.st index 9170c07f9..d4a0d8068 100644 --- a/src/GToolkit-Coder/GtCoderUpdateStrategy.class.st +++ b/src/GToolkit-Coder/GtCoderUpdateStrategy.class.st @@ -10,11 +10,36 @@ Class { #superclass : #Object, #instVars : [ 'announcementSource', - 'isSynchronous' + 'isSynchronous', + 'properties', + 'textEditCommand' ], #category : #'GToolkit-Coder-Coders' } +{ #category : #'gt - extensions' } +GtCoderUpdateStrategy >> gtViewPropertiesFor: aView [ + + + (properties isNil or: [ properties isEmpty ]) ifTrue: [ ^ aView empty ]. + + ^ aView forward + title: 'Properties'; + priority: 100; + object: [ properties ]; + view: #gtItemsFor: +] + +{ #category : #'accessing - properties' } +GtCoderUpdateStrategy >> hasPropertyNamed: aPropertyName [ + ^ self properties includesKey: aPropertyName +] + +{ #category : #testing } +GtCoderUpdateStrategy >> hasTextEditCommand [ + ^ self textEditCommand notNil +] + { #category : #accessing } GtCoderUpdateStrategy >> isSynchronous [ ^ isSynchronous ifNil: [ false ] @@ -35,6 +60,32 @@ GtCoderUpdateStrategy >> makeSynchronous [ isSynchronous := true ] +{ #category : #copying } +GtCoderUpdateStrategy >> postCopy [ + properties := properties copy +] + +{ #category : #'accessing - properties' } +GtCoderUpdateStrategy >> properties [ + ^ properties ifNil: [ + properties := OrderedDictionary new] +] + +{ #category : #'accessing - properties' } +GtCoderUpdateStrategy >> propertyNamed: aPropertyName [ + ^ self properties at: aPropertyName +] + +{ #category : #'accessing - properties' } +GtCoderUpdateStrategy >> propertyNamed: aPropertyName ifAbsent: anAbsentBlock [ + ^ self properties at: aPropertyName ifAbsent: anAbsentBlock +] + +{ #category : #'accessing - properties' } +GtCoderUpdateStrategy >> propertyNamed: aPropertyName put: aValue [ + ^ self properties at: aPropertyName put: aValue +] + { #category : #accessing } GtCoderUpdateStrategy >> source [ ^ announcementSource @@ -44,3 +95,13 @@ GtCoderUpdateStrategy >> source [ GtCoderUpdateStrategy >> source: anObject [ announcementSource := anObject ] + +{ #category : #accessing } +GtCoderUpdateStrategy >> textEditCommand [ + ^ textEditCommand +] + +{ #category : #accessing } +GtCoderUpdateStrategy >> textEditCommand: anObject [ + textEditCommand := anObject +] diff --git a/src/GToolkit-Coder/GtCodersCodersChanged.class.st b/src/GToolkit-Coder/GtCodersCodersChanged.class.st index fc8549497..85025c36e 100644 --- a/src/GToolkit-Coder/GtCodersCodersChanged.class.st +++ b/src/GToolkit-Coder/GtCodersCodersChanged.class.st @@ -1,5 +1,54 @@ Class { #name : #GtCodersCodersChanged, #superclass : #GtCodersAnnouncement, + #instVars : [ + 'newCoders', + 'oldCoders' + ], #category : #'GToolkit-Coder-Event' } + +{ #category : #accessing } +GtCodersCodersChanged >> addedCoders [ + + ^ self newCoders reject: [ :aCoder | + self oldCoders identityIncludes: aCoder ] +] + +{ #category : #accessing } +GtCodersCodersChanged >> newCoders [ + ^ newCoders +] + +{ #category : #accessing } +GtCodersCodersChanged >> newCoders: aCollection [ + + self + assert: [ aCollection isNotNil ] + description: [ 'New coders collection must be non-nil' ]. + + newCoders := aCollection +] + +{ #category : #accessing } +GtCodersCodersChanged >> oldCoders [ + + ^ oldCoders +] + +{ #category : #accessing } +GtCodersCodersChanged >> oldCoders: aCollection [ + + self + assert: [ aCollection isNotNil ] + description: [ 'Old coders collection must be non-nil' ]. + + oldCoders := aCollection +] + +{ #category : #accessing } +GtCodersCodersChanged >> removedCoders [ + + ^ self oldCoders reject: [ :aCoder | + self newCoders identityIncludes: aCoder ] +] diff --git a/src/GToolkit-Coder/GtCodersModel.class.st b/src/GToolkit-Coder/GtCodersModel.class.st index 7f49bb960..fee752fe0 100644 --- a/src/GToolkit-Coder/GtCodersModel.class.st +++ b/src/GToolkit-Coder/GtCodersModel.class.st @@ -116,10 +116,14 @@ GtCodersModel >> unsubscribeFromSystem [ { #category : #updating } GtCodersModel >> updateCoders [ + | oldCoders | + oldCoders := coders. coders := items collect: [ :each | self newCoderFor: each ]. self announce: (GtCodersCodersChanged new coders: self; + oldCoders: oldCoders; + newCoders: coders; yourself) ] diff --git a/src/GToolkit-Coder/GtCompositeDiffChange.class.st b/src/GToolkit-Coder/GtCompositeDiffChange.class.st new file mode 100644 index 000000000..b3c2a91ae --- /dev/null +++ b/src/GToolkit-Coder/GtCompositeDiffChange.class.st @@ -0,0 +1,188 @@ +Class { + #name : #GtCompositeDiffChange, + #superclass : #GtDiffChange, + #instVars : [ + 'changes', + 'from', + 'to', + 'isLineBased' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #'instance creation' } +GtCompositeDiffChange class >> forChanges: aCollection [ + ^ self new + changes: aCollection; + yourself +] + +{ #category : #'instance creation' } +GtCompositeDiffChange class >> from: from to: to changes: aCollection [ + ^ self new + from: from; + to: to; + changes: aCollection; + yourself +] + +{ #category : #formatting } +GtCompositeDiffChange >> applyAttributesToInput: aText [ + changes do: [ :each | each applyAttributesToInput: aText ] +] + +{ #category : #formatting } +GtCompositeDiffChange >> applyAttributesToOutput: aText [ + changes do: [ :each | each applyAttributesToOutput: aText ] +] + +{ #category : #accessing } +GtCompositeDiffChange >> changes [ + ^ changes +] + +{ #category : #accessing } +GtCompositeDiffChange >> changes: aCollection [ + changes := aCollection +] + +{ #category : #iterating } +GtCompositeDiffChange >> deletionChangesDo: aBlock [ + changes do: [ :each | each deletionChangesDo: aBlock ] +] + +{ #category : #accessing } +GtCompositeDiffChange >> from [ + ^ from +] + +{ #category : #accessing } +GtCompositeDiffChange >> from: anObject [ + from := anObject +] + +{ #category : #inspecting } +GtCompositeDiffChange >> gtChangeViewFor: aView [ + + ^ aView columnedTree + priority: 10; + title: 'Changes'; + items: [ self changes ]; + children: [ :each | + each isReplacement + ifTrue: [ {each deletionChange. + each insertionChange} ] + ifFalse: [ #() ] ]; + column: 'Kind' + text: [ :each | (each class name removePrefix: 'Gt') removeSuffix: 'DiffChange' ] + width: 150; + column: 'Range' + text: [ :each | each range ] + width: 100; + column: 'Objects' text: [ :each | each gtObjectsText ] +] + +{ #category : #inspecting } +GtCompositeDiffChange >> gtListView: aView [ + + from isString ifTrue: [ ^ aView empty ]. + ^ aView explicit + priority: 10; + title: 'List'; + stencil: [ | pane fromList toList deletedIndices insertedIndices | + deletedIndices := Set new. + self + deletionChangesDo: [ :each | deletedIndices addAll: (each startIndex to: each stopIndex) ]. + insertedIndices := Set new. + self + insertionChangesDo: [ :each | insertedIndices addAll: (each startIndex to: each stopIndex) ]. + pane := BrHorizontalPane new. + pane matchParent. + pane padding: (BlInsets all: 10). + fromList := BrColumnedList new. + fromList matchParent. + fromList column + width: 45; + title: 'Index'; + stencil: [ :each :i | + BrLabel new + aptitude: BrGlamorousLabelAptitude new; + text: (i asRopedText foreground: Color gray) ]. + fromList column + title: 'Value'; + stencil: [ :each :i | + | text | + text := each gtDisplayText. + (deletedIndices includes: i) + ifTrue: [ text highlight: GtDeletionDiffChange highlightColor ]. + BrLabel new + aptitude: BrGlamorousLabelAptitude new; + text: text ]. + fromList items: from. + toList := BrColumnedList new. + toList matchParent. + toList column + width: 45; + title: 'Index'; + stencil: [ :each :i | + BrLabel new + aptitude: BrGlamorousLabelAptitude new; + text: (i asRopedText foreground: Color gray) ]. + toList column + title: 'Value'; + stencil: [ :each :i | + | text | + text := each gtDisplayText. + (insertedIndices includes: i) + ifTrue: [ text highlight: GtInsertionDiffChange highlightColor ]. + BrLabel new + aptitude: BrGlamorousLabelAptitude new; + text: text ]. + toList items: to. + pane + addChild: fromList; + addChild: toList. + pane ] +] + +{ #category : #inspecting } +GtCompositeDiffChange >> gtTextView: aView [ + + from isString ifFalse: [ ^ aView empty ]. + ^ aView explicit + priority: 10; + title: 'Text'; + stencil: [ self asElement ] +] + +{ #category : #initialization } +GtCompositeDiffChange >> initialize [ + super initialize. + changes := #(). + isLineBased := false +] + +{ #category : #iterating } +GtCompositeDiffChange >> insertionChangesDo: aBlock [ + changes do: [ :each | each insertionChangesDo: aBlock ] +] + +{ #category : #accessing } +GtCompositeDiffChange >> isLineBased [ + ^ isLineBased +] + +{ #category : #accessing } +GtCompositeDiffChange >> isLineBased: aBoolean [ + isLineBased := aBoolean +] + +{ #category : #accessing } +GtCompositeDiffChange >> to [ + ^ to +] + +{ #category : #accessing } +GtCompositeDiffChange >> to: aCollection [ + to := aCollection +] diff --git a/src/GToolkit-Coder/GtDeletionDiffChange.class.st b/src/GToolkit-Coder/GtDeletionDiffChange.class.st new file mode 100644 index 000000000..acbe6ea7b --- /dev/null +++ b/src/GToolkit-Coder/GtDeletionDiffChange.class.st @@ -0,0 +1,47 @@ +Class { + #name : #GtDeletionDiffChange, + #superclass : #GtSourceDiffChange, + #instVars : [ + 'newIndex' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #accessing } +GtDeletionDiffChange class >> highlightColor [ + ^ BrGlamorousColors errorBackgroundColor +] + +{ #category : #accessing } +GtDeletionDiffChange >> applyAttributesToInput: aText [ + aText + attribute: (BlTextHighlightAttribute paint: self highlightColor) + beNotOverwritableByStyler + from: self startIndex + to: self stopIndex +] + +{ #category : #accessing } +GtDeletionDiffChange >> deletedObjects [ + ^ self objects +] + +{ #category : #accessing } +GtDeletionDiffChange >> deletionChange [ + ^ self +] + +{ #category : #testing } +GtDeletionDiffChange >> isDeletion [ + ^ true +] + +{ #category : #accessing } +GtDeletionDiffChange >> newIndex [ + ^ newIndex +] + +{ #category : #accessing } +GtDeletionDiffChange >> newIndex: anInteger [ + newIndex := anInteger +] diff --git a/src/GToolkit-Coder/GtDiffBuilder.class.st b/src/GToolkit-Coder/GtDiffBuilder.class.st new file mode 100644 index 000000000..5fa514e03 --- /dev/null +++ b/src/GToolkit-Coder/GtDiffBuilder.class.st @@ -0,0 +1,271 @@ +Class { + #name : #GtDiffBuilder, + #superclass : #Object, + #instVars : [ + 'splitter', + 'from', + 'to', + 'fromSplits', + 'toSplits', + 'fromStart', + 'fromEnd', + 'toStart', + 'toEnd', + 'differences' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #computing } +GtDiffBuilder class >> computeDifferencesFrom: fromCollection to: toCollection using: aGtDiffSplitter [ + | builder | + builder := self new. + builder from: fromCollection. + builder to: toCollection. + builder splitter: aGtDiffSplitter. + ^ builder computeDifferences +] + +{ #category : #private } +GtDiffBuilder >> addDeletions [ + | startIndex stopIndex | + startIndex := fromStart > 1 + ifTrue: [ (fromSplits at: fromStart) startIndex ] + ifFalse: [ 1 ]. + stopIndex := fromEnd < from size + ifTrue: [ (fromSplits at: fromEnd) stopIndex ] + ifFalse: [ from size ]. + differences + add: ((GtDeletionDiffChange + on: from + from: startIndex + to: stopIndex) + newIndex: (toStart > toSplits size + ifTrue: [ to size + 1 ] + ifFalse: [ (toSplits at: toStart) startIndex ])) +] + +{ #category : #private } +GtDiffBuilder >> addInsertions [ + | startIndex stopIndex | + startIndex := toStart > 1 + ifTrue: [ (toSplits at: toStart) startIndex ] + ifFalse: [ 1 ]. + stopIndex := toEnd < to size + ifTrue: [ (toSplits at: toEnd) stopIndex ] + ifFalse: [ to size ]. + differences + add: ((GtInsertionDiffChange + on: to + from: startIndex + to: stopIndex) + originalIndex: (fromStart > fromSplits size + ifTrue: [ from size + 1 ] + ifFalse: [ (fromSplits at: fromStart) startIndex ])) +] + +{ #category : #private } +GtDiffBuilder >> addInsertionsAndDeletionsIgnoring [ + [ fromStart <= fromEnd or: [ toStart <= toEnd ] ] + whileTrue: [ | deletion insertion start end split fromTop | + fromTop := fromStart. + [ fromStart <= fromEnd + and: [ (split := fromSplits at: fromStart) hasMatch not ] ] + whileTrue: [ start ifNil: [ start := split startIndex ]. + end := split stopIndex. + fromStart := fromStart + 1 ]. + start + ifNotNil: [ deletion := GtDeletionDiffChange + on: from + from: start + to: end. + deletion + newIndex: (toStart > toSplits size + ifTrue: [ to size + 1 ] + ifFalse: [ (toSplits at: toStart) startIndex ]) ]. + start := nil. + [ toStart <= toEnd and: [ (split := toSplits at: toStart) hasMatch not ] ] + whileTrue: [ start ifNil: [ start := split startIndex ]. + end := split stopIndex. + toStart := toStart + 1 ]. + start + ifNotNil: [ insertion := GtInsertionDiffChange + on: to + from: start + to: end. + insertion + originalIndex: (fromTop > fromSplits size + ifTrue: [ from size + 1 ] + ifFalse: [ (fromSplits at: fromTop) startIndex ]) ]. + deletion + ifNil: [ differences add: insertion ] + ifNotNil: [ insertion + ifNil: [ differences add: deletion ] + ifNotNil: [ differences + add: (GtReplacementDiffChange delete: deletion andInsert: insertion) ] ]. + [ fromStart <= fromEnd + and: [ toStart <= toEnd + and: [ (fromSplits at: fromStart) hasMatch and: [ (toSplits at: toStart) hasMatch ] ] ] ] + whileTrue: [ fromStart := fromStart + 1. + toStart := toStart + 1 ] ] +] + +{ #category : #private } +GtDiffBuilder >> buildDifferencesList [ + differences := OrderedCollection new. + fromStart > fromEnd + ifTrue: [ toStart > toEnd ifTrue: [ ^ self ]. + ^ self addInsertions ]. + toStart > toEnd ifTrue: [ ^ self addDeletions ]. + self computeLCSElements +] + +{ #category : #computing } +GtDiffBuilder >> computeDifferences [ + | change | + self initializeSplits. + self skipEqualElementsAtStart. + self skipEqualElementsAtEnd. + self buildDifferencesList. + change := GtCompositeDiffChange + from: from + to: to + changes: differences. + change isLineBased: splitter isLineBased. + ^ change +] + +{ #category : #private } +GtDiffBuilder >> computeLCSElements [ + | filter filteredFrom filteredTo | + filter := Set new. + fromStart to: fromEnd do: [ :i | filter add: (fromSplits at: i) ]. + filteredTo := OrderedCollection new. + toStart + to: toEnd + do: [ :i | + | value | + (filter includes: (value := toSplits at: i)) + ifTrue: [ filteredTo add: value ] ]. + filter := Set new. + toStart to: toEnd do: [ :i | filter add: (toSplits at: i) ]. + filteredFrom := OrderedCollection new. + fromStart + to: fromEnd + do: [ :i | + | value | + (filter includes: (value := fromSplits at: i)) + ifTrue: [ filteredFrom add: value ] ]. + self computeLCSElementsBetween: filteredFrom and: filteredTo. + self addInsertionsAndDeletionsIgnoring +] + +{ #category : #private } +GtDiffBuilder >> computeLCSElementsBetween: filteredFrom and: filteredTo [ + | map fromNumbers toNumbers lcs | + (filteredFrom isEmpty or: [ filteredTo isEmpty ]) ifTrue: [ ^ self ]. + map := Dictionary new. + fromNumbers := filteredFrom + collect: [ :each | map at: each ifAbsentPut: [ map size ] ]. + toNumbers := filteredTo + collect: [ :each | map at: each ifAbsentPut: [ map size ] ]. + lcs := self lcsFor: fromNumbers and: toNumbers. + [ lcs notNil ] + whileTrue: [ (filteredFrom at: lcs first) match. + (filteredTo at: lcs second) match. + lcs := lcs last ] +] + +{ #category : #accessing } +GtDiffBuilder >> from [ + ^ from +] + +{ #category : #accessing } +GtDiffBuilder >> from: aCollection [ + from := aCollection +] + +{ #category : #private } +GtDiffBuilder >> initializeSplits [ + fromSplits := splitter split: from. + toSplits := splitter split: to +] + +{ #category : #private } +GtDiffBuilder >> lcsFor: fromNumbers and: toNumbers [ + | fromSize toSize endPoints lcss maxSize | + fromSize := fromNumbers size. + toSize := toNumbers size. + maxSize := toSize + fromSize. + endPoints := Array new: 2 * maxSize + 1. + endPoints at: maxSize + 2 put: 0. + lcss := Array new: 2 * maxSize + 1. + 0 + to: maxSize + do: [ :d | + d negated + to: d + by: 2 + do: [ :k | + | index lcs x y | + index := maxSize + k. + (k + d = 0 or: [ k ~= d and: [ (endPoints at: index) < (endPoints at: index + 2) ] ]) + ifTrue: [ x := endPoints at: (index := index + 2) ] + ifFalse: [ x := (endPoints at: index) + 1 ]. + y := x - k. + lcs := lcss at: index. + [ x < fromSize + and: [ y < toSize and: [ (fromNumbers at: x + 1) = (toNumbers at: y + 1) ] ] ] + whileTrue: [ lcs := {x := x + 1. + y := y + 1. + lcs} ]. + (x >= fromSize and: [ y >= toSize ]) ifTrue: [ ^ lcs ]. + endPoints at: maxSize + k + 1 put: x. + lcss at: maxSize + k + 1 put: lcs ] ]. +] + +{ #category : #private } +GtDiffBuilder >> skipEqualElementsAtEnd [ + fromEnd := fromSplits size. + toEnd := toSplits size. + [ fromEnd >= fromStart + and: [ toEnd >= toStart and: [ (fromSplits at: fromEnd) = (toSplits at: toEnd) ] ] ] + whileTrue: [ fromEnd := fromEnd - 1. + toEnd := toEnd - 1 ] +] + +{ #category : #private } +GtDiffBuilder >> skipEqualElementsAtStart [ + | fromObject toObject | + fromStart := 1. + [ fromStart <= fromSplits size + and: [ fromStart <= toSplits size + and: [ fromObject := fromSplits at: fromStart. + toObject := toSplits at: fromStart. + fromObject = toObject ] ] ] + whileTrue: [ fromObject match. + toObject match. + fromStart := fromStart + 1 ]. + toStart := fromStart +] + +{ #category : #accessing } +GtDiffBuilder >> splitter [ + ^ splitter +] + +{ #category : #accessing } +GtDiffBuilder >> splitter: aGtDiffSplitter [ + splitter := aGtDiffSplitter +] + +{ #category : #accessing } +GtDiffBuilder >> to [ + ^ to +] + +{ #category : #accessing } +GtDiffBuilder >> to: aCollection [ + to := aCollection +] diff --git a/src/GToolkit-Coder/GtDiffChange.class.st b/src/GToolkit-Coder/GtDiffChange.class.st new file mode 100644 index 000000000..d702cf6a2 --- /dev/null +++ b/src/GToolkit-Coder/GtDiffChange.class.st @@ -0,0 +1,63 @@ +Class { + #name : #GtDiffChange, + #superclass : #Object, + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #formatting } +GtDiffChange >> applyAttributesToInput: aText [ +] + +{ #category : #formatting } +GtDiffChange >> applyAttributesToOutput: aText [ +] + +{ #category : #accessing } +GtDiffChange >> deletedObjects [ + ^ nil +] + +{ #category : #accessing } +GtDiffChange >> deletionChange [ + ^ nil +] + +{ #category : #iterating } +GtDiffChange >> deletionChangesDo: aBlock [ + self deletionChange ifNotNil: aBlock +] + +{ #category : #accessing } +GtDiffChange >> gtObjectsText [ + ^ '' asRopedText +] + +{ #category : #accessing } +GtDiffChange >> insertedObjects [ + ^ nil +] + +{ #category : #accessing } +GtDiffChange >> insertionChange [ + ^ nil +] + +{ #category : #iterating } +GtDiffChange >> insertionChangesDo: aBlock [ + self insertionChange ifNotNil: aBlock +] + +{ #category : #testing } +GtDiffChange >> isDeletion [ + ^ false +] + +{ #category : #testing } +GtDiffChange >> isInsertion [ + ^ false +] + +{ #category : #testing } +GtDiffChange >> isReplacement [ + ^ false +] diff --git a/src/GToolkit-Coder/GtDiffSplit.class.st b/src/GToolkit-Coder/GtDiffSplit.class.st new file mode 100644 index 000000000..8f7a4b176 --- /dev/null +++ b/src/GToolkit-Coder/GtDiffSplit.class.st @@ -0,0 +1,71 @@ +Class { + #name : #GtDiffSplit, + #superclass : #Object, + #instVars : [ + 'source', + 'startIndex', + 'matched' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #comparing } +GtDiffSplit >> = anObject [ + ^ self class = anObject class ifTrue: [ self object = anObject object ] +] + +{ #category : #testing } +GtDiffSplit >> hasMatch [ + ^ matched +] + +{ #category : #comparing } +GtDiffSplit >> hash [ + ^ self object hash +] + +{ #category : #initialization } +GtDiffSplit >> initialize [ + super initialize. + matched := false +] + +{ #category : #accessing } +GtDiffSplit >> match [ + matched := true +] + +{ #category : #accessing } +GtDiffSplit >> object [ + ^ self subclassResponsibility +] + +{ #category : #accessing } +GtDiffSplit >> range [ + ^ self startIndex printString , '-' , self stopIndex printString +] + +{ #category : #accessing } +GtDiffSplit >> source [ + ^ source +] + +{ #category : #accessing } +GtDiffSplit >> source: aCollection [ + source := aCollection +] + +{ #category : #accessing } +GtDiffSplit >> startIndex [ + ^ startIndex +] + +{ #category : #accessing } +GtDiffSplit >> startIndex: anInteger [ + startIndex := anInteger +] + +{ #category : #accessing } +GtDiffSplit >> stopIndex [ + ^ self subclassResponsibility +] diff --git a/src/GToolkit-Coder/GtDiffSplitCollection.class.st b/src/GToolkit-Coder/GtDiffSplitCollection.class.st new file mode 100644 index 000000000..f92fc01fd --- /dev/null +++ b/src/GToolkit-Coder/GtDiffSplitCollection.class.st @@ -0,0 +1,51 @@ +Class { + #name : #GtDiffSplitCollection, + #superclass : #Object, + #instVars : [ + 'splits' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #adding } +GtDiffSplitCollection >> addSplit: aGtDiffSplit [ + splits add: aGtDiffSplit +] + +{ #category : #accessing } +GtDiffSplitCollection >> at: anInteger [ + ^ splits at: anInteger +] + +{ #category : #accessing } +GtDiffSplitCollection >> gtLiveFor: aView [ + + ^ aView columnedList + title: 'Splits'; + items: [ self splits ]; + column: 'Range' + text: [ :each | each range ] + width: 100; + column: 'Value' text: [ :each | each object ] +] + +{ #category : #initialization } +GtDiffSplitCollection >> initialize [ + super initialize. + splits := OrderedCollection new +] + +{ #category : #accessing } +GtDiffSplitCollection >> size [ + ^ splits size +] + +{ #category : #accessing } +GtDiffSplitCollection >> splits [ + ^ splits +] + +{ #category : #accessing } +GtDiffSplitCollection >> splits: aCollection [ + splits := aCollection +] diff --git a/src/GToolkit-Coder/GtDiffSplitter.class.st b/src/GToolkit-Coder/GtDiffSplitter.class.st new file mode 100644 index 000000000..c1fc090f4 --- /dev/null +++ b/src/GToolkit-Coder/GtDiffSplitter.class.st @@ -0,0 +1,31 @@ +Class { + #name : #GtDiffSplitter, + #superclass : #Object, + #instVars : [ + 'source' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #'as yet unclassified' } +GtDiffSplitter >> descriptionString [ + ^ self subclassResponsibility +] + +{ #category : #testing } +GtDiffSplitter >> isLineBased [ + ^ false +] + +{ #category : #actions } +GtDiffSplitter >> split: aCollection [ + | splits | + splits := GtDiffSplitCollection new. + self split: aCollection into: splits. + ^ splits +] + +{ #category : #actions } +GtDiffSplitter >> split: aCollection into: aGtDiffSplits [ + self subclassResponsibility +] diff --git a/src/GToolkit-Coder/GtFilterExampleAllState.class.st b/src/GToolkit-Coder/GtFilterExampleAllState.class.st deleted file mode 100644 index 484a09f77..000000000 --- a/src/GToolkit-Coder/GtFilterExampleAllState.class.st +++ /dev/null @@ -1,30 +0,0 @@ -Class { - #name : #GtFilterExampleAllState, - #superclass : #GtFilterExampleState, - #category : #'GToolkit-Coder-Filters - Support' -} - -{ #category : #'api - testing' } -GtFilterExampleAllState >> includesCoder: aCoder [ - ^ aCoder example isNotNil -] - -{ #category : #'api - testing' } -GtFilterExampleAllState >> isStatusFor: aGtExampleWithResult [ - ^ false -] - -{ #category : #'api - accessing' } -GtFilterExampleAllState >> label [ - ^ 'All' -] - -{ #category : #'api - accessing' } -GtFilterExampleAllState >> numberOfExamplesFor: anExampler [ - ^ anExampler numberOfAllExamples -] - -{ #category : #'api - accessing' } -GtFilterExampleAllState >> order [ - ^ 1 -] diff --git a/src/GToolkit-Coder/GtFilterExampleErrorState.class.st b/src/GToolkit-Coder/GtFilterExampleErrorState.class.st deleted file mode 100644 index baa501f6f..000000000 --- a/src/GToolkit-Coder/GtFilterExampleErrorState.class.st +++ /dev/null @@ -1,39 +0,0 @@ -Class { - #name : #GtFilterExampleErrorState, - #superclass : #GtFilterExampleState, - #category : #'GToolkit-Coder-Filters - Support' -} - -{ #category : #'api - accessing' } -GtFilterExampleErrorState >> color [ - ^ BrGlamorousColors errorBackgroundColor -] - -{ #category : #'api - testing' } -GtFilterExampleErrorState >> includesCoder: aCoder [ - ^ aCoder canExecuteExample and: [ - aCoder exampleResult isNotNil and: [ - aCoder exampleResult isError ] ] -] - -{ #category : #'api - testing' } -GtFilterExampleErrorState >> isStatusFor: aGtExampleWithResult [ - ^ aGtExampleWithResult isNotNil and: [ - aGtExampleWithResult hasResult and: [ - aGtExampleWithResult isError ] ] -] - -{ #category : #'api - accessing' } -GtFilterExampleErrorState >> label [ - ^ 'Error' -] - -{ #category : #'api - accessing' } -GtFilterExampleErrorState >> numberOfExamplesFor: anExampler [ - ^ anExampler numberOfErrorExamples -] - -{ #category : #'api - accessing' } -GtFilterExampleErrorState >> order [ - ^ 4 -] diff --git a/src/GToolkit-Coder/GtFilterExampleFailureState.class.st b/src/GToolkit-Coder/GtFilterExampleFailureState.class.st deleted file mode 100644 index ff48b9e0e..000000000 --- a/src/GToolkit-Coder/GtFilterExampleFailureState.class.st +++ /dev/null @@ -1,39 +0,0 @@ -Class { - #name : #GtFilterExampleFailureState, - #superclass : #GtFilterExampleState, - #category : #'GToolkit-Coder-Filters - Support' -} - -{ #category : #'api - accessing' } -GtFilterExampleFailureState >> color [ - ^ BrGlamorousColors failureBackgroundColor -] - -{ #category : #'api - testing' } -GtFilterExampleFailureState >> includesCoder: aCoder [ - ^ aCoder canExecuteExample and: [ - aCoder exampleResult isNotNil and: [ - aCoder exampleResult isFailure ] ] -] - -{ #category : #'api - testing' } -GtFilterExampleFailureState >> isStatusFor: aGtExampleWithResult [ - ^ aGtExampleWithResult isNotNil and: [ - aGtExampleWithResult hasResult and: [ - aGtExampleWithResult isFailure ] ] -] - -{ #category : #'api - accessing' } -GtFilterExampleFailureState >> label [ - ^ 'Failure' -] - -{ #category : #'api - accessing' } -GtFilterExampleFailureState >> numberOfExamplesFor: anExampler [ - ^ anExampler numberOfFailureExamples -] - -{ #category : #'api - accessing' } -GtFilterExampleFailureState >> order [ - ^ 3 -] diff --git a/src/GToolkit-Coder/GtFilterExampleNotExecutedState.class.st b/src/GToolkit-Coder/GtFilterExampleNotExecutedState.class.st deleted file mode 100644 index 2540d0e47..000000000 --- a/src/GToolkit-Coder/GtFilterExampleNotExecutedState.class.st +++ /dev/null @@ -1,36 +0,0 @@ -Class { - #name : #GtFilterExampleNotExecutedState, - #superclass : #GtFilterExampleState, - #category : #'GToolkit-Coder-Filters - Support' -} - -{ #category : #'api - accessing' } -GtFilterExampleNotExecutedState >> color [ - ^ BrGlamorousColors neutralBackgroundColor -] - -{ #category : #'api - testing' } -GtFilterExampleNotExecutedState >> includesCoder: aCoder [ - ^ aCoder exampleResult isNil -] - -{ #category : #'api - testing' } -GtFilterExampleNotExecutedState >> isStatusFor: aGtExampleWithResult [ - ^ aGtExampleWithResult isNotNil and: [ - aGtExampleWithResult hasResult not ] -] - -{ #category : #'api - accessing' } -GtFilterExampleNotExecutedState >> label [ - ^ 'Not Executed' -] - -{ #category : #'api - accessing' } -GtFilterExampleNotExecutedState >> numberOfExamplesFor: anExampler [ - ^ anExampler numberOfNotExecutedExamples -] - -{ #category : #'api - accessing' } -GtFilterExampleNotExecutedState >> order [ - ^ 5 -] diff --git a/src/GToolkit-Coder/GtFilterExampleState.class.st b/src/GToolkit-Coder/GtFilterExampleState.class.st deleted file mode 100644 index 179811c14..000000000 --- a/src/GToolkit-Coder/GtFilterExampleState.class.st +++ /dev/null @@ -1,96 +0,0 @@ -Class { - #name : #GtFilterExampleState, - #superclass : #Object, - #traits : 'TBlDebug + TGtUniqueInstance', - #classTraits : 'TBlDebug classTrait + TGtUniqueInstance classTrait', - #category : #'GToolkit-Coder-Filters - Support' -} - -{ #category : #'api - accessing' } -GtFilterExampleState >> allLabels [ - "Return all available labels" - - ^ self allStates collect: #label -] - -{ #category : #'api - accessing' } -GtFilterExampleState >> allStates [ - | allInstances | - allInstances := self class allSubclasses collect: #default. - allInstances sort: [ :a :b | a order < b order ]. - ^ allInstances -] - -{ #category : #'api - accessing' } -GtFilterExampleState >> color [ - "Return a color that represents given state" - - ^ Color transparent -] - -{ #category : #'api - testing' } -GtFilterExampleState >> includesCoder: aCoder [ - "Return true if a method filter should be displayed. - Return false otherwise." - ^ true -] - -{ #category : #'api - testing' } -GtFilterExampleState >> isStatusFor: aGtExampleWithResult [ - "Return true if the status is equal to the example result status" - - ^ self subclassResponsibility -] - -{ #category : #'api - accessing' } -GtFilterExampleState >> label [ - "Return a name that is used by ${class:GtSearchExamplesFilter}$ - to display available example filters in ${class:GtFiltersElement}$." - - ^ self className -] - -{ #category : #'api - accessing' } -GtFilterExampleState >> labelWithAmountFor: anExampler [ - - | aTotal aTotalString aTotalText | - aTotal := self numberOfExamplesFor: anExampler. - aTotalString := ' (', aTotal asString, (' example' asPluralBasedOn: aTotal), ')'. - aTotalText := aTotalString asRopedText foreground: Color gray. - ^ self label asRopedText - append: aTotalText. -] - -{ #category : #'api - accessing' } -GtFilterExampleState >> numberOfExamplesFor: anExampler [ - "Return number of examples with a given state" - ^ 0 -] - -{ #category : #'api - accessing' } -GtFilterExampleState >> order [ - "Return a number that is used by ${class:GtSearchExamplesFilter}$ - to display available example filters in ${class:GtFiltersElement}$ - in a specific order (higher number, later in a list)." - - ^ 50 -] - -{ #category : #'api - accessing' } -GtFilterExampleState >> stateFromExample: aGtExampleWithResult [ - aGtExampleWithResult ifNotNil: [ - self allStates do: [ :eachStatus | - (eachStatus isStatusFor: aGtExampleWithResult) - ifTrue: [ ^ eachStatus ] ] ]. - ^ GtFilterExampleNotExecutedState default -] - -{ #category : #'api - accessing' } -GtFilterExampleState >> stateFromLabel: aLabel [ - - | aString | - aString := aLabel asString. - ^ self allStates - detect: [ :eachStatus | eachStatus label = aString ] - ifNone: [ GtFilterExampleAllState default ] -] diff --git a/src/GToolkit-Coder/GtFilterExampleSuccessState.class.st b/src/GToolkit-Coder/GtFilterExampleSuccessState.class.st deleted file mode 100644 index c94d32a35..000000000 --- a/src/GToolkit-Coder/GtFilterExampleSuccessState.class.st +++ /dev/null @@ -1,39 +0,0 @@ -Class { - #name : #GtFilterExampleSuccessState, - #superclass : #GtFilterExampleState, - #category : #'GToolkit-Coder-Filters - Support' -} - -{ #category : #'api - accessing' } -GtFilterExampleSuccessState >> color [ - ^ BrGlamorousColors successBackgroundColor -] - -{ #category : #'api - testing' } -GtFilterExampleSuccessState >> includesCoder: aCoder [ - ^ aCoder canExecuteExample and: [ - aCoder exampleResult isNotNil and: [ - aCoder exampleResult isSuccess ] ] -] - -{ #category : #'api - testing' } -GtFilterExampleSuccessState >> isStatusFor: aGtExampleWithResult [ - ^ aGtExampleWithResult isNotNil and: [ - aGtExampleWithResult hasResult and: [ - aGtExampleWithResult isSuccess ] ] -] - -{ #category : #'api - accessing' } -GtFilterExampleSuccessState >> label [ - ^ 'Success' -] - -{ #category : #'api - accessing' } -GtFilterExampleSuccessState >> numberOfExamplesFor: anExampler [ - ^ anExampler numberOfSuccessExamples -] - -{ #category : #'api - accessing' } -GtFilterExampleSuccessState >> order [ - ^ 2 -] diff --git a/src/GToolkit-Coder/GtInsertionDiffChange.class.st b/src/GToolkit-Coder/GtInsertionDiffChange.class.st new file mode 100644 index 000000000..c780b7fef --- /dev/null +++ b/src/GToolkit-Coder/GtInsertionDiffChange.class.st @@ -0,0 +1,47 @@ +Class { + #name : #GtInsertionDiffChange, + #superclass : #GtSourceDiffChange, + #instVars : [ + 'originalIndex' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #accessing } +GtInsertionDiffChange class >> highlightColor [ + ^ BrGlamorousColors successBackgroundColor +] + +{ #category : #accessing } +GtInsertionDiffChange >> applyAttributesToOutput: aText [ + aText + attribute: (BlTextHighlightAttribute paint: self highlightColor) + beNotOverwritableByStyler + from: self startIndex + to: self stopIndex. +] + +{ #category : #accessing } +GtInsertionDiffChange >> insertedObjects [ + ^ self objects +] + +{ #category : #accessing } +GtInsertionDiffChange >> insertionChange [ + ^ self +] + +{ #category : #testing } +GtInsertionDiffChange >> isInsertion [ + ^ true +] + +{ #category : #'as yet unclassified' } +GtInsertionDiffChange >> originalIndex [ + ^ originalIndex +] + +{ #category : #accessing } +GtInsertionDiffChange >> originalIndex: anInteger [ + originalIndex := anInteger +] diff --git a/src/GToolkit-Coder/GtLineDiffSplitter.class.st b/src/GToolkit-Coder/GtLineDiffSplitter.class.st new file mode 100644 index 000000000..128377026 --- /dev/null +++ b/src/GToolkit-Coder/GtLineDiffSplitter.class.st @@ -0,0 +1,52 @@ +Class { + #name : #GtLineDiffSplitter, + #superclass : #GtDiffSplitter, + #instVars : [ + 'includeEOLChars' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #'instance creation' } +GtLineDiffSplitter class >> ignoringEOLChars [ + ^ self new + includeEOLChars: false; + yourself +] + +{ #category : #'as yet unclassified' } +GtLineDiffSplitter >> descriptionString [ + ^ 'by lines' +] + +{ #category : #accessing } +GtLineDiffSplitter >> includeEOLChars [ + ^ includeEOLChars +] + +{ #category : #accessing } +GtLineDiffSplitter >> includeEOLChars: aBoolean [ + includeEOLChars := aBoolean +] + +{ #category : #initialization } +GtLineDiffSplitter >> initialize [ + super initialize. + includeEOLChars := true +] + +{ #category : #testing } +GtLineDiffSplitter >> isLineBased [ + ^ true +] + +{ #category : #actions } +GtLineDiffSplitter >> split: aString into: aGtDiffSplits [ + aString + lineIndicesDo: [ :start :end :endWithEOL | + aGtDiffSplits + addSplit: (GtRangeDiffSplit + on: aString + from: start + to: (includeEOLChars ifTrue: [ endWithEOL ] ifFalse: [ end ])) ] +] diff --git a/src/GToolkit-Coder/GtMethodProtocolCompletionStrategy.class.st b/src/GToolkit-Coder/GtMethodProtocolCompletionStrategy.class.st index 0696b85af..cc49d7ed0 100644 --- a/src/GToolkit-Coder/GtMethodProtocolCompletionStrategy.class.st +++ b/src/GToolkit-Coder/GtMethodProtocolCompletionStrategy.class.st @@ -1,6 +1,6 @@ Class { #name : #GtMethodProtocolCompletionStrategy, - #superclass : #GtStringsCompletionStrategy, + #superclass : #GtWeightedStringsCompletionStrategy, #category : #'GToolkit-Coder-Completion' } @@ -9,9 +9,9 @@ GtMethodProtocolCompletionStrategy >> buildCompletions [ | protocols | protocols := GtPrefixTree new. Smalltalk globals - allBehaviorsDo: [ :cls | protocols addAll: cls organization categories ]. - RPackage organizer packages - do: [ :each | protocols add: '*' , each name ]. + allBehaviorsDo: [ :cls | protocols addAll: cls protocolNames ]. + + self packageOrganizer packages do: [ :each | protocols add: '*' , each name ]. ^ protocols ] diff --git a/src/GToolkit-Coder/GtMethodsCoderExampler.class.st b/src/GToolkit-Coder/GtMethodsCoderExampler.class.st deleted file mode 100644 index c2dbc0dbc..000000000 --- a/src/GToolkit-Coder/GtMethodsCoderExampler.class.st +++ /dev/null @@ -1,31 +0,0 @@ -" -I am an example executor. -I am used by {{gtClass:GtMethodsCoder}}, see: {{gtMethod:Behavior>>#gtCoderMethodsFor:context:}} for a usage. - - -" -Class { - #name : #GtMethodsCoderExampler, - #superclass : #GtCoderExampler, - #category : #'GToolkit-Coder-Exampler' -} - -{ #category : #'api - enumeration' } -GtMethodsCoderExampler >> allExampleCodersDo: aBlock [ - self coder allCoders do: [ :eachCoder | - eachCoder example ifNotNil: [ :anExample | - aBlock cull: eachCoder cull: anExample ] ] -] - -{ #category : #'api - accessing' } -GtMethodsCoderExampler >> coderFor: aCompiledMethod [ - - ^ self coder coderFor: aCompiledMethod -] - -{ #category : #'api - enumeration' } -GtMethodsCoderExampler >> selectedExampleCodersDo: aBlock [ - self coder coders do: [ :eachCoder | - eachCoder example ifNotNil: [ :anExample | - aBlock cull: eachCoder cull: anExample ] ] -] diff --git a/src/GToolkit-Coder/GtNoCoderRequester.class.st b/src/GToolkit-Coder/GtNoCoderRequester.class.st new file mode 100644 index 000000000..b50dcc1a0 --- /dev/null +++ b/src/GToolkit-Coder/GtNoCoderRequester.class.st @@ -0,0 +1,7 @@ +Class { + #name : #GtNoCoderRequester, + #superclass : #Object, + #traits : 'TGtCoderRequesterObject + TGtUniqueInstance', + #classTraits : 'TGtCoderRequesterObject classTrait + TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-Coders - Evaluation' +} diff --git a/src/GToolkit-Coder/GtNullDiffSplitter.class.st b/src/GToolkit-Coder/GtNullDiffSplitter.class.st new file mode 100644 index 000000000..8b8ade8ad --- /dev/null +++ b/src/GToolkit-Coder/GtNullDiffSplitter.class.st @@ -0,0 +1,17 @@ +Class { + #name : #GtNullDiffSplitter, + #superclass : #GtDiffSplitter, + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #'as yet unclassified' } +GtNullDiffSplitter >> descriptionString [ + ^ 'do not split' +] + +{ #category : #actions } +GtNullDiffSplitter >> split: aCollection into: aGtDiffSplits [ + 1 + to: aCollection size + do: [ :i | aGtDiffSplits addSplit: (GtSingleObjectDiffSplit on: aCollection at: i) ] +] diff --git a/src/GToolkit-Coder/GtPackageClassesCompletionStrategy.class.st b/src/GToolkit-Coder/GtPackageClassesCompletionStrategy.class.st index 1569823d6..dd63a5708 100644 --- a/src/GToolkit-Coder/GtPackageClassesCompletionStrategy.class.st +++ b/src/GToolkit-Coder/GtPackageClassesCompletionStrategy.class.st @@ -1,6 +1,6 @@ Class { #name : #GtPackageClassesCompletionStrategy, - #superclass : #GtCompletionStrategy, + #superclass : #GtStreamedCompletionStrategy, #instVars : [ 'packageSearchString' ], @@ -8,29 +8,20 @@ Class { } { #category : #accessing } -GtPackageClassesCompletionStrategy >> classesMatching: aString [ +GtPackageClassesCompletionStrategy >> classStreamMatching: aString [ | classNamePattern matchedClasses | matchedClasses := Set new. classNamePattern := '*' , aString , '*'. - (GtPackagesCompletionStrategy findPackagesMatching: packageSearchString) do: [ :each | - matchedClasses addAll: each classes ]. - (GtPackagesCompletionStrategy findPackageTagsMatching: packageSearchString) do: [ :each | - matchedClasses addAll: each classes ]. - ^ matchedClasses select: [ :each | classNamePattern match: each name ] + ^ (((GtPackagesCompletionStrategy packageAndTagStreamMatching: packageSearchString) + collect: [ :each | each classes asAsyncStream ]) flatten select: [ :each | classNamePattern match: each name ]) + withoutDuplicates ] { #category : #accessing } -GtPackageClassesCompletionStrategy >> completionActionsFor: aText at: positionInteger max: maxInteger [ +GtPackageClassesCompletionStrategy >> completionActionStreamFor: aText at: positionInteger requested: aBoolean [ | string | string := aText asString. - ^ ((self classesMatching: string) collect: [ :each | - GtReplaceTextCompletionAction forText: each name ]) - asSortedCollection: [ :a :b | a text < b text ] -] - -{ #category : #testing } -GtPackageClassesCompletionStrategy >> hasCompletionEntryFor: aString [ - ^ true + ^ (self classStreamMatching: string) collect: [ :each | GtReplaceTextCompletionAction forText: each name ] ] { #category : #testing } diff --git a/src/GToolkit-Coder/GtPackagesCoder.class.st b/src/GToolkit-Coder/GtPackagesCoder.class.st index 92e692a26..e199baba0 100644 --- a/src/GToolkit-Coder/GtPackagesCoder.class.st +++ b/src/GToolkit-Coder/GtPackagesCoder.class.st @@ -35,7 +35,7 @@ GtPackagesCoder >> hash [ { #category : #accessing } GtPackagesCoder >> icon [ - ^ BrGlamorousIcons packageicon asElement + ^ BrGlamorousVectorIcons packageicon asElement ] { #category : #accessing } diff --git a/src/GToolkit-Coder/GtPackagesCompletionStrategy.class.st b/src/GToolkit-Coder/GtPackagesCompletionStrategy.class.st index 83a9dd9ea..99c089291 100644 --- a/src/GToolkit-Coder/GtPackagesCompletionStrategy.class.st +++ b/src/GToolkit-Coder/GtPackagesCompletionStrategy.class.st @@ -1,17 +1,17 @@ Class { #name : #GtPackagesCompletionStrategy, - #superclass : #GtCompletionStrategy, + #superclass : #GtStreamedCompletionStrategy, #category : #'GToolkit-Coder-Completion' } { #category : #private } GtPackagesCompletionStrategy class >> allPackages [ - ^ RPackageOrganizer default packages + ^ self packageOrganizer packages asSortedCollection: [ :a :b | a name < b name ] ] { #category : #private } GtPackagesCompletionStrategy class >> allPackagesDo: aBlock [ - ^ RPackageOrganizer default packagesDo: aBlock + ^ self packageOrganizer packagesDo: aBlock ] { #category : #querying } @@ -20,7 +20,7 @@ GtPackagesCompletionStrategy class >> findPackageTagsMatching: aString [ searchString := '*' , aString , '*'. results := OrderedCollection new. self allPackagesDo: [ :each | - each classTags + each tags do: [ :tag | (searchString match: tag categoryName) ifTrue: [ results add: tag ] ] ]. @@ -38,24 +38,23 @@ GtPackagesCompletionStrategy class >> findPackagesMatching: aString [ ^ results ] -{ #category : #accessing } -GtPackagesCompletionStrategy >> completionActionsFor: aText at: positionInteger max: maxInteger [ - | string completionActions | - string := aText asString. - string isEmpty - ifTrue: [ ^#() ]. - completionActions := Set new. - (self class findPackagesMatching: string) do: [ :each | - completionActions add: (GtReplaceTextCompletionAction forText: each name) ]. - (self class findPackageTagsMatching: string) do: [ :each | - completionActions add: (GtReplaceTextCompletionAction forText: each categoryName) ]. - ^ completionActions asSortedCollection: [ :a :b | - a text < b text ] +{ #category : #querying } +GtPackagesCompletionStrategy class >> packageStreamMatching: searchString [ + ^ self allPackages asAsyncStream + select: + [ :each | + searchString match: each name] ] -{ #category : #testing } -GtPackagesCompletionStrategy >> hasCompletionEntryFor: aString [ - ^ true +{ #category : #accessing } +GtPackagesCompletionStrategy >> completionActionStreamFor: aText at: positionInteger requested: aBoolean [ + | string searchString | + string := aText asString. + string isEmpty ifTrue: [ ^ #() ]. + searchString := '*' , string , '*'. + ^ ((self class packageStreamMatching: searchString) + collect: [ :each | (each isPharoPackageModel) ifTrue: [ each name ] ifFalse: [ each categoryName ] ]) withoutDuplicates + collect: [ :each | GtReplaceTextCompletionAction forText: each ] ] { #category : #testing } diff --git a/src/GToolkit-Coder/GtPerformedChange.class.st b/src/GToolkit-Coder/GtPerformedChange.class.st new file mode 100644 index 000000000..f11b4b28b --- /dev/null +++ b/src/GToolkit-Coder/GtPerformedChange.class.st @@ -0,0 +1,67 @@ +Class { + #name : #GtPerformedChange, + #superclass : #RBRefactoryChange, + #instVars : [ + 'change', + 'timestamp' + ], + #category : #'GToolkit-Coder-Refactoring' +} + +{ #category : #executing } +GtPerformedChange class >> onChange: aChange [ + ^ self new + timestamp: DateAndTime now; + change: aChange; + yourself +] + +{ #category : #accessing } +GtPerformedChange >> change [ + ^ change +] + +{ #category : #accessing } +GtPerformedChange >> change: anObject [ + change := anObject +] + +{ #category : #accessing } +GtPerformedChange >> executeNotifying: aBlock [ + ^ change executeNotifying: aBlock +] + +{ #category : #accessing } +GtPerformedChange >> gtChangesFor: aView [ + + (change respondsTo: #gtChangesFor:) ifFalse: [ ^ aView empty ]. + ^ aView forward + title: 'Changes'; + object: [ change ]; + view: #gtChangesFor: +] + +{ #category : #accessing } +GtPerformedChange >> gtViewDefinitionFor: aView [ + + (change respondsTo: #gtViewDefinitionFor:) ifFalse: [ ^ aView empty ]. + ^ aView forward + title: 'Definition'; + object: [ change ]; + view: #gtViewDefinitionFor: +] + +{ #category : #accessing } +GtPerformedChange >> renameChangesForClass: oldClassName to: newClassName [ + ^ change renameChangesForClass: oldClassName to: newClassName +] + +{ #category : #accessing } +GtPerformedChange >> timestamp [ + ^ timestamp +] + +{ #category : #accessing } +GtPerformedChange >> timestamp: anObject [ + timestamp := anObject +] diff --git a/src/GToolkit-Coder/GtPharoAssociation.class.st b/src/GToolkit-Coder/GtPharoAssociation.class.st deleted file mode 100644 index fbed6f252..000000000 --- a/src/GToolkit-Coder/GtPharoAssociation.class.st +++ /dev/null @@ -1,32 +0,0 @@ -Class { - #name : #GtPharoAssociation, - #superclass : #Object, - #category : #'GToolkit-Coder-Support-Pharo' -} - -{ #category : #'instance creation' } -GtPharoAssociation >> from [ - ^ self subclassResponsibility -] - -{ #category : #'instance creation' } -GtPharoAssociation >> fromPackage [ - ^ self from gtPackageScope -] - -{ #category : #printing } -GtPharoAssociation >> gtDisplayOn: stream [ - self from gtDisplayOn: stream. - stream nextPutAll: '->'. - self to gtDisplayOn: stream. -] - -{ #category : #'instance creation' } -GtPharoAssociation >> to [ - ^ self subclassResponsibility -] - -{ #category : #'instance creation' } -GtPharoAssociation >> toPackage [ - ^ self to gtPackageScope -] diff --git a/src/GToolkit-Coder/GtPharoClassExtension.class.st b/src/GToolkit-Coder/GtPharoClassExtension.class.st deleted file mode 100644 index 3e3745141..000000000 --- a/src/GToolkit-Coder/GtPharoClassExtension.class.st +++ /dev/null @@ -1,34 +0,0 @@ -Class { - #name : #GtPharoClassExtension, - #superclass : #GtPharoAssociation, - #instVars : [ - 'extendedClass', - 'extendingMethod' - ], - #category : #'GToolkit-Coder-Support-Pharo' -} - -{ #category : #accessing } -GtPharoClassExtension >> extendedClass [ - ^ self extendingMethod methodClass -] - -{ #category : #accessing } -GtPharoClassExtension >> extendingMethod [ - ^ extendingMethod -] - -{ #category : #accessing } -GtPharoClassExtension >> extendingMethod: anObject [ - extendingMethod := anObject -] - -{ #category : #'instance creation' } -GtPharoClassExtension >> from [ - ^ self extendingMethod -] - -{ #category : #'instance creation' } -GtPharoClassExtension >> to [ - ^ self extendedClass -] diff --git a/src/GToolkit-Coder/GtPharoClassReference.class.st b/src/GToolkit-Coder/GtPharoClassReference.class.st deleted file mode 100644 index 3a42c0067..000000000 --- a/src/GToolkit-Coder/GtPharoClassReference.class.st +++ /dev/null @@ -1,48 +0,0 @@ -Class { - #name : #GtPharoClassReference, - #superclass : #GtPharoAssociation, - #instVars : [ - 'referencingMethod', - 'referencedClass' - ], - #category : #'GToolkit-Coder-Support-Pharo' -} - -{ #category : #accessing } -GtPharoClassReference >> from [ - ^ self referencingMethod -] - -{ #category : #accessing } -GtPharoClassReference >> gtSourceFor: aView [ - - ^ aView forward - title: 'Source'; - object: [self referencingMethod]; - view: #gtSourceFor: -] - -{ #category : #accessing } -GtPharoClassReference >> referencedClass [ - ^ referencedClass -] - -{ #category : #accessing } -GtPharoClassReference >> referencedClass: anObject [ - referencedClass := anObject -] - -{ #category : #accessing } -GtPharoClassReference >> referencingMethod [ - ^ referencingMethod -] - -{ #category : #accessing } -GtPharoClassReference >> referencingMethod: anObject [ - referencingMethod := anObject -] - -{ #category : #accessing } -GtPharoClassReference >> to [ - ^ self referencedClass -] diff --git a/src/GToolkit-Coder/GtPharoDiffSplitter.class.st b/src/GToolkit-Coder/GtPharoDiffSplitter.class.st new file mode 100644 index 000000000..c4b879943 --- /dev/null +++ b/src/GToolkit-Coder/GtPharoDiffSplitter.class.st @@ -0,0 +1,25 @@ +Class { + #name : #GtPharoDiffSplitter, + #superclass : #GtSmaCCDiffSplitter, + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #'as yet unclassified' } +GtPharoDiffSplitter >> createCommentSplitFrom: start to: stop into: aGtDiffSplits [ + GtCharacterGroupDiffSplitter words + split: string + from: start + to: stop + into: aGtDiffSplits +] + +{ #category : #'as yet unclassified' } +GtPharoDiffSplitter >> createTokenSplitFrom: aToken into: aGtDiffSplits [ + aToken value first = $' + ifFalse: [ ^ super createTokenSplitFrom: aToken into: aGtDiffSplits ]. + GtCharacterGroupDiffSplitter words + split: string + from: aToken startPosition + to: aToken stopPosition + into: aGtDiffSplits +] diff --git a/src/GToolkit-Coder/GtPharoInheritance.class.st b/src/GToolkit-Coder/GtPharoInheritance.class.st deleted file mode 100644 index 9c15d8153..000000000 --- a/src/GToolkit-Coder/GtPharoInheritance.class.st +++ /dev/null @@ -1,39 +0,0 @@ -Class { - #name : #GtPharoInheritance, - #superclass : #GtPharoAssociation, - #instVars : [ - 'superclass', - 'subclass' - ], - #category : #'GToolkit-Coder-Support-Pharo' -} - -{ #category : #'instance creation' } -GtPharoInheritance >> from [ - ^ self subclass -] - -{ #category : #accessing } -GtPharoInheritance >> subclass [ - ^ subclass -] - -{ #category : #accessing } -GtPharoInheritance >> subclass: anObject [ - subclass := anObject -] - -{ #category : #accessing } -GtPharoInheritance >> superclass [ - ^ superclass -] - -{ #category : #accessing } -GtPharoInheritance >> superclass: anObject [ - superclass := anObject -] - -{ #category : #'instance creation' } -GtPharoInheritance >> to [ - ^ self superclass -] diff --git a/src/GToolkit-Coder/GtPharoTraitUsage.class.st b/src/GToolkit-Coder/GtPharoTraitUsage.class.st deleted file mode 100644 index dca9674a9..000000000 --- a/src/GToolkit-Coder/GtPharoTraitUsage.class.st +++ /dev/null @@ -1,39 +0,0 @@ -Class { - #name : #GtPharoTraitUsage, - #superclass : #GtPharoAssociation, - #instVars : [ - 'trait', - 'baseBehavior' - ], - #category : #'GToolkit-Coder-Support-Pharo' -} - -{ #category : #accessing } -GtPharoTraitUsage >> baseBehavior [ - ^ baseBehavior -] - -{ #category : #accessing } -GtPharoTraitUsage >> baseBehavior: aBehavior [ - baseBehavior := aBehavior -] - -{ #category : #'instance creation' } -GtPharoTraitUsage >> from [ - ^ self baseBehavior -] - -{ #category : #'instance creation' } -GtPharoTraitUsage >> to [ - ^ self usedTrait -] - -{ #category : #accessing } -GtPharoTraitUsage >> usedTrait [ - ^ trait -] - -{ #category : #accessing } -GtPharoTraitUsage >> usedTrait: aTrait [ - trait := aTrait -] diff --git a/src/GToolkit-Coder/GtPoolsCompletionStrategy.class.st b/src/GToolkit-Coder/GtPoolsCompletionStrategy.class.st index 40fef5e10..ce4126d85 100644 --- a/src/GToolkit-Coder/GtPoolsCompletionStrategy.class.st +++ b/src/GToolkit-Coder/GtPoolsCompletionStrategy.class.st @@ -1,6 +1,6 @@ Class { #name : #GtPoolsCompletionStrategy, - #superclass : #GtCompletionStrategy, + #superclass : #GtStreamedCompletionStrategy, #instVars : [ 'candidatePools' ], @@ -18,23 +18,17 @@ GtPoolsCompletionStrategy >> candidatePools [ ] { #category : #accessing } -GtPoolsCompletionStrategy >> completionActionsFor: aText at: positionInteger max: maxInteger [ - | currentInput completionActions | - +GtPoolsCompletionStrategy >> completionActionStreamFor: aText at: positionInteger requested: aBoolean [ + | currentInput | currentInput := aText asString. - completionActions := (self candidatePools first: maxInteger startingWith: currentInput) collect: [ :poolName | - GtInsertTextCompletionAction - labeled: poolName - completion: (poolName allButFirst: currentInput size) - position: positionInteger - from: positionInteger - currentInput size + 1 ]. - - ^ completionActions -] - -{ #category : #testing } -GtPoolsCompletionStrategy >> hasCompletionEntryFor: aString [ - ^ true + ^ (self candidatePools asyncStreamStartingWith: currentInput) + collect: + [ :poolName | + GtInsertTextCompletionAction + labeled: (self labelFor: poolName withSearch: currentInput) + completion: (poolName allButFirst: currentInput size) + position: positionInteger + from: positionInteger - currentInput size + 1 ] ] { #category : #testing } diff --git a/src/GToolkit-Coder/GtRBASTStyler.class.st b/src/GToolkit-Coder/GtRBASTStyler.class.st deleted file mode 100644 index 02772fdd1..000000000 --- a/src/GToolkit-Coder/GtRBASTStyler.class.st +++ /dev/null @@ -1,94 +0,0 @@ -Class { - #name : #GtRBASTStyler, - #superclass : #BlTextStyler, - #classTraits : 'TRBProgramNodeVisitor classTrait', - #category : #'GToolkit-Coder-Styler/Highlighter' -} - -{ #category : #styling } -GtRBASTStyler >> extraStyle: aText ast: ast [ - -] - -{ #category : #'gt-extension' } -GtRBASTStyler >> gtViewTextFor: aView [ - - text ifNil: [ ^ aView empty ]. - ^ aView textEditor - title: 'Last styled text'; - priority: 105; - text: [ text ] -] - -{ #category : #private } -GtRBASTStyler >> parse: aText [ - | code | - code := aText asString. - ^ RBParser parseFaultyMethod: code -] - -{ #category : #private } -GtRBASTStyler >> privateStyle: aText [ - | ast | - ast := self parse: aText. - ast isNil - ifTrue: [ ^ aText ]. - [ self style: aText ast: ast. - self extraStyle: aText ast: ast ] - on: Error - do: [ :e | e return ]. - ^ aText -] - -{ #category : #styling } -GtRBASTStyler >> style: aText ast: ast [ - text := aText. - self visitNode: ast -] - -{ #category : #private } -GtRBASTStyler >> styleFrom: from to: to with: attributes [ - (text from: from to: to) attributes: attributes -] - -{ #category : #accessing } -GtRBASTStyler >> typeOf: aNode in: aClass [ - aNode isSelf - ifTrue: [ ^ aClass ]. - aNode isSuper - ifTrue: [ ^ aClass ifNotNil: [ :class | class superclass ] ]. - aNode isLiteralNode - ifTrue: [ ^ aNode value class ]. - aNode isDynamicArray - ifTrue: [ ^ {} class ]. - aNode isMessage - ifTrue: [ aNode selector = #class - ifTrue: [ ^ (self typeOf: aNode receiver in: aClass) - ifNotNil: [ :class | class class ] ]. - (#(new new:) includes: aNode selector) - ifTrue: [ ^ (self typeOf: aNode receiver in: aClass) - ifNotNil: [ :class | class instanceSide ] ] ]. - aNode isVariable - ifTrue: [ aNode binding - ifNotNil: [ :binding | - binding isLiteralVariable - ifTrue: [ binding value ifNotNil: [ :object | ^ object class ] ]. - (binding isSpecialVariable and: [ binding isThisContext ]) - ifTrue: [ ^ Context ] ] ]. - ^ nil -] - -{ #category : #visiting } -GtRBASTStyler >> visitArgumentNodes: aNodeCollection [ - "Sent *once* when visiting method and block nodes" - ^aNodeCollection do: [ :each | self visitNode: each ] -] - -{ #category : #visiting } -GtRBASTStyler >> visitTemporaryDeclarationNode: aTemporaryDeclarationNode [ - "| temp | - temp is a temporary node as we can find in the body of methods, but it can't be visited the same way. - IT redirects the message on argumentNodeVisitor as a way to keep retrocompatibility" - - ^ self visitNode: aTemporaryDeclarationNode -] diff --git a/src/GToolkit-Coder/GtRangeDiffSplit.class.st b/src/GToolkit-Coder/GtRangeDiffSplit.class.st new file mode 100644 index 000000000..5c27752e7 --- /dev/null +++ b/src/GToolkit-Coder/GtRangeDiffSplit.class.st @@ -0,0 +1,33 @@ +Class { + #name : #GtRangeDiffSplit, + #superclass : #GtDiffSplit, + #instVars : [ + 'stopIndex', + 'object' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #accessing } +GtRangeDiffSplit class >> on: aCollection from: startIndex to: stopIndex [ + ^ self new + source: aCollection; + startIndex: startIndex; + stopIndex: stopIndex; + yourself +] + +{ #category : #accessing } +GtRangeDiffSplit >> object [ + ^ object ifNil: [ object := source copyFrom: startIndex to: stopIndex ] +] + +{ #category : #accessing } +GtRangeDiffSplit >> stopIndex [ + ^ stopIndex +] + +{ #category : #accessing } +GtRangeDiffSplit >> stopIndex: anInteger [ + stopIndex := anInteger +] diff --git a/src/GToolkit-Coder/GtRefactoryChangeManager.class.st b/src/GToolkit-Coder/GtRefactoryChangeManager.class.st new file mode 100644 index 000000000..fb8119797 --- /dev/null +++ b/src/GToolkit-Coder/GtRefactoryChangeManager.class.st @@ -0,0 +1,243 @@ +Class { + #name : #GtRefactoryChangeManager, + #superclass : #Object, + #traits : 'TGtAnnouncer', + #classTraits : 'TGtAnnouncer classTrait', + #instVars : [ + 'undo', + 'redo', + 'isPerformingChanges', + 'announcer' + ], + #classVars : [ + 'Instance', + 'UndoSize' + ], + #category : #'GToolkit-Coder-Refactoring' +} + +{ #category : #'class initialization' } +GtRefactoryChangeManager class >> initialize [ + self nuke. + UndoSize := 200. + SessionManager default registerUserClassNamed: self name +] + +{ #category : #accessing } +GtRefactoryChangeManager class >> instance [ + ^ Instance ifNil: [ Instance := self new ] +] + +{ #category : #'class initialization' } +GtRefactoryChangeManager class >> nuke [ + Instance notNil ifTrue: [ Instance release ]. + Instance := nil +] + +{ #category : #'system startup' } +GtRefactoryChangeManager class >> startUp: isImageStarting [ + "Ensure the instance exists." + + self instance +] + +{ #category : #accessing } +GtRefactoryChangeManager class >> undoSize [ + ^ UndoSize +] + +{ #category : #accessing } +GtRefactoryChangeManager class >> undoSize: anInteger [ + UndoSize := anInteger max: 0. + Instance + ifNotNil: [ Instance checkUndoStackLimit ifTrue: [ Instance announceUndoChanged ] ] +] + +{ #category : #accessing } +GtRefactoryChangeManager class >> unload [ + self nuke +] + +{ #category : #'undo/redo' } +GtRefactoryChangeManager >> addUndo: aChange [ + undo addLast: (GtPerformedChange onChange: aChange). + self checkUndoStackLimit. + redo removeAll. + self announceUndoChanged +] + +{ #category : #executing } +GtRefactoryChangeManager >> announceUndoChanged [ + self + announce: (GtRefactoryChangeManagerUndoChangedEvent new + changeManager: self; + yourself) +] + +{ #category : #announcer } +GtRefactoryChangeManager >> announcer [ + ^ announcer ifNil: [ announcer := Announcer new ] +] + +{ #category : #accessing } +GtRefactoryChangeManager >> changeFactory [ + ^ RBRefactoryChangeFactory new +] + +{ #category : #'undo/redo' } +GtRefactoryChangeManager >> checkUndoStackLimit [ + | previousSize | + previousSize := undo size. + [ undo size > UndoSize ] whileTrue: [ undo removeFirst ]. + ^ previousSize ~= undo size +] + +{ #category : #private } +GtRefactoryChangeManager >> clearUndoRedoList [ + (undo isEmpty and: [ redo isEmpty ]) ifTrue: [ ^ self ]. + undo removeAll. + redo removeAll. + self announceUndoChanged +] + +{ #category : #private } +GtRefactoryChangeManager >> connectToChanges [ + SystemAnnouncer uniqueInstance weak + when: ClassAdded , ClassModifiedClassDefinition , ClassRemoved , ClassRenamed + , MethodAdded , MethodModified , MethodRemoved + send: #update: + to: self +] + +{ #category : #private } +GtRefactoryChangeManager >> disconnectFromChanges [ + SystemAnnouncer uniqueInstance unsubscribe: self +] + +{ #category : #accessing } +GtRefactoryChangeManager >> gtUndoFor: aView [ + + | update | + update := [ :element | + element + allParentsDetect: [ :each | each phlow isViewContent ] + ifFound: [ :each | each phlow syncUpdate ] + ifNone: [ ] ]. + ^ aView columnedList + title: 'Undo'; + priority: 10; + items: [ redo , undo reversed ]; + column: 'Timestamp' + text: [ :each | + | text | + text := each timestamp printToSeconds asRopedText. + (redo identityIncludes: each) + ifTrue: [ text foreground: BrGlamorousColors disabledHeaderTextColor ]. + text ] + width: 130; + column: 'Change' + text: [ :each | + | text | + text := each change gtDescription asRopedText. + (redo identityIncludes: each) + ifTrue: [ text foreground: BrGlamorousColors disabledHeaderTextColor ]. + text ]; + contextItemLabel: 'Undo' + action: [ :element :each | + self undoChange. + update value: element ] + if: [ :each | (undo at: undo size ifAbsent: [ ]) == each ]; + contextItemLabel: 'Undo up to here' + action: [ :element :each | + | found | + found := false. + [ self hasUndoableOperations and: [ found not ] ] + whileTrue: [ found := undo last == each. + self undoChange ]. + update value: element ] + if: [ :each | (undo at: undo size ifAbsent: [ ]) ~~ each and: [ undo identityIncludes: each ] ]; + contextItemLabel: 'Redo' + action: [ :element :each | + self redoChange. + update value: element ] + if: [ :each | (redo at: redo size ifAbsent: [ ]) == each ]; + contextItemLabel: 'Redo back to here' + action: [ :element :each | + | found | + found := false. + [ self hasRedoableOperations and: [ found not ] ] + whileTrue: [ found := redo last == each. + self redoChange ]. + update value: element ] + if: [ :each | (redo at: redo size ifAbsent: [ ]) ~~ each and: [ redo identityIncludes: each ] ]; + actionButtonIcon: BrGlamorousVectorIcons refresh + tooltip: 'Update undo/redo stacks' + action: [ :aButton :aTab | + aButton disable. + [ aTab viewContentElement phlow syncUpdate ] asAsyncPromise + then: [ :aResult | aButton enable ] + otherwise: [ :anError | aButton enable ] ] +] + +{ #category : #testing } +GtRefactoryChangeManager >> hasRedoableOperations [ + ^ redo notEmpty +] + +{ #category : #testing } +GtRefactoryChangeManager >> hasUndoableOperations [ + ^ undo notEmpty +] + +{ #category : #executing } +GtRefactoryChangeManager >> ignoreChangesWhile: aBlock [ + isPerformingChanges ifTrue: [ ^ aBlock value ]. + isPerformingChanges := true. + aBlock ensure: [ isPerformingChanges := false ] +] + +{ #category : #initialization } +GtRefactoryChangeManager >> initialize [ + super initialize. + undo := OrderedCollection new. + redo := OrderedCollection new. + isPerformingChanges := false. + self connectToChanges +] + +{ #category : #executing } +GtRefactoryChangeManager >> performChange: aRefactoringChange [ + self + ignoreChangesWhile: [ | now | + now := DateAndTime now. + self addUndo: aRefactoringChange execute. + aRefactoringChange + gtAllChangesDo: [ :each | each attributeNamed: #timestamp put: now ] ] +] + +{ #category : #executing } +GtRefactoryChangeManager >> redoChange [ + redo isEmpty ifTrue: [ ^ self ]. + self + ignoreChangesWhile: [ undo add: (GtPerformedChange onChange: redo removeLast change execute) ]. + self announceUndoChanged +] + +{ #category : #initialization } +GtRefactoryChangeManager >> release [ + self disconnectFromChanges. + super release +] + +{ #category : #executing } +GtRefactoryChangeManager >> undoChange [ + undo isEmpty ifTrue: [ ^ self ]. + self + ignoreChangesWhile: [ redo add: (GtPerformedChange onChange: undo removeLast change execute) ]. + self announceUndoChanged +] + +{ #category : #private } +GtRefactoryChangeManager >> update: anEvent [ + isPerformingChanges ifFalse: [ self clearUndoRedoList ] +] diff --git a/src/GToolkit-Coder/GtRefactoryChangeManagerUndoChangedEvent.class.st b/src/GToolkit-Coder/GtRefactoryChangeManagerUndoChangedEvent.class.st new file mode 100644 index 000000000..5aaba71c5 --- /dev/null +++ b/src/GToolkit-Coder/GtRefactoryChangeManagerUndoChangedEvent.class.st @@ -0,0 +1,18 @@ +Class { + #name : #GtRefactoryChangeManagerUndoChangedEvent, + #superclass : #Announcement, + #instVars : [ + 'changeManager' + ], + #category : #'GToolkit-Coder-Refactoring' +} + +{ #category : #accessing } +GtRefactoryChangeManagerUndoChangedEvent >> changeManager [ + ^ changeManager +] + +{ #category : #accessing } +GtRefactoryChangeManagerUndoChangedEvent >> changeManager: anObject [ + changeManager := anObject +] diff --git a/src/GToolkit-Coder/GtReplacementDiffChange.class.st b/src/GToolkit-Coder/GtReplacementDiffChange.class.st new file mode 100644 index 000000000..42b9e766e --- /dev/null +++ b/src/GToolkit-Coder/GtReplacementDiffChange.class.st @@ -0,0 +1,67 @@ +Class { + #name : #GtReplacementDiffChange, + #superclass : #GtDiffChange, + #instVars : [ + 'insertionChange', + 'deletionChange' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #'instance creation' } +GtReplacementDiffChange class >> delete: aDeletionChange andInsert: anInsertionChange [ + ^ self new + deletionChange: aDeletionChange; + insertionChange: anInsertionChange; + yourself +] + +{ #category : #formatting } +GtReplacementDiffChange >> applyAttributesToInput: aText [ + deletionChange applyAttributesToInput: aText +] + +{ #category : #formatting } +GtReplacementDiffChange >> applyAttributesToOutput: aText [ + insertionChange applyAttributesToOutput: aText +] + +{ #category : #accessing } +GtReplacementDiffChange >> deletedObjects [ + ^ deletionChange objects +] + +{ #category : #accessing } +GtReplacementDiffChange >> deletionChange [ + ^ deletionChange +] + +{ #category : #accessing } +GtReplacementDiffChange >> deletionChange: aDeletionChange [ + deletionChange := aDeletionChange +] + +{ #category : #accessing } +GtReplacementDiffChange >> insertedObjects [ + ^ insertionChange objects +] + +{ #category : #accessing } +GtReplacementDiffChange >> insertionChange [ + ^ insertionChange +] + +{ #category : #accessing } +GtReplacementDiffChange >> insertionChange: anInsertionChange [ + insertionChange := anInsertionChange +] + +{ #category : #testing } +GtReplacementDiffChange >> isReplacement [ + ^ true +] + +{ #category : #accessing } +GtReplacementDiffChange >> range [ + ^ self deletionChange range , ', ' , self insertionChange range +] diff --git a/src/GToolkit-Coder/GtSearchFilter.class.st b/src/GToolkit-Coder/GtSearchFilter.class.st deleted file mode 100644 index 95ea719cf..000000000 --- a/src/GToolkit-Coder/GtSearchFilter.class.st +++ /dev/null @@ -1,106 +0,0 @@ -Class { - #name : #GtSearchFilter, - #superclass : #Object, - #category : #'GToolkit-Coder-Filters' -} - -{ #category : #'logical operations' } -GtSearchFilter >> & aFilter [ - ^ GtSearchIntersectionFilter forFilter: self filter: aFilter -] - -{ #category : #evaluating } -GtSearchFilter >> applyInScope: aSearchScope [ - ^ self resultType new - scope: aSearchScope; - filter: self -] - -{ #category : #converting } -GtSearchFilter >> asCoder [ - ^ self result asCoder -] - -{ #category : #converting } -GtSearchFilter >> asElement [ - ^ self asCoder asElement -] - -{ #category : #accessing } -GtSearchFilter >> defaultFilterScope [ - ^ GtSearchNullFilter new -] - -{ #category : #iterating } -GtSearchFilter >> do: aBlock [ - self result do: aBlock -] - -{ #category : #converting } -GtSearchFilter >> gtExamples [ - | examples | - examples := OrderedCollection new. - self result do: [ :each | examples addAll: each gtExamples ]. - ^ GtExampleGroup withAll: examples -] - -{ #category : #ui } -GtSearchFilter >> gtItemsFor: aView [ - "this is a view that is polymorphic with the one defined in ${method:Collection>>#gtItemsFor:}$" - - ^ aView forward - title: 'Live'; - priority: 10; - object: [ self result ]; - view: #gtItemsFor: -] - -{ #category : #accessing } -GtSearchFilter >> highlighter [ - ^ nil -] - -{ #category : #accessing } -GtSearchFilter >> isEmpty [ - ^ self result isEmpty -] - -{ #category : #testing } -GtSearchFilter >> matches: anObject [ - self subclassResponsibility -] - -{ #category : #'logical operations' } -GtSearchFilter >> not [ - ^ GtSearchNegationFilter new originalFilter: self -] - -{ #category : #testing } -GtSearchFilter >> notEmpty [ - ^ self isEmpty not -] - -{ #category : #converting } -GtSearchFilter >> result [ - ^ self applyInScope: self defaultFilterScope -] - -{ #category : #accessing } -GtSearchFilter >> resultType [ - ^ GtSearchNullGroupResult -] - -{ #category : #'logical operations' } -GtSearchFilter >> select: aBlock [ - ^ self & (GtSearchBlockFilter forBlock: aBlock) -] - -{ #category : #accessing } -GtSearchFilter >> size [ - ^ self result size -] - -{ #category : #'logical operations' } -GtSearchFilter >> | aFilter [ - ^ GtSearchUnionFilter forFilter: self filter: aFilter -] diff --git a/src/GToolkit-Coder/GtSearchGroupResult.class.st b/src/GToolkit-Coder/GtSearchGroupResult.class.st deleted file mode 100644 index 8702a2fae..000000000 --- a/src/GToolkit-Coder/GtSearchGroupResult.class.st +++ /dev/null @@ -1,163 +0,0 @@ -Class { - #name : #GtSearchGroupResult, - #superclass : #Object, - #instVars : [ - 'items', - 'filter', - 'filtered', - 'scope' - ], - #category : #'GToolkit-Coder-Filters' -} - -{ #category : #'as yet unclassified' } -GtSearchGroupResult class >> mergeTypeWith: aGroupResultClass [ - ^ aGroupResultClass mergeTypeWithDefaultGroup -] - -{ #category : #'as yet unclassified' } -GtSearchGroupResult class >> mergeTypeWithDefaultGroup [ - "The common type between the default group and any other group is the other group." - ^ self -] - -{ #category : #'as yet unclassified' } -GtSearchGroupResult class >> mergeTypeWithGenericGroup [ - "The common type between a generic group and any other group is the other group." - ^ self -] - -{ #category : #'as yet unclassified' } -GtSearchGroupResult class >> mergeTypeWithTypedGroup: aGroupType [ - ^ self -] - -{ #category : #adding } -GtSearchGroupResult >> add: anObject [ - self refreshItems. - ^ items add: anObject -] - -{ #category : #enumerating } -GtSearchGroupResult >> collect: aBlock [ - self refreshItems. - ^ items collect: aBlock -] - -{ #category : #enumerating } -GtSearchGroupResult >> do: aBlock [ - self refreshItems. - ^ items do: aBlock -] - -{ #category : #accessing } -GtSearchGroupResult >> filter [ - ^ filter -] - -{ #category : #accessing } -GtSearchGroupResult >> filter: methodsFilter [ - filtered := false. - items := OrderedCollection new. - filter := methodsFilter -] - -{ #category : #enumerating } -GtSearchGroupResult >> groupedBy: aBlockClosureOrSymbol [ - "Group the filteres items using the given block. The returned groups are just a copy based on - the current items at the time of the call. They are not updated if the filtered items change." - self refreshItems. - ^ items groupedBy: aBlockClosureOrSymbol -] - -{ #category : #ui } -GtSearchGroupResult >> gtItemsFor: aView [ - - self refreshItems. - ^ aView list - title: 'Items' translated; - priority: 10; - items: [ items ifNil: [ #() ] ] -] - -{ #category : #accessing } -GtSearchGroupResult >> highlighter [ - ^ filter notNil - ifTrue: [ filter highlighter ] - ifFalse: [ nil ] -] - -{ #category : #'as yet unclassified' } -GtSearchGroupResult >> includes: aCollection [ - self refreshItems. - ^ items includes: aCollection -] - -{ #category : #initialization } -GtSearchGroupResult >> initialize [ - super initialize. - filtered := false. - items := OrderedCollection new. -] - -{ #category : #testing } -GtSearchGroupResult >> isEmpty [ - "Answer whether the receiver contains any elements." - ^ self size = 0 -] - -{ #category : #enumerating } -GtSearchGroupResult >> refreshItems [ - | filteredItems | - filtered - ifTrue: [ ^ self ]. - filteredItems := OrderedCollection new. - filter ifNotNil: [ - scope itemsDo: [ :anObject | - (filter matches: anObject) ifTrue: [ - filteredItems add: anObject ] ] ]. - items := filteredItems. - filtered := true. -] - -{ #category : #removing } -GtSearchGroupResult >> remove: anItem ifAbsent: aBlock [ - self refreshItems. - ^ items remove: anItem ifAbsent: aBlock -] - -{ #category : #accessing } -GtSearchGroupResult >> scope [ - ^ scope -] - -{ #category : #accessing } -GtSearchGroupResult >> scope: aSearchScope [ - scope := aSearchScope -] - -{ #category : #accessing } -GtSearchGroupResult >> size [ - self refreshItems. - ^ items size -] - -{ #category : #private } -GtSearchGroupResult >> species [ - self refreshItems. - ^ items class -] - -{ #category : #enumerating } -GtSearchGroupResult >> sumNumbers: aBlock [ - self refreshItems. - ^ items sumNumbers: aBlock -] - -{ #category : #initialization } -GtSearchGroupResult >> updateForFilter: aFilter [ - self filter: aFilter. - self scope: (aFilter - ifNil: [ GtSearchNullFilter new ] - ifNotNil: [ aFilter defaultFilterScope ]) -] diff --git a/src/GToolkit-Coder/GtSearchNullGroupResult.class.st b/src/GToolkit-Coder/GtSearchNullGroupResult.class.st deleted file mode 100644 index 53a3fbc9a..000000000 --- a/src/GToolkit-Coder/GtSearchNullGroupResult.class.st +++ /dev/null @@ -1,23 +0,0 @@ -Class { - #name : #GtSearchNullGroupResult, - #superclass : #GtSearchGroupResult, - #category : #'GToolkit-Coder-Filters' -} - -{ #category : #'as yet unclassified' } -GtSearchNullGroupResult class >> mergeTypeWith: aGroupResultClass [ - ^ aGroupResultClass mergeTypeWithGenericGroup. -] - -{ #category : #'as yet unclassified' } -GtSearchNullGroupResult class >> mergeTypeWithTypedGroup: aGroupType [ - ^ aGroupType -] - -{ #category : #enumerating } -GtSearchNullGroupResult >> refreshItems [ - filtered - ifTrue: [ ^ self ]. - items := OrderedCollection new. - filtered := true. -] diff --git a/src/GToolkit-Coder/GtSearchTypedGroupResult.class.st b/src/GToolkit-Coder/GtSearchTypedGroupResult.class.st deleted file mode 100644 index 3562395e0..000000000 --- a/src/GToolkit-Coder/GtSearchTypedGroupResult.class.st +++ /dev/null @@ -1,36 +0,0 @@ -Class { - #name : #GtSearchTypedGroupResult, - #superclass : #GtSearchGroupResult, - #instVars : [ - 'announcer' - ], - #category : #'GToolkit-Coder-Filters' -} - -{ #category : #'as yet unclassified' } -GtSearchTypedGroupResult class >> mergeTypeWith: aGroupResultClass [ - ^ aGroupResultClass mergeTypeWithTypedGroup: self. -] - -{ #category : #'as yet unclassified' } -GtSearchTypedGroupResult class >> mergeTypeWithTypedGroup: aGroupType [ - ^ aGroupType = self - ifTrue: [ self ] - ifFalse: [ GtSearchGroupResult ] -] - -{ #category : #accessing } -GtSearchTypedGroupResult >> announcer [ - ^ announcer -] - -{ #category : #initialization } -GtSearchTypedGroupResult >> initialize [ - super initialize. - announcer := Announcer new. - self subscribe -] - -{ #category : #subscription } -GtSearchTypedGroupResult >> subscribe [ -] diff --git a/src/GToolkit-Coder/GtSingleObjectDiffSplit.class.st b/src/GToolkit-Coder/GtSingleObjectDiffSplit.class.st new file mode 100644 index 000000000..0b2b1c001 --- /dev/null +++ b/src/GToolkit-Coder/GtSingleObjectDiffSplit.class.st @@ -0,0 +1,23 @@ +Class { + #name : #GtSingleObjectDiffSplit, + #superclass : #GtDiffSplit, + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #'instance creation' } +GtSingleObjectDiffSplit class >> on: aCollection at: anIndex [ + ^ self new + source: aCollection; + startIndex: anIndex; + yourself +] + +{ #category : #accessing } +GtSingleObjectDiffSplit >> object [ + ^ source at: startIndex +] + +{ #category : #accessing } +GtSingleObjectDiffSplit >> stopIndex [ + ^ startIndex +] diff --git a/src/GToolkit-Coder/GtSmaCCDiffSplitter.class.st b/src/GToolkit-Coder/GtSmaCCDiffSplitter.class.st new file mode 100644 index 000000000..076329ec3 --- /dev/null +++ b/src/GToolkit-Coder/GtSmaCCDiffSplitter.class.st @@ -0,0 +1,69 @@ +Class { + #name : #GtSmaCCDiffSplitter, + #superclass : #GtDiffSplitter, + #instVars : [ + 'scannerClass', + 'string' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #accessing } +GtSmaCCDiffSplitter >> createCommentSplitFrom: start to: stop into: aGtDiffSplits [ + aGtDiffSplits + addSplit: (GtRangeDiffSplit + on: string + from: start + to: stop) +] + +{ #category : #accessing } +GtSmaCCDiffSplitter >> createTokenSplitFrom: aToken into: aGtDiffSplits [ + aGtDiffSplits + addSplit: (GtRangeDiffSplit + on: string + from: aToken startPosition + to: aToken stopPosition) +] + +{ #category : #'as yet unclassified' } +GtSmaCCDiffSplitter >> descriptionString [ + ^ 'using ' , self scannerClass asString +] + +{ #category : #'as yet unclassified' } +GtSmaCCDiffSplitter >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + nextPutAll: scannerClass asString; + nextPut: $) +] + +{ #category : #accessing } +GtSmaCCDiffSplitter >> scannerClass [ + ^ scannerClass +] + +{ #category : #accessing } +GtSmaCCDiffSplitter >> scannerClass: aSmaCCScannerClass [ + scannerClass := aSmaCCScannerClass +] + +{ #category : #accessing } +GtSmaCCDiffSplitter >> split: aString into: aGtDiffSplits [ + | scanner commentIndex token | + string := aString. + scanner := scannerClass on: string readStream. + commentIndex := 0. + [ token := scanner next. + [ commentIndex < scanner comments size ] + whileTrue: [ commentIndex := commentIndex + 1. + self + createCommentSplitFrom: (scanner comments at: commentIndex) first + to: (scanner comments at: commentIndex) last + into: aGtDiffSplits ]. + token ids first ~= scanner emptySymbolTokenId ] + whileTrue: [ self createTokenSplitFrom: token into: aGtDiffSplits ] +] diff --git a/src/GToolkit-Coder/GtSourceCoder.class.st b/src/GToolkit-Coder/GtSourceCoder.class.st index 1d443df0e..817b7ce57 100644 --- a/src/GToolkit-Coder/GtSourceCoder.class.st +++ b/src/GToolkit-Coder/GtSourceCoder.class.st @@ -1,18 +1,15 @@ " -1. Source coder +# Source coder Is a language agnostic model of a source coder. My subclasses extend my by adding support for a specific language. -1. # Code execution +### Code execution To add support for code execution the subclasses must implement -- {{gtMethod:GtSourceCoder>>#evaluationRequester}}. -- {{gtMethod:GtSourceCoder>>#evaluationRequesterWithInterval}}. -- {{gtMethod:GtSourceCoder>>#primitiveEvaluate:requester:onFailDo:}}. -1. # Code debugging +- {{gtMethod:GtSourceCoder>>#primitiveEvaluate:inContext:onFailDo:}}. +### Code debugging To add support for code debugging the subclasses must implement -- {{gtMethod:GtSourceCoder>>#debug:}} - +- {{gtMethod:GtSourceCoder>>#primitiveDebug:inContext:onFailDo:}} " Class { #name : #GtSourceCoder, @@ -20,6 +17,11 @@ Class { #category : #'GToolkit-Coder-Coders' } +{ #category : #addons } +GtSourceCoder class >> contextHeaderMenuAddOnsPragma [ + ^#gtCoderHeaderContextMenuAddOns +] + { #category : #'api - actions' } GtSourceCoder >> debug [ self debugInContext: self evaluationContext @@ -59,10 +61,9 @@ GtSourceCoder >> discardChanges [ self isModified ifFalse: [ ^ self ]. - self sourceCode resetCollapsedText. self sourceCode resetSourceText. - self sourceChanged + self sourceChangedTo: nil ] { #category : #'api - actions' } @@ -86,9 +87,11 @@ GtSourceCoder >> doItAndGoInContext: aGtSourceCoderEvaluationContext [ ^ self evaluate: [ :thisCoder | thisCoder currentSourceString ] inContext: aGtSourceCoderEvaluationContext - thenDo: [ :anEvaluationResult | + thenDo: [ :anEvaluationResult | self notifyEvaluatedWithResult: anEvaluationResult. - self notifyObjectSpawnFromEvaluationResult: anEvaluationResult ] + self + notifyObjectSpawnFromEvaluationResult: anEvaluationResult + requesterObject: aGtSourceCoderEvaluationContext requesterObject ] ] { #category : #'api - actions' } @@ -215,8 +218,10 @@ GtSourceCoder >> evaluateBlock: aBlock onErrorDo: anErrorBlock [ aResultWithError error: (GtCoderEvaluationUnhandledError new exception: anError; sourceCoder: self; + evaluatedCode: aResultWithError evaluatedCode; sourceString: aResultWithError sourceString; - sourceInterval: aResultWithError sourceInterval). + sourceInterval: aResultWithError sourceInterval; + requesterObject: aResultWithError requesterObject). anError resignalAs: aResultWithError error. ^ nil ]. @@ -232,11 +237,106 @@ GtSourceCoder >> evaluateBlock: aBlock onErrorDo: anErrorBlock [ GtSourceCoder >> evaluationContext [ - ^ GtSourceCoderEvaluationContext new - requesterObject: self; + ^ self newEvaluationContext + requesterObject: (GtSourceCoderRequester new coder: self); coder: self ] +{ #category : #testing } +GtSourceCoder >> hasImplicitVariableReferenceTo: aString [ + self implicitVariableReferencesTo: aString do: [ :each | ^ true ]. + ^ false +] + +{ #category : #accessing } +GtSourceCoder >> implicitVariableNames [ + ^ #() +] + +{ #category : #'api - ast' } +GtSourceCoder >> implicitVariableReferencesTo: aString do: aBlock [ + "Evaluate aBlock for every ast node that is a variable reference to an implicitly defined variable named aString." + + +] + +{ #category : #'private - actions' } +GtSourceCoder >> newEvaluationContext [ + ^ GtSourceCoderEvaluationContext new +] + +{ #category : #'private - ast' } +GtSourceCoder >> nodeAt: anIndex [ + ^ self + nodeAt: anIndex + ifFound: [ :aNode | + aNode isSequence + ifTrue: [ aNode statements + detect: [ :eachNode | eachNode intersectsInterval: (anIndex to: anIndex) ] ] + ifFalse: [ aNode ] ] + ifNone: [ nil ] +] + +{ #category : #'private - ast' } +GtSourceCoder >> nodeAt: aTextPosition ifFound: aFoundBlock ifNone: aNoneBlock [ + + [ + | theAST innerNode | + theAST := self astSync. + theAST + withAllNodesDo: [ :node | + (aTextPosition between: node startPosition - 1 and: node stopPosition) + ifTrue: [ innerNode := node ] ]. + + ^ innerNode ifNil: aNoneBlock ifNotNil: aFoundBlock ] + on: Error + do: [ :ex | ex return ]. + + ^ aNoneBlock value +] + +{ #category : #accessing } +GtSourceCoder >> nodeFrom: aCoderViewModel [ + | allSelections allCursors | + allSelections := aCoderViewModel selection allSelections. + allCursors := aCoderViewModel cursors allCursors. + + ^ allSelections size = 1 + ifTrue: [ self nodeWithin: (allSelections first from + 1 + to: allSelections first to) ] + ifFalse: [ allCursors size = 1 ifTrue: [ + self nodeAt: allCursors first position ] ] +] + +{ #category : #accessing } +GtSourceCoder >> nodeWithin: anInterval [ + ^ self + nodeWithin: anInterval + ifFound: [ :aNode | + aNode isSequence + ifTrue: [ aNode statements + detect: [ :eachNode | eachNode intersectsInterval: anInterval ] ] + ifFalse: [ aNode ] ] + ifNone: [ ] +] + +{ #category : #'private - ast' } +GtSourceCoder >> nodeWithin: anInterval ifFound: aFoundBlock ifNone: aNoneBlock [ + + + [ + | theAST | + theAST := self astSync. + ^ theAST + bestNodeFor: anInterval + ifFound: aFoundBlock + ifNone: aNoneBlock ] + on: Error + do: [ :ex | ex return ]. + + ^ aNoneBlock value +] + { #category : #'private - notifying' } GtSourceCoder >> notifyEvaluatedWithResult: anEvaluationResult [ self announce: (GtCoderEvaluationAnnouncement new @@ -245,28 +345,47 @@ GtSourceCoder >> notifyEvaluatedWithResult: anEvaluationResult [ ] { #category : #'private - notifying' } -GtSourceCoder >> notifyObjectSpawn: anObject [ +GtSourceCoder >> notifyObjectSpawn: anObject requesterObject: aRequester [ self notifyObjectSpawn: anObject withDestination: self spawnDestination + requesterObject: aRequester ] { #category : #'private - notifying' } -GtSourceCoder >> notifyObjectSpawn: anObject withDestination: aSpawnDestination [ +GtSourceCoder >> notifyObjectSpawn: anObject withDestination: aSpawnDestination requesterObject: aRequester [ self announce: (GtCoderObjectSpawnRequest new object: anObject; spawnDestination: aSpawnDestination; - coder: self) + coder: self; + requesterObject: aRequester) ] { #category : #'private - notifying' } -GtSourceCoder >> notifyObjectSpawnFromEvaluationResult: anEvaluationResult [ - - anEvaluationResult isSuccess - ifFalse: [ ^ self ]. - +GtSourceCoder >> notifyObjectSpawnFromEvaluationResult: anEvaluationResult requesterObject: aRequester [ + anEvaluationResult isSuccess ifFalse: [ ^ self ]. + + self notifyObjectSpawn: anEvaluationResult value requesterObject: aRequester +] + +{ #category : #'private - notifying' } +GtSourceCoder >> notifyParseError: aString at: anInteger requesterObject: aRequester [ + self announce: (GtCoderParseError new + coder: self; + errorMessage: aString; + location: anInteger; + requesterObject: aRequester) +] + +{ #category : #'private - notifying' } +GtSourceCoder >> notifyParseError: aSemanticWarning requesterObject: aRequester [ self - notifyObjectSpawn: anEvaluationResult value + announce: (GtCoderParseError new + coder: self; + exception: aSemanticWarning; + errorMessage: aSemanticWarning messageText; + location: aSemanticWarning location; + requesterObject: aRequester) ] { #category : #'private - notifying' } @@ -277,19 +396,12 @@ GtSourceCoder >> notifyPrintResult: anEvaluationResult [ ] { #category : #'private - notifying' } -GtSourceCoder >> notifyShowDebuggerRequest: aDebugSession dueTo: anException sourceString: aSourceString sourceInterval: aSourceInterval [ - (self announcer subscriptionsForClass: GtCoderShowDebuggerRequest) size isZero - ifTrue: [ - ^ Smalltalk tools debugger - openOn: aDebugSession - withFullView: true ]. - - self announce: (GtCoderShowDebuggerRequest new - coder: self; - debugSession: aDebugSession; - exception: anException; - sourceString: aSourceString; - sourceInterval: aSourceInterval) +GtSourceCoder >> notifyToolSpawn: aTool withDestination: aSpawnDestination requesterObject: aRequester [ + self announce: (GtCoderToolSpawnRequest new + tool: aTool; + spawnDestination: aSpawnDestination; + requesterObject: aRequester; + coder: self) ] { #category : #'private - actions' } @@ -310,6 +422,21 @@ GtSourceCoder >> printItInContext: aGtSourceCoderEvaluationContext [ thenDo: [ :aResult | self notifyPrintResult: aResult ] ] +{ #category : #'api - ast' } +GtSourceCoder >> renameImplicitTemporary: oldName to: newName [ + | source | + source := SmaCCString on: self currentSourceString. + self + implicitVariableReferencesTo: oldName + do: + [ :node | + source + replaceFrom: node startPosition + to: node stopPosition + with: newName ]. + self currentSourceString: source asString +] + { #category : #accessing } GtSourceCoder >> spawnDestination [ ^ self @@ -321,3 +448,23 @@ GtSourceCoder >> spawnDestination [ GtSourceCoder >> spawnDestination: aSpawnDestination [ self attributeNamed: #spawnDestination put: aSpawnDestination ] + +{ #category : #'private - actions' } +GtSourceCoder >> validateSyntax [ + + ^ self validateSyntaxInContext: self evaluationContext +] + +{ #category : #'private - actions' } +GtSourceCoder >> validateSyntaxInContext: aGtPharoSourceCoderEvaluationContext [ + + [ self computeCoderAst ] + on: SmaCCParserError + do: [ :ex | + self + notifyParseError: ex messageText + at: ex tag position + requesterObject: aGtPharoSourceCoderEvaluationContext requesterObject. + ^ false ]. + ^ true +] diff --git a/src/GToolkit-Coder/GtSourceCoderEvaluationContext.class.st b/src/GToolkit-Coder/GtSourceCoderEvaluationContext.class.st index 23bba3841..2577b8979 100644 --- a/src/GToolkit-Coder/GtSourceCoderEvaluationContext.class.st +++ b/src/GToolkit-Coder/GtSourceCoderEvaluationContext.class.st @@ -7,7 +7,9 @@ Class { 'sourceInterval', 'sourceString', 'evaluatedInterval', - 'evaluatedSourceString' + 'evaluatedSourceString', + 'evaluatedCode', + 'executionStrategy' ], #category : #'GToolkit-Coder-Coders - Evaluation' } @@ -29,6 +31,22 @@ GtSourceCoderEvaluationContext >> coder: aGtSourceCoder [ coder := aGtSourceCoder ] +{ #category : #'api - context' } +GtSourceCoderEvaluationContext >> evaluatedCode [ + "Return an object, representing the evaluated source string. + For example in case of Pharo, it is a reference to a compiled method." + + + ^ evaluatedCode ifNil: [ evaluatedCode := GtSourceCoderNoEvaluatedCode default ] +] + +{ #category : #'api - context' } +GtSourceCoderEvaluationContext >> evaluatedCode: aContextReference [ + "Set a {{gtClass:TGtSourceCoderEvaluatedCode}}" + + evaluatedCode := aContextReference +] + { #category : #accessing } GtSourceCoderEvaluationContext >> evaluatedInterval [ ^ evaluatedInterval @@ -49,6 +67,16 @@ GtSourceCoderEvaluationContext >> evaluatedSourceString: anObject [ evaluatedSourceString := anObject ] +{ #category : #accessing } +GtSourceCoderEvaluationContext >> executionStrategy [ + ^ executionStrategy +] + +{ #category : #accessing } +GtSourceCoderEvaluationContext >> executionStrategy: anObject [ + executionStrategy := anObject +] + { #category : #'api - context' } GtSourceCoderEvaluationContext >> from: aStartPosition to: aStopPosition [ sourceInterval := GtSourceCoderEvaluationPartialSource new @@ -66,12 +94,13 @@ GtSourceCoderEvaluationContext >> initialize [ { #category : #accessing } GtSourceCoderEvaluationContext >> requesterObject [ - ^ requesterObject + + ^ requesterObject ifNil: [ GtNoCoderRequester uniqueInstance ] ] { #category : #accessing } GtSourceCoderEvaluationContext >> requesterObject: anObject [ - requesterObject := anObject + requesterObject := anObject asCoderRequesterObject ] { #category : #accessing } @@ -97,3 +126,12 @@ GtSourceCoderEvaluationContext >> sourceString: anObject [ sourceString := anObject ] + +{ #category : #accessing } +GtSourceCoderEvaluationContext >> targetBehavior [ + "Return a target behavior in which the method should be compiled. The target behavior can differ from + the method's behavior if for example the method originates from the trait" + + + ^ nil +] diff --git a/src/GToolkit-Coder/GtSourceCoderEvaluationResult.class.st b/src/GToolkit-Coder/GtSourceCoderEvaluationResult.class.st index 02cb69ff1..a1428f501 100644 --- a/src/GToolkit-Coder/GtSourceCoderEvaluationResult.class.st +++ b/src/GToolkit-Coder/GtSourceCoderEvaluationResult.class.st @@ -7,6 +7,15 @@ Class { #category : #'GToolkit-Coder-Coders - Evaluation' } +{ #category : #accessing } +GtSourceCoderEvaluationResult >> evaluatedCode [ + "Return an object, representing the evaluated source string. + For example in case of Pharo, it is a reference to a compiled method." + + + ^ self evaluationContext evaluatedCode +] + { #category : #accessing } GtSourceCoderEvaluationResult >> evaluationContext [ ^ evaluationContext @@ -39,6 +48,7 @@ GtSourceCoderEvaluationResult >> isSyntaxError [ { #category : #accessing } GtSourceCoderEvaluationResult >> requesterObject [ + ^ self evaluationContext requesterObject ] diff --git a/src/GToolkit-Coder/GtSourceCoderNoEvaluatedCode.class.st b/src/GToolkit-Coder/GtSourceCoderNoEvaluatedCode.class.st new file mode 100644 index 000000000..158dad0e5 --- /dev/null +++ b/src/GToolkit-Coder/GtSourceCoderNoEvaluatedCode.class.st @@ -0,0 +1,7 @@ +Class { + #name : #GtSourceCoderNoEvaluatedCode, + #superclass : #Object, + #traits : 'TGtSourceCoderEvaluatedCode + TGtUniqueInstance', + #classTraits : 'TGtSourceCoderEvaluatedCode classTrait + TGtUniqueInstance classTrait', + #category : #'GToolkit-Coder-Coders - Evaluation' +} diff --git a/src/GToolkit-Coder/GtSourceCoderRequester.class.st b/src/GToolkit-Coder/GtSourceCoderRequester.class.st new file mode 100644 index 000000000..071922403 --- /dev/null +++ b/src/GToolkit-Coder/GtSourceCoderRequester.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtSourceCoderRequester, + #superclass : #Object, + #traits : 'TGtCoderRequesterObject', + #classTraits : 'TGtCoderRequesterObject classTrait', + #instVars : [ + 'coder' + ], + #category : #'GToolkit-Coder-Coders - Evaluation' +} + +{ #category : #accessing } +GtSourceCoderRequester >> coder [ + ^ coder +] + +{ #category : #accessing } +GtSourceCoderRequester >> coder: anObject [ + coder := anObject +] diff --git a/src/GToolkit-Coder/GtSourceDiffChange.class.st b/src/GToolkit-Coder/GtSourceDiffChange.class.st new file mode 100644 index 000000000..dc931d8b2 --- /dev/null +++ b/src/GToolkit-Coder/GtSourceDiffChange.class.st @@ -0,0 +1,95 @@ +Class { + #name : #GtSourceDiffChange, + #superclass : #GtDiffChange, + #instVars : [ + 'startIndex', + 'stopIndex', + 'source' + ], + #category : #'GToolkit-Coder-Diff-Algorithm' +} + +{ #category : #accessing } +GtSourceDiffChange class >> highlightColor [ + ^ self subclassResponsibility +] + +{ #category : #'instance creation' } +GtSourceDiffChange class >> on: aCollection from: startIndex to: stopIndex [ + ^ self new + source: aCollection; + startIndex: startIndex; + stopIndex: stopIndex; + yourself +] + +{ #category : #accessing } +GtSourceDiffChange >> gtLiveFor: aView [ + + ^ aView textEditor + title: 'Source'; + text: [ | stream | + stream := BlTextStream new. + stream next putAll: (source copyFrom: 1 to: startIndex - 1) asString. + stream next + highlight: self highlightColor; + putAll: self objects asString. + stream next putAll: (source copyFrom: stopIndex + 1 to: source size) asString. + stream contents ] +] + +{ #category : #accessing } +GtSourceDiffChange >> gtObjectsText [ + ^ self objects asString asRopedText glamorousRegularFont + highlight: self highlightColor +] + +{ #category : #accessing } +GtSourceDiffChange >> highlightColor [ + ^ self class highlightColor +] + +{ #category : #testing } +GtSourceDiffChange >> isEmpty [ + ^ startIndex > stopIndex +] + +{ #category : #accessing } +GtSourceDiffChange >> objects [ + ^ source copyFrom: self startIndex to: self stopIndex +] + +{ #category : #accessing } +GtSourceDiffChange >> range [ + ^ self startIndex printString , '-' , self stopIndex printString +] + +{ #category : #accessing } +GtSourceDiffChange >> source [ + ^ source +] + +{ #category : #accessing } +GtSourceDiffChange >> source: aCollection [ + source := aCollection +] + +{ #category : #accessing } +GtSourceDiffChange >> startIndex [ + ^ startIndex +] + +{ #category : #accessing } +GtSourceDiffChange >> startIndex: anInteger [ + startIndex := anInteger +] + +{ #category : #accessing } +GtSourceDiffChange >> stopIndex [ + ^ stopIndex +] + +{ #category : #accessing } +GtSourceDiffChange >> stopIndex: anInteger [ + stopIndex := anInteger +] diff --git a/src/GToolkit-Coder/GtSourceReferenceHighlighter.class.st b/src/GToolkit-Coder/GtSourceReferenceHighlighter.class.st index 594e1295e..36b03375c 100644 --- a/src/GToolkit-Coder/GtSourceReferenceHighlighter.class.st +++ b/src/GToolkit-Coder/GtSourceReferenceHighlighter.class.st @@ -7,6 +7,11 @@ Class { #category : #'GToolkit-Coder-Styler/Highlighter' } +{ #category : #accessing } +GtSourceReferenceHighlighter class >> highlightAttribute [ + ^ HighlightAttributes first +] + { #category : #'class initialization' } GtSourceReferenceHighlighter class >> initialize [ HighlightAttributes := { diff --git a/src/GToolkit-Coder/GtTextCoder.class.st b/src/GToolkit-Coder/GtTextCoder.class.st index 1a6f88585..7eda3ab5d 100644 --- a/src/GToolkit-Coder/GtTextCoder.class.st +++ b/src/GToolkit-Coder/GtTextCoder.class.st @@ -20,12 +20,7 @@ GtTextCoder >> computeAst: theSourceString [ { #category : #initialization } GtTextCoder >> forString: aString [ - self sourceCode: (GtCoderExplicitSourceCode new source: aString) -] - -{ #category : #'instance creation' } -GtTextCoder >> newCompletionStrategy [ - ^ GtCompletionStrategy new + self sourceCode: (GtCoderExplicitStringSource new source: aString) ] { #category : #accessing } @@ -47,8 +42,3 @@ GtTextCoder >> parserStartingState [ GtTextCoder >> parserStartingState: anObject [ parserStartingState := anObject ] - -{ #category : #'api - ast' } -GtTextCoder >> supportsAstCache [ - ^ false -] diff --git a/src/GToolkit-Coder/GtTextualCoder.class.st b/src/GToolkit-Coder/GtTextualCoder.class.st index 77f41c052..4ffefaf9c 100644 --- a/src/GToolkit-Coder/GtTextualCoder.class.st +++ b/src/GToolkit-Coder/GtTextualCoder.class.st @@ -3,40 +3,46 @@ Class { #superclass : #GtCoderModel, #instVars : [ 'sourceCode', - 'completionStrategy', + 'astPromise', 'astCache', - 'newAstCache', - 'astMonitor', - 'astCommand', - 'astCommandBlock' + 'previousAst' + ], + #classVars : [ + 'UseIncrementalParsing' ], #category : #'GToolkit-Coder-Coders' } +{ #category : #accessing } +GtTextualCoder class >> useIncrementalParsing [ + ^ UseIncrementalParsing ifNil: [ false ] +] + +{ #category : #accessing } +GtTextualCoder class >> useIncrementalParsing: aBoolean [ + UseIncrementalParsing := aBoolean +] + { #category : #'private - addons' } GtTextualCoder >> addOnsClass [ ^ GtTextualCoderAddOns ] { #category : #'api - text' } -GtTextualCoder >> appendString: aString [ - self critical: [ - self sourceCode appendString: aString. - self sourceChanged ] +GtTextualCoder >> appendString: aString [ + self currentSourceText: (self currentSourceText copy appendString: aString) ] { #category : #'api - text' } GtTextualCoder >> appendText: aText [ - self critical: [ - self sourceCode appendText: aText. - self sourceChanged ] + self currentSourceText: (self currentSourceText copy append: aText) ] { #category : #'api - text' } GtTextualCoder >> appendText: aText with: aGtCoderUpdateStragegy [ - self critical: [ - self sourceCode appendText: aText. - self sourceChangedWith: aGtCoderUpdateStragegy ] + self + currentSourceText: (self currentSourceText copy append: aText) + with: aGtCoderUpdateStragegy ] { #category : #'api - converting' } @@ -47,124 +53,111 @@ GtTextualCoder >> asNewCoderModelWithSameSubject [ ^ self shouldNotImplement ] +{ #category : #'api - ast' } +GtTextualCoder >> ast [ + + + ^ self coderAst +] + { #category : #'api - ast' } GtTextualCoder >> astAsyncDo: aBlock [ - astMonitor critical: [ - astCommand ifNotNil: [ - "the ast is being computed, subscribe to the ast changed announcement to wait until is finishes" - ^ self - when: GtCoderAstChanged - doOnce: [ :anAnnouncement | - aBlock value: anAnnouncement ast ] - for: #astChanged ]. - - "the ast is already computed, return it directly" - newAstCache ifNotNil: [ :anAST | ^ aBlock value: anAST ]. - - astCommandBlock := [ - | theSourceString isModified astCacheKey aComputedAst | - - self critical: [ - theSourceString := self currentSourceString. - isModified := self isModified. - astCacheKey := self sourceCode asAstCacheKey ]. - - aComputedAst := isModified - ifTrue: [ self computeAst: theSourceString ] - ifFalse: [ - self supportsAstCache - ifTrue: [ - GtAstCache default - cachedAstAt: astCacheKey - ifAbsentPut: [ self computeAst: theSourceString ] ] - ifFalse: [ self computeAst: theSourceString ] ]. - - astMonitor critical: [ - newAstCache := aComputedAst. - astCommand := nil. - astCommandBlock := nil ]. - self announce: (GtCoderAstChanged new coder: self; ast: aComputedAst). - aBlock value: aComputedAst ]. - astCommand := astCommandBlock asBlTktTerminableCommand asBlTktLoggingCommand. - - BlUseAsyncFeatures - ifEnabledDo: [ BlTktWorkerProvider coderAstPool schedule: astCommand ] - otherwise: [ astCommand execute ] ] + self coderAst asyncThen: [ :aGtCoderAst | aBlock value: aGtCoderAst ast ] ] { #category : #'api - ast' } GtTextualCoder >> astAwait [ - | theAst | - - theAst := nil. - self astAwaitDo: [ :aComputedAst | theAst := aComputedAst ]. - ^ theAst + self + deprecated: 'Please use #astSync instead.' + transformWith: '`@receiver astAwait' -> '`@receiver astSync'. + + ^ self astSync ] { #category : #'api - ast' } -GtTextualCoder >> astAwaitDo: aBlock [ - | aSemaphore anAst | - - aSemaphore := Semaphore new. - anAst := nil. - self astAsyncDo: [ :aComputedAst | - anAst := aComputedAst. - aSemaphore signal ]. - - aSemaphore wait: 5 seconds. - aBlock value: anAst +GtTextualCoder >> astFuture [ + + + ^ self coderAstFuture map: [ :aCoderAst | aCoderAst ast ] ] -{ #category : #'private - ast' } -GtTextualCoder >> astCache [ - ^ astMonitor critical: [ astCache ] +{ #category : #'api - ast' } +GtTextualCoder >> astSync [ + ^ self coderAstSync ast ] -{ #category : #'private - ast' } -GtTextualCoder >> astCache: anAssociation [ - astMonitor critical: [ astCache := anAssociation ]. - self onAstChanged +{ #category : #'api - ast' } +GtTextualCoder >> coderAst [ + + + ^ self coderAstFuture await: self class commonExecutionConfiguration +] + +{ #category : #'api - ast' } +GtTextualCoder >> coderAstFuture [ + + ^ AsyncCachedFuture new + future: [ self computeCoderAst ] asAsyncFuture + cache: astCache +] + +{ #category : #'api - ast' } +GtTextualCoder >> coderAstSync [ + "Attempt to take a Coder AST from cache, otherwise compute it synchronously" + + + ^ astCache + ifSome: [ :aValue | aValue ] + ifNonePut: [ self computeCoderAst] ] { #category : #'api - text' } GtTextualCoder >> collapsedText [ + | aCurrentText | - ^ self sourceCode collapsedText + aCurrentText := self currentSourceText. + ^ aCurrentText + ifEmpty: [ '' asRopedText ] + ifNotEmpty: [ aCurrentText iterator nextLine ] ] -{ #category : #'api - accessing' } -GtTextualCoder >> completionStrategy [ - - - ^ completionStrategy - ifNil: [ completionStrategy := self newCompletionStrategy ] -] +{ #category : #'api - text' } +GtTextualCoder >> collapsedTextPromise [ + -{ #category : #'api - accessing' } -GtTextualCoder >> completionStrategy: aGtCompletionStrategy [ - completionStrategy := aGtCompletionStrategy + ^ [ self collapsedText ] asAsyncFuture await: self class commonExecutionConfiguration ] { #category : #'api - ast' } -GtTextualCoder >> computeAst [ - ^ self computeAst: self currentSourceString +GtTextualCoder >> computeAst: theSourceString [ + "Given a string, compute and return a domain specific AST" + + ^ self subclassResponsibility ] { #category : #'api - ast' } -GtTextualCoder >> computeAst: theSourceString [ - ^ self subclassResponsibility +GtTextualCoder >> computeCoderAst [ + + | aGtCoderSourceString | + + aGtCoderSourceString := self sourceCode currentSourceString. + ^ GtCoderAst new + ast: (self computeAst: aGtCoderSourceString string); + sourceString: aGtCoderSourceString; + epoch: aGtCoderSourceString epoch ] { #category : #'api - text' } GtTextualCoder >> correctFrom: aFromIndex to: aToIndex with: aString [ - self critical: [ + self flag: #TODO. + "self critical: [ self sourceCode replaceFrom: aFromIndex to: aToIndex withString: aString. self sourceChanged. - self notifyCorrectionFrom: aFromIndex to: aToIndex with: aString ] + self notifyCorrectionFrom: aFromIndex to: aToIndex with: aString ]" ] { #category : #'api - text' } @@ -172,16 +165,22 @@ GtTextualCoder >> currentSourceString [ "Return a String representation of the current source text" - ^ self critical: [ self sourceCode sourceString ] + ^ self sourceCode currentSourceString string ] { #category : #'api - text' } GtTextualCoder >> currentSourceString: aString [ "Set a new source text" - self critical: [ - self sourceCode currentSourceString: aString. - self sourceChanged ] + self currentSourceText: aString asRopedText +] + +{ #category : #'api - text' } +GtTextualCoder >> currentSourceText [ + + "Set a new source text" + + ^ self sourceCode currentSourceText text ] { #category : #'api - text' } @@ -190,16 +189,49 @@ GtTextualCoder >> currentSourceText: aText [ self critical: [ self sourceCode currentSourceText: aText. - self sourceChanged ] + self sourceChangedTo: aText ] ] { #category : #'api - text' } GtTextualCoder >> currentSourceText: aText with: aGtCoderUpdateStragegy [ "Set a new source text" + self + currentSourceText: aText + with: aGtCoderUpdateStragegy + dueTo: nil +] + +{ #category : #'api - text' } +GtTextualCoder >> currentSourceText: aText with: aGtCoderUpdateStragegy dueTo: aReason [ + "Set a new source text, giving also a reason for the change" + self critical: [ + | previousSourceText| + aText == self currentSourceText ifTrue: [ ^ self ]. + previousSourceText := self currentSourceText copy. self sourceCode currentSourceText: aText. - self sourceChangedWith: aGtCoderUpdateStragegy ] + self + sourceChangedTo: aText + from: previousSourceText + with: aGtCoderUpdateStragegy + dueTo: aReason ] +] + +{ #category : #'api - text' } +GtTextualCoder >> currentSourceText: aText withEditCommand: anEditCommand [ + "Set a new source text, together with a command used to edit the text." + + self critical: [ + self sourceCode currentSourceText: aText. + self sourceChangedTo: aText withEditCommand: anEditCommand ] +] + +{ #category : #'api - text' } +GtTextualCoder >> currentSourceTextPromise [ + + + ^ [ self currentSourceText ] asAsyncFuture await: self class commonExecutionConfiguration ] { #category : #'api - text' } @@ -208,14 +240,15 @@ GtTextualCoder >> currentSourceTextSynchronously: aText [ self critical: [ self sourceCode currentSourceText: aText. - self sourceChangedWith: GtCoderUpdateStrategy new makeSynchronous ] + self + sourceChangedTo: aText + with: GtCoderUpdateStrategy new + makeSynchronous ] ] { #category : #'api - text' } GtTextualCoder >> deleteSourceTextFrom: aFromIndex to: aToIndex [ - self critical: [ - self sourceCode delete: aFromIndex to: aToIndex. - self sourceChanged ] + self currentSourceText: (self currentSourceText copy delete: aFromIndex to: aToIndex) ] { #category : #'api - ast' } @@ -226,11 +259,16 @@ GtTextualCoder >> ensureAst [ { #category : #initialize } GtTextualCoder >> initialize [ super initialize. - - astMonitor := Monitor new. + + astCache := AsyncFutureCache new. self reset ] +{ #category : #testing } +GtTextualCoder >> isForMethod [ + ^ false +] + { #category : #testing } GtTextualCoder >> isModified [ @@ -238,9 +276,19 @@ GtTextualCoder >> isModified [ ^ self sourceCode isModified ] -{ #category : #'instance creation' } -GtTextualCoder >> newCompletionStrategy [ - ^ self subclassResponsibility +{ #category : #testing } +GtTextualCoder >> isModifiedFuture [ + + + ^ [ self sourceCode isModified ] asAsyncFuture +] + +{ #category : #testing } +GtTextualCoder >> isModifiedPromise [ + "Deprecated: use `GtTextualCoderViewModel>>#isTextModifiedPromise` instead." + + + ^ self isModifiedFuture asAsyncFuture await: self class commonExecutionConfiguration ] { #category : #'private - notifying' } @@ -253,115 +301,116 @@ GtTextualCoder >> notifyCorrectionFrom: aFromIndex to: aToIndex with: aString [ ] { #category : #'private - notifying' } -GtTextualCoder >> notifySourceChangedWith: anUpdateStragegy [ +GtTextualCoder >> notifySourceChangedTo: aNewText from: aPreviousText with: anUpdateStragegy dueTo: aReason [ "Notify the text editor that it should update the text (for example due to refactoring changes)" - - self announce: (GtCoderSourceCodeChanged new - updateStrategy: anUpdateStragegy; - coder: self) -] -{ #category : #'private - notifying' } -GtTextualCoder >> notifyStylersUpdated [ - "Is sent when AddOn stylers changed. It also means that the text editor should restyle the text" + "If user didn't specify the source, we assume that the coder model plays a role of the source" + anUpdateStragegy source ifNil: [ + anUpdateStragegy source: self ]. - self announce: (GtCoderStylerChanged new + self announce: (GtCoderSourceCodeChanged new + updateStrategy: anUpdateStragegy; + newText: aNewText; + previousText: aPreviousText; coder: self; - stylers: self addOns stylers copy) + reason: aReason) ] -{ #category : #'private - ast' } -GtTextualCoder >> onAstChanged [ -] - -{ #category : #private } -GtTextualCoder >> pragmasNamed: aSymbol inHierarchy: aClass [ - | actions | - actions := OrderedCollection new. - aClass withAllSuperclassesDo: [ :each | actions addAll: (Pragma allNamed: aSymbol in: each) ]. - actions sort: [ :a :b | a arguments first < b arguments first ]. - ^ actions +{ #category : #'private - notifying' } +GtTextualCoder >> notifySourceCodeReplaced: aSourceCode [ + self announce: (GtCoderSourceCodeReplaced new + sourceCode: aSourceCode; + coder: self) ] { #category : #'api - text' } GtTextualCoder >> requestStyleSourceText [ "Request the text editor to restyle the text (for example due to environmental changes)" - self resetASTCache. + self resetAstCache. self announce: (GtCoderStyleTextRequest new coder: self) ] -{ #category : #'api - text' } -GtTextualCoder >> requestStyleSourceTextAndAfterDo: aBlock [ - "Request the text editor to restyle the text (for example due to environmental changes)" - - self resetASTCache. - self - announce: - (GtCoderStyleTextRequest new - coder: self; - afterAction: aBlock) -] - -{ #category : #'api - text' } -GtTextualCoder >> requestUpdateCollapsedText [ - "Request to update the collapsed text, for example due to changes in the current source text. - It is not guaranteed that the collapsed text actually changes as for example in the method coder - opened on a compiled method" - - self sourceCode resetCollapsedText. - self announce: (GtCoderCollapsedLabelChanged new coder: self) -] - { #category : #initialize } GtTextualCoder >> reset [ - self resetASTCache. + self resetAstCache. "initialized lazily in #sourceCode" sourceCode := nil. - completionStrategy := nil ] { #category : #'private - ast' } -GtTextualCoder >> resetASTCache [ - self astCache: (nil -> nil) +GtTextualCoder >> resetAstCache [ + ^ self resetAstCache: nil +] + +{ #category : #'private - ast' } +GtTextualCoder >> resetAstCache: aReason [ + self + critical: [ astPromise := nil. + self class useIncrementalParsing + ifTrue: [ (aReason notNil and: [ aReason isModification ]) + ifTrue: [ astCache ifSome: [ :value | previousAst := value ast ] ifNone: [ ]. + (previousAst isKindOf: SmaCCParseNode) + ifTrue: [ aReason isInsertion + ifTrue: [ aReason + indicesAndLengthsDo: [ :index :len | + previousAst + invalidateFrom: index + 1 + to: index + inserting: len ] ] + ifFalse: [ aReason + indicesAndLengthsDo: [ :index :len | + previousAst + invalidateFrom: index + to: index + len - 1 + inserting: 0 ] ] ] ] + ifFalse: [ previousAst := nil ] ]. + astCache resetCache ] ] { #category : #'event handling' } -GtTextualCoder >> sourceChanged [ - self sourceChangedWith: GtCoderUpdateStrategy new makeAsynchronous +GtTextualCoder >> sourceChangedTo: aNewText [ + self + sourceChangedTo: aNewText + withEditCommand: nil ] { #category : #'event handling' } -GtTextualCoder >> sourceChangedWith: anUpdateStrategy [ - "if there is an ast computation that was triggered before source code change we re-schedule - the computation. Any #astAsyncDo: caller will be waiting for the result with the new ast" - - astMonitor critical: [ - newAstCache := nil. - astCommand ifNotNil: [ :anAstCommand | - anAstCommand terminate. - astCommand := astCommandBlock asBlTktTerminableCommand asBlTktLoggingCommand. - [ BlTktWorkerProvider coderAstPool schedule: astCommand ] on: Error do: [ :anError | - astCommand := nil. - anError emit. - NonInteractiveTranscript stderr - nextPut: $[; - nextPutAll: self class name; - nextPutAll: '#>>sourceChangedWith:'; - nextPut: $]; - nextPutAll: ' Failed to schedule an ast computation command' ] ] ]. - - self notifySourceChangedWith: anUpdateStrategy. - - self requestUpdateCollapsedText +GtTextualCoder >> sourceChangedTo: aNewText from: aPreviousText with: anUpdateStrategy dueTo: aReason [ + self resetAstCache: aReason. + self + notifySourceChangedTo: aNewText + from: aPreviousText + with: anUpdateStrategy + dueTo: aReason. + self requestUpdateAddOns +] + +{ #category : #'event handling' } +GtTextualCoder >> sourceChangedTo: aNewText with: anUpdateStrategy [ + + self + sourceChangedTo: aNewText + from: nil + with: anUpdateStrategy + dueTo: nil +] + +{ #category : #'event handling' } +GtTextualCoder >> sourceChangedTo: aNewText withEditCommand: anEditCommand [ + self + sourceChangedTo: aNewText + with: (GtCoderUpdateStrategy new + makeAsynchronous; + textEditCommand: anEditCommand) ] { #category : #'api - accessing' } GtTextualCoder >> sourceCode [ - + - sourceCode ifNil: [ self sourceCode: (GtCoderExplicitSourceCode new source: '') ]. + sourceCode ifNil: [ self sourceCode: (GtCoderExplicitStringSource new source: '') ]. ^ sourceCode ] @@ -379,7 +428,8 @@ GtTextualCoder >> sourceCode: aGtCoderSourceCode withStrategy: anUpdateStragegy "Set the source code of this coder to a given one and notify about the changes" sourceCode := aGtCoderSourceCode. - self sourceChangedWith: anUpdateStragegy. + self notifySourceCodeReplaced: aGtCoderSourceCode. + self sourceChangedTo: nil with: anUpdateStragegy ] { #category : #'api - accessing' } @@ -391,11 +441,13 @@ GtTextualCoder >> sourceCodeSynchronously: aGtCoderSourceCode [ { #category : #'api - text' } GtTextualCoder >> sourceText [ - "Return the source text of this coder. The computation of the source text may be expensive, use me with caution. - If the operation you would like to perform on the source text is optional consider using #sourceTextDo:" - + - ^ self sourceCode sourceText + self + deprecated: 'Use #currentSourceText' + transformWith: '`@receiver sourceText' -> '`@receiver currentSourceText'. + + ^ self currentSourceText ] { #category : #'api - text' } @@ -410,17 +462,18 @@ GtTextualCoder >> stylers [ ^ #() ] -{ #category : #'api - ast' } -GtTextualCoder >> supportsAstCache [ - "Return true if ast cache should be used, false otherwise" - - ^ true -] +{ #category : #'api - text' } +GtTextualCoder >> textReplaced: aText with: aGtCoderUpdateStragegy dueTo: aReason [ + "Set a new source text if the characters are different, giving also a reason for the change" -{ #category : #'api - ast' } -GtTextualCoder >> terminateAstCommand [ - astMonitor critical: [ - astCommand ifNotNil: [ :anAstCommand | anAstCommand terminate ]. - astCommand := nil. - astCommandBlock := nil ] + self + critical: [ | previousSourceText | + previousSourceText := self currentSourceText copy. + (previousSourceText equalsIgnoringAttributes: aText) + ifFalse: [ self sourceCode currentSourceText: aText. + self + sourceChangedTo: aText + from: previousSourceText + with: aGtCoderUpdateStragegy + dueTo: aReason ] ] ] diff --git a/src/GToolkit-Coder/GtTextualCoderAddOns.class.st b/src/GToolkit-Coder/GtTextualCoderAddOns.class.st index 08a907cf2..0625e1941 100644 --- a/src/GToolkit-Coder/GtTextualCoderAddOns.class.st +++ b/src/GToolkit-Coder/GtTextualCoderAddOns.class.st @@ -2,7 +2,8 @@ Class { #name : #GtTextualCoderAddOns, #superclass : #GtCoderAddOns, #instVars : [ - 'stylers' + 'stylers', + 'ast' ], #category : #'GToolkit-Coder-Coders - Addons' } @@ -27,21 +28,16 @@ GtTextualCoderAddOns >> addStylers: aCollectionOfStylers [ aCollectionOfStylers do: [ :eachStyler | self addStyler: eachStyler ] ] -{ #category : #changes } -GtTextualCoderAddOns >> differenceWith: aGtCoderAddOns on: aStream [ - super differenceWith: aGtCoderAddOns on: aStream. - - self stylers = aGtCoderAddOns stylers - ifFalse: [ aStream nextPut: GtCoderAddOnsStylersDifference new ] +{ #category : #accessing } +GtTextualCoderAddOns >> ast [ + + + ^ ast ] -{ #category : #'gt-extensions' } -GtTextualCoderAddOns >> gtViewStylersFor: aView [ - - ^ aView list - title: 'Stylers' translated; - priority: 30; - items: [ self stylers ] +{ #category : #accessing } +GtTextualCoderAddOns >> ast: aGtCoderAst [ + ast := aGtCoderAst ] { #category : #'initialize-release' } @@ -56,6 +52,11 @@ GtTextualCoderAddOns >> postCopy [ stylers := stylers copy ] +{ #category : #'api - stylers' } +GtTextualCoderAddOns >> removeStylersOfClass: aStylerClass [ + stylers removeAllSuchThat: [ :each | each class = aStylerClass ] +] + { #category : #'api - stylers' } GtTextualCoderAddOns >> stylers [ ^ stylers diff --git a/src/GToolkit-Coder/GtTraitsCompletionStrategy.class.st b/src/GToolkit-Coder/GtTraitsCompletionStrategy.class.st index fb2c49d27..d41f9dce5 100644 --- a/src/GToolkit-Coder/GtTraitsCompletionStrategy.class.st +++ b/src/GToolkit-Coder/GtTraitsCompletionStrategy.class.st @@ -1,6 +1,6 @@ Class { #name : #GtTraitsCompletionStrategy, - #superclass : #GtCompletionStrategy, + #superclass : #GtStreamedCompletionStrategy, #instVars : [ 'candidateTraits' ], @@ -16,23 +16,17 @@ GtTraitsCompletionStrategy >> candidateTraits [ ] { #category : #accessing } -GtTraitsCompletionStrategy >> completionActionsFor: aText at: positionInteger max: maxInteger [ - | currentInput completionActions | - +GtTraitsCompletionStrategy >> completionActionStreamFor: aText at: positionInteger requested: aBoolean [ + | currentInput | currentInput := aText asString. - completionActions := (self candidateTraits first: maxInteger startingWith: currentInput) collect: [ :traitName | - GtInsertTextCompletionAction - labeled: traitName - completion: (traitName allButFirst: currentInput size) - position: positionInteger - from: positionInteger - currentInput size + 1 ]. - - ^ completionActions -] - -{ #category : #testing } -GtTraitsCompletionStrategy >> hasCompletionEntryFor: aString [ - ^ true + ^ (self candidateTraits asyncStreamStartingWith: currentInput) + collect: + [ :traitName | + GtInsertTextCompletionAction + labeled: (self labelFor: traitName withSearch: currentInput) + completion: (traitName allButFirst: currentInput size) + position: positionInteger + from: positionInteger - currentInput size + 1 ] ] { #category : #testing } diff --git a/src/GToolkit-Coder/OCArrayNode.extension.st b/src/GToolkit-Coder/OCArrayNode.extension.st new file mode 100644 index 000000000..3b4c77473 --- /dev/null +++ b/src/GToolkit-Coder/OCArrayNode.extension.st @@ -0,0 +1,10 @@ +Extension { #name : #OCArrayNode } + +{ #category : #'*GToolkit-Coder' } +OCArrayNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + periods + ifNotNil: [ periods := periods collect: [ :f | f + anInteger ] ]. + left ifNotNil: [ left := left + anInteger ]. + right ifNotNil: [ right := right + anInteger ] +] diff --git a/src/GToolkit-Coder/OCAssignmentNode.extension.st b/src/GToolkit-Coder/OCAssignmentNode.extension.st new file mode 100644 index 000000000..0d737364c --- /dev/null +++ b/src/GToolkit-Coder/OCAssignmentNode.extension.st @@ -0,0 +1,7 @@ +Extension { #name : #OCAssignmentNode } + +{ #category : #'*GToolkit-Coder' } +OCAssignmentNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + assignment ifNotNil: [ assignment := assignment + anInteger ] +] diff --git a/src/GToolkit-Coder/OCBlockNode.extension.st b/src/GToolkit-Coder/OCBlockNode.extension.st new file mode 100644 index 000000000..9aa0dd76e --- /dev/null +++ b/src/GToolkit-Coder/OCBlockNode.extension.st @@ -0,0 +1,10 @@ +Extension { #name : #OCBlockNode } + +{ #category : #'*GToolkit-Coder' } +OCBlockNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + colons ifNotNil: [ colons := colons collect: [ :f | f + anInteger ] ]. + left ifNotNil: [ left := left + anInteger ]. + right ifNotNil: [ right := right + anInteger ]. + bar ifNotNil: [ bar := bar + anInteger ] +] diff --git a/src/GToolkit-Coder/OCCascadeNode.extension.st b/src/GToolkit-Coder/OCCascadeNode.extension.st new file mode 100644 index 000000000..0279814b5 --- /dev/null +++ b/src/GToolkit-Coder/OCCascadeNode.extension.st @@ -0,0 +1,9 @@ +Extension { #name : #OCCascadeNode } + +{ #category : #'*GToolkit-Coder' } +OCCascadeNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + (messages size - 1) timesRepeat: [ self receiver gtMoveBy: 0 - anInteger ]. + semicolons + ifNotNil: [ semicolons := semicolons collect: [ :f | f + anInteger ] ] +] diff --git a/src/GToolkit-Coder/OCLiteralNode.extension.st b/src/GToolkit-Coder/OCLiteralNode.extension.st new file mode 100644 index 000000000..0d11d0feb --- /dev/null +++ b/src/GToolkit-Coder/OCLiteralNode.extension.st @@ -0,0 +1,8 @@ +Extension { #name : #OCLiteralNode } + +{ #category : #'*GToolkit-Coder' } +OCLiteralNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + start ifNotNil: [ start := start + anInteger ]. + stop ifNotNil: [ stop := stop + anInteger ] +] diff --git a/src/GToolkit-Coder/OCMessageNode.extension.st b/src/GToolkit-Coder/OCMessageNode.extension.st new file mode 100644 index 000000000..a944b680a --- /dev/null +++ b/src/GToolkit-Coder/OCMessageNode.extension.st @@ -0,0 +1,9 @@ +Extension { #name : #OCMessageNode } + +{ #category : #'*GToolkit-Coder' } +OCMessageNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + keywordsPositions + ifNotNil: [ keywordsPositions := keywordsPositions + collect: [ :f | f + anInteger ] ] +] diff --git a/src/GToolkit-Coder/OCMethodNode.extension.st b/src/GToolkit-Coder/OCMethodNode.extension.st new file mode 100644 index 000000000..5884ee44f --- /dev/null +++ b/src/GToolkit-Coder/OCMethodNode.extension.st @@ -0,0 +1,9 @@ +Extension { #name : #OCMethodNode } + +{ #category : #'*GToolkit-Coder' } +OCMethodNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + keywordsPositions + ifNotNil: [ keywordsPositions := keywordsPositions + collect: [ :f | f + anInteger ] ] +] diff --git a/src/GToolkit-Coder/OCParseErrorNode.extension.st b/src/GToolkit-Coder/OCParseErrorNode.extension.st new file mode 100644 index 000000000..bfbf9a75f --- /dev/null +++ b/src/GToolkit-Coder/OCParseErrorNode.extension.st @@ -0,0 +1,7 @@ +Extension { #name : #OCParseErrorNode } + +{ #category : #'*GToolkit-Coder' } +OCParseErrorNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + start ifNotNil: [ start := start + anInteger ] +] diff --git a/src/GToolkit-Coder/OCPragmaNode.extension.st b/src/GToolkit-Coder/OCPragmaNode.extension.st new file mode 100644 index 000000000..86314fa43 --- /dev/null +++ b/src/GToolkit-Coder/OCPragmaNode.extension.st @@ -0,0 +1,10 @@ +Extension { #name : #OCPragmaNode } + +{ #category : #'*GToolkit-Coder' } +OCPragmaNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + keywordsPositions + ifNotNil: [ keywordsPositions := keywordsPositions collect: [ :f | f + anInteger ] ]. + left ifNotNil: [ left := left + anInteger ]. + right ifNotNil: [ right := right + anInteger ] +] diff --git a/src/GToolkit-Coder/OCProgramNode.extension.st b/src/GToolkit-Coder/OCProgramNode.extension.st new file mode 100644 index 000000000..aa72f5434 --- /dev/null +++ b/src/GToolkit-Coder/OCProgramNode.extension.st @@ -0,0 +1,21 @@ +Extension { #name : #OCProgramNode } + +{ #category : #'*GToolkit-Coder' } +OCProgramNode >> gtMoveAllBy: anInteger [ + self gtMoveBy: anInteger. + self children do: [ :e | e gtMoveAllBy: anInteger ] +] + +{ #category : #'*GToolkit-Coder' } +OCProgramNode >> gtMoveBy: anInteger [ + self comments do: [ :f | f with: f contents at: f start + anInteger ] +] + +{ #category : #'*GToolkit-Coder' } +OCProgramNode >> withAllParentsDo: aBlock [ + | node | + node := self. + [ node notNil ] + whileTrue: [ aBlock value: node. + node := node parent ] +] diff --git a/src/GToolkit-Coder/OCSequenceNode.extension.st b/src/GToolkit-Coder/OCSequenceNode.extension.st new file mode 100644 index 000000000..9e198ef15 --- /dev/null +++ b/src/GToolkit-Coder/OCSequenceNode.extension.st @@ -0,0 +1,9 @@ +Extension { #name : #OCSequenceNode } + +{ #category : #'*GToolkit-Coder' } +OCSequenceNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + periods ifNotNil: [ periods := periods collect: [ :f | f + anInteger ] ]. + leftBar ifNotNil: [ leftBar := leftBar + anInteger ]. + rightBar ifNotNil: [ rightBar := rightBar + anInteger ] +] diff --git a/src/GToolkit-Coder/OCValueNode.extension.st b/src/GToolkit-Coder/OCValueNode.extension.st new file mode 100644 index 000000000..0347c25a6 --- /dev/null +++ b/src/GToolkit-Coder/OCValueNode.extension.st @@ -0,0 +1,7 @@ +Extension { #name : #OCValueNode } + +{ #category : #'*GToolkit-Coder' } +OCValueNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + parentheses ifNotNil: [ parentheses := parentheses collect: [ :f | f + anInteger ] ]. +] diff --git a/src/GToolkit-Coder/OCVariableNode.extension.st b/src/GToolkit-Coder/OCVariableNode.extension.st new file mode 100644 index 000000000..207e6cafe --- /dev/null +++ b/src/GToolkit-Coder/OCVariableNode.extension.st @@ -0,0 +1,12 @@ +Extension { #name : #OCVariableNode } + +{ #category : #'*GToolkit-Coder' } +OCVariableNode >> gtIsLocal [ + ^ (self whoDefines: self name) notNil +] + +{ #category : #'*GToolkit-Coder' } +OCVariableNode >> gtMoveBy: anInteger [ + super gtMoveBy: anInteger. + start ifNotNil: [ start := start + anInteger ] +] diff --git a/src/GToolkit-Coder/RPackageTag.extension.st b/src/GToolkit-Coder/PackageTag.extension.st similarity index 66% rename from src/GToolkit-Coder/RPackageTag.extension.st rename to src/GToolkit-Coder/PackageTag.extension.st index 5bfacab0a..3a10e073d 100644 --- a/src/GToolkit-Coder/RPackageTag.extension.st +++ b/src/GToolkit-Coder/PackageTag.extension.st @@ -1,7 +1,7 @@ -Extension { #name : #RPackageTag } +Extension { #name : #PackageTag } { #category : #'*GToolkit-Coder' } -RPackageTag >> gtClassesCoderFor: aView context: aPhlowContext [ +PackageTag >> gtClassesCoderFor: aView context: aPhlowContext [ (aPhlowContext isKindOf: GtPhlowExecutionContext) ifFalse: [ ^ aView empty ]. aPhlowContext hasPackageTagCoder ifFalse: [ ^ aView empty ]. @@ -11,3 +11,8 @@ RPackageTag >> gtClassesCoderFor: aView context: aPhlowContext [ disableAsync; stencil: [ aPhlowContext packageTagCoder classesCoder asElement ] ] + +{ #category : #'*GToolkit-Coder' } +PackageTag >> gtTagName [ + ^ self name +] diff --git a/src/GToolkit-Coder/RBAddPoolVariableChange.extension.st b/src/GToolkit-Coder/RBAddPoolVariableChange.extension.st index 21b503f5a..0472962ec 100644 --- a/src/GToolkit-Coder/RBAddPoolVariableChange.extension.st +++ b/src/GToolkit-Coder/RBAddPoolVariableChange.extension.st @@ -2,5 +2,7 @@ Extension { #name : #RBAddPoolVariableChange } { #category : #'*GToolkit-Coder' } RBAddPoolVariableChange >> changeObject [ + + ^ Smalltalk globals at: self variable asSymbol ] diff --git a/src/GToolkit-Coder/RBCommentChange.extension.st b/src/GToolkit-Coder/RBCommentChange.extension.st index 7d0dc9f5f..6c8534273 100644 --- a/src/GToolkit-Coder/RBCommentChange.extension.st +++ b/src/GToolkit-Coder/RBCommentChange.extension.st @@ -6,5 +6,5 @@ RBCommentChange >> gtChangesFor: aView [ ^ aView explicit title: 'Diff'; priority: 10; - stencil: [ self gtDiff asElement ] + stencil: [ self gtDiffChange asElement ] ] diff --git a/src/GToolkit-Coder/RBCompositeRefactoryChange.extension.st b/src/GToolkit-Coder/RBCompositeRefactoryChange.extension.st deleted file mode 100644 index 21e075d5d..000000000 --- a/src/GToolkit-Coder/RBCompositeRefactoryChange.extension.st +++ /dev/null @@ -1,8 +0,0 @@ -Extension { #name : #RBCompositeRefactoryChange } - -{ #category : #'*GToolkit-Coder' } -RBCompositeRefactoryChange >> gtWithoutChanges [ - ^ self shallowCopy - changes: #(); - yourself -] diff --git a/src/GToolkit-Coder/RBCondition.extension.st b/src/GToolkit-Coder/RBCondition.extension.st index 4a4297077..93659a053 100644 --- a/src/GToolkit-Coder/RBCondition.extension.st +++ b/src/GToolkit-Coder/RBCondition.extension.st @@ -3,7 +3,6 @@ Extension { #name : #RBCondition } { #category : #'*GToolkit-Coder' } RBCondition class >> includesPoolDictionary: aString in: aClass [ ^ self new - type: (Array with: #includesPool with: aString with: aClass) block: [ aClass sharedPoolNames includes: aString asSymbol ] errorString: aString , ' is <1?:not >included in ' , aClass name ] @@ -11,7 +10,6 @@ RBCondition class >> includesPoolDictionary: aString in: aClass [ { #category : #'*GToolkit-Coder' } RBCondition class >> isPoolDictionary: aString in: aRBSmalltalk [ ^ self new - type: (Array with: #isPoolDictionary with: aString) block: [ (aRBSmalltalk includesGlobal: aString asSymbol) and: [ Smalltalk globals at: aString asSymbol diff --git a/src/GToolkit-Coder/RBNamespace.extension.st b/src/GToolkit-Coder/RBNamespace.extension.st index 10504677a..15008558d 100644 --- a/src/GToolkit-Coder/RBNamespace.extension.st +++ b/src/GToolkit-Coder/RBNamespace.extension.st @@ -2,7 +2,8 @@ Extension { #name : #RBNamespace } { #category : #'*GToolkit-Coder' } RBNamespace >> category: aString for: aClass [ - + + ^ changes addChange: (RBClassCategoryChange category: aString for: aClass) ] @@ -13,7 +14,8 @@ RBNamespace >> changedClasses [ { #category : #'*GToolkit-Coder' } RBNamespace >> classObjectFor: anObject [ - + + (anObject isBehavior or: [anObject isTrait]) ifTrue: [ ^ self classFor: anObject ]. anObject isSymbol diff --git a/src/GToolkit-Coder/RBRefactoring.extension.st b/src/GToolkit-Coder/RBRefactoring.extension.st index cb782cb97..af802e3d0 100644 --- a/src/GToolkit-Coder/RBRefactoring.extension.st +++ b/src/GToolkit-Coder/RBRefactoring.extension.st @@ -11,14 +11,6 @@ RBRefactoring >> gtDangers [ description: (eachCondition errorMacro expandMacrosWith: false) ] ] -{ #category : #'*GToolkit-Coder' } -RBRefactoring >> gtExecute: isForce [ - isForce - ifTrue: [ self transform ] - ifFalse: [ self primitiveExecute ]. - RBRefactoryChangeManager instance performChange: self changes; addUndoPointer: 1 -] - { #category : #'*GToolkit-Coder' } RBRefactoring >> gtPreconditions [ ^ self preconditions diff --git a/src/GToolkit-Coder/RBRefactoryChange.extension.st b/src/GToolkit-Coder/RBRefactoryChange.extension.st deleted file mode 100644 index 1d1a47fd3..000000000 --- a/src/GToolkit-Coder/RBRefactoryChange.extension.st +++ /dev/null @@ -1,6 +0,0 @@ -Extension { #name : #RBRefactoryChange } - -{ #category : #'*GToolkit-Coder' } -RBRefactoryChange >> gtWithoutChanges [ - ^ self -] diff --git a/src/GToolkit-Coder/RBRemoveInstanceVariableRefactoring.extension.st b/src/GToolkit-Coder/RBRemoveInstanceVariableRefactoring.extension.st deleted file mode 100644 index 80a3fe941..000000000 --- a/src/GToolkit-Coder/RBRemoveInstanceVariableRefactoring.extension.st +++ /dev/null @@ -1,11 +0,0 @@ -Extension { #name : #RBRemoveInstanceVariableRefactoring } - -{ #category : #'*GToolkit-Coder' } -RBRemoveInstanceVariableRefactoring >> gtPreconditions [ - | references | - - references := RBCondition hierarchyOf: class referencesInstanceVariable: variableName. - references errorMacro: ( '<1s> is referenced.' expandMacrosWith: variableName). - - ^ ( RBCondition definesInstanceVariable: variableName asString in: class ) & references not -] diff --git a/src/GToolkit-Coder/RBRemovePoolVariableChange.extension.st b/src/GToolkit-Coder/RBRemovePoolVariableChange.extension.st index bd8df502d..b5412b257 100644 --- a/src/GToolkit-Coder/RBRemovePoolVariableChange.extension.st +++ b/src/GToolkit-Coder/RBRemovePoolVariableChange.extension.st @@ -2,5 +2,7 @@ Extension { #name : #RBRemovePoolVariableChange } { #category : #'*GToolkit-Coder' } RBRemovePoolVariableChange >> changeObject [ + + ^ Smalltalk globals at: self variable asSymbol ] diff --git a/src/GToolkit-Coder/RBReplaceMethodRefactoring.extension.st b/src/GToolkit-Coder/RBReplaceMethodRefactoring.extension.st deleted file mode 100644 index 5f7e3047b..000000000 --- a/src/GToolkit-Coder/RBReplaceMethodRefactoring.extension.st +++ /dev/null @@ -1,10 +0,0 @@ -Extension { #name : #RBReplaceMethodRefactoring } - -{ #category : #'*GToolkit-Coder' } -RBReplaceMethodRefactoring >> gtPreconditions [ - |conditions| - conditions := (RBCondition withBlock: [ self haveSameNumberOfArgs. true]) - &(RBCondition definesSelector: oldSelector in: class ) - & (RBCondition definesSelector: newSelector in: class ). - ^ conditions -] diff --git a/src/GToolkit-Coder/RPackage.extension.st b/src/GToolkit-Coder/RPackage.extension.st deleted file mode 100644 index 724adf04f..000000000 --- a/src/GToolkit-Coder/RPackage.extension.st +++ /dev/null @@ -1,86 +0,0 @@ -Extension { #name : #RPackage } - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtClassExtensions [ - ^ self extensionMethods collect: [ :each | GtPharoClassExtension new extendingMethod: each ] -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtClassReferences [ - ^ self gtMethodReferences flatCollectAsSet: [ :m | - m method gtReferencedClasses collect: [ :each | - GtPharoClassReference new - referencingMethod: m; - referencedClass: each ]] -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtDependencies [ - | all | - all := Set new. - all addAll: self gtClassExtensions. - all addAll: self gtInheritances. - all addAll: self gtTraitUsages. - all addAll: self gtClassReferences. - ^ all select: [ :each | each toPackage ~= self ] -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtInheritances [ - ^ self definedClasses - select: [ : each | each isClass and: [ each superclass notNil ] ] - thenCollect: [ :each | each gtInheritance ] -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtMethodReferences [ - ^ self methods collect: [:each | each methodReference] thenSelect: [:each| each isValid and: [each isLocalSelector]]. -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtPackageScope [ - ^ self -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtPackagesUsed [ - | result | - result := Set new. - result addAll: self gtPackagesUsedThroughClassExtensions. - result addAll: self gtPackagesUsedThroughSubclassing. - result addAll: self gtPackagesUsedThroughTraitUsages. - result addAll: self gtPackagesUsedThroughClassReferences. - ^ result - -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtPackagesUsedThroughClassExtensions [ - ^ self gtClassExtensions collectAsSet: [ :each | each toPackage ] -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtPackagesUsedThroughClassReferences [ - ^ (self gtClassReferences collectAsSet: [ :each | each toPackage ]) - remove: self ifAbsent: []; - yourself -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtPackagesUsedThroughSubclassing [ - ^ (self gtInheritances collectAsSet: [ :each | each toPackage ]) - remove: self ifAbsent: []; - yourself -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtPackagesUsedThroughTraitUsages [ - ^ (self gtTraitUsages collectAsSet: [ :each | each toPackage ]) - remove: self ifAbsent: []; - yourself -] - -{ #category : #'*GToolkit-Coder' } -RPackage >> gtTraitUsages [ - ^ self definedClasses flatCollect: [ :each | each gtTraitUsages ] -] diff --git a/src/GToolkit-Coder/ReRefactoring.extension.st b/src/GToolkit-Coder/ReRefactoring.extension.st new file mode 100644 index 000000000..00b191e31 --- /dev/null +++ b/src/GToolkit-Coder/ReRefactoring.extension.st @@ -0,0 +1,22 @@ +Extension { #name : #ReRefactoring } + +{ #category : #'*GToolkit-Coder' } +ReRefactoring >> gtDangers [ + + + ^ self gtPreconditions gtLeafChildren + reject: #check + thenCollect: [ :eachCondition | GtRefactoringDanger new + condition: eachCondition; + description: (eachCondition errorMacro expandMacrosWith: false) ] +] + +{ #category : #'*GToolkit-Coder' } +ReRefactoring >> gtPreconditions [ + ^ self preconditions +] + +{ #category : #'*GToolkit-Coder' } +ReRefactoring >> gtTransform [ + self transform +] diff --git a/src/GToolkit-Coder/ReTransformation.extension.st b/src/GToolkit-Coder/ReTransformation.extension.st new file mode 100644 index 000000000..62ae62a61 --- /dev/null +++ b/src/GToolkit-Coder/ReTransformation.extension.st @@ -0,0 +1,22 @@ +Extension { #name : #ReTransformation } + +{ #category : #'*GToolkit-Coder' } +ReTransformation >> gtDangers [ + + + ^ self gtPreconditions gtLeafChildren + reject: #check + thenCollect: [ :eachCondition | GtRefactoringDanger new + condition: eachCondition; + description: (eachCondition errorMacro expandMacrosWith: false) ] +] + +{ #category : #'*GToolkit-Coder' } +ReTransformation >> gtPreconditions [ + ^ self preconditions +] + +{ #category : #'*GToolkit-Coder' } +ReTransformation >> gtTransform [ + self privateTransform +] diff --git a/src/GToolkit-Coder/TGtCoderObject.trait.st b/src/GToolkit-Coder/TGtCoderObject.trait.st new file mode 100644 index 000000000..f838e3490 --- /dev/null +++ b/src/GToolkit-Coder/TGtCoderObject.trait.st @@ -0,0 +1,13 @@ +Trait { + #name : #TGtCoderObject, + #traits : 'TGtPhlowObject', + #classTraits : 'TGtPhlowObject classTrait', + #category : #'GToolkit-Coder-Object Holder' +} + +{ #category : #testing } +TGtCoderObject classSide >> isDeprecated [ + "Use my Trait" + + ^ true +] diff --git a/src/GToolkit-Coder/TGtCoderRequesterObject.trait.st b/src/GToolkit-Coder/TGtCoderRequesterObject.trait.st new file mode 100644 index 000000000..19791559a --- /dev/null +++ b/src/GToolkit-Coder/TGtCoderRequesterObject.trait.st @@ -0,0 +1,24 @@ +Trait { + #name : #TGtCoderRequesterObject, + #category : #'GToolkit-Coder-Coders - Evaluation' +} + +{ #category : #converting } +TGtCoderRequesterObject >> asCoderRequesterObject [ + ^ self +] + +{ #category : #'api - notifying' } +TGtCoderRequesterObject >> notifyShowDebuggerRequest: aDebugSession dueTo: anException sourceString: aSourceString sourceInterval: aSourceInterval evaluationInfo: anEvaluationInfo [ + "Return true if announcement was handled (and debugger displayed in some way). + Return false otherwise." + + ^ false +] + +{ #category : #accessing } +TGtCoderRequesterObject >> selfObject [ + "Return an object that is represented by `self` (or `super`) key" + + ^ nil +] diff --git a/src/GToolkit-Coder/TGtCoderWithBehaviorHolder.trait.st b/src/GToolkit-Coder/TGtCoderWithBehaviorHolder.trait.st new file mode 100644 index 000000000..c5cc1c98d --- /dev/null +++ b/src/GToolkit-Coder/TGtCoderWithBehaviorHolder.trait.st @@ -0,0 +1,13 @@ +Trait { + #name : #TGtCoderWithBehaviorHolder, + #traits : 'TGtPhlowWithBehaviorHolder', + #classTraits : 'TGtPhlowWithBehaviorHolder classTrait', + #category : #'GToolkit-Coder-Object Holder' +} + +{ #category : #testing } +TGtCoderWithBehaviorHolder classSide >> isDeprecated [ + "Use my Trait instead" + + ^ true +] diff --git a/src/GToolkit-Coder/TGtCoderWithObjectHolder.trait.st b/src/GToolkit-Coder/TGtCoderWithObjectHolder.trait.st new file mode 100644 index 000000000..017740364 --- /dev/null +++ b/src/GToolkit-Coder/TGtCoderWithObjectHolder.trait.st @@ -0,0 +1,13 @@ +Trait { + #name : #TGtCoderWithObjectHolder, + #traits : 'TGtPhlowWithObjectHolder', + #classTraits : 'TGtPhlowWithObjectHolder classTrait', + #category : #'GToolkit-Coder-Object Holder' +} + +{ #category : #testing } +TGtCoderWithObjectHolder classSide >> isDeprecated [ + "Use my Trait instead" + + ^ true +] diff --git a/src/GToolkit-Coder/TGtSourceCoderEvaluatedCode.trait.st b/src/GToolkit-Coder/TGtSourceCoderEvaluatedCode.trait.st new file mode 100644 index 000000000..215da8a76 --- /dev/null +++ b/src/GToolkit-Coder/TGtSourceCoderEvaluatedCode.trait.st @@ -0,0 +1,20 @@ +Trait { + #name : #TGtSourceCoderEvaluatedCode, + #category : #'GToolkit-Coder-Coders - Evaluation' +} + +{ #category : #accessing } +TGtSourceCoderEvaluatedCode >> findRelevantContextInStack: aStack [ + "Return a stack context that corresponds to a given evaluated code." + + + ^ nil +] + +{ #category : #accessing } +TGtSourceCoderEvaluatedCode >> findSourceIntervalForContext: aContext sourceString: aSourceString [ + "Return source code interval that corresponds to a given executed code." + + + ^ nil +] diff --git a/src/GToolkit-Coder/UndefinedObject.extension.st b/src/GToolkit-Coder/UndefinedObject.extension.st new file mode 100644 index 000000000..db42e5873 --- /dev/null +++ b/src/GToolkit-Coder/UndefinedObject.extension.st @@ -0,0 +1,6 @@ +Extension { #name : #UndefinedObject } + +{ #category : #'*GToolkit-Coder' } +UndefinedObject >> asCoderRequesterObject [ + ^ GtNoCoderRequester uniqueInstance +] diff --git a/src/GToolkit-SearchFilters-PhlowTool/GtPhlowSearchFilterTool.class.st b/src/GToolkit-SearchFilters-PhlowTool/GtPhlowSearchFilterTool.class.st new file mode 100644 index 000000000..96b72cdb4 --- /dev/null +++ b/src/GToolkit-SearchFilters-PhlowTool/GtPhlowSearchFilterTool.class.st @@ -0,0 +1,61 @@ +Class { + #name : #GtPhlowSearchFilterTool, + #superclass : #GtPhlowTool, + #instVars : [ + 'searchFilter' + ], + #category : #'GToolkit-SearchFilters-PhlowTool' +} + +{ #category : #'instance creation' } +GtPhlowSearchFilterTool class >> forSearchFilter: aSearchFilter [ + ^ self new + searchFilter: aSearchFilter +] + +{ #category : #'api - converting' } +GtPhlowSearchFilterTool >> asElementDo: aOneArgBlock [ + | toolElement | + toolElement := GtPhlowSearchFilterToolElement new + searchFilter: self searchFilter. + ^ aOneArgBlock cull: toolElement +] + +{ #category : #'api - accessing' } +GtPhlowSearchFilterTool >> icon [ + "Return a tool icon" + + ^ BrGlamorousVectorIcons search +] + +{ #category : #'api - accessing' } +GtPhlowSearchFilterTool >> name [ + "Return a tool name" + + ^ 'Search Filter' +] + +{ #category : #'api - accessing' } +GtPhlowSearchFilterTool >> object [ + ^ self searchFilter +] + +{ #category : #'api - accessing' } +GtPhlowSearchFilterTool >> object: aSearchFilter [ + self searchFilter: aSearchFilter +] + +{ #category : #accessing } +GtPhlowSearchFilterTool >> searchFilter [ + ^ searchFilter +] + +{ #category : #accessing } +GtPhlowSearchFilterTool >> searchFilter: aSearchFilter [ + searchFilter := aSearchFilter +] + +{ #category : #'api - accessing' } +GtPhlowSearchFilterTool >> tabLook [ + ^ BrGlamorousTabSwitcherWithIconAptitude +] diff --git a/src/GToolkit-SearchFilters-PhlowTool/GtPhlowSearchFilterToolElement.class.st b/src/GToolkit-SearchFilters-PhlowTool/GtPhlowSearchFilterToolElement.class.st new file mode 100644 index 000000000..b9e88b011 --- /dev/null +++ b/src/GToolkit-SearchFilters-PhlowTool/GtPhlowSearchFilterToolElement.class.st @@ -0,0 +1,246 @@ +Class { + #name : #GtPhlowSearchFilterToolElement, + #superclass : #BrVerticalPane, + #instVars : [ + 'titleNotifier', + 'searchFilter' + ], + #category : #'GToolkit-SearchFilters-PhlowTool' +} + +{ #category : #'building - widgets' } +GtPhlowSearchFilterToolElement >> buildHeaderElement [ + ^ BrVerticalPane new + padding: (BlInsets left: 5); + vFitContent; + hMatchParent +] + +{ #category : #'building - widgets' } +GtPhlowSearchFilterToolElement >> buildPlaygroundPageElement [ + | playgroundElement | + playgroundElement := (GtInspector environment + at: GtInspector embeddedPlaygroundName) new. + + playgroundElement collapse. + ^ playgroundElement +] + +{ #category : #'building - widgets' } +GtPhlowSearchFilterToolElement >> buildTitleContainer [ + ^ BrHorizontalPane new + vFitContent; + hMatchParent +] + +{ #category : #'building - widgets' } +GtPhlowSearchFilterToolElement >> buildTitleLabel [ + ^ BrLabel new + aptitude: (BrGlamorousLabelAptitude new + bold; + fontSize: 18); + text: '' +] + +{ #category : #'building - widgets' } +GtPhlowSearchFilterToolElement >> buildToolLabel [ + ^ BrLabel new + aptitude: (BrGlamorousLabelAptitude new glamorousRegularFontAndSize + foreground: Color gray; + fontSize: 12); + text: 'Search Filter'. +] + +{ #category : #'building - widgets' } +GtPhlowSearchFilterToolElement >> buildToolbar [ + ^ BrToolbar new + aptitude: BrGlamorousToolbarAptitude new; + fitContent; + constraintsDo: [ :c | + c ignoreByLayout. + c ignored horizontal alignRight. + c ignored vertical alignTop ] +] + +{ #category : #'building - widgets' } +GtPhlowSearchFilterToolElement >> buildViewsContainerElement [ + ^ BrFrame new + matchParent +] + +{ #category : #accessing } +GtPhlowSearchFilterToolElement >> collectToolActions [ + ^ GtPhlowActionsCollector new + pragmaName: #gtToolAction; + fromObject: searchFilter; + collect +] + +{ #category : #accessing } +GtPhlowSearchFilterToolElement >> collectToolViews [ + ^ GtPhlowViewsCollector new + pragmaName: #gtToolView; + fromObject: searchFilter; + collect +] + +{ #category : #'accessing - elements' } +GtPhlowSearchFilterToolElement >> headerElement [ + ^ self childNamed: #header +] + +{ #category : #initialization } +GtPhlowSearchFilterToolElement >> initialize [ + super initialize. + + self matchParent. + self padding: (BlInsets top: 5 left: 5 bottom: 0 right: 5). + + self initializeContent. + self initializeTitleNotifier. + + self phlow + accessTool: (GtPhlowToolClassType forClass: GtSearchFilter) + viewModel: #searchFilter + object: #searchFilter +] + +{ #category : #initialization } +GtPhlowSearchFilterToolElement >> initializeContent [ + self initializeHeaderElement. + + self addChild: self buildViewsContainerElement as: #viewsContainer. + self addChild: self buildPlaygroundPageElement as: #playgroundPage. +] + +{ #category : #initialization } +GtPhlowSearchFilterToolElement >> initializeHeaderElement [ + | headerElement titleContainer | + + headerElement := self buildHeaderElement. + + titleContainer := self buildTitleContainer. + titleContainer addChild: self buildTitleLabel as: #titleLabel. + titleContainer addChild: self buildToolbar as: #headerToolbar. + + headerElement addChild: self buildToolLabel as: #toolLabel. + headerElement addChild: titleContainer as: #titleContainer. + + self addChild: headerElement as: #header. +] + +{ #category : #accessing } +GtPhlowSearchFilterToolElement >> initializeTitleNotifier [ + titleNotifier := GtPhlowTitleIconAndLabelNotifier new + longLabel: [ self searchFilterDescription ]; + icon: BrGlamorousVectorIcons search. + self addEventHandler: titleNotifier. +] + +{ #category : #'accessing - elements' } +GtPhlowSearchFilterToolElement >> playgroundElement [ + ^ self childNamed: #playgroundPage +] + +{ #category : #accessing } +GtPhlowSearchFilterToolElement >> searchFilter [ + + ^ searchFilter +] + +{ #category : #accessing } +GtPhlowSearchFilterToolElement >> searchFilter: aSearchFilter [ + searchFilter := aSearchFilter. + + self updateContent. +] + +{ #category : #accessing } +GtPhlowSearchFilterToolElement >> searchFilterDescription [ + ^ searchFilter + ifNil: [ 'Search Filter' ] + ifNotNil: [ :aSearchFilter | + 'Filter: ', aSearchFilter gtDisplayString ] +] + +{ #category : #'accessing - elements' } +GtPhlowSearchFilterToolElement >> titleLabel [ + ^ self headerElement childNamed: #titleLabel +] + +{ #category : #accessing } +GtPhlowSearchFilterToolElement >> titleNotifier [ + ^ titleNotifier +] + +{ #category : #'accessing - elements' } +GtPhlowSearchFilterToolElement >> toolbarElement [ + ^ self headerElement childNamed: #headerToolbar +] + +{ #category : #updating } +GtPhlowSearchFilterToolElement >> updateContainedViews [ + | tabGroup compositeView | + + compositeView := GtPhlowCompositeView new + views:self collectToolViews. + + "This particular views do not update" + "compositeView views do: [ :aView | + aView autoUpdates definitions removeAll ]." + + tabGroup := compositeView asElementDo: [ :e | e ]. + + self viewsContainerElement + removeChildren; + addChild: tabGroup +] + +{ #category : #updating } +GtPhlowSearchFilterToolElement >> updateContent [ + self updateHeader. + self updateContainedViews. + self updatePlayground. +] + +{ #category : #updating } +GtPhlowSearchFilterToolElement >> updateHeader [ + self updateTitle. + self updateToolbarActions. +] + +{ #category : #updating } +GtPhlowSearchFilterToolElement >> updatePlayground [ + self playgroundElement + objectHolder: (GtInspectorObject new + object: searchFilter) +] + +{ #category : #updating } +GtPhlowSearchFilterToolElement >> updateTabLabel [ + self titleNotifier notify +] + +{ #category : #updating } +GtPhlowSearchFilterToolElement >> updateTitle [ + self titleLabel + text: self searchFilterDescription. +] + +{ #category : #updating } +GtPhlowSearchFilterToolElement >> updateToolbarActions [ + | toolbarElement| + toolbarElement := self toolbarElement. + toolbarElement removeAllItems. + + self collectToolActions do: [ :aPhlowAction | + aPhlowAction asElement: [ :actionElement | + toolbarElement + addItem: actionElement ] + withHostElement: self ] +] + +{ #category : #'accessing - elements' } +GtPhlowSearchFilterToolElement >> viewsContainerElement [ + ^ self childNamed: #viewsContainer +] diff --git a/src/GToolkit-SearchFilters-PhlowTool/GtSearchFilter.extension.st b/src/GToolkit-SearchFilters-PhlowTool/GtSearchFilter.extension.st new file mode 100644 index 000000000..876ac2e34 --- /dev/null +++ b/src/GToolkit-SearchFilters-PhlowTool/GtSearchFilter.extension.st @@ -0,0 +1,9 @@ +Extension { #name : #GtSearchFilter } + +{ #category : #'*GToolkit-SearchFilters-PhlowTool' } +GtSearchFilter >> gtDefaultInspectorTool [ + ^ GtPhlowCompositeTool new + addTool: (GtPhlowSearchFilterTool forSearchFilter: self); + addTool: (super gtDefaultInspectorTool); + name: 'Filter: ', self gtDisplayString +] diff --git a/src/GToolkit-SearchFilters-PhlowTool/package.st b/src/GToolkit-SearchFilters-PhlowTool/package.st new file mode 100644 index 000000000..11a87872b --- /dev/null +++ b/src/GToolkit-SearchFilters-PhlowTool/package.st @@ -0,0 +1 @@ +Package { #name : #'GToolkit-SearchFilters-PhlowTool' } diff --git a/src/GToolkit-SearchFilters/GtAsyncSearchGroupStream.class.st b/src/GToolkit-SearchFilters/GtAsyncSearchGroupStream.class.st new file mode 100644 index 000000000..d94b94cc7 --- /dev/null +++ b/src/GToolkit-SearchFilters/GtAsyncSearchGroupStream.class.st @@ -0,0 +1,132 @@ +Class { + #name : #GtAsyncSearchGroupStream, + #superclass : #Object, + #traits : 'TAsyncStream', + #classTraits : 'TAsyncStream classTrait', + #instVars : [ + 'itemsStream', + 'filter', + 'scope' + ], + #category : #'GToolkit-SearchFilters-Results' +} + +{ #category : #'as yet unclassified' } +GtAsyncSearchGroupStream class >> mergeTypeWith: aGroupResultClass [ + ^ aGroupResultClass mergeTypeWithDefaultGroup +] + +{ #category : #'as yet unclassified' } +GtAsyncSearchGroupStream class >> mergeTypeWithDefaultGroup [ + "The common type between the default group and any other group is the other group." + ^ self +] + +{ #category : #'as yet unclassified' } +GtAsyncSearchGroupStream class >> mergeTypeWithGenericGroup [ + "The common type between a generic group and any other group is the other group." + ^ self +] + +{ #category : #'as yet unclassified' } +GtAsyncSearchGroupStream class >> mergeTypeWithTypedGroup: aGroupType [ + ^ self +] + +{ #category : #accessing } +GtAsyncSearchGroupStream >> filter [ + ^ filter +] + +{ #category : #'private - updating' } +GtAsyncSearchGroupStream >> filteredItemsStream [ + ^ scope asyncSimilarCopy filter: filter +] + +{ #category : #initialization } +GtAsyncSearchGroupStream >> forFilter: aFilter [ + self + forFilter: aFilter + scope: (aFilter + ifNil: [ AsyncEmptyStream new ] + ifNotNil: [ aFilter defaultFilterScope ]) +] + +{ #category : #initialization } +GtAsyncSearchGroupStream >> forFilter: aFilter scope: aScopeStream [ + filter := aFilter. + scope := aScopeStream. + self updateItems +] + +{ #category : #'gt - extensions' } +GtAsyncSearchGroupStream >> gtItemsFor: aView [ + + + ^ aView list + title: 'Items' translated; + priority: 10; + items: [ self asyncSimilarCopy ] +] + +{ #category : #'gt - extensions' } +GtAsyncSearchGroupStream >> gtMetricsFor: aView [ + + + ^ aView empty +] + +{ #category : #accessing } +GtAsyncSearchGroupStream >> highlighter [ + ^ filter notNil + ifTrue: [ filter highlighter ] + ifFalse: [ nil ] +] + +{ #category : #initialization } +GtAsyncSearchGroupStream >> initialize [ + super initialize. + + itemsStream := AsyncEmptyStream new +] + +{ #category : #'api - stream' } +GtAsyncSearchGroupStream >> pollNext: anAsynchronousContext [ + "Attempt to pull out the next value of this stream, registering the current task for wakeup if the value is not yet available, and returning None if the stream is exhausted. + There are several possible return values, each indicating a distinct stream state: + - Poll::Pending means that this stream's next value is not ready yet. Implementations will ensure that the current task will be notified when the next value may be ready. + - Poll::Ready(Some(val)) means that the stream has successfully produced a value, val, and may produce further values on subsequent poll_next calls. + - Poll::Ready(None) means that the stream has terminated, and poll_next should not be invoked again." + + + ^ itemsStream pollNext: anAsynchronousContext +] + +{ #category : #'api - stream copy' } +GtAsyncSearchGroupStream >> postSimilarCopy [ + "Is called by similarCopy. self is a shallow copy, implementors should set the fields as necessary to complete the similar copy" + scope := scope asyncSimilarCopy. + self updateItems +] + +{ #category : #'private - updating' } +GtAsyncSearchGroupStream >> refreshItems [ + self updateItems +] + +{ #category : #accessing } +GtAsyncSearchGroupStream >> scope [ + ^ scope +] + +{ #category : #'api - stream' } +GtAsyncSearchGroupStream >> sizeHint [ + + + ^ itemsStream sizeHint +] + +{ #category : #'private - updating' } +GtAsyncSearchGroupStream >> updateItems [ + itemsStream := self filteredItemsStream +] diff --git a/src/GToolkit-SearchFilters/GtAsyncSearchNullGroupStream.class.st b/src/GToolkit-SearchFilters/GtAsyncSearchNullGroupStream.class.st new file mode 100644 index 000000000..fa0c8a051 --- /dev/null +++ b/src/GToolkit-SearchFilters/GtAsyncSearchNullGroupStream.class.st @@ -0,0 +1,20 @@ +Class { + #name : #GtAsyncSearchNullGroupStream, + #superclass : #GtAsyncSearchGroupStream, + #category : #'GToolkit-SearchFilters-Results' +} + +{ #category : #'as yet unclassified' } +GtAsyncSearchNullGroupStream class >> mergeTypeWith: aGroupResultClass [ + ^ aGroupResultClass mergeTypeWithGenericGroup. +] + +{ #category : #'as yet unclassified' } +GtAsyncSearchNullGroupStream class >> mergeTypeWithTypedGroup: aGroupType [ + ^ aGroupType +] + +{ #category : #'private - updating' } +GtAsyncSearchNullGroupStream >> filteredItemsStream [ + ^ AsyncEmptyStream new +] diff --git a/src/GToolkit-SearchFilters/GtAsyncSearchTypedGroupStream.class.st b/src/GToolkit-SearchFilters/GtAsyncSearchTypedGroupStream.class.st new file mode 100644 index 000000000..1ba4bbfe2 --- /dev/null +++ b/src/GToolkit-SearchFilters/GtAsyncSearchTypedGroupStream.class.st @@ -0,0 +1,36 @@ +Class { + #name : #GtAsyncSearchTypedGroupStream, + #superclass : #GtAsyncSearchGroupStream, + #instVars : [ + 'announcer' + ], + #category : #'GToolkit-SearchFilters-Results' +} + +{ #category : #'as yet unclassified' } +GtAsyncSearchTypedGroupStream class >> mergeTypeWith: aGroupResultClass [ + ^ aGroupResultClass mergeTypeWithTypedGroup: self. +] + +{ #category : #'as yet unclassified' } +GtAsyncSearchTypedGroupStream class >> mergeTypeWithTypedGroup: aGroupType [ + ^ aGroupType = self + ifTrue: [ self ] + ifFalse: [ GtAsyncSearchGroupStream ] +] + +{ #category : #accessing } +GtAsyncSearchTypedGroupStream >> announcer [ + ^ announcer +] + +{ #category : #initialization } +GtAsyncSearchTypedGroupStream >> initialize [ + super initialize. + announcer := Announcer new. + self subscribe +] + +{ #category : #subscription } +GtAsyncSearchTypedGroupStream >> subscribe [ +] diff --git a/src/GToolkit-SearchFilters/GtLabeledSearchFilter.class.st b/src/GToolkit-SearchFilters/GtLabeledSearchFilter.class.st new file mode 100644 index 000000000..947820bd6 --- /dev/null +++ b/src/GToolkit-SearchFilters/GtLabeledSearchFilter.class.st @@ -0,0 +1,91 @@ +Class { + #name : #GtLabeledSearchFilter, + #superclass : #GtSearchFilter, + #instVars : [ + 'filter', + 'label', + 'methodDefinition' + ], + #category : #'GToolkit-SearchFilters' +} + +{ #category : #comparing } +GtLabeledSearchFilter >> = anObject [ + ^ self class = anObject class + and: [ self filterLabel = anObject filterLabel and: [ self filter = anObject filter ] ] +] + +{ #category : #accessing } +GtLabeledSearchFilter >> children [ + ^ {filter} +] + +{ #category : #accessing } +GtLabeledSearchFilter >> defaultFilterScope [ + ^ filter defaultFilterScope +] + +{ #category : #accessing } +GtLabeledSearchFilter >> filter [ + ^ filter +] + +{ #category : #accessing } +GtLabeledSearchFilter >> filter: aSearchFilter [ + filter := aSearchFilter +] + +{ #category : #accessing } +GtLabeledSearchFilter >> filterLabel [ + ^ label +] + +{ #category : #accessing } +GtLabeledSearchFilter >> filterLabel: aString [ + label := aString +] + +{ #category : #accessing } +GtLabeledSearchFilter >> filterValueString [ + ^ filter filterValueString +] + +{ #category : #accessing } +GtLabeledSearchFilter >> gtDisplayOn: aStream [ + label gtDisplayOn: aStream +] + +{ #category : #comparing } +GtLabeledSearchFilter >> hash [ + ^ self filterLabel hash hashMultiply bitXor: self filter hash +] + +{ #category : #accessing } +GtLabeledSearchFilter >> highlighter [ + ^ filter highlighter +] + +{ #category : #accessing } +GtLabeledSearchFilter >> matches: anObject [ + ^ filter matches: anObject +] + +{ #category : #accessing } +GtLabeledSearchFilter >> methodDefinition [ + ^ methodDefinition +] + +{ #category : #accessing } +GtLabeledSearchFilter >> methodDefinition: aCompiledMethod [ + methodDefinition := aCompiledMethod +] + +{ #category : #accessing } +GtLabeledSearchFilter >> resultType [ + ^ filter resultType +] + +{ #category : #accessing } +GtLabeledSearchFilter >> size [ + ^ filter size +] diff --git a/src/GToolkit-Coder/GtSearchBinaryFilter.class.st b/src/GToolkit-SearchFilters/GtSearchBinaryFilter.class.st similarity index 72% rename from src/GToolkit-Coder/GtSearchBinaryFilter.class.st rename to src/GToolkit-SearchFilters/GtSearchBinaryFilter.class.st index 4bca919fe..dc134b4f0 100644 --- a/src/GToolkit-Coder/GtSearchBinaryFilter.class.st +++ b/src/GToolkit-SearchFilters/GtSearchBinaryFilter.class.st @@ -5,7 +5,7 @@ Class { 'left', 'right' ], - #category : #'GToolkit-Coder-Filters' + #category : #'GToolkit-SearchFilters' } { #category : #'instance creation' } @@ -22,14 +22,9 @@ GtSearchBinaryFilter >> filter: leftFilter filter: rightFilter [ ] { #category : #accessing } -GtSearchBinaryFilter >> highlighter [ - ^ left highlighter - ifNil: [ right highlighter ] - ifNotNil: [ :lh | - right highlighter - ifNil: [ lh ] - ifNotNil: - [ :rh | GtCompositeHighlighter forHighlighters: (Array with: lh with: rh) ] ] +GtSearchBinaryFilter >> filterValueString [ + ^ left filterValueString + ifNil: [ right filterValueString ] ] { #category : #accessing } diff --git a/src/GToolkit-Coder/GtSearchBlockFilter.class.st b/src/GToolkit-SearchFilters/GtSearchBlockFilter.class.st similarity index 91% rename from src/GToolkit-Coder/GtSearchBlockFilter.class.st rename to src/GToolkit-SearchFilters/GtSearchBlockFilter.class.st index 1c0c489fe..7e622c9d4 100644 --- a/src/GToolkit-Coder/GtSearchBlockFilter.class.st +++ b/src/GToolkit-SearchFilters/GtSearchBlockFilter.class.st @@ -4,7 +4,7 @@ Class { #instVars : [ 'block' ], - #category : #'GToolkit-Coder-Filters' + #category : #'GToolkit-SearchFilters-Filters' } { #category : #'instance creation' } diff --git a/src/GToolkit-SearchFilters/GtSearchDisjunctionFilter.class.st b/src/GToolkit-SearchFilters/GtSearchDisjunctionFilter.class.st new file mode 100644 index 000000000..d4b6b6449 --- /dev/null +++ b/src/GToolkit-SearchFilters/GtSearchDisjunctionFilter.class.st @@ -0,0 +1,33 @@ +Class { + #name : #GtSearchDisjunctionFilter, + #superclass : #GtSearchBinaryFilter, + #category : #'GToolkit-SearchFilters-Filters' +} + +{ #category : #accessing } +GtSearchDisjunctionFilter >> defaultFilterScope [ + ^ left defaultFilterScope +] + +{ #category : #printing } +GtSearchDisjunctionFilter >> gtDisplayOn: stream [ + left gtDisplayOn: stream. + stream nextPutAll: ' - '. + right gtDisplayOn: stream. +] + +{ #category : #testing } +GtSearchDisjunctionFilter >> matches: anObject [ + | leftResult rightResult | + leftResult := left matches: anObject. + rightResult := right matches: anObject. + + ^ leftResult ifTrue: [ rightResult not ] ifFalse: [ rightResult ] +] + +{ #category : #printing } +GtSearchDisjunctionFilter >> printOn: stream [ + left printOn: stream. + stream nextPutAll: ' - '. + right printOn: stream. +] diff --git a/src/GToolkit-SearchFilters/GtSearchFilter.class.st b/src/GToolkit-SearchFilters/GtSearchFilter.class.st new file mode 100644 index 000000000..3ca325913 --- /dev/null +++ b/src/GToolkit-SearchFilters/GtSearchFilter.class.st @@ -0,0 +1,183 @@ +Class { + #name : #GtSearchFilter, + #superclass : #Object, + #category : #'GToolkit-SearchFilters' +} + +{ #category : #accessing } +GtSearchFilter class >> allSearchFilterMethods [ + | methods | + methods := ((Pragma allNamed: #gtSearchFilter) + select: [ :each | each method methodClass isClassSide and: [ each method numArgs = 0 ] ]) + collect: [ :each | each method ]. + ^ methods +] + +{ #category : #accessing } +GtSearchFilter class >> allSearchFilters [ + ^ (self allSearchFilterMethods + collect: [ :each | + ([ each methodClass instanceSide perform: each selector ] + on: Error + do: [ :ex | ex return ]) + ifNotNil: [ :filter | + filter filterLabel + ifNil: [ (filter + labeled: each methodClass instanceSide name asString , ' ' , each selector) + methodDefinition: each; + yourself ] + ifNotNil: [ filter ] ] ]) reject: #isNil +] + +{ #category : #'logical operations' } +GtSearchFilter >> & aFilter [ + ^ GtSearchIntersectionFilter forFilter: self filter: aFilter +] + +{ #category : #'logical operations' } +GtSearchFilter >> - aFilter [ + ^ GtSearchDisjunctionFilter forFilter: self filter: aFilter +] + +{ #category : #comparing } +GtSearchFilter >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + + ^ self class = anObject class +] + +{ #category : #evaluating } +GtSearchFilter >> applyInScope: aStream [ + ^ self resultType new + forFilter: self + scope: aStream +] + +{ #category : #converting } +GtSearchFilter >> asAsyncStream [ + ^ self result +] + +{ #category : #accessing } +GtSearchFilter >> contents [ + + "Convenience method. By analogy with PositionableStream>>#contents. + Sometimes you really want the actual result, and not a future. + For example: + (#gtExample gtPragmas & #assert: gtSenders) contents + asOrderedCollection sort: [ :a :b | a size < b size ] + will return the actual result array so you can sort it. + The async way to do that would be: + ((#gtExample gtPragmas & #assert: gtSenders) result toArray + then: [ :anArray | anArray sorted: [ :a :b | a size < b size ] ]) await + " + + ^ self result toArray wait +] + +{ #category : #accessing } +GtSearchFilter >> defaultFilterScope [ + ^ AsyncEmptyStream new +] + +{ #category : #iterating } +GtSearchFilter >> do: aBlock [ + (self result do: aBlock) wait +] + +{ #category : #accessing } +GtSearchFilter >> filterLabel [ + ^ nil +] + +{ #category : #accessing } +GtSearchFilter >> filterType [ + "Filter type is used to compare whether two filters are of a same type." + + ^ self class +] + +{ #category : #accessing } +GtSearchFilter >> filterValueString [ + ^ nil +] + +{ #category : #comparing } +GtSearchFilter >> hash [ + ^ self class hash +] + +{ #category : #testing } +GtSearchFilter >> isEmpty [ + ^ self notEmpty not +] + +{ #category : #accessing } +GtSearchFilter >> labeled: aString [ + ^ GtLabeledSearchFilter new + filter: self; + filterLabel: aString; + methodDefinition: thisContext sender method method; + yourself +] + +{ #category : #testing } +GtSearchFilter >> matches: anObject [ + self subclassResponsibility +] + +{ #category : #'logical operations' } +GtSearchFilter >> not [ + ^ GtSearchNegationFilter new originalFilter: self +] + +{ #category : #testing } +GtSearchFilter >> notEmpty [ + ^ self result hasNext wait +] + +{ #category : #'api - accessing' } +GtSearchFilter >> requesterContext: aRequesterContext [ + "Set a requester context" + + +] + +{ #category : #'api - accessing' } +GtSearchFilter >> requesterContextDo: aBlock [ + "Pass a requester context to aBlock if the context is available" + + ^ nil +] + +{ #category : #converting } +GtSearchFilter >> result [ + ^ self applyInScope: self defaultFilterScope +] + +{ #category : #accessing } +GtSearchFilter >> resultType [ + ^ GtAsyncSearchNullGroupStream +] + +{ #category : #'logical operations' } +GtSearchFilter >> select: aBlock [ + ^ self & (GtSearchBlockFilter forBlock: aBlock) +] + +{ #category : #accessing } +GtSearchFilter >> size [ + ^ (self result inject: 0 into: [ :counter :eachItem | counter + 1 ]) wait +] + +{ #category : #testing } +GtSearchFilter >> value: anObject [ + "To be polymorphic with block closures" + + ^ self matches: anObject +] + +{ #category : #'logical operations' } +GtSearchFilter >> | aFilter [ + ^ GtSearchUnionFilter forFilter: self filter: aFilter +] diff --git a/src/GToolkit-SearchFilters/GtSearchFilterContext.class.st b/src/GToolkit-SearchFilters/GtSearchFilterContext.class.st new file mode 100644 index 000000000..5812353c0 --- /dev/null +++ b/src/GToolkit-SearchFilters/GtSearchFilterContext.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtSearchFilterContext, + #superclass : #Object, + #category : #'GToolkit-SearchFilters' +} diff --git a/src/GToolkit-SearchFilters/GtSearchFilterNullContext.class.st b/src/GToolkit-SearchFilters/GtSearchFilterNullContext.class.st new file mode 100644 index 000000000..2303ea52a --- /dev/null +++ b/src/GToolkit-SearchFilters/GtSearchFilterNullContext.class.st @@ -0,0 +1,5 @@ +Class { + #name : #GtSearchFilterNullContext, + #superclass : #GtSearchFilterContext, + #category : #'GToolkit-SearchFilters' +} diff --git a/src/GToolkit-Coder/GtSearchIntersectionFilter.class.st b/src/GToolkit-SearchFilters/GtSearchIntersectionFilter.class.st similarity index 93% rename from src/GToolkit-Coder/GtSearchIntersectionFilter.class.st rename to src/GToolkit-SearchFilters/GtSearchIntersectionFilter.class.st index 54a49dbc3..61b4cd672 100644 --- a/src/GToolkit-Coder/GtSearchIntersectionFilter.class.st +++ b/src/GToolkit-SearchFilters/GtSearchIntersectionFilter.class.st @@ -1,7 +1,7 @@ Class { #name : #GtSearchIntersectionFilter, #superclass : #GtSearchBinaryFilter, - #category : #'GToolkit-Coder-Filters' + #category : #'GToolkit-SearchFilters-Filters' } { #category : #accessing } diff --git a/src/GToolkit-Coder/GtSearchNegationFilter.class.st b/src/GToolkit-SearchFilters/GtSearchNegationFilter.class.st similarity index 85% rename from src/GToolkit-Coder/GtSearchNegationFilter.class.st rename to src/GToolkit-SearchFilters/GtSearchNegationFilter.class.st index 30ac0a934..5f1b9c942 100644 --- a/src/GToolkit-Coder/GtSearchNegationFilter.class.st +++ b/src/GToolkit-SearchFilters/GtSearchNegationFilter.class.st @@ -4,7 +4,7 @@ Class { #instVars : [ 'originalFilter' ], - #category : #'GToolkit-Coder-Filters' + #category : #'GToolkit-SearchFilters-Filters' } { #category : #accessing } @@ -12,6 +12,11 @@ GtSearchNegationFilter >> defaultFilterScope [ ^ originalFilter defaultFilterScope ] +{ #category : #accessing } +GtSearchNegationFilter >> filterValueString [ + ^ originalFilter filterValueString +] + { #category : #printing } GtSearchNegationFilter >> gtDisplayOn: stream [ self originalFilter gtDisplayOn: stream. diff --git a/src/GToolkit-Coder/GtSearchNullFilter.class.st b/src/GToolkit-SearchFilters/GtSearchNullFilter.class.st similarity index 75% rename from src/GToolkit-Coder/GtSearchNullFilter.class.st rename to src/GToolkit-SearchFilters/GtSearchNullFilter.class.st index 2c104d79d..2f582f538 100644 --- a/src/GToolkit-Coder/GtSearchNullFilter.class.st +++ b/src/GToolkit-SearchFilters/GtSearchNullFilter.class.st @@ -1,7 +1,7 @@ Class { #name : #GtSearchNullFilter, #superclass : #GtSearchFilter, - #category : #'GToolkit-Coder-Filters' + #category : #'GToolkit-SearchFilters-Filters' } { #category : #comparing } @@ -13,7 +13,12 @@ GtSearchNullFilter >> = anObject [ { #category : #accessing } GtSearchNullFilter >> defaultFilterScope [ - ^ self + ^ AsyncEmptyStream new +] + +{ #category : #comparing } +GtSearchNullFilter >> hash [ + ^self class hash ] { #category : #enumerating } diff --git a/src/GToolkit-Coder/GtSearchTypedEntitiesFilter.class.st b/src/GToolkit-SearchFilters/GtSearchTypedEntitiesFilter.class.st similarity index 86% rename from src/GToolkit-Coder/GtSearchTypedEntitiesFilter.class.st rename to src/GToolkit-SearchFilters/GtSearchTypedEntitiesFilter.class.st index 29e1b8345..b9e802d42 100644 --- a/src/GToolkit-Coder/GtSearchTypedEntitiesFilter.class.st +++ b/src/GToolkit-SearchFilters/GtSearchTypedEntitiesFilter.class.st @@ -1,7 +1,7 @@ Class { #name : #GtSearchTypedEntitiesFilter, #superclass : #GtSearchFilter, - #category : #'GToolkit-Coder-Filters' + #category : #'GToolkit-SearchFilters-Filters' } { #category : #accessing } diff --git a/src/GToolkit-Coder/GtSearchUnionFilter.class.st b/src/GToolkit-SearchFilters/GtSearchUnionFilter.class.st similarity index 70% rename from src/GToolkit-Coder/GtSearchUnionFilter.class.st rename to src/GToolkit-SearchFilters/GtSearchUnionFilter.class.st index 646f49350..f7722b151 100644 --- a/src/GToolkit-Coder/GtSearchUnionFilter.class.st +++ b/src/GToolkit-SearchFilters/GtSearchUnionFilter.class.st @@ -1,14 +1,19 @@ Class { #name : #GtSearchUnionFilter, #superclass : #GtSearchBinaryFilter, - #category : #'GToolkit-Coder-Filters' + #instVars : [ + 'defaultFilterScope' + ], + #category : #'GToolkit-SearchFilters-Filters' } { #category : #accessing } GtSearchUnionFilter >> defaultFilterScope [ - ^ left defaultFilterScope = right defaultFilterScope - ifTrue: [ left defaultFilterScope ] - ifFalse: [ self ] + defaultFilterScope + ifNil: [ defaultFilterScope := left defaultFilterScope = right defaultFilterScope + ifTrue: [ left defaultFilterScope ] + ifFalse: [ (left defaultFilterScope merge: right defaultFilterScope) withoutDuplicates ] ]. + ^ defaultFilterScope asyncSimilarCopy ] { #category : #printing } diff --git a/src/GToolkit-SearchFilters/ManifestGToolkitSearchFilters.class.st b/src/GToolkit-SearchFilters/ManifestGToolkitSearchFilters.class.st new file mode 100644 index 000000000..8f8594d15 --- /dev/null +++ b/src/GToolkit-SearchFilters/ManifestGToolkitSearchFilters.class.st @@ -0,0 +1,21 @@ +" +Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser +" +Class { + #name : #ManifestGToolkitSearchFilters, + #superclass : #PackageManifest, + #category : #'GToolkit-SearchFilters-Manifest' +} + +{ #category : #accessing } +ManifestGToolkitSearchFilters class >> mustOnlyDependOn [ + ^ { + 'Announcements-Core'. + 'Collections-Unordered'. + 'Futures'. + 'Kernel' + }, (self + forPharo12AndNewer: [{ + 'Kernel-CodeModel'.}] + forPharo11: [{}]) +] diff --git a/src/GToolkit-SearchFilters/package.st b/src/GToolkit-SearchFilters/package.st new file mode 100644 index 000000000..7323e7880 --- /dev/null +++ b/src/GToolkit-SearchFilters/package.st @@ -0,0 +1 @@ +Package { #name : #'GToolkit-SearchFilters' } diff --git a/src/GToolkit-VariableBindings/ClassDescription.extension.st b/src/GToolkit-VariableBindings/ClassDescription.extension.st new file mode 100644 index 000000000..3e40e10a4 --- /dev/null +++ b/src/GToolkit-VariableBindings/ClassDescription.extension.st @@ -0,0 +1,15 @@ +Extension { #name : #ClassDescription } + +{ #category : #'*GToolkit-VariableBindings' } +ClassDescription >> gtSlotNamed: aName ifFound: foundBlock ifNone: exceptionBlock [ + "By default, searches through all the slots in the object, however proxies may want to override this to display a different set (e.g. none)" + ^self classLayout resolveSlot: aName asSymbol ifFound: foundBlock ifNone: exceptionBlock +] + +{ #category : #'*GToolkit-VariableBindings' } +ClassDescription >> gtSlotNames [ + "Answer the slot names to be bound by GtPharoSourceCoderViewModel. + By default, this is all the slots in the object, however proxies may want to override this to display a different set (e.g. none)." + + ^ self slotNames +] diff --git a/src/GToolkit-VariableBindings/GtBindingExamples.class.st b/src/GToolkit-VariableBindings/GtBindingExamples.class.st deleted file mode 100644 index 28d985696..000000000 --- a/src/GToolkit-VariableBindings/GtBindingExamples.class.st +++ /dev/null @@ -1,164 +0,0 @@ -Class { - #name : #GtBindingExamples, - #superclass : #Object, - #category : #'GToolkit-VariableBindings-Examples' -} - -{ #category : #'binding strategy' } -GtBindingExamples >> defaultHighlightingBindingStrategy [ - - - ^ GtHighlightingBindingStrategy new -] - -{ #category : #'binding strategy' } -GtBindingExamples >> defaultSnippetBindingStrategy [ - - - ^ GtSnippetBindingStrategy new -] - -{ #category : #'binding strategy' } -GtBindingExamples >> highlightingBindingStrategy [ - - - ^ self defaultHighlightingBindingStrategy - bindings: self snippetEmptyBindings -] - -{ #category : #'binding strategy' } -GtBindingExamples >> queryDefaultHighlightingBindingStrategy [ - - - | strategy result | - strategy := self defaultHighlightingBindingStrategy. - result := strategy bindingOf: self variableOneName. - self assert: result isNil. - ^ strategy -] - -{ #category : #'binding strategy' } -GtBindingExamples >> queryDefaultSnippetBindingStrategy [ - - - | strategy result | - strategy := self defaultSnippetBindingStrategy. - result := strategy bindingOf: self variableOneName. - self assert: result isNil. - ^ strategy -] - -{ #category : #'bindings - snippet' } -GtBindingExamples >> queryEmptySnippetBindings [ - - - | emptyBindings result | - emptyBindings := self snippetEmptyBindings. - result := emptyBindings - bindingOf: self variableOneName. - self assert: result isNil. - ^ emptyBindings -] - -{ #category : #'binding strategy' } -GtBindingExamples >> queryHighlightingBindingStrategy [ - - - | strategy result | - strategy := self highlightingBindingStrategy. - result := strategy bindingOf: self variableOneName. - self assert: result isNil. - ^ strategy -] - -{ #category : #'binding strategy' } -GtBindingExamples >> querySnippetBindingStrategy [ - - - | strategy result | - strategy := self snippetBindingStrategy. - result := strategy bindingOf: self variableOneName. - self assert: result notNil. - self assert: result key equals: self variableOneName. - self assert: result value equals: nil. - ^ result -] - -{ #category : #'bindings - snippet' } -GtBindingExamples >> querySnippetBindingsWithReceiver [ - - - | bindings result | - bindings := self snippetEmptyBindings. - bindings receiver: self setSnippetBindingsReceiver. - result := bindings bindingOf: #receiver. - self assert: result isNil. - ^ bindings -] - -{ #category : #'bindings - snippet' } -GtBindingExamples >> setSnippetBindings [ - - - | bindings result | - bindings := self snippetEmptyBindings. - bindings at: self variableOneName put: 42. - result := bindings - bindingOf: self variableOneName. - self assert: result notNil. - self assert: result value equals: 42. - self assert: result key equals: self variableOneName. - ^ bindings -] - -{ #category : #'bindings - snippet' } -GtBindingExamples >> setSnippetBindingsReceiver [ - - - | bindings | - bindings := self snippetEmptyBindings. - bindings receiver: 42. - self assert: bindings receiver equals: 42. - self assert: bindings receiverClass equals: 42 class. - ^ bindings -] - -{ #category : #'bindings - snippet' } -GtBindingExamples >> setSnippetBindingsWithReceiver [ - - | bindings result receiver | - bindings := self querySnippetBindingsWithReceiver. - receiver := self setSnippetBindingsReceiver. - bindings receiver: receiver. - bindings at: #receiver put: 42. - result := receiver receiver. - self assert: result notNil. - self assert: result equals: 42. - ^ bindings -] - -{ #category : #'binding strategy' } -GtBindingExamples >> snippetBindingStrategy [ - - - ^ self defaultSnippetBindingStrategy - bindings: self snippetEmptyBindings -] - -{ #category : #'bindings - snippet' } -GtBindingExamples >> snippetEmptyBindings [ - - - | aBindings | - aBindings := GtSnippetBindings new. - self assert: aBindings receiver isNil. - self assert: aBindings asDictionary isEmpty. - ^ aBindings -] - -{ #category : #accessing } -GtBindingExamples >> variableOneName [ - - - ^ #variableOne -] diff --git a/src/GToolkit-VariableBindings/GtBindingStrategy.class.st b/src/GToolkit-VariableBindings/GtBindingStrategy.class.st deleted file mode 100644 index b5fb1141a..000000000 --- a/src/GToolkit-VariableBindings/GtBindingStrategy.class.st +++ /dev/null @@ -1,90 +0,0 @@ -Class { - #name : #GtBindingStrategy, - #superclass : #Object, - #traits : 'TGtAssert', - #classTraits : 'TGtAssert classTrait', - #instVars : [ - 'bindings' - ], - #category : #'GToolkit-VariableBindings-Core' -} - -{ #category : #asserting } -GtBindingStrategy class >> assert: aBlock description: aStringOrBlock [ - "Throw an assertion error if aBlock does not evaluates to true. - We check for true explicitly to make the assertion fail for non booleans." - - self isAssertionEnabled - ifTrue: [ (aBlock value == true) ifFalse: [ - AssertionFailure signal: aStringOrBlock value ] ] -] - -{ #category : #'instance creation' } -GtBindingStrategy class >> bindings: aGtBindings [ - ^ self new bindings: aGtBindings -] - -{ #category : #converting } -GtBindingStrategy >> asDictionary [ - ^ self bindings asDictionary -] - -{ #category : #binding } -GtBindingStrategy >> bindingOf: aSymbol [ - - | assocOrNil | - - self - assert: [ aSymbol isSymbol ] - description: [ 'Bindings should be referenced by a symbol' ]. - - self - forPharo8: [ ] - forPharo9: [ - (self class environment lookupVar: aSymbol) - ifNotNil: [ :aVariable | ^ aVariable ] ]. - - assocOrNil := bindings bindingOf: aSymbol. - ^ self treatBinding: assocOrNil of: aSymbol. - -] - -{ #category : #accessing } -GtBindingStrategy >> bindings [ - ^ bindings -] - -{ #category : #accessing } -GtBindingStrategy >> bindings: anObject [ - self assert: [ anObject notNil ] description: [ 'Bindings should be an object' ]. - bindings := anObject -] - -{ #category : #'gt-extension' } -GtBindingStrategy >> gtBindingsFor: aView [ - - ^ (self bindings respondsTo: #gtBindingsFor:) - ifTrue: [ self bindings gtBindingsFor: aView ] - ifFalse: [ aView empty ] -] - -{ #category : #binding } -GtBindingStrategy >> hasBindingOf: aSymbol [ - ^ self bindings hasBindingOf: aSymbol -] - -{ #category : #initialization } -GtBindingStrategy >> initialize [ - super initialize. - self initializeBindings. -] - -{ #category : #initialization } -GtBindingStrategy >> initializeBindings [ - bindings := GtNoBindings uniqueInstance. -] - -{ #category : #private } -GtBindingStrategy >> treatBinding: anAssocOrNil of: aSymbol [ - self subclassResponsibility -] diff --git a/src/GToolkit-VariableBindings/GtBindingStrategyWithRequestor.class.st b/src/GToolkit-VariableBindings/GtBindingStrategyWithRequestor.class.st deleted file mode 100644 index a6034a2eb..000000000 --- a/src/GToolkit-VariableBindings/GtBindingStrategyWithRequestor.class.st +++ /dev/null @@ -1,57 +0,0 @@ -Class { - #name : #GtBindingStrategyWithRequestor, - #superclass : #Object, - #instVars : [ - 'requestor', - 'bindingStrategy' - ], - #category : #'GToolkit-VariableBindings-Commands' -} - -{ #category : #binding } -GtBindingStrategyWithRequestor >> bindingOf: aSymbol [ - - ^ self bindingStrategy bindingOf: aSymbol -] - -{ #category : #accessing } -GtBindingStrategyWithRequestor >> bindingStrategy [ - - ^ bindingStrategy -] - -{ #category : #accessing } -GtBindingStrategyWithRequestor >> bindingStrategy: aGtBindingStrategy [ - self - assert: [ aGtBindingStrategy notNil ] - description: [ 'Binding strategy must be non-nil' ]. - bindingStrategy := aGtBindingStrategy -] - -{ #category : #initialization } -GtBindingStrategyWithRequestor >> initialize [ - super initialize. - bindingStrategy := GtSnippetBindingStrategy new. - requestor := nil. -] - -{ #category : #accessing } -GtBindingStrategyWithRequestor >> requestor [ - ^ requestor -] - -{ #category : #accessing } -GtBindingStrategyWithRequestor >> requestor: anObject [ - requestor := anObject -] - -{ #category : #accessing } -GtBindingStrategyWithRequestor >> selection [ - - ^ self requestor - ifNotNil: [ :aRequestor | - (aRequestor respondsTo: #selection) - ifTrue: [ aRequestor selection ] - ifFalse: [ '' ] ] - ifNil: [ '' ] -] diff --git a/src/GToolkit-VariableBindings/GtBindingsTrait.trait.st b/src/GToolkit-VariableBindings/GtBindingsTrait.trait.st deleted file mode 100644 index f634b1e01..000000000 --- a/src/GToolkit-VariableBindings/GtBindingsTrait.trait.st +++ /dev/null @@ -1,36 +0,0 @@ -Trait { - #name : #GtBindingsTrait, - #category : #'GToolkit-VariableBindings-Core' -} - -{ #category : #converting } -GtBindingsTrait >> allNames [ - ^ self asDictionary keys -] - -{ #category : #converting } -GtBindingsTrait >> asDictionary [ - - ^ self explicitRequirement -] - -{ #category : #adding } -GtBindingsTrait >> at: aSymbol put: anObject [ - "Set bindings for a variable named aSymbol and value anObject." - self explicitRequirement -] - -{ #category : #accessing } -GtBindingsTrait >> bindingOf: aSymbol [ - "aSymbol is a variable name. - Return a literal variable if the variable has a value assigned. - Return nil, if the variable has no value assigned." - - ^ self explicitRequirement -] - -{ #category : #testing } -GtBindingsTrait >> hasBindingOf: aSymbol [ - - ^ self explicitRequirement -] diff --git a/src/GToolkit-VariableBindings/GtCommandExamples.class.st b/src/GToolkit-VariableBindings/GtCommandExamples.class.st deleted file mode 100644 index db9a4b375..000000000 --- a/src/GToolkit-VariableBindings/GtCommandExamples.class.st +++ /dev/null @@ -1,96 +0,0 @@ -Class { - #name : #GtCommandExamples, - #superclass : #Object, - #category : #'GToolkit-VariableBindings-Examples' -} - -{ #category : #'code evaluation' } -GtCommandExamples >> evaluateSourceCodeCommand [ - - - | aCommand | - aCommand := GtEvaluateSourceCodeCommand new. - self assert: aCommand sourceCode isString. - self assert: aCommand bindingStrategy notNil. - self assert: aCommand result isNil. - self assert: aCommand receiver isNil. - ^ aCommand -] - -{ #category : #'code evaluation' } -GtCommandExamples >> executeDefaultSourceCode [ - - - | aCommand | - aCommand := self evaluateSourceCodeCommand. - aCommand execute. - self assert: aCommand result isNil. - ^ aCommand -] - -{ #category : #'code evaluation' } -GtCommandExamples >> executeSourceCode [ - - - | aCommand | - aCommand := self evaluateSourceCodeCommand. - aCommand sourceCode: '4 factorial'. - aCommand execute. - self assert: aCommand result equals: 24. - ^ aCommand -] - -{ #category : #'code evaluation' } -GtCommandExamples >> executeSourceCodeWithUndefinedVariable [ - - - | aCommand aWarning | - aCommand := self evaluateSourceCodeCommand. - aCommand sourceCode: 'aValue := 4 factorial'. - [ aCommand execute ] - on: OCUndeclaredVariableWarning - do: [ :theWarning | aWarning := theWarning ]. - self assert: aWarning notNil. - self assert: aWarning class equals: OCUndeclaredVariableWarning. - self assert: aWarning node name equals: #aValue. - self assert: aCommand result isNil. - ^ aCommand -] - -{ #category : #'code evaluation' } -GtCommandExamples >> executeSourceCodeWithUndefinedVariableUsingPlaygroundBindings [ - - - | aCommand | - aCommand := self evaluateSourceCodeCommand. - aCommand sourceCode: 'aValue := 4 factorial'. - aCommand variableBindings: GtSnippetBindings new. - aCommand execute. - self assert: aCommand result equals: 24. - self assert: (aCommand variableBindings bindingOf: #aValue) notNil. - self assert: (aCommand variableBindings bindingOf: #aValue) value equals: 24. - ^ aCommand -] - -{ #category : #'code evaluation' } -GtCommandExamples >> executeUndefinedVariable [ - - - | aCommand aVariable | - aCommand := self evaluateSourceCodeCommand. - aCommand variableBindings: self playgroundBindings. - aCommand sourceCode: 'aValue := 42'. - aCommand execute. - self assert: aCommand result equals: 42. - self assert: aCommand variableBindings notNil. - aVariable := aCommand variableBindings bindingOf: #aValue. - self assert: aVariable notNil. - self assert: aVariable value equals: 42. - ^ aCommand -] - -{ #category : #'code evaluation' } -GtCommandExamples >> playgroundBindings [ - - ^ GtSnippetBindings new -] diff --git a/src/GToolkit-VariableBindings/GtCompositeVariablesBindings.class.st b/src/GToolkit-VariableBindings/GtCompositeVariablesBindings.class.st new file mode 100644 index 000000000..fc8094c5f --- /dev/null +++ b/src/GToolkit-VariableBindings/GtCompositeVariablesBindings.class.st @@ -0,0 +1,85 @@ +Class { + #name : #GtCompositeVariablesBindings, + #superclass : #Object, + #traits : 'TGtVariablesBindings', + #classTraits : 'TGtVariablesBindings classTrait', + #instVars : [ + 'bindings' + ], + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #comparing } +GtCompositeVariablesBindings >> = anObject [ + + "Answer whether the receiver and anObject represent the same object." + + self == anObject ifTrue: [ ^ true ]. + self class = anObject class ifFalse: [ ^ false ]. + ^ bindings = anObject bindings +] + +{ #category : #adding } +GtCompositeVariablesBindings >> addBindings: aGtVariablesBindings [ + bindings add: aGtVariablesBindings +] + +{ #category : #binding } +GtCompositeVariablesBindings >> bindingNames [ + ^ bindings flatCollect: [ :eachVariableBindings | eachVariableBindings bindingNames ] +] + +{ #category : #binding } +GtCompositeVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + + bindings do: [ :eachVariableBindings | + (eachVariableBindings bindingOf: aSymbol) + ifNotNil: [ :aVariable | ^ aVariable ] ]. + + ^ nil +] + +{ #category : #binding } +GtCompositeVariablesBindings >> bindingValueOf: aSymbol [ + "aSymbol is a variable name. + Return a value of the variable." + + + bindings do: [ :eachVariableBindings | + (eachVariableBindings bindingValueOf: aSymbol) + ifNotNil: [ :aVariable | ^ aVariable ] ]. + + ^ nil +] + +{ #category : #accessing } +GtCompositeVariablesBindings >> bindings [ + + ^ bindings +] + +{ #category : #testing } +GtCompositeVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ bindings anySatisfy: [ :eachVariableBindings | eachVariableBindings hasBindingOf: aSymbol ] +] + +{ #category : #comparing } +GtCompositeVariablesBindings >> hash [ + + "Answer an integer value that is related to the identity of the receiver." + + ^ bindings hash +] + +{ #category : #initialization } +GtCompositeVariablesBindings >> initialize [ + super initialize. + + bindings := OrderedCollection new +] diff --git a/src/GToolkit-VariableBindings/GtContextHighlightingBindingStrategy.class.st b/src/GToolkit-VariableBindings/GtContextHighlightingBindingStrategy.class.st deleted file mode 100644 index e422ab8e8..000000000 --- a/src/GToolkit-VariableBindings/GtContextHighlightingBindingStrategy.class.st +++ /dev/null @@ -1,29 +0,0 @@ -Class { - #name : #GtContextHighlightingBindingStrategy, - #superclass : #GtHighlightingBindingStrategy, - #instVars : [ - 'context' - ], - #category : #'GToolkit-VariableBindings-Core' -} - -{ #category : #binding } -GtContextHighlightingBindingStrategy >> bindingOf: aSymbol [ - | value | - (super bindingOf: aSymbol) ifNotNil: [ :aBinding | aBinding ]. - (self hasBindingOf: aSymbol) ifFalse: [ ^ nil ]. - - value := [ (context tempNamed: aSymbol) ] on: Error do: [ nil ]. - ^ WorkspaceVariable key: aSymbol value: value -] - -{ #category : #accessing } -GtContextHighlightingBindingStrategy >> context: aContext [ - context := aContext -] - -{ #category : #binding } -GtContextHighlightingBindingStrategy >> hasBindingOf: aSymbol [ - ^ (super hasBindingOf: aSymbol) or: [ - context hasTemporaryVariableNamed: aSymbol ] -] diff --git a/src/GToolkit-VariableBindings/GtContextVariablesBindings.class.st b/src/GToolkit-VariableBindings/GtContextVariablesBindings.class.st new file mode 100644 index 000000000..f85c967a0 --- /dev/null +++ b/src/GToolkit-VariableBindings/GtContextVariablesBindings.class.st @@ -0,0 +1,78 @@ +Class { + #name : #GtContextVariablesBindings, + #superclass : #Object, + #traits : 'TGtVariablesBindings', + #classTraits : 'TGtVariablesBindings classTrait', + #instVars : [ + 'context' + ], + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #comparing } +GtContextVariablesBindings >> = anObject [ + + "Answer whether the receiver and anObject represent the same object." + + self == anObject ifTrue: [ ^ true ]. + self class = anObject class ifFalse: [ ^ false ]. + ^ context = anObject context +] + +{ #category : #binding } +GtContextVariablesBindings >> bindingNames [ + ^ context tempNames +] + +{ #category : #binding } +GtContextVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + | var | + var := [ context lookupVar: aSymbol ] + on: Error + do: [ nil ]. + var + ifNil: [ ^ nil ]. + var isLocalVariable + ifFalse: [ ^ nil ]. + ^ var asDoItVariableFrom: context. +] + +{ #category : #binding } +GtContextVariablesBindings >> bindingValueOf: aSymbol [ + "aSymbol is a variable name. + Return a value of the variable." + + ^ (self bindingOf: aSymbol) + ifNotNil: [ :aBinding | aBinding readInContext: context ] +] + +{ #category : #accessing } +GtContextVariablesBindings >> context [ + + ^ context +] + +{ #category : #accessing } +GtContextVariablesBindings >> context: anObject [ + + context := anObject +] + +{ #category : #testing } +GtContextVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ (self bindingOf: aSymbol) notNil +] + +{ #category : #comparing } +GtContextVariablesBindings >> hash [ + + "Answer an integer value that is related to the identity of the receiver." + + ^ context hash +] diff --git a/src/GToolkit-VariableBindings/GtEmptyVariablesBindings.class.st b/src/GToolkit-VariableBindings/GtEmptyVariablesBindings.class.st new file mode 100644 index 000000000..a9bc2c770 --- /dev/null +++ b/src/GToolkit-VariableBindings/GtEmptyVariablesBindings.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GtEmptyVariablesBindings, + #superclass : #Object, + #traits : 'TGtVariablesBindings', + #classTraits : 'TGtVariablesBindings classTrait', + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #comparing } +GtEmptyVariablesBindings >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + ^ self class = anObject class +] + +{ #category : #binding } +GtEmptyVariablesBindings >> bindingNames [ + ^ #() +] + +{ #category : #binding } +GtEmptyVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + + ^ nil +] + +{ #category : #testing } +GtEmptyVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ false +] + +{ #category : #comparing } +GtEmptyVariablesBindings >> hash [ + ^ self class hash +] diff --git a/src/GToolkit-VariableBindings/GtEvaluateSourceCodeCommand.class.st b/src/GToolkit-VariableBindings/GtEvaluateSourceCodeCommand.class.st deleted file mode 100644 index 1e16970fb..000000000 --- a/src/GToolkit-VariableBindings/GtEvaluateSourceCodeCommand.class.st +++ /dev/null @@ -1,112 +0,0 @@ -Class { - #name : #GtEvaluateSourceCodeCommand, - #superclass : #BlTktCommand, - #instVars : [ - 'result', - 'sourceCode', - 'receiver', - 'requestorAndBindings' - ], - #category : #'GToolkit-VariableBindings-Commands' -} - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> bindingStrategy [ - - ^ requestorAndBindings bindingStrategy -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> bindingStrategy: aGtBindingStrategy [ - requestorAndBindings bindingStrategy: aGtBindingStrategy -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> catchingErrors [ - "Return an error or collections of errors to catch during a command execution" - ^ Error, OCSemanticWarning -] - -{ #category : #execution } -GtEvaluateSourceCodeCommand >> execute [ - | aCompiler | - aCompiler := self class compiler - source: self sourceCode "readStream" "Looks like it can be a String instead of readSream"; - context: nil "TODO: what is context? evaluation uses `context method methodClass`"; - receiver: self receiver "TODO: what is receiver? evaluation uses `receiver class` if context is nil"; - requestor: requestorAndBindings. "TODO: requestor should be an editor? - evaluation uses `requestor selection` to evaluate only selected part" - result := aCompiler evaluate. - "evaluation does: - `receiver withArgs: context or #() executeMethod: - parse uses `noPattern`, - if context is not nil, `context tempNames` is used in `rewriteTempsForContext:` - doSemanticAnalysis uses: - `compilationContext failBlock` on an exception - `compilationContext scope newMethodScope to validate source code - `lookupVariableForWrite:` uses `scope lookupVar: aVarNameString" - "parseExpression uses `parserClass` and `optionParseErrors` to parse faulty expressions" - ^ result -] - -{ #category : #initialization } -GtEvaluateSourceCodeCommand >> initialize [ - super initialize. - sourceCode := 'nil'. - result := nil. - requestorAndBindings := GtBindingStrategyWithRequestor new. -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> receiver [ - ^ receiver -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> receiver: anObject [ - receiver := anObject -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> requestor [ - ^ requestorAndBindings requestor -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> requestor: anObject [ - requestorAndBindings requestor: anObject -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> result [ - "Return a source code evaluation result" - ^ result -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> sourceCode [ - - ^ sourceCode -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> sourceCode: aString [ - self - assert: [ aString notNil ] - description: [ 'Source code must be non-nil' ]. - sourceCode := aString asString -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> variableBindings [ - - ^ requestorAndBindings bindingStrategy bindings -] - -{ #category : #accessing } -GtEvaluateSourceCodeCommand >> variableBindings: aGtBindingsTrait [ - self - assert: [ aGtBindingsTrait notNil ] - description: [ 'Bindings must be non-nil' ]. - requestorAndBindings bindingStrategy bindings: aGtBindingsTrait -] diff --git a/src/GToolkit-VariableBindings/GtGlobalVariablesBindings.class.st b/src/GToolkit-VariableBindings/GtGlobalVariablesBindings.class.st new file mode 100644 index 000000000..ea5160296 --- /dev/null +++ b/src/GToolkit-VariableBindings/GtGlobalVariablesBindings.class.st @@ -0,0 +1,40 @@ +Class { + #name : #GtGlobalVariablesBindings, + #superclass : #Object, + #traits : 'TGtVariablesBindings', + #classTraits : 'TGtVariablesBindings classTrait', + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #comparing } +GtGlobalVariablesBindings >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + ^ self class = anObject class +] + +{ #category : #binding } +GtGlobalVariablesBindings >> bindingNames [ + ^ self class environment keys +] + +{ #category : #binding } +GtGlobalVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + + ^ self class environment bindingOf: aSymbol +] + +{ #category : #testing } +GtGlobalVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ self class environment includesKey: aSymbol +] + +{ #category : #comparing } +GtGlobalVariablesBindings >> hash [ + ^ self class hash +] diff --git a/src/GToolkit-VariableBindings/GtHighlightingBindingStrategy.class.st b/src/GToolkit-VariableBindings/GtHighlightingBindingStrategy.class.st deleted file mode 100644 index dc26a58ed..000000000 --- a/src/GToolkit-VariableBindings/GtHighlightingBindingStrategy.class.st +++ /dev/null @@ -1,12 +0,0 @@ -Class { - #name : #GtHighlightingBindingStrategy, - #superclass : #GtBindingStrategy, - #category : #'GToolkit-VariableBindings-Core' -} - -{ #category : #private } -GtHighlightingBindingStrategy >> treatBinding: anAssocOrNil of: aSymbol [ - "Subclasses can treat existing or missing bindings of a variable named aSymbol" - - ^ anAssocOrNil -] diff --git a/src/GToolkit-VariableBindings/GtLocalSnippetBindings.class.st b/src/GToolkit-VariableBindings/GtLocalSnippetBindings.class.st deleted file mode 100644 index 215f86896..000000000 --- a/src/GToolkit-VariableBindings/GtLocalSnippetBindings.class.st +++ /dev/null @@ -1,174 +0,0 @@ -" -GtLocalSnippetBindings provide a multi-tier set of bindings, allowing snippets to have local variables, e.g. `thisSnippet`, and shared variables. - - -1. # Internal Representation and Key Implementation Points. - - -1. ## Instance Variables - - localBindings: Bindings local to the user of these bindings - sharedBindings: Bindings shared amongst muiltiple users - - -1. ## Implementation Points - - -" -Class { - #name : #GtLocalSnippetBindings, - #superclass : #Object, - #traits : 'GtBindingsTrait + TGtAssert', - #classTraits : 'GtBindingsTrait classTrait + TGtAssert classTrait', - #instVars : [ - 'sharedBindings', - 'localBindings' - ], - #category : #'GToolkit-VariableBindings-Core' -} - -{ #category : #comparing } -GtLocalSnippetBindings >> = anObject [ - "Answer whether the receiver and anObject represent the same object." - - self == anObject - ifTrue: [ ^ true ]. - self class = anObject class - ifFalse: [ ^ false ]. - ^ sharedBindings = anObject sharedBindings - and: [ localBindings = anObject localBindings ] -] - -{ #category : #accessing } -GtLocalSnippetBindings >> allNames [ - - ^ self localNames, self sharedNames -] - -{ #category : #converting } -GtLocalSnippetBindings >> asDictionary [ - - | dictionary | - - dictionary := localBindings copy. - dictionary addAll: sharedBindings bindings. - ^ dictionary -] - -{ #category : #accessing } -GtLocalSnippetBindings >> at: aSymbol put: anObject [ - "By default, bindings are global" - - ^ localBindings - at: aSymbol - ifPresent: [ localBindings at: aSymbol put: anObject ] - ifAbsent: [ sharedBindings at: aSymbol put: anObject ]. - -] - -{ #category : #binding } -GtLocalSnippetBindings >> bindingOf: aSymbol [ - - ^ localBindings - at: aSymbol - ifPresent: [ :aValue | aValue ] - ifAbsent: [ sharedBindings bindingOf: aSymbol ] -] - -{ #category : #'gt-extension' } -GtLocalSnippetBindings >> gtBindingsFor: aView [ - - ^ self asDictionary gtItemsFor: aView -] - -{ #category : #binding } -GtLocalSnippetBindings >> hasBindingOf: aSymbol [ - ^ (sharedBindings hasBindingOf: aSymbol) or: [ localBindings includesKey: aSymbol ] -] - -{ #category : #comparing } -GtLocalSnippetBindings >> hash [ - "Answer an integer value that is related to the identity of the receiver." - - ^ sharedBindings hash bitXor: localBindings hash -] - -{ #category : #initialization } -GtLocalSnippetBindings >> initialize [ - - super initialize. - localBindings := IdentityDictionary new. -] - -{ #category : #accessing } -GtLocalSnippetBindings >> localAt: aSymbol put: anObject [ - - self assert: [ aSymbol isSymbol ] description: [ 'Variable name should be a symbol' ]. - localBindings - at: aSymbol - put: ((WorkspaceVariable named: aSymbol) value: anObject) -] - -{ #category : #accessing } -GtLocalSnippetBindings >> localBindings [ - ^ localBindings -] - -{ #category : #accessing } -GtLocalSnippetBindings >> localBindings: anObject [ - localBindings := anObject -] - -{ #category : #accessing } -GtLocalSnippetBindings >> localNames [ - "Answer the names of the local bindings" - - ^ localBindings keys -] - -{ #category : #copying } -GtLocalSnippetBindings >> postCopy [ - sharedBindings := sharedBindings copy. - localBindings := localBindings copy -] - -{ #category : #accessing } -GtLocalSnippetBindings >> receiver [ - - ^ sharedBindings receiver -] - -{ #category : #accessing } -GtLocalSnippetBindings >> receiver: anObject [ - - sharedBindings receiver: anObject -] - -{ #category : #accessing } -GtLocalSnippetBindings >> receiverClass [ - ^ sharedBindings receiver class -] - -{ #category : #removing } -GtLocalSnippetBindings >> remove: aSymbol [ - self assert: [ aSymbol isSymbol ] description: [ 'Variable name should be a symbol' ]. - localBindings - removeKey: aSymbol - ifAbsent: [ sharedBindings remove: aSymbol ] -] - -{ #category : #accessing } -GtLocalSnippetBindings >> sharedBindings [ - ^ sharedBindings -] - -{ #category : #accessing } -GtLocalSnippetBindings >> sharedBindings: anObject [ - sharedBindings := anObject -] - -{ #category : #accessing } -GtLocalSnippetBindings >> sharedNames [ - - ^ sharedBindings names -] diff --git a/src/GToolkit-VariableBindings/GtLocalVariablesBindings.class.st b/src/GToolkit-VariableBindings/GtLocalVariablesBindings.class.st new file mode 100644 index 000000000..d7c81806b --- /dev/null +++ b/src/GToolkit-VariableBindings/GtLocalVariablesBindings.class.st @@ -0,0 +1,96 @@ +Class { + #name : #GtLocalVariablesBindings, + #superclass : #Object, + #traits : 'TGtVariablesBindings', + #classTraits : 'TGtVariablesBindings classTrait', + #instVars : [ + 'bindings' + ], + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #comparing } +GtLocalVariablesBindings >> = anObject [ + + "Answer whether the receiver and anObject represent the same object." + + self == anObject ifTrue: [ ^ true ]. + self class = anObject class ifFalse: [ ^ false ]. + ^ bindings = anObject bindings +] + +{ #category : #binding } +GtLocalVariablesBindings >> bindingNames [ + ^ bindings keys +] + +{ #category : #enumerating } +GtLocalVariablesBindings >> bindingNamesAndValuesDo: aBlock [ + bindings + keysAndValuesDo: [ :aName :aVariable | aBlock cull: aName cull: aVariable ] +] + +{ #category : #binding } +GtLocalVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + + ^ bindings + at: aSymbol + ifAbsent: [ nil ] +] + +{ #category : #accessing } +GtLocalVariablesBindings >> bindings [ + + ^ bindings +] + +{ #category : #testing } +GtLocalVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ bindings includesKey: aSymbol +] + +{ #category : #comparing } +GtLocalVariablesBindings >> hash [ + + "Answer an integer value that is related to the identity of the receiver." + + ^ bindings hash +] + +{ #category : #initialization } +GtLocalVariablesBindings >> initialize [ + super initialize. + + bindings := Dictionary new +] + +{ #category : #accessing } +GtLocalVariablesBindings >> localAt: aSymbol put: anObject [ + self + assert: [ aSymbol isSymbol ] + description: [ 'Variable name should be a symbol' ]. + + bindings + at: aSymbol + put: (WorkspaceVariable key: aSymbol value: anObject) +] + +{ #category : #accessing } +GtLocalVariablesBindings >> localAt: aSymbol putVariable: aVariable [ + self + assert: [ aSymbol isSymbol ] + description: [ 'Variable name should be a symbol' ]. + self + assert: [ aVariable isKindOf: Variable ] + description: [ 'Variable must be kind of Variable: {1}' format: { aVariable } ]. + + bindings + at: aSymbol + put: aVariable +] diff --git a/src/GToolkit-VariableBindings/GtNoBindings.class.st b/src/GToolkit-VariableBindings/GtNoBindings.class.st deleted file mode 100644 index 4b1c0003b..000000000 --- a/src/GToolkit-VariableBindings/GtNoBindings.class.st +++ /dev/null @@ -1,67 +0,0 @@ -Class { - #name : #GtNoBindings, - #superclass : #Object, - #traits : 'GtBindingsTrait', - #classTraits : 'GtBindingsTrait classTrait', - #classInstVars : [ - 'uniqueInstance' - ], - #category : #'GToolkit-VariableBindings-Core' -} - -{ #category : #cleanup } -GtNoBindings class >> cleanUp [ - self reset. -] - -{ #category : #accessing } -GtNoBindings class >> reset [ - uniqueInstance := nil -] - -{ #category : #accessing } -GtNoBindings class >> uniqueInstance [ - ^ uniqueInstance ifNil: [ uniqueInstance := self new ] -] - -{ #category : #comparing } -GtNoBindings >> = anObject [ - self == anObject ifTrue: [ ^ true ]. - ^ self class = anObject class -] - -{ #category : #converting } -GtNoBindings >> asDictionary [ - - ^ Dictionary new -] - -{ #category : #adding } -GtNoBindings >> at: aString put: anObject [ - "ignore by default" -] - -{ #category : #binding } -GtNoBindings >> bindingOf: aSymbol [ - ^ nil -] - -{ #category : #binding } -GtNoBindings >> hasBindingOf: aSymbol [ - ^ false -] - -{ #category : #comparing } -GtNoBindings >> hash [ - ^ self class hash -] - -{ #category : #accessing } -GtNoBindings >> receiver [ - ^ nil -] - -{ #category : #accessing } -GtNoBindings >> receiverClass [ - ^ self receiver class -] diff --git a/src/GToolkit-VariableBindings/GtReservedVariablesBindings.class.st b/src/GToolkit-VariableBindings/GtReservedVariablesBindings.class.st new file mode 100644 index 000000000..f295f8b3e --- /dev/null +++ b/src/GToolkit-VariableBindings/GtReservedVariablesBindings.class.st @@ -0,0 +1,50 @@ +Class { + #name : #GtReservedVariablesBindings, + #superclass : #Object, + #traits : 'TGtVariablesBindings', + #classTraits : 'TGtVariablesBindings classTrait', + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #comparing } +GtReservedVariablesBindings >> = anObject [ + self == anObject ifTrue: [ ^ true ]. + ^ self class = anObject class +] + +{ #category : #binding } +GtReservedVariablesBindings >> bindingNames [ + ^ self pseudoVariables keys collect: [ :each | each asSymbol ] +] + +{ #category : #binding } +GtReservedVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + + ^ self pseudoVariables + at: aSymbol + ifAbsent: [ nil ] +] + +{ #category : #testing } +GtReservedVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ self pseudoVariables + includesKey: aSymbol +] + +{ #category : #comparing } +GtReservedVariablesBindings >> hash [ + ^ self class hash +] + +{ #category : #binding } +GtReservedVariablesBindings >> pseudoVariables [ + ^ self + forPharo12AndNewer: [ self class environment pseudoVariables ] + forPharo11: [ self class environment reservedVariables ] +] diff --git a/src/GToolkit-VariableBindings/GtSharedVariablesBindings.class.st b/src/GToolkit-VariableBindings/GtSharedVariablesBindings.class.st new file mode 100644 index 000000000..fab16e8fd --- /dev/null +++ b/src/GToolkit-VariableBindings/GtSharedVariablesBindings.class.st @@ -0,0 +1,77 @@ +Class { + #name : #GtSharedVariablesBindings, + #superclass : #Object, + #traits : 'TGtVariablesBindings', + #classTraits : 'TGtVariablesBindings classTrait', + #instVars : [ + 'bindings' + ], + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #comparing } +GtSharedVariablesBindings >> = anObject [ + + "Answer whether the receiver and anObject represent the same object." + + self == anObject ifTrue: [ ^ true ]. + self class = anObject class ifFalse: [ ^ false ]. + ^ bindings = anObject bindings +] + +{ #category : #binding } +GtSharedVariablesBindings >> bindingNames [ + ^ bindings keys +] + +{ #category : #binding } +GtSharedVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + + ^ bindings + at: aSymbol + ifAbsentPut: [ WorkspaceVariable key: aSymbol ] +] + +{ #category : #accessing } +GtSharedVariablesBindings >> bindings [ + + ^ bindings +] + +{ #category : #testing } +GtSharedVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ bindings includesKey: aSymbol +] + +{ #category : #comparing } +GtSharedVariablesBindings >> hash [ + + "Answer an integer value that is related to the identity of the receiver." + + ^ bindings hash +] + +{ #category : #initialization } +GtSharedVariablesBindings >> initialize [ + super initialize. + + bindings := Dictionary new +] + +{ #category : #accessing } +GtSharedVariablesBindings >> interactive [ + ^ false +] + +{ #category : #binding } +GtSharedVariablesBindings >> removeBindingOf: aSymbol [ + + + ^ bindings removeKey: aSymbol ifAbsent: [ "ignore" ] +] diff --git a/src/GToolkit-VariableBindings/GtSlotVariablesBindings.class.st b/src/GToolkit-VariableBindings/GtSlotVariablesBindings.class.st new file mode 100644 index 000000000..65c76d3df --- /dev/null +++ b/src/GToolkit-VariableBindings/GtSlotVariablesBindings.class.st @@ -0,0 +1,98 @@ +Class { + #name : #GtSlotVariablesBindings, + #superclass : #Object, + #traits : 'TGtVariablesBindings', + #classTraits : 'TGtVariablesBindings classTrait', + #instVars : [ + 'object' + ], + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #comparing } +GtSlotVariablesBindings >> = anObject [ + + "Answer whether the receiver and anObject represent the same object." + + self == anObject ifTrue: [ ^ true ]. + self class = anObject class ifFalse: [ ^ false ]. + ^ object = anObject object +] + +{ #category : #binding } +GtSlotVariablesBindings >> bindingNames [ + ^ object class gtSlotNames, object class classVarNames +] + +{ #category : #binding } +GtSlotVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + + ^ object class + gtSlotNamed: aSymbol + ifFound: [ :aSlot | aSlot ] + ifNone: [ + object class isInstanceSide + ifTrue: [ object class + classVariableNamed: aSymbol + ifAbsent: [ nil ] ] + ifFalse: [ object + classVariableNamed: aSymbol + ifAbsent: [ nil ] ] ] +] + +{ #category : #binding } +GtSlotVariablesBindings >> bindingValueOf: aSymbol [ + "aSymbol is a variable name. + Return a value of the variable." + + ^ (self bindingOf: aSymbol) ifNotNil: [ :aSlot | + aSlot isClassVariable + ifTrue: [ aSlot value ] + ifFalse: [ aSlot read: self object ] ] +] + +{ #category : #testing } +GtSlotVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ object class + gtSlotNamed: aSymbol + ifFound: [ :slot | true ] + ifNone: [ object class isInstanceSide + ifTrue: [ object class hasClassVarNamed: aSymbol ] + ifFalse: [ object hasClassVarNamed: aSymbol ] ] +] + +{ #category : #comparing } +GtSlotVariablesBindings >> hash [ + + "Answer an integer value that is related to the identity of the receiver." + + ^ object hash +] + +{ #category : #accessing } +GtSlotVariablesBindings >> object [ + + ^ object +] + +{ #category : #accessing } +GtSlotVariablesBindings >> object: anObject [ + + object := anObject +] + +{ #category : #printing } +GtSlotVariablesBindings >> printOn: aStream [ + super printOn: aStream. + + aStream + nextPut: $(; + print: object; + nextPut: $) +] diff --git a/src/GToolkit-VariableBindings/GtSnippetBindingStrategy.class.st b/src/GToolkit-VariableBindings/GtSnippetBindingStrategy.class.st deleted file mode 100644 index 0ad02e532..000000000 --- a/src/GToolkit-VariableBindings/GtSnippetBindingStrategy.class.st +++ /dev/null @@ -1,52 +0,0 @@ -Class { - #name : #GtSnippetBindingStrategy, - #superclass : #GtBindingStrategy, - #category : #'GToolkit-VariableBindings-Core' -} - -{ #category : #comparing } -GtSnippetBindingStrategy >> = anObject [ - "Answer whether the receiver and anObject represent the same object." - - self == anObject ifTrue: [ ^ true ]. - self class = anObject class ifFalse: [ ^ false ]. - ^ bindings = anObject bindings -] - -{ #category : #binding } -GtSnippetBindingStrategy >> bindingOf: aSymbol [ - - (self isBindingInReceiverScope: aSymbol) ifTrue: [ - ^ nil ]. - ^ super bindingOf: aSymbol - -] - -{ #category : #comparing } -GtSnippetBindingStrategy >> hash [ - "Answer an integer value that is related to the identity of the receiver." - - ^ bindings hash -] - -{ #category : #testing } -GtSnippetBindingStrategy >> isBindingInReceiverScope: aSymbol [ - "If the requested binding is an instance variable of the receiver then return nil - and allow the instance scope to return the binding. This needs to happen as currenty - the following scopes are searched: OCMethodScope -> OCRequestorScope -> OCInstanceScope. - If the requestor scope returns a variable that is present in the instance scope the value - of that variable will be read/written from the binding and not from the instance." - | receiverClass | - receiverClass := bindings receiver class. - ^ receiverClass allInstVarNames includes: aSymbol - -] - -{ #category : #private } -GtSnippetBindingStrategy >> treatBinding: anAssocOrNil of: aSymbol [ - "Subclasses can treat existing or missing bindings of a variable named aSymbol" - - ^ anAssocOrNil ifNil: [ - bindings at: aSymbol put: nil. - bindings bindingOf: aSymbol ] -] diff --git a/src/GToolkit-VariableBindings/GtSnippetBindings.class.st b/src/GToolkit-VariableBindings/GtSnippetBindings.class.st deleted file mode 100644 index d8c3ece97..000000000 --- a/src/GToolkit-VariableBindings/GtSnippetBindings.class.st +++ /dev/null @@ -1,111 +0,0 @@ -Class { - #name : #GtSnippetBindings, - #superclass : #Object, - #traits : 'GtBindingsTrait + TGtAssert', - #classTraits : 'GtBindingsTrait classTrait + TGtAssert classTrait', - #instVars : [ - 'bindings', - 'receiver' - ], - #category : #'GToolkit-VariableBindings-Core' -} - -{ #category : #comparing } -GtSnippetBindings >> = anObject [ - "Answer whether the receiver and anObject represent the same object." - - self == anObject ifTrue: [ ^ true ]. - self class = anObject class ifFalse: [ ^ false ]. - ^ receiver = anObject receiver and: [ bindings = anObject bindings ] -] - -{ #category : #converting } -GtSnippetBindings >> asDictionary [ - - | aDictionary | - - "bindings copy is not enough, because each value is a workspace variable which - is in fact an association of ket -> value. When converting to the dictionary we should - flatten the associations" - aDictionary := Dictionary new. - bindings keysAndValuesDo: [ :eachKey :eachWorkSpaceVariable | - aDictionary at: eachKey put: eachWorkSpaceVariable value ]. - - ^ aDictionary -] - -{ #category : #adding } -GtSnippetBindings >> at: aSymbol put: anObject [ - self assert: [ aSymbol isSymbol ] description: [ 'Variable name should be a symbol' ]. - - bindings - at: aSymbol - put: ((WorkspaceVariable named: aSymbol) value: anObject) -] - -{ #category : #binding } -GtSnippetBindings >> bindingOf: aSymbol [ - ^ bindings - at: aSymbol - ifPresent: [ :aValue | aValue ] - ifAbsent: [ nil ] -] - -{ #category : #accessing } -GtSnippetBindings >> bindings [ - ^ bindings -] - -{ #category : #'gt-extension' } -GtSnippetBindings >> gtBindingsFor: aView [ - - ^ bindings gtItemsFor: aView -] - -{ #category : #binding } -GtSnippetBindings >> hasBindingOf: aSymbol [ - ^ bindings includesKey: aSymbol -] - -{ #category : #comparing } -GtSnippetBindings >> hash [ - "Answer an integer value that is related to the identity of the receiver." - - ^ receiver hash bitXor: bindings hash -] - -{ #category : #initialization } -GtSnippetBindings >> initialize [ - super initialize. - bindings := Dictionary new. - receiver := nil. -] - -{ #category : #accessing } -GtSnippetBindings >> names [ - - ^ bindings keys -] - -{ #category : #accessing } -GtSnippetBindings >> receiver [ - ^ receiver -] - -{ #category : #accessing } -GtSnippetBindings >> receiver: anObject [ - receiver := anObject -] - -{ #category : #accessing } -GtSnippetBindings >> receiverClass [ - ^ self receiver class -] - -{ #category : #removing } -GtSnippetBindings >> remove: aSymbol [ - self assert: [ aSymbol isSymbol ] description: [ 'Variable name should be a symbol' ]. - bindings - removeKey: aSymbol - ifAbsent: [ "ignore" ] -] diff --git a/src/GToolkit-VariableBindings/ManifestGToolkitVariableBindings.class.st b/src/GToolkit-VariableBindings/ManifestGToolkitVariableBindings.class.st index bb9b20cfb..508902c37 100644 --- a/src/GToolkit-VariableBindings/ManifestGToolkitVariableBindings.class.st +++ b/src/GToolkit-VariableBindings/ManifestGToolkitVariableBindings.class.st @@ -1,10 +1,20 @@ " -I store metadata for this package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser - - +Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser " Class { #name : #ManifestGToolkitVariableBindings, #superclass : #PackageManifest, #category : #'GToolkit-VariableBindings-Manifest' } + +{ #category : #accessing } +ManifestGToolkitVariableBindings class >> mustOnlyDependOn [ + ^ { + 'Collections-Sequenceable'. + 'Collections-Unordered'. + 'Kernel' + }, (self + forPharo12AndNewer: [{ + 'Kernel-CodeModel'.}] + forPharo11: [{}]) +] diff --git a/src/GToolkit-VariableBindings/TGtVariablesBindings.trait.st b/src/GToolkit-VariableBindings/TGtVariablesBindings.trait.st new file mode 100644 index 000000000..5e7cff896 --- /dev/null +++ b/src/GToolkit-VariableBindings/TGtVariablesBindings.trait.st @@ -0,0 +1,55 @@ +Trait { + #name : #TGtVariablesBindings, + #category : #'GToolkit-VariableBindings-Bindings' +} + +{ #category : #converting } +TGtVariablesBindings >> asDictionary [ + | aDictionary | + + aDictionary := Dictionary new. + self bindingNames do: [ :eachBindingName | + aDictionary at: eachBindingName put: (self bindingOf: eachBindingName) ]. + + ^ aDictionary +] + +{ #category : #binding } +TGtVariablesBindings >> bindingNames [ + ^ self explicitRequirement +] + +{ #category : #binding } +TGtVariablesBindings >> bindingOf: aSymbol [ + "aSymbol is a variable name. + Return a literal variable if the variable has a value assigned. + Return nil, if the variable has no value assigned." + + + ^ self explicitRequirement +] + +{ #category : #binding } +TGtVariablesBindings >> bindingValueOf: aSymbol [ + "aSymbol is a variable name. + Return a value of the variable." + + ^ (self bindingOf: aSymbol) ifNotNil: [ :aBinding | aBinding value ] +] + +{ #category : #binding } +TGtVariablesBindings >> bindingValueOf: aSymbol ifPresent: aPresentBlock ifAbsent: anAbsentBlock [ + "aSymbol is a variable name. + Return a value of variable name" + + ^ (self hasBindingOf: aSymbol) + ifTrue: [ aPresentBlock cull: (self bindingValueOf: aSymbol) ] + ifFalse: [ anAbsentBlock value ] +] + +{ #category : #testing } +TGtVariablesBindings >> hasBindingOf: aSymbol [ + + + ^ self explicitRequirement +]