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