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.20131109170017.16504: * @file leoVim.py 

4#@@first 

5""" 

6Leo's vim mode. 

7 

8**Important** 

9 

10`@bool vim-mode` enables vim *mode*. 

11 

12`@keys Vim bindings` enables vim *emulation*. 

13 

14Vim *mode* is independent of vim *emulation* because 

15k.masterKeyHandler dispatches keys to vim mode before 

16doing the normal key handling that vim emulation uses. 

17""" 

18import os 

19import string 

20from leo.core import leoGlobals as g 

21from leo.core.leoGui import LeoKeyEvent 

22#@+others 

23#@+node:ekr.20140802183521.17997: ** show_stroke 

24#@@nobeautify 

25 

26def show_stroke(stroke): 

27 """Return the best human-readable form of stroke.""" 

28 s = stroke.s if g.isStroke(stroke) else stroke 

29 d = { 

30 '\n': r'<NL>', 

31 'Ctrl+Left': '<Ctrl+Lt>', 

32 'Ctrl+Right': '<Ctrl+Rt>', 

33 'Ctrl+r': '<Ctrl+r>', 

34 'Down': '<Dn>', 

35 'Escape': '<Esc>', 

36 'Left': '<Lt>', 

37 'Right': '<Rt>', 

38 'Up': '<Up>', 

39 'colon': ':', 

40 'dollar': '$', 

41 'period': '.', 

42 'space': ' ', 

43 } 

44 return d.get(s, s) 

45#@+node:ekr.20150509040011.1: ** vc.cmd (decorator) 

46def cmd(name): 

47 """Command decorator for the VimCommands class.""" 

48 return g.new_cmd_decorator(name, ['c', 'vimCommands',]) 

49#@+node:ekr.20140802183521.17996: ** class VimEvent 

50class VimEvent: 

51 """A class to contain the components of the dot.""" 

52 

53 def __init__(self, c, char, stroke, w): 

54 """ctor for the VimEvent class.""" 

55 self.c = c 

56 self.char = char # For Leo's core. 

57 self.stroke = stroke 

58 self.w = w 

59 self.widget = w # For Leo's core. 

60 

61 def __repr__(self): 

62 """Return the representation of the stroke.""" 

63 return show_stroke(self.stroke) 

64 

65 __str__ = __repr__ 

66#@+node:ekr.20131113045621.16547: ** class VimCommands 

67class VimCommands: 

68 """ 

69 A class that handles vim mode in Leo. 

70 

71 In vim mode, k.masterKeyHandler calls 

72 

73 """ 

74 #@+others 

75 #@+node:ekr.20131109170017.16507: *3* vc.ctor & helpers 

76 def __init__(self, c): 

77 """The ctor for the VimCommands class.""" 

78 self.c = c 

79 self.k = c.k 

80 self.trace_flag = 'keys' in g.app.debug 

81 # Toggled by :toggle-vim-trace. 

82 self.init_constant_ivars() 

83 self.init_dot_ivars() 

84 self.init_persistent_ivars() 

85 self.init_state_ivars() 

86 self.create_dispatch_dicts() 

87 #@+node:ekr.20140805130800.18157: *4* dispatch dicts... 

88 #@+node:ekr.20140805130800.18162: *5* vc.create_dispatch_dicts 

89 def create_dispatch_dicts(self): 

90 """Create all dispatch dicts.""" 

91 self.normal_mode_dispatch_d = d1 = self.create_normal_dispatch_d() 

92 # Dispatch table for normal mode. 

93 self.motion_dispatch_d = d2 = self.create_motion_dispatch_d() 

94 # Dispatch table for motions. 

95 self.vis_dispatch_d = d3 = self.create_vis_dispatch_d() 

96 # Dispatch table for visual mode. 

97 # Add all entries in arrow dict to the other dicts. 

98 self.arrow_d = arrow_d = self.create_arrow_d() 

99 for d, tag in ((d1, 'normal'), (d2, 'motion'), (d3, 'visual')): 

100 for key in arrow_d: 

101 if key in d: 

102 g.trace(f"duplicate arrow key in {tag} dict: {key}") 

103 else: 

104 d[key] = arrow_d.get(key) 

105 if 1: 

106 # Check for conflicts between motion dict (d2) and the normal and visual dicts. 

107 # These are not necessarily errors, but are useful for debugging. 

108 for d, tag in ((d1, 'normal'), (d3, 'visual')): 

109 for key in d2: 

110 f, f2 = d.get(key), d2.get(key) 

111 if f2 and f and f != f2: 

112 g.trace( 

113 f"conflicting motion key in {tag} " 

114 f"dict: {key} {f2.__name__} {f.__name__}") 

115 elif f2 and not f: 

116 g.trace( 

117 f"missing motion key in {tag} " 

118 f"dict: {key} {f2.__name__}") 

119 # d[key] = f2 

120 #@+node:ekr.20140222064735.16702: *5* vc.create_motion_dispatch_d 

121 #@@nobeautify 

122 

123 def create_motion_dispatch_d(self): 

124 """ 

125 Return the dispatch dict for motions. 

126 Keys are strokes, values are methods. 

127 """ 

128 d = { 

129 '^': self.vim_caret, 

130 '~': None, 

131 '*': None, 

132 '@': None, 

133 '|': None, 

134 '{': None, 

135 '}': None, 

136 '[': None, 

137 ']': None, 

138 ':': None, # Not a motion. 

139 ',': None, 

140 '$': self.vim_dollar, 

141 '>': None, 

142 '<': None, 

143 '-': None, 

144 '#': None, 

145 '(': None, 

146 ')': None, 

147 '%': None, 

148 '.': None, # Not a motion. 

149 '+': None, 

150 '?': self.vim_question, 

151 '"': None, 

152 '`': None, 

153 '\n': self.vim_return, 

154 ';': None, 

155 '/': self.vim_slash, 

156 '_': None, 

157 # Digits. 

158 '0': self.vim_0, # Only 0 starts a motion. 

159 # Uppercase letters. 

160 'A': None, # vim doesn't enter insert mode. 

161 'B': None, 

162 'C': None, 

163 'D': None, 

164 'E': None, 

165 'F': self.vim_F, 

166 'G': self.vim_G, 

167 'H': None, 

168 'I': None, 

169 'J': None, 

170 'K': None, 

171 'L': None, 

172 'M': None, 

173 'N': None, 

174 'O': None, # vim doesn't enter insert mode. 

175 'P': None, 

176 'R': None, 

177 'S': None, 

178 'T': self.vim_T, 

179 'U': None, 

180 'V': None, 

181 'W': None, 

182 'X': None, 

183 'Y': self.vim_Y, # Yank Leo outline. 

184 'Z': None, 

185 # Lowercase letters... 

186 'a': None, # vim doesn't enter insert mode. 

187 'b': self.vim_b, 

188 # 'c': self.vim_c, 

189 'd': None, # Not valid. 

190 'e': self.vim_e, 

191 'f': self.vim_f, 

192 'g': self.vim_g, 

193 'h': self.vim_h, 

194 'i': None, # vim doesn't enter insert mode. 

195 'j': self.vim_j, 

196 'k': self.vim_k, 

197 'l': self.vim_l, 

198 # 'm': self.vim_m, 

199 # 'n': self.vim_n, 

200 'o': None, # vim doesn't enter insert mode. 

201 # 'p': self.vim_p, 

202 # 'q': self.vim_q, 

203 # 'r': self.vim_r, 

204 # 's': self.vim_s, 

205 't': self.vim_t, 

206 # 'u': self.vim_u, 

207 # 'v': self.vim_v, 

208 'w': self.vim_w, 

209 # 'x': self.vim_x, 

210 # 'y': self.vim_y, 

211 # 'z': self.vim_z, 

212 } 

213 return d 

214 #@+node:ekr.20131111061547.16460: *5* vc.create_normal_dispatch_d 

215 def create_normal_dispatch_d(self): 

216 """ 

217 Return the dispatch dict for normal mode. 

218 Keys are strokes, values are methods. 

219 """ 

220 d = { 

221 # Vim hard-coded control characters... 

222 # 'Ctrl+r': self.vim_ctrl_r, 

223 '^': self.vim_caret, 

224 '~': None, 

225 '*': self.vim_star, 

226 '@': None, 

227 '|': None, 

228 '{': None, 

229 '}': None, 

230 '[': None, 

231 ']': None, 

232 ':': self.vim_colon, 

233 ',': None, 

234 '$': self.vim_dollar, 

235 '>': None, 

236 '<': None, 

237 '-': None, 

238 '#': self.vim_pound, 

239 '(': None, 

240 ')': None, 

241 '%': None, 

242 '.': self.vim_dot, 

243 '+': None, 

244 '?': self.vim_question, 

245 '"': None, 

246 '`': None, 

247 '\n': self.vim_return, 

248 ';': None, 

249 '/': self.vim_slash, 

250 '_': None, 

251 # Digits. 

252 '0': self.vim_0, 

253 '1': self.vim_digits, 

254 '2': self.vim_digits, 

255 '3': self.vim_digits, 

256 '4': self.vim_digits, 

257 '5': self.vim_digits, 

258 '6': self.vim_digits, 

259 '7': self.vim_digits, 

260 '8': self.vim_digits, 

261 '9': self.vim_digits, 

262 # Uppercase letters. 

263 'A': self.vim_A, 

264 'B': None, 

265 'C': None, 

266 'D': None, 

267 'E': None, 

268 'F': self.vim_F, 

269 'G': self.vim_G, 

270 'H': None, 

271 'I': None, 

272 'J': None, 

273 'K': None, 

274 'L': None, 

275 'M': None, 

276 'N': self.vim_N, 

277 'O': self.vim_O, 

278 'P': self.vim_P, # Paste *outline* 

279 'R': None, 

280 'S': None, 

281 'T': self.vim_T, 

282 'U': None, 

283 'V': self.vim_V, 

284 'W': None, 

285 'X': None, 

286 'Y': self.vim_Y, 

287 'Z': None, 

288 # Lowercase letters... 

289 'a': self.vim_a, 

290 'b': self.vim_b, 

291 'c': self.vim_c, 

292 'd': self.vim_d, 

293 'e': self.vim_e, 

294 'f': self.vim_f, 

295 'g': self.vim_g, 

296 'h': self.vim_h, 

297 'i': self.vim_i, 

298 'j': self.vim_j, 

299 'k': self.vim_k, 

300 'l': self.vim_l, 

301 'm': self.vim_m, 

302 'n': self.vim_n, 

303 'o': self.vim_o, 

304 'p': self.vim_p, 

305 'q': self.vim_q, 

306 'r': self.vim_r, 

307 's': self.vim_s, 

308 't': self.vim_t, 

309 'u': self.vim_u, 

310 'v': self.vim_v, 

311 'w': self.vim_w, 

312 'x': self.vim_x, 

313 'y': self.vim_y, 

314 'z': self.vim_z, 

315 } 

316 return d 

317 #@+node:ekr.20140222064735.16630: *5* vc.create_vis_dispatch_d 

318 def create_vis_dispatch_d(self): 

319 """ 

320 Create a dispatch dict for visual mode. 

321 Keys are strokes, values are methods. 

322 """ 

323 d = { 

324 '\n': self.vim_return, 

325 ' ': self.vim_l, 

326 # Terminating commands... 

327 'Escape': self.vis_escape, 

328 'J': self.vis_J, 

329 'c': self.vis_c, 

330 'd': self.vis_d, 

331 'u': self.vis_u, 

332 'v': self.vis_v, 

333 'y': self.vis_y, 

334 # Motions... 

335 '0': self.vim_0, 

336 '1': self.vim_digits, 

337 '2': self.vim_digits, 

338 '3': self.vim_digits, 

339 '4': self.vim_digits, 

340 '5': self.vim_digits, 

341 '6': self.vim_digits, 

342 '7': self.vim_digits, 

343 '8': self.vim_digits, 

344 '9': self.vim_digits, 

345 'F': self.vim_F, 

346 'G': self.vim_G, 

347 'T': self.vim_T, 

348 'Y': self.vim_Y, 

349 '^': self.vim_caret, 

350 'b': self.vim_b, 

351 '$': self.vim_dollar, 

352 'e': self.vim_e, 

353 'f': self.vim_f, 

354 'g': self.vim_g, 

355 'h': self.vim_h, 

356 'j': self.vim_j, 

357 'k': self.vim_k, 

358 'l': self.vim_l, 

359 'n': self.vim_n, 

360 '?': self.vim_question, 

361 '/': self.vim_slash, 

362 't': self.vim_t, 

363 'V': self.vim_V, 

364 'w': self.vim_w, 

365 } 

366 return d 

367 #@+node:ekr.20140805130800.18161: *5* vc.create_arrow_d 

368 def create_arrow_d(self): 

369 """Return a dict binding *all* arrows to self.arrow.""" 

370 d = {} 

