Monday, February 22, 2010

Lift Textmate bundle now with primitive code-completion

It's now been two weeks since I said I would have developed further on the Lift TextMate bundle in one week so I though I'd better start writing ;)

Last time I said I would implement the following features

  1. Primitive autocompletion for methods of the current objet. This is only realistic when the type of the object has been explicitly stated in the document
  2. Snippet output
  3. Images in the drop-down list that indicates if it's a function, object or a class

And so far I've managed to get #1 and #2 done, I'm fairly happy with the result even though it's not that clever when it comes to working out the type of a variable - I'll be teaching it a thing or two before the next release

What is planned for the future:

  • Improving the type interference (it's extremely lightweight now)
  • Teach it about packages to help your organize/add/remove imports

NB: Ctags was created to help the developer navigate source files and as such it doesn't record each function in each Class/Object/Trait but instead each function in each of the files it analyzes. This means that you might get some weird suggestion if there's more than one class in each file. But don't worry the compiler won't allow you to do anything illegal :)

I'll be rolling out my own static analyzer when I get the time (hopefully soon) to address this issue

Requirements

  • If you don't have it already install ctags
  • Add the contents of this file to your .ctags file. If you don't have one already simple place this one in your home folder (~/)

How to get started

  • Download the bundle here
  • Add the following shell variables to TextMate LIFTWEB_PATH - point it to the lift source code for me this is /Users/Mads/Dev/liftweb/framework/. If you don't have it download it here and SCALA_LIBRARY_PATH - point it to the scala library source code for me this is /Users/Mads/Dev/scala-2.7.7.final-sources/src/library/ you can download it here
  • Create the ctags file by pressing cmd+shift+c
  • Code, code, code - try hitting option+escape every once in a while to see if it works

As stated above it's not very good at figuring out the type of a variable yet - in fact it only knows what type to look for when you explicitly state it like so:

val myList :Array[String] = "H,e,l,l,o".split(",")

This is not very convienient as it forces you to drop type-interference, however, if you want to get a list of the methods List has you can simply type 'List.' and hit option+escape

Remember this is still extremely beta - It works for me but I can't promise it will work for you. If you find any issues feel free to fork me on github and help me out :)

Tuesday, February 9, 2010

The making of http://www.robocatapps.com/stickers

I recently coded a small one-pager for Robocat Apps and thought it would be fun to record the process. Slicing out the .psd file and coding the markup/css took about 1 hour and 50 minutes from scratch. The movie doesn't show the initial slicing of the photoshop file because frankly, that's now very interesting to look at ;)

I'm using:

  • Textmate for editing
  • the compass framework which has a nice list of mixins to use and to do continuous compilation of the sass code to css
  • chrome (webkit) to test the site

Check it out:

Monday, February 8, 2010

Class-level autocompletion for Lift-based projects in Textmate

I'm currently working on a bundle for Textmate that brings autocompletion for Lift-based scala projects to Textmate. It's very very primitive right now - It only knows how to complete partial typed classes and the ouput isn't snippets so you can't use tab to jump through the different arguments.

I'm planning to implement the following the next week or so:

  1. Primitive autocompletion for methods of the current objet. This is only realistic when the type of the object has been explicitly stated in the document
  2. Snippet output
  3. Images in the drop-down list that indicates if it's a function, object or a class

I'm using:

  • ctags to index the classes, methods, objects etc. in every file in the lift project
  • grep to find the lines in the ctags file that are relevant
  • 34 lines of ruby to match the class names and arguments from the lines returned by grep

Here's a small demo of it so far:

Sunday, February 7, 2010

Creating Textmate bundles

I know a lot of people are anticipating the release of Textmate 2 and I can understand why. The current version of Textmate is awesome, so awesome that it's hard to image that whatever Alan Oddegaard and XXXX has spent the last three years on will be nothing short of mind blowing.

But to be honest I'm quite happy with Textmate 1.5.9 and ... I checked out the top 20 list of requested features and on top 5 was "Code completion for language «xyz»" - I'm sorry to say it but I doubt Textmate 2 will ship with code completion support for your favorite language. So what to do then? Just dump Textmate (your favorite editor) all-together and use a bloated IDE? Don't be silly, you implement it yourself of course! This post will guide you through the process and in the end you should have a good idea of what to do next to get code completion for favorite language in Textmate

If you're about to leave this post I'm sorry but you don't deserve code-completion - If you're not ready to do a little bit scripting you don't want it enough.

