~ubuntu-branches/ubuntu/trusty/spyder/trusty-backports

« back to all changes in this revision

Viewing changes to spyderlib/widgets/shell.py

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2013-05-08 21:12:32 UTC
  • mfrom: (1.2.1) (18.1.5 experimental)
  • Revision ID: package-import@ubuntu.com-20130508211232-r867h9xoenknvuxf
Tags: 2.2.0+dfsg-1
* Imported Upstream version 2.2.0+dfsg
* Depends on ipython-qtconsole (< 1.0)
* Removed the obsolte DM-Upload-Allowed

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
import time
16
16
import os.path as osp
17
17
import re
 
18
import sys
18
19
 
19
 
from spyderlib.qt.QtGui import (QMenu, QApplication, QCursor, QToolTip,
20
 
                                QKeySequence, QMessageBox, QMouseEvent,
21
 
                                QTextCursor, QTextCharFormat, QShortcut)
22
 
from spyderlib.qt.QtCore import Qt, QCoreApplication, QTimer, SIGNAL, Property
 
20
from spyderlib.qt.QtGui import (QMenu, QApplication, QToolTip, QKeySequence,
 
21
                                QMessageBox, QTextCursor, QTextCharFormat,
 
22
                                QShortcut)
 
23
from spyderlib.qt.QtCore import (Qt, QCoreApplication, QTimer, SIGNAL,
 
24
                                 Property)
23
25
from spyderlib.qt.compat import getsavefilename
24
26
 
25
27
# Local import
26
 
from spyderlib.baseconfig import get_conf_path, _, STDERR
27
 
from spyderlib.config import CONF, get_icon, get_font
 
28
from spyderlib.baseconfig import get_conf_path, _, STDERR, DEBUG
 
29
from spyderlib.guiconfig import CONF, get_font
28
30
from spyderlib.utils import encoding
29
 
from spyderlib.utils.misc import get_error_match
30
 
from spyderlib.utils.dochelpers import getobj
31
31
from spyderlib.utils.qthelpers import (keybinding, create_action, add_actions,
32
 
                                       restore_keyevent)
 
32
                                       restore_keyevent, get_icon)
33
33
from spyderlib.widgets.sourcecode.base import ConsoleBaseWidget
34
 
 
35
 
 
36
 
HISTORY_FILENAMES = []
37
 
 
38
 
 
39
 
class ShellBaseWidget(ConsoleBaseWidget):
 
34
from spyderlib.widgets.mixins import (InspectObjectMixin, TracebackLinksMixin,
 
35
                                      SaveHistoryMixin)
 
36
 
 
37
 
 
38
class ShellBaseWidget(ConsoleBaseWidget, SaveHistoryMixin):
40
39
    """
41
40
    Shell base widget
42
41
    """
43
 
    INITHISTORY = None
44
 
    SEPARATOR = None
45
42
    
46
 
    def __init__(self, parent, history_filename, debug=False, profile=False):
 
43
    def __init__(self, parent, history_filename, profile=False):
47
44
        """
48
45
        parent : specifies the parent widget
49
46
        """
50
47
        ConsoleBaseWidget.__init__(self, parent)
 
48
        SaveHistoryMixin.__init__(self)
51
49
                
52
50
        # Prompt position: tuple (line, index)
53
51
        self.current_prompt_pos = None
68
66
        self.menu = None
69
67
        self.setup_context_menu()
70
68
 
71
 
        # Debug mode
72
 
        self.debug = debug
73
 
 
74
69
        # Simple profiling test
75
70
        self.profile = profile
76
71
        
156
151
        
157
152
        
158
153
    #------ Input buffer
159
 
    def get_current_line_to_cursor(self):
160
 
        return self.get_text(self.current_prompt_pos, 'cursor')
161
 
    
162
154
    def get_current_line_from_cursor(self):
163
155
        return self.get_text('cursor', 'eof')
164
156
    
208
200
        """
209
201
        Print a new prompt and save its (line, index) position
210
202
        """
 
203
        if self.get_cursor_line_column()[1] != 0:
 
204
            self.write('\n')