371 for arrow in ('Left', 'Right', 'Up', 'Down'): 

372 for mod in ('', 

373 'Alt+', 'Alt+Ctrl', 'Alt+Ctrl+Shift', 

374 'Ctrl+', 'Shift+', 'Ctrl+Shift+' 

375 ): 

376 d[mod + arrow] = self.vim_arrow 

377 return d 

378 #@+node:ekr.20140804222959.18930: *4* vc.finishCreate 

379 def finishCreate(self): 

380 """Complete the initialization for the VimCommands class.""" 

381 # Set the widget for set_border. 

382 c = self.c 

383 if c.vim_mode: 

384 # g.registerHandler('idle',self.on_idle) 

385 try: 

386 # Be careful: c.frame or c.frame.body may not exist in some gui's. 

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

388 except Exception: 

389 self.w = None 

390 if c.config.getBool('vim-trainer-mode', default=False): 

391 self.toggle_vim_trainer_mode() 

392 #@+node:ekr.20140803220119.18103: *4* vc.init helpers 

393 # Every ivar of this class must be initied in exactly one init helper. 

394 #@+node:ekr.20140803220119.18104: *5* vc.init_dot_ivars 

395 def init_dot_ivars(self): 

396 """Init all dot-related ivars.""" 

397 self.in_dot = False 

398 # True if we are executing the dot command. 

399 self.dot_list = [] 

400 # This list is preserved across commands. 

401 self.old_dot_list = [] 

402 # The dot_list saved at the start of visual mode. 

403 #@+node:ekr.20140803220119.18109: *5* vc.init_constant_ivars 

404 def init_constant_ivars(self): 

405 """Init ivars whose values never change.""" 

406 self.chars = [ch for ch in string.printable if 32 <= ord(ch) < 128] 

407 # List of printable characters 

408 self.register_names = string.ascii_letters 

409 # List of register names. 

410 #@+node:ekr.20140803220119.18106: *5* vc.init_state_ivars 

411 def init_state_ivars(self): 

412 """Init all ivars related to command state.""" 

413 self.ch = None 

414 # The incoming character. 

415 self.command_i = None 

416 # The offset into the text at the start of a command. 

417 self.command_list = [] 

418 # The list of all characters seen in this command. 

419 self.command_n = None 

420 # The repeat count in effect at the start of a command. 

421 self.command_w = None 

422 # The widget in effect at the start of a command. 

423 self.event = None 

424 # The event for the current key. 

425 self.extend = False 

426 # True: extending selection. 

427 self.handler = self.do_normal_mode 

428 # Use the handler for normal mode. 

429 self.in_command = False 

430 # True: we have seen some command characters. 

431 self.in_motion = False 

432 # True if parsing an *inner* motion, the 2j in d2j. 

433 self.motion_func = None 

434 # The callback handler to execute after executing an inner motion. 

435 self.motion_i = None 

436 # The offset into the text at the start of a motion. 

437 self.n1 = 1 

438 # The first repeat count. 

439 self.n = 1 

440 # The second repeat count. 

441 self.n1_seen = False 

442 # True if self.n1 has been set. 

443 self.next_func = None 

444 # The continuation of a multi-character command. 

445 self.old_sel = None 

446 # The selection range at the start of a command. 

447 self.repeat_list = [] 

448 # The characters of the current repeat count. 

449 self.return_value = True 

450 # The value returned by do_key(). 

451 # Handlers set this to False to tell k.masterKeyHandler to handle the key. 

452 self.state = 'normal' 

453 # in ('normal','insert','visual',) 

454 self.stroke = None 

455 # The incoming stroke. 

456 self.visual_line_flag = False 

457 # True: in visual-line state. 

458 self.vis_mode_i = None 

459 # The insertion point at the start of visual mode. 

460 self.vis_mode_w = None 

461 # The widget in effect at the start of visual mode. 

462 #@+node:ekr.20140803220119.18107: *5* vc.init_persistent_ivars 

463 def init_persistent_ivars(self): 

464 """Init ivars that are never re-inited.""" 

465 c = self.c 

466 self.colon_w = None 

467 # The widget that has focus when a ':' command begins. May be None. 

468 self.cross_lines = c.config.getBool('vim-crosses-lines', default=True) 

469 # True: allow f,F,h,l,t,T,x to cross line boundaries. 

470 self.register_d = {} 

471 # Keys are letters; values are strings. 

472 self.search_stroke = None 

473 # The stroke ('/' or '?') that starts a vim search command. 

474 self.trainer = False 

475 # True: in vim-training mode: 

476 # Mouse clicks and arrows are disable. 

477 self.w = None 

478 # The present widget. 

479 # c.frame.body.wrapper is a QTextBrowser. 

480 self.j_changed = True 

481 # False if the .leo file's change indicator should be 

482 # cleared after doing the j,j abbreviation. 

483 #@+node:ekr.20140802225657.18023: *3* vc.acceptance methods 

484 # All key handlers must end with a call to an acceptance method. 

485 # 

486 # Acceptance methods set the return_value ivar, which becomes the value 

487 # returned to k.masterKeyHandler by c.vimCommands.do_key: 

488 # 

489 # - True: k.masterKeyHandler returns. 

490 # Vim mode has completely handled the key. 

491 # 

492 # - False: k.masterKeyHander handles the key. 

493 

494 #@+node:ekr.20140803220119.18097: *4* direct acceptance methods 

495 #@+node:ekr.20140802225657.18031: *5* vc.accept 

496 def accept(self, add_to_dot=True, handler=None): 

497 """ 

498 Accept the present stroke. 

499 Optionally, this can set the dot or change self.handler. 

500 This can be a no-op, but even then it is recommended. 

501 """ 

502 self.do_trace() 

503 if handler: 

504 if self.in_motion: 

505 # Tricky: queue up do_inner_motion() to continue the motion. 

506 self.handler = self.do_inner_motion 

507 self.next_func = handler 

508 else: 

509 # Queue the outer handler as usual. 

510 self.handler = handler 

511 if add_to_dot: 

512 self.add_to_dot() 

513 self.show_status() 

514 self.return_value = True 

515 #@+node:ekr.20140802225657.18024: *5* vc.delegate 

516 def delegate(self): 

517 """Delegate the present key to k.masterKeyHandler.""" 

518 self.do_trace() 

519 self.show_status() 

520 self.return_value = False 

521 #@+node:ekr.20140222064735.16631: *5* vc.done 

522 def done(self, add_to_dot=True, return_value=True, set_dot=True, stroke=None): 

523 """Complete a command, preserving text and optionally updating the dot.""" 

524 self.do_trace() 

525 if self.state == 'visual': 

526 self.handler = self.do_visual_mode 

527 # A major bug fix. 

528 if set_dot: 

529 stroke2 = stroke or self.stroke if add_to_dot else None 

530 self.compute_dot(stroke2) 

531 self.command_list = [] 

532 self.show_status() 

533 self.return_value = True 

534 else: 

535 if set_dot: 

536 stroke2 = stroke or self.stroke if add_to_dot else None 

537 self.compute_dot(stroke2) 

538 # Undoably preserve any changes to the body. 

539 self.save_body() 

540 # Clear all state, enter normal mode & show the status. 

541 if self.in_motion: 

542 self.next_func = None 

543 # Do *not* change in_motion! 

544 else: 

545 self.init_state_ivars() 

546 self.show_status() 

547 self.return_value = return_value 

548 #@+node:ekr.20140802225657.18025: *5* vc.ignore 

549 def ignore(self): 

550 """ 

551 Ignore the present key without passing it to k.masterKeyHandler. 

552 

553 **Important**: all code now calls quit() after ignore(). 

554 This code could do that, but calling quit() emphasizes what happens. 

555 """ 

556 self.do_trace() 

557 aList = [z.stroke if isinstance(z, VimEvent) else z for z in self.command_list] 

558 aList = [show_stroke(self.c.k.stroke2char(z)) for z in aList] 

559 g.es_print( 

560 f"ignoring {self.stroke} " 

561 f"in {self.state} mode " 

562 f"after {''.join(aList)}", 

563 color='blue', 

564 ) 

565 # This is a surprisingly helpful trace. 

566 # g.trace(g.callers()) 

567 self.show_status() 

568 self.return_value = True 

569 #@+node:ekr.20140806204042.18115: *5* vc.not_ready 

570 def not_ready(self): 

571 """Print a not ready message and quit.""" 

572 g.es('not ready', g.callers(1)) 

573 self.quit() 

574 #@+node:ekr.20160918060654.1: *5* vc.on_activate 

575 def on_activate(self): 

576 """Handle an activate event.""" 

577 # Fix #270: Vim keys don't always work after double Alt+Tab. 

578 self.quit() 

579 self.show_status() 

580 # This seems not to be needed. 

581 # self.c.k.keyboardQuit() 

582 #@+node:ekr.20140802120757.17999: *5* vc.quit 

583 def quit(self): 

584 """ 

585 Abort any present command. 

586 Don't set the dot and enter normal mode. 

587 """ 

588 self.do_trace() 

589 # Undoably preserve any changes to the body. 

590 self.save_body() 

591 self.init_state_ivars() 

592 self.state = 'normal' 

593 self.show_status() 

594 self.return_value = True 

595 #@+node:ekr.20140807070500.18163: *5* vc.reset 

596 def reset(self, setFocus): 

597 """ 

598 Called from k.keyboardQuit when the user types Ctrl-G (setFocus = True). 

599 Also called when the user clicks the mouse (setFocus = False). 

600 """ 

601 self.do_trace() 

602 if setFocus: 

603 # A hard reset. 

604 self.quit() 

605 elif 0: 

606 # Do *not* change state! 

607 g.trace('no change! state:', self.state, g.callers()) 

608 #@+node:ekr.20140802225657.18034: *4* indirect acceptance methods 

609 #@+node:ekr.20140222064735.16709: *5* vc.begin_insert_mode 

610 def begin_insert_mode(self, i=None, w=None): 

611 """Common code for beginning insert mode.""" 

612 self.do_trace() 

613 # c = self.c 

614 if not w: 

615 w = self.w 

616 self.state = 'insert' 

617 self.command_i = w.getInsertPoint() if i is None else i 

618 self.command_w = w 

619 if 1: 

620 # Add the starting character to the dot, but don't show it. 

621 self.accept(handler=self.do_insert_mode, add_to_dot=False) 

622 self.show_status() 

623 self.add_to_dot() 

624 else: 

625 self.accept(handler=self.do_insert_mode, add_to_dot=True) 

626 #@+node:ekr.20140222064735.16706: *5* vc.begin_motion 

627 def begin_motion(self, motion_func): 

628 """Start an inner motion.""" 

629 self.do_trace() 

630 w = self.w 

631 self.command_w = w 

632 self.in_motion = True 

633 self.motion_func = motion_func 

634 self.motion_i = w.getInsertPoint() 

635 self.n = 1 

636 if self.stroke in '123456789': 

637 self.vim_digits() 

638 else: 

639 self.do_inner_motion() 

640 #@+node:ekr.20140801121720.18076: *5* vc.end_insert_mode 

641 def end_insert_mode(self): 

642 """End an insert mode started with the a,A,i,o and O commands.""" 

643 # Called from vim_esc. 

644 self.do_trace() 

645 w = self.w 

646 s = w.getAllText() 

647 i1 = self.command_i 

648 i2 = w.getInsertPoint() 

649 if i1 > i2: 

650 i1, i2 = i2, i1 

651 s2 = s[i1:i2] 

652 if self.n1 > 1: 

653 s3 = s2 * (self.n1 - 1) 

654 w.insert(i2, s3) 

655 for stroke in s2: 

656 self.add_to_dot(stroke) 

657 self.done() 

658 #@+node:ekr.20140222064735.16629: *5* vc.vim_digits 

659 def vim_digits(self): 

660 """Handle a digit that starts an outer repeat count.""" 

661 self.do_trace() 

662 self.repeat_list = [] 

663 self.repeat_list.append(self.stroke) 

664 self.accept(handler=self.vim_digits_2) 

665 

666 def vim_digits_2(self): 

667 self.do_trace() 

668 if self.stroke in '0123456789': 

669 self.repeat_list.append(self.stroke) 

670 self.accept(handler=self.vim_digits_2) 

671 else: 

672 # Set self.n1 before self.n, so that inner motions won't repeat 

673 # until the end of vim mode. 

674 try: 

675 n = int(''.join(self.repeat_list)) 

676 except Exception: 

677 n = 1 

678 if self.n1_seen: 

679 self.n = n 

680 else: 

681 self.n1_seen = True 

682 self.n1 = n 

683 # Don't clear the repeat_list here. 

684 # The ending character may not be valid, 

685 if self.in_motion: 

686 # Handle the stroke that ended the repeat count. 

687 self.do_inner_motion(restart=True) 

688 else: 

689 # Restart the command. 

690 self.do_normal_mode() 

691 #@+node:ekr.20131111061547.16467: *3* vc.commands 

