I’ve finished an internship in RMOD team in Inria and I was doing some retrospective in the work I’ve done.
The idea for my internship was to improve the developer experience while using Pharo [http://www.pharo-project.org/], (quite challenging!), so in order to do this I was mainly focused on 4 points:
- Code Navigation
- Syntax Coloring
- Class Definition representation
While we are coding we usually want to apply actions depending on the element we are writing/seeing, for example if it’s a variable we may want to rename it. But in order to do this, we have big menus to find what we want, usually with lot of options that don’t apply.
The idea in smart suggestions is that based on the context offer only the relevant options.
We use the current AST to do this through RBParser (parseFaultyMethod:parseFaultyExpresion:) and Opal Semantic Analysis.
We choose RBParser because we can parse faulty expressions, with this feature we can offer suggestions while the user types the method.
Opal is the new compiler integrated in Pharo, it’s a very clean implementation and we think that will replace the old compiler, so we didn’t want to couple suggestions with an old compiler. The semantic analysis helps us to complete information form a code, because there is information that we can not know from the code without the context, by example: if we have a temporary variable or an instance one.
With the best AST node for the selection we have the available suggestions and with the semantic analysis we go one level deeper for the variables getting to know the nature: temporary, instance or class to refine the suggestions.
If you want to define a new suggestion you only have to annotate a method with the scope where you would like to offer the suggestion and return an object respecting the SugsSuggestions API.
For returning the object you can extend SugsSuggestion and redefine the desired methods, you can see various examples with the current implementation in the SmartSuggestions-Suggestion package.
Also you can instantiate a generic suggestion: with SugsSuggestion class>>#for:named:icon: where the first parameter is a block with the action to execute and receives a valid SugsAbstractContext.
The available scopes are:
Here we have an example defining a Dummy action in
DummyAction class >>
newFormattingSuggestion <globalCommand> ^ SugsSuggestion for: [ :context | context formatSourceCode ] named: 'My global formater' icon: Smalltalk ui icons smallFindIcon
Sometimes while browsing code we think in programming terms instead of text, for example we think in a messageSend or a statement instead of word, spaces or symbols.
The idea is to use context information and let the programmer navigate the code thinking in those terms.
In order to do this we find the best ast node through the RBParser and we offer navigations in different directions:
– Parent: The node that contains the selected one. For example if we have the code ‘aNumber between: anotherNumber’ and we are selecting the variable anotherNumber if we navigate to the parent we will go to the message send.
– Sibling: The node in the same level as the selected node. For example in a temporary variables definition: ‘| one two three |’if we are in the variable one we can go to the siblings two or three.
– Child: Node contained by the selected node. For example if we are in a message send: ‘aNumber between: anotherNumber’ we will go the parameter anotherNumber.
It’s very easy to see how the Suggestions + Navigation work together, and how with not too much effort you can improve a lot.
If you want to activate the Node navigation using Command (or Control if unix) + Arrows go to the System Settings and activate: AST Navigation.
We want to color the syntax we are writing having the most information possible, in order to be able of select the scope where we are or show information associated to that piece of code.
In order to do that we use the AST and the semantic analysis (we need the semantic analysis because we want to show different kinds of variables with different colors, like undeclared variables), through the RBParser (parseFaultyMethod:/parseFaultyExpression:) to obtain the AST representation. The implementation it’s very simple because we can define a new Visitor to which we delegate the coloring algorithm and once we define the coloring from each syntax representation we just visit the tree: ast acceptVisitor: “visitorImplmentation”.
To enable the syntax coloring activate in System Settings: Enable AST based coloring.
Include a representation modeling the arguments and the message as 1st class object. About this point I have written a whole blog post.
After working with the AST I can say that is a very powerful tool and also is very easy to use. The visitor model is great and very flexible. But we must be careful because we can easily add too many responsibilities there and complicate the design.
Changing the AST structure has an obvious impact in the visitor implementors, and in the current Pharo implementation this can be complicated because we have some interesting users of the AST (like the new Compiler), and if we make a mistake it is very easy to break the image, so if we are going to do some experiments with this it is better to change the compiler first.
Obtaining an AST representation is not so hard to do when we have valid code, but when we are trying to find the best representation for a faulty expression things get nasty, and if we want to colour the text while we are writing we need this feature.
But the main point of everything is the re-affirmation that Pharo is a great tool fordoing experiments, the power of a live environment and reflectivity give us an edge over other environments.
With not so much effort we can implement some tools that improve a lot the programming experience a lot, and everyone can do this!