Paste weekday name for ISO8601 date in filepath of active document

My habit is to work with files which include a YYYY-MM-DD date in the filepath.

The corresponding weekday name may not be clear at a glance

  • once the date is no longer close to today, or
  • if it falls at some point in the future.

This macro just pastes the weekday name which corresponds to the last (if any) YYYY-MM-DD date found in the file path of the active document.

In other words, if both folder and filename contain a yyyy-mmm-dd date, then we paste the file date.

It should work in many (not all) macOS document-based applications.

Paste weekday name for ISO8601 date in filepath of active document.kmmacros (8.0 KB)


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

    ObjC.import("AppKit");

    // Rob Trew @2022

    // Weekday name from *last* ISO8601 date in the filepath
    // of the front document.
    // E.g. if both folder and file contain a date,
    // we take the file date.

    // Ver 0.02

    const main = () =>
        either(
            alert("Day name from file path")
        )(
            // Returned to Keyboard Maestro
            weekdayName => weekdayName
        )(
            dayNameFromFrontWindowFilePathLR()
        );

    // ------- WEEKDAY FROM FRONT WINDOW FILEPATH --------

    // dayNameFromFrontWindowFilePathLR :: IO () ->
    // Either String String
    const dayNameFromFrontWindowFilePathLR = () =>
        // Either a message, or the locale name of the week
        // day whose date is found in ISO8601 format in the
        // file path of the document in the front window.
        bindLR(
            filePathFromFrontWindowLR()
        )(
            compose(
                fmapLR(
                    dte => dte.toLocaleDateString(
                        $.NSLocale.currentLocale
                        .localeIdentifier, {
                            weekday: "long"
                        }
                    )
                ),
                dayDateFromStringIncludingISO8601LR
            )
        );


    // dateFromStringWithISO8601LR :: String ->
    // Either String Date
    const dayDateFromStringIncludingISO8601LR = s => {
        // Either a message or a date corresponding to
        // the last yyyy-mm-dd string found in s.
        const ms = s.match(/\d{4}-\d{2}-\d{2}/ug);

        return Boolean(ms) ? (
            // Rightmost match
            Right(new Date(last(ms)))
        ) : Left(
            `No ISO8601 day date found in filepath:\n\n\t'${s}'`
        );
    };


    // --------------------- JXA ---------------------

    // 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"
                }),
                s
            );
        };


    // filePathFromFrontWindowLR  ::
    // () -> Either String FilePath
    const filePathFromFrontWindowLR = () => {
        const
            appName = ObjC.unwrap(
                $.NSWorkspace.sharedWorkspace
                .frontmostApplication.localizedName
            ),
            ws = Application("System Events")
            .applicationProcesses.byName(appName).windows;

        return bindLR(
            0 < ws.length ? Right(
                ws.at(0).attributes.byName("AXDocument")
                .value()
            ) : Left(
                `No document windows open in ${appName}.`
            )
        )(
            docURL => null !== docURL ? (
                Right(decodeURIComponent(docURL.slice(7)))
            ) : Left(
                `No saved document active in ${appName}.`
            )
        );
    };

    // --------------------- GENERIC ---------------------

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


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


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

    // compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
    const compose = (...fs) =>
        // A function defined by the right-to-left
        // composition of all the functions in fs.
        fs.reduce(
            (f, g) => x => f(g(x)),
            x => x
        );


    // 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 => e.Left ? (
            fl(e.Left)
        ) : fr(e.Right);


    // fmapLR (<$>) :: (b -> c) -> Either a b -> Either a c
    const fmapLR = f =>
        // Either f mapped into the contents of any Right
        // value in e, or e unchanged if is a Left value.
        e => "Left" in e ? (
            e
        ) : Right(f(e.Right));


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

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