692 #@+node:ekr.20140805130800.18158: *4* vc.arrow... 

693 def vim_arrow(self): 

694 """ 

695 Handle all non-Alt arrows in any mode. 

696 This method attempts to leave focus unchanged. 

697 """ 

698 # pylint: disable=maybe-no-member 

699 s = self.stroke.s if g.isStroke(self.stroke) else self.stroke 

700 if s.find('Alt+') > -1: 

701 # Any Alt key changes c.p. 

702 self.quit() 

703 self.delegate() 

704 elif self.trainer: 

705 # Ignore all non-Alt arrow keys in text widgets. 

706 if self.is_text_wrapper(self.w): 

707 self.ignore() 

708 self.quit() 

709 else: 

710 # Allow plain-arrow keys work in the outline pane. 

711 self.delegate() 

712 else: 

713 # Delegate all arrow keys. 

714 self.delegate() 

715 #@+node:ekr.20140806075456.18152: *4* vc.vim_return 

716 def vim_return(self): 

717 """ 

718 Handle a return key, regardless of mode. 

719 In the body pane only, it has special meaning. 

720 """ 

721 if self.w: 

722 if self.is_body(self.w): 

723 if self.state == 'normal': 

724 # Entering insert mode is confusing for real vim users. 

725 # It should advance the cursor to the next line. 

726 self.vim_j() 

727 elif self.state == 'visual': 

728 # same as v 

729 self.stroke = 'v' 

730 self.vis_v() 

731 else: 

732 self.done() 

733 else: 

734 self.delegate() 

735 else: 

736 self.delegate() 

737 #@+node:ekr.20140222064735.16634: *4* vc.vim...(normal mode) 

738 #@+node:ekr.20140810181832.18220: *5* vc.update_dot_before_search 

739 def update_dot_before_search(self, find_pattern, change_pattern): 

740 """ 

741 A callback that updates the dot just before searching. 

742 At present, this **leaves the dot unchanged**. 

743 Use the n or N commands to repeat searches, 

744 """ 

745 self.command_list = [] 

746 if 0: # Don't do anything else! 

747 

748 # Don't use add_to_dot(): it updates self.command_list. 

749 

750 def add(stroke): 

751 event = VimEvent(c=self.c, char=stroke, stroke=stroke, w=self.w) 

752 self.dot_list.append(event) 

753 

754 if self.in_dot: 

755 # Don't set the dot again. 

756 return 

757 if self.search_stroke is None: 

758 # We didn't start the search with / or ? 

759 return 

760 if 1: 

761 # This is all we can do until there is a substitution command. 

762 self.change_pattern = change_pattern 

763 # Not used at present. 

764 add(self.search_stroke) 

765 for ch in find_pattern: 

766 add(ch) 

767 self.search_stroke = None 

768 else: 

769 # We could do this is we had a substitution command. 

770 if change_pattern is None: 

771 # A search pattern. 

772 add(self.search_stroke) 

773 for ch in find_pattern: 

774 add(ch) 

775 else: 

776 # A substitution: :%s/find_pattern/change_pattern/g 

777 for s in (":%s/", find_pattern, "/", change_pattern, "/g"): 

778 for ch in s: 

779 add(ch) 

780 self.search_stroke = None 

781 #@+node:ekr.20140811044942.18243: *5* vc.update_selection_after_search 

782 def update_selection_after_search(self): 

783 """ 

784 Extend visual mode's selection after a search. 

785 Called from leoFind.show_success. 

786 """ 

787 if self.state == 'visual': 

788 w = self.w 

789 if w == g.app.gui.get_focus(): 

790 if self.visual_line_flag: 

791 self.visual_line_helper() 

792 else: 

793 i = w.getInsertPoint() 

794 w.setSelectionRange(self.vis_mode_i, i, insert=i) 

795 else: 

796 g.trace('Search has changed nodes.') 

797 #@+node:ekr.20140221085636.16691: *5* vc.vim_0 

798 def vim_0(self): 

799 """Handle zero, either the '0' command or part of a repeat count.""" 

800 if self.is_text_wrapper(self.w): 

801 if self.repeat_list: 

802 self.vim_digits() 

803 else: 

804 if self.state == 'visual': 

805 self.do('beginning-of-line-extend-selection') 

806 else: 

807 self.do('beginning-of-line') 

808 self.done() 

809 elif self.in_tree(self.w): 

810 self.do('goto-first-visible-node') 

811 self.done() 

812 else: 

813 self.quit() 

814 #@+node:ekr.20140220134748.16614: *5* vc.vim_a 

815 def vim_a(self): 

816 """Append text after the cursor N times.""" 

817 if self.in_tree(self.w): 

818 c = self.c 

819 c.bodyWantsFocusNow() 

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

821 else: 

822 w = self.w 

823 if self.is_text_wrapper(w): 

824 self.do('forward-char') 

825 self.begin_insert_mode() 

826 else: 

827 self.quit() 

828 #@+node:ekr.20140730175636.17981: *5* vc.vim_A 

829 def vim_A(self): 

830 """Append text at the end the line N times.""" 

831 if self.in_tree(self.w): 

832 c = self.c 

833 c.bodyWantsFocusNow() 

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

835 else: 

836 w = self.w 

837 if self.is_text_wrapper(w): 

838 self.do('end-of-line') 

839 self.begin_insert_mode() 

840 else: 

841 self.quit() 

842 #@+node:ekr.20140220134748.16618: *5* vc.vim_b 

843 def vim_b(self): 

844 """N words backward.""" 

845 if self.is_text_wrapper(self.w): 

846 for z in range(self.n1 * self.n): 

847 if self.state == 'visual': 

848 self.do('back-word-extend-selection') 

849 else: 

850 self.do('back-word') 

851 self.done() 

852 else: 

853 self.quit() 

854 #@+node:ekr.20140220134748.16619: *5* vc.vim_c (to do) 

855 def vim_c(self): 

856 """ 

857 N cc change N lines 

858 N c{motion} change the text that is moved over with {motion} 

859 VIS c change the highlighted text 

860 """ 

861 self.not_ready() 

862 # self.accept(handler=self.vim_c2) 

863 

864 def vim_c2(self): 

865 if self.is_text_wrapper(self.w): 

866 g.trace(self.stroke) 

867 self.done() 

868 else: 

869 self.quit() 

870 #@+node:ekr.20140807152406.18128: *5* vc.vim_caret 

871 def vim_caret(self): 

872 """Move to start of line.""" 

873 if self.is_text_wrapper(self.w): 

874 if self.state == 'visual': 

875 self.do('back-to-home-extend-selection') 

876 else: 

877 self.do('back-to-home') 

878 self.done() 

879 else: 

880 self.quit() 

881 #@+node:ekr.20140730175636.17983: *5* vc.vim_colon 

882 def vim_colon(self): 

883 """Enter the minibuffer.""" 

884 k = self.k 

885 self.colon_w = self.w # A scratch ivar, for :gt & gT commands. 

886 self.quit() 

887 event = VimEvent(c=self.c, char=':', stroke='colon', w=self.w) 

888 k.fullCommand(event=event) 

889 k.extendLabel(':') 

890 #@+node:ekr.20140806123540.18159: *5* vc.vim_comma (not used) 

891 # This was an attempt to be clever: two commas would switch to insert mode. 

892 

893 def vim_comma(self): 

894 """Handle a comma in normal mode.""" 

895 if self.is_text_wrapper(self.w): 

896 self.accept(handler=self.vim_comma2) 

897 else: 

898 self.quit() 

899 

900 def vim_comma2(self): 

901 if self.is_text_wrapper(self.w): 

902 if self.stroke == 'comma': 

903 self.begin_insert_mode() 

904 else: 

905 self.done() 

906 else: 

907 self.quit() 

908 #@+node:ekr.20140730175636.17992: *5* vc.vim_ctrl_r 

909 def vim_ctrl_r(self): 

910 """Redo the last command.""" 

911 self.c.undoer.redo() 

912 self.done() 

913 #@+node:ekr.20131111171616.16498: *5* vc.vim_d & helpers 

914 def vim_d(self): 

915 """ 

916 N dd delete N lines 

917 d{motion} delete the text that is moved over with {motion} 

918 """ 

919 if self.is_text_wrapper(self.w): 

920 self.n = 1 

921 self.accept(handler=self.vim_d2) 

922 else: 

923 self.quit() 

924 #@+node:ekr.20140811175537.18146: *6* vc.vim_d2 

925 def vim_d2(self): 

926 """Handle the second stroke of the d command.""" 

927 w = self.w 

928 if self.is_text_wrapper(w): 

929 if self.stroke == 'd': 

930 i = w.getInsertPoint() 

931 for z in range(self.n1 * self.n): 

932 # It's simplest just to get the text again. 

933 s = w.getAllText() 

934 i, j = g.getLine(s, i) 

935 # Special case for end of buffer only for n == 1. 

936 # This is exactly how vim works. 

937 if self.n1 * self.n == 1 and i == j == len(s): 

938 i = max(0, i - 1) 

939 g.app.gui.replaceClipboardWith(s[i:j]) 

940 w.delete(i, j) 

941 self.done() 

942 elif self.stroke == 'i': 

943 self.accept(handler=self.vim_di) 

944 else: 

945 self.d_stroke = self.stroke # A scratch var. 

946 self.begin_motion(self.vim_d3) 

947 else: 

948 self.quit() 

949 #@+node:ekr.20140811175537.18147: *6* vc.vim_d3 

950 def vim_d3(self): 

951 """Complete the d command after the cursor has moved.""" 

952 # d2w doesn't extend to line. d2j does. 

953 w = self.w 

954 if self.is_text_wrapper(w): 

955 extend_to_line = self.d_stroke in 'jk' 

956 s = w.getAllText() 

957 i1, i2 = self.motion_i, w.getInsertPoint() 

958 if i1 == i2: 

959 pass 

960 elif i1 < i2: 

961 for z in range(self.n1 * self.n): 

962 if extend_to_line: 

963 i2 = self.to_eol(s, i2) 

964 if i2 < len(s) and s[i2] == '\n': 

965 i2 += 1 

966 g.app.gui.replaceClipboardWith(s[i1:i2]) 

967 w.delete(i1, i2) 

968 else: # i1 > i2 

969 i1, i2 = i2, i1 

970 for z in range(self.n1 * self.n): 

971 if extend_to_line: 

972 i1 = self.to_bol(s, i1) 

973 g.app.gui.replaceClipboardWith(s[i1:i2]) 

974 w.delete(i1, i2) 

975 self.done() 

976 else: 

977 self.quit() 

978 #@+node:ekr.20140811175537.18145: *6* vc.vim_di 

979 def vim_di(self): 

980 """Handle delete inner commands.""" 

981 w = self.w 

982 if self.is_text_wrapper(w): 

983 if self.stroke == 'w': 

984 # diw 

985 self.do('extend-to-word') 

986 g.app.gui.replaceClipboardWith(w.getSelectedText()) 

987 self.do('backward-delete-char') 

988 self.done() 

989 else: 

990 self.ignore() 

991 self.quit() 

992 else: 

993 self.quit() 

994 #@+node:ekr.20140730175636.17991: *5* vc.vim_dollar 

995 def vim_dollar(self): 

996 """Move the cursor to the end of the line.""" 

997 if self.is_text_wrapper(self.w): 

998 if self.state == 'visual': 

999 self.do('end-of-line-extend-selection') 

1000 else: 

1001 self.do('end-of-line') 

1002 self.done() 

1003 else: 

1004 self.quit() 

1005 #@+node:ekr.20131111105746.16544: *5* vc.vim_dot 

1006 def vim_dot(self): 

1007 """Repeat the last command.""" 

1008 if self.in_dot: 

1009 return 

1010 try: 

1011 self.in_dot = True 

1012 # Save the dot list. 

1013 self.old_dot_list = self.dot_list[:] 

1014 # Copy the list so it can't change in the loop. 

1015 for event in self.dot_list[:]: 

1016 # Only k.masterKeyHandler can insert characters! 

1017 # 

1018 # #1757: Create a LeoKeyEvent. 

1019 event = LeoKeyEvent( 

1020 binding=g.KeyStroke(event.stroke), 

1021 c=self.c, 

1022 char=event.char, 

1023 event=event, 

1024 w=self.w, 

1025 ) 

1026 self.k.masterKeyHandler(event) 

1027 # For the dot list to be the old dot list, whatever happens. 

1028 self.command_list = self.old_dot_list[:] 

1029 self.dot_list = self.old_dot_list[:] 

1030 finally: 

1031 self.in_dot = False 

1032 self.done() 

1033 #@+node:ekr.20140222064735.16623: *5* vc.vim_e 

1034 def vim_e(self): 

1035 """Forward to the end of the Nth word.""" 

1036 if self.is_text_wrapper(self.w): 

1037 for z in range(self.n1 * self.n): 

