Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/misc/edit_module.py
4036 views
1
"""
2
Edit the source code of Sage interactively
3
4
AUTHORS:
5
6
- Nils Bruin
7
- William Stein -- touch up for inclusion in Sage.
8
- Simon King: Make it usable on extension classes that do not have
9
a docstring; include this module into the reference manual and
10
fix some syntax errors in the doc strings.
11
12
This module provides a routine to open the source file of a python
13
object in an editor of your choice, if the source file can be figured
14
out. For files that appear to be from the sage library, the path name
15
gets modified to the corresponding file in the current branch, i.e.,
16
the file that gets copied into the library upon 'sage -br'.
17
18
The editor to be run, and the way it should be called to open the
19
requested file at the right line number, can be supplied via a
20
template. For a limited number of editors, templates are already known
21
to the system. In those cases it suffices to give the editor name.
22
23
In fact, if the environment variable :envvar:`EDITOR` is set to a known editor,
24
then the system will use that if no template has been set explicitly.
25
"""
26
27
######################################################################
28
# Copyright (C) 2007 Nils Bruin <[email protected]> and
29
# William Stein <[email protected]>
30
#
31
# Distributed under the terms of the GNU General Public License (GPL)
32
#
33
# This code is distributed in the hope that it will be useful,
34
# but WITHOUT ANY WARRANTY; without even the implied warranty
35
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
36
#
37
# See the GNU General Public License for more details; the full text
38
# is available at:
39
#
40
# http://www.gnu.org/licenses/
41
######################################################################
42
43
44
import sage.misc.sageinspect
45
import inspect
46
import os
47
import re
48
import IPython
49
50
from string import Template
51
52
#by default we do not have an edit template
53
edit_template = None
54
55
#we can set some defaults, however. Add your own if you like.
56
57
template_defaults = {
58
'vi' : Template('vi -c ${line} ${file}'),
59
'vim' : Template('vim -c ${line} ${file}'),
60
'emacs' : Template('emacs ${opts} +${line} ${file}'),
61
'nedit-nc' : Template('nedit-nc -line ${line} ${file}'),
62
'nedit-client' : Template('nedit-client -line ${line} ${file}'),
63
'ncl' : Template('ncl -line ${line} ${file}'),
64
'gedit' : Template('gedit +${line} ${file} &'),
65
'kate' : Template('kate -u --line +${line} ${file} &') }
66
67
def file_and_line(obj):
68
r"""
69
Look up source file and line number of obj.
70
71
If the file lies in the Sage library, the path name of the
72
corresponding file in the current branch (i.e., the file that gets
73
copied into the Sage library upon running 'sage -br'). Note that
74
the first line of a file is considered to be 1 rather than 0
75
because most editors think that this is the case.
76
77
AUTHORS:
78
79
- Nils Bruin (2007-10-03)
80
- Simon King (2011-05): Use :mod:`~sage.misc.sageinspect` to get the file
81
and the line.
82
83
EXAMPLES::
84
85
sage: import sage.misc.edit_module as edit_module
86
sage: edit_module.file_and_line(sage)
87
('...sage/__init__.py', 0)
88
89
The following tests against a bug that was fixed in trac ticket #11298::
90
91
sage: edit_module.file_and_line(x)
92
('...sage/symbolic/expression.pyx', ...)
93
94
"""
95
#d = inspect.getdoc(obj)
96
#ret = sage.misc.sageinspect._extract_embedded_position(d);
97
#if ret is not None:
98
# (_, filename, lineno) = ret
99
#else:
100
# filename = inspect.getsourcefile(obj)
101
# _,lineno = inspect.findsource(obj)
102
103
#
104
# for sage files, the registered source file is the result of the preparsing
105
# these files end in ".py" and have "*autogenerated*" on the second line
106
# for those files, we replace the extension by ".sage" and we subtract
107
# 3 from the line number to compensate for the 3 lines that were prefixed
108
# in the preparsing process
109
#
110
from sage.misc.sageinspect import sage_getfile, sage_getsourcelines
111
filename = sage_getfile(obj)
112
lineno = sage_getsourcelines(obj)[1] - 1
113
if filename.endswith('.py'):
114
infile=open(filename,'r')
115
infile.readline()
116
if infile.readline().find("*autogenerated*") >= 0:
117
filename=filename[:-3]+'.sage'
118
lineno = lineno-3
119
120
sageroot = sage.misc.sageinspect.SAGE_ROOT+'/'
121
runpathpattern = '^'+sageroot+'local/lib/python[^/]*/site-packages'
122
develbranch = sageroot+'devel/sage'
123
filename=re.sub(runpathpattern,develbranch,filename)
124
125
return filename, lineno+1
126
127
def template_fields(template):
128
r"""
129
Given a String.Template object, returns the fields.
130
131
AUTHOR:
132
133
- Nils Bruin (2007-10-22)
134
135
EXAMPLES::
136
137
sage: from sage.misc.edit_module import template_fields
138
sage: from string import Template
139
sage: t=Template("Template ${one} with ${two} and ${three}")
140
sage: template_fields(t)
141
['three', 'two', 'one']
142
"""
143
dict={}
144
dummy=None
145
while not(dummy):
146
try:
147
dummy=template.substitute(dict)
148
except KeyError, inst:
149
dict[inst.args[0]]=None
150
return dict.keys()
151
152
## The routine set_edit_template should only do some consistency checks on template_string
153
## It should not do any magic. This routine should give the user full control over what is
154
## going on.
155
156
def set_edit_template(template_string):
157
r"""
158
Sets default edit template string.
159
160
It should reference ``${file}`` and ``${line}``. This routine normally
161
needs to be called prior to using 'edit'. However, if the editor
162
set in the shell variable :envvar:`EDITOR` is known, then the system will
163
substitute an appropriate template for you. See
164
edit_module.template_defaults for the recognised templates.
165
166
AUTHOR:
167
168
- Nils Bruin (2007-10-03)
169
170
EXAMPLES::
171
172
sage: from sage.misc.edit_module import set_edit_template
173
sage: set_edit_template("echo EDIT ${file}:${line}")
174
sage: edit(sage) # not tested
175
EDIT /usr/local/sage/default/devel/sage/sage/__init__.py:1
176
"""
177
global edit_template
178
179
if not(isinstance(template_string,Template)):
180
template_string = Template(template_string)
181
fields = set(template_fields(template_string))
182
if not(fields <= set(['file','line']) and ('file' in fields)):
183
raise ValueError, "Only ${file} and ${line} are allowed as template variables, and ${file} must occur."
184
edit_template = template_string
185
186
## The routine set_editor is for convenience and hence is allowed to apply magic. Given an editor name
187
## and possibly some options, it should try to set an editor_template that is as appropriate as possible
188
## for the situation. If it's necessary to query the environment for 'DISPLAY' to figure out if
189
## certain editors should be run in the background, this is where the magic should go.
190
191
def set_editor(editor_name,opts=''):
192
r"""
193
Sets the editor to be used by the edit command by basic editor name.
194
195
Currently, the system only knows appropriate call strings for a limited number
196
of editors. If you want to use another editor, you should set the
197
whole edit template via set_edit_template.
198
199
AUTHOR:
200
201
- Nils Bruin (2007-10-05)
202
203
EXAMPLES::
204
205
sage: from sage.misc.edit_module import set_editor
206
sage: set_editor('vi')
207
sage: sage.misc.edit_module.edit_template.template
208
'vi -c ${line} ${file}'
209
"""
210
211
if sage.misc.edit_module.template_defaults.has_key(editor_name):
212
set_edit_template(Template(template_defaults[editor_name].safe_substitute(opts=opts)))
213
else:
214
raise ValueError, "editor_name not known. Try set_edit_template(<template_string>) instead."
215
216
def edit(obj, editor=None, bg=None):
217
r"""nodetex
218
Open source code of obj in editor of your choice.
219
220
INPUT:
221
222
- editor -- str (default: None); If given, use specified editor. Choice is stored for next time.
223
224
AUTHOR:
225
226
- Nils Bruin (2007-10-03)
227
228
EXAMPLES:
229
230
This is a typical example of how to use this routine.
231
::
232
233
# make some object obj
234
sage: edit(obj) # not tested
235
236
Now for more details and customization::
237
238
sage: import sage.misc.edit_module as m
239
sage: m.set_edit_template("vi -c ${line} ${file}")
240
241
In fact, since vi is a well-known editor, you could also just use
242
::
243
244
sage: m.set_editor("vi")
245
246
To illustrate::
247
248
sage: m.edit_template.template
249
'vi -c ${line} ${file}'
250
251
And if your environment variable :envvar:`EDITOR` is set to a recognised
252
editor, you would not have to set anything.
253
254
To edit the source of an object, just type something like::
255
256
sage: edit(edit) # not tested
257
"""
258
global edit_template
259
260
if editor:
261
set_editor(editor)
262
elif not(edit_template):
263
try:
264
ED = os.environ['EDITOR']
265
EDITOR = ED.split()
266
base = EDITOR[0]
267
opts = ' '.join(EDITOR[1:]) #for future use
268
set_editor(base,opts=opts)
269
except (ValueError, KeyError, IndexError):
270
raise ValueError, "Use set_edit_template(<template_string>) to set a default"
271
272
if not(edit_template):
273
raise ValueError, "Use set_edit_template(<template_string>) to set a default"
274
275
filename, lineno = file_and_line(obj)
276
cmd = edit_template.substitute(line = lineno, file = filename)
277
278
if bg is True and cmd[-1] != '&':
279
cmd=cmd+'&'
280
if bg is False and cmd[-1] == '&':
281
cmd=cmd[:-1]
282
283
os.system(cmd)
284
285
286
def edit_devel(self, filename, linenum):
287
"""
288
This function is for internal use and is called by IPython when you use
289
the IPython commands ``%edit`` or ``%ed``.
290
291
This hook calls the default implementation, but changes the filename for
292
files that appear to be from the sage library: if the filename begins with
293
SAGE_ROOT/local/lib/python.../site-packages/ it replaces this by
294
SAGE_ROOT/devel/sage
295
296
EXAMPLES::
297
298
sage: %edit gcd # indirect doctest, not tested
299
sage: %ed gcd # indirect doctest, not tested
300
301
The above should open your favorite editor (as stored in the environment
302
variable :envvar:`EDITOR`) with the file in which gcd is defined, and when your
303
editor supports it, also at the line in wich gcd is defined.
304
"""
305
sageroot = sage.misc.sageinspect.SAGE_ROOT+'/'
306
runpathpattern = '^'+sageroot+'local/lib/python[^/]*/site-packages'
307
develbranch = sageroot+'devel/sage'
308
filename=re.sub(runpathpattern,develbranch,filename)
309
IPython.hooks.editor(self, filename, linenum)
310
311
312
ip = IPython.ipapi.get()
313
if ip:
314
ip.set_hook('editor', edit_devel)
315
316
317
318