Wednesday, June 26, 2019

#1 2018-01-24 05:25:43 am

Jono
Member
Registered: 2008-07-08
Posts: 100

Reformat text 'from_like_this' 'toLikeThis'

I have quite a few old AppleScripts where the variables are_like_this, and I'm wanting to change them toLikeThis. I managed to cobble together a script that would change (for example) thisText to thisText, but it only works on text with one underscore and on one variable.

Applescript:

set theString to "the_text"

(* Set Variables *)
set firstWord to text 1 thru ((offset of "_" in theString) - 1) of theString
set lastWord to text ((offset of "_" in theString) + 1) thru -1 of theString

(* Process Variables *)
set capLastWord to capFirstLetter(lastWord)
set newVariable to firstWord & capLastWord

(* Show Notification *)
set theTitle to "Text Reformatted"
set theSound to "Tink"
set theMessage to quoted form of theString & " changed to " & quoted form of newVariable
display notification theMessage with title theTitle sound name theSound

(* Capitalise 1st letter *)
on capFirstLetter(theString)
   set theLength to length of theString
   set theResult to "" as string
   try
       if theLength > 1 then
           set endString to characters 2 thru theLength of theString as text
           set theResult to upperCaseChar(character 1 of theString) & endString as text
       else
           set theResult to upperCaseChar(character 1 of theString) as text
       end if
   end try
   return theResult
end capFirstLetter
on upperCaseChar(theChar)
   set asciiChar to ASCII number of theChar
   if asciiChar ≥ 97 and asciiChar ≤ 122 then set theChar to ASCII character of (asciiChar - 32)
   return theChar
end upperCaseChar

What I'd like to do if possible is to copy one of my old AppleScripts to the clipboard, run the new script and it go through all the variables with multiple underscores and formatThemLikeThis.

Is something like that possible?

Offline

 

#2 2018-01-24 04:55:48 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4884

Re: Reformat text 'from_like_this' 'toLikeThis'

This capitalises every letter following an underscore and then loses the underscores. Obviously it'll act on every underscore in the text, not just those between words in variable names:

Applescript:

set theString to "set the_text to \"Hello\"
set another_multiword_variable to missing value"


set astid to AppleScript's text item delimiters
-- Break the text at the underscores.
set AppleScript's text item delimiters to "_"
set textChunks to theString's text items

-- Capitalise the initial letter of every chunk except the first.
repeat with i from 2 to (count textChunks)
   set thisChunk to item i of textChunks
   set chunkLen to (count thisChunk)
   if (chunkLen > 0) then
       set intitialCapital to upperCaseChar(character 1 of thisChunk)
       if (chunkLen > 1) then
           set thisChunk to intitialCapital & text 2 thru -1 of thisChunk
       else
           set thisChunk to intitialCapital
       end if
       set item i of textChunks to thisChunk
   end if
end repeat

-- Rejoin the doctored chunks without the underscores.
set AppleScript's text item delimiters to ""
set newString to textChunks as text
set AppleScript's text item delimiters to astid

return newString

on upperCaseChar(theChar)
   set asciiChar to id of theChar -- 'ASCII number' and 'ASCII character' are deprecated. Use 'id'.
   if asciiChar ≥ 97 and asciiChar ≤ 122 then set theChar to character id (asciiChar - 32)
   return theChar
end upperCaseChar

Last edited by Nigel Garvey (2018-01-24 04:57:38 pm)


NG

Offline

 

#3 2018-01-24 05:43:19 pm

waltr
Member
Registered: 2005-09-13
Posts: 317

Re: Reformat text 'from_like_this' 'toLikeThis'

By using some of the code you already have, but adding recursion you could get what you want:

Applescript:


set myList to {"this_function", "that_other_method", "all_the_things_to_do"}

on iterateList(theList)
   set newList to {}
   repeat with currentName in theList
       set newList to newList & underscoreToCamelCase(currentName)
   end repeat
   return newList
end iterateList

