YaTeX はカスタマイズ性に優れ,かゆいところに手が届くどころか YaTeX の機能を使いたいが為にかゆいところを探すほどである. 他の統合環境とは違い,設定さえできればどのようなマクロに対しても自由自在に入力支援が得られる. 特にマクロを自作する人やオプション引数に keyval が多用されているマクロやパッケージをよく利用する人は YaTeX 一択と言い切れる程である. さらに,lisp に慣れていない人のために自作の入力支援関数を作成するためのパッケージも付属している. 詳しくは yatex/docs/yatexadd.docyatex/yatexadd.el および yatex/docs/yatexgen.docyatex/yatexgen.el を参照のこと. YaTeX の入力支援関数の作成支援を体験してみたい人は TeX のソースファイルで M-x YaTeX-generateM-x YaTeX-generate-simple してみるといいかもかも.

もちろん YaTeX がパーフェクトであるというつもりはない.ちょっとした愚痴もある.

カスタマイズ変数一覧の一部

以下は YaTeX のヘルプなどから抜き出し,私の変更点を加えただけのものである. なお,emacs で YaTeX のヘルプを見ようとしても文字化けして見れないという人は, 騙されたと思って C-x [ENTER] r sjis-dos [ENTER] としてみてください. 理由はわからないが Melpa で配布されている YaTeX パッケージは2019年2月1日現在 docshelp の2つのディレクトリが含まれていない. すなわち,help/YATEXHLP.(jp|eng) の2つのファイルが含まれていないため Emacs からヘルプを見ることができない. Melpa のよいところがまったく活かせないので早くどうにかしてもらいたい.

YaTeX-japan

YaTeX のメッセージを英語/日本語の選択をする(default : nil [英語])

t [日本語]

この変数の値によっていくつかの変数のデフォルトが変化する. tex-command は,YaTeX-use-LaTeX2et のとき platex が設定され, YaTeX-use-LaTeX2enil のときかつ YaTeX-japant のとき jlatex が設定され, いずれも nil ならば latex が設定される. 以下,YaTeX-japan の値に関して, bibtex-command は,t のとき jbibtexnil のとき bibtexが設定される. YaTeX-default-document-style および YaTeX-default-documentclass は,t のとき jsarticlenil のとき articleが設定される. mode-name は,t のとき やてふnil のとき YaTeX が設定される. help-file は,t のとき YATEXHLP.jpnil のとき YATEXHLP.eng が設定される. ちなみに,YaTeX モードのとき C-c C-x I とすると YaTeX のヘルプファイルが表示される. (1.81.2 より YaTeX-default-document-styleYaTeX-default-documentclass の挙動が同じになる. 以前は YaTeX-japant のとき YaTeX-default-documentclassjarticle であった.)

YaTeX-use-LaTeX2e bibtex-command YaTeX-default-document-style YaTeX-default-documentclass mode-name help-file
t nil
YaTeX-japan t platex jlatex jbibtex jsarticle やてふ YATEXHLP.jp
nil latex bibtex article YaTeX YATEXHLP.eng
YaTeX-japan の値と他の変数の対応(ただし YaTeX-use-LaTeX2e との関係は tex-command の値である.)

これらの値が気に入らないときは,yatex.el を読み込んだ後に (setq tex-command "/usr/local/bin/ptex2pdf -l -u") などと変更すればよい.

YaTeX-kanji-code

文書を作成する時の漢字コード(default : nil = 既存のコードのまま)

pLaTeX を使っているのであればデフォルトの nil でいいが,LuaLaTeX を使っているため 4 に設定し強制的にソースファイルの文字コードを UTF-8 にしている. ソースを utf-8 で記述しなければならない uplatex,XeLaTeX を使う人も 4 に設定しておいた方がいいだろう.

0=no-conversion1=Shift JIS2=JIS3=EUC4=UTF-8

4

tex-command

LaTeX タイプセッタコマンド名(default : platex)

基本的にタイプセットに関する設定はメインのソースファイルの冒頭に %#!lualatex -synctex=1 filename と設定しているので, この設定はほとんど使われることはない.

ptex2pdf -l -u -ot '-synctex=1'

dvi2-command

プレヴューアコマンド名(default : xdvi -geo +0+0 -s 4)

/usr/bin/evince

makeindex-command

makeindex コマンド(default : makeindex)

mendex -g -s mystyle.ist

YaTeX-dvipdf-command

dvi を PDF に変換するコマンド(default : dvipdfmx)

