Coverage for C:\leo.repo\leo-editor\leo\commands\killBufferCommands.py: 53%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2#@+leo-ver=5-thin
3#@+node:ekr.20150514040142.1: * @file ../commands/killBufferCommands.py
4#@@first
5"""Leo's kill-buffer commands."""
6#@+<< imports >>
7#@+node:ekr.20150514050411.1: ** << imports >> (killBufferCommands.py)
8from leo.core import leoGlobals as g
9from leo.commands.baseCommands import BaseEditCommandsClass
10#@-<< imports >>
12def cmd(name):
13 """Command decorator for the KillBufferCommandsClass class."""
14 return g.new_cmd_decorator(name, ['c', 'killBufferCommands',])
16#@+others
17#@+node:ekr.20160514120919.1: ** class KillBufferCommandsClass
18class KillBufferCommandsClass(BaseEditCommandsClass):
19 """A class to manage the kill buffer."""
20 #@+others
21 #@+node:ekr.20150514063305.409: *3* kill.ctor & reloadSettings
22 def __init__(self, c):
23 """Ctor for KillBufferCommandsClass class."""
24 # pylint: disable=super-init-not-called
25 self.c = c
26 self.kbiterator = self.iterateKillBuffer()
27 # For interacting with system clipboard.
28 self.last_clipboard = None
29 # Position of the last item returned by iterateKillBuffer.
30 self.lastYankP = None
31 # The index of the next item to be returned in
32 # g.app.globalKillBuffer by iterateKillBuffer.
33 self.reset = None
34 self.reloadSettings()
36 def reloadSettings(self):
37 """KillBufferCommandsClass.reloadSettings."""
38 c = self.c
39 self.addWsToKillRing = c.config.getBool('add-ws-to-kill-ring')
40 #@+node:ekr.20150514063305.411: *3* addToKillBuffer
41 def addToKillBuffer(self, text):
42 """
43 Insert the text into the kill buffer if force is True or
44 the text contains something other than whitespace.
45 """
46 if self.addWsToKillRing or text.strip():
47 g.app.globalKillBuffer = [z for z in g.app.globalKillBuffer if z != text]
48 g.app.globalKillBuffer.insert(0, text)
49 #@+node:ekr.20150514063305.412: *3* backwardKillSentence
50 @cmd('backward-kill-sentence')
51 def backwardKillSentence(self, event):
52 """Kill the previous sentence."""
53 w = self.editWidget(event)
54 if not w:
55 return
56 s = w.getAllText()
57 ins = w.getInsertPoint()
58 i = s.rfind('.', ins)
59 if i == -1:
60 return
61 undoType = 'backward-kill-sentence'
62 self.beginCommand(w, undoType=undoType)
63 i2 = s.rfind('.', 0, i) + 1
64 self.killHelper(event, i2, i + 1, undoType=undoType)
65 w.setInsertPoint(i2)
66 self.endCommand(changed=True, setLabel=True)
67 #@+node:ekr.20150514063305.413: *3* backwardKillWord & killWord
68 @cmd('backward-kill-word')
69 def backwardKillWord(self, event):
70 """Kill the previous word."""
71 c = self.c
72 w = self.editWidget(event)
73 if w:
74 self.beginCommand(w, undoType='backward-kill-word')
75 c.editCommands.backwardWord(event)
76 self.killWordHelper(event)
78 @cmd('kill-word')
79 def killWord(self, event):
80 """Kill the word containing the cursor."""
81 w = self.editWidget(event)
82 if w:
83 self.beginCommand(w, undoType='kill-word')
84 self.killWordHelper(event)
86 def killWordHelper(self, event):
87 c = self.c
88 e = c.editCommands
89 w = e.editWidget(event)
90 if w:
91 # self.killWs(event)
92 e.extendToWord(event)
93 i, j = w.getSelectionRange()
94 self.killHelper(event, i, j)
95 self.endCommand(changed=True, setLabel=True)
96 #@+node:ekr.20150514063305.414: *3* clearKillRing
97 @cmd('clear-kill-ring')
98 def clearKillRing(self, event=None):
99 """Clear the kill ring."""
100 g.app.globalKillbuffer = []
101 #@+node:ekr.20150514063305.415: *3* getClipboard
102 def getClipboard(self):
103 """Return the contents of the clipboard."""
104 try:
105 ctxt = g.app.gui.getTextFromClipboard()
106 if not g.app.globalKillBuffer or ctxt != self.last_clipboard:
107 self.last_clipboard = ctxt
108 if not g.app.globalKillBuffer or g.app.globalKillBuffer[0] != ctxt:
109 return ctxt
110 except Exception:
111 g.es_exception()
112 return None
113 #@+node:ekr.20150514063305.416: *3* class iterateKillBuffer
114 class KillBufferIterClass:
115 """Returns a list of positions in a subtree, possibly including the root of the subtree."""
116 #@+others
117 #@+node:ekr.20150514063305.417: *4* __init__ & __iter__ (iterateKillBuffer)
118 def __init__(self, c):
119 """Ctor for KillBufferIterClass class."""
120 self.c = c
121 self.index = 0 # The index of the next item to be returned.
123 def __iter__(self):
124 return self
125 #@+node:ekr.20150514063305.418: *4* __next__
126 def __next__(self):
127 commands = self.c.killBufferCommands
128 aList = g.app.globalKillBuffer # commands.killBuffer
129 if not aList:
130 self.index = 0
131 return None
132 if commands.reset is None:
133 i = self.index
134 else:
135 i = commands.reset
136 commands.reset = None
137 if i < 0 or i >= len(aList):
138 i = 0
139 val = aList[i]
140 self.index = i + 1
141 return val
143 #@-others
145 def iterateKillBuffer(self):
146 return self.KillBufferIterClass(self.c)
147 #@+node:ekr.20150514063305.419: *3* ec.killHelper
148 def killHelper(self, event, frm, to, undoType=None):
149 """
150 A helper method for all kill commands except kill-paragraph commands.
151 """
152 w = self.editWidget(event)
153 if not w:
154 return
155 # Extend (frm, to) if it spans a line.
156 i, j = w.getSelectionRange()
157 s = w.get(i, j)
158 if s.find('\n') > -1:
159 frm, to = i, j
160 s = w.get(frm, to)
161 if undoType:
162 self.beginCommand(w, undoType=undoType)
163 self.addToKillBuffer(s)
164 g.app.gui.replaceClipboardWith(s)
165 w.delete(frm, to)
166 w.setInsertPoint(frm)
167 if undoType:
168 self.endCommand(changed=True, setLabel=True)
169 #@+node:ekr.20220121073752.1: *3* ec.killParagraphHelper
170 def killParagraphHelper(self, event, frm, to, undoType=None):
171 """A helper method for kill-paragraph commands."""
172 w = self.editWidget(event)
173 if not w:
174 return
175 s = w.get(frm, to)
176 if undoType:
177 self.beginCommand(w, undoType=undoType)
178 self.addToKillBuffer(s)
179 g.app.gui.replaceClipboardWith(s)
180 w.delete(frm, to)
181 w.setInsertPoint(frm)
182 if undoType:
183 self.endCommand(changed=True, setLabel=True)
184 #@+node:ekr.20150514063305.420: *3* ec.killToEndOfLine
185 @cmd('kill-to-end-of-line')
186 def killToEndOfLine(self, event):
187 """Kill from the cursor to end of the line."""
188 w = self.editWidget(event)
189 if not w:
190 return
191 s = w.getAllText()
192 ins = w.getInsertPoint()
193 i, j = g.getLine(s, ins)
194 if ins >= len(s) and g.match(s, j - 1, '\n'):
195 # Kill the trailing newline of the body text.
196 i = max(0, len(s) - 1)
197 j = len(s)
198 elif ins + 1 < j and s[ins : j - 1].strip() and g.match(s, j - 1, '\n'):
199 # Kill the line, but not the newline.
200 i, j = ins, j - 1
201 elif g.match(s, j - 1, '\n'):
202 i = ins # Kill the newline in the present line.
203 else:
204 i = j
205 if i < j:
206 self.killHelper(event, i, j, undoType='kill-to-end-of-line')
207 #@+node:ekr.20150514063305.421: *3* ec.killLine
208 @cmd('kill-line')
209 def killLine(self, event):
210 """Kill the line containing the cursor."""
211 w = self.editWidget(event)
212 if not w:
213 return
214 s = w.getAllText()
215 ins = w.getInsertPoint()
216 i, j = g.getLine(s, ins)
217 if ins >= len(s) and g.match(s, j - 1, '\n'):
218 # Kill the trailing newline of the body text.
219 i = max(0, len(s) - 1)
220 j = len(s)
221 elif j > i + 1 and g.match(s, j - 1, '\n'):
222 # Kill the line, but not the newline.
223 j -= 1
224 else:
225 pass # Kill the newline in the present line.
226 self.killHelper(event, i, j, undoType='kill-line')
227 #@+node:ekr.20150514063305.422: *3* killRegion & killRegionSave
228 @cmd('kill-region')
229 def killRegion(self, event):
230 """Kill the text selection."""
231 w = self.editWidget(event)
232 if not w:
233 return
234 i, j = w.getSelectionRange()
235 if i == j:
236 return
237 s = w.getSelectedText()
238 self.beginCommand(w, undoType='kill-region')
239 w.delete(i, j)
240 self.endCommand(changed=True, setLabel=True)
241 self.addToKillBuffer(s)
242 g.app.gui.replaceClipboardWith(s)
244 @cmd('kill-region-save')
245 def killRegionSave(self, event):
246 """Add the selected text to the kill ring, but do not delete it."""
247 w = self.editWidget(event)
248 if not w:
249 return
250 i, j = w.getSelectionRange()
251 if i == j:
252 return
253 s = w.getSelectedText()
254 self.addToKillBuffer(s)
255 g.app.gui.replaceClipboardWith(s)
256 #@+node:ekr.20150514063305.423: *3* ec.killSentence
257 @cmd('kill-sentence')
258 def killSentence(self, event):
259 """Kill the sentence containing the cursor."""
260 w = self.editWidget(event)
261 if not w:
262 return
263 s = w.getAllText()
264 ins = w.getInsertPoint()
265 i = s.find('.', ins)
266 if i == -1:
267 return
268 undoType = 'kill-sentence'
269 self.beginCommand(w, undoType=undoType)
270 i2 = s.rfind('.', 0, ins) + 1
271 self.killHelper(event, i2, i + 1, undoType=undoType)
272 w.setInsertPoint(i2)
273 self.endCommand(changed=True, setLabel=True)
274 #@+node:ekr.20150514063305.424: *3* killWs
275 @cmd('kill-ws')
276 def killWs(self, event, undoType='kill-ws'):
277 """Kill whitespace."""
278 ws = ''
279 w = self.editWidget(event)
280 if not w:
281 return
282 s = w.getAllText()
283 i = j = ins = w.getInsertPoint()
284 while i >= 0 and s[i] in (' ', '\t'):
285 i -= 1
286 if i < ins:
287 i += 1
288 while j < len(s) and s[j] in (' ', '\t'):
289 j += 1
290 if j > i:
291 ws = s[i:j]
292 w.delete(i, j)
293 if undoType:
294 self.beginCommand(w, undoType=undoType)
295 if self.addWsToKillRing:
296 self.addToKillBuffer(ws)
297 if undoType:
298 self.endCommand(changed=True, setLabel=True)
299 #@+node:ekr.20150514063305.425: *3* yank & yankPop
300 @cmd('yank')
301 @cmd('yank')
302 def yank(self, event=None):
303 """Insert the next entry of the kill ring."""
304 self.yankHelper(event, pop=False)
306 @cmd('yank-pop')
307 def yankPop(self, event=None):
308 """Insert the first entry of the kill ring."""
309 self.yankHelper(event, pop=True)
311 def yankHelper(self, event, pop):
312 """
313 Helper for yank and yank-pop:
314 pop = False: insert the first entry of the kill ring.
315 pop = True: insert the next entry of the kill ring.
316 """
317 c = self.c
318 w = self.editWidget(event)
319 if not w:
320 return
321 current = c.p
322 if not current:
323 return
324 text = w.getAllText()
325 i, j = w.getSelectionRange()
326 clip_text = self.getClipboard()
327 if not g.app.globalKillBuffer and not clip_text:
328 return
329 undoType = 'yank-pop' if pop else 'yank'
330 self.beginCommand(w, undoType=undoType)
331 try:
332 if not pop or self.lastYankP and self.lastYankP != current:
333 self.reset = 0
334 s = self.kbiterator.__next__()
335 if s is None:
336 s = clip_text or ''
337 if i != j:
338 w.deleteTextSelection()
339 if s != s.lstrip(): # s contains leading whitespace.
340 i2, j2 = g.getLine(text, i)
341 k = g.skip_ws(text, i2)
342 if i2 < i <= k:
343 # Replace the line's leading whitespace by s's leading whitespace.
344 w.delete(i2, k)
345 i = i2
346 w.insert(i, s)
347 # Fix bug 1099035: Leo yank and kill behaviour not quite the same as emacs.
348 # w.setSelectionRange(i,i+len(s),insert=i+len(s))
349 w.setInsertPoint(i + len(s))
350 self.lastYankP = current.copy()
351 finally:
352 self.endCommand(changed=True, setLabel=True)
353 #@+node:ekr.20150514063305.427: *3* zapToCharacter
354 @cmd('zap-to-character')
355 def zapToCharacter(self, event):
356 """Kill characters from the insertion point to a given character."""
357 k = self.c.k
358 w = self.editWidget(event)
359 if not w:
360 return
361 state = k.getState('zap-to-char')
362 if state == 0:
363 k.setLabelBlue('Zap To Character: ')
364 k.setState('zap-to-char', 1, handler=self.zapToCharacter)
365 else:
366 ch = event.char if event else ' '
367 k.resetLabel()
368 k.clearState()
369 s = w.getAllText()
370 ins = w.getInsertPoint()
371 i = s.find(ch, ins)
372 if i == -1:
373 return
374 self.beginCommand(w, undoType='zap-to-char')
375 self.addToKillBuffer(s[ins:i])
376 g.app.gui.replaceClipboardWith(s[ins:i]) # Support for proper yank.
377 w.setAllText(s[:ins] + s[i:])
378 w.setInsertPoint(ins)
379 self.endCommand(changed=True, setLabel=True)
380 #@-others
381#@-others
382#@-leo