1038 if self.state == 'visual': 

1039 self.do('forward-word-extend-selection') 

1040 else: 

1041 self.do('forward-word') 

1042 self.done() 

1043 elif self.in_tree(self.w): 

1044 self.do('goto-last-visible-node') 

1045 self.done() 

1046 else: 

1047 self.quit() 

1048 #@+node:ekr.20140222064735.16632: *5* vc.vim_esc 

1049 def vim_esc(self): 

1050 """ 

1051 Handle Esc while accumulating a normal mode command. 

1052 

1053 Esc terminates the a,A,i,o and O commands normally. 

1054 Call self.end_insert command to support repeat counts 

1055 such as 5a<lots of typing><esc> 

1056 """ 

1057 if self.state == 'insert': 

1058 self.end_insert_mode() 

1059 elif self.state == 'visual': 

1060 # Clear the selection and reset dot. 

1061 self.vis_v() 

1062 else: 

1063 # self.done() 

1064 self.quit() # It's helpful to clear everything. 

1065 #@+node:ekr.20140222064735.16687: *5* vc.vim_F 

1066 def vim_F(self): 

1067 """Back to the Nth occurrence of <char>.""" 

1068 if self.is_text_wrapper(self.w): 

1069 self.accept(handler=self.vim_F2) 

1070 else: 

1071 self.quit() 

1072 

1073 def vim_F2(self): 

1074 """Handle F <stroke>""" 

1075 if self.is_text_wrapper(self.w): 

1076 w = self.w 

1077 s = w.getAllText() 

1078 if s: 

1079 i = i1 = w.getInsertPoint() 

1080 match_i, n = None, self.n1 * self.n 

1081 i -= 1 # Ensure progress 

1082 while i >= 0: 

1083 if s[i] == self.ch: 

1084 match_i, n = i, n - 1 

1085 if n == 0: 

1086 break 

1087 elif s[i] == '\n' and not self.cross_lines: 

1088 break 

1089 i -= 1 

1090 if match_i is not None: 

1091 for z in range(i1 - match_i): 

1092 if self.state == 'visual': 

1093 self.do('back-char-extend-selection') 

1094 else: 

1095 self.do('back-char') 

1096 self.done() 

1097 else: 

1098 self.quit() 

1099 #@+node:ekr.20140220134748.16620: *5* vc.vim_f 

1100 def vim_f(self): 

1101 """move past the Nth occurrence of <stroke>.""" 

1102 if self.is_text_wrapper(self.w): 

1103 self.accept(handler=self.vim_f2) 

1104 else: 

1105 self.quit() 

1106 

1107 def vim_f2(self): 

1108 """Handle f <stroke>""" 

1109 if self.is_text_wrapper(self.w): 

1110 # ec = self.c.editCommands 

1111 w = self.w 

1112 s = w.getAllText() 

1113 if s: 

1114 i = i1 = w.getInsertPoint() 

1115 match_i, n = None, self.n1 * self.n 

1116 while i < len(s): 

1117 if s[i] == self.ch: 

1118 match_i, n = i, n - 1 

1119 if n == 0: 

1120 break 

1121 elif s[i] == '\n' and not self.cross_lines: 

1122 break 

1123 i += 1 

1124 if match_i is not None: 

1125 for z in range(match_i - i1 + 1): 

1126 if self.state == 'visual': 

1127 self.do('forward-char-extend-selection') 

1128 else: 

1129 self.do('forward-char') 

1130 self.done() 

1131 else: 

1132 self.quit() 

1133 #@+node:ekr.20140803220119.18112: *5* vc.vim_G 

1134 def vim_G(self): 

1135 """Put the cursor on the last character of the file.""" 

1136 if self.is_text_wrapper(self.w): 

1137 if self.state == 'visual': 

1138 self.do('end-of-buffer-extend-selection') 

1139 else: 

1140 self.do('end-of-buffer') 

1141 self.done() 

1142 else: 

1143 self.quit() 

1144 #@+node:ekr.20140220134748.16621: *5* vc.vim_g 

1145 def vim_g(self): 

1146 """ 

1147 N ge backward to the end of the Nth word 

1148 N gg goto line N (default: first line), on the first non-blank character 

1149 gv start highlighting on previous visual area 

1150 """ 

1151 if self.is_text_wrapper(self.w): 

1152 self.accept(handler=self.vim_g2) 

1153 else: 

1154 self.quit() 

1155 

1156 def vim_g2(self): 

1157 if self.is_text_wrapper(self.w): 

1158 # event = self.event 

1159 w = self.w 

1160 extend = self.state == 'visual' 

1161 s = w.getAllText() 

1162 i = w.getInsertPoint() 

1163 if self.stroke == 'g': 

1164 # Go to start of buffer. 

1165 on_line = self.on_same_line(s, 0, i) 

1166 if on_line and extend: 

1167 self.do('back-to-home-extend-selection') 

1168 elif on_line: 

1169 self.do('back-to-home') 

1170 elif extend: 

1171 self.do('beginning-of-buffer-extend-selection') 

1172 else: 

1173 self.do('beginning-of-buffer') 

1174 self.done() 

1175 elif self.stroke == 'b': 

1176 # go to beginning of line: like 0. 

1177 if extend: 

1178 self.do('beginning-of-line-extend-selection') 

1179 else: 

1180 self.do('beginning-of-line') 

1181 self.done() 

1182 elif self.stroke == 'e': 

1183 # got to end of line: like $ 

1184 if self.state == 'visual': 

1185 self.do('end-of-line-extend-selection') 

1186 else: 

1187 self.do('end-of-line') 

1188 self.done() 

1189 elif self.stroke == 'h': 

1190 # go home: like ^. 

1191 if extend: 

1192 self.do('back-to-home-extend-selection') 

1193 elif on_line: 

1194 self.do('back-to-home') 

1195 self.done() 

1196 else: 

1197 self.ignore() 

1198 self.quit() 

1199 else: 

1200 self.quit() 

1201 #@+node:ekr.20131111061547.16468: *5* vc.vim_h 

1202 def vim_h(self): 

1203 """Move the cursor left n chars, but not out of the present line.""" 

1204 if self.is_text_wrapper(self.w): 

1205 w = self.w 

1206 s = w.getAllText() 

1207 i = w.getInsertPoint() 

1208 if i == 0 or (i > 0 and s[i - 1] == '\n'): 

1209 pass 

1210 else: 

1211 for z in range(self.n1 * self.n): 

1212 if i > 0 and s[i - 1] != '\n': 

1213 i -= 1 

1214 if i == 0 or (i > 0 and s[i - 1] == '\n'): 

1215 break # Don't go past present line. 

1216 if self.state == 'visual': 

1217 w.setSelectionRange(self.vis_mode_i, i, insert=i) 

1218 else: 

1219 w.setInsertPoint(i) 

1220 self.done() 

1221 elif self.in_tree(self.w): 

1222 self.do('contract-or-go-left') 

1223 self.done() 

1224 else: 

1225 self.quit() 

1226 #@+node:ekr.20140222064735.16618: *5* vc.vim_i 

1227 def vim_i(self): 

1228 """Insert text before the cursor N times.""" 

1229 if self.in_tree(self.w): 

1230 c = self.c 

1231 c.bodyWantsFocusNow() 

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

1233 else: 

1234 w = self.w 

1235 if self.is_text_wrapper(w): 

1236 self.begin_insert_mode() 

1237 else: 

1238 self.done() 

1239 #@+node:ekr.20140220134748.16617: *5* vc.vim_j 

1240 def vim_j(self): 

1241 """N j Down n lines.""" 

1242 if self.is_text_wrapper(self.w): 

1243 for z in range(self.n1 * self.n): 

1244 if self.state == 'visual': 

1245 self.do('next-line-extend-selection') 

1246 else: 

1247 self.do('next-line') 

1248 self.done() 

1249 elif self.in_tree(self.w): 

1250 self.do('goto-next-visible') 

1251 self.done() 

1252 else: 

1253 self.quit() 

1254 #@+node:ekr.20140222064735.16628: *5* vc.vim_k 

1255 def vim_k(self): 

1256 """Cursor up N lines.""" 

1257 if self.is_text_wrapper(self.w): 

1258 for z in range(self.n1 * self.n): 

1259 if self.state == 'visual': 

1260 self.do('previous-line-extend-selection') 

1261 else: 

1262 self.do('previous-line') 

1263 self.done() 

1264 elif self.in_tree(self.w): 

1265 self.do('goto-prev-visible') 

1266 self.done() 

1267 else: 

1268 self.quit() 

1269 #@+node:ekr.20140222064735.16627: *5* vc.vim_l 

1270 def vim_l(self): 

1271 """Move the cursor right self.n chars, but not out of the present line.""" 

1272 if self.is_text_wrapper(self.w): 

1273 w = self.w 

1274 s = w.getAllText() 

1275 i = w.getInsertPoint() 

1276 if i >= len(s) or s[i] == '\n': 

1277 pass 

1278 else: 

1279 for z in range(self.n1 * self.n): 

1280 if i < len(s) and s[i] != '\n': 

1281 i += 1 

1282 if i >= len(s) or s[i] == '\n': 

1283 break # Don't go past present line. 

1284 if self.state == 'visual': 

1285 w.setSelectionRange(self.vis_mode_i, i, insert=i) 

1286 else: 

1287 w.setInsertPoint(i) 

1288 self.done() 

1289 elif self.in_tree(self.w): 

1290 self.do('expand-and-go-right') 

1291 self.done() 

1292 else: 

1293 self.quit() 

1294 #@+node:ekr.20131111171616.16497: *5* vc.vim_m (to do) 

1295 def vim_m(self): 

1296 """m<a-zA-Z> mark current position with mark.""" 

1297 self.not_ready() 

1298 # self.accept(handler=self.vim_m2) 

1299 

1300 def vim_m2(self): 

1301 g.trace(self.stroke) 

1302 self.done() 

1303 #@+node:ekr.20140220134748.16625: *5* vc.vim_n 

1304 def vim_n(self): 

1305 """Repeat last search N times.""" 

1306 fc = self.c.findCommands 

1307 fc.setup_ivars() 

1308 old_node_only = fc.node_only 

1309 fc.node_only = True 

1310 for z in range(self.n1 * self.n): 

1311 if not fc.findNext(): 

1312 break 

1313 fc.node_only = old_node_only 

1314 self.done() 

1315 #@+node:ekr.20140823045819.18292: *5* vc.vim_N 

1316 def vim_N(self): 

1317 """Repeat last search N times (reversed).""" 

1318 fc = self.c.findCommands 

1319 fc.setup_ivars() 

1320 old_node_only = fc.node_only 

1321 old_reverse = fc.reverse 

1322 fc.node_only = True 

1323 fc.reverse = True 

1324 for z in range(self.n1 * self.n): 

1325 if not fc.findNext(): 

1326 break 

1327 fc.node_only = old_node_only 

1328 fc.reverse = old_reverse 

1329 self.done() 

1330 #@+node:ekr.20140222064735.16692: *5* vc.vim_O 

1331 def vim_O(self): 

1332 """Open a new line above the current line N times.""" 

1333 if self.in_tree(self.w): 

1334 c = self.c 

1335 c.bodyWantsFocusNow() 

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

1337 if self.is_text_wrapper(self.w): 

1338 self.do(['beginning-of-line', 'insert-newline', 'back-char']) 

1339 self.begin_insert_mode() 

1340 else: 

1341 self.quit() 

1342 #@+node:ekr.20140222064735.16619: *5* vc.vim_o 

1343 def vim_o(self): 

1344 """Open a new line below the current line N times.""" 

1345 if self.in_tree(self.w): 

1346 c = self.c 

1347 c.bodyWantsFocusNow() 

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

1349 else: 

1350 w = self.w 

1351 if self.is_text_wrapper(w): 

1352 self.do(['end-of-line', 'insert-newline']) 

1353 self.begin_insert_mode() 

1354 else: 

1355 self.quit() 

1356 #@+node:ekr.20140220134748.16622: *5* vc.vim_p 

1357 def vim_p(self): 

1358 """Paste after the cursor.""" 

1359 if self.in_tree(self.w): 

1360 self.do('paste-node') 

1361 self.done() 

1362 elif self.is_text_wrapper(self.w): 

1363 self.do('paste-text') 

1364 self.done() 

1365 else: 

1366 self.quit() 

1367 #@+node:ekr.20140807152406.18125: *5* vc.vim_P 

1368 def vim_P(self): 

1369 """Paste text at the cursor or paste a node before the present node.""" 

1370 if self.in_tree(self.w): 

1371 self.do(['goto-prev-visible', 'paste-node']) 

1372 self.done() 

1373 elif self.is_text_wrapper(self.w): 

1374 self.do(['back-char', 'paste-text']) 

1375 self.done() 

1376 else: 

1377 self.quit() 

1378 #@+node:ekr.20140808173212.18070: *5* vc.vim_pound 

