Cycle case of selected text ( Mixed -> Upper -> Lower -> Initial Caps ...)


#1

Cycle the case of the selected text through three states:

Mixed case -> Upper -> Lower -> Title Case -> Upper etc.

http://actions.getdrafts.com/a/1I9

JS source:

(() => {
    'use strict';

    // main :: () -> IO String
    const main = () => {
        const
            e = editor,
            strReCased = caseToggled(
                e.getSelectedText()
            );
        return strReCased.length > 0 ? (
            e.setSelectedText(
                strReCased
            ),
            strReCased
        ) : '';
    };

    // caseToggled :: String -> String
    const caseToggled = s => {
        const cs = chars(s);
        return !any(isUpper, cs) ? (
            toInitialCaps(s)
        ) : !any(isLower, cs) ? (
            toLower(s)
        ) : toUpper(s);
    };

    // GENERIC FUNCTIONS ------------------------------------

    // any :: (a -> Bool) -> [a] -> Bool
    const any = (p, xs) => xs.some(p);

    // chars :: String -> [Char]
    const chars = s => s.split('');

    // isLower :: Char -> Bool
    const isLower = c =>
        /[a-z]/.test(c);

    // isUpper :: Char -> Bool
    const isUpper = c =>
        /[A-Z]/.test(c);

   // regexMatches :: String -> String -> [[String]]
    const regexMatches = (strRgx, strHay) => {
        const rgx = new RegExp(strRgx, 'g');
        let m = rgx.exec(strHay),
            xs = [];
        while (m)(xs.push(m), m = rgx.exec(strHay));
        return xs;
    };

    // toLower :: String -> String
    const toLower = s => s.toLowerCase();

    // toInitialCaps :: String -> String
    const toInitialCaps = s =>
        regexMatches(/(\w)(\w*)(\b[\W]*|$)/g, s)
        .map(ms => ms[1].toUpperCase() + ms[2].toLowerCase() + ms[3])
        .join('');

    // toUpper :: String -> String
    const toUpper = s => s.toUpperCase();

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

#2

Love it, thank you one less action to make. :+1:t2:


#3

Will try to put the text format in a Prompt :grinning: or would you know if that’s been done? Thx


#4

Sounds like a good exercise


#5

There’s a bug in this such that single letter words disappear when toggling to title case.

Also it’s not true title case, it’s just capitalized. Not to be pedantic but this matters if you’re trying to keep a clean bibliographic database. See my Alfred workflow for the difference and an example for how to implement in Perl: https://www.alfredforum.com/topic/2180-case-converter-including-title-case/

And here’s a JS implementation based on the Perl script that I borrowed there:


#6

Thanks for the bug report, and sure – it’s the most simple-minded version of Title Case - better described as Capitalized – as in the NSString method:capitalizedStringWithLocale

single letter words disappear when toggling to title case

Could you give us an example ?

(I can see single letter words left unaffected by case toggling, but I haven’t managed to get one to disappear yet).

Ah. Got it : -)


#7

Single-character-word issue fixed now, I think, and ‘title’ changed to ‘Initial Caps’

Thanks again, and let me know if there’s anything else.

http://actions.getdrafts.com/a/1I9


#8

Updated the Initial Caps phase of the cycle to work with diacritics on latin alphabets.

http://actions.getdrafts.com/a/1I9


#9

Updated again – to select the nearest word (before case-cycling) if the selection is collapsed.

http://actions.getdrafts.com/a/1I9


#10

I notice that output from this regex doesn’t always match that produced with AP, MLA, Chicago or Wikipedia settings from online converters like:

https://titlecaseconverter.com/

or

https://capitalizemytitle.com/

(particularly when small words are flanked by or adjacent to hyphens or other punctuation)

Is there a tool which seems more or less canonical to you for Anglo title casing, and which could be used for testing a slightly more legible and maintainable JS function ?