##// END OF EJS Templates
Merge pull request #2946 from minrk/qteol...
Brian E. Granger -
r9718:2a61853e merge
parent child Browse files
Show More
@@ -1,296 +1,302 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 # use the *shortest* of the cursor column and the history prefix
79 # use the *shortest* of the cursor column and the history prefix
80 # to determine if the prefix has changed
80 # to determine if the prefix has changed
81 n = min(col, len(self._history_prefix))
81 n = min(col, len(self._history_prefix))
82
82
83 # prefix changed, restart search from the beginning
83 # prefix changed, restart search from the beginning
84 if (self._history_prefix[:n] != input_buffer[:n]):
84 if (self._history_prefix[:n] != input_buffer[:n]):
85 self._history_index = len(self._history)
85 self._history_index = len(self._history)
86
86
87 # the only time we shouldn't set the history prefix
87 # the only time we shouldn't set the history prefix
88 # to the line up to the cursor is if we are already
88 # to the line up to the cursor is if we are already
89 # in a simple scroll (no prefix),
89 # in a simple scroll (no prefix),
90 # and the cursor is at the end of the first line
90 # and the cursor is at the end of the first line
91 first_line = input_buffer.split('\n', 1)[0]
91
92 # check if we are at the end of the first line
93 c = self._get_cursor()
94 current_pos = c.position()
95 c.movePosition(QtGui.QTextCursor.EndOfLine)
96 at_eol = (c.position() == current_pos)
97
92 if self._history_index == len(self._history) or \
98 if self._history_index == len(self._history) or \
93 not (self._history_prefix == '' and col == len(first_line)) or \
99 not (self._history_prefix == '' and at_eol) or \
94 not (self._get_edited_history(self._history_index)[:col] == input_buffer[:col]):
100 not (self._get_edited_history(self._history_index)[:col] == input_buffer[:col]):
95 self._history_prefix = input_buffer[:col]
101 self._history_prefix = input_buffer[:col]
96
102
97 # Perform the search.
103 # Perform the search.
98 self.history_previous(self._history_prefix,
104 self.history_previous(self._history_prefix,
99 as_prefix=not shift_modifier)
105 as_prefix=not shift_modifier)
100
106
101 # Go to the first line of the prompt for seemless history scrolling.
107 # Go to the first line of the prompt for seemless history scrolling.
102 # Emulate readline: keep the cursor position fixed for a prefix
108 # Emulate readline: keep the cursor position fixed for a prefix
103 # search.
109 # search.
104 cursor = self._get_prompt_cursor()
110 cursor = self._get_prompt_cursor()
105 if self._history_prefix:
111 if self._history_prefix:
106 cursor.movePosition(QtGui.QTextCursor.Right,
112 cursor.movePosition(QtGui.QTextCursor.Right,
107 n=len(self._history_prefix))
113 n=len(self._history_prefix))
108 else:
114 else:
109 cursor.movePosition(QtGui.QTextCursor.EndOfLine)
115 cursor.movePosition(QtGui.QTextCursor.EndOfLine)
110 self._set_cursor(cursor)
116 self._set_cursor(cursor)
111
117
112 return False
118 return False
113
119
114 return True
120 return True
115
121
116 def _down_pressed(self, shift_modifier):
122 def _down_pressed(self, shift_modifier):
117 """ Called when the down key is pressed. Returns whether to continue
123 """ Called when the down key is pressed. Returns whether to continue
118 processing the event.
124 processing the event.
119 """
125 """
120 end_cursor = self._get_end_cursor()
126 end_cursor = self._get_end_cursor()
121 if self._get_cursor().blockNumber() == end_cursor.blockNumber():
127 if self._get_cursor().blockNumber() == end_cursor.blockNumber():
122 # Bail out if we're locked.
128 # Bail out if we're locked.
123 if self._history_locked() and not shift_modifier:
129 if self._history_locked() and not shift_modifier:
124 return False
130 return False
125
131
126 # Perform the search.
132 # Perform the search.
127 replaced = self.history_next(self._history_prefix,
133 replaced = self.history_next(self._history_prefix,
128 as_prefix=not shift_modifier)
134 as_prefix=not shift_modifier)
129
135
130 # Emulate readline: keep the cursor position fixed for a prefix
136 # Emulate readline: keep the cursor position fixed for a prefix
131 # search. (We don't need to move the cursor to the end of the buffer
137 # search. (We don't need to move the cursor to the end of the buffer
132 # in the other case because this happens automatically when the
138 # in the other case because this happens automatically when the
133 # input buffer is set.)
139 # input buffer is set.)
134 if self._history_prefix and replaced:
140 if self._history_prefix and replaced:
135 cursor = self._get_prompt_cursor()
141 cursor = self._get_prompt_cursor()
136 cursor.movePosition(QtGui.QTextCursor.Right,
142 cursor.movePosition(QtGui.QTextCursor.Right,
137 n=len(self._history_prefix))
143 n=len(self._history_prefix))
138 self._set_cursor(cursor)
144 self._set_cursor(cursor)
139
145
140 return False
146 return False
141
147
142 return True
148 return True
143
149
144 #---------------------------------------------------------------------------
150 #---------------------------------------------------------------------------
145 # 'HistoryConsoleWidget' public interface
151 # 'HistoryConsoleWidget' public interface
146 #---------------------------------------------------------------------------
152 #---------------------------------------------------------------------------
147
153
148 def history_previous(self, substring='', as_prefix=True):
154 def history_previous(self, substring='', as_prefix=True):
149 """ If possible, set the input buffer to a previous history item.
155 """ If possible, set the input buffer to a previous history item.
150
156
151 Parameters:
157 Parameters:
152 -----------
158 -----------
153 substring : str, optional
159 substring : str, optional
154 If specified, search for an item with this substring.
160 If specified, search for an item with this substring.
155 as_prefix : bool, optional
161 as_prefix : bool, optional
156 If True, the substring must match at the beginning (default).
162 If True, the substring must match at the beginning (default).
157
163
158 Returns:
164 Returns:
159 --------
165 --------
160 Whether the input buffer was changed.
166 Whether the input buffer was changed.
161 """
167 """
162 index = self._history_index
168 index = self._history_index
163 replace = False
169 replace = False
164 while index > 0:
170 while index > 0:
165 index -= 1
171 index -= 1
166 history = self._get_edited_history(index)
172 history = self._get_edited_history(index)
167 if (as_prefix and history.startswith(substring)) \
173 if (as_prefix and history.startswith(substring)) \
168 or (not as_prefix and substring in history):
174 or (not as_prefix and substring in history):
169 replace = True
175 replace = True
170 break
176 break
171
177
172 if replace:
178 if replace:
173 self._store_edits()
179 self._store_edits()
174 self._history_index = index
180 self._history_index = index
175 self.input_buffer = history
181 self.input_buffer = history
176
182
177 return replace
183 return replace
178
184
179 def history_next(self, substring='', as_prefix=True):
185 def history_next(self, substring='', as_prefix=True):
180 """ If possible, set the input buffer to a subsequent history item.
186 """ If possible, set the input buffer to a subsequent history item.
181
187
182 Parameters:
188 Parameters:
183 -----------
189 -----------
184 substring : str, optional
190 substring : str, optional
185 If specified, search for an item with this substring.
191 If specified, search for an item with this substring.
186 as_prefix : bool, optional
192 as_prefix : bool, optional
187 If True, the substring must match at the beginning (default).
193 If True, the substring must match at the beginning (default).
188
194
189 Returns:
195 Returns:
190 --------
196 --------
191 Whether the input buffer was changed.
197 Whether the input buffer was changed.
192 """
198 """
193 index = self._history_index
199 index = self._history_index
194 replace = False
200 replace = False
195 while index < len(self._history):
201 while index < len(self._history):
196 index += 1
202 index += 1
197 history = self._get_edited_history(index)
203 history = self._get_edited_history(index)
198 if (as_prefix and history.startswith(substring)) \
204 if (as_prefix and history.startswith(substring)) \
199 or (not as_prefix and substring in history):
205 or (not as_prefix and substring in history):
200 replace = True
206 replace = True
201 break
207 break
202
208
203 if replace:
209 if replace:
204 self._store_edits()
210 self._store_edits()
205 self._history_index = index
211 self._history_index = index
206 self.input_buffer = history
212 self.input_buffer = history
207
213
208 return replace
214 return replace
209
215
210 def history_tail(self, n=10):
216 def history_tail(self, n=10):
211 """ Get the local history list.
217 """ Get the local history list.
212
218
213 Parameters:
219 Parameters:
214 -----------
220 -----------
215 n : int
221 n : int
216 The (maximum) number of history items to get.
222 The (maximum) number of history items to get.
217 """
223 """
218 return self._history[-n:]
224 return self._history[-n:]
219
225
220 def _request_update_session_history_length(self):
226 def _request_update_session_history_length(self):
221 msg_id = self.kernel_manager.shell_channel.execute('',
227 msg_id = self.kernel_manager.shell_channel.execute('',
222 silent=True,
228 silent=True,
223 user_expressions={
229 user_expressions={
224 'hlen':'len(get_ipython().history_manager.input_hist_raw)',
230 'hlen':'len(get_ipython().history_manager.input_hist_raw)',
225 }
231 }
226 )
232 )
227 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'save_magic')
233 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'save_magic')
228
234
229 def _handle_execute_reply(self, msg):
235 def _handle_execute_reply(self, msg):
230 """ Handles replies for code execution, here only session history length
236 """ Handles replies for code execution, here only session history length
231 """
237 """
232 msg_id = msg['parent_header']['msg_id']
238 msg_id = msg['parent_header']['msg_id']
233 info = self._request_info['execute'].pop(msg_id,None)
239 info = self._request_info['execute'].pop(msg_id,None)
234 if info and info.kind == 'save_magic' and not self._hidden:
240 if info and info.kind == 'save_magic' and not self._hidden:
235 content = msg['content']
241 content = msg['content']
236 status = content['status']
242 status = content['status']
237 if status == 'ok':
243 if status == 'ok':
238 self._max_session_history=(int(content['user_expressions']['hlen']))
244 self._max_session_history=(int(content['user_expressions']['hlen']))
239
245
240 def save_magic(self):
246 def save_magic(self):
241 # update the session history length
247 # update the session history length
242 self._request_update_session_history_length()
248 self._request_update_session_history_length()
243
249
244 file_name,extFilter = QtGui.QFileDialog.getSaveFileName(self,
250 file_name,extFilter = QtGui.QFileDialog.getSaveFileName(self,
245 "Enter A filename",
251 "Enter A filename",
246 filter='Python File (*.py);; All files (*.*)'
252 filter='Python File (*.py);; All files (*.*)'
247 )
253 )
248
254
249 # let's the user search/type for a file name, while the history length
255 # let's the user search/type for a file name, while the history length
250 # is fetched
256 # is fetched
251
257
252 if file_name:
258 if file_name:
253 hist_range, ok = QtGui.QInputDialog.getText(self,
259 hist_range, ok = QtGui.QInputDialog.getText(self,
254 'Please enter an interval of command to save',
260 'Please enter an interval of command to save',
255 'Saving commands:',
261 'Saving commands:',
256 text=str('1-'+str(self._max_session_history))
262 text=str('1-'+str(self._max_session_history))
257 )
263 )
258 if ok:
264 if ok:
259 self.execute("%save"+" "+file_name+" "+str(hist_range))
265 self.execute("%save"+" "+file_name+" "+str(hist_range))
260
266
261 #---------------------------------------------------------------------------
267 #---------------------------------------------------------------------------
262 # 'HistoryConsoleWidget' protected interface
268 # 'HistoryConsoleWidget' protected interface
263 #---------------------------------------------------------------------------
269 #---------------------------------------------------------------------------
264
270
265 def _history_locked(self):
271 def _history_locked(self):
266 """ Returns whether history movement is locked.
272 """ Returns whether history movement is locked.
267 """
273 """
268 return (self.history_lock and
274 return (self.history_lock and
269 (self._get_edited_history(self._history_index) !=
275 (self._get_edited_history(self._history_index) !=
270 self.input_buffer) and
276 self.input_buffer) and
271 (self._get_prompt_cursor().blockNumber() !=
277 (self._get_prompt_cursor().blockNumber() !=
272 self._get_end_cursor().blockNumber()))
278 self._get_end_cursor().blockNumber()))
273
279
274 def _get_edited_history(self, index):
280 def _get_edited_history(self, index):
275 """ Retrieves a history item, possibly with temporary edits.
281 """ Retrieves a history item, possibly with temporary edits.
276 """
282 """
277 if index in self._history_edits:
283 if index in self._history_edits:
278 return self._history_edits[index]
284 return self._history_edits[index]
279 elif index == len(self._history):
285 elif index == len(self._history):
280 return unicode()
286 return unicode()
281 return self._history[index]
287 return self._history[index]
282
288
283 def _set_history(self, history):
289 def _set_history(self, history):
284 """ Replace the current history with a sequence of history items.
290 """ Replace the current history with a sequence of history items.
285 """
291 """
286 self._history = list(history)
292 self._history = list(history)
287 self._history_edits = {}
293 self._history_edits = {}
288 self._history_index = len(self._history)
294 self._history_index = len(self._history)
289
295
290 def _store_edits(self):
296 def _store_edits(self):
291 """ If there are edits to the current input buffer, store them.
297 """ If there are edits to the current input buffer, store them.
292 """
298 """
293 current = self.input_buffer
299 current = self.input_buffer
294 if self._history_index == len(self._history) or \
300 if self._history_index == len(self._history) or \
295 self._history[self._history_index] != current:
301 self._history[self._history_index] != current:
296 self._history_edits[self._history_index] = current
302 self._history_edits[self._history_index] = current
General Comments 0
You need to be logged in to leave comments. Login now