YaTeX-item-regexp

\item の桁揃えの時に用いる,\item の正規表現(default : (concat (regexp-quote "\\") "\\(sub\\|bib\\)*item"))

edaenumerate 環境および hlist 環境を使用することにしたので \edaitem\hitem も含める.

(eval-after-load 'yatex
                 (setq YaTeX-item-regexp (concat (regexp-quote "\\") "\\(sub\\|bib\\|eda\\|h\\)*item")))

YaTeX-no-begend-shortcut

C-c C-b ?? のショートカットを使わず,C-c C-b だけで補完入力に入る(default : nil)

t

YaTeX-create-file-prefix-g

\include などで C-c C-g した時に, ジャンプ先が存在しないファイルであってもオープンする(default : nil)

個人的には t にしておいた方が自分でファイルを作成する手間が省けて嬉しい. \include の引数を間違えておかしなファイル名のバッファができたとしてもすぐに C-x k すればバッファから消え去るから問題ない.

t

YaTeX-use-AMS-LaTeX

AMS-LaTeX を使用する場合は t に設定する(default : nil)

align 環境など amsmath パッケージで定義されている環境を利用するのであれば t に設定する. nil では align 環境等で数式モードで動作しない.

t

YaTeX-electric-indent-mode

Emacs 24.4 で導入された改行時の,自動インデントをどうするか. この値がそのまま electric-indent-local-mode に渡される. -1 で off.

Emacs 24.4 以降のヴァージョン場合,デフォルトのままでは enumerate 環境などで改行をした場合勝手に桁揃えをしてくれない. Emacs 24.4 以前のヴァージョンの動作が好みの場合は t にしておく.

t

YaTeX-on-the-fly-preview-interval

ヘルプでは「環境タイプセット」「on-the-fly プレビュー」「環境即時プレビュー」「即時プレビュー」などブレがあるが C-c C-t e として動作する機能があり, それは一般的にはポイントが含まれる最も内側の環境や数式環境内の場合はポイントが含まれる数式環境全体を自動で領域タイプセットしプレビューする機能であり, これが動作するとその環境内で文字等の修正が行われる度に自動で新たにタイプセット及びプレビューが行われるのだが, その際のキー入力から動作するまでの無動作時間を設定する変数である. ただし,nil を指定した場合は「環境タイプセット」は動作しない. この「環境タイプセット」は一度動作をすると YaTeX ではその動作を解除する手法が与えられておらず, 他のバッファに一度移動するなどをすることで解除できるが, 勝手に何度も入力者の意志に反してタイプセットが行われるのは甚だ鬱陶しいのでこの機能を動作しないように nil にしている.

ヘルプには nil で動作しないようなことが書いてあるのだが,2018年1月7日時点では nil でも「環境タイプセット」が動作してしまう.

nil

YaTeX-ref-generate-label-function

\ref{} のラベル名自動生成のときに使う関数のシンボル. デフォルトは標準の YaTeX::ref-generate-label 関数が割り当ててある. 引数を2つ取る関数を定義して,この変数にセットするとその関数を呼んだ結果をデフォルトのラベル名候補とする.

この2つの引数は C-c C-s ref\ref コマンドを利用したときにそのラベルを自動生成し, さらに対応する \label コマンドの位置を指定するが, そのときに選択したアイテムに合わせて,第1引数にはそのコマンドや環境名,第2引数には第1引数が \section 系の場合その引数が設定される. 例えば \label 位置の選択で align 環境内の行を選択すると第1引数には align,第2引数は空文字列 \setction{hoge} を選択したすると第1引数には section,第2引数には hoge が代入される.

このことを利用するとデフォルトでは \ref を設定するとそのラベルは時刻を用いた文字列が自動で生成されるが, 好みの任意のラベルをつけることができるようになる. 以下はこの変数の設定例を参考にした私の設定である(ほとんど設定例のままであるが…).