1379 def vim_pound(self): 

1380 """Find previous occurance of word under the cursor.""" 

1381 # ec = self.c.editCommands 

1382 w = self.w 

1383 if self.is_text_wrapper(w): 

1384 i1 = w.getInsertPoint() 

1385 if not w.hasSelection(): 

1386 self.do('extend-to-word') 

1387 if w.hasSelection(): 

1388 fc = self.c.findCommands 

1389 s = w.getSelectedText() 

1390 w.setSelectionRange(i1, i1) 

1391 if not self.in_dot: 

1392 self.dot_list.append(self.stroke) 

1393 old_node_only = fc.node_only 

1394 fc.reverse = True 

1395 fc.find_text = s 

1396 fc.findNext() 

1397 fc.reverse = False 

1398 fc.node_only = old_node_only 

1399 self.done() 

1400 else: 

1401 self.quit() 

1402 #@+node:ekr.20140220134748.16623: *5* vc.vim_q (registers) 

1403 def vim_q(self): 

1404 """ 

1405 q stop recording 

1406 q<A-Z> record typed characters, appended to register <a-z> 

1407 q<a-z> record typed characters into register <a-z> 

1408 """ 

1409 self.not_ready() 

1410 # self.accept(handler=self.vim_q2) 

1411 

1412 def vim_q2(self): 

1413 g.trace(self.stroke) 

1414 # letters = string.ascii_letters 

1415 self.done() 

1416 #@+node:ekr.20140807152406.18127: *5* vc.vim_question 

1417 def vim_question(self): 

1418 """Begin a search.""" 

1419 if self.is_text_wrapper(self.w): 

1420 fc = self.c.findCommands 

1421 self.search_stroke = self.stroke 

1422 # A scratch ivar for update_dot_before_search(). 

1423 fc.reverse = True 

1424 fc.openFindTab(self.event) 

1425 fc.ftm.clear_focus() 

1426 old_node_only = fc.node_only 

1427 fc.start_search1(self.event) 

1428 # This returns immediately, before the actual search. 

1429 # leoFind.show_success calls update_selection_after_search(). 

1430 fc.node_only = old_node_only 

1431 self.done(add_to_dot=False, set_dot=False) 

1432 else: 

1433 self.quit() 

1434 #@+node:ekr.20140220134748.16624: *5* vc.vim_r (to do) 

1435 def vim_r(self): 

1436 """Replace next N characters with <char>""" 

1437 self.not_ready() 

1438 # self.accept(handler=self.vim_r2) 

1439 

1440 def vim_r2(self): 

1441 g.trace(self.n, self.stroke) 

1442 self.done() 

1443 #@+node:ekr.20140222064735.16625: *5* vc.vim_redo (to do) 

1444 def vim_redo(self): 

1445 """N Ctrl-R redo last N changes""" 

1446 self.not_ready() 

1447 #@+node:ekr.20140222064735.16626: *5* vc.vim_s (to do) 

1448 def vim_s(self): 

1449 """Change N characters""" 

1450 self.not_ready() 

1451 # self.accept(handler=self.vim_s2) 

1452 

1453 def vim_s2(self): 

1454 g.trace(self.n, self.stroke) 

1455 self.done() 

1456 #@+node:ekr.20140222064735.16622: *5* vc.vim_slash 

1457 def vim_slash(self): 

1458 """Begin a search.""" 

1459 if self.is_text_wrapper(self.w): 

1460 fc = self.c.findCommands 

1461 self.search_stroke = self.stroke 

1462 # A scratch ivar for update_dot_before_search(). 

1463 fc.reverse = False 

1464 fc.openFindTab(self.event) 

1465 fc.ftm.clear_focus() 

1466 old_node_only = fc.node_only 

1467 fc.start_search1(self.event) 

1468 # This returns immediately, before the actual search. 

1469 # leoFind.show_success calls update_selection_after_search(). 

1470 fc.node_only = old_node_only 

1471 fc.reverse = False 

1472 self.done(add_to_dot=False, set_dot=False) 

1473 else: 

1474 self.quit() 

1475 #@+node:ekr.20140810210411.18239: *5* vc.vim_star 

1476 def vim_star(self): 

1477 """Find previous occurance of word under the cursor.""" 

1478 # ec = self.c.editCommands 

1479 w = self.w 

1480 if self.is_text_wrapper(w): 

1481 i1 = w.getInsertPoint() 

1482 if not w.hasSelection(): 

1483 self.do('extend-to-word') 

1484 if w.hasSelection(): 

1485 fc = self.c.findCommands 

1486 s = w.getSelectedText() 

1487 w.setSelectionRange(i1, i1) 

1488 if not self.in_dot: 

1489 self.dot_list.append(self.stroke) 

1490 old_node_only = fc.node_only 

1491 fc.reverse = False 

1492 fc.find_text = s 

1493 fc.findNext() 

1494 fc.node_only = old_node_only 

1495 self.done() 

1496 else: 

1497 self.quit() 

1498 #@+node:ekr.20140222064735.16620: *5* vc.vim_t 

1499 def vim_t(self): 

1500 """Move before the Nth occurrence of <char> to the right.""" 

1501 if self.is_text_wrapper(self.w): 

1502 self.accept(handler=self.vim_t2) 

1503 else: 

1504 self.quit() 

1505 

1506 def vim_t2(self): 

1507 """Handle t <stroke>""" 

1508 if self.is_text_wrapper(self.w): 

1509 # ec = self.c.editCommands 

1510 w = self.w 

1511 s = w.getAllText() 

1512 if s: 

1513 i = i1 = w.getInsertPoint() 

1514 # ensure progress: 

1515 if i < len(s) and s[i] == self.ch: 

1516 i += 1 

1517 match_i, n = None, self.n1 * self.n 

1518 while i < len(s): 

1519 if s[i] == self.ch: 

1520 match_i, n = i, n - 1 

1521 if n == 0: 

1522 break 

1523 elif s[i] == '\n' and not self.cross_lines: 

1524 break 

1525 i += 1 

1526 if match_i is not None: 

1527 for z in range(match_i - i1): 

1528 if self.state == 'visual': 

1529 self.do('forward-char-extend-selection') 

1530 else: 

1531 self.do('forward-char') 

1532 self.done() 

1533 else: 

1534 self.quit() 

1535 #@+node:ekr.20140222064735.16686: *5* vc.vim_T 

1536 def vim_T(self): 

1537 """Back before the Nth occurrence of <char>.""" 

1538 if self.is_text_wrapper(self.w): 

1539 self.accept(handler=self.vim_T2) 

1540 else: 

1541 self.quit() 

1542 

1543 def vim_T2(self): 

1544 """Handle T <stroke>""" 

1545 if self.is_text_wrapper(self.w): 

1546 # ec = self.c.editCommands 

1547 w = self.w 

1548 s = w.getAllText() 

1549 if s: 

1550 i = i1 = w.getInsertPoint() 

1551 match_i, n = None, self.n1 * self.n 

1552 i -= 1 

1553 if i >= 0 and s[i] == self.ch: 

1554 i -= 1 

1555 while i >= 0: 

1556 if s[i] == self.ch: 

1557 match_i, n = i, n - 1 

1558 if n == 0: 

1559 break 

1560 elif s[i] == '\n' and not self.cross_lines: 

1561 break 

1562 i -= 1 

1563 if match_i is not None: 

1564 for z in range(i1 - match_i - 1): 

1565 if self.state == 'visual': 

1566 self.do('back-char-extend-selection') 

1567 else: 

1568 self.do('back-char') 

1569 self.done() 

1570 else: 

1571 self.quit() 

1572 #@+node:ekr.20140220134748.16626: *5* vc.vim_u 

1573 def vim_u(self): 

1574 """U undo the last command.""" 

1575 self.c.undoer.undo() 

1576 self.quit() 

1577 #@+node:ekr.20140220134748.16627: *5* vc.vim_v 

1578 def vim_v(self): 

1579 """Start visual mode.""" 

1580 if self.n1_seen: 

1581 self.ignore() 

1582 self.quit() 

1583 # self.beep('%sv not valid' % self.n1) 

1584 # self.done() 

1585 elif self.is_text_wrapper(self.w): 

1586 # Enter visual mode. 

1587 self.vis_mode_w = w = self.w 

1588 self.vis_mode_i = w.getInsertPoint() 

1589 self.state = 'visual' 

1590 # Save the dot list in case v terminates visual mode. 

1591 self.old_dot_list = self.dot_list[:] 

1592 self.accept(add_to_dot=False, handler=self.do_visual_mode) 

1593 else: 

1594 self.quit() 

1595 #@+node:ekr.20140811110221.18250: *5* vc.vim_V 

1596 def vim_V(self): 

1597 """Visually select line.""" 

1598 if self.is_text_wrapper(self.w): 

1599 if self.state == 'visual': 

1600 self.visual_line_flag = not self.visual_line_flag 

1601 if self.visual_line_flag: 

1602 # Switch visual mode to visual-line mode. 

1603 # do_visual_mode extends the selection. 

1604 # pylint: disable=unnecessary-pass 

1605 pass 

1606 else: 

1607 # End visual mode. 

1608 self.quit() 

1609 else: 

1610 # Enter visual line mode. 

1611 self.vis_mode_w = w = self.w 

1612 self.vis_mode_i = w.getInsertPoint() 

1613 self.state = 'visual' 

1614 self.visual_line_flag = True 

1615 bx = 'beginning-of-line' 

1616 ex = 'end-of-line-extend-selection' 

1617 self.do([bx, ex]) 

1618 self.done() 

1619 else: 

1620 self.quit() 

1621 #@+node:ekr.20140222064735.16624: *5* vc.vim_w 

1622 def vim_w(self): 

1623 """N words forward.""" 

1624 if self.is_text_wrapper(self.w): 

1625 for z in range(self.n1 * self.n): 

1626 if self.state == 'visual': 

1627 self.do('forward-word-extend-selection') 

1628 else: 

1629 self.do('forward-word') 

1630 self.done() 

1631 else: 

1632 self.quit() 

1633 #@+node:ekr.20140220134748.16629: *5* vc.vim_x 

1634 def vim_x(self): 

1635 """ 

1636 Works like Del if there is a character after the cursor. 

1637 Works like Backspace otherwise. 

1638 """ 

1639 w = self.w 

1640 if self.is_text_wrapper(w): 

1641 delete_flag = False 

1642 for z in range(self.n1 * self.n): 

1643 # It's simplest just to get the text again. 

1644 s = w.getAllText() 

1645 i = w.getInsertPoint() 

1646 if i >= 0: 

1647 if self.cross_lines or s[i] != '\n': 

1648 w.delete(i, i + 1) 

1649 delete_flag = True 

1650 else: 

1651 break 

1652 # Vim works exactly this way: 

1653 # backspace over one character, regardless of count, 

1654 # if nothing has been deleted so far. 

1655 if not delete_flag: 

1656 s = w.getAllText() 

1657 i = w.getInsertPoint() 

1658 if i > 0 and (self.cross_lines or s[i - 1] != '\n'): 

1659 w.delete(i - 1, i) 

1660 self.done() 

1661 else: 

1662 self.quit() 

1663 #@+node:ekr.20140220134748.16630: *5* vc.vim_y 

1664 def vim_y(self): 

1665 """ 

1666 N yy yank N lines 

1667 N y{motion} yank the text moved over with {motion} 

1668 """ 

1669 if self.is_text_wrapper(self.w): 

1670 self.accept(handler=self.vim_y2) 

1671 elif self.in_tree(self.w): 

1672 # Paste an outline. 

1673 c = self.c 

1674 g.es(f"Yank outline: {c.p.h}") 

1675 c.copyOutline() 

1676 self.done() 

1677 else: 

1678 self.quit() 

1679 

1680 def vim_y2(self): 

1681 if self.is_text_wrapper(self.w): 

1682 if self.stroke == 'y': 

1683 # Yank n lines. 

1684 w = self.w 

1685 i1 = i = w.getInsertPoint() 

1686 s = w.getAllText() 

1687 for z in range(self.n1 * self.n): 

1688 i, j = g.getLine(s, i) 

1689 i = j + 1 

1690 w.setSelectionRange(i1, j, insert=j) 

1691 self.c.frame.copyText(event=self.event) 

1692 w.setInsertPoint(i1) 

1693 self.done() 

1694 else: 

1695 self.y_stroke = self.stroke # A scratch var. 

1696 self.begin_motion(self.vim_y3) 

1697 else: 

1698 self.quit() 

1699 

1700 def vim_y3(self): 

1701 """Complete the y command after the cursor has moved.""" 

1702 # The motion is responsible for all repeat counts. 

1703 # y2w doesn't extend to line. y2j does. 

1704 if self.is_text_wrapper(self.w): 

1705 extend_to_line = self.y_stroke in 'jk' 

1706 # n = self.n1 * self.n 

1707 w = self.w 

