Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- 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 self.namespaces = [] 

167 # additional namespaces to search for objects, other code 

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

169 self.qw = None 

170 # The object that supports qcompletion methods. 

171 self.tabName = None 

172 # The name of the main completion tab. 

173 self.verbose = False 

174 # True: print all members, regardless of how many there are. 

175 self.w = None 

176 # The widget that gets focus after autocomplete is done. 

177 self.warnings = {} 

178 # Keys are language names. 

179 # Codewise pre-computes... 

180 self.codewiseSelfList = [] 

181 # The (global) completions for "self." 

182 self.completionsDict = {} 

183 # Keys are prefixes, values are completion lists. 

184 self.reloadSettings() 

185 

186 def reloadSettings(self): 

187 c = self.c 

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

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

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

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

192 # True: show results in autocompleter tab. 

193 # False: show results in a QCompleter widget. 

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

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

196 @ac_cmd('auto-complete') 

197 def autoComplete(self, event=None): 

198 """An event handler for autocompletion.""" 

199 c, k = self.c, self.k 

200 # pylint: disable=consider-using-ternary 

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

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

203 return 

204 c.insertCharFromEvent(event) 

205 if c.exists: 

206 c.frame.updateStatusLine() 

207 # Allow autocompletion only in the body pane. 

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

209 return 

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

211 if w and k.enable_autocompleter: 

212 self.w = w 

213 self.start(event) 

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

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

216 def autoCompleteForce(self, event=None): 

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

218 c, k = self.c, self.k 

219 # pylint: disable=consider-using-ternary 

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

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

222 return 

223 if c.exists: 

224 c.frame.updateStatusLine() 

225 # Allow autocompletion only in the body pane. 

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

227 return 

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

229 if w: 

230 self.w = w 

231 self.start(event) 

232 

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

234 @ac_cmd('disable-autocompleter') 

235 def disableAutocompleter(self, event=None): 

236 """Disable the autocompleter.""" 

237 self.k.enable_autocompleter = False 

238 self.showAutocompleterStatus() 

239 

240 @ac_cmd('disable-calltips') 

241 def disableCalltips(self, event=None): 

242 """Disable calltips.""" 

243 self.k.enable_calltips = False 

244 self.showCalltipsStatus() 

245 

246 @ac_cmd('enable-autocompleter') 

247 def enableAutocompleter(self, event=None): 

248 """Enable the autocompleter.""" 

249 self.k.enable_autocompleter = True 

250 self.showAutocompleterStatus() 

251 

252 @ac_cmd('enable-calltips') 

253 def enableCalltips(self, event=None): 

254 """Enable calltips.""" 

255 self.k.enable_calltips = True 

256 self.showCalltipsStatus() 

257 

258 @ac_cmd('toggle-autocompleter') 

259 def toggleAutocompleter(self, event=None): 

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

261 self.k.enable_autocompleter = not self.k.enable_autocompleter 

262 self.showAutocompleterStatus() 

263 

264 @ac_cmd('toggle-calltips') 

265 def toggleCalltips(self, event=None): 

266 """Toggle whether calltips are enabled.""" 

267 self.k.enable_calltips = not self.k.enable_calltips 

268 self.showCalltipsStatus() 

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

270 @ac_cmd('show-calltips') 

271 def showCalltips(self, event=None): 

272 """Show the calltips at the cursor.""" 

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

274 if not w: 

275 return 

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

277 if k.enable_calltips and not is_headline: 

278 self.w = w 

279 self.calltip() 

280 else: 

281 c.insertCharFromEvent(event) 

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

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

284 def showCalltipsForce(self, event=None): 

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

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

287 if not w: 

288 return 

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

290 if not is_headline: 

291 self.w = w 

292 self.calltip() 

293 else: 

294 c.insertCharFromEvent(event) 

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

296 def showAutocompleterStatus(self): 

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

298 k = self.k 

299 if not g.unitTesting: 

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

301 g.red(s) 

302 

303 def showCalltipsStatus(self): 

304 """Show the autocompleter status.""" 

305 k = self.k 

306 if not g.unitTesting: 

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

308 g.red(s) 

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

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

311 def exit(self): 

312 

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

314 if trace: 

315 g.trace('(AutoCompleterClass)') 

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

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

318 c.k.keyboardQuit() 

319 if self.use_qcompleter: 

320 if self.qw: 

321 self.qw.end_completer() 

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

323 else: 

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

325 c.frame.log.deleteTab(name) 

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

327 c.widgetWantsFocusNow(w) 

328 i, j = w.getSelectionRange() 

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

330 newText = w.getAllText() 

331 if p.b == newText: 

332 return 

333 bunch = u.beforeChangeBody(p) 

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

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

336 

337 finish = exit 

338 abort = exit 

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

340 def appendTabName(self, word): 

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

342 

343 def beginTabName(self, word): 

344 self.setTabName('AutoComplete ' + word) 

345 

346 def clearTabName(self): 

347 self.setTabName('AutoComplete ') 

348 

349 def popTabName(self): 

350 s = self.tabName 

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

352 if i > -1: 

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

354 

355 # Underscores are not valid in Pmw tab names! 

356 

357 def setTabName(self, s): 

358 c = self.c 

359 if self.tabName: 

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

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

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

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

364 def attr_matches(self, s, namespace): 

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

366 

367 Evaluates s using eval(s,namespace) 

368 

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

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

371 dir()) are used as possible completions. 

372 

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

374 

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

376 with a __getattr__ hook is evaluated. 

377 

378 """ 

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

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

381 if not m: 

382 return [] 

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

384 try: 

385 safe_expr = self.strip_brackets(expr) 

386 obj = eval(safe_expr, namespace) 

387 except Exception: 

388 return [] 

389 # Build the result. 

390 words = dir(obj) 

391 n = len(attr) 

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

393 return result 

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

395 def auto_completer_state_handler(self, event): 

396 """Handle all keys while autocompleting.""" 

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

398 state = k.getState(tag) 

399 ch = event.char if event else '' 

400 stroke = event.stroke if event else '' 

401 is_plain = k.isPlainKey(stroke) 

402 if state == 0: 

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

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

405 if tabList: 

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

407 else: 

408 self.exit() 

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

410 self.exit() 

411 elif ch == 'Escape': 

412 self.exit() 

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

414 self.compute_completion_list() 

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

416 self.do_backspace() 

417 elif ch == '.': 

418 self.insert_string('.') 

419 self.compute_completion_list() 

420 elif ch == '?': 

421 self.info() 

422 elif ch == '!': 

423 # Toggle between verbose and brief listing. 

424 self.verbose = not self.verbose 

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

426 message = f"verbose completions {kind}" 

427 g.es_print(message) 

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

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

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

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

432 self.compute_completion_list() 

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

434 # self.onDown() 

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

436 # self.onUp() 

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

438 self.insert_general_char(ch) 

439 elif stroke == k.autoCompleteForceKey: 

440 # This is probably redundant because completions will exist. 

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

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

443 if tabList: 

444 self.show_completion_list(common_prefix, prefix, tabList) 

445 else: 

446 g.warning('No completions') 

447 self.exit() 

448 else: 

449 self.abort() 

450 return 'do-standard-keys' 

451 return None 

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

453 def calltip(self): 

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

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

456 """ 

457 obj, prefix = self.get_object() 

458 if obj: 

459 self.calltip_success(prefix, obj) 

460 else: 

461 self.calltip_fail(prefix) 

462 self.exit() 

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

464 def calltip_fail(self, prefix): 

465 """Evaluation of prefix failed.""" 

466 self.insert_string('(') 

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

468 def calltip_success(self, prefix, obj): 

469 try: 

470 # Get the parenthesized argument list. 

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

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

473 except Exception: 

474 self.insert_string('(') 

475 return 

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

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

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

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

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

481 else: 

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

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

484 self.insert_string(s, select=True) 

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

486 def compute_completion_list(self): 

487 """Return the autocompleter completion list.""" 

488 prefix = self.get_autocompleter_prefix() 

489 key, options = self.get_cached_options(prefix) 

490 if not options: 

491 options = self.get_completions(prefix) 

492 tabList, common_prefix = g.itemsMatchingPrefixInList( 

493 prefix, options, matchEmptyPrefix=False) 

494 if not common_prefix: 

495 tabList, common_prefix = g.itemsMatchingPrefixInList( 

496 prefix, options, matchEmptyPrefix=True) 

497 if tabList: 

498 self.show_completion_list(common_prefix, prefix, tabList) 

499 return common_prefix, prefix, tabList 

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

501 def get_cached_options(self, prefix): 

502 d = self.completionsDict 

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

504 i = len(prefix) 

505 while i > 0: 

506 key = prefix[:i] 

507 i -= 1 

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

509 if key.endswith('.'): 

510 return key, [] 

511 options = d.get(key) 

512 if options: 

513 return key, options 

514 return None, [] 

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

516 def do_backspace(self): 

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

518 c, w = self.c, self.w 

519 c.bodyWantsFocusNow() 

520 i = w.getInsertPoint() 

521 if i <= 0: 

522 self.exit() 

523 return 

524 w.delete(i - 1, i) 

525 w.setInsertPoint(i - 1) 

526 if i <= 1: 

527 self.exit() 

528 else: 

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

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

531 if not prefix: 

532 self.exit() 

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

534 def do_qcompleter_tab(self, prefix, options): 

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

536 matches, common_prefix = g.itemsMatchingPrefixInList( 

537 prefix, options, matchEmptyPrefix=False) 

538 return common_prefix 

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

540 def get_autocompleter_prefix(self): 

541 # Only the body pane supports auto-completion. 

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

543 s = w.getAllText() 

544 if not s: 

545 return '' 

546 i = w.getInsertPoint() - 1 

547 i = j = max(0, i) 

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

549 i -= 1 

550 i += 1 

551 j += 1 

552 prefix = s[i:j] 

553 return prefix 

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

555 jedi_warning = False 

556 

557 def get_completions(self, prefix): 

558 """Return jedi or codewise completions.""" 

559 d = self.completionsDict 

560 if self.use_jedi: 

561 try: 

562 import jedi 

563 except ImportError: 

564 jedi = None 

565 if not self.jedi_warning: 

566 self.jedi_warning = True 

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

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

569 if jedi: 

570 aList = ( 

571 self.get_jedi_completions(prefix) or 

572 # Prefer the jedi completions. 

573 self.get_leo_completions(prefix)) 

574 d[prefix] = aList 

575 return aList 

576 # 

577 # Not jedi. Use codewise. 

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

579 if not self.codewiseSelfList: 

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

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

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

583 # Use the cached list if it exists. 

584 aList = d.get(prefix) 

585 if aList: 

586 return aList 

587 aList = ( 

588 self.get_leo_completions(prefix) or 

589 # Prefer the Leo completions. 

590 self.get_codewise_completions(prefix) 

591 ) 

592 d[prefix] = aList 

593 return aList 

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

595 def get_codewise_completions(self, prefix): 

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

597 c = self.c 

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

599 if m: 

600 varname = m.group(1) 

601 ivar = m.group(3) 

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

603 else: 

604 kind, aList = 'none', [] 

605 varname, ivar = None, None 

606 if aList: 

607 if kind == 'class': 

608 hits = self.lookup_methods(aList, ivar) 

609 hits.extend(self.codewiseSelfList) 

610 elif kind == 'module': 

611 hits = self.lookup_modules(aList, ivar) 

612 else: 

613 aList2 = prefix.split('.') 

614 if aList2: 

615 func = aList2[-1] 

616 hits = self.lookup_functions(func) 

617 else: 

618 hits = [] 

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

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

621 return hits 

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

623 def clean(self, hits): 

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

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

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

627 aList.sort() 

628 return aList 

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

630 def clean_for_display(self, hits): 

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

632 aList = [] 

633 for h in hits: 

634 s = h[0] 

635 # Display oriented: no good for completion list. 

636 fn = h[1].strip() 

637 if fn.startswith('/'): 

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

639 else: 

640 sig = fn 

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

642 aList = list(set(aList)) 

643 aList.sort() 

644 return aList 

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

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

647 """Return kind, class_list""" 

648 # if varname == 'g': 

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

650 if varname == 'p': 

651 return 'class', ['position'] 

652 if varname == 'c': 

653 return 'class', ['Commands'] 

654 if varname == 'self': 

655 # Return the nearest enclosing class. 

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

657 h = p.h 

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

659 if m: 

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

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

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

663 return 'class', [] 

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

665 def lookup_functions(self, prefix): 

666 aList = codewise.cmd_functions([prefix]) 

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

668 return self.clean(hits) 

669 

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

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

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

673 return self.clean(hits) 

674 

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

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

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

678 return self.clean(hits) 

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

680 def get_jedi_completions(self, prefix): 

681 

682 c = self.c 

683 w = c.frame.body.wrapper 

684 i = w.getInsertPoint() 

685 p = c.p 

686 body_s = p.b 

687 # 

688 # Get the entire source for jedi. 

689 t1 = time.process_time() 

690 goto = gotoCommands.GoToCommands(c) 

691 root, fileName = goto.find_root(p) 

692 if root: 

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

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

695 if n0 is None: 

696 n0 = 0 

697 else: 

698 source = body_s 

699 n0 = 0 

700 t2 = time.process_time() 

701 # 

702 # Get local line 

703 lines = g.splitLines(body_s) 

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

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

706 return [] 

707 line = lines[row] 

708 # 

709 # Find the global line, and compute offsets. 

710 source_lines = g.splitLines(source) 

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

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

713 # Adjust the column. 

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

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

716 if indent2 >= indent1: 

717 local_column = column # For traces. 

718 column += abs(indent2 - indent1) 

719 break 

720 else: 

721 completions = None 

722 jedi_line, indent1, indent2 = None, None, None 

723 if 0: # This *can* happen. 

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

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

726 # 

727 # Get the jedi completions. 

728 if jedi and jedi_line is not None: 

729 try: 

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

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

732 completions = script.complete( 

733 line=1 + n0 + jedi_line, 

734 column=column, 

735 ) 

736 t3 = time.process_time() 

737 except Exception: 

738 t3 = time.process_time() 

739 completions = None 

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

741 print('ERROR', p.h) 

742 if not completions: 

743 return [] 

744 # May be used in traces below. 

745 assert t3 >= t2 >= t1 

746 assert local_column is not None 

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

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

749 # Retain these for now... 

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

751 # head = line[:local_column] 

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

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

754 return completions 

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

756 def add_prefix(self, prefix, s): 

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

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

759 aList = prefix.split('.') 

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

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

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

763 def get_leo_completions(self, prefix): 

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

765 aList = [] 

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

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

768 aList.sort() 

769 return aList 

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

771 def get_leo_namespace(self, prefix): 

772 """ 

773 Return an environment in which to evaluate prefix. 

774 Add some common standard library modules as needed. 

775 """ 

776 k = self.k 

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

778 aList = prefix.split('.') 

779 if len(aList) > 1: 

780 name = aList[0] 

781 m = sys.modules.get(name) 

782 if m: 

783 d[name] = m 

784 return d 

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

786 def get_object(self): 

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

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

789 if not aList: 

790 return None, prefix1 

791 if len(aList) == 1: 

792 prefix = aList[0] 

793 else: 

794 prefix = common_prefix 

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

796 prefix += self.qcompleter.get_selection() 

797 safe_prefix = self.strip_brackets(prefix) 

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

799 try: 

800 obj = eval(safe_prefix, d) 

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

802 except AttributeError: 

803 obj = None 

804 except NameError: 

805 obj = None 

806 except SyntaxError: 

807 obj = None 

808 except Exception: 

809 g.es_exception() 

810 obj = None 

811 return obj, prefix 

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

813 def info(self): 

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

815 c = self.c 

816 obj, prefix = self.get_object() 

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

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

819 put(prefix) 

820 try: 

821 argspec = inspect.getargspec(obj) 

822 # uses None instead of empty list 

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

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

825 put("args:") 

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

827 if not simple_args: 

828 put(' (none)') 

829 else: 

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

831 put("keyword args:") 

832 if not argspec.defaults: 

833 put(' (none)') 

834 for i in range(defn): 

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

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

837 if argspec.varargs: 

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

839 if argspec.keywords: 

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

841 put('\n') # separate docstring 

842 except TypeError: 

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

844 doc = inspect.getdoc(obj) 

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

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

847 def init_qcompleter(self, event=None): 

848 

849 # Compute the prefix and the list of options. 

850 prefix = self.get_autocompleter_prefix() 

851 options = self.get_completions(prefix) 

852 w = self.c.frame.body.wrapper.widget 

