Search is pervasive in software development. Yet, most IDEs treat it somewhat as a side issue. As a consequence, you get multiple different search interfaces within the same environment, each of them being limited to a specific search, and not being composable with other search intefaces.
Alex Syrel, Andrei Chis and I decided it is time to change this state of facts, and we are happy to announce the first public version of GTSpotter, a novel interface for spotting objects that is part of the Glamorous Toolkit.
We had two broad goals when developing the GTSpotter interface:
- Provide a uniform yet moldable interface that can work on any object,
- Handle searching through arbitrary levels of object nesting.
Let’s take a closer look.
Searching for classes or packages is a common code search requirement. Although very similar in nature, these two searches are too often supported through separated search interfaces. GTSpotter integrates them easily in one. In the example below we see the basic interface that is triggered via Cmd+Enter. On top, the user can enter a textual query, and below the search is executed through multiple search categories. In our case, entering
GTSpo, leads to matching 39 classes (of which only 5 are shown) and 1 package. For each element, the matching substring is underlined.
The interface is fully controllable through the keyboard. The user can move with ArrowUp/ArrowDown between items or Cmd+Shift+ArrowUp/ArrowDown through categories. At the same time, the search field has the focus, so the user can switch seamlessly between selecting items and refining the search. Pressing Enter reveals the code browser.
While packages and classes are typical structural entities, annotations can be equally important. GTSpotter searches for Pharo pragmas in the same interface. In this case, pressing Enter opens an inspector on the
Searching for implementors is another common code search use case. Until now, the way to search for implementors in Pharo is to open a Playground, enter a the symbol, press Cmd+m and then close the Playground window. Tedious. GTSpotter makes it easier.
But, it's not just code that is interesting for search. Finding a tool is a search activity as well. That is why, the main GTSpotter offers, by default, the list with the items from the World menu.
GTSpotter matches substrings. For example, searching for
m b gets to the System Browser menu item, and pressing Enter spawns the browser. In essence, this produces a simple shortcut scheme.
GTPlayground remembers all snippets ever used within the image. This is nice, but it can quickly become hard to find a previous snippet. That is why, GTSpotter makes this list searchable. For example, a common use of Playground is to enable the triggering of a Gofer loading script. Searching for
Gofer new reveals all snippets used in the past. Pressing Enter reveals a Playground populated with the desired code.
Since a while, the GTPlayground has the ability of publishing the contents to the sharing service available at: http://ws.stfx.eu. However, until now, the playground offered no easy way to load the contents of a published page. GTSpotter fixes the situation: simply pasting the url offers an object with the remote page. Pressing Enter opens a playground with the page contents.
GTSpotter can find various types of objects rather fast. Yet, often we just want to get to the objects we just visited recently. It is for this reason that GTSpotter offers the history of previously spotted objects by default. For example, the picture below shows a history of 5 most recent objects.
Typical IDE search tools behave like general search tools in that they offer only one level of search. However, software systems have structure, and we often need to be able to search inside a found object. To this end, GTSpotter allows the user to dive in an object and continue searching through the same interface. This is accomplished by pressing Cmd+RightArrow.
For example, the below picture shows the case of going inside the
GTSpotter class. The context of the current search is shown in the breadcrumb on top of the window. For this method, the user can search through class related facts such as methods, variables or references.
Diving in a method, again offers the same interface through which the user can search for senders or implementors.
Diving in a sender reveals the senders and implementors. Thus, this simple interface provides the basic block for replacing other interfaces that are dedicated to showing the list of senders, implementors or references in a separate window.
But, GTSpotter is about objects, not about code. To exemplify the implication of this, let’s consider the typical use case of looking for a file somewhere in the directory structure inside the current directory. In the below picture, the main GTSpotter finds a directory.
Diving in the directory, allows the user to continue searching for deeper items.
Many development actions can be seen as search activities. Let’s consider the typical case of committing the changes of a package. First, we have to find the dirty package. Then, we have to find and choose the target repository, and then we commit.
These actions can be done directly from GTSpotter. When a dirty package exists in the image, it appears by default in the main GTSpotter in a separate searchable category.
Diving in the package, reveals the repositories.
Selecting a repository and pressing Enter triggers the commit dialog. This is but an example of how a search interface can change established workflows. This is certainly an area that has great potential.
Let’s search for
do:. The search raises 81 results. However, only 5 of them are shown.
How can we get a hold of
To do this, GTSpotter offers an extra action: diving in a category. Pressing Cmd+Shift+ArrowRight dives in the collection object containing only the items from that category. Thus, we can continue refining the search inside the category.
Refining a search can be applied to any category.
The existing implementations already comes with more than 30 various categories that support a rather extensive set of common search actions. However, the true power of GTSpotter comes from it being moldable.
Like with the GTInspector, each category is defined through corresponding extension method in the object representing the current search context.
By default, the main GTSpotter opens on itself. Thus, all the top level categories are defined as extensions of the
GTSpotter class. For example, searching for all classes in the image, is achieved as follows:
GTSpotter>>spotterForClassesFor: aStep <spotterOrder: 10> aStep listProcessor allCandidates: [ Smalltalk allClasses ]; title: 'Classes'; matchSubstring; itemIcon: #systemIcon
Similarly, searching for the instance side methods inside a class, is achieved through an extension method of
Class>>spotterMethodsFor: aStep <spotterOrder: 10> aStep listProcessor title: 'Instance methods'; allCandidates: [ self methods ]; itemName: [ :method | method selector ]; matchSubstring
That means that you can get your objects to be equally searchable with just a handful of lines of code.
GTSpotter offers a novel interface of getting hold of an object.
If the GTPlayground and GTInspector enable a deep conversation with an object, the GTSpotter makes it possible to start that conversation quickly.