I want to implement a string interpolation in AppleScript, similar to the basic python implementation. For example in python:
print('Hello {}'.format('earth')) #> Hello World
In AppleScript, how do I implement the format handler?
to format(theString, {tokens})
-- TODO
end format
log format("Hello {}", {"Apple"}) -- # Hello Apple
The python syntax and the AppleScript/ObjC syntax are not compatible.
If you are talking about only one argument there is a simple solution with help of NSString's stringWithFormat
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
to format(theString, tokens)
return (current application's NSString's stringWithFormat_(theString, tokens)) as text
end format
log format("Hello %#", "Apple") -- # Hello Apple
For more arguments you have to use an if - else clause to convert the argument array to the ObjC va_list
to format(theString, tokens)
set numberOfArguments to count tokens
if numberOfArguments = 1 then
return (current application's NSString's stringWithFormat_(theString, item 1 of tokens)) as text
else if numberOfArguments = 2 then
return (current application's NSString's stringWithFormat_(theString, item 1 of tokens, item 2 of tokens)) as text
else if numberOfArguments = 3 then
return (current application's NSString's stringWithFormat_(theString, item 1 of tokens, item 2 of tokens, item 3 of tokens)) as text
else
error "Invalid number of arguments"
end if
end format
log format("Hello I'm %# and I'm %# years old", {"John", 25}) -- # Hello I'm John and I'm 25 years old
I wrote a simple implementation using sed
to format(theString as text, theTokens as list)
if class of theTokens is text then set theTokens to {theTokens}
set builtString to theString
repeat with nextToken in theTokens
set builtString to do shell script "echo '" & builtString & "' | sed 's/{}/" & nextToken & "/'"
end repeat
return builtString
end format
I have tested it with only 2 scenarios, I'm sure there are more I haven't covered:
format("Hello {}", "baby") -- # Hello baby
"Hello {}, how are you {}", {"baby", "mom"} -- # Hellow baby, how are you mom
Related
I've created a script that appends whatever's on my clipboard to an Apple note. However, the formatting of the appended text is not preserved, not even the line formatting. How to I append the clipboard to the note while preserving the LINE formatting of the clipboard? I don't care as much about the other formatting, although it would be nice to preserve it if possible.
Also, I would like the text to be appended as a new line with a line break in between the pre-existing and appended text, and the text size of the entire note—including the pre-existing and appended text—to be 18 points.
set AppendText to (the clipboard)
tell application "Notes"
tell account "iCloud"
tell folder "Clipboard"
set OriginalText to the body of note 1 -- the contents of the notes are encoded in HTML
end tell
end tell
end tell
tell application "Notes"
tell account "iCloud"
tell folder "Clipboard"
set body of note 1 to {"<div style=\"font-size: 18px\">" & OriginalText & "<br>" & AppendText & "</div>"}
end tell
end tell
end tell
Suppose that the pre-existing text of the note is
Original text line 1
Original text line 2
Original text line 3
and that the text that needs to be appended is
Append text line 1
Append text line 2
Append text line 3
When I run the script the text of the note is set to
Original text line 1
Original text line 2
Original text line 3
Append text line 1 Append text line 2 Append text line 3
Whereas I want it to be
Original text line 1
Original text line 2
Original text line 3
Append text line 1
Append text line 2
Append text line 3
Since the body of the note is HTML, one solution would be to use the textutil utility to convert the append text before adding (the Notes application handles merging the HTML), for example:
set appendText to (the clipboard)
set convertedText to (do shell script "echo " & quoted form of appendText & " | textutil -convert html -excludedelements '(p)' -stdin -stdout")
tell application "Notes"
tell folder "Whatever" -- example
set originalText to body of note 1
set body of note 1 to originalText & convertedText
end tell
end tell
Thanks to red_menace and user3439894, I've completed the script. Here it is.
set appendText to (the clipboard)
set convertedText to (do shell script "echo " & quoted form of appendText & ¬
" | textutil -convert html -fontsize 18 -excludedelements '(p)' -stdin -stdout")
tell application "Notes"
tell account "iCloud"
tell folder "Clipboard"
set originalText to the body of note 1
if originalText as string is "" then
set body of note 1 to {"<div style=\"font-size: 18px\">" & convertedText & "</div>"}
else
set body of note 1 to {"<div style=\"font-size: 18px\">" & originalText & ¬
"</div><div><span style=\"font-size: 18px\"><br></span></div> <div style=\"font-size: 18px\">" & ¬
convertedText & "</div>"}
end if
end tell
end tell
end tell
I am trying to remove all the formating from the data in the clipboard.
The input is like this:
Name: William R Wells
Date of Birth: 8 Dec 1942
Birth Place: Fayette, Kentucky, USA
Mother's name: Suda Hatton
Volume Number: 179
Certificate Number: 89145
Volume Year: 1942
I want it to look like this:
Name: William R Wells, Date of Birth: 8 Dec 1942, Birth Place: Fayette, Kentucky, USA, Mother's name: Suda Hatton, Volume Number: 179, Certificate, Number: 89145, Volume Year: 1942
This script works but does not delete the extra spaces and place a comma after the data.
try
do shell script "export LC_CTYPE=UTF-8; pbpaste | fmt -w 99999 | pbcopy"
end try
end
Any suggestions?
Thanks
Roger
Here's an AppleScript that will do the job. Not sure it's the most efficient way, but it works.
use framework "Foundation"
use scripting additions
--------------------------------------------------------------------------------
get the clipboard as text
re_match from the result against "\t+" given replacement:" "
re_match from the result against "[\r\n]" given replacement:", "
re_match from the result against " +" given replacement:" "
set the clipboard to the result
--------------------------------------------------------------------------------
###HANDLERS
on re_match against pattern from str given replacement:fmt
set regex to current application's NSRegularExpression's ¬
regularExpressionWithPattern:pattern ¬
options:(current application's ¬
NSRegularExpressionCaseInsensitive) ¬
|error|:(missing value)
(regex's stringByReplacingMatchesInString:str ¬
options:0 range:{0, length of str} ¬
withTemplate:fmt) ¬
as text
end re_match
This next AppleScript is much simpler and shorter but less versatile. However, if your clipboard contents is largely the same in format, it should be as effective as the above script.
set str to the clipboard as text
set text item delimiters to {null, tab}
set str to text items of str as text
set the text item delimiters to {", ", linefeed, return}
set str to text items of str as text
set the text item delimiters to {": ", ":"}
set str to text items of str as text
set the clipboard to str
I didn't did an Applescript app on Xcode from ages,
here is my code
script AppDelegate
property parent : class "NSObject"
-- IBOutlets
property theWindow : missing value
property myTextField : missing value
on applicationWillFinishLaunching_(aNotification)
-- Insert code here to initialize your application before any files are opened
end applicationWillFinishLaunching_
on applicationShouldTerminate_(sender)
-- Insert code here to do any housekeeping before your application quits
return current application's NSTerminateNow
end applicationShouldTerminate_
on myButton_(sender)
-- AppleScript
end myButton_
on saveNote_(sender)
-- AppleScript
end saveNote_
end script
but I have this error :
/AppDelegate.applescript:32: error: Expected “end” but found unknown token. (-2741)
Command /usr/bin/osacompile failed with exit code 1
Can you help me here?
PS here is the AppleScript
to getInputByClass2(theClass, num)
tell application "Safari"
set input to do JavaScript "
document.getElementsByClassName('" & theClass & "')[" & num & "].innerHTML;" in document 1
end tell
return input
end getInputByClass2
getInputByClass2("field type-string field-DATAID", 0)
set theText to Unicode text
set theSource to getInputByClass2("field type-string field-DATAID", 0)
property leftEdge : "class=\"value\">"
property rightEdge : "</span>"
set saveTID to text item delimiters
set text item delimiters to leftEdge
set classValue to text item 2 of theSource
set text item delimiters to rightEdge
set theDATAID to text item 1 of classValue
set text item delimiters to saveTID
theDATAID
here is the result on xcode
You're using the reserved word date as a variable name which is not allowed. To avoid that use aDate or theDate or something meaningful compound like startDate.
Consider that you are actually using none of the variables at the end of the sections and most of the code is redundant and could be reduced with a repeat loop.
My script searches a website for songs, but when there are spaces it doesn't search, you have to add underscores. I was wondering if there was a way to replace my spaces with underscores.
Could you please use my current code below to show me how to do it?
set search to text returned of (display dialog "Enter song you wish to find" default answer "" buttons {"Search", "Cancel"} default button 1)
open location "http://www.mp3juices.com/search/" & search
end
Note: The solution no longer works as of Big Sur (macOS 11) - it sounds like a bug; do tell us if you have more information.
Try the following:
set search to text returned of (display dialog "Enter song you wish to find" default answer "" buttons {"Search", "Cancel"} default button 1)
do shell script "open 'http://www.mp3juices.com/search/'" & quoted form of search
end
What you need is URL encoding (i.e., encoding of a string for safe inclusion in a URL), which involves more than just replacing spaces.
The open command-line utility, thankfully, performs this encoding for you, so you can just pass it the string directly; you need do shell script to invoke open, and quoted form of ensures that the string is passed through unmodified (to be URI-encoded by open later).
As you'll see, the kind of URL encoding open performs replaces spaces with %20, not underscores, but that should still work.
mklement0's answer is correct about url encoding but mp3juices uses RESTful URLs (clean URLs). RESTful URLs want's to keep the URL human readable and you won't see/use typical hex values in your url presenting an ASCII number. A snake_case, as you have mentioned (is false), but it is pretty common to use an substitution for whitespaces (%20) (and other characters) in RESTful URLs. However the slug of an RESTful must be converted to RESTful's own RESTful encoding before it can be handled by standard URL encoding.
set search to text returned of (display dialog "Enter song you wish to find" default answer "" buttons {"Search", "Cancel"} default button 1)
set search to stringReplace(search, space, "-")
do shell script "open 'http://www.mp3juices.com/search/'" & quoted form of search
on stringReplace(theText, searchString, replaceString)
set {oldTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, searchString}
set textItems to every text item of theText
set AppleScript's text item delimiters to replaceString
set newText to textItems as string
set AppleScript's text item delimiters to oldTID
return newText
end stringReplace
EDIT: updated the code, unlike the question mentioned that spaces are converted to underscores, mp3juice uses hyphens as substitution for whitespaces.
An update on this, despite the fact that the answer is 3 years old, as I faced the same problem: on recent versions of macOS/OS X/Mac OS X (I think, 10.10 or later), you can use ASOC, the AppleScript/Objective-C bridge:
use framework "Foundation"
urlEncode("my search string with [{?#äöü or whatever characters")
on urlEncode(input)
tell current application's NSString to set rawUrl to stringWithString_(input)
set theEncodedURL to rawUrl's stringByAddingPercentEscapesUsingEncoding:4 -- 4 is NSUTF8StringEncoding
return theEncodedURL as Unicode text
end urlEncode
It should be noted that stringByAddingPercentEscapesUsingEncoding is deprecated, but it will take some time until it’s removed from macOS.
URL encoding in AppleScript
For a general use case (for me at the moment to pass any ASCII url containing chars like #, &, ß, ö to the bit.ly API), I stumbled upon a nice code snippet that instantly added full support to my ShortURL clipboard pasting shortcut. Here's a quote from source:
i was looking for a quick and dirty way to encode some data to pass to a url via POST or GET with applescript and Internet Explorer, there were a few OSAXen which have that ability, but i didn't feel like installing anything, so i wrote this thing (works with standard ascii characters, characters above ascii 127 may run into character set issues see: applescript for converting macroman to windows-1252 encoding)
Notes
Double encoding should be duly noted.
Not tested on non-ASCII URLs.
Tested on OS X 10.8.5.
Code
on urlencode(theText)
set theTextEnc to ""
repeat with eachChar in characters of theText
set useChar to eachChar
set eachCharNum to ASCII number of eachChar
if eachCharNum = 32 then
set useChar to "+"
else if (eachCharNum ≠ 42) and (eachCharNum ≠ 95) and (eachCharNum < 45 or eachCharNum > 46) and (eachCharNum < 48 or eachCharNum > 57) and (eachCharNum < 65 or eachCharNum > 90) and (eachCharNum < 97 or eachCharNum > 122) then
set firstDig to round (eachCharNum / 16) rounding down
set secondDig to eachCharNum mod 16
if firstDig > 9 then
set aNum to firstDig + 55
set firstDig to ASCII character aNum
end if
if secondDig > 9 then
set aNum to secondDig + 55
set secondDig to ASCII character aNum
end if
set numHex to ("%" & (firstDig as string) & (secondDig as string)) as string
set useChar to numHex
end if
set theTextEnc to theTextEnc & useChar as string
end repeat
return theTextEnc
end urlencode
If you need to get the URL as a string (not just feed it into open which does a nifty job of encoding for you) and you're not above using a little Automator, you can throw some JavaScript into your AppleScript:
encodeURIComponent is a built in JavaScript function - it is a complete solution for encoding components of URIs.
For copy/pasters, here are all three scripts in the above Automator chain:
on run {input, parameters}
return text returned of (display dialog "Enter song you wish to find" default answer "" buttons {"Search", "Cancel"} default button 1)
end run
function run(input, parameters) {
return encodeURIComponent(input);
}
on run {input, parameters}
display dialog "http://www.mp3juices.com/search/" & input buttons {"okay!"} default button 1
end run
I was hunting around for URL encoding and decoding and came across this helpful link.
Which you can use like so:
set theurl to "https://twitter.com/zackshapiro?format=json"
do shell script "php -r 'echo urlencode(\"" & theurl & "\");'"
# gives me "https%3A%2F%2Ftwitter.com%2Fzackshapiro%3Fformat%3Djson"
set theurl to "https%3A%2F%2Ftwitter.com%2Fzackshapiro%3Fformat%3Djson"
return do shell script "php -r 'echo urldecode(\"" & theurl & "\");'"
# gives me "https://twitter.com/zackshapiro?format=json"
Or as functions:
on encode(str)
do shell script "php -r 'echo urlencode(\"" & str & "\");'"
end encode
on decode(str)
do shell script "php -r 'echo urldecode(\"" & str & "\");'"
end decode
Just so it's said, AppleScriptObjC allows us to use NSString to do the encoding. The script is complicated by the fact that different parts of the URL allow different characters (all of which I've added options for) but in most cases the 'query' option will be used.
See NSCharacterSet's dev page (the section called "Getting Character Sets for URL Encoding") for descriptions of the various URL parts.
use AppleScript version "2.4" -- Yosemite 10.10 or later
use framework "Foundation"
property NSString : class "NSString"
property NSCharacterSet : class "NSCharacterSet"
-- example usage
my percentEncode:"some text" ofType:"query"
on percentEncode:someText ofType:encodeType
set unencodedString to NSString's stringWithString:someText
set allowedCharSet to my charSetForEncodeType:encodeType
set encodedString to unencodedString's stringByAddingPercentEncodingWithAllowedCharacters:allowedCharSet
return encodedString as text
end percentEncode:ofType:
on charSetForEncodeType:encodeType
if encodeType is "path" then
return NSCharacterSet's URLPathAllowedCharacterSet()
else if encodeType is "query" then
return NSCharacterSet's URLQueryAllowedCharacterSet()
else if encodeType is "fragment" then
return NSCharacterSet's URLFragmentAllowedCharacterSet()
else if encodeType is "host" then
return NSCharacterSet's URLHostAllowedCharacterSet()
else if encodeType is "user" then
return NSCharacterSet's URLUserAllowedCharacterSet()
else if encodeType is "password" then
return NSCharacterSet's URLPasswordAllowedCharacterSet()
else
return missing value
end if
end charSetForEncodeType:
The Python Approach:
Find your python3 path (which python3) or if you don't have it, install using brew or miniconda
Now try this:
python_path = /path/to/python3
set search_query to "testy test"
tell application "Google Chrome"
set win to make new window
open location "https://www.google.com/search?q=" & url_encode(q)
end tell
on url_encode(input)
return (do shell script "echo " & input & " | " & python_path & " -c \"import urllib.parse, sys; print(urllib.parse.quote(sys.stdin.read()))\"
")
end url_encode
credits to #Murphy https://stackoverflow.com/a/56321886
Please note: I am new to TDD & cucumber, so the answer may be very easy.
I am creating a basic image editor for a test (the image is just a sequence of letters).
I have written a Cucumber story:
Scenario Outline: edit commands
Given I start the editor
And a 3 x 3 image is created
When I type the command <command>
Then the image should look like <image>
The step
Scenarios: colour single pixel
| command | image |
| L 1 2 C | OOOCOOOOO |
always fails, returning
expected: "OOOCOOOOO"
got: " OOOOOOOO" (using ==) (RSpec::Expectations::ExpectationNotMetError)
This is the step code:
When /^I type the command (.*)$/ do |command|
#editor.exec_cmd(command).should be
end
The function exec_cmd in the program recognizes the command and launches the appropriate action. In this case it will launch the following
def colorize_pixel(x, y, color)
if !#image.nil?
x = x.to_i
y = y.to_i
pos = (y - 1) * #image[:columns] + x
#image[:content].insert(pos, color).slice!(pos - 1)
else
#messenger.puts "There's no image. Create one first!"
end
end
However, this always fails unless I hardcode the values of the two local variables (pos and color) in the function in the program itself.
Why? It doesn's seem I'm doing anything wrong in the program itself: the function does what it's supposed to do and those two variables are only useful locally. So I'd think this is a problem with my use of cucumber. How do I properly test this?
---edit---
def exec_cmd(cmd = nil)
if !cmd.nil?
case cmd.split.first
when "I" then create_image(cmd[1], cmd[2])
when "S" then show_image
when "C" then clear_table
when "L" then colorize_pixel(cmd[1], cmd[2], cmd[3])
else
#messenger.puts "Incorrect command. " + "Commands available: I C L V H F S X."
end
else
#messenger.puts "Please enter a command."
end
end
When /^I type the command (.*)$/ do |command|
#output = #editor.exec_cmd(command)
end
Then /^the image should look like (.)*$/ do |expected_image|
#output.should == expected_image
end
Hope this may help you.
It's not a cucumber issue.
The problem was that, in exec_cmd, split was called only in the "case" clause, not in the "when"s. This meant that, since the command's format was "a 1 2 b", cmd[1] in the "when" would call the second character of the string, a space, not the second value of the array, and the other functions would convert that to_i, returning 0.
I changed exec_cmd like this:
def exec_cmd(cmd = nil)
if !cmd.nil?
cmd = cmd.split
case cmd.first
when "I" then create_image(cmd[1], cmd[2])
[...]
end
which fixed the issue.