The following is a small list of tools we'll use to make it a lot easier to implement

  • Ctags
    Exuberant Ctags is a multilanguage implementation of Ctags which supports 41 different programming languages. We use it to recursively scan a folder of source files and output a list with a line for each class, method, interface <<whatever programming constructs your language uses>>
  • Dialog2
    This is a ruby wrapper that is bundles with Textmate it's what we'll use to display the drop-down list of potential completions
  • grep
    grep (global / regular expression / print) from wikipedia: The grep command searches files or standard input globally for lines matching a given regular expression, and prints them to the program's standard output.
  • small amount of ruby scripting
    If you don't know ruby then don't worry, I don't really know it either xxx link to _why xxx should be enough to get your started (It's quite fun!)

Just to convince you that I'm not all talk and no action, here's a small video clip of the auto-completion i implemented for scala/lift

Okay, great! Lets get started!

First, create the ctags file

I hope you enjoyed reading this, if you have any questions or implemented code completion for a language, please post a comment below

Thursday, February 4, 2010

View-first templating in Lift

EDIT: I've been informed that there's an article on github that coveres the same subject (go to 'Advanced Binding')

I'm currently re-writing a project from PHP to Scala using the Lift framework. I'm very new to both Scala and the Lift Framework, but I'm enjoying working with it none the less. I'm slowly getting used to the 'View-First' approach that the Lift framework uses but the other day I got stuck when I needed to iterate over a collection of data.
Normally I simply inject a few lines of inline code in the template to iterate over the collection, but since you can't have logic in a template in Lift I was stuck. After searching through the lift google group and playing around with a bit of code I worked out one way to do it and I thought I would share it here. I won't be explaining how the Scala code works, I'll just explain my approach

Controller-first vs. View-first approach

Before going further it might be wise to summarize the two approaches:

Controller first approach This is the approach I'm used to. The controller fetches and prepares the data I want to display and makes it available to the template. The template is usually xhtml/html with inline code whenever it's necessary to iterate over a collection of data or inject some of the data prepared by the controller.

View first approach This is the templating approach used in Lift and it's named 'View-first' because it starts with the xhtml and the controller (it's called Snippet in Lift lingo) can then fill in the data in the desired places.

EDIT: I don't view snippets as controllers at all. Comparing to Spring MVC for instance AFAIK controllers are the ones that process the requests and generate data that will be rendered (in MVC way). Here snippets per say never process requests although they typically bind user's function that in turn could be compared with controllers. Snippets just render dynamic markup. Comment by Marius Danciu

For a more in-depth description of the 'view-first' approach take a look at the article on the lift wiki

The template (view)
<lift:surround with="default" at="content"> 
    <h2>Welcome to your project!</h2> 
    <p>
      <lift:helloWorld.howdy>
        <p> Welcome to helloworld at <howdy:date /> </p> 
      </lift:helloWorld.howdy>
    </p> 
</lift:surround>

This is prettty cool, the template is pure xhtml, no inline code or other nasties.

The snippet (controller)
class HelloWorld { 
  def howdy(in :NodeSeq) :NodeSeq {
    bind("howdy", in, "date" -> Text(new java.util.Date))
  } 
}

Pretty simple, however, on the projects I work on the pages are rarely this simple. I normally have to interate over a few collections etc. so lets look at a bit more elaborate example

The more ellaborate template
<lift:surround with="default" at="content">
<h2>Welcome to your project!</h2>
 <lift:helloWorld.howdyAll>
  <p> Welcome to helloworld at <howdy:date /> </p>  
  <ul>
   <howdy:users>
    <li><user:name /></li>
   </howdy:users>
  </ul>
 </lift:helloWorld.howdyAll>
</lift:surround>
A bit more advanced snippet
class HelloWorld {

  def howdyAll(in: NodeSeq) :NodeSeq = {
    // list of users
    val userList :List[String] = "Arthur Dent" :: "Ford Prefect" :: "Zaphod Beeblebrox" :: Nil

    // extract the tag <howdy:users> and all of it's children
    val usersTemplate :NodeSeq = template(in, "howdy", "users") match {
      case Full(xhtml) => xhtml
      case Empty => <strong>Error</strong>
      case Failure(msg, _, _) => <strong>Error</strong>
    }

    // loop through each user and bind the values
    val users :NodeSeq =
      userList.flatMap { name :String =>
        bind("user", usersTemplate, "name" -> Text(name))
        }

    // now bind the tags <howdy:date> and <howdy:users>
    val date = new java.util.Date
    bind("howdy", in, "date" -> Text(date.toString),"users" -> users)
    }
}

Nice and simply, hope it will help someone else new to Lift