2019/11/17

Disable Text Replacement on Safari in Mojave

iOS で文字入力をする際、入力が億劫な定型文とかを登録してみる。
  • General > Keyboard > Text Replacement 
で追加できます。

そこそこ快適になったと思ったら、iCloud 経由で自動的に Mac にも sync される様子。
基本的には無害なのですが、一度 GitHub の sha1 の末尾が置換対象と認識されて誤爆しかけた。ので無効化させる事に。
以下、 iOS では Text Replacement を有効にしつつ、Mac では無効化したログ。意外と大冒険だった。


環境

  • OS: macOS Mojave 10.14.6
  • Saferi: Version 13.0.3 (14608.3.10.10.1)
  • iOS: 12.3.2 and 13.2.2


結論から言うと

以下のコマンドを実行すると Mac の Text Replacement 設定を全て削除できます。
  • $ echo 'delete from ZTEXTREPLACEMENTENTRY;' | sqlite3 ~/Library/KeyboardServices/TextReplacements.db
  • $ defaults delete -g NSUserDictionaryReplacementItems
これで macOS では Text Replacement が発生しなくなります。
あくまで delete なので、iOS 側で設定を追加すると、同期された設定を再度削除する必要があります。


設定が存在しないか探す

iCloud の設定等、怪しい部分を探すも、それらしきものを発見できず。


$ defaults write -g WebAutomaticTextReplacementEnabled -bool false

ググると同じ考えの人はいて、
  • $ defaults write -g WebAutomaticTextReplacementEnabled -bool false
すると良い、とのことで実行。しかし変化無し。

きちんと設定されているか分からないのでまずは設定を表示させる。
man defaults すると read があるので実行してみる。
  • $ defaults read -g WebAutomaticTextReplacementEnabled
    • => 0
0なので一応 false になってる、という事なのだろうか。
delete もあるので実行する。
  • $ defaults delete -g WebAutomaticTextReplacementEnabled
  • $ defaults read -g WebAutomaticTextReplacementEnabled
    • => The domain/default pair of (kCFPreferencesAnyApplication, WebAutomaticTextReplacementEnabled) does not exist
delete した場合は 0 すら返らないので、設定できていたことになる。
しかし効果は表れなかったので、この方法では無理そう。


$ defaults find TextReplacement

defaults には find 機能もあるようだったので、
  • $ defaults find TextReplacement
とすると大量の設定が返ってくる。

defaults は多分 NSUserDefauts を macOS 側からいじる用のコマンドだと思われる。
Application 毎に設定があるし、Application 側で値を変更したら defaults で見られる情報に変化があった。
具体的には 'com.apple.iWork.Keynote' の 'TSWPAutomaticTextReplacement' の値とか。
Keynote で Text Replacement を disable にすると 'TSWPAutomaticTextReplacement' の値が 0 になる。

ちなみに -g は global option らしい。
Safari だけに効くよう、以下のように domain を指定して設定してみる。
  • $ defaults write com.apple.Safari TSWPAutomaticTextReplacement -bool false
しかし無効化できず。うーん。


$ defaults write -g WebAutomaticTextReplacementEnabled -bool true

逆に 0 でなく 1 を設定してみる。そうすると 
  • Menu bar(Safari) > Edit > Substitutions
が select できるようになる。
その中の 'Text Replacement' を disable にすると置換が止まった。
後はこれを defaults のどこに書くのか調べると良さそう。

と、思ったがそうは問屋が卸さなかった。defaults を使っても設定が見付けられない。
その上、 Saferi で 新規 Window を開くと 'Text Replacement' が Enabled な Window が増える。
あと、mac を reboot したら Text Replacement がまた有効になる。
どこかに設定があるからそれに辿りつけば良いんだけれど、対症療法ではどうも分が悪そう。


$ defaults write -g NSUserDictionaryReplacementItems <dictionary>

設定の場所を特定するためにわざと 'hoge' とかを TextReplacement に追加する。
そして
  • $ defaults find hoge
で調べると、NSUserDictionaryReplacementItems という Dictionary に格納されている。

NSUserDictionaryReplacementItems をググると少ないが記事はある
それを辿ると ~/Library/Preferences/.GlobalPreferences.plist というファイルの存在が示唆されている。
  • $ ls ~/Library/Preferences/.GlobalPreferences.plist
すると一応存在している。
  • $ open -a XCode.app ~/Library/Preferences/.GlobalPreferences.plist
内容は defaults read -g と同じっぽい。のでこれをいじっても defaults とやっている事は変わらない筈。

またググって引っかかった記事によれば
  • NSUserReplacementItems は 10.8 より前のバージョンで使われている
  • NSUserDictionaryReplacementItems は 10.9 で使われてる
とのことで。NSUserDictionaryReplacementItems をいじることにする。