853 # A LeoQTextBrowser. May be none for unit tests. 

854 if w and options: 

855 self.qw = w 

856 self.qcompleter = w.init_completer(options) 

857 self.auto_completer_state_handler(event) 

858 else: 

859 if not g.unitTesting: 

860 g.warning('No completions') 

861 self.exit() 

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

863 def init_tabcompleter(self, event=None): 

864 # Compute the prefix and the list of options. 

865 prefix = self.get_autocompleter_prefix() 

866 options = self.get_completions(prefix) 

867 if options: 

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

869 self.auto_completer_state_handler(event) 

870 else: 

871 g.warning('No completions') 

872 self.exit() 

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

874 def insert_general_char(self, ch): 

875 

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

877 k, w = self.k, self.w 

878 if g.isWordChar(ch): 

879 if trace: 

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

881 self.insert_string(ch) 

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

883 if not aList: 

884 if self.forbid_invalid: 

885 # Delete the character we just inserted. 

886 self.do_backspace() 

887 # @bool auto_tab_complete is deprecated. 

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

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

890 extend = common_prefix[len(prefix) :] 

891 ins = w.getInsertPoint() 

892 if trace: 

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

894 w.insert(ins, extend) 

895 return 

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

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

898 self.calltip() 

899 else: 

900 if trace: 

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

902 self.insert_string(ch) 

903 self.exit() 

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

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

906 """ 

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

908 

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

910 """ 

911 c, w = self.c, self.w 

912 if not g.isTextWrapper(w): 

913 return 

914 c.widgetWantsFocusNow(w) 

915 # 

916 # Don't make this undoable. 

917 # oldText = w.getAllText() 

918 # oldSel = w.getSelectionRange() 

919 # bunch = u.beforeChangeBody(p) 

920 i = w.getInsertPoint() 

921 w.insert(i, s) 

922 if select: 

923 j = i + len(s) 

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

925 # 

926 # Don't make this undoable. 

927 # if 0: 

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

929 # oldSel=oldSel, 

930 # oldText=oldText, 

931 # newText=w.getAllText(), 

932 # newInsert=w.getInsertPoint(), 

933 # newSel=w.getSelectionRange()) 

934 # else: 

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

936 if self.use_qcompleter and self.qw: 

937 c.widgetWantsFocusNow(self.qw.leo_qc) 

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

939 def is_leo_source_file(self): 

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

941 c = self.c 

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

943 'leoDocs.leo', 

944 'LeoGui.leo', 'LeoGuiPluginsRef.leo', 

945 # 'leoPlugins.leo', 'leoPluginsRef.leo', 

946 'leoPy.leo', 'leoPyRef.leo', 

947 'myLeoSettings.leo', 'leoSettings.leo', 

948 'ekr.leo', 

949 # 'test.leo', 

950 )) 

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

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

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

954 """Put s to the given tab. 

955 

956 May be overridden in subclasses.""" 

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

958 if g.unitTesting: 

959 pass 

960 else: 

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

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

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

964 

965 c = self.c 

966 aList = common_prefix.split('.') 

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

968 # "!" toggles self.verbose. 

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

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

971 else: 

972 tabList = self.get_summary_list(header, tabList) 

973 if self.use_qcompleter: 

974 # Put the completions in the QListView. 

975 if self.qw: 

976 self.qw.show_completions(tabList) 

977 else: 

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

979 c.widgetWantsFocus(self.w) 

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

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

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

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

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

985 def clean_completion_list(self, header, tabList): 

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

987 return [ 

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

989 for z in tabList] 

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

991 def get_summary_list(self, header, tabList): 

992 """Show the possible starting letters, 

993 but only if there are more than one. 

994 """ 

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

996 for z in tabList: 

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

998 if tail.startswith('.'): 

999 tail = tail[1:] 

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

1001 if ch: 

1002 n = d.get(ch, 0) 

1003 d[ch] = n + 1 

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

1005 if len(aList) > 1: 

1006 tabList = aList 

1007 else: 

1008 tabList = self.clean_completion_list(header, tabList) 

1009 return tabList 

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

1011 def start(self, event): 

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

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

1014 c = self.c 

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

1016 self.completionsDict = {} 

1017 if self.use_qcompleter: 

1018 self.init_qcompleter(event) 

1019 else: 

1020 self.init_tabcompleter(event) 

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

1022 def strip_brackets(self, s): 

1023 """Return s with all brackets removed. 

1024 

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

1026 """ 

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

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

1029 return s 

1030 #@-others 

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

1032class ContextSniffer: 

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

1034 

1035 For simple dynamic code completion engines. 

1036 """ 

1037 

1038 def __init__(self): 

1039 self.vars = {} 

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

1041 #@+others 

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

1043 def get_classes(self, s, varname): 

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

1045 self.push_declarations(s) 

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

1047 return aList 

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

1049 # def set_small_context(self, body): 

1050 # """ Set immediate function """ 

1051 # self.push_declarations(body) 

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

1053 def push_declarations(self, s): 

1054 for line in s.splitlines(): 

1055 line = line.lstrip() 

1056 if line.startswith('#'): 

1057 line = line.lstrip('#') 

1058 parts = line.split(':') 

1059 if len(parts) == 2: 

1060 a, b = parts 

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

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

1063 def declare(self, var, klass): 

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

1065 if not vars: 

1066 self.vars[var] = vars 

1067 vars.append(klass) 

1068 #@-others 

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

1070class FileNameChooser: 

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

1072 #@+others 

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

1074 def __init__(self, c): 

1075 """Ctor for FileNameChooser class.""" 

1076 self.c = c 

1077 self.k = c.k 

1078 assert c and c.k 

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

1080 self.callback = None 

1081 self.filterExt = None 

1082 self.log = None # inited later. 

1083 self.prompt = None 

1084 self.tabName = None 

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

1086 def compute_tab_list(self): 

1087 """Compute the list of completions.""" 

1088 path = self.get_label() 

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

1090 path = g.os_path_expanduser(path) 

1091 sep = os.path.sep 

1092 if g.os_path_exists(path): 

1093 if g.os_path_isdir(path): 

1094 if path.endswith(os.sep): 

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

1096 else: 

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

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

1099 else: 

1100 # An existing file. 

1101 tabList = [path] 

1102 else: 

1103 if path and path.endswith(sep): 

1104 path = path[:-1] 

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

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

1107 if self.filterExt: 

1108 for ext in self.filterExt: 

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

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

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

1112 return common_prefix, tabList 

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

1114 def do_back_space(self): 

1115 """Handle a back space.""" 

1116 w = self.c.k.w 

1117 if w and w.hasSelection(): 

1118 # s = w.getAllText() 

1119 i, j = w.getSelectionRange() 

1120 w.delete(i, j) 

1121 s = self.get_label() 

1122 else: 

1123 s = self.get_label() 

1124 if s: 

1125 s = s[:-1] 

1126 self.set_label(s) 

1127 if s: 

1128 common_prefix, tabList = self.compute_tab_list() 

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

1130 else: 

1131 tabList = [] 

1132 self.show_tab_list(tabList) 

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

1134 def do_char(self, char): 

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

1136 w = self.c.k.w 

1137 if w and w.hasSelection: 

1138 # s = w.getAllText() 

1139 i, j = w.getSelectionRange() 

1140 w.delete(i, j) 

1141 w.setInsertPoint(i) 

1142 w.insert(i, char) 

1143 else: 

1144 self.extend_label(char) 

1145 common_prefix, tabList = self.compute_tab_list() 

1146 self.show_tab_list(tabList) 

1147 if common_prefix: 

1148 if 0: 

1149 # This is a bit *too* helpful. 

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

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

1152 self.set_label(common_prefix) 

1153 # Recompute the tab list. 

1154 common_prefix, tabList = self.compute_tab_list() 

1155 self.show_tab_list(tabList) 

1156 if len(tabList) == 1: 

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

1158 self.set_label(common_prefix) 

1159 else: 

1160 # Restore everything. 

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

1162 self.extend_label(char) 

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

1164 def do_tab(self): 

1165 """Handle tab completion.""" 

1166 old = self.get_label() 

1167 common_prefix, tabList = self.compute_tab_list() 

1168 self.show_tab_list(tabList) 

1169 if len(tabList) == 1: 

1170 common_prefix = tabList[0] 

1171 self.set_label(common_prefix) 

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

1173 self.set_label(common_prefix) 

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

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

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

1177 c, k = self.c, self.c.k 

1178 tag = 'get-file-name' 

1179 state = k.getState(tag) 

1180 char = event.char if event else '' 

1181 if state == 0: 

1182 # Re-init all ivars. 

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

1184 self.callback = callback 

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

1186 self.prompt = prompt 

1187 self.tabName = tabName 

1188 join = g.os_path_finalize_join 

1189 finalize = g.os_path_finalize 

1190 normslashes = g.os_path_normslashes 

1191 # #467: Add setting for preferred directory. 

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

1193 if directory: 

1194 directory = finalize(directory) 

1195 if not g.os_path_exists(directory): 

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

1197 normslashes(directory)) 

1198 directory = None 

1199 if not directory: 

1200 directory = finalize(os.curdir) 

1201 # Init the label and state. 

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

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

1204 self.set_label(normslashes(label)) 

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

1206 self.log.selectTab(self.tabName) 

1207 junk, tabList = self.compute_tab_list() 

1208 self.show_tab_list(tabList) 

1209 c.minibufferWantsFocus() 

1210 elif char == 'Escape': 

1211 k.keyboardQuit() 

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

1213 self.log.deleteTab(self.tabName) 

1214 path = self.get_label() 

1215 k.keyboardQuit() 

1216 if self.callback: 

1217 # pylint: disable=not-callable 

1218 self.callback(path) 

1219 else: 

1220 g.trace('no callback') 

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

1222 self.do_tab() 

1223 c.minibufferWantsFocus() 

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

1225 self.do_back_space() 

1226 c.minibufferWantsFocus() 

1227 elif k.isPlainKey(char): 

1228 self.do_char(char) 

1229 else: 

1230 pass 

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

1232 def extend_label(self, s): 

1233 """Extend the label by s.""" 

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

1235 

1236 def get_label(self): 

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

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

1239 

1240 def set_label(self, s): 

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

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

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

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

1245 def show_tab_list(self, tabList): 

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

1247 self.log.clearTab(self.tabName) 

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

1249 es = [] 

1250 for path in tabList: 

1251 theDir, fileName = g.os_path_split(path) 

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

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

1254 es.append(s) 

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

1256 #@-others 

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

1258class GetArg: 

1259 """ 

1260 A class encapsulating all k.getArg logic. 

1261 

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

1263 

1264 For details, see the docstring for ga.get_arg 

1265 """ 

1266 #@+others 

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

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

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

1270 """Ctor for GetArg class.""" 

1271 # Common ivars. 

1272 self.c = c 

1273 self.k = c.k 

1274 assert c 

1275 assert c.k 

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

1277 self.tabName = tabName 

1278 # State vars. 

1279 self.after_get_arg_state = None, None, None 

1280 self.arg_completion = True 

1281 self.handler = None 

1282 self.tabList = [] 

1283 # Tab cycling ivars... 

1284 self.cycling_prefix = None 

1285 self.cycling_index = -1 

1286 self.cycling_tabList = [] 

1287 # The following are k globals. 

1288 # k.arg. 

1289 # k.argSelectedText 

1290 # k.oneCharacterArg 

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

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

1293 

1294 def compute_tab_list(self, tabList): 

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

1296 # Support vim-mode commands. 

1297 command = self.get_label() 

1298 if self.is_command(command): 

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

1300 return common_prefix, tabList 

1301 # 

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

1303 command = self.get_command(command) 

1304 return command, [command] 

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

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

1307 

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

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

1310 k = self.k 

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

1312 # Update the label. 

1313 w = k.w 

1314 i, j = w.getSelectionRange() 

1315 ins = w.getInsertPoint() 

1316 if ins > len(k.mb_prefix): 

1317 # Step 1: actually delete the character. 

1318 i, j = w.getSelectionRange() 

1319 if i == j: 

1320 ins -= 1 

1321 w.delete(ins) 

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

1323 else: 

1324 ins = i 

1325 w.delete(i, j) 

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

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

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

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

1330 else: 

1331 tabList = [] 

1332 if completion: 

1333 # #323. 

1334 common_prefix, tabList = self.compute_tab_list(tabList) 

1335 self.show_tab_list(tabList) 

1336 self.reset_tab_cycling() 

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

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

1339 

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

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

1342 c = self.c 

1343 if completion: 

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

1345 common_prefix, tabList = self.compute_tab_list(tabList) 

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

1347 self.cycling_prefix = common_prefix 

1348 # 

1349 # No tab cycling for completed commands having 

1350 # a 'tab_callback' attribute. 

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

1352 return 

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

1354 self.do_tab_cycling(common_prefix, tabList) 

1355 c.minibufferWantsFocus() 

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

1357 def do_tab_callback(self): 

1358 """ 

1359 If the command-name handler has a tab_callback, 

1360 call handler.tab_callback() and return True. 

1361 """ 

1362 c, k = self.c, self.k 

1363 commandName, tail = k.getMinibufferCommandName() 

1364 handler = c.commandsDict.get(commandName) 

1365 if hasattr(handler, 'tab_callback'): 

1366 self.reset_tab_cycling() 

1367 k.functionTail = tail 

1368 # For k.getFileName. 

1369 handler.tab_callback() 

1370 return True 

1371 return False 

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

1373 def do_tab_cycling(self, common_prefix, tabList): 

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

1375 s = self.get_label() 

1376 if not common_prefix: 

1377 # Leave the minibuffer as it is. 

1378 self.show_tab_list(tabList) 

1379 # #323. 

1380 elif ( 

1381 self.cycling_prefix and 

1382 s.startswith(self.cycling_prefix) and 

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

1384 ): 

1385 n = self.cycling_index 

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

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

1388 self.show_tab_list(self.cycling_tabList) 

1389 else: 

1390 # Restart. 

1391 self.show_tab_list(tabList) 

1392 self.cycling_tabList = tabList[:] 

1393 self.cycling_prefix = common_prefix 

1394 self.set_label(common_prefix) 

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

1396 self.cycling_index = 0 

1397 else: 

1398 self.cycling_index = -1 

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

1400 def reset_tab_cycling(self): 

1401 """Reset all tab cycling ivars.""" 

1402 self.cycling_prefix = None 

1403 self.cycling_index = -1 

1404 self.cycling_tabList = [] 

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

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

1407 

1408 if 0: 

1409 

1410 def extend_label(self, s): 

1411 """Extend the label by s.""" 

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

1413 

1414 def get_label(self): 

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

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

1417 

1418 def set_label(self, s): 

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

1420 k = self.c.k 

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

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

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

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

1425 def get_arg(self, event, 

1426 returnKind=None, returnState=None, handler=None, 

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

1428 stroke=None, useMinibuffer=True 

1429 ): 

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

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

1432 """ 

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

1434 

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

1436 

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

1438 

1439 The arguments to ga.get_arg are as follows: 

1440 

1441 event: The event passed to the command. 

1442 

1443 returnKind=None: A string. 

1444 returnState=None, An int. 

1445 handler=None, A function. 

1446 

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

1448 

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

1450 

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

1452 

1453 completion=True: True if completions are enabled. 

1454 

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

1456 

1457 stroke=None: The incoming key stroke. 

1458 

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

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

1461 

