Coverage for C:\leo.repo\leo-editor\leo\core\leoKeys.py: 28%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

2526 statements  

1# -*- coding: utf-8 -*- 

2#@+leo-ver=5-thin 

3#@+node:ekr.20061031131434: * @file leoKeys.py 

4#@@first 

5"""Gui-independent keystroke handling for Leo.""" 

6# pylint: disable=eval-used 

7# pylint: disable=deprecated-method 

8#@+<< imports >> 

9#@+node:ekr.20061031131434.1: ** << imports >> (leoKeys) 

10import inspect 

11import os 

12import re 

13import string 

14import sys 

15import textwrap 

16import time 

17from typing import Dict, List, Tuple 

18from leo.core import leoGlobals as g 

19from leo.commands import gotoCommands 

20from leo.external import codewise 

21try: 

22 import jedi 

23except ImportError: 

24 jedi = None 

25#@-<< imports >> 

26#@+<< Key bindings, an overview >> 

27#@+node:ekr.20130920121326.11281: ** << Key bindings, an overview >> 

28#@@language rest 

29#@+at 

30# The big pictures of key bindings: 

31# 

32# 1. Code in leoKeys.py and in leoConfig.py converts user key settings to 

33# various Python **binding dictionaries** defined in leoKeys.py. 

34# 

35# 2. An instance of LeoQtEventFilter should be attached to all visible panes 

36# in Leo's main window. g.app.gui.setFilter does this. 

37# 

38# 3. LeoQtEventFilter.eventFilter calls k.masterKeyhandler for every 

39# keystroke. eventFilter passes only just the event argument to 

40# k.masterKeyHandler. The event arg gives both the widget in which the 

41# event occurs and the keystroke. 

42# 

43# 4. k.masterKeyHandler and its helpers use the event argument and the 

44# binding dictionaries to execute the Leo command (if any) associated with 

45# the incoming keystroke. 

46# 

47# Important details: 

48# 

49# 1. g.app.gui.setFilter allows various traces and assertions to be made 

50# uniformly. The obj argument to setFilter is a QWidget object; the w 

51# argument to setFilter can be either the same as obj, or a Leo 

52# wrapper class. **Important**: the types of obj and w are not 

53# actually all that important, as discussed next. 

54# 

55# 2. The logic in k.masterKeyHandler and its helpers is long and involved: 

56# 

57# A. k.getPaneBinding associates a command with the incoming keystroke based 

58# on a) the widget's name and b) whether the widget is a text widget 

59# (which depends on the type of the widget). 

60# 

61# To do this, k.getPaneBinding uses a **binding priority table**. This 

62# table is defined within k.getPaneBinding itself. The table indicates 

63# which of several possible bindings should have priority. For instance, 

64# if the widget is a text widget, a user binding for a 'text' widget takes 

65# priority over a default key binding. Similarly, if the widget is Leo's 

66# tree widget, a 'tree' binding has top priority. There are many other 

67# details encapsulated in the table. The exactly details of the binding 

68# priority table are open to debate, but in practice the resulting 

69# bindings are as expeced. 

70# 

71# B. If k.getPaneBinding finds a command associated with the incoming 

72# keystroke, k.masterKeyHandler executes the command. 

73# 

74# C. If k.getPaneBinding fails to bind the incoming keystroke to a command, 

75# k.masterKeyHandler calls k.handleUnboundKeys to handle the keystroke. 

76# Depending on the widget, and settings, and the keystroke, 

77# k.handleUnboundKeys may do nothing, or it may call k.masterCommand to 

78# insert a plain key into the widget. 

79#@-<< Key bindings, an overview >> 

80#@+<< about 'internal' bindings >> 

81#@+node:ekr.20061031131434.2: ** << about 'internal' bindings >> 

82#@@language rest 

83#@+at 

84# Here are the rules for translating key bindings (in leoSettings.leo) 

85# into keys for k.bindingsDict: 

86# 

87# 1. The case of plain letters is significant: a is not A. 

88# 

89# 2. The Shift- prefix can be applied *only* to letters. Leo will ignore 

90# (with a warning) the shift prefix applied to any other binding, 

91# e.g., Ctrl-Shift-( 

92# 

93# 3. The case of letters prefixed by Ctrl-, Alt-, Key- or Shift- is 

94# *not* significant. Thus, the Shift- prefix is required if you want 

95# an upper-case letter (with the exception of 'bare' uppercase 

96# letters.) 

97# 

98# The following table illustrates these rules. In each row, the first 

99# entry is the key (for k.bindingsDict) and the other entries are 

100# equivalents that the user may specify in leoSettings.leo: 

101# 

102# a, Key-a, Key-A 

103# A, Shift-A 

104# Alt-a, Alt-A 

105# Alt-A, Alt-Shift-a, Alt-Shift-A 

106# Ctrl-a, Ctrl-A 

107# Ctrl-A, Ctrl-Shift-a, Ctrl-Shift-A 

108# , Key-!,Key-exclam,exclam 

109# 

110# This table is consistent with how Leo already works (because it is 

111# consistent with Tk's key-event specifiers). It is also, I think, the 

112# least confusing set of rules. 

113#@-<< about 'internal' bindings >> 

114#@+<< about key dicts >> 

115#@+node:ekr.20061031131434.3: ** << about key dicts >> 

116#@@language rest 

117#@+at 

118# ivar Keys Values 

119# ---- ---- ------ 

120# c.commandsDict command names (1) functions 

121# k.bindingsDict shortcuts lists of BindingInfo objects 

122# k.masterBindingsDict scope names (2) Interior masterBindingDicts (3) 

123# k.masterGuiBindingsDict strokes list of widgets in which stoke is bound 

124# inverseBindingDict (5) command names lists of tuples (pane,key) 

125# modeCommandsDict (6) command name (7) inner modeCommandsDicts (8) 

126# 

127# New in Leo 4.7: 

128# k.killedBindings is a list of command names for which bindings have been killed in local files. 

129# 

130# Notes: 

131# 

132# (1) Command names are minibuffer names (strings) 

133# (2) Scope names are 'all','text',etc. 

134# (3) Interior masterBindingDicts: Keys are strokes; values are BindingInfo objects. 

135# (5) inverseBindingDict is **not** an ivar: it is computed by k.computeInverseBindingDict. 

136# (6) A global dict: g.app.gui.modeCommandsDict 

137# (7) enter-x-command 

138# (8) Keys are command names, values are lists of BindingInfo objects. 

139#@-<< about key dicts >> 

140#@+others 

141#@+node:ekr.20150509035140.1: ** ac_cmd (decorator) 

142def ac_cmd(name): 

143 """Command decorator for the AutoCompleter class.""" 

144 return g.new_cmd_decorator(name, ['c', 'k', 'autoCompleter']) 

145#@+node:ekr.20150509035028.1: ** cmd (decorator) 

146def cmd(name): 

147 """Command decorator for the leoKeys class.""" 

148 return g.new_cmd_decorator(name, ['c', 'k',]) 

149#@+node:ekr.20061031131434.4: ** class AutoCompleterClass 

150class AutoCompleterClass: 

151 """A class that inserts autocompleted and calltip text in text widgets. 

152 This class shows alternatives in the tabbed log pane. 

153 

154 The keyHandler class contains hooks to support these characters: 

155 invoke-autocompleter-character (default binding is '.') 

156 invoke-calltips-character (default binding is '(') 

157 """ 

158 #@+others 

159 #@+node:ekr.20061031131434.5: *3* ac.ctor & reloadSettings 

160 def __init__(self, k): 

161 """Ctor for AutoCompleterClass class.""" 

162 # Ivars... 

163 self.c = k.c 

164 self.k = k 

165 self.language = None 

166 # additional namespaces to search for objects, other code 

167 # can append namespaces to this to extend scope of search 

168 self.namespaces = [] 

169 self.qw = None # The object that supports qcompletion methods. 

170 self.tabName = None # The name of the main completion tab. 

171 self.verbose = False # True: print all members, regardless of how many there are. 

172 self.w = None # The widget that gets focus after autocomplete is done. 

173 self.warnings = {} # Keys are language names. 

174 # Codewise pre-computes... 

175 self.codewiseSelfList = [] # The (global) completions for "self." 

176 self.completionsDict = {} # Keys are prefixes, values are completion lists. 

177 self.reloadSettings() 

178 

179 def reloadSettings(self): 

180 c = self.c 

181 self.auto_tab = c.config.getBool('auto-tab-complete', True) 

182 self.forbid_invalid = c.config.getBool('forbid-invalid-completions', False) 

183 self.use_jedi = c.config.getBool('use-jedi', False) 

184 # True: show results in autocompleter tab. 

185 # False: show results in a QCompleter widget. 

186 self.use_qcompleter = c.config.getBool('use-qcompleter', False) 

187 #@+node:ekr.20061031131434.8: *3* ac.Top level 

188 #@+node:ekr.20061031131434.9: *4* ac.autoComplete 

189 @ac_cmd('auto-complete') 

190 def autoComplete(self, event=None): 

191 """An event handler for autocompletion.""" 

192 c, k = self.c, self.k 

193 # pylint: disable=consider-using-ternary 

194 w = event and event.w or c.get_focus() 

195 if k.unboundKeyAction not in ('insert', 'overwrite'): 

196 return 

197 c.insertCharFromEvent(event) 

198 if c.exists: 

199 c.frame.updateStatusLine() 

200 # Allow autocompletion only in the body pane. 

201 if not c.widget_name(w).lower().startswith('body'): 

202 return 

203 self.language = g.scanForAtLanguage(c, c.p) 

204 if w and k.enable_autocompleter: 

205 self.w = w 

206 self.start(event) 

207 #@+node:ekr.20061031131434.10: *4* ac.autoCompleteForce 

208 @ac_cmd('auto-complete-force') 

209 def autoCompleteForce(self, event=None): 

210 """Show autocompletion, even if autocompletion is not presently enabled.""" 

211 c, k = self.c, self.k 

212 # pylint: disable=consider-using-ternary 

213 w = event and event.w or c.get_focus() 

214 if k.unboundKeyAction not in ('insert', 'overwrite'): 

215 return 

216 if c.exists: 

217 c.frame.updateStatusLine() 

218 # Allow autocompletion only in the body pane. 

219 if not c.widget_name(w).lower().startswith('body'): 

220 return 

221 self.language = g.scanForAtLanguage(c, c.p) 

222 if w: 

223 self.w = w 

224 self.start(event) 

225 

226 #@+node:ekr.20061031131434.12: *4* ac.enable/disable/toggleAutocompleter/Calltips 

227 @ac_cmd('disable-autocompleter') 

228 def disableAutocompleter(self, event=None): 

229 """Disable the autocompleter.""" 

230 self.k.enable_autocompleter = False 

231 self.showAutocompleterStatus() 

232 

233 @ac_cmd('disable-calltips') 

234 def disableCalltips(self, event=None): 

235 """Disable calltips.""" 

236 self.k.enable_calltips = False 

237 self.showCalltipsStatus() 

238 

239 @ac_cmd('enable-autocompleter') 

240 def enableAutocompleter(self, event=None): 

241 """Enable the autocompleter.""" 

242 self.k.enable_autocompleter = True 

243 self.showAutocompleterStatus() 

244 

245 @ac_cmd('enable-calltips') 

246 def enableCalltips(self, event=None): 

247 """Enable calltips.""" 

248 self.k.enable_calltips = True 

249 self.showCalltipsStatus() 

250 

251 @ac_cmd('toggle-autocompleter') 

252 def toggleAutocompleter(self, event=None): 

253 """Toggle whether the autocompleter is enabled.""" 

254 self.k.enable_autocompleter = not self.k.enable_autocompleter 

255 self.showAutocompleterStatus() 

256 

257 @ac_cmd('toggle-calltips') 

258 def toggleCalltips(self, event=None): 

259 """Toggle whether calltips are enabled.""" 

260 self.k.enable_calltips = not self.k.enable_calltips 

261 self.showCalltipsStatus() 

262 #@+node:ekr.20061031131434.13: *4* ac.showCalltips 

263 @ac_cmd('show-calltips') 

264 def showCalltips(self, event=None): 

265 """Show the calltips at the cursor.""" 

266 c, k, w = self.c, self.c.k, event and event.w 

267 if not w: 

268 return 

269 is_headline = c.widget_name(w).startswith('head') 

270 if k.enable_calltips and not is_headline: 

271 self.w = w 

272 self.calltip() 

273 else: 

274 c.insertCharFromEvent(event) 

275 #@+node:ekr.20061031131434.14: *4* ac.showCalltipsForce 

276 @ac_cmd('show-calltips-force') 

277 def showCalltipsForce(self, event=None): 

278 """Show the calltips at the cursor, even if calltips are not presently enabled.""" 

279 c, w = self.c, event and event.w 

280 if not w: 

281 return 

282 is_headline = c.widget_name(w).startswith('head') 

283 if not is_headline: 

284 self.w = w 

285 self.calltip() 

286 else: 

287 c.insertCharFromEvent(event) 

288 #@+node:ekr.20061031131434.15: *4* ac.showAutocompleter/CalltipsStatus 

289 def showAutocompleterStatus(self): 

290 """Show the autocompleter status.""" 

291 k = self.k 

292 if not g.unitTesting: 

293 s = f"autocompleter {'On' if k.enable_autocompleter else 'Off'}" 

294 g.red(s) 

295 

296 def showCalltipsStatus(self): 

297 """Show the autocompleter status.""" 

298 k = self.k 

299 if not g.unitTesting: 

300 s = f"calltips {'On'}" if k.enable_calltips else 'Off' 

301 g.red(s) 

302 #@+node:ekr.20061031131434.16: *3* ac.Helpers 

303 #@+node:ekr.20110512212836.14469: *4* ac.exit 

304 def exit(self): 

305 

306 trace = all(z in g.app.debug for z in ('abbrev', 'verbose')) 

307 if trace: 

308 g.trace('(AutoCompleterClass)') 

309 c, p, u = self.c, self.c.p, self.c.undoer 

310 w = self.w or c.frame.body.wrapper 

311 c.k.keyboardQuit() 

312 if self.use_qcompleter: 

313 if self.qw: 

314 self.qw.end_completer() 

315 self.qw = None # Bug fix: 2013/09/24. 

316 else: 

317 for name in (self.tabName, 'Modules', 'Info'): 

318 c.frame.log.deleteTab(name) 

319 # Restore the selection range that may have been destroyed by changing tabs. 

320 c.widgetWantsFocusNow(w) 

321 i, j = w.getSelectionRange() 

322 w.setSelectionRange(i, j, insert=j) 

323 newText = w.getAllText() 

324 if p.b == newText: 

325 return 

326 bunch = u.beforeChangeBody(p) 

327 p.v.b = newText # p.b would cause a redraw. 

328 u.afterChangeBody(p, 'auto-completer', bunch) 

329 

330 finish = exit 

331 abort = exit 

332 #@+node:ekr.20061031131434.18: *4* ac.append/begin/popTabName 

333 def appendTabName(self, word): 

334 self.setTabName(self.tabName + '.' + word) 

335 

336 def beginTabName(self, word): 

337 self.setTabName('AutoComplete ' + word) 

338 

339 def clearTabName(self): 

340 self.setTabName('AutoComplete ') 

341 

342 def popTabName(self): 

343 s = self.tabName 

344 i = s.rfind('.', 0, -1) 

345 if i > -1: 

346 self.setTabName(s[0:i]) 

347 

348 # Underscores are not valid in Pmw tab names! 

349 

350 def setTabName(self, s): 

351 c = self.c 

352 if self.tabName: 

353 c.frame.log.deleteTab(self.tabName) 

354 self.tabName = s.replace('_', '') or '' 

355 c.frame.log.clearTab(self.tabName) 

356 #@+node:ekr.20110509064011.14556: *4* ac.attr_matches 

357 def attr_matches(self, s, namespace): 

358 """Compute matches when string s is of the form name.name....name. 

359 

360 Evaluates s using eval(s,namespace) 

361 

362 Assuming the text is of the form NAME.NAME....[NAME], and is evaluatable in 

363 the namespace, it will be evaluated and its attributes (as revealed by 

364 dir()) are used as possible completions. 

365 

366 For class instances, class members are are also considered.) 

367 

368 **Warning**: this can still invoke arbitrary C code, if an object 

369 with a __getattr__ hook is evaluated. 

370 

371 """ 

372 # Seems to work great. Catches things like ''.<tab> 

373 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", s) 

374 if not m: 

375 return [] 

376 expr, attr = m.group(1, 3) 

377 try: 

378 safe_expr = self.strip_brackets(expr) 

379 obj = eval(safe_expr, namespace) 

380 except Exception: 

381 return [] 

382 # Build the result. 

383 words = dir(obj) 

384 n = len(attr) 

385 result = [f"{expr}.{w}" for w in words if w[:n] == attr] 

386 return result 

387 #@+node:ekr.20061031131434.11: *4* ac.auto_completer_state_handler 

388 def auto_completer_state_handler(self, event): 

389 """Handle all keys while autocompleting.""" 

390 c, k, tag = self.c, self.k, 'auto-complete' 

391 state = k.getState(tag) 

392 ch = event.char if event else '' 

393 stroke = event.stroke if event else '' 

394 is_plain = k.isPlainKey(stroke) 

395 if state == 0: 

396 c.frame.log.clearTab(self.tabName) 

397 common_prefix, prefix, tabList = self.compute_completion_list() 

398 if tabList: 

399 k.setState(tag, 1, handler=self.auto_completer_state_handler) 

400 else: 

401 self.exit() 

402 elif ch in ('\n', 'Return'): 

403 self.exit() 

404 elif ch == 'Escape': 

405 self.exit() 

406 elif ch in ('\t', 'Tab'): 

407 self.compute_completion_list() 

408 elif ch in ('\b', 'BackSpace'): 

409 self.do_backspace() 

410 elif ch == '.': 

411 self.insert_string('.') 

412 self.compute_completion_list() 

413 elif ch == '?': 

414 self.info() 

415 elif ch == '!': 

416 # Toggle between verbose and brief listing. 

417 self.verbose = not self.verbose 

418 kind = 'ON' if self.verbose else 'OFF' 

419 message = f"verbose completions {kind}" 

420 g.es_print(message) 

421 # This doesn't work because compute_completion_list clears the autocomplete tab. 

422 # self.put('', message, tabName=self.tabName) 

423 # This is almost invisible: the fg='red' is not honored. 

424 c.frame.putStatusLine(message, fg='red') 

425 self.compute_completion_list() 

426 # elif ch == 'Down' and hasattr(self,'onDown'): 

427 # self.onDown() 

428 # elif ch == 'Up' and hasattr(self,'onUp'): 

429 # self.onUp() 

430 elif is_plain and ch and ch in string.printable: 

431 self.insert_general_char(ch) 

432 elif stroke == k.autoCompleteForceKey: 

433 # This is probably redundant because completions will exist. 

434 # However, it doesn't hurt, and it may be useful rarely. 

435 common_prefix, prefix, tabList = self.compute_completion_list() 

436 if tabList: 

437 self.show_completion_list(common_prefix, prefix, tabList) 

438 else: 

439 g.warning('No completions') 

440 self.exit() 

441 else: 

442 self.abort() 

443 return 'do-standard-keys' 

444 return None 

445 #@+node:ekr.20061031131434.20: *4* ac.calltip & helpers 

446 def calltip(self): 

447 """Show the calltips for the present prefix. 

448 ch is '(' if the user has just typed it. 

449 """ 

450 obj, prefix = self.get_object() 

451 if obj: 

452 self.calltip_success(prefix, obj) 

453 else: 

454 self.calltip_fail(prefix) 

455 self.exit() 

456 #@+node:ekr.20110512090917.14468: *5* ac.calltip_fail 

457 def calltip_fail(self, prefix): 

458 """Evaluation of prefix failed.""" 

459 self.insert_string('(') 

460 #@+node:ekr.20110512090917.14469: *5* ac.calltip_success 

461 def calltip_success(self, prefix, obj): 

462 try: 

463 # Get the parenthesized argument list. 

464 s1, s2, s3, s4 = inspect.getargspec(obj) 

465 s = inspect.formatargspec(s1, s2, s3, s4) 

466 except Exception: 

467 self.insert_string('(') 

468 return 

469 # Clean s and insert it: don't include the opening "(". 

470 if g.match(s, 1, 'self,'): 

471 s = s[6:].strip() 

472 elif g.match_word(s, 1, 'self'): 

473 s = s[5:].strip() 

474 else: 

475 s = s[1:].strip() 

476 self.insert_string("(", select=False) 

477 self.insert_string(s, select=True) 

478 #@+node:ekr.20061031131434.28: *4* ac.compute_completion_list & helper 

479 def compute_completion_list(self): 

480 """Return the autocompleter completion list.""" 

481 prefix = self.get_autocompleter_prefix() 

482 key, options = self.get_cached_options(prefix) 

483 if not options: 

484 options = self.get_completions(prefix) 

485 tabList, common_prefix = g.itemsMatchingPrefixInList( 

486 prefix, options, matchEmptyPrefix=False) 

487 if not common_prefix: 

488 tabList, common_prefix = g.itemsMatchingPrefixInList( 

489 prefix, options, matchEmptyPrefix=True) 

490 if tabList: 

491 self.show_completion_list(common_prefix, prefix, tabList) 

492 return common_prefix, prefix, tabList 

493 #@+node:ekr.20110514051607.14524: *5* ac.get_cached_options 

494 def get_cached_options(self, prefix): 

495 d = self.completionsDict 

496 # Search the completions Dict for shorter and shorter prefixes. 

497 i = len(prefix) 

498 while i > 0: 

499 key = prefix[:i] 

500 i -= 1 

501 # Make sure we report hits only of real objects. 

502 if key.endswith('.'): 

503 return key, [] 

504 options = d.get(key) 

505 if options: 

506 return key, options 

507 return None, [] 

508 #@+node:ekr.20061031131434.29: *4* ac.do_backspace 

509 def do_backspace(self): 

510 """Delete the character and recompute the completion list.""" 

511 c, w = self.c, self.w 

512 c.bodyWantsFocusNow() 

513 i = w.getInsertPoint() 

514 if i <= 0: 

515 self.exit() 

516 return 

517 w.delete(i - 1, i) 

518 w.setInsertPoint(i - 1) 

519 if i <= 1: 

520 self.exit() 

521 else: 

522 # Update the list. Abort if there is no prefix. 

523 common_prefix, prefix, tabList = self.compute_completion_list() 

524 if not prefix: 

525 self.exit() 

526 #@+node:ekr.20110510133719.14548: *4* ac.do_qcompleter_tab (not used) 

527 def do_qcompleter_tab(self, prefix, options): 

528 """Return the longest common prefix of all the options.""" 

