You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
9.3 KiB
278 lines
9.3 KiB
6 years ago
|
# IDLEX EXTENSION
|
||
|
## """
|
||
|
## Copyright(C) 2011 The Board of Trustees of the University of Illinois.
|
||
|
## All rights reserved.
|
||
|
##
|
||
|
## Developed by: Roger D. Serwy
|
||
|
## University of Illinois
|
||
|
##
|
||
|
## Permission is hereby granted, free of charge, to any person obtaining
|
||
|
## a copy of this software and associated documentation files (the
|
||
|
## "Software"), to deal with the Software without restriction, including
|
||
|
## without limitation the rights to use, copy, modify, merge, publish,
|
||
|
## distribute, sublicense, and/or sell copies of the Software, and to
|
||
|
## permit persons to whom the Software is furnished to do so, subject to
|
||
|
## the following conditions:
|
||
|
##
|
||
|
## + Redistributions of source code must retain the above copyright
|
||
|
## notice, this list of conditions and the following disclaimers.
|
||
|
## + Redistributions in binary form must reproduce the above copyright
|
||
|
## notice, this list of conditions and the following disclaimers in the
|
||
|
## documentation and/or other materials provided with the distribution.
|
||
|
## + Neither the names of Roger D. Serwy, the University of Illinois, nor
|
||
|
## the names of its contributors may be used to endorse or promote
|
||
|
## products derived from this Software without specific prior written
|
||
|
## permission.
|
||
|
##
|
||
|
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||
|
## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||
|
## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||
|
## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||
|
## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
|
||
|
##
|
||
|
##
|
||
|
##
|
||
|
##
|
||
|
## Terminal Mode Extension - makes PyShell behave more like a terminal
|
||
|
##
|
||
|
## This extension makes PyShell behave more like a terminal.
|
||
|
## The cursor is confined to the input area. Up and down arrow keys
|
||
|
## the navigate command history.
|
||
|
##
|
||
|
## Selected text in the Shell Window will not be copied to the
|
||
|
## input area when pressing enter.
|
||
|
##
|
||
|
## Part of Issue2704
|
||
|
##
|
||
|
## """
|
||
|
|
||
|
config_extension_def = """
|
||
|
|
||
|
[Terminal]
|
||
|
enable=1
|
||
|
enable_shell=1
|
||
|
enable_editor=0
|
||
|
terminal=True
|
||
|
|
||
|
[Terminal_cfgBindings]
|
||
|
terminal-toggle=
|
||
|
|
||
|
"""
|
||
|
|
||
|
import sys
|
||
|
|
||
|
if sys.version < '3':
|
||
|
from Tkinter import INSERT, END, SEL_FIRST, SEL_LAST, SEL
|
||
|
else:
|
||
|
from tkinter import INSERT, END, SEL_FIRST, SEL_LAST, SEL
|
||
|
|
||
|
from idlelib.configHandler import idleConf
|
||
|
|
||
|
class Terminal:
|
||
|
|
||
|
menudefs = [
|
||
|
('options', [
|
||
|
('!Terminal Mode', '<<terminal-toggle>>'),
|
||
|
]),]
|
||
|
|
||
|
|
||
|
def __init__(self, editwin):
|
||
|
self.editwin = editwin
|
||
|
self.text = self.editwin.text
|
||
|
|
||
|
self.enabled = idleConf.GetOption("extensions", "Terminal",
|
||
|
"terminal", type="bool", default=True)
|
||
|
self.editwin.setvar("<<terminal-toggle>>", not not self.enabled)
|
||
|
|
||
|
# this extension is loaded from EditorWindow.py, but before
|
||
|
# PyShell.py makes its changes. Will use tk .after to make
|
||
|
# changes
|
||
|
|
||
|
self.last_insert = 'end-1c'
|
||
|
|
||
|
if self.enabled:
|
||
|
self.text.after(10, self.delay_init)
|
||
|
|
||
|
def close(self):
|
||
|
self.editwin = None
|
||
|
self.text = None
|
||
|
|
||
|
def delay_init(self):
|
||
|
if hasattr(self.editwin, 'history'):
|
||
|
self.terminal_mode_init()
|
||
|
return
|
||
|
self.text.after(100, self.delay_init)
|
||
|
|
||
|
def terminal_toggle_event(self, event=None):
|
||
|
self.enabled = not self.enabled
|
||
|
self.editwin.setvar("<<terminal-toggle>>", not not self.enabled)
|
||
|
|
||
|
if self.enabled == False:
|
||
|
self.terminal_mode_exit()
|
||
|
else:
|
||
|
self.terminal_mode_init()
|
||
|
|
||
|
idleConf.SetOption("extensions", "Terminal", "terminal", '%s' % self.enabled)
|
||
|
|
||
|
|
||
|
def terminal_mode_init(self, event=None):
|
||
|
# redefine some key bindings
|
||
|
self.enabled = True
|
||
|
|
||
|
text = self.editwin.text
|
||
|
|
||
|
# up/down keys
|
||
|
text.bind("<<term-key-up-callback>>", self.key_up_callback)
|
||
|
text.bind("<<term-key-down-callback>>", self.key_down_callback)
|
||
|
text.event_add("<<term-key-up-callback>>", "<Key-Up>")
|
||
|
text.event_add("<<term-key-down-callback>>", "<Key-Down>")
|
||
|
|
||
|
# page up/down keys
|
||
|
text.bind("<<term-key-pgup-callback>>", self.key_pgup_callback, '+')
|
||
|
text.bind("<<term-key-pgdown-callback>>", self.key_pgdown_callback, '+')
|
||
|
text.event_add("<<term-key-pgup-callback>>", "<Key-Prior>")
|
||
|
text.event_add("<<term-key-pgdown-callback>>", "<Key-Next>")
|
||
|
|
||
|
# home
|
||
|
text.bind("<<term-home-callback>>", self.home_callback, '+')
|
||
|
text.event_add("<<term-home-callback>>", "<Key-Home>")
|
||
|
|
||
|
# keep the cursor in the input area
|
||
|
text.bind("<<key-in-input>>", self.key_in_input)
|
||
|
text.event_add("<<key-in-input>>", "<Key-Left>")
|
||
|
text.bind("<<mouse-press>>", self.mouse_press)
|
||
|
text.event_add("<<mouse-press>>", "<ButtonPress>")
|
||
|
text.bind("<<mouse-release>>", self.mouse_release)
|
||
|
text.event_add("<<mouse-release>>", "<ButtonRelease>")
|
||
|
|
||
|
|
||
|
# make sure cursor is in input
|
||
|
self.key_in_input(event)
|
||
|
|
||
|
# override the enter-recall functionality
|
||
|
text.bind('<Return>', self.enter_callback)
|
||
|
|
||
|
def enter_callback(self, ev=None):
|
||
|
if self.enabled:
|
||
|
self.text.tag_remove('sel', '1.0', 'end')
|
||
|
return self.editwin.enter_callback(ev)
|
||
|
|
||
|
def terminal_mode_exit(self, event=None):
|
||
|
self.enabled = False
|
||
|
text = self.editwin.text
|
||
|
text.unbind("<<term-key-up-callback>>")
|
||
|
text.unbind("<<term-key-down-callback>>")
|
||
|
text.unbind("<<key-in-input>>")
|
||
|
text.unbind("<<mouse-in-input>>")
|
||
|
|
||
|
def home_callback(self, event):
|
||
|
text = self.text
|
||
|
if self.text.compare("insert", "<", "iomark"):
|
||
|
self.text.mark_set("insert", "iomark")
|
||
|
return "break"
|
||
|
if self.text.compare("iomark", "==", "insert"):
|
||
|
return "break"
|
||
|
return
|
||
|
|
||
|
def mouse_press(self, event=None):
|
||
|
text = self.text
|
||
|
self.last_insert = text.index('insert')
|
||
|
if text.compare('insert', '<=', 'iomark'):
|
||
|
self.last_insert = text.index('end-1c')
|
||
|
|
||
|
def mouse_release(self, event=None):
|
||
|
if self.enabled == False: return
|
||
|
# a mouse click on the window
|
||
|
text = self.text
|
||
|
if text.compare('insert', '<=', 'iomark'):
|
||
|
text.mark_set('insert', self.last_insert)
|
||
|
|
||
|
def key_in_input(self, event=None):
|
||
|
if self.enabled == False: return
|
||
|
# some key stroke
|
||
|
if self.text.compare('insert', '<=', 'iomark'):
|
||
|
self.text.mark_set('insert', 'iomark')
|
||
|
return "break"
|
||
|
|
||
|
def key_pgup_callback(self, event):
|
||
|
text = self.text
|
||
|
text.yview_scroll(-1, 'pages')
|
||
|
return "break"
|
||
|
|
||
|
def key_pgdown_callback(self, event):
|
||
|
text = self.text
|
||
|
text.yview_scroll(1, 'pages')
|
||
|
return "break"
|
||
|
|
||
|
def history_ismember(self):
|
||
|
""" if the current input is in the history, return true """
|
||
|
s = self.editwin.history._get_source('iomark', 'end-1c')
|
||
|
return (s in self.editwin.history.history)
|
||
|
|
||
|
def key_up_callback(self, event):
|
||
|
if self.enabled == False: return
|
||
|
if self.editwin.executing:
|
||
|
return "break"
|
||
|
|
||
|
s = self.text.get('iomark', 'end-1c')
|
||
|
|
||
|
if '\n' not in s:
|
||
|
# single line input
|
||
|
if self.text.compare('insert', '==', 'end-1c'):
|
||
|
self.editwin.history.history_prev(event)
|
||
|
return "break"
|
||
|
else:
|
||
|
self.text.mark_set('insert', 'end-1c')
|
||
|
return "break"
|
||
|
else:
|
||
|
# multiline input
|
||
|
if self.text.compare('insert', '==', 'end-1c'):
|
||
|
if self.history_ismember():
|
||
|
self.editwin.history.history_prev(event)
|
||
|
return "break"
|
||
|
|
||
|
elif self.text.compare('insert', '<=', 'iomark'):
|
||
|
# don't leave the input area
|
||
|
self.text.mark_set('insert', 'iomark')
|
||
|
return "break"
|
||
|
elif self.text.compare('insert-1l', '<=', 'iomark'):
|
||
|
self.text.mark_set('insert', 'iomark')
|
||
|
return "break"
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def key_down_callback(self, event):
|
||
|
if self.enabled == False: return
|
||
|
|
||
|
if self.editwin.executing:
|
||
|
return "break"
|
||
|
|
||
|
|
||
|
s = self.text.get('iomark', 'end-1c')
|
||
|
|
||
|
if '\n' not in s:
|
||
|
# single line input
|
||
|
if self.text.compare('insert', '==', 'end-1c'):
|
||
|
self.editwin.history.history_next(event)
|
||
|
return "break"
|
||
|
else:
|
||
|
self.text.mark_set('insert', 'end-1c')
|
||
|
return "break"
|
||
|
|
||
|
else:
|
||
|
# multiline input
|
||
|
if self.text.compare('insert', '<', 'end-1c') and \
|
||
|
self.text.compare('insert lineend', '==', 'end-1c'):
|
||
|
self.text.mark_set('insert', 'end-1c')
|
||
|
return "break"
|
||
|
|
||
|
if self.history_ismember():
|
||
|
if self.text.compare('insert', '==', 'end-1c'):
|
||
|
self.editwin.history.history_next(event)
|
||
|
return "break"
|
||
|
elif self.text.compare('insert lineend', '==', 'end-1c'):
|
||
|
return "break"
|