Tuesday, March 30, 2010

IDE tip: AST-based Selection

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


AST-based selection is a feature which lets you select the surrounding "block", based not on syntactic clues (such as braces and parentheses, as is offered by practically all editors), but based on the parse tree created by the compiler (or the "AST", for abstract syntax tree).

In NetBeans, there are two complementary actions to deal with AST selection: Select Surrounding and its reverse. They have other names which I can never remember, but the keybindings are as follows:

Select surrounding block


  • Mac: Ctrl-Shift-. (period)
  • Windows/Linux/Solaris/Others: Alt-Shift-.

Reverse:

  • Mac: Ctrl-Shift-, (comma)
  • Windows/Linux/Solaris/Others: Alt-Shift-,


On my U.S. keyboard, the comma and period keys are marked with "<" and ">" for the shift key which is somewhat reminiscent of blocks.

These two actions cooperate and are stateful: they remember the original caret location such that you can "go back".
Here's how it works. You want to select something surrounding the caret, say the whole method. In that case you just
hit the surrounding action. With your fingers holding ctrl and shift you hit dot -- once, twice, bam bam bam. If you do it
quickly you might "overshoot" and go one too far and select say the whole class instead of just the method. If so just press
comma and voila, it goes back one step down the selection hierarchy you just came up.

This makes this a very quick way to select code blocks -- no more need to grab the mouse and try to hit exactly the selection
boundaries between characters.

Here's an example of where this comes in useful. I sometimes remove long expressions from method call arguments by pulling it
out as a separate variable. Here's my call, and I want to extract the whole Math.min expression.
First I move the caret anywhere within that expression:


Then I press Alt-Shift-. (or Ctrl-Shift-. on Mac):


One more time:


Let's pretend I was doing it quickly and hit it one extra time:


That's too far, so I press comma (with the modifier keys still pressed):


In practice (and with practice) this is done very quickly, faster than I can
accurately hit both ends of the expression with a mouse drag. And to finish
out the example, let me show a couple of more things related to the task.
Notice how there's a light bulb in the left hand side margin of the editor.
That tells me there are associated quick fixes. I press Alt Enter,
and the quick fix dialog is shown:


I choose "Introduce Variable" and press Return. This gives
a dialog where I can choose the variable name (and whether to replace all occurrences
when applicable). It suggests "min" since that's the function
name of the expression we're extracting.


Just press Return to apply the refactoring, and we've pulled out the argument
expression:


I just pressed return and got the default name. Then I realize that this may
not be a great name. What is that last parameter again? I can place the caret
somewhere within the last argument and press Cmd-P (or Ctrl+P
on other platforms) to get NetBeans to "Show Method Parameters".


Let's rename it to length instead. Press Ctrl-R (all platforms) to
apply rename refactoring, which (because this is a local or private variable)
will have "instant rename" applied where your edits are applied immediately
for every keystroke:


Just type the new name - notice that all the boxed expressions are edited synchronously.


Using AST-based selection to identify code blocks to extract as variables or methods
is useful, but it can be used in other ways as well. Let's say I want to add
a second validation step to this function. First I select the code block using
AST selection:



NetBeans has a keyboard command for duplicating the current line: Ctrl-Shift-Down
(and on Mac, it's Cmd-Shift-Down.) Note that when you have a selection, this
duplicates the entire selection. So all we have to do now is apply the duplicate command
and we've got a second if clause we can just tweak to whatever it is it needs to do.



AST based selection should be available in nearly all editor types in NetBeans.
In Ruby, JavaScript, Python, etc it also handles documentation
blocks well - so if you apply select surrounding in say a Ruby comment, it will first select
the current line, then the whole documentation block, then outwards in the AST hierarchy.

Hope you find this helpful. If there are Eclipse or IntelliJ readers who know corresponding
keybindings in Eclipse, feel free to leave a comment so others can find it easily.



Thursday, March 25, 2010

IDE tips

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


One of the sessions we had at the Roundup was one entitled "IDE tips and tricks". We basically sat there with our laptops and did a show & tell of various "hidden" IDE features we knew about to help productivity. There were Eclipse, IntelliJ and NetBeans users there - and even some advocacy for Vim :) We discovered pretty quickly that this would not make for compelling listening, so we just turned off the recorder.



One thing I discovered (and which I saw during our languages coding dojo day as well) is that a lot of people don't use many of the productivity boosters. Therefore, I thought I'd bring some of these up here. Since I've been intending to start blogging more again, I'll make this a regular "column".



To kick things off, I'll just point to an old blog entry which is still relevant: Hippie Completion. Learn to use Ctrl-K while editing, it will be worth your while. (Eclipse has this too as of Eclipse 3.1; I believe it's bound to Alt-/).


Transparent windows on Linux

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