529 matches, common_prefix = g.itemsMatchingPrefixInList( 

530 prefix, options, matchEmptyPrefix=False) 

531 return common_prefix 

532 #@+node:ekr.20110509064011.14561: *4* ac.get_autocompleter_prefix 

533 def get_autocompleter_prefix(self): 

534 # Only the body pane supports auto-completion. 

535 w = self.c.frame.body.wrapper 

536 s = w.getAllText() 

537 if not s: 

538 return '' 

539 i = w.getInsertPoint() - 1 

540 i = j = max(0, i) 

541 while i >= 0 and (s[i].isalnum() or s[i] in '._'): 

542 i -= 1 

543 i += 1 

544 j += 1 

545 prefix = s[i:j] 

546 return prefix 

547 #@+node:ekr.20110512212836.14471: *4* ac.get_completions & helpers 

548 jedi_warning = False 

549 

550 def get_completions(self, prefix): 

551 """Return jedi or codewise completions.""" 

552 d = self.completionsDict 

553 if self.use_jedi: 

554 try: 

555 import jedi 

556 except ImportError: 

557 jedi = None 

558 if not self.jedi_warning: 

559 self.jedi_warning = True 

560 g.es_print('can not import jedi') 

561 g.es_print('ignoring @bool use_jedi = True') 

562 if jedi: 

563 aList = ( 

564 self.get_jedi_completions(prefix) or 

565 # Prefer the jedi completions. 

566 self.get_leo_completions(prefix)) 

567 d[prefix] = aList 

568 return aList 

569 # 

570 # Not jedi. Use codewise. 

571 # Precompute the codewise completions for '.self'. 

572 if not self.codewiseSelfList: 

573 aList = self.get_codewise_completions('self.') 

574 self.codewiseSelfList = [z[5:] for z in aList] 

575 d['self.'] = self.codewiseSelfList 

576 # Use the cached list if it exists. 

577 aList = d.get(prefix) 

578 if aList: 

579 return aList 

580 aList = ( 

581 self.get_leo_completions(prefix) or 

582 # Prefer the Leo completions. 

583 self.get_codewise_completions(prefix) 

584 ) 

585 d[prefix] = aList 

586 return aList 

587 #@+node:ekr.20110510120621.14539: *5* ac.get_codewise_completions & helpers 

588 def get_codewise_completions(self, prefix): 

589 """Use codewise to generate a list of hits.""" 

590 c = self.c 

591 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", prefix) 

592 if m: 

593 varname = m.group(1) 

594 ivar = m.group(3) 

595 kind, aList = self.guess_class(c, varname) 

596 else: 

597 kind, aList = 'none', [] 

598 varname, ivar = None, None 

599 if aList: 

600 if kind == 'class': 

601 hits = self.lookup_methods(aList, ivar) 

602 hits.extend(self.codewiseSelfList) 

603 elif kind == 'module': 

604 hits = self.lookup_modules(aList, ivar) 

605 else: 

606 aList2 = prefix.split('.') 

607 if aList2: 

608 func = aList2[-1] 

609 hits = self.lookup_functions(func) 

610 else: 

611 hits = [] 

612 if 1: # A kludge: add the prefix to each hit. 

613 hits = [f"{varname}.{z}" for z in hits] 

614 return hits 

615 #@+node:ekr.20110510120621.14540: *6* ac.clean 

616 def clean(self, hits): 

617 """Clean up hits, a list of ctags patterns, for use in completion lists.""" 

618 # Just take the function name: ignore the signature & file. 

619 aList = list(set([z[0] for z in hits])) 

620 aList.sort() 

621 return aList 

622 #@+node:ekr.20110512232915.14481: *6* ac.clean_for_display (not used) 

623 def clean_for_display(self, hits): 

624 """Clean up hits, a list of ctags patterns, for display purposes.""" 

625 aList = [] 

626 for h in hits: 

627 s = h[0] 

628 # Display oriented: no good for completion list. 

629 fn = h[1].strip() 

630 if fn.startswith('/'): 

631 sig = fn[2:-4].strip() 

632 else: 

633 sig = fn 

634 aList.append(f"{s}: {sig}") 

635 aList = list(set(aList)) 

636 aList.sort() 

637 return aList 

638 #@+node:ekr.20110510120621.14542: *6* ac.guess_class 

639 def guess_class(self, c, varname) -> Tuple[str, List[str]]: 

640 """Return kind, class_list""" 

641 # if varname == 'g': 

642 # return 'module',['leoGlobals'] 

643 if varname == 'p': 

644 return 'class', ['position'] 

645 if varname == 'c': 

646 return 'class', ['Commands'] 

647 if varname == 'self': 

648 # Return the nearest enclosing class. 

649 for p in c.p.parents(): 

650 h = p.h 

651 m = re.search(r'class\s+(\w+)', h) 

652 if m: 

653 return 'class', [m.group(1)] 

654 # This is not needed now that we add the completions for 'self'. 

655 # aList = ContextSniffer().get_classes(c.p.b, varname) 

656 return 'class', [] 

657 #@+node:ekr.20110510120621.14543: *6* ac.lookup_functions/methods/modules 

658 def lookup_functions(self, prefix): 

659 aList = codewise.cmd_functions([prefix]) 

660 hits = [z.split(None, 1) for z in aList if z.strip()] 

661 return self.clean(hits) 

662 

663 def lookup_methods(self, aList, prefix): # prefix not used, only aList[0] used. 

664 aList = codewise.cmd_members([aList[0]]) 

665 hits = [z.split(None, 1) for z in aList if z.strip()] 

666 return self.clean(hits) 

667 

668 def lookup_modules(self, aList, prefix): # prefix not used, only aList[0] used. 

669 aList = codewise.cmd_functions([aList[0]]) 

670 hits = [z.split(None, 1) for z in aList if z.strip()] 

671 return self.clean(hits) 

672 #@+node:ekr.20180519111302.1: *5* ac.get_jedi_completions & helper 

673 def get_jedi_completions(self, prefix): 

674 

675 c = self.c 

676 w = c.frame.body.wrapper 

677 i = w.getInsertPoint() 

678 p = c.p 

679 body_s = p.b 

680 # 

681 # Get the entire source for jedi. 

682 t1 = time.process_time() 

683 goto = gotoCommands.GoToCommands(c) 

684 root, fileName = goto.find_root(p) 

685 if root: 

686 source = goto.get_external_file_with_sentinels(root=root or p) 

687 n0 = goto.find_node_start(p=p, s=source) 

688 if n0 is None: 

689 n0 = 0 

690 else: 

691 source = body_s 

692 n0 = 0 

693 t2 = time.process_time() 

694 # 

695 # Get local line 

696 lines = g.splitLines(body_s) 

697 row, column = g.convertPythonIndexToRowCol(body_s, i) 

698 if row >= len(lines): # 2020/11/27 

699 return [] 

700 line = lines[row] 

701 # 

702 # Find the global line, and compute offsets. 

703 source_lines = g.splitLines(source) 

704 for jedi_line, g_line in enumerate(source_lines[n0:]): 

705 if line.lstrip() == g_line.lstrip(): 

706 # Adjust the column. 

707 indent1 = len(line) - len(line.lstrip()) 

708 indent2 = len(g_line) - len(g_line.lstrip()) 

709 if indent2 >= indent1: 

710 local_column = column # For traces. 

711 column += abs(indent2 - indent1) 

712 break 

713 else: 

714 completions = None 

715 jedi_line, indent1, indent2 = None, None, None 

716 if 0: # This *can* happen. 

717 g.printObj(source_lines[n0 - 1 : n0 + 30]) 

718 print(f"can not happen: not found: {line!r}") 

719 # 

720 # Get the jedi completions. 

721 if jedi and jedi_line is not None: 

722 try: 

723 # https://jedi.readthedocs.io/en/latest/docs/api.html#script 

724 script = jedi.Script(source, path=g.shortFileName(fileName)) 

725 completions = script.complete( 

726 line=1 + n0 + jedi_line, 

727 column=column, 

728 ) 

729 t3 = time.process_time() 

730 except Exception: 

731 t3 = time.process_time() 

732 completions = None 

733 g.printObj(source_lines[n0 - 1 : n0 + 30]) 

734 print('ERROR', p.h) 

735 if not completions: 

736 return [] 

737 # May be used in traces below. 

738 assert t3 >= t2 >= t1 

739 assert local_column is not None 

740 completions = [z.name for z in completions] 

741 completions = [self.add_prefix(prefix, z) for z in completions] 

742 # Retain these for now... 

743 # g.printObj(completions[:5]) 

744 # head = line[:local_column] 

745 # ch = line[local_column:local_column+1] 

746 # g.trace(len(completions), repr(ch), head.strip()) 

747 return completions 

748 #@+node:ekr.20180526211127.1: *6* ac.add_prefix 

749 def add_prefix(self, prefix, s): 

750 """A hack to match the callers expectations.""" 

751 if prefix.find('.') > -1: 

752 aList = prefix.split('.') 

753 prefix = '.'.join(aList[:-1]) + '.' 

754 return s if s.startswith(prefix) else prefix + s 

755 #@+node:ekr.20110509064011.14557: *5* ac.get_leo_completions 

756 def get_leo_completions(self, prefix): 

757 """Return completions in an environment defining c, g and p.""" 

758 aList = [] 

759 for d in self.namespaces + [self.get_leo_namespace(prefix)]: 

760 aList.extend(self.attr_matches(prefix, d)) 

761 aList.sort() 

762 return aList 

763 #@+node:ekr.20110512090917.14466: *4* ac.get_leo_namespace 

764 def get_leo_namespace(self, prefix): 

765 """ 

766 Return an environment in which to evaluate prefix. 

767 Add some common standard library modules as needed. 

768 """ 

769 k = self.k 

770 d = {'c': k.c, 'p': k.c.p, 'g': g} 

771 aList = prefix.split('.') 

772 if len(aList) > 1: 

773 name = aList[0] 

774 m = sys.modules.get(name) 

775 if m: 

776 d[name] = m 

777 return d 

778 #@+node:ekr.20110512170111.14472: *4* ac.get_object 

779 def get_object(self): 

780 """Return the object corresponding to the current prefix.""" 

781 common_prefix, prefix1, aList = self.compute_completion_list() 

782 if not aList: 

783 return None, prefix1 

784 if len(aList) == 1: 

785 prefix = aList[0] 

786 else: 

787 prefix = common_prefix 

788 if prefix.endswith('.') and self.use_qcompleter: 

789 prefix += self.qcompleter.get_selection() 

790 safe_prefix = self.strip_brackets(prefix) 

791 for d in self.namespaces + [self.get_leo_namespace(prefix)]: 

792 try: 

793 obj = eval(safe_prefix, d) 

794 break # only reached if none of the exceptions below occur 

795 except AttributeError: 

796 obj = None 

797 except NameError: 

798 obj = None 

799 except SyntaxError: 

800 obj = None 

801 except Exception: 

802 g.es_exception() 

803 obj = None 

804 return obj, prefix 

805 #@+node:ekr.20061031131434.38: *4* ac.info 

806 def info(self): 

807 """Show the docstring for the present completion.""" 

808 c = self.c 

809 obj, prefix = self.get_object() 

810 c.frame.log.clearTab('Info', wrap='word') 

811 put = lambda s: self.put('', s, tabName='Info') 

812 put(prefix) 

813 try: 

814 argspec = inspect.getargspec(obj) 

815 # uses None instead of empty list 

816 argn = len(argspec.args or []) 

817 defn = len(argspec.defaults or []) 

818 put("args:") 

819 simple_args = argspec.args[: argn - defn] 

820 if not simple_args: 

821 put(' (none)') 

822 else: 

823 put(' ' + ', '.join(' ' + i for i in simple_args)) 

824 put("keyword args:") 

825 if not argspec.defaults: 

826 put(' (none)') 

827 for i in range(defn): 

828 arg = argspec.args[-defn + i] 

829 put(f" {arg} = {repr(argspec.defaults[i])}") 

830 if argspec.varargs: 

831 put("varargs: *" + argspec.varargs) 

832 if argspec.keywords: 

833 put("keywords: **" + argspec.keywords) 

834 put('\n') # separate docstring 

835 except TypeError: 

836 put('\n') # not a callable 

837 doc = inspect.getdoc(obj) 

838 put(doc if doc else "No docstring for " + repr(prefix)) 

839 #@+node:ekr.20110510071925.14586: *4* ac.init_qcompleter 

840 def init_qcompleter(self, event=None): 

841 

842 # Compute the prefix and the list of options. 

843 prefix = self.get_autocompleter_prefix() 

844 options = self.get_completions(prefix) 

845 w = self.c.frame.body.wrapper.widget # A LeoQTextBrowser. May be none for unit tests. 

846 if w and options: 

847 self.qw = w 

848 self.qcompleter = w.init_completer(options) 

849 self.auto_completer_state_handler(event) 

850 else: 

851 if not g.unitTesting: 

852 g.warning('No completions') 

853 self.exit() 

854 #@+node:ekr.20110511133940.14552: *4* ac.init_tabcompleter 

855 def init_tabcompleter(self, event=None): 

856 # Compute the prefix and the list of options. 

857 prefix = self.get_autocompleter_prefix() 

858 options = self.get_completions(prefix) 

859 if options: 

860 self.clearTabName() # Creates the tabbed pane. 

861 self.auto_completer_state_handler(event) 

862 else: 

863 g.warning('No completions') 

864 self.exit() 

865 #@+node:ekr.20061031131434.39: *4* ac.insert_general_char 

866 def insert_general_char(self, ch): 

867 

868 trace = all(z in g.app.debug for z in ('abbrev', 'verbose')) 

869 k, w = self.k, self.w 

870 if g.isWordChar(ch): 

871 if trace: 

872 g.trace('ch', repr(ch)) 

873 self.insert_string(ch) 

874 common_prefix, prefix, aList = self.compute_completion_list() 

875 if not aList: 

876 if self.forbid_invalid: 

877 # Delete the character we just inserted. 

878 self.do_backspace() 

879 # @bool auto_tab_complete is deprecated. 

880 # Auto-completion makes no sense if it is False. 

881 elif self.auto_tab and len(common_prefix) > len(prefix): 

882 extend = common_prefix[len(prefix) :] 

883 ins = w.getInsertPoint() 

884 if trace: 

885 g.trace('extend', repr(extend)) 

886 w.insert(ins, extend) 

887 return 

888 if ch == '(' and k.enable_calltips: 

889 # This calls self.exit if the '(' is valid. 

890 self.calltip() 

891 else: 

892 if trace: 

893 g.trace('ch', repr(ch)) 

894 self.insert_string(ch) 

895 self.exit() 

896 #@+node:ekr.20061031131434.31: *4* ac.insert_string 

897 def insert_string(self, s, select=False): 

898 """ 

899 Insert an auto-completion string s at the insertion point. 

900 

901 Leo 6.4. This *part* of auto-completion is no longer undoable. 

902 """ 

903 c, w = self.c, self.w 

904 if not g.isTextWrapper(w): 

905 return 

906 c.widgetWantsFocusNow(w) 

907 # 

908 # Don't make this undoable. 

909 # oldText = w.getAllText() 

910 # oldSel = w.getSelectionRange() 

911 # bunch = u.beforeChangeBody(p) 

912 i = w.getInsertPoint() 

913 w.insert(i, s) 

914 if select: 

915 j = i + len(s) 

916 w.setSelectionRange(i, j, insert=j) 

917 # 

918 # Don't make this undoable. 

919 # if 0: 

920 # u.doTyping(p, 'Typing', 

921 # oldSel=oldSel, 

922 # oldText=oldText, 

923 # newText=w.getAllText(), 

924 # newInsert=w.getInsertPoint(), 

925 # newSel=w.getSelectionRange()) 

926 # else: 

927 # u.afterChangeBody(p, 'auto-complete', bunch) 

928 if self.use_qcompleter and self.qw: 

929 c.widgetWantsFocusNow(self.qw.leo_qc) 

930 #@+node:ekr.20110314115639.14269: *4* ac.is_leo_source_file 

931 def is_leo_source_file(self): 

932 """Return True if this is one of Leo's source files.""" 

933 c = self.c 

934 table = (z.lower() for z in ( 

935 'leoDocs.leo', 

936 'LeoGui.leo', 'LeoGuiPluginsRef.leo', 

937 # 'leoPlugins.leo', 'leoPluginsRef.leo', 

938 'leoPy.leo', 'leoPyRef.leo', 

939 'myLeoSettings.leo', 'leoSettings.leo', 

940 'ekr.leo', 

941 # 'test.leo', 

942 )) 

943 return c.shortFileName().lower() in table 

944 #@+node:ekr.20101101175644.5891: *4* ac.put 

945 def put(self, *args, **keys): 

946 """Put s to the given tab. 

947 

948 May be overridden in subclasses.""" 

949 # print('autoCompleter.put',args,keys) 

950 if g.unitTesting: 

951 pass 

952 else: 

953 g.es(*args, **keys) 

954 #@+node:ekr.20110511133940.14561: *4* ac.show_completion_list & helpers 

955 def show_completion_list(self, common_prefix, prefix, tabList): 

956 

957 c = self.c 

958 aList = common_prefix.split('.') 

959 header = '.'.join(aList[:-1]) 

960 # "!" toggles self.verbose. 

961 if self.verbose or self.use_qcompleter or len(tabList) < 20: 

962 tabList = self.clean_completion_list(header, tabList,) 

963 else: 

964 tabList = self.get_summary_list(header, tabList) 

965 if self.use_qcompleter: 

966 # Put the completions in the QListView. 

967 if self.qw: 

968 self.qw.show_completions(tabList) 

969 else: 

970 # Update the tab name, creating the tab if necessary. 

971 c.widgetWantsFocus(self.w) 

972 c.frame.log.clearTab(self.tabName) 

973 self.beginTabName(header + '.' if header else '') 

974 s = '\n'.join(tabList) 

975 self.put('', s, tabName=self.tabName) 

976 #@+node:ekr.20110513104728.14453: *5* ac.clean_completion_list 

977 def clean_completion_list(self, header, tabList): 

978 """Return aList with header removed from the start of each list item.""" 

979 return [ 

980 z[len(header) + 1 :] if z.startswith(header) else z 

981 for z in tabList] 

982 #@+node:ekr.20110513104728.14454: *5* ac.get_summary_list 

983 def get_summary_list(self, header, tabList): 

984 """Show the possible starting letters, 

985 but only if there are more than one. 

986 """ 

987 d: Dict[str, int] = {} 

988 for z in tabList: 

989 tail = z[len(header) :] if z else '' 

990 if tail.startswith('.'): 

991 tail = tail[1:] 

992 ch = tail[0] if tail else '' 

993 if ch: 

994 n = d.get(ch, 0) 

995 d[ch] = n + 1 

996 aList = [f"{ch2} {d.get(ch2)}" for ch2 in sorted(d)] 

997 if len(aList) > 1: 

998 tabList = aList 

999 else: 

1000 tabList = self.clean_completion_list(header, tabList) 

1001 return tabList 

1002 #@+node:ekr.20061031131434.46: *4* ac.start 

1003 def start(self, event): 

1004 """Init the completer and start the state handler.""" 

1005 # We don't need to clear this now that we don't use ContextSniffer. 

1006 c = self.c 

1007 if c.config.getBool('use-jedi', default=True): 

1008 self.completionsDict = {} 

1009 if self.use_qcompleter: 

1010 self.init_qcompleter(event) 

1011 else: 

1012 self.init_tabcompleter(event) 

1013 #@+node:ekr.20110512170111.14471: *4* ac.strip_brackets 

1014 def strip_brackets(self, s): 

1015 """Return s with all brackets removed. 

1016 

1017 This (mostly) ensures that eval will not execute function calls, etc. 

1018 """ 

1019 for ch in '[]{}()': 

1020 s = s.replace(ch, '') 

1021 return s 

1022 #@-others 

1023#@+node:ekr.20110312162243.14260: ** class ContextSniffer 

1024class ContextSniffer: 

1025 """ Class to analyze surrounding context and guess class 

1026 

1027 For simple dynamic code completion engines. 

1028 """ 

1029 

1030 def __init__(self): 

1031 self.vars = {} 

1032 # Keys are var names; values are list of classes 

1033 #@+others 

1034 #@+node:ekr.20110312162243.14261: *3* get_classes 

1035 def get_classes(self, s, varname): 

1036 """Return a list of classes for string s.""" 

1037 self.push_declarations(s) 

1038 aList = self.vars.get(varname, []) 

1039 return aList 

1040 #@+node:ekr.20110312162243.14262: *3* set_small_context 

1041 # def set_small_context(self, body): 

1042 # """ Set immediate function """ 

1043 # self.push_declarations(body) 

1044 #@+node:ekr.20110312162243.14263: *3* push_declarations & helper 

1045 def push_declarations(self, s): 

1046 for line in s.splitlines(): 

1047 line = line.lstrip() 

1048 if line.startswith('#'): 

1049 line = line.lstrip('#') 

1050 parts = line.split(':') 

1051 if len(parts) == 2: 

1052 a, b = parts 

1053 self.declare(a.strip(), b.strip()) 

1054 #@+node:ekr.20110312162243.14264: *4* declare 

1055 def declare(self, var, klass): 

1056 vars = self.vars.get(var, []) 

1057 if not vars: 

1058 self.vars[var] = vars 

1059 vars.append(klass) 

1060 #@-others 

1061#@+node:ekr.20140813052702.18194: ** class FileNameChooser 

1062class FileNameChooser: 

1063 """A class encapsulation file selection & completion logic.""" 

1064 #@+others 

1065 #@+node:ekr.20140813052702.18195: *3* fnc.__init__ 

1066 def __init__(self, c): 

1067 """Ctor for FileNameChooser class.""" 

1068 self.c = c 

1069 self.k = c.k 

1070 assert c and c.k 

1071 self.log = c.frame.log or g.NullObject() 

1072 self.callback = None 

1073 self.filterExt = None 

1074 self.log = None # inited later. 

1075 self.prompt = None 

1076 self.tabName = None 

1077 #@+node:ekr.20140813052702.18196: *3* fnc.compute_tab_list 

1078 def compute_tab_list(self): 

1079 """Compute the list of completions.""" 

1080 path = self.get_label() 

1081 # #215: insert-file-name doesn't process ~ 

1082 path = g.os_path_expanduser(path) 

1083 sep = os.path.sep 

1084 if g.os_path_exists(path): 

1085 if g.os_path_isdir(path): 

1086 if path.endswith(os.sep): 

1087 aList = g.glob_glob(path + '*') 

1088 else: 

1089 aList = g.glob_glob(path + sep + '*') 

1090 tabList = [z + sep if g.os_path_isdir(z) else z for z in aList] 

1091 else: 