on underscoreToCamelCase(theString)
   set firstWord to text 1 thru ((offset of "_" in theString) - 1) of theString
   set lastWord to text ((offset of "_" in theString) + 1) thru -1 of theString
   set capLastWord to capFirstLetter(lastWord)
   set newString to firstWord & capLastWord
   if "_" is in newString then
       underscoreToCamelCase(newString)
   else
       return newString
   end if
end underscoreToCamelCase

on capFirstLetter(theString)
   set theLength to length of theString
   set theResult to "" as string
   try
       if theLength > 1 then
           set endString to characters 2 thru theLength of theString as text
           set theResult to upperCaseChar(character 1 of theString) & endString as text
       else
           set theResult to upperCaseChar(character 1 of theString) as text
       end if
   end try
   return theResult
end capFirstLetter

on upperCaseChar(theChar)
   set asciiChar to ASCII number of theChar
   if asciiChar ≥ 97 and asciiChar ≤ 122 then set theChar to ASCII character of (asciiChar - 32)
   return theChar
end upperCaseChar

log iterateList(myList)


I use Google or any search engine to find out things I don't know!

Offline

 

#4 2018-01-25 04:56:33 am

Jono
Member
Registered: 2008-07-08
Posts: 100

Re: Reformat text 'from_like_this' 'toLikeThis'

Perfect, thanks a lot. Both of you! smile

Offline

 

#5 2018-01-25 05:56:13 am

timbaldwin
Member
Registered: 2006-08-14
Posts: 68

Re: Reformat text 'from_like_this' 'toLikeThis'

Sorry,  but doesn't Script Editor come with a find and replace (command+F) function?

Or am I missing something?

Tim

Offline

 

#6 2018-01-25 06:36:46 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4884

Re: Reformat text 'from_like_this' 'toLikeThis'

timbaldwin wrote:

Sorry,  but doesn't Script Editor come with a find and replace (command+F) function?

Or am I missing something?


It doesn't do generic editing.  smile


NG

Offline

 

#7 2018-01-25 06:50:07 am

timbaldwin
Member
Registered: 2006-08-14
Posts: 68

Re: Reformat text 'from_like_this' 'toLikeThis'

Oh.  OK smile

Offline

 

#8 2018-01-25 08:41:00 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4884

Re: Reformat text 'from_like_this' 'toLikeThis'

Here's a very slightly more controlled script using ASObjC. It only acts when an underscore is followed by a lower-case letter and if they follow a letter or digit character. It replaces every instance of each match at once, which saves the script itself having to deal with every underscore in the text individually. But it'll still act on any matching text in the code being edited, not just variable names.

Applescript:

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

set theString to "set the_text to \"Hello\"
set another_multiword_variable to missing value
set _leadingUnderscoreVariable to 23
set trailingUnderscoreVariable_ to _leadingUnderscoreVariable"


set newString to replaceUnderscoring(theString)

