Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

タグ: Xcode

Xcodeで最前面のAS sourceを構文確認

Posted on 3月 27 by Takaaki Naganoya

Xcodeで編集中のAppleScriptのプログラムを構文確認(コンパイルと表記されている、構文チェックと中間コードへの変換チェック、省略表記の展開など)するAppleScriptです。

本ScriptはXcode 9.2+macOS 10.12.6でテストしたものです。macOS 10.14+Xcode 10.2で実行したところ、file documentのnameに「– Edited」という入っていてはいけない文字列が入っていたり(それはWindowのtitleであってdocumentのnameじゃないだろう、、、、)、AppleScriptからXcodeを(データ書き込み)操作するとXcodeがクラッシュしたりと、まともに動作していない印象です。

Xcode 10.1+macOS 10.13.6で動かしたぶんには、Xcode上の表示は更新されていないものの、プロジェクト内の他のファイルをいったん表示させたあとに再度Scriptを表示させると書き換わっていました。Xcode 10.2+macOS 10.14.4の組み合わせでおかしな動きを行なっているようです。

XcodeのテキストエディタはAppleScriptの構文色分けを反映して表示しませんし、編集中にコンパイル(構文確認)するための機能がありません。インデント合わせやtellブロックやifブロックのネスティングの確認なども行えないため、記述のための最低限の機能を備えておらず、まともにプログラムを書くのであればScript Debuggerの併用は必須といえます。

先日のmacOS Nativeのミーティングにて、プレーンテキストエディタであるCotEditor上に記述したテキストのAppleScriptをコンパイル(構文確認)し、実際に実行して結果をCotEditor上の新規書類に出力するデモを実施。

このデモ内容は、はるかかなた昔から行なってきたものであり、CotEditorにかぎらず、あまねくどんなテキストエディタに対しても実行できるものです。macOS標準搭載のテキストエディットに対してすら同様のScriptを実行できます。

……と、振り返っていたときに、冒頭の「機能が致命的に足りない」Xcodeのテキストエディタのことを思い出したのです。

Xcodeのテキストエディタは(AppleScriptにとっては)致命的に機能が足りない欠陥品ですが、欠陥品には欠陥品なりの扱いをすればよいのではないかと気づきました。Xcodeに対してAppleScriptを実行することで、ソースを取得し、AppleScriptでAppleScriptをコンパイルし、Xcodeに書き戻してやればよいのではないか、と。

ずいぶんと昔に書いたXcode操作のScriptに若干のルーチンを足して動かしてみたところ、想像どおりうまく動きました。

ただし、少々のテストを行なっただけなので、実際にもっと使い込んでみる必要性を感じます。また、Xcode側が不可思議な挙動を行うため、Xcode上でAppleScriptを記述する場合には、依然としてScript Debugger必須です。

ちなみに、XcodeのAppleScriptサポート機能はいまひとつ不思議な挙動を行います。tellブロックでオブジェクト(documentとか)をくくり、その内側でオブジェクトに対する操作を行おうとするとエラー。tellブロックを正常に認識せず、tellブロック内でもそのオブジェクトへの参照を「of it」のように書く必要があります(実は、CotEditorもこのタイプ)。

最近、そのような不思議な挙動を行うアプリケーションがいくつか見られたため、「はいはい。君はof itが必要な人なんだね」と乗り切りましたが、これに気づかないと永遠にXcodeのまともなScriptは書けません。要注意です。

AppleScript名:最前面のAS sourceを構文確認.scptd
— Created 2018-05-19 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "OSAKit"

property OSAScript : a reference to current application’s OSAScript
property OSALanguage : a reference to current application’s OSALanguage
property OSALanguageInstance : a reference to current application’s OSALanguageInstance

property targX : 1024 –View Width
property targY : 2048 –View Height

tell application "Xcode"
  –最前面のWindowの情報を取得
  
set winProp to properties of front window
  
set aDoc to document of winProp –Xcode のworkspace document(xcodeproj)
  
–> workspace document "WKWebViewDemo.xcodeproj" of application "Xcode"
  
  
set docProp to properties of aDoc
  