1092 # An existing file. 

1093 tabList = [path] 

1094 else: 

1095 if path and path.endswith(sep): 

1096 path = path[:-1] 

1097 aList = g.glob_glob(path + '*') 

1098 tabList = [z + sep if g.os_path_isdir(z) else z for z in aList] 

1099 if self.filterExt: 

1100 for ext in self.filterExt: 

1101 tabList = [z for z in tabList if not z.endswith(ext)] 

1102 tabList = [g.os_path_normslashes(z) for z in tabList] 

1103 junk, common_prefix = g.itemsMatchingPrefixInList(path, tabList) 

1104 return common_prefix, tabList 

1105 #@+node:ekr.20140813052702.18197: *3* fnc.do_back_space 

1106 def do_back_space(self): 

1107 """Handle a back space.""" 

1108 w = self.c.k.w 

1109 if w and w.hasSelection(): 

1110 # s = w.getAllText() 

1111 i, j = w.getSelectionRange() 

1112 w.delete(i, j) 

1113 s = self.get_label() 

1114 else: 

1115 s = self.get_label() 

1116 if s: 

1117 s = s[:-1] 

1118 self.set_label(s) 

1119 if s: 

1120 common_prefix, tabList = self.compute_tab_list() 

1121 # Do *not* extend the label to the common prefix. 

1122 else: 

1123 tabList = [] 

1124 self.show_tab_list(tabList) 

1125 #@+node:ekr.20140813052702.18198: *3* fnc.do_char 

1126 def do_char(self, char): 

1127 """Handle a non-special character.""" 

1128 w = self.c.k.w 

1129 if w and w.hasSelection: 

1130 # s = w.getAllText() 

1131 i, j = w.getSelectionRange() 

1132 w.delete(i, j) 

1133 w.setInsertPoint(i) 

1134 w.insert(i, char) 

1135 else: 

1136 self.extend_label(char) 

1137 common_prefix, tabList = self.compute_tab_list() 

1138 self.show_tab_list(tabList) 

1139 if common_prefix: 

1140 if 0: 

1141 # This is a bit *too* helpful. 

1142 # It's too easy to type ahead by mistake. 

1143 # Instead, completion should happen only when the user types <tab>. 

1144 self.set_label(common_prefix) 

1145 # Recompute the tab list. 

1146 common_prefix, tabList = self.compute_tab_list() 

1147 self.show_tab_list(tabList) 

1148 if len(tabList) == 1: 

1149 # Automatically complete the typing only if there is only one item in the list. 

1150 self.set_label(common_prefix) 

1151 else: 

1152 # Restore everything. 

1153 self.set_label(self.get_label()[:-1]) 

1154 self.extend_label(char) 

1155 #@+node:ekr.20140813052702.18199: *3* fnc.do_tab 

1156 def do_tab(self): 

1157 """Handle tab completion.""" 

1158 old = self.get_label() 

1159 common_prefix, tabList = self.compute_tab_list() 

1160 self.show_tab_list(tabList) 

1161 if len(tabList) == 1: 

1162 common_prefix = tabList[0] 

1163 self.set_label(common_prefix) 

1164 elif len(common_prefix) > len(old): 

1165 self.set_label(common_prefix) 

1166 #@+node:ekr.20140813052702.18200: *3* fnc.get_file_name (entry) 

1167 def get_file_name(self, event, callback, filterExt, prompt, tabName): 

1168 """Get a file name, supporting file completion.""" 

1169 c, k = self.c, self.c.k 

1170 tag = 'get-file-name' 

1171 state = k.getState(tag) 

1172 char = event.char if event else '' 

1173 if state == 0: 

1174 # Re-init all ivars. 

1175 self.log = c.frame.log or g.NullObject() 

1176 self.callback = callback 

1177 self.filterExt = filterExt or ['.pyc', '.bin',] 

1178 self.prompt = prompt 

1179 self.tabName = tabName 

1180 join = g.os_path_finalize_join 

1181 finalize = g.os_path_finalize 

1182 normslashes = g.os_path_normslashes 

1183 # #467: Add setting for preferred directory. 

1184 directory = c.config.getString('initial-chooser-directory') 

1185 if directory: 

1186 directory = finalize(directory) 

1187 if not g.os_path_exists(directory): 

1188 g.es_print('@string initial-chooser-directory not found', 

1189 normslashes(directory)) 

1190 directory = None 

1191 if not directory: 

1192 directory = finalize(os.curdir) 

1193 # Init the label and state. 

1194 tail = k.functionTail and k.functionTail.strip() 

1195 label = join(directory, tail) if tail else directory + os.sep 

1196 self.set_label(normslashes(label)) 

1197 k.setState(tag, 1, self.get_file_name) 

1198 self.log.selectTab(self.tabName) 

1199 junk, tabList = self.compute_tab_list() 

1200 self.show_tab_list(tabList) 

1201 c.minibufferWantsFocus() 

1202 elif char == 'Escape': 

1203 k.keyboardQuit() 

1204 elif char in ('\n', 'Return'): 

1205 self.log.deleteTab(self.tabName) 

1206 path = self.get_label() 

1207 k.keyboardQuit() 

1208 if self.callback: 

1209 # pylint: disable=not-callable 

1210 self.callback(path) 

1211 else: 

1212 g.trace('no callback') 

1213 elif char in ('\t', 'Tab'): 

1214 self.do_tab() 

1215 c.minibufferWantsFocus() 

1216 elif char in ('\b', 'BackSpace'): 

1217 self.do_back_space() 

1218 c.minibufferWantsFocus() 

1219 elif k.isPlainKey(char): 

1220 self.do_char(char) 

1221 else: 

1222 pass 

1223 #@+node:ekr.20140813052702.18201: *3* fnc.extend/get/set_label 

1224 def extend_label(self, s): 

1225 """Extend the label by s.""" 

1226 self.c.k.extendLabel(s, select=False, protect=False) 

1227 

1228 def get_label(self): 

1229 """Return the label, not including the prompt.""" 

1230 return self.c.k.getLabel(ignorePrompt=True) 

1231 

1232 def set_label(self, s): 

1233 """Set the label after the prompt to s. The prompt never changes.""" 

1234 self.c.k.setLabel(self.prompt, protect=True) 

1235 self.c.k.extendLabel(s or '', select=False, protect=False) 

1236 #@+node:ekr.20140813052702.18202: *3* fnc.show_tab_list 

1237 def show_tab_list(self, tabList): 

1238 """Show the tab list in the log tab.""" 

1239 self.log.clearTab(self.tabName) 

1240 s = g.os_path_finalize(os.curdir) + os.sep 

1241 es = [] 

1242 for path in tabList: 

1243 theDir, fileName = g.os_path_split(path) 

1244 s = theDir if path.endswith(os.sep) else fileName 

1245 s = fileName or g.os_path_basename(theDir) + os.sep 

1246 es.append(s) 

1247 g.es('', '\n'.join(es), tabName=self.tabName) 

1248 #@-others 

1249#@+node:ekr.20140816165728.18940: ** class GetArg 

1250class GetArg: 

1251 """ 

1252 A class encapsulating all k.getArg logic. 

1253 

1254 k.getArg maps to ga.get_arg, which gets arguments in the minibuffer. 

1255 

1256 For details, see the docstring for ga.get_arg 

1257 """ 

1258 #@+others 

1259 #@+node:ekr.20140818052417.18241: *3* ga.birth 

1260 #@+node:ekr.20140816165728.18952: *4* ga.__init__ 

1261 def __init__(self, c, prompt='full-command: ', tabName='Completion'): 

1262 """Ctor for GetArg class.""" 

1263 # Common ivars. 

1264 self.c = c 

1265 self.k = c.k 

1266 assert c 

1267 assert c.k 

1268 self.log = c.frame.log or g.NullObject() 

1269 self.tabName = tabName 

1270 # State vars. 

1271 self.after_get_arg_state = None, None, None 

1272 self.arg_completion = True 

1273 self.handler = None 

1274 self.tabList = [] 

1275 # Tab cycling ivars... 

1276 self.cycling_prefix = None 

1277 self.cycling_index = -1 

1278 self.cycling_tabList = [] 

1279 # The following are k globals. 

1280 # k.arg. 

1281 # k.argSelectedText 

1282 # k.oneCharacterArg 

1283 #@+node:ekr.20140817110228.18321: *3* ga.compute_tab_list 

1284 # Called from k.doTabCompletion: with tabList = list(c.commandsDict.keys()) 

1285 

1286 def compute_tab_list(self, tabList): 

1287 """Compute and show the available completions.""" 

1288 # Support vim-mode commands. 

1289 command = self.get_label() 

1290 if self.is_command(command): 

1291 tabList, common_prefix = g.itemsMatchingPrefixInList(command, tabList) 

1292 return common_prefix, tabList 

1293 # 

1294 # For now, disallow further completions if something follows the command. 

1295 command = self.get_command(command) 

1296 return command, [command] 

1297 #@+node:ekr.20140816165728.18965: *3* ga.do_back_space (entry) 

1298 # Called from k.fullCommand: with defaultTabList = list(c.commandsDict.keys()) 

1299 

1300 def do_back_space(self, tabList, completion=True): 

1301 """Handle a backspace and update the completion list.""" 

1302 k = self.k 

1303 self.tabList = tabList[:] if tabList else [] 

1304 # Update the label. 

1305 w = k.w 

1306 i, j = w.getSelectionRange() 

1307 ins = w.getInsertPoint() 

1308 if ins > len(k.mb_prefix): 

1309 # Step 1: actually delete the character. 

1310 i, j = w.getSelectionRange() 

1311 if i == j: 

1312 ins -= 1 

1313 w.delete(ins) 

1314 w.setSelectionRange(ins, ins, insert=ins) 

1315 else: 

1316 ins = i 

1317 w.delete(i, j) 

1318 w.setSelectionRange(i, i, insert=ins) 

1319 if w.getAllText().strip(): 

1320 junk, tabList = self.compute_tab_list(self.tabList) 

1321 # Do *not* extend the label to the common prefix. 

1322 else: 

1323 tabList = [] 

1324 if completion: 

1325 # #323. 

1326 common_prefix, tabList = self.compute_tab_list(tabList) 

1327 self.show_tab_list(tabList) 

1328 self.reset_tab_cycling() 

1329 #@+node:ekr.20140817110228.18323: *3* ga.do_tab (entry) & helpers 

1330 # Used by ga.get_arg and k.fullCommand. 

1331 

1332 def do_tab(self, tabList, completion=True): 

1333 """Handle tab completion when the user hits a tab.""" 

1334 c = self.c 

1335 if completion: 

1336 tabList = self.tabList = tabList[:] if tabList else [] 

1337 common_prefix, tabList = self.compute_tab_list(tabList) 

1338 if self.cycling_prefix and not self.cycling_prefix.startswith(common_prefix): 

1339 self.cycling_prefix = common_prefix 

1340 # 

1341 # No tab cycling for completed commands having 

1342 # a 'tab_callback' attribute. 

1343 if len(tabList) == 1 and self.do_tab_callback(): 

1344 return 

1345 # #323: *Always* call ga.do_tab_list. 

1346 self.do_tab_cycling(common_prefix, tabList) 

1347 c.minibufferWantsFocus() 

1348 #@+node:ekr.20140818145250.18235: *4* ga.do_tab_callback 

1349 def do_tab_callback(self): 

1350 """ 

1351 If the command-name handler has a tab_callback, 

1352 call handler.tab_callback() and return True. 

1353 """ 

1354 c, k = self.c, self.k 

1355 commandName, tail = k.getMinibufferCommandName() 

1356 handler = c.commandsDict.get(commandName) 

1357 if hasattr(handler, 'tab_callback'): 

1358 self.reset_tab_cycling() 

1359 k.functionTail = tail # For k.getFileName. 

1360 handler.tab_callback() 

1361 return True 

1362 return False 

1363 #@+node:ekr.20140819050118.18317: *4* ga.do_tab_cycling 

1364 def do_tab_cycling(self, common_prefix, tabList): 

1365 """Put the next (or first) completion in the minibuffer.""" 

1366 s = self.get_label() 

1367 if not common_prefix: 

1368 # Leave the minibuffer as it is. 

1369 self.show_tab_list(tabList) 

1370 # #323. 

1371 elif ( 

1372 self.cycling_prefix and 

1373 s.startswith(self.cycling_prefix) and 

1374 sorted(self.cycling_tabList) == sorted(tabList) # Bug fix: 2016/10/14 

1375 ): 

1376 n = self.cycling_index 

1377 n = self.cycling_index = n + 1 if n + 1 < len(self.cycling_tabList) else 0 

1378 self.set_label(self.cycling_tabList[n]) 

1379 self.show_tab_list(self.cycling_tabList) 

1380 else: 

1381 # Restart. 

1382 self.show_tab_list(tabList) 

1383 self.cycling_tabList = tabList[:] 

1384 self.cycling_prefix = common_prefix 

1385 self.set_label(common_prefix) 

1386 if tabList and common_prefix == tabList[0]: 

1387 self.cycling_index = 0 

1388 else: 

1389 self.cycling_index = -1 

1390 #@+node:ekr.20140819050118.18318: *4* ga.reset_tab_cycling 

1391 def reset_tab_cycling(self): 

1392 """Reset all tab cycling ivars.""" 

1393 self.cycling_prefix = None 

1394 self.cycling_index = -1 

1395 self.cycling_tabList = [] 

1396 #@+node:ekr.20140816165728.18958: *3* ga.extend/get/set_label 

1397 # Not useful because k.entendLabel doesn't handle selected text. 

1398 

1399 if 0: 

1400 

1401 def extend_label(self, s): 

1402 """Extend the label by s.""" 

1403 self.c.k.extendLabel(s, select=False, protect=False) 

1404 

1405 def get_label(self): 

1406 """Return the label, not including the prompt.""" 

1407 return self.c.k.getLabel(ignorePrompt=True) 

1408 

1409 def set_label(self, s): 

1410 """Set the label after the prompt to s. The prompt never changes.""" 

1411 k = self.c.k 

1412 # Using k.mb_prefix is simplest. No ga.ivars need be inited. 

1413 k.setLabel(k.mb_prefix, protect=True) 

1414 k.extendLabel(s or '', select=False, protect=False) 

1415 #@+node:ekr.20140816165728.18941: *3* ga.get_arg (entry) & helpers 

1416 def get_arg(self, event, 

1417 returnKind=None, returnState=None, handler=None, 

1418 tabList=None, completion=True, oneCharacter=False, 

1419 stroke=None, useMinibuffer=True 

1420 ): 

1421 #@+<< ga.get_arg docstring >> 

1422 #@+node:ekr.20140822051549.18299: *4* << ga.get_arg docstring >> 

1423 """ 

1424 Accumulate an argument. Enter the given return state when done. 

1425 

1426 Ctrl-G will abort this processing at any time. 

1427 

1428 All commands needing user input call k.getArg, which just calls ga.get_arg. 

1429 

1430 The arguments to ga.get_arg are as follows: 

1431 

1432 event: The event passed to the command. 

1433 

1434 returnKind=None: A string. 

1435 returnState=None, An int. 

1436 handler=None, A function. 

1437 

1438 When the argument is complete, ga.do_end does:: 

1439 

1440 if kind: k.setState(kind,n,handler) 

1441 

1442 tabList=[]: A list of possible completions. 

1443 

1444 completion=True: True if completions are enabled. 

1445 

1446 oneCharacter=False: True if k.arg should be a single character. 

1447 

1448 stroke=None: The incoming key stroke. 

1449 

1450 useMinibuffer=True: True: put focus in the minibuffer while accumulating arguments. 

1451 False allows sort-lines, for example, to show the selection range. 

1452 

1453 """ 

1454 #@-<< ga.get_arg docstring >> 

1455 if tabList is None: 

1456 tabList = [] 

1457 c, k = self.c, self.k 

1458 state = k.getState('getArg') 

1459 c.check_event(event) 

1460 c.minibufferWantsFocusNow() 

1461 char = event.char if event else '' 

1462 if state == 0: 

1463 self.do_state_zero(completion, event, handler, oneCharacter, 

1464 returnKind, returnState, tabList, useMinibuffer) 

1465 return 

1466 if char == 'Escape': 

1467 k.keyboardQuit() 

1468 elif self.should_end(char, stroke): 

1469 self.do_end(event, char, stroke) 

1470 elif char in ('\t', 'Tab'): 

1471 self.do_tab(self.tabList, self.arg_completion) 

1472 elif char in ('\b', 'BackSpace'): 

1473 self.do_back_space(self.tabList, self.arg_completion) 

1474 c.minibufferWantsFocus() 

1475 elif k.isFKey(stroke): 

1476 # Ignore only F-keys. Ignoring all except plain keys would kill unicode searches. 

1477 pass 

1478 else: 

1479 self.do_char(event, char) 

1480 #@+node:ekr.20161019060054.1: *4* ga.cancel_after_state 

1481 def cancel_after_state(self): 

1482 

1483 self.after_get_arg_state = None 

1484 #@+node:ekr.20140816165728.18955: *4* ga.do_char 

1485 def do_char(self, event, char): 

1486 """Handle a non-special character.""" 

1487 k = self.k 

1488 k.updateLabel(event) 

1489 # Any plain key resets tab cycling. 

1490 self.reset_tab_cycling() 

1491 #@+node:ekr.20140817110228.18316: *4* ga.do_end 

1492 def do_end(self, event, char, stroke): 

1493 """A return or escape has been seen.""" 

1494 k = self.k 

1495 if char == '\t' and char in k.getArgEscapes: 

1496 k.getArgEscapeFlag = True 

1497 if stroke and stroke in k.getArgEscapes: 

1498 k.getArgEscapeFlag = True 

1499 # Get the value. 

1500 gui_arg = getattr(g.app.gui, 'curses_gui_arg', None) 

1501 if k.oneCharacterArg: 

1502 k.arg = char 

1503 else: 

1504 # A hack to support the curses gui. 

1505 k.arg = gui_arg or self.get_label() 

1506 kind, n, handler = self.after_get_arg_state 

1507 if kind: 

1508 k.setState(kind, n, handler) 

1509 self.log.deleteTab('Completion') 

1510 self.reset_tab_cycling() 

1511 if handler: 

1512 # pylint: disable=not-callable 

1513 handler(event) 

1514 #@+node:ekr.20140817110228.18317: *4* ga.do_state_zero 

1515 def do_state_zero(self, completion, event, handler, oneCharacter, 

1516 returnKind, returnState, tabList, useMinibuffer 

1517 ): 

1518 """Do state 0 processing.""" 

1519 c, k = self.c, self.k 

1520 # 

1521 # Set the ga globals... 

1522 k.getArgEscapeFlag = False 

1523 self.after_get_arg_state = returnKind, returnState, handler 

1524 self.arg_completion = completion 

1525 self.cycling_prefix = None 

1526 self.handler = handler 

1527 self.tabList = tabList[:] if tabList else [] 

1528 # 

1529 # Set the k globals... 

1530 k.functionTail = None 

1531 k.oneCharacterArg = oneCharacter 

1532 # 

1533 # Do *not* change the label here! 

1534 # Enter the next state. 

1535 c.widgetWantsFocus(c.frame.body.wrapper) 

1536 k.setState('getArg', 1, k.getArg) 

1537 # pylint: disable=consider-using-ternary 

1538 k.afterArgWidget = event and event.widget or c.frame.body.wrapper 

1539 if useMinibuffer: 

1540 c.minibufferWantsFocus() 

1541 #@+node:ekr.20140818103808.18234: *4* ga.should_end 

1542 def should_end(self, char, stroke): 

1543 """Return True if ga.get_arg should return.""" 

1544 k = self.k 

1545 return ( 

1546 char in ('\n', 'Return',) or 

1547 k.oneCharacterArg or 

1548 stroke and stroke in k.getArgEscapes or 

1549 char == '\t' and char in k.getArgEscapes 

1550 # The Find Easter Egg. 

1551 ) 

1552 #@+node:ekr.20140818103808.18235: *4* ga.trace_state 

1553 def trace_state(self, char, completion, handler, state, stroke): 

1554 """Trace the vars and ivars.""" 

1555 k = self.c.k 

1556 g.trace( 

1557 'state', state, 'char', repr(char), 'stroke', repr(stroke), 

1558 # 'isPlain',k.isPlainKey(stroke), 

1559 '\n', 

1560 'escapes', k.getArgEscapes, 

1561 'completion', self.arg_completion, 

1562 'handler', self.handler and self.handler.__name__ or 'None', 

1563 ) 

1564 #@+node:ekr.20140818074502.18222: *3* ga.get_command 

1565 def get_command(self, s): 

1566 """Return the command part of a minibuffer contents s.""" 

1567 # #1121. 

1568 if ' ' in s: 

1569 return s[: s.find(' ')].strip() 

1570 return s 

1571 #@+node:ekr.20140818085719.18227: *3* ga.get_minibuffer_command_name 

1572 def get_minibuffer_command_name(self): 

1573 """Return the command name in the minibuffer.""" 

1574 s = self.get_label() 

1575 command = self.get_command(s) 

1576 tail = s[len(command) :] 

1577 return command, tail 

1578 #@+node:ekr.20140818074502.18221: *3* ga.is_command 

1579 def is_command(self, s): 

1580 """Return False if something, even a blank, follows a command.""" 

1581 # #1121: only ascii space terminates a command. 

1582 return ' ' not in s 

1583 #@+node:ekr.20140816165728.18959: *3* ga.show_tab_list & helper 

1584 def show_tab_list(self, tabList): 

1585 """Show the tab list in the log tab.""" 

1586 k = self.k 

1587 self.log.clearTab(self.tabName) 

1588 d = k.computeInverseBindingDict() 

1589 data, legend, n = [], False, 0 

1590 for commandName in tabList: 

1591 dataList = d.get(commandName, []) 

1592 if dataList: 

1593 for z in dataList: 

1594 pane, key = z 

1595 s1a = '' if pane in ('all:', 'button:') else f"{pane} " 

1596 s1b = k.prettyPrintKey(key) 

1597 s1 = s1a + s1b 

1598 s2 = self.command_source(commandName) 

1599 if s2 != ' ': 

1600 legend = True 

1601 s3 = commandName 

1602 data.append((s1, s2, s3),) 

1603 n = max(n, len(s1)) 

1604 else: 

1605 # Bug fix: 2017/03/26 

1606 data.append(('', ' ', commandName),) 

1607 aList = ['%*s %s %s' % (-n, z1, z2, z3) for z1, z2, z3 in data] 

1608 if legend: 

1609 aList.extend([ 

1610 '', 

1611 'legend:', 

1612 'G leoSettings.leo', 

1613 'M myLeoSettings.leo', 

1614 'L local .leo File', 

1615 ]) 

