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