set xcodeDocPath to path of docProp
  
–> "/Users/me/Documents/Objective-C/WKWebViewDemo_MAC-master/WKWebViewDemo.xcodeproj"
  
  
set aFileDoc to name of winProp
  
–> "ViewController.m"
  
  
try
    set aDoc to file document aFileDoc
    
–> source document "ViewController.m" of application "Xcode"
  on error
    –Storyboard/Xib file/info.plist/Assetsなどを表示中の場合にはエラーになる
    
return false
  end try
  
  
set aDocPath to path of aDoc
  
–> "/Users/me/Documents/Objective-C/WKWebViewDemo_MAC-master/WKWebViewDemo/ViewController.m"
end tell

if aDocPath does not end with ".applescript" then
  display notification "Front Xcode document does not seem an AppleScriptObjC source"
  
return
end if

set theSource to read ((POSIX file aDocPath) as alias) as «class utf8»
set aRes to retComiledAppleScriptString(theSource, "AppleScript") of me

tell application "Xcode"
  tell aDoc
    set text of it to aRes
  end tell
end tell

–Compile AppleScript Source
on retComiledAppleScriptString(aStr as string, osalangName as string)
  set osaCon to current application’s OSAScriptController’s alloc()’s init()
  
set osaView to current application’s OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
osaCon’s setScriptView:osaView
  
osaCon’s setLanguage:(OSALanguage’s languageForName:osalangName)
  
osaView’s setString:aStr
  
osaCon’s compileScript:(missing value) –Compile(構文確認)
  
  
set aRes to (osaView’s |string|()) as string
  
return aRes
end retComiledAppleScriptString

★Click Here to Open This Script 

Posted in OSA Raw AppleEvent Code Text | Tagged 10.12savvy OSALanguage OSALanguageInstance OSAScript OSAScriptController OSAScriptView Xcode | 1 Comment

ASOCからclass名を抽出してpropertyとして展開 v4

Posted on 8月 5, 2018 by Takaaki Naganoya

スクリプトエディタの最前面のドキュメントを解釈して、CocoaのClass名とEnumを抽出してpropertyに展開して、新規書類に展開するAppleScriptです。

いわゆる「AppleScriptの内容を解析して書き換える」AppleScriptです。


▲左が処理前、右が本Scriptで処理を行ったAppleScript

Cocoa Classのチェックのために、実際にNSClassFromStringでオブジェクトを作れるかどうかチェックしています。Enumについては、ヘッダーファイルを検索してチェックを行っています。そのため、本AppleScriptは実行環境にXcodeがインストールされていないとEnumチェックが行えません。

前バージョンではXcodeのアプリケーションバンドル以下に存在しているヘッダーファイルのパスを計算するときにXcodeが起動されていましたが、本バージョンではXcodeが起動しないように修正しました。

また、propertyを展開するときに、プロポーショナルフォントで画面描画した際の幅を実際に計算して、文字の描画幅のピクセル数順にソートしています。

本AppleScriptはEnumの確認のために全ヘッダーファイルを走査しているため、この部分で少々処理時間がかかります。次にバージョンアップすることがあれば、このCocoa ClassとEnumの確認をキャッシュするようにして、二度目以降は同じ文字列の問い合わせを行わないようにしたいところです。

AppleScript名:ASOCからclass名を抽出してpropertyとして展開 v4
— 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

★Click Here to Open This Script 

Posted in file OSA recursive call Text | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSAttributedString NSBundle NSColor NSCountedSet NSData NSDictionary NSFileManager NSFont NSMutableArray NSMutableAttributedString NSMutableDictionary NSMutableParagraphStyle NSPredicate NSSortDescriptor NSString NSTextView OSALanguage OSALanguageInstance OSAScript OSAScriptController OSAScriptView Script Editor Xcode | Leave a comment

Xcodeで表示中のソースの選択中の範囲のテキストを取得する v3

Posted on 5月 19, 2018 by Takaaki Naganoya

Xcodeで編集中のテキストの選択中の範囲のテキストを取得するAppleScriptです。