1708 s = w.getAllText() 

1709 i1, i2 = self.motion_i, w.getInsertPoint() 

1710 if i1 == i2: 

1711 pass 

1712 elif i1 < i2: 

1713 if extend_to_line: 

1714 i2 = self.to_eol(s, i2) 

1715 if i2 < len(s) and s[i2] == '\n': 

1716 i2 += 1 

1717 else: # i1 > i2 

1718 i1, i2 = i2, i1 

1719 if extend_to_line: 

1720 i1 = self.to_bol(s, i1) 

1721 if i1 != i2: 

1722 w.setSelectionRange(i1, i2, insert=i2) 

1723 self.c.frame.copyText(event=self.event) 

1724 w.setInsertPoint(self.motion_i) 

1725 self.done() 

1726 else: 

1727 self.quit() 

1728 #@+node:ekr.20140807152406.18126: *5* vc.vim_Y 

1729 def vim_Y(self): 

1730 """Yank a Leo outline.""" 

1731 self.not_ready() 

1732 #@+node:ekr.20140220134748.16631: *5* vc.vim_z (to do) 

1733 def vim_z(self): 

1734 """ 

1735 zb redraw current line at bottom of window 

1736 zz redraw current line at center of window 

1737 zt redraw current line at top of window 

1738 """ 

1739 self.not_ready() 

1740 # self.accept(handler=self.vim_z2) 

1741 

1742 def vim_z2(self): 

1743 g.trace(self.stroke) 

1744 self.done() 

1745 #@+node:ekr.20140222064735.16658: *4* vc.vis_...(motions) (just notes) 

1746 #@+node:ekr.20140801121720.18071: *5* Notes 

1747 #@@language rest 

1748 #@+at 

1749 # Not yet: 

1750 # N B (motion) N blank-separated WORDS backward 

1751 # N E (motion) forward to the end of the Nth blank-separated WORD 

1752 # N G (motion) goto line N (default: last line), on the first non-blank character 

1753 # N N (motion) repeat last search, in opposite direction 

1754 # N W (motion) N blank-separated WORDS forward 

1755 # N g# (motion) like "#", but also find partial matches 

1756 # N g$ (motion) to last character in screen line (differs from "$" when lines wrap) 

1757 # N g* (motion) like "*", but also find partial matches 

1758 # N g0 (motion) to first character in screen line (differs from "0" when lines wrap) 

1759 # gD (motion) goto global declaration of identifier under the cursor 

1760 # N gE (motion) backward to the end of the Nth blank-separated WORD 

1761 # gd (motion) goto local declaration of identifier under the cursor 

1762 # N ge (motion) backward to the end of the Nth word 

1763 # N gg (motion) goto line N (default: first line), on the first non-blank character 

1764 # N gj (motion) down N screen lines (differs from "j" when line wraps) 

1765 # N gk (motion) up N screen lines (differs from "k" when line wraps) 

1766 #@+node:ekr.20140222064735.16635: *5* motion non-letters (to do) 

1767 #@@nobeautify 

1768 #@@nocolor-node 

1769 #@+at 

1770 # 

1771 # First: 

1772 # 

1773 # 0 (motion) to first character in the line (also: <Home> key) 

1774 # N $ (motion) go to the last character in the line (N-1 lines lower) (also: <End> key) 

1775 # ^ (motion) go to first non-blank character in the line 

1776 # N , (motion) repeat the last "f", "F", "t", or "T" N times in opposite direction 

1777 # N ; (motion) repeat the last "f", "F", "t", or "T" N times 

1778 # N /<CR> (motion) repeat last search, in the forward direction 

1779 # N /{pattern}[/[offset]]<CR> (motion) search forward for the Nth occurrence of {pattern} 

1780 # N ?<CR> (motion) repeat last search, in the backward direction 

1781 # N ?{pattern}[?[offset]]<CR> (motion) search backward for the Nth occurrence of {pattern} 

1782 # 

1783 # Later or never: 

1784 # 

1785 # N CTRL-I (motion) go to Nth newer position in jump list 

1786 # N CTRL-O (motion) go to Nth older position in jump list 

1787 # N CTRL-T (motion) Jump back from Nth older tag in tag list 

1788 # 

1789 # N + (motion) down N lines, on the first non-blank character (also: CTRL-M and <CR>) 

1790 # N _ (motion) down N-1 lines, on the first non-blank character 

1791 # N - (motion) up N lines, on the first non-blank character 

1792 # 

1793 # N ( (motion) N sentences backward 

1794 # N ) (motion) N sentences forward 

1795 # N { (motion) N paragraphs backward 

1796 # N } (motion) N paragraphs forward 

1797 # N | (motion) to column N (default: 1) 

1798 # `" (motion) go to the position when last editing this file 

1799 # '<a-zA-Z0-9[]'"<>> (motion) same as `, but on the first non-blank in the line 

1800 # `< (motion) go to the start of the (previous) Visual area 

1801 # `<0-9> (motion) go to the position where Vim was last exited 

1802 # `<A-Z> (motion) go to mark <A-Z> in any file 

1803 # `<a-z> (motion) go to mark <a-z> within current file 

1804 # `> (motion) go to the end of the (previous) Visual area 

1805 # `[ (motion) go to the start of the previously operated or put text 

1806 # `] (motion) go to the end of the previously operated or put text 

1807 # `` (motion) go to the position before the last jump 

1808 # 

1809 # N % (motion) goto line N percentage down in the file. 

1810 # N must be given, otherwise it is the % command. 

1811 # % (motion) find the next brace, bracket, comment, 

1812 # or "#if"/ "#else"/"#endif" in this line and go to its match 

1813 # 

1814 # N # (motion) search backward for the identifier under the cursor 

1815 # N * (motion) search forward for the identifier under the cursor 

1816 # 

1817 # N [# (motion) N times back to unclosed "#if" or "#else" 

1818 # N [( (motion) N times back to unclosed '(' 

1819 # N [* (motion) N times back to start of comment "/*" 

1820 # N [[ (motion) N sections backward, at start of section 

1821 # N [] (motion) N sections backward, at end of section 

1822 # N [p (motion?) like P, but adjust indent to current line 

1823 # N [{ (motion) N times back to unclosed '{' 

1824 # N ]# (motion) N times forward to unclosed "#else" or "#endif" 

1825 # N ]) (motion) N times forward to unclosed ')' 

1826 # N ]* (motion) N times forward to end of comment "*/" 

1827 # N ][ (motion) N sections forward, at end of section 

1828 # N ]] (motion) N sections forward, at start of section 

1829 # N ]p (motion?) like p, but adjust indent to current line 

1830 # N ]} (motion) N times forward to unclosed '}' 

1831 #@+node:ekr.20140222064735.16655: *6* vis_minus 

1832 #@+node:ekr.20140222064735.16654: *6* vis_plus 

1833 #@+node:ekr.20140222064735.16647: *4* vc.vis_...(terminators) 

1834 # Terminating commands call self.done(). 

1835 #@+node:ekr.20140222064735.16684: *5* vis_escape 

1836 def vis_escape(self): 

1837 """Handle Escape in visual mode.""" 

1838 self.state = 'normal' 

1839 self.done() 

1840 #@+node:ekr.20140222064735.16661: *5* vis_J 

1841 def vis_J(self): 

1842 """Join the highlighted lines.""" 

1843 self.state = 'normal' 

1844 self.not_ready() 

1845 # self.done(set_dot=True) 

1846 #@+node:ekr.20140222064735.16656: *5* vis_c (to do) 

1847 def vis_c(self): 

1848 """Change the highlighted text.""" 

1849 self.state = 'normal' 

1850 self.not_ready() 

1851 # self.done(set_dot=True) 

1852 #@+node:ekr.20140222064735.16657: *5* vis_d 

1853 def vis_d(self): 

1854 """Delete the highlighted text and terminate visual mode.""" 

1855 w = self.vis_mode_w 

1856 if self.is_text_wrapper(w): 

1857 i1 = self.vis_mode_i 

1858 i2 = w.getInsertPoint() 

1859 g.app.gui.replaceClipboardWith(w.getSelectedText()) 

1860 w.delete(i1, i2) 

1861 self.state = 'normal' 

1862 self.done(set_dot=True) 

1863 else: 

1864 self.quit() 

1865 #@+node:ekr.20140222064735.16659: *5* vis_u 

1866 def vis_u(self): 

1867 """Make highlighted text lowercase.""" 

1868 self.state = 'normal' 

1869 self.not_ready() 

1870 # self.done(set_dot=True) 

1871 #@+node:ekr.20140222064735.16681: *5* vis_v 

1872 def vis_v(self): 

1873 """End visual mode.""" 

1874 if 1: 

1875 # End visual node, retain the selection, and set the dot. 

1876 # This makes much more sense in Leo. 

1877 self.state = 'normal' 

1878 self.done() 

1879 else: 

1880 # The real vim clears the selection. 

1881 w = self.w 

1882 if self.is_text_wrapper(w): 

1883 i = w.getInsertPoint() 

1884 w.setSelectionRange(i, i) 

1885 # Visual mode affects the dot only if there is a terminating command. 

1886 self.dot_list = self.old_dot_list 

1887 self.state = 'normal' 

1888 self.done(set_dot=False) 

1889 #@+node:ekr.20140222064735.16660: *5* vis_y 

1890 def vis_y(self): 

1891 """Yank the highlighted text.""" 

1892 if self.is_text_wrapper(self.w): 

1893 self.c.frame.copyText(event=self.event) 

1894 self.state = 'normal' 

1895 self.done(set_dot=True) 

1896 else: 

1897 self.quit() 

1898 #@+node:ekr.20140221085636.16685: *3* vc.do_key & helpers 

1899 def do_key(self, event): 

1900 """ 

1901 Handle the next key in vim mode: 

1902 - Set event, w, stroke and ch ivars for *all* handlers. 

1903 - Call handler(). 

1904 Return True if k.masterKeyHandler should handle this key. 

1905 """ 

1906 try: 

1907 self.init_scanner_vars(event) 

1908 self.do_trace(blank_line=True) 

1909 self.return_value = None 

1910 if not self.handle_specials(): 

1911 self.handler() 

1912 if self.return_value not in (True, False): 

1913 # It looks like no acceptance method has been called. 

1914 self.oops( 

1915 f"bad return_value: {repr(self.return_value)} " 

1916 f"{self.state} {self.next_func}") 

1917 self.done() # Sets self.return_value to True. 

1918 except Exception: 

1919 g.es_exception() 

1920 self.quit() 

1921 return self.return_value 

1922 #@+node:ekr.20140802225657.18021: *4* vc.handle_specials 

1923 def handle_specials(self): 

1924 """Return True self.stroke is an Escape or a Return in the outline pane.""" 

1925 if self.stroke == 'Escape': 

1926 # k.masterKeyHandler handles Ctrl-G. 

1927 # Escape will end insert mode. 

1928 self.vim_esc() 

1929 return True 

1930 if self.stroke == '\n' and self.in_headline(self.w): 

1931 # End headline editing and enter normal mode. 

1932 self.c.endEditing() 

1933 self.done() 

1934 return True 

1935 return False 

1936 #@+node:ekr.20140802120757.18003: *4* vc.init_scanner_vars 

1937 def init_scanner_vars(self, event): 

1938 """Init all ivars used by the scanner.""" 

1939 assert event 

1940 self.event = event 

1941 stroke = event.stroke 

1942 self.ch = event.char # Required for f,F,t,T. 

1943 self.stroke = stroke.s if g.isStroke(stroke) else stroke 

1944 self.w = event and event.w 

1945 if not self.in_command: 

1946 self.in_command = True # May be cleared later. 

1947 if self.is_text_wrapper(self.w): 

1948 self.old_sel = self.w.getSelectionRange() 

1949 #@+node:ekr.20140815160132.18821: *3* vc.external commands 

1950 #@+node:ekr.20140815160132.18823: *4* class vc.LoadFileAtCursor (:r) 

1951 class LoadFileAtCursor: 

1952 """ 

1953 A class to handle Vim's :r command. 

1954 This class supports the do_tab callback. 

1955 """ 

1956 

1957 def __init__(self, vc): 

1958 """Ctor for VimCommands.LoadFileAtCursor class.""" 

1959 self.vc = vc 

1960 

1961 __name__ = ':r' 

1962 # Required. 

1963 #@+others 

1964 #@+node:ekr.20140820034724.18316: *5* :r.__call__ 

1965 def __call__(self, event=None): 

1966 """Prompt for a file name, then load it at the cursor.""" 

1967 self.vc.c.k.getFileName(event, callback=self.load_file_at_cursor) 

1968 #@+node:ekr.20140820034724.18317: *5* :r.load_file_at_cursor 

1969 def load_file_at_cursor(self, fn): 

1970 vc = self.vc 

1971 c, w = vc.c, vc.colon_w 

1972 if not w: 

1973 w = vc.w = c.frame.body.wrapper 