1616 g.es('', '\n'.join(aList), tabName=self.tabName) 

1617 #@+node:ekr.20150402034643.1: *4* ga.command_source 

1618 def command_source(self, commandName): 

1619 """ 

1620 Return the source legend of an @button/@command node. 

1621 'G' leoSettings.leo 

1622 'M' myLeoSettings.leo 

1623 'L' local .leo File 

1624 ' ' not an @command or @button node 

1625 """ 

1626 c = self.c 

1627 if commandName.startswith('@'): 

1628 d = c.commandsDict 

1629 func = d.get(commandName) 

1630 if hasattr(func, 'source_c'): 

1631 c2 = func.source_c 

1632 fn2 = c2.shortFileName().lower() 

1633 if fn2.endswith('myleosettings.leo'): 

1634 return 'M' 

1635 if fn2.endswith('leosettings.leo'): 

1636 return 'G' 

1637 return 'L' 

1638 return '?' 

1639 return ' ' 

1640 #@-others 

1641#@+node:ekr.20061031131434.74: ** class KeyHandlerClass 

1642class KeyHandlerClass: 

1643 """ 

1644 A class to support emacs-style commands. 

1645 c.k is an instance of this class. 

1646 """ 

1647 #@+others 

1648 #@+node:ekr.20061031131434.75: *3* k.Birth 

1649 #@+node:ekr.20061031131434.76: *4* k.__init__& helpers 

1650 def __init__(self, c): 

1651 """Create a key handler for c.""" 

1652 self.c = c 

1653 self.dispatchEvent = None 

1654 self.fnc = None # A singleton defined in k.finishCreate. 

1655 self.getArgInstance = None # A singleton defined in k.finishCreate. 

1656 self.inited = False # Set at end of finishCreate. 

1657 self.killedBindings = [] # A list of commands whose bindings have been set to None in the local file. 

1658 self.replace_meta_with_alt = False # True: (Mac only) swap Meta and Alt keys. 

1659 self.w = None # Will be None for NullGui. 

1660 # Generalize... 

1661 self.x_hasNumeric = ['sort-lines', 'sort-fields'] 

1662 self.altX_prompt = 'full-command: ' 

1663 # Access to data types defined in leoKeys.py 

1664 self.KeyStroke = g.KeyStroke 

1665 # Define all ivars... 

1666 self.defineExternallyVisibleIvars() 

1667 self.defineInternalIvars() 

1668 self.reloadSettings() 

1669 self.defineSingleLineCommands() 

1670 self.defineMultiLineCommands() 

1671 self.autoCompleter = AutoCompleterClass(self) 

1672 self.qcompleter = None # Set by AutoCompleter.start. 

1673 self.setDefaultUnboundKeyAction() 

1674 self.setDefaultEditingAction() 

1675 #@+node:ekr.20061031131434.78: *5* k.defineExternallyVisibleIvars 

1676 def defineExternallyVisibleIvars(self): 

1677 

1678 self.abbrevOn = False # True: abbreviations are on. 

1679 self.arg = '' # The value returned by k.getArg. 

1680 self.getArgEscapeFlag = False # True: the user escaped getArg in an unusual way. 

1681 self.getArgEscapes = [] 

1682 self.inputModeName = '' # The name of the input mode, or None. 

1683 self.modePrompt = '' # The mode promopt. 

1684 self.state = g.bunch(kind=None, n=None, handler=None) 

1685 

1686 # Remove ??? 

1687 self.givenArgs = [] # Args specified after the command name in k.simulateCommand. 

1688 self.functionTail = None # For commands that take minibuffer arguments. 

1689 #@+node:ekr.20061031131434.79: *5* k.defineInternalIvars 

1690 def defineInternalIvars(self): 

1691 """Define internal ivars of the KeyHandlerClass class.""" 

1692 self.abbreviationsDict = {} # Abbreviations created by @alias nodes. 

1693 # Previously defined bindings... 

1694 self.bindingsDict = {} # Keys are Tk key names, values are lists of BindingInfo objects. 

1695 # Previously defined binding tags. 

1696 self.bindtagsDict = {} # Keys are strings (the tag), values are 'True' 

1697 self.commandHistory = [] 

1698 # Up arrow will select commandHistory[commandIndex] 

1699 self.commandIndex = 0 # List/stack of previously executed commands. 

1700 # Keys are scope names: 'all','text',etc. or mode names. 

1701 # Values are dicts: keys are strokes, values are BindingInfo objects. 

1702 self.masterBindingsDict = {} 

1703 self.masterGuiBindingsDict = {} # Keys are strokes; value is True. 

1704 # Special bindings for k.fullCommand... 

1705 self.mb_copyKey = None 

1706 self.mb_pasteKey = None 

1707 self.mb_cutKey = None 

1708 # Keys whose bindings are computed by initSpecialIvars... 

1709 self.abortAllModesKey = None 

1710 self.autoCompleteForceKey = None 

1711 self.demoNextKey = None # New support for the demo.py plugin. 

1712 self.demoPrevKey = None # New support for the demo.py plugin. 

1713 # Used by k.masterKeyHandler... 

1714 self.stroke = None 

1715 self.mb_event = None 

1716 self.mb_history = [] 

1717 self.mb_help = False 

1718 self.mb_helpHandler = None 

1719 # Important: these are defined in k.defineExternallyVisibleIvars... 

1720 # self.getArgEscapes = [] 

1721 # self.getArgEscapeFlag 

1722 # For onIdleTime... 

1723 self.idleCount = 0 

1724 # For modes... 

1725 self.modeBindingsDict = {} 

1726 self.modeWidget = None 

1727 self.silentMode = False 

1728 #@+node:ekr.20080509064108.7: *5* k.defineMultiLineCommands 

1729 def defineMultiLineCommands(self): 

1730 k = self 

1731 k.multiLineCommandList = [ 

1732 # EditCommandsClass 

1733 'add-space-to-lines', 

1734 'add-tab-to-lines', 

1735 'back-page', 

1736 'back-page-extend-selection', 

1737 'back-paragraph', 

1738 'back-paragraph-extend-selection', 

1739 'back-sentence', 

1740 'back-sentence-extend-selection', 

1741 'backward-kill-paragraph', 

1742 'beginning-of-buffer', 

1743 'beginning-of-buffer-extend-selection', 

1744 'center-line', 

1745 'center-region', 

1746 'clean-all-lines', 

1747 'clean-lines', 

1748 'downcase-region', 

1749 'end-of-buffer', 

1750 'end-of-buffer-extend-selection', 

1751 'extend-to-paragraph', 

1752 'extend-to-sentence', 

1753 'fill-paragraph', 

1754 'fill-region', 

1755 'fill-region-as-paragraph', 

1756 'flush-lines', 

1757 'forward-page', 

1758 'forward-page-extend-selection', 

1759 'forward-paragraph', 

1760 'forward-paragraph-extend-selection', 

1761 'forward-sentence', 

1762 'forward-sentence-extend-selection', 

1763 'indent-relative', 

1764 'indent-rigidly', 

1765 'indent-to-comment-column', 

1766 'move-lines-down', 

1767 'move-lines-up', 

1768 'next-line', 

1769 'next-line-extend-selection', 

1770 'previous-line', 

1771 'previous-line-extend-selection', 

1772 'remove-blank-lines', 

1773 'remove-space-from-lines', 

1774 'remove-tab-from-lines', 

1775 'reverse-region', 

1776 'reverse-sort-lines', 

1777 'reverse-sort-lines-ignoring-case', 

1778 'scroll-down-half-page', 

1779 'scroll-down-line', 

1780 'scroll-down-page', 

1781 'scroll-up-half-page', 

1782 'scroll-up-line', 

1783 'scroll-up-page', 

1784 'simulate-begin-drag', 

1785 'simulate-end-drag', 

1786 'sort-columns', 

1787 'sort-fields', 

1788 'sort-lines', 

1789 'sort-lines-ignoring-case', 

1790 'split-line', 

1791 'tabify', 

1792 'transpose-lines', 

1793 'untabify', 

1794 'upcase-region', 

1795 # KeyHandlerCommandsClass 

1796 'repeat-complex-command', 

1797 # KillBufferCommandsClass 

1798 'backward-kill-sentence', 

1799 'kill-sentence', 

1800 'kill-region', 

1801 'kill-region-save', 

1802 # QueryReplaceCommandsClass 

1803 'query-replace', 

1804 'query-replace-regex', 

1805 # RectangleCommandsClass 

1806 'clear-rectangle', 

1807 'close-rectangle', 

1808 'delete-rectangle', 

1809 'kill-rectangle', 

1810 'open-rectangle', 

1811 'string-rectangle', 

1812 'yank-rectangle', 

1813 # SearchCommandsClass 

1814 'change', 

1815 'change-then-find', 

1816 'find-next', 

1817 'find-prev', 

1818 ] 

1819 #@+node:ekr.20080509064108.6: *5* k.defineSingleLineCommands 

1820 def defineSingleLineCommands(self): 

1821 k = self 

1822 # These commands can be executed in the minibuffer. 

1823 k.singleLineCommandList = [ 

1824 # EditCommandsClass 

1825 'back-to-indentation', 

1826 'back-to-home', # 2010/02/01 

1827 'back-char', 

1828 'back-char-extend-selection', 

1829 'back-word', 

1830 'back-word-extend-selection', 

1831 'backward-delete-char', 

1832 'backward-find-character', 

1833 'backward-find-character-extend-selection', 

1834 'beginning-of-line', 

1835 'beginning-of-line-extend-selection', 

1836 'capitalize-word', 

1837 'delete-char', 

1838 'delete-indentation', 

1839 'delete-spaces', 

1840 'downcase-word', 

1841 'end-of-line', 

1842 'end-of-line-extend-selection', 

1843 'escape', 

1844 'exchange-point-mark', 

1845 'extend-to-line', 

1846 'extend-to-word', 

1847 'find-character', 

1848 'find-character-extend-selection', 

1849 'find-word', 

1850 'find-word-in-line', 

1851 'forward-char', 

1852 'forward-char-extend-selection', 

1853 'forward-end-word', 

1854 'forward-end-word-extend-selection', 

1855 'forward-word', 

1856 'forward-word-extend-selection', 

1857 'insert-newline', 

1858 'insert-parentheses', 

1859 'move-past-close', 

1860 'move-past-close-extend-selection', 

1861 'newline-and-indent', 

1862 'select-all', 

1863 'transpose-chars', 

1864 'transpose-words', 

1865 'upcase-word', 

1866 # LeoFind class. 

1867 'start-search', # #2041. 

1868 # KeyHandlerCommandsClass 

1869 'full-command', # #2041. 

1870 # 'auto-complete', 

1871 # 'negative-argument', 

1872 # 'number-command', 

1873 # 'number-command-0', 

1874 # 'number-command-1', 

1875 # 'number-command-2', 

1876 # 'number-command-3', 

1877 # 'number-command-4', 

1878 # 'number-command-5', 

1879 # 'number-command-6', 

1880 # 'number-command-7', 

1881 # 'number-command-8', 

1882 # 'universal-argument', 

1883 # KillBufferCommandsClass 

1884 'backward-kill-word', 

1885 'kill-line', 

1886 'kill-word', 

1887 'kill-ws', 

1888 'yank', 

1889 'yank-pop', 

1890 'zap-to-character', 

1891 # leoCommands 

1892 'cut-text', 

1893 'copy-text', 

1894 'paste-text', 

1895 # MacroCommandsClass 

1896 'call-last-kbd-macro', 

1897 # search commands 

1898 # 'replace-string', # A special case so Shift-Ctrl-r will work after Ctrl-f. 

1899 'set-find-everywhere', # 2011/06/07 

1900 'set-find-node-only', # 2011/06/07 

1901 'set-find-suboutline-only', # 2011/06/07 

1902 'toggle-find-collapses_nodes', 

1903 'toggle-find-ignore-case-option', 

1904 'toggle-find-in-body-option', 

1905 'toggle-find-in-headline-option', 

1906 'toggle-find-mark-changes-option', 

1907 'toggle-find-mark-finds-option', 

1908 'toggle-find-regex-option', 

1909 'toggle-find-reverse-option', 

1910 'toggle-find-word-option', 

1911 'toggle-find-wrap-around-option', 

1912 ] 

1913 #@+node:ekr.20061031131434.80: *4* k.finishCreate 

1914 def finishCreate(self): 

1915 """ 

1916 Complete the construction of the keyHandler class. 

1917 c.commandsDict has been created when this is called. 

1918 """ 

1919 c, k = self.c, self 

1920 k.w = c.frame.miniBufferWidget # Will be None for NullGui. 

1921 k.fnc = FileNameChooser(c) # A singleton. Defined here so that c.k will exist. 

1922 k.getArgInstance = GetArg(c) # A singleton. Defined here so that c.k will exist. 

1923 # Important: This must be called this now, 

1924 # even though LM.load calls g.app.makeAllBindings later. 

1925 k.makeAllBindings() 

1926 k.initCommandHistory() 

1927 k.inited = True 

1928 k.setDefaultInputState() 

1929 k.resetLabel() 

1930 #@+node:ekr.20061101071425: *4* k.oops 

1931 def oops(self): 

1932 

1933 g.trace('Should be defined in subclass:', g.callers(4)) 

1934 #@+node:ekr.20120217070122.10479: *4* k.reloadSettings 

1935 def reloadSettings(self): 

1936 # Part 1: These were in the ctor. 

1937 c = self.c 

1938 getBool = c.config.getBool 

1939 getColor = c.config.getColor 

1940 self.enable_autocompleter = getBool('enable-autocompleter-initially') 

1941 self.enable_calltips = getBool('enable-calltips-initially') 

1942 self.ignore_unbound_non_ascii_keys = getBool('ignore-unbound-non-ascii-keys') 

1943 self.minibuffer_background_color = getColor( 

1944 'minibuffer-background-color') or 'lightblue' 

1945 self.minibuffer_foreground_color = getColor( 

1946 'minibuffer-foreground-color') or 'black' 

1947 self.minibuffer_warning_color = getColor( 

1948 'minibuffer-warning-color') or 'lightgrey' 

1949 self.minibuffer_error_color = getColor('minibuffer-error-color') or 'red' 

1950 self.replace_meta_with_alt = getBool('replace-meta-with-alt') 

1951 self.warn_about_redefined_shortcuts = getBool('warn-about-redefined-shortcuts') 

1952 # Has to be disabled (default) for AltGr support on Windows 

1953 self.enable_alt_ctrl_bindings = c.config.getBool('enable-alt-ctrl-bindings') 

1954 # Part 2: These were in finishCreate. 

1955 # Set mode colors used by k.setInputState. 

1956 bg = c.config.getColor('body-text-background-color') or 'white' 

1957 fg = c.config.getColor('body-text-foreground-color') or 'black' 

1958 self.command_mode_bg_color = getColor('command-mode-bg-color') or bg 

1959 self.command_mode_fg_color = getColor('command-mode-fg-color') or fg 

1960 self.insert_mode_bg_color = getColor('insert-mode-bg-color') or bg 

1961 self.insert_mode_fg_color = getColor('insert-mode-fg-color') or fg 

1962 self.overwrite_mode_bg_color = getColor('overwrite-mode-bg-color') or bg 

1963 self.overwrite_mode_fg_color = getColor('overwrite-mode-fg-color') or fg 

1964 self.unselected_body_bg_color = getColor('unselected-body-bg-color') or bg 

1965 self.unselected_body_fg_color = getColor('unselected-body-fg-color') or bg 

1966 #@+node:ekr.20110209093958.15413: *4* k.setDefaultEditingKeyAction 

1967 def setDefaultEditingAction(self): 

1968 c = self.c 

1969 action = c.config.getString('default-editing-state') or 'insert' 

1970 action.lower() 

1971 if action not in ('command', 'insert', 'overwrite'): 

1972 g.trace(f"ignoring default_editing_state: {action}") 

1973 action = 'insert' 

1974 self.defaultEditingAction = action 

1975 #@+node:ekr.20061031131434.82: *4* k.setDefaultUnboundKeyAction 

1976 def setDefaultUnboundKeyAction(self, allowCommandState=True): 

1977 c, k = self.c, self 

1978 defaultAction = c.config.getString('top-level-unbound-key-action') or 'insert' 

1979 defaultAction.lower() 

1980 if defaultAction == 'command' and not allowCommandState: 

1981 self.unboundKeyAction = 'insert' 

1982 elif defaultAction in ('command', 'insert', 'overwrite'): 

1983 self.unboundKeyAction = defaultAction 

1984 else: 

1985 g.trace(f"ignoring top_level_unbound_key_action setting: {defaultAction}") 

1986 self.unboundKeyAction = 'insert' 

1987 self.defaultUnboundKeyAction = self.unboundKeyAction 

1988 k.setInputState(self.defaultUnboundKeyAction) 

1989 #@+node:ekr.20061031131434.88: *3* k.Binding 

1990 #@+node:ekr.20061031131434.89: *4* k.bindKey & helpers 

1991 def bindKey(self, pane, shortcut, callback, commandName, modeFlag=False, tag=None): 

1992 """ 

1993 Bind the indicated shortcut (a Tk keystroke) to the callback. 

1994 

1995 No actual gui bindings are made: only entries in k.masterBindingsDict 

1996 and k.bindingsDict. 

1997 

1998 tag gives the source of the binding. 

1999 

2000 Return True if the binding was made successfully. 

2001 """ 

2002 k = self 

2003 if not shortcut: 

2004 # Don't use this method to undo bindings. 

2005 return False 

2006 if not k.check_bind_key(commandName, pane, shortcut): 

2007 return False 

2008 aList = k.bindingsDict.get(shortcut, []) 

2009 try: 

2010 if not shortcut: 

2011 stroke = None 

2012 elif g.isStroke(shortcut): 

2013 stroke = shortcut 

2014 assert stroke.s, stroke 

2015 else: 

2016 assert shortcut, g.callers() 

2017 stroke = g.KeyStroke(binding=shortcut) 

2018 bi = g.BindingInfo( 

2019 kind=tag, 

2020 pane=pane, 

2021 func=callback, 

2022 commandName=commandName, 

2023 stroke=stroke) 

2024 if shortcut: 

2025 k.bindKeyToDict(pane, shortcut, bi) 

2026 # Updates k.masterBindingsDict 

2027 if shortcut and not modeFlag: 

2028 aList = k.remove_conflicting_definitions( 

2029 aList, commandName, pane, shortcut) 

2030 # 2013/03/02: a real bug fix. 

2031 aList.append(bi) 

2032 if shortcut: 

2033 assert stroke 

2034 k.bindingsDict[stroke] = aList 

2035 return True 

2036 except Exception: # Could be a user error. 

2037 if g.unitTesting or not g.app.menuWarningsGiven: 

2038 g.es_print('exception binding', shortcut, 'to', commandName) 

2039 g.print_exception() 

2040 g.app.menuWarningsGiven = True 

2041 return False 

2042 

2043 bindShortcut = bindKey # For compatibility 

2044 #@+node:ekr.20120130074511.10228: *5* k.check_bind_key 

2045 def check_bind_key(self, commandName, pane, stroke): 

2046 """ 

2047 Return True if the binding of stroke to commandName for the given 

2048 pane can be made. 

2049 """ 

2050 # k = self 

2051 assert g.isStroke(stroke) 

2052 # Give warning and return if we try to bind to Enter or Leave. 

2053 for s in ('enter', 'leave'): 

2054 if stroke.lower().find(s) > -1: 

2055 g.warning('ignoring invalid key binding:', f"{commandName} = {stroke}") 

2056 return False 

2057 if pane.endswith('-mode'): 

2058 g.trace('oops: ignoring mode binding', stroke, commandName, g.callers()) 

2059 return False 

2060 return True 

2061 #@+node:ekr.20120130074511.10227: *5* k.kill_one_shortcut 

2062 def kill_one_shortcut(self, stroke): 

2063 """ 

2064 Update the *configuration* dicts so that c.config.getShortcut(name) 

2065 will return None for all names *presently* bound to the stroke. 

2066 """ 

2067 c = self.c 

2068 lm = g.app.loadManager 

2069 if 0: 

2070 # This does not fix 327: Create a way to unbind bindings 

2071 assert stroke in (None, 'None', 'none') or g.isStroke(stroke), repr(stroke) 

2072 else: 

2073 # A crucial shortcut: inverting and uninverting dictionaries is slow. 

2074 # Important: the comparison is valid regardless of the type of stroke. 

2075 if stroke in (None, 'None', 'none'): 

2076 return 

2077 assert g.isStroke(stroke), stroke 

2078 d = c.config.shortcutsDict 

2079 if d is None: 

2080 d = g.TypedDict( # was TypedDictOfLists. 

2081 name='empty shortcuts dict', 

2082 keyType=type('commandName'), 

2083 valType=g.BindingInfo, 

2084 ) 

2085 inv_d = lm.invert(d) 

2086 inv_d[stroke] = [] 

2087 c.config.shortcutsDict = lm.uninvert(inv_d) 

2088 #@+node:ekr.20061031131434.92: *5* k.remove_conflicting_definitions 

2089 def remove_conflicting_definitions(self, aList, commandName, pane, shortcut): 

2090 

2091 k = self 

2092 result = [] 

2093 for bi in aList: 

2094 if pane in ('button', 'all', bi.pane): 

2095 k.kill_one_shortcut(shortcut) 

2096 else: 

2097 result.append(bi) 

2098 return result 

2099 #@+node:ekr.20061031131434.93: *5* k.bindKeyToDict 

2100 def bindKeyToDict(self, pane, stroke, bi): 

2101 """Update k.masterBindingsDict for the stroke.""" 

2102 # New in Leo 4.4.1: Allow redefintions. 

2103 # Called from makeBindingsFromCommandsDict. 

2104 k = self 

2105 assert g.isStroke(stroke), stroke 

2106 d = k.masterBindingsDict.get(pane, {}) 

2107 d[stroke] = bi 

2108 k.masterBindingsDict[pane] = d 

2109 #@+node:ekr.20061031131434.94: *5* k.bindOpenWith 

2110 def bindOpenWith(self, d): 

2111 """Register an open-with command.""" 

2112 c, k = self.c, self 

2113 shortcut = d.get('shortcut') or '' 

2114 name = d.get('name') 

2115 # The first parameter must be event, and it must default to None. 

2116 

2117 def openWithCallback(event=None, c=c, d=d): 

2118 return c.openWith(d=d) 

2119 

2120 # Use k.registerCommand to set the shortcuts in the various binding dicts. 

2121 

2122 commandName = f"open-with-{name.lower()}" 

2123 k.registerCommand( 

2124 allowBinding=True, 

2125 commandName=commandName, 

2126 func=openWithCallback, 

2127 pane='all', 

2128 shortcut=shortcut, 

2129 ) 

2130 #@+node:ekr.20061031131434.95: *4* k.checkBindings 

2131 def checkBindings(self): 

2132 """ 

2133 Print warnings if commands do not have any @shortcut entry. 

2134 The entry may be `None`, of course.""" 

2135 c, k = self.c, self 

2136 if not c.config.getBool('warn-about-missing-settings'): 

2137 return 

2138 for name in sorted(c.commandsDict): 

2139 abbrev = k.abbreviationsDict.get(name) 

2140 key = c.frame.menu.canonicalizeMenuName(abbrev or name) 

2141 key = key.replace('&', '') 

2142 if not c.config.exists(key, 'shortcut'): 

2143 if abbrev: 

2144 g.trace(f"No shortcut for abbrev {name} -> {abbrev} = {key}") 

2145 else: 

2146 g.trace(f"No shortcut for {name} = {key}") 

2147 #@+node:ekr.20061031131434.97: *4* k.completeAllBindings 

2148 def completeAllBindings(self, w=None): 

2149 """ 

2150 Make an actual binding in *all* the standard places. 

2151 

2152 The event will go to k.masterKeyHandler as always, so nothing really changes. 

2153 except that k.masterKeyHandler will know the proper stroke. 

2154 """ 

2155 k = self 

2156 for stroke in k.bindingsDict: 

2157 assert g.isStroke(stroke), repr(stroke) 

2158 k.makeMasterGuiBinding(stroke, w=w) 

2159 #@+node:ekr.20061031131434.96: *4* k.completeAllBindingsForWidget 

2160 def completeAllBindingsForWidget(self, w): 

2161 """Make all a master gui binding for widget w.""" 

2162 k = self 

2163 for stroke in k.bindingsDict: 

2164 assert g.isStroke(stroke), repr(stroke) 

2165 k.makeMasterGuiBinding(stroke, w=w) 

2166 #@+node:ekr.20070218130238: *4* k.dumpMasterBindingsDict 

2167 def dumpMasterBindingsDict(self): 

2168 """Dump k.masterBindingsDict.""" 

2169 k = self 

2170 d = k.masterBindingsDict 

2171 g.pr('\nk.masterBindingsDict...\n') 

2172 for key in sorted(d): 

2173 g.pr(key, '-' * 40) 

2174 d2 = d.get(key) 

2175 for key2 in sorted(d2): 

2176 bi = d2.get(key2) 

2177 g.pr(f"{key2:20} {bi.commandName}") 

2178 #@+node:ekr.20061031131434.99: *4* k.initAbbrev & helper 

2179 def initAbbrev(self): 

2180 c = self.c 

2181 d = c.config.getAbbrevDict() 

2182 if d: 

2183 for key in d: 

2184 commandName = d.get(key) 

2185 if commandName.startswith('press-') and commandName.endswith('-button'): 

2186 pass # Must be done later in k.registerCommand. 

2187 else: 

2188 self.initOneAbbrev(commandName, key) 

2189 #@+node:ekr.20130924035029.12741: *5* k.initOneAbbrev 

2190 def initOneAbbrev(self, commandName, key): 

2191 """Enter key as an abbreviation for commandName in c.commandsDict.""" 

2192 c = self.c 

2193 if c.commandsDict.get(key): 

2194 g.trace('ignoring duplicate abbrev: %s', key) 

2195 else: 

2196 func = c.commandsDict.get(commandName) 

2197 if func: 

2198 c.commandsDict[key] = func 

2199 else: 

2200 g.warning('bad abbrev:', key, 'unknown command name:', commandName) 

2201 #@+node:ekr.20061031131434.101: *4* k.initSpecialIvars 

2202 def initSpecialIvars(self): 

2203 """Set ivars for special keystrokes from previously-existing bindings.""" 

2204 c, k = self.c, self 

2205 warn = c.config.getBool('warn-about-missing-settings') 

2206 for ivar, commandName in ( 

2207 ('abortAllModesKey', 'keyboard-quit'), 

2208 ('autoCompleteForceKey', 'auto-complete-force'), 

2209 ('demoNextKey', 'demo-next'), 

2210 ('demoPrevKey', 'demo-prev'), 

2211 ): 

2212 junk, aList = c.config.getShortcut(commandName) 

2213 aList, found = aList or [], False 

2214 for pane in ('text', 'all'): 

2215 for bi in aList: 

2216 if bi.pane == pane: 

2217 setattr(k, ivar, bi.stroke) 

2218 found = True 

2219 break 

2220 if not found and warn: 

2221 g.trace(f"no setting for {commandName}") 

2222 #@+node:ekr.20061031131434.98: *4* k.makeAllBindings 

2223 def makeAllBindings(self): 

2224 """Make all key bindings in all of Leo's panes.""" 

2225 k = self 

2226 k.bindingsDict = {} 

2227 k.addModeCommands() 

2228 k.makeBindingsFromCommandsDict() 

2229 k.initSpecialIvars() 

2230 k.initAbbrev() 

2231 k.completeAllBindings() 

2232 k.checkBindings() 

2233 #@+node:ekr.20061031131434.102: *4* k.makeBindingsFromCommandsDict 

2234 def makeBindingsFromCommandsDict(self): 

2235 """Add bindings for all entries in c.commandsDict.""" 

2236 c, k = self.c, self 

2237 d = c.commandsDict 

2238 # 

2239 # Step 1: Create d2. 

2240 # Keys are strokes. Values are lists of bi with bi.stroke == stroke. 

2241 d2 = g.TypedDict( # was TypedDictOfLists. 

2242 name='makeBindingsFromCommandsDict helper dict', 

2243 keyType=g.KeyStroke, 

2244 valType=g.BindingInfo, 

2245 ) 

2246 for commandName in sorted(d): 

2247 command = d.get(commandName) 

2248 key, aList = c.config.getShortcut(commandName) 

2249 for bi in aList: 

2250 # Important: bi.stroke is already canonicalized. 

2251 stroke = bi.stroke 

2252 bi.commandName = commandName 

2253 if stroke: 

2254 assert g.isStroke(stroke) 

2255 d2.add_to_list(stroke, bi) 

2256 # 

2257 # Step 2: make the bindings. 

2258 for stroke in sorted(d2.keys()): 

2259 aList2 = d2.get(stroke) 

2260 for bi in aList2: 

2261 commandName = bi.commandName 

2262 command = c.commandsDict.get(commandName) 

2263 tag = bi.kind 

2264 pane = bi.pane 

2265 if stroke and not pane.endswith('-mode'): 

2266 k.bindKey(pane, stroke, command, commandName, tag=tag) 

2267 #@+node:ekr.20061031131434.103: *4* k.makeMasterGuiBinding 

2268 def makeMasterGuiBinding(self, stroke, w=None): 

2269 """Make a master gui binding for stroke in pane w, or in all the standard widgets.""" 

2270 c, k = self.c, self 

2271 f = c.frame 

2272 if w: 

2273 widgets = [w] 

2274 else: 

2275 # New in Leo 4.5: we *must* make the binding in the binding widget. 

2276 bindingWidget = ( 

2277 f.tree 

2278 and hasattr(f.tree, 'bindingWidget') 

2279 and f.tree.bindingWidget 

2280 or None) 

2281 wrapper = f.body and hasattr(f.body, 'wrapper') and f.body.wrapper or None 

2282 canvas = f.tree and hasattr(f.tree, 'canvas') and f.tree.canvas or None 

2283 widgets = [c.miniBufferWidget, wrapper, canvas, bindingWidget] 

2284 for w in widgets: 

2285 if not w: 

2286 continue 

2287 # Make the binding only if no binding for the stroke exists in the widget. 

2288 aList = k.masterGuiBindingsDict.get(stroke, []) 

2289 if w not in aList: 

2290 aList.append(w) 

2291 k.masterGuiBindingsDict[stroke] = aList 

2292 #@+node:ekr.20150402111403.1: *3* k.Command history 

2293 #@+node:ekr.20150402111413.1: *4* k.addToCommandHistory 

2294 def addToCommandHistory(self, commandName): 

2295 """Add a name to the command history.""" 

2296 k = self 

2297 h = k.commandHistory 

2298 if commandName in h: 

2299 h.remove(commandName) 

2300 h.append(commandName) 

2301 k.commandIndex = None 

2302 #@+node:ekr.20150402165918.1: *4* k.commandHistoryDown 

2303 def commandHistoryFwd(self): 

2304 """ 

2305 Move down the Command History - fall off the bottom (return empty string) 

2306 if necessary 

2307 """ 

2308 k = self 

2309 h, i = k.commandHistory, k.commandIndex 

2310 if h: 

2311 commandName = '' 

2312 if i == len(h) - 1: 

2313 # fall off the bottom 

2314 i = None 

2315 elif i is not None: 

2316 # move to next down in list 

2317 i += 1 

2318 commandName = h[i] 

2319 k.commandIndex = i 

2320 k.setLabel(k.mb_prefix + commandName) 

2321 #@+node:ekr.20150402171131.1: *4* k.commandHistoryUp 

2322 def commandHistoryBackwd(self): 

2323 """ 

2324 Return the previous entry in the Command History - stay at the top 

2325 if we are there 

2326 """ 

2327 k = self 

2328 h, i = k.commandHistory, k.commandIndex 

2329 if h: 

2330 if i is None: 

2331 # first time in - set to last entry 

2332 i = len(h) - 1 

2333 elif i > 0: 

2334 i -= 1 

2335 commandName = h[i] 

2336 k.commandIndex = i 

2337 k.setLabel(k.mb_prefix + commandName) 

2338 #@+node:ekr.20150425143043.1: *4* k.initCommandHistory 

2339 def initCommandHistory(self): 

2340 """Init command history from @data command-history nodes.""" 

2341 k, c = self, self.c 

2342 aList = c.config.getData('history-list') or [] 

2343 for command in reversed(aList): 

2344 k.addToCommandHistory(command) 

2345 

2346 def resetCommandHistory(self): 

2347 """ reset the command history index to indicate that 

2348 we are pointing 'past' the last entry 

2349 """ 

2350 self.commandIndex = None 

2351 # 

2352 #@+node:ekr.20150402111935.1: *4* k.sortCommandHistory 

2353 def sortCommandHistory(self): 

2354 """Sort the command history.""" 

2355 k = self 

2356 k.commandHistory.sort() 

2357 k.commandIndex = None 

2358 #@+node:ekr.20061031131434.104: *3* k.Dispatching 

2359 #@+node:ekr.20061031131434.111: *4* k.fullCommand (alt-x) & helper 

2360 @cmd('full-command') 

2361 def fullCommand( 

2362 self, 

2363 event, 

2364 specialStroke=None, 

2365 specialFunc=None, 

2366 help=False, 

2367 helpHandler=None, 

2368 ): 

2369 """Handle 'full-command' (alt-x) mode.""" 

2370 try: 

2371 c, k = self.c, self 

2372 state = k.getState('full-command') 

2373 helpPrompt = 'Help for command: ' 

2374 c.check_event(event) 

2375 ch = char = event.char if event else '' 

2376 if state == 0: 

2377 k.mb_event = event # Save the full event for later. 

2378 k.setState('full-command', 1, handler=k.fullCommand) 

2379 prompt = helpPrompt if help else k.altX_prompt 

2380 k.setLabelBlue(prompt) 

2381 k.mb_help = help 

2382 k.mb_helpHandler = helpHandler 

2383 c.minibufferWantsFocus() 

2384 elif char == 'Ins' or k.isFKey(char): 

2385 pass 

2386 elif char == 'Escape': 

2387 k.keyboardQuit() 

2388 elif char == 'Down': 

2389 k.commandHistoryFwd() 

2390 elif char == 'Up': 

2391 k.commandHistoryBackwd() 

2392 elif char in ('\n', 'Return'): 

2393 # Fix bug 157: save and restore the selection. 

2394 w = k.mb_event and k.mb_event.w 

2395 if w and hasattr(w, 'hasSelection') and w.hasSelection(): 

2396 sel1, sel2 = w.getSelectionRange() 

2397 ins = w.getInsertPoint() 

2398 c.frame.log.deleteTab('Completion') 

2399 w.setSelectionRange(sel1, sel2, insert=ins) 

2400 else: 

2401 c.frame.log.deleteTab('Completion') 

2402 # 2016/04/27 

2403 if k.mb_help: 

2404 s = k.getLabel() 

2405 commandName = s[len(helpPrompt) :].strip() 

2406 k.clearState() 

2407 k.resetLabel() 

2408 if k.mb_helpHandler: 

2409 k.mb_helpHandler(commandName) 

2410 else: 

2411 s = k.getLabel(ignorePrompt=True) 

2412 commandName = s.strip() 

2413 ok = k.callAltXFunction(k.mb_event) 

2414 if ok: 

2415 k.addToCommandHistory(commandName) 

2416 elif char in ('\t', 'Tab'): 

2417 k.doTabCompletion(list(c.commandsDict.keys())) 

2418 c.minibufferWantsFocus() 

2419 elif char in ('\b', 'BackSpace'): 

2420 k.doBackSpace(list(c.commandsDict.keys())) 

2421 c.minibufferWantsFocus() 

2422 elif k.ignore_unbound_non_ascii_keys and len(ch) > 1: 

2423 if specialStroke: 

2424 g.trace(specialStroke) 

2425 specialFunc() 

2426 c.minibufferWantsFocus() 

2427 else: 

2428 # Clear the list, any other character besides tab indicates that a new prefix is in effect. 

2429 k.mb_tabList = [] 

2430 k.updateLabel(event) 

2431 k.mb_tabListPrefix = k.getLabel() 

2432 c.minibufferWantsFocus() 

2433 except Exception: 

2434 g.es_exception() 

2435 self.keyboardQuit() 

2436 #@+node:ekr.20061031131434.112: *5* k.callAltXFunction 

2437 def callAltXFunction(self, event): 

2438 """Call the function whose name is in the minibuffer.""" 

2439 c, k = self.c, self 

2440 k.mb_tabList = [] 

2441 commandName, tail = k.getMinibufferCommandName() 

2442 k.functionTail = tail 

2443 if commandName and commandName.isdigit(): 

2444 # The line number Easter Egg. 

2445 

2446 def func(event=None): 

2447 c.gotoCommands.find_file_line(n=int(commandName)) 

2448 

2449 else: 

2450 func = c.commandsDict.get(commandName) 

2451 if func: 

2452 # These must be done *after* getting the command. 

2453 k.clearState() 

2454 k.resetLabel() 

2455 if commandName != 'repeat-complex-command': 

2456 k.mb_history.insert(0, commandName) 

2457 w = event and event.widget 

2458 if hasattr(w, 'permanent') and not w.permanent: 

2459 # In a headline that is being edited. 

2460 c.endEditing() 

2461 c.bodyWantsFocusNow() 

2462 # Change the event widget so we don't refer to the to-be-deleted headline widget. 

2463 event.w = event.widget = c.frame.body.wrapper.widget 

2464 else: 

2465 c.widgetWantsFocusNow(event and event.widget) # So cut-text works, e.g. 

2466 try: 

2467 func(event) 

2468 except Exception: 

2469 g.es_exception() 

2470 return True 

2471 # Show possible completions if the command does not exist. 

2472 k.doTabCompletion(list(c.commandsDict.keys())) 

2473 return False 

2474 #@+node:ekr.20061031131434.114: *3* k.Externally visible commands 

2475 #@+node:ekr.20070613133500: *4* k.menuCommandKey 

2476 def menuCommandKey(self, event=None): 

2477 # This method must exist, but it never gets called. 

2478 pass 

2479 #@+node:ekr.20061031131434.119: *4* k.printBindings & helper 

2480 @cmd('show-bindings') 

2481 def printBindings(self, event=None): 

2482 """Print all the bindings presently in effect.""" 

2483 c, k = self.c, self 

2484 d = k.bindingsDict 

2485 tabName = 'Bindings' 

2486 c.frame.log.clearTab(tabName) 

2487 legend = '''\ 

2488 legend: 

2489 [ ] leoSettings.leo 

2490 [D] default binding 

2491 [F] loaded .leo File 

2492 [M] myLeoSettings.leo 

2493 [@] @mode, @button, @command 

2494 

2495 ''' 

2496 if not d: 

2497 return g.es('no bindings') 

2498 legend = textwrap.dedent(legend) 

2499 data = [] 

2500 for stroke in sorted(d): 

2501 assert g.isStroke(stroke), stroke 

2502 aList = d.get(stroke, []) 

2503 for bi in aList: 

2504 s1 = '' if bi.pane == 'all' else bi.pane 

2505 s2 = k.prettyPrintKey(stroke) 

2506 s3 = bi.commandName 

2507 s4 = bi.kind or '<no hash>' 

2508 data.append((s1, s2, s3, s4),) 

2509 # Print keys by type. 

2510 result = [] 

2511 result.append('\n' + legend) 

2512 for prefix in ( 

2513 'Alt+Ctrl+Shift', 'Alt+Ctrl', 'Alt+Shift', 'Alt', # 'Alt+Key': done by Alt. 

2514 'Ctrl+Meta+Shift', 'Ctrl+Meta', 'Ctrl+Shift', 'Ctrl', # Ctrl+Key: done by Ctrl. 

2515 'Meta+Key', 'Meta+Shift', 'Meta', 

2516 'Shift', 

2517 'F', # #1972 

2518 # Careful: longer prefixes must come before shorter prefixes. 

2519 ): 

2520 data2 = [] 

2521 for item in data: 

2522 s1, s2, s3, s4 = item 

2523 if s2.startswith(prefix): 

2524 data2.append(item) 

2525 result.append(f"{prefix} keys...\n") 

2526 self.printBindingsHelper(result, data2, prefix=prefix) 

2527 # Remove all the items in data2 from data. 

2528 # This must be done outside the iterator on data. 

2529 for item in data2: 

2530 data.remove(item) 

2531 # Print all plain bindings. 

2532 result.append('Plain keys...\n') 

2533 self.printBindingsHelper(result, data, prefix=None) 

2534 if not g.unitTesting: 

2535 g.es_print('', ''.join(result), tabName=tabName) 

2536 k.showStateAndMode() 

2537 return result # for unit test. 

2538 #@+node:ekr.20061031131434.120: *5* printBindingsHelper 

2539 def printBindingsHelper(self, result, data, prefix): 

2540 """Helper for k.printBindings""" 

2541 c, lm = self.c, g.app.loadManager 

2542 data.sort(key=lambda x: x[1]) 

2543 data2, n = [], 0 

2544 for pane, key, commandName, kind in data: 

2545 key = key.replace('+Key', '') 

2546 letter = lm.computeBindingLetter(c, kind) 

2547 pane = f"{pane if pane else 'all':4}: " 

2548 left = pane + key # pane and shortcut fields 

2549 n = max(n, len(left)) 

2550 data2.append((letter, left, commandName),) 

2551 for z in data2: 

2552 letter, left, commandName = z 

2553 result.append('%s %*s %s\n' % (letter, -n, left, commandName)) 

2554 if data: 

2555 result.append('\n') 

2556 #@+node:ekr.20120520174745.9867: *4* k.printButtons 

2557 @cmd('show-buttons') 

2558 def printButtons(self, event=None): 

2559 """Print all @button and @command commands, their bindings and their source.""" 

2560 c = self.c 

2561 tabName = '@buttons && @commands' 

2562 c.frame.log.clearTab(tabName) 

2563 

2564 def put(s): 

2565 g.es('', s, tabName=tabName) 

2566 

2567 data = [] 

2568 for aList in [c.config.getButtons(), c.config.getCommands()]: 

2569 for z in aList: 

2570 try: # #2536. 

2571 p, script = z # getCommands created the tuple. 

2572 except ValueError: 

2573 p, script, rclicks = z # getButtons created the tuple. 

2574 c = p.v.context 

2575 tag = 'M' if c.shortFileName().endswith('myLeoSettings.leo') else 'G' 

2576 data.append((p.h, tag),) 

2577 for aList in [g.app.config.atLocalButtonsList, g.app.config.atLocalCommandsList]: 

2578 for p in aList: 

2579 data.append((p.h, 'L'),) 

2580 result = [f"{z[1]} {z[0]}" for z in sorted(data)] 

2581 result.extend([ 

2582 '', 

2583 'legend:', 

2584 'G leoSettings.leo', 

2585 'L local .leo File', 

2586 'M myLeoSettings.leo', 

2587 ]) 

2588 put('\n'.join(result)) 

2589 #@+node:ekr.20061031131434.121: *4* k.printCommands 

2590 @cmd('show-commands') 

2591 def printCommands(self, event=None): 

2592 """Print all the known commands and their bindings, if any.""" 

2593 c, k = self.c, self 

2594 tabName = 'Commands' 

2595 c.frame.log.clearTab(tabName) 

2596 inverseBindingDict = k.computeInverseBindingDict() 

2597 data, n = [], 0 

2598 for commandName in sorted(c.commandsDict): 

2599 dataList = inverseBindingDict.get(commandName, [('', ''),]) 

2600 for z in dataList: 

2601 pane, key = z 

2602 pane = f"{pane} " if pane != 'all:' else '' 

2603 key = k.prettyPrintKey(key).replace('+Key', '') 

2604 s1 = pane + key 

2605 s2 = commandName 

2606 n = max(n, len(s1)) 

2607 data.append((s1, s2),) 

2608 # This isn't perfect in variable-width fonts. 

2609 lines = ['%*s %s\n' % (-n, z1, z2) for z1, z2 in data] 

2610 g.es_print('', ''.join(lines), tabName=tabName) 

2611 #@+node:tom.20220320235059.1: *4* k.printCommandsWithDocs 

2612 @cmd('show-commands-with-docs') 

2613 def printCommandsWithDocs(self, event=None): 

2614 """Show all the known commands and their bindings, if any.""" 

2615 c, k = self.c, self 

2616 tabName = 'List' 

2617 c.frame.log.clearTab(tabName) 

2618 inverseBindingDict = k.computeInverseBindingDict() 

2619 data = [] 

2620 for commandName in sorted(c.commandsDict): 

2621 dataList = inverseBindingDict.get(commandName, [('', ''),]) 

2622 for pane, key in dataList: 

2623 key = k.prettyPrintKey(key) 

2624 binding = pane + key 

2625 cmd = commandName.strip() 

2626 doc = f'{c.commandsDict.get(commandName).__doc__}' or '' 

2627 if doc == 'None': 

2628 doc = '' 

2629 # Formatting for multi-line docstring 

2630 if doc.count('\n') > 0: 

2631 doc = f'\n{doc}\n' 

2632 else: 

2633 doc = f' {doc}' 

2634 if doc.startswith('\n'): 

2635 doc.replace('\n', '', 1) 

2636 toolong = doc.count('\n') > 5 

2637 manylines = False 