Xcode v9.2+macOS 10.12.6およびXcode v9.3.1+macOS 10.13.5betaで動作を確認しています。

Xcode上で編集中のソーステキストの選択範囲(selected character range)を取得し、さらに実際に選択範囲のテキストを取得しています。

Xcode上でソーステキストではなくXibファイルやplistなどの他のファイルを選択中の場合にはfalseを返します。

また、本Scriptでは個別に独立したWindow上に表示中のテキストについては無視します。

常識的なMacのテキスト編集系のGUIアプリケーションであれば、

 ①指定パスのテキストをオープンできる
 ②テキストを指定パスに保存できる
 ③表示中のテキストの選択範囲のテキストを取得できる
 ④表示中のテキストの選択範囲の内容を書き換えることができる

という動作は期待したいところですが、Xcodeについては①②が怪しく(Workspace Documentに新規テキストを追加するとかできそうもない)、③はなんとかなったものの、④ができるかどうか試行錯誤してみないとわからない、といったところです(パスを取得できているので、Xcodeを経由しないで直接ファイルを書き換えれば不可能ではなさそうですが、、、)。

AppleScript対応アプリケーションが標準でそなえているopen/close/quit/count/delete/exists/make/moveなどのコマンドをのぞくと、XcodeのAppleScript用語辞書に掲載されているコマンドは、build、clean、stop、run、testの5つだけ。archiveとかのproduct(ビルド後に生成されるバイナリ)を操作するための命令は用意されていません(わざと?)。

AppleScript名:表示中のソースの選択中の範囲のテキストを取得する v3
— Created 2018-05-19 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

tell application "Xcode"
  tell front file document
    try
      set selRange to selected character range
      
if selRange = {} or selRange = missing value then return false –No Selection or other
      
      
copy selRange to {aSelBegin, aSelEnd}
      
set aText to text of it
      
set aSelText to text aSelBegin thru aSelEnd of aText
    on error
      return "" –No Selection or other
    end try
    
    
return aSelText
  end tell
end tell

–>
(*
"  property parent : class \"NSObject\"
  property NSTimer : class \"NSTimer\""
*)

★Click Here to Open This Script 

Posted in Text | Tagged 10.12savvy 10.13savvy Xcode | Leave a comment

Xcodeでオープン中のソースコードのパスを求める

Posted on 5月 19, 2018 by Takaaki Naganoya

Xcodeでオープン中のソースコードのファイルパスを求めるAppleScriptです。

Xcode v9.2+macOS 10.12.6、Xcode v9.3.1+macOS 10.13.5betaで確認しました。

おおまかにいえば、Xcodeは大きくわけて2種類の書類を扱っています。1つは、拡張子「.xcodeproj」のXcodeプロジェクト(Workspace)書類。

そして、個別のソースコード書類(SwiftとかObjective-CとかAppleScriptとか)。

そんなわけで、普通のGUIアプリケーションとはdocumentの概念がやや異なり、少々めんどくさい上にXcodeのAppleScript用語辞書に載っている用例自体も「え、そんな書き方を推奨するの?」という、なんとも不思議な仕上がりになっていますが、まあいいんでしょう。

ちなみに、本ScriptはXcodeのメインウィンドウ上でソースコードを表示する場合と、個別のウィンドウで表示する場合に対応しています。

また、Xcodeのウィンドウ上でXibファイルやInfo.plistやアイコンのAssetsなどソースコード書類以外のものを表示している場合には、エラートラップをかけてfalseを返すようにしています。


▲XcodeのAppleScript用語辞書に書かれている記述例。Workspace Documentのオープン完了待ちをpropertyを確認しつつループで待つという仕様。そんなとこ、非同期実行してドーする、という不思議感満載

AppleScript名:Xcodeでオープン中のソースコードのパスを求める
— Created 2018-05-19 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

tell application "Xcode"
  –最前面のWindowの情報を取得
  
set winProp to properties of front window
  
set aDoc to document of winProp –Xcode のworkspace document(xcodeproj)
  