1974 if g.os_path_exists(fn): 

1975 f = open(fn) 

1976 s = f.read() 

1977 f.close() 

1978 i = w.getInsertPoint() 

1979 w.insert(i, s) 

1980 vc.save_body() 

1981 else: 

1982 g.es('does not exist:', fn) 

1983 #@+node:ekr.20140820034724.18318: *5* :r.tab_callback 

1984 def tab_callback(self): 

1985 """Called when the user types :r<tab>""" 

1986 self.vc.c.k.getFileName(event=None, callback=self.load_file_at_cursor) 

1987 #@-others 

1988 #@+node:ekr.20140815160132.18828: *4* class vc.Substitution (:%s & :s) 

1989 class Substitution: 

1990 """A class to handle Vim's :% command.""" 

1991 

1992 def __init__(self, vc, all_lines): 

1993 """Ctor for VimCommands.tabnew class.""" 

1994 self.all_lines = all_lines 

1995 # True: :%s command. False: :s command. 

1996 self.vc = vc 

1997 

1998 __name__ = ':%' 

1999 # Required. 

2000 #@+others 

2001 #@+node:ekr.20140820063930.18321: *5* Substitution.__call__ (:%s & :s) 

2002 def __call__(self, event=None): 

2003 """Handle the :s and :%s commands. Neither command affects the dot.""" 

2004 vc = self.vc 

2005 c, w = vc.c, vc.w 

2006 w = vc.w if c.vim_mode else c.frame.body 

2007 if vc.is_text_wrapper(w): 

2008 fc = vc.c.findCommands 

2009 vc.search_stroke = None 

2010 # Tell vc.update_dot_before_search not to update the dot. 

2011 fc.reverse = False 

2012 fc.openFindTab(vc.event) 

2013 fc.ftm.clear_focus() 

2014 fc.node_only = True 

2015 # Doesn't work. 

2016 fc.start_search1(vc.event) 

2017 # This returns immediately, before the actual search. 

2018 # leoFind.show_success calls vc.update_selection_after_search. 

2019 if c.vim_mode: 

2020 vc.done(add_to_dot=False, set_dot=False) 

2021 elif c.vim_mode: 

2022 vc.quit() 

2023 #@+node:ekr.20140820063930.18323: *5* :%.tab_callback (not used) 

2024 if 0: 

2025 # This Easter Egg is a bad idea. 

2026 # It will just confuse real vim users. 

2027 

2028 def tab_callback(self): 

2029 """ 

2030 Called when the user types :%<tab> or :%/x<tab>. 

2031 This never ends the command: only return does that. 

2032 """ 

2033 k = self.vc.k 

2034 tail = k.functionTail 

2035 tail = tail[1:] if tail.startswith(' ') else tail 

2036 if not tail.startswith('/'): 

2037 tail = '/' + tail 

2038 k.setLabel(k.mb_prefix) 

2039 k.extendLabel(':%' + tail + '/') 

2040 #@-others 

2041 #@+node:ekr.20140815160132.18829: *4* class vc.Tabnew (:e & :tabnew) 

2042 class Tabnew: 

2043 """ 

2044 A class to handle Vim's :tabnew command. 

2045 This class supports the do_tab callback. 

2046 """ 

2047 

2048 def __init__(self, vc): 

2049 """Ctor for VimCommands.tabnew class.""" 

2050 self.vc = vc 

2051 

2052 __name__ = ':tabnew' 

2053 # Required. 

2054 #@+others 

2055 #@+node:ekr.20140820034724.18313: *5* :tabnew.__call__ 

2056 def __call__(self, event=None): 

2057 """Prompt for a file name, the open a new Leo tab.""" 

2058 self.vc.c.k.getFileName(event, callback=self.open_file_by_name) 

2059 #@+node:ekr.20140820034724.18315: *5* :tabnew.open_file_by_name 

2060 def open_file_by_name(self, fn): 

2061 c = self.vc.c 

2062 if fn and g.os_path_isdir(fn): 

2063 # change the working directory. 

2064 c.new() 

2065 try: 

2066 os.chdir(fn) 

2067 g.es(f"chdir({fn})", color='blue') 

2068 except Exception: 

2069 g.es('curdir not changed', color='red') 

2070 elif fn: 

2071 c2 = g.openWithFileName(fn, old_c=c) 

2072 else: 

2073 c.new() 

2074 try: 

2075 g.app.gui.runAtIdle(c2.treeWantsFocusNow) 

2076 except Exception: 

2077 pass 

2078 #@+node:ekr.20140820034724.18314: *5* :tabnew.tab_callback 

2079 def tab_callback(self): 

2080 """Called when the user types :tabnew<tab>""" 

2081 self.vc.c.k.getFileName(event=None, callback=self.open_file_by_name) 

2082 #@-others 

2083 #@+node:ekr.20150509050905.1: *4* vc.e_command & tabnew_command 

2084 @cmd(':e') 

2085 def e_command(self, event=None): 

2086 self.Tabnew(self) 

2087 

2088 @cmd(':tabnew') 

2089 def tabnew_command(self, event=None): 

2090 self.Tabnew(self) 

2091 #@+node:ekr.20140815160132.18824: *4* vc.print_dot (:print-dot) 

2092 @cmd(':print-dot') 

2093 def print_dot(self, event=None): 

2094 """Print the dot.""" 

2095 aList = [z.stroke if isinstance(z, VimEvent) else z for z in self.dot_list] 

2096 aList = [show_stroke(self.c.k.stroke2char(z)) for z in aList] 

2097 if self.n1 > 1: 

2098 g.es_print('dot repeat count:', self.n1) 

2099 i, n = 0, 0 

2100 while i < len(aList): 

2101 g.es_print(f"dot[{n}]:", ''.join(aList[i : i + 10])) 

2102 i += 10 

2103 n += 1 

2104 #@+node:ekr.20140815160132.18825: *4* vc.q/qa_command & quit_now (:q & q! & :qa) 

2105 @cmd(':q') 

2106 def q_command(self, event=None): 

2107 """Quit the present Leo outline, prompting for saves.""" 

2108 g.app.closeLeoWindow(self.c.frame, new_c=None) 

2109 

2110 @cmd(':qa') 

2111 def qa_command(self, event=None): 

2112 """Quit only if there are no unsaved changes.""" 

2113 for c in g.app.commanders(): 

2114 if c.isChanged(): 

2115 return 

2116 g.app.onQuit(event) 

2117 

2118 @cmd(':q!') 

2119 def quit_now(self, event=None): 

2120 """Quit immediately.""" 

2121 g.app.forceShutdown() 

2122 #@+node:ekr.20150509050918.1: *4* vc.r_command 

2123 @cmd(':r') 

2124 def r_command(self, event=None): 

2125 self.LoadFileAtCursor(self) 

2126 #@+node:ekr.20140815160132.18826: *4* vc.revert (:e!) 

2127 @cmd(':e!') 

2128 def revert(self, event=None): 

2129 """Revert all changes to a .leo file, prompting if there have been changes.""" 

2130 self.c.revert() 

2131 #@+node:ekr.20150509050755.1: *4* vc.s_command & percent_s_command 

2132 @cmd(':%s') 

2133 def percent_s_command(self, event=None): 

2134 self.Substitution(self, all_lines=True) 

2135 

2136 @cmd(':s') 

2137 def s_command(self, event=None): 

2138 self.Substitution(self, all_lines=False) 

2139 #@+node:ekr.20140815160132.18827: *4* vc.shell_command (:!) 

2140 @cmd(':!') 

2141 def shell_command(self, event=None): 

2142 """Execute a shell command.""" 

2143 c, k = self.c, self.c.k 

2144 if k.functionTail: 

2145 command = k.functionTail 

2146 c.controlCommands.executeSubprocess(event, command) 

2147 else: 

2148 event = VimEvent(c=self.c, char='', stroke='', w=self.colon_w) 

2149 self.do('shell-command', event=event) 

2150 #@+node:ekr.20140815160132.18830: *4* vc.toggle_vim_mode 

2151 @cmd(':toggle-vim-mode') 

2152 def toggle_vim_mode(self, event=None): 

2153 """toggle vim-mode.""" 

2154 c = self.c 

2155 c.vim_mode = not c.vim_mode 

2156 val = 'on' if c.vim_mode else 'off' 

2157 g.es(f"vim-mode: {val}", color='red') 

2158 if c.vim_mode: 

2159 self.quit() 

2160 else: 

2161 try: 

2162 self.state = 'insert' 

2163 c.bodyWantsFocusNow() 

2164 w = c.frame.body.widget 

2165 self.set_border(kind=None, w=w, activeFlag=True) 

2166 except Exception: 

2167 # g.es_exception() 

2168 pass 

2169 #@+node:ekr.20140909140052.18128: *4* vc.toggle_vim_trace 

2170 @cmd(':toggle-vim-trace') 

2171 def toggle_vim_trace(self, event=None): 

2172 """toggle vim tracing.""" 

2173 self.trace_flag = not self.trace_flag 

2174 val = 'On' if self.trace_flag else 'Off' 

2175 g.es_print(f"vim tracing: {val}") 

2176 #@+node:ekr.20140815160132.18831: *4* vc.toggle_vim_trainer_mode 

2177 @cmd(':toggle-vim-trainer-mode') 

2178 def toggle_vim_trainer_mode(self, event=None): 

2179 """toggle vim-trainer mode.""" 

2180 self.trainer = not self.trainer 

2181 val = 'on' if self.trainer else 'off' 

2182 g.es(f"vim-trainer-mode: {val}", color='red') 

2183 #@+node:ekr.20140815160132.18832: *4* w/xa/wq_command (:w & :xa & wq) 

2184 @cmd(':w') 

2185 def w_command(self, event=None): 

2186 """Save the .leo file.""" 

2187 self.c.save() 

2188 

2189 @cmd(':xa') 

2190 def xa_command(self, event=None): # same as :xa 

2191 """Save all open files and keep working.""" 

2192 for c in g.app.commanders(): 

2193 if c.isChanged(): 

2194 c.save() 

2195 

2196 @cmd(':wq') 

2197 def wq_command(self, event=None): 

2198 """Save all open files and exit.""" 

2199 for c in g.app.commanders(): 

2200 c.save() 

2201 g.app.onQuit(event) 

2202 #@+node:ekr.20140802225657.18026: *3* vc.state handlers 

2203 # Neither state handler nor key handlers ever return non-None. 

2204 #@+node:ekr.20140803220119.18089: *4* vc.do_inner_motion 

2205 def do_inner_motion(self, restart=False): 

2206 """Handle strokes in motions.""" 

2207 try: 

2208 assert self.in_motion 

2209 if restart: 

2210 self.next_func = None 

2211 func = self.next_func or self.motion_dispatch_d.get(self.stroke) 

2212 if func: 

2213 func() 

2214 if self.motion_func: 

2215 self.motion_func() 

2216 self.in_motion = False # Required. 

2217 self.done() 

2218 elif self.is_plain_key(self.stroke): 

2219 self.ignore() 

2220 self.quit() 

2221 else: 

2222 # Pass non-plain keys to k.masterKeyHandler 

2223 self.delegate() 

2224 except Exception: 

2225 g.es_exception() 

2226 self.quit() 

2227 #@+node:ekr.20140803220119.18090: *4* vc.do_insert_mode & helper 

2228 def do_insert_mode(self): 

2229 """Handle insert mode: delegate all strokes to k.masterKeyHandler.""" 

2230 # Support the jj abbreviation when there is no selection. 

2231 self.do_trace() 

2232 try: 

2233 self.state = 'insert' 

2234 w = self.w 

2235 if self.is_text_wrapper(w) and self.test_for_insert_escape(w): 

2236 if self.trace_flag: 

2237 g.trace('*** abort ***', w) 

2238 return 

2239 # Special case for arrow keys. 

2240 if self.stroke in self.arrow_d: 

2241 self.vim_arrow() 

2242 else: 

2243 self.delegate() 

2244 except Exception: 

2245 g.es_exception() 

2246 self.quit() 

2247 #@+node:ekr.20140807112800.18122: *5* vc.test_for_insert_escape 

2248 def test_for_insert_escape(self, w): 

2249 """Return True if the j,j escape sequence has ended insert mode.""" 

2250 c = self.c 

2251 s = w.getAllText() 

2252 i = w.getInsertPoint() 

2253 i2, j = w.getSelectionRange() 

2254 if i2 == j and self.stroke == 'j': 

2255 if i > 0 and s[i - 1] == 'j': 

2256 w.delete(i - 1, i) 

2257 w.setInsertPoint(i - 1) 

2258 # A benign hack: simulate an Escape for the dot. 

2259 self.stroke = 'Escape' 

2260 self.end_insert_mode() 

2261 return True 

2262 # Remember the changed state when we saw the first 'j'. 

2263 self.j_changed = c.isChanged() 

2264 return False 