2638 if toolong: 

2639 lines = doc.split('\n')[:4] 

2640 lines[-1] += ' ...\n' 

2641 doc = '\n'.join(lines) 

2642 manylines = True 

2643 n = min(2, len(binding)) 

2644 if manylines: 

2645 doc = textwrap.fill(doc, width=50, initial_indent=' ' * 4, 

2646 subsequent_indent=' ' * 4) 

2647 data.append((binding, cmd, doc)) 

2648 lines = ['[%*s] %s%s\n' % (-n, binding, cmd, doc) for binding, cmd, doc in data] 

2649 g.es(''.join(lines), tabName=tabName) 

2650 #@+node:ekr.20061031131434.122: *4* k.repeatComplexCommand 

2651 @cmd('repeat-complex-command') 

2652 def repeatComplexCommand(self, event): 

2653 """Repeat the previously executed minibuffer command.""" 

2654 k = self 

2655 # #2286: Always call k.fullCommand. 

2656 k.setState('getArg', 0, handler=k.fullCommand) 

2657 k.fullCommand(event) # #2334 

2658 if not k.mb_history: 

2659 k.mb_history = list(reversed(k.commandHistory)) 

2660 command = k.mb_history[0] if k.mb_history else '' 

2661 k.setLabelBlue(f"{k.altX_prompt}", protect=True) 

2662 k.extendLabel(command, select=False, protect=False) 

2663 #@+node:ekr.20061031131434.123: *4* k.set-xxx-State 

2664 @cmd('set-command-state') 

2665 def setCommandState(self, event): 

2666 """Enter the 'command' editing state.""" 

2667 k = self 

2668 k.setInputState('command', set_border=True) 

2669 # This command is also valid in headlines. 

2670 # k.c.bodyWantsFocus() 

2671 k.showStateAndMode() 

2672 

2673 @cmd('set-insert-state') 

2674 def setInsertState(self, event): 

2675 """Enter the 'insert' editing state.""" 

2676 k = self 

2677 k.setInputState('insert', set_border=True) 

2678 # This command is also valid in headlines. 

2679 # k.c.bodyWantsFocus() 

2680 k.showStateAndMode() 

2681 

2682 @cmd('set-overwrite-state') 

2683 def setOverwriteState(self, event): 

2684 """Enter the 'overwrite' editing state.""" 

2685 k = self 

2686 k.setInputState('overwrite', set_border=True) 

2687 # This command is also valid in headlines. 

2688 # k.c.bodyWantsFocus() 

2689 k.showStateAndMode() 

2690 #@+node:ekr.20061031131434.124: *4* k.toggle-input-state 

2691 @cmd('toggle-input-state') 

2692 def toggleInputState(self, event=None): 

2693 """The toggle-input-state command.""" 

2694 c, k = self.c, self 

2695 default = c.config.getString('top-level-unbound-key-action') or 'insert' 

2696 state = k.unboundKeyAction 

2697 if default == 'insert': 

2698 state = 'command' if state == 'insert' else 'insert' 

2699 elif default == 'overwrite': 

2700 state = 'command' if state == 'overwrite' else 'overwrite' 

2701 else: 

2702 state = 'insert' if state == 'command' else 'command' # prefer insert to overwrite. 

2703 k.setInputState(state) 

2704 k.showStateAndMode() 

2705 #@+node:ekr.20061031131434.125: *3* k.Externally visible helpers 

2706 #@+node:ekr.20140816165728.18968: *4* Wrappers for GetArg methods 

2707 # New in Leo 5.4 

2708 

2709 def getNextArg(self, handler): 

2710 """ 

2711 Get the next arg. For example, after a Tab in the find commands. 

2712 See the docstring for k.get1Arg for examples of its use. 

2713 """ 

2714 # Replace the current handler. 

2715 self.getArgInstance.after_get_arg_state = ('getarg', 1, handler) 

2716 self.c.minibufferWantsFocusNow() 

2717 

2718 # New in Leo 5.4 

2719 

2720 def get1Arg(self, event, handler, 

2721 prefix=None, tabList=None, completion=True, oneCharacter=False, 

2722 stroke=None, useMinibuffer=True 

2723 ): 

2724 #@+<< docstring for k.get1arg >> 

2725 #@+node:ekr.20161020031633.1: *5* << docstring for k.get1arg >> 

2726 """ 

2727 k.get1Arg: Handle the next character the user types when accumulating 

2728 a user argument from the minibuffer. Ctrl-G will abort this processing 

2729 at any time. 

2730 

2731 Commands should use k.get1Arg to get the first minibuffer argument and 

2732 k.getNextArg to get all other arguments. 

2733 

2734 Before going into the many details, let's look at some examples. This 

2735 code will work in any class having a 'c' ivar bound to a commander. 

2736 

2737 Example 1: get one argument from the user: 

2738 

2739 @g.command('my-command') 

2740 def myCommand(self, event): 

2741 k = self.c.k 

2742 k.setLabelBlue('prompt: ') 

2743 k.get1Arg(event, handler=self.myCommand1) 

2744 

2745 def myCommand1(self, event): 

2746 k = self.c.k 

2747 # k.arg contains the argument. 

2748 # Finish the command. 

2749 ... 

2750 # Reset the minibuffer. 

2751 k.clearState() 

2752 k.resetLabel() 

2753 k.showStateAndMode() 

2754 

2755 Example 2: get two arguments from the user: 

2756 

2757 @g.command('my-command') 

2758 def myCommand(self, event): 

2759 k = self.c.k 

2760 k.setLabelBlue('first prompt: ') 

2761 k.get1Arg(event, handler=self.myCommand1) 

2762 

2763 def myCommand1(self, event): 

2764 k = self.c.k 

2765 self.arg1 = k.arg 

2766 k.extendLabel(' second prompt: ', select=False, protect=True) 

2767 k.getNextArg(handler=self.myCommand2) 

2768 

2769 def myCommand2(self, event): 

2770 k = self.c.k 

2771 # k.arg contains second argument. 

2772 # Finish the command, using self.arg1 and k.arg. 

2773 ... 

2774 # Reset the minibuffer. 

2775 k.clearState() 

2776 k.resetLabel() 

2777 k.showStateAndMode() 

2778 

2779 k.get1Arg and k.getNextArg are a convenience methods. They simply pass 

2780 their arguments to the get_arg method of the singleton GetArg 

2781 instance. This docstring describes k.get1arg and k.getNextArg as if 

2782 they were the corresponding methods of the GetArg class. 

2783 

2784 k.get1Arg is a state machine. Logically, states are tuples (kind, n, 

2785 handler) though they aren't represented that way. When the state 

2786 machine in the GetArg class is active, the kind is 'getArg'. This 

2787 constant has special meaning to Leo's key-handling code. 

2788 

2789 The arguments to k.get1Arg are as follows: 

2790 

2791 event: The event passed to the command. 

2792 

2793 handler=None, An executable. k.get1arg calls handler(event) 

2794 when the user completes the argument by typing 

2795 <Return> or (sometimes) <tab>. 

2796 

2797 tabList=[]: A list of possible completions. 

2798 

2799 completion=True: True if completions are enabled. 

2800 

2801 oneCharacter=False: True if k.arg should be a single character. 

2802 

2803 stroke=None: The incoming key stroke. 

2804 

2805 useMinibuffer=True: True: put focus in the minibuffer while accumulating arguments. 

2806 False allows sort-lines, for example, to show the selection range. 

2807 

2808 """ 

2809 #@-<< docstring for k.get1arg >> 

2810 returnKind, returnState = None, None 

2811 assert handler, g.callers() 

2812 self.getArgInstance.get_arg(event, returnKind, returnState, handler, 

2813 tabList, completion, oneCharacter, stroke, useMinibuffer) 

2814 

2815 def getArg(self, event, 

2816 returnKind=None, returnState=None, handler=None, 

2817 prefix=None, tabList=None, completion=True, oneCharacter=False, 

2818 stroke=None, useMinibuffer=True 

2819 ): 

2820 """Convenience method mapping k.getArg to ga.get_arg.""" 

2821 self.getArgInstance.get_arg(event, returnKind, returnState, handler, 

2822 tabList, completion, oneCharacter, stroke, useMinibuffer) 

2823 

2824 def doBackSpace(self, tabList, completion=True): 

2825 """Convenience method mapping k.doBackSpace to ga.do_back_space.""" 

2826 self.getArgInstance.do_back_space(tabList, completion) 

2827 

2828 def doTabCompletion(self, tabList): 

2829 """Convenience method mapping k.doTabCompletion to ga.do_tab.""" 

2830 self.getArgInstance.do_tab(tabList) 

2831 

2832 def getMinibufferCommandName(self): 

2833 """ 

2834 Convenience method mapping k.getMinibufferCommandName to 

2835 ga.get_minibuffer_command_name. 

2836 """ 

2837 return self.getArgInstance.get_minibuffer_command_name() 

2838 #@+node:ekr.20061031131434.130: *4* k.keyboardQuit 

2839 @cmd('keyboard-quit') 

2840 def keyboardQuit(self, event=None, setFocus=True): 

2841 """Clears the state and the minibuffer label.""" 

2842 c, k = self.c, self 

2843 if g.app.quitting: 

2844 return 

2845 c.endEditing() 

2846 # Completely clear the mode. 

2847 if setFocus: 

2848 c.frame.log.deleteTab('Mode') 

2849 c.frame.log.hideTab('Completion') 

2850 if k.inputModeName: 

2851 k.endMode() 

2852 # Complete clear the state. 

2853 k.state.kind = None 

2854 k.state.n = None 

2855 k.clearState() 

2856 k.resetLabel() 

2857 if setFocus: 

2858 c.bodyWantsFocus() 

2859 # At present, only the auto-completer suppresses this. 

2860 k.setDefaultInputState() 

2861 if c.vim_mode and c.vimCommands: 

2862 c.vimCommands.reset(setFocus=setFocus) 

2863 else: 

2864 # This was what caused the unwanted scrolling. 

2865 k.showStateAndMode(setFocus=setFocus) 

2866 k.resetCommandHistory() 

2867 #@+node:ekr.20061031131434.126: *4* k.manufactureKeyPressForCommandName (only for unit tests!) 

2868 def manufactureKeyPressForCommandName(self, w, commandName): 

2869 """ 

2870 Implement a command by passing a keypress to the gui. 

2871 

2872 **Only unit tests use this method.** 

2873 """ 

2874 c, k = self.c, self 

2875 # Unit tests do not ordinarily read settings files. 

2876 stroke = k.getStrokeForCommandName(commandName) 

2877 if stroke is None: 

2878 # Create the stroke and binding info data. 

2879 stroke = g.KeyStroke('Ctrl+F1') 

2880 bi = g.BindingInfo( 

2881 kind='manufactured-binding', 

2882 commandName=commandName, 

2883 func=None, 

2884 pane='all', 

2885 stroke=stroke, 

2886 ) 

2887 # Make the binding! 

2888 # k.masterBindingsDict keys: scope names; values: masterBindingDicts (3) 

2889 # Interior masterBindingDicts: Keys are strokes; values are BindingInfo objects. 

2890 d = k.masterBindingsDict 

2891 d2 = d.get('all', {}) 

2892 d2[stroke] = bi 

2893 d['all'] = d2 

2894 assert g.isStroke(stroke), (commandName, stroke.__class__.__name__) 

2895 shortcut = stroke.s 

2896 shortcut = g.checkUnicode(shortcut) 

2897 if shortcut and w: 

2898 g.app.gui.set_focus(c, w) 

2899 g.app.gui.event_generate(c, None, shortcut, w) 

2900 else: 

2901 message = f"no shortcut for {commandName}" 

2902 if g.unitTesting: 

2903 raise AttributeError(message) 

2904 g.error(message) 

2905 #@+node:ekr.20071212104050: *4* k.overrideCommand 

2906 def overrideCommand(self, commandName, func): 

2907 # Override entries in c.k.masterBindingsDict 

2908 k = self 

2909 d = k.masterBindingsDict 

2910 for key in d: 

2911 d2 = d.get(key) 

2912 for key2 in d2: 

2913 bi = d2.get(key2) 

2914 if bi.commandName == commandName: 

2915 bi.func = func 

2916 d2[key2] = bi 

2917 #@+node:ekr.20061031131434.131: *4* k.registerCommand 

2918 def registerCommand(self, commandName, func, 

2919 allowBinding=False, 

2920 pane='all', 

2921 shortcut=None, # Must be None unless allowBindings is True. 

2922 **kwargs 

2923 ): 

2924 """ 

2925 Make the function available as a minibuffer command. 

2926 

2927 You can wrap any method in a callback function, so the 

2928 restriction to functions is not significant. 

2929 

2930 Ignore the 'shortcut' arg unless 'allowBinding' is True. 

2931 

2932 Only k.bindOpenWith and the mod_scripting.py plugin should set 

2933 allowBinding. 

2934 """ 

2935 c, k = self.c, self 

2936 if not func: 

2937 g.es_print('Null func passed to k.registerCommand\n', commandName) 

2938 return 

2939 f = c.commandsDict.get(commandName) 

2940 if f and f.__name__ != func.__name__: 

2941 g.trace('redefining', commandName, f, '->', func) 

2942 c.commandsDict[commandName] = func 

2943 # Warn about deprecated arguments. 

2944 if shortcut and not allowBinding: 

2945 g.es_print('The "shortcut" keyword arg to k.registerCommand will be ignored') 

2946 g.es_print('Called from', g.callers()) 

2947 shortcut = None 

2948 for arg, val in kwargs.items(): 

2949 if val is not None: 

2950 g.es_print(f'The "{arg}" keyword arg to k.registerCommand is deprecated') 

2951 g.es_print('Called from', g.callers()) 

2952 # Make requested bindings, even if a warning has been given. 

2953 # This maintains strict compatibility with existing plugins and scripts. 

2954 k.registerCommandShortcut( 

2955 commandName=commandName, 

2956 func=func, 

2957 pane=pane, 

2958 shortcut=shortcut, 

2959 ) 

2960 #@+node:ekr.20171124043747.1: *4* k.registerCommandShortcut 

2961 def registerCommandShortcut(self, commandName, func, pane, shortcut): 

2962 """ 

2963 Register a shortcut for the a command. 

2964 

2965 **Important**: Bindings created here from plugins can not be overridden. 

2966 This includes @command and @button bindings created by mod_scripting.py. 

2967 """ 

2968 c, k = self.c, self 

2969 is_local = c.shortFileName() not in ('myLeoSettings.leo', 'leoSettings.leo') 

2970 assert not g.isStroke(shortcut) 

2971 if shortcut: 

2972 stroke = g.KeyStroke(binding=shortcut) if shortcut else None 

2973 elif commandName.lower() == 'shortcut': # Causes problems. 

2974 stroke = None 

2975 elif is_local: 

2976 # 327: Don't get defaults when handling a local file. 

2977 stroke = None 

2978 else: 

2979 # Try to get a stroke from leoSettings.leo. 

2980 stroke = None 

2981 junk, aList = c.config.getShortcut(commandName) 

2982 for bi in aList: 

2983 if bi.stroke and not bi.pane.endswith('-mode'): 

2984 stroke = bi.stroke 

2985 pane = bi.pane # 2015/05/11. 

2986 break 

2987 if stroke: 

2988 k.bindKey(pane, stroke, func, commandName, tag='register-command') # Must be a stroke. 

2989 k.makeMasterGuiBinding(stroke) # Must be a stroke. 

2990 # Fixup any previous abbreviation to press-x-button commands. 

2991 if commandName.startswith('press-') and commandName.endswith('-button'): 

2992 d = c.config.getAbbrevDict() # Keys are full command names, values are abbreviations. 

2993 if commandName in list(d.values()): 

2994 for key in d: 

2995 if d.get(key) == commandName: 

2996 c.commandsDict[key] = c.commandsDict.get(commandName) 

2997 break 

2998 #@+node:ekr.20061031131434.127: *4* k.simulateCommand 

2999 def simulateCommand(self, commandName, event=None): 

3000 """Execute a Leo command by name.""" 

3001 c = self.c 

3002 if not event: 

3003 # Create a default key event. 

3004 event = g.app.gui.create_key_event(c) 

3005 c.doCommandByName(commandName, event) 

3006 #@+node:ekr.20140813052702.18203: *4* k.getFileName 

3007 def getFileName(self, event, callback=None, 

3008 filterExt=None, prompt='Enter File Name: ', tabName='Dired' 

3009 ): 

3010 """Get a file name from the minibuffer.""" 

3011 k = self 

3012 k.fnc.get_file_name(event, callback, filterExt, prompt, tabName) 

3013 #@+node:ekr.20061031131434.145: *3* k.Master event handlers 

3014 #@+node:ekr.20061031131434.146: *4* k.masterKeyHandler & helpers 

3015 def masterKeyHandler(self, event): 

3016 """The master key handler for almost all key bindings.""" 

3017 trace = 'keys' in g.app.debug 

3018 c, k = self.c, self 

3019 # Setup... 

3020 if trace: 

3021 g.trace(repr(k.state.kind), repr(event.char), repr(event.stroke)) 

3022 k.checkKeyEvent(event) 

3023 k.setEventWidget(event) 

3024 k.traceVars(event) 

3025 # Order is very important here... 

3026 if k.isSpecialKey(event): 

3027 return 

3028 if k.doKeyboardQuit(event): 

3029 return 

3030 if k.doDemo(event): 

3031 return 

3032 if k.doMode(event): 

3033 return 

3034 if k.doVim(event): 

3035 return 

3036 if k.doBinding(event): 

3037 return 

3038 # Handle abbreviations. 

3039 if k.abbrevOn and c.abbrevCommands.expandAbbrev(event, event.stroke): 

3040 return 

3041 # Handle the character given by event *without* 

3042 # executing any command that might be bound to it. 

3043 c.insertCharFromEvent(event) 

3044 #@+node:ekr.20200524151214.1: *5* Setup... 

3045 #@+node:ekr.20180418040158.1: *6* k.checkKeyEvent 

3046 def checkKeyEvent(self, event): 

3047 """Perform sanity checks on the incoming event.""" 

3048 # These assert's should be safe, because eventFilter 

3049 # calls k.masterKeyHandler inside a try/except block. 

3050 c = self.c 

3051 assert event is not None 

3052 c.check_event(event) 

3053 assert hasattr(event, 'char') 

3054 assert hasattr(event, 'stroke') 

3055 if not hasattr(event, 'widget'): 

3056 event.widget = None 

3057 assert g.isStrokeOrNone(event.stroke) 

3058 if event: 

3059 assert event.stroke.s not in g.app.gui.ignoreChars, repr(event.stroke.s) 

3060 # A continuous unit test, better than "@test k.isPlainKey". 

3061 #@+node:ekr.20180418034305.1: *6* k.setEventWidget 

3062 def setEventWidget(self, event): 

3063 """ 

3064 A hack: redirect the event to the text part of the log. 

3065 """ 

3066 c = self.c 

3067 w = event.widget 

3068 w_name = c.widget_name(w) 

3069 if w_name.startswith('log'): 

3070 event.widget = c.frame.log.logCtrl 

3071 #@+node:ekr.20180418031417.1: *6* k.traceVars 

3072 def traceVars(self, event): 

3073 

3074 trace = False and not g.unitTesting 

3075 if not trace: 

3076 return 

3077 k = self 

3078 char = event.char 

3079 state = k.state.kind 

3080 stroke = event.stroke 

3081 g.trace( 

3082 f"stroke: {stroke!r}, " 

3083 f"char: {char!r}, " 

3084 f"state: {state}, " 

3085 f"state2: {k.unboundKeyAction}") 

3086 #@+node:ekr.20180418031118.1: *5* 1. k.isSpecialKey 

3087 def isSpecialKey(self, event): 

3088 """Return True if char is a special key.""" 

3089 if not event: 

3090 # An empty event is not an error. 

3091 return False 

3092 # Fix #917. 

3093 if len(event.char) > 1 and not event.stroke.s: 

3094 # stroke.s was cleared, but not event.char. 

3095 return True 

3096 return event.char in g.app.gui.ignoreChars 

3097 #@+node:ekr.20180418024449.1: *5* 2. k.doKeyboardQuit 

3098 def doKeyboardQuit(self, event): 

3099 """ 

3100 A helper for k.masterKeyHandler: Handle keyboard-quit logic. 

3101 

3102 return True if k.masterKeyHandler should return. 

3103 """ 

3104 c, k = self.c, self 

3105 stroke = getattr(event, 'stroke', None) 

3106 if k.abortAllModesKey and stroke and stroke == k.abortAllModesKey: 

3107 if getattr(c, 'screenCastController', None): 

3108 c.screenCastController.quit() 

3109 c.doCommandByName('keyboard-quit', event) 

3110 return True 

3111 return False 

3112 #@+node:ekr.20180418023827.1: *5* 3. k.doDemo 

3113 def doDemo(self, event): 

3114 """ 

3115 Support the demo.py plugin. 

3116 Return True if k.masterKeyHandler should return. 

3117 """ 

3118 k = self 

3119 stroke = event.stroke 

3120 demo = getattr(g.app, 'demo', None) 

3121 if not demo: 

3122 return False 

3123 # 

3124 # Shortcut everything so that demo-next or demo-prev won't alter of our ivars. 

3125 if k.demoNextKey and stroke == k.demoNextKey: 

3126 if demo.trace: 

3127 g.trace('demo-next', stroke) 

3128 demo.next_command() 

3129 return True 

3130 if k.demoPrevKey and stroke == k.demoPrevKey: 

3131 if demo.trace: 

3132 g.trace('demo-prev', stroke) 

3133 demo.prev_command() 

3134 return True 

3135 return False 

3136 #@+node:ekr.20091230094319.6244: *5* 4. k.doMode & helpers 

3137 def doMode(self, event): 

3138 """ 

3139 Handle mode bindings. 

3140 Return True if k.masterKeyHandler should return. 

3141 """ 

3142 # #1757: Leo's default vim bindings make heavy use of modes. 

3143 # Retain these traces! 

3144 trace = 'keys' in g.app.debug 

3145 k = self 

3146 state = k.state.kind 

3147 stroke = event.stroke 

3148 if not k.inState(): 

3149 return False 

3150 # First, honor minibuffer bindings for all except user modes. 

3151 if state == 'input-shortcut': 

3152 k.handleInputShortcut(event, stroke) 

3153 if trace: 

3154 g.trace(state, 'k.handleInputShortcut', stroke) 

3155 return True 

3156 if state in ( 

3157 'getArg', 'getFileName', 'full-command', 'auto-complete', 'vim-mode' 

3158 ): 

3159 if k.handleMiniBindings(event, state, stroke): 

3160 if trace: 

3161 g.trace(state, 'k.handleMiniBindings', stroke) 