–> workspace document "WKWebViewDemo.xcodeproj" of application "Xcode"
  
  
set docProp to properties of aDoc
  
set xcodeDocPath to path of docProp
  
–> "/Users/me/Documents/Objective-C/WKWebViewDemo_MAC-master/WKWebViewDemo.xcodeproj"
  
  
set aFileDoc to name of winProp
  
–> "ViewController.m"
  
  
try
    set aDoc to file document aFileDoc
    
–> source document "ViewController.m" of application "Xcode"
  on error
    –Storyboard/Xib file/info.plist/Assetsなどを表示中の場合にはエラーになる
    
return false
  end try
  
  
set aDocPath to path of aDoc
  
–> "/Users/me/Documents/Objective-C/WKWebViewDemo_MAC-master/WKWebViewDemo/ViewController.m"
end tell

★Click Here to Open This Script 

Posted in File path | Tagged 10.12savvy 10.13savvy Xcode | Leave a comment

電子書籍(PDF)をオンラインストアで販売中!

Popular Posts

  • 2019年に書いた価値あるAppleScript
  • macOS 10.15beta関連
  • ダークモードの検出 v4
  • (GET)openBDで1冊分の情報を取得する
  • Numbersで選択範囲のセルのデータを取得して重複データを抽出
  • ファイルパスの変換(Alias→POSIX path→NSURL→POSIX path→file→Alias)
  • Mojaveに合わせて「Mac OS XのバージョンとAppleScriptの動向の年表」を改定
  • Pages書類の1ページ目の表の背景色を置換
  • ファイルの存在確認
  • 青空文庫のテキストのルビタグを超高速削除
  • mouseClickを用いて指定座標をクリック
  • 与えられた画像ファイルがNSImageでハンドリング可能かを取得 v2
  • note.muで指定のユーザーのノートを取得する
  • Numbersの表を回転
  • macOS 10.14で新設されたエラーコード-1743を確認する
  • アラートダイアログ上にTable View+Map Viewを表示
  • POSIX pathからファイル名と親フォルダを抽出
  • iTunes上で選択中のTrackのアートワークを指定画像に設定する
  • (GET)駅すぱあとAPIで平均待ち時間「バスのみ探索」
  • tccKitで指定Bundle IDのアプリケーションの「オートメーション」認証状況を取得

Tags

10.11savvy (1121) 10.12savvy (1249) 10.13savvy (1243) 10.14savvy (301) 10.15savvy (77) CotEditor (43) Finder (36) ITLibrary (13) iTunes (24) Keynote (46) Mail (11) NSAlert (33) NSAlertSecondButtonReturn (13) NSArray (51) NSBezierPath (11) NSBitmapImageRep (15) NSButton (17) NSColor (39) NSCountedSet (16) NSDictionary (26) NSFileManager (20) NSFont (13) NSImage (31) NSJSONSerialization (11) NSMutableArray (48) NSMutableDictionary (19) NSPredicate (37) NSRunningApplication (31) NSScreen (22) NSScrollView (17) NSSortDescriptor (15) NSString (87) NSTextView (11) NSURL (58) NSUTF8StringEncoding (12) NSUUID (15) NSView (27) NSWindow (11) NSWorkspace (12) Numbers (33) OSALanguage (11) OSAScript (17) Safari (24) Script Editor (16) TextEdit (13)

カテゴリー

  • AirDrop
  • AirPlay
  • AppleScript Application on Xcode
  • Bluetooth
  • boolean
  • Bug
  • Calendar
  • call by reference
  • Clipboard
  • Code Sign
  • Color
  • dialog
  • drive
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • font
  • geolocation
  • GUI
  • GUI Scripting
  • How To
  • Icon
  • Image
  • Input Method
  • Internet
  • JXA
  • Keychain
  • Language
  • list
  • Locale
  • Machine Learning
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • OCR
  • OSA
  • PDF
  • QR Code
  • Raw AppleEvent Code
  • Record
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • shell script
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Text
  • Text to Speech
  • timezone
  • Tools
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • 登録
  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC