##// END OF EJS Templates
Merge pull request #2819 from minrk/qt_hist_up...
Bussonnier Matthias -
r9460:eab14951 merge
parent child Browse files
Show More
@@ -1,283 +1,296 b''
1 1 # System library imports
2 2 from IPython.external.qt import QtGui
3 3
4 4 # Local imports
5 5 from IPython.utils.traitlets import Bool
6 6 from console_widget import ConsoleWidget
7 7
8 8
9 9 class HistoryConsoleWidget(ConsoleWidget):
10 10 """ A ConsoleWidget that keeps a history of the commands that have been
11 11 executed and provides a readline-esque interface to this history.
12 12 """
13 13
14 14 #------ Configuration ------------------------------------------------------
15 15
16 16 # If enabled, the input buffer will become "locked" to history movement when
17 17 # an edit is made to a multi-line input buffer. To override the lock, use
18 18 # Shift in conjunction with the standard history cycling keys.
19 19 history_lock = Bool(False, config=True)
20 20
21 21 #---------------------------------------------------------------------------
22 22 # 'object' interface
23 23 #---------------------------------------------------------------------------
24 24
25 25 def __init__(self, *args, **kw):
26 26 super(HistoryConsoleWidget, self).__init__(*args, **kw)
27 27
28 28 # HistoryConsoleWidget protected variables.
29 29 self._history = []
30 30 self._history_edits = {}
31 31 self._history_index = 0
32 32 self._history_prefix = ''
33 33
34 34 #---------------------------------------------------------------------------
35 35 # 'ConsoleWidget' public interface
36 36 #---------------------------------------------------------------------------
37 37
38 38 def execute(self, source=None, hidden=False, interactive=False):
39 39 """ Reimplemented to the store history.
40 40 """
41 41 if not hidden:
42 42 history = self.input_buffer if source is None else source
43 43
44 44 executed = super(HistoryConsoleWidget, self).execute(
45 45 source, hidden, interactive)
46 46
47 47 if executed and not hidden:
48 48 # Save the command unless it was an empty string or was identical
49 49 # to the previous command.
50 50 history = history.rstrip()
51 51 if history and (not self._history or self._history[-1] != history):
52 52 self._history.append(history)
53 53
54 54 # Emulate readline: reset all history edits.
55 55 self._history_edits = {}
56 56
57 57 # Move the history index to the most recent item.
58 58 self._history_index = len(self._history)
59 59
60 60 return executed
61 61
62 62 #---------------------------------------------------------------------------
63 63 # 'ConsoleWidget' abstract interface
64 64 #---------------------------------------------------------------------------
65 65
66 66 def _up_pressed(self, shift_modifier):
67 67 """ Called when the up key is pressed. Returns whether to continue
68 68 processing the event.
69 69 """
70 70 prompt_cursor = self._get_prompt_cursor()
71 71 if self._get_cursor().blockNumber() == prompt_cursor.blockNumber():
72 72 # Bail out if we're locked.
73 73 if self._history_locked() and not shift_modifier:
74 74 return False
75 75
76 76 # Set a search prefix based on the cursor position.
77 77 col = self._get_input_buffer_cursor_column()
78 78 input_buffer = self.input_buffer
79 if self._history_index == len(self._history) or \
80 (self._history_prefix and col != len(self._history_prefix)):
79 # use the *shortest* of the cursor column and the history prefix
80 # to determine if the prefix has changed
81 n = min(col, len(self._history_prefix))
82
83 # prefix changed, restart search from the beginning
84 if (self._history_prefix[:n] != input_buffer[:n]):
81 85 self._history_index = len(self._history)
86
87 # the only time we shouldn't set the history prefix
88 # to the line up to the cursor is if we are already
89 # in a simple scroll (no prefix),
90 # and the cursor is at the end of the first line
91 first_line = input_buffer.split('\n', 1)[0]
92 if self._history_index == len(self._history) or \
93 not (self._history_prefix == '' and col == len(first_line)) or \
94 not (self._get_edited_history(self._history_index)[:col] == input_buffer[:col]):
82 95 self._history_prefix = input_buffer[:col]
83 96
84 97 # Perform the search.
85 98 self.history_previous(self._history_prefix,
86 99 as_prefix=not shift_modifier)
87 100
88 101 # Go to the first line of the prompt for seemless history scrolling.
89 102 # Emulate readline: keep the cursor position fixed for a prefix
90 103 # search.
91 104 cursor = self._get_prompt_cursor()
92 105 if self._history_prefix:
93 106 cursor.movePosition(QtGui.QTextCursor.Right,
94 107 n=len(self._history_prefix))
95 108 else:
96 109 cursor.movePosition(QtGui.QTextCursor.EndOfLine)
97 110 self._set_cursor(cursor)
98 111
99 112 return False
100 113
101 114 return True
102 115
103 116 def _down_pressed(self, shift_modifier):
104 117 """ Called when the down key is pressed. Returns whether to continue
105 118 processing the event.
106 119 """
107 120 end_cursor = self._get_end_cursor()
108 121 if self._get_cursor().blockNumber() == end_cursor.blockNumber():
109 122 # Bail out if we're locked.
110 123 if self._history_locked() and not shift_modifier:
111 124 return False
112 125
113 126 # Perform the search.
114 127 replaced = self.history_next(self._history_prefix,
115 128 as_prefix=not shift_modifier)
116 129
117 130 # Emulate readline: keep the cursor position fixed for a prefix
118 131 # search. (We don't need to move the cursor to the end of the buffer
119 132 # in the other case because this happens automatically when the
120 133 # input buffer is set.)
121 134 if self._history_prefix and replaced:
122 135 cursor = self._get_prompt_cursor()
123 136 cursor.movePosition(QtGui.QTextCursor.Right,
124 137 n=len(self._history_prefix))
125 138 self._set_cursor(cursor)
126 139
127 140 return False
128 141
129 142 return True
130 143
131 144 #---------------------------------------------------------------------------
132 145 # 'HistoryConsoleWidget' public interface
133 146 #---------------------------------------------------------------------------
134 147
135 148 def history_previous(self, substring='', as_prefix=True):
136 149 """ If possible, set the input buffer to a previous history item.
137 150
138 151 Parameters:
139 152 -----------
140 153 substring : str, optional
141 154 If specified, search for an item with this substring.
142 155 as_prefix : bool, optional
143 156 If True, the substring must match at the beginning (default).
144 157
145 158 Returns:
146 159 --------
147 160 Whether the input buffer was changed.
148 161 """
149 162 index = self._history_index
150 163 replace = False
151 164 while index > 0:
152 165 index -= 1
153 166 history = self._get_edited_history(index)
154 167 if (as_prefix and history.startswith(substring)) \
155 168 or (not as_prefix and substring in history):
156 169 replace = True
157 170 break
158 171
159 172 if replace:
160 173 self._store_edits()
161 174 self._history_index = index
162 175 self.input_buffer = history
163 176
164 177 return replace
165 178
166 179 def history_next(self, substring='', as_prefix=True):
167 180 """ If possible, set the input buffer to a subsequent history item.
168 181
169 182 Parameters:
170 183 -----------
171 184 substring : str, optional
172 185 If specified, search for an item with this substring.
173 186 as_prefix : bool, optional
174 187 If True, the substring must match at the beginning (default).
175 188
176 189 Returns:
177 190 --------
178 191 Whether the input buffer was changed.
179 192 """
180 193 index = self._history_index
181 194 replace = False
182 while self._history_index < len(self._history):
195 while index < len(self._history):
183 196 index += 1
184 197 history = self._get_edited_history(index)
185 198 if (as_prefix and history.startswith(substring)) \
186 199 or (not as_prefix and substring in history):
187 200 replace = True
188 201 break
189 202
190 203 if replace:
191 204 self._store_edits()
192 205 self._history_index = index
193 206 self.input_buffer = history
194 207
195 208 return replace
196 209
197 210 def history_tail(self, n=10):
198 211 """ Get the local history list.
199 212
200 213 Parameters:
201 214 -----------
202 215 n : int
203 216 The (maximum) number of history items to get.
204 217 """
205 218 return self._history[-n:]
206 219
207 220 def _request_update_session_history_length(self):
208 221 msg_id = self.kernel_manager.shell_channel.execute('',
209 222 silent=True,
210 223 user_expressions={
211 224 'hlen':'len(get_ipython().history_manager.input_hist_raw)',
212 225 }
213 226 )
214 227 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'save_magic')
215 228
216 229 def _handle_execute_reply(self, msg):
217 230 """ Handles replies for code execution, here only session history length
218 231 """
219 232 msg_id = msg['parent_header']['msg_id']
220 233 info = self._request_info['execute'].pop(msg_id,None)
221 234 if info and info.kind == 'save_magic' and not self._hidden:
222 235 content = msg['content']
223 236 status = content['status']
224 237 if status == 'ok':
225 238 self._max_session_history=(int(content['user_expressions']['hlen']))
226 239
227 240 def save_magic(self):
228 241 # update the session history length
229 242 self._request_update_session_history_length()
230 243
231 244 file_name,extFilter = QtGui.QFileDialog.getSaveFileName(self,
232 245 "Enter A filename",
233 246 filter='Python File (*.py);; All files (*.*)'
234 247 )
235 248
236 249 # let's the user search/type for a file name, while the history length
237 250 # is fetched
238 251
239 252 if file_name:
240 253 hist_range, ok = QtGui.QInputDialog.getText(self,
241 254 'Please enter an interval of command to save',
242 255 'Saving commands:',
243 256 text=str('1-'+str(self._max_session_history))
244 257 )
245 258 if ok:
246 259 self.execute("%save"+" "+file_name+" "+str(hist_range))
247 260
248 261 #---------------------------------------------------------------------------
249 262 # 'HistoryConsoleWidget' protected interface
250 263 #---------------------------------------------------------------------------
251 264
252 265 def _history_locked(self):
253 266 """ Returns whether history movement is locked.
254 267 """
255 268 return (self.history_lock and
256 269 (self._get_edited_history(self._history_index) !=
257 270 self.input_buffer) and
258 271 (self._get_prompt_cursor().blockNumber() !=
259 272 self._get_end_cursor().blockNumber()))
260 273
261 274 def _get_edited_history(self, index):
262 275 """ Retrieves a history item, possibly with temporary edits.
263 276 """
264 277 if index in self._history_edits:
265 278 return self._history_edits[index]
266 279 elif index == len(self._history):
267 280 return unicode()
268 281 return self._history[index]
269 282
270 283 def _set_history(self, history):
271 284 """ Replace the current history with a sequence of history items.
272 285 """
273 286 self._history = list(history)
274 287 self._history_edits = {}
275 288 self._history_index = len(self._history)
276 289
277 290 def _store_edits(self):
278 291 """ If there are edits to the current input buffer, store them.
279 292 """
280 293 current = self.input_buffer
281 294 if self._history_index == len(self._history) or \
282 295 self._history[self._history_index] != current:
283 296 self._history_edits[self._history_index] = current
General Comments 0
You need to be logged in to leave comments. Login now