211
205
        self.write(prompt, prompt=True)
212
206
        # now we update our cursor giving end of prompt
213
207
        self.current_prompt_pos = self.get_position('cursor')
230
224
        """Copy text to clipboard... or keyboard interrupt"""
231
225
        if self.has_selected_text():
232
226
            ConsoleBaseWidget.copy(self)
233
 
        else:
234
 
            self.emit(SIGNAL("keyboard_interrupt()"))
235
 
            
 
227
        elif not sys.platform == 'darwin':
 
228
            self.interrupt()
 
229
 
 
230
    def interrupt(self):
 
231
        """Keyboard interrupt"""
 
232
        self.emit(SIGNAL("keyboard_interrupt()"))
 
233
 
236
234
    def cut(self):
237
235
        """Cut text"""
238
236
        self.check_selection()
306
304
        # (otherwise, right below, we would remove selection
307
305
        #  if not on current line)
308
306
        ctrl = event.modifiers() & Qt.ControlModifier
309
 
        if event.key() == Qt.Key_C and ctrl:
310
 
            self.copy()
 
307
        meta = event.modifiers() & Qt.MetaModifier    # meta=ctrl in OSX
 
308
        if event.key() == Qt.Key_C and \
 
309
          ((Qt.MetaModifier | Qt.ControlModifier) & event.modifiers()):
 
310
            if meta and sys.platform == 'darwin':
 
311
                self.interrupt()
 
312
            elif ctrl:
 
313
                self.copy()
311
314
            event.accept()
312
315
            return True
313
316
        
373
376
            method('word' if ctrl else 'character', direction='right')
374
377
 
375
378
        elif (key == Qt.Key_Home) or ((key == Qt.Key_Up) and ctrl):
376
 
            self._key_home(shift)
 
379
            self._key_home(shift, ctrl)
377
380
 
378
381
        elif (key == Qt.Key_End) or ((key == Qt.Key_Down) and ctrl):
379
 
            self._key_end(shift)
 
382
            self._key_end(shift, ctrl)
380
383
 
381
384
        elif key == Qt.Key_Up:
382
385
            if not self.is_cursor_on_last_line():
404
407
            # the event queue - i.e. if the busy buffer is ever implemented)
405
408
            ConsoleBaseWidget.keyPressEvent(self, event)
406
409
 
407
 
        elif key == Qt.Key_Escape and ctrl and shift:
408
 
            self._key_ctrl_shift_escape()    
409
 
 
410
410
        elif key == Qt.Key_Escape and shift:
411
 
            self._key_shift_escape()
 
411
            self.clear_line()
412
412
 
413
413
        elif key == Qt.Key_Escape:
414
414
            self._key_escape()
415
415
                
 
416
        elif key == Qt.Key_L and ctrl:
 
417
            self.clear_terminal()
 
418
            
416
419
        elif key == Qt.Key_V and ctrl:
417
420
            self.paste()
418
421
            
461
464
        raise NotImplementedError
462
465
    def _key_ctrl_space(self):
463
466
        raise NotImplementedError
464
 
    def _key_home(self, shift):
 
467
    def _key_home(self, shift, ctrl):
465
468
        raise NotImplementedError
466
 
    def _key_end(self, shift):
 
469
    def _key_end(self, shift, ctrl):
467
470
        raise NotImplementedError
468
471
    def _key_pageup(self):
469
472
        raise NotImplementedError
471
474
        raise NotImplementedError
472
475
    def _key_escape(self):
473
476
        raise NotImplementedError
474
 
    def _key_shift_escape(self):
475
 
        raise NotImplementedError
476
 
    def _key_ctrl_shift_escape(self):
477
 
        raise NotImplementedError
478
477
    def _key_question(self, text):
479
478
        raise NotImplementedError
480
479
    def _key_parenleft(self, text):
506
505
        encoding.writelines(rawhistory, self.history_filename)
507
506
        return history
508
507
        
509
 
    def add_to_history(self, command):
510
 
        """Add command to history"""
511
 
        command = unicode(command)
512
 
        if command in ['', '\n'] or command.startswith('Traceback'):
