Chris Muller uploaded a new version of Files to project The Trunk:
http://source.squeak.org/trunk/Files-cmm.207.mcz
==================== Summary ====================
Name: Files-cmm.207
Author: cmm
Time: 28 April 2024, 7:24:18.020282 pm
UUID: 7f3afa72-a69e-48be-aae7-a50900bfd4a3
Ancestors: Files-ul.206
- Support #currentDirectoryNickname from FileDirectory class>>#on:.
- Don't allow FileStream class>>#newFileNamed:do: and #forceNewFileNamed:do: to fail silently.
=============== Diff against Files-ul.206 ===============
Item was changed:
----- Method: FileDirectory class>>on: (in category 'instance creation') -----
on: pathString
"Return a new file directory for the given path, of the appropriate FileDirectory subclass for the current OS platform."
| pathName |
DirectoryClass ifNil: [self setDefaultDirectoryClass].
"If path ends with a delimiter (: or /) then remove it"
pathName := pathString.
(pathName at: pathName size ifAbsent: nil) = self pathNameDelimiter ifTrue:
[pathName := pathName allButLast].
DirectoryClass parentDirectoryNickname ifNotNil:
[:parentName|
(pathName beginsWith: parentName) ifTrue:
[pathName = parentName ifTrue:
[^self default containingDirectory].
(pathName at: parentName size + 1 ifAbsent: nil) = self pathNameDelimiter ifTrue:
[^self default containingDirectory on: (pathName allButFirst: parentName size + 1)]]].
+ DirectoryClass currentDirectoryNickname ifNotNil:
+ [: currentName|
+ (pathName beginsWith: currentName) ifTrue:
+ [pathName = currentName ifTrue:
+ [^self default].
+ (pathName at: currentName size + 1 ifAbsent: nil) = self pathNameDelimiter ifTrue:
+ [^self default on: (pathName allButFirst: currentName size + 1)]]].
+
^DirectoryClass new setPathName: pathName!
Item was changed:
----- Method: FileStream class>>forceNewFileNamed:do: (in category 'instance creation') -----
+ forceNewFileNamed: fileName do: aBlock
+ "Create a file named fileName and value aBlock with a ReadWriteStream on its contents, which is closed automatically upon completion of aBlock. If the file identified by fileName already exists, it will be replaced. If the file can't be created for any reason, signal an error."
+ ^ (self forceNewFileNamed: fileName)
+ ifNil: [ self error: 'file not created' ]
+ ifNotNil: [ : fileStream | self detectFile: fileStream do: aBlock ]!
- forceNewFileNamed: fileName do: aBlock
- "Avi Bryant says, ''This idiom is quite common in other languages that make heavy use of closures (i.e. Lisp (with-file 'foo' (f) ...) and Ruby (File.open('foo'){|f|...})). It's time Squeak had it, too.''
-
- Returns the result of aBlock."
-
- ^self detectFile: (self forceNewFileNamed: fileName) do: aBlock!
Item was changed:
----- Method: FileStream class>>newFileNamed:do: (in category 'instance creation') -----
+ newFileNamed: fileName do: aBlock
+ "Create a file named fileName and value aBlock with a ReadWriteStream on its contents, which is closed automatically upon completion of aBlock. If the file identified by fileName already exists, signal an error. If the file can't be created for any reason, signal an error."
+ ^ (self newFileNamed: fileName)
+ ifNil: [ self error: 'file not created' ]
+ ifNotNil: [ : fileStream | self detectFile: fileStream do: aBlock ]!
- newFileNamed: fileName do: aBlock
- "Avi Bryant says, ''This idiom is quite common in other languages that make heavy use of closures (i.e. Lisp (with-file 'foo' (f) ...) and Ruby (File.open('foo'){|f|...})). It's time Squeak had it, too.''
-
- Returns the result of aBlock."
-
- ^self detectFile: (self newFileNamed: fileName) do: aBlock!
Christoph Thiede uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-ct.2155.mcz
==================== Summary ====================
Name: Morphic-ct.2155
Author: ct
Time: 3 May 2024, 6:24:46.710377 pm
UUID: ea10ff8e-6579-4cd4-9131-d2af6e8a800a
Ancestors: Morphic-mt.2152
Fixes halo transfer to morphs that overlap with another morph that overlaps with the current halo. This was a regression from Morphic-ct.1794.
=============== Diff against Morphic-mt.2152 ===============
Item was changed:
----- Method: MorphicHaloDispatcher>>dispatchHalo:createFor: (in category 'dispatching') -----
dispatchHalo: anEvent createFor: aContainer
"Invoke a halo on any aContainer's submorph that wants it. Dispatch uses anEvent's #position. The dispatch only ends in that container if no other morph wants it. Note that the event's #shiftPressed state determines whether the dispatch goes innermost-to-outermost (if pressed) or the other way around (if not pressed).
If there already is a halo, check whether the event still points into the same hierarchy. If it does, do nothing here but rely on the halo itself to process the event (see implementors of #transferHalo:from:). If, however, the event points to a different hierarchy in the container, invoke a new halo and discard the current one. We do this here because the current halo should not bother with its container but only its #target."
| stack innermost haloTarget |
"The stack is the frontmost (i.e. innermost) to backmost (i.e. outermost) morph."
stack := (aContainer morphsAt: anEvent position unlocked: true useFullBounds: true) select:
[ : each | each wantsHaloFromClick or: [ each isRenderer ] ].
"self assert: [ stack last == aContainer ]."
innermost := anEvent hand halo
ifNil: [ stack first ]
ifNotNil:
[ : existingHalo |
"self assert: [ existingHalo wantsHaloFromClick not ]. "
stack
+ detect: [ : each | each owner == aContainer ]
- detect: [ : each | each owner == aContainer
- and: [ existingHalo bounds intersects: (existingHalo haloBoundsFor: each) ] ]
ifFound:
[ : topInContainer | "Is existingHalo's target part of the same topInContainer as the morph clicked?"
(existingHalo target withAllOwners includes: topInContainer)
ifTrue: [ "same hierarchy, let #transferHalo: continue to handle it for now." ^ false ]
ifFalse:
[ "different hierarchy, remove + add."
anEvent hand removeHalo.
anEvent shiftPressed
ifTrue: [ stack first ]
ifFalse: [ topInContainer ] ] ]
ifNone: [ "existingHalo is on the World, defer to #transferHalo: for now." ^ false ] ].
"If modifier key is pressed, start at innermost (the target), otherwise the outermost (direct child of the world (self))."
haloTarget := (innermost == aContainer or: [ anEvent shiftPressed ])
ifTrue: [ innermost ]
ifFalse:
[ "Find the outermost owner that wants it. Ignore containment above aContainer."
stack := innermost withAllOwners.
(stack first: (stack findFirst: [ : each | each owner == aContainer ])) reversed
detect: [ : each | each wantsHaloFromClick or: [ each isRenderer ] ]
ifNone: [ "haloTarget has its own mouseDown handler, don't halo." ^ false ] ].
"Now that we have the haloTarget, show the halo."
self invokeHaloOrMove: anEvent on: haloTarget.
^ true!
Chris Muller uploaded a new version of SUnit to project The Trunk:
http://source.squeak.org/trunk/SUnit-cmm.147.mcz
==================== Summary ====================
Name: SUnit-cmm.147
Author: cmm
Time: 1 May 2024, 3:44:58.989279 pm
UUID: fa6494d6-4801-4f74-b349-938d8125ba74
Ancestors: SUnit-cmm.146
Revert cmm.146 and continue override #defaultTimeout instead.
=============== Diff against SUnit-cmm.146 ===============
Item was changed:
----- Method: TestCase>>debug (in category 'running') -----
debug
"Run the receiver and open a debugger on the first failure or error."
+ ^ self assureResourcesDuring: [self runCaseWithoutTimeout]!
- ^ self assureResourcesDuring: [self runCase]!
Item was changed:
----- Method: TestCase>>debugAsFailure (in category 'running') -----
debugAsFailure
"Spawn a debugger that is ready to debug the receiver."
(Process
forBlock: [self debug]
runUntil: [:context | context isClosureContext "navigate the process directly to the point where it is about to send #setUp"
+ and: [context selector = #runCaseWithoutTimeout]])
- and: [context selector = #runCase]])
debug.!
Christoph Thiede uploaded a new version of Regex-Core to project The Trunk:
http://source.squeak.org/trunk/Regex-Core-ct.89.mcz
==================== Summary ====================
Name: Regex-Core-ct.89
Author: ct
Time: 1 May 2024, 6:10:49.419117 pm
UUID: d2c4145b-80ff-4c4f-baa6-7fcd9f929a42
Ancestors: Regex-Core-ct.87
Fixes and documents order of keyed subexpressions to be top-down and left-to-right (complements Regex-Tests-Core-ct.36). Documents index offset in positional #subexpression: and #subexpressions:.
=============== Diff against Regex-Core-ct.87 ===============
Item was changed:
----- Method: RxMatcher>>keyedSubexpressionRanges: (in category 'accessing') -----
keyedSubexpressionRanges: key
+ "Answer an array of all match ranges (inclusiveStart -> inclusiveStop) of the subexpression at the given key. If a key is defined in multiple groups, sort ranges top-down and left-to-right."
- "Answer an array of all match ranges (inclusiveStart -> inclusiveStop) of the subexpression at the given key."
^ ((keyedMarkerPositions at: key) gather: [:pair |
(markerPositions at: pair first)
with: (markerPositions at: pair second)
collect: [:start :stop | start + 1 to: stop]])
"Since the same key can be defined for multiple markers, ordering the matches is non-trivial."
+ reverse sort: [:range | range start] ascending!
- sort: [:range | range stop] ascending!
Item was changed:
----- Method: RxMatcher>>subexpression: (in category 'accessing') -----
subexpression: subIndex
"Answer a string that matched the subexpression at the given index.
If there are multiple matches, answer the last one.
If there are no matches, answer nil.
+ NB: index 1 is the entire match, index 2 is the first capture group, etc.
(NB: it used to answer an empty string but I think nil makes more sense)."
^ (self subexpressions: subIndex)
ifNotEmpty: [:expressions | expressions last]
ifEmpty: [nil]!
Item was changed:
----- Method: RxMatcher>>subexpressions: (in category 'accessing') -----
subexpressions: subIndex
"Answer an array of all matches of the subexpression at the given index.
+ The answer is always an array; it is empty if there are no matches.
+ NB: index 1 is the entire match, index 2 is the first capture group, etc."
- The answer is always an array; it is empty if there are no matches."
| originalPosition startPositions stopPositions reply |
originalPosition := stream position.
startPositions := self subBeginning: subIndex.
stopPositions := self subEnd: subIndex.
(startPositions isEmpty or: [stopPositions isEmpty]) ifTrue: [^Array new].
reply := Array new: startPositions size.
1 to: reply size do: [ :index |
| start stop |
start := startPositions at: index.
stop := stopPositions at: index.
stream position: start.
reply at: index put: (stream next: stop - start) ].
stream position: originalPosition.
^reply!
Chris Muller uploaded a new version of SUnit to project The Trunk:
http://source.squeak.org/trunk/SUnit-cmm.146.mcz
==================== Summary ====================
Name: SUnit-cmm.146
Author: cmm
Time: 30 April 2024, 12:43:19.759559 am
UUID: 51424d55-94d0-4d2e-b8dc-f470b05cb44d
Ancestors: SUnit-mt.145
Let SUnit provide consistent results when running vs. debugging, rather than quietly hiding SUnit timeout configuration bugs when debugging.
=============== Diff against SUnit-mt.145 ===============
Item was changed:
----- Method: TestCase>>debug (in category 'running') -----
debug
"Run the receiver and open a debugger on the first failure or error."
+ ^ self assureResourcesDuring: [self runCase]!
- ^ self assureResourcesDuring: [self runCaseWithoutTimeout]!
Item was changed:
----- Method: TestCase>>debugAsFailure (in category 'running') -----
debugAsFailure
"Spawn a debugger that is ready to debug the receiver."
(Process
forBlock: [self debug]
runUntil: [:context | context isClosureContext "navigate the process directly to the point where it is about to send #setUp"
+ and: [context selector = #runCase]])
- and: [context selector = #runCaseWithoutTimeout]])
debug.!
Eliot Miranda uploaded a new version of Tools to project The Trunk:
http://source.squeak.org/trunk/Tools-eem.1251.mcz
==================== Summary ====================
Name: Tools-eem.1251
Author: eem
Time: 29 April 2024, 2:53:17.573251 pm
UUID: f493712c-4f55-4c9e-96f9-394e45e29b32
Ancestors: Tools-eem.1250
For the TreeBrowser, if a system category is already selected, use it as teh stem of the prompt to add a new category, so that it is easier to add a subcategory.
Guard against empty categories in answering the class list of a PackagePaneBrowser.
=============== Diff against Tools-eem.1250 ===============
Item was changed:
----- Method: Browser>>addSystemCategory (in category 'system category functions') -----
addSystemCategory
"Prompt for a new category name and add it before the
current selection, or at the end if no current selection"
| oldSelection newName |
self okToChange ifFalse: [^ self].
oldSelection := self selectedSystemCategory.
newName := self
request: 'Please type new category name'
+ initialAnswer: (oldSelection ifNil: ['Category-Name'] ifNotNil: [oldSelection, '-Name']).
+ newName isEmpty ifTrue: [^ self].
- initialAnswer: 'Category-Name'.
- newName isEmpty
- ifTrue: [^ self]
- ifFalse: [newName := newName asSymbol].
systemOrganizer
+ addCategory: (newName := newName asSymbol)
- addCategory: newName
before: self selectedSystemCategory.
+ self selectSystemCategory: newName.
- self selectSystemCategory:
- (oldSelection isNil
- ifTrue: [ self systemCategoryList last ]
- ifFalse: [ oldSelection ]).
self changed: #systemCategoryList.!
Item was changed:
----- Method: PackagePaneBrowser>>classList (in category 'class list') -----
classList
self hasSystemCategorySelected ifTrue:
[| thisPackage thisCatSufix categories |
thisPackage := self package.
thisCatSufix := self selectedSystemCategoryWithoutPackage.
categories := systemOrganizer categories select: [:eachCat |
eachCat size >= thisPackage size
and: ["package prefix may occur in different cases, because they are assembled in the same package"
((eachCat first: thisPackage size) compare: thisPackage caseSensitive: false) = 2]
and: ["rest of system category must have exact case, because it is discrimnated by the system category list"
(eachCat allButFirst: thisPackage size) = thisCatSufix]].
+ categories ifNotEmpty:
+ [^categories gather: [:eachCat | systemOrganizer listAtCategoryNamed: eachCat]]].
- ^ categories gather: [:eachCat |
- systemOrganizer listAtCategoryNamed: eachCat]].
self hasPackageSelected ifTrue:
[^ self packageClasses].
^ systemOrganizer allElements!
Marcel Taeumel uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel-mt.232.mcz
==================== Summary ====================
Name: FFI-Kernel-mt.232
Author: mt
Time: 17 April 2024, 11:17:11.319706 am
UUID: 7bf5313f-0238-2541-97d4-daeeadc57839
Ancestors: FFI-Kernel-mt.231
For communicating failed FFI callouts, add support for PrimitiveError and ExceptionInFFICallError.
=============== Diff against FFI-Kernel-mt.231 ===============
Item was changed:
----- Method: ExternalFunction class>>externalCallFailedWith: (in category 'error handling') -----
externalCallFailedWith: primErrorCode
"Raise an error after a failed call to an external function.
The primFailCode could be any of:
- a symbol; one of the standard primitive errors defined in Smalltalk primitiveErrorTable
- nil; the VM does not support primitive errors and is not providing error codes
- an integer; one of the FFI codes incremented by Smalltalk primitiveErrorTable size + 2
+ so as not to clash with the standard primitive errors
+ - an instance of PrimitiveError that encodes an FFI marshalling error
+ - an instance of PrimitiveError that encodes an FFI callout exception error."
+
+ primErrorCode isExceptionInFFICallError ifTrue:
+ [^self error: primErrorCode errorName, ' (', primErrorCode pc asString, ')'].
+ primErrorCode isPrimitiveError ifTrue:
+ [^self error: primErrorCode errorName, ' (', (self errorMessageFor: primErrorCode errorCode), ')'].
+
- so as not to clash with the standard primitive errors."
^self error: (primErrorCode isInteger
ifTrue: [self errorMessageFor: primErrorCode - (Smalltalk primitiveErrorTable size + 2)]
ifFalse: [primErrorCode isNil
ifTrue: ['Call to external function failed']
ifFalse: [primErrorCode]])!
Item was added:
+ ----- Method: Object>>externalCallFailedWith: (in category '*FFI-Kernel') -----
+ externalCallFailedWith: primErrorCode
+
+ ^ ExternalFunction externalCallFailedWith: primErrorCode!