How to deselect & move cursor to end?

Hi. I use a couple of keyboard actions to format text for use in my to-do lists. A part of this formatting involves moving the cursor to the start of the draft and then inserting a special character using the following:

editor.setSelectedRange(0, 0);
editor.setSelectedText("✦ ");

Sometimes, after inserting the character, I realise I want to continue typing the to-do. The trouble is that the above leaves the "✦ " selected at the start of the draft. I can’t figure out how to deselect the special character and move the cursor to the end as part of the action to allow for seamless writing. Any help would be appreciated. Ta.

The special character goes to the start of the whole draft (rather than the start of the line) ?

Something like this ?

(() => {

    const
        strPrefix = '✦ ',
        strDraft = draft.content,
        // Is the mark needed, or already there ?
        blnDelta = (strPrefix !== strDraft.slice(0, 2));

    return blnDelta ? (() => {
        const strUpdated = strPrefix + strDraft;
        return (
            draft.content = strUpdated,
            draft.update(),
            editor.setSelectedRange(strUpdated.length, 0),
            editor.getSelectedRange()
        );
    })() : 'stet';
})()

Thanks! That worked great. I hadn’t even considered adding the character at the start of each line as I tend to only use the action for adding single to-dos as/when they occur to me throughout the day. If there’s a simple adjustment that’ll do the start of every line thing, I’d find that really useful too (not competent enough to figure it out myself, I tend to cobble things together from other people’s actions).

A simple version (assuming that you don’t need to handle indented lines) might look something like:

(() => {
    const
        strPrefix = '✦ ',
        strUpdated = draft.content
        .split('\n')
        .map(
            s => strPrefix !== s.slice(0, 2) ? (
                strPrefix + s
            ) : s
        )
        .join('\n');
    return (
        draft.content = strUpdated,
        draft.update(),
        editor.setSelectedRange(strUpdated.length, 0),
        editor.getSelectedRange()
    );
})()

Cheers! That’s so cool. If it’s not too much to ask, I’ve also been using the following to convert the to-dos to title case as part of the same keyboard action:

/* 
  * To Title Case 2.1 – http://individed.com/code/to-title-case/
  * Copyright © 2008–2013 David Gouch. Licensed under the MIT License.
 */

String.prototype.toTitleCase = function(){
  var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i;

  return this.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, function(match, index, title){
    if (index > 0 && index + match.length !== title.length &&
      match.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
      (title.charAt(index + match.length) !== '-' || title.charAt(index - 1) === '-') &&
      title.charAt(index - 1).search(/[^\s-]/) < 0) {
      return match.toLowerCase();
    }

    if (match.substr(1).search(/[A-Z]|\../) > -1) {
      return match;
    }

    return match.charAt(0).toUpperCase() + match.substr(1);
  });
};

// Addition by Dr. Drang to work as a Drafts keyboard extension.
// Convert selected text to title case.
var s = getSelectedText();
setSelectedText(s.toTitleCase());

Is there a way to have this work in conjunction with the steps that you’ve provided (I tried dropping everything into the same script box but the title case doesn’t apply)? No worries if not, it’s easy enough to trigger each via separate buttons anyway. Thanks again!

Something like this ?

(I’ve taken the liberty, accorded by the titleCase function’s MIT license, of tidying it a little)

(() => {
    'use strict';

    const main = () => {
        const
            strPrefix = '✦ ',
            strUpdated = draft.content
            .split('\n')
            .map(
                s => strPrefix !== s.slice(0, 2) ? (
                    strPrefix + toTitle(s)
                ) : toTitle(s)
            )
            .join('\n');
        return (
            draft.content = strUpdated,
            draft.update(),
            editor.setSelectedRange(strUpdated.length, 0),
            editor.getSelectedRange()
        );
    };


    /* Adapted by Rob Trew (2018) from a function by David Gouch:
     *
     * To toTitleCase 2.1 – http://individed.com/code/to-t-case/
     * Copyright © 2008–2013 David Gouch. Licensed under the MIT License.
     */
    // toTitle :: String -> String
    const toTitle = s => {
        const
            smalls = `a an and as at but by en for if
                        in nor of on or per the to vs`,
            rgxSmall = new RegExp(
                `^(${smalls.split(/\s+/).join('|')}?\.?|via)$`,
                'i'
            );
        return s.replace(
            /[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g,
            (m, i, t) => (i > 0 && i + m.length !== t.length &&
                m.search(rgxSmall) > -1 && t.charAt(i - 2) !== ":" &&
                (t.charAt(i + m.length) !== '-' ||
                    t.charAt(i - 1) === '-') &&
                t.charAt(i - 1).search(/[^\s-]/) < 0) ? (
                m.toLowerCase()
            ) : (m.substr(1).search(/[A-Z]|\../) > -1) ? (
                m
            ) : m.charAt(0).toUpperCase() + m.substr(1)
        );
    };

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

Perfect! Exactly what I needed (and I didn’t even know I needed it). I’ll be using this every day for the foreseeable future. Thanks so much.

1 Like