Is there a way to keep the text cursor in place?

Hello all!

I have created a script that focuses in on the project that the text cursor is in.

Currently, it places the cursor at the start of the item that it was in.

Is it possible to keep the cursor in the exact position that it was in before the script is executed?

Here is the current script:

function TaskPaperContext(editor, options) {
	let outline = editor.outline
	let selection = editor.selection
	let currentItem = selection.startItem
	let followingProjects = outline.evaluateItemPath('ancestor-or-self::project', currentItem)
	let nextProject = followingProjects[followingProjects.length - 1]
	if (nextProject) {
		editor.focusedItem = nextProject
	editor.moveSelectionToItems(currentItem)
	}
}

Application('TaskPaper').documents[0].evaluate({
  script: TaskPaperContext.toString()
});

Yes, I think that is certainly possible. Try this:

(() => {
    "use strict";

    // main :: IO ()
    const main = () => {
        const inner = () => {
            const
                ds = Application("TaskPaper")
                .documents;

            return either(alert("Problem"))(x => x)(
                bindLR(
                    0 < ds.length ? (
                        Right(ds.at(0))
                    ) : Left("No TaskPaper documents open")
                )(
                    d => d.evaluate({
                        script: tp3Context.toString(),
                        withOptions: {}
                    })
                )
            );
        };

        // -------------- TASKPAPER CONTEXT --------------
        // tp3Context :: (Editor, Dict) -> Either String a
        const tp3Context = (editor, options) => {
            // main :: ()
            const main = () => {
                const
                    outline = editor.outline,
                    seln = editor.selection,
                    item = seln.startItem,
                    locn = seln.startOffset,
                    xs = seln.startItem.ancestors.filter(
                        x => "project" === x.getAttribute("data-type")
                    );

                return bindLR(
                    0 === xs.length ? (
                        Left("No parent project")
                    ) : Right(last(xs))
                )(
                    x => (
                        editor.focusedItem = x,
                        editor.moveSelectionToItems(item, locn),
                        Right(`Focused on project "${
                            x.bodyContentString
                        }"`)
                    )
                )
            };

            // FUNCTIONS --------------------------------
            // GENERIC FUNCTIONS --------------------------------------------
            // https://github.com/RobTrew/prelude-jxa
            // Right :: b -> Either a b
            const Right = x => ({
                type: "Either",
                Right: x
            });

            // Left :: a -> Either a b
            const Left = x => ({
                type: "Either",
                Left: x
            });

            // bindLR (>>=) :: Either a ->
            // (a -> Either b) -> Either b
            const bindLR = m =>
                mf => m.Left ? (
                    m
                ) : mf(m.Right);

            // last :: [a] -> a
            const last = xs =>
                // The last item of a list.
                0 < xs.length ? (
                    xs.slice(-1)[0]
                ) : null;

            return main();
        };

        // alert :: String => String -> IO String
        const alert = title => s => {
            const
                sa = Object.assign(Application("System Events"), {
                    includeStandardAdditions: true
                });

            return (
                sa.activate(),
                sa.displayDialog(s, {
                    withTitle: title,
                    buttons: ["OK"],
                    defaultButton: "OK",
                    withIcon: sa.pathToResource("TaskPaper.icns", {
                        inBundle: "Applications/TaskPaper.app"
                    })
                }),
                s
            );
        };

        return inner();
    };

    // GENERIC FUNCTIONS --------------------------------------------
    // https://github.com/RobTrew/prelude-jxa
    // Right :: b -> Either a b
    const Right = x => ({
        type: "Either",
        Right: x
    });

    // Left :: a -> Either a b
    const Left = x => ({
        type: "Either",
        Left: x
    });

    // bindLR (>>=) :: Either a ->
    // (a -> Either b) -> Either b
    const bindLR = m =>
        mf => m.Left ? (
            m
        ) : mf(m.Right);

    // either :: (a -> c) -> (b -> c) -> Either a b -> c
    const either = fl =>
        // Application of the function fl to the
        // contents of any Left value in e, or
        // the application of fr to its Right value.
        fr => e => "Left" in e ? (
            fl(e.Left)
        ) : fr(e.Right);

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

Amazing! Thank you @unlocked2412 !

You’re welcome, @Jim.

@unlocked2412 — A question:

I noticed that you invoke a message (No parent project) if the cursor is in a top level project.

Is there a simple way to change that behavior?

Basically. if the cursor is in a top level project, I want that project to be focused and the selection to remain in its same position it was in before the project was focused.

Yes, that is possible. I will look into it, tomorrow.

1 Like

Much appreciated!

I’ve found an edge case. What would you like to happen in case the cursor is in a project which happens to have a parent project ?

In these edge cases, for my needs/desires, I would want to focus in on the sub-project (To Create Items, in the example) and keep the cursor in place (in the example: It|ems)

Good. I’ve just published the script to my repository. Tell me if it does what you want.

1 Like

It works well!

Thank you again @unlocked2412 !