Search code examples
macrosemeditor

Macro to swap selections


As an author, I frequently have to swap two words, phrases or sentences. I do that by dragging and dropping or by using the clipboard history, or by retyping, all of which are cumbersome and prone to mistakes.

  1. Is there a command or macro to automatically swap two selections?

Example: after selecting 'short' and 'simple' in the sentence 'This is a short and simple example' and swapping them, the sentence would become 'This is a simple and short example'

  1. For simple word swapping, it would be handy if such a function would default to selecting the words with a cursor (empty selection) in them.

Example: place two cursors in the above sentence, one in the word 'simple' and one in 'short', and they'll be automatically selected and reversed.

  1. For swapping a word and a phrase, it would be handy if I could select the word by Ctrl-clicking and the phrase by dragging to have it automatically selected (that is, a combination of 1 and 2).

  2. More generally, I would like to be able to invert any number of selections of any length anywhere in a document.

Simple example: the text 'a b c d e f g' with the a, the c, the e and the g selected becomes 'g b e d c f a'.

Obviously swapping two selections is a special case of reversing any number of selections.


Solution

  • Here is a JavaScript macro for EmEditor to swap multiple selections or words at the cursor positions.

    Redraw = false;  // optimize for speed
    CombineHistory =  true; // combine Undo
    nMax = document.selection.Count;  // retrieve the number of selections
    ax = Array( nMax );
    ax2 = Array( nMax );
    ay = Array( nMax );
    ay2 = Array( nMax );
    as = Array( nMax );
    for( i = 0; i < nMax; ++i ) {  // retrieve position of each selection
        ax[i] = document.selection.GetTopPointX( eePosLogical, i + 1 );
        ax2[i] = document.selection.GetBottomPointX( eePosLogical, i + 1 );
        ay[i] = document.selection.GetTopPointY( eePosLogical, i + 1 );
        ay2[i] = document.selection.GetBottomPointY( eePosLogical, i + 1 );
    }
    for( i = 0; i < nMax; ++i ) {  // retrieve word text and positions
        if( ax[i] == ax2[i] && ay[i] == ay2[i] ) {
            document.selection.SetActivePoint( eePosLogical, ax[i], ay[i] );  // set cursor position
            document.selection.SelectWord();  // select a word
            ax[i] = document.selection.GetTopPointX( eePosLogical );    // retrieve selection position
            ax2[i] = document.selection.GetBottomPointX( eePosLogical );
            as[i] = document.selection.Text;  // retrieve selected text
        }
        else {
            document.selection.SetActivePoint( eePosLogical, ax[i], ay[i] );
            document.selection.SetActivePoint( eePosLogical, ax2[i], ay2[i], true );
            as[i] = document.selection.Text;  // retrieve selected text
        }
    }
    for( i = nMax - 1; i >= 0; --i ) {   // set a selection from bottom to top
        document.selection.SetActivePoint( eePosLogical, ax[i], ay[i] );
        document.selection.SetActivePoint( eePosLogical, ax2[i], ay2[i], true );
        document.selection.Text = as[nMax - i - 1];  // set new text
    }
    

    To run this, save this code as, for instance, Macro.jsee, and then select this file from Select... in the Macros menu. Finally, select Run Macro.jsee in the Macros menu while multiple selections are made.