Text Editing & Formatting

Clean Em Dashes

Replace excessive em dashes with natural punctuation.

OfficialOn-device

Download action

What Clean Em Dashes does

Converts paired and standalone em dashes to commas, changes dash-led lines to Markdown bullets, and cleans spaced en dashes and double hyphens. Numeric en-dash ranges stay unchanged.

CategoryText Editing & Formatting
Action typeJavaScript
Default resultReplace selected text by default
InternetNot required

Action and outcome

Clean Em Dashes example
ActionOutcome
Clean Em DashesSelected text: The new release — announced yesterday — delivers faster speeds.The new release, announced yesterday, delivers faster speeds.

Use Clean Em Dashes

Follow these steps to replace excessive em dashes with natural punctuation with ActionClip.

  1. Select the text you want to use.
  2. Choose Clean Em Dashes from ActionClip.
  3. In editable text, ActionClip replaces the selection immediately. In read-only text, it opens the result for review and copying.

Requirements and privacy

  • Before you start: No setup required.
  • Your selected text: Runs locally in ActionClip’s JavaScript sandbox. Selected text does not leave your Mac.

Action script

The script below is included so you can inspect how the action works before downloading it.

JavaScript definition
function isHorizontalSpace(character) {
  return character === " " || character === "\t";
}

function dashLengthAt(text, index) {
  var character = text.charAt(index);
  if (character === "—") return 1;

  if (character === "-" && text.charAt(index + 1) === "-") {
    var end = index + 2;
    while (text.charAt(end) === "-") end += 1;
    return end - index;
  }

  if (
    character === "–" &&
    isHorizontalSpace(text.charAt(index - 1)) &&
    isHorizontalSpace(text.charAt(index + 1))
  ) {
    return 1;
  }

  return 0;
}

function removeTrailingSpace(text) {
  var end = text.length;
  while (end > 0 && isHorizontalSpace(text.charAt(end - 1))) end -= 1;
  return text.slice(0, end);
}

function cleanProseLine(line) {
  var output = "";
  var index = 0;

  while (index < line.length) {
    var dashLength = dashLengthAt(line, index);
    if (dashLength === 0) {
      output += line.charAt(index);
      index += 1;
      continue;
    }

    output = removeTrailingSpace(output);
    var nextIndex = index + dashLength;
    while (nextIndex < line.length && isHorizontalSpace(line.charAt(nextIndex))) {
      nextIndex += 1;
    }

    var left = output.charAt(output.length - 1);
    var right = line.charAt(nextIndex);
    var rightHasPunctuation = /[.,;:!?)\]}]/.test(right);
    var leftHasPunctuation = /[.,;:!?(\[{]/.test(left);

    if (left && right && !rightHasPunctuation) {
      if (!leftHasPunctuation) output += ",";
      if (left !== "(" && left !== "[" && left !== "{") output += " ";
    }

    index = nextIndex;
  }

  return output;
}

function cleanLine(line) {
  var bullet = line.match(/^([ \t]*)(?:—|-{2,})[ \t]+/);
  if (bullet) {
    return bullet[1] + "- " + cleanProseLine(line.slice(bullet[0].length));
  }
  return cleanProseLine(line);
}

function run(selected_text) {
  if (!selected_text) return "";

  var pieces = selected_text.split(/(\r\n|[\n\r\u000B\u000C\u0085\u2028\u2029])/);
  for (var index = 0; index < pieces.length; index += 2) {
    pieces[index] = cleanLine(pieces[index]);
  }
  return pieces.join("");
}