元の値を一度取り出して
  • $ defaults read -g NSUserDictionaryReplacementItems 
    • => '( { on = 1; replace = hoge; with = "hogefugapiyo"; } )'
on を 0 にして、書き込む
  • $ defaults write -g NSUserDictionaryReplacementItems '( { on = 0; replace = hoge; with = "hogefugapiyo"; } )'
しかし Text Replacement は発生してしまう。
が、 Safari を起動すると、Text Replacement が発生しなくなった。ということでコレで良さそうだ。
その後、 macOS を再起動しても on flag は 0 のままだった。これで解決かな。


復活する NSUserDictionaryReplacementItems

解決かと思ったがそうでも無かった。しばらくすると on flag が 1 になっていた。
iCloud から sync した時に 1 になるなら理解できるが、特に編集せずとも 1 になる時がある。

ググると DB に格納されている、という記事が。
  • $ ls /Library/Dictionaries/CoreDataUbiquitySupport/atton~*/UserDictionary/local/store/UserDictionary.db
確かにある。sqlite3 で中身を覗いてみるとデータが無い。
というか ls -l したら 0B だった。のでここでは無い。


ちなみに、 on flag が 1 になる再現条件の一つは
  • System Preferences > Keyboard > Text を見る
でした。とりあえず確認に使えるので再現方法が1つ見付かっただけありがたい。


echo 'delete from ZTEXTREPLACEMENTENTRY;' | sqlite3 ~/Library/KeyboardServices/TextReplacements.db

Dictionary の save 先が無いかなー、とかググっていると ~/Library/Spelling に text edit の auto correction の情報がある、という記事にぶつかる。

~/Library をいろいろ見ると
  • $ ls ~/Library/KeyboardServices/TextReplacements.db
という怪しい物体を発見。早速中を見る。
  • $  file KeyboardServices/TextReplacements.db
    • => KeyboardServices/TextReplacements.db: SQLite 3.x database, last written using SQLite version 3016000
  • $ sqlite3 ~/Library/KeyboardServices/TextReplacements.db
    • sqlite> .schema
      • CREATE TABLE ZTEXTREPLACEMENTENTRY
      • CREATE TABLE ZTRCLOUDKITSYNCSTATE 
      • CREATE INDEX ...
      • CREATE TABLE Z_PRIMARYKEY
      • CREATE TABLE Z_METADATA
      • CREATE TABLE Z_MODELCACHE
    • sqlite> select * from ZTEXTREPLACEMENTENTRY limit 1;
      • 4|1|1|0|0|582166527.750349|三度寝|さんどね|C561DE44-D3D9-4F37-95E6-5916FF62C390|bplist00=>X$versionX$objectsY$archiverT$top
システム設定が参照しているかはともかく、コイツが Text Replacement のデータを保存しているのは間違いなさそう。

とりあえずバックアップを取って消してみる。
  • sqlite> delete from ZTEXTREPLACEMENTENTRY;
  • sqlite> select * from ZTEXTREPLACEMENTENTRY;
    • 消えてるので record は無い
System Preferences > Keyboard > Text を開くと見事に消えている。
ので NSUserDictionaryReplacementItems が復活する事も無さそう。
ということで defaults で NSUserDictionaryReplacementItems を消す。
  • $ defaults delete -g NSUserDictionaryReplacementItems
これにて一段落。これらを調べた結果が最初に書いた例の2行です。



余談: WebKit の shallow clone とか

ソース読むかー、ということで WebKit を落とそうとするも時間がかかりすぎる。
  • $ git clone --depth 1 git://git.webkit.org/WebKit.git WebKit
何かないかと git grep -i replacementした結果
  • NSUserDictionaryReplacementItems
  • ContextMenuItemTagTextReplacement
  • WebMenuItemTagTextReplacement
  • kWKContextMenuItemTagTextReplacement
等があって更に混乱した。それと対応してなのか
  • ContextMenuItemTagSmartCopyPaste
  • WebMenuItemTagSmartCopyPaste
  • kWKContextMenuItemTagSmartCopyPaste
等もあり迷宮じみてる感じ。ちなみに depth 1 でも 4.5GB というトンデモサイズです。


参考

2019/11/10

zle を使って特定の条件下のコマンドを実行する際、自動で丸括弧でくくる

前回の記事では peep コマンドを使ってプロセスの終了時に通知を行ないました。
また、peep-mac-tmux というコマンドを作って pid の指定を省略しました。

ですが、 peep-mac-tmux を使う際、上手く動いてくれないパターンがいくつか出てきました。
具体的には以下のようなパターンです。

  • '&&' で繋がれたコマンド
    • peep するのは実行中のコマンドなので、途中のコマンドが終了すると通知してしまう
  • `xargs -L1` のように何度も子プロセスを生成するコマンド
    • 1つの子プロセスが終わった時に通知してしまう

