Newer
Older
# Dependencies:
#
# python-gobject2
# python-dbus
#
# Thorsten Wißmann, 2015
# in case of bugs, contact: edu _at_ thorsten-wissmann _dot_ de
def print_help():
print("""Usage: {0} SESSIONNAME [PDFFILE]
A katarakt wrapper for synctex communication between vim and katarakt.
SESSIONNAME is the name of your vim session, i.e. you have to start a vim
session *yourself* and *beforehand* with
vim --servername SESSIONNAME
Behaviour:
This script starts katarakt for PDFFILE. If no PDFFILE is specified, it tries
to autodetect the correct pdf file (by finding a corresponding .synctex.gz
file in the current directory).
It registers a keybinding in vim (per default "ZE"), to jump to the
corresponding page in katarakt. The vim keybinding can be overwritten by the
VIM_KEY environment variable.
If Katarakt emits the edit signal (usually on Ctrl+LeftMouse), then it
opens the corresponding tex file in vim and jumps to the corresponding
line in the texfile.
If the user presses ZE in the editor, a message is sent to this script.
This script calls synctex and sends the pdf-coordinates to katarakt.
When katarakt quits, then this script exits as well.
Requirements:
You need to compile the latex document using the option -synctex=1 in order to
obtain the required .synctex.gz file.
Hint: VIM configuration:
Add the following line to your vimrc:
au VimEnter *.tex execute "nmap ZE :! synctex-katarakt-vim " . v:servername . " 2>/dev/null >/dev/null &<LEFT><LEFT>"
When typing ZE the first time, it automatically calls this script, overwrites
the ZE keybinding, and opens a katarakt instance. After typing ZE the first
time, you are prompted such that you can specify an alternate PDF file. If
you want an alternate vim keybinding (e.g. <Leader>f), add this line:
au VimEnter *.tex execute "nmap ZF :! VIM_KEY='ZF' synctex-katarakt-vim " . v:servername . " 2>/dev/null >/dev/null &<LEFT><LEFT>"
If your key contains something like <Leader> (or other keys containing "<"),
escape it properly in the VIM_KEY= assignment.
""".format(sys.argv[0]))
from gi.repository.GObject import MainLoop, threads_init
import sys
import dbus
import dbus.service
import subprocess
import time # only for sleep()
import threading
import os
import re # for regular expressions
from dbus.mainloop.glib import DBusGMainLoop
# tell dbus that we use the gobject main loop
DBusGMainLoop(set_as_default=True)
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#########################################
# Settings #
#########################################
def detect_synctexfile():
# try to find the synctex.gz file in the current directory
a = re.compile(".*\.synctex\.gz$")
files = [f for f in os.listdir('.') if os.path.isfile(f) and a.match(f)]
return files
def die(msg):
print(msg)
exit(1)
try:
session_name = sys.argv[1]
except IndexError:
print_help()
exit(0)
if session_name == "-h" or session_name == "--help":
print_help()
exit(0)
try:
pdf_filename = sys.argv[2]
except IndexError:
try:
synctex_filename = detect_synctexfile()[0]
pdf_filename = re.sub("\.synctex\.gz$", ".pdf", synctex_filename)
print("auto-detected {0}".format(pdf_filename))
except IndexError:
die("no *.synctex.gz file found in current directory")
vim_session = session_name
vim_view_keybind = os.getenv('VIM_KEY', 'ZE')
pdfprocess = subprocess.Popen(['katarakt', '--single-instance', 'false', pdf_filename])
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
pdf_pid = pdfprocess.pid
view_command = ("qdbus katarakt.pid%d" % pdf_pid +
" / katarakt.SourceCorrelate.view" +
" %{output} %{page} %{x} %{y}")
# connect to dbus
bus = dbus.SessionBus()
# wait for katarakt to show up
# it doesn't seem to be trivial to wait for an
# application to show up at dbus. hence the (still racy) hack
katarakt_booted = False
while pdfprocess.pid != None and not katarakt_booted:
try:
katarakt = bus.get_object('katarakt.pid%d' % pdf_pid, '/')
iface = dbus.Interface(katarakt, 'katarakt.SourceCorrelate')
katarakt_booted = True
except dbus.exceptions.DBusException:
time.sleep(0.01)
# register an own service on the session bus
busName = dbus.service.BusName('katarakt.synctex.vim.' + session_name,
bus = dbus.SessionBus())
class BridgeObject(dbus.service.Object):
def __init__(self, object_path):
dbus.service.Object.__init__(self, busName,
object_path)
@dbus.service.method(dbus_interface='katarakt.bridge',
in_signature='sii', out_signature='')
def View(self, filename, line, col):
subprocess.call([
"synctex", "view",
"-i", "%d:%d:%s" % (line,col,filename),
"-o", pdf_filename,
"-x", view_command,
])
# create the dbus object and bind it to the bus
BridgeObject('/')
# inject the keybinding for viewing into the vim session
returncode = subprocess.call([
"vim", "--servername", vim_session,
"--remote-send",
("<ESC>:map %s" % vim_view_keybind
+ " :exec \"execute ('!qdbus katarakt.synctex.vim.%s" % session_name
+ " / katarakt.bridge.View % ' . line('.') . ' 0')\"<lt>CR><lt>CR><CR><CR>")
])
if returncode != 0:
print("Error when trying to register keybinding in the vim session \"{0}\"".format(vim_session))
exit(1)
# callback if the signal for edit is sent by katarakt
def on_edit(filename,page,x,y):
#print ("go to page %d at %d,%d" % (page,x,y))
subprocess.call([
"synctex", "edit",
"-o", ("%d:%d:%d:%s" % (1+page,x,y,filename)),
"-x", "vim --servername '" + vim_session + "' --remote-silent '+%{line}' %{input}",
])
iface.connect_to_signal("edit", on_edit)
# Main loop and cleanup:
def quit_if_pdf_exits():
pdfprocess.wait()
loop.quit()
thread = threading.Thread(target=quit_if_pdf_exits, args=())
thread.start()
# enable threads again, otherwise the quit_if_pdf_exits-thread will not be
# executed