Remove spaces before and after punctuation

Hi all,

Hope you can help. I’m hoping to achieve the following using an AppleScript:

  • select all text in current/active window (eg Pages, Notes, Mail)
  • copy that text
  • clean-up the copied text by: 1. Removing any spaces before the punctuation ,-?/!)]}:; and 2. removing any spaces after -/([{
  • paste the cleaned-up text back into the app

Hope this is possible. TIA

Mark

Here’s an example of the appleScript part to actually make the changes.

The next step would be to add handlers that would get selected text from Pages, Notes and Mail, and then replace the selection with the fixed text.

But, to do that we’d need to know what context the script would be running in. You could do a version for each app, living in the Script Menu. (That’s how I would do it).

use AppleScript version "2.4"
use scripting additions

set myText to " , - ? / !{{ { }} { ) ].   } : ; - / ( [ {"

FixPunctuation(myText)

on FixPunctuation(myText)
   set killSpaceBefore to {",", "-", "?", "/", "!", ")", "]", "}", ":", ";"}
   set killSpaceAfter to {"-", "/", "(", "[", "{"}
   repeat with thisChar in killSpaceBefore
      set myText to my ReplaceAllInText(" " & thisChar as text, thisChar as text, myText)
   end repeat
   repeat with thisChar in killSpaceAfter
      set myText to my ReplaceAllInText((thisChar as text) & " ", thisChar as text, myText)
   end repeat
   return myText
end FixPunctuation

on ReplaceAllInText(findString, replaceString, textToFix)
   local findString, replaceString, textToFix
   set saveTID to AppleScript's text item delimiters
   repeat
      set AppleScript's text item delimiters to findString as list
      set textToFix to every text item of textToFix
      if (count of textToFix) = 1 then
         set textToFix to textToFix as text
         exit repeat
      end if
      set AppleScript's text item delimiters to {replaceString}
      set textToFix to textToFix as text
      if replaceString is in {findString} then exit repeat -- exits after one pass to avoid infinite loop
   end repeat
   set AppleScript's text item delimiters to saveTID
   return textToFix as text
end ReplaceAllInText

Thank you so much for your reply and for doing that for me!

I use HotKey to run applications/scripts from keyboard shortcuts, so – for simplicity – I was wanting to use the same script (therefore the same keyboard shortcut) for each app. I’ll be ‘cleaning up’ the text in any app that deals with bodies of text, so Pages/Notes/Mail/Safari/TextEdit and any others. Hope that helps?

To accomplish what you want, you have to use GUI scripting, which is far from reliable. Also, the script has to be run in the background, and I don’t know if that’s the case with HotKey. Anyways, I tested the following script with TextEdit, and it worked as expected:

use framework "Foundation"
use scripting additions

tell application "System Events" to key code 0 using command down -- select all
delay 0.5
tell application "System Events" to key code 8 using command down -- copy
delay 0.5

set theString to (the clipboard)
set theString to current application's NSMutableString's stringWithString:theString
set thePattern to " ([\\-?/!)\\]}:;])"
theString's replaceOccurrencesOfString:thePattern withString:"$1" options:1024 range:{0, theString's |length|()}
set thePattern to "([\\-/(\\[{]) "
theString's replaceOccurrencesOfString:thePattern withString:"$1" options:1024 range:{0, theString's |length|()}
set the clipboard to (theString as text)

delay 0.5
tell application "System Events" to key code 9 using command down -- paste

A better approach IMO is:

  1. Manually select and copy the text to the clipboard in whatever app is being used.
  2. Run the script which will clean the text on the clipboard.
  3. Manually paste the cleaned text into the app.

Or, even better, use a shortcut if your version of macOS includes the Shortcut app. It is really good at doing stuff like this.

Many thanks for doing this :slight_smile:

It returned the error “System Events got an error: Untitled is not allowed to send keystrokes. (1002)”.

Does that mean we can’t do it all fully in AppleScript? If not, I have BetterTouchTool so could do a sequence including the keystrokes before and after the script (it can also have delays between each step). So in effect I will ‘manually’: select all, copy, [run the clean-up script on the clipboard text, including copying the results back to clipboard], paste.

If that sounds okay, when you have time, would you mind amending the [script] as above please? :slight_smile:

Sadly I looked into Shortcuts earlier today but I don’t have it on my (oldish) version of macOS!

If you get the “not allowed to send keystrokes” I think the solution is to go to System Preferences> Security & Privacy> Accessibility

Allow Shortcuts (or HotKey) to control your computer.

Thanks, I’ve just done that but I get the same error…

I would take a different approach with this. For this project I created a text file on my Desktop and pasted this following code into that new document and named it “SED_Commands.txt”.

pbpaste |sed -E -e 's@\ ,@,@g' -e 's@\ -@-@g' -e 's@\ \?@\?@g' -e 's@\ /@/@g' -e 's@\ !@!@g' -e 's@\ )@)@g' -e 's@\ ]@]@g' -e 's@\ }@}@g' -e 's@\ :@:@g' -e 's@\ ;@;@g' -e 's@-\ @-@g' -e 's@/\ @/@g' -e 's@\(\ @\(@g' -e 's@\[\ @\[@g' -e 's@{\ @{@g' |pbcopy

I did this because it’s easier to just use AppleScript to read in all of those sed commands than to worry about figuring out all of the proper escape sequences to use in the do shell script command.

Then in Automator, I created a new Quick Action and added A Run AppleScript action to the Workflow with the following code, and saved that as “SED TEMP”

tell application "System Events" to keystroke "c" using command down
delay 0.1
do shell script (read alias (((path to desktop) as text) & "SED_Commands.txt"))
delay 0.1
tell application "System Events" to keystroke "v" using command down

Now because I created a new Quick Action, I can now assign this a Keyboard Shortcut, in System Settings

Now I’m all set. Anytime I have text selected in any application that I want to remove the spaces before and after the punctuation, all I need to do is use my new Keyboard Shortcut, and the currently selected text will be edited.

SED

NOTE: You will need to grant the appropriate permissions in System Settings to allow System Events and Automator to control your computer.

MW21. I retested my earlier script and it worked as expected. I don’t know why it doesn’t work for you.

I’ve modified my script to do the following:

  1. Get the contents of the clipboard.
  2. Report an error if the contents of the clipboard is not a string (there may be a delay of a few seconds before reporting this error).
  3. Remove unwanted spaces from the string.
  4. Show a dialog reporting the number of spaces deleted (this dialog can be omitted if desired).
  5. Place the edited string back on the clipboard.
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

try
	set theString to (the clipboard)
	set theString to current application's NSMutableString's stringWithString:theString
	set thePattern to " ([,\\-?/!)\\]}:;])"
	set countOne to theString's replaceOccurrencesOfString:thePattern withString:"$1" options:1024 range:{0, theString's |length|()}
	set thePattern to "([\\-/(\\[{]) "
	set countTwo to theString's replaceOccurrencesOfString:thePattern withString:"$1" options:1024 range:{0, theString's |length|()}
	display alert "The contents of the clipboard were processed" message ((countOne + countTwo) as text) & " spaces were deleted" buttons {"OK"} default button 1
	set the clipboard to (theString as text)
