Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/emacs-for-python/rope-dist/ropemacs/__init__.py
990 views
1
"""ropemacs, an emacs mode for using rope refactoring library"""
2
import sys
3
4
import ropemode.decorators
5
import ropemode.environment
6
import ropemode.interface
7
from Pymacs import lisp
8
from rope.base import utils
9
10
11
class LispUtils(ropemode.environment.Environment):
12
13
def ask(self, prompt, default=None, starting=None):
14
if default is not None:
15
prompt = prompt + ('[%s] ' % default)
16
result = lisp.read_from_minibuffer(prompt, starting, None, None,
17
None, default, None)
18
if result == '' and default is not None:
19
return default
20
return result
21
22
def ask_values(self, prompt, values, default=None, starting=None, exact=True):
23
if self._emacs_version() < 22:
24
values = [[value, value] for value in values]
25
if exact and default is not None:
26
prompt = prompt + ('[%s] ' % default)
27
reader = lisp['ropemacs-completing-read-function'].value()
28
result = reader(prompt, values, None, exact, starting)
29
if result == '' and exact:
30
return default
31
return result
32
33
def ask_completion(self, prompt, values, starting=None):
34
return self.ask_values(prompt, values, starting=starting, exact=None)
35
36
def ask_directory(self, prompt, default=None, starting=None):
37
location = starting or default
38
if location is not None:
39
prompt = prompt + ('[%s] ' % location)
40
if lisp.fboundp(lisp['read-directory-name']):
41
# returns default when starting is entered
42
result = lisp.read_directory_name(prompt, location, location)
43
else:
44
result = lisp.read_file_name(prompt, location, location)
45
if result == '' and location is not None:
46
return location
47
return result
48
49
def message(self, msg):
50
message(msg)
51
52
def yes_or_no(self, prompt):
53
return lisp.yes_or_no_p(prompt)
54
55
def y_or_n(self, prompt):
56
return lisp.y_or_n_p(prompt)
57
58
def get(self, name, default=None):
59
lispname = 'ropemacs-' + name.replace('_', '-')
60
if lisp.boundp(lisp[lispname]):
61
return lisp[lispname].value()
62
return default
63
64
def get_offset(self):
65
return lisp.point() - 1
66
67
def get_text(self):
68
end = lisp.buffer_size() + 1
69
old_min = lisp.point_min()
70
old_max = lisp.point_max()
71
narrowed = (old_min != 1 or old_max != end)
72
if narrowed:
73
lisp.narrow_to_region(1, lisp.buffer_size() + 1)
74
try:
75
return lisp.buffer_string()
76
finally:
77
if narrowed:
78
lisp.narrow_to_region(old_min, old_max)
79
80
def get_region(self):
81
offset1 = self.get_offset()
82
lisp.exchange_point_and_mark()
83
offset2 = self.get_offset()
84
lisp.exchange_point_and_mark()
85
return min(offset1, offset2), max(offset1, offset2)
86
87
def filename(self):
88
return lisp.buffer_file_name()
89
90
def is_modified(self):
91
return lisp.buffer_modified_p()
92
93
def goto_line(self, lineno):
94
lisp.goto_line(lineno)
95
96
def insert_line(self, line, lineno):
97
current = lisp.point()
98
lisp.goto_line(lineno)
99
lisp.insert(line + '\n')
100
lisp.goto_char(current + len(line) + 1)
101
102
def insert(self, text):
103
lisp.insert(text)
104
105
def delete(self, start, end):
106
lisp.delete_region(start, end)
107
108
def filenames(self):
109
result = []
110
for buffer in lisp.buffer_list():
111
filename = lisp.buffer_file_name(buffer)
112
if filename:
113
result.append(filename)
114
return result
115
116
def save_files(self, filenames):
117
ask = self.get('confirm_saving')
118
initial = lisp.current_buffer()
119
for filename in filenames:
120
buffer = lisp.find_buffer_visiting(filename)
121
if buffer:
122
if lisp.buffer_modified_p(buffer):
123
if not ask or lisp.y_or_n_p('Save %s buffer?' % filename):
124
lisp.set_buffer(buffer)
125
lisp.save_buffer()
126
lisp.set_buffer(initial)
127
128
def reload_files(self, filenames, moves={}):
129
if self.filename() in moves:
130
initial = None
131
else:
132
initial = lisp.current_buffer()
133
for filename in filenames:
134
buffer = lisp.find_buffer_visiting(filename)
135
if buffer:
136
if filename in moves:
137
lisp.kill_buffer(buffer)
138
lisp.find_file(moves[filename])
139
else:
140
lisp.set_buffer(buffer)
141
lisp.revert_buffer(False, True)
142
if initial is not None:
143
lisp.set_buffer(initial)
144
145
def find_file(self, filename, readonly=False, other=False):
146
if other:
147
lisp.find_file_other_window(filename)
148
elif readonly:
149
lisp.find_file_read_only(filename)
150
else:
151
lisp.find_file(filename)
152
153
def _make_buffer(self, name, contents, empty_goto=True, switch=False,
154
window='other', modes=[], fit_lines=None):
155
"""Make an emacs buffer
156
157
`window` can be one of `None`, 'current' or 'other'.
158
"""
159
new_buffer = lisp.get_buffer_create(name)
160
lisp.set_buffer(new_buffer)
161
lisp.toggle_read_only(-1)
162
lisp.erase_buffer()
163
if contents or empty_goto:
164
lisp.insert(contents)
165
for mode in modes:
166
lisp[mode + '-mode']()
167
lisp.buffer_disable_undo(new_buffer)
168
lisp.toggle_read_only(1)
169
if switch:
170
if window == 'current':
171
lisp.switch_to_buffer(new_buffer)
172
else:
173
lisp.switch_to_buffer_other_window(new_buffer)
174
lisp.goto_char(lisp.point_min())
175
elif window == 'other':
176
new_window = lisp.display_buffer(new_buffer)
177
lisp.set_window_point(new_window, lisp.point_min())
178
if fit_lines and lisp.fboundp(lisp['fit-window-to-buffer']):
179
lisp.fit_window_to_buffer(new_window, fit_lines)
180
lisp.bury_buffer(new_buffer)
181
return new_buffer
182
183
def _hide_buffer(self, name, delete=True):
184
buffer = lisp.get_buffer(name)
185
if buffer is not None:
186
window = lisp.get_buffer_window(buffer)
187
if window is not None:
188
lisp.bury_buffer(buffer)
189
if delete:
190
lisp.delete_window(window)
191
else:
192
if lisp.buffer_name(lisp.current_buffer()) == name:
193
lisp.switch_to_buffer(None)
194
195
def _emacs_version(self):
196
return int(lisp['emacs-version'].value().split('.')[0])
197
198
def create_progress(self, name):
199
if lisp.fboundp(lisp['make-progress-reporter']):
200
progress = _LispProgress(name)
201
else:
202
progress = _OldProgress(name)
203
return progress
204
205
def current_word(self):
206
return lisp.current_word()
207
208
def push_mark(self):
209
lisp.push_mark()
210
211
def prefix_value(self, prefix):
212
return lisp.prefix_numeric_value(prefix)
213
214
def show_occurrences(self, locations):
215
text = ['List of occurrences:', '']
216
for location in locations:
217
line = '%s : %s %s %s' % (location.filename, location.lineno,
218
location.note, location.offset)
219
text.append(line)
220
text = '\n'.join(text) + '\n'
221
buffer = self._make_buffer('*rope-occurrences*', text, switch=False)
222
lisp.set_buffer(buffer)
223
lisp.toggle_read_only(1)
224
lisp.set(lisp["next-error-function"], lisp.rope_occurrences_next)
225
lisp.local_set_key('\r', lisp.rope_occurrences_goto)
226
lisp.local_set_key('q', lisp.delete_window)
227
228
def show_doc(self, docs, altview=False):
229
use_minibuffer = not altview
230
if self.get('separate_doc_buffer'):
231
use_minibuffer = not use_minibuffer
232
if not use_minibuffer:
233
fit_lines = self.get('max_doc_buffer_height')
234
buffer = self._make_buffer('*rope-pydoc*', docs,
235
empty_goto=False,
236
fit_lines=fit_lines)
237
lisp.local_set_key('q', lisp.bury_buffer)
238
elif docs:
239
docs = '\n'.join(docs.split('\n')[:7])
240
self.message(docs)
241
242
def preview_changes(self, diffs):
243
self._make_buffer('*rope-preview*', diffs, switch=True,
244
modes=['diff'], window='current')
245
try:
246
return self.yes_or_no('Do the changes? ')
247
finally:
248
self._hide_buffer('*rope-preview*', delete=False)
249
250
def local_command(self, name, callback, key=None, prefix=False):
251
globals()[name] = callback
252
self._set_interaction(callback, prefix)
253
if self.local_prefix and key:
254
key = self._key_sequence(self.local_prefix + ' ' + key)
255
self._bind_local(_lisp_name(name), key)
256
257
def _bind_local(self, name, key):
258
lisp('(define-key ropemacs-local-keymap "%s" \'%s)' %
259
(self._key_sequence(key), name))
260
261
def global_command(self, name, callback, key=None, prefix=False):
262
globals()[name] = callback
263
self._set_interaction(callback, prefix)
264
if self.global_prefix and key:
265
key = self._key_sequence(self.global_prefix + ' ' + key)
266
lisp.global_set_key(key, lisp[_lisp_name(name)])
267
268
def _key_sequence(self, sequence):
269
result = []
270
for key in sequence.split():
271
if key.startswith('C-'):
272
number = ord(key[-1].upper()) - ord('A') + 1
273
result.append(chr(number))
274
elif key.startswith('M-'):
275
number = ord(key[-1].upper()) + 0x80
276
result.append(chr(number))
277
else:
278
result.append(key)
279
return ''.join(result)
280
281
def _set_interaction(self, callback, prefix):
282
if hasattr(callback, 'im_func'):
283
callback = callback.im_func
284
if prefix:
285
callback.interaction = 'P'
286
else:
287
callback.interaction = ''
288
289
def add_hook(self, name, callback, hook):
290
mapping = {'before_save': 'before-save-hook',
291
'after_save': 'after-save-hook',
292
'exit': 'kill-emacs-hook'}
293
globals()[name] = callback
294
lisp.add_hook(lisp[mapping[hook]], lisp[_lisp_name(name)])
295
296
def project_opened(self):
297
'''
298
This method is called when a new project is opened, it runs
299
the hooks associated with rope-open-project-hook.
300
'''
301
lisp.run_hooks(lisp["rope-open-project-hook"])
302
303
@property
304
@utils.saveit
305
def global_prefix(self):
306
return self.get('global_prefix')
307
308
@property
309
@utils.saveit
310
def local_prefix(self):
311
return self.get('local_prefix')
312
313
314
def _lisp_name(name):
315
return 'rope-' + name.replace('_', '-')
316
317
class _LispProgress(object):
318
319
def __init__(self, name):
320
self.progress = lisp.make_progress_reporter('%s ... ' % name, 0, 100)
321
322
def update(self, percent):
323
lisp.progress_reporter_update(self.progress, percent)
324
325
def done(self):
326
lisp.progress_reporter_done(self.progress)
327
328
class _OldProgress(object):
329
330
def __init__(self, name):
331
self.name = name
332
self.update(0)
333
334
def update(self, percent):
335
if percent != 0:
336
message('%s ... %s%%%%' % (self.name, percent))
337
else:
338
message('%s ... ' % self.name)
339
340
def done(self):
341
message('%s ... done' % self.name)
342
343
344
def message(message):
345
lisp.message(message.replace('%', '%%'))
346
347
def occurrences_goto():
348
if lisp.line_number_at_pos() < 3:
349
lisp.forward_line(3 - lisp.line_number_at_pos())
350
lisp.end_of_line()
351
end = lisp.point()
352
lisp.beginning_of_line()
353
line = lisp.buffer_substring_no_properties(lisp.point(), end)
354
tokens = line.split()
355
if tokens:
356
filename = tokens[0]
357
offset = int(tokens[-1])
358
resource = _interface._get_resource(filename)
359
LispUtils().find_file(resource.real_path, other=True)
360
lisp.goto_char(offset + 1)
361
occurrences_goto.interaction = ''
362
363
def occurrences_next(arg, reset):
364
lisp.switch_to_buffer_other_window('*rope-occurrences*', True)
365
if reset:
366
lisp.goto_char(lisp.point_min())
367
lisp.forward_line(arg)
368
if lisp.eobp():
369
lisp.message("Cycling rope occurences")
370
lisp.goto_char(lisp.point_min())
371
occurrences_goto()
372
occurrences_next.interaction = ''
373
374
375
DEFVARS = """\
376
(defgroup ropemacs nil
377
"ropemacs, an emacs plugin for rope."
378
:link '(url-link "http://rope.sourceforge.net/ropemacs.html")
379
:prefix "rope-")
380
381
(defcustom ropemacs-confirm-saving t
382
"Shows whether to confirm saving modified buffers before refactorings.
383
384
If non-nil, you have to confirm saving all modified
385
python files before refactorings; otherwise they are
386
saved automatically.")
387
388
(defcustom ropemacs-codeassist-maxfixes 1
389
"The number of errors to fix before code-assist.
390
391
How many errors to fix, at most, when proposing code completions.")
392
393
(defcustom ropemacs-separate-doc-buffer t
394
"Should `rope-show-doc' use a separate buffer or the minibuffer.")
395
(defcustom ropemacs-max-doc-buffer-height 22
396
"The maximum buffer height for `rope-show-doc'.")
397
398
(defcustom ropemacs-enable-autoimport 'nil
399
"Specifies whether autoimport should be enabled.")
400
(defcustom ropemacs-autoimport-modules nil
401
"The name of modules whose global names should be cached.
402
403
The `rope-generate-autoimport-cache' reads this list and fills its
404
cache.")
405
(defcustom ropemacs-autoimport-underlineds 'nil
406
"If set, autoimport will cache names starting with underlines, too.")
407
408
(defcustom ropemacs-completing-read-function (if (and (boundp 'ido-mode)
409
ido-mode)
410
'ido-completing-read
411
'completing-read)
412
"Function to call when prompting user to choose between a list of options.
413
This should take the same arguments as `completing-read'.
414
Possible values are `completing-read' and `ido-completing-read'.
415
Note that you must set `ido-mode' if using`ido-completing-read'."
416
:type 'function)
417
418
(make-obsolete-variable
419
'rope-confirm-saving 'ropemacs-confirm-saving)
420
(make-obsolete-variable
421
'rope-code-assist-max-fixes 'ropemacs-codeassist-maxfixes)
422
423
(defcustom ropemacs-local-prefix "C-c r"
424
"The prefix for ropemacs refactorings.
425
426
Use nil to prevent binding keys.")
427
428
(defcustom ropemacs-global-prefix "C-x p"
429
"The prefix for ropemacs project commands.
430
431
Use nil to prevent binding keys.")
432
433
(defcustom ropemacs-enable-shortcuts 't
434
"Shows whether to bind ropemacs shortcuts keys.
435
436
If non-nil it binds:
437
438
================ ============================
439
Key Command
440
================ ============================
441
M-/ rope-code-assist
442
C-c g rope-goto-definition
443
C-c d rope-show-doc
444
C-c f rope-find-occurrences
445
M-? rope-lucky-assist
446
================ ============================
447
")
448
449
(defvar ropemacs-local-keymap (make-sparse-keymap))
450
451
(easy-menu-define ropemacs-mode-menu ropemacs-local-keymap
452
"`ropemacs' menu"
453
'("Rope"
454
["Code assist" rope-code-assist t]
455
["Lucky assist" rope-lucky-assist t]
456
["Goto definition" rope-goto-definition t]
457
["Jump to global" rope-jump-to-global t]
458
["Show documentation" rope-show-doc t]
459
["Find Occurrences" rope-find-occurrences t]
460
["Analyze module" rope-analyze-module t]
461
("Refactor"
462
["Inline" rope-inline t]
463
["Extract Variable" rope-extract-variable t]
464
["Extract Method" rope-extract-method t]
465
["Organize Imports" rope-organize-imports t]
466
["Rename" rope-rename t]
467
["Move" rope-move t]
468
["Restructure" rope-restructure t]
469
["Use Function" rope-use-function t]
470
["Introduce Factory" rope-introduce-factory t]
471
("Generate"
472
["Class" rope-generate-class t]
473
["Function" rope-generate-function t]
474
["Module" rope-generate-module t]
475
["Package" rope-generate-package t]
476
["Variable" rope-generate-variable t]
477
)
478
("Module"
479
["Module to Package" rope-module-to-package t]
480
["Rename Module" rope-rename-current-module t]
481
["Move Module" rope-move-current-module t]
482
)
483
"--"
484
["Undo" rope-undo t]
485
["Redo" rope-redo t]
486
)
487
("Project"
488
["Open project" rope-open-project t]
489
["Close project" rope-close-project t]
490
["Find file" rope-find-file t]
491
["Open project config" rope-project-config t]
492
)
493
("Create"
494
["Module" rope-create-module t]
495
["Package" rope-create-package t]
496
["File" rope-create-file t]
497
["Directory" rope-create-directory t]
498
)
499
))
500
501
(defcustom ropemacs-guess-project 'nil
502
"Try to guess the project when needed.
503
504
If non-nil, ropemacs tries to guess and open the project that contains
505
a file on which the rope command is performed when no project is
506
already opened.")
507
508
(provide 'ropemacs)
509
"""
510
511
MINOR_MODE = """\
512
(define-minor-mode ropemacs-mode
513
"ropemacs, rope in emacs!" nil " Rope" ropemacs-local-keymap
514
:global nil)
515
)
516
"""
517
518
shortcuts = [('M-/', 'rope-code-assist'),
519
('M-?', 'rope-lucky-assist'),
520
('C-c g', 'rope-goto-definition'),
521
('C-c d', 'rope-show-doc'),
522
('C-c f', 'rope-find-occurrences')]
523
524
525
_interface = None
526
527
def _load_ropemacs():
528
global _interface
529
ropemode.decorators.logger.message = message
530
lisp(DEFVARS)
531
_interface = ropemode.interface.RopeMode(env=LispUtils())
532
_interface.init()
533
lisp(MINOR_MODE)
534
535
if LispUtils().get('enable_shortcuts'):
536
for key, command in shortcuts:
537
LispUtils()._bind_local(command, key)
538
539
lisp.add_hook(lisp['python-mode-hook'], lisp['ropemacs-mode'])
540
541
def _started_from_pymacs():
542
import inspect
543
frame = sys._getframe()
544
while frame:
545
# checking frame.f_code.co_name == 'pymacs_load_helper' might
546
# be very fragile.
547
if inspect.getfile(frame).rstrip('c').endswith('pymacs.py'):
548
return True
549
frame = frame.f_back
550
551
if _started_from_pymacs():
552
_load_ropemacs()
553
554