Move the line up and down?
Posted: 12 November 2009 01:56 PM   [ Ignore ]
Jr. Member
RankRank
Total Posts:  48
Joined  2009-09-13

Hi folks, is there a way to move the active line up and down, like in TextMate?

Profile
 
 
Posted: 12 November 2009 08:55 PM   [ Ignore ]   [ # 1 ]
Moderator
RankRankRankRank
Total Posts:  984
Joined  2008-10-28

Nope, but you could probably code your own version using TEA or Spice.

 Signature 

Ian Beck
MacRabbit Support

My stuff:
TEA (docs / source) .:. Spice.sugar .:. HTMLBundle.sugar .:. Quiet Light & Earthworm

Profile
 
 
Posted: 14 November 2009 02:20 PM   [ Ignore ]   [ # 2 ]
Sr. Member
RankRankRankRank
Total Posts:  211
Joined  2008-11-19

I, too, would like this!

Profile
 
 
Posted: 15 November 2009 12:18 AM   [ Ignore ]   [ # 3 ]
Moderator
RankRankRankRank
Total Posts:  984
Joined  2008-10-28

I thought you’d be able to do this with Spice.sugar, but I actually hadn’t coded any line handling methods yet. That is now fixed as of 1.0b4, so here’s a quick tutorial/intro to Spice to allow you to add these move line actions yourself. I opted to write up an example for Spice rather than TEA because I’m hoping to make some converts; I’ve been surprised at how few people have tried it out, and it helps me a lot to know how to improve it if I can get people using it. Spice is a great way to get into Espresso actions because it allows you to code Javascript (which should be a lot easier for most people on the forums than Python) and it also provides numerous helper classes to make life easier so you don’t have to worry about knowing the Espresso API or Objective-C/JSCocoa.

First off, install Spice 1.0b4:

http://onecrayon.com/downloads/Spice.zip

To get this example working, you’ll need to enable custom user actions in the TEA preferences (Actions=>TEA=>Preferences), if you haven’t already. Then navigate here:

~/Library/Application Support/Espresso

And create a folder called Support. Inside Support create two folders: TextActions and Scripts. Inside TextActions, create a file called Spice.xml with these contents:

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

        <
setup>
            <
script>move_line</script>
            
<undo_name>Move Line Up</undo_name>

            <
arguments>
                <array>
                    <
string>up</string>
                </array>
            </
arguments>
        </
setup>
    </
action>
    <
action id="com.onecrayon.MoveLineDown" category="actions.text.generic">
        <class>
Spice</class>
        <
title>Move Line Down</title>

        <
setup>
            <
script>move_line</script>
            
<undo_name>Move Line Down</undo_name>

            <
arguments>
                <array>
                    <
string>down</string>
                </array>
            </
arguments>
        </
setup>
    </
action>
</
action-recipes

This defines two actions, both of which use the same Javascript file. More info about XML action definitions for Spice. You’ll need to restart Espresso twice after creating this file to get your actions recognized (this is due to limitations in how Espresso loads actions from XML).

In the Scripts folder create a file called move_line.js with the following contents:

// move_line
// Moves the current line up or down

// require() allows you easy modular access to Spice's helper classes
// These particular modules are documented here:
//    http://onecrayon.com/spice/docs/text_action_context-js/
//    http://onecrayon.com/spice/docs/text_recipe-js/
var textContext = require('text_action_context').textContext;
var 
TextRecipe = require('text_recipe').TextRecipe;

// exports.main is your primary function
// You could do without a function, but you couldn't return false to have Espresso beep
exports.main = function(direction{
    
// Make sure that there's a direction parameter passed
    
if (!$chk(direction)) {
        log
('move_line requires a single string parameter ("up" or "down" are valid)');
        return 
false;
    
}
    
// Grab our current and target line numbers
    
var current textContext.lineNumber();
    var 
target;
    if (
direction.toLowerCase() == 'up')
        
target current 1;
    else
        
target current 1;
    
// Grab the ranges for the current and target line numbers
    
current textContext.rangeForLine(current);
    
next textContext.rangeForLine(target);
    
// Make sure the target line is inside document bounds
    
if (next !== false{
        
// Create a new text recipe
        
var recipe = new TextRecipe();
        
        
// Swap linebreaks to make sure that things work at the doc's end
        
var currentText current.string();
        var 
curBreak currentText.replace(/^.*([\n\r]*)$/, '$1');
        var 
nextText next.string();
        var 
nextBreak nextText.replace(/^.*([\n\r]*)$/, '$1');
        
currentText currentText.replace(/^(.*)[\n\r]*$/, '$1' nextBreak);
        
nextText nextText.replace(/^(.*)[\n\r]*$/, '$1' curBreak);
        
        
// For some reason the order you specify the replacements matters; not sure why
        
if (direction.toLowerCase() == 'up')
            
recipe.replace(currentTextnext).replace(nextTextcurrent).apply();
        else
            
recipe.replace(nextTextcurrent).replace(currentTextnext).apply();
        
// Now that the line's moved, select it
        
textContext.setSelection(textContext.rangeForLine(target));
        return 
true;
    
else {
        
return false;
    
}

After creating the Javascript file, you don’t have to relaunch Espresso at all. It will always execute the most recently saved version of the file, so you can experiment in real time. You only have to restart if you change the XML (and only have to restart twice if you add a new XML file).

I’ve commented things pretty heavily in the hopes that it’ll be useful for figuring out exactly what’s going on, and it should provide a good example of manipulating text using Spice. The functionality is pretty basic: it simply swaps the current line (line your cursor is on, or line at the start of your first selection) with the line immediately above or below it. You could, if you wanted, get a little fancier and code in handling for moving whole selections, etc. I leave it to you to add shortcuts to the actions or expand on the Javascript logic (see the Spice docs for help). If you get to tinkering, remember you can use log(message) to output things to Console.app (very useful for debugging). Enjoy!

 Signature 

Ian Beck
MacRabbit Support

My stuff:
TEA (docs / source) .:. Spice.sugar .:. HTMLBundle.sugar .:. Quiet Light & Earthworm

Profile
 
 
Posted: 15 November 2009 01:54 AM   [ Ignore ]   [ # 4 ]
Jr. Member
RankRank
Total Posts:  48
Joined  2009-09-13

Oh wow! Thank you, Ian, for this detailed tutorial! I’m not very good with JavaScript — doing mostly HTML/CSS and sometimes XSL stuff these days, but will definitely try :) Thanks again!

Profile