(eval-after-load 'yatexadd
  `(progn
     (setq YaTeX::ref-mathenv-regexp (concat YaTeX::ref-mathenv-regexp "\\|\\(sub\\)?numcases")
	   YaTeX-ref-generate-label-function 'my-yatex-generate-label)
     (defun my-yatex-generate-label (command value)
       (and (string= command "caption")
	    (re-search-backward "\\\\begin{\\(figure\\|table\\)}" nil t)
	    (setq command (match-string 1)))
       (let ((alist '(("chapter" . "chap")
		      ("section" . "sec")
		      ("subsection" . "subsec")
		      ("figure" . "fig")
		      ("table" . "tbl")
		      ("align" . "eq")
		      ("gather" . "eq")
		      ("numcases" . "eq")
		      ("subnumcases" . "eq")
		      ("equation" . "eq")
		      ("eqnarray" . "eq")
		      ("item" . "enu")))
	     (labelname (replace-regexp-in-string
			 "\\(:\\|-\\)" ":"
			 (concat (if (> (length YaTeX-parent-file) 0)
				     (concat (file-name-sans-extension
					      (file-name-nondirectory
					       YaTeX-parent-file)) ":"))
				 (file-name-sans-extension
				  (file-name-nondirectory
				   (buffer-name)))))))
	 (if (setq command (cdr (assoc command alist)))
	     (concat command ":"
		     (read-string "ユニークなラベルになるような文字列・番号などを入力してください: "
				  (concat labelname ":" value)))
	   (YaTeX::ref-generate-label nil nil))))
     ))

YaTeX::ref-mathenv-regexpC-c C-s ref で検索する \label を付けられる数式環境の一覧であり, その一覧に \\|\\(sub)?numcases を追加している. 例えば,親ファイルが main.tex,子ファイルが child.texC-c C-s ref\label をつける場所を align 環境に設定し, pythagorean-theorem という文字列を入力した場合,\ref および \label の引数は, eq:main:child:pythagorean-theorem という文字列が設定される. ちなみに YaTeX::ref-generate-label は,デフォルトの \ref および \label の引数を作成する関数であり,時間を元に引数が作成される. また,YaTeX-parent-file は,メインファイルのディレクトリを含めた文字列が代入されているようである.

恥ずかしながら最近まで C-c C-s label\label を設定し C-y\ref を貼り付けていた.しかし,これは間違った使い方である. \ref を設定し,それに応じて \label が設定されるべきというポリシーのもとに YaTeX が設計されている.

TeX の入力支援のカスタマイズ・強化

YaTeX で定義されている関数一覧の一部

他の統合環境などと比較して YaTeX の素晴らしい点として,強力な入力支援とそのカスタマイズ性が挙げられる. 例えば,enumitem パッケージの enumerate 環境のオプションとして resume というのがあるが, C-c C-b enumerate したときにオプション resume を利用するかどうか聞くようにしたいときは次のようにする.

(defun YaTeX:enumerate ()
  ((let (op (if y-or-n-p "resume オプションを使いますか? ")
	    "resume"))
   (if (> (length op) 0)
       (concat "[" op "]"))))

これは非常に簡単な例であるが,工夫次第で様々なオプションに対応した入力支援が得られる. 以下は,その工夫時に利用できる予め YaTeX で定義されている関数である.

YaTeX:read-position (引数:[] の中に入れてもよい文字を羅列した文字列)

[htb] などのような location 指定を作成します. 何も入力せずリターンを押すと,[] 自体も省略されます. [] の中に来るべき文字が htbp に限られているなら,(YaTeX:read-position htbp) と呼び出します.

YaTeX:read-coordinates (基本プロンプト,X座標プロンプト,Y座標プロンプト(全て省略可))

「基本プロンプト X座標プロンプト:」というプロンプトを出して,X座標を読み込み, 「基本プロンプト Y座標プロンプト:」を出して,Y座標を読み込み,(X座標,Y座標) の様な形式を作成します. 何も入力せずリターンを押しても,(,) が返されます. 各プロンプトのデフォルトはそれぞれ,Dimension, X, Y です.

YaTeX-intelligent-newline-foo

YaTeX では enumerate 環境や tabular 環境で M-[ENTER]ESC [ENTER] とすると「おまかせ改行」機能が働くが, enumitem パッケージなどで \newlist{centerenum}{enumerate}{1} などとして新たな enumerate 環境を定義した場合, これらの環境でも「おまかせ改行」をして欲しくなる. このとき使うのが YaTeX-intelligent-newline-foo である. foo の部分は対応させる環境により決まる. 今回の例の場合は ~/.emacs.d/init.el において (fset 'YaTeX-intelligent-newline-centerenum 'YaTeX-intelligent-newline-itemize) としておくと, 新たに定義した centerenum 環境でも itemize 環境と同様に「おまかせ改行」が利用できるようになる.

また,hlist パッケージの hlist 環境は \linewidth を等間隔に分割して enumerate 環境のように番号付けを行う環境(emath の edaenumerate 環境に相当する)であるが, \item ではなく \hitem で項目を指定する. この場合は YaTeX-intelligent-newline-itemize の定義を模倣して次のようにする.

(YaTeX-intelligent-newline-hlist ()
	"Insert  '\\hitem' ."
	(insert "\\hitem ")
	(YaTeX-indent-line))

自作した入力支援用関数などは yatexhks.el に記述するように指示されているが,もしものことを考え, my-func.el などというユニークなファイルを作成してそれに記述し, yatexhks.el 内に (require 'my-func) とだけ記述するほうが YaTeX 一式を新調したときも自作の関数は上書きされず安全である.

YaTeX の入力支援を補強する自作の関数

lisp 初心者でヘルプなどを見様見真似で作ったものである. emath という接頭語がついているが,これらは元々が emath の pszahyou 環境のオプションを覚えきれなかったため, 足りない頭の代わりに YaTeX に頑張ってもらおうとして関数を作成したという経緯のためである. emath を my などに置換すればいいのだろうが, 如何せん置換した後に問題なく動くという自信がないので置換していない. この接頭語は無視してください.

emath-select-key-value

(emath-select-key-value "hoge" "[option]" `(("ふが" "fuga") ("あわわ" "awawa"))) とすると,``[option] 0:設定しない 1:ふが 2:あわわ: '' という選択肢を表示し, 入力した数字によって 1 --> hoge=fuga 2 -->hoge=awawa という文字列を返す. "hoge"を""としたときは 1--> fuga 2-->awawa という文字列を返す. 下請けの関数で直接使うことはまずない. 定義は次の通りである.

