All this stuff is filed under "software"

Snippets.sugar for Espresso

I’ve always been intrigued by the concept of a program for storing code snippets, but I’ve never found one that I could put to any practical use. I inevitably would end up storing a few snippets in it, and then abandoning it for my text editor’s built-in snippet management because it introduced too many extra steps when it came to inserting snippets as I code. External snippets managers had some very real benefits, but none that outweighed the usefulness of tab completions and tab stops.

Fortunately, I think I finally have a snippet manager that will work for me. Snippets.app 1.1.1 offers a unique new feature: a bridge API that allows third party applications to invoke the Snippets search panel or global menu and then do whatever they need to do with the user’s selected snippet. While they were working on the feature, Lucky Ants asked me if I’d like to help them show it off, so I collaborated with them to develop the Snippets.sugar for Espresso (download it here).

In case you don’t have a clear idea of why this is awesome, here’s what you can do with it:

  1. Once the Sugar is installed, choose Actions→Snippets.app→Insert Snippet (control-S) while editing a document in Espresso to jump to the Snippets search panel (or global menu)
  2. Find the snippet you want to insert, and hit enter
  3. The snippet will be inserted into Espresso, and any placeholders in the snippet will be converted into tab stops

It’s that last feature that vastly improves Snippets.app’s usefulness. Previously, snippets managers were basically just cut and paste. With Snippets.sugar’s integration into the core application’s APIs, though, you can leverage the full power of Espresso’s text snippet syntax.

That’s fun, but it gets better:

  • Your snippet can contain Snippets.app-style named placeholders or Espresso/Textmate-style numbered placeholders and they’ll automatically be converted into tab stops
  • Your snippet can contain Espresso or Textmate special variables like $EDITOR_PATH or $TM_SELECTED_TEXT, allowing your snippet to interact with your selected code, prefill the path to the document, and so forth
  • Because it’s an Espresso snippet, you can also use interpolated shell code, placeholder mirrors and transformations, etc.
  • When inserting a snippet that doesn’t contain any placeholders, it will be inserted as plain text, so you won’t have to worry about losing special characters like $ that sometimes get messed up in Espresso snippets

Additionally, you can move in the other direction. Just select some text in Espresso and choose Actions→Snippets.app→New Snippet (control-shift-S) to create a new snippet in Snippets.app using the selected text.

To choose whether to use the search panel or the global menu, open up the Espresso preferences, switch to the Advanced pane, and make sure that Snippets.sugar is selected in the dropdown at the top. There you’ll be able to choose what interface you like, force the Sugar to always use a particular type of snippet logic (if that’s necessary for some reason), and set some defaults for when you’re creating new snippets:

Snippets.sugar Preferences

Although this is a fantastic feature, you’ll still need to weigh up some pros and cons before you jump straight into it. On the positive side, it’s now feasible with Snippets.app to have a single, well-organized snippet library that includes advanced tab stopped snippets and can be shared across multiple text editors (for instance, this would make it a breeze to migrate a Textmate snippet collection for use with Espresso). Thanks to Snippets.app’s synchronization capabilities, you could also use your snippets collection across multiple computers far more easily than when Espresso’s snippet management features.

The main downside is that although the Snippets.sugar integrates Espreso and Snippets.app far more closely than before, it still is not a seamless integration because Espresso doesn’t know anything about the snippets until you select them. This means that you can’t use tab completions to trigger snippets, so the process of selecting the snippet you want to use is still relatively time consuming compared to using native snippets.

For more information about the other goodies available in Snippets.app 1.1.1, see the 1.1.1 blog announcement. I hope you enjoy Snippets.sugar!

TouchNote is now TapNote

Although I hadn’t expected to have an issue with TouchNote.com, they evidently didn’t like my use of the word “TouchNote” (despite using it for a completely different purpose on a platform they don’t appear to have any interest in) and rather than ask nicely had their lawyers threaten me with a trademark lawsuit.

Thanks, guys. It’s always a pleasure to interact with people who skip that whole tedious friendly request stage and go straight to threats.

In any case, I don’t have the resources for a legal battle (and would not have used the name in the first place if I thought they’d have a problem with it), so my app is now named TapNote.

The name change is going to take a couple of days to percolate through Palm’s system, but TapNote v1.1 should be in the app catalog probably Friday or Monday, depending on Palm’s turnaround.

The pricing of TapNote

Once I’d finished up the basics for TapNote 1.0, I faced the difficult task of choosing a price for it. After much deliberation, I settled on an asking price of $4.00. This price appealed to me because it was in keeping with the pricing for the better-class note-taking apps available in the app catalog, but wasn’t a .99 price point (which I hate; I think they’re dishonest, and even if they perform better—something I doubt—I’d rather provide an upfront price to people rather than trying to trick them into rounding down in their head).

Then Palm approved TapNote, I excitedly posted about it here and on Twitter, and absolutely no one bought it. A week later, my Palm stats have a number of purchases I can count on one hand, and no one has cared enough to leave a star rating, much less a review of the program.

This is, to say the least, discouraging.

Since then I’ve been vacillating between leaving the price as-is and sharply cutting it. A fellow WebOS developer on Twitter recommend cutting the base price in half and reducing it further as a promotion to drive initial sales and hopefully get on the Hot Apps list. This was tempting, since the whole reason I released version 1.0 without synch was that I wanted to get it out in the window for Hot Apps.

However, I truly believe that TapNote is worth $4.00 (or more, to be honest; $4.00 won’t buy you a decent lunch, and what I’m planning to do with TapNote 1.x is way better than a decent lunch), but what I believe and what the market is willing to pay are two different things, so if I want to sell it I’m going to need to conform to the market’s expectations.

After giving this lots of thought, waiting to see specific numbers in Palm’s download reports, and sleeping on the problem, I’ve made up my mind: I’m leaving the price alone. People can either cough up four bucks if they, like myself, aren’t happy with the existing options for note-taking, or they can settle for one of the existing solutions. I’m unlikely to sell many copies up front, and unless a high profile blogger or review site notices TapNote and gives me some favorable press there’s no way I’ll even make it into the lower tier of Hot Apps, but I’m alright with that.

First off, I don’t need the money. I created TapNote because I badly needed it, and although I’d like a little recompense for all the time and effort I’ve spent, I’ve already got a full time job that’s paying the mortgage. It makes me a little sad that other people don’t appreciate TapNote, but it won’t impact me negatively if TapNote remains in obscurity for a while (and the nonexistent support burden is definitely nice).

Secondly, the whole race to the bottom that continues to occur in app stores leaves a bad taste in my mouth. Developers complain about how they sink months into an app and then people won’t even give them the cost of a cup of coffee for it, yet they still slash prices down to nothing in an effort to sell. In the Apple app store, this is about the only option, of course, because the competition is so heavy it’s virtually impossible to get noticed. For the Palm app catalog, though, there’s relatively little competition. Just keeping an app on the “Recently Updated” pages with regular updates is enough to get a reasonable amount of exposure (based on things I’ve read in the developer forums).