3162 return True 

3163 # Second, honor general modes. 

3164 if state == 'getArg': 

3165 # New in Leo 5.8: Only call k.getArg for keys it can handle. 

3166 if k.isPlainKey(stroke): 

3167 k.getArg(event, stroke=stroke) 

3168 if trace: 

3169 g.trace(state, 'k.isPlain: getArg', stroke) 

3170 return True 

3171 if stroke.s in ('Escape', 'Tab', 'BackSpace'): 

3172 k.getArg(event, stroke=stroke) 

3173 if trace: 

3174 g.trace(state, f"{stroke.s!r}: getArg", stroke) 

3175 return True 

3176 return False 

3177 if state in ('getFileName', 'get-file-name'): 

3178 k.getFileName(event) 

3179 if trace: 

3180 g.trace(state, 'k.getFileName', stroke) 

3181 return True 

3182 if state in ('full-command', 'auto-complete'): 

3183 # Do the default state action. Calls end-command. 

3184 val = k.callStateFunction(event) 

3185 if val != 'do-standard-keys': 

3186 handler = k.state.handler and k.state.handler.__name__ or '<no handler>' 

3187 if trace: 

3188 g.trace(state, 'k.callStateFunction:', handler, stroke) 

3189 return True 

3190 return False 

3191 # Third, pass keys to user modes. 

3192 d = k.masterBindingsDict.get(state) 

3193 if d: 

3194 assert g.isStrokeOrNone(stroke) 

3195 bi = d.get(stroke) 

3196 if bi: 

3197 # Bound keys continue the mode. 

3198 k.generalModeHandler(event, 

3199 commandName=bi.commandName, 

3200 func=bi.func, 

3201 modeName=state, 

3202 nextMode=bi.nextMode) 

3203 if trace: 

3204 g.trace(state, 'k.generalModeHandler', stroke) 

3205 return True 

3206 # Unbound keys end mode. 

3207 k.endMode() 

3208 return False 

3209 # Fourth, call the state handler. 

3210 handler = k.getStateHandler() 

3211 if handler: 

3212 handler(event) 

3213 if trace: 

3214 handler_name = handler and handler.__name__ or '<no handler>' 

3215 g.trace(state, 'handler:', handler_name, stroke) 

3216 return True 

3217 #@+node:ekr.20061031131434.108: *6* k.callStateFunction 

3218 def callStateFunction(self, event): 

3219 """Call the state handler associated with this event.""" 

3220 k = self 

3221 ch = event.char 

3222 # 

3223 # Defensive programming 

3224 if not k.state.kind: 

3225 return None 

3226 if not k.state.handler: 

3227 g.error('callStateFunction: no state function for', k.state.kind) 

3228 return None 

3229 # 

3230 # Handle auto-completion before checking for unbound keys. 

3231 if k.state.kind == 'auto-complete': 

3232 # k.auto_completer_state_handler returns 'do-standard-keys' for control keys. 

3233 val = k.state.handler(event) 

3234 return val 

3235 # 

3236 # Ignore unbound non-ascii keys. 

3237 if ( 

3238 k.ignore_unbound_non_ascii_keys and 

3239 len(ch) == 1 and 

3240 ch and ch not in ('\b', '\n', '\r', '\t') and 

3241 (ord(ch) < 32 or ord(ch) > 128) 

3242 ): 

3243 return None 

3244 # 

3245 # Call the state handler. 

3246 val = k.state.handler(event) 

3247 return val 

3248 #@+node:ekr.20061031131434.152: *6* k.handleMiniBindings 

3249 def handleMiniBindings(self, event, state, stroke): 

3250 """Find and execute commands bound to the event.""" 

3251 k = self 

3252 # 

3253 # Special case for bindings handled in k.getArg: 

3254 if state == 'full-command' and stroke in ('Up', 'Down'): 

3255 return False 

3256 # 

3257 # Ignore other special keys in the minibuffer. 

3258 if state in ('getArg', 'full-command'): 

3259 if stroke in ( 

3260 '\b', 'BackSpace', 

3261 '\r', 'Linefeed', 

3262 '\n', 'Return', 

3263 '\t', 'Tab', 

3264 'Escape', 

3265 ): 

3266 return False 

3267 if k.isFKey(stroke): 

3268 return False 

3269 # 

3270 # Ignore autocompletion state. 

3271 if state.startswith('auto-'): 

3272 return False 

3273 # 

3274 # Ignore plain key binding in the minibuffer. 

3275 if not stroke or k.isPlainKey(stroke): 

3276 return False 

3277 # 

3278 # Get the command, based on the pane. 

3279 for pane in ('mini', 'all', 'text'): 

3280 result = k.handleMinibufferHelper(event, pane, state, stroke) 

3281 assert result in ('continue', 'found', 'ignore') 

3282 if result == 'ignore': 

3283 return False # Let getArg handle it. 

3284 if result == 'found': 

3285 # Do not call k.keyboardQuit here! 

3286 return True 

3287 # 

3288 # No binding exists. 

3289 return False 

3290 #@+node:ekr.20180418114300.1: *7* k.handleMinibufferHelper 

3291 def handleMinibufferHelper(self, event, pane, state, stroke): 

3292 """ 

3293 Execute a pane binding in the minibuffer. 

3294 Return 'continue', 'ignore', 'found' 

3295 """ 

3296 c, k = self.c, self 

3297 d = k.masterBindingsDict.get(pane) 

3298 if not d: 

3299 return 'continue' 

3300 bi = d.get(stroke) 

3301 if not bi: 

3302 return 'continue' 

3303 assert bi.stroke == stroke, f"bi: {bi} stroke: {stroke}" 

3304 # Ignore the replace-string command in the minibuffer. 

3305 if bi.commandName == 'replace-string' and state == 'getArg': 

3306 return 'ignore' 

3307 # Execute this command. 

3308 if bi.commandName not in k.singleLineCommandList: 

3309 k.keyboardQuit() 

3310 else: 

3311 c.minibufferWantsFocus() 

3312 c.doCommandByName(bi.commandName, event) 

3313 # Careful: the command could exit. 

3314 if c.exists and not k.silentMode: 

3315 # Use the state *after* executing the command. 

3316 if k.state.kind: 

3317 c.minibufferWantsFocus() 

3318 else: 

3319 c.bodyWantsFocus() 

3320 return 'found' 

3321 #@+node:vitalije.20170708161511.1: *6* k.handleInputShortcut 

3322 def handleInputShortcut(self, event, stroke): 

3323 c, k, p, u = self.c, self, self.c.p, self.c.undoer 

3324 k.clearState() 

3325 if p.h.startswith(('@shortcuts', '@mode')): 

3326 # line of text in body 

3327 w = c.frame.body.wrapper 

3328 before, sel, after = w.getInsertLines() 

3329 m = k._cmd_handle_input_pattern.search(sel) 

3330 assert m # edit-shortcut was invoked on a malformed body line 

3331 sel = f"{m.group(0)} {stroke.s}" 

3332 udata = u.beforeChangeNodeContents(p) 

3333 pos = w.getYScrollPosition() 

3334 i = len(before) 

3335 j = max(i, len(before) + len(sel) - 1) 

3336 w.setAllText(before + sel + after) 

3337 w.setSelectionRange(i, j, insert=j) 

3338 w.setYScrollPosition(pos) 

3339 u.afterChangeNodeContents(p, 'change shortcut', udata) 

3340 cmdname = m.group(0).rstrip('= ') 

3341 k.editShortcut_do_bind_helper(stroke, cmdname) 

3342 return 

3343 if p.h.startswith(('@command', '@button')): 

3344 udata = u.beforeChangeNodeContents(p) 

3345 cmd = p.h.split('@key', 1)[0] 

3346 p.h = f"{cmd} @key={stroke.s}" 

3347 u.afterChangeNodeContents(p, 'change shortcut', udata) 

3348 try: 

3349 cmdname = cmd.split(' ', 1)[1].strip() 

3350 k.editShortcut_do_bind_helper(stroke, cmdname) 

3351 except IndexError: 

3352 pass 

3353 return 

3354 # this should never happen 

3355 g.error('not in settings node shortcut') 

3356 #@+node:vitalije.20170709151653.1: *7* k.isInShortcutBodyLine 

3357 _cmd_handle_input_pattern = re.compile(r'[A-Za-z0-9_\-]+\s*=') 

3358 

3359 def isInShortcutBodyLine(self): 

3360 c, k = self.c, self 

3361 p = c.p 

3362 if p.h.startswith(('@shortcuts', '@mode')): 

3363 # line of text in body 

3364 w = c.frame.body 

3365 before, sel, after = w.getInsertLines() 

3366 m = k._cmd_handle_input_pattern.search(sel) 

3367 return bool(m) 

3368 return p.h.startswith(('@command', '@button')) 

3369 #@+node:vitalije.20170709151658.1: *7* k.isEditShortcutSensible 

3370 def isEditShortcutSensible(self): 

3371 c, k = self.c, self 

3372 p = c.p 

3373 return p.h.startswith(('@command', '@button')) or k.isInShortcutBodyLine() 

3374 #@+node:vitalije.20170709202924.1: *7* k.editShortcut_do_bind_helper 

3375 def editShortcut_do_bind_helper(self, stroke, cmdname): 

3376 c, k = self.c, self 

3377 cmdfunc = c.commandsDict.get(cmdname) 

3378 if cmdfunc: 

3379 k.bindKey('all', stroke, cmdfunc, cmdname) 

3380 g.es('bound', stroke, 'to command', cmdname) 

3381 #@+node:ekr.20180418025241.1: *5* 5. k.doVim 

3382 def doVim(self, event): 

3383 """ 

3384 Handle vim mode. 

3385 Return True if k.masterKeyHandler should return. 

3386 """ 

3387 trace = all(z in g.app.debug for z in ('keys', 'verbose')) 

3388 c = self.c 

3389 if c.vim_mode and c.vimCommands: 

3390 # The "acceptance methods" in leoVim.py return True 

3391 # if vim node has completely handled the key. 

3392 # Otherwise, processing in k.masterKeyHandler continues. 

3393 ok = c.vimCommands.do_key(event) 

3394 if trace: 

3395 g.trace('do_key returns', ok, repr(event and event.stroke)) 

3396 return ok 

3397 return False 

3398 #@+node:ekr.20180418033838.1: *5* 6. k.doBinding & helpers 

3399 def doBinding(self, event): 

3400 """ 

3401 Attempt to find a binding for the event's stroke. 

3402 If found, execute the command and return True 

3403 Otherwise, return False 

3404 """ 

3405 trace = 'keys' in g.app.debug 

3406 c, k = self.c, self 

3407 # 

3408 # Experimental special case: 

3409 # Inserting a '.' always invokes the auto-completer. 

3410 # The auto-completer just inserts a '.' if it isn't enabled. 

3411 stroke = event.stroke 

3412 if ( 

3413 stroke.s == '.' 

3414 and k.isPlainKey(stroke) 

3415 and self.unboundKeyAction in ('insert', 'overwrite') 

3416 ): 

3417 c.doCommandByName('auto-complete', event) 

3418 return True 

3419 # 

3420 # Use getPaneBindings for *all* keys. 

3421 bi = k.getPaneBinding(event) 

3422 # 

3423 # #327: Ignore killed bindings. 

3424 if bi and bi.commandName in k.killedBindings: 

3425 return False 

3426 # 

3427 # Execute the command if the binding exists. 

3428 if bi: 

3429 # A superb trace. !s gives shorter trace. 

3430 if trace: 

3431 g.trace(f"{event.stroke!s} {bi.commandName}") 

3432 c.doCommandByName(bi.commandName, event) 

3433 return True 

3434 # 

3435 # No binding exists. 

3436 return False 

3437 #@+node:ekr.20091230094319.6240: *6* k.getPaneBinding & helper 

3438 def getPaneBinding(self, event): 

3439 c, k, state = self.c, self, self.unboundKeyAction 

3440 stroke, w = event.stroke, event.w 

3441 if not g.assert_is(stroke, g.KeyStroke): 

3442 return None 

3443 # #1757: Always insert plain keys in the body. 

3444 # Valid because mode bindings have already been handled. 

3445 if ( 

3446 k.isPlainKey(stroke) 

3447 and w == c.frame.body.widget 

3448 and state in ('insert', 'overwrite') 

3449 ): 

3450 return None 

3451 for key, name in ( 

3452 # Order here is similar to bindtags order. 

3453 ('command', None), 

3454 ('insert', None), 

3455 ('overwrite', None), 

3456 ('button', None), 

3457 ('body', 'body'), 

3458 ('text', 'head'), # Important: text bindings in head before tree bindings. 

3459 ('tree', 'head'), 

3460 ('tree', 'canvas'), 

3461 ('log', 'log'), 

3462 ('text', 'log'), 

3463 ('text', None), 

3464 ('all', None), 

3465 ): 

3466 bi = k.getBindingHelper(key, name, stroke, w) 

3467 if bi: 

3468 return bi 

3469 return None 

3470 #@+node:ekr.20180418105228.1: *7* getPaneBindingHelper 

3471 def getBindingHelper(self, key, name, stroke, w): 

3472 """Find a binding for the widget with the given name.""" 

3473 c, k = self.c, self 

3474 # 

3475 # Return if the pane's name doesn't match the event's widget. 

3476 state = k.unboundKeyAction 

3477 w_name = c.widget_name(w) 

3478 pane_matches = ( 

3479 name and w_name.startswith(name) or 

3480 key in ('command', 'insert', 'overwrite') and state == key or 

3481 key in ('text', 'all') and g.isTextWrapper(w) or 

3482 key in ('button', 'all') 

3483 ) 

3484 if not pane_matches: 

3485 return None 

3486 # 

3487 # Return if there is no binding at all. 

3488 d = k.masterBindingsDict.get(key, {}) 

3489 if not d: 

3490 return None 

3491 bi = d.get(stroke) 

3492 if not bi: 

3493 return None 

3494 # 

3495 # Ignore previous/next-line commands while editing headlines. 

3496 if ( 

3497 key == 'text' and 

3498 name == 'head' and 

3499 bi.commandName in ('previous-line', 'next-line') 

3500 ): 

3501 return None 

3502 # 

3503 # The binding has been found. 

3504 return bi 

3505 #@+node:ekr.20160409035115.1: *6* k.searchTree 

3506 def searchTree(self, char): 

3507 """Search all visible nodes for a headline starting with stroke.""" 

3508 if not char: 

3509 return 

3510 c = self.c 

3511 if not c.config.getBool('plain-key-outline-search'): 

3512 return 

3513 

3514 def match(p): 

3515 """Return True if p contains char.""" 

3516 s = p.h.lower() if char.islower() else p.h 

3517 return s.find(char) > -1 

3518 

3519 # Start at c.p, then retry everywhere. 

3520 

3521 for p in (c.p, c.rootPosition()): 

3522 p = p.copy() 

3523 if p == c.p and match(p): 

3524 p.moveToVisNext(c) 

3525 while p: 

3526 if match(p): 

3527 c.selectPosition(p) 

3528 c.redraw() 

3529 return 

3530 p.moveToVisNext(c) 

3531 

3532 # Too confusing for the user. 

3533 # re_pat = re.compile(r'^@(\w)+[ \t](.+)') 

3534 

3535 # def match(p, pattern): 

3536 # s = p.h.lower() 

3537 # if pattern: 

3538 # m = pattern.search(s) 

3539 # found = (s.startswith(char) or 

3540 # m and m.group(2).lower().startswith(char)) 

3541 # else: 

3542 # found = s.find(char) > -1 

3543 # if found: 

3544 # c.selectPosition(p) 

3545 # c.redraw() 

3546 # return found 

3547 #@+node:ekr.20061031170011.3: *3* k.Minibuffer 

3548 # These may be overridden, but this code is now gui-independent. 

3549 #@+node:ekr.20061031170011.9: *4* k.extendLabel 

3550 def extendLabel(self, s, select=False, protect=False): 

3551 

3552 c, k, w = self.c, self, self.w 

3553 if not (w and s): 

3554 return 

3555 c.widgetWantsFocusNow(w) 

3556 w.insert('end', s) 

3557 if select: 

3558 i, j = k.getEditableTextRange() 

3559 w.setSelectionRange(i, j, insert=j) 

3560 if protect: 

3561 k.protectLabel() 

3562 #@+node:ekr.20061031170011.13: *4* k.getEditableTextRange 

3563 def getEditableTextRange(self): 

3564 k, w = self, self.w 

3565 s = w.getAllText() 

3566 i = len(k.mb_prefix) 

3567 j = len(s) 

3568 return i, j 

3569 #@+node:ekr.20061031170011.5: *4* k.getLabel 

3570 def getLabel(self, ignorePrompt=False): 

3571 k, w = self, self.w 

3572 if not w: 

3573 return '' 

3574 s = w.getAllText() 

3575 if ignorePrompt: 

3576 return s[len(k.mb_prefix) :] 

3577 return s or '' 

3578 #@+node:ekr.20080408060320.791: *4* k.killLine 

3579 def killLine(self, protect=True): 

3580 k = self 

3581 w = k.w 

3582 s = w.getAllText() 

3583 s = s[: len(k.mb_prefix)] 

3584 w.setAllText(s) 

3585 n = len(s) 

3586 w.setSelectionRange(n, n, insert=n) 

3587 if protect: 

3588 k.mb_prefix = s 

3589 #@+node:ekr.20061031131434.135: *4* k.minibufferWantsFocus 

3590 # def minibufferWantsFocus(self): 

3591 # c = self.c 

3592 # c.widgetWantsFocus(c.miniBufferWidget) 

3593 #@+node:ekr.20061031170011.6: *4* k.protectLabel 

3594 def protectLabel(self): 

3595 k, w = self, self.w 

3596 if not w: 

3597 return 

3598 k.mb_prefix = w.getAllText() 

3599 #@+node:ekr.20061031170011.7: *4* k.resetLabel 

3600 def resetLabel(self): 

3601 """Reset the minibuffer label.""" 

3602 k = self 

3603 c, w = k.c, k.w 

3604 k.setLabelGrey('') 

3605 k.mb_prefix = '' 

3606 if w: 

3607 w.setSelectionRange(0, 0, insert=0) 

3608 state = k.unboundKeyAction 

3609 if c.vim_mode and c.vimCommands: 

3610 c.vimCommands.show_status() 

3611 else: 

3612 k.setLabelBlue(label=f"{state.capitalize()} State") 

3613 #@+node:ekr.20080408060320.790: *4* k.selectAll 

3614 def selectAll(self): 

3615 """Select all the user-editable text of the minibuffer.""" 

3616 w = self.w 

3617 i, j = self.getEditableTextRange() 

3618 w.setSelectionRange(i, j, insert=j) 

3619 #@+node:ekr.20061031170011.8: *4* k.setLabel 

3620 def setLabel(self, s, protect=False): 

3621 """Set the label of the minibuffer.""" 

3622 c, k, w = self.c, self, self.w 

3623 if w: 

3624 # Support for the curses gui. 

3625 if hasattr(g.app.gui, 'set_minibuffer_label'): 

3626 g.app.gui.set_minibuffer_label(c, s) 

3627 w.setAllText(s) 

3628 n = len(s) 

3629 w.setSelectionRange(n, n, insert=n) 

3630 if protect: 

3631 k.mb_prefix = s 

3632 #@+node:ekr.20061031170011.10: *4* k.setLabelBlue 

3633 def setLabelBlue(self, label, protect=True): 

3634 """Set the minibuffer label.""" 

3635 k, w = self, self.w 

3636 if hasattr(g.app.gui, 'set_minibuffer_label'): 

3637 g.app.gui.set_minibuffer_label(self.c, label) 

3638 elif w: 

3639 w.setStyleClass('') # normal state, not warning or error 

3640 if label is not None: 

3641 k.setLabel(label, protect=protect) 

3642 #@+node:ekr.20061031170011.11: *4* k.setLabelGrey 

3643 def setLabelGrey(self, label=None): 

3644 k, w = self, self.w 

3645 if not w: 

3646 return 

3647 w.setStyleClass('minibuffer_warning') 

3648 if label is not None: 

3649 k.setLabel(label) 

3650 

3651 setLabelGray = setLabelGrey 

3652 #@+node:ekr.20080510153327.2: *4* k.setLabelRed 

3653 def setLabelRed(self, label=None, protect=False): 

3654 k, w = self, self.w 

3655 if not w: 

3656 return 

3657 w.setStyleClass('minibuffer_error') 

3658 if label is not None: 

3659 k.setLabel(label, protect) 

3660 #@+node:ekr.20140822051549.18298: *4* k.setStatusLabel 

3661 def setStatusLabel(self, s): 

3662 """ 

3663 Set the label to s. 

3664 

3665 Use k.setStatusLabel, not k.setLael, to report the status of a Leo 

3666 command. This allows the option to use g.es instead of the minibuffer 

3667 to report status. 

3668 """ 

3669 k = self 

3670 k.setLabel(s, protect=False) 

3671 #@+node:ekr.20061031170011.12: *4* k.updateLabel 

3672 def updateLabel(self, event): 

3673 """ 

3674 Mimic what would happen with the keyboard and a Text editor 

3675 instead of plain accumulation. 

3676 """ 

3677 c, k, w = self.c, self, self.w 

3678 if not event: 

3679 return 

3680 ch, stroke = event.char, event.stroke 

3681 if ch in "\n\r": 

3682 return 

3683 if stroke and not k.isPlainKey(stroke): 

3684 return # #2041. 

3685 c.widgetWantsFocusNow(w) 

3686 i, j = w.getSelectionRange() 

3687 ins = w.getInsertPoint() 

3688 if i != j: 

3689 w.delete(i, j) 

3690 if ch == '\b': 

3691 s = w.getAllText() 

3692 if len(s) > len(k.mb_prefix): 

3693 w.delete(i - 1) 

3694 i -= 1 

3695 else: 

3696 w.insert(ins, ch) 

3697 i = ins + 1 

3698 #@+node:ekr.20120208064440.10190: *3* k.Modes 

3699 #@+node:ekr.20061031131434.100: *4* k.addModeCommands (enterModeCallback) 

3700 def addModeCommands(self): 

3701 """Add commands created by @mode settings to c.commandsDict.""" 

3702 c, k = self.c, self 

3703 d = g.app.config.modeCommandsDict # Keys are command names: enter-x-mode. 

3704 # Create the callback functions and update c.commandsDict. 

3705 for key in d.keys(): 

3706 # pylint: disable=cell-var-from-loop 

3707 

3708 def enterModeCallback(event=None, name=key): 

3709 k.enterNamedMode(event, name) 

3710 

3711 c.commandsDict[key] = enterModeCallback 

3712 #@+node:ekr.20061031131434.157: *4* k.badMode 

