mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Console now has history
This commit is contained in:
parent
9b44f55785
commit
d9e5e74695
@ -24,6 +24,8 @@ def console_config():
|
|||||||
c = Config('console', desc)
|
c = Config('console', desc)
|
||||||
|
|
||||||
c.add_opt('theme', default='native', help='The color theme')
|
c.add_opt('theme', default='native', help='The color theme')
|
||||||
|
c.add_opt('scrollback', default=10000,
|
||||||
|
help='Max number of lines to keep in the scrollback buffer')
|
||||||
|
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import sys, textwrap, traceback, StringIO
|
import sys, textwrap, traceback, StringIO
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from codeop import CommandCompiler
|
||||||
|
|
||||||
from PyQt4.Qt import QTextEdit, Qt, QTextFrameFormat, pyqtSignal, \
|
from PyQt4.Qt import QTextEdit, Qt, QTextFrameFormat, pyqtSignal, \
|
||||||
QApplication, QColor, QPalette, QMenu, QActionGroup, QTimer
|
QApplication, QColor, QPalette, QMenu, QActionGroup, QTimer
|
||||||
@ -16,8 +17,9 @@ from pygments.styles import get_all_styles
|
|||||||
|
|
||||||
from calibre.utils.pyconsole.formatter import Formatter
|
from calibre.utils.pyconsole.formatter import Formatter
|
||||||
from calibre.utils.pyconsole.controller import Controller
|
from calibre.utils.pyconsole.controller import Controller
|
||||||
|
from calibre.utils.pyconsole.history import History
|
||||||
from calibre.utils.pyconsole import prints, prefs, __appname__, \
|
from calibre.utils.pyconsole import prints, prefs, __appname__, \
|
||||||
__version__, error_dialog
|
__version__, error_dialog, dynamic
|
||||||
|
|
||||||
class EditBlock(object): # {{{
|
class EditBlock(object): # {{{
|
||||||
|
|
||||||
@ -73,6 +75,7 @@ class ThemeMenu(QMenu): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class Console(QTextEdit):
|
class Console(QTextEdit):
|
||||||
|
|
||||||
running = pyqtSignal()
|
running = pyqtSignal()
|
||||||
@ -114,7 +117,9 @@ class Console(QTextEdit):
|
|||||||
parent=None):
|
parent=None):
|
||||||
QTextEdit.__init__(self, parent)
|
QTextEdit.__init__(self, parent)
|
||||||
self.shutting_down = False
|
self.shutting_down = False
|
||||||
|
self.compiler = CommandCompiler()
|
||||||
self.buf = self.old_buf = []
|
self.buf = self.old_buf = []
|
||||||
|
self.history = History([''], dynamic.get('console_history', []))
|
||||||
self.prompt_frame = None
|
self.prompt_frame = None
|
||||||
self.allow_output = False
|
self.allow_output = False
|
||||||
self.prompt_frame_format = QTextFrameFormat()
|
self.prompt_frame_format = QTextFrameFormat()
|
||||||
@ -122,7 +127,7 @@ class Console(QTextEdit):
|
|||||||
self.prompt_frame_format.setBorderStyle(QTextFrameFormat.BorderStyle_Solid)
|
self.prompt_frame_format.setBorderStyle(QTextFrameFormat.BorderStyle_Solid)
|
||||||
self.prompt_len = len(prompt)
|
self.prompt_len = len(prompt)
|
||||||
|
|
||||||
self.doc.setMaximumBlockCount(10000)
|
self.doc.setMaximumBlockCount(int(prefs['scrollback']))
|
||||||
self.lexer = PythonLexer(ensurenl=False)
|
self.lexer = PythonLexer(ensurenl=False)
|
||||||
self.tb_lexer = PythonTracebackLexer()
|
self.tb_lexer = PythonTracebackLexer()
|
||||||
|
|
||||||
@ -139,6 +144,8 @@ class Console(QTextEdit):
|
|||||||
self.key_dispatcher = { # {{{
|
self.key_dispatcher = { # {{{
|
||||||
Qt.Key_Enter : self.enter_pressed,
|
Qt.Key_Enter : self.enter_pressed,
|
||||||
Qt.Key_Return : self.enter_pressed,
|
Qt.Key_Return : self.enter_pressed,
|
||||||
|
Qt.Key_Up : self.up_pressed,
|
||||||
|
Qt.Key_Down : self.down_pressed,
|
||||||
Qt.Key_Home : self.home_pressed,
|
Qt.Key_Home : self.home_pressed,
|
||||||
Qt.Key_End : self.end_pressed,
|
Qt.Key_End : self.end_pressed,
|
||||||
Qt.Key_Left : self.left_pressed,
|
Qt.Key_Left : self.left_pressed,
|
||||||
@ -153,15 +160,17 @@ class Console(QTextEdit):
|
|||||||
'''.format(sys.version.splitlines()[0], __appname__,
|
'''.format(sys.version.splitlines()[0], __appname__,
|
||||||
__version__))
|
__version__))
|
||||||
|
|
||||||
|
sys.excepthook = self.unhandled_exception
|
||||||
|
|
||||||
self.controllers = []
|
self.controllers = []
|
||||||
QTimer.singleShot(0, self.launch_controller)
|
QTimer.singleShot(0, self.launch_controller)
|
||||||
|
|
||||||
sys.excepthook = self.unhandled_exception
|
|
||||||
|
|
||||||
with EditBlock(self.cursor):
|
with EditBlock(self.cursor):
|
||||||
self.render_block(motd)
|
self.render_block(motd)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
dynamic.set('console_history', self.history.serialize())
|
||||||
self.shutton_down = True
|
self.shutton_down = True
|
||||||
for c in self.controllers:
|
for c in self.controllers:
|
||||||
c.kill()
|
c.kill()
|
||||||
@ -365,7 +374,7 @@ class Console(QTextEdit):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Keyboard handling {{{
|
# Keyboard management {{{
|
||||||
|
|
||||||
def keyPressEvent(self, ev):
|
def keyPressEvent(self, ev):
|
||||||
text = unicode(ev.text())
|
text = unicode(ev.text())
|
||||||
@ -394,6 +403,20 @@ class Console(QTextEdit):
|
|||||||
self.setTextCursor(c)
|
self.setTextCursor(c)
|
||||||
self.ensureCursorVisible()
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
|
def up_pressed(self):
|
||||||
|
lineno, pos = self.cursor_pos
|
||||||
|
if lineno < 0: return
|
||||||
|
if lineno == 0:
|
||||||
|
b = self.history.back()
|
||||||
|
if b is not None:
|
||||||
|
self.set_prompt(b)
|
||||||
|
else:
|
||||||
|
c = self.cursor
|
||||||
|
c.movePosition(c.Up)
|
||||||
|
self.setTextCursor(c)
|
||||||
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
|
|
||||||
def backspace_pressed(self):
|
def backspace_pressed(self):
|
||||||
lineno, pos = self.cursor_pos
|
lineno, pos = self.cursor_pos
|
||||||
if lineno < 0: return
|
if lineno < 0: return
|
||||||
@ -414,7 +437,6 @@ class Console(QTextEdit):
|
|||||||
lineno, pos = self.cursor_pos
|
lineno, pos = self.cursor_pos
|
||||||
if lineno < 0: return
|
if lineno < 0: return
|
||||||
c = self.cursor
|
c = self.cursor
|
||||||
lineno, pos = self.cursor_pos
|
|
||||||
cp = list(self.prompt(False))
|
cp = list(self.prompt(False))
|
||||||
if pos < len(cp[lineno]):
|
if pos < len(cp[lineno]):
|
||||||
c.movePosition(c.NextCharacter)
|
c.movePosition(c.NextCharacter)
|
||||||
@ -423,6 +445,22 @@ class Console(QTextEdit):
|
|||||||
self.setTextCursor(c)
|
self.setTextCursor(c)
|
||||||
self.ensureCursorVisible()
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
|
def down_pressed(self):
|
||||||
|
lineno, pos = self.cursor_pos
|
||||||
|
if lineno < 0: return
|
||||||
|
c = self.cursor
|
||||||
|
cp = list(self.prompt(False))
|
||||||
|
if lineno >= len(cp) - 1:
|
||||||
|
b = self.history.forward()
|
||||||
|
if b is not None:
|
||||||
|
self.set_prompt(b)
|
||||||
|
else:
|
||||||
|
c = self.cursor
|
||||||
|
c.movePosition(c.Down)
|
||||||
|
self.setTextCursor(c)
|
||||||
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
|
|
||||||
def home_pressed(self):
|
def home_pressed(self):
|
||||||
if self.prompt_frame is not None:
|
if self.prompt_frame is not None:
|
||||||
mods = QApplication.keyboardModifiers()
|
mods = QApplication.keyboardModifiers()
|
||||||
@ -454,6 +492,19 @@ class Console(QTextEdit):
|
|||||||
return self.no_controller_error()
|
return self.no_controller_error()
|
||||||
cp = list(self.prompt())
|
cp = list(self.prompt())
|
||||||
if cp[0]:
|
if cp[0]:
|
||||||
|
try:
|
||||||
|
ret = self.compiler('\n'.join(cp))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if ret is None:
|
||||||
|
c = self.prompt_frame.lastCursorPosition()
|
||||||
|
c.insertBlock()
|
||||||
|
self.setTextCursor(c)
|
||||||
|
self.render_current_prompt()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.history.enter(cp)
|
||||||
self.execute(cp)
|
self.execute(cp)
|
||||||
|
|
||||||
def text_typed(self, text):
|
def text_typed(self, text):
|
||||||
@ -461,6 +512,7 @@ class Console(QTextEdit):
|
|||||||
self.move_cursor_to_prompt()
|
self.move_cursor_to_prompt()
|
||||||
self.cursor.insertText(text)
|
self.cursor.insertText(text)
|
||||||
self.render_current_prompt(restore_cursor=True)
|
self.render_current_prompt(restore_cursor=True)
|
||||||
|
self.history.current = list(self.prompt())
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -104,7 +104,6 @@ class Controller(QThread):
|
|||||||
def returncode(self):
|
def returncode(self):
|
||||||
return self.process.returncode
|
return self.process.returncode
|
||||||
|
|
||||||
@property
|
|
||||||
def interrupt(self):
|
def interrupt(self):
|
||||||
if hasattr(signal, 'SIGINT'):
|
if hasattr(signal, 'SIGINT'):
|
||||||
os.kill(self.process.pid, signal.SIGINT)
|
os.kill(self.process.pid, signal.SIGINT)
|
||||||
|
@ -11,6 +11,7 @@ from Queue import Queue, Empty
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
from multiprocessing.connection import Client
|
from multiprocessing.connection import Client
|
||||||
|
from repr import repr as safe_repr
|
||||||
|
|
||||||
from calibre.utils.pyconsole import preferred_encoding, isbytestring, \
|
from calibre.utils.pyconsole import preferred_encoding, isbytestring, \
|
||||||
POLL_TIMEOUT
|
POLL_TIMEOUT
|
||||||
@ -35,7 +36,7 @@ def tounicode(raw): # {{{
|
|||||||
try:
|
try:
|
||||||
raw = raw.decode(preferred_encoding, 'replace')
|
raw = raw.decode(preferred_encoding, 'replace')
|
||||||
except:
|
except:
|
||||||
raw = repr(raw)
|
raw = safe_repr(raw)
|
||||||
|
|
||||||
if isbytestring(raw):
|
if isbytestring(raw):
|
||||||
try:
|
try:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user