on replaceUnderscoring(theText)
   set |⌘| to current application
   -- Get an NSMutableString version of the text for editing.
   set workString to |⌘|'s class "NSMutableString"'s stringWithString:(theText)
   -- Preset the start of a regex which will be added to later …
   set regexStart to |⌘|'s class "NSString"'s stringWithString:("(?<=[a-zA-Z0-9])")
   -- … and a regex for finding an underscore and lower-case English letter which come after an English letter or a digit.
   set genericRegex to |⌘|'s class "NSString"'s stringWithString:("(?<=[a-zA-Z0-9])_[a-z]")
   
   repeat
       -- Find the first current match for the generic regex in the text.
       set matchRange to workString's rangeOfString:(genericRegex) options:(|⌘|'s NSRegularExpressionSearch) range:({0, workString's |length|()})
       -- If none, then we've finished.
       if (matchRange's |length| is 0) then exit repeat
       -- Otherwise, get the text of the match.
       set matchedText to workString's substringWithRange:(matchRange)
       -- Zap the underscore and capitalise the letter.
       set capitalisedLetter to (matchedText's stringByReplacingOccurrencesOfString:("_") withString:(""))'s uppercaseString()
       -- Set a regex to find the matched text specifically.
       set specificRegex to regexStart's stringByAppendingString:(matchedText)
       -- Replace every instance of the matched text in the same situation with just the capitalised letter.
       tell workString to replaceOccurrencesOfString:(specificRegex) withString:(capitalisedLetter) options:(|⌘|'s NSRegularExpressionSearch) range:({0, its |length|()})
   end repeat
   
   return workString as text
end replaceUnderscoring

Edit: Just noticed the upper-case "Z"s in the regexes weren't upper-case!  roll  It didn't affect the working of the script though, which has to be a bug in the system. In fact, even the nonsense regex "(?<=[a-aA-a0-9])_[a-z]" gets the same results in High Sierra 10.13.3.  hmm
Later: The anomaly appears to be due to a difference in the interpretation of character class ranges rather than a bug. I understood these ranges specifically to cover lower-case letters, upper-case letters, or numerical digits. In the ICU implementation, they cover Unicode code points, so the "A-z" in my original typo "(?<=[a-zA-z0-9])" was actually matching the entire range from upper-case "A" to lower-case "z", including the six non-letters between "Z" and "a". Learn something every day.

Last edited by Nigel Garvey (2018-01-26 09:36:13 am)


NG

Offline

 

#9 2018-01-26 07:04:40 am

Jono
Member
Registered: 2008-07-08
Posts: 100

Re: Reformat text 'from_like_this' 'toLikeThis'

Even better, thanks a lot!

Offline

 

#10 2018-01-26 07:46:29 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4884

Re: Reformat text 'from_like_this' 'toLikeThis'

I've corrected the regexes in the script in post #8. (But see the edit note.)


NG

Offline

 

#11 2018-01-26 11:26:12 am

Jono
Member
Registered: 2008-07-08
Posts: 100

Re: Reformat text 'from_like_this' 'toLikeThis'

OK, thanks again.

Offline

 

#12 2018-12-28 05:56:52 am

technomorph
Member
Registered: 2017-12-14
Posts: 114

Re: Reformat text 'from_like_this' 'toLikeThis'

here's one using ObjC

Applescript:

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

-- classes, constants, and enums used
property NSString : a reference to current application's NSString
property NSMutableString : a reference to current application's NSMutableString

set capString to "MODIFIED_DATE_INFO"
set camlStringTest to my camelCase:capString withSepator:"_"
--> RESULT "modifiedDateInfo"

on camelCase:aString withSepator:aDelim
   set allLowerCase to (NSString's stringWithString:aString)'s lowercaseString
   set aList to (allLowerCase's componentsSeparatedByString:aDelim)
   set camelString to NSMutableString's |string|()
   set firstWord to aList's item 1
   camelString's appendString:firstWord
   set remaingWords to aList's subarrayWithRange:{1, (aList's |count|()) - 1}
   repeat with aWord in remaingWords
       (camelString's appendString:(aWord's capitalizedString))
   end repeat
   return camelString
end camelCase:withSepator:

and one if you would like to add a preFix

Applescript:

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

-- classes, constants, and enums used
property NSString : a reference to current application's NSString
property NSMutableString : a reference to current application's NSMutableString

set capString to "MODIFIED_DATE_INFO"
set camlStringTest to my camelCase:capString addingPrefix:"PL" withSepator:"_"
--> RESULT "plModifiedDateInfo"

on camelCase:aString addingPrefix:aPrefix withSepator:aDelim
   set allLowerCase to (NSString's stringWithString:aString)'s lowercaseString
   set camelString to (NSMutableString's stringWithString:aPrefix)'s lowercaseString
   set allWords to (allLowerCase's componentsSeparatedByString:aDelim)
   repeat with aWord in allWords
       (camelString's appendString:(aWord's capitalizedString))
   end repeat
   return camelString
end camelCase:addingPrefix:withSepator:


Filed under: camelCase

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)