This time I am going describe Calypso navigation model.
In Calypso users query environment for specific set of objects.
First you need environment instance. There is global one for current image:
You can use your own navigation instance instead of global one:
For next steps you need a scope where you will look at environment objects.
It can be scope of full system:
Result is represented by requested ClyEnvironmentContent subclass. In first example it is instance of ClySortedClasses which is sent as argument. Query method accepts query instance (subclasses of ClyEnvironmentQuery) or compatible object which implements #asEnvironmentQuery message. And class of result itself plays role of most trivial query ClyAllItemsQuery. This query is responsible to return all objects accessible from given scope in requested form. For example you can query all classes in hierarchical form:
Computation of properties is slow. Imaging that you look at all classes in system and for each class you want abstract property. It will require scanning almost all methods in system.
Calypso solves this problem in two ways:
This logic provides important optimization for remote scenario where observed content is remote object. In this case only used part of items is transferred over network. And when next part is needed it is loaded to client. It makes remote browser very fast because only visible part of packages, classes and methods are sent over network and all required information is available in few requests.
Now let's play with example cursor. It can be opened in table morph:
Cursor provides query API to evaluate new queries with modified parameters.
For example you can ask different kind of result for original query:
Or you can evaluate new query with extra package:
Or you can evaluate new query without existing package:
With cursor you can navigate to the children scope of particular items. For example you can query methods of selected classes:
You can also evaluate original query in different scope. It allows fetch class side methods of selected classes:
All these queries never modify cursor state. They always return new cursor instance which points to new result.
On top of this navigation model Calypso implements tree structure. For example you can look at classes and methods in same table morph:
Tree structure can include many levels. For example packages -> class groups -> classes:
Also it is possible to show hierarchical items with collapsing buttons:
At the end let's implement new environment content RandomOrderedMethods:
When ClyAllItemsQuery is evaluated by given scope it asks requested content to be built from concrete objects. For package scope it will ask content for #buildFromPackages:. For class scope it will ask content for #buildFromClasses:.
For simplicity ClyRandomOrderedMethods will support only class scope:
"Building" methods are required to be able evaluate "all items queries" with new implemented form. More advanced queries could require another code. Generally queries implement one method:
you can switch it to new order:
For more details look at code and read class comments.
Next time I will show how extend browser with new functions.
In Calypso users query environment for specific set of objects.
First you need environment instance. There is global one for current image:
env := ClyNavigationEnvironment currentImageIt is navigation environment which is created over some system environment. In this case it is current image:
ClySystemEnvironment currentImageSystem environment models the image. It includes package manager, globals and system announcer. And navigation environment provides interface to query information from it. It organizes cache of all queries. If you will browse senders of message #do: same result will be returned for second call. Cache optimizes performance and memory usage during navigation.
You can use your own navigation instance instead of global one:
env := ClyNavigationEnvironment over: ClySystemEnvironment currentImageExternal libraries can provide own system environment and Calypso will be able browse it. For example new version of Ring allows browse code repository with all Calypso features.
For next steps you need a scope where you will look at environment objects.
It can be scope of full system:
systemScope := env systemScopeOr it can be scope of concrete objects:
packageScope := env selectScope: ClyPackageScope of: {Point package}.
classScope := env selectScope: ClyClassScope of: {Point. Collection}With scope you can evaluate queries:
packageScope query: ClySortedClasses
classScope query: ClySortedMethods
packageScope query: (ClyMessageSenders of: #(do: x))
classScope query: (ClyMessageImplementors of: #(select: y:))Any query returns instance of cursor which provides stream access to result (details below).
Result is represented by requested ClyEnvironmentContent subclass. In first example it is instance of ClySortedClasses which is sent as argument. Query method accepts query instance (subclasses of ClyEnvironmentQuery) or compatible object which implements #asEnvironmentQuery message. And class of result itself plays role of most trivial query ClyAllItemsQuery. This query is responsible to return all objects accessible from given scope in requested form. For example you can query all classes in hierarchical form:
packageScope query: ClyHierarchicallySortedClassesAny query is created with requested content or defines default one. In example senders and implementors use sorted methods by default. But they can use different:
classScope query: (ClyMessageSenders of: #(do:) as: ClyHierarchicallySortedMethods)As was mentioned above actual result of #query: is cursor object, the instance of ClyEnvironmentCursor:
cursor := classScope query: ClySortedMethods.Cursor provides stream access to requested content items:
cursor currentItem.
cursor nextItem.
cursor moveTo: itemPosition.
cursor retrieveAll.Returned items are not raw objects like methods or classes. Instead they are instances of ClyEnvironmentItem which wraps actual object. Items extend objects with arbitrary set of properties. For example if class has abstract method it can be marked with "abstract property". If method is overridden in subclasses it can be marked by "overridden property". Then tools use these properties to provide specific representation for items. For example browser can show abstract classes with italic font and it can show special icon nearly overridden method.
Computation of properties is slow. Imaging that you look at all classes in system and for each class you want abstract property. It will require scanning almost all methods in system.
Calypso solves this problem in two ways:
- All queries are cached. Computation will be performed only once
- Properties are computed lazily when objects are really used. For example they computed for objects which are shown to user, for only visible part of them.
This logic provides important optimization for remote scenario where observed content is remote object. In this case only used part of items is transferred over network. And when next part is needed it is loaded to client. It makes remote browser very fast because only visible part of packages, classes and methods are sent over network and all required information is available in few requests.
Now let's play with example cursor. It can be opened in table morph:
cursor := packageScope query: ClySortedClasses.
dataSource := ClyCollapsedDataSource on: cursor.
table := FTTableMorph new.
table
extent: 200 @ 400;
dataSource: dataSource;
openInWindow.
Cursor provides query API to evaluate new queries with modified parameters.
For example you can ask different kind of result for original query:
cursor := cursor query: ClyHierarchicallySortedClasses
Or you can evaluate new query with extra package:
cursor := cursor queryContentInScopeWith: #'AST-Core' asPackage.
Or you can evaluate new query without existing package:
cursor := cursor queryContentInScopeWithout: #Kernel asPackage.
With cursor you can navigate to the children scope of particular items. For example you can query methods of selected classes:
cursor := cursor query: ClySortedMethods from: {Point. Array}.
You can also evaluate original query in different scope. It allows fetch class side methods of selected classes:
cursor := cursor queryContentInNewScope: ClyClassSideScope.
All these queries never modify cursor state. They always return new cursor instance which points to new result.
On top of this navigation model Calypso implements tree structure. For example you can look at classes and methods in same table morph:
cursor := packageScope query: ClySortedClasses.
dataSource := ClyCollapsedDataSource on: cursor.
dataSource childrenStructure: { ClySortedMethods }.
table := FTTableMorph new.
table
extent: 200 @ 400;
dataSource: dataSource;
openInWindow
Tree structure can include many levels. For example packages -> class groups -> classes:
cursor := env systemScope query: ClySortedPackages.
dataSource := ClyCollapsedDataSource on: cursor.
dataSource childrenStructure: { ClySortedClassGroups. ClyHierarchicallySortedClasses}.
table := FTTableMorph new.
table
extent: 200 @ 400;
dataSource: dataSource;
openInWindow
Also it is possible to show hierarchical items with collapsing buttons:
cursor := packageScope query: ClyHierarchicallySortedClasses.
dataSource := ClyExpandedDataSource on: cursor.
table := FTTableMorph new.
table
extent: 200 @ 400;
dataSource: dataSource;
openInWindow
At the end let's implement new environment content RandomOrderedMethods:
ClyMethodsContent subclass: #ClyRandomOrderedMethodsEach kind of environment content implements set of the building methods depending on what scope it supports. For example ClySortedMethods supports extracting methods from classes and method groups. ClySortedClasses supports extracting classes from packages and class groups.
instanceVariableNames: ''
classVariableNames: ''
package: 'Calypso-Example'
When ClyAllItemsQuery is evaluated by given scope it asks requested content to be built from concrete objects. For package scope it will ask content for #buildFromPackages:. For class scope it will ask content for #buildFromClasses:.
For simplicity ClyRandomOrderedMethods will support only class scope:
buildFromClasses: classesNow look at methods in new form:
items := OrderedCollection new.
classes do: [ :eachClass |
eachClass localMethods do: [ :each |
items add: (ClyEnvironmentItem named: each selector with: each)] ].
items shuffle
cursor := env systemScope query: ClySortedPackages.
dataSource := ClyCollapsedDataSource on: cursor.
dataSource childrenStructure: { ClySortedClasses. RandomOrderedMethods}.
table := FTTableMorph new.
table
extent: 200 @ 400;
dataSource: dataSource;
openInWindow
- fetchContent: anEnvironmentContent from: anEnvironmentScope
buildFromMethods: methodsIf you open senders browser:
items := methods shuffled collect: [ :each |
ClyEnvironmentItem named: each selector with: each]
browser := ClyMethodBrowser browseSendersOf: #do:.
you can switch it to new order:
browser switchToResultContent: ClyRandomOrderedMethods
For more details look at code and read class comments.
Next time I will show how extend browser with new functions.