So cutting the price when I don’t really need the sales feels to me like devaluing the app for no good purpose. Increasing prices for high quality apps has to start somewhere, and to be honest $4.00 is hardly a high premium. Based on my competition, a premium price would actually be up in the $7-10 range.

Additionally, I am highly skeptical that vastly reduced prices is actually profitable. Certainly it’s useful for getting an app into the public eye, but when I look at the apps in the $10,000 bracket of Hot Apps, very few of them cost less than $5 (and of those less than $5, most are at the $3-4 mark). Palm has been crowing about TweetMe’s meteoric rise up the charts, but I notice that it remains in the $1,000 bracket, while it’s higher-priced competitors are looking at a handy $10,000 bonus and a $4 notes app that was featured in the app catalog’s Feature Apps area has quietly jumped into the $10,000 bracket, as well.

All of which says to me that although cutting prices would probably get TapNote noticed, I would have to time things very carefully in order to not ultimately lose profits. Because Palm’s download reporting for paid apps is almost a week delayed (!!!) and any price change requires at least 2-3 days while they review it, it is virtually impossible for me to tweak the price lower and then raise it back up when its usefulness has run its course. If I’m going to lower it, I need to be content with it being lowered for a week at minimum and more like two weeks before I can accurately judge the effectiveness of the change.

None of which is worth it to me. I would rather continue as I have with a solid product and great updates and then see if some marketing efforts once I’ve integrated synchronization don’t give it a boost in popularity. That way, I lose nothing, ultimately will likely make more money than if I halved the price and got a $1,000 Hot Apps bonus for it, and I don’t devalue my app in a market that hasn’t yet figured out that a lowest-common-denominator price doesn’t often make for a good user experience.

WebOS first impressions and the making of TapNote

About two months ago, I switched to a Palm Pre Plus to replace my iPhone. As soon as I got home I of course started playing with my new toy, and it did not take me long at all to realize that although Palm has included an excellent suite of default software and there are some wonderful apps in the app store, I was desperately going to miss WriteRoom on the iPhone. There were several note-taking apps already out, but none of them offered the features I needed in an attractive package. Most, in point of fact, tended toward feature-bloat by my standards.

And so TapNote was born. I created TapNote because I badly needed it, and I released it as a paid app because I figured other people would be interested, too.

Getting into mobile app development using WebOS was an interesting journey, so I wanted to share some of the things that I discovered along the way, as well as discuss the specific design decisions that influenced TapNote’s development.

WebOS: a quick intro

It’s pretty obvious that developing for WebOS means using web technologies, but what exactly does that mean?

Right now, there are two basic types of WebOS apps and a third on the horizon:

  1. SDK apps are coded entirely in HTML, CSS, and Javascript and leverage Palm’s Mojo framework to interact with the system hardware.
  2. PDK apps are coded in C or C++ and interact directly with frameworks like SDL to display things on the screen and interact with the user. The 3D games that you’ll find in the app catalog are all PDK apps.

The third type of application is one that mixes the web tech SDK with compiled C code in the PDK to leverage the strengths of both. We haven’t seen any such apps yet, because Palm hasn’t provided an API for communicating back and forth between Javascript and C.

The WebOS SDK: not all sweetness and light

Developing for WebOS has been an intriguing mixture of simplicity and frustration. I am a front-end web developer by trade, so WebOS sounded perfect to me: no new languages to learn, no need to muck around with compiled code, and the ability to use my current development environment. Although Palm is pushing Eclipse for development, I was able to whip up an Espresso Sugar for WebOS development my first weekend and hit the ground running the next week.

However, WebOS is not the panacea that it sounds. Web developers get pretty hot under the collar when they hear you can make true applications using HTML and CSS, but here’s the truth: you can’t. Coding for WebOS is about Javascript. Yes, you can do a certain amount using CSS animations, but for the most part it’s Javascript, Javascript, Javascript. Javascript to set up the HTML and register it to be transformed into widgets, Javascript to handle interactions with users, Javascript to set the classes that trigger your CSS animations. If you want to develop for WebOS, you need to know Javascript.

It isn’t like this is unexpected, of course. HTML+CSS isn’t optimized for handling touch-based interactions, and there is no concept of URLs for typical apps which means the only way to get new content is via asynchronous Javascript. So although the reliance on Javascript is obvious in retrospect, I mention it because most of the web developers I know have a hazy grasp of Javascript or know only jQuery and I don’t wish this to come as a rude shock. It’s also worth mentioning because the Palm documentation is terrible if you’re a Javascript newcomer.

Thankfully, I am not, but I pity newcomers to the language. Palm’s documentation is all vanilla Javascript, and filled with references to object prototypes and so forth. Then you boot up their example apps and discover that rather than eat their own dog food, they’re using the Prototype framework’s Class object to create a nice classical-inheritance object flow. Had I not been well-versed in the concept of using classical inheritance alongside Javascript’s prototypal inheritance from my past work with Mootools, getting up and running would have been a major headache.

Additionally, although Palm claims to support the latest web technologies, they do not. There are two reasons for this:

First, they enforce an awful, non-semantic div soup markup style. If you want to use a Palm widget to speed things up (which you mostly will, particularly the list widget), you have to declare it using a div and it is then automatically filled with a plethora of other divs. To make matters worse, the Palm example projects rarely use any elements other than divs (even when the element is clearly a header, paragraph, etc.) and there are no reset styles in the CSS that’s lumped in with the Mojo framework so if you end up trying to code something vaguely semantic you’ll have to restyle every element. Clearly Palm’s markup was developed by people who have never worked as front-end developers, and it’s a crying shame because it makes development on WebOS for people who care about writing elegant markup quite frustrating. And yes, good markup matters. Aside from making accessibility on the platform easier down the road, simple semantic markup is easier to style, read, and modify, which directly impacts development time.

Second, WebOS is driven by Mobile Webkit. The good side of this is that you have access to a whole new world of CSS and, to a far lesser extent, HTML (without needing to worry about cross-browser debugging! Huzzah!). The bad is that Mobile Webkit’s support of CSS 3 and so forth is inconsistent at best. For instance, when I was creating TapNote’s editing interface, I wanted to fade out the header pill when text scrolled under it rather than attaching the header to the top of the page. This was super easy: I just added a CSS transition to fade the opacity of the element down.

But the text remained at full opacity. After a bunch of experimentation, web searches, and so forth I discovered that Mobile Webkit (or possibly just the version employed by Palm) can’t apply opacity to an element’s text for some unknown reason. Not so helpful. (Thankfully in this instance, there was a workaround: Palm’s Webkit does support rgba() colors, so with some extra styles I was able to make the whole thing fade.)

