自動補完ツールはひとつでいいという話
適当にいじってそのまま放っておいたEmacsの自動補完関連の環境をきれいにした.
適当に行き当たったサイトを参考にしてHelmをインストールして,ついでにそこで(たぶん)紹介されていたAuto-Completeも有効にして使っていたが,Elpyを使うときにElpyのデフォルトの補完とAuto-Completeの補完が両方出て非常に操作しにくくなるということが時々起こっていた(毎回起こるわけではないのも不可解だった).私の環境ではElpyの補完は黄色いプルダウンで,Auto-Completeのは灰色っぽい水色のプルダウンで表示されていた.Auto-Completeの補完は出たり出なかったりしてよくわからない.
で,試しにAuto-CompleteをElpy-modeでオフにしてみたら,ちゃんと毎回補完が出るし,二系統の補完が出て操作に支障が出るということもなくなった.ついでに,Elpyの補完で不満だった,C-n
とC-p
でカーソルを移動できないという問題も,割と一般的なものらしく,調べたら解決策がすぐにわかった.
その過程で,Elpyの補完はCompanyを使っているということがわかったので,統一したほうが混乱がないのですべてのモードでAuto-Comleteをオフにした.だいぶ快適になった.畢竟,HelmやElpyのことはよくわからないし調べるのに時間もかかりそうだからと,不満のある(というより,いじって壊してしまったという印象だった)環境の解決を棚上げしていたのだった.
さらに,ESS-modeのときにdocumentationを勝手に検索してバッファが切り替わってしまうことがあるのが非常に問題だった(この問題はまったく同じ設定にしていても端末によって起きたり起きなかったりするのがさらに不可解だった).これもACを使わないことで解決した.ただし,Companyにはdocumentationを閲覧する機能はないらしいので,これを使いたい場合には困るかもしれない.
以下,Elpy, Auto-Complete(全コメントアウト),Companyの設定.
Companyについては以下のサイトを参照.
qiita.com
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;elpy settings (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(package-selected-packages (quote (markdown-mode+ exec-path-from-shell helm-c-yasnippet helm markdown-mode ess elmacro pyenv-mode-auto elpygen pyenv-mode elpy)))) (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. ) (package-initialize) (elpy-enable) (pyenv-mode) ;; ;; auto-completeをelpy-modeで無効化 ;; (add-hook 'elpy-mode-hook ;; '(lambda () ;; (auto-complete-mode -1))) (defun ssbb-pyenv-hook () "Automatically activates pyenv version if .python-version file exists." (f-traverse-upwards (lambda (path) (let ((pyenv-version-path (f-expand ".python-version" path))) (if (f-exists? pyenv-version-path) (pyenv-mode-set (s-trim (f-read-text pyenv-version-path 'utf-8)))))))) (add-hook 'find-file-hook 'ssbb-pyenv-hook) (add-hook 'before-save-hook 'whitespace-cleanup) ;; (require 'set-pyenv-version-path) ;; (add-hook 'find-file-hook 'set-pyenv-version-path) ;; (add-to-list 'exec-path "~/.pyenv/shims") ;; (setq split-width-threshold nil) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Auto Completeの設定 ;; -> Companyに移行 ;; ;; (require 'auto-complete) ;; (require 'auto-complete-config) ;; (global-auto-complete-mode t) ;; 個別に指定しなくてもこれでいいのか ;; (ac-config-default) ;; ;; (add-to-list 'ac-modes 'text-mode) ;; text-modeでも自動的に有効にする ;; ;; (add-to-list 'ac-modes 'fundamental-mode) ;; fundamental-mode ;; ;; (add-to-list 'ac-modes 'org-mode) ;; ;; (add-to-list 'ac-modes 'Emacs-Lisp-mode) ;; ;; (add-to-list 'ac-modes 'yatex-mode) ;; ;; (add-to-list 'ac-modes 'R-mode) ;; ;; (add-to-list 'ac-modes 'python-mode) ;; (ac-set-trigger-key "TAB") ;; (setq ac-use-menu-map t) ;; 補完メニュー表示時にC-n/C-pで補完候補選択 ;; (setq ac-use-fuzzy t) ;; 曖昧マッチ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Companyの設定 ;; https://qiita.com/blue0513/items/c0dc35a880170997c3f5 ;; (require 'company) (global-company-mode) ; 全バッファで有効にする (setq company-transformers '(company-sort-by-backend-importance)) ;; ソート順 (setq company-idle-delay 0) ; デフォルトは0.5 (setq company-minimum-prefix-length 3) ; デフォルトは4 (setq company-selection-wrap-around t) ; 候補の一番下でさらに下に行こうとすると一番上に戻る (setq completion-ignore-case t) (setq company-dabbrev-downcase nil) (global-set-key (kbd "C-M-i") 'company-complete) (define-key company-active-map (kbd "C-n") 'company-select-next) ;; C-n, C-pで補完候補を次/前の候補を選択 (define-key company-active-map (kbd "C-p") 'company-select-previous) (define-key company-search-map (kbd "C-n") 'company-select-next) (define-key company-search-map (kbd "C-p") 'company-select-previous) (define-key company-active-map (kbd "C-s") 'company-filter-candidates) ;; C-sで絞り込む (define-key company-active-map (kbd "C-i") 'company-complete-selection) ;; TABで候補を設定 (define-key company-active-map [tab] 'company-complete-selection) ;; TABで候補を設定 (define-key company-active-map (kbd "C-f") 'company-complete-selection) ;; C-fで候補を設定 (define-key emacs-lisp-mode-map (kbd "C-M-i") 'company-complete) ;; 各種メジャーモードでも C-M-iで company-modeの補完を使う ;; yasnippetとの連携 (defvar company-mode/enable-yas t "Enable yasnippet for all backends.") (defun company-mode/backend-with-yas (backend) (if (or (not company-mode/enable-yas) (and (listp backend) (member 'company-yasnippet backend))) backend (append (if (consp backend) backend (list backend)) '(:with company-yasnippet)))) (setq company-backends (mapcar #'company-mode/backend-with-yas company-backends))
ついでに,下記のサイトを参考にCompanyの色設定を変更した.
auto-completeからの乗り換え時の違和感を減らしたかったという動機はなかったが,黄色いポップアップはいかにもださく,それに比べれば青灰基調のほうがだいぶよいように思ったから.今後色は適当に変えるかもしれない.
参考サイトとは色を一部変えている.
;; color settings (set-face-attribute 'company-tooltip nil :foreground "black" :background "lightgrey") (set-face-attribute 'company-tooltip-common nil :foreground "black" :background "lightgrey") (set-face-attribute 'company-tooltip-common-selection nil :foreground "white" :background "steelblue") (set-face-attribute 'company-tooltip-selection nil :foreground "black" :background "steelblue") (set-face-attribute 'company-preview-common nil :background nil :foreground "lightgrey" :underline t) (set-face-attribute 'company-scrollbar-fg nil :background "grey60") (set-face-attribute 'company-scrollbar-bg nil :background "gray40")
gitのローカルリポジトリに自動コミット・自動プッシュを設定する
以前のエントリで示したように,ローカルの作業フォルダをgitで管理してクラウドドライブにpushするようにしたわけだが,
livingdead0812.hatenablog.com
案の定,手動での同期は操作忘れを頻発することがわかったので,自動でのコミットとプッシュのためにいくつか方法を試みた.
やったこと1: .bash_profileで自動でコミットするスクリプトを実行する
簡便な方法として,一定時間おきにgitコマンドを実行するシェルスクリプトをログイン時に実行するという方法を試した.
#! /bin/bash while true; do git pull git add . git commit -m "auto commit" git push sleep 300
下記のサイトを参考に上のようなスクリプトを作成し,bash /hoge/huga/auto-commit.sh
を.bash_profileに書き込んだ.
qiita.com
結果,.bash_profileを読み込んで起動するすべてのシェル上でスクリプトが実行され,すべてのシェルが待機状態になるという結果になった.
用は足りるのかもしれないが,そういうことがしたかったのではない.
やったこと2: launchdを設定して定期的に実行する
以下を参考にして,git操作だけをするシェルスクリプトを実行するようにした.
qiita.com
#! /bin/bash git pull git add . git commit -m "auto commit" git push
実行するためには,以下のようなplistファイルを作り,~/Library/LaunchAgent/
に置く.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>auto-commit</string> <key>ProgramArguments</key> <array> <string>/bin/bash</string> <string>/Users/username/work/auto-commit.sh</string> </array> <key>StartInterval</key> <integer>300</integer> <key>StandardOutPath</key> <string>/Users/username/commitlog/auto-commit.out</string> <key>StandardErrorPath</key> <string>/Users/username/commitlog/auto-commit.err</string> </dict> </plist>
plistファイルを作ったら,以下のコマンドを実行.
$ launchctl load ~/Library/LaunchAgent/filename.plist
これでバックグラウンドでgitコマンドが実行されるようになる.
やったこと3: もう少し行き届いたコミットをさせたい
ここまでだと,コミットメッセージがずっと同じで追随性が悪いし,いろいろ不便がある.
コミットメッセージに変更をある程度反映させたい
とりあえず変更のあったファイル名を取得し,それをコミットメッセージに含めるため,commit
の行を以下のように変更.
git commit -m " auto commit `git diff --name-only`"
git diff --name-only
で変更のあったファイル名を取得し,それをバッククオートでくくることで文字列として出力する.
これをcommit -m
のクオート内に書くことでコメントに流しこむ.
もう少し変更内容を反映したい
git diff --name-only
のオプションを--stat
に変えると,以下のような表示が得られる.
hoge | 2 -- huga | 0 2 files changed, 2 deletions(-)
新規ファイルも同様に反映したい
上記二つの方法は既存のファイルしか表示してくれない.
新規ファイルの差分も取得するためには,git add -N filename
を使う.
git add -N filename
は新規ファイルをステージ対象にするだけで,実際にステージするわけではない(ステージするだけならgit add .
でいい)ので,このあとでgit add
が必要.
最終的にはこんな感じ.
#! /bin/bash git pull git add -N . git add . git commit -m "auto commit" -m "`git diff --stat`" git push
2019/5/12追記
上記の設定でとりあえず起動すると,gitの出力が保存されるcommitlogというフォルダが作られる.
勝手に作られた状態だと,オーナーがrootでパーミッションが711(たぶん)になっていて,launchdが書き込めない状態になっている.
これが原因で,この状態だと動かず,ステータスコード78が返ってくる.
stackoverflow.com
この問題はパーミッションとオーナーを変えれば解決する.
cd # -> to home directory sudo chmod 755 commtlog sudo chown username commitlog
このログをファイルに残す必要があるかというと,ないような気がするが,StandardOutを指定しないとうまく動かなかったので,検討の余地あり.
残さないためには出力先を/dev/null
にすればいいとは思う.
リストやタプルの中身を展開して関数に引数として渡す
いくつかの関数は,可変長位置引数としてリストやタプルを渡すことができるが,できないものもある.
参考:変数の種類について
note.crohaco.net
たとえば,osモジュールのos.path.join()などがそれである.
os.path.join()は,直接複数の文字列を渡すとパス区切りで繋いでくれるが,リストを渡すとリストがそのまま返ってくる.
import os os.path.join('hoge', 'huga', 'piyo') # -> 'hoge/huga/piyo' lst = ['hoge', 'huga', 'piyo'] os.path.join(lst) # -> ['hoge', 'huga', 'piyo']
じゃあリストの形で保持している引数は渡せないのかというと,さっと検索すればこんなのがある.
torina.top
os.path.join(*lst)
# -> 'hoge/huga/piyo'
リストにアステリスクをつけると展開される.
同様のことはapply()というのでもできるらしい.
python-reference.readthedocs.io
ところが,アステリスクによって展開したリストはほかの文字列の引数と共存できない(少なくとのこの場合は).
os.path.join(*lst, 'paru') # -> File "<stdin>", line 1 # -> SyntaxError: only named arguments may follow *expression
まあ,この仕様のことがわかっていれば,追加したい文字列をリストにあらかじめ追加しておけばいいのだが.
ちなみにpython2を前提に考えているが,3系統だと違った挙動をするのかも.
また,join()系統はリストに空要素がある場合には無視するらしい.
つまり絶対パスを.split()なんかで分割してしまった場合,冒頭の'/'の前の部分を指していた空要素は消えてしまう.
lst2 = '/hoge/huga/piyo'.split(os.sep) # -> lst2 = ['', 'hoge', 'huga', 'piyo'] os.path.join(*lst2) # -> 'hoge/huga/piyo'
OS X のemacsをシェルから起動してもPATHが引き継がれなくなった問題
起こったこと
あるとき気がついたら,YaTeXのC-c t j
やC-c t b
が効かなくなっていた.
具体的には,
/bin/bash: platex: command not found
というメッセージが出て止まってしまう.
ターミナルでは,
which platex /Library/TeX/texbin/platex
となる一方,M-! which platex
とすると何も表示されないので,platexのパスが通っていないらしい.
やってみたこと
emacsが参照しているパスを確認するには,環境変数を確認するコマンドM-x getenv
からPATH
を選択する.
/use/bin:/bin:/usr/sbin:/sbin
なるほど全然ダメだ.
tasuwo.github.io
最初はYaTeXの問題かと思ったので,M-x describe-variable
からtex-command
を見てみたものの,
tex-command is a variable defined in ‘yatex.el’. Its value is "platex" This variable may be risky if used as a file-local variable. Documentation: ∗Default command for typesetting LaTeX text. Overridden with ‘%#! CommandLine...’ in the buffer.
となるので,.emacs
やyatex.el
のplatex
に正しいパスを追記してみるも,効果なし.
また,M-! source ~/.bash_profile
などとやってみても効果なし.
解決した方法
emacsには変数PATH
とexec-path
があり,M-!
などでの実行時に読まれているのはM-!
らしい.
おそらくもっとも簡便なのは,exec-path-from-shell
を設定すること.
sakito.jp
keisanbutsuriya.hateblo.jp
github.com
このときのトラブルのためか,M-x package-install
が正常に動作しなかったので,M-x package-list-packages
で一覧から探してインストールした.
ついで.emacs
(など)に,
(when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize))
と書いて,再起動.
無事PATHは引き継がれるようになり,YaTeXも正常に動作するようになりました.
しかしなんでシェルから起動してるのに引き継がれないのだろう.
しばらくして
なぜか.bash_profile
, .bashrc
, .emacs
, .Rprofile
のテキストがすべて消えるという謎現象が起こった.
クラウドドライブにgitのリモートリポジトリを構築する
そもそもクラウド自体がgit(だと思う)を使って実現しているわけだから,いかにも屋上屋を架している感があるが,メリットがないわけではない.
こういう場合に
やったこと
mac osxの場合(ほかはわからない)
gitのユーザー名とメールアドレスを設定(一度もgitを使ったことがない場合)
$ git config --global user.name "username" $ git config --global user.email "user@email.com"
リモートリポジトリを作る
pushする先になるディレクトリをDropboxなどの適当な場所に作る.
末尾を ".git" にするのが慣習らしい.
ディレクトリを作ったら,gitをリモートとして(--bareオプション)初期化する.
$ mkdir ~/Dropbox/myrepo.git $ git --bare init --shared
公開鍵認証の設定
今回ほとんど唯一と言っていい,はまった箇所.
公開鍵暗号を設定すると,いちいちコマンドを打つたびにパスワードを聞かれるということがなくなる.
...はずなのだが,この公開鍵認証が一向に成功せず,パスワードを聞かれ続け,
やっと聞かれなくなったと思ったら認証が通らない状態になった.
こんな感じのエラーが出て,
username@localhost: Permission denied (publickey,password). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
$ ssh -v localhost
でデバッグモードで接続してログを詳細表示すると,
OpenSSH_7.9p1, OpenSSL 1.0.2q 20 Nov 2018 debug1: Reading configuration data /usr/local/etc/ssh/ssh_config debug1: Connecting to localhost [::1] port 22. debug1: Connection established. debug1: identity file /Users/username/.ssh/id_rsa type 0 debug1: identity file /Users/username/.ssh/id_rsa-cert type -1 debug1: identity file /Users/username/.ssh/id_dsa type -1 debug1: identity file /Users/username/.ssh/id_dsa-cert type -1 debug1: identity file /Users/username/.ssh/id_ecdsa type -1 debug1: identity file /Users/username/.ssh/id_ecdsa-cert type -1 debug1: identity file /Users/username/.ssh/id_ed25519 type -1 debug1: identity file /Users/username/.ssh/id_ed25519-cert type -1 debug1: identity file /Users/username/.ssh/id_xmss type -1 debug1: identity file /Users/username/.ssh/id_xmss-cert type -1 debug1: Local version string SSH-2.0-OpenSSH_7.9 debug1: Remote protocol version 2.0, remote software version OpenSSH_6.9 debug1: match: OpenSSH_6.9 pat OpenSSH* compat 0x04000000 debug1: Authenticating to localhost:22 as 'username' debug1: SSH2_MSG_KEXINIT sent debug1: SSH2_MSG_KEXINIT received debug1: kex: algorithm: curve25519-sha256@libssh.org debug1: kex: host key algorithm: ecdsa-sha2-nistp256 debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC:compression: none debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: compression: none debug1: expecting SSH2_MSG_KEX_ECDH_REPLY debug1: Server host key: ecdsa-sha2-nistp256 SHA256:(なんらかの鍵) debug1: Host 'localhost' is known and matches the ECDSA host key. debug1: Found key in /Users/username/.ssh/known_hosts:2 debug1: rekey after 134217728 blocks debug1: SSH2_MSG_NEWKEYS sent debug1: expecting SSH2_MSG_NEWKEYS debug1: SSH2_MSG_NEWKEYS received debug1: rekey after 134217728 blocks debug1: Will attempt key: /Users/username/.ssh/id_rsa RSA SHA256:(公開鍵) debug1: Will attempt key: /Users/username/.ssh/id_dsa debug1: Will attempt key: /Users/username/.ssh/id_ecdsa debug1: Will attempt key: /Users/username/.ssh/id_ed25519 debug1: Will attempt key: /Users/username/.ssh/id_xmss debug1: SSH2_MSG_SERVICE_ACCEPT received debug1: Authentications that can continue: publickey debug1: Next authentication method: publickey debug1: Offering public key: /Users/username/.ssh/id_rsa RSA SHA256:(公開鍵) debug1: Authentications that can continue: publickey debug1: Trying private key: /Users/username/.ssh/id_dsa debug1: Trying private key: /Users/username/.ssh/id_ecdsa debug1: Trying private key: /Users/username/.ssh/id_ed25519 debug1: Trying private key: /Users/username/.ssh/id_xmss debug1: No more authentication methods to try. username@localhost: Permission denied (publickey).
実際には,ちょっとずつ設定をいじるたびにちょっとずつ内容が変わるが,
だいたい最後のところでパスワードを認証に使うかどうかというところが違うくらい.
やったこと
公開鍵と秘密鍵のペアを作る
$ ssh-keygen # いろいろ聞かれるが,基本的にデフォルトのままenterでOK # id_rsaとid_rsa.pubが生成される $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys # 要するに名前を変えてコピーしてるだけ # とんでもない間違いに気づいたので修正…
再帰的に関数を実行するときに返り値が取得できない
はまったこと
数値を入力させたいなど,正しい値が得られるまで同じ処理を繰り返したいことがある.
具体的には,例えばこんな感じ.
def get_number(): try: num = raw_input('enter number: ') except ValueError: get_number() else: return num get_number() type(get_number)
try節でValueErrorが発生したらexcept節で再帰的にget_number()を呼び出す.
いつかValueErrorが発生しない値が得られたら,else節でnumをreturnして終了.
そう考えたくなる.
ところが,
None <type 'NoneType'>
実行結果はこうなる.
値が返っていない.
正しい書き方
def get_number(): try: num = raw_input('enter number: ') except ValueError: return get_number() else: return num get_number() type(get_number)
except節で自身を呼び出すときにもreturnしてやると正しく実行できる.
stackoverflow.com
再帰的に実行しているときにreturnで値が返っても,最上位のtry-except文においてはelse節が実行されていないからこうなるらしい.
AppleScriptで動画の長さを取得する
ファイルの情報を取得するのにはFinderにファイルを渡して問い合わせるのが一般的だと思う.
2/3 AppleScriptの構造を上手に調べる [Mac OSの使い方] All About
choose file set theFile to result tell application "Finder" properties of item theFile end tell
こんな感じにすると一通りの情報が出てくるが,動画に関しては長さやビットレート,解像度といった情報は出てこない.
Finderには表示されるわけだが,これは何らかの方法でFinder以外の部分が計算しているのだろう.
とりあえず方法としては,QuickTimeでファイルを開いてdocumentの要素を取り出すというものがある.
tell application "QuickTime Player" set tDoc to open theFile tell tDoc duration of tDoc end tell end tell
一旦開くことになるのがいまいちかもしれない方法.
ちなみに何の値が取り出せるのかは,Script Editorで「ファイル」→「用語説明」→「QuickTime Player」と辿るとわかる.
AppleScriptでQuickTime Playerを自動化する | QuickTime