Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

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

120

121

122

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

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

""" 

Edit the source code of Sage interactively 

 

AUTHORS: 

 

- Nils Bruin 

- William Stein -- touch up for inclusion in Sage. 

- Simon King: Make it usable on extension classes that do not have 

a docstring; include this module into the reference manual and 

fix some syntax errors in the doc strings. 

 

This module provides a routine to open the source file of a python 

object in an editor of your choice, if the source file can be figured 

out. For files that appear to be from the sage library, the path name 

gets modified to the corresponding file in the current branch, i.e., 

the file that gets copied into the library upon 'sage -br'. 

 

The editor to be run, and the way it should be called to open the 

requested file at the right line number, can be supplied via a 

template. For a limited number of editors, templates are already known 

to the system. In those cases it suffices to give the editor name. 

 

In fact, if the environment variable :envvar:`EDITOR` is set to a known editor, 

then the system will use that if no template has been set explicitly. 

""" 

 

#***************************************************************************** 

# Copyright (C) 2007 Nils Bruin <nbruin@sfu.ca> and 

# William Stein <wstein@math.ucsd.edu> 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 2 of the License, or 

# (at your option) any later version. 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

 

from __future__ import absolute_import 

 

import sage.misc.sageinspect 

import inspect 

import os 

import re 

 

from string import Template 

 

#by default we do not have an edit template 

edit_template = None 

 

#we can set some defaults, however. Add your own if you like. 

 

template_defaults = { 

'vi' : Template('vi -c ${line} ${file}'), 

'vim' : Template('vim -c ${line} ${file}'), 

'emacs' : Template('emacs ${opts} +${line} ${file}'), 

'nedit-nc' : Template('nedit-nc -line ${line} ${file}'), 

'nedit-client' : Template('nedit-client -line ${line} ${file}'), 

'ncl' : Template('ncl -line ${line} ${file}'), 

'gedit' : Template('gedit +${line} ${file} &'), 

'kate' : Template('kate -u --line +${line} ${file} &') } 

 

def file_and_line(obj): 

r""" 

Look up source file and line number of obj. 

 

If the file lies in the Sage library, the path name of the 

corresponding file in the current branch (i.e., the file that gets 

copied into the Sage library upon running 'sage -br'). Note that 

the first line of a file is considered to be 1 rather than 0 

because most editors think that this is the case. 

 

AUTHORS: 

 

- Nils Bruin (2007-10-03) 

- Simon King (2011-05): Use :mod:`~sage.misc.sageinspect` to get the file 

and the line. 

 

EXAMPLES:: 

 

sage: import sage.misc.edit_module as edit_module 

sage: edit_module.file_and_line(sage) 

('...sage/__init__.py', 0) 

 

The following tests against a bug that was fixed in :trac:`11298`:: 

 

sage: edit_module.file_and_line(x) 

('...sage/symbolic/expression.pyx', ...) 

 

""" 

#d = inspect.getdoc(obj) 

#ret = sage.misc.sageinspect._extract_embedded_position(d); 

#if ret is not None: 

# (_, filename, lineno) = ret 

#else: 

# filename = inspect.getsourcefile(obj) 

# _,lineno = inspect.findsource(obj) 

 

# 

# for sage files, the registered source file is the result of the preparsing 

# these files end in ".py" and have "*autogenerated*" on the second line 

# for those files, we replace the extension by ".sage" and we subtract 

# 3 from the line number to compensate for the 3 lines that were prefixed 

# in the preparsing process 

# 

from sage.misc.sageinspect import sage_getfile, sage_getsourcelines 

filename = sage_getfile(obj) 

lineno = sage_getsourcelines(obj)[1] - 1 

if filename.endswith('.py'): 

infile=open(filename,'r') 

infile.readline() 

if infile.readline().find("*autogenerated*") >= 0: 

filename=filename[:-3]+'.sage' 

lineno = lineno-3 

 

runpathpattern = '^'+sage.env.SAGE_LIB 

develbranch = sage.env.SAGE_SRC 

filename=re.sub(runpathpattern,develbranch,filename) 

 

return filename, lineno+1 

 

def template_fields(template): 

r""" 

Given a String.Template object, returns the fields. 

 

AUTHOR: 

 

- Nils Bruin (2007-10-22) 

 

EXAMPLES:: 

 

sage: from sage.misc.edit_module import template_fields 

sage: from string import Template 

sage: t=Template("Template ${one} with ${two} and ${three}") 

sage: template_fields(t) 

['three', 'two', 'one'] 

""" 

dict={} 

dummy=None 

while not(dummy): 

try: 

dummy=template.substitute(dict) 

except KeyError as inst: 

dict[inst.args[0]]=None 

return dict.keys() 

 

## The routine set_edit_template should only do some consistency checks on template_string 

## It should not do any magic. This routine should give the user full control over what is 

## going on. 

 

def set_edit_template(template_string): 