Don’t get me wrong: I love WebOS and I think it is hands-down the best mobile OS on the market for developers. However, that doesn’t mean that you shouldn’t go into it with eyes open. WebOS is a great idea, and Palm has done some truly visionary work, but it is by no means perfect.

Diving in

I’ve thought a lot about design over the course of TapNote’s initial development. When I was browsing through the app store for a note-taking app, what struck me wasn’t that the available options were bad, per se. They just weren’t designed to the standards I’ve come to expect having used Mac OS X and iPhone software for so long.

This doesn’t mean that they weren’t visually appealing. For some of them, far from it. However, aesthetically pleasing design is not the same as having a well-designed interface.

A great example of this is Palm’s own Memos app. It’s a perfect example of software kitsch: visually appealing, but ultimately useless and reliant on a cheesy metaphor that has no applicability to the medium. A corkboard covered in Post-Its is a worthless conceit on a device where screen real estate is at such a premium, since aside from color you can’t quickly distinguish between notes without opening them. Add to that the fact that the metaphor has no basis in reality (Post-Its won’t stick to corkboard) and you’ve got an app that did not receive terribly deep attention to its design even though someone with great aesthetic sense obviously put some love into the graphics.

In short, the Memos app looks great and feels terrible, which means that it wasn’t well designed. Truly good design requires thinking deeply about the visual appearance and how it interacts with the app’s functionality (among other things, like whether a given bit of functionality is even necessary). Too many of the note-taking apps that I tried seemed like they were focused on implementing a targeted feature list in a visually appealing way, without thinking enough how those features and interface really impact the user experience.

I did not want to rehash these problems with TapNote, so when I started designing it I decided there were only three things I couldn’t live without:

  • Plain text editing
  • A document-based layout, a la WriteRoom
  • Wireless synchronization with my Mac

(Only two of those three bullets made it into version 1.0; synchronization sadly didn’t make the cut for the initial release because I had a soft deadline of releasing the first version during the Hot Apps contest to ensure that this didn’t become one of those software products that never gets released at all.)

I drew some sketches to make sure that the ideas I was picturing were worth pursuing, and then since most of the app relies on Palm’s provided styling anyway, I started straight into development.

With a framework like WebOS, I think this was the right move. Were TapNote more visually complicated, I would have needed to create some detailed mockups, but in some ways usability matters even more than visuals for a mobile app, and you won’t know how usable your app is until it’s in your hand.

Simplicity versus obviousness

For TapNote, I decided the single most important thing to me was interface simplicity rather than making everything as obvious as possible. As a result, I avoided buttons for a lot of the functionality in favor of items in the app menu and contextual menus (which can be accessed by touching and holding an item).

In many ways, this is in keeping with WebOS’s core design, and is a major difference between WebOS and iPhone OS. On the iPhone, simplicity often comes second place to obviousness. A good example of this is the ubiquitous back button in iPhone apps. The back button takes makes for a more complex interface, but it is also dead obvious how to return to the previous screen. WebOS, on the other hand, provides a universal back gesture that works in every app, obviating the need for the visual clutter of a back button. This increases the visual simplicity of apps, but increases the initial conceptual complexity because users have to learn the back gesture.

Both approaches have their strengths and weaknesses, but deciding which approach to take is a core design decision for any mobile app, particularly on WebOS (you don’t have as much choice on the iPhone, of course, thanks to the lack of support for back gestures or standardized app menus).

An example of how I followed this design path for TapNote is the “email document” function. I added this to the app when it became obvious that synching wasn’t going to make it for version 1.0, and the most obvious way to do it would have been to put a button with an icon at the bottom of the editing screen. However, this would add visual clutter to the app for an action that most users are going to utilize rarely, so I instead placed an “Email Document” item in the app menu for editing documents. Because it is hidden in the app menu, it’s unlikely that users will discover it without looking for it specifically (the downside) but it also streamlines the main purpose of the app, which is reading and writing text.

It’s the details

It wasn’t until I created the website for TapNote that I realized it’s not a visually impressive app. Yes, it looks good and it does exactly what I designed it to do (which is draw your focus to the text in a visually pleasing way), but it’s not flashy in quite the same way as an app like TweetMe. I was originally intending to do the classic “large phone with screenshot” approach for the webpage, but it didn’t work at all because any TapNote screenshot I took was just a bunch of text that distracted from the web page (and not quite enough text that I could fit the marketing verbiage I wanted in there).

Such a realization was a slight shock to me, because I’d been using TapNote at that point for about a month and it had always felt special to me. When I thought about it, though, it wasn’t special because it was outstandingly pretty (my designs rarely are; attractive simplicity is my forte), but because I’d focused my attention on the little details that matter in day to day use.

This is something that is overlooked far too often in mobile development. For instance, when I downloaded other note-taking apps, a common thing I would do was tap the new note button, tap the title, type a title, and then tap right at the bottom of the screen (right above the keyboard, where my hands already were) to place the edit cursor in the main text field.

The only problem with this was that none of the apps I did this in actually placed the cursor in the main editing text area. After my tap, I had to start tapping up near the top of the page, hoping that I’d hit the nebulous text input so I could start typing.

So in TapNote, although it took a bit of work (and got inadvertently broken several times during the course of development), I made sure that on short documents tapping below the editing area would place the cursor at the end of the editing area.

I’m a firm believer that this is the type of detail that deserves the most attention in mobile apps (and frankly thanks to my self-imposed deadline I didn’t focus on quite as many of these little details as I should have; I’ve been talking up TapNote like it’s the One True App, but as is always the case in a 1.0 product, my ideals are in a far better place than the actual product ended up).

Moving forward

Palm’s WebOS provides a unique opportunity for web developers to explore a new market and interaction framework for their development, and I have no regrets about investing effort into learning the Mojo framework to develop TapNote. I love creating websites, but designing something that I can carry about in my pocket and use every day has a special kind of appeal.

Although I’m happy with how TapNote 1.0 turned out, I’ve got a lot of work still to do. I’ve barely scratched the surface of my ultimate vision for the app, and for all my lofty ideals of great design I didn’t spend quite enough time on a few aspects of the app in my attempt to get it out the door.

My hope is that others will find my early experiences and opinions on design useful in their own development processes, whether they agree with my approach or move in an entirely different direction. Developing TapNote has been a great and ongoing learning experience for me, and I hope other people will take the same leap into WebOS development. This is an area where high attention to detail is not yet the norm and (unlike the Apple app store) there’s lots of room for innovation and improvement on existing solutions because the app catalog is nowhere near as saturated.

Announcing TapNote

I’m very pleased to announce that TapNote, my dead-simple note-taking app for Palm WebOS phones, is now available in the Palm App Catalog for $4.00. I’ve been working on TapNote ever since I picked up a Palm Pre and discovered that none of the notes apps made me happy.

Version 1.0 has all the basic functionality that I needed to take notes comfortably on my phone, but I’ve got some fun plans for the 1.x line (easy-as-breathing synchronization being my top priority). If you have any feature requests or annoyances with the app, let me know!