そこで、zsh の機能である zle を使い、これらのパターンの時に括弧で括って実行するようにしました。
丸括弧で括ってあると subshell 内部でコマンドが実行されます。
なので、peep は subshell の pid を peep する事で適切に動作させる狙いです。


環境

  • OS: macOS Mojave 10.14.6
  • Homebrew: 2.1.16-38-ge2c76cc
  • Homebrew/homebrew-core: (git revision 0f36; last commit 2019-11-09)
  • zsh: 5.7.1 (x86_64-apple-darwin18.2.0)


zle とは

zsh command line editor という機能で、man zshzle すると詳細が見られます。

今回の場合、'&&' が含まれる or '|' が含まれる、という時に括弧を追加することにします。
そうして作成したのが以下の call-subshell です。


安直に、egrep で特定の pattern がある時、 BUFFER という vairable を編集する事で追加します。
これで peep-mac-tmux の指定もやりやすくなりました。


無限に増える括弧

さて、peep-mac-tmux しやすくなりました、万歳、とはいかないようです。
この定義ではパターンが含まれるコマンドに、() を追加するので、後のコマンドにもパターンが含まれる。
なので、履歴からコマンドを呼び出すと括弧が増え続けます。これは困る。

そこで括弧が重複しないようにしたのが以下の call-subshell です。


一応、前後に空白があった場合にも対応しています。
これで良し。


まとめ

zle を使うとコマンドを編集できて便利、というお話でした。

ちなみにオチとしては、
  • peep が上手くいくメリット < () がある事で誤爆したりするデメリット
と感じたので、作成して2ヶ月くらいで消してしまいました


参考

2019/11/02

peep を使って、プロセス終了時に Notification Center へ音付きで通知をする

peep というコマンドがあります。pid を指定することで、そのプロセスが終了した際に通知等が行なえます。
この記事は peep コマンドを macOS 上で使ったり、自前 function を作ったりしたログです。


環境

  • OS: macOS Mojave 10.14.6
  • Homebrew: 2.1.15-63-g090259b
  • Homebrew/homebrew-core (git revision 51ba; last commit 2019-10-25)
  • peep: 0.1.2 (rev: f770419/go1.11.4)
  • terminal-notifier: 2.0.0
  • tmux: 2.9a(border-ascii)


peep の install

Homebrew を使ってインストールしてみます。作成者の Songmu さんの記事 の通り
  • $ brew install Songmu/tap/peep
で install できます。


peep-mac: peep + terminal-notifier

peep を install すると peep-notify というコマンドも一緒に install されます。
peep-notify コマンドは Slack に通知したり、macOS の notification center に通知したりできます。

ただ、notification center への通知は音が鳴らずにメッセージだけ表示されます。
軽く読んでみたところ、peep-notify は内部で gosx-notifier を使っていてTitle と Messge のみ設定されています。

peep 自体は pid を監視するコマンドなので、通知に必ずしも peep-notify を使う必要はありません。
そこで、 macOS で音付きで通知できるものは無いかと探す事にしました。
いくつか試したのですが、 terminal-notifier が良さそうです。

terminal-notifier は homebrew に取り込まれているので
  • $ brew install terminal-notifier
で Install できます。

peep + terminal-notifier を使って peep-mac function を作ってみました。 zsh で動きます。
  • $ peep-mac <PID>
とすると、その pid のコマンド名を表示。
その後、プロセス終了時に terminal-notifier で通知が生成されて "Glass" が鳴ります。便利。


peep-mac-tmux: peep + terminal-notifier + tmux

さて、peep-mac を作ったことで、長いプロセスの実行時、音が鳴るまで別の事ができるようになりました。
しかし、 peep-mac を使っていて思ったのです。「pid 調べるのが面倒」と。

そこで作成したのが peep-mac-tmux function です。
tmux 環境下 + 使い方次第では pid の指定をせずに peep-mac が動きます。

具体的には
  • 長いプロセスが実行中の window に pane を追加。
  • 追加した pane 上で peep-mac-tmux を実行。
すると良いです。ここまで来ると完全に自分専用感もありますね。

peep-mac-tmux の実装に関しては
  • pane は 2 つであること
    • 長い process を実行している pane と
    • それをこれから監視しようとする pane
  • active な pane を tmux list-panes から特定する
  • そこで実行されている zsh の pid を取得
  • ps -o <ZSH_PID> で zsh が parent になっている process 一覧を取得
  • pid を sort -r して、一番上のものを peep-mac に投げる
みたいな感じです。大体良い感じに peep してくれます。

ただ、sort -r で監視対象の pid 取得をするので、pidが 一巡してる時には、上手く指定できない事も。
そういう事態はあまり発生しないので、現状はこの実装で運用してます。何か良い解決策無いかな。

と、いうことで、『実行時間長いから終わったら呼んで』みたいな事ができるので便利、みたいなお話でした。


参考