r""" 

Sets default edit template string. 

 

It should reference ``${file}`` and ``${line}``. This routine normally 

needs to be called prior to using 'edit'. However, if the editor 

set in the shell variable :envvar:`EDITOR` is known, then the system will 

substitute an appropriate template for you. See 

edit_module.template_defaults for the recognised templates. 

 

AUTHOR: 

 

- Nils Bruin (2007-10-03) 

 

EXAMPLES:: 

 

sage: from sage.misc.edit_module import set_edit_template 

sage: set_edit_template("echo EDIT ${file}:${line}") 

sage: edit(sage) # not tested 

EDIT /usr/local/sage/src/sage/__init__.py:1 

""" 

global edit_template 

 

if not(isinstance(template_string,Template)): 

template_string = Template(template_string) 

fields = set(template_fields(template_string)) 

if not(fields <= set(['file','line']) and ('file' in fields)): 

raise ValueError("Only ${file} and ${line} are allowed as template variables, and ${file} must occur.") 

edit_template = template_string 

 

## The routine set_editor is for convenience and hence is allowed to apply magic. Given an editor name 

## and possibly some options, it should try to set an editor_template that is as appropriate as possible 

## for the situation. If it's necessary to query the environment for 'DISPLAY' to figure out if 

## certain editors should be run in the background, this is where the magic should go. 

 

def set_editor(editor_name,opts=''): 

r""" 

Sets the editor to be used by the edit command by basic editor name. 

 

Currently, the system only knows appropriate call strings for a limited number 

of editors. If you want to use another editor, you should set the 

whole edit template via set_edit_template. 

 

AUTHOR: 

 

- Nils Bruin (2007-10-05) 

 

EXAMPLES:: 

 

sage: from sage.misc.edit_module import set_editor 

sage: set_editor('vi') 

sage: sage.misc.edit_module.edit_template.template 

'vi -c ${line} ${file}' 

""" 

 

if editor_name in sage.misc.edit_module.template_defaults: 

set_edit_template(Template(template_defaults[editor_name].safe_substitute(opts=opts))) 

else: 

raise ValueError("editor_name not known. Try set_edit_template(<template_string>) instead.") 

 

def edit(obj, editor=None, bg=None): 

r"""nodetex 

Open source code of obj in editor of your choice. 

 

INPUT: 

 

- editor -- str (default: None); If given, use specified editor. Choice is stored for next time. 

 

AUTHOR: 

 

- Nils Bruin (2007-10-03) 

 

EXAMPLES: 

 

This is a typical example of how to use this routine. 

:: 

 

# make some object obj 

sage: edit(obj) # not tested 

 

Now for more details and customization:: 

 

sage: import sage.misc.edit_module as m 

sage: m.set_edit_template("vi -c ${line} ${file}") 

 

In fact, since vi is a well-known editor, you could also just use 

:: 

 

sage: m.set_editor("vi") 

 

To illustrate:: 

 

sage: m.edit_template.template 

'vi -c ${line} ${file}' 

 

And if your environment variable :envvar:`EDITOR` is set to a recognised 

editor, you would not have to set anything. 

 

To edit the source of an object, just type something like:: 

 

sage: edit(edit) # not tested 

""" 

global edit_template 

 

if editor: 

set_editor(editor) 

elif not(edit_template): 

try: 

ED = os.environ['EDITOR'] 

EDITOR = ED.split() 

base = EDITOR[0] 

opts = ' '.join(EDITOR[1:]) #for future use 

set_editor(base,opts=opts) 

except (ValueError, KeyError, IndexError): 

raise ValueError("Use set_edit_template(<template_string>) to set a default") 

 

if not(edit_template): 

raise ValueError("Use set_edit_template(<template_string>) to set a default") 

 

filename, lineno = file_and_line(obj) 

cmd = edit_template.substitute(line = lineno, file = filename) 

 

if bg is True and cmd[-1] != '&': 

cmd=cmd+'&' 

if bg is False and cmd[-1] == '&': 

cmd=cmd[:-1] 

 

os.system(cmd) 

 

 

def edit_devel(self, filename, linenum): 

""" 

This function is for internal use and is called by IPython when you use 

the IPython commands ``%edit`` or ``%ed``. 

 

This hook calls the default implementation, but changes the filename for 

files that appear to be from the sage library: if the filename begins with 

'SAGE_LOCAL/lib/python.../site-packages', it replaces this by 

'SAGE_ROOT/src'. 

 

EXAMPLES:: 

 

sage: %edit gcd # indirect doctest, not tested 

sage: %ed gcd # indirect doctest, not tested 

 

The above should open your favorite editor (as stored in the environment 

variable :envvar:`EDITOR`) with the file in which gcd is defined, and when your 

editor supports it, also at the line in which gcd is defined. 

""" 

import IPython.core.hooks 

runpathpattern = '^'+sage.env.SAGE_LIB 

develbranch = sage.env.SAGE_SRC 

filename=re.sub(runpathpattern,develbranch,filename) 

IPython.core.hooks.editor(self, filename, linenum)