513
 
            return
514
 
        if command.endswith('\n'):
515
 
            command = command[:-1]
516
 
        self.histidx = None
517
 
        if len(self.history)>0 and self.history[-1] == command:
518
 
            return
519
 
        self.history.append(command)
520
 
        text = os.linesep + command
521
 
        
522
 
        # When the first entry will be written in history file,
523
 
        # the separator will be append first:
524
 
        if self.history_filename not in HISTORY_FILENAMES:
525
 
            HISTORY_FILENAMES.append(self.history_filename)
526
 
            text = self.SEPARATOR + text
527
 
            
528
 
        encoding.write(text, self.history_filename, mode='ab')
529
 
        self.emit(SIGNAL('append_to_history(QString,QString)'),
530
 
                  self.history_filename, text)
531
 
        
532
508
    def browse_history(self, backward):
533
509
        """Browse history"""
534
510
        if self.is_cursor_before('eol') and self.hist_wholeline:
578
554
        """Simulate stderr"""
579
555
        self.flush()
580
556
        self.write(text, flush=True, error=True)
581
 
        if self.debug:
 
557
        if DEBUG:
582
558
            STDERR.write(text)
583
559
 
584
560
    def write(self, text, flush=False, error=False, prompt=False):
631
607
        if next:
632
608
            return False
633
609
        return ConsoleBaseWidget.focusNextPrevChild(self, next)
634
 
        
635
 
    def mousePressEvent(self, event):
636
 
        """
637
 
        Re-implemented to handle the mouse press event.
638
 
        event: the mouse press event (QMouseEvent)
639
 
        """
640
 
        if event.button() == Qt.MidButton:
641
 
            text = self.get_selected_text()
642
 
            # Simulating left mouse button:
643
 
            event = QMouseEvent(QMouseEvent.MouseButtonPress, event.pos(),
644
 
                                Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
645
 
            ConsoleBaseWidget.mousePressEvent(self, event)
646
 
            if self.new_input_line:
647
 
                self.on_new_line()
648
 
            self.insert_text(text)
649
 
            event.accept()
650
 
        else:
651
 
            ConsoleBaseWidget.mousePressEvent(self, event)
652
610
 
653
611
    
654
612
    #------ Drag and drop
682
640
        raise NotImplementedError
683
641
 
684
642
 
685
 
class PythonShellWidget(ShellBaseWidget):
686
 
    """
687
 
    Python shell widget
688
 
    """
 
643
class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget,
 
644
                        InspectObjectMixin):
 
645
    """Python shell widget"""
 
646
    QT_CLASS = ShellBaseWidget
 
647
 
689
648
    INITHISTORY = ['# -*- coding: utf-8 -*-',
690
649
                   '# *** Spyder Python Console History Log ***',]
691
650
    SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime())
692
651
    
693
 
    def __init__(self, parent, history_filename, debug=False, profile=False):
694
 
        ShellBaseWidget.__init__(self, parent, history_filename, debug, profile)
695
 
        
696
 
        self.inspector = None
697
 
        self.inspector_enabled = True
698
 
        
699
 
        # Mouse cursor
700
 
        self.__cursor_changed = False
 
652
    def __init__(self, parent, history_filename, profile=False):
 
653
        ShellBaseWidget.__init__(self, parent, history_filename, profile)
 
654
        TracebackLinksMixin.__init__(self)
 
655
        InspectObjectMixin.__init__(self)
701
656
 
702
657
        # Local shortcuts
703
658
        self.inspectsc = QShortcut(QKeySequence("Ctrl+I"), self,
755
710
        QApplication.clipboard().setText(text)
756
711
    
757
712
    
758
 
    #------Mouse events
759
 
    def mouseReleaseEvent(self, event):
760
 
        """Go to error"""
761
 
        ConsoleBaseWidget.mouseReleaseEvent(self, event)            
762
 
        text = self.get_line_at(event.pos())
763
 
        if get_error_match(text) and not self.has_selected_text():
764
 
            self.emit(SIGNAL("go_to_error(QString)"), text)
765
 
 
766
 
    def mouseMoveEvent(self, event):
767
 
        """Show Pointing Hand Cursor on error messages"""
768
 
        text = self.get_line_at(event.pos())
769
 
        if get_error_match(text):
770
 
            if not self.__cursor_changed:
771
 
                QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor))