on error
	display alert "An error has occured" message "This most ccommonly happens when a string is not found on the clipboard"
end try

BTW, you may need to place a delay in Better Touch Tool before running this script. Also, as written my script only removes one space before or after the specified characters, but this can be changed to remove multiple spaces if that’s desired.

Thanks for your help everyone, I’ve now got it working! The only remaining problem is that it’s stripped any styling from the cleaned-up text, which will be a problem e.g. in a text document in Notes or Pages. Is there any way around this?

Maybe. It would take a different approach for each app, depending on the app’s dictionary.

I think I have something similar for Pages, I’ll see.

Also, let us know what you did to get it working. I’m curious.

Great, thank you :pray:t2:

I used peavine’s amended script above, run by a keyboard shortcut in BetterTouchTool, which also included the Mac keystrokes (select all, copy, paste) around the script, with a short delay for the script to complete. Works brilliantly in every app, save for the formatting issue.

1 Like

I’ve just realised that trimming a double- or triple-space down to a single space, would also be beneficial to me. Would anyone be so kind as to paste me a script for that that I can include in Peavine’s script above?

MW21. I’ve revised my script to replace two and three consecutive spaces with a single space. The timing result with a document that contained 1,025 paragraphs of text with 19,456 replacements was 40 milliseconds.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

try
	set theString to (the clipboard)
	set theString to current application's NSMutableString's stringWithString:theString
	set thePattern to " {2,3}" -- 2 or 3 consecutive spaces
	set countOne to theString's replaceOccurrencesOfString:thePattern withString:" " options:1024 range:{0, theString's |length|()}
	set thePattern to " ([,\\-?/!)\\]}:;])" -- space before specified character
	set countTwo to theString's replaceOccurrencesOfString:thePattern withString:"$1" options:1024 range:{0, theString's |length|()}
	set thePattern to "([\\-/(\\[{]) " -- space after specified character
	set countThree to theString's replaceOccurrencesOfString:thePattern withString:"$1" options:1024 range:{0, theString's |length|()}
	display alert "The contents of the clipboard were processed" message ((countOne + countTwo + countThree) as text) & " replacements were made" buttons {"OK"} default button 1
	set the clipboard to (theString as text)
on error
	display alert "An error has occured" message "This most ccommonly happens when a string is not found on the clipboard"
end try

BTW, apps vary in how they format a document and interact with the clipboard, and I do not know of a way to retain formatting as you want. However, my knowledge of the clipboard is limited, and perhaps another forum member will have a solution.

Can you post the document you used for timing and testing purposes?

Thank you, that’s done it :blush:

Regarding formatting, if we just do scripts for Notes and Pages, would that be possible? If not, hope someone else has some ideas re that. I saw the below thread yesterday but haven’t read through it yet:

1 Like

The use of a regular expression technique to capture patterns provides a succinct shortcut in using Objective C’s replaceOccurrencesOfString method.

1 Like

Robert. I created the test string with a script, which I failed to save, but I’ve created a reasonable facsimile of it below. I retested my script in post 15 and the timing results were 42 milliseconds with 17,408 replacements.

set theString to "this , is - a ? test / this ! is ( a ) test [ this ] is { a } test : this ; is two  spaces and three   spaces" & linefeed

repeat 10 times
	set theString to theString & theString
end repeat

set the clipboard to theString

count paragraphs of theString --> 1025

The OP does not have the Shortcuts app on his computer, but I thought I would provide a shortcut solution just in case a forum member or Googler might need one in the future. To test this shortcut:

  1. Download and install the shortcut (it’s easily deleted).
  2. Open an app with the text to be processed.
  3. Select the text to be processed.
  4. Run the script by way of the app’s Services menu.

After doing the above, the shortcut will clean the text and will replace the selected text in the app with the cleaned text. In very-limited testing with a TextEdit Rich Text document, some formatting was retained but some was lost.

Process Text.shortcut (21.9 KB)