JavaFX makes it easy to create non-rectangular windows -- just set a StageStyle.TRANSPARENT on your stage.style.
We use rounded corners on our popup menus, and we also have a non-rectangular and alpha-blended splash screen.

The other day I was checking how the cursors were looking on different platforms, and imagine my horror when I discovered how our new splash screen and rounded menus looked on Linux! Here they are -- as you can see you end up with white rectangles around the supposed-to-be-blended areas:





That looks.... craptastic!

Historically, the JDK didn't support alpha blended windows on Linux. However, that was added a while ago (I'm not sure exactly which version, but I think it was JDK 6 update 14). It turns out that the FX code which initializes the native frame for the stage does not do conditional checking for this; it simply turns off transparency on Linux. Fortunately, there's a System property you can set to force it to respect the transparency flag. You would obviously only do this if you know you are running on a JDK which supports transparency. And that's easy!

In your startup code, do something like this:


if (Utils.IS_LINUX and Utils.jdkAtLeast(1, 6, 0, 14)) {
java.lang.System.setProperty("javafx.allowTransparentStage", "true");
}

There are a couple of utility methods here that are simple - just looking at some system properties to determine whether we're on Linux and whether we're on a particular version of the JDK or higher (if you run on non-Sun/Oracle JDKs you may want to check for that as well).


public def IS_LINUX = osName.contains("linux");
public def IS_MAC = osName.contains("mac");
public def IS_WINDOWS = osName.contains("windows");
public def IS_SOLARIS = osName.contains("solaris");

/**
* Is the version of the running JDK at least major.minor.micro_update?
* In 1.6.0_18 macro=1,minor=6,micro=0,update=18
*/
public function jdkAtLeast(macro: Integer, minor: Integer, micro: Integer, update: Integer): Boolean {
def runtimeVersion = java.lang.System.getProperty("java.runtime.version");
def pattern = java.util.regex.Pattern.compile("^(\\d)\\.(\\d)\\.(\\d)_(\\d+)-");
def matcher = pattern.matcher(runtimeVersion);
if (matcher.find()) {
def currentMacro = Integer.valueOf(matcher.group(1));
def currentMinor = Integer.valueOf(matcher.group(2));
def currentMicro = Integer.valueOf(matcher.group(3));
def currentUpdate = Integer.valueOf(matcher.group(4));
if (currentMacro < macro or currentMinor < minor or
currentMicro < micro or currentUpdate < update) {
return false;
}
}

true
}

With that modification at startup, we get much nicer results:





P.S. Yes, there's a JIRA issue tracking this to be automatically done by the platform, RT-4797.

P.S.2. This is for desktop; I'm not sure this code is mobile-safe.


Wednesday, March 24, 2010

300 Episodes

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


We had a milestone in the podcast last week: 300 episodes. That's more than one episode per week for the 4½ years we've been doing it!
We spent our 300th episode as a retrospective, looking back at highlights over the years.



We looked up some statistics, too. The most amazing one (to us) is that we are now up to 15,000 downloads per episode! Our feedback alias has over 10,000 e-mails (not counting spam). And our discussion forum has nearly 20,000 messages at this point! Wow! Thanks for listening and participating!






(Photo by Gunnar Hillert)


Thursday, March 18, 2010

Java Posse Roundup

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


I've spending the week in beautiful Crested Butte, Colorado at the Java Posse Roundup. It's the fourth year in a row, with record attendance. And as always, it's been fantastic. It's hard to explain an Open Space conference - it doesn't sound like it would work, and I was personally surprised to see how well it worked when I attended the first time. Now I simply expect it, and it always delivers. One key reason why this open space conference works so well compared to say "unconference" events attached to major face-forward conferences is that you need to be away from everything and really immerse yourself - and here up in the Rockies we certainly are isolated!



The day before the conference started we had a "Languages Dojo" day where people pick different languages they're interested and go off building something. We had planned in advance to build a JavaFX version of the "Ohm Writer", a Zen-like text editor that is full screen, has nice relaxing background music, background sounds and typing sounds. We had a great time, and made a lot of progress. Unfortunately, we spent a lot of the day fighting with git (the version control system). I'm a very happy Mercurial user, but I've had a little bit of git-envy since I know it can combine local changesets into a single changeset to be pushed to the repository. I could see myself using that a lot. And setting things up on github for collaboration was very easy. But that's where the fun ended - nothing worked, simple merges aborted, error messages were completely unhelpful, and in general we repeatedly ended up checking out new clean workspaces and hand applying changes. I liked Mercurial before but now I appreciate it even more. (I hear the guys who were doing functional programming also were ripping their hair out with git. P.S. Joel Spolsky just posted a Mercurial tutorial).



The app is functional, and more importantly we made the editor start up immediately, load images and audio in the background and gradually fade in the image as soon as it's available. We also had difficulty playing the keyboard "click" sound until we realized you don't want to just repeatedly call play() on a media player -- you have to reset it -- either setting mediaPlayer.currentTime = 0s or calling mediaPlayer.playFromStart().