MobileMe email settings for Palm Pre

For the record, I finally bit the bullet and ditched my iPhone for a Palm Pre last week, and I’m extremely happy with the switch. I’ll post in more detail about why the Pre (and particularly WebOS) is freaking fantastic soon, but for now here’s a quick tip for other Palm Pre owners who want to configure their Pre to use MobileMe and can’t figure out why it keeps claiming you’re doing it wrong.

Although some people have apparently had luck using the automatic setup (where you just enter an email address and your password), I needed to use the manual setup in order to get it to use IMAP rather than POP. I think recent MobileMe accounts don’t even have POP activated by default, so maybe the age of my account is the problem. In any case, if the automatic setup booted you into POP3 rather than IMAP, delete the account and recreate it. From the screen where it asks for your username and password, open the menu and choose “Manual Setup” (why they didn’t just stick a Manual Setup button on the main screen where anybody could find it is beyond me, but I digress).

Here’s all the settings you’ll need to get rolling:

  • Incoming mail server: mail.me.com
  • Username: [your username, without @me.com or @mac.com]
  • Password: [your password]
  • Encryption: SSL
  • Port #: 993 (this should be automatically selected when you choose SSL)
  • Outgoing mail server: smtp.me.com
  • User Authentication: On
  • (Username and password will be auto-filled)
  • Encryption: TLS (this is what was probably screwing you up if you tried manual setup already)
  • Port #: 587

These settings work perfectly for me; hopefully they’ll save other Pre users with MobileMe the headache of “SMTP setup failed” errors.

Understanding Espresso actions

A fair number of people in the Espresso forums ask questions like, “Is there a shortcut to delete the current line the way there is in Textmate?” Usually the answer is, “No, why don’t you create one?” Yet people remain leery of creating text actions by hand. I understand that; heck it’s extra work, and who wants that? What I think most people don’t realize is that, particularly for text manipulations like deleting the current line, the amount of work is minimal. All you need is a basic understanding of how Espresso works and the ability to author simple Javascript and XML.

Anatomy of an action

Espresso actions come in two parts:

  1. The XML definition, which tells Espresso the action’s title, what Objective-C class to execute when the action is invoked, and additional information like keyboard shortcuts or tab triggers that should invoke the action.
  2. The Objective-C class (or, if using a plugin like TEA or Spice, the script that defines the action’s logic)

Most often, XML action definitions are included with a Sugar (you can make a Sugar for custom text actions simply by adding a .sugar extension to a folder that contains a TextActions folder with the action XML files in it). If you wish, you can also define action XML files outside of a Sugar by enabling TEA custom user actions in the Advanced pane of the Espresso Preferences (more info about TEA custom user actions).

As of Espresso 1.1 there isn’t an in-program GUI for editing actions yet, but hopefully that will come in time.

Spice up your actions

Spice is an Espresso Sugar that I’m currently developing that provides access to the Espresso API using Javascript (thanks to JSCocoa), and additionally provides a selection of utility classes that allow you to interact with the API without needing to know anything about Objective-C or the JSCocoa bridge.

The benefits of this should be fairly obvious, but there’s another reason creating actions with Spice is easier than using TEA or Objective-C; not only are the action scripts not compiled, but you don’t need to relaunch Espresso to see your changes. When I’m coding a Spice action, I usually open the Javascript action in Espresso, and then run the action right there on the Javascript when I need to test it.

Although Spice’s utility classes make creating custom Espresso actions easier, you still need a basic understanding of how the Espresso API works.

Contexts, ranges, recipes, and snippets

When a user invokes an action from the Actions menu, Espresso executes the action’s class and sends it an object representing the text or file context (depending on whether you’re using a text or file action; I’ll be focusing only on text actions because the file action API is currently underwhelming). The text context allows you to access the active file’s text, information about the selection (or selections, since you can select multiple things at once), line information, preferences (like what line ending or indentation is being used), and access to the syntax system.

When you are ready to change some text in the file, your action messages the text context and tells it what to change and how to do it.

As of Espresso 1.1, the easiest actions to create are ones that are invoked by the user, access and change something about the active file’s text, and then immediately exit. It’s possible to do things that are a bit more complicated or on-going, but at the moment Espresso doesn’t make it easy.

When you’re working with text, you’ll mainly be dealing with ranges. A range represents a section of text in the active file and is composed of a location and a length. Internally Espresso keeps track of text by counting all of the characters in the active file starting at zero, so the range location is the index of the starting character and the length is the number of characters contained within the range. Typically you’ll be dealing with selections, so you’ll have ranges like {0, 10} (the first ten characters in the file) or {100, 30} (characters 100 through 130). However, it’s also possible to have ranges like {10, 0}, which represents the cursor location at character index #10 in the file.

A typical text action will fetch the currently selected ranges from Espresso, manipulate the text within them somehow, and then tell Espresso to change the range(s) accordingly. In order to queue up changes like this, you’ll use text recipes.

Text recipes are something that is unique to Espresso, so far as I know. Basically, you create a recipe and tell it things like “delete the text in this range”, “replace the text in this second range”, and “add this text at a third range”. You can compose a multiple step recipe without worrying about tracking how ranges will change; for instance, if you add characters to an early range, you don’t have to adjust the location of later ranges. Instead, the recipe does that for you when you apply it to the document.

Text recipes allow you to make complex changes to multiple ranges in the active file, but you can instead insert text snippets, as well. A text snippet is a specially formatted string that allows Espresso to create tab stops, mirrored segments, and more after you’ve inserted it. Many of the custom actions bundled with Espresso are little more than text snippets; for instance Wrap Selection In Tag merely grabs the selected text and wraps it in a simple snippet that mirrors the HTML element from the opening tag to the closing tag. You can do some pretty magical-seeming things with very simple actions simply through tricky use of text snippets. The one downside to keep in mind for text snippets, however, is that you can only insert one at a time (unlike text recipes, you can’t insert multiple text snippets over discontiguous selections).

Bringing it all together

With an understanding of how the Espresso API works and a quick peek at the Spice docs, setting up an action to delete the current line (to take one example) becomes simple enough:

  1. First, we’ll need to define an action in XML, and create a Javascript file with the action logic
  2. In the Javascript, we’ll need to query the text context to get the range of the current line, and then create a text recipe to delete that range

The action XML is easy enough; simply create the file Actions.xml and place it either in a Sugar’s TextAction folder or in your Espresso Support folder located here:

~/Library/Application Support/Espresso/Support/TextActions/

The contents of the action XML definition should be this:

<action-recipes>
    <action id="com.onecrayon.DeleteLine" category="actions.text.generic">
        <class>Spice</class>
        <title>Delete Line</title>

        <setup>
            <script>delete_line</script>
            <undo_name>Delete Line</undo_name>
        </setup>
    </action>
</action-recipes>