772
 
                self.__cursor_changed = True
773
 
            event.accept()
774
 
            return
775
 
        if self.__cursor_changed:
776
 
            QApplication.restoreOverrideCursor()
777
 
            self.__cursor_changed = False
778
 
        ConsoleBaseWidget.mouseMoveEvent(self, event)
779
 
        
780
 
    def leaveEvent(self, event):
781
 
        """If cursor has not been restored yet, do it now"""
782
 
        if self.__cursor_changed:
783
 
            QApplication.restoreOverrideCursor()
784
 
            self.__cursor_changed = False
785
 
        ConsoleBaseWidget.leaveEvent(self, event)
786
 
 
787
 
                
788
713
    #------ Key handlers
789
714
    def postprocess_keyevent(self, event):
790
715
        """Process keypress event"""
827
752
        if not self.is_completion_widget_visible():
828
753
            self.show_code_completion(automatic=False)
829
754
                
830
 
    def _key_home(self, shift):
 
755
    def _key_home(self, shift, ctrl):
831
756
        """Action for Home key"""
832
757
        if self.is_cursor_on_last_line():
833
 
            self.stdkey_home(shift, self.current_prompt_pos)
 
758
            self.stdkey_home(shift, ctrl, self.current_prompt_pos)
834
759
                
835
 
    def _key_end(self, shift):
 
760
    def _key_end(self, shift, ctrl):
836
761
        """Action for End key"""
837
762
        if self.is_cursor_on_last_line():
838
 
            self.stdkey_end(shift)
 
763
            self.stdkey_end(shift, ctrl)
839
764
                
840
765
    def _key_pageup(self):
841
766
        """Action for PageUp key"""
849
774
        """Action for ESCAPE key"""
850
775
        if self.is_completion_widget_visible():
851
776
            self.hide_completion_widget()
852
 
    
853
 
    def _key_shift_escape(self):
854
 
        self.clear_line()
855
 
 
856
 
    def _key_ctrl_shift_escape(self):
857
 
        self.clear_terminal()
858
777
 
859
778
    def _key_question(self, text):
860
779
        """Action for '?'"""
911
830
    def get_dir(self, objtxt):
912
831
        """Return dir(object)"""
913
832
        raise NotImplementedError
914
 
    def get_completion(self, objtxt):
915
 
        """Return completion list associated to object name"""
916
 
        pass
917
833
    def get_module_completion(self, objtxt):
918
834
        """Return module completion list associated to object name"""
919
835
        pass
970
886
                                      automatic=automatic)
971
887
            return
972
888
        
973
 
        #-- IPython only -------------------------------------------------------
974
 
        # Using IPython code completion feature: __IP.complete
975
 
        elif ' ' in text and not text.endswith(' '):
976
 
            try1 = text.split(' ')[-1]
977
 
            obj_list = self.get_completion(try1)
978
 
            if obj_list:
979
 
                self.show_completion_list(obj_list, completion_text=try1,
980
 
                                          automatic=automatic)
981
 
                return
982
 
        elif text.startswith('%'):
983
 
            # IPython magic commands
984
 
            obj_list = self.get_completion(text)
985
 
            if obj_list:
986
 
                self.show_completion_list(obj_list, completion_text=text,
987
 
                                          automatic=automatic)
988
 
            # There is no point continuing the process when text starts with '%'
989
 
            return
990
 
        obj_list = self.get_completion(last_obj)
991
 
        if not text.endswith('.') and last_obj and obj_list:
992
 
            self.show_completion_list(obj_list, completion_text=last_obj,
993
 
                                      automatic=automatic)
994
 
            return
995
 
        #-----------------------------------------------------------------------
996
 
        
997
889
        obj_dir = self.get_dir(last_obj)
998
890
        if last_obj and obj_dir and text.endswith('.'):