1462 """ 

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

1464 if tabList is None: 

1465 tabList = [] 

1466 c, k = self.c, self.k 

1467 state = k.getState('getArg') 

1468 c.check_event(event) 

1469 c.minibufferWantsFocusNow() 

1470 char = event.char if event else '' 

1471 if state == 0: 

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

1473 returnKind, returnState, tabList, useMinibuffer) 

1474 return 

1475 if char == 'Escape': 

1476 k.keyboardQuit() 

1477 elif self.should_end(char, stroke): 

1478 self.do_end(event, char, stroke) 

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

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

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

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

1483 c.minibufferWantsFocus() 

1484 elif k.isFKey(stroke): 

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

1486 pass 

1487 else: 

1488 self.do_char(event, char) 

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

1490 def cancel_after_state(self): 

1491 

1492 self.after_get_arg_state = None 

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

1494 def do_char(self, event, char): 

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

1496 k = self.k 

1497 k.updateLabel(event) 

1498 # Any plain key resets tab cycling. 

1499 self.reset_tab_cycling() 

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

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

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

1503 k = self.k 

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

1505 k.getArgEscapeFlag = True 

1506 if stroke and stroke in k.getArgEscapes: 

1507 k.getArgEscapeFlag = True 

1508 # Get the value. 

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

1510 if k.oneCharacterArg: 

1511 k.arg = char 

1512 else: 

1513 # A hack to support the curses gui. 

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

1515 kind, n, handler = self.after_get_arg_state 

1516 if kind: 

1517 k.setState(kind, n, handler) 

1518 self.log.deleteTab('Completion') 

1519 self.reset_tab_cycling() 

1520 if handler: 

1521 # pylint: disable=not-callable 

1522 handler(event) 

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

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

1525 returnKind, returnState, tabList, useMinibuffer 

1526 ): 

1527 """Do state 0 processing.""" 

1528 c, k = self.c, self.k 

1529 # 

1530 # Set the ga globals... 

1531 k.getArgEscapeFlag = False 

1532 self.after_get_arg_state = returnKind, returnState, handler 

1533 self.arg_completion = completion 

1534 self.cycling_prefix = None 

1535 self.handler = handler 

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

1537 # 

1538 # Set the k globals... 

1539 k.functionTail = None 

1540 k.oneCharacterArg = oneCharacter 

1541 # 

1542 # Do *not* change the label here! 

1543 # Enter the next state. 

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

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

1546 # pylint: disable=consider-using-ternary 

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

1548 if useMinibuffer: 

1549 c.minibufferWantsFocus() 

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

1551 def should_end(self, char, stroke): 

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

1553 k = self.k 

1554 return ( 

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

1556 k.oneCharacterArg or 

1557 stroke and stroke in k.getArgEscapes or 

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

1559 # The Find Easter Egg. 

1560 ) 

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

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

1563 """Trace the vars and ivars.""" 

1564 k = self.c.k 

1565 g.trace( 

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

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

1568 '\n', 

1569 'escapes', k.getArgEscapes, 

1570 'completion', self.arg_completion, 

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

1572 ) 

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

1574 def get_command(self, s): 

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

1576 # #1121. 

1577 if ' ' in s: 

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

1579 return s 

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

1581 def get_minibuffer_command_name(self): 

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

1583 s = self.get_label() 

1584 command = self.get_command(s) 

1585 tail = s[len(command) :] 

1586 return command, tail 

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

1588 def is_command(self, s): 

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

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

1591 return ' ' not in s 

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

1593 def show_tab_list(self, tabList): 

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

1595 k = self.k 

1596 self.log.clearTab(self.tabName) 

1597 d = k.computeInverseBindingDict() 

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

1599 for commandName in tabList: 

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

1601 if dataList: 

1602 for z in dataList: 

1603 pane, key = z 

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

1605 s1b = k.prettyPrintKey(key) 

1606 s1 = s1a + s1b 

1607 s2 = self.command_source(commandName) 

1608 if s2 != ' ': 

1609 legend = True 

1610 s3 = commandName 

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

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

1613 else: 

1614 # Bug fix: 2017/03/26 

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

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

1617 if legend: 

1618 aList.extend([ 

1619 '', 

1620 'legend:', 

1621 'G leoSettings.leo', 

1622 'M myLeoSettings.leo', 

1623 'L local .leo File', 

1624 ]) 

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

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

1627 def command_source(self, commandName): 

1628 """ 

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

1630 'G' leoSettings.leo 

1631 'M' myLeoSettings.leo 

1632 'L' local .leo File 

1633 ' ' not an @command or @button node 

1634 """ 

1635 c = self.c 

1636 if commandName.startswith('@'): 

1637 d = c.commandsDict 

1638 func = d.get(commandName) 

1639 if hasattr(func, 'source_c'): 

1640 c2 = func.source_c 

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

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

1643 return 'M' 

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

1645 return 'G' 

1646 return 'L' 

1647 return '?' 

1648 return ' ' 

1649 #@-others 

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

1651class KeyHandlerClass: 

1652 """ 

1653 A class to support emacs-style commands. 

1654 c.k is an instance of this class. 

1655 """ 

1656 #@+others 

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

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

1659 def __init__(self, c): 

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

1661 self.c = c 

1662 self.dispatchEvent = None 

1663 self.fnc = None 

1664 # A singleton defined in k.finishCreate. 

1665 self.getArgInstance = None 

1666 # A singleton defined in k.finishCreate. 

1667 self.inited = False 

1668 # Set at end of finishCreate. 

1669 self.killedBindings = [] 

1670 # A list of commands whose bindings have been set to None in the local file. 

1671 self.replace_meta_with_alt = False 

1672 # True: (Mac only) swap Meta and Alt keys. 

1673 self.w = None 

1674 # Note: will be None for NullGui. 

1675 # Generalize... 

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

1677 self.altX_prompt = 'full-command: ' 

1678 # Access to data types defined in leoKeys.py 

1679 self.KeyStroke = g.KeyStroke 

1680 # Define all ivars... 

1681 self.defineExternallyVisibleIvars() 

1682 self.defineInternalIvars() 

1683 self.reloadSettings() 

1684 self.defineSingleLineCommands() 

1685 self.defineMultiLineCommands() 

1686 self.autoCompleter = AutoCompleterClass(self) 

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

1688 self.setDefaultUnboundKeyAction() 

1689 self.setDefaultEditingAction() 

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

1691 def defineExternallyVisibleIvars(self): 

1692 

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

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

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

1696 self.getArgEscapes = [] 

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

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

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

1700 

1701 # Remove ??? 

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

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

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

1705 def defineInternalIvars(self): 

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

1707 self.abbreviationsDict = {} 

1708 # Abbreviations created by @alias nodes. 

1709 # Previously defined bindings... 

1710 self.bindingsDict = {} 

1711 # Keys are Tk key names, values are lists of BindingInfo objects. 

1712 # Previously defined binding tags. 

1713 self.bindtagsDict = {} 

1714 # Keys are strings (the tag), values are 'True' 

1715 self.commandHistory = [] 

1716 self.commandIndex = 0 

1717 # List/stack of previously executed commands. 

1718 # Up arrow will select commandHistory[commandIndex] 

1719 self.masterBindingsDict = {} 

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

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

1722 self.masterGuiBindingsDict = {} 

1723 # Keys are strokes; value is True; 

1724 # Special bindings for k.fullCommand... 

1725 self.mb_copyKey = None 

1726 self.mb_pasteKey = None 

1727 self.mb_cutKey = None 

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

1729 self.abortAllModesKey = None 

1730 self.autoCompleteForceKey = None 

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

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

1733 # Used by k.masterKeyHandler... 

1734 self.stroke = None 

1735 self.mb_event = None 

1736 self.mb_history = [] 

1737 self.mb_help = False 

1738 self.mb_helpHandler = None 

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

1740 # self.getArgEscapes = [] 

1741 # self.getArgEscapeFlag 

1742 # For onIdleTime... 

1743 self.idleCount = 0 

1744 # For modes... 

1745 self.modeBindingsDict = {} 

1746 self.modeWidget = None 

1747 self.silentMode = False 

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

1749 def defineMultiLineCommands(self): 

1750 k = self 

1751 k.multiLineCommandList = [ 

1752 # EditCommandsClass 

1753 'add-space-to-lines', 

1754 'add-tab-to-lines', 

1755 'back-page', 

1756 'back-page-extend-selection', 

1757 'back-paragraph', 

1758 'back-paragraph-extend-selection', 

1759 'back-sentence', 

1760 'back-sentence-extend-selection', 

1761 'backward-kill-paragraph', 

1762 'beginning-of-buffer', 

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

1764 'center-line', 

1765 'center-region', 

1766 'clean-all-lines', 

1767 'clean-lines', 

1768 'downcase-region', 

1769 'end-of-buffer', 

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

1771 'extend-to-paragraph', 

1772 'extend-to-sentence', 

1773 'fill-paragraph', 

1774 'fill-region', 

1775 'fill-region-as-paragraph', 

1776 'flush-lines', 

1777 'forward-page', 

1778 'forward-page-extend-selection', 

1779 'forward-paragraph', 

1780 'forward-paragraph-extend-selection', 

1781 'forward-sentence', 

1782 'forward-sentence-extend-selection', 

1783 'indent-relative', 

1784 'indent-rigidly', 

1785 'indent-to-comment-column', 

1786 'move-lines-down', 

1787 'move-lines-up', 

1788 'next-line', 

1789 'next-line-extend-selection', 

1790 'previous-line', 

1791 'previous-line-extend-selection', 

1792 'remove-blank-lines', 

1793 'remove-space-from-lines', 

1794 'remove-tab-from-lines', 

1795 'reverse-region', 

1796 'reverse-sort-lines', 

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

1798 'scroll-down-half-page', 

1799 'scroll-down-line', 

1800 'scroll-down-page', 

1801 'scroll-up-half-page', 

1802 'scroll-up-line', 

1803 'scroll-up-page', 

1804 'simulate-begin-drag', 

1805 'simulate-end-drag', 

1806 'sort-columns', 

1807 'sort-fields', 

1808 'sort-lines', 

1809 'sort-lines-ignoring-case', 

1810 'split-line', 

1811 'tabify', 

1812 'transpose-lines', 

1813 'untabify', 

1814 'upcase-region', 

1815 # KeyHandlerCommandsClass 

1816 'repeat-complex-command', 

1817 # KillBufferCommandsClass 

1818 'backward-kill-sentence', 

1819 'kill-sentence', 

1820 'kill-region', 

1821 'kill-region-save', 

1822 # QueryReplaceCommandsClass 

1823 'query-replace', 

1824 'query-replace-regex', 

1825 # RectangleCommandsClass 

1826 'clear-rectangle', 

1827 'close-rectangle', 

1828 'delete-rectangle', 

1829 'kill-rectangle', 

1830 'open-rectangle', 

1831 'string-rectangle', 

1832 'yank-rectangle', 

1833 # SearchCommandsClass 

1834 'change', 

1835 'change-then-find', 

1836 'find-next', 

1837 'find-prev', 

1838 ] 

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

1840 def defineSingleLineCommands(self): 

1841 k = self 

1842 # These commands can be executed in the minibuffer. 

1843 k.singleLineCommandList = [ 

1844 # EditCommandsClass 

1845 'back-to-indentation', 

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

1847 'back-char', 

1848 'back-char-extend-selection', 

1849 'back-word', 

1850 'back-word-extend-selection', 

1851 'backward-delete-char', 

1852 'backward-find-character', 

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

1854 'beginning-of-line', 

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

1856 'capitalize-word', 

1857 'delete-char', 

1858 'delete-indentation', 

1859 'delete-spaces', 

1860 'downcase-word', 

1861 'end-of-line', 

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

1863 'escape', 

1864 'exchange-point-mark', 

1865 'extend-to-line', 

1866 'extend-to-word', 

1867 'find-character', 

1868 'find-character-extend-selection', 

1869 'find-word', 

1870 'find-word-in-line', 

1871 'forward-char', 

1872 'forward-char-extend-selection', 

1873 'forward-end-word', 

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

1875 'forward-word', 

1876 'forward-word-extend-selection', 

1877 'insert-newline', 

1878 'insert-parentheses', 

1879 'move-past-close', 

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

1881 'newline-and-indent', 

1882 'select-all', 

1883 'transpose-chars', 

1884 'transpose-words', 

1885 'upcase-word', 

1886 # LeoFind class. 

1887 'start-search', # #2041. 

1888 # KeyHandlerCommandsClass 

1889 'full-command', # #2041. 

1890 # 'auto-complete', 

1891 # 'negative-argument', 

1892 # 'number-command', 

1893 # 'number-command-0', 

1894 # 'number-command-1', 

1895 # 'number-command-2', 

1896 # 'number-command-3', 

1897 # 'number-command-4', 

1898 # 'number-command-5', 

1899 # 'number-command-6', 

1900 # 'number-command-7', 

1901 # 'number-command-8', 

1902 # 'universal-argument', 

1903 # KillBufferCommandsClass 

1904 'backward-kill-word', 

1905 'kill-line', 

1906 'kill-word', 

1907 'kill-ws', 

1908 'yank', 

1909 'yank-pop', 

1910 'zap-to-character', 

1911 # leoCommands 

1912 'cut-text', 

1913 'copy-text', 

1914 'paste-text', 

1915 # MacroCommandsClass 

1916 'call-last-kbd-macro', 

1917 # search commands 

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

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

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

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

1922 'toggle-find-collapses_nodes', 

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

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

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

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

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

1928 'toggle-find-regex-option', 

1929 'toggle-find-reverse-option', 

1930 'toggle-find-word-option', 

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

1932 ] 

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

1934 def finishCreate(self): 

1935 """ 

1936 Complete the construction of the keyHandler class. 

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

1938 """ 

1939 c, k = self.c, self 

1940 k.w = c.frame.miniBufferWidget 

1941 # Will be None for NullGui. 

1942 k.fnc = FileNameChooser(c) 

1943 # A singleton. Defined here so that c.k will exist. 

1944 k.getArgInstance = GetArg(c) 

1945 # a singleton. Defined here so that c.k will exist. 

1946 k.makeAllBindings() 

1947 # Important: This must be called this now, 

1948 # even though LM.laod calls g.app.makeAllBindings later. 

1949 k.initCommandHistory() 

1950 k.inited = True 

1951 k.setDefaultInputState() 

1952 k.resetLabel() 

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

1954 def oops(self): 

1955 

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

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

1958 def reloadSettings(self): 

1959 # Part 1: These were in the ctor. 

1960 c = self.c 

1961 getBool = c.config.getBool 

1962 getColor = c.config.getColor 

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

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

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

1966 self.minibuffer_background_color = getColor( 

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

1968 self.minibuffer_foreground_color = getColor( 

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

1970 self.minibuffer_warning_color = getColor( 

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

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

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

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

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

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

1977 # Part 2: These were in finishCreate. 

1978 # Set mode colors used by k.setInputState. 

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

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

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

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

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

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

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

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

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

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

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

1990 def setDefaultEditingAction(self): 

1991 c = self.c 

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

1993 action.lower() 

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

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

1996 action = 'insert' 

1997 self.defaultEditingAction = action 

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

1999 def setDefaultUnboundKeyAction(self, allowCommandState=True): 

2000 c, k = self.c, self 

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

2002 defaultAction.lower() 

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

2004 self.unboundKeyAction = 'insert' 

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

2006 self.unboundKeyAction = defaultAction 

2007 else: 

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

2009 self.unboundKeyAction = 'insert' 

2010 self.defaultUnboundKeyAction = self.unboundKeyAction 

2011 k.setInputState(self.defaultUnboundKeyAction) 

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

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

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

2015 """ 

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

2017 

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

2019 and k.bindingsDict. 

2020 

2021 tag gives the source of the binding. 

2022 

2023 Return True if the binding was made successfully. 

2024 """ 

2025 k = self 

2026 if not shortcut: 

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

2028 return False 

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

2030 return False 

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

2032 try: 

2033 if not shortcut: 

2034 stroke = None 

2035 elif g.isStroke(shortcut): 

2036 stroke = shortcut 

2037 assert stroke.s, stroke 

2038 else: 

2039 assert shortcut, g.callers() 

2040 stroke = g.KeyStroke(binding=shortcut) 

2041 bi = g.BindingInfo( 

2042 kind=tag, 

2043 pane=pane, 

2044 func=callback, 

2045 commandName=commandName, 

2046 stroke=stroke) 

2047 if shortcut: 

2048 k.bindKeyToDict(pane, shortcut, bi) 

2049 # Updates k.masterBindingsDict 

2050 if shortcut and not modeFlag: 

2051 aList = k.remove_conflicting_definitions( 

2052 aList, commandName, pane, shortcut) 

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

2054 aList.append(bi) 

2055 if shortcut: 

2056 assert stroke 

2057 k.bindingsDict[stroke] = aList 

2058 return True 

2059 except Exception: # Could be a user error. 

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

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

2062 g.print_exception() 

2063 g.app.menuWarningsGiven = True 

2064 return False 

2065 

2066 bindShortcut = bindKey # For compatibility 

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

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

2069 """ 

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

2071 pane can be made. 

2072 """ 

2073 # k = self 

2074 assert g.isStroke(stroke) 

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

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

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

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

2079 return False 

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

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

2082 return False 

2083 return True 

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

2085 def kill_one_shortcut(self, stroke): 

2086 """ 

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

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

2089 """ 

2090 c = self.c 

2091 lm = g.app.loadManager 

2092 if 0: 

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

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

2095 else: 

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

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

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

2099 return 

2100 assert g.isStroke(stroke), stroke 

2101 d = c.config.shortcutsDict 

2102 if d is None: 

2103 d = g.TypedDict( # was TypedDictOfLists. 

2104 name='empty shortcuts dict', 

2105 keyType=type('commandName'), 

2106 valType=g.BindingInfo, 

2107 ) 

2108 inv_d = lm.invert(d) 

2109 inv_d[stroke] = [] 

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

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

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

2113 

2114 k = self 

2115 result = [] 

2116 for bi in aList: 

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

2118 k.kill_one_shortcut(shortcut) 

2119 else: 

2120 result.append(bi) 

2121 return result 

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

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

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

2125 # New in Leo 4.4.1: Allow redefintions. 

2126 # Called from makeBindingsFromCommandsDict. 

2127 k = self 

2128 assert g.isStroke(stroke), stroke 

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

2130 d[stroke] = bi 

2131 k.masterBindingsDict[pane] = d 

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

2133 def bindOpenWith(self, d): 

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

2135 c, k = self.c, self 

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

2137 name = d.get('name') 

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

2139 

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

2141 return c.openWith(d=d) 

2142 

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

2144 

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

2146 k.registerCommand( 

2147 allowBinding=True, 

2148 commandName=commandName, 

2149 func=openWithCallback, 

2150 pane='all', 

2151 shortcut=shortcut, 

2152 ) 

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

2154 def checkBindings(self): 

2155 """ 

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

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

2158 c, k = self.c, self 

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

2160 return 

2161 for name in sorted(c.commandsDict): 

2162 abbrev = k.abbreviationsDict.get(name) 

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

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

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

2166 if abbrev: 

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

2168 else: 

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

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

2171 def completeAllBindings(self, w=None): 

2172 """ 

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

2174 

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

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

2177 """ 

2178 k = self 

2179 for stroke in k.bindingsDict: 

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

2181 k.makeMasterGuiBinding(stroke, w=w) 

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

2183 def completeAllBindingsForWidget(self, w): 

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

2185 k = self 

2186 for stroke in k.bindingsDict: 

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

2188 k.makeMasterGuiBinding(stroke, w=w) 

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

2190 def dumpMasterBindingsDict(self): 

2191 """Dump k.masterBindingsDict.""" 

2192 k = self 

2193 d = k.masterBindingsDict 

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

2195 for key in sorted(d): 

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

2197 d2 = d.get(key) 

2198 for key2 in sorted(d2): 

2199 bi = d2.get(key2) 

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

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

2202 def initAbbrev(self): 

2203 c = self.c 

2204 d = c.config.getAbbrevDict() 

2205 if d: 

2206 for key in d: 

2207 commandName = d.get(key) 

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

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

2210 else: 

2211 self.initOneAbbrev(commandName, key) 

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

2213 def initOneAbbrev(self, commandName, key): 

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

2215 c = self.c 

2216 if c.commandsDict.get(key): 

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

2218 else: 

2219 func = c.commandsDict.get(commandName) 

2220 if func: 

2221 c.commandsDict[key] = func 

2222 else: 

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

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

2225 def initSpecialIvars(self): 

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

2227 c, k = self.c, self 

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

2229 for ivar, commandName in ( 

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

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

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

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

2234 ): 

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

2236 aList, found = aList or [], False 

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

2238 for bi in aList: 

2239 if bi.pane == pane: 

2240 setattr(k, ivar, bi.stroke) 

2241 found = True 

2242 break 

2243 if not found and warn: 

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

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

2246 def makeAllBindings(self): 

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

2248 k = self 

2249 k.bindingsDict = {} 

2250 k.addModeCommands() 

2251 k.makeBindingsFromCommandsDict() 

2252 k.initSpecialIvars() 

2253 k.initAbbrev() 

2254 k.completeAllBindings() 

2255 k.checkBindings() 

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

2257 def makeBindingsFromCommandsDict(self): 

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

2259 c, k = self.c, self 

2260 d = c.commandsDict 

2261 # 

2262 # Step 1: Create d2. 

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

2264 d2 = g.TypedDict( # was TypedDictOfLists. 

2265 name='makeBindingsFromCommandsDict helper dict', 

2266 keyType=g.KeyStroke, 

2267 valType=g.BindingInfo, 

2268 ) 

2269 for commandName in sorted(d): 

2270 command = d.get(commandName) 

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

2272 for bi in aList: 

2273 # Important: bi.stroke is already canonicalized. 

2274 stroke = bi.stroke 

2275 bi.commandName = commandName 

2276 if stroke: 

2277 assert g.isStroke(stroke) 

2278 d2.add_to_list(stroke, bi) 

2279 # 

2280 # Step 2: make the bindings. 

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

2282 aList2 = d2.get(stroke) 

2283 for bi in aList2: 

2284 commandName = bi.commandName 

2285 command = c.commandsDict.get(commandName) 

2286 tag = bi.kind 

2287 pane = bi.pane 

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

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

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

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

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

2293 c, k = self.c, self 

2294 f = c.frame 

2295 if w: 

2296 widgets = [w] 

2297 else: 

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

2299 bindingWidget = ( 

2300 f.tree 

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

2302 and f.tree.bindingWidget 

2303 or None) 

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

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

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

2307 for w in widgets: 

2308 if not w: 

2309 continue 

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

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

2312 if w not in aList: 

2313 aList.append(w) 

2314 k.masterGuiBindingsDict[stroke] = aList 

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

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

2317 def addToCommandHistory(self, commandName): 

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

2319 k = self 

2320 h = k.commandHistory 

2321 if commandName in h: 

2322 h.remove(commandName) 

2323 h.append(commandName) 

2324 k.commandIndex = None 

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

2326 def commandHistoryFwd(self): 

2327 """ 

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

2329 if necessary 

2330 """ 

2331 k = self 

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

2333 if h: 

2334 commandName = '' 

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

2336 # fall off the bottom 

2337 i = None 

2338 elif i is not None: 

2339 # move to next down in list 

2340 i += 1 

2341 commandName = h[i] 

2342 k.commandIndex = i 

2343 k.setLabel(k.mb_prefix + commandName) 

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

2345 def commandHistoryBackwd(self): 

2346 """ 

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

2348 if we are there 

2349 """ 

2350 k = self 

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

2352 if h: 

2353 if i is None: 

2354 # first time in - set to last entry 

2355 i = len(h) - 1 

2356 elif i > 0: 

2357 i -= 1 

2358 commandName = h[i] 

2359 k.commandIndex = i 

2360 k.setLabel(k.mb_prefix + commandName) 

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

2362 def initCommandHistory(self): 

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

2364 k, c = self, self.c 

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

2366 for command in reversed(aList): 

2367 k.addToCommandHistory(command) 

2368 

2369 def resetCommandHistory(self): 

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

2371 we are pointing 'past' the last entry 

2372 """ 

2373 self.commandIndex = None 

2374 # 

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

2376 def sortCommandHistory(self): 

2377 """Sort the command history.""" 

2378 k = self 

2379 k.commandHistory.sort() 

2380 k.commandIndex = None 

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

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

2383 @cmd('full-command') 

2384 def fullCommand( 

2385 self, 

2386 event, 

2387 specialStroke=None, 

2388 specialFunc=None, 

2389 help=False, 

2390 helpHandler=None, 

2391 ): 

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

2393 try: 

2394 c, k = self.c, self 

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

2396 helpPrompt = 'Help for command: ' 

2397 c.check_event(event) 

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

2399 if state == 0: 

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

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

2402 prompt = helpPrompt if help else k.altX_prompt 

2403 k.setLabelBlue(prompt) 

2404 k.mb_help = help 

2405 k.mb_helpHandler = helpHandler 

2406 c.minibufferWantsFocus() 

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

2408 pass 

2409 elif char == 'Escape': 

2410 k.keyboardQuit() 

2411 elif char == 'Down': 

2412 k.commandHistoryFwd() 

2413 elif char == 'Up': 

2414 k.commandHistoryBackwd() 

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

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

2417 w = k.mb_event and k.mb_event.w 

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

2419 sel1, sel2 = w.getSelectionRange() 

2420 ins = w.getInsertPoint() 

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

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

2423 else: 

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

2425 # 2016/04/27 

2426 if k.mb_help: 

2427 s = k.getLabel() 

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

2429 k.clearState() 

2430 k.resetLabel() 

2431 if k.mb_helpHandler: 

2432 k.mb_helpHandler(commandName) 

2433 else: 

2434 s = k.getLabel(ignorePrompt=True) 

2435 commandName = s.strip() 

2436 ok = k.callAltXFunction(k.mb_event) 

2437 if ok: 

2438 k.addToCommandHistory(commandName) 

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

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

2441 c.minibufferWantsFocus() 

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

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

2444 c.minibufferWantsFocus() 

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

2446 if specialStroke: 

2447 g.trace(specialStroke) 

2448 specialFunc() 

2449 c.minibufferWantsFocus() 

2450 else: 

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

2452 k.mb_tabList = [] 

2453 k.updateLabel(event) 

2454 k.mb_tabListPrefix = k.getLabel() 

2455 c.minibufferWantsFocus() 

2456 except Exception: 

2457 g.es_exception() 

2458 self.keyboardQuit() 

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

2460 def callAltXFunction(self, event): 

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

2462 c, k = self.c, self 

2463 k.mb_tabList = [] 

2464 commandName, tail = k.getMinibufferCommandName() 

2465 k.functionTail = tail 

2466 if commandName and commandName.isdigit(): 

2467 # The line number Easter Egg. 

2468 

2469 def func(event=None): 

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

2471 

2472 else: 

2473 func = c.commandsDict.get(commandName) 

2474 if func: 

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

2476 k.clearState() 

2477 k.resetLabel() 

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

2479 k.mb_history.insert(0, commandName) 

2480 w = event and event.widget 

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

2482 # In a headline that is being edited. 

2483 c.endEditing() 

2484 c.bodyWantsFocusNow() 

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

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

2487 else: 

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

2489 try: 

2490 func(event) 

2491 except Exception: 

2492 g.es_exception() 

2493 return True 

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

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

2496 return False 

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

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

2499 def menuCommandKey(self, event=None): 

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

2501 pass 

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

2503 @cmd('show-bindings') 

2504 def printBindings(self, event=None): 

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

2506 c, k = self.c, self 

2507 d = k.bindingsDict 

2508 tabName = 'Bindings' 

2509 c.frame.log.clearTab(tabName) 

2510 legend = '''\ 

2511 legend: 

2512 [ ] leoSettings.leo 

2513 [D] default binding 

2514 [F] loaded .leo File 

2515 [M] myLeoSettings.leo 

2516 [@] @mode, @button, @command 

2517 

2518 ''' 

2519 if not d: 

2520 return g.es('no bindings') 

2521 legend = textwrap.dedent(legend) 

2522 data = [] 

2523 for stroke in sorted(d): 

2524 assert g.isStroke(stroke), stroke 

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

2526 for bi in aList: 

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

2528 s2 = k.prettyPrintKey(stroke) 

2529 s3 = bi.commandName 

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

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

2532 # Print keys by type. 

2533 result = [] 

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

2535 for prefix in ( 

2536 'Alt+Ctrl+Shift', 'Alt+Ctrl', 'Alt+Shift', 'Alt', # 'Alt+Key': done by Alt. 

2537 'Ctrl+Meta+Shift', 'Ctrl+Meta', 'Ctrl+Shift', 'Ctrl', # Ctrl+Key: done by Ctrl. 

2538 'Meta+Key', 'Meta+Shift', 'Meta', 

2539 'Shift', 

2540 'F', # #1972 

2541 # Careful: longer prefixes must come before shorter prefixes. 

2542 ): 

2543 data2 = [] 

2544 for item in data: 

2545 s1, s2, s3, s4 = item 

2546 if s2.startswith(prefix): 

2547 data2.append(item) 

2548 result.append(f"{prefix} keys...\n") 

2549 self.printBindingsHelper(result, data2, prefix=prefix) 

2550 # Remove all the items in data2 from data. 

2551 # This must be done outside the iterator on data. 

2552 for item in data2: 

2553 data.remove(item) 

2554 # Print all plain bindings. 

2555 result.append('Plain keys...\n') 

2556 self.printBindingsHelper(result, data, prefix=None) 

2557 if not g.unitTesting: 

2558 g.es_print('', ''.join(result), tabName=tabName) 

2559 k.showStateAndMode() 

2560 return result # for unit test. 

2561 #@+node:ekr.20061031131434.120: *5* printBindingsHelper 

2562 def printBindingsHelper(self, result, data, prefix): 

2563 """Helper for k.printBindings""" 

2564 c, lm = self.c, g.app.loadManager 

2565 data.sort(key=lambda x: x[1]) 

2566 data2, n = [], 0 

2567 for pane, key, commandName, kind in data: 

2568 key = key.replace('+Key', '') 

2569 letter = lm.computeBindingLetter(c, kind) 

2570 pane = f"{pane if pane else 'all':4}: " 

2571 left = pane + key # pane and shortcut fields 

2572 n = max(n, len(left)) 

2573 data2.append((letter, left, commandName),) 

2574 for z in data2: 

2575 letter, left, commandName = z 

2576 result.append('%s %*s %s\n' % (letter, -n, left, commandName)) 

2577 if data: 

2578 result.append('\n') 

2579 #@+node:ekr.20120520174745.9867: *4* k.printButtons 

2580 @cmd('show-buttons') 

2581 def printButtons(self, event=None): 

2582 """Print all @button and @command commands, their bindings and their source.""" 

2583 c = self.c 

2584 tabName = '@buttons && @commands' 

2585 c.frame.log.clearTab(tabName) 

2586 

2587 def put(s): 

2588 g.es('', s, tabName=tabName) 

2589 

2590 data = [] 

2591 for aList in [c.config.getButtons(), c.config.getCommands()]: 

2592 for z in aList: 

2593 p, script = z 

2594 c = p.v.context 

2595 tag = 'M' if c.shortFileName().endswith('myLeoSettings.leo') else 'G' 

2596 data.append((p.h, tag),) 

2597 for aList in [g.app.config.atLocalButtonsList, g.app.config.atLocalCommandsList]: 

2598 for p in aList: 

2599 data.append((p.h, 'L'),) 

2600 result = [f"{z[1]} {z[0]}" for z in sorted(data)] 

2601 result.extend([ 

2602 '', 

2603 'legend:', 

2604 'G leoSettings.leo', 

2605 'L local .leo File', 

2606 'M myLeoSettings.leo', 

2607 ]) 

2608 put('\n'.join(result)) 

2609 #@+node:ekr.20061031131434.121: *4* k.printCommands 

2610 @cmd('show-commands') 

2611 def printCommands(self, event=None): 

2612 """Print all the known commands and their bindings, if any.""" 

2613 c, k = self.c, self 

2614 tabName = 'Commands' 

2615 c.frame.log.clearTab(tabName) 

2616 inverseBindingDict = k.computeInverseBindingDict() 

2617 data, n = [], 0 

2618 for commandName in sorted(c.commandsDict): 

2619 dataList = inverseBindingDict.get(commandName, [('', ''),]) 

2620 for z in dataList: 

2621 pane, key = z 

2622 pane = f"{pane} " if pane != 'all:' else '' 

2623 key = k.prettyPrintKey(key).replace('+Key', '') 

2624 s1 = pane + key 

2625 s2 = commandName 

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

2627 data.append((s1, s2),) 

2628 # This isn't perfect in variable-width fonts. 

2629 lines = ['%*s %s\n' % (-n, z1, z2) for z1, z2 in data] 

2630 g.es_print('', ''.join(lines), tabName=tabName) 

2631 #@+node:ekr.20061031131434.122: *4* k.repeatComplexCommand 

2632 @cmd('repeat-complex-command') 

2633 def repeatComplexCommand(self, event): 

2634 """Repeat the previously executed minibuffer command.""" 

2635 k = self 

2636 # #2286: Always call k.fullCommand. 

2637 k.setState('getArg', 0, handler=k.fullCommand) 

2638 k.fullCommand(event) # #2334 

2639 if not k.mb_history: 

2640 k.mb_history = list(reversed(k.commandHistory)) 

2641 command = k.mb_history[0] if k.mb_history else '' 

2642 k.setLabelBlue(f"{k.altX_prompt}", protect=True) 

2643 k.extendLabel(command, select=False, protect=False) 

2644 #@+node:ekr.20061031131434.123: *4* k.set-xxx-State 

2645 @cmd('set-command-state') 

2646 def setCommandState(self, event): 

2647 """Enter the 'command' editing state.""" 

2648 k = self 

2649 k.setInputState('command', set_border=True) 

2650 # This command is also valid in headlines. 

2651 # k.c.bodyWantsFocus() 

2652 k.showStateAndMode() 

2653 

2654 @cmd('set-insert-state') 

2655 def setInsertState(self, event): 

2656 """Enter the 'insert' editing state.""" 

2657 k = self 

2658 k.setInputState('insert', set_border=True) 

2659 # This command is also valid in headlines. 

2660 # k.c.bodyWantsFocus() 

2661 k.showStateAndMode() 

2662 

2663 @cmd('set-overwrite-state') 

2664 def setOverwriteState(self, event): 

2665 """Enter the 'overwrite' editing state.""" 

2666 k = self 

2667 k.setInputState('overwrite', set_border=True) 

2668 # This command is also valid in headlines. 

2669 # k.c.bodyWantsFocus() 

2670 k.showStateAndMode() 

2671 #@+node:ekr.20061031131434.124: *4* k.toggle-input-state 

2672 @cmd('toggle-input-state') 

2673 def toggleInputState(self, event=None): 

2674 """The toggle-input-state command.""" 

2675 c, k = self.c, self 

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

2677 state = k.unboundKeyAction 

2678 if default == 'insert': 

2679 state = 'command' if state == 'insert' else 'insert' 

2680 elif default == 'overwrite': 

2681 state = 'command' if state == 'overwrite' else 'overwrite' 

2682 else: 

2683 state = 'insert' if state == 'command' else 'command' # prefer insert to overwrite. 

2684 k.setInputState(state) 

2685 k.showStateAndMode() 

2686 #@+node:ekr.20061031131434.125: *3* k.Externally visible helpers 

2687 #@+node:ekr.20140816165728.18968: *4* Wrappers for GetArg methods 

2688 # New in Leo 5.4 

2689 

2690 def getNextArg(self, handler): 

2691 """ 

2692 Get the next arg. For example, after a Tab in the find commands. 

2693 See the docstring for k.get1Arg for examples of its use. 

2694 """ 

2695 # Replace the current handler. 

2696 self.getArgInstance.after_get_arg_state = ('getarg', 1, handler) 

2697 self.c.minibufferWantsFocusNow() 

2698 

2699 # New in Leo 5.4 

2700 

2701 def get1Arg(self, event, handler, 

2702 prefix=None, tabList=None, completion=True, oneCharacter=False, 

2703 stroke=None, useMinibuffer=True 

2704 ): 

2705 #@+<< docstring for k.get1arg >> 

2706 #@+node:ekr.20161020031633.1: *5* << docstring for k.get1arg >> 

2707 """ 

2708 k.get1Arg: Handle the next character the user types when accumulating 

2709 a user argument from the minibuffer. Ctrl-G will abort this processing 

2710 at any time. 

2711 

2712 Commands should use k.get1Arg to get the first minibuffer argument and 

2713 k.getNextArg to get all other arguments. 

2714 

2715 Before going into the many details, let's look at some examples. This 

2716 code will work in any class having a 'c' ivar bound to a commander. 

2717 

2718 Example 1: get one argument from the user: 

2719 

2720 @g.command('my-command') 

2721 def myCommand(self, event): 

2722 k = self.c.k 

2723 k.setLabelBlue('prompt: ') 

2724 k.get1Arg(event, handler=self.myCommand1) 

2725 

2726 def myCommand1(self, event): 

2727 k = self.c.k 

2728 # k.arg contains the argument. 

2729 # Finish the command. 

2730 ... 

2731 # Reset the minibuffer. 

2732 k.clearState() 

2733 k.resetLabel() 

2734 k.showStateAndMode() 

2735 

2736 Example 2: get two arguments from the user: 

2737 

2738 @g.command('my-command') 

2739 def myCommand(self, event): 

2740 k = self.c.k 

2741 k.setLabelBlue('first prompt: ') 

2742 k.get1Arg(event, handler=self.myCommand1) 

2743 

2744 def myCommand1(self, event): 

2745 k = self.c.k 

2746 self.arg1 = k.arg 

2747 k.extendLabel(' second prompt: ', select=False, protect=True) 

2748 k.getNextArg(handler=self.myCommand2) 

2749 

2750 def myCommand2(self, event): 

2751 k = self.c.k 

2752 # k.arg contains second argument. 

2753 # Finish the command, using self.arg1 and k.arg. 

2754 ... 

2755 # Reset the minibuffer. 

2756 k.clearState() 

2757 k.resetLabel() 

2758 k.showStateAndMode() 

2759 

2760 k.get1Arg and k.getNextArg are a convenience methods. They simply pass 

2761 their arguments to the get_arg method of the singleton GetArg 

2762 instance. This docstring describes k.get1arg and k.getNextArg as if 

2763 they were the corresponding methods of the GetArg class. 

2764 

2765 k.get1Arg is a state machine. Logically, states are tuples (kind, n, 

2766 handler) though they aren't represented that way. When the state 

2767 machine in the GetArg class is active, the kind is 'getArg'. This 

2768 constant has special meaning to Leo's key-handling code. 

2769 

2770 The arguments to k.get1Arg are as follows: 

2771 

2772 event: The event passed to the command. 

2773 

2774 handler=None, An executable. k.get1arg calls handler(event) 

2775 when the user completes the argument by typing 

2776 <Return> or (sometimes) <tab>. 

2777 

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

2779 

2780 completion=True: True if completions are enabled. 

2781 

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

2783 

2784 stroke=None: The incoming key stroke. 

2785 

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

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

2788 

2789 """ 

2790 #@-<< docstring for k.get1arg >> 

2791 returnKind, returnState = None, None 

2792 assert handler, g.callers() 

2793 self.getArgInstance.get_arg(event, returnKind, returnState, handler, 

2794 tabList, completion, oneCharacter, stroke, useMinibuffer) 

2795 

2796 def getArg(self, event, 

2797 returnKind=None, returnState=None, handler=None, 

2798 prefix=None, tabList=None, completion=True, oneCharacter=False, 

2799 stroke=None, useMinibuffer=True 

2800 ): 

2801 """Convenience method mapping k.getArg to ga.get_arg.""" 

2802 self.getArgInstance.get_arg(event, returnKind, returnState, handler, 

2803 tabList, completion, oneCharacter, stroke, useMinibuffer) 

2804 

2805 def doBackSpace(self, tabList, completion=True): 

2806 """Convenience method mapping k.doBackSpace to ga.do_back_space.""" 

2807 self.getArgInstance.do_back_space(tabList, completion) 

2808 

2809 def doTabCompletion(self, tabList): 

2810 """Convenience method mapping k.doTabCompletion to ga.do_tab.""" 

2811 self.getArgInstance.do_tab(tabList) 

2812 

2813 def getMinibufferCommandName(self): 

2814 """ 

2815 Convenience method mapping k.getMinibufferCommandName to 

2816 ga.get_minibuffer_command_name. 

2817 """ 

2818 return self.getArgInstance.get_minibuffer_command_name() 

2819 #@+node:ekr.20061031131434.130: *4* k.keyboardQuit 

2820 @cmd('keyboard-quit') 

2821 def keyboardQuit(self, event=None, setFocus=True): 

2822 """Clears the state and the minibuffer label.""" 

2823 c, k = self.c, self 

2824 if g.app.quitting: 

2825 return 

2826 c.endEditing() 

2827 # Completely clear the mode. 

2828 if setFocus: 

2829 c.frame.log.deleteTab('Mode') 

2830 c.frame.log.hideTab('Completion') 

2831 if k.inputModeName: 

2832 k.endMode() 

2833 # Complete clear the state. 

2834 k.state.kind = None 

2835 k.state.n = None 

2836 k.clearState() 

2837 k.resetLabel() 

2838 if setFocus: 

2839 c.bodyWantsFocus() 

2840 # At present, only the auto-completer suppresses this. 

2841 k.setDefaultInputState() 

2842 if c.vim_mode and c.vimCommands: 

2843 c.vimCommands.reset(setFocus=setFocus) 

2844 else: 

2845 # This was what caused the unwanted scrolling. 

2846 k.showStateAndMode(setFocus=setFocus) 

2847 k.resetCommandHistory() 

2848 #@+node:ekr.20061031131434.126: *4* k.manufactureKeyPressForCommandName (only for unit tests!) 

2849 def manufactureKeyPressForCommandName(self, w, commandName): 

2850 """ 

2851 Implement a command by passing a keypress to the gui. 

2852 

2853 **Only unit tests use this method.** 

2854 """ 

2855 c, k = self.c, self 

2856 # Unit tests do not ordinarily read settings files. 

2857 stroke = k.getStrokeForCommandName(commandName) 

2858 if stroke is None: 

2859 # Create the stroke and binding info data. 

2860 stroke = g.KeyStroke('Ctrl+F1') 

2861 bi = g.BindingInfo( 

2862 kind='manufactured-binding', 

2863 commandName=commandName, 

2864 func=None, 

2865 pane='all', 

2866 stroke=stroke, 

2867 ) 

2868 # Make the binding! 

2869 # k.masterBindingsDict keys: scope names; values: masterBindingDicts (3) 

2870 # Interior masterBindingDicts: Keys are strokes; values are BindingInfo objects. 

2871 d = k.masterBindingsDict 

2872 d2 = d.get('all', {}) 

2873 d2[stroke] = bi 

2874 d['all'] = d2 

2875 assert g.isStroke(stroke), (commandName, stroke.__class__.__name__) 

2876 shortcut = stroke.s 

2877 shortcut = g.checkUnicode(shortcut) 

2878 if shortcut and w: 

2879 g.app.gui.set_focus(c, w) 

2880 g.app.gui.event_generate(c, None, shortcut, w) 

2881 else: 

2882 message = f"no shortcut for {commandName}" 

2883 if g.unitTesting: 

2884 raise AttributeError(message) 

2885 g.error(message) 

2886 #@+node:ekr.20071212104050: *4* k.overrideCommand 

2887 def overrideCommand(self, commandName, func): 

2888 # Override entries in c.k.masterBindingsDict 

2889 k = self 

2890 d = k.masterBindingsDict 

2891 for key in d: 

2892 d2 = d.get(key) 

2893 for key2 in d2: 

2894 bi = d2.get(key2) 

2895 if bi.commandName == commandName: 

2896 bi.func = func 

2897 d2[key2] = bi 

2898 #@+node:ekr.20061031131434.131: *4* k.registerCommand 

2899 def registerCommand(self, commandName, func, 

2900 allowBinding=False, 

2901 pane='all', 

2902 shortcut=None, # Must be None unless allowBindings is True. 

2903 ** kwargs 

2904 ): 

2905 """ 

2906 Make the function available as a minibuffer command. 

2907 

2908 You can wrap any method in a callback function, so the 

2909 restriction to functions is not significant. 

2910 

2911 Ignore the 'shortcut' arg unless 'allowBinding' is True. 

2912 

2913 Only k.bindOpenWith and the mod_scripting.py plugin should set 

2914 allowBinding. 

2915 """ 

2916 c, k = self.c, self 

2917 if not func: 

2918 g.es_print('Null func passed to k.registerCommand\n', commandName) 

2919 return 

2920 f = c.commandsDict.get(commandName) 

2921 if f and f.__name__ != func.__name__: 

2922 g.trace('redefining', commandName, f, '->', func) 

2923 c.commandsDict[commandName] = func 

2924 # Warn about deprecated arguments. 

2925 if shortcut and not allowBinding: 

2926 g.es_print('The "shortcut" keyword arg to k.registerCommand will be ignored') 

2927 g.es_print('Called from', g.callers()) 

2928 shortcut = None 

2929 for arg, val in kwargs.items(): 

2930 if val is not None: 

2931 g.es_print(f'The "{arg}" keyword arg to k.registerCommand is deprecated') 

2932 g.es_print('Called from', g.callers()) 

2933 # Make requested bindings, even if a warning has been given. 

2934 # This maintains strict compatibility with existing plugins and scripts. 

2935 k.registerCommandShortcut( 

2936 commandName=commandName, 

2937 func=func, 

2938 pane=pane, 

2939 shortcut=shortcut, 

2940 ) 

2941 #@+node:ekr.20171124043747.1: *4* k.registerCommandShortcut 

2942 def registerCommandShortcut(self, commandName, func, pane, shortcut): 

2943 """ 

2944 Register a shortcut for the a command. 

2945 

2946 **Important**: Bindings created here from plugins can not be overridden. 

2947 This includes @command and @button bindings created by mod_scripting.py. 

2948 """ 

2949 c, k = self.c, self 

2950 is_local = c.shortFileName() not in ('myLeoSettings.leo', 'leoSettings.leo') 

2951 assert not g.isStroke(shortcut) 

2952 if shortcut: 

2953 stroke = g.KeyStroke(binding=shortcut) if shortcut else None 

2954 elif commandName.lower() == 'shortcut': # Causes problems. 

2955 stroke = None 

2956 elif is_local: 

2957 # 327: Don't get defaults when handling a local file. 

2958 stroke = None 

2959 else: 

2960 # Try to get a stroke from leoSettings.leo. 

2961 stroke = None 

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

2963 for bi in aList: 

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

2965 stroke = bi.stroke 

2966 pane = bi.pane # 2015/05/11. 

2967 break 

2968 if stroke: 

2969 k.bindKey(pane, stroke, func, commandName, tag='register-command') 

2970 # Must be a stroke. 

2971 k.makeMasterGuiBinding(stroke) # Must be a stroke. 

2972 # Fixup any previous abbreviation to press-x-button commands. 

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

2974 d = c.config.getAbbrevDict() 

2975 # Keys are full command names, values are abbreviations. 

2976 if commandName in list(d.values()): 

2977 for key in d: 

2978 if d.get(key) == commandName: 

2979 c.commandsDict[key] = c.commandsDict.get(commandName) 

2980 break 

2981 #@+node:ekr.20061031131434.127: *4* k.simulateCommand 

2982 def simulateCommand(self, commandName, event=None): 

2983 """Execute a Leo command by name.""" 

2984 c = self.c 

2985 if not event: 

2986 # Create a default key event. 

2987 event = g.app.gui.create_key_event(c) 

2988 c.doCommandByName(commandName, event) 

2989 #@+node:ekr.20140813052702.18203: *4* k.getFileName 

2990 def getFileName(self, event, callback=None, 

2991 filterExt=None, prompt='Enter File Name: ', tabName='Dired' 

2992 ): 

2993 """Get a file name from the minibuffer.""" 

2994 k = self 

2995 k.fnc.get_file_name(event, callback, filterExt, prompt, tabName) 

2996 #@+node:ekr.20061031131434.145: *3* k.Master event handlers 

2997 #@+node:ekr.20061031131434.146: *4* k.masterKeyHandler & helpers 

2998 def masterKeyHandler(self, event): 

2999 """The master key handler for almost all key bindings.""" 

3000 trace = 'keys' in g.app.debug 

3001 c, k = self.c, self 

3002 # Setup... 

3003 if trace: 

3004 g.trace(repr(k.state.kind), repr(event.char), repr(event.stroke)) 

3005 k.checkKeyEvent(event) 

3006 k.setEventWidget(event) 

3007 k.traceVars(event) 

3008 # Order is very important here... 

3009 if k.isSpecialKey(event): 

3010 return 

3011 if k.doKeyboardQuit(event): 

3012 return 

3013 if k.doDemo(event): 

3014 return 

3015 if k.doMode(event): 

3016 return 

3017 if k.doVim(event): 

3018 return 

3019 if k.doBinding(event): 

3020 return 

3021 # Handle abbreviations. 

3022 if k.abbrevOn and c.abbrevCommands.expandAbbrev(event, event.stroke): 

3023 return 

3024 # Handle the character given by event *without* 

3025 # executing any command that might be bound to it. 

3026 c.insertCharFromEvent(event) 

3027 #@+node:ekr.20200524151214.1: *5* Setup... 

3028 #@+node:ekr.20180418040158.1: *6* k.checkKeyEvent 

3029 def checkKeyEvent(self, event): 

3030 """Perform sanity checks on the incoming event.""" 

3031 # These assert's should be safe, because eventFilter 

3032 # calls k.masterKeyHandler inside a try/except block. 

3033 c = self.c 

3034 assert event is not None 

3035 c.check_event(event) 

3036 assert hasattr(event, 'char') 

3037 assert hasattr(event, 'stroke') 

3038 if not hasattr(event, 'widget'): 

3039 event.widget = None 

3040 assert g.isStrokeOrNone(event.stroke) 

3041 if event: 

3042 assert event.stroke.s not in g.app.gui.ignoreChars, repr(event.stroke.s) 

3043 # A continuous unit test, better than "@test k.isPlainKey". 

3044 #@+node:ekr.20180418034305.1: *6* k.setEventWidget 

3045 def setEventWidget(self, event): 

3046 """ 

3047 A hack: redirect the event to the text part of the log. 

3048 """ 

3049 c = self.c 

3050 w = event.widget 

3051 w_name = c.widget_name(w) 

3052 if w_name.startswith('log'): 

3053 event.widget = c.frame.log.logCtrl 

3054 #@+node:ekr.20180418031417.1: *6* k.traceVars 

3055 def traceVars(self, event): 

3056 

3057 trace = False and not g.unitTesting 

3058 if not trace: 

3059 return 

3060 k = self 

3061 char = event.char 

3062 state = k.state.kind 

3063 stroke = event.stroke 

3064 g.trace( 

3065 f"stroke: {stroke!r}, " 

3066 f"char: {char!r}, " 

3067 f"state: {state}, " 

3068 f"state2: {k.unboundKeyAction}") 

3069 #@+node:ekr.20180418031118.1: *5* 1. k.isSpecialKey 

3070 def isSpecialKey(self, event): 

3071 """Return True if char is a special key.""" 

3072 if not event: 

3073 # An empty event is not an error. 

3074 return False 

3075 # Fix #917. 

3076 if len(event.char) > 1 and not event.stroke.s: 

3077 # stroke.s was cleared, but not event.char. 

3078 return True 

3079 return event.char in g.app.gui.ignoreChars 

3080 #@+node:ekr.20180418024449.1: *5* 2. k.doKeyboardQuit 

3081 def doKeyboardQuit(self, event): 

3082 """ 

3083 A helper for k.masterKeyHandler: Handle keyboard-quit logic. 

3084 

3085 return True if k.masterKeyHandler should return. 

3086 """ 

3087 c, k = self.c, self 

3088 stroke = getattr(event, 'stroke', None) 

3089 if k.abortAllModesKey and stroke and stroke == k.abortAllModesKey: 

3090 if getattr(c, 'screenCastController', None): 

3091 c.screenCastController.quit() 

3092 c.doCommandByName('keyboard-quit', event) 

3093 return True 

3094 return False 

3095 #@+node:ekr.20180418023827.1: *5* 3. k.doDemo 

3096 def doDemo(self, event): 

3097 """ 

3098 Support the demo.py plugin. 

3099 Return True if k.masterKeyHandler should return. 

3100 """ 

3101 k = self 

3102 stroke = event.stroke 

3103 demo = getattr(g.app, 'demo', None) 

3104 if not demo: 

3105 return False 

3106 # 

3107 # Shortcut everything so that demo-next or demo-prev won't alter of our ivars. 

3108 if k.demoNextKey and stroke == k.demoNextKey: 

3109 if demo.trace: 

3110 g.trace('demo-next', stroke) 

3111 demo.next_command() 

3112 return True 

3113 if k.demoPrevKey and stroke == k.demoPrevKey: 

3114 if demo.trace: 

3115 g.trace('demo-prev', stroke) 

3116 demo.prev_command() 

3117 return True 

3118 return False 

3119 #@+node:ekr.20091230094319.6244: *5* 4. k.doMode & helpers 

3120 def doMode(self, event): 

3121 """ 

3122 Handle mode bindings. 

3123 Return True if k.masterKeyHandler should return. 

3124 """ 

3125 # 

3126 # #1757: Leo's default vim bindings make heavy use of modes. 

3127 # Retain these traces! 

3128 trace = 'keys' in g.app.debug 

3129 k = self 

3130 state = k.state.kind 

3131 stroke = event.stroke 

3132 if not k.inState(): 

3133 return False 

3134 # 

3135 # First, honor minibuffer bindings for all except user modes. 

3136 if state == 'input-shortcut': 

3137 k.handleInputShortcut(event, stroke) 

3138 if trace: 

3139 g.trace(state, 'k.handleInputShortcut', stroke) 

3140 return True 

3141 if state in ( 

3142 'getArg', 'getFileName', 'full-command', 'auto-complete', 'vim-mode' 

3143 ): 

3144 if k.handleMiniBindings(event, state, stroke): 

3145 if trace: 

3146 g.trace(state, 'k.handleMiniBindings', stroke) 

3147 return True 

3148 # 

3149 # Second, honor general modes. 

3150 # 

3151 if state == 'getArg': 

3152 # New in Leo 5.8: Only call k.getArg for keys it can handle. 

3153 if k.isPlainKey(stroke): 

3154 k.getArg(event, stroke=stroke) 

3155 if trace: 

3156 g.trace(state, 'k.isPlain: getArg', stroke) 

3157 return True 

3158 if stroke.s in ('Escape', 'Tab', 'BackSpace'): 

3159 k.getArg(event, stroke=stroke) 

3160 if trace: 

3161 g.trace(state, f"{stroke.s!r}: getArg", stroke) 

3162 return True 

3163 return False 

3164 if state in ('getFileName', 'get-file-name'): 

3165 k.getFileName(event) 

3166 if trace: 

3167 g.trace(state, 'k.getFileName', stroke) 

3168 return True 

3169 if state in ('full-command', 'auto-complete'): 

3170 val = k.callStateFunction(event) 

3171 # Do the default state action. 

3172 # Calls end-command. 

3173 if val != 'do-standard-keys': 

3174 handler = k.state.handler and k.state.handler.__name__ or '<no handler>' 

3175 if trace: 

3176 g.trace(state, 'k.callStateFunction:', handler, stroke) 

3177 return True 

3178 return False 

3179 # 

3180 # Third, pass keys to user modes. 

3181 # 

3182 d = k.masterBindingsDict.get(state) 

3183 if d: 

3184 assert g.isStrokeOrNone(stroke) 

3185 bi = d.get(stroke) 

3186 if bi: 

3187 # Bound keys continue the mode. 

3188 k.generalModeHandler(event, 

3189 commandName=bi.commandName, 

3190 func=bi.func, 

3191 modeName=state, 

3192 nextMode=bi.nextMode) 

3193 if trace: 

3194 g.trace(state, 'k.generalModeHandler', stroke) 

3195 return True 

3196 # Unbound keys end mode. 

3197 k.endMode() 

3198 return False 

3199 # 

3200 # Fourth, call the state handler. 

3201 # 

3202 handler = k.getStateHandler() 

3203 if handler: 

3204 handler(event) 

3205 if trace: 

3206 handler_name = handler and handler.__name__ or '<no handler>' 

3207 g.trace(state, 'handler:', handler_name, stroke) 

3208 return True 

3209 #@+node:ekr.20061031131434.108: *6* k.callStateFunction 

3210 def callStateFunction(self, event): 

3211 """Call the state handler associated with this event.""" 

3212 k = self 

3213 ch = event.char 

3214 # 

3215 # Defensive programming 

3216 if not k.state.kind: 

3217 return None 

3218 if not k.state.handler: 

3219 g.error('callStateFunction: no state function for', k.state.kind) 

3220 return None 

3221 # 

3222 # Handle auto-completion before checking for unbound keys. 

3223 if k.state.kind == 'auto-complete': 

3224 # k.auto_completer_state_handler returns 'do-standard-keys' for control keys. 

3225 val = k.state.handler(event) 

3226 return val 

3227 # 

3228 # Ignore unbound non-ascii keys. 

3229 if ( 

3230 k.ignore_unbound_non_ascii_keys and 

3231 len(ch) == 1 and 

3232 ch and ch not in ('\b', '\n', '\r', '\t') and 

3233 (ord(ch) < 32 or ord(ch) > 128) 

3234 ): 

3235 return None 

3236 # 

3237 # Call the state handler. 

3238 val = k.state.handler(event) 

3239 return val 

3240 #@+node:ekr.20061031131434.152: *6* k.handleMiniBindings 

3241 def handleMiniBindings(self, event, state, stroke): 

3242 """Find and execute commands bound to the event.""" 

3243 k = self 

3244 # 

3245 # Special case for bindings handled in k.getArg: 

3246 if state == 'full-command' and stroke in ('Up', 'Down'): 

3247 return False 

3248 # 

3249 # Ignore other special keys in the minibuffer. 

3250 if state in ('getArg', 'full-command'): 

3251 if stroke in ( 

3252 '\b', 'BackSpace', 

3253 '\r', 'Linefeed', 

3254 '\n', 'Return', 

3255 '\t', 'Tab', 

3256 'Escape', 

3257 ): 

3258 return False 

3259 if k.isFKey(stroke): 

3260 return False 

3261 # 

3262 # Ignore autocompletion state. 

3263 if state.startswith('auto-'): 

3264 return False 

3265 # 

3266 # Ignore plain key binding in the minibuffer. 

3267 if not stroke or k.isPlainKey(stroke): 

3268 return False 

3269 # 

3270 # Get the command, based on the pane. 

3271 for pane in ('mini', 'all', 'text'): 

3272 result = k.handleMinibufferHelper(event, pane, state, stroke) 

3273 assert result in ('continue', 'found', 'ignore') 

3274 if result == 'ignore': 

3275 return False # Let getArg handle it. 

3276 if result == 'found': 

3277 # Do not call k.keyboardQuit here! 

3278 return True 

3279 # 

3280 # No binding exists. 

3281 return False 

3282 #@+node:ekr.20180418114300.1: *7* k.handleMinibufferHelper 

3283 def handleMinibufferHelper(self, event, pane, state, stroke): 

3284 """ 

3285 Execute a pane binding in the minibuffer. 

3286 Return 'continue', 'ignore', 'found' 

3287 """ 

3288 c, k = self.c, self 

3289 d = k.masterBindingsDict.get(pane) 

3290 if not d: 

3291 return 'continue' 

3292 bi = d.get(stroke) 

3293 if not bi: 

3294 return 'continue' 

3295 assert bi.stroke == stroke, f"bi: {bi} stroke: {stroke}" 

3296 # Ignore the replace-string command in the minibuffer. 

3297 if bi.commandName == 'replace-string' and state == 'getArg': 

3298 return 'ignore' 

3299 # Execute this command. 

3300 if bi.commandName not in k.singleLineCommandList: 

3301 k.keyboardQuit() 

3302 else: 

3303 c.minibufferWantsFocus() 

3304 c.doCommandByName(bi.commandName, event) 

3305 # Careful: the command could exit. 

3306 if c.exists and not k.silentMode: 

3307 # Use the state *after* executing the command. 

3308 if k.state.kind: 

3309 c.minibufferWantsFocus() 

3310 else: 

3311 c.bodyWantsFocus() 

3312 return 'found' 

3313 #@+node:vitalije.20170708161511.1: *6* k.handleInputShortcut 

3314 def handleInputShortcut(self, event, stroke): 

3315 c, k, p, u = self.c, self, self.c.p, self.c.undoer 

3316 k.clearState() 

3317 if p.h.startswith(('@shortcuts', '@mode')): 

3318 # line of text in body 

3319 w = c.frame.body.wrapper 

3320 before, sel, after = w.getInsertLines() 

3321 m = k._cmd_handle_input_pattern.search(sel) 

3322 assert m # edit-shortcut was invoked on a malformed body line 

3323 sel = f"{m.group(0)} {stroke.s}" 

3324 udata = u.beforeChangeNodeContents(p) 

3325 pos = w.getYScrollPosition() 

3326 i = len(before) 

3327 j = max(i, len(before) + len(sel) - 1) 

3328 w.setAllText(before + sel + after) 

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

3330 w.setYScrollPosition(pos) 

3331 u.afterChangeNodeContents(p, 'change shortcut', udata) 

3332 cmdname = m.group(0).rstrip('= ') 

3333 k.editShortcut_do_bind_helper(stroke, cmdname) 

3334 return 

3335 if p.h.startswith(('@command', '@button')): 

3336 udata = u.beforeChangeNodeContents(p) 

3337 cmd = p.h.split('@key', 1)[0] 

3338 p.h = f"{cmd} @key={stroke.s}" 

3339 u.afterChangeNodeContents(p, 'change shortcut', udata) 

3340 try: 

3341 cmdname = cmd.split(' ', 1)[1].strip() 

3342 k.editShortcut_do_bind_helper(stroke, cmdname) 

3343 except IndexError: 

3344 pass 

3345 return 

3346 # this should never happen 

3347 g.error('not in settings node shortcut') 

3348 #@+node:vitalije.20170709151653.1: *7* k.isInShortcutBodyLine 

3349 _cmd_handle_input_pattern = re.compile(r'[A-Za-z0-9_\-]+\s*=') 

3350 

3351 def isInShortcutBodyLine(self): 

3352 c, k = self.c, self 

3353 p = c.p 

3354 if p.h.startswith(('@shortcuts', '@mode')): 

3355 # line of text in body 

3356 w = c.frame.body 

3357 before, sel, after = w.getInsertLines() 

3358 m = k._cmd_handle_input_pattern.search(sel) 

3359 return bool(m) 

3360 return p.h.startswith(('@command', '@button')) 

3361 #@+node:vitalije.20170709151658.1: *7* k.isEditShortcutSensible 

3362 def isEditShortcutSensible(self): 

3363 c, k = self.c, self 

3364 p = c.p 

3365 return p.h.startswith(('@command', '@button')) or k.isInShortcutBodyLine() 

3366 #@+node:vitalije.20170709202924.1: *7* k.editShortcut_do_bind_helper 

3367 def editShortcut_do_bind_helper(self, stroke, cmdname): 

3368 c, k = self.c, self 

3369 cmdfunc = c.commandsDict.get(cmdname) 

3370 if cmdfunc: 

3371 k.bindKey('all', stroke, cmdfunc, cmdname) 

3372 g.es('bound', stroke, 'to command', cmdname) 

3373 #@+node:ekr.20180418025241.1: *5* 5. k.doVim 

3374 def doVim(self, event): 

3375 """ 

3376 Handle vim mode. 

3377 Return True if k.masterKeyHandler should return. 

3378 """ 

3379 trace = all(z in g.app.debug for z in ('keys', 'verbose')) 

3380 c = self.c 

3381 if c.vim_mode and c.vimCommands: 

3382 # The "acceptance methods" in leoVim.py return True 

3383 # if vim node has completely handled the key. 

3384 # Otherwise, processing in k.masterKeyHandler continues. 

3385 ok = c.vimCommands.do_key(event) 

3386 if trace: 

3387 g.trace('do_key returns', ok, repr(event and event.stroke)) 

3388 return ok 

3389 return False 

3390 #@+node:ekr.20180418033838.1: *5* 6. k.doBinding & helpers 

3391 def doBinding(self, event): 

3392 """ 

3393 Attempt to find a binding for the event's stroke. 

3394 If found, execute the command and return True 

3395 Otherwise, return False 