(If you wish to add a keyboard shortcut, you can do that with the key-equivalent entity; see the Spice action XML docs.)

If you’re using the Support folder option, you’ll need to enable TEA custom user actions in the preferences and restart the program twice. Otherwise you’ll need to make sure the Sugar is installed and restart the program once (action XML definitions are loaded when Espresso boots up; unfortunately as of Espresso 1.1 there isn’t a way to refresh action XML definitions without a relaunch).

Now that you’ve got the action defined (and showing up in the Actions menu) you can create the Javascript file delete_line.js (referenced in the script element of the action XML). If you are using a Sugar, it should be located here:

MySugar.sugar/Support/Scripts/

If you’re using custom user actions in the Espresso Support folder, you’ll save it here:

~/Library/Application Support/Espresso/Support/Scripts/

The simplest way to delete the current line would be this:

// Deletes the current line

// require() allows you easy modular access to Spice's helper classes
var textContext = require('text_action_context').textContext;
var TextRecipe = require('text_recipe').TextRecipe;

// exports.main is your primary function, run automatically by Spice
exports.main = function() {
    // Grab the range of the current line
    var linerange = textContext.rangeForLine();
    // Run the actual removal
    return new TextRecipe().remove(linerange).apply();
}

First, the script uses the universal require() function to include the Spice utility object textContext (which contains methods for interacting with the Espresso text context) and utility class TextRecipe (which allows access to Espresso text recipes).

Spice’s utility classes are provided in a modular system that allows you to only require what you need in order to run your action. Spice modules have the following naming conventions:

  • The module is named for the primary class, converted to lowercase and with underscores between words (so primary class TextActionContext becomes module text_action_context)
  • Classes are all camel-case with the first letter capitalized (TextActionContext)
  • Objects (instantiated versions of a class) are camel-case with the first letter lowercase (textContext is an instantiated version of TextActionContext)

You can see what a given module exports in the Spice docs (along with full references to the object methods available). The text_action_context module is one of the few that exports an object as well as the class, since you’ll never need more than one object for referencing the text context.

Once you’ve saved the Javascript file, you’ve officially created your first Espresso action! You can immediately run the action from the Actions menu, and it will delete the current line. If you test it out, though, you may notice that the action behaves differently when you delete the final line in the document; instead of removing the line completely, it only removes all the text. To fix this, you could modify the action like so:

// Deletes the current line

// require() allows you easy modular access to Spice's helper classes
var textContext = require('text_action_context').textContext;
var TextRecipe = require('text_recipe').TextRecipe;
var Range = require('range').Range;

// exports.main is your primary function, run automatically by Spice
exports.main = function() {
    // Grab the range of the current line
    var linerange = textContext.rangeForLine();
    // If on the last line of the doc, remove the line break prior to the line
    // This isn't strictly necessary, but it's nice to have
    if (textContext.lineNumber() != 1 && textContext.rangeForLine(textContext.lineNumber() + 1) === false) {
        linerange = new Range(linerange.location - 1, linerange.length + 1);
    }
    // Run the actual removal
    return new TextRecipe().remove(linerange).apply();
}

The only addition is some logic to check if we’re on the last line of the document, and if so create a custom Range object that includes the linebreak from the previous line.

Debugging

As you work on your own custom actions, you will of course run into problems and errors. The best way to debug is to keep Console.app open (located in your Applications/Utilities folder), since most errors will be output there. You can also use the globally available system.log('message') to output directly to the console. Many Spice utility classes also include a log() method to quickly log the contents of an object.

When using Spice, keep in mind that it’s still under development (at the time of this writing, it’s at version 1.0 beta 6). If you run into something that seems buggy or a limitation of the utility classes, please let me know.

Parting thoughts

Even if you don’t use Spice, the basic strategy for adding a custom action to Espresso remains the same. TEA offers several alternative methods to creating text actions (from Python scripts with full access to the API to scripts in arbitrary languages like Ruby or PHP that have more limited capabilities). Although the basic editor has a nice interface and some great features, I’ve found that the extensibility of Espresso is what keeps me coming back for more. A modicum of effort is often all that’s required to add a custom text action, so there’s little excuse for not giving it a try if you find yourself missing functionality you’ve grown used to in other editors.

If you do write any great custom actions, please think about sharing them either via a Sugar, GitHub, or the Espresso forums! I’d also love to hear how people are using Spice; I’m still planning out the additions I want to make now that I’ve got most of the basic Espresso API for text actions covered by the utility classes, so your input is extremely valuable.

Wrap Selection In Link in any program on Mac OS 10.6

The Mac OS X Services menu got a serious bit of love for 10.6, but until now I haven’t really played around with it. Today, however, I finally got around to trying it out, and for the first time since I started using OS X years ago, Services are actually becoming useful for me.

The specific need I wanted to address was my inability to easily insert some common HTML elements (particularly links) in WriteRoom. I’ve owned WriteRoom for years thanks to a software bundle, but I never had any use for it until I discovered QuickCursor. Now I can’t get enough of it; full screen editing for text fields in Safari is brilliant.

The only thing I haven’t been happy about was needing to type out every blessed character when I needed light HTML. Although WriteRoom’s Applescript support does not apparently provide access to selected text, the new Services menu does.

To add HTML link insertion to WriteRoom (or any other text editor that supports Services, for that matter) first boot up Automator and create a new Service workflow:

service_workflow.jpg

By default, Automator will have you processing selected text in any application. Check the “Replaces selected text” checkbox, and if you’re only going to be using the action in WriteRoom (or similar application) make sure to use the dropdown to target that app and avoid cluttering up your Services menu:

service_config.jpg

Over at the top right, type “Applescript” in the search box to filter the list for the “Run Applescript” action (make sure you have the Library highlighted, or it may not show up). Drag the action into your workflow:

services_action.jpg

Select everything in the text box, and replace it with this:

on run {input, parameters}
    set linkDefault to (the clipboard as string)
    set linkDefault to my switchText(linkDefault, "\\\"", "\"")
    set targetLink to do shell script "echo \"" & linkDefault & "\"|sed -E \"s/(mailto:)?(.+@.+\\..+)/mailto:\\2/\""
    if targetLink does not start with "mailto:" then
        set targetLink to do shell script "echo \"" & linkDefault & "\"|sed -E \"s/^(([a-zA-Z0-9-]+\\.)*[a-zA-Z0-9-]+\\.[a-zA-Z]{2,4}(\\/.*)?)/http:\\/\\/\\1/\""
        if targetLink does not start with "http" then
            set targetLink to "http://"
        end if
    end if

    return "<a href=\"" & targetLink & "\">" & input & "</a>"
end run

on switchText(fromText, targetText, replaceText)
    set d to text item delimiters
    set text item delimiters to replaceText
    set fromText to fromText's text items
    set text item delimiters to targetText
    tell fromText to set fromText to item 1 & ({""} & rest)
    set text item delimiters to d
    return fromText