We also ran into another bug -- and this is a gotcha I've seen before, so it seems useful to highlight it here: For JavaFX Strings, null and "" are the same! Therefore, you don't want to write code like this:


while ((line = reader.readLine()) != null) {
// use line
}

because this will terminate the loop as soon as you reach an empty line!! Be very careful about checking for nulls explicitly when dealing with Strings.



We've also had Lightning Talks in the evenings. If you're not familiar with Lightning Talks, these are very quick presentations, one after the other, on any subject, but limited to 5 minutes. Yes, with a HARD 5 minute limit. The advantage of limiting the lightning talks to 5 minutes is that it forces the presenter to really focus on the interesting parts of the subject, and if it's really not interesting, at least you're only bored for a couple of minutes! The topics this year were really great though - there was even a fire-eating demonstration!
One way to keep track of the time is to use a countdown timer. We wrote one in JavaFX last year, which displays a classic countdown timer (imported Photoshop graphics) and a sound file playing as the timer is expiring. It's available as a webstart app if you want to run it for your own lightning talks, or you can study the code.




In addition to the morning open space conference sessions, the evening lightning talks, the progressive dinners, we've had some afternoon technical presentations and coding sessions -- check out our improvised projector screen in our living room:



Monday, March 1, 2010

Using Mercurial over ssh without typing the password

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


We're using Mercurial. Our release engineering servers run web servers, so we can browse our repositories, just like the public NetBeans ones at http://hg.netbeans.org, and pull down new changesets anonymously. However, for authentication purposes, we also use ssh, so all pushes to the repository has to go through ssh.


$ cat .hg/hgrc
[paths]
default = http://our.server.sun.com/our/repository
default-push = ssh://our.server.sun.com//our/repository

(P.S. Notice how there are 2 slashes in the SSH path and only one in the http path - if you forget about that Bad Stuff(tm) happens.)



This means that whenever I pull (or determine incoming changes via hg incoming) it executes immediately, but whenever I want to push (or determine outgoing changes), I need to supply a password. And let's just say typing my password is not easy, since the password requirements at Sun (and shortly, Oracle) are really strict - no nice, short and simple passwords here!



I've been putting up with it for a year now - after all, it's just a couple of seconds here and a couple of seconds there - but I knew it should be possible to fix this, since back in my hardcore Solaris days I had all this stuff configured correctly via the ssh key agent so that I could ssh from one account to the next. On the other hand, I've googled it (mercurial + ssh) a couple of times, and the information I've found has always been for doing more complicated things (1,2) than the simple authentication setup I wanted. So I just put it off.



I decided to bite the bullet and look into configuring it - and it was really trivial. I'm bummed I haven't tried earlier! I thought I'd write this up in case it helps anyone else in a similar situation.



The reason it's trivial, is that it turns out there is nothing specific about using Mercurial here. This is actually a case where Googling something was harmful! If I had just tried it, instead of searching for a recipe and getting confused, I would have had this set up a long time ago! Hopefully this blog entry will help anyone searching for "hg ssh passwords" ! Anyway... You just need to ensure that you can ssh directly into the system you are trying to push to, and if you can do that, then mercurial can do the rest. And this setup is easy and well documented.



First, you need to generate a local key.


$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/tor/.ssh/id_rsa):
Created directory '/Users/tor/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/tor/.ssh/id_rsa.
Your public key has been saved in /Users/tor/.ssh/id_rsa.pub.
The key fingerprint is:
... (omitted) ...

Next you need to copy this file to the server and call it ~/.ssh/authorized_keys. Actually, that file may already exist and you really want to append to it, not replace it. So first ensure the directory exists:

$ ssh tor@our.server.sun.com mkdir -p .ssh
Password:

And finally copy your authentication key over to the server:

$ scp .ssh/id_rsa.pub tor@our.server.sun.com:.ssh/authorized_keys
Password:
id_rsa.pub 100% 422 0.4KB/s 00:00

That's it! Now try logging in again:

$ ssh tor@our.server.sun.com

On my Mac, this actually pops up the system authentication dialog:



Not only can I enter my password in the dialog, but I can tell it to remember this key in the keychain, and from now on, the system supplies the password to ssh when it wants to log in to hosts.

$ ssh tor@our.server.sun.com
Identity added: details omitted
Last login: Mon Mar 1 19:22:18 2010



And now, the whole point of this exercise -- I can run "hg out" and "hg push" (as well as scripts which operate over multiple mercurial repositories) without the need to type that annoying password again. On the other hand, how will I remember it now that I'm not repeating it like a mantra dozens of times per day?


$ hg out
comparing with ssh://our.server.sun.com//our/repository
searching for changes
no changes found



P.S. Here's a copy of my own authentication keys in case that helps you configure your own system. Please don't use these to log into our system.