3396 """ 

3397 trace = 'keys' in g.app.debug 

3398 c, k = self.c, self 

3399 # 

3400 # Experimental special case: 

3401 # Inserting a '.' always invokes the auto-completer. 

3402 # The auto-completer just inserts a '.' if it isn't enabled. 

3403 stroke = event.stroke 

3404 if ( 

3405 stroke.s == '.' 

3406 and k.isPlainKey(stroke) 

3407 and self.unboundKeyAction in ('insert', 'overwrite') 

3408 ): 

3409 c.doCommandByName('auto-complete', event) 

3410 return True 

3411 # 

3412 # Use getPaneBindings for *all* keys. 

3413 bi = k.getPaneBinding(event) 

3414 # 

3415 # #327: Ignore killed bindings. 

3416 if bi and bi.commandName in k.killedBindings: 

3417 return False 

3418 # 

3419 # Execute the command if the binding exists. 

3420 if bi: 

3421 # A superb trace. !s gives shorter trace. 

3422 if trace: 

3423 g.trace(f"{event.stroke!s} {bi.commandName}") 

3424 c.doCommandByName(bi.commandName, event) 

3425 return True 

3426 # 

3427 # No binding exists. 

3428 return False 

3429 #@+node:ekr.20091230094319.6240: *6* k.getPaneBinding & helper 

3430 def getPaneBinding(self, event): 

3431 c, k, state = self.c, self, self.unboundKeyAction 

3432 stroke, w = event.stroke, event.w 

3433 if not g.assert_is(stroke, g.KeyStroke): 

3434 return None 

3435 # #1757: Always insert plain keys in the body. 

3436 # Valid because mode bindings have already been handled. 

3437 if ( 

3438 k.isPlainKey(stroke) 

3439 and w == c.frame.body.widget 

3440 and state in ('insert', 'overwrite') 

3441 ): 

3442 return None 

3443 for key, name in ( 

3444 # Order here is similar to bindtags order. 

3445 ('command', None), 

3446 ('insert', None), 

3447 ('overwrite', None), 

3448 ('button', None), 

3449 ('body', 'body'), 

3450 ('text', 'head'), # Important: text bindings in head before tree bindings. 

3451 ('tree', 'head'), 

3452 ('tree', 'canvas'), 

3453 ('log', 'log'), 

3454 ('text', 'log'), 

3455 ('text', None), 

3456 ('all', None), 

3457 ): 

3458 bi = k.getBindingHelper(key, name, stroke, w) 

3459 if bi: 

3460 return bi 

3461 return None 

3462 #@+node:ekr.20180418105228.1: *7* getPaneBindingHelper 

3463 def getBindingHelper(self, key, name, stroke, w): 

3464 """Find a binding for the widget with the given name.""" 

3465 c, k = self.c, self 

3466 # 

3467 # Return if the pane's name doesn't match the event's widget. 

3468 state = k.unboundKeyAction 

3469 w_name = c.widget_name(w) 

3470 pane_matches = ( 

3471 name and w_name.startswith(name) or 

3472 key in ('command', 'insert', 'overwrite') and state == key or 

3473 key in ('text', 'all') and g.isTextWrapper(w) or 

3474 key in ('button', 'all') 

3475 ) 

3476 if not pane_matches: 

3477 return None 

3478 # 

3479 # Return if there is no binding at all. 

3480 d = k.masterBindingsDict.get(key, {}) 

3481 if not d: 

3482 return None 

3483 bi = d.get(stroke) 

3484 if not bi: 

3485 return None 

3486 # 

3487 # Ignore previous/next-line commands while editing headlines. 

3488 if ( 

3489 key == 'text' and 

3490 name == 'head' and 

3491 bi.commandName in ('previous-line', 'next-line') 

3492 ): 

3493 return None 

3494 # 

3495 # The binding has been found. 

3496 return bi 

3497 #@+node:ekr.20160409035115.1: *6* k.searchTree 

3498 def searchTree(self, char): 

3499 """Search all visible nodes for a headline starting with stroke.""" 

3500 if not char: 

3501 return 

3502 c = self.c 

3503 if not c.config.getBool('plain-key-outline-search'): 

3504 return 

3505 

3506 def match(p): 

3507 """Return True if p contains char.""" 

3508 s = p.h.lower() if char.islower() else p.h 

3509 return s.find(char) > -1 

3510 

3511 # Start at c.p, then retry everywhere. 

3512 

3513 for p in (c.p, c.rootPosition()): 

3514 p = p.copy() 

3515 if p == c.p and match(p): 

3516 p.moveToVisNext(c) 

3517 while p: 

3518 if match(p): 

3519 c.selectPosition(p) 

3520 c.redraw() 

3521 return 

3522 p.moveToVisNext(c) 

3523 

3524 # Too confusing for the user. 

3525 # re_pat = re.compile(r'^@(\w)+[ \t](.+)') 

3526 

3527 # def match(p, pattern): 

3528 # s = p.h.lower() 

3529 # if pattern: 

3530 # m = pattern.search(s) 

3531 # found = (s.startswith(char) or 

3532 # m and m.group(2).lower().startswith(char)) 

3533 # else: 

3534 # found = s.find(char) > -1 

3535 # if found: 

3536 # c.selectPosition(p) 

3537 # c.redraw() 

3538 # return found 

3539 #@+node:ekr.20061031170011.3: *3* k.Minibuffer 

3540 # These may be overridden, but this code is now gui-independent. 

3541 #@+node:ekr.20061031170011.9: *4* k.extendLabel 

3542 def extendLabel(self, s, select=False, protect=False): 

3543 

3544 c, k, w = self.c, self, self.w 

3545 if not (w and s): 

3546 return 

3547 c.widgetWantsFocusNow(w) 

3548 w.insert('end', s) 

3549 if select: 

3550 i, j = k.getEditableTextRange() 

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

3552 if protect: 

3553 k.protectLabel() 

3554 #@+node:ekr.20061031170011.13: *4* k.getEditableTextRange 

3555 def getEditableTextRange(self): 

3556 k, w = self, self.w 

3557 s = w.getAllText() 

3558 i = len(k.mb_prefix) 

3559 j = len(s) 

3560 return i, j 

3561 #@+node:ekr.20061031170011.5: *4* k.getLabel 

3562 def getLabel(self, ignorePrompt=False): 

3563 k, w = self, self.w 

3564 if not w: 

3565 return '' 

3566 s = w.getAllText() 

3567 if ignorePrompt: 

3568 return s[len(k.mb_prefix) :] 

3569 return s or '' 

3570 #@+node:ekr.20080408060320.791: *4* k.killLine 

3571 def killLine(self, protect=True): 

3572 k = self 

3573 w = k.w 

3574 s = w.getAllText() 

3575 s = s[: len(k.mb_prefix)] 

3576 w.setAllText(s) 

3577 n = len(s) 

3578 w.setSelectionRange(n, n, insert=n) 

3579 if protect: 

3580 k.mb_prefix = s 

3581 #@+node:ekr.20061031131434.135: *4* k.minibufferWantsFocus 

3582 # def minibufferWantsFocus(self): 

3583 # c = self.c 

3584 # c.widgetWantsFocus(c.miniBufferWidget) 

3585 #@+node:ekr.20061031170011.6: *4* k.protectLabel 

3586 def protectLabel(self): 

3587 k, w = self, self.w 

3588 if not w: 

3589 return 

3590 k.mb_prefix = w.getAllText() 

3591 #@+node:ekr.20061031170011.7: *4* k.resetLabel 

3592 def resetLabel(self): 

3593 """Reset the minibuffer label.""" 

3594 k = self 

3595 c, w = k.c, k.w 

3596 k.setLabelGrey('') 

3597 k.mb_prefix = '' 

3598 if w: 

3599 w.setSelectionRange(0, 0, insert=0) 

3600 state = k.unboundKeyAction 

3601 if c.vim_mode and c.vimCommands: 

3602 c.vimCommands.show_status() 

3603 else: 

3604 k.setLabelBlue(label=f"{state.capitalize()} State") 

3605 #@+node:ekr.20080408060320.790: *4* k.selectAll 

3606 def selectAll(self): 

3607 """Select all the user-editable text of the minibuffer.""" 

3608 w = self.w 

3609 i, j = self.getEditableTextRange() 

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

3611 #@+node:ekr.20061031170011.8: *4* k.setLabel 

3612 def setLabel(self, s, protect=False): 

3613 """Set the label of the minibuffer.""" 

3614 c, k, w = self.c, self, self.w 

3615 if w: 

3616 # Support for the curses gui. 

3617 if hasattr(g.app.gui, 'set_minibuffer_label'): 

3618 g.app.gui.set_minibuffer_label(c, s) 

3619 w.setAllText(s) 

3620 n = len(s) 

3621 w.setSelectionRange(n, n, insert=n) 

3622 if protect: 

3623 k.mb_prefix = s 

3624 #@+node:ekr.20061031170011.10: *4* k.setLabelBlue 

3625 def setLabelBlue(self, label, protect=True): 

3626 """Set the minibuffer label.""" 

3627 k, w = self, self.w 

3628 if hasattr(g.app.gui, 'set_minibuffer_label'): 

3629 g.app.gui.set_minibuffer_label(self.c, label) 

3630 elif w: 

3631 w.setStyleClass('') # normal state, not warning or error 

3632 if label is not None: 

3633 k.setLabel(label, protect=protect) 

3634 #@+node:ekr.20061031170011.11: *4* k.setLabelGrey 

3635 def setLabelGrey(self, label=None): 

3636 k, w = self, self.w 

3637 if not w: 

3638 return 

3639 w.setStyleClass('minibuffer_warning') 

3640 if label is not None: 

3641 k.setLabel(label) 

3642 

3643 setLabelGray = setLabelGrey 

3644 #@+node:ekr.20080510153327.2: *4* k.setLabelRed 

3645 def setLabelRed(self, label=None, protect=False): 

3646 k, w = self, self.w 

3647 if not w: 

3648 return 

3649 w.setStyleClass('minibuffer_error') 

3650 if label is not None: 

3651 k.setLabel(label, protect) 

3652 #@+node:ekr.20140822051549.18298: *4* k.setStatusLabel 

3653 def setStatusLabel(self, s): 

3654 """ 

3655 Set the label to s. 

3656 

3657 Use k.setStatusLabel, not k.setLael, to report the status of a Leo 

3658 command. This allows the option to use g.es instead of the minibuffer 

3659 to report status. 

3660 """ 

3661 k = self 

3662 k.setLabel(s, protect=False) 

3663 #@+node:ekr.20061031170011.12: *4* k.updateLabel 

3664 def updateLabel(self, event): 

3665 """ 

3666 Mimic what would happen with the keyboard and a Text editor 

3667 instead of plain accumulation. 

3668 """ 

3669 c, k, w = self.c, self, self.w 

3670 if not event: 

3671 return 

3672 ch, stroke = event.char, event.stroke 

3673 if ch in "\n\r": 

3674 return 

3675 if stroke and not k.isPlainKey(stroke): 

3676 return # #2041. 

3677 c.widgetWantsFocusNow(w) 

3678 i, j = w.getSelectionRange() 

3679 ins = w.getInsertPoint() 

3680 if i != j: 

3681 w.delete(i, j) 

3682 if ch == '\b': 

3683 s = w.getAllText() 

3684 if len(s) > len(k.mb_prefix): 

3685 w.delete(i - 1) 

3686 i -= 1 

3687 else: 

3688 w.insert(ins, ch) 

3689 i = ins + 1 

3690 #@+node:ekr.20120208064440.10190: *3* k.Modes 

3691 #@+node:ekr.20061031131434.100: *4* k.addModeCommands (enterModeCallback) 

3692 def addModeCommands(self): 

3693 """Add commands created by @mode settings to c.commandsDict.""" 

3694 c, k = self.c, self 

3695 d = g.app.config.modeCommandsDict # Keys are command names: enter-x-mode. 

3696 # Create the callback functions and update c.commandsDict. 

3697 for key in d.keys(): 

3698 # pylint: disable=cell-var-from-loop 

3699 

3700 def enterModeCallback(event=None, name=key): 

3701 k.enterNamedMode(event, name) 

3702 

3703 c.commandsDict[key] = enterModeCallback 

3704 #@+node:ekr.20061031131434.157: *4* k.badMode 

3705 def badMode(self, modeName): 

3706 k = self 

3707 k.clearState() 

3708 if modeName.endswith('-mode'): 

3709 modeName = modeName[:-5] 

3710 k.setLabelGrey(f"@mode {modeName} is not defined (or is empty)") 

3711 #@+node:ekr.20061031131434.158: *4* k.createModeBindings 

3712 def createModeBindings(self, modeName, d, w): 

3713 """Create mode bindings for the named mode using dictionary d for w, a text widget.""" 

3714 c, k = self.c, self 

3715 assert d.name().endswith('-mode') 

3716 for commandName in d.keys(): 

3717 if commandName in ('*entry-commands*', '*command-prompt*'): 

3718 # These are special-purpose dictionary entries. 

3719 continue 

3720 func = c.commandsDict.get(commandName) 

3721 if not func: 

3722 g.es_print('no such command:', commandName, 'Referenced from', modeName) 

3723 continue 

3724 aList = d.get(commandName, []) 

3725 for bi in aList: 

3726 stroke = bi.stroke 

3727 # Important: bi.val is canonicalized. 

3728 if stroke and stroke not in ('None', 'none', None): 

3729 assert g.isStroke(stroke) 

3730 k.makeMasterGuiBinding(stroke) 

3731 # Create the entry for the mode in k.masterBindingsDict. 

3732 # Important: this is similar, but not the same as k.bindKeyToDict. 

3733 # Thus, we should **not** call k.bindKey here! 

3734 d2 = k.masterBindingsDict.get(modeName, {}) 

3735 d2[stroke] = g.BindingInfo( 

3736 kind=f"mode<{modeName}>", 

3737 commandName=commandName, 

3738 func=func, 

3739 nextMode=bi.nextMode, 

3740 stroke=stroke) 

3741 k.masterBindingsDict[modeName] = d2 

3742 #@+node:ekr.20120208064440.10179: *4* k.endMode 

3743 def endMode(self): 

3744 c, k = self.c, self 

3745 w = g.app.gui.get_focus(c) 

3746 if w: 

3747 c.frame.log.deleteTab('Mode') # Changes focus to the body pane 

3748 k.inputModeName = None 

3749 k.clearState() 

3750 k.resetLabel() 

3751 k.showStateAndMode() # Restores focus. 

3752 if w: 

3753 c.widgetWantsFocusNow(w) 

3754 #@+node:ekr.20061031131434.160: *4* k.enterNamedMode 

3755 def enterNamedMode(self, event, commandName): 

3756 c, k = self.c, self 

3757 modeName = commandName[6:] 

3758 c.inCommand = False # Allow inner commands in the mode. 

3759 k.generalModeHandler(event, modeName=modeName) 

3760 #@+node:ekr.20061031131434.161: *4* k.exitNamedMode 

3761 @cmd('exit-named-mode') 

3762 def exitNamedMode(self, event=None): 

3763 """Exit an input mode.""" 

3764 k = self 

3765 if k.inState(): 

3766 k.endMode() 

3767 k.showStateAndMode() 

3768 #@+node:ekr.20120208064440.10199: *4* k.generalModeHandler 

3769 def generalModeHandler(self, event, 

3770 commandName=None, 

3771 func=None, 

3772 modeName=None, 

3773 nextMode=None, 

3774 prompt=None 

3775 ): 

3776 """Handle a mode defined by an @mode node in leoSettings.leo.""" 

3777 c, k = self.c, self 

3778 state = k.getState(modeName) 

3779 if state == 0: 

3780 k.inputModeName = modeName 

3781 k.modePrompt = prompt or modeName 

3782 k.modeWidget = event and event.widget 

3783 k.setState(modeName, 1, handler=k.generalModeHandler) 

3784 self.initMode(event, modeName) 

3785 # Careful: k.initMode can execute commands that will destroy a commander. 

3786 if g.app.quitting or not c.exists: 

3787 return 

3788 if not k.silentMode: 

3789 if c.config.getBool('showHelpWhenEnteringModes'): 

3790 k.modeHelp(event) 

3791 else: 

3792 c.frame.log.hideTab('Mode') 

3793 elif not func: 

3794 g.trace('No func: improper key binding') 

3795 else: 

3796 if commandName == 'mode-help': 

3797 func(event) 

3798 else: 

3799 self.endMode() 

3800 # New in 4.4.1 b1: pass an event describing the original widget. 

3801 if event: 

3802 event.w = event.widget = k.modeWidget 

3803 else: 

3804 event = g.app.gui.create_key_event(c, w=k.modeWidget) 

3805 func(event) 

3806 if g.app.quitting or not c.exists: 

3807 pass 

3808 elif nextMode in (None, 'none'): 

3809 # Do *not* clear k.inputModeName or the focus here. 

3810 # func may have put us in *another* mode. 

3811 pass 

3812 elif nextMode == 'same': 

3813 silent = k.silentMode 

3814 k.setState(modeName, 1, handler=k.generalModeHandler) 

3815 self.reinitMode(modeName) # Re-enter this mode. 

3816 k.silentMode = silent 

3817 else: 

3818 k.silentMode = False # All silent modes must do --> set-silent-mode. 

3819 self.initMode(event, nextMode) # Enter another mode. 

3820 #@+node:ekr.20061031131434.163: *4* k.initMode 

3821 def initMode(self, event, modeName): 

3822 

3823 c, k = self.c, self 

3824 if not modeName: 

3825 g.trace('oops: no modeName') 

3826 return 

3827 d = g.app.config.modeCommandsDict.get('enter-' + modeName) 

3828 if not d: 

3829 self.badMode(modeName) 

3830 return 

3831 k.modeBindingsDict = d 

3832 bi = d.get('*command-prompt*') 

3833 prompt = bi.kind if bi else modeName 

3834 k.inputModeName = modeName 

3835 k.silentMode = False 

3836 aList = d.get('*entry-commands*', []) 

3837 if aList: 

3838 for bi in aList: 

3839 commandName = bi.commandName 

3840 k.simulateCommand(commandName) 

3841 # Careful, the command can kill the commander. 

3842 if g.app.quitting or not c.exists: 

3843 return 

3844 # New in Leo 4.5: a startup command can immediately transfer to another mode. 

3845 if commandName.startswith('enter-'): 

3846 return 

3847 # Create bindings after we know whether we are in silent mode. 

3848 w = k.modeWidget if k.silentMode else k.w 

3849 k.createModeBindings(modeName, d, w) 

3850 k.showStateAndMode(prompt=prompt) 

3851 #@+node:ekr.20061031131434.165: *4* k.modeHelp & helper 

3852 @cmd('mode-help') 

3853 def modeHelp(self, event): 

3854 """ 