(defun emath-select-key-value (key title lists)
  (let* ((guidelist (mapcar 'car lists));;lists内の各々のリストに対してcarを実行して新たなリストを作成
	 (valuelist (mapcar '(lambda (x) (nth 1 x)) lists));;mapcarの第1引数が引数をとる関数のときの処理
;; この無名関数はリストの引数を1つとりその2番目の要素を返す
;; すなわち valuelist は lists の要素である各々のリストに対して2番目の要素を次々と取り出して並べたリスト
	 (emnum (emath-get-number-from-list title guidelist)))
    (if (or (< emnum 1)(> emnum (length guidelist))) ""
	(emath-option-combine "=" `(,key ,(nth (- emnum 1) valuelist))))))
emath-get-number-from-list

emath-select-key-value の下請け関数. (emath-get-number-from-list "[option]" `("ほげ" "ふが" "あわわ") "attention") で, "[option] 1:ほげ 2:ふが 3:あわわ attention" と表示し数字の入力を促し,入力された数値を返す. 単独で (cond ((= num 1)(...))((= num 2)(...))(()())) などの num の設定にも使える. 定義は次の通りである.

(defun emath-get-number-from-list (title guides &optional addguide)
  (message (format "%s%s%s: "
		   (if (> (length title) 0)(concat title " ")"")
		   (emath-make-guide 1 guides)
		   (if (> (length addguide) 0) addguide "")))
  (let* ((string (read-char)))
    (string-to-number (char-to-string string))))
emath-make-guide

emath-get-number-from-list の下請け関数. (emath-make-guide 0 `("ほげ" "ふが" "あわわ")) で, 第2引数の list から中身を順に取り出し 0:ほげ 1:ふが 2:あわわ のように番号付けをした文字列を作り出す. 第1引数が 1 なら 1:ほげ 2:ふが 3:あわわ となる. 定義は次の通りである.

(defun emath-make-guide (num guides)
  (let* ((emnum num)
	 (emlist (mapcar '(lambda (x) (prog1 (format "%s:%s " emnum x)
					(setq emnum (+ emnum 1)))) guides)))
    (mapconcat 'concat emlist "")))
emath-option-combine

emath-select-key-value の下請け関数. (emath-option-combine separator optionlist) で, optionlist の中身を separator で繋げる. そのときリスト内の文字列の前後のどちらか空文字列 "" であれば separator はつかない. リスト内に変数 var1var2var3 がある場合は `(,var1 ,var2 ,var3) と指定する. 具体的には,separator が "," で optionlist("a" "" "b" "" "c" "d" "" "" "" "e" "f" "" "")の場合, "a,b,c,d,e,f" という文字列を作り出す. 定義は次の通りである.

(defun emath-option-combine (separator oplist)
  (let* ((emlist (split-string (mapconcat 'concat oplist separator) (concat separator "+") t)))
    (mapconcat 'concat emlist separator)))
emath-setoption

ここからが本命. (emath-setoption options str guide leftbrace rightbrace default) で, 変数 options に追加する文字列を guide を表示し文字列の入力を促し, (concat options ",str=leftbrace入力文字列rightbrace") という文字列を返す. ただし,入力文字列が空文字列 "" の場合は options をそのまま返す. leftbrace 以降はオプショナルであり,デフォルトは空文字列 "" である. options は空文字列 "" でも構わない.

具体的な使用例は,次のようなコードを yatexhks.el などに記述し C-c C-s hoge などと YaTeX の入力支援を実行すると, 順に「左マージンの指定: 」「右マージンの指定: 」「背景色の指定: 」と聞いてくるので, 例えば順に「10pt」「」(Enterキーを押すだけ)「red」と入力すると, 変数 opleftmargin=10pt,background-color=red という文字列が代入される. するとソースファイルに \hoge[leftmargin=10pt,background-color=red]{} という文字列が追加される. このように keyval 系の複数のオプションの付加を容易にするための関数である.

(defun YaTeX:hoge ()
  (let* ((op (emath-setoption "" "leftmargin" "左マージンの指定"))
	 (op (emath-setoption op "rightmargin" "左マージンの指定"))
	 (op (emath-setoption op "background-color" "背景色の指定")))
    (if (> (length op) 0)
	(concat "[" op "]"))))

例えば,オプションが coord={(1,2)} のように「,」が含まれる場合はオプションの指定に使う文字列を「{}」でくくらなければならない. そのようなときは

(emath-setoption op "coord" "座標の指定" "{" "}")
とする. また,オプションの指定文字列としてよく使う文字をデフォルトで入力済みにするときは
(emath-setoption op "color" "色の指定" "" "" "black")
などとする.この場合は「色の指定: black」のように最初から文字列「black」が「色の指定:」と同時に出力される. これにより出力される文字列「black」はミニバッファで編集可能である.

定義は次の通りである.

(defun emath-setoption (options key guide &optional leftbrace rightbrace default)
  (let* ((temp (read-string (format "[option] %s: " guide) default))
	 (emleft (if leftbrace leftbrace ""))
	 (emright (if rightbrace rightbrace ""))
	 (emoption (concat options ","
			   (if (> (length temp) 0) (format "%s=%s%s%s" key emleft temp emright) "")))
	 (emlist (split-string emoption "," t)))
    (mapconcat 'concat emlist ",")))
emath-tenretu-loop

(emath-tenretu-loop separator guide seq) で, guide を表示して入力を促し,入力されたものを separator で区切って繋げていく. Enterキーのみを押すと終了する. 具体的な使用例は,次のコードを yatexhks.el などに記述し C-c C-m draw などと YaTeX の入力支援を実行すると,「折れ線が通る点を順に入力:」と聞いてくるので, 順に「(A)」「(B)」「(C)」「」と入力すると, ソースファイルに \draw(A)--(B)--(C); という文字列が代入される. seq はこの関数が再帰的に使用される際に用いるもので通常は指定する必要はない.

(defun YaTeX:draw ()
  (concat (emath-tenretu-loop "--" "折れ線が通る点を順に入力") ";"))

定義は次の通りである.

(defun emath-tenretu-loop (separator guide &optional seq)
  (let* ((empoint (read-string (format "%s: " guide))))
    (if (> (length empoint) 0)
	(let* ((empoint (emath-option-combine separator `(,seq ,empoint))))
	  (emath-tenretu-loop separator guide empoint))
      (concat seq))))

YaTeX と evince の連携

連携の前の下準備

連携を行うための下準備として,~/bin に次のコードからなる fwdevince というファイルを作成して, chmod +x fwdevince として実行権限を与える. ただし,python のバージョンは3以上でなければ動作しないので python -V としてバージョンを確かめ, #!/usr/bin/python は環境によって #!/usr/bin/python3 などと変更する必要がある. fwdevince 本体 (7.6KB)

また,TeX をコンパイルする際はオプションに -synctex=1 を付加する. TeX のソースの冒頭に %#!uplatex -synctex=1 source file name%#!ptex2pdf -l -u -ot "-synctex=1" source file name などと記述するか, ~/.emacs.d/init.el (Spacemacs の場合は .spacemacsdotspacemacs/user-config) に (setq tex-command "lualatex -synctex=1") などと記述しておけばよい.

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2010 Jose Aliste
#               2011 Benjamin Kellermann
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public Licence as published by the Free Software
# Foundation; either version 2 of the Licence, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public Licence for more
# details.
#
# You should have received a copy of the GNU General Public Licence along with
# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
# Street, Fifth Floor, Boston, MA  02110-1301, USA
import dbus
import argparse
import os.path
import traceback
import sys
import urllib.parse

class EvinceForwardSearch:
    def parse_args(self):
        parser = argparse.ArgumentParser(description='Forward search with Evince')
        parser.add_argument('pdf', nargs=1, help='PDF file')
        parser.add_argument('line', nargs=1, type=int, help='Line')
        parser.add_argument('tex', nargs=1, help='TeX file')
        return parser.parse_args()
    def run(self):
        args = self.parse_args()
        pdf = os.path.abspath(args.pdf[0]).replace(" ", "%20")
        line = int(args.line[0])
        tex = os.path.join(os.path.dirname(os.path.abspath(args.tex[0])), './', os.path.basename(os.path.abspath(args.tex[0])))
        try:
            import time
            bus = dbus.SessionBus()
            daemon = bus.get_object('org.gnome.evince.Daemon', '/org/gnome/evince/Daemon')
            dbus_name = daemon.FindDocument('file://' + urllib.parse.quote(pdf, safe="%/:=&?~#+!$,;'@()*[]"), True, dbus_interface='org.gnome.evince.Daemon')
            window = bus.get_object(dbus_name, '/org/gnome/evince/Window/0')
            time.sleep(0.2)
            window.SyncView(tex, (line, 1), 0, dbus_interface='org.gnome.evince.Window')
        except dbus.DBusException:
            traceback.print_exc()
class EvinceInverseSearch:
    def parse_args(self):
        parser = argparse.ArgumentParser(description='Inverse search with Evince')
        parser.add_argument('pdf', nargs=1, help='PDF file')
        parser.add_argument('editor', nargs=1, help='Editor command')
        return parser.parse_args()
    def run(self):
        import dbus.mainloop.glib
        from gi.repository import GObject
        args = self.parse_args()
        pdf = os.path.abspath(args.pdf[0])
        editor = args.editor[0]
        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
        a = EvinceWindowProxy('file://' + pdf, editor, True)
        loop = GObject.MainLoop()
        loop.run()

class EvinceWindowProxy:
    """A Dbus proxy for an Evince Window."""
    daemon = None
    bus = None
    RUNNING = range(2)
    CLOSED = range(2)
    EV_DAEMON_PATH = '/org/gnome/evince/Daemon'
    EV_DAEMON_NAME = 'org.gnome.evince.Daemon'
    EV_DAEMON_IFACE = 'org.gnome.evince.Daemon'
    EVINCE_PATH = '/org/gnome/evince/Evince'
    EVINCE_IFACE = 'org.gnome.evince.Application'
    EV_WINDOW_IFACE = 'org.gnome.evince.Window'
    def __init__(self, uri, editor, apawn=False, logger=None):
        self._log = logger
        self.uri = uri.replace(" ", "%20")
        self.editor = editor
        self.status = self.CLOSED
        self.source_handler = None
        self.dbus_name = ''
        self._handler = None
        try:
            if EvinceWindowProxy.bus is None:
                EvinceWindowProxy.bus = dbus.SessionBus()
            if EvinceWindowProxy.daemon is None:
                EvinceWindowProxy.daemon = EvinceWindowProxy.bus.get_object(self.EV_DAEMON_NAME,
                    self.EV_DAEMON_PATH,
                    follow_name_owner_changes=True)
            EvinceWindowProxy.bus.add_signal_receiver(self._on_doc_loaded,
                signal_name='DocumentLoaded',
                dbus_interface=self.EV_WINDOW_IFACE,
                sender_keyword='sender')
            self._get_dbus_name(False)
        except dbus.DBusException:
            traceback.print_exc()
            if self._log:
                self._log.debug('Could not connect to the Evince Daemon')
    def _on_doc_loaded(self, uri, **keyargs):
        if uri == self.uri and self._handler is None:
            self.handle_find_document_reply(keyargs['sender'])
    def _get_dbus_name(self, spawn):
        EvinceWindowProxy.daemon.FindDocument(self.uri, spawn,
                     reply_handler=self.handle_find_document_reply,
                     error_handler=self.handle_find_document_error,
                     dbus_interface = self.EV_DAEMON_IFACE)
    def handle_find_document_error(self, error):
        if self._log:
            self._log.debug('FindDocument DBus call has failed')

    def handle_find_document_reply(self, evince_name):
        if self._handler is not None:
            handler = self._handler
        else:
            handler = self.handle_get_window_list_reply
        if evince_name != '':
            self.dbus_name = evince_name
            self.status = self.RUNNING
            self.evince = EvinceWindowProxy.bus.get_object(self.dbus_name, self.EVINCE_PATH)
            self.evince.GetWindowList(dbus_interface = self.EVINCE_IFACE,
                          reply_handler = handler,
                          error_handler = self.handle_get_window_list_error)
    def handle_get_window_list_error (self, e):
        if self._log:
            self._log.debug("GetWindowList DBus call has failed")
    def handle_get_window_list_reply (self, window_list):
        if len(window_list) > 0:
            window_obj = EvinceWindowProxy.bus.get_object(self.dbus_name, window_list[0])
            self.window = dbus.Interface(window_obj,self.EV_WINDOW_IFACE)
            self.window.connect_to_signal("SyncSource", self.on_sync_source)
        else:
            #That should never happen.
            if self._log:
                self._log.debug("GetWindowList returned empty list")

    def on_sync_source(self, input_file, source_link, timestamp):
        import subprocess
        import re
        print(input_file + ':' + str(source_link[0]))
        # This is probably useless
        input_file = input_file.replace("%20", " ")
        # This is to deal with source files with non-ascii names
        # We get url-quoted UTF-8 from dbus; convert to url-quoted ascii
        # and then unquote. If you don't first convert ot ascii, it fails.
        # It's a bit magical, but it seems to work
        #input_file = urllib.parse.unquote(input_file.encode('ascii'))
        input_file = urllib.parse.unquote(input_file)
        print(type(input_file), input_file)
        #cmd = re.sub("%f", input_file, self.editor)
        cmd = re.sub("%f", input_file.replace('file://', ''), self.editor)
        cmd = re.sub("%l", str(source_link[0]), cmd)
        print(cmd)
        subprocess.call(cmd, shell=True)
        if self.source_handler is not None:
            self.source_handler(input_file, source_link, timestamp)
if __name__ == '__main__':
    cmd = os.path.basename(sys.argv[0])
    if cmd == 'fwdevince' or cmd == 'evince_forward_search':
        EvinceForwardSearch().run()
    elif cmd == 'invevince' or cmd == 'bwdevince' or cmd == 'evince_inverse_search' or cmd == 'evince_backward_search':
        EvinceInverseSearch().run()
    else:
        sys.stderr.write("rename 'fwdevince' or 'invevince'\n")
        sys.exit(1)

forword search

YaTeX から evince への連携は,以下を ~/.emacs.d/init.el (Spacemacs の場合は .spacemacsdotspacemacs/user-config) に次のコードを記述すると平文で C-c C-g でカーソル位置に対応する PDF ファイルの箇所に飛ぶことができる.

(setq YaTeX-inhibit-prefix-letter t)
(setq YaTeX-dvi2-command-ext-alist
      '((Preview\\|evince\\|okular\\|zathura\\|qpdfview\\|pdfopen\\|xdg-open\\|open\\|start" . ".pdf")))
(setq dvi2-command "evince")
(setq tex-pdfview-command "evince")

inverse search

evince から YaTeX への連携は,以下を ~/.emacs.d/init.el (Spacemacs の場合は .spacemacsdotspacemacs/user-config) に記述すると evince で ctrl + 右クリック でクリックした場所に該当する TeX のソースに飛ぶ.

(require 'dbus)

(defun un-urlify (fname-or-url)
  "A trivial function that replaces a prefix of file:/// with just /."
  (if (string= (substring fname-or-url 0 8) "file:///")
      (substring fname-or-url 7)
    fname-or-url))

(defun evince-inverse-search (file linecol &rest ignored)
  (let* ((fname (decode-coding-string (url-unhex-string (un-urlify file)) 'utf-8))
         (buf (find-file fname))
         (line (car linecol))
         (col (cadr linecol)))
    (if (null buf)
        (message "[Synctex]: %s is not opened..." fname)
      (switch-to-buffer buf)
      (goto-line (car linecol))
      (unless (= col -1)
        (move-to-column col)))))

(dbus-register-signal
 :session nil "/org/gnome/evince/Window/0"
 "org.gnome.evince.Window" "SyncSource"
 'evince-inverse-search)

その他

Spacemacs を使用する場合の設定

私は Spacemacs を用いているのだが helm と YaTeX の相性がいいようには見えない. そのため,Emacs YaTeX/yahtml の入力支援では helm を無効にする を参考にして YaTeX が動作中には helm が使えないようにしている.具体的には,~/.spacemacsdotspacemacs/user-config に次の1011行を追加している.

(ad-to-list 'helm-completiong-read-handlers-alist '(YaTeX-change-environment . nil)) を追加する.

(with-eval-after-load "helm-mode"
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-make-accent . nil))
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-make-begin-end . nil))
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-make-fontsize . nil))
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-make-section . nil))
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-make-singlecmd . nil))
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-make-begin-end-region . nil))
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-make-fontsize-region . nil))
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-make-section-region . nil))
    (add-to-list 'helm-completing-read-handlers-alist '(YaTeX-change-environment . nil))
)

YaTeX への愚痴

\ref コマンドを入力時に \label を挿入する候補がズラッと並べられるが,enumerate 環境を多用していると \item ばかりが並んでどれを選択していいのかわからなくなる. 特に enumerate 環境を入れ子にしているときは最悪である. \item だけを絞り込めるから大丈夫だろうと思っているのかもしれないが, \item が7個も8個も並ぶとそこから \label の正しい挿入位置を選択するのは大変である. これに関しては作者に入れ子の状態に応じてインデントなどで違いがわかるようにしてもらえないかと相談したのだが,現在の \label を挿入する場所を抽出するロジックでは無理だと言われた. 本当にこれに関してはどうにか改善してもらいたい… 現状では,\ref コマンドを入力時に一つの関数で \label が挿入可能なすべての場所を抽出しそれらを表示するという手法で,必要ならば使用者が「数式環境」 「enumerate 環境」などを絞り込むという方法である. そして,\label を挿入する場所をたった一つの関数によって抽出する手法に拘るがためにこのような状況になっている. それを,\ref コマンドを入力時に「数式環境」「\chapter などの見出し用コマンド」「enumerate 環境」「定理環境」などと最初に絞りこませて, それから各々に応じた \label を挿入する場所を抽出する関数を呼びだせば済むと思う(現時点では enumerate 環境に対する \label の挿入位置に関して問題があるので enumerate 環境に関する \label 挿入位置を抽出する関数だけ新規で作成することになると思うが…). \ref コマンド入力時に YaTeX 使用者はどこに \label を挿入したいかわかっているので,\label 挿入位置をすべて抽出した後でどこに挿入するか絞りこませるのではなく, 使用者に \lable 挿入位置を絞りこませてからそこを対象に \label 挿入位置を抽出するという順序でも全く問題がないと思うのだが…

また,enumerate 環境下の \label コマンドの挿入位置にも不満がある.\item 直後に \label を挿入しなければ,次の \item までの間に改ページが起こりそれ以降に \label が挿入されてしまうと, \pageref などをしたときに参照したいページと異なる場合があると意見したことがあるが, \ref が参照する番号はページに依存しないなどとトンチンカンな回答をされ無視されている. 何か勘違いをしているのだと思うが,これもどうにかして欲しい案件である. enumerate 環境下で \label が挿入される度に \item 直後へと移動させているのがとても面倒である.

C-c C-t e」で起動される「環境タイプセット」が厄介である. 一度設定されるとその環境内で何かしら変更がある度にタイプセットが行われるのだがこれが非常に鬱陶しい. 何故こんなものが作られたのか理解に苦しむ.さらに,一度設定されると解除する方法は YaTeX では準備されていない(少なくともマニュアルには書かれていない). 大抵は \include された子ファイルでの作業中にミスタイプで「環境タイプセット」が実行されてしまう. そのときは「C-c ^」で親ファイルに移動してから戻ってくると解除されるようなのでそのようにしている. 「環境タイプセット」をなくせとは言わないが,少なくとも「C-c C-t」としたときに「環境タイプセット」を実行するための「e」だけではなく, 「環境タイプセット」を終了させるためのキーバインドも用意してもらいたい. 個人的には「領域タイプセット」というものがすでに存在しているのだから「環境タイプセット」は不要だと思っている. マシンパワーが貧弱な YaTeX 利用者にとっては不要なだけでなく迷惑な存在だろう.

2018年1月7日時点では,YaTeX-on-the-fly-preview-intervalnil にしても「環境タイプセット」が動作してしまう.