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