3855 The mode-help command. 

3856 

3857 A possible convention would be to bind <Tab> to this command in most modes, 

3858 by analogy with tab completion. 

3859 """ 

3860 c, k = self.c, self 

3861 c.endEditing() 

3862 if k.inputModeName: 

3863 d = g.app.config.modeCommandsDict.get('enter-' + k.inputModeName) 

3864 k.modeHelpHelper(d) 

3865 if not k.silentMode: 

3866 c.minibufferWantsFocus() 

3867 #@+node:ekr.20061031131434.166: *5* modeHelpHelper 

3868 def modeHelpHelper(self, d): 

3869 c, k = self.c, self 

3870 tabName = 'Mode' 

3871 c.frame.log.clearTab(tabName) 

3872 data, n = [], 0 

3873 for key in sorted(d.keys()): 

3874 if key in ('*entry-commands*', '*command-prompt*'): 

3875 pass 

3876 else: 

3877 aList = d.get(key) 

3878 for bi in aList: 

3879 stroke = bi.stroke 

3880 if stroke not in (None, 'None'): 

3881 s1 = key 

3882 s2 = k.prettyPrintKey(stroke) 

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

3884 data.append((s1, s2),) 

3885 data.sort() 

3886 modeName = k.inputModeName.replace('-', ' ') 

3887 if modeName.endswith('mode'): 

3888 modeName = modeName[:-4].strip() 

3889 prompt = d.get('*command-prompt*') 

3890 if prompt: 

3891 g.es('', f"{prompt.kind.strip()}\n\n", tabName=tabName) 

3892 else: 

3893 g.es('', f"{modeName} mode\n\n", tabName=tabName) 

3894 # This isn't perfect in variable-width fonts. 

3895 for s1, s2 in data: 

3896 g.es('', '%*s %s' % (n, s1, s2), tabName=tabName) 

3897 #@+node:ekr.20061031131434.164: *4* k.reinitMode 

3898 def reinitMode(self, modeName): 

3899 k = self 

3900 d = k.modeBindingsDict 

3901 k.inputModeName = modeName 

3902 w = k.modeWidget if k.silentMode else k.w 

3903 k.createModeBindings(modeName, d, w) 

3904 if k.silentMode: 

3905 k.showStateAndMode() 

3906 else: 

3907 # Do not set the status line here. 

3908 k.setLabelBlue(modeName + ': ') # ,protect=True) 

3909 #@+node:ekr.20061031131434.181: *3* k.Shortcuts & bindings 

3910 #@+node:ekr.20061031131434.176: *4* k.computeInverseBindingDict 

3911 def computeInverseBindingDict(self): 

3912 k = self 

3913 d = {} 

3914 # keys are minibuffer command names, values are shortcuts. 

3915 for stroke in k.bindingsDict: 

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

3917 aList = k.bindingsDict.get(stroke, []) 

3918 for bi in aList: 

3919 shortcutList = k.bindingsDict.get(bi.commandName, []) 

3920 # Bug fix: 2017/03/26. 

3921 bi_list = k.bindingsDict.get( 

3922 stroke, g.BindingInfo(kind='dummy', pane='all')) 

3923 # Important: only bi.pane is required below. 

3924 for bi in bi_list: 

3925 pane = f"{bi.pane}:" 

3926 data = (pane, stroke) 

3927 if data not in shortcutList: 

3928 shortcutList.append(data) 

3929 d[bi.commandName] = shortcutList 

3930 return d 

3931 #@+node:ekr.20061031131434.179: *4* k.getShortcutForCommandName 

3932 def getStrokeForCommandName(self, commandName): 

3933 c, k = self.c, self 

3934 command = c.commandsDict.get(commandName) 

3935 if command: 

3936 for stroke, aList in k.bindingsDict.items(): 

3937 for bi in aList: 

3938 if bi.commandName == commandName: 

3939 return stroke 

3940 return None 

3941 #@+node:ekr.20090518072506.8494: *4* k.isFKey 

3942 def isFKey(self, stroke): 

3943 # k = self 

3944 if not stroke: 

3945 return False 

3946 assert isinstance(stroke, str) or g.isStroke(stroke) 

3947 s = stroke.s if g.isStroke(stroke) else stroke 

3948 s = s.lower() 

3949 return s.startswith('f') and len(s) <= 3 and s[1:].isdigit() 

3950 #@+node:ekr.20061031131434.182: *4* k.isPlainKey 

3951 def isPlainKey(self, stroke): 

3952 """Return true if the shortcut refers to a plain (non-Alt,non-Ctl) key.""" 

3953 if not stroke: 

3954 return False 

3955 if not g.isStroke(stroke): 

3956 # Happens during unit tests. 

3957 stroke = g.KeyStroke(stroke) 

3958 # 

3959 # altgr combos (Alt+Ctrl) are always plain keys 

3960 # g.KeyStroke does not handle this, because it has no "c" ivar. 

3961 # 

3962 if stroke.isAltCtrl() and not self.enable_alt_ctrl_bindings: 

3963 return True 

3964 return stroke.isPlainKey() 

3965 #@+node:ekr.20061031131434.191: *4* k.prettyPrintKey 

3966 def prettyPrintKey(self, stroke, brief=False): 

3967 

3968 if not stroke: 

3969 return '' 

3970 if not g.assert_is(stroke, g.KeyStroke): 

3971 return stroke 

3972 return stroke.prettyPrint() 

3973 #@+node:ekr.20110606004638.16929: *4* k.stroke2char 

3974 def stroke2char(self, stroke): 

3975 """ 

3976 Convert a stroke to an (insertable) char. 

3977 This method allows Leo to use strokes everywhere. 

3978 """ 

3979 if not stroke: 

3980 return '' 

3981 if not g.isStroke(stroke): 

3982 # vim commands pass a plain key. 

3983 stroke = g.KeyStroke(stroke) 

3984 return stroke.toInsertableChar() 

3985 #@+node:ekr.20061031131434.193: *3* k.States 

3986 #@+node:ekr.20061031131434.194: *4* k.clearState 

3987 def clearState(self): 

3988 """Clear the key handler state.""" 

3989 k = self 

3990 k.state.kind = None 

3991 k.state.n = None 

3992 k.state.handler = None 

3993 #@+node:ekr.20061031131434.196: *4* k.getState 

3994 def getState(self, kind): 

3995 k = self 

3996 val = k.state.n if k.state.kind == kind else 0 

3997 return val 

3998 #@+node:ekr.20061031131434.195: *4* k.getStateHandler 

3999 def getStateHandler(self): 

4000 return self.state.handler 

4001 #@+node:ekr.20061031131434.197: *4* k.getStateKind 

4002 def getStateKind(self): 

4003 return self.state.kind 

4004 #@+node:ekr.20061031131434.198: *4* k.inState 

4005 def inState(self, kind=None): 

4006 k = self 

4007 if kind: 

4008 return k.state.kind == kind and k.state.n is not None 

4009 return k.state.kind and k.state.n is not None 

4010 #@+node:ekr.20080511122507.4: *4* k.setDefaultInputState 

4011 def setDefaultInputState(self): 

4012 k = self 

4013 state = k.defaultUnboundKeyAction 

4014 k.setInputState(state) 

4015 #@+node:ekr.20110209093958.15411: *4* k.setEditingState 

4016 def setEditingState(self): 

4017 k = self 

4018 state = k.defaultEditingAction 

4019 k.setInputState(state) 

4020 #@+node:ekr.20061031131434.133: *4* k.setInputState 

4021 def setInputState(self, state, set_border=False): 

4022 k = self 

4023 k.unboundKeyAction = state 

4024 #@+node:ekr.20061031131434.199: *4* k.setState 

4025 def setState(self, kind, n, handler=None): 

4026 

4027 k = self 

4028 if kind and n is not None: 

4029 k.state.kind = kind 

4030 k.state.n = n 

4031 if handler: 

4032 k.state.handler = handler 

4033 else: 

4034 k.clearState() 

4035 # k.showStateAndMode() 

4036 #@+node:ekr.20061031131434.192: *4* k.showStateAndMode 

4037 def showStateAndMode(self, w=None, prompt=None, setFocus=True): 

4038 """Show the state and mode at the start of the minibuffer.""" 

4039 c, k = self.c, self 

4040 state = k.unboundKeyAction 

4041 mode = k.getStateKind() 

4042 if not g.app.gui: 

4043 return 

4044 if not w: 

4045 if hasattr(g.app.gui, 'set_minibuffer_label'): 

4046 pass # we don't need w 

4047 else: 

4048 w = g.app.gui.get_focus(c) 

4049 if not w: 

4050 return 

4051 isText = g.isTextWrapper(w) 

4052 # This fixes a problem with the tk gui plugin. 

4053 if mode and mode.lower().startswith('isearch'): 

4054 return 

4055 wname = g.app.gui.widget_name(w).lower() 

4056 # Get the wrapper for the headline widget. 

4057 if wname.startswith('head'): 

4058 if hasattr(c.frame.tree, 'getWrapper'): 

4059 if hasattr(w, 'widget'): 

4060 w2 = w.widget 

4061 else: 

4062 w2 = w 

4063 w = c.frame.tree.getWrapper(w2, item=None) 

4064 isText = bool(w) # A benign hack. 

4065 if mode: 

4066 if mode in ('getArg', 'getFileName', 'full-command'): 

4067 s = None 

4068 elif prompt: 

4069 s = prompt 

4070 else: 

4071 mode = mode.strip() 

4072 if mode.endswith('-mode'): 

4073 mode = mode[:-5] 

4074 s = f"{mode.capitalize()} Mode" 

4075 elif c.vim_mode and c.vimCommands: 

4076 c.vimCommands.show_status() 

4077 return 

4078 else: 

4079 s = f"{state.capitalize()} State" 

4080 if c.editCommands.extendMode: 

4081 s = s + ' (Extend Mode)' 

4082 if s: 

4083 k.setLabelBlue(s) 

4084 if w and isText: 

4085 # k.showStateColors(inOutline,w) 

4086 k.showStateCursor(state, w) 

4087 # 2015/07/11: reset the status line. 

4088 if hasattr(c.frame.tree, 'set_status_line'): 

4089 c.frame.tree.set_status_line(c.p) 

4090 #@+node:ekr.20110202111105.15439: *4* k.showStateCursor 

4091 def showStateCursor(self, state, w): 

4092 pass 

4093 #@-others 

4094#@+node:ekr.20120208064440.10150: ** class ModeInfo 

4095class ModeInfo: 

4096 

4097 def __repr__(self): 

4098 return f"<ModeInfo {self.name}>" 

4099 

4100 __str__ = __repr__ 

4101 #@+others 

4102 #@+node:ekr.20120208064440.10193: *3* mode_i. ctor 

4103 def __init__(self, c, name, aList): 

4104 

4105 self.c = c 

4106 self.d = {} # The bindings in effect for this mode. 

4107 # Keys are names of (valid) command names, values are BindingInfo objects. 

4108 self.entryCommands = [] 

4109 # A list of BindingInfo objects. 

4110 self.k = c.k 

4111 self.name = self.computeModeName(name) 

4112 self.prompt = self.computeModePrompt(self.name) 

4113 self.init(name, aList) 

4114 #@+node:ekr.20120208064440.10152: *3* mode_i.computeModeName 

4115 def computeModeName(self, name): 

4116 s = name.strip().lower() 

4117 j = s.find(' ') 

4118 if j > -1: 

4119 s = s[:j] 

4120 if s.endswith('mode'): 

4121 s = s[:-4].strip() 

4122 if s.endswith('-'): 

4123 s = s[:-1] 

4124 i = s.find('::') 

4125 if i > -1: 

4126 # The actual mode name is everything up to the "::" 

4127 # The prompt is everything after the prompt. 

4128 s = s[:i] 

4129 return s + '-mode' 

4130 #@+node:ekr.20120208064440.10156: *3* mode_i.computeModePrompt 

4131 def computeModePrompt(self, name): 

4132 assert name == self.name 

4133 s = 'enter-' + name.replace(' ', '-') 

4134 i = s.find('::') 

4135 if i > -1: 

4136 # The prompt is everything after the '::' 

4137 prompt = s[i + 2 :].strip() 

4138 else: 

4139 prompt = s 

4140 return prompt 

4141 #@+node:ekr.20120208064440.10160: *3* mode_i.createModeBindings 

4142 def createModeBindings(self, w): 

4143 """Create mode bindings for w, a text widget.""" 

4144 c, d, k, modeName = self.c, self.d, self.k, self.name 

4145 for commandName in d: 

4146 func = c.commandsDict.get(commandName) 

4147 if not func: 

4148 g.es_print(f"no such command: {commandName} Referenced from {modeName}") 

4149 continue 

4150 aList = d.get(commandName, []) 

4151 for bi in aList: 

4152 stroke = bi.stroke 

4153 # Important: bi.val is canonicalized. 

4154 if stroke and stroke not in ('None', 'none', None): 

4155 assert g.isStroke(stroke) 

4156 k.makeMasterGuiBinding(stroke) 

4157 # Create the entry for the mode in k.masterBindingsDict. 

4158 # Important: this is similar, but not the same as k.bindKeyToDict. 

4159 # Thus, we should **not** call k.bindKey here! 

4160 d2 = k.masterBindingsDict.get(modeName, {}) 

4161 d2[stroke] = g.BindingInfo( 

4162 kind=f"mode<{modeName}>", 

4163 commandName=commandName, 

4164 func=func, 

4165 nextMode=bi.nextMode, 

4166 stroke=stroke) 

4167 k.masterBindingsDict[modeName] = d2 

4168 #@+node:ekr.20120208064440.10195: *3* mode_i.createModeCommand 

4169 def createModeCommand(self): 

4170 c = self.c 

4171 key = 'enter-' + self.name.replace(' ', '-') 

4172 

4173 def enterModeCallback(event=None, self=self): 

4174 self.enterMode() 

4175 

4176 c.commandsDict[key] = f = enterModeCallback 

4177 g.trace('(ModeInfo)', f.__name__, key, 

4178 'len(c.commandsDict.keys())', len(list(c.commandsDict.keys()))) 

4179 #@+node:ekr.20120208064440.10180: *3* mode_i.enterMode 

4180 def enterMode(self): 

4181 

4182 c, k = self.c, self.k 

4183 c.inCommand = False 

4184 # Allow inner commands in the mode. 

4185 event = None 

4186 k.generalModeHandler(event, modeName=self.name) 

4187 #@+node:ekr.20120208064440.10153: *3* mode_i.init 

4188 def init(self, name, dataList): 

4189 """aList is a list of tuples (commandName,bi).""" 

4190 c, d, modeName = self.c, self.d, self.name 

4191 for name, bi in dataList: 

4192 if not name: 

4193 # An entry command: put it in the special *entry-commands* key. 

4194 self.entryCommands.append(bi) 

4195 elif bi is not None: 

4196 # A regular shortcut. 

4197 bi.pane = modeName 

4198 aList = d.get(name, []) 

4199 # Important: use previous bindings if possible. 

4200 key2, aList2 = c.config.getShortcut(name) 

4201 aList3 = [z for z in aList2 if z.pane != modeName] 

4202 if aList3: 

4203 aList.extend(aList3) 

4204 aList.append(bi) 

4205 d[name] = aList 

4206 #@+node:ekr.20120208064440.10158: *3* mode_i.initMode 

4207 def initMode(self): 

4208 

4209 c, k = self.c, self.c.k 

4210 k.inputModeName = self.name 

4211 k.silentMode = False 

4212 for bi in self.entryCommands: 

4213 commandName = bi.commandName 

4214 k.simulateCommand(commandName) 

4215 # Careful, the command can kill the commander. 

4216 if g.app.quitting or not c.exists: 

4217 return 

4218 # New in Leo 4.5: a startup command can immediately transfer to another mode. 

4219 if commandName.startswith('enter-'): 

4220 return 

4221 # Create bindings after we know whether we are in silent mode. 

4222 w = k.modeWidget if k.silentMode else k.w 

4223 k.createModeBindings(self.name, self.d, w) 

4224 k.showStateAndMode(prompt=self.name) 

4225 #@-others 

4226#@-others 

4227#@@language python 

4228#@@tabwidth -4 

4229#@@pagewidth 70 

4230#@-leo