— Created 2017-08-12 by Takaaki Naganoya — Modified 2018-08-05 by Takaaki Naganoya — 2018 Piyomaru Software use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" use framework "OSAKit"
property NSFont : a reference to current application’s NSFont property NSData : a reference to current application’s NSData property NSColor : a reference to current application’s NSColor property NSArray : a reference to current application’s NSArray property NSString : a reference to current application’s NSString property NSBundle : a reference to current application’s NSBundle property OSAScript : a reference to current application’s OSAScript property NSPredicate : a reference to current application’s NSPredicate property NSTextView : a reference to current application’s NSTextView property NSDictionary : a reference to current application’s NSDictionary property NSFileManager : a reference to current application’s NSFileManager property OSALanguage : a reference to current application’s OSALanguage property NSCountedSet : a reference to current application’s NSCountedSet property OSAScriptView : a reference to current application’s OSAScriptView property NSMutableArray : a reference to current application’s NSMutableArray property NSSortDescriptor : a reference to current application’s NSSortDescriptor property NSAttributedString : a reference to current application’s NSAttributedString property OSAScriptController : a reference to current application’s OSAScriptController property NSMutableDictionary : a reference to current application’s NSMutableDictionary property NSFontAttributeName : a reference to current application’s NSFontAttributeName property NSKernAttributeName : a reference to current application’s NSKernAttributeName property OSALanguageInstance : a reference to current application’s OSALanguageInstance property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding property NSMutableParagraphStyle : a reference to current application’s NSMutableParagraphStyle property NSLigatureAttributeName : a reference to current application’s NSLigatureAttributeName property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString property NSUnderlineStyleAttributeName : a reference to current application’s NSUnderlineStyleAttributeName property NSParagraphStyleAttributeName : a reference to current application’s NSParagraphStyleAttributeName property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName
set aCon to getFrontmostSEContents() of me set aaList to paragraphs of aCon
set aList to retPropertiesClass(aCon) of me set bList to retCurrentApplicationsClass(aCon) of me
–置換してはいけないリスト。text encoding系はenumの桁数が足りなくてpropertyに代入できない。Cのメソッドは代入しても効かない set cList to {"\"", "\",", ",", "(", ")", "NSMakeRange", "NSMakePoint", "NSMakeRect", "NSMakeSize", "NSHomeDirectory", "NSUTF16BigEndianStringEncoding", "NSUTF16LittleEndianStringEncoding", "MKMapPointMake", "NSNotFound", "CGRectZero", "NSClassFromString"}
set aSet to current application’s NSMutableSet’s setWithArray:aList –property宣言文の一覧 set bSet to current application’s NSMutableSet’s setWithArray:bList –script中のcurrent application文のクラス set cSet to current application’s NSMutableSet’s setWithArray:cList
bSet’s minusSet:aSet –current applicationで指定したクラスで、property宣言していないものを計算 bSet’s minusSet:cSet –置換禁止リスト
set dList to bSet’s allObjects() as list
–Cocoa Class, Cocoa Enum Check set ddList to {} repeat with i in dList set j to (contents of i) as string –Cocoa Class Check set fRes to searchClassInFrameworks(j) of me if fRes = false then –Cocoa Eum Check set fRes to searchEnumFromHeaderFiles(j) of me end if if fRes is not in {false, {}} then if j is not in cList then if j does not contain "(" then set the end of ddList to j end if end if end if end repeat
–Scriptの本文を書き換える repeat with i in ddList set j to contents of i set tmpStr to "current application’s " & j set aCon to repChar(aCon, tmpStr, " " & j) of me end repeat
–挿入部分のテキストを組み立てる set repStr to return repeat with i in ddList set j to contents of i set repStr to repStr & "property " & j & " : a reference to current application’s " & j & return end repeat set repStr to repStr & return
–AppleScriptソース文字列からAttributed Stringを生成して返す set anAttrStr to compileASandReturnAttributedString(repStr) of me
–Attributed StringからAttribute Runsに似たデータを作成する set attrList to getAttributeRunsFromAttrString(anAttrStr) of me set anArray to (NSArray’s arrayWithArray:attrList)’s valueForKeyPath:"fontName" set aFontList to (countItemsByItsAppearance(anArray) of me) set aFontName to theName of first item of aFontList
–クリップボードから取得した文字データについて処理 set aList to paragraphs of repStr –行ごとにparseしてlist化 set bList to {} repeat with i in aList set j to contents of i if j ≠ {} then set jList to words of j if jList ≠ {} then if contents of first item of jList = "property" then set curLabel to contents of second item of jList –行をAttributed Stringとして組み立てて、画面描画時の仕上がりサイズを取得 set anAssrStr to makeRTFfromParameters(j, aFontName, 16, -2, 16) of me set aSize to anAssrStr’s |size|() –画面描画時のサイズを取得 if class of aSize = record then set attrStrWidth to width of aSize set attrStrHeight to height of aSize else if class of aSize = list then –macOS 10.13.xのバグ回避 copy aSize to {attrStrWidth, attrStrHeight} end if set the end of bList to {aLabel:curLabel, aCon:j, aWidth:attrStrWidth} end if end if end if end repeat
if bList = {} then display dialog "Error" buttons {"OK"} default button 1 return end if
–複数キーでソート(書式つきテキストの仕上がりサイズ幅、文字コード順でソート) set aArray to NSArray’s arrayWithArray:bList set desc1 to NSSortDescriptor’s sortDescriptorWithKey:"aWidth" ascending:true set desc2 to NSSortDescriptor’s sortDescriptorWithKey:"aLabel" ascending:true selector:"localizedCaseInsensitiveCompare:" set bArray to aArray’s sortedArrayUsingDescriptors:{desc1, desc2}
–ソートしたlist of recordからaCon(元のproperty宣言行そのもの)を一括で取り出す set dArray to (NSMutableArray’s arrayWithArray:bArray)’s valueForKeyPath:"aCon"
–listをデリミタつきのテキストに set dStr to retStrFromArrayWithDelimiter(dArray, return) of me set dStr to return & dStr & return & return
set aInsPoint to getFirstInsertionPoint(aCon) of me
tell application "Script Editor" set aDoc to make new document tell front document set contents to aCon set selection to paragraph aInsPoint set contents of selection to the dStr try compile end try end tell end tell
on getFirstInsertionPoint(aCon) set aList to paragraphs of aCon set aCount to 1 repeat with i in aList set j to contents of i if j = "" then exit repeat end if set aCount to aCount + 1 end repeat return aCount end getFirstInsertionPoint
on retPropertiesClass(aCon) set resList to {} set regStr to "(property .*?:)" set aRes to my findPattern:regStr inString:aCon capturing:1 repeat with i in aRes set j to first item of (paragraphs of i) –正規表現もどきで取り出してみた(ぜんぜん改良の余地がある) set j1 to repChar(j, "current application’s ", "") of me set j2 to repChar(j1, "property", "") of me set j3 to repChar(j2, ":", "") of me set j4 to repChar(j3, " ", "") of me set the end of resList to j4 end repeat set res2List to uniquify1DList(resList, false) of me return res2List end retPropertiesClass
on retCurrentApplicationsClass(aCon) set resList to {} set regStr to "(current application’s .*? )" set aRes to my findPattern:regStr inString:aCon capturing:1 repeat with i in aRes set j to first item of (paragraphs of i) –正規表現もどきで取り出してみた(ぜんぜん改良の余地がある) set j1 to repChar(j, "current application’s ", "") of me set j2 to repChar(j1, "’s ", "") of me set j3 to repChar(j2, ")", "") of me set j4 to repChar(j3, "}", "") of me set j5 to repChar(j4, "}", "") of me set j6 to repChar(j5, " ", "") of me set the end of resList to j6 end repeat set res2List to uniquify1DList(resList, false) of me return res2List end retCurrentApplicationsClass
on getFrontmostSEContents() tell application "Script Editor" set docCount to count every document if docCount = 0 then error "No Document" tell front document return contents end tell end tell end getFrontmostSEContents
on replaceThis:thePattern inString:theString usingThis:theTemplate set theOptions to ((current application’s NSRegularExpressionDotMatchesLineSeparators) as integer) + ((current application’s NSRegularExpressionAnchorsMatchLines) as integer) set theRegEx to current application’s NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value) set theResult to theRegEx’s stringByReplacingMatchesInString:theString options:0 range:{location:0, |length|:length of theString} withTemplate:theTemplate return theResult as text end replaceThis:inString:usingThis:
on findPattern:thePattern inString:theString set theOptions to ((current application’s NSRegularExpressionDotMatchesLineSeparators) as integer) + ((current application’s NSRegularExpressionAnchorsMatchLines) as integer) set theRegEx to current application’s NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value) set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString} set theFinds to theFinds as list — so we can loop through set theResult to {} — we will add to this set theNSString to current application’s NSString’s stringWithString:theString repeat with i from 1 to count of items of theFinds set theRange to (item i of theFinds)’s range() set end of theResult to (theNSString’s substringWithRange:theRange) as string end repeat return theResult end findPattern:inString:
on findPattern:thePattern inString:theString capturing:n set theOptions to ((current application’s NSRegularExpressionDotMatchesLineSeparators) as integer) + ((current application’s NSRegularExpressionAnchorsMatchLines) as integer) set theRegEx to current application’s NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value) set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString} set theFinds to theFinds as list — so we can loop through set theResult to {} — we will add to this set theNSString to current application’s NSString’s stringWithString:theString repeat with i from 1 to count of items of theFinds set oneFind to (item i of theFinds) if (oneFind’s numberOfRanges()) as integer < (n + 1) then set end of theResult to missing value else set theRange to (oneFind’s rangeAtIndex:n) set end of theResult to (theNSString’s substringWithRange:theRange) as string end if end repeat return theResult end findPattern:inString:capturing:
–文字置換 on repChar(aStr, targStr, repStr) set aString to current application’s NSString’s stringWithString:aStr set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr return bString as string end repChar
–1D/2D Listをユニーク化 on uniquify1DList(theList as list, aBool as boolean) set aArray to current application’s NSArray’s arrayWithArray:theList set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self" return bArray as list end uniquify1DList
–指定クラスがいずれかのCocoa Frameworkに所属しているかを検索 on searchClassInFrameworks(aTarget) set aClass to current application’s NSClassFromString(aTarget) if aClass = missing value then return false set theComponenents to (NSBundle’s bundleForClass:aClass)’s bundleURL’s pathComponents() set thePred to NSPredicate’s predicateWithFormat:"pathExtension == ’framework’" set aRes to (theComponenents’s filteredArrayUsingPredicate:thePred)’s firstObject() as list of string or string return aRes end searchClassInFrameworks
–指定文字列がどこかのCocoa FrameworkのEnumとして定義されていないかチェック on searchEnumFromHeaderFiles(targString) set aClass to current application’s NSClassFromString(targString) if aClass is not equal to missing value then return false set dPath to retAppAbusolutePathFromBundleID("com.apple.dt.Xcode") of me set aFol to dPath & "/Contents/Developer/Platforms/MacOSX.platform/" & "Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" –ルーチンの仕様変更によりパス表記(トップ)変更 set bList to retFullPathWithinAFolderWithRecursiveFilterByExt(aFol, "h") of me set matchedList to {} repeat with i in bList set j to contents of i set aStr to (NSString’s stringWithContentsOfFile:j encoding:NSUTF8StringEncoding |error|:(missing value)) if aStr ≠ missing value then set aRange to (aStr’s rangeOfString:targString) if aRange’s location() ≠ current application’s NSNotFound and (aRange’s location()) < 9.99999999E+8 then set tmpStr to (current application’s NSString’s stringWithString:j) set pathList to tmpStr’s pathComponents() set thePred to (current application’s NSPredicate’s predicateWithFormat:"pathExtension == ’framework’") set aRes to (pathList’s filteredArrayUsingPredicate:thePred)’s firstObject() as text set the end of matchedList to aRes end if end if end repeat set aArray to current application’s NSArray’s arrayWithArray:matchedList set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self" return bArray as list end searchEnumFromHeaderFiles
–指定フォルダ以下のすべてのファイルを再帰で取得(拡張子で絞り込み) on retFullPathWithinAFolderWithRecursiveFilterByExt(aFol, aExt) set anArray to NSMutableArray’s array() set aPath to NSString’s stringWithString:aFol set dirEnum to NSFileManager’s defaultManager()’s enumeratorAtPath:aPath repeat set aName to (dirEnum’s nextObject()) if aName = missing value then exit repeat set aFullPath to aPath’s stringByAppendingPathComponent:aName anArray’s addObject:aFullPath end repeat set thePred to NSPredicate’s predicateWithFormat:"pathExtension == [c]%@" argumentArray:{aExt} set bArray to anArray’s filteredArrayUsingPredicate:thePred return bArray as list end retFullPathWithinAFolderWithRecursiveFilterByExt
–Bundle IDからアプリケーションのPathを返す on retAppAbusolutePathFromBundleID(aBundleID) set appPath to current application’s NSWorkspace’s sharedWorkspace()’s absolutePathForAppBundleWithIdentifier:aBundleID if appPath = missing value then return false return appPath as string end retAppAbusolutePathFromBundleID
–RAM上にスクリプトエディタと同じ部品を組み立て(非表示)、AppleScriptのソーステキストからObjectを生成し、Attributed Stringデータを返す on compileASandReturnAttributedString(theSource as string) set targX to 1024 –View Width set targY to 2048 –View Height set osaCon to OSAScriptController’s alloc()’s init() set osaView to OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY)) set resView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY)) resView’s setRichText:true resView’s useAllLigatures:true osaCon’s setScriptView:osaView osaCon’s setLanguage:(OSALanguage’s languageForName:"AppleScript") osaCon’s setResultView:resView osaView’s setString:theSource osaCon’s compileScript:(missing value) –Compile(構文確認) set aRes to (osaView’s attributedString()) return aRes end compileASandReturnAttributedString
–1D Listを文字列長でソート v2 on sort1DListByIndicatedStringLength(aList as list, aSortKey as string, sortOrder as boolean) set aArray to NSArray’s arrayWithArray:aList set descLabel1 to NSString’s stringWithString:(aSortKey & ".length") set descLabel2 to NSString’s stringWithString:aSortKey set desc1 to NSSortDescriptor’s sortDescriptorWithKey:descLabel1 ascending:sortOrder set desc2 to NSSortDescriptor’s sortDescriptorWithKey:descLabel2 ascending:true selector:"localizedCaseInsensitiveCompare:" set bArray to aArray’s sortedArrayUsingDescriptors:{desc1, desc2} return bArray as list end sort1DListByIndicatedStringLength
–リストを指定デリミタをはさんでテキスト化 on retStrFromArrayWithDelimiter(aList as list, aDelim as string) set anArray to NSArray’s arrayWithArray:aList set aRes to anArray’s componentsJoinedByString:aDelim return aRes as text end retStrFromArrayWithDelimiter
–書式つきテキストを組み立てる on makeRTFfromParameters(aStr as string, fontName as string, aFontSize as real, aKerning as real, aLineSpacing as real) set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize set aKey1 to (NSFontAttributeName) set aVal2 to NSColor’s blackColor() set aKey2 to (NSForegroundColorAttributeName) set aVal3 to aKerning set akey3 to (NSKernAttributeName) set aVal4 to 0 set akey4 to (NSUnderlineStyleAttributeName) set aVal5 to 2 –all ligature ON set akey5 to (NSLigatureAttributeName) set aParagraphStyle to NSMutableParagraphStyle’s alloc()’s init() aParagraphStyle’s setMinimumLineHeight:(aLineSpacing) aParagraphStyle’s setMaximumLineHeight:(aLineSpacing) set akey7 to (NSParagraphStyleAttributeName) set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey7} set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aParagraphStyle} set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary return attrStr end makeRTFfromParameters
–指定のNSAttributedStringから書式情報をlist of recordで取得 on getAttributeRunsFromAttrString(theStyledText) script aSpd property styleList : {} end script set (styleList of aSpd) to {} —for output set thePureString to theStyledText’s |string|() –pure string from theStyledText set theLength to theStyledText’s |length|() set startIndex to 0 repeat until (startIndex = theLength) set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex} set aText to (thePureString’s substringWithRange:theRange) as string set aColor to (theAtts’s valueForKeyPath:"NSColor") if aColor is not equal to missing value then set aSpace to aColor’s colorSpace() set aRed to (aColor’s redComponent()) * 255 set aGreen to (aColor’s greenComponent()) * 255 set aBlue to (aColor’s blueComponent()) * 255 set colList to {aRed as integer, aGreen as integer, aBlue as integer} set colStrForFind to (aRed as integer as string) & " " & (aGreen as integer as string) & " " & (aBlue as integer as string) else set colList to {0, 0, 0} set colStrForFind to "0 0 0" end if set aFont to (theAtts’s valueForKeyPath:"NSFont") if aFont is not equal to missing value then set aDFontName to aFont’s displayName() set aDFontSize to aFont’s pointSize() end if set the end of (styleList of aSpd) to {stringVal:aText, colorStr:colStrForFind, colorVal:colList, fontName:aDFontName as string, fontSize:aDFontSize} set startIndex to current application’s NSMaxRange(theRange) end repeat return (styleList of aSpd) end getAttributeRunsFromAttrString
–1D Listをアイテムの出現頻度順でソートして返す on countItemsByItsAppearance(aList as list) set aSet to NSCountedSet’s alloc()’s initWithArray:aList set bArray to NSMutableArray’s array() set theEnumerator to aSet’s objectEnumerator() repeat set aValue to theEnumerator’s nextObject() if aValue is missing value then exit repeat bArray’s addObject:(NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{"theName", "numberOfTimes"}) end repeat set theDesc to NSSortDescriptor’s sortDescriptorWithKey:"numberOfTimes" ascending:false bArray’s sortUsingDescriptors:{theDesc} return bArray as list end countItemsByItsAppearance
|