3713 def badMode(self, modeName): 

3714 k = self 

3715 k.clearState() 

3716 if modeName.endswith('-mode'): 

3717 modeName = modeName[:-5] 

3718 k.setLabelGrey(f"@mode {modeName} is not defined (or is empty)") 

3719 #@+node:ekr.20061031131434.158: *4* k.createModeBindings 

3720 def createModeBindings(self, modeName, d, w): 

3721 """Create mode bindings for the named mode using dictionary d for w, a text widget.""" 

3722 c, k = self.c, self 

3723 assert d.name().endswith('-mode') 

3724 for commandName in d.keys(): 

3725 if commandName in ('*entry-commands*', '*command-prompt*'): 

3726 # These are special-purpose dictionary entries. 

3727 continue 

3728 func = c.commandsDict.get(commandName) 

3729 if not func: 

3730 g.es_print('no such command:', commandName, 'Referenced from', modeName) 

3731 continue 

3732 aList = d.get(commandName, []) 

3733 for bi in aList: 

3734 stroke = bi.stroke 

3735 # Important: bi.val is canonicalized. 

3736 if stroke and stroke not in ('None', 'none', None): 

3737 assert g.isStroke(stroke) 

3738 k.makeMasterGuiBinding(stroke) 

3739 # Create the entry for the mode in k.masterBindingsDict. 

3740 # Important: this is similar, but not the same as k.bindKeyToDict. 

3741 # Thus, we should **not** call k.bindKey here! 

3742 d2 = k.masterBindingsDict.get(modeName, {}) 

3743 d2[stroke] = g.BindingInfo( 

3744 kind=f"mode<{modeName}>", 

3745 commandName=commandName, 

3746 func=func, 

3747 nextMode=bi.nextMode, 

3748 stroke=stroke) 

3749 k.masterBindingsDict[modeName] = d2 

3750 #@+node:ekr.20120208064440.10179: *4* k.endMode 

3751 def endMode(self): 

3752 c, k = self.c, self 

3753 w = g.app.gui.get_focus(c) 

3754 if w: 

3755 c.frame.log.deleteTab('Mode') # Changes focus to the body pane 

3756 k.inputModeName = None 

3757 k.clearState() 

3758 k.resetLabel() 

3759 k.showStateAndMode() # Restores focus. 

3760 if w: 

3761 c.widgetWantsFocusNow(w) 

3762 #@+node:ekr.20061031131434.160: *4* k.enterNamedMode 

3763 def enterNamedMode(self, event, commandName): 

3764 c, k = self.c, self 

3765 modeName = commandName[6:] 

3766 c.inCommand = False # Allow inner commands in the mode. 

3767 k.generalModeHandler(event, modeName=modeName) 

3768 #@+node:ekr.20061031131434.161: *4* k.exitNamedMode 

3769 @cmd('exit-named-mode') 

3770 def exitNamedMode(self, event=None): 

3771 """Exit an input mode.""" 

3772 k = self 

3773 if k.inState(): 

3774 k.endMode() 

3775 k.showStateAndMode() 

3776 #@+node:ekr.20120208064440.10199: *4* k.generalModeHandler 

3777 def generalModeHandler(self, event, 

3778 commandName=None, 

3779 func=None, 

3780 modeName=None, 

3781 nextMode=None, 

3782 prompt=None 

3783 ): 

3784 """Handle a mode defined by an @mode node in leoSettings.leo.""" 

3785 c, k = self.c, self 

3786 state = k.getState(modeName) 

3787 if state == 0: 

3788 k.inputModeName = modeName 

3789 k.modePrompt = prompt or modeName 

3790 k.modeWidget = event and event.widget 

3791 k.setState(modeName, 1, handler=k.generalModeHandler) 

3792 self.initMode(event, modeName) 

3793 # Careful: k.initMode can execute commands that will destroy a commander. 

3794 if g.app.quitting or not c.exists: 

3795 return 

3796 if not k.silentMode: 

3797 if c.config.getBool('showHelpWhenEnteringModes'): 

3798 k.modeHelp(event) 

3799 else: 

3800 c.frame.log.hideTab('Mode') 

3801 elif not func: 

3802 g.trace('No func: improper key binding') 

3803 else: 

3804 if commandName == 'mode-help': 

3805 func(event) 

3806 else: 

3807 self.endMode() 

3808 # New in 4.4.1 b1: pass an event describing the original widget. 

3809 if event: 

3810 event.w = event.widget = k.modeWidget 

3811 else: 

3812 event = g.app.gui.create_key_event(c, w=k.modeWidget) 

3813 func(event) 

3814 if g.app.quitting or not c.exists: 

3815 pass 

3816 elif nextMode in (None, 'none'): 

3817 # Do *not* clear k.inputModeName or the focus here. 

3818 # func may have put us in *another* mode. 

3819 pass 

3820 elif nextMode == 'same': 

3821 silent = k.silentMode 

3822 k.setState(modeName, 1, handler=k.generalModeHandler) 

3823 self.reinitMode(modeName) # Re-enter this mode. 

3824 k.silentMode = silent 

3825 else: 

3826 k.silentMode = False # All silent modes must do --> set-silent-mode. 

3827 self.initMode(event, nextMode) # Enter another mode. 

3828 #@+node:ekr.20061031131434.163: *4* k.initMode 

3829 def initMode(self, event, modeName): 

3830 

3831 c, k = self.c, self 

3832 if not modeName: 

3833 g.trace('oops: no modeName') 

3834 return 

3835 d = g.app.config.modeCommandsDict.get('enter-' + modeName) 

3836 if not d: 

3837 self.badMode(modeName) 

3838 return 

3839 k.modeBindingsDict = d 

3840 bi = d.get('*command-prompt*') 

3841 prompt = bi.kind if bi else modeName 

3842 k.inputModeName = modeName 

3843 k.silentMode = False 

3844 aList = d.get('*entry-commands*', []) 

3845 if aList: 

3846 for bi in aList: 

3847 commandName = bi.commandName 

3848 k.simulateCommand(commandName) 

3849 # Careful, the command can kill the commander. 

3850 if g.app.quitting or not c.exists: 

3851 return 

3852 # New in Leo 4.5: a startup command can immediately transfer to another mode. 

3853 if commandName.startswith('enter-'): 

3854 return 

3855 # Create bindings after we know whether we are in silent mode. 

3856 w = k.modeWidget if k.silentMode else k.w 

3857 k.createModeBindings(modeName, d, w) 

3858 k.showStateAndMode(prompt=prompt) 

3859 #@+node:ekr.20061031131434.165: *4* k.modeHelp & helper 

3860 @cmd('mode-help') 

3861 def modeHelp(self, event): 

3862 """ 

3863 The mode-help command. 

3864 

3865 A possible convention would be to bind <Tab> to this command in most modes, 

3866 by analogy with tab completion. 

3867 """ 

3868 c, k = self.c, self 

3869 c.endEditing() 

3870 if k.inputModeName: 

3871 d = g.app.config.modeCommandsDict.get('enter-' + k.inputModeName) 

3872 k.modeHelpHelper(d) 

3873 if not k.silentMode: 

3874 c.minibufferWantsFocus() 

3875 #@+node:ekr.20061031131434.166: *5* modeHelpHelper 

3876 def modeHelpHelper(self, d): 

3877 c, k = self.c, self 

3878 tabName = 'Mode' 

3879 c.frame.log.clearTab(tabName) 

3880 data, n = [], 0 

3881 for key in sorted(d.keys()): 

3882 if key in ('*entry-commands*', '*command-prompt*'): 

3883 pass 

3884 else: 

3885 aList = d.get(key) 

3886 for bi in aList: 

3887 stroke = bi.stroke 

3888 if stroke not in (None, 'None'): 

3889 s1 = key 

3890 s2 = k.prettyPrintKey(stroke) 

3891 n = max(n, len(s1)) 

3892 data.append((s1, s2),) 

3893 data.sort() 

3894 modeName = k.inputModeName.replace('-', ' ') 

3895 if modeName.endswith('mode'): 

3896 modeName = modeName[:-4].strip() 

3897 prompt = d.get('*command-prompt*') 

3898 if prompt: 

3899 g.es('', f"{prompt.kind.strip()}\n\n", tabName=tabName) 

3900 else: 

3901 g.es('', f"{modeName} mode\n\n", tabName=tabName) 

3902 # This isn't perfect in variable-width fonts. 

3903 for s1, s2 in data: 

3904 g.es('', '%*s %s' % (n, s1, s2), tabName=tabName) 

3905 #@+node:ekr.20061031131434.164: *4* k.reinitMode 

3906 def reinitMode(self, modeName): 

3907 k = self 

3908 d = k.modeBindingsDict 

3909 k.inputModeName = modeName 

3910 w = k.modeWidget if k.silentMode else k.w 

3911 k.createModeBindings(modeName, d, w) 

3912 if k.silentMode: 

3913 k.showStateAndMode() 

3914 else: 

3915 # Do not set the status line here. 

3916 k.setLabelBlue(modeName + ': ') # ,protect=True) 

3917 #@+node:ekr.20061031131434.181: *3* k.Shortcuts & bindings 

3918 #@+node:ekr.20061031131434.176: *4* k.computeInverseBindingDict 

3919 def computeInverseBindingDict(self): 

3920 k = self 

3921 d = {} # keys are minibuffer command names, values are shortcuts. 

3922 for stroke in k.bindingsDict: 

3923 assert g.isStroke(stroke), repr(stroke) 

3924 aList = k.bindingsDict.get(stroke, []) 

3925 for bi in aList: 

3926 shortcutList = k.bindingsDict.get(bi.commandName, []) 

3927 bi_list = k.bindingsDict.get( 

3928 stroke, g.BindingInfo(kind='dummy', pane='all')) 

3929 # Important: only bi.pane is required below. 

3930 for bi in bi_list: 

3931 pane = f"{bi.pane}:" 

3932 data = (pane, stroke) 

3933 if data not in shortcutList: 

3934 shortcutList.append(data) 

3935 d[bi.commandName] = shortcutList 

3936 return d 

3937 #@+node:ekr.20061031131434.179: *4* k.getShortcutForCommandName 

3938 def getStrokeForCommandName(self, commandName): 

3939 c, k = self.c, self 

3940 command = c.commandsDict.get(commandName) 

3941 if command: 

3942 for stroke, aList in k.bindingsDict.items(): 

3943 for bi in aList: 

3944 if bi.commandName == commandName: 

3945 return stroke 

3946 return None 

3947 #@+node:ekr.20090518072506.8494: *4* k.isFKey 

3948 def isFKey(self, stroke): 

3949 # k = self 

3950 if not stroke: 

3951 return False 

3952 assert isinstance(stroke, str) or g.isStroke(stroke) 

3953 s = stroke.s if g.isStroke(stroke) else stroke 

3954 s = s.lower() 

3955 return s.startswith('f') and len(s) <= 3 and s[1:].isdigit() 

3956 #@+node:ekr.20061031131434.182: *4* k.isPlainKey 

3957 def isPlainKey(self, stroke): 

3958 """Return true if the shortcut refers to a plain (non-Alt,non-Ctl) key.""" 

3959 if not stroke: 

3960 return False 

3961 if not g.isStroke(stroke): 

3962 # Happens during unit tests. 

3963 stroke = g.KeyStroke(stroke) 

3964 # 

3965 # altgr combos (Alt+Ctrl) are always plain keys 

3966 # g.KeyStroke does not handle this, because it has no "c" ivar. 

3967 # 

3968 if stroke.isAltCtrl() and not self.enable_alt_ctrl_bindings: 

3969 return True 

3970 return stroke.isPlainKey() 

3971 #@+node:ekr.20061031131434.191: *4* k.prettyPrintKey 

3972 def prettyPrintKey(self, stroke, brief=False): 

3973 

3974 if not stroke: 

3975 return '' 

3976 if not g.assert_is(stroke, g.KeyStroke): 

3977 return stroke 

3978 return stroke.prettyPrint() 

3979 #@+node:ekr.20110606004638.16929: *4* k.stroke2char 

3980 def stroke2char(self, stroke): 

3981 """ 

3982 Convert a stroke to an (insertable) char. 

3983 This method allows Leo to use strokes everywhere. 

3984 """ 

3985 if not stroke: 

3986 return '' 

3987 if not g.isStroke(stroke): 

3988 # vim commands pass a plain key. 

3989 stroke = g.KeyStroke(stroke) 

3990 return stroke.toInsertableChar() 

3991 #@+node:ekr.20061031131434.193: *3* k.States 

3992 #@+node:ekr.20061031131434.194: *4* k.clearState 

3993 def clearState(self): 

3994 """Clear the key handler state.""" 

3995 k = self 

3996 k.state.kind = None 

3997 k.state.n = None 

3998 k.state.handler = None 

3999 #@+node:ekr.20061031131434.196: *4* k.getState 

4000 def getState(self, kind): 

4001 k = self 

4002 val = k.state.n if k.state.kind == kind else 0 

4003 return val 

4004 #@+node:ekr.20061031131434.195: *4* k.getStateHandler 

4005 def getStateHandler(self): 

4006 return self.state.handler 

4007 #@+node:ekr.20061031131434.197: *4* k.getStateKind 

4008 def getStateKind(self): 

4009 return self.state.kind 

4010 #@+node:ekr.20061031131434.198: *4* k.inState 

4011 def inState(self, kind=None): 

4012 k = self 

4013 if kind: 

4014 return k.state.kind == kind and k.state.n is not None 

4015 return k.state.kind and k.state.n is not None 

4016 #@+node:ekr.20080511122507.4: *4* k.setDefaultInputState 

4017 def setDefaultInputState(self): 

4018 k = self 

4019 state = k.defaultUnboundKeyAction 

4020 k.setInputState(state) 

4021 #@+node:ekr.20110209093958.15411: *4* k.setEditingState 

4022 def setEditingState(self): 

4023 k = self 

4024 state = k.defaultEditingAction 

4025 k.setInputState(state) 

4026 #@+node:ekr.20061031131434.133: *4* k.setInputState 

4027 def setInputState(self, state, set_border=False): 

4028 k = self 

4029 k.unboundKeyAction = state 

4030 #@+node:ekr.20061031131434.199: *4* k.setState 

4031 def setState(self, kind, n, handler=None): 

4032 

4033 k = self 

4034 if kind and n is not None: 

4035 k.state.kind = kind 

4036 k.state.n = n 

4037 if handler: 

4038 k.state.handler = handler 

4039 else: 

4040 k.clearState() 

4041 # k.showStateAndMode() 

4042 #@+node:ekr.20061031131434.192: *4* k.showStateAndMode 

4043 def showStateAndMode(self, w=None, prompt=None, setFocus=True): 

4044 """Show the state and mode at the start of the minibuffer.""" 

4045 c, k = self.c, self 

4046 state = k.unboundKeyAction 

4047 mode = k.getStateKind() 

4048 if not g.app.gui: 

4049 return 

4050 if not w: 

4051 if hasattr(g.app.gui, 'set_minibuffer_label'): 

4052 pass # we don't need w 

4053 else: 

4054 w = g.app.gui.get_focus(c) 

4055 if not w: 

4056 return 

4057 isText = g.isTextWrapper(w) 

4058 # This fixes a problem with the tk gui plugin. 

4059 if mode and mode.lower().startswith('isearch'): 

4060 return 

4061 wname = g.app.gui.widget_name(w).lower() 

4062 # Get the wrapper for the headline widget. 

4063 if wname.startswith('head'): 

4064 if hasattr(c.frame.tree, 'getWrapper'): 

4065 if hasattr(w, 'widget'): 

4066 w2 = w.widget 

4067 else: 

4068 w2 = w 

4069 w = c.frame.tree.getWrapper(w2, item=None) 

4070 isText = bool(w) # A benign hack. 

4071 if mode: 

4072 if mode in ('getArg', 'getFileName', 'full-command'): 

4073 s = None 

4074 elif prompt: 

4075 s = prompt 

4076 else: 

4077 mode = mode.strip() 

4078 if mode.endswith('-mode'): 

4079 mode = mode[:-5] 

4080 s = f"{mode.capitalize()} Mode" 

4081 elif c.vim_mode and c.vimCommands: 

4082 c.vimCommands.show_status() 

4083 return 

4084 else: 

4085 s = f"{state.capitalize()} State" 

4086 if c.editCommands.extendMode: 

4087 s = s + ' (Extend Mode)' 

4088 if s: 

4089 k.setLabelBlue(s) 

4090 if w and isText: 

4091 # k.showStateColors(inOutline,w) 

4092 k.showStateCursor(state, w) 

4093 # 2015/07/11: reset the status line. 

4094 if hasattr(c.frame.tree, 'set_status_line'): 

4095 c.frame.tree.set_status_line(c.p) 

4096 #@+node:ekr.20110202111105.15439: *4* k.showStateCursor 

4097 def showStateCursor(self, state, w): 

4098 pass 

4099 #@-others 

4100#@+node:ekr.20120208064440.10150: ** class ModeInfo 

4101class ModeInfo: 

4102 

4103 def __repr__(self): 

4104 return f"<ModeInfo {self.name}>" 

4105 

4106 __str__ = __repr__ 

4107 #@+others 

4108 #@+node:ekr.20120208064440.10193: *3* mode_i. ctor 

4109 def __init__(self, c, name, aList): 

4110 

4111 self.c = c 

4112 self.d = {} # The bindings in effect for this mode. 

4113 # Keys are names of (valid) command names, values are BindingInfo objects. 

4114 self.entryCommands = [] 

4115 # A list of BindingInfo objects. 

4116 self.k = c.k 

4117 self.name = self.computeModeName(name) 

4118 self.prompt = self.computeModePrompt(self.name) 

4119 self.init(name, aList) 

4120 #@+node:ekr.20120208064440.10152: *3* mode_i.computeModeName 

4121 def computeModeName(self, name): 

4122 s = name.strip().lower() 

4123 j = s.find(' ') 

4124 if j > -1: 

4125 s = s[:j] 

4126 if s.endswith('mode'): 

4127 s = s[:-4].strip() 

4128 if s.endswith('-'): 

4129 s = s[:-1] 

4130 i = s.find('::') 

4131 if i > -1: 

4132 # The actual mode name is everything up to the "::" 

4133 # The prompt is everything after the prompt. 

4134 s = s[:i] 

4135 return s + '-mode' 

4136 #@+node:ekr.20120208064440.10156: *3* mode_i.computeModePrompt 

4137 def computeModePrompt(self, name): 

4138 assert name == self.name 

4139 s = 'enter-' + name.replace(' ', '-') 

4140 i = s.find('::') 

4141 if i > -1: 

4142 # The prompt is everything after the '::' 

4143 prompt = s[i + 2 :].strip() 

4144 else: 

4145 prompt = s 

4146 return prompt 

4147 #@+node:ekr.20120208064440.10160: *3* mode_i.createModeBindings 

4148 def createModeBindings(self, w): 

4149 """Create mode bindings for w, a text widget.""" 

4150 c, d, k, modeName = self.c, self.d, self.k, self.name 

4151 for commandName in d: 

4152 func = c.commandsDict.get(commandName) 

4153 if not func: 

4154 g.es_print(f"no such command: {commandName} Referenced from {modeName}") 

4155 continue 

4156 aList = d.get(commandName, []) 

4157 for bi in aList: 

4158 stroke = bi.stroke 

4159 # Important: bi.val is canonicalized. 

4160 if stroke and stroke not in ('None', 'none', None): 

4161 assert g.isStroke(stroke) 

4162 k.makeMasterGuiBinding(stroke) 

4163 # Create the entry for the mode in k.masterBindingsDict. 

4164 # Important: this is similar, but not the same as k.bindKeyToDict. 

4165 # Thus, we should **not** call k.bindKey here! 

4166 d2 = k.masterBindingsDict.get(modeName, {}) 

4167 d2[stroke] = g.BindingInfo( 

4168 kind=f"mode<{modeName}>", 

4169 commandName=commandName, 

4170 func=func, 

4171 nextMode=bi.nextMode, 

4172 stroke=stroke) 

4173 k.masterBindingsDict[modeName] = d2 

4174 #@+node:ekr.20120208064440.10195: *3* mode_i.createModeCommand 

4175 def createModeCommand(self): 

4176 c = self.c 

4177 key = 'enter-' + self.name.replace(' ', '-') 

4178 

4179 def enterModeCallback(event=None, self=self): 

4180 self.enterMode() 

4181 

4182 c.commandsDict[key] = f = enterModeCallback 

4183 g.trace('(ModeInfo)', f.__name__, key, 

4184 'len(c.commandsDict.keys())', len(list(c.commandsDict.keys()))) 

4185 #@+node:ekr.20120208064440.10180: *3* mode_i.enterMode 

4186 def enterMode(self): 

4187 

4188 c, k = self.c, self.k 

4189 c.inCommand = False # Allow inner commands in the mode. 

4190 event = None 

4191 k.generalModeHandler(event, modeName=self.name) 

4192 #@+node:ekr.20120208064440.10153: *3* mode_i.init 

4193 def init(self, name, dataList): 

4194 """aList is a list of tuples (commandName,bi).""" 

4195 c, d, modeName = self.c, self.d, self.name 

4196 for name, bi in dataList: 

4197 if not name: 

4198 # An entry command: put it in the special *entry-commands* key. 

4199 self.entryCommands.append(bi) 

4200 elif bi is not None: 

4201 # A regular shortcut. 

4202 bi.pane = modeName 

4203 aList = d.get(name, []) 

4204 # Important: use previous bindings if possible. 

4205 key2, aList2 = c.config.getShortcut(name) 

4206 aList3 = [z for z in aList2 if z.pane != modeName] 

4207 if aList3: 

4208 aList.extend(aList3) 

4209 aList.append(bi) 

4210 d[name] = aList 

4211 #@+node:ekr.20120208064440.10158: *3* mode_i.initMode 

4212 def initMode(self): 

4213 

4214 c, k = self.c, self.c.k 

4215 k.inputModeName = self.name 

4216 k.silentMode = False 

4217 for bi in self.entryCommands: 

4218 commandName = bi.commandName 

4219 k.simulateCommand(commandName) 

4220 # Careful, the command can kill the commander. 

4221 if g.app.quitting or not c.exists: 

4222 return 

4223 # New in Leo 4.5: a startup command can immediately transfer to another mode. 

4224 if commandName.startswith('enter-'): 

4225 return 

4226 # Create bindings after we know whether we are in silent mode. 

4227 w = k.modeWidget if k.silentMode else k.w 

4228 k.createModeBindings(self.name, self.d, w) 

4229 k.showStateAndMode(prompt=self.name) 

4230 #@-others 

4231#@-others 

4232#@@language python 

4233#@@tabwidth -4 

4234#@@pagewidth 70 

4235#@-leo