Smalltalkers always claim that Smalltalk has invented the mouse and the User Interface. Even if that can be discussed, I have to say it: I HATE THE MOUSE. The less I can use it, the best. The more I can stay with the keyboard, the best. Besides I am Smalltalk lover, I always try to admit when Smalltalk is not the best in certain aspect. And at least in Pharo, the need of the mouse is too much.
I have worked in industry with Eclipse for 3 years. And I am a geek who stays hours in front of the PC and when I was in Linux/Windows I was using all the time Total Commander or something similar. I used to know all possible shortcuts of all the software I was using in my machine. When I then started to program in Squeak it has like if I come back 20 years in time. I was not able to use the keyboard as I was doing it in the rest of my machine (and Eclipse included). It felt really frustrating. But ok…Smalltalk showed me so many cool stuff that I could live with the lack of proper shortcuts.
Today, I can say Pharo has improved a little in the direction of improving the user experience with the keyboard. This is thanks to Guillermo Pollito who has been developing Keymapping for a while. And now Débora Fortini has joined. Keymapping is a really small tool that let you define keyboard shortcuts per morph. So you can take any subclass of Morph and define your OWN shortcuts to do what you want. Of course, it may happen that there are conflicts with shortcuts of other morphs or even with global shortcuts of your Operating System. Nevertheless, the framework is there and working.
So….why keymapping rocks? Well, during Smalltalks I say to Guillermo “If you give me shortcuts to step into, step over, restart, proceed, and go up and down in the Debugger, I pay you several beers”. After 5 minutes, they were already working perfectly. What was needed? First just create the class:
Object subclass: #KMDebuggerShortcuts instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Keymapping-Debugger'
And then put a class side method :
buildDebuggerShortcutsOn: aBuilder <keymap> (aBuilder shortcut: #stackDown) category: #DebuggerShortcuts default: $d command shift, Character arrowDown do: [ :target | target down ]. (aBuilder shortcut: #stackUp) category: #DebuggerShortcuts default: $d command shift, Character arrowUp do: [ :target | target up ]. (aBuilder shortcut: #proceedDebugger) category: #DebuggerShortcuts default: $d command shift, $p do: [ :target | target proceed ]. (aBuilder shortcut: #restartDebugger) category: #DebuggerShortcuts default: $d command shift, $r do: [ :target | target restart ]. (aBuilder shortcut: #stepIntoDebugger) category: #DebuggerShortcuts default: $d command shift, $i do: [ :target | target send ]. (aBuilder shortcut: #stepOverDebugger) category: #DebuggerShortcuts default: $d command shift, $o do: [ :target | target doStep ]. (aBuilder shortcut: #stepIntoBlockDebugger) category: #DebuggerShortcuts default: $d command shift, $t do: [ :target | target stepIntoBlock ]. (aBuilder shortcut: #SeeFullStackDebugger) category: #DebuggerShortcuts default: $d command shift, $f do: [ :target | target fullStack ]. (aBuilder shortcut: #runHereDebugger) category: #DebuggerShortcuts default: $d command shift, $h do: [ :target | target runToSelection ].
All shortcuts are defined in the same way: you create a method which receives a builder. The method #shortcut receives a shortcut name and creates an instance of KMKeymapBuilder. Then, sending the message #category:default:do: to the created KMKeymapBuilder, you can define all shortcuts. The first parameter is a category. The second is the combination of keys for the shortcut and the last parameter is the block to evaluate when such shortcut is pressed. Notice that the block receives at least the target object. In this case the target is the debugger.
Now, the last part is to tell the Debugger that it should attach those shortcuts. To do that, we had to overwrite (for example) the method Debugger >> #openFullMorphicLabel:
openFullMorphicLabel: aLabelString "Open a full morphic debugger with the given label" | window | window := UIManager default openDebugger: self fullMorphicLabel: aLabelString. window attachKeymapCategory: #DebuggerShortcuts targetting: self. ^window
Notice in this method the line number 6. So…after 5 minutes that took Guille to code that, I had the shortcuts I ever wanted.
What is important of Keymapping is the infrastructure since it give us the tool to create our own shortcuts. Or even better, integrate in Pharo new shortcuts. Just to give you an idea, KMMonticelloShortcuts has shortcuts for committing, open repository, view changes, etc. KMGlobalShortcuts has shortcuts for saving the image, to open the world menu, to open a workspace, to open a transcript, to open Monticello, to open a class browser, etc… So…come on!! what are you waiting to give it a try?
How to install it? As easy as always:
Gofer new squeaksource: 'ShortWays'; package: 'ConfigurationOfKeymapping'; load. (ConfigurationOfKeymapping project version: #bleedingEdge) load
Notice that Keymapping has an integration with the Settings framework. So if you open the Setting Browser and go to the category “Keymapping” you will see some of the shortcuts and of course you will be able to modify them. Note that not all shortcuts are yet integrated to the Settings framework since Keymapping is still in development 🙂
What I would like in the future
- The VM so far cannot handle some key events of certain keys like functions. Moreover, it seems (but I am not sure) that different VMs (the Windows VM, Linux, Mac OSX) do not handle all the key events the same way. It would be nice if they can be uniform.
- In Pharo, when you do right-click or whatever menu that show you options like “do-it”, “print it”, etc., they all show the shortcut for them. However, if you do change them with KeyMappings, then such menu is not updated. It would be nice if the menu is auto-generated depending on the current configured shortcut.
- It would be nice to have a way of tracking conflicts between shortcuts.