2265 #@+node:ekr.20140803220119.18091: *4* vc.do_normal_mode 

2266 def do_normal_mode(self): 

2267 """Handle strokes in normal mode.""" 

2268 # Unlike visual mode, there is no need to init anything, 

2269 # because all normal mode commands call self.done. 

2270 self.do_state(self.normal_mode_dispatch_d, 'normal') 

2271 #@+node:ekr.20140802225657.18029: *4* vc.do_state 

2272 def do_state(self, d, mode_name): 

2273 """General dispatcher code. d is a dispatch dict.""" 

2274 try: 

2275 func = d.get(self.stroke) 

2276 if func: 

2277 func() 

2278 elif self.is_plain_key(self.stroke): 

2279 self.ignore() 

2280 self.quit() 

2281 else: 

2282 # Pass non-plain keys to k.masterKeyHandler 

2283 self.delegate() 

2284 except Exception: 

2285 g.es_exception() 

2286 self.quit() 

2287 #@+node:ekr.20140803220119.18092: *4* vc.do_visual_mode 

2288 def do_visual_mode(self): 

2289 """Handle strokes in visual mode.""" 

2290 try: 

2291 self.n1 = self.n = 1 

2292 self.do_state(self.vis_dispatch_d, 

2293 mode_name='visual-line' if self.visual_line_flag else 'visual') 

2294 if self.visual_line_flag: 

2295 self.visual_line_helper() 

2296 except Exception: 

2297 g.es_exception() 

2298 self.quit() 

2299 #@+node:ekr.20140222064735.16682: *3* vc.Utilities 

2300 #@+node:ekr.20140802183521.17998: *4* vc.add_to_dot 

2301 def add_to_dot(self, stroke=None): 

2302 """ 

2303 Add a new VimEvent to self.command_list. 

2304 Never change self.command_list if self.in_dot is True 

2305 Never add . to self.command_list 

2306 """ 

2307 if not self.in_dot: 

2308 s = stroke or self.stroke 

2309 # Never add '.' to the dot list. 

2310 if s and s != 'period': 

2311 event = VimEvent(c=self.c, char=s, stroke=s, w=self.w) 

2312 self.command_list.append(event) 

2313 #@+node:ekr.20140802120757.18002: *4* vc.compute_dot 

2314 def compute_dot(self, stroke): 

2315 """Compute the dot and set the dot ivar.""" 

2316 if stroke: 

2317 self.add_to_dot(stroke) 

2318 if self.command_list: 

2319 self.dot_list = self.command_list[:] 

2320 #@+node:ekr.20140810214537.18241: *4* vc.do 

2321 def do(self, o, event=None): 

2322 """Do one or more Leo commands by name.""" 

2323 if not event: 

2324 event = self.event 

2325 if isinstance(o, (tuple, list)): 

2326 for z in o: 

2327 self.c.k.simulateCommand(z, event=event) 

2328 else: 

2329 self.c.k.simulateCommand(o, event=event) 

2330 #@+node:ekr.20180424055522.1: *4* vc.do_trace 

2331 def do_trace(self, blank_line=False): 

2332 

2333 if self.stroke and self.trace_flag and not g.unitTesting: 

2334 if blank_line: 

2335 print('') 

2336 g.es_print(f"{g.caller():20}: {self.stroke!r}") 

2337 #@+node:ekr.20140802183521.17999: *4* vc.in_headline & vc.in_tree 

2338 def in_headline(self, w): 

2339 """Return True if we are in a headline edit widget.""" 

2340 return self.widget_name(w).startswith('head') 

2341 

2342 def in_tree(self, w): 

2343 """Return True if we are in the outline pane, but not in a headline.""" 

2344 return self.widget_name(w).startswith('canvas') 

2345 #@+node:ekr.20140806081828.18157: *4* vc.is_body & is_head 

2346 def is_body(self, w): 

2347 """Return True if w is the QTextBrowser of the body pane.""" 

2348 w2 = self.c.frame.body.wrapper 

2349 return w == w2 

2350 

2351 def is_head(self, w): 

2352 """Return True if w is an headline edit widget.""" 

2353 return self.widget_name(w).startswith('head') 

2354 #@+node:ekr.20140801121720.18083: *4* vc.is_plain_key & is_text_wrapper 

2355 def is_plain_key(self, stroke): 

2356 """Return True if stroke is a plain key.""" 

2357 return self.k.isPlainKey(stroke) 

2358 

2359 def is_text_wrapper(self, w=None): 

2360 """Return True if w is a text widget.""" 

2361 return self.is_body(w) or self.is_head(w) or g.isTextWrapper(w) 

2362 #@+node:ekr.20140805064952.18153: *4* vc.on_idle (no longer used) 

2363 def on_idle(self, tag, keys): 

2364 """The idle-time handler for the VimCommands class.""" 

2365 c = keys.get('c') 

2366 if c and c.vim_mode and self == c.vimCommands: 

2367 # #1273: only for vim mode. 

2368 g.trace('=====') 

2369 # Call set_border only for the presently selected tab. 

2370 try: 

2371 # Careful: we may not have tabs. 

2372 w = g.app.gui.frameFactory.masterFrame 

2373 except AttributeError: 

2374 w = None 

2375 if w: 

2376 i = w.indexOf(c.frame.top) 

2377 if i == w.currentIndex(): 

2378 self.set_border() 

2379 else: 

2380 self.set_border() 

2381 #@+node:ekr.20140801121720.18079: *4* vc.on_same_line 

2382 def on_same_line(self, s, i1, i2): 

2383 """Return True if i1 and i2 are on the same line.""" 

2384 # Ensure that i1 <= i2 and that i1 and i2 are in range. 

2385 if i1 > i2: 

2386 i1, i2 = i2, i1 

2387 if i1 < 0: 

2388 i1 = 0 

2389 if i1 >= len(s): 

2390 i1 = len(s) - 1 

2391 if i2 < 0: 

2392 i2 = 0 

2393 if i2 >= len(s): 

2394 i2 = len(s) - 1 

2395 if s[i2] == '\n': 

2396 i2 = max(0, i2 - 1) 

2397 return s[i1:i2].count('\n') == 0 

2398 #@+node:ekr.20140802225657.18022: *4* vc.oops 

2399 def oops(self, message): 

2400 """Report an internal error""" 

2401 g.warning(f"Internal vim-mode error: {message}") 

2402 #@+node:ekr.20140802120757.18001: *4* vc.save_body (handles undo) 

2403 def save_body(self): 

2404 """Undoably preserve any changes to body text.""" 

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

2406 w = self.command_w or self.w 

2407 name = c.widget_name(w) 

2408 if w and name.startswith('body'): 

2409 bunch = u.beforeChangeBody(p) 

2410 # Similar to selfInsertCommand. 

2411 newText = w.getAllText() 

2412 if c.p.b != newText: 

2413 p.v.b = newText 

2414 u.afterChangeBody(p, 'vc-save-body', bunch) 

2415 #@+node:ekr.20140804123147.18929: *4* vc.set_border & helper 

2416 def set_border(self, kind=None, w=None, activeFlag=None): 

2417 """ 

2418 Set the border color of self.w, depending on state. 

2419 Called from qtBody.onFocusColorHelper and self.show_status. 

2420 """ 

2421 if not w: 

2422 w = g.app.gui.get_focus() 

2423 if not w: 

2424 return 

2425 w_name = self.widget_name(w) 

2426 if w_name == 'richTextEdit': 

2427 self.set_property(w, focus_flag=activeFlag in (None, True)) 

2428 elif w_name.startswith('head'): 

2429 self.set_property(w, True) 

2430 elif w_name != 'richTextEdit': 

2431 # Clear the border in the body pane. 

2432 try: 

2433 w = self.c.frame.body.widget 

2434 self.set_property(w, False) 

2435 except Exception: 

2436 pass 

2437 #@+node:ekr.20140807070500.18161: *5* vc.set_property 

2438 def set_property(self, w, focus_flag): 

2439 """Set the property of w, depending on focus and state.""" 

2440 c, state = self.c, self.state 

2441 # 

2442 # #1221: Use a style sheet based on new settings. 

2443 if focus_flag: 

2444 d = { 

2445 'normal': ('vim-mode-normal-border', 'border: 3px solid white'), 

2446 'insert': ('vim-mode-insert-border', 'border: 3px solid red'), 

2447 'visual': ('vim-mode-visual-border', 'border: 3px solid yellow'), 

2448 } 

2449 data = d.get(state) 

2450 if not data: 

2451 g.trace('bad vim mode', repr(state)) 

2452 return 

2453 setting, default_border = data 

2454 else: 

2455 setting = 'vim-mode-unfocused-border' 

2456 default_border = 'border: 3px dashed white' 

2457 border = c.config.getString(setting) or default_border 

2458 # g.trace(setting, border) 

2459 w.setStyleSheet(border) 

2460 return 

2461 # 

2462 # This code doesn't work on Qt 5, because of a Qt bug. 

2463 # It probably isn't coming back. 

2464 # selector = f"vim_{state}" if focus_flag else 'vim_unfocused' 

2465 # w.setProperty('vim_state', selector) 

2466 # w.style().unpolish(w) 

2467 # w.style().polish(w) 

2468 #@+node:ekr.20140802142132.17981: *4* vc.show_dot & show_list 

2469 def show_command(self): 

2470 """Show the accumulating command.""" 

2471 return ''.join([repr(z) for z in self.command_list]) 

2472 

2473 def show_dot(self): 

2474 """Show the dot.""" 

2475 s = ''.join([repr(z) for z in self.dot_list[:10]]) 

2476 if len(self.dot_list) > 10: 

2477 s = s + '...' 

2478 return s 

2479 #@+node:ekr.20140222064735.16615: *4* vc.show_status 

2480 def show_status(self): 

2481 """Show self.state and self.command_list""" 

2482 k = self.k 

2483 self.set_border() 

2484 if k.state.kind: 

2485 pass 

2486 # elif self.state == 'visual': 

2487 # s = '%8s:' % self.state.capitalize() 

2488 # k.setLabelBlue(s) 

2489 else: 

2490 if self.visual_line_flag: 

2491 state_s = 'Visual Line' 

2492 else: 

2493 state_s = self.state.capitalize() 

2494 command_s = self.show_command() 

2495 dot_s = self.show_dot() 

2496 # if self.in_motion: state_s = state_s + '(in_motion)' 

2497 if 1: # Don't show the dot: 

2498 s = f"{state_s:8}: {command_s}" 

2499 else: 

2500 s = f"{state_s:8}: {command_s:>5} dot: {dot_s}" 

2501 k.setLabelBlue(s) 

2502 #@+node:ekr.20140801121720.18080: *4* vc.to_bol & vc.eol 

2503 def to_bol(self, s, i): 

2504 """Return the index of the first character on the line containing s[i]""" 

2505 if i >= len(s): 

2506 i = len(s) 

2507 while i > 0 and s[i - 1] != '\n': 

2508 i -= 1 

2509 return i 

2510 

2511 def to_eol(self, s, i): 

2512 """Return the index of the last character on the line containing s[i]""" 

2513 while i < len(s) and s[i] != '\n': 

2514 i += 1 

2515 return i 

2516 #@+node:ekr.20140822072856.18256: *4* vc.visual_line_helper 

2517 def visual_line_helper(self): 

2518 """Extend the selection as necessary in visual line mode.""" 

2519 bx = 'beginning-of-line-extend-selection' 

2520 ex = 'end-of-line-extend-selection' 

2521 w = self.w 

2522 i = w.getInsertPoint() 

2523 # We would like to set insert=i0, but 

2524 # w.setSelectionRange requires either insert==i or insert==j. 

2525 # i0 = i 

2526 if self.vis_mode_i < i: 

2527 # Select from the beginning of the line containing self.vismode_i 

2528 # to the end of the line containing i. 

2529 w.setInsertPoint(self.vis_mode_i) 

2530 self.do(bx) 

2531 i1, i2 = w.getSelectionRange() 

2532 w.setInsertPoint(i) 

2533 self.do(ex) 

2534 j1, j2 = w.getSelectionRange() 

2535 i, j = min(i1, i2), max(j1, j2) 

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

2537 else: 

2538 # Select from the beginning of the line containing i 

2539 # to the end of the line containing self.vismode_i. 

2540 w.setInsertPoint(i) 

2541 self.do(bx) 

2542 i1, i2 = w.getSelectionRange() 

2543 w.setInsertPoint(self.vis_mode_i) 

2544 self.do(ex) 

2545 j1, j2 = w.getSelectionRange() 

2546 i, j = min(i1, i2), max(j1, j2) 

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

2548 #@+node:ekr.20140805064952.18152: *4* vc.widget_name 

2549 def widget_name(self, w): 

2550 return self.c.widget_name(w) 

2551 #@-others 

2552#@-others 

2553#@@language python 

2554#@@tabwidth -4 

2555#@@pagewidth 70 

2556#@-leo