Remove spaces before and after punctuation

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)

can you test time my version?

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

repeat with tids in {{" ", "  "}, {"/", "/ "}, {"-", "- "}, {"(", "( "}, {"[", "[ "}, {"{", "{ "}, {",", " ,"}, {"-", " -"}, {"?", " ?"}, {"/", " /"}, {"!", " !"}, {")", " )"}, {"]", " ]"}, {"}", " }"}, {":", " :"}, {";", " ;"}}
	set tids to contents of tids
	set fnd to item 2 of tids
	set reps to item 1 of tids
	repeat while (offset of fnd in theString) > 0
		set text item delimiters to fnd
		set tmp to text items of theString
		set text item delimiters to reps
		set theString to tmp as text
	end repeat
end repeat
set the clipboard to theString

Robert. To compare like with like, I modified your script to get the source text from the clipboard. The timing was 72 milliseconds. As written, your script took 67 milliseconds.

can you test this slightly modified version. it seems to be faster for me,

set THESTRING to the clipboard

repeat with tids in {{" ", "  "}, {"/", "/ "}, {"-", "- "}, {"(", "( "}, {"[", "[ "}, {"{", "{ "}, {",", " ,"}, {"-", " -"}, {"?", " ?"}, {"/", " /"}, {"!", " !"}, {")", " )"}, {"]", " ]"}, {"}", " }"}, {":", " :"}, {";", " ;"}}
	set tids to contents of tids
	set fnd to item 2 of tids
	set reps to item 1 of tids
	repeat while fnd is in THESTRING
		set text item delimiters to fnd
		set tmp to text items of THESTRING
		set text item delimiters to reps
		set THESTRING to tmp as text
	end repeat
end repeat
1 Like

Robert. Your revised script took 65 milliseconds. Just for the sake of consistency, I added a line that set the clipboard to THESTRING before running the tests.

FWIW, the timing result was 52 milliseconds if I deleted the repeat loop that tests to see if fnd is in THESTRING and added a sublist that replaces three spaces with one. This changes the result if there are 5 or more consecutive spaces, but that would not seem a significant issue.