999
891
            self.show_completion_list(obj_dir, automatic=automatic)
1036
928
                                      completion_text=text[q_pos+1:],
1037
929
                                      automatic=automatic)
1038
930
            return
1039
 
    
1040
 
    def show_docstring(self, text, call=False, force=False):
1041
 
        """Show docstring or arguments"""
1042
 
        text = unicode(text) # Useful only for ExternalShellBase
1043
 
        
1044
 
        insp_enabled = self.inspector_enabled or force
1045
 
        if force and self.inspector is not None:
1046
 
            self.inspector.dockwidget.setVisible(True)
1047
 
            self.inspector.dockwidget.raise_()
1048
 
        if insp_enabled and (self.inspector is not None) and \
1049
 
           (self.inspector.dockwidget.isVisible()):
1050
 
            # ObjectInspector widget exists and is visible
1051
 
            self.inspector.set_shell(self)
1052
 
            self.inspector.set_object_text(text, ignore_unknown=True)
1053
 
            self.setFocus() # if inspector was not at top level, raising it to
1054
 
                            # top will automatically give it focus because of
1055
 
                            # the visibility_changed signal, so we must give
1056
 
                            # focus back to shell
1057
 
            if call and self.calltips:
1058
 
                # Display argument list if this is function call
1059
 
                iscallable = self.iscallable(text)
1060
 
                if iscallable is not None:
1061
 
                    if iscallable:
1062
 
                        arglist = self.get_arglist(text)
1063
 
                        if isinstance(arglist, bool):
1064
 
                            arglist = []
1065
 
                        if arglist:
1066
 
                            self.show_calltip(_("Arguments"),
1067
 
                                              arglist, '#129625')
1068
 
        elif self.calltips: # inspector is not visible or link is disabled
1069
 
            doc = self.get__doc__(text)
1070
 
            if doc is not None:
1071
 
                self.show_calltip(_("Documentation"), doc)
1072
 
        
1073
 
        
1074
 
    #------ Miscellanous
1075
 
    def get_last_obj(self, last=False):
1076
 
        """
1077
 
        Return the last valid object on the current line
1078
 
        """
1079
 
        return getobj(self.get_current_line_to_cursor(), last=last)
1080
 
        
1081
 
    def set_inspector(self, inspector):
1082
 
        """Set ObjectInspector DockWidget reference"""
1083
 
        self.inspector = inspector
1084
 
        self.inspector.set_shell(self)
1085
 
 
1086
 
    def set_inspector_enabled(self, state):
1087
 
        self.inspector_enabled = state
1088
 
        
1089
 
    def inspect_current_object(self):
1090
 
        text = ''
1091
 
        text1 = self.get_text('sol', 'cursor')
1092
 
        tl1 = re.findall(r'([a-zA-Z_]+[0-9a-zA-Z_\.]*)', text1)
1093
 
        if tl1 and text1.endswith(tl1[-1]):
1094
 
            text += tl1[-1]
1095
 
        text2 = self.get_text('cursor', 'eol')
1096
 
        tl2 = re.findall(r'([0-9a-zA-Z_\.]+[0-9a-zA-Z_\.]*)', text2)
1097
 
        if tl2 and text2.startswith(tl2[0]):
1098
 
            text += tl2[0]
1099
 
        if text:
1100
 
            self.show_docstring(text, force=True)
1101
931
            
1102
932
    #------ Drag'n Drop
1103
933
    def drop_pathlist(self, pathlist):
1122
952
    INITHISTORY = ['%s *** Spyder Terminal History Log ***' % COM, COM,]
1123
953
    SEPARATOR = '%s%s ---(%s)---' % (os.linesep*2, COM, time.ctime())
1124
954
    
1125
 
    def __init__(self, parent, history_filename, debug=False, profile=False):
1126
 
        ShellBaseWidget.__init__(self, parent, history_filename, debug, profile)
 
955
    def __init__(self, parent, history_filename, profile=False):
 
956
        ShellBaseWidget.__init__(self, parent, history_filename, profile)
1127
957
        
1128
958
    #------ Key handlers
1129
959
    def _key_other(self, text):