end switchText

Save your workflow, and you’re basically done! When you select some text in WriteRoom or elsewhere and run the action from the Services menu, it will wrap the text with a link. It also will check the clipboard to see if there’s a recognizable email address or URL on it, and if so will use it for the link (otherwise defaults to http://).

For maximum productivity benefits, of course, you’ll want to visit the System Preferences Keyboard pane and give your new Service action a shortcut; I’m using control-shift-L since that’s what I’m used to from Textmate and Espresso.

Worth noting is the fact that the above Applescript doesn’t display a dialog box to query you for a URL if it can’t detect one on the clipboard, so for maximum effect you’ll want to copy your target URL prior to running the action if possible. The reason for this minor problem is that the dialogs spawned by my Service action never received focus, and for myself mousing over to give the dialog focus is more work than option arrowing a few times to edit the default URL.

You can, of course, add simple Service actions to surround text in other arbitrary HTML tags, as well. Simply use an Applescript similar to this instead:

on run {input, parameters}
	return "<strong>" & input & "</strong>"
end run

The only major downside that I’ve found of using Services this way is that it isn’t as quick as I’d like. I experimented with using shell scripts or Python instead of Applescript, but there were no significant speed boosts; as best I can tell, however Services is harvesting the selected text and then replacing it afterward is simply a slow process.

I’d love to hear how other people are leveraging the new 10.6 Services menu! I have the feeling that I’m just scratching the surface with this script.

Comparing StoryMill and Scrivener

I’m a long-time user of StoryMill (starting when it was originally called Avenir), but I’m also something of a software junkie, so when Scrivener came out I tried using it for a few projects. Particularly now that StoryMill has a timeline view (as of this writing the only Mac creative writing software to implement the feature) and Mariner Software is distributing it, it seems like a more and more people are wondering whether they should use StoryMill or Scrivener.

Well, here it is: my definitive StoryMill vs. Scrivener review. Although StoryMill is my personal application of choice, there’s a lot to love (and some things to dislike) about both programs.

Quick and dirty

Not everyone wants to wade through my periphrastic meanderings (just discovered that word, and it’s making me really happy; sorry for sounding like a total vocab snob), so here’s the quick and dirty:

  • If you primarily write fiction and want a program that will provide you with an easy framework for organizing your writing, you’ll probably prefer StoryMill.
  • If you primarily write non-fiction or screenplays or write fiction and want a program that will let you do pretty much whatever the hell you want workflow-wise (with a correspondingly higher level of confusion), you’ll probably prefer Scrivener.

A broader picture

As is often the case, the fast and dirty comparison is a bit misleading: either program can help you write fiction or non-fiction. The reason the fiction/non-fiction comparison is common is because StoryMill is explicitly focused on fiction (and doesn’t support screenplays at all, since that would steal sales from its companion software Montage) while Scrivener provides a general writing metaphor that can apply to either genre equally well (with limited support for screenplay formatting and footnotes).

The truth is that regardless of your genre the deciding factor for which software to use will be a matter of style. StoryMill’s approach is to provide you with a specific framework for writing and organizing, complemented with a focused group of powerful features. In contrast, Scrivener is much more flexible and offers a larger number of features that you can pick and choose from to form your workflow. You can do most of the things in Scrivener that you can in StoryMill (with a few key exceptions), but it will be slightly more effort.

If StoryMill’s framework makes sense to you and you don’t have an urgent need for any of the features that are Scrivener-only, then StoryMill will be the easiest environment to write in. However, for some people the time necessary to set up their own framework in Scrivener is well worth the effort because the program’s flexibility allows them to write most effectively.

To figure out which style, and thus program, is the best choice for you, you’ll need to consider two big questions: what metaphors do you use for writing, and what specific features are most important for you?

StoryMill: a novel framework

The foundation of StoryMill’s approach to writing and organizing is the scene. Scenes in StoryMill are the building blocks which create chapters and ultimately the story itself (it’s worth noting that you can think of scenes and chapters as whatever content blocks make sense for your story; the names don’t really limit the function). Though you’ll track your characters, locations, and so forth elsewhere, the scenes are where you’ll tie them together.

StoryMill offers several other types of items like characters, locations, research, and even submission tracking for when you complete the novel, but the scenes are the core of the program. If working with scenes makes sense to you, and you like the ability to directly relate characters to scenes and organize both in plot order and chronological order (the latter via the timeline feature), then StoryMill will likely appeal.

This scene-centric framework has actually taken a page from Scrivener’s book in recent updates, as well. You can use scenes either as an outline (in the Scenes view) or as the actual text of the story (in the Chapters view). Storing text in scenes can be a little bit confusing (particularly since you can still store text in chapters and use scenes purely for organization if you choose), but this allows you to take advantage of the outline-as-text feature that Scrivener executes with such panache.

StoryMill also has some specific stand-out features that influenced my decision to use it. For me, annotations are one of the biggest. In StoryMill, you annotate text by selecting it and choosing “Annotate”. The text turns the standard link blue with underline, and a little window opens up in which you can type the annotation’s title (defaults to the selected text) and add your annotation. This is great for a number of reasons:

  1. Annotated text is clearly marked, yet the annotations themselves are completely invisible unless you want to see them.
  2. Annotations are rich text, so they can contain just about anything. This includes images, formatted text, etc.
  3. StoryMill handles annotations intelligently: if you open up the annotations window and start moving through the text with your cursor, the displayed annotation will update based on which linked text the cursor is in. You can also open the annotations window for a given annotation with a hotkey (no clicking the link required).

Some people prefer the margin-notes approach to annotations in Pages, Word, etc., but I find those extremely limiting because if you type more than a sentence or two they become unwieldy, it’s not always easy to tell what text the annotation applies to, and you can’t have very many on a page before they get out of control. The only thing they have over StoryMill’s annotations is that you can view all of them at once, but in practice I’ve never found this to matter.

Another big draw for StoryMill is its timeline functionality. Timelines allow you to view and organize scenes in chronological time, even if the flow of the novel is completely different. This can be fantastically helpful for preventing plot holes and other inconsistencies, and getting a general overview of the flow of time through your novel can be useful in its own right. The downside to timeline is that it’s still a young feature; the interface could use a little refinement and it had a bit of a rocky start thanks to some bugs, but there’s still nothing comparable out there (aside from dedicated timeline software).

StoryMill has numerous other small features that I can’t live without, as well: the progress meter is a one that I’ve surprisingly grown to rely on. It visually tracks how far along you are for both your session and project word goals. It also can emit a sound when you hit your session goal, which is really nice feedback if you’re working in full screen. Full screen is of course another great feature, although one that’s available in most writing software these days. The reason I like StoryMill’s better than Scrivener’s is that it’s truly nothing but you and your writing; no annotations, no floating windows, nothing but the text. Tags and smart views, export templates, and the project-wide find and replace dialog are other reasons to love StoryMill.

Scrivener: your digital corkboard

Scrivener takes a slightly different approach to the writing process. Where StoryMill provides a framework with carefully designed parts, Scrivener offers the user a beautifully executed corkboard metaphor and then hangs potentially useful features around it.

Scrivener’s corkboard is where it really shines. The connection between outline, visual organization of “index cards”, and your actual text is simple, sensible, and flexible enough to handle virtually any kind of writing. Want to break things down into beats, then scenes, then chapters, then acts? Go for it. Your only real limit is your creativity. Scrivener’s central metaphor additionally captures in a digital format a way of working that instantly makes sense to most people who have written by hand. This is a definite strength over StoryMill, which takes a more relational database-driven approach to organization and writing that may not be as easy to initially access for some people.

Aside from its elegant central metaphor, Scrivener also offers a slew of useful features. I encourage you to check out the Scrivener website and free trial to figure out which ones you’ll care about, but the primary things of interest to me are snapshots (saving multiple versions of a single piece of text), wiki-style links to internal documents, the vastly flexible exporting system, and the simple script-writing formatting tools.

It’s worth taking a moment to dwell on snapshots. Although StoryMill is planning to implement similar functionality for their next version, this is a key area where Scrivener is clearly the better choice. Organizing multiple revisions of the same text in StoryMill is extremely kludgy at the current time, while Scrivener handles it with ease.

Additionally, you can (with a little bit of work) mimic some of StoryMill’s strengths in Scrivener. For instance, the Document References could be used to associate characters with scenes. You can also mimic StoryMill’s annotations to some extent using wiki-links that open in split views (Scrivener’s built-in annotations are inline with the text, making them only useful for very short notes to yourself that you don’t mind reading every time you go back over things). They’re not as easy to use as StoryMill annotations, sure, but this kind of flexibility is another of Scrivener’s strengths. The menus may be confusing and (to my eye) bloated, but all those disparate features mean that with a little work you can achieve numerous different workflows.

Beyond the software

Of course, there’s more to writing than just the software itself. Both Scrivener and StoryMill have healthy communities and refreshingly responsive developers. Scrivener has a larger community (and one more inclined to chat about whatever the heck is on their minds), but you may get a faster response in StoryMill’s forums simply because there aren’t as many threads. Your mileage will doubtless vary, but I highly suggest dropping by the forums for whichever software you’re leaning toward and asking any questions you have.

An additional concern is interoperability between your writing software and other software on your computer. Both Scrivener and StoryMill store data in proprietary formats but both also offer flexible ways to export that data. Which export system you like better will probably depend a lot on what you need to export, but both allow you to get all of your data out of the program without much fuss. If you’re trying either software’s free trial, definitely play with the export system before purchasing it.

Scrivener additionally allows easy editing of text in external programs, which can be nice if you like editing text in WriteRoom, BBEdit, or similar.

Time to write

Both StoryMill and Scrivener were created because the developers couldn’t find a tool that fit their respective needs as authors, and the bottom line for any potential user is you should use whichever program makes it easiest for you to write.

For myself, that program is StoryMill. Its framework makes sense to me and I’ve become addicted to its overall slimmed-down focus on the features that matter most (not to mention some specific niceties like rich annotations and timelines).

I can certainly appreciate the draw of Scrivener, however. Every time I open it I’m amazed anew at how simple and relevant a metaphor for writing it provides. StoryMill’s niggling issues with the separation between outline and text are nonexistent in Scrivener thanks to its solid basis in the idea of a corkboard. Sure, to get the kind of interconnectivity that StoryMill encourages you have to do a bit more work, but with Scrivener’s large and helpful community figuring out a document layout and workflow shouldn’t be too painful.

Ironically, in a few short years we’ve gone from having no great alternative writing environments to Word and the other word processors to having a difficult choice between two strong contenders (and that’s discounting the scads of similar but less popular software like CopyWrite, Jer’s Novel Writer, Storyist, or Ulysses, the program that started it all); no choice has exploded into too much choice. Hopefully by focusing on which general approach and specific features are most helpful for your workflow you’ll be able to select the best software for you and get on to what’s really important: your writing.

Text Editor Actions for Espresso

I am extremely happy to announce that my Text Editor Actions for Espresso (or “TEA” for short) has at last been released as version 1.0. Version 1.0 is available for download, or you’ll also find it bundled in the upcoming Espresso 1.0.7.

So just what the heck is TEA for Espresso? Simply this:

  1. A selection of my favorite text actions, mostly (but not entirely) copied from Textmate
  2. Generic actions that allow you to create variations on TEA’s bundled functionality to suit your workflow by editing simple XML
  3. A general framework for coding and running text actions in arbitrary languages without needing to create a Sugar or (for third party Sugars) without needing compiled Objective-C classes

Espresso’s Sugar API was already pretty sweet. TEA makes it that much better by lowering the barrier to creating custom text actions for users and Sugar developers alike.

Documentation for TEA is currently limited to info on creating your own actions, so I’ll walk you through the basic actions included with the plugin.

The vast majority of TEA’s built-in actions focus on making HTML easier to edit, because editing HTML is most often why I need a text editor.

Generic text actions

Spaces To Tabs… and Tabs To Spaces…
As you might expect, these actions convert the type of indentation in your document or (if it exists) your selected text. When you run the actions you’ll be prompted to enter the number of spaces per tabs you wish to use (it defaults to whatever is in your Espresso preferences, so you can just hit enter most of the time).

Trim Line(s)
Trim Line(s) will, when invoked, either trim all of the lines in your selection or the current line the cursor is on (if no selection exists). Unlike some trim lines actions, TEA’s Trim Line(s) attempts to be smart about what whitespace it removes:

  • All whitespace at the end of the line will be stripped
  • Any whitespace at the beginning of the line that isn’t part of the indentation will be stripped

What the latter means is that if in the Espresso preferences you have the program set to use spaces instead of tabs with four spaces per tab, and the beginning of a line has ten spaces, two of the spaces will be stripped.

Select → Word, Select → Line, Select → Line Contents
As you might expect, these actions select the word under the cursor, the line under the cursor (including leading and trailing whitespace), or the textual contents of the line under the cursor (excluding leading and trailing whitespace), respectively.

Sorting → Sort Lines (Ascending) and Sorting → Sort Lines (Descending)
As you might expect, these actions sort all lines in the selection (or document, if no selection) in ascending and descending order, respectively.

Sorting → Randomize Lines
This randomly sorts all lines in the selection (or document, if no selection).

Sorting → Remove Duplicate Lines
If for some reason you need to strip all duplicate lines from your selection or document, this is the command for you.

Formatting commands

Indent New Line (command-shift-enter)
One of my favorite parts of Textmate is that after creating an HTML tag, I only have to hit enter once to get a perfectly indented tag pair with the cursor in between and bumped in a level. The fact that Espresso doesn’t do this irks me greatly, and so this action allows you to force the issue. Indent New Line will turn this (pipe represents cursor):

<div>|</div>

Into this:

<div>
    |
</div>

If you have any text selected when you run the action, the selected text will be moved to the middle line and indented.

Insert Linebreak(s) (control-enter)
In HTML, Insert Linebreak(s) will insert a break tag (<br />). In some other contexts (like PHP double quoted strings), it will insert \n. In Markdown it will insert two spaces and a linebreak. If you have one or more selections, the tag or textual linebreak will be inserted at the end of each selection.

In case you didn’t quite catch that, Espresso allows you to have multiple selections (hold down command while you select multiple items with your mouse), and this action will affect all of them. This is extremely cool, and one of the features that I’m still learning to use; before now, I’d never come across a text editor that allowed me to so much as select multiple items at once. Of course, it isn’t all that often that you need to append br tags in a whole bunch of places around a document, but what about when you want tags for…

Strong (command-B) and Emphasize (command-shift-I)
These do about what you’d expect. If you have one or more selections, they’re surrounded with strong or em tags. If no selection, you get a tag wrapping your cursor. Incidentally, if you’re working with a single selection (or no selection) you’ll get a text snippet with tab stops, so hit the tab key to edit what’s inside the tag.

A note on Emphasize’s shortcut; command-I by default is used to show and hide the navigator sidebar, hence this somewhat odd shortcut for italics. If you wish to switch the shortcuts, you can do so through the System Preferences Keyboard & Mouse controls.

HTML actions

Entities → Convert To Named Entities (control-&) and Entities → Convert To Numeric Entities
Run one of these actions to have the character immediately to the left of the cursor converted from Unicode into an HTML character entity. If you have one or more selections, all non-ASCII Unicode characters will be converted to entities of the desired variety. If using named entities, Unicode characters without a named entity will still be converted to their numeric equivalent. These actions will also convert ampersands (but will ignore ampersands that are already part of an entity).

Entities → Insert Non-Breaking Space, etc.
Use these actions to quickly insert the named HTML entity for the given character.

Expand Abbreviation (control-,)
This action is much like Textmate’s “Insert Open/Close Tag (With Current Word)” which, when I saw it demoed in a screencast, changed my life. For far too long had I been toiling away, typing out every blessed less than/greater than symbol. With Expand Abbreviation, I merely type the HTML tag, hit the shortcut, and voilà. I have the complete tag ready to go with barely any effort at all.

And the fun doesn’t stop there! The reason for the action’s name change is that Expand Abbreviation is powered by the fantastic zen coding project, so in addition to Textmate’s functionality Expand Abbreviation offers the full range of zen coding abbreviations and CSS-selector style syntax to create complex markup from very simple declarations. Here’s a quick example of zen coding’s awesomeness:

div#stuff.things.booyah

Type that, hit control-, and you’ll end up with this (pipe represents cursor):

<div id="stuff" class="things booyah">|</div>

Or if you want to do something a little more complicated:

div#nav+div#content>p.item$*2

Which leads to this:

<div id="nav">|</div>
<div id="content">
	<p class="item1"></p>
	<p class="item2"></p>
</div>

And that’s just the tip of the iceberg. Zen coding offers numerous other selectors and scads of abbreviations for HTML and CSS. All of them will work with Expand Abbreviation.

You may also, if you need, use the old Textmate-style tag creation where you type out everything in the tag except the carets, highlight it, and run it through Expand Abbreviation to get a full tag. For instance, this:

div style="width:100%;"

Once selected and run through Expand Abbreviation leads to this markup:

<div style="width:100%;">|</div>

If there is no selection, this action will use the current word regardless of where the cursor falls in it (Textmate will only parse to the left of the cursor).

Wrap Selection In Tag (control-shift-W)
As you might expect, if you select some text and invoke Wrap Selection In Tag, the selection will be wrapped in an HTML tag. Just like in Textmate, you can type out tag attributes and they won’t be mirrored to the closing tag, and moving outside the tag is a tab away.

Wrap Selected Lines In Tag (command-control-shift-W)
This one acts just like Wrap Selection In Tag, except that each line in the selection is wrapped.

Wrap Selection In Link (control-shift-L)
Unsurprisingly, selecting some text and invoking this command will wrap it in an HTML link tag. What makes this action more worthwhile than some of the others is that if you have a recognizable link on your clipboard it will be inserted, and there are several tab stops set up to make removing or editing the link’s title extremely easy. Unlike Textmate, this action does not attempt to populate the title from the actual webpage’s title. I’ve had Textmate hang while it waits to retrieve the webpage too many times to want to implement that functionality myself.

If you use this action while editing Markdown or Textile, the selection will be wrapped in a Markdown or Textile link rather than an HTML anchor.

Documentation For Tag (control-H)
If your cursor is inside an HTML tag, you can run Documentation For Tag to have the word under the cursor (or the selection) searched for in HTMLHelp.com’s HTML reference. If the cursor is inside an HTML tag, you’ll be taken straight to the first result (almost always the correct tag page). Otherwise, you’ll get a Google result listing.

TEA Preferences

TEA → Preferences offers a GUI to modify some TEA-related preferences. You’ll need to have a document open in order to access the prefs due to limitations in how Espresso sets up actions.

General Prefs
Checking “use XHTML by default” will cause TEA-based snippets that use the $E_XHTML variable to leave it blank. At some point in the future, TEA will hopefully be more intelligent about detecting whether a document is HTML or XHTML, but for now you’ll need to control it using this preference.

Similar to Textmate, anything entered in the Custom Shell Variables section of the preferences will be available as an environmental variable to any shell scripts you run through TEA. For instance, if you add a variable with the name “MY_CUSTOM_VARIABLE” and the contents “I love TEA!” then wherever you use the shell environmental variable $MY_CUSTOM_VARIABLE you’ll get “I love TEA!”

Actions
If you check “Enable custom user actions” you will be able to create custom actions without needing a custom sugar. This is useful not only for custom TEA-based actions, but for custom actions using third party sugars, as well.

Beyond the bundle

If TEA’s included actions aren’t enough for you, it’s extremely easy to add your own custom actions, port actions from Textmate bundles, and otherwise use TEA to jumpstart your own Espresso customizations. The TEA for Espresso wiki has lots of info on this sort of thing, or you can take a look at the HTMLBundle.sugar’s source for an example of porting Textmate snippets and bundle items (the HTMLBundle.sugar may also be of use to other folks who want Textmate’s HTML tab completions, among other things; download it here).

I’m also usually available in the forums or Espresso IRC channel if you have questions about using TEA, feature requests, bug reports, or other comments. Alternatively if you have a GitHub account, you can file bug reports and feature requests directly into the TEA for Espresso Issues tracker.

I hope you enjoy TEA with your Espresso!

Track me like a stalker:
  • Tagamac
  • Twitter
  • beckbits
Clicky Web Analytics