Drafts Action Directory

Change Indentation

Posted by @edgauthier, Last update about 1 month ago

Given a selection of lines, or the entire draft, determines the shortest whitespace indentation (tabs or spaces), and then offers a choice to change the indentation.

If both tabs and spaces are mixed, this script does its best to convert to one or the other, but doesn’t handle all cases.

Defaults to offering options for 2-space, 4-space, or tab indentation. if you want other options, change the defaultOpts list in the script. “Spaces” entries must start with a number.

Install

Steps

  • script

    // Editor - Change Indentation
    
    // Starts with either the selected text (extended to include the entire line(s)) or uses the entire draft's contents if there is no selection.
    
    // Determines the shortest whitespace indentation (tabs or spaces), and then offers a choice to change the indentation.
    
    // If both tabs and spaces are mixed, this script does its best to convert to one or the other, but doesn't handle all cases. 
    
    // Defaults to offering options for 2-space, 4-space, or tab indentation. if you want other options, change the defaultOpts list below. "Spaces" entries must start with a number. 
    
    (() => {
    
      const defaultOpts = ["Tabs", "2 spaces", "4 spaces"];
    
      // Return the selected lines if any
      // Otherwise, select and return entire draft contents
      const getSelectedLinesOrAllText = () => {
        const
          r = editor.getSelectedRange(),
          rl = editor.getSelectedLineRange(),
          s = r[1] > 0 
            // if there's a selection - get the selected line text
            ? editor.getTextInRange(...rl)
            : draft.content,
          // start new selection at beginning of draft if nothing was selected
          // otherwise, start selection at the beginning of selected line
          p = r[1] === 0 ? 0 : rl[0];
        // update select range
        editor.setSelectedRange(p, s.length); 
        return s;
      };
    
      const
        // Get current state
        lines = getSelectedLinesOrAllText().split("\n"),
        range = editor.getSelectedRange(),
        indents = lines.reduce((ls, l) => 
          (m = l.match(/^\s+/)) ? ls.concat(m) : ls, []),
        mixed = (indents.some(s => s.search("\t") !== -1)
          && indents.some(s => s.search(" ") !== -1)),
        tabs = indents.every(s => s.search(" ") === -1),
        spaces = indents.every(s => s.search("\t") === -1),
        // get the minimum length of indents with spaces
        spacesLen = tabs ? 0 
          : indents.filter(s => s.search(" ") !== -1)
            .map(s => s.length)
            .reduce((m, l) => Math.min(m, l)),
        curIndent = tabs ? "\t" : " ".repeat(spacesLen),
        curIndentName = mixed ? "Mixed tabs and spaces" 
          : (tabs ? "Tabs" : spacesLen + " spaces"),
    
        // Set up prompt with buttons to change indentation
        // Exclude the current indentation if its one of the defaults
        opts = defaultOpts.filter(s => s != curIndentName),
        p = opts.reduce((p, b) => {p.addButton(b);return p},
          Object.assign(Prompt.create(), 
            {title:"Switch Indentation", message:"Current: " + curIndentName}));
    
      if (!p.show()){
        context.cancel();
        return;
      }
    
      const
        newIndent = (p.buttonPressed === "Tabs") ? "\t"
          : " ".repeat(parseInt(p.buttonPressed, 10)),
        // pre-processing: in mixed tab/spaces scenario, replace each
        // tab with spaces so subsequent processing works as expected
        preProcessed = !mixed ? lines // not mixed, just return the lines
          : lines.map(l => l.replace(/^\t+/,
            m => curIndent.repeat(m.length))),
        // replace all leading whitespace with new indentation
        newText = preProcessed.map(l => l.replace(/^\s+/, 
          m => newIndent.repeat(m.length/curIndent.length))).join("\n");
    
      editor.setSelectedText(newText);
      editor.setSelectedRange(range[0], newText.length);
    
    })();

Options

  • After Success Default
    Notification Error
    Log Level Error

Comments

Actions available in the Action Directory are uploaded by community members. Use appropriate caution reviewing downloaded actions before use.