Block Travel Script

This script implements block travel (go to next empty line) as inspired by this Visual Studio plugin.

tell front document of application "Bike"
	set currentId to id of selection row
	set candidateRows to rows whose visible = true and (name = "" or id = currentId)
	
	set foundCurrent to false
	repeat with each in candidateRows
		if id of each = currentId then
			set foundCurrent to true
		else if foundCurrent then
			select at each
			return
		end if
	end repeat
	
	beep
end tell

Note: This script is pretty brute force :). I think it should be fine for intended use as a command, but if there’s a more elegant way to do it in AppleScript let me know.

1 Like

Here, FWIW, is a JavaScript for Automation version,
which can be used for moving DOWN (next blank line)
or moving UP (previous blank line).

(the directional option is set by editing the value of directionDown at the top of the script to true or false)

To test in Script Editor.app, set the language selector at top left to JavaScript, and paste in all of the code (scrolling right down to the bottom) behind the disclosure triangle below:

Expand disclosure triangle to view JS Source
(() => {
    "use strict";

    // BIKE – Select next (or previous) empty line.
    // Rob Trew @2022
    // Ver 0.02

    // JavaScript for Automation
    // Test in Script Editor with language selector at
    // top left set to JavaScript

    // ---------------- DIRECTION OPTION -----------------
    // To jump to PREVIOUS blank line,
    // edit the value of `directionDown` to false.
    const directionDown = true;

    // main :: IO ()
    // eslint-disable-next-line max-lines-per-function
    const main = () => {
        const
            bike = Application("Bike"),
            doc = bike.documents.at(0);

        return doc.exists() ? (() => {
            const
                // IDs paired with names
                rows = doc.rows.where({
                    visible: true
                }),
                idNames = zip(
                    rows.id()
                )(
                    rows.name()
                ),

                // Position of selected line
                selectedID = doc.selectionRow.id(),
                selnIndex = idNames.findIndex(
                    ab => selectedID === ab[0]
                ),

                // Offset of first blank in remaining or
                // preceding lines.
                nextIndex = (
                    directionDown ? (
                        idNames.slice(1 + selnIndex)
                    ) : (
                        idNames.slice(0, selnIndex)
                        .reverse()
                    )
                )
                .findIndex(ab => "" === ab[1]);

            bike.activate();

            // First following blank line selected (if any)
            return -1 !== nextIndex ? (
                doc.select({
                    at: rows.at(
                        directionDown ? (
                            selnIndex + (1 + nextIndex)
                        ) : selnIndex - (1 + nextIndex)
                    )
                }),
                "Selected"
            ) : (
                Object.assign(
                    Application.currentApplication(), {
                        includeStandardAdditions: true
                    }
                ).beep(),
                `No more blank rows visible in ${doc.name()}`
            );
        })() : "No document open in Bike.";
    };

    // --------------------- GENERIC ---------------------
    // https://github.com/RobTrew/prelude-jxa


    // zip :: [a] -> [b] -> [(a, b)]
    const zip = xs =>
        // The paired members of xs and ys, up to
        // the length of the shorter of the two lists.
        ys => Array.from({
            length: Math.min(xs.length, ys.length)
        }, (_, i) => [xs[i], ys[i]]);


    // MAIN ---
    return main();
})();

If you use Keyboard Maestro, the pair of macros in this zip:

NextOrPreviousBikeEmptyLine.kmmacros.zip (2.6 KB)

initially assign the two directions to ⌥⌘Up Arrow and ⌥⌘Down Arrow respectively, but you can readily change to any other pair of keyboard bindings.

2 Likes

Thanks @complexpoint. The one functional difference I notice is I think the JavaScript version is navigating to next empty row, while my version is navigating to next visible empty row.

2 Likes

Thanks ! I’ll make an edit to ver 0.02 :slight_smile:


UPDATE

JS Source and Keyboard Maestro macros updated to Ver 0.02 above, to jump to next or previous visible row that is empty.

2 Likes