New Upstream Snapshot - themole
Ready changes
Summary
Merged new upstream version: 0.3+git20131102.1.daacccf (was: 0.3).
Resulting package
Built on 2023-01-25T02:46 (took 4m26s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-snapshots themole
Lintian Result
Diff
diff --git a/commands.py b/commands.py
index d521126..5694b5e 100644
--- a/commands.py
+++ b/commands.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from sys import exit
from urllib.parse import urlencode, parse_qs
@@ -313,11 +313,14 @@ class QueryCommand(Command):
index_limit = params.index('limit') if 'limit' in params else -1
index_where = params.index('where') if 'where' in params else -1
index_offset = params.index('offset') if 'offset' in params else -1
- min_end = min(index_limit if index_limit != -1 else 0x7fffff, index_offset if index_offset != -1 else 0x7fffff)
+ index_outfile = params.index('outfile') if 'outfile' in params else -1
+ min_end = min(index_limit if index_limit != -1 else 0x7fffff, index_outfile if index_outfile != -1 else 0x7fffff. index_offset if index_offset != -1 else 0x7fffff)
if min_end == 0x7fffff:
min_end = -1
where_end = min_end if min_end != -1 and min_end > index_where else len(params) + 1
+ outfile_end = min_end if min_end != -1 and min_end > index_outfile else len(params) + 1
condition = ' '.join(params[index_where + 1:where_end]) if index_where != -1 else '1=1'
+ outfile = ''.join(params[index_outfile+1:outfile_end]) if index_outfile != -1 else ''
if index_limit == len(params) - 1:
raise CommandException('Limit argument requires row numbers.')
if index_offset == len(params) - 1:
@@ -340,13 +343,24 @@ class QueryCommand(Command):
except themole.QueryError as ex:
output_manager.error('Query error: {0}'.format(ex)).line_break()
return
- res_out = output_manager.results_output(columns)
- for i in result:
- res_out.put(i)
- res_out.end_sequence()
+ if len(outfile) == 0:
+ res_out = output_manager.results_output(columns)
+ for i in result:
+ res_out.put(i)
+ res_out.end_sequence()
+ else:
+ output_manager.line_break()
+ try:
+ fd = open(outfile, 'a+')
+ for i in result:
+ fd.write(', '.join(i) + '\n')
+ fd.close()
+ output_manager.info('Saved results to ' + outfile).line_break()
+ except IOError as ex:
+ output_manager.error(str(ex))
def usage(self, cmd_name):
- return cmd_name + ' <SCHEMA> <TABLE> <COLUMNS> [where <CONDITION>] [limit <NUM_ROWS>] [offset <OFFSET>]'
+ return cmd_name + ' <SCHEMA> <TABLE> <COLUMNS> [where <CONDITION>] [limit <NUM_ROWS>] [outfile <PATH>][offset <OFFSET>]'
def parameters(self, mole, current_params):
if len(current_params) == 0:
@@ -360,7 +374,7 @@ class QueryCommand(Command):
return []
return tables
elif len(current_params) == 3 :
- return ['where', 'limit', 'offset']
+ return ['where', 'limit', 'offset', 'outfile']
else:
columns = mole.poll_columns(current_params[0], current_params[1])
return columns if columns else []
diff --git a/completion.py b/completion.py
index 10c967b..c01e9dd 100644
--- a/completion.py
+++ b/completion.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
try:
import readline
@@ -100,7 +100,7 @@ class CompletionManager:
while match:
output += self.nice_split(line[start_index:match.start()])
start_index += match.end()
- output.append(match.groups()[0][1:-1].strip())
+ output.append(match.groups()[0][1:-1])
match = self.parse_regex.search(line[start_index:])
if start_index < len(line):
output += self.nice_split(line[start_index:])
diff --git a/connection/request.py b/connection/request.py
index f01fb78..43c3b87 100644
--- a/connection/request.py
+++ b/connection/request.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from urllib.parse import urlparse, urlencode
diff --git a/connection/requester.py b/connection/requester.py
index 52614b0..10696af 100644
--- a/connection/requester.py
+++ b/connection/requester.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import urllib.parse
import time
@@ -101,11 +101,11 @@ class Requester(object):
def request(self, query):
"""Make the request applying the filters and return the response.
-
+
@param query: String containing the query to inject in the
vulnerable param.
@return: String containing the response in html format.
-
+
"""
query = self.__query_filters.apply_filters(query)
@@ -136,6 +136,15 @@ class Requester(object):
response.content = self.decode(response.content)
+ if len(query) > 0:
+ new_response = ''
+ while query.lower() in response.content.lower():
+ index = response.content.lower().find(query.lower())
+ new_response += response.content[:index]
+ response.content = response.content[index+len(query):]
+ new_response += response.content
+ response.content = new_response
+
self.__response_filters.apply_filters(response)
return response.content
@@ -194,11 +203,11 @@ class Requester(object):
@property
def get_parameters(self):
return self.__get_parameters
-
+
@property
def encoding(self):
return self.__encoding
-
+
@encoding.setter
def encoding(self, new_encoding):
self.__encoding = new_encoding
diff --git a/connection/requestsender.py b/connection/requestsender.py
index 6c76efd..c0afa3a 100644
--- a/connection/requestsender.py
+++ b/connection/requestsender.py
@@ -16,12 +16,13 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import http.client
import socket
+import zlib
from urllib.parse import urlencode, urlparse
from moleexceptions import ConnectionException
@@ -53,7 +54,7 @@ class BaseRequestSender():
for _ in range(self.max_retries):
try:
- data = self.fetch_data(request, connection)
+ (data, headers) = self.fetch_data(request, connection)
break
except (socket.error,
socket.timeout,
@@ -68,6 +69,8 @@ class BaseRequestSender():
if data is None:
raise ConnectionException(str(exception))
+ if 'Content-Encoding' in headers and headers['Content-Encoding'] == 'gzip':
+ data = decompressed_data=zlib.decompress(data, 16+zlib.MAX_WBITS)
return Response(data)
class HttpRequestSender(BaseRequestSender):
@@ -86,7 +89,7 @@ class HttpRequestSender(BaseRequestSender):
connection.request('GET', parsed.path, None, request.headers)
resp = connection.getresponse()
resp_headers = dict(resp.getheaders())
- return resp.read()
+ return (resp.read(), resp_headers)
def __str__(self):
return 'httpsender'
@@ -102,7 +105,7 @@ class HttpHeadRequestSender(BaseRequestSender):
output = ''
for header in headers:
output += '<div>{0} {1}</div>\n'.format(header, headers[header])
- return '<html><body>{0}</body></html>'.format(output).encode()
+ return ('<html><body>{0}</body></html>'.format(output).encode(), headers)
def __str__(self):
return 'headsender'
diff --git a/connection/response.py b/connection/response.py
index 8f5c688..c37b84e 100644
--- a/connection/response.py
+++ b/connection/response.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
class Response():
def __init__(self, content):
diff --git a/datadumper.py b/datadumper.py
index 9cc7bb2..612455a 100644
--- a/datadumper.py
+++ b/datadumper.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from moleexceptions import QueryError, ConnectionException
diff --git a/dbdump.py b/dbdump.py
index 7dc225c..b56fbac 100644
--- a/dbdump.py
+++ b/dbdump.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
class DatabaseDump():
def __init__(self):
diff --git a/dbmsmoles/__init__.py b/dbmsmoles/__init__.py
index f6a61f7..a2be468 100644
--- a/dbmsmoles/__init__.py
+++ b/dbmsmoles/__init__.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from dbmsmoles.dbmsmole import DbmsMole, FingerBase
diff --git a/dbmsmoles/dbmsmole.py b/dbmsmoles/dbmsmole.py
index c47f59a..421fdeb 100644
--- a/dbmsmoles/dbmsmole.py
+++ b/dbmsmoles/dbmsmole.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import re
diff --git a/dbmsmoles/mysql.py b/dbmsmoles/mysql.py
index 6da5716..2fc4772 100644
--- a/dbmsmoles/mysql.py
+++ b/dbmsmoles/mysql.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from . import DbmsMole, FingerBase
diff --git a/dbmsmoles/oracle.py b/dbmsmoles/oracle.py
index d5155af..3be1f9a 100644
--- a/dbmsmoles/oracle.py
+++ b/dbmsmoles/oracle.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from . import DbmsMole, FingerBase
diff --git a/dbmsmoles/postgres.py b/dbmsmoles/postgres.py
index f7ee08f..2745052 100644
--- a/dbmsmoles/postgres.py
+++ b/dbmsmoles/postgres.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from . import DbmsMole, FingerBase
@@ -105,7 +105,7 @@ class PostgresMole(DbmsMole):
@classmethod
def dbms_check_blind_query(cls):
- return ' and {op_par}0 < (select length(getpgusername()))'
+ return ' and 0 < (select length(getpgusername()))'
@classmethod
def field_finger(cls, finger):
@@ -180,7 +180,7 @@ class PostgresMole(DbmsMole):
if len(fields) == 1 and fields[0] == 'distinct(schemaname)':
query += " distinct on(schemaname) "
fields = ['schemaname']
- query_list[injectable_field] = ("cast(cast(" + PostgresMole.integer_out_delimiter +
+ query_list[injectable_field] = ("cast(cast(" + PostgresMole.integer_out_delimiter +
" as varchar(10))||length(" + self._concat_fields(fields) +
")||cast(" + PostgresMole.integer_out_delimiter + " as varchar(10)) as bigint)"
)
@@ -201,9 +201,9 @@ class PostgresMole(DbmsMole):
if len(fields) == 1 and fields[0] == 'distinct(schemaname)':
query += " distinct on(schemaname) "
fields = ['schemaname']
- query_list[injectable_field] = ("cast(cast(" + PostgresMole.integer_out_delimiter +
+ query_list[injectable_field] = ("cast(cast(" + PostgresMole.integer_out_delimiter +
" as varchar(10))||ascii(substring(" + self._concat_fields(fields) +
- ", " + str(index)+",1))||cast(" + PostgresMole.integer_out_delimiter +
+ ", " + str(index)+",1))||cast(" + PostgresMole.integer_out_delimiter +
" as varchar(10)) as bigint)"
)
query += ','.join(query_list)
diff --git a/dbmsmoles/sqlserver.py b/dbmsmoles/sqlserver.py
index 8d13507..1f17b45 100644
--- a/dbmsmoles/sqlserver.py
+++ b/dbmsmoles/sqlserver.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from . import DbmsMole, FingerBase
@@ -110,7 +110,7 @@ class SQLServerMole(DbmsMole):
@classmethod
def dbms_check_blind_query(cls):
- return ' and {op_par}0 < (select len(user_name()))'
+ return ' and 0 < (select len(user_name()))'
@classmethod
def dbms_name(cls):
diff --git a/debian/changelog b/debian/changelog
index d9ba482..7e7e021 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+themole (0.3+git20131102.1.daacccf-1) UNRELEASED; urgency=low
+
+ * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk> Wed, 25 Jan 2023 02:43:27 -0000
+
themole (0.3-3.1) unstable; urgency=medium
* Non-maintainer upload.
diff --git a/domanalyser.py b/domanalyser.py
index 0d2d4ba..16cd3a7 100644
--- a/domanalyser.py
+++ b/domanalyser.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import lxml.html as lxml
diff --git a/filters.py b/filters.py
index 3b6b1eb..866daaa 100644
--- a/filters.py
+++ b/filters.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import os
import sys
diff --git a/injectioninspector.py b/injectioninspector.py
index bf6bb19..7edfb71 100644
--- a/injectioninspector.py
+++ b/injectioninspector.py
@@ -17,9 +17,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from dbmsmoles import DbmsMole
from moleexceptions import StoppedQueryException, ConnectionException
@@ -90,11 +90,11 @@ class InjectionInspector:
req = mole.make_request(' order by 15000')
except ConnectionException as ex:
raise ColumnNumberNotFound(str(ex))
- content_of_needle = mole.analyser.node_content(req)
+ content_of_needle = mole.analyser.node_content(req) or ''
mole.stop_query = False
last = 2
try:
- new_needle_content = mole.analyser.node_content(mole.make_request(' order by %d ' % (last,)))
+ new_needle_content = mole.analyser.node_content(mole.make_request(' order by %d ' % (last,))) or ''
except ConnectionException as ex:
raise ColumnNumberNotFound(str(ex))
while new_needle_content != content_of_needle and not DbmsMole.is_error(new_needle_content):
@@ -103,7 +103,7 @@ class InjectionInspector:
last *= 2
output_manager.info('Trying {0} columns'.format(last))
try:
- new_needle_content = mole.analyser.node_content(mole.make_request(' order by %d ' % (last,)))
+ new_needle_content = mole.analyser.node_content(mole.make_request(' order by %d ' % (last,))) or ''
except ConnectionException as ex:
raise ColumnNumberNotFound(str(ex))
pri = last // 2
@@ -113,7 +113,8 @@ class InjectionInspector:
medio = ((pri + last) // 2) + ((pri + last) & 1)
output_manager.info('Trying {0} columns'.format(medio))
try:
- new_needle_content = mole.analyser.node_content(mole.make_request(' order by %d ' % (medio,)))
+ new_needle_content = mole.analyser.node_content(mole.make_request(' order by %d ' % (medio,))) or ''
+
except ConnectionException as ex:
raise ColumnNumberNotFound(str(ex))
if new_needle_content != content_of_needle and not DbmsMole.is_error(new_needle_content):
diff --git a/mole.py b/mole.py
index 854a7f3..406f3d4 100755
--- a/mole.py
+++ b/mole.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from sys import exit
import getopt, sys
@@ -130,7 +130,6 @@ r""" _____ _ ___ ___ _
| | | | | | __/ | | | | (_) | | __/
\_/ |_| |_|\___| \_| |_/\___/|_|\___|
- Developed by Nasel(http://www.nasel.com.ar).
Published under GPLv3.
Be efficient and have fun!
"""
diff --git a/moleexceptions.py b/moleexceptions.py
index aa73d89..9118098 100644
--- a/moleexceptions.py
+++ b/moleexceptions.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
#General Exceptions
diff --git a/outputmanager.py b/outputmanager.py
index 27652fa..62773de 100644
--- a/outputmanager.py
+++ b/outputmanager.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import sys
import threading
diff --git a/parameters.py b/parameters.py
index 3042bfc..cb1ddb0 100644
--- a/parameters.py
+++ b/parameters.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
class Parameter:
def __init__(self, exec_function=None, no_args_str='Expected arguments', invalid_args_function=None):
diff --git a/pyreadline/__init__.py b/pyreadline/__init__.py
new file mode 100644
index 0000000..c15af44
--- /dev/null
+++ b/pyreadline/__init__.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+from . import unicode_helper, logger, clipboard, lineeditor, modes, console
+from .rlmain import *
+from . import rlmain
diff --git a/pyreadline/clipboard/__init__.py b/pyreadline/clipboard/__init__.py
new file mode 100644
index 0000000..7c9d857
--- /dev/null
+++ b/pyreadline/clipboard/__init__.py
@@ -0,0 +1,72 @@
+import sys
+success = True
+in_ironpython = "IronPython" in sys.version
+if in_ironpython:
+ try:
+ from .ironpython_clipboard import GetClipboardText, SetClipboardText
+ except ImportError:
+ from .no_clipboard import GetClipboardText, SetClipboardText
+
+else:
+ try:
+ from .win32_clipboard import GetClipboardText, SetClipboardText
+ except ImportError:
+ from .no_clipboard import GetClipboardText, SetClipboardText
+
+
+def send_data(lists):
+ SetClipboardText(make_tab(lists))
+
+
+def set_clipboard_text(toclipboard):
+ SetClipboardText(str(toclipboard))
+
+def make_tab(lists):
+ if hasattr(lists, "tolist"):
+ lists = lists.tolist()
+ ut = []
+ for rad in lists:
+ if type(rad) in [list, tuple]:
+ ut.append("\t".join(["%s"%x for x in rad]))
+ else:
+ ut.append("%s"%rad)
+ return "\n".join(ut)
+
+def make_list_of_list(txt):
+ def make_num(x):
+ try:
+ return int(x)
+ except ValueError:
+ try:
+ return float(x)
+ except ValueError:
+ try:
+ return complex(x)
+ except ValueError:
+ return x
+ return x
+ ut = []
+ flag = False
+ for rad in [x for x in txt.split("\r\n") if x != ""]:
+ raden=[make_num(x) for x in rad.split("\t")]
+ if str in list(map(type,raden)):
+ flag = True
+ ut.append(raden)
+ return ut, flag
+
+
+def get_clipboard_text_and_convert(paste_list=False):
+ """Get txt from clipboard. if paste_list==True the convert tab separated
+ data to list of lists. Enclose list of list in array() if all elements are
+ numeric"""
+ txt = GetClipboardText()
+ if txt:
+ if paste_list and "\t" in txt:
+ array, flag = make_list_of_list(txt)
+ if flag:
+ txt = repr(array)
+ else:
+ txt = "array(%s)"%repr(array)
+ txt = "".join([c for c in txt if c not in " \t\r\n"])
+ return txt
+
diff --git a/pyreadline/clipboard/ironpython_clipboard.py b/pyreadline/clipboard/ironpython_clipboard.py
new file mode 100644
index 0000000..6aaab65
--- /dev/null
+++ b/pyreadline/clipboard/ironpython_clipboard.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import clr
+clr.AddReferenceByPartialName("System.Windows.Forms")
+import System.Windows.Forms.Clipboard as cb
+
+def GetClipboardText():
+ text = ""
+ if cb.ContainsText():
+ text = cb.GetText()
+
+ return text
+
+def SetClipboardText(text):
+ cb.SetText(text)
+
+if __name__ == '__main__':
+ txt = GetClipboardText() # display last text clipped
+ print(txt)
+
+
+
+
\ No newline at end of file
diff --git a/pyreadline/clipboard/no_clipboard.py b/pyreadline/clipboard/no_clipboard.py
new file mode 100644
index 0000000..3b3e290
--- /dev/null
+++ b/pyreadline/clipboard/no_clipboard.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+
+
+mybuffer = ""
+
+def GetClipboardText():
+ return mybuffer
+
+def SetClipboardText(text):
+ global mybuffer
+ mybuffer = text
+
diff --git a/pyreadline/clipboard/win32_clipboard.py b/pyreadline/clipboard/win32_clipboard.py
new file mode 100644
index 0000000..95f966a
--- /dev/null
+++ b/pyreadline/clipboard/win32_clipboard.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Jack Trainor.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+###################################
+#
+# Based on recipe posted to ctypes-users
+# see archive
+# http://aspn.activestate.com/ASPN/Mail/Message/ctypes-users/1771866
+#
+#
+
+###################################################################################
+#
+# The Python win32clipboard lib functions work well enough ... except that they
+# can only cut and paste items from within one application, not across
+# applications or processes.
+#
+# I've written a number of Python text filters I like to run on the contents of
+# the clipboard so I need to call the Windows clipboard API with global memory
+# for my filters to work properly.
+#
+# Here's some sample code solving this problem using ctypes.
+#
+# This is my first work with ctypes. It's powerful stuff, but passing arguments
+# in and out of functions is tricky. More sample code would have been helpful,
+# hence this contribution.
+#
+###################################################################################
+
+from ctypes import *
+from pyreadline.keysyms.winconstants import CF_TEXT, GHND
+from pyreadline.unicode_helper import ensure_unicode,ensure_str
+
+OpenClipboard = windll.user32.OpenClipboard
+OpenClipboard.argtypes = [c_int]
+
+EmptyClipboard = windll.user32.EmptyClipboard
+
+GetClipboardData = windll.user32.GetClipboardData
+GetClipboardData.argtypes = [c_int]
+
+GetClipboardFormatName = windll.user32.GetClipboardFormatNameA
+GetClipboardFormatName.argtypes = [c_uint,c_char_p,c_int]
+
+SetClipboardData = windll.user32.SetClipboardData
+SetClipboardData.argtypes = [c_int,c_int]
+
+EnumClipboardFormats = windll.user32.EnumClipboardFormats
+EnumClipboardFormats.argtypes = [c_int]
+
+CloseClipboard = windll.user32.CloseClipboard
+CloseClipboard.argtypes = []
+
+
+GlobalAlloc = windll.kernel32.GlobalAlloc
+GlobalLock = windll.kernel32.GlobalLock
+GlobalLock.argtypes = [c_int]
+GlobalUnlock = windll.kernel32.GlobalUnlock
+GlobalUnlock.argtypes = [c_int]
+memcpy = cdll.msvcrt.memcpy
+
+def enum():
+ OpenClipboard(0)
+ q = EnumClipboardFormats(0)
+ while q:
+ q = EnumClipboardFormats(q)
+ CloseClipboard()
+
+def getformatname(format):
+ buffer = c_buffer(" "*100)
+ bufferSize = sizeof(buffer)
+ OpenClipboard(0)
+ GetClipboardFormatName(format, buffer, bufferSize)
+ CloseClipboard()
+ return buffer.value
+
+def GetClipboardText():
+ text = ""
+ if OpenClipboard(0):
+ hClipMem = GetClipboardData(CF_TEXT)
+ if hClipMem:
+ GlobalLock.restype = c_char_p
+ text = GlobalLock(hClipMem)
+ GlobalUnlock(hClipMem)
+ CloseClipboard()
+ return ensure_unicode(text)
+
+def SetClipboardText(text):
+ buffer = c_buffer(ensure_str(text))
+ bufferSize = sizeof(buffer)
+ hGlobalMem = GlobalAlloc(c_int(GHND), c_int(bufferSize))
+ GlobalLock.restype = c_void_p
+ lpGlobalMem = GlobalLock(c_int(hGlobalMem))
+ memcpy(lpGlobalMem, addressof(buffer), c_int(bufferSize))
+ GlobalUnlock(c_int(hGlobalMem))
+ if OpenClipboard(0):
+ EmptyClipboard()
+ SetClipboardData(c_int(CF_TEXT), c_int(hGlobalMem))
+ CloseClipboard()
+
+if __name__ == '__main__':
+ txt = GetClipboardText() # display last text clipped
+ print(txt)
diff --git a/pyreadline/console/__init__.py b/pyreadline/console/__init__.py
new file mode 100644
index 0000000..3407b00
--- /dev/null
+++ b/pyreadline/console/__init__.py
@@ -0,0 +1,22 @@
+import glob,sys
+
+success = False
+in_ironpython = "IronPython" in sys.version
+
+if in_ironpython:
+ try:
+ from .ironpython_console import *
+ success = True
+ except ImportError:
+ raise
+else:
+ try:
+ from .console import *
+ success = True
+ except ImportError:
+ pass
+ raise
+
+if not success:
+ raise ImportError(
+ "Could not find a console implementation for your platform")
diff --git a/pyreadline/console/ansi.py b/pyreadline/console/ansi.py
new file mode 100644
index 0000000..dcfb4a5
--- /dev/null
+++ b/pyreadline/console/ansi.py
@@ -0,0 +1,190 @@
+# -*- coding: ISO-8859-1 -*-
+import re,sys,os
+
+terminal_escape = re.compile('(\001?\033\\[[0-9;]*m\002?)')
+escape_parts = re.compile('\001?\033\\[([0-9;]*)m\002?')
+
+
+class AnsiState(object):
+ def __init__(self,bold=False,inverse=False,color="white",background="black",backgroundbold=False):
+ self.bold = bold
+ self.inverse = inverse
+ self.color = color
+ self.background = background
+ self.backgroundbold = backgroundbold
+
+ trtable = {"black":0, "red":4, "green":2, "yellow":6,
+ "blue":1, "magenta":5, "cyan":3, "white":7}
+ revtable = dict(list(zip(list(trtable.values()),list(trtable.keys()))))
+ def get_winattr(self):
+ attr = 0
+ if self.bold:
+ attr |= 0x0008
+ if self.backgroundbold:
+ attr |= 0x0080
+ if self.inverse:
+ attr |= 0x4000
+ attr |= self.trtable[self.color]
+ attr |= (self.trtable[self.background] << 4)
+ return attr
+
+ def set_winattr(self, attr):
+ self.bold = bool(attr & 0x0008)
+ self.backgroundbold = bool(attr & 0x0080)
+ self.inverse = bool(attr & 0x4000)
+ self.color = self.revtable[attr & 0x0007]
+ self.background = self.revtable[(attr & 0x0070) >> 4]
+
+ winattr=property(get_winattr,set_winattr)
+ def __repr__(self):
+ return 'AnsiState(bold=%s,inverse=%s,color=%9s,' \
+ 'background=%9s,backgroundbold=%s)# 0x%x'% \
+ (self.bold, self.inverse, '"%s"'%self.color,
+ '"%s"'%self.background, self.backgroundbold,
+ self.winattr)
+
+ def copy(self):
+ x = AnsiState()
+ x.bold = self.bold
+ x.inverse = self.inverse
+ x.color = self.color
+ x.background = self.background
+ x.backgroundbold = self.backgroundbold
+ return x
+
+defaultstate = AnsiState(False,False,"white")
+
+trtable = {0:"black", 1:"red", 2:"green", 3:"yellow",
+ 4:"blue", 5:"magenta", 6:"cyan", 7:"white"}
+
+class AnsiWriter(object):
+ def __init__(self, default=defaultstate):
+ if isinstance(defaultstate, AnsiState):
+ self.defaultstate = default
+ else:
+ self.defaultstate=AnsiState()
+ self.defaultstate.winattr = defaultstate
+
+
+ def write_color(self,text, attr=None):
+ '''write text at current cursor position and interpret color escapes.
+
+ return the number of characters written.
+ '''
+ if isinstance(attr,AnsiState):
+ defaultstate = attr
+ elif attr is None: #use attribute form initial console
+ attr = self.defaultstate.copy()
+ else:
+ defaultstate = AnsiState()
+ defaultstate.winattr = attr
+ attr = defaultstate
+ chunks = terminal_escape.split(text)
+ n = 0 # count the characters we actually write, omitting the escapes
+ res=[]
+ for chunk in chunks:
+ m = escape_parts.match(chunk)
+ if m:
+ parts = m.group(1).split(";")
+ if len(parts) == 1 and parts[0] == "0":
+ attr = self.defaultstate.copy()
+ continue
+ for part in parts:
+ if part == "0": # No text attribute
+ attr = self.defaultstate.copy()
+ attr.bold=False
+ elif part == "7": # switch on reverse
+ attr.inverse=True
+ elif part == "1": # switch on bold (i.e. intensify foreground color)
+ attr.bold=True
+ elif len(part) == 2 and "30" <= part <= "37": # set foreground color
+ attr.color = trtable[int(part) - 30]
+ elif len(part) == 2 and "40" <= part <= "47": # set background color
+ attr.backgroundcolor = trtable[int(part) - 40]
+ continue
+ n += len(chunk)
+ if True:
+ res.append((attr.copy(), chunk))
+ return n,res
+
+ def parse_color(self,text, attr=None):
+ n,res=self.write_color(text, attr)
+ return n, [attr.winattr for attr, text in res]
+
+def write_color(text, attr=None):
+ a = AnsiWriter(defaultstate)
+ return a.write_color(text, attr)
+
+def write_color_old( text, attr=None):
+ '''write text at current cursor position and interpret color escapes.
+
+ return the number of characters written.
+ '''
+ res = []
+ chunks = terminal_escape.split(text)
+ n = 0 # count the characters we actually write, omitting the escapes
+ if attr is None:#use attribute from initial console
+ attr = 15
+ for chunk in chunks:
+ m = escape_parts.match(chunk)
+ if m:
+ for part in m.group(1).split(";"):
+ if part == "0": # No text attribute
+ attr = 0
+ elif part == "7": # switch on reverse
+ attr |= 0x4000
+ if part == "1": # switch on bold (i.e. intensify foreground color)
+ attr |= 0x08
+ elif len(part) == 2 and "30" <= part <= "37": # set foreground color
+ part = int(part)-30
+ # we have to mirror bits
+ attr = (attr & ~0x07) | ((part & 0x1) << 2) | (part & 0x2) | ((part & 0x4) >> 2)
+ elif len(part) == 2 and "40" <= part <= "47": # set background color
+ part = int(part) - 40
+ # we have to mirror bits
+ attr = (attr & ~0x70) | ((part & 0x1) << 6) | ((part & 0x2) << 4) | ((part & 0x4) << 2)
+ # ignore blink, underline and anything we don't understand
+ continue
+ n += len(chunk)
+ if chunk:
+ res.append(("0x%x"%attr, chunk))
+ return res
+
+
+#trtable={0:"black",1:"red",2:"green",3:"yellow",4:"blue",5:"magenta",6:"cyan",7:"white"}
+
+if __name__=="__main__x":
+ import pprint
+ pprint=pprint.pprint
+
+ s="\033[0;31mred\033[0;32mgreen\033[0;33myellow\033[0;34mblue\033[0;35mmagenta\033[0;36mcyan\033[0;37mwhite\033[0m"
+ pprint (write_color(s))
+ pprint (write_color_old(s))
+ s="\033[1;31mred\033[1;32mgreen\033[1;33myellow\033[1;34mblue\033[1;35mmagenta\033[1;36mcyan\033[1;37mwhite\033[0m"
+ pprint (write_color(s))
+ pprint (write_color_old(s))
+
+ s="\033[0;7;31mred\033[0;7;32mgreen\033[0;7;33myellow\033[0;7;34mblue\033[0;7;35mmagenta\033[0;7;36mcyan\033[0;7;37mwhite\033[0m"
+ pprint (write_color(s))
+ pprint (write_color_old(s))
+ s="\033[1;7;31mred\033[1;7;32mgreen\033[1;7;33myellow\033[1;7;34mblue\033[1;7;35mmagenta\033[1;7;36mcyan\033[1;7;37mwhite\033[0m"
+ pprint (write_color(s))
+ pprint (write_color_old(s))
+
+
+if __name__=="__main__":
+ from . import console
+ import pprint
+ pprint=pprint.pprint
+
+ c=console.Console()
+ c.write_color("dhsjdhs")
+ c.write_color("\033[0;32mIn [\033[1;32m1\033[0;32m]:")
+ print()
+ pprint (write_color("\033[0;32mIn [\033[1;32m1\033[0;32m]:"))
+
+if __name__=="__main__x":
+ import pprint
+ pprint=pprint.pprint
+ s="\033[0;31mred\033[0;32mgreen\033[0;33myellow\033[0;34mblue\033[0;35mmagenta\033[0;36mcyan\033[0;37mwhite\033[0m"
+ pprint (write_color(s))
diff --git a/pyreadline/console/console.py b/pyreadline/console/console.py
new file mode 100644
index 0000000..61d0c1f
--- /dev/null
+++ b/pyreadline/console/console.py
@@ -0,0 +1,845 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+'''Cursor control and color for the Windows console.
+
+This was modeled after the C extension of the same name by Fredrik Lundh.
+'''
+
+# primitive debug printing that won't interfere with the screen
+
+import sys,os
+import traceback
+import re
+
+import pyreadline.unicode_helper as unicode_helper
+
+from pyreadline.logger import log
+from pyreadline.unicode_helper import ensure_unicode, ensure_str
+from pyreadline.keysyms import make_KeyPress, KeyPress
+from pyreadline.console.ansi import AnsiState,AnsiWriter
+
+try:
+ import ctypes.util
+ from ctypes import *
+ from _ctypes import call_function
+ from ctypes.wintypes import *
+except ImportError:
+ raise ImportError("You need ctypes to run this code")
+
+if sys.version_info < (2, 6):
+ bytes = str
+
+def nolog(string):
+ pass
+
+log = nolog
+
+
+# some constants we need
+STD_INPUT_HANDLE = -10
+STD_OUTPUT_HANDLE = -11
+ENABLE_WINDOW_INPUT = 0x0008
+ENABLE_MOUSE_INPUT = 0x0010
+ENABLE_PROCESSED_INPUT = 0x0001
+WHITE = 0x7
+BLACK = 0
+MENU_EVENT = 0x0008
+KEY_EVENT = 0x0001
+MOUSE_MOVED = 0x0001
+MOUSE_EVENT = 0x0002
+WINDOW_BUFFER_SIZE_EVENT = 0x0004
+FOCUS_EVENT = 0x0010
+MENU_EVENT = 0x0008
+VK_SHIFT = 0x10
+VK_CONTROL = 0x11
+VK_MENU = 0x12
+GENERIC_READ = int(0x80000000)
+GENERIC_WRITE = 0x40000000
+
+# Windows structures we'll need later
+class COORD(Structure):
+ _fields_ = [("X", c_short),
+ ("Y", c_short)]
+
+class SMALL_RECT(Structure):
+ _fields_ = [("Left", c_short),
+ ("Top", c_short),
+ ("Right", c_short),
+ ("Bottom", c_short)]
+
+class CONSOLE_SCREEN_BUFFER_INFO(Structure):
+ _fields_ = [("dwSize", COORD),
+ ("dwCursorPosition", COORD),
+ ("wAttributes", c_short),
+ ("srWindow", SMALL_RECT),
+ ("dwMaximumWindowSize", COORD)]
+
+class CHAR_UNION(Union):
+ _fields_ = [("UnicodeChar", c_wchar),
+ ("AsciiChar", c_char)]
+
+class CHAR_INFO(Structure):
+ _fields_ = [("Char", CHAR_UNION),
+ ("Attributes", c_short)]
+
+class KEY_EVENT_RECORD(Structure):
+ _fields_ = [("bKeyDown", c_byte),
+ ("pad2", c_byte),
+ ('pad1', c_short),
+ ("wRepeatCount", c_short),
+ ("wVirtualKeyCode", c_short),
+ ("wVirtualScanCode", c_short),
+ ("uChar", CHAR_UNION),
+ ("dwControlKeyState", c_int)]
+
+class MOUSE_EVENT_RECORD(Structure):
+ _fields_ = [("dwMousePosition", COORD),
+ ("dwButtonState", c_int),
+ ("dwControlKeyState", c_int),
+ ("dwEventFlags", c_int)]
+
+class WINDOW_BUFFER_SIZE_RECORD(Structure):
+ _fields_ = [("dwSize", COORD)]
+
+class MENU_EVENT_RECORD(Structure):
+ _fields_ = [("dwCommandId", c_uint)]
+
+class FOCUS_EVENT_RECORD(Structure):
+ _fields_ = [("bSetFocus", c_byte)]
+
+class INPUT_UNION(Union):
+ _fields_ = [("KeyEvent", KEY_EVENT_RECORD),
+ ("MouseEvent", MOUSE_EVENT_RECORD),
+ ("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD),
+ ("MenuEvent", MENU_EVENT_RECORD),
+ ("FocusEvent", FOCUS_EVENT_RECORD)]
+
+class INPUT_RECORD(Structure):
+ _fields_ = [("EventType", c_short),
+ ("Event", INPUT_UNION)]
+
+class CONSOLE_CURSOR_INFO(Structure):
+ _fields_ = [("dwSize", c_int),
+ ("bVisible", c_byte)]
+
+
+# I didn't want to have to individually import these so I made a list, they are
+# added to the Console class later in this file.
+
+funcs = [
+ 'AllocConsole',
+ 'CreateConsoleScreenBuffer',
+ 'FillConsoleOutputAttribute',
+ 'FillConsoleOutputCharacterW',
+ 'FreeConsole',
+ 'GetConsoleCursorInfo',
+ 'GetConsoleMode',
+ 'GetConsoleScreenBufferInfo',
+ 'GetConsoleTitleW',
+ 'GetProcAddress',
+ 'GetStdHandle',
+ 'PeekConsoleInputW',
+ 'ReadConsoleInputW',
+ 'ScrollConsoleScreenBufferW',
+ 'SetConsoleActiveScreenBuffer',
+ 'SetConsoleCursorInfo',
+ 'SetConsoleCursorPosition',
+ 'SetConsoleMode',
+ 'SetConsoleScreenBufferSize',
+ 'SetConsoleTextAttribute',
+ 'SetConsoleTitleW',
+ 'SetConsoleWindowInfo',
+ 'WriteConsoleW',
+ 'WriteConsoleOutputCharacterW',
+ 'WriteFile',
+ ]
+
+# I don't want events for these keys, they are just a bother for my application
+key_modifiers = { VK_SHIFT : 1,
+ VK_CONTROL : 1,
+ VK_MENU : 1, # alt key
+ 0x5b : 1, # windows key
+ }
+
+def split_block(text, size=1000):
+ return [text[start:start + size] for start in range(0, len(text), size)]
+
+
+
+class Console(object):
+ '''Console driver for Windows.
+
+ '''
+
+ def __init__(self, newbuffer=0):
+ '''Initialize the Console object.
+
+ newbuffer=1 will allocate a new buffer so the old content will be restored
+ on exit.
+ '''
+ #Do I need the following line? It causes a console to be created whenever
+ #readline is imported into a pythonw application which seems wrong. Things
+ #seem to work without it...
+ #self.AllocConsole()
+
+ if newbuffer:
+ self.hout = self.CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ 0, None, 1, None)
+ self.SetConsoleActiveScreenBuffer(self.hout)
+ else:
+ self.hout = self.GetStdHandle(STD_OUTPUT_HANDLE)
+
+ self.hin = self.GetStdHandle(STD_INPUT_HANDLE)
+ self.inmode = DWORD(0)
+ self.GetConsoleMode(self.hin, byref(self.inmode))
+ self.SetConsoleMode(self.hin, 0xf)
+ info = CONSOLE_SCREEN_BUFFER_INFO()
+ self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+ self.attr = info.wAttributes
+ self.saveattr = info.wAttributes # remember the initial colors
+ self.defaultstate = AnsiState()
+ self.defaultstate.winattr = info.wAttributes
+ self.ansiwriter = AnsiWriter(self.defaultstate)
+
+ background = self.attr & 0xf0
+ for escape in self.escape_to_color:
+ if self.escape_to_color[escape] is not None:
+ self.escape_to_color[escape] |= background
+ log('initial attr=%x' % self.attr)
+ self.softspace = 0 # this is for using it as a file-like object
+ self.serial = 0
+
+ self.pythondll = \
+ CDLL('python%s%s' % (sys.version[0], sys.version[2]))
+ self.pythondll.PyMem_Malloc.restype = c_size_t
+ self.pythondll.PyMem_Malloc.argtypes = [c_size_t]
+ self.inputHookPtr = \
+ c_void_p.from_address(addressof(self.pythondll.PyOS_InputHook)).value
+ setattr(Console, 'PyMem_Malloc', self.pythondll.PyMem_Malloc)
+
+ def __del__(self):
+ '''Cleanup the console when finished.'''
+ # I don't think this ever gets called
+ self.SetConsoleTextAttribute(self.hout, self.saveattr)
+ self.SetConsoleMode(self.hin, self.inmode)
+ self.FreeConsole()
+
+ def _get_top_bot(self):
+ info = CONSOLE_SCREEN_BUFFER_INFO()
+ self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+ rect = info.srWindow
+ top = rect.Top
+ bot = rect.Bottom
+ return top,bot
+
+ def fixcoord(self, x, y):
+ '''Return a long with x and y packed inside,
+ also handle negative x and y.'''
+ if x < 0 or y < 0:
+ info = CONSOLE_SCREEN_BUFFER_INFO()
+ self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+ if x < 0:
+ x = info.srWindow.Right - x
+ y = info.srWindow.Bottom + y
+
+ # this is a hack! ctypes won't pass structures but COORD is
+ # just like a long, so this works.
+ return c_int(y << 16 | x)
+
+ def pos(self, x=None, y=None):
+ '''Move or query the window cursor.'''
+ if x is None:
+ info = CONSOLE_SCREEN_BUFFER_INFO()
+ self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+ return (info.dwCursorPosition.X, info.dwCursorPosition.Y)
+ else:
+ return self.SetConsoleCursorPosition(self.hout,
+ self.fixcoord(x, y))
+
+ def home(self):
+ '''Move to home.'''
+ self.pos(0, 0)
+
+# Map ANSI color escape sequences into Windows Console Attributes
+
+ terminal_escape = re.compile('(\001?\033\\[[0-9;]+m\002?)')
+ escape_parts = re.compile('\001?\033\\[([0-9;]+)m\002?')
+ escape_to_color = { '0;30': 0x0, #black
+ '0;31': 0x4, #red
+ '0;32': 0x2, #green
+ '0;33': 0x4+0x2, #brown?
+ '0;34': 0x1, #blue
+ '0;35': 0x1+0x4, #purple
+ '0;36': 0x2+0x4, #cyan
+ '0;37': 0x1+0x2+0x4, #grey
+ '1;30': 0x1+0x2+0x4, #dark gray
+ '1;31': 0x4+0x8, #red
+ '1;32': 0x2+0x8, #light green
+ '1;33': 0x4+0x2+0x8, #yellow
+ '1;34': 0x1+0x8, #light blue
+ '1;35': 0x1+0x4+0x8, #light purple
+ '1;36': 0x1+0x2+0x8, #light cyan
+ '1;37': 0x1+0x2+0x4+0x8, #white
+ '0': None,
+ }
+
+ # This pattern should match all characters that change the cursor position differently
+ # than a normal character.
+ motion_char_re = re.compile('([\n\r\t\010\007])'.encode('ascii'))
+
+ def write_scrolling(self, text, attr=None):
+ '''write text at current cursor position while watching for scrolling.
+
+ If the window scrolls because you are at the bottom of the screen
+ buffer, all positions that you are storing will be shifted by the
+ scroll amount. For example, I remember the cursor position of the
+ prompt so that I can redraw the line but if the window scrolls,
+ the remembered position is off.
+
+ This variant of write tries to keep track of the cursor position
+ so that it will know when the screen buffer is scrolled. It
+ returns the number of lines that the buffer scrolled.
+
+ '''
+ x, y = self.pos()
+ w, h = self.size()
+ scroll = 0 # the result
+ # split the string into ordinary characters and funny characters
+ chunks = self.motion_char_re.split(ensure_str(text))
+ for chunk in chunks:
+ n = self.write_color(chunk, attr)
+ if len(chunk) == 1: # the funny characters will be alone
+ if chunk[0] == '\n': # newline
+ x = 0
+ y += 1
+ elif chunk[0] == '\r': # carriage return
+ x = 0
+ elif chunk[0] == '\t': # tab
+ x = 8 * (int(x / 8) + 1)
+ if x > w: # newline
+ x -= w
+ y += 1
+ elif chunk[0] == '\007': # bell
+ pass
+ elif chunk[0] == '\010':
+ x -= 1
+ if x < 0:
+ y -= 1 # backed up 1 line
+ else: # ordinary character
+ x += 1
+ if x == w: # wrap
+ x = 0
+ y += 1
+ if y == h: # scroll
+ scroll += 1
+ y = h - 1
+ else: # chunk of ordinary characters
+ x += n
+ l = int(x / w) # lines we advanced
+ x = x % w # new x value
+ y += l
+ if y >= h: # scroll
+ scroll += y - h + 1
+ y = h - 1
+ return scroll
+
+ def write_color(self, text, attr=None):
+ text = ensure_unicode(text)
+ n, res= self.ansiwriter.write_color(text, attr)
+ junk = DWORD(0)
+ for attr,chunk in res:
+ log("console.attr:%s"%str(attr))
+ log("console.chunk:%s"%str(chunk))
+ self.SetConsoleTextAttribute(self.hout, attr.winattr)
+ for short_chunk in split_block(chunk):
+ self.WriteConsoleW(self.hout, short_chunk,
+ len(short_chunk), byref(junk), None)
+ return n
+
+ def write_plain(self, text, attr=None):
+ '''write text at current cursor position.'''
+ text = ensure_unicode(text)
+ log('write("%s", %s)' %(text, attr))
+ if attr is None:
+ attr = self.attr
+ junk = DWORD(0)
+ self.SetConsoleTextAttribute(self.hout, attr)
+ for short_chunk in split_block(chunk):
+ self.WriteConsoleW(self.hout, ensure_unicode(short_chunk),
+ len(short_chunk), byref(junk), None)
+ return len(text)
+
+ #This function must be used to ensure functioning with EMACS
+ #Emacs sets the EMACS environment variable
+ if "EMACS" in os.environ:
+ def write_color(self, text, attr=None):
+ text = ensure_str(text)
+ junk = DWORD(0)
+ self.WriteFile(self.hout, text, len(text), byref(junk), None)
+ return len(text)
+ write_plain = write_color
+
+ # make this class look like a file object
+ def write(self, text):
+ text = ensure_unicode(text)
+ log('write("%s")' % text)
+ return self.write_color(text)
+
+ #write = write_scrolling
+
+ def isatty(self):
+ return True
+
+ def flush(self):
+ pass
+
+ def page(self, attr=None, fill=' '):
+ '''Fill the entire screen.'''
+ if attr is None:
+ attr = self.attr
+ if len(fill) != 1:
+ raise ValueError
+ info = CONSOLE_SCREEN_BUFFER_INFO()
+ self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+ if info.dwCursorPosition.X != 0 or info.dwCursorPosition.Y != 0:
+ self.SetConsoleCursorPosition(self.hout, self.fixcoord(0, 0))
+
+ w = info.dwSize.X
+ n = DWORD(0)
+ for y in range(info.dwSize.Y):
+ self.FillConsoleOutputAttribute(self.hout, attr,
+ w, self.fixcoord(0, y), byref(n))
+ self.FillConsoleOutputCharacterW(self.hout, ord(fill[0]),
+ w, self.fixcoord(0, y), byref(n))
+
+ self.attr = attr
+
+ def text(self, x, y, text, attr=None):
+ '''Write text at the given position.'''
+ if attr is None:
+ attr = self.attr
+
+ pos = self.fixcoord(x, y)
+ n = DWORD(0)
+ self.WriteConsoleOutputCharacterW(self.hout, text,
+ len(text), pos, byref(n))
+ self.FillConsoleOutputAttribute(self.hout, attr, n, pos, byref(n))
+
+ def clear_to_end_of_window(self):
+ top, bot = self._get_top_bot()
+ pos = self.pos()
+ w, h = self.size()
+ self.rectangle( (pos[0], pos[1], w, pos[1] + 1))
+ if pos[1] < bot:
+ self.rectangle((0, pos[1] + 1, w, bot + 1))
+
+ def rectangle(self, rect, attr=None, fill=' '):
+ '''Fill Rectangle.'''
+ x0, y0, x1, y1 = rect
+ n = DWORD(0)
+ if attr is None:
+ attr = self.attr
+ for y in range(y0, y1):
+ pos = self.fixcoord(x0, y)
+ self.FillConsoleOutputAttribute(self.hout, attr, x1 - x0,
+ pos, byref(n))
+ self.FillConsoleOutputCharacterW(self.hout, ord(fill[0]), x1 - x0,
+ pos, byref(n))
+
+ def scroll(self, rect, dx, dy, attr=None, fill=' '):
+ '''Scroll a rectangle.'''
+ if attr is None:
+ attr = self.attr
+ x0, y0, x1, y1 = rect
+ source = SMALL_RECT(x0, y0, x1 - 1, y1 - 1)
+ dest = self.fixcoord(x0 + dx, y0 + dy)
+ style = CHAR_INFO()
+ style.Char.AsciiChar = ensure_str(fill[0])
+ style.Attributes = attr
+
+ return self.ScrollConsoleScreenBufferW(self.hout, byref(source),
+ byref(source), dest, byref(style))
+
+ def scroll_window(self, lines):
+ '''Scroll the window by the indicated number of lines.'''
+ info = CONSOLE_SCREEN_BUFFER_INFO()
+ self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+ rect = info.srWindow
+ log('sw: rtop=%d rbot=%d' % (rect.Top, rect.Bottom))
+ top = rect.Top + lines
+ bot = rect.Bottom + lines
+ h = bot - top
+ maxbot = info.dwSize.Y-1
+ if top < 0:
+ top = 0
+ bot = h
+ if bot > maxbot:
+ bot = maxbot
+ top = bot - h
+
+ nrect = SMALL_RECT()
+ nrect.Top = top
+ nrect.Bottom = bot
+ nrect.Left = rect.Left
+ nrect.Right = rect.Right
+ log('sn: top=%d bot=%d' % (top, bot))
+ r=self.SetConsoleWindowInfo(self.hout, True, byref(nrect))
+ log('r=%d' % r)
+
+ def get(self):
+ '''Get next event from queue.'''
+ inputHookFunc = c_void_p.from_address(self.inputHookPtr).value
+
+ Cevent = INPUT_RECORD()
+ count = DWORD(0)
+ while 1:
+ if inputHookFunc:
+ call_function(inputHookFunc, ())
+ status = self.ReadConsoleInputW(self.hin,
+ byref(Cevent), 1, byref(count))
+ if status and count.value == 1:
+ e = event(self, Cevent)
+ return e
+
+ def getkeypress(self):
+ '''Return next key press event from the queue, ignoring others.'''
+ while 1:
+ e = self.get()
+ if e.type == 'KeyPress' and e.keycode not in key_modifiers:
+ log("console.getkeypress %s"%e)
+ if e.keyinfo.keyname == 'next':
+ self.scroll_window(12)
+ elif e.keyinfo.keyname == 'prior':
+ self.scroll_window(-12)
+ else:
+ return e
+ elif ((e.type == 'KeyRelease') and
+ (e.keyinfo == KeyPress('S', False, True, False, 'S'))):
+ log("getKeypress:%s,%s,%s"%(e.keyinfo, e.keycode, e.type))
+ return e
+
+
+ def getchar(self):
+ '''Get next character from queue.'''
+
+ Cevent = INPUT_RECORD()
+ count = DWORD(0)
+ while 1:
+ status = self.ReadConsoleInputW(self.hin,
+ byref(Cevent), 1, byref(count))
+ if (status and
+ (count.value == 1) and
+ (Cevent.EventType == 1) and
+ Cevent.Event.KeyEvent.bKeyDown):
+ sym = keysym(Cevent.Event.KeyEvent.wVirtualKeyCode)
+ if len(sym) == 0:
+ sym = Cevent.Event.KeyEvent.uChar.AsciiChar
+ return sym
+
+ def peek(self):
+ '''Check event queue.'''
+ Cevent = INPUT_RECORD()
+ count = DWORD(0)
+ status = self.PeekConsoleInputW(self.hin,
+ byref(Cevent), 1, byref(count))
+ if status and count == 1:
+ return event(self, Cevent)
+
+ def title(self, txt=None):
+ '''Set/get title.'''
+ if txt:
+ self.SetConsoleTitleW(txt)
+ else:
+ buffer = create_unicode_buffer(200)
+ n = self.GetConsoleTitleW(buffer, 200)
+ if n > 0:
+ return buffer.value[:n]
+
+ def size(self, width=None, height=None):
+ '''Set/get window size.'''
+ info = CONSOLE_SCREEN_BUFFER_INFO()
+ status = self.GetConsoleScreenBufferInfo(self.hout, byref(info))
+ if not status:
+ return None
+ if width is not None and height is not None:
+ wmin = info.srWindow.Right - info.srWindow.Left + 1
+ hmin = info.srWindow.Bottom - info.srWindow.Top + 1
+ #print wmin, hmin
+ width = max(width, wmin)
+ height = max(height, hmin)
+ #print width, height
+ self.SetConsoleScreenBufferSize(self.hout,
+ self.fixcoord(width, height))
+ else:
+ return (info.dwSize.X, info.dwSize.Y)
+
+ def cursor(self, visible=None, size=None):
+ '''Set cursor on or off.'''
+ info = CONSOLE_CURSOR_INFO()
+ if self.GetConsoleCursorInfo(self.hout, byref(info)):
+ if visible is not None:
+ info.bVisible = visible
+ if size is not None:
+ info.dwSize = size
+ self.SetConsoleCursorInfo(self.hout, byref(info))
+
+ def bell(self):
+ self.write('\007')
+
+ def next_serial(self):
+ '''Get next event serial number.'''
+ self.serial += 1
+ return self.serial
+
+# add the functions from the dll to the class
+for func in funcs:
+ setattr(Console, func, getattr(windll.kernel32, func))
+
+if sys.version_info[:2] < (2, 6):
+ msvcrt = cdll.msvcrt
+else:
+ msvcrt = cdll.LoadLibrary(ctypes.util.find_msvcrt())
+
+_strncpy = msvcrt.strncpy
+if sys.version [:1] == 2: #Bad fix for crash on python3
+ _strncpy.restype = c_char_p
+ _strncpy.argtypes = [c_char_p, c_char_p, c_size_t]
+_strdup = msvcrt._strdup
+_strdup.restype = c_char_p
+_strdup.argtypes = [c_char_p]
+
+LPVOID = c_void_p
+LPCVOID = c_void_p
+FARPROC = c_void_p
+LPDWORD = POINTER(DWORD)
+
+Console.AllocConsole.restype = BOOL
+Console.AllocConsole.argtypes = [] #void
+Console.CreateConsoleScreenBuffer.restype = HANDLE
+Console.CreateConsoleScreenBuffer.argtypes = [DWORD, DWORD, c_void_p, DWORD, LPVOID] #DWORD, DWORD, SECURITY_ATTRIBUTES*, DWORD, LPVOID
+Console.FillConsoleOutputAttribute.restype = BOOL
+Console.FillConsoleOutputAttribute.argtypes = [HANDLE, WORD, DWORD, c_int, LPDWORD] #HANDLE, WORD, DWORD, COORD, LPDWORD
+Console.FillConsoleOutputCharacterW.restype = BOOL
+Console.FillConsoleOutputCharacterW.argtypes = [HANDLE, c_ushort, DWORD, c_int, LPDWORD] #HANDLE, TCHAR, DWORD, COORD, LPDWORD
+Console.FreeConsole.restype = BOOL
+Console.FreeConsole.argtypes = [] #void
+Console.GetConsoleCursorInfo.restype = BOOL
+Console.GetConsoleCursorInfo.argtypes = [HANDLE, c_void_p] #HANDLE, PCONSOLE_CURSOR_INFO
+Console.GetConsoleMode.restype = BOOL
+Console.GetConsoleMode.argtypes = [HANDLE, LPDWORD] #HANDLE, LPDWORD
+Console.GetConsoleScreenBufferInfo.restype = BOOL
+Console.GetConsoleScreenBufferInfo.argtypes = [HANDLE, c_void_p] #HANDLE, PCONSOLE_SCREEN_BUFFER_INFO
+Console.GetConsoleTitleW.restype = DWORD
+Console.GetConsoleTitleW.argtypes = [c_wchar_p, DWORD] #LPTSTR , DWORD
+Console.GetProcAddress.restype = FARPROC
+Console.GetProcAddress.argtypes = [HMODULE, c_char_p] #HMODULE , LPCSTR
+Console.GetStdHandle.restype = HANDLE
+Console.GetStdHandle.argtypes = [DWORD]
+Console.PeekConsoleInputW.restype = BOOL
+Console.PeekConsoleInputW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD] #HANDLE, PINPUT_RECORD, DWORD, LPDWORD
+Console.ReadConsoleInputW.restype = BOOL
+Console.ReadConsoleInputW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD] #HANDLE, PINPUT_RECORD, DWORD, LPDWORD
+Console.ScrollConsoleScreenBufferW.restype = BOOL
+Console.ScrollConsoleScreenBufferW.argtypes = [HANDLE, c_void_p, c_void_p, c_int, c_void_p] #HANDLE, SMALL_RECT*, SMALL_RECT*, COORD, LPDWORD
+Console.SetConsoleActiveScreenBuffer.restype = BOOL
+Console.SetConsoleActiveScreenBuffer.argtypes = [HANDLE] #HANDLE
+Console.SetConsoleCursorInfo.restype = BOOL
+Console.SetConsoleCursorInfo.argtypes = [HANDLE, c_void_p] #HANDLE, CONSOLE_CURSOR_INFO*
+Console.SetConsoleCursorPosition.restype = BOOL
+Console.SetConsoleCursorPosition.argtypes = [HANDLE, c_int] #HANDLE, COORD
+Console.SetConsoleMode.restype = BOOL
+Console.SetConsoleMode.argtypes = [HANDLE, DWORD] #HANDLE, DWORD
+Console.SetConsoleScreenBufferSize.restype = BOOL
+Console.SetConsoleScreenBufferSize.argtypes = [HANDLE, c_int] #HANDLE, COORD
+Console.SetConsoleTextAttribute.restype = BOOL
+Console.SetConsoleTextAttribute.argtypes = [HANDLE, WORD] #HANDLE, WORD
+Console.SetConsoleTitleW.restype = BOOL
+Console.SetConsoleTitleW.argtypes = [c_wchar_p] #LPCTSTR
+Console.SetConsoleWindowInfo.restype = BOOL
+Console.SetConsoleWindowInfo.argtypes = [HANDLE, BOOL, c_void_p] #HANDLE, BOOL, SMALL_RECT*
+Console.WriteConsoleW.restype = BOOL
+Console.WriteConsoleW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD, LPVOID] #HANDLE, VOID*, DWORD, LPDWORD, LPVOID
+Console.WriteConsoleOutputCharacterW.restype = BOOL
+Console.WriteConsoleOutputCharacterW.argtypes = [HANDLE, c_wchar_p, DWORD, c_int, LPDWORD] #HANDLE, LPCTSTR, DWORD, COORD, LPDWORD
+Console.WriteFile.restype = BOOL
+Console.WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, c_void_p] #HANDLE, LPCVOID , DWORD, LPDWORD , LPOVERLAPPED
+
+
+
+from .event import Event
+
+VkKeyScan = windll.user32.VkKeyScanA
+
+
+class event(Event):
+ '''Represent events from the console.'''
+ def __init__(self, console, input):
+ '''Initialize an event from the Windows input structure.'''
+ self.type = '??'
+ self.serial = console.next_serial()
+ self.width = 0
+ self.height = 0
+ self.x = 0
+ self.y = 0
+ self.char = ''
+ self.keycode = 0
+ self.keysym = '??'
+ self.keyinfo = None # a tuple with (control, meta, shift, keycode) for dispatch
+ self.width = None
+
+ if input.EventType == KEY_EVENT:
+ if input.Event.KeyEvent.bKeyDown:
+ self.type = "KeyPress"
+ else:
+ self.type = "KeyRelease"
+ self.char = input.Event.KeyEvent.uChar.UnicodeChar
+ self.keycode = input.Event.KeyEvent.wVirtualKeyCode
+ self.state = input.Event.KeyEvent.dwControlKeyState
+ self.keyinfo = make_KeyPress(self.char,self.state,self.keycode)
+
+ elif input.EventType == MOUSE_EVENT:
+ if input.Event.MouseEvent.dwEventFlags & MOUSE_MOVED:
+ self.type = "Motion"
+ else:
+ self.type = "Button"
+ self.x = input.Event.MouseEvent.dwMousePosition.X
+ self.y = input.Event.MouseEvent.dwMousePosition.Y
+ self.state = input.Event.MouseEvent.dwButtonState
+ elif input.EventType == WINDOW_BUFFER_SIZE_EVENT:
+ self.type = "Configure"
+ self.width = input.Event.WindowBufferSizeEvent.dwSize.X
+ self.height = input.Event.WindowBufferSizeEvent.dwSize.Y
+ elif input.EventType == FOCUS_EVENT:
+ if input.Event.FocusEvent.bSetFocus:
+ self.type = "FocusIn"
+ else:
+ self.type = "FocusOut"
+ elif input.EventType == MENU_EVENT:
+ self.type = "Menu"
+ self.state = input.Event.MenuEvent.dwCommandId
+
+
+def getconsole(buffer=1):
+ """Get a console handle.
+
+ If buffer is non-zero, a new console buffer is allocated and
+ installed. Otherwise, this returns a handle to the current
+ console buffer"""
+
+ c = Console(buffer)
+
+ return c
+
+# The following code uses ctypes to allow a Python callable to
+# substitute for GNU readline within the Python interpreter. Calling
+# raw_input or other functions that do input, inside your callable
+# might be a bad idea, then again, it might work.
+
+# The Python callable can raise EOFError or KeyboardInterrupt and
+# these will be translated into the appropriate outputs from readline
+# so that they will then be translated back!
+
+# If the Python callable raises any other exception, a traceback will
+# be printed and readline will appear to return an empty line.
+
+# I use ctypes to create a C-callable from a Python wrapper that
+# handles the exceptions and gets the result into the right form.
+
+# the type for our C-callable wrapper
+HOOKFUNC22 = CFUNCTYPE(c_char_p, c_char_p)
+HOOKFUNC23 = CFUNCTYPE(c_char_p, c_void_p, c_void_p, c_char_p)
+
+readline_hook = None # the python hook goes here
+readline_ref = None # reference to the c-callable to keep it alive
+
+def hook_wrapper_23(stdin, stdout, prompt):
+ '''Wrap a Python readline so it behaves like GNU readline.'''
+ try:
+ # call the Python hook
+ res = ensure_str(readline_hook(prompt))
+ # make sure it returned the right sort of thing
+ if res and not isinstance(res, bytes):
+ raise TypeError('readline must return a string.')
+ except KeyboardInterrupt:
+ # GNU readline returns 0 on keyboard interrupt
+ return 0
+ except EOFError:
+ # It returns an empty string on EOF
+ res = ''
+ except:
+ print('Readline internal error', file=sys.stderr)
+ traceback.print_exc()
+ res = '\n'
+ # we have to make a copy because the caller expects to free the result
+ n = len(res)
+ p = Console.PyMem_Malloc(n + 1)
+ _strncpy(cast(p, c_char_p), res, n + 1)
+ return p
+
+def hook_wrapper(prompt):
+ '''Wrap a Python readline so it behaves like GNU readline.'''
+ try:
+ # call the Python hook
+ res = ensure_str(readline_hook(prompt))
+ # make sure it returned the right sort of thing
+ if res and not isinstance(res, bytes):
+ raise TypeError('readline must return a string.')
+ except KeyboardInterrupt:
+ # GNU readline returns 0 on keyboard interrupt
+ return 0
+ except EOFError:
+ # It returns an empty string on EOF
+ res = ''
+ except:
+ print('Readline internal error', file=sys.stderr)
+ traceback.print_exc()
+ res = '\n'
+ # we have to make a copy because the caller expects to free the result
+ p = _strdup(res)
+ return p
+
+def install_readline(hook):
+ '''Set up things for the interpreter to call
+ our function like GNU readline.'''
+ global readline_hook, readline_ref
+ # save the hook so the wrapper can call it
+ readline_hook = hook
+ # get the address of PyOS_ReadlineFunctionPointer so we can update it
+ PyOS_RFP = c_void_p.from_address(Console.GetProcAddress(sys.dllhandle,
+ "PyOS_ReadlineFunctionPointer".encode('ascii')))
+ # save a reference to the generated C-callable so it doesn't go away
+ if sys.version < '2.3':
+ readline_ref = HOOKFUNC22(hook_wrapper)
+ else:
+ readline_ref = HOOKFUNC23(hook_wrapper_23)
+ # get the address of the function
+ func_start = c_void_p.from_address(addressof(readline_ref)).value
+ # write the function address into PyOS_ReadlineFunctionPointer
+ PyOS_RFP.value = func_start
+
+if __name__ == '__main__':
+ import time, sys
+
+
+ def p(char):
+ return chr(VkKeyScan(ord(char)) & 0xff)
+
+ c = Console(0)
+ sys.stdout = c
+ sys.stderr = c
+ c.page()
+ print(p("d"), p("D"))
+ c.pos(5, 10)
+ c.write('hi there')
+ print('some printed output')
+ for i in range(10):
+ q = c.getkeypress()
+ print(q)
+ del c
diff --git a/pyreadline/console/console_attributes.py b/pyreadline/console/console_attributes.py
new file mode 100644
index 0000000..9262331
--- /dev/null
+++ b/pyreadline/console/console_attributes.py
@@ -0,0 +1,16 @@
+
+FOREGROUND_BLUE = 0x0001
+FOREGROUND_GREEN = 0x0002
+FOREGROUND_RED = 0x0004
+FOREGROUND_INTENSITY = 0x0008
+BACKGROUND_BLUE = 0x0010
+BACKGROUND_GREEN = 0x0020
+BACKGROUND_RED = 0x0040
+BACKGROUND_INTENSITY = 0x0080
+COMMON_LVB_LEADING_BYTE = 0x0100
+COMMON_LVB_TRAILING_BYTE = 0x0200
+COMMON_LVB_GRID_HORIZONTAL= 0x0400
+COMMON_LVB_GRID_LVERTICAL = 0x0800
+COMMON_LVB_GRID_RVERTICAL = 0x1000
+COMMON_LVB_REVERSE_VIDEO = 0x2000
+COMMON_LVB_UNDERSCORE = 0x4000
diff --git a/pyreadline/console/consolebase.py b/pyreadline/console/consolebase.py
new file mode 100644
index 0000000..79afd24
--- /dev/null
+++ b/pyreadline/console/consolebase.py
@@ -0,0 +1,52 @@
+class baseconsole:
+ def __init__(self):
+ pass
+
+ def bell(self):
+ raise NotImplementedError
+
+ def pos(self, x=None, y=None):
+ '''Move or query the window cursor.'''
+ raise NotImplementedError
+
+ def size(self):
+ raise NotImplementedError
+
+ def rectangle(self, rect, attr=None, fill=' '):
+ '''Fill Rectangle.'''
+ raise NotImplementedError
+
+ def write_scrolling(self, text, attr=None):
+ '''write text at current cursor position while watching for scrolling.
+
+ If the window scrolls because you are at the bottom of the screen
+ buffer, all positions that you are storing will be shifted by the
+ scroll amount. For example, I remember the cursor position of the
+ prompt so that I can redraw the line but if the window scrolls,
+ the remembered position is off.
+
+ This variant of write tries to keep track of the cursor position
+ so that it will know when the screen buffer is scrolled. It
+ returns the number of lines that the buffer scrolled.
+
+ '''
+ raise NotImplementedError
+
+ def getkeypress(self):
+ '''Return next key press event from the queue, ignoring others.'''
+ raise NotImplementedError
+
+ def write(self, text):
+ raise NotImplementedError
+
+ def page(self, attr=None, fill=' '):
+ '''Fill the entire screen.'''
+ raise NotImplementedError
+
+ def isatty(self):
+ return True
+
+ def flush(self):
+ pass
+
+
\ No newline at end of file
diff --git a/pyreadline/console/event.py b/pyreadline/console/event.py
new file mode 100644
index 0000000..05f7232
--- /dev/null
+++ b/pyreadline/console/event.py
@@ -0,0 +1,29 @@
+class Event(object):
+ '''Represent events from the console.'''
+ def __init__(self, console, input):
+ pass
+
+ def __repr__(self):
+ '''Display an event for debugging.'''
+ if self.type in ['KeyPress', 'KeyRelease']:
+ chr = self.char
+ if ord(chr)<ord("A"):
+ chr = "?"
+ s = "%s char='%s'%d keysym='%s' keycode=%d:%x state=%x keyinfo=%s" % \
+ (self.type, chr, ord(self.char), self.keysym, self.keycode, self.keycode,
+ self.state, self.keyinfo)
+ elif self.type in ['Motion', 'Button']:
+ s = '%s x=%d y=%d state=%x' % (self.type, self.x, self.y, self.state)
+ elif self.type == 'Configure':
+ s = '%s w=%d h=%d' % (self.type, self.width, self.height)
+ elif self.type in ['FocusIn', 'FocusOut']:
+ s = self.type
+ elif self.type == 'Menu':
+ s = '%s state=%x' % (self.type, self.state)
+ else:
+ s = 'unknown event type'
+ return s
+
+
+# def __str__(self):
+# return "('%s',%s,%s,%s)"%(self.char,self.key,self.state,self.keyinfo)
\ No newline at end of file
diff --git a/pyreadline/console/ironpython_console.py b/pyreadline/console/ironpython_console.py
new file mode 100644
index 0000000..25b5d3e
--- /dev/null
+++ b/pyreadline/console/ironpython_console.py
@@ -0,0 +1,424 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+'''Cursor control and color for the .NET console.
+'''
+
+#
+# Ironpython requires a patch to work do:
+#
+# In file PythonCommandLine.cs patch line:
+# class PythonCommandLine
+# {
+
+# to:
+# public class PythonCommandLine
+# {
+#
+#
+#
+# primitive debug printing that won't interfere with the screen
+
+import clr,sys
+clr.AddReferenceToFileAndPath(sys.executable)
+import IronPythonConsole
+
+import sys
+import re
+import os
+
+import System
+
+from .event import Event
+from pyreadline.logger import log
+
+from pyreadline.keysyms import \
+ make_keysym, make_keyinfo, make_KeyPress, make_KeyPress_from_keydescr
+from pyreadline.console.ansi import AnsiState
+color = System.ConsoleColor
+
+ansicolor={"0;30": color.Black,
+ "0;31": color.DarkRed,
+ "0;32": color.DarkGreen,
+ "0;33": color.DarkYellow,
+ "0;34": color.DarkBlue,
+ "0;35": color.DarkMagenta,
+ "0;36": color.DarkCyan,
+ "0;37": color.DarkGray,
+ "1;30": color.Gray,
+ "1;31": color.Red,
+ "1;32": color.Green,
+ "1;33": color.Yellow,
+ "1;34": color.Blue,
+ "1;35": color.Magenta,
+ "1;36": color.Cyan,
+ "1;37": color.White
+ }
+
+winattr = {"black" : 0, "darkgray" : 0+8,
+ "darkred" : 4, "red" : 4+8,
+ "darkgreen" : 2, "green" : 2+8,
+ "darkyellow" : 6, "yellow" : 6+8,
+ "darkblue" : 1, "blue" : 1+8,
+ "darkmagenta" : 5, "magenta" : 5+8,
+ "darkcyan" : 3, "cyan" : 3+8,
+ "gray" : 7, "white" : 7+8}
+
+class Console(object):
+ '''Console driver for Windows.
+
+ '''
+
+ def __init__(self, newbuffer=0):
+ '''Initialize the Console object.
+
+ newbuffer=1 will allocate a new buffer so the old content will be restored
+ on exit.
+ '''
+ self.serial = 0
+ self.attr = System.Console.ForegroundColor
+ self.saveattr = winattr[str(System.Console.ForegroundColor).lower()]
+ self.savebg = System.Console.BackgroundColor
+ log('initial attr=%s' % self.attr)
+
+ def _get(self):
+ top = System.Console.WindowTop
+ log("WindowTop:%s"%top)
+ return top
+
+ def _set(self, value):
+ top = System.Console.WindowTop
+ log("Set WindowTop:old:%s,new:%s"%(top, value))
+
+ WindowTop = property(_get, _set)
+ del _get, _set
+
+ def __del__(self):
+ '''Cleanup the console when finished.'''
+ # I don't think this ever gets called
+ pass
+
+ def pos(self, x=None, y=None):
+ '''Move or query the window cursor.'''
+ if x is not None:
+ System.Console.CursorLeft=x
+ else:
+ x = System.Console.CursorLeft
+ if y is not None:
+ System.Console.CursorTop=y
+ else:
+ y = System.Console.CursorTop
+ return x, y
+
+ def home(self):
+ '''Move to home.'''
+ self.pos(0, 0)
+
+# Map ANSI color escape sequences into Windows Console Attributes
+
+ terminal_escape = re.compile('(\001?\033\\[[0-9;]*m\002?)')
+ escape_parts = re.compile('\001?\033\\[([0-9;]*)m\002?')
+
+ # This pattern should match all characters that change the cursor position differently
+ # than a normal character.
+ motion_char_re = re.compile('([\n\r\t\010\007])')
+
+ def write_scrolling(self, text, attr=None):
+ '''write text at current cursor position while watching for scrolling.
+
+ If the window scrolls because you are at the bottom of the screen
+ buffer, all positions that you are storing will be shifted by the
+ scroll amount. For example, I remember the cursor position of the
+ prompt so that I can redraw the line but if the window scrolls,
+ the remembered position is off.
+
+ This variant of write tries to keep track of the cursor position
+ so that it will know when the screen buffer is scrolled. It
+ returns the number of lines that the buffer scrolled.
+
+ '''
+ x, y = self.pos()
+ w, h = self.size()
+ scroll = 0 # the result
+
+ # split the string into ordinary characters and funny characters
+ chunks = self.motion_char_re.split(text)
+ for chunk in chunks:
+ n = self.write_color(chunk, attr)
+ if len(chunk) == 1: # the funny characters will be alone
+ if chunk[0] == '\n': # newline
+ x = 0
+ y += 1
+ elif chunk[0] == '\r': # carriage return
+ x = 0
+ elif chunk[0] == '\t': # tab
+ x = 8 * (int(x / 8) + 1)
+ if x > w: # newline
+ x -= w
+ y += 1
+ elif chunk[0] == '\007': # bell
+ pass
+ elif chunk[0] == '\010':
+ x -= 1
+ if x < 0:
+ y -= 1 # backed up 1 line
+ else: # ordinary character
+ x += 1
+ if x == w: # wrap
+ x = 0
+ y += 1
+ if y == h: # scroll
+ scroll += 1
+ y = h - 1
+ else: # chunk of ordinary characters
+ x += n
+ l = int(x / w) # lines we advanced
+ x = x % w # new x value
+ y += l
+ if y >= h: # scroll
+ scroll += y - h + 1
+ y = h - 1
+ return scroll
+
+ trtable = {0 : color.Black, 4 : color.DarkRed, 2 : color.DarkGreen,
+ 6 : color.DarkYellow, 1 : color.DarkBlue, 5 : color.DarkMagenta,
+ 3 : color.DarkCyan, 7 : color.Gray, 8 : color.DarkGray,
+ 4+8 : color.Red, 2+8 : color.Green, 6+8 : color.Yellow,
+ 1+8 : color.Blue, 5+8 : color.Magenta,3+8 : color.Cyan,
+ 7+8 : color.White}
+
+ def write_color(self, text, attr=None):
+ '''write text at current cursor position and interpret color escapes.
+
+ return the number of characters written.
+ '''
+ log('write_color("%s", %s)' % (text, attr))
+ chunks = self.terminal_escape.split(text)
+ log('chunks=%s' % repr(chunks))
+ bg = self.savebg
+ n = 0 # count the characters we actually write, omitting the escapes
+ if attr is None:#use attribute from initial console
+ attr = self.attr
+ try:
+ fg = self.trtable[(0x000f&attr)]
+ bg = self.trtable[(0x00f0&attr)>>4]
+ except TypeError:
+ fg = attr
+
+ for chunk in chunks:
+ m = self.escape_parts.match(chunk)
+ if m:
+ log(m.group(1))
+ attr = ansicolor.get(m.group(1), self.attr)
+ n += len(chunk)
+ System.Console.ForegroundColor = fg
+ System.Console.BackgroundColor = bg
+ System.Console.Write(chunk)
+ return n
+
+ def write_plain(self, text, attr=None):
+ '''write text at current cursor position.'''
+ log('write("%s", %s)' %(text, attr))
+ if attr is None:
+ attr = self.attr
+ n = c_int(0)
+ self.SetConsoleTextAttribute(self.hout, attr)
+ self.WriteConsoleA(self.hout, text, len(text), byref(n), None)
+ return len(text)
+
+ if "EMACS" in os.environ:
+ def write_color(self, text, attr=None):
+ junk = c_int(0)
+ self.WriteFile(self.hout, text, len(text), byref(junk), None)
+ return len(text)
+ write_plain = write_color
+
+ # make this class look like a file object
+ def write(self, text):
+ log('write("%s")' % text)
+ return self.write_color(text)
+
+ #write = write_scrolling
+
+ def isatty(self):
+ return True
+
+ def flush(self):
+ pass
+
+ def page(self, attr=None, fill=' '):
+ '''Fill the entire screen.'''
+ System.Console.Clear()
+
+ def text(self, x, y, text, attr=None):
+ '''Write text at the given position.'''
+ self.pos(x, y)
+ self.write_color(text, attr)
+
+ def clear_to_end_of_window(self):
+ oldtop = self.WindowTop
+ lastline = self.WindowTop+System.Console.WindowHeight
+ pos = self.pos()
+ w, h = self.size()
+ length = w - pos[0] + min((lastline - pos[1] - 1), 5) * w - 1
+ self.write_color(length * " ")
+ self.pos(*pos)
+ self.WindowTop = oldtop
+
+ def rectangle(self, rect, attr=None, fill=' '):
+ '''Fill Rectangle.'''
+ oldtop = self.WindowTop
+ oldpos = self.pos()
+ #raise NotImplementedError
+ x0, y0, x1, y1 = rect
+ if attr is None:
+ attr = self.attr
+ if fill:
+ rowfill = fill[:1] * abs(x1 - x0)
+ else:
+ rowfill = ' ' * abs(x1 - x0)
+ for y in range(y0, y1):
+ System.Console.SetCursorPosition(x0, y)
+ self.write_color(rowfill, attr)
+ self.pos(*oldpos)
+
+ def scroll(self, rect, dx, dy, attr=None, fill=' '):
+ '''Scroll a rectangle.'''
+ raise NotImplementedError
+
+ def scroll_window(self, lines):
+ '''Scroll the window by the indicated number of lines.'''
+ top = self.WindowTop + lines
+ if top < 0:
+ top = 0
+ if top + System.Console.WindowHeight > System.Console.BufferHeight:
+ top = System.Console.BufferHeight
+ self.WindowTop = top
+
+ def getkeypress(self):
+ '''Return next key press event from the queue, ignoring others.'''
+ ck = System.ConsoleKey
+ while 1:
+ e = System.Console.ReadKey(True)
+ if e.Key == System.ConsoleKey.PageDown: #PageDown
+ self.scroll_window(12)
+ elif e.Key == System.ConsoleKey.PageUp:#PageUp
+ self.scroll_window(-12)
+ elif str(e.KeyChar) == "\000":#Drop deadkeys
+ log("Deadkey: %s"%e)
+ return event(self, e)
+ else:
+ return event(self, e)
+
+ def title(self, txt=None):
+ '''Set/get title.'''
+ if txt:
+ System.Console.Title = txt
+ else:
+ return System.Console.Title
+
+ def size(self, width=None, height=None):
+ '''Set/get window size.'''
+ sc = System.Console
+ if width is not None and height is not None:
+ sc.BufferWidth, sc.BufferHeight = width,height
+ else:
+ return sc.BufferWidth, sc.BufferHeight
+
+ if width is not None and height is not None:
+ sc.WindowWidth, sc.WindowHeight = width,height
+ else:
+ return sc.WindowWidth - 1, sc.WindowHeight - 1
+
+ def cursor(self, visible=True, size=None):
+ '''Set cursor on or off.'''
+ System.Console.CursorVisible = visible
+
+ def bell(self):
+ System.Console.Beep()
+
+ def next_serial(self):
+ '''Get next event serial number.'''
+ self.serial += 1
+ return self.serial
+
+class event(Event):
+ '''Represent events from the console.'''
+ def __init__(self, console, input):
+ '''Initialize an event from the Windows input structure.'''
+ self.type = '??'
+ self.serial = console.next_serial()
+ self.width = 0
+ self.height = 0
+ self.x = 0
+ self.y = 0
+ self.char = str(input.KeyChar)
+ self.keycode = input.Key
+ self.state = input.Modifiers
+ log("%s,%s,%s"%(input.Modifiers, input.Key, input.KeyChar))
+ self.type = "KeyRelease"
+ self.keysym = make_keysym(self.keycode)
+ self.keyinfo = make_KeyPress(self.char, self.state, self.keycode)
+
+def make_event_from_keydescr(keydescr):
+ def input():
+ return 1
+ input.KeyChar = "a"
+ input.Key = System.ConsoleKey.A
+ input.Modifiers = System.ConsoleModifiers.Shift
+ input.next_serial = input
+ e = event(input,input)
+ del input.next_serial
+ keyinfo = make_KeyPress_from_keydescr(keydescr)
+ e.keyinfo = keyinfo
+ return e
+
+CTRL_C_EVENT=make_event_from_keydescr("Control-c")
+
+def install_readline(hook):
+ def hook_wrap():
+ try:
+ res = hook()
+ except KeyboardInterrupt as x: #this exception does not seem to be caught
+ res = ""
+ except EOFError:
+ return None
+ if res[-1:] == "\n":
+ return res[:-1]
+ else:
+ return res
+ class IronPythonWrapper(IronPythonConsole.IConsole):
+ def ReadLine(self, autoIndentSize):
+ return hook_wrap()
+ def Write(self, text, style):
+ System.Console.Write(text)
+ def WriteLine(self, text, style):
+ System.Console.WriteLine(text)
+ IronPythonConsole.PythonCommandLine.MyConsole = IronPythonWrapper()
+
+
+
+if __name__ == '__main__':
+ import time, sys
+ c = Console(0)
+ sys.stdout = c
+ sys.stderr = c
+ c.page()
+ c.pos(5, 10)
+ c.write('hi there')
+ c.title("Testing console")
+# c.bell()
+ print()
+ print("size", c.size())
+ print(' some printed output')
+ for i in range(10):
+ e = c.getkeypress()
+ print(e.Key, chr(e.KeyChar), ord(e.KeyChar), e.Modifiers)
+ del c
+
+ System.Console.Clear()
diff --git a/pyreadline/error.py b/pyreadline/error.py
new file mode 100644
index 0000000..be2c24e
--- /dev/null
+++ b/pyreadline/error.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+
+
+class ReadlineError(Exception):
+ pass
+
+class GetSetError(ReadlineError):
+ pass
diff --git a/pyreadline/get_doc.py b/pyreadline/get_doc.py
new file mode 100644
index 0000000..2c5cccb
--- /dev/null
+++ b/pyreadline/get_doc.py
@@ -0,0 +1,19 @@
+import sys,textwrap
+import collections
+
+rlmain = sys.modules["pyreadline.rlmain"]
+rl = rlmain.rl
+
+def get_doc(rl):
+ methods = [(x, getattr(rl, x)) for x in dir(rl) if isinstance(getattr(rl, x), collections.Callable)]
+ return [ (x, m.__doc__ )for x, m in methods if m.__doc__]
+
+
+def get_rest(rl):
+ q = get_doc(rl)
+ out = []
+ for funcname, doc in q:
+ out.append(funcname)
+ out.append("\n".join(textwrap.wrap(doc, 80, initial_indent=" ")))
+ out.append("")
+ return out
\ No newline at end of file
diff --git a/pyreadline/keysyms/__init__.py b/pyreadline/keysyms/__init__.py
new file mode 100644
index 0000000..062414a
--- /dev/null
+++ b/pyreadline/keysyms/__init__.py
@@ -0,0 +1,20 @@
+import sys
+
+success = False
+in_ironpython = "IronPython" in sys.version
+
+if in_ironpython:
+ try:
+ from .ironpython_keysyms import *
+ success = True
+ except ImportError as x:
+ raise
+else:
+ try:
+ from .keysyms import *
+ success = True
+ except ImportError as x:
+ pass
+
+if not success:
+ raise ImportError("Could not import keysym for local pythonversion", x)
\ No newline at end of file
diff --git a/pyreadline/keysyms/common.py b/pyreadline/keysyms/common.py
new file mode 100644
index 0000000..63a9977
--- /dev/null
+++ b/pyreadline/keysyms/common.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+# table for translating virtual keys to X windows key symbols
+
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+from pyreadline.unicode_helper import ensure_unicode
+
+validkey =set(['cancel', 'backspace', 'tab', 'clear',
+ 'return', 'shift_l', 'control_l', 'alt_l',
+ 'pause', 'caps_lock', 'escape', 'space',
+ 'prior', 'next', 'end', 'home',
+ 'left', 'up', 'right', 'down',
+ 'select', 'print', 'execute', 'snapshot',
+ 'insert', 'delete', 'help', 'f1',
+ 'f2', 'f3', 'f4', 'f5',
+ 'f6', 'f7', 'f8', 'f9',
+ 'f10', 'f11', 'f12', 'f13',
+ 'f14', 'f15', 'f16', 'f17',
+ 'f18', 'f19', 'f20', 'f21',
+ 'f22', 'f23', 'f24', 'num_lock',
+ 'scroll_lock', 'vk_apps', 'vk_processkey','vk_attn',
+ 'vk_crsel', 'vk_exsel', 'vk_ereof', 'vk_play',
+ 'vk_zoom', 'vk_noname', 'vk_pa1', 'vk_oem_clear',
+ 'numpad0', 'numpad1', 'numpad2', 'numpad3',
+ 'numpad4', 'numpad5', 'numpad6', 'numpad7',
+ 'numpad8', 'numpad9', 'divide', 'multiply',
+ 'add', 'subtract', 'vk_decimal'])
+
+escape_sequence_to_special_key = {"\\e[a" : "up", "\\e[b" : "down", "del" : "delete"}
+
+class KeyPress(object):
+ def __init__(self, char="", shift=False, control=False, meta=False, keyname=""):
+ if control or meta or shift:
+ char = char.upper()
+ self.info = dict(char=char,
+ shift=shift,
+ control=control,
+ meta=meta,
+ keyname=keyname)
+
+ def create(name):
+ def get(self):
+ return self.info[name]
+
+ def set(self, value):
+ self.info[name] = value
+ return property(get, set)
+ char = create("char")
+ shift = create("shift")
+ control = create("control")
+ meta = create("meta")
+ keyname = create("keyname")
+
+ def __repr__(self):
+ return "(%s,%s,%s,%s)"%tuple(map(ensure_unicode, self.tuple()))
+
+ def tuple(self):
+ if self.keyname:
+ return (self.control, self.meta, self.shift, self.keyname)
+ else:
+ if self.control or self.meta or self.shift:
+ return (self.control, self.meta, self.shift, self.char.upper())
+ else:
+ return (self.control, self.meta, self.shift, self.char)
+
+ def __eq__(self, other):
+ if isinstance(other, KeyPress):
+ s = self.tuple()
+ o = other.tuple()
+ return s == o
+ else:
+ return False
+
+def make_KeyPress_from_keydescr(keydescr):
+ keyinfo = KeyPress()
+ if len(keydescr) > 2 and keydescr[:1] == '"' and keydescr[-1:] == '"':
+ keydescr = keydescr[1:-1]
+
+ while 1:
+ lkeyname = keydescr.lower()
+ if lkeyname.startswith('control-'):
+ keyinfo.control = True
+ keydescr = keydescr[8:]
+ elif lkeyname.startswith('ctrl-'):
+ keyinfo.control = True
+ keydescr = keydescr[5:]
+ elif keydescr.lower().startswith('\\c-'):
+ keyinfo.control = True
+ keydescr = keydescr[3:]
+ elif keydescr.lower().startswith('\\m-'):
+ keyinfo.meta = True
+ keydescr = keydescr[3:]
+ elif keydescr in escape_sequence_to_special_key:
+ keydescr = escape_sequence_to_special_key[keydescr]
+ elif lkeyname.startswith('meta-'):
+ keyinfo.meta = True
+ keydescr = keydescr[5:]
+ elif lkeyname.startswith('alt-'):
+ keyinfo.meta = True
+ keydescr = keydescr[4:]
+ elif lkeyname.startswith('shift-'):
+ keyinfo.shift = True
+ keydescr = keydescr[6:]
+ else:
+ if len(keydescr) > 1:
+ if keydescr.strip().lower() in validkey:
+ keyinfo.keyname = keydescr.strip().lower()
+ keyinfo.char = ""
+ else:
+ raise IndexError("Not a valid key: '%s'"%keydescr)
+ else:
+ keyinfo.char = keydescr
+ return keyinfo
+
+if __name__ == "__main__":
+ import startup
+
\ No newline at end of file
diff --git a/pyreadline/keysyms/ironpython_keysyms.py b/pyreadline/keysyms/ironpython_keysyms.py
new file mode 100644
index 0000000..4cdd67c
--- /dev/null
+++ b/pyreadline/keysyms/ironpython_keysyms.py
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import System
+from .common import validkey, KeyPress, make_KeyPress_from_keydescr
+
+c32 = System.ConsoleKey
+Shift = System.ConsoleModifiers.Shift
+Control = System.ConsoleModifiers.Control
+Alt = System.ConsoleModifiers.Alt
+# table for translating virtual keys to X windows key symbols
+code2sym_map = {#c32.CANCEL: u'Cancel',
+ c32.Backspace: 'BackSpace',
+ c32.Tab: 'Tab',
+ c32.Clear: 'Clear',
+ c32.Enter: 'Return',
+# c32.Shift: u'Shift_L',
+# c32.Control: u'Control_L',
+# c32.Menu: u'Alt_L',
+ c32.Pause: 'Pause',
+# c32.Capital: u'Caps_Lock',
+ c32.Escape: 'Escape',
+# c32.Space: u'space',
+ c32.PageUp: 'Prior',
+ c32.PageDown: 'Next',
+ c32.End: 'End',
+ c32.Home: 'Home',
+ c32.LeftArrow: 'Left',
+ c32.UpArrow: 'Up',
+ c32.RightArrow: 'Right',
+ c32.DownArrow: 'Down',
+ c32.Select: 'Select',
+ c32.Print: 'Print',
+ c32.Execute: 'Execute',
+# c32.Snapshot: u'Snapshot',
+ c32.Insert: 'Insert',
+ c32.Delete: 'Delete',
+ c32.Help: 'Help',
+ c32.F1: 'F1',
+ c32.F2: 'F2',
+ c32.F3: 'F3',
+ c32.F4: 'F4',
+ c32.F5: 'F5',
+ c32.F6: 'F6',
+ c32.F7: 'F7',
+ c32.F8: 'F8',
+ c32.F9: 'F9',
+ c32.F10: 'F10',
+ c32.F11: 'F11',
+ c32.F12: 'F12',
+ c32.F13: 'F13',
+ c32.F14: 'F14',
+ c32.F15: 'F15',
+ c32.F16: 'F16',
+ c32.F17: 'F17',
+ c32.F18: 'F18',
+ c32.F19: 'F19',
+ c32.F20: 'F20',
+ c32.F21: 'F21',
+ c32.F22: 'F22',
+ c32.F23: 'F23',
+ c32.F24: 'F24',
+# c32.Numlock: u'Num_Lock,',
+# c32.Scroll: u'Scroll_Lock',
+# c32.Apps: u'VK_APPS',
+# c32.ProcesskeY: u'VK_PROCESSKEY',
+# c32.Attn: u'VK_ATTN',
+# c32.Crsel: u'VK_CRSEL',
+# c32.Exsel: u'VK_EXSEL',
+# c32.Ereof: u'VK_EREOF',
+# c32.Play: u'VK_PLAY',
+# c32.Zoom: u'VK_ZOOM',
+# c32.Noname: u'VK_NONAME',
+# c32.Pa1: u'VK_PA1',
+ c32.OemClear: 'VK_OEM_CLEAR',
+ c32.NumPad0: 'NUMPAD0',
+ c32.NumPad1: 'NUMPAD1',
+ c32.NumPad2: 'NUMPAD2',
+ c32.NumPad3: 'NUMPAD3',
+ c32.NumPad4: 'NUMPAD4',
+ c32.NumPad5: 'NUMPAD5',
+ c32.NumPad6: 'NUMPAD6',
+ c32.NumPad7: 'NUMPAD7',
+ c32.NumPad8: 'NUMPAD8',
+ c32.NumPad9: 'NUMPAD9',
+ c32.Divide: 'Divide',
+ c32.Multiply: 'Multiply',
+ c32.Add: 'Add',
+ c32.Subtract: 'Subtract',
+ c32.Decimal: 'VK_DECIMAL'
+ }
+
+# function to handle the mapping
+def make_keysym(keycode):
+ try:
+ sym = code2sym_map[keycode]
+ except KeyError:
+ sym = ''
+ return sym
+
+sym2code_map = {}
+for code,sym in code2sym_map.items():
+ sym2code_map[sym.lower()] = code
+
+def key_text_to_keyinfo(keytext):
+ '''Convert a GNU readline style textual description of a key to keycode with modifiers'''
+ if keytext.startswith('"'): # "
+ return keyseq_to_keyinfo(keytext[1:-1])
+ else:
+ return keyname_to_keyinfo(keytext)
+
+
+def char_to_keyinfo(char, control=False, meta=False, shift=False):
+ vk = (ord(char))
+ if vk & 0xffff == 0xffff:
+ print('VkKeyScan("%s") = %x' % (char, vk))
+ raise ValueError('bad key')
+ if vk & 0x100:
+ shift = True
+ if vk & 0x200:
+ control = True
+ if vk & 0x400:
+ meta = True
+ return (control, meta, shift, vk & 0xff)
+
+def keyname_to_keyinfo(keyname):
+ control = False
+ meta = False
+ shift = False
+
+ while 1:
+ lkeyname = keyname.lower()
+ if lkeyname.startswith('control-'):
+ control = True
+ keyname = keyname[8:]
+ elif lkeyname.startswith('ctrl-'):
+ control = True
+ keyname = keyname[5:]
+ elif lkeyname.startswith('meta-'):
+ meta = True
+ keyname = keyname[5:]
+ elif lkeyname.startswith('alt-'):
+ meta = True
+ keyname = keyname[4:]
+ elif lkeyname.startswith('shift-'):
+ shift = True
+ keyname = keyname[6:]
+ else:
+ if len(keyname) > 1:
+ return (control, meta, shift, sym2code_map.get(keyname.lower()," "))
+ else:
+ return char_to_keyinfo(keyname, control, meta, shift)
+
+def keyseq_to_keyinfo(keyseq):
+ res = []
+ control = False
+ meta = False
+ shift = False
+
+ while 1:
+ if keyseq.startswith('\\C-'):
+ control = True
+ keyseq = keyseq[3:]
+ elif keyseq.startswith('\\M-'):
+ meta = True
+ keyseq = keyseq[3:]
+ elif keyseq.startswith('\\e'):
+ res.append(char_to_keyinfo('\033', control, meta, shift))
+ control = meta = shift = False
+ keyseq = keyseq[2:]
+ elif len(keyseq) >= 1:
+ res.append(char_to_keyinfo(keyseq[0], control, meta, shift))
+ control = meta = shift = False
+ keyseq = keyseq[1:]
+ else:
+ return res[0]
+
+def make_keyinfo(keycode, state):
+ control = False
+ meta =False
+ shift = False
+ return (control, meta, shift, keycode)
+
+
+def make_KeyPress(char, state, keycode):
+
+ shift = bool(int(state) & int(Shift))
+ control = bool(int(state) & int(Control))
+ meta = bool(int(state) & int(Alt))
+ keyname = code2sym_map.get(keycode, "").lower()
+ if control and meta: #equivalent to altgr so clear flags
+ control = False
+ meta = False
+ elif control:
+ char = str(keycode)
+ return KeyPress(char, shift, control, meta, keyname)
+
diff --git a/pyreadline/keysyms/keysyms.py b/pyreadline/keysyms/keysyms.py
new file mode 100644
index 0000000..33d971f
--- /dev/null
+++ b/pyreadline/keysyms/keysyms.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+from . import winconstants as c32
+from pyreadline.logger import log
+from ctypes import windll
+import ctypes
+# table for translating virtual keys to X windows key symbols
+
+from .common import validkey, KeyPress, make_KeyPress_from_keydescr
+
+code2sym_map = {c32.VK_CANCEL: 'cancel',
+ c32.VK_BACK: 'backspace',
+ c32.VK_TAB: 'tab',
+ c32.VK_CLEAR: 'clear',
+ c32.VK_RETURN: 'return',
+ c32.VK_SHIFT: 'shift_l',
+ c32.VK_CONTROL: 'control_l',
+ c32.VK_MENU: 'alt_l',
+ c32.VK_PAUSE: 'pause',
+ c32.VK_CAPITAL: 'caps_lock',
+ c32.VK_ESCAPE: 'escape',
+ c32.VK_SPACE: 'space',
+ c32.VK_PRIOR: 'prior',
+ c32.VK_NEXT: 'next',
+ c32.VK_END: 'end',
+ c32.VK_HOME: 'home',
+ c32.VK_LEFT: 'left',
+ c32.VK_UP: 'up',
+ c32.VK_RIGHT: 'right',
+ c32.VK_DOWN: 'down',
+ c32.VK_SELECT: 'select',
+ c32.VK_PRINT: 'print',
+ c32.VK_EXECUTE: 'execute',
+ c32.VK_SNAPSHOT: 'snapshot',
+ c32.VK_INSERT: 'insert',
+ c32.VK_DELETE: 'delete',
+ c32.VK_HELP: 'help',
+ c32.VK_F1: 'f1',
+ c32.VK_F2: 'f2',
+ c32.VK_F3: 'f3',
+ c32.VK_F4: 'f4',
+ c32.VK_F5: 'f5',
+ c32.VK_F6: 'f6',
+ c32.VK_F7: 'f7',
+ c32.VK_F8: 'f8',
+ c32.VK_F9: 'f9',
+ c32.VK_F10: 'f10',
+ c32.VK_F11: 'f11',
+ c32.VK_F12: 'f12',
+ c32.VK_F13: 'f13',
+ c32.VK_F14: 'f14',
+ c32.VK_F15: 'f15',
+ c32.VK_F16: 'f16',
+ c32.VK_F17: 'f17',
+ c32.VK_F18: 'f18',
+ c32.VK_F19: 'f19',
+ c32.VK_F20: 'f20',
+ c32.VK_F21: 'f21',
+ c32.VK_F22: 'f22',
+ c32.VK_F23: 'f23',
+ c32.VK_F24: 'f24',
+ c32.VK_NUMLOCK: 'num_lock,',
+ c32.VK_SCROLL: 'scroll_lock',
+ c32.VK_APPS: 'vk_apps',
+ c32.VK_PROCESSKEY: 'vk_processkey',
+ c32.VK_ATTN: 'vk_attn',
+ c32.VK_CRSEL: 'vk_crsel',
+ c32.VK_EXSEL: 'vk_exsel',
+ c32.VK_EREOF: 'vk_ereof',
+ c32.VK_PLAY: 'vk_play',
+ c32.VK_ZOOM: 'vk_zoom',
+ c32.VK_NONAME: 'vk_noname',
+ c32.VK_PA1: 'vk_pa1',
+ c32.VK_OEM_CLEAR: 'vk_oem_clear',
+ c32.VK_NUMPAD0: 'numpad0',
+ c32.VK_NUMPAD1: 'numpad1',
+ c32.VK_NUMPAD2: 'numpad2',
+ c32.VK_NUMPAD3: 'numpad3',
+ c32.VK_NUMPAD4: 'numpad4',
+ c32.VK_NUMPAD5: 'numpad5',
+ c32.VK_NUMPAD6: 'numpad6',
+ c32.VK_NUMPAD7: 'numpad7',
+ c32.VK_NUMPAD8: 'numpad8',
+ c32.VK_NUMPAD9: 'numpad9',
+ c32.VK_DIVIDE: 'divide',
+ c32.VK_MULTIPLY: 'multiply',
+ c32.VK_ADD: 'add',
+ c32.VK_SUBTRACT: 'subtract',
+ c32.VK_DECIMAL: 'vk_decimal'
+ }
+
+VkKeyScan = windll.user32.VkKeyScanA
+
+def char_to_keyinfo(char, control=False, meta=False, shift=False):
+ k=KeyPress()
+ vk = VkKeyScan(ord(char))
+ if vk & 0xffff == 0xffff:
+ print('VkKeyScan("%s") = %x' % (char, vk))
+ raise ValueError('bad key')
+ if vk & 0x100:
+ k.shift = True
+ if vk & 0x200:
+ k.control = True
+ if vk & 0x400:
+ k.meta = True
+ k.char=chr(vk & 0xff)
+ return k
+
+def make_KeyPress(char, state, keycode):
+ control = (state & (4+8)) != 0
+ meta = (state & (1+2)) != 0
+ shift = (state & 0x10) != 0
+ if control and not meta:#Matches ctrl- chords should pass keycode as char
+ char = chr(keycode)
+ elif control and meta: #Matches alt gr and should just pass on char
+ control = False
+ meta = False
+ try:
+ keyname=code2sym_map[keycode]
+ except KeyError:
+ keyname = ""
+ out = KeyPress(char, shift, control, meta, keyname)
+ return out
+
+if __name__=="__main__":
+ import startup
+
\ No newline at end of file
diff --git a/pyreadline/keysyms/winconstants.py b/pyreadline/keysyms/winconstants.py
new file mode 100644
index 0000000..5e0ed63
--- /dev/null
+++ b/pyreadline/keysyms/winconstants.py
@@ -0,0 +1,171 @@
+#This file contains constants that are normally found in win32all
+#But included here to avoid the dependency
+
+VK_LBUTTON=1
+VK_RBUTTON=2
+VK_CANCEL=3
+VK_MBUTTON=4
+VK_XBUTTON1=5
+VK_XBUTTON2=6
+VK_BACK=8
+VK_TAB=9
+VK_CLEAR=12
+VK_RETURN=13
+VK_SHIFT=16
+VK_CONTROL=17
+VK_MENU=18
+VK_PAUSE=19
+VK_CAPITAL=20
+VK_KANA=0x15
+VK_HANGEUL=0x15
+VK_HANGUL=0x15
+VK_JUNJA=0x17
+VK_FINAL=0x18
+VK_HANJA=0x19
+VK_KANJI=0x19
+VK_ESCAPE=0x1B
+VK_CONVERT=0x1C
+VK_NONCONVERT=0x1D
+VK_ACCEPT=0x1E
+VK_MODECHANGE=0x1F
+VK_SPACE=32
+VK_PRIOR=33
+VK_NEXT=34
+VK_END=35
+VK_HOME=36
+VK_LEFT=37
+VK_UP=38
+VK_RIGHT=39
+VK_DOWN=40
+VK_SELECT=41
+VK_PRINT=42
+VK_EXECUTE=43
+VK_SNAPSHOT=44
+VK_INSERT=45
+VK_DELETE=46
+VK_HELP=47
+VK_LWIN=0x5B
+VK_RWIN=0x5C
+VK_APPS=0x5D
+VK_SLEEP=0x5F
+VK_NUMPAD0=0x60
+VK_NUMPAD1=0x61
+VK_NUMPAD2=0x62
+VK_NUMPAD3=0x63
+VK_NUMPAD4=0x64
+VK_NUMPAD5=0x65
+VK_NUMPAD6=0x66
+VK_NUMPAD7=0x67
+VK_NUMPAD8=0x68
+VK_NUMPAD9=0x69
+VK_MULTIPLY=0x6A
+VK_ADD=0x6B
+VK_SEPARATOR=0x6C
+VK_SUBTRACT=0x6D
+VK_DECIMAL=0x6E
+VK_DIVIDE=0x6F
+VK_F1=0x70
+VK_F2=0x71
+VK_F3=0x72
+VK_F4=0x73
+VK_F5=0x74
+VK_F6=0x75
+VK_F7=0x76
+VK_F8=0x77
+VK_F9=0x78
+VK_F10=0x79
+VK_F11=0x7A
+VK_F12=0x7B
+VK_F13=0x7C
+VK_F14=0x7D
+VK_F15=0x7E
+VK_F16=0x7F
+VK_F17=0x80
+VK_F18=0x81
+VK_F19=0x82
+VK_F20=0x83
+VK_F21=0x84
+VK_F22=0x85
+VK_F23=0x86
+VK_F24=0x87
+VK_NUMLOCK=0x90
+VK_SCROLL=0x91
+VK_LSHIFT=0xA0
+VK_RSHIFT=0xA1
+VK_LCONTROL=0xA2
+VK_RCONTROL=0xA3
+VK_LMENU=0xA4
+VK_RMENU=0xA5
+VK_BROWSER_BACK=0xA6
+VK_BROWSER_FORWARD=0xA7
+VK_BROWSER_REFRESH=0xA8
+VK_BROWSER_STOP=0xA9
+VK_BROWSER_SEARCH=0xAA
+VK_BROWSER_FAVORITES=0xAB
+VK_BROWSER_HOME=0xAC
+VK_VOLUME_MUTE=0xAD
+VK_VOLUME_DOWN=0xAE
+VK_VOLUME_UP=0xAF
+VK_MEDIA_NEXT_TRACK=0xB0
+VK_MEDIA_PREV_TRACK=0xB1
+VK_MEDIA_STOP=0xB2
+VK_MEDIA_PLAY_PAUSE=0xB3
+VK_LAUNCH_MAIL=0xB4
+VK_LAUNCH_MEDIA_SELECT=0xB5
+VK_LAUNCH_APP1=0xB6
+VK_LAUNCH_APP2=0xB7
+VK_OEM_1=0xBA
+VK_OEM_PLUS=0xBB
+VK_OEM_COMMA=0xBC
+VK_OEM_MINUS=0xBD
+VK_OEM_PERIOD=0xBE
+VK_OEM_2=0xBF
+VK_OEM_3=0xC0
+VK_OEM_4=0xDB
+VK_OEM_5=0xDC
+VK_OEM_6=0xDD
+VK_OEM_7=0xDE
+VK_OEM_8=0xDF
+VK_OEM_102=0xE2
+VK_PROCESSKEY=0xE5
+VK_PACKET=0xE7
+VK_ATTN=0xF6
+VK_CRSEL=0xF7
+VK_EXSEL=0xF8
+VK_EREOF=0xF9
+VK_PLAY=0xFA
+VK_ZOOM=0xFB
+VK_NONAME=0xFC
+VK_PA1=0xFD
+VK_OEM_CLEAR=0xFE
+
+CF_TEXT=1
+CF_BITMAP=2
+CF_METAFILEPICT=3
+CF_SYLK=4
+CF_DIF=5
+CF_TIFF=6
+CF_OEMTEXT=7
+CF_DIB=8
+CF_PALETTE=9
+CF_PENDATA=10
+CF_RIFF=11
+CF_WAVE=12
+CF_UNICODETEXT=13
+CF_ENHMETAFILE=14
+CF_HDROP=15
+CF_LOCALE=16
+CF_MAX=17
+CF_OWNERDISPLAY=128
+CF_DSPTEXT=129
+CF_DSPBITMAP=130
+CF_DSPMETAFILEPICT=131
+CF_DSPENHMETAFILE=142
+CF_PRIVATEFIRST=512
+CF_PRIVATELAST=767
+CF_GDIOBJFIRST=768
+CF_GDIOBJLAST=1023
+
+
+GPTR=64
+GHND=66
diff --git a/pyreadline/lineeditor/__init__.py b/pyreadline/lineeditor/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pyreadline/lineeditor/history.py b/pyreadline/lineeditor/history.py
new file mode 100644
index 0000000..d3d4b71
--- /dev/null
+++ b/pyreadline/lineeditor/history.py
@@ -0,0 +1,262 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import re, operator,string, sys,os
+
+from pyreadline.unicode_helper import ensure_unicode, ensure_str
+if "pyreadline" in sys.modules:
+ pyreadline = sys.modules["pyreadline"]
+else:
+ import pyreadline
+
+from . import lineobj
+
+class EscapeHistory(Exception):
+ pass
+
+from pyreadline.logger import log
+
+
+class LineHistory(object):
+ def __init__(self):
+ self.history = []
+ self._history_length = 100
+ self._history_cursor = 0
+ self.history_filename = os.path.expanduser('~/.history') #Cannot expand unicode strings correctly on python2.4
+ self.lastcommand = None
+ self.query = ""
+ self.last_search_for = ""
+
+ def get_current_history_length(self):
+ '''Return the number of lines currently in the history.
+ (This is different from get_history_length(), which returns
+ the maximum number of lines that will be written to a history file.)'''
+ value = len(self.history)
+ log("get_current_history_length:%d"%value)
+ return value
+
+ def get_history_length(self):
+ '''Return the desired length of the history file. Negative values imply
+ unlimited history file size.'''
+ value = self._history_length
+ log("get_history_length:%d"%value)
+ return value
+
+ def get_history_item(self, index):
+ '''Return the current contents of history item at index (starts with index 1).'''
+ item = self.history[index - 1]
+ log("get_history_item: index:%d item:%r"%(index, item))
+ return item.get_line_text()
+
+ def set_history_length(self, value):
+ log("set_history_length: old:%d new:%d"%(self._history_length, value))
+ self._history_length = value
+
+ def get_history_cursor(self):
+ value = self._history_cursor
+ log("get_history_cursor:%d"%value)
+ return value
+
+ def set_history_cursor(self, value):
+ log("set_history_cursor: old:%d new:%d"%(self._history_cursor, value))
+ self._history_cursor = value
+
+ history_length = property(get_history_length, set_history_length)
+ history_cursor = property(get_history_cursor, set_history_cursor)
+
+ def clear_history(self):
+ '''Clear readline history.'''
+ self.history[:] = []
+ self.history_cursor = 0
+
+ def read_history_file(self, filename=None):
+ '''Load a readline history file.'''
+ if filename is None:
+ filename = self.history_filename
+ try:
+ for line in open(filename, 'r'):
+ self.add_history(lineobj.ReadLineTextBuffer(ensure_unicode(line.rstrip())))
+ except IOError:
+ self.history = []
+ self.history_cursor = 0
+
+ def write_history_file(self, filename = None):
+ '''Save a readline history file.'''
+ if filename is None:
+ filename = self.history_filename
+ fp = open(filename, 'wb')
+ for line in self.history[-self.history_length:]:
+ fp.write(ensure_str(line.get_line_text()))
+ fp.write('\n'.encode('ascii'))
+ fp.close()
+
+
+ def add_history(self, line):
+ '''Append a line to the history buffer, as if it was the last line typed.'''
+ if not hasattr(line, "get_line_text"):
+ line = lineobj.ReadLineTextBuffer(line)
+ if not line.get_line_text():
+ pass
+ elif len(self.history) > 0 and self.history[-1].get_line_text() == line.get_line_text():
+ pass
+ else:
+ self.history.append(line)
+ self.history_cursor = len(self.history)
+
+ def previous_history(self, current): # (C-p)
+ '''Move back through the history list, fetching the previous command. '''
+ if self.history_cursor == len(self.history):
+ self.history.append(current.copy()) #do not use add_history since we do not want to increment cursor
+
+ if self.history_cursor > 0:
+ self.history_cursor -= 1
+ current.set_line(self.history[self.history_cursor].get_line_text())
+ current.point = lineobj.EndOfLine
+
+ def next_history(self, current): # (C-n)
+ '''Move forward through the history list, fetching the next command. '''
+ if self.history_cursor < len(self.history) - 1:
+ self.history_cursor += 1
+ current.set_line(self.history[self.history_cursor].get_line_text())
+
+ def beginning_of_history(self): # (M-<)
+ '''Move to the first line in the history.'''
+ self.history_cursor = 0
+ if len(self.history) > 0:
+ self.l_buffer = self.history[0]
+
+ def end_of_history(self, current): # (M->)
+ '''Move to the end of the input history, i.e., the line currently
+ being entered.'''
+ self.history_cursor = len(self.history)
+ current.set_line(self.history[-1].get_line_text())
+
+ def reverse_search_history(self, searchfor, startpos=None):
+ if startpos is None:
+ startpos = self.history_cursor
+ origpos = startpos
+
+ result = lineobj.ReadLineTextBuffer("")
+
+ for idx, line in list(enumerate(self.history))[startpos:0:-1]:
+ if searchfor in line:
+ startpos = idx
+ break
+
+ #If we get a new search without change in search term it means
+ #someone pushed ctrl-r and we should find the next match
+ if self.last_search_for == searchfor and startpos > 0:
+ startpos -= 1
+ for idx, line in list(enumerate(self.history))[startpos:0:-1]:
+ if searchfor in line:
+ startpos = idx
+ break
+
+ if self.history:
+ result = self.history[startpos].get_line_text()
+ else:
+ result = ""
+ self.history_cursor = startpos
+ self.last_search_for = searchfor
+ log("reverse_search_history: old:%d new:%d result:%r"%(origpos, self.history_cursor, result))
+ return result
+
+ def forward_search_history(self, searchfor, startpos=None):
+ if startpos is None:
+ startpos = min(self.history_cursor, max(0, self.get_current_history_length()-1))
+ origpos = startpos
+
+ result = lineobj.ReadLineTextBuffer("")
+
+ for idx, line in list(enumerate(self.history))[startpos:]:
+ if searchfor in line:
+ startpos = idx
+ break
+
+ #If we get a new search without change in search term it means
+ #someone pushed ctrl-r and we should find the next match
+ if self.last_search_for == searchfor and startpos < self.get_current_history_length()-1:
+ startpos += 1
+ for idx, line in list(enumerate(self.history))[startpos:]:
+ if searchfor in line:
+ startpos = idx
+ break
+
+ if self.history:
+ result = self.history[startpos].get_line_text()
+ else:
+ result = ""
+ self.history_cursor = startpos
+ self.last_search_for = searchfor
+ return result
+
+ def _search(self, direction, partial):
+ try:
+ if (self.lastcommand != self.history_search_forward and
+ self.lastcommand != self.history_search_backward):
+ self.query = ''.join(partial[0:partial.point].get_line_text())
+ hcstart = max(self.history_cursor,0)
+ hc = self.history_cursor + direction
+ while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
+ h = self.history[hc]
+ if not self.query:
+ self.history_cursor = hc
+ result = lineobj.ReadLineTextBuffer(h, point=len(h.get_line_text()))
+ return result
+ elif (h.get_line_text().startswith(self.query) and (h != partial.get_line_text())):
+ self.history_cursor = hc
+ result = lineobj.ReadLineTextBuffer(h, point=partial.point)
+ return result
+ hc += direction
+ else:
+ if len(self.history) == 0:
+ pass
+ elif hc >= len(self.history) and not self.query:
+ self.history_cursor = len(self.history)
+ return lineobj.ReadLineTextBuffer("", point=0)
+ elif self.history[max(min(hcstart, len(self.history) - 1), 0)]\
+ .get_line_text().startswith(self.query) and self.query:
+ return lineobj.ReadLineTextBuffer(self.history\
+ [max(min(hcstart, len(self.history) - 1),0)],
+ point = partial.point)
+ else:
+ return lineobj.ReadLineTextBuffer(partial,
+ point=partial.point)
+ return lineobj.ReadLineTextBuffer(self.query,
+ point=min(len(self.query),
+ partial.point))
+ except IndexError:
+ raise
+
+ def history_search_forward(self, partial): # ()
+ '''Search forward through the history for the string of characters
+ between the start of the current line and the point. This is a
+ non-incremental search. By default, this command is unbound.'''
+ q= self._search(1, partial)
+ return q
+
+ def history_search_backward(self, partial): # ()
+ '''Search backward through the history for the string of characters
+ between the start of the current line and the point. This is a
+ non-incremental search. By default, this command is unbound.'''
+
+ q= self._search(-1, partial)
+ return q
+
+if __name__=="__main__":
+ import pdb
+ q = LineHistory()
+ r = LineHistory()
+ s = LineHistory()
+ RL = lineobj.ReadLineTextBuffer
+ q.add_history(RL("aaaa"))
+ q.add_history(RL("aaba"))
+ q.add_history(RL("aaca"))
+ q.add_history(RL("akca"))
+ q.add_history(RL("bbb"))
+ q.add_history(RL("ako"))
+ r.add_history(RL("ako"))
diff --git a/pyreadline/lineeditor/lineobj.py b/pyreadline/lineeditor/lineobj.py
new file mode 100644
index 0000000..dedc8b1
--- /dev/null
+++ b/pyreadline/lineeditor/lineobj.py
@@ -0,0 +1,799 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import re, operator, sys
+
+from . import wordmatcher
+import pyreadline.clipboard as clipboard
+from pyreadline.logger import log
+from pyreadline.unicode_helper import ensure_unicode, biter
+
+kill_ring_to_clipboard = False #set to true to copy every addition to kill ring to clipboard
+
+
+class NotAWordError(IndexError):
+ pass
+
+
+def quote_char(c):
+ if ord(c) > 0:
+ return c
+
+############## Line positioner ########################
+
+class LinePositioner(object):
+ def __call__(self, line):
+ NotImplementedError("Base class !!!")
+
+class NextChar(LinePositioner):
+ def __call__(self, line):
+ if line.point < len(line.line_buffer):
+ return line.point + 1
+ else:
+ return line.point
+NextChar = NextChar()
+
+class PrevChar(LinePositioner):
+ def __call__(self, line):
+ if line.point > 0:
+ return line.point - 1
+ else:
+ return line.point
+PrevChar = PrevChar()
+
+class NextWordStart(LinePositioner):
+ def __call__(self, line):
+ return line.next_start_segment(line.line_buffer, line.is_word_token)[line.point]
+NextWordStart = NextWordStart()
+
+class NextWordEnd(LinePositioner):
+ def __call__(self, line):
+ return line.next_end_segment(line.line_buffer, line.is_word_token)[line.point]
+NextWordEnd = NextWordEnd()
+
+class PrevWordStart(LinePositioner):
+ def __call__(self, line):
+ return line.prev_start_segment(line.line_buffer, line.is_word_token)[line.point]
+PrevWordStart = PrevWordStart()
+
+
+class WordStart(LinePositioner):
+ def __call__(self, line):
+ if line.is_word_token(line.get_line_text()[Point(line):Point(line) + 1]):
+ if Point(line) > 0 and line.is_word_token(line.get_line_text()[Point(line) - 1:Point(line)]):
+ return PrevWordStart(line)
+ else:
+ return line.point
+ else:
+ raise NotAWordError("Point is not in a word")
+WordStart = WordStart()
+
+class WordEnd(LinePositioner):
+ def __call__(self, line):
+ if line.is_word_token(line.get_line_text()[Point(line):Point(line) + 1]):
+ if line.is_word_token(line.get_line_text()[Point(line) + 1:Point(line) + 2]):
+ return NextWordEnd(line)
+ else:
+ return line.point
+ else:
+ raise NotAWordError("Point is not in a word")
+WordEnd = WordEnd()
+
+class PrevWordEnd(LinePositioner):
+ def __call__(self, line):
+ return line.prev_end_segment(line.line_buffer, line.is_word_token)[line.point]
+PrevWordEnd = PrevWordEnd()
+
+class PrevSpace(LinePositioner):
+ def __call__(self, line):
+ point = line.point
+ if line[point - 1:point].get_line_text() == " ":
+ while point > 0 and line[point - 1:point].get_line_text() == " ":
+ point -= 1
+ while point > 0 and line[point - 1:point].get_line_text() != " ":
+ point -= 1
+ return point
+PrevSpace = PrevSpace()
+
+
+class StartOfLine(LinePositioner):
+ def __call__(self, line):
+ return 0
+StartOfLine = StartOfLine()
+
+class EndOfLine(LinePositioner):
+ def __call__(self, line):
+ return len(line.line_buffer)
+EndOfLine = EndOfLine()
+
+class Point(LinePositioner):
+ def __call__(self, line):
+ return line.point
+Point = Point()
+
+class Mark(LinePositioner):
+ def __call__(self, line):
+ return line.mark
+k = Mark()
+
+all_positioners = [(value.__class__.__name__, value)
+ for key, value in list(globals().items())
+ if isinstance(value, LinePositioner)]
+all_positioners.sort()
+
+############### LineSlice #################
+
+class LineSlice(object):
+ def __call__(self, line):
+ NotImplementedError("Base class !!!")
+
+
+class CurrentWord(LineSlice):
+ def __call__(self, line):
+ return slice(WordStart(line), WordEnd(line), None)
+CurrentWord = CurrentWord()
+
+class NextWord(LineSlice):
+ def __call__(self, line):
+ work = TextLine(line)
+ work.point = NextWordStart
+ start = work.point
+ stop = NextWordEnd(work)
+ return slice(start, stop)
+NextWord = NextWord()
+
+class PrevWord(LineSlice):
+ def __call__(self, line):
+ work = TextLine(line)
+ work.point = PrevWordEnd
+ stop = work.point
+ start = PrevWordStart(work)
+ return slice(start, stop)
+PrevWord = PrevWord()
+
+class PointSlice(LineSlice):
+ def __call__(self, line):
+ return slice(Point(line), Point(line) + 1, None)
+PointSlice = PointSlice()
+
+
+############### TextLine ######################
+
+class TextLine(object):
+ def __init__(self, txtstr, point = None, mark = None):
+ self.line_buffer = []
+ self._point = 0
+ self.mark = -1
+ self.undo_stack = []
+ self.overwrite = False
+ if isinstance(txtstr, TextLine): #copy
+ self.line_buffer = txtstr.line_buffer[:]
+ if point is None:
+ self.point = txtstr.point
+ else:
+ self.point = point
+ if mark is None:
+ self.mark = txtstr.mark
+ else:
+ self.mark = mark
+ else:
+ self._insert_text(txtstr)
+ if point is None:
+ self.point = 0
+ else:
+ self.point = point
+ if mark is None:
+ self.mark = -1
+ else:
+ self.mark = mark
+
+ self.is_word_token = wordmatcher.is_word_token
+ self.next_start_segment = wordmatcher.next_start_segment
+ self.next_end_segment = wordmatcher.next_end_segment
+ self.prev_start_segment = wordmatcher.prev_start_segment
+ self.prev_end_segment = wordmatcher.prev_end_segment
+
+ def push_undo(self):
+ ltext = self.get_line_text()
+ if self.undo_stack and ltext == self.undo_stack[-1].get_line_text():
+ self.undo_stack[-1].point = self.point
+ else:
+ self.undo_stack.append(self.copy())
+
+ def pop_undo(self):
+ if len(self.undo_stack) >= 2:
+ self.undo_stack.pop()
+ self.set_top_undo()
+ self.undo_stack.pop()
+ else:
+ self.reset_line()
+ self.undo_stack = []
+
+ def set_top_undo(self):
+ if self.undo_stack:
+ undo = self.undo_stack[-1]
+ self.line_buffer = undo.line_buffer
+ self.point = undo.point
+ self.mark = undo.mark
+ else:
+ pass
+
+ def __repr__(self):
+ return 'TextLine("%s",point=%s,mark=%s)'%(self.line_buffer, self.point, self.mark)
+
+ def copy(self):
+ return self.__class__(self)
+
+ def set_point(self,value):
+ if isinstance(value, LinePositioner):
+ value = value(self)
+ assert (value <= len(self.line_buffer))
+ if value > len(self.line_buffer):
+ value = len(self.line_buffer)
+ self._point = value
+ def get_point(self):
+ return self._point
+ point = property(get_point, set_point)
+
+
+ def visible_line_width(self, position = Point):
+ """Return the visible width of the text in line buffer up to position."""
+ extra_char_width = len([ None for c in self[:position].line_buffer if 0x2013 <= ord(c) <= 0xFFFD])
+ return len(self[:position].quoted_text()) + self[:position].line_buffer.count("\t")*7 + extra_char_width
+
+ def quoted_text(self):
+ quoted = [ quote_char(c) for c in self.line_buffer ]
+ self.line_char_width = [ len(c) for c in quoted ]
+ return ''.join(map(ensure_unicode, quoted))
+
+ def get_line_text(self):
+ buf = self.line_buffer
+ buf = list(map(ensure_unicode, buf))
+ return ''.join(buf)
+
+ def set_line(self, text, cursor = None):
+ self.line_buffer = [ c for c in str(text) ]
+ if cursor is None:
+ self.point = len(self.line_buffer)
+ else:
+ self.point = cursor
+
+ def reset_line(self):
+ self.line_buffer = []
+ self.point = 0
+
+ def end_of_line(self):
+ self.point = len(self.line_buffer)
+
+ def _insert_text(self, text, argument=1):
+ text = text * argument
+ if self.overwrite:
+ for c in biter(text):
+ #if self.point:
+ self.line_buffer[self.point] = c
+ self.point += 1
+ else:
+ for c in biter(text):
+ self.line_buffer.insert(self.point, c)
+ self.point += 1
+
+ def __getitem__(self, key):
+ #Check if key is LineSlice, convert to regular slice
+ #and continue processing
+ if isinstance(key, LineSlice):
+ key = key(self)
+ if isinstance(key, slice):
+ if key.step is None:
+ pass
+ else:
+ raise Error
+ if key.start is None:
+ start = StartOfLine(self)
+ elif isinstance(key.start,LinePositioner):
+ start = key.start(self)
+ else:
+ start = key.start
+ if key.stop is None:
+ stop = EndOfLine(self)
+ elif isinstance(key.stop, LinePositioner):
+ stop = key.stop(self)
+ else:
+ stop = key.stop
+ return self.__class__(self.line_buffer[start:stop], point=0)
+ elif isinstance(key, LinePositioner):
+ return self.line_buffer[key(self)]
+ elif isinstance(key, tuple):
+ raise IndexError("Cannot use step in line buffer indexing") #Multiple slice not allowed
+ else:
+ # return TextLine(self.line_buffer[key])
+ return self.line_buffer[key]
+
+ def __delitem__(self, key):
+ point = self.point
+ if isinstance(key, LineSlice):
+ key = key(self)
+ if isinstance(key, slice):
+ start = key.start
+ stop = key.stop
+ if isinstance(start, LinePositioner):
+ start = start(self)
+ elif start is None:
+ start=0
+ if isinstance(stop, LinePositioner):
+ stop = stop(self)
+ elif stop is None:
+ stop = EndOfLine(self)
+ elif isinstance(key, LinePositioner):
+ start = key(self)
+ stop = start + 1
+ else:
+ start = key
+ stop = key + 1
+ prev = self.line_buffer[:start]
+ rest = self.line_buffer[stop:]
+ self.line_buffer = prev + rest
+ if point > stop:
+ self.point = point - (stop - start)
+ elif point >= start and point <= stop:
+ self.point = start
+
+
+ def __setitem__(self, key, value):
+ if isinstance(key, LineSlice):
+ key = key(self)
+ if isinstance(key, slice):
+ start = key.start
+ stop = key.stop
+ elif isinstance(key, LinePositioner):
+ start = key(self)
+ stop = start + 1
+ else:
+ start = key
+ stop = key + 1
+ prev = self.line_buffer[:start]
+ value = self.__class__(value).line_buffer
+ rest = self.line_buffer[stop:]
+ out = prev + value + rest
+ if len(out) >= len(self):
+ self.point = len(self)
+ self.line_buffer = out
+
+ def __len__(self):
+ return len(self.line_buffer)
+
+ def upper(self):
+ self.line_buffer = [x.upper() for x in self.line_buffer]
+ return self
+
+ def lower(self):
+ self.line_buffer = [x.lower() for x in self.line_buffer]
+ return self
+
+ def capitalize(self):
+ self.set_line(self.get_line_text().capitalize(), self.point)
+ return self
+
+ def startswith(self, txt):
+ return self.get_line_text().startswith(txt)
+
+ def endswith(self, txt):
+ return self.get_line_text().endswith(txt)
+
+ def __contains__(self, txt):
+ return txt in self.get_line_text()
+
+
+lines = [TextLine("abc"),
+ TextLine("abc def"),
+ TextLine("abc def ghi"),
+ TextLine(" abc def "),
+ ]
+l = lines[2]
+l.point = 5
+
+
+
+class ReadLineTextBuffer(TextLine):
+ def __init__(self,txtstr, point = None, mark = None):
+ super(ReadLineTextBuffer, self).__init__(txtstr, point, mark)
+ self.enable_win32_clipboard = True
+ self.selection_mark = -1
+ self.enable_selection = True
+ self.kill_ring = []
+
+ def __repr__(self):
+ return 'ReadLineTextBuffer'\
+ '("%s",point=%s,mark=%s,selection_mark=%s)'%\
+ (self.line_buffer, self.point, self.mark,self.selection_mark)
+
+
+ def insert_text(self, char, argument=1):
+ self.delete_selection()
+ self.selection_mark = -1
+ self._insert_text(char, argument)
+
+ def to_clipboard(self):
+ if self.enable_win32_clipboard:
+ clipboard.set_clipboard_text(self.get_line_text())
+
+######### Movement
+
+ def beginning_of_line(self):
+ self.selection_mark = -1
+ self.point = StartOfLine
+
+ def end_of_line(self):
+ self.selection_mark = -1
+ self.point = EndOfLine
+
+ def forward_char(self,argument = 1):
+ if argument < 0:
+ self.backward_char(-argument)
+ self.selection_mark = -1
+ for x in range(argument):
+ self.point = NextChar
+
+ def backward_char(self, argument=1):
+ if argument < 0:
+ self.forward_char(-argument)
+ self.selection_mark = -1
+ for x in range(argument):
+ self.point = PrevChar
+
+ def forward_word(self,argument=1):
+ if argument<0:
+ self.backward_word(-argument)
+ self.selection_mark=-1
+ for x in range(argument):
+ self.point = NextWordStart
+
+ def backward_word(self, argument=1):
+ if argument < 0:
+ self.forward_word(-argument)
+ self.selection_mark = -1
+ for x in range(argument):
+ self.point = PrevWordStart
+
+ def forward_word_end(self, argument=1):
+ if argument < 0:
+ self.backward_word_end(-argument)
+ self.selection_mark = -1
+ for x in range(argument):
+ self.point = NextWordEnd
+
+ def backward_word_end(self, argument=1):
+ if argument < 0:
+ self.forward_word_end(-argument)
+ self.selection_mark = -1
+ for x in range(argument):
+ self.point = NextWordEnd
+
+######### Movement select
+ def beginning_of_line_extend_selection(self):
+ if self.enable_selection and self.selection_mark < 0:
+ self.selection_mark = self.point
+ self.point = StartOfLine
+
+ def end_of_line_extend_selection(self):
+ if self.enable_selection and self.selection_mark < 0:
+ self.selection_mark = self.point
+ self.point = EndOfLine
+
+ def forward_char_extend_selection(self,argument=1):
+ if argument < 0:
+ self.backward_char_extend_selection(-argument)
+ if self.enable_selection and self.selection_mark < 0:
+ self.selection_mark = self.point
+ for x in range(argument):
+ self.point = NextChar
+
+ def backward_char_extend_selection(self, argument=1):
+ if argument < 0:
+ self.forward_char_extend_selection(-argument)
+ if self.enable_selection and self.selection_mark < 0:
+ self.selection_mark = self.point
+ for x in range(argument):
+ self.point = PrevChar
+
+ def forward_word_extend_selection(self, argument=1):
+ if argument < 0:
+ self.backward_word_extend_selection(-argument)
+ if self.enable_selection and self.selection_mark < 0:
+ self.selection_mark = self.point
+ for x in range(argument):
+ self.point = NextWordStart
+
+ def backward_word_extend_selection(self, argument=1):
+ if argument < 0:
+ self.forward_word_extend_selection(-argument)
+ if self.enable_selection and self.selection_mark < 0:
+ self.selection_mark = self.point
+ for x in range(argument):
+ self.point = PrevWordStart
+
+
+ def forward_word_end_extend_selection(self, argument=1):
+ if argument < 0:
+ self.backward_word_end_extend_selection(-argument)
+ if self.enable_selection and self.selection_mark < 0:
+ self.selection_mark = self.point
+ for x in range(argument):
+ self.point = NextWordEnd
+
+ def backward_word_end_extend_selection(self, argument=1):
+ if argument < 0:
+ self.forward_word_end_extend_selection(-argument)
+ if self.enable_selection and self.selection_mark < 0:
+ self.selection_mark = self.point
+ for x in range(argument):
+ self.point = PrevWordEnd
+
+
+######### delete
+
+ def delete_selection(self):
+ if self.enable_selection and self.selection_mark >= 0:
+ if self.selection_mark < self.point:
+ del self[self.selection_mark:self.point]
+ self.selection_mark = -1
+ else:
+ del self[self.point:self.selection_mark]
+ self.selection_mark = -1
+ return True
+ else:
+ self.selection_mark = -1
+ return False
+
+ def delete_char(self, argument=1):
+ if argument < 0:
+ self.backward_delete_char(-argument)
+ if self.delete_selection():
+ argument -= 1
+ for x in range(argument):
+ del self[Point]
+
+ def backward_delete_char(self, argument=1):
+ if argument < 0:
+ self.delete_char(-argument)
+ if self.delete_selection():
+ argument -= 1
+ for x in range(argument):
+ if self.point > 0:
+ self.backward_char()
+ self.delete_char()
+
+ def forward_delete_word(self, argument=1):
+ if argument < 0:
+ self.backward_delete_word(-argument)
+ if self.delete_selection():
+ argument -= 1
+ for x in range(argument):
+ del self[Point:NextWordStart]
+
+ def backward_delete_word(self, argument=1):
+ if argument < 0:
+ self.forward_delete_word(-argument)
+ if self.delete_selection():
+ argument -= 1
+ for x in range(argument):
+ del self[PrevWordStart:Point]
+
+ def delete_current_word(self):
+ if not self.delete_selection():
+ del self[CurrentWord]
+ self.selection_mark =- 1
+
+ def delete_horizontal_space(self):
+ if self[Point] in " \t":
+ del self[PrevWordEnd:NextWordStart]
+ self.selection_mark = -1
+######### Case
+
+ def upcase_word(self):
+ p = self.point
+ try:
+ self[CurrentWord] = self[CurrentWord].upper()
+ self.point = p
+ except NotAWordError:
+ pass
+
+ def downcase_word(self):
+ p = self.point
+ try:
+ self[CurrentWord] = self[CurrentWord].lower()
+ self.point = p
+ except NotAWordError:
+ pass
+
+ def capitalize_word(self):
+ p = self.point
+ try:
+ self[CurrentWord] = self[CurrentWord].capitalize()
+ self.point = p
+ except NotAWordError:
+ pass
+########### Transpose
+ def transpose_chars(self):
+ p2 = Point(self)
+ if p2 == 0:
+ return
+ elif p2 == len(self):
+ p2 = p2 - 1
+ p1 = p2 - 1
+ self[p2], self[p1] = self[p1], self[p2]
+ self.point = p2 + 1
+
+ def transpose_words(self):
+ word1 = TextLine(self)
+ word2 = TextLine(self)
+ if self.point == len(self):
+ word2.point = PrevWordStart
+ word1.point = PrevWordStart(word2)
+ else:
+ word1.point = PrevWordStart
+ word2.point = NextWordStart
+ stop1 = NextWordEnd(word1)
+ stop2 = NextWordEnd(word2)
+ start1 = word1.point
+ start2 = word2.point
+ self[start2:stop2] = word1[Point:NextWordEnd]
+ self[start1:stop1] = word2[Point:NextWordEnd]
+ self.point = stop2
+
+
+############ Kill
+
+ def kill_line(self):
+ self.add_to_kill_ring(self[self.point:])
+ del self.line_buffer[self.point:]
+
+ def kill_whole_line(self):
+ self.add_to_kill_ring(self[:])
+ del self[:]
+
+ def backward_kill_line(self):
+ del self[StartOfLine:Point]
+
+ def unix_line_discard(self):
+ del self[StartOfLine:Point]
+ pass
+
+ def kill_word(self):
+ """Kills to next word ending"""
+ del self[Point:NextWordEnd]
+
+ def backward_kill_word(self):
+ """Kills to next word ending"""
+ if not self.delete_selection():
+ del self[PrevWordStart:Point]
+ self.selection_mark = -1
+
+ def forward_kill_word(self):
+ """Kills to next word ending"""
+ if not self.delete_selection():
+ del self[Point:NextWordEnd]
+ self.selection_mark = -1
+
+ def unix_word_rubout(self):
+ if not self.delete_selection():
+ del self[PrevSpace:Point]
+ self.selection_mark = -1
+
+ def kill_region(self):
+ pass
+
+ def copy_region_as_kill(self):
+ pass
+
+ def copy_backward_word(self):
+ pass
+
+ def copy_forward_word(self):
+ pass
+
+
+ def yank(self):
+ self.paste_from_kill_ring()
+
+ def yank_pop(self):
+ pass
+
+############## Mark
+
+ def set_mark(self):
+ self.mark = self.point
+
+ def exchange_point_and_mark(self):
+ pass
+
+
+ def copy_region_to_clipboard(self): # ()
+ '''Copy the text in the region to the windows clipboard.'''
+ if self.enable_win32_clipboard:
+ mark = min(self.mark, len(self.line_buffer))
+ cursor = min(self.point, len(self.line_buffer))
+ if self.mark == -1:
+ return
+ begin = min(cursor, mark)
+ end = max(cursor, mark)
+ toclipboard = "".join(self.line_buffer[begin:end])
+ clipboard.SetClipboardText(toclipboard)
+
+ def copy_selection_to_clipboard(self): # ()
+ '''Copy the text in the region to the windows clipboard.'''
+ if self.enable_win32_clipboard and self.enable_selection and self.selection_mark >= 0:
+ selection_mark = min(self.selection_mark,len(self.line_buffer))
+ cursor = min(self.point,len(self.line_buffer))
+ if self.selection_mark == -1:
+ return
+ begin = min(cursor, selection_mark)
+ end = max(cursor, selection_mark)
+ toclipboard = "".join(self.line_buffer[begin:end])
+ clipboard.SetClipboardText(toclipboard)
+
+
+ def cut_selection_to_clipboard(self): # ()
+ self.copy_selection_to_clipboard()
+ self.delete_selection()
+############## Paste
+
+
+############## Kill ring
+ def add_to_kill_ring(self,txt):
+ self.kill_ring = [txt]
+ if kill_ring_to_clipboard:
+ clipboard.SetClipboardText(txt.get_line_text())
+
+
+ def paste_from_kill_ring(self):
+ if self.kill_ring:
+ self.insert_text(self.kill_ring[0])
+
+
+##################################################################
+q = ReadLineTextBuffer("asff asFArw ewrWErhg", point=8)
+q = TextLine("asff asFArw ewrWErhg", point=8)
+
+def show_pos(buff, pos, chr = "."):
+ l = len(buff.line_buffer)
+ def choice(bool):
+ if bool:
+ return chr
+ else:
+ return " "
+ return "".join([choice(pos==idx) for idx in range(l + 1)])
+
+
+def test_positioner(buff, points, positioner):
+ print((" %s "%positioner.__class__.__name__).center(40, "-"))
+ buffstr = buff.line_buffer
+
+ print('"%s"'%(buffstr))
+ for point in points:
+ b = TextLine(buff, point = point)
+ out=[" "] * (len(buffstr) + 1)
+ pos = positioner(b)
+ if pos == point:
+ out[pos] = "&"
+ else:
+ out[point] = "."
+ out[pos] = "^"
+ print('"%s"'%("".join(out)))
+
+if __name__ == "__main__":
+
+ print('%-15s "%s"'%("Position", q.get_line_text()))
+ print('%-15s "%s"'%("Point", show_pos(q, q.point)))
+
+
+ for name, positioner in all_positioners:
+ pos = positioner(q)
+ []
+ print('%-15s "%s"'%(name, show_pos(q, pos, "^")))
+
+ l = ReadLineTextBuffer("kjjk asads asad")
+ l.point = EndOfLine
diff --git a/pyreadline/lineeditor/wordmatcher.py b/pyreadline/lineeditor/wordmatcher.py
new file mode 100644
index 0000000..1f84cf9
--- /dev/null
+++ b/pyreadline/lineeditor/wordmatcher.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+
+import re, operator
+
+
+def str_find_all(str, ch):
+ result = []
+ index = 0
+ while index >= 0:
+ index = str.find(ch, index)
+ if index >= 0:
+ result.append(index)
+ index += 1
+ return result
+
+
+word_pattern = re.compile("(x*)")
+
+def markwords(str, iswordfun):
+ markers = {True : "x", False : "o"}
+ return "".join([markers[iswordfun(ch)] for ch in str])
+
+def split_words(str, iswordfun):
+ return [x for x in word_pattern.split(markwords(str,iswordfun)) if x != ""]
+
+def mark_start_segment(str, is_segment):
+ def mark_start(s):
+ if s[0:1] == "x":
+ return "s" + s[1:]
+ else:
+ return s
+ return "".join(map(mark_start, split_words(str, is_segment)))
+
+def mark_end_segment(str, is_segment):
+ def mark_start(s):
+ if s[0:1] == "x":
+ return s[:-1] + "s"
+ else:
+ return s
+ return "".join(map(mark_start, split_words(str, is_segment)))
+
+def mark_start_segment_index(str, is_segment):
+ return str_find_all(mark_start_segment(str, is_segment), "s")
+
+def mark_end_segment_index(str, is_segment):
+ return [x + 1 for x in str_find_all(mark_end_segment(str, is_segment), "s")]
+
+
+################ Following are used in lineobj ###########################
+
+def is_word_token(str):
+ return not is_non_word_token(str)
+
+def is_non_word_token(str):
+ if len(str) != 1 or str in " \t\n":
+ return True
+ else:
+ return False
+
+def next_start_segment(str, is_segment):
+ str = "".join(str)
+ result = []
+ for start in mark_start_segment_index(str, is_segment):
+ result[len(result):start] = [start for x in range(start - len(result))]
+ result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)]
+ return result
+
+def next_end_segment(str, is_segment):
+ str = "".join(str)
+ result = []
+ for start in mark_end_segment_index(str, is_segment):
+ result[len(result):start] = [start for x in range(start - len(result))]
+ result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)]
+ return result
+
+
+def prev_start_segment(str, is_segment):
+ str = "".join(str)
+ result = []
+ prev = 0
+ for start in mark_start_segment_index(str, is_segment):
+ result[len(result):start+1] = [prev for x in range(start - len(result) + 1)]
+ prev=start
+ result[len(result):len(str)] = [prev for x in range(len(str) - len(result) + 1)]
+ return result
+
+def prev_end_segment(str, is_segment):
+ str = "".join(str)
+ result = []
+ prev = 0
+ for start in mark_end_segment_index(str, is_segment):
+ result[len(result):start + 1] = [prev for x in range(start - len(result) + 1)]
+ prev=start
+ result[len(result):len(str)] = [len(str) for x in range(len(str) - len(result) + 1)]
+ return result
+
diff --git a/pyreadline/logger.py b/pyreadline/logger.py
new file mode 100644
index 0000000..61e77f6
--- /dev/null
+++ b/pyreadline/logger.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+
+import socket, logging, logging.handlers
+from pyreadline.unicode_helper import ensure_str
+
+host = "localhost"
+port = logging.handlers.DEFAULT_TCP_LOGGING_PORT
+
+
+pyreadline_logger = logging.getLogger('PYREADLINE')
+pyreadline_logger.setLevel(logging.DEBUG)
+pyreadline_logger.propagate = False
+formatter = logging.Formatter('%(message)s')
+file_handler = None
+
+class NULLHandler(logging.Handler):
+ def emit(self, s):
+ pass
+
+class SocketStream(object):
+ def __init__(self, host, port):
+ self.logsocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
+
+ def write(self, s):
+ self.logsocket.sendto(ensure_str(s), (host, port))
+
+ def flush(self):
+ pass
+
+ def close(self):
+ pass
+
+socket_handler = None
+pyreadline_logger.addHandler(NULLHandler())
+
+def start_socket_log():
+ global socket_handler
+ socket_handler = logging.StreamHandler(SocketStream(host, port))
+ socket_handler.setFormatter(formatter)
+ pyreadline_logger.addHandler(socket_handler)
+
+def stop_socket_log():
+ global socket_handler
+ if socket_handler:
+ pyreadline_logger.removeHandler(socket_handler)
+ socket_handler = None
+
+def start_file_log(filename):
+ global file_handler
+ file_handler = logging.FileHandler(filename, "w")
+ pyreadline_logger.addHandler(file_handler)
+
+def stop_file_log():
+ global file_handler
+ if file_handler:
+ pyreadline_logger.removeHandler(file_handler)
+ file_handler.close()
+ file_handler = None
+
+def stop_logging():
+ log("STOPING LOG")
+ stop_file_log()
+ stop_socket_log()
+
+def log(s):
+ s = ensure_str(s)
+ pyreadline_logger.debug(s)
diff --git a/pyreadline/logserver.py b/pyreadline/logserver.py
new file mode 100644
index 0000000..e154086
--- /dev/null
+++ b/pyreadline/logserver.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import pickle
+import logging
+import logging.handlers
+import socketserver
+import struct,socket
+
+try:
+ import msvcrt
+except ImportError:
+ msvcrt = None
+ print("problem")
+
+
+port = logging.handlers.DEFAULT_TCP_LOGGING_PORT
+host = 'localhost'
+
+def check_key():
+ if msvcrt is None:
+ return False
+ else:
+ if msvcrt.kbhit() != 0:
+ q = msvcrt.getch()
+ return q
+ return ""
+
+
+singleline=False
+
+def main():
+ print("Starting TCP logserver on port:", port)
+ print("Press q to quit logserver", port)
+ print("Press c to clear screen", port)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+ s.bind(("", port))
+ s.settimeout(1)
+ while 1:
+ try:
+ data, addr = s.recvfrom(100000)
+ print(data, end=' ')
+ except socket.timeout:
+ key = check_key().lower()
+ if "q" == key:
+ print("Quitting logserver")
+ break
+ elif "c" == key:
+ print("\n" * 100)
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/pyreadline/modes/__init__.py b/pyreadline/modes/__init__.py
new file mode 100644
index 0000000..c1078ae
--- /dev/null
+++ b/pyreadline/modes/__init__.py
@@ -0,0 +1,5 @@
+__all__=["emacs","notemacs","vi"]
+from . import emacs,notemacs,vi
+editingmodes=[emacs.EmacsMode,notemacs.NotEmacsMode,vi.ViMode]
+
+#add check to ensure all modes have unique mode names
\ No newline at end of file
diff --git a/pyreadline/modes/basemode.py b/pyreadline/modes/basemode.py
new file mode 100644
index 0000000..f91261a
--- /dev/null
+++ b/pyreadline/modes/basemode.py
@@ -0,0 +1,562 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import os,re,math,glob,sys,time
+import pyreadline.logger as logger
+from pyreadline.logger import log
+from pyreadline.keysyms.common import make_KeyPress_from_keydescr
+import pyreadline.lineeditor.lineobj as lineobj
+import pyreadline.lineeditor.history as history
+import pyreadline.clipboard as clipboard
+from pyreadline.error import ReadlineError,GetSetError
+from pyreadline.unicode_helper import ensure_str, ensure_unicode
+import collections
+in_ironpython="IronPython" in sys.version
+
+class BaseMode(object):
+ mode="base"
+ def __init__(self,rlobj):
+ self.argument=0
+ self.rlobj=rlobj
+ self.exit_dispatch = {}
+ self.key_dispatch = {}
+ self.argument=1
+ self.prevargument=None
+ self.l_buffer=lineobj.ReadLineTextBuffer("")
+ self._history=history.LineHistory()
+ self.completer_delims = " \t\n\"\\'`@$><=;|&{("
+ self.show_all_if_ambiguous = 'off'
+ self.mark_directories = 'on'
+ self.complete_filesystem = 'off'
+ self.completer = None
+ self.begidx = 0
+ self.endidx = 0
+ self.tabstop = 4
+ self.startup_hook = None
+ self.pre_input_hook = None
+ self.first_prompt = True
+ self.cursor_size=25
+
+ self.prompt = ">>> "
+
+ #Paste settings
+ #assumes data on clipboard is path if shorter than 300 characters and doesn't contain \t or \n
+ #and replace \ with / for easier use in ipython
+ self.enable_ipython_paste_for_paths=True
+
+ #automatically convert tabseparated data to list of lists or array constructors
+ self.enable_ipython_paste_list_of_lists=True
+ self.enable_win32_clipboard=True
+
+ self.paste_line_buffer=[]
+
+ self._sub_modes=[]
+
+
+ def __repr__(self):
+ return "<BaseMode>"
+
+ def _gs(x):
+ def g(self):
+ return getattr(self.rlobj,x)
+ def s(self,q):
+ setattr(self.rlobj,x,q)
+ return g,s
+
+ def _g(x):
+ def g(self):
+ return getattr(self.rlobj,x)
+ return g
+
+ def _argreset(self):
+ val=self.argument
+ self.argument=0
+ if val==0:
+ val=1
+ return val
+ argument_reset=property(_argreset)
+
+#used in readline
+ ctrl_c_tap_time_interval=property(*_gs("ctrl_c_tap_time_interval"))
+ allow_ctrl_c=property(*_gs("allow_ctrl_c"))
+ _print_prompt=property(_g("_print_prompt"))
+ _update_line=property(_g("_update_line"))
+ console=property(_g("console"))
+ prompt_begin_pos=property(_g("prompt_begin_pos"))
+ prompt_end_pos=property(_g("prompt_end_pos"))
+
+#used in completer _completions
+# completer_delims=property(*_gs("completer_delims"))
+ _bell=property(_g("_bell"))
+ bell_style=property(_g("bell_style"))
+
+#used in emacs
+ _clear_after=property(_g("_clear_after"))
+ _update_prompt_pos=property(_g("_update_prompt_pos"))
+
+#not used in basemode or emacs
+
+ def process_keyevent(self, keyinfo):
+ raise NotImplementedError
+
+ def readline_setup(self, prompt=''):
+ self.l_buffer.selection_mark=-1
+ if self.first_prompt:
+ self.first_prompt = False
+ if self.startup_hook:
+ try:
+ self.startup_hook()
+ except:
+ print('startup hook failed')
+ traceback.print_exc()
+
+ self.l_buffer.reset_line()
+ self.prompt = prompt
+
+ if self.pre_input_hook:
+ try:
+ self.pre_input_hook()
+ except:
+ print('pre_input_hook failed')
+ traceback.print_exc()
+ self.pre_input_hook = None
+
+
+####################################
+
+
+ def finalize(self):
+ """Every bindable command should call this function for cleanup.
+ Except those that want to set argument to a non-zero value.
+ """
+ self.argument = 0
+
+
+ def add_history(self, text):
+ self._history.add_history(lineobj.ReadLineTextBuffer(text))
+
+
+ #Create key bindings:
+ def rl_settings_to_string(self):
+ out=["%-20s: %s"%("show all if ambigous",self.show_all_if_ambiguous)]
+ out.append("%-20s: %s"%("mark_directories",self.mark_directories))
+ out.append("%-20s: %s"%("bell_style",self.bell_style))
+ out.append("------------- key bindings ------------")
+ tablepat="%-7s %-7s %-7s %-15s %-15s "
+ out.append(tablepat%("Control","Meta","Shift","Keycode/char","Function"))
+ bindings=[(k[0],k[1],k[2],k[3],v.__name__) for k,v in self.key_dispatch.items()]
+ bindings.sort()
+ for key in bindings:
+ out.append(tablepat%(key))
+ return out
+
+
+ def _bind_key(self, key, func):
+ """setup the mapping from key to call the function."""
+ if not isinstance(func, collections.Callable):
+ print("Trying to bind non method to keystroke:%s,%s"%(key,func))
+ raise ReadlineError("Trying to bind non method to keystroke:%s,%s,%s,%s"%(key,func,type(func),type(self._bind_key)))
+ keyinfo = make_KeyPress_from_keydescr(key.lower()).tuple()
+ log(">>>%s -> %s<<<"%(keyinfo,func.__name__))
+ self.key_dispatch[keyinfo] = func
+
+ def _bind_exit_key(self, key):
+ """setup the mapping from key to call the function."""
+ keyinfo = make_KeyPress_from_keydescr(key.lower()).tuple()
+ self.exit_dispatch[keyinfo] = None
+
+ def init_editing_mode(self, e): # (C-e)
+ """When in vi command mode, this causes a switch to emacs editing
+ mode."""
+
+ raise NotImplementedError
+#completion commands
+
+ def _get_completions(self):
+ """Return a list of possible completions for the string ending at the point.
+ Also set begidx and endidx in the process."""
+ completions = []
+ self.begidx = self.l_buffer.point
+ self.endidx = self.l_buffer.point
+ buf=self.l_buffer.line_buffer
+ if self.completer:
+ # get the string to complete
+ while self.begidx > 0:
+ self.begidx -= 1
+ if buf[self.begidx] in self.completer_delims:
+ self.begidx += 1
+ break
+ text = ensure_str(''.join(buf[self.begidx:self.endidx]))
+ log('complete text="%s"' % ensure_unicode(text))
+ i = 0
+ while 1:
+ try:
+ r = self.completer(ensure_unicode(text), i)
+ except IndexError:
+ break
+ i += 1
+ if r is None:
+ break
+ elif r and r not in completions:
+ completions.append(r)
+ else:
+ pass
+ log('text completions=<%s>' % list(map(ensure_unicode, completions)))
+ if (self.complete_filesystem == "on") and not completions:
+ # get the filename to complete
+ while self.begidx > 0:
+ self.begidx -= 1
+ if buf[self.begidx] in ' \t\n':
+ self.begidx += 1
+ break
+ text = ensure_str(''.join(buf[self.begidx:self.endidx]))
+ log('file complete text="%s"' % ensure_unicode(text))
+ completions = list(map(ensure_unicode, glob.glob(os.path.expanduser(text) + '*'.encode('ascii'))))
+ if self.mark_directories == 'on':
+ mc = []
+ for f in completions:
+ if os.path.isdir(f):
+ mc.append(f + os.sep)
+ else:
+ mc.append(f)
+ completions = mc
+ log('fnames=<%s>' % list(map(ensure_unicode, completions)))
+ return completions
+
+
+ def _display_completions(self, completions):
+ if not completions:
+ return
+ self.console.write('\n')
+ wmax = max(list(map(len, completions)))
+ w, h = self.console.size()
+ cols = max(1, int((w-1) / (wmax+1)))
+ rows = int(math.ceil(float(len(completions)) / cols))
+ for row in range(rows):
+ s = ''
+ for col in range(cols):
+ i = col*rows + row
+ if i < len(completions):
+ self.console.write(completions[i].ljust(wmax+1))
+ self.console.write('\n')
+ if in_ironpython:
+ self.prompt=sys.ps1
+ self._print_prompt()
+
+
+ def complete(self, e): # (TAB)
+ """Attempt to perform completion on the text before point. The
+ actual completion performed is application-specific. The default is
+ filename completion."""
+ completions = self._get_completions()
+ if completions:
+ cprefix = commonprefix(completions)
+ if len(cprefix) > 0:
+ rep = [ c for c in cprefix ]
+ point=self.l_buffer.point
+ self.l_buffer[self.begidx:self.endidx] = rep
+ self.l_buffer.point = point + len(rep) - (self.endidx - self.begidx)
+ if len(completions) > 1:
+ if self.show_all_if_ambiguous == 'on':
+ self._display_completions(completions)
+ else:
+ self._bell()
+ else:
+ self._bell()
+ self.finalize()
+
+ def possible_completions(self, e): # (M-?)
+ """List the possible completions of the text before point. """
+ completions = self._get_completions()
+ self._display_completions(completions)
+ self.finalize()
+
+ def insert_completions(self, e): # (M-*)
+ """Insert all completions of the text before point that would have
+ been generated by possible-completions."""
+ completions = self._get_completions()
+ b = self.begidx
+ e = self.endidx
+ for comp in completions:
+ rep = [ c for c in comp ]
+ rep.append(' ')
+ self.l_buffer[b:e] = rep
+ b += len(rep)
+ e = b
+ self.line_cursor = b
+ self.finalize()
+
+ def menu_complete(self, e): # ()
+ """Similar to complete, but replaces the word to be completed with a
+ single match from the list of possible completions. Repeated
+ execution of menu-complete steps through the list of possible
+ completions, inserting each match in turn. At the end of the list of
+ completions, the bell is rung (subject to the setting of bell-style)
+ and the original text is restored. An argument of n moves n
+ positions forward in the list of matches; a negative argument may be
+ used to move backward through the list. This command is intended to
+ be bound to TAB, but is unbound by default."""
+ self.finalize()
+
+ ### Methods below here are bindable emacs functions
+
+
+ def insert_text(self, string):
+ """Insert text into the command line."""
+ self.l_buffer.insert_text(string, self.argument_reset)
+ self.finalize()
+
+ def beginning_of_line(self, e): # (C-a)
+ """Move to the start of the current line. """
+ self.l_buffer.beginning_of_line()
+ self.finalize()
+
+ def end_of_line(self, e): # (C-e)
+ """Move to the end of the line. """
+ self.l_buffer.end_of_line()
+ self.finalize()
+
+ def forward_char(self, e): # (C-f)
+ """Move forward a character. """
+ self.l_buffer.forward_char(self.argument_reset)
+ self.finalize()
+
+ def backward_char(self, e): # (C-b)
+ """Move back a character. """
+ self.l_buffer.backward_char(self.argument_reset)
+ self.finalize()
+
+ def forward_word(self, e): # (M-f)
+ """Move forward to the end of the next word. Words are composed of
+ letters and digits."""
+ self.l_buffer.forward_word(self.argument_reset)
+ self.finalize()
+
+ def backward_word(self, e): # (M-b)
+ """Move back to the start of the current or previous word. Words are
+ composed of letters and digits."""
+ self.l_buffer.backward_word(self.argument_reset)
+ self.finalize()
+
+ def forward_word_end(self, e): # ()
+ """Move forward to the end of the next word. Words are composed of
+ letters and digits."""
+ self.l_buffer.forward_word_end(self.argument_reset)
+ self.finalize()
+
+ def backward_word_end(self, e): # ()
+ """Move forward to the end of the next word. Words are composed of
+ letters and digits."""
+ self.l_buffer.backward_word_end(self.argument_reset)
+ self.finalize()
+
+### Movement with extend selection
+ def beginning_of_line_extend_selection(self, e): #
+ """Move to the start of the current line. """
+ self.l_buffer.beginning_of_line_extend_selection()
+ self.finalize()
+
+ def end_of_line_extend_selection(self, e): #
+ """Move to the end of the line. """
+ self.l_buffer.end_of_line_extend_selection()
+ self.finalize()
+
+ def forward_char_extend_selection(self, e): #
+ """Move forward a character. """
+ self.l_buffer.forward_char_extend_selection(self.argument_reset)
+ self.finalize()
+
+ def backward_char_extend_selection(self, e): #
+ """Move back a character. """
+ self.l_buffer.backward_char_extend_selection(self.argument_reset)
+ self.finalize()
+
+ def forward_word_extend_selection(self, e): #
+ """Move forward to the end of the next word. Words are composed of
+ letters and digits."""
+ self.l_buffer.forward_word_extend_selection(self.argument_reset)
+ self.finalize()
+
+ def backward_word_extend_selection(self, e): #
+ """Move back to the start of the current or previous word. Words are
+ composed of letters and digits."""
+ self.l_buffer.backward_word_extend_selection(self.argument_reset)
+ self.finalize()
+
+ def forward_word_end_extend_selection(self, e): #
+ """Move forward to the end of the next word. Words are composed of
+ letters and digits."""
+ self.l_buffer.forward_word_end_extend_selection(self.argument_reset)
+ self.finalize()
+
+ def backward_word_end_extend_selection(self, e): #
+ """Move forward to the end of the next word. Words are composed of
+ letters and digits."""
+ self.l_buffer.forward_word_end_extend_selection(self.argument_reset)
+ self.finalize()
+
+
+######## Change case
+
+ def upcase_word(self, e): # (M-u)
+ """Uppercase the current (or following) word. With a negative
+ argument, uppercase the previous word, but do not move the cursor."""
+ self.l_buffer.upcase_word()
+ self.finalize()
+
+ def downcase_word(self, e): # (M-l)
+ """Lowercase the current (or following) word. With a negative
+ argument, lowercase the previous word, but do not move the cursor."""
+ self.l_buffer.downcase_word()
+ self.finalize()
+
+ def capitalize_word(self, e): # (M-c)
+ """Capitalize the current (or following) word. With a negative
+ argument, capitalize the previous word, but do not move the cursor."""
+ self.l_buffer.capitalize_word()
+ self.finalize()
+
+
+########
+ def clear_screen(self, e): # (C-l)
+ """Clear the screen and redraw the current line, leaving the current
+ line at the top of the screen."""
+ self.console.page()
+ self.finalize()
+
+ def redraw_current_line(self, e): # ()
+ """Refresh the current line. By default, this is unbound."""
+ self.finalize()
+
+ def accept_line(self, e): # (Newline or Return)
+ """Accept the line regardless of where the cursor is. If this line
+ is non-empty, it may be added to the history list for future recall
+ with add_history(). If this line is a modified history line, the
+ history line is restored to its original state."""
+ self.finalize()
+ return True
+
+ def delete_char(self, e): # (C-d)
+ """Delete the character at point. If point is at the beginning of
+ the line, there are no characters in the line, and the last
+ character typed was not bound to delete-char, then return EOF."""
+ self.l_buffer.delete_char(self.argument_reset)
+ self.finalize()
+
+ def backward_delete_char(self, e): # (Rubout)
+ """Delete the character behind the cursor. A numeric argument means
+ to kill the characters instead of deleting them."""
+ self.l_buffer.backward_delete_char(self.argument_reset)
+ self.finalize()
+
+ def backward_delete_word(self, e): # (Control-Rubout)
+ """Delete the character behind the cursor. A numeric argument means
+ to kill the characters instead of deleting them."""
+ self.l_buffer.backward_delete_word(self.argument_reset)
+ self.finalize()
+
+ def forward_delete_word(self, e): # (Control-Delete)
+ """Delete the character behind the cursor. A numeric argument means
+ to kill the characters instead of deleting them."""
+ self.l_buffer.forward_delete_word(self.argument_reset)
+ self.finalize()
+
+ def delete_horizontal_space(self, e): # ()
+ """Delete all spaces and tabs around point. By default, this is unbound. """
+ self.l_buffer.delete_horizontal_space()
+ self.finalize()
+
+ def self_insert(self, e): # (a, b, A, 1, !, ...)
+ """Insert yourself. """
+ if e.char and ord(e.char)!=0: #don't insert null character in buffer, can happen with dead keys.
+ self.insert_text(e.char)
+ self.finalize()
+
+
+# Paste from clipboard
+
+ def paste(self,e):
+ """Paste windows clipboard.
+ Assume single line strip other lines and end of line markers and trailing spaces""" #(Control-v)
+ if self.enable_win32_clipboard:
+ txt=clipboard.get_clipboard_text_and_convert(False)
+ txt=txt.split("\n")[0].strip("\r").strip("\n")
+ log("paste: >%s<"%list(map(ord,txt)))
+ self.insert_text(txt)
+ self.finalize()
+
+ def paste_mulitline_code(self,e):
+ """Paste windows clipboard as multiline code.
+ Removes any empty lines in the code"""
+ reg=re.compile("\r?\n")
+ if self.enable_win32_clipboard:
+ txt=clipboard.get_clipboard_text_and_convert(False)
+ t=reg.split(txt)
+ t=[row for row in t if row.strip()!=""] #remove empty lines
+ if t!=[""]:
+ self.insert_text(t[0])
+ self.add_history(self.l_buffer.copy())
+ self.paste_line_buffer=t[1:]
+ log("multi: >%s<"%self.paste_line_buffer)
+ return True
+ else:
+ return False
+ self.finalize()
+
+ def ipython_paste(self,e):
+ """Paste windows clipboard. If enable_ipython_paste_list_of_lists is
+ True then try to convert tabseparated data to repr of list of lists or
+ repr of array.
+ If enable_ipython_paste_for_paths==True then change \\ to / and spaces to \space"""
+ if self.enable_win32_clipboard:
+ txt=clipboard.get_clipboard_text_and_convert(
+ self.enable_ipython_paste_list_of_lists)
+ if self.enable_ipython_paste_for_paths:
+ if len(txt)<300 and ("\t" not in txt) and ("\n" not in txt):
+ txt=txt.replace("\\","/").replace(" ",r"\ ")
+ self.insert_text(txt)
+ self.finalize()
+
+
+ def copy_region_to_clipboard(self, e): # ()
+ """Copy the text in the region to the windows clipboard."""
+ self.l_buffer.copy_region_to_clipboard()
+ self.finalize()
+
+ def copy_selection_to_clipboard(self, e): # ()
+ """Copy the text in the region to the windows clipboard."""
+ self.l_buffer.copy_selection_to_clipboard()
+ self.finalize()
+
+ def cut_selection_to_clipboard(self, e): # ()
+ """Copy the text in the region to the windows clipboard."""
+ self.l_buffer.cut_selection_to_clipboard()
+ self.finalize()
+
+ def dump_functions(self, e): # ()
+ """Print all of the functions and their key bindings to the Readline
+ output stream. If a numeric argument is supplied, the output is
+ formatted in such a way that it can be made part of an inputrc
+ file. This command is unbound by default."""
+ print()
+ txt="\n".join(self.rl_settings_to_string())
+ print(txt)
+ self._print_prompt()
+ self.finalize()
+
+def commonprefix(m):
+ "Given a list of pathnames, returns the longest common leading component"
+ if not m: return ''
+ prefix = m[0]
+ for item in m:
+ for i in range(len(prefix)):
+ if prefix[:i+1].lower() != item[:i+1].lower():
+ prefix = prefix[:i]
+ if i == 0: return ''
+ break
+ return prefix
diff --git a/pyreadline/modes/emacs.py b/pyreadline/modes/emacs.py
new file mode 100644
index 0000000..f007d2d
--- /dev/null
+++ b/pyreadline/modes/emacs.py
@@ -0,0 +1,730 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import os, sys, time
+import pyreadline.logger as logger
+from pyreadline.logger import log
+from pyreadline.lineeditor.lineobj import Point
+import pyreadline.lineeditor.lineobj as lineobj
+import pyreadline.lineeditor.history as history
+from . import basemode
+from pyreadline.unicode_helper import ensure_unicode
+
+
+def format(keyinfo):
+ if len(keyinfo[-1]) != 1:
+ k = keyinfo + (-1,)
+ else:
+ k = keyinfo + (ord(keyinfo[-1]),)
+ return "(%s,%s,%s,%s,%x)"%k
+
+in_ironpython = "IronPython" in sys.version
+
+
+class IncrementalSearchPromptMode(object):
+ def __init__(self, rlobj):
+ pass
+
+ def _process_incremental_search_keyevent(self, keyinfo):
+ log("_process_incremental_search_keyevent")
+ keytuple = keyinfo.tuple()
+ #dispatch_func = self.key_dispatch.get(keytuple, default)
+ revtuples = []
+ fwdtuples = []
+ for ktuple, func in self.key_dispatch.items():
+ if func == self.reverse_search_history:
+ revtuples.append(ktuple)
+ elif func == self.forward_search_history:
+ fwdtuples.append(ktuple)
+
+
+ log("IncrementalSearchPromptMode %s %s"%(keyinfo, keytuple))
+ if keyinfo.keyname == 'backspace':
+ self.subsearch_query = self.subsearch_query[:-1]
+ if len(self.subsearch_query) > 0:
+ self.line = self.subsearch_fun(self.subsearch_query)
+ else:
+ self._bell()
+ self.line = "" # empty query means no search result
+ elif keyinfo.keyname in ['return', 'escape']:
+ self._bell()
+ self.prompt = self.subsearch_oldprompt
+ self.process_keyevent_queue = self.process_keyevent_queue[:-1]
+ self._history.history_cursor = len(self._history.history)
+ if keyinfo.keyname == 'escape':
+ self.l_buffer.set_line(self.subsearch_old_line)
+ return True
+ elif keyinfo.keyname:
+ pass
+ elif keytuple in revtuples:
+ self.subsearch_fun = self._history.reverse_search_history
+ self.subsearch_prompt = "reverse-i-search%d`%s': "
+ self.line = self.subsearch_fun(self.subsearch_query)
+ elif keytuple in fwdtuples:
+ self.subsearch_fun = self._history.forward_search_history
+ self.subsearch_prompt = "forward-i-search%d`%s': "
+ self.line = self.subsearch_fun(self.subsearch_query)
+ elif keyinfo.control == False and keyinfo.meta == False:
+ self.subsearch_query += keyinfo.char
+ self.line = self.subsearch_fun(self.subsearch_query)
+ else:
+ pass
+ self.prompt = self.subsearch_prompt%(self._history.history_cursor, self.subsearch_query)
+ self.l_buffer.set_line(self.line)
+
+ def _init_incremental_search(self, searchfun, init_event):
+ """Initialize search prompt
+ """
+ log("init_incremental_search")
+ self.subsearch_query = ''
+ self.subsearch_fun = searchfun
+ self.subsearch_old_line = self.l_buffer.get_line_text()
+
+ queue = self.process_keyevent_queue
+ queue.append(self._process_incremental_search_keyevent)
+
+ self.subsearch_oldprompt = self.prompt
+
+ if (self.previous_func != self.reverse_search_history and
+ self.previous_func != self.forward_search_history):
+ self.subsearch_query = self.l_buffer[0:Point].get_line_text()
+
+ if self.subsearch_fun == self.reverse_search_history:
+ self.subsearch_prompt = "reverse-i-search%d`%s': "
+ else:
+ self.subsearch_prompt = "forward-i-search%d`%s': "
+
+ self.prompt = self.subsearch_prompt%(self._history.history_cursor, "")
+
+ if self.subsearch_query:
+ self.line = self._process_incremental_search_keyevent(init_event)
+ else:
+ self.line = ""
+
+
+class SearchPromptMode(object):
+ def __init__(self, rlobj):
+ pass
+
+ def _process_non_incremental_search_keyevent(self, keyinfo):
+ keytuple = keyinfo.tuple()
+ log("SearchPromptMode %s %s"%(keyinfo, keytuple))
+ history = self._history
+
+ if keyinfo.keyname == 'backspace':
+ self.non_inc_query = self.non_inc_query[:-1]
+ elif keyinfo.keyname in ['return', 'escape']:
+ if self.non_inc_query:
+ if self.non_inc_direction == -1:
+ res = history.reverse_search_history(self.non_inc_query)
+ else:
+ res = history.forward_search_history(self.non_inc_query)
+
+ self._bell()
+ self.prompt = self.non_inc_oldprompt
+ self.process_keyevent_queue = self.process_keyevent_queue[:-1]
+ self._history.history_cursor = len(self._history.history)
+ if keyinfo.keyname == 'escape':
+ self.l_buffer = self.non_inc_oldline
+ else:
+ self.l_buffer.set_line(res)
+ return False
+ elif keyinfo.keyname:
+ pass
+ elif keyinfo.control == False and keyinfo.meta == False:
+ self.non_inc_query += keyinfo.char
+ else:
+ pass
+ self.prompt = self.non_inc_oldprompt + ":" + self.non_inc_query
+
+ def _init_non_i_search(self, direction):
+ self.non_inc_direction = direction
+ self.non_inc_query = ""
+ self.non_inc_oldprompt = self.prompt
+ self.non_inc_oldline = self.l_buffer.copy()
+ self.l_buffer.reset_line()
+ self.prompt = self.non_inc_oldprompt + ":"
+ queue = self.process_keyevent_queue
+ queue.append(self._process_non_incremental_search_keyevent)
+
+ def non_incremental_reverse_search_history(self, e): # (M-p)
+ '''Search backward starting at the current line and moving up
+ through the history as necessary using a non-incremental search for
+ a string supplied by the user.'''
+ return self._init_non_i_search(-1)
+
+ def non_incremental_forward_search_history(self, e): # (M-n)
+ '''Search forward starting at the current line and moving down
+ through the the history as necessary using a non-incremental search
+ for a string supplied by the user.'''
+ return self._init_non_i_search(1)
+
+
+class LeaveModeTryNext(Exception):
+ pass
+
+
+class DigitArgumentMode(object):
+ def __init__(self, rlobj):
+ pass
+
+ def _process_digit_argument_keyevent(self, keyinfo):
+ log("DigitArgumentMode.keyinfo %s"%keyinfo)
+ keytuple = keyinfo.tuple()
+ log("DigitArgumentMode.keytuple %s %s"%(keyinfo, keytuple))
+ if keyinfo.keyname in ['return']:
+ self.prompt = self._digit_argument_oldprompt
+ self.process_keyevent_queue = self.process_keyevent_queue[:-1]
+ return True
+ elif keyinfo.keyname:
+ pass
+ elif (keyinfo.char in "0123456789" and
+ keyinfo.control == False and
+ keyinfo.meta == False):
+ log("arg %s %s"%(self.argument, keyinfo.char))
+ self.argument = self.argument * 10 + int(keyinfo.char)
+ else:
+ self.prompt = self._digit_argument_oldprompt
+ raise LeaveModeTryNext
+ self.prompt = "(arg: %s) "%self.argument
+
+ def _init_digit_argument(self, keyinfo):
+ """Initialize search prompt
+ """
+ c = self.console
+ line = self.l_buffer.get_line_text()
+ self._digit_argument_oldprompt = self.prompt
+ queue = self.process_keyevent_queue
+ queue = self.process_keyevent_queue
+ queue.append(self._process_digit_argument_keyevent)
+
+ if keyinfo.char == "-":
+ self.argument = -1
+ elif keyinfo.char in "0123456789":
+ self.argument = int(keyinfo.char)
+ log("<%s> %s"%(self.argument, type(self.argument)))
+ self.prompt = "(arg: %s) "%self.argument
+ log("arg-init %s %s"%(self.argument, keyinfo.char))
+
+
+class EmacsMode(DigitArgumentMode, IncrementalSearchPromptMode,
+ SearchPromptMode, basemode.BaseMode):
+ mode = "emacs"
+
+ def __init__(self, rlobj):
+ basemode.BaseMode.__init__(self, rlobj)
+ IncrementalSearchPromptMode.__init__(self, rlobj)
+ SearchPromptMode.__init__(self, rlobj)
+ DigitArgumentMode.__init__(self, rlobj)
+ self._keylog = (lambda x, y: None)
+ self.previous_func = None
+ self.prompt = ">>> "
+ self._insert_verbatim = False
+ self.next_meta = False # True to force meta on next character
+
+ self.process_keyevent_queue = [self._process_keyevent]
+
+ def __repr__(self):
+ return "<EmacsMode>"
+
+ def add_key_logger(self, logfun):
+ """logfun should be function that takes disp_fun and line_"""\
+ """buffer object """
+ self._keylog = logfun
+
+ def process_keyevent(self, keyinfo):
+ try:
+ r = self.process_keyevent_queue[-1](keyinfo)
+ except LeaveModeTryNext:
+ self.process_keyevent_queue = self.process_keyevent_queue[:-1]
+ r = self.process_keyevent(keyinfo)
+ if r:
+ self.add_history(self.l_buffer.copy())
+ return True
+ return False
+
+ def _process_keyevent(self, keyinfo):
+ """return True when line is final
+ """
+ #Process exit keys. Only exit on empty line
+ log("_process_keyevent <%s>"%keyinfo)
+
+ def nop(e):
+ pass
+ if self.next_meta:
+ self.next_meta = False
+ keyinfo.meta = True
+ keytuple = keyinfo.tuple()
+
+ if self._insert_verbatim:
+ self.insert_text(keyinfo)
+ self._insert_verbatim = False
+ self.argument = 0
+ return False
+
+ if keytuple in self.exit_dispatch:
+ pars = (self.l_buffer, lineobj.EndOfLine(self.l_buffer))
+ log("exit_dispatch:<%s, %s>"%pars)
+ if lineobj.EndOfLine(self.l_buffer) == 0:
+ raise EOFError
+ if keyinfo.keyname or keyinfo.control or keyinfo.meta:
+ default = nop
+ else:
+ default = self.self_insert
+ dispatch_func = self.key_dispatch.get(keytuple, default)
+
+ log("readline from keyboard:<%s,%s>"%(keytuple, dispatch_func))
+
+ r = None
+ if dispatch_func:
+ r = dispatch_func(keyinfo)
+ self._keylog(dispatch_func, self.l_buffer)
+ self.l_buffer.push_undo()
+
+ self.previous_func = dispatch_func
+ return r
+
+######### History commands
+ def previous_history(self, e): # (C-p)
+ '''Move back through the history list, fetching the previous
+ command. '''
+ self._history.previous_history(self.l_buffer)
+ self.l_buffer.point = lineobj.EndOfLine
+ self.finalize()
+
+ def next_history(self, e): # (C-n)
+ '''Move forward through the history list, fetching the next
+ command. '''
+ self._history.next_history(self.l_buffer)
+ self.finalize()
+
+ def beginning_of_history(self, e): # (M-<)
+ '''Move to the first line in the history.'''
+ self._history.beginning_of_history()
+ self.finalize()
+
+ def end_of_history(self, e): # (M->)
+ '''Move to the end of the input history, i.e., the line currently
+ being entered.'''
+ self._history.end_of_history(self.l_buffer)
+ self.finalize()
+
+ def reverse_search_history(self, e): # (C-r)
+ '''Search backward starting at the current line and moving up
+ through the history as necessary. This is an incremental search.'''
+ log("rev_search_history")
+ self._init_incremental_search(self._history.reverse_search_history, e)
+ self.finalize()
+
+ def forward_search_history(self, e): # (C-s)
+ '''Search forward starting at the current line and moving down
+ through the the history as necessary. This is an incremental
+ search.'''
+ log("fwd_search_history")
+ self._init_incremental_search(self._history.forward_search_history, e)
+ self.finalize()
+
+ def history_search_forward(self, e): # ()
+ '''Search forward through the history for the string of characters
+ between the start of the current line and the point. This is a
+ non-incremental search. By default, this command is unbound.'''
+ if (self.previous_func and
+ hasattr(self._history, self.previous_func.__name__)):
+ self._history.lastcommand = getattr(self._history,
+ self.previous_func.__name__)
+ else:
+ self._history.lastcommand = None
+ q = self._history.history_search_forward(self.l_buffer)
+ self.l_buffer = q
+ self.l_buffer.point = q.point
+ self.finalize()
+
+ def history_search_backward(self, e): # ()
+ '''Search backward through the history for the string of characters
+ between the start of the current line and the point. This is a
+ non-incremental search. By default, this command is unbound.'''
+ if (self.previous_func and
+ hasattr(self._history, self.previous_func.__name__)):
+ self._history.lastcommand = getattr(self._history,
+ self.previous_func.__name__)
+ else:
+ self._history.lastcommand = None
+ q = self._history.history_search_backward(self.l_buffer)
+ self.l_buffer = q
+ self.l_buffer.point = q.point
+ self.finalize()
+
+ def yank_nth_arg(self, e): # (M-C-y)
+ '''Insert the first argument to the previous command (usually the
+ second word on the previous line) at point. With an argument n,
+ insert the nth word from the previous command (the words in the
+ previous command begin with word 0). A negative argument inserts the
+ nth word from the end of the previous command.'''
+ self.finalize()
+
+ def yank_last_arg(self, e): # (M-. or M-_)
+ '''Insert last argument to the previous command (the last word of
+ the previous history entry). With an argument, behave exactly like
+ yank-nth-arg. Successive calls to yank-last-arg move back through
+ the history list, inserting the last argument of each line in turn.'''
+ self.finalize()
+
+ def forward_backward_delete_char(self, e): # ()
+ '''Delete the character under the cursor, unless the cursor is at
+ the end of the line, in which case the character behind the cursor
+ is deleted. By default, this is not bound to a key.'''
+ self.finalize()
+
+ def quoted_insert(self, e): # (C-q or C-v)
+ '''Add the next character typed to the line verbatim. This is how to
+ insert key sequences like C-q, for example.'''
+ self._insert_verbatim = True
+ self.finalize()
+
+ def tab_insert(self, e): # (M-TAB)
+ '''Insert a tab character. '''
+ cursor = min(self.l_buffer.point, len(self.l_buffer.line_buffer))
+ ws = ' ' * (self.tabstop - (cursor % self.tabstop))
+ self.insert_text(ws)
+ self.finalize()
+
+ def transpose_chars(self, e): # (C-t)
+ '''Drag the character before the cursor forward over the character
+ at the cursor, moving the cursor forward as well. If the insertion
+ point is at the end of the line, then this transposes the last two
+ characters of the line. Negative arguments have no effect.'''
+ self.l_buffer.transpose_chars()
+ self.finalize()
+
+ def transpose_words(self, e): # (M-t)
+ '''Drag the word before point past the word after point, moving
+ point past that word as well. If the insertion point is at the end
+ of the line, this transposes the last two words on the line.'''
+ self.l_buffer.transpose_words()
+ self.finalize()
+
+ def overwrite_mode(self, e): # ()
+ '''Toggle overwrite mode. With an explicit positive numeric
+ argument, switches to overwrite mode. With an explicit non-positive
+ numeric argument, switches to insert mode. This command affects only
+ emacs mode; vi mode does overwrite differently. Each call to
+ readline() starts in insert mode. In overwrite mode, characters
+ bound to self-insert replace the text at point rather than pushing
+ the text to the right. Characters bound to backward-delete-char
+ replace the character before point with a space.'''
+ self.finalize()
+
+ def kill_line(self, e): # (C-k)
+ '''Kill the text from point to the end of the line. '''
+ self.l_buffer.kill_line()
+ self.finalize()
+
+ def backward_kill_line(self, e): # (C-x Rubout)
+ '''Kill backward to the beginning of the line. '''
+ self.l_buffer.backward_kill_line()
+ self.finalize()
+
+ def unix_line_discard(self, e): # (C-u)
+ '''Kill backward from the cursor to the beginning of the current
+ line. '''
+ # how is this different from backward_kill_line?
+ self.l_buffer.unix_line_discard()
+ self.finalize()
+
+ def kill_whole_line(self, e): # ()
+ '''Kill all characters on the current line, no matter where point
+ is. By default, this is unbound.'''
+ self.l_buffer.kill_whole_line()
+ self.finalize()
+
+ def kill_word(self, e): # (M-d)
+ '''Kill from point to the end of the current word, or if between
+ words, to the end of the next word. Word boundaries are the same as
+ forward-word.'''
+ self.l_buffer.kill_word()
+ self.finalize()
+
+ forward_kill_word = kill_word
+
+ def backward_kill_word(self, e): # (M-DEL)
+ '''Kill the word behind point. Word boundaries are the same as
+ backward-word. '''
+ self.l_buffer.backward_kill_word()
+ self.finalize()
+
+ def unix_word_rubout(self, e): # (C-w)
+ '''Kill the word behind point, using white space as a word
+ boundary. The killed text is saved on the kill-ring.'''
+ self.l_buffer.unix_word_rubout()
+ self.finalize()
+
+ def kill_region(self, e): # ()
+ '''Kill the text in the current region. By default, this command is
+ unbound. '''
+ self.finalize()
+
+ def copy_region_as_kill(self, e): # ()
+ '''Copy the text in the region to the kill buffer, so it can be
+ yanked right away. By default, this command is unbound.'''
+ self.finalize()
+
+ def copy_backward_word(self, e): # ()
+ '''Copy the word before point to the kill buffer. The word
+ boundaries are the same as backward-word. By default, this command
+ is unbound.'''
+ self.finalize()
+
+ def copy_forward_word(self, e): # ()
+ '''Copy the word following point to the kill buffer. The word
+ boundaries are the same as forward-word. By default, this command is
+ unbound.'''
+ self.finalize()
+
+ def yank(self, e): # (C-y)
+ '''Yank the top of the kill ring into the buffer at point. '''
+ self.l_buffer.yank()
+ self.finalize()
+
+ def yank_pop(self, e): # (M-y)
+ '''Rotate the kill-ring, and yank the new top. You can only do this
+ if the prior command is yank or yank-pop.'''
+ self.l_buffer.yank_pop()
+ self.finalize()
+
+ def delete_char_or_list(self, e): # ()
+ '''Deletes the character under the cursor if not at the beginning or
+ end of the line (like delete-char). If at the end of the line,
+ behaves identically to possible-completions. This command is unbound
+ by default.'''
+ self.finalize()
+
+ def start_kbd_macro(self, e): # (C-x ()
+ '''Begin saving the characters typed into the current keyboard
+ macro. '''
+ self.finalize()
+
+ def end_kbd_macro(self, e): # (C-x ))
+ '''Stop saving the characters typed into the current keyboard macro
+ and save the definition.'''
+ self.finalize()
+
+ def call_last_kbd_macro(self, e): # (C-x e)
+ '''Re-execute the last keyboard macro defined, by making the
+ characters in the macro appear as if typed at the keyboard.'''
+ self.finalize()
+
+ def re_read_init_file(self, e): # (C-x C-r)
+ '''Read in the contents of the inputrc file, and incorporate any
+ bindings or variable assignments found there.'''
+ self.finalize()
+
+ def abort(self, e): # (C-g)
+ '''Abort the current editing command and ring the terminals bell
+ (subject to the setting of bell-style).'''
+ self._bell()
+ self.finalize()
+
+ def do_uppercase_version(self, e): # (M-a, M-b, M-x, ...)
+ '''If the metafied character x is lowercase, run the command that is
+ bound to the corresponding uppercase character.'''
+ self.finalize()
+
+ def prefix_meta(self, e): # (ESC)
+ '''Metafy the next character typed. This is for keyboards without a
+ meta key. Typing ESC f is equivalent to typing M-f. '''
+ self.next_meta = True
+ self.finalize()
+
+ def undo(self, e): # (C-_ or C-x C-u)
+ '''Incremental undo, separately remembered for each line.'''
+ self.l_buffer.pop_undo()
+ self.finalize()
+
+ def revert_line(self, e): # (M-r)
+ '''Undo all changes made to this line. This is like executing the
+ undo command enough times to get back to the beginning.'''
+ self.finalize()
+
+ def tilde_expand(self, e): # (M-~)
+ '''Perform tilde expansion on the current word.'''
+ self.finalize()
+
+ def set_mark(self, e): # (C-@)
+ '''Set the mark to the point. If a numeric argument is supplied, the
+ mark is set to that position.'''
+ self.l_buffer.set_mark()
+ self.finalize()
+
+ def exchange_point_and_mark(self, e): # (C-x C-x)
+ '''Swap the point with the mark. The current cursor position is set
+ to the saved position, and the old cursor position is saved as the
+ mark.'''
+ self.finalize()
+
+ def character_search(self, e): # (C-])
+ '''A character is read and point is moved to the next occurrence of
+ that character. A negative count searches for previous occurrences.'''
+ self.finalize()
+
+ def character_search_backward(self, e): # (M-C-])
+ '''A character is read and point is moved to the previous occurrence
+ of that character. A negative count searches for subsequent
+ occurrences.'''
+ self.finalize()
+
+ def insert_comment(self, e): # (M-#)
+ '''Without a numeric argument, the value of the comment-begin
+ variable is inserted at the beginning of the current line. If a
+ numeric argument is supplied, this command acts as a toggle: if the
+ characters at the beginning of the line do not match the value of
+ comment-begin, the value is inserted, otherwise the characters in
+ comment-begin are deleted from the beginning of the line. In either
+ case, the line is accepted as if a newline had been typed.'''
+ self.finalize()
+
+ def dump_variables(self, e): # ()
+ '''Print all of the settable variables and their values to the
+ Readline output stream. If a numeric argument is supplied, the
+ output is formatted in such a way that it can be made part of an
+ inputrc file. This command is unbound by default.'''
+ self.finalize()
+
+ def dump_macros(self, e): # ()
+ '''Print all of the Readline key sequences bound to macros and the
+ strings they output. If a numeric argument is supplied, the output
+ is formatted in such a way that it can be made part of an inputrc
+ file. This command is unbound by default.'''
+ self.finalize()
+
+ def digit_argument(self, e): # (M-0, M-1, ... M--)
+ '''Add this digit to the argument already accumulating, or start a
+ new argument. M-- starts a negative argument.'''
+ self._init_digit_argument(e)
+ #Should not finalize
+
+ def universal_argument(self, e): # ()
+ '''This is another way to specify an argument. If this command is
+ followed by one or more digits, optionally with a leading minus
+ sign, those digits define the argument. If the command is followed
+ by digits, executing universal-argument again ends the numeric
+ argument, but is otherwise ignored. As a special case, if this
+ command is immediately followed by a character that is neither a
+ digit or minus sign, the argument count for the next command is
+ multiplied by four. The argument count is initially one, so
+ executing this function the first time makes the argument count
+ four, a second time makes the argument count sixteen, and so on. By
+ default, this is not bound to a key.'''
+ #Should not finalize
+
+ #Create key bindings:
+ def init_editing_mode(self, e): # (C-e)
+ '''When in vi command mode, this causes a switch to emacs editing
+ mode.'''
+ self._bind_exit_key('Control-d')
+ self._bind_exit_key('Control-z')
+
+ # I often accidentally hold the shift or control while typing space
+ self._bind_key('space', self.self_insert)
+ self._bind_key('Shift-space', self.self_insert)
+ self._bind_key('Control-space', self.self_insert)
+ self._bind_key('Return', self.accept_line)
+ self._bind_key('Left', self.backward_char)
+ self._bind_key('Control-b', self.backward_char)
+ self._bind_key('Right', self.forward_char)
+ self._bind_key('Control-f', self.forward_char)
+ self._bind_key('Control-h', self.backward_delete_char)
+ self._bind_key('BackSpace', self.backward_delete_char)
+ self._bind_key('Control-BackSpace', self.backward_delete_word)
+
+ self._bind_key('Home', self.beginning_of_line)
+ self._bind_key('End', self.end_of_line)
+ self._bind_key('Delete', self.delete_char)
+ self._bind_key('Control-d', self.delete_char)
+ self._bind_key('Clear', self.clear_screen)
+ self._bind_key('Alt-f', self.forward_word)
+ self._bind_key('Alt-b', self.backward_word)
+ self._bind_key('Control-l', self.clear_screen)
+ self._bind_key('Control-p', self.previous_history)
+ self._bind_key('Up', self.history_search_backward)
+ self._bind_key('Control-n', self.next_history)
+ self._bind_key('Down', self.history_search_forward)
+ self._bind_key('Control-a', self.beginning_of_line)
+ self._bind_key('Control-e', self.end_of_line)
+ self._bind_key('Alt-<', self.beginning_of_history)
+ self._bind_key('Alt->', self.end_of_history)
+ self._bind_key('Control-r', self.reverse_search_history)
+ self._bind_key('Control-s', self.forward_search_history)
+ self._bind_key('Control-Shift-r', self.forward_search_history)
+ self._bind_key('Alt-p',
+ self.non_incremental_reverse_search_history)
+ self._bind_key('Alt-n',
+ self.non_incremental_forward_search_history)
+ self._bind_key('Control-z', self.undo)
+ self._bind_key('Control-_', self.undo)
+ self._bind_key('Escape', self.kill_whole_line)
+ self._bind_key('Meta-d', self.kill_word)
+ self._bind_key('Control-Delete', self.forward_delete_word)
+ self._bind_key('Control-w', self.unix_word_rubout)
+ #self._bind_key('Control-Shift-v', self.quoted_insert)
+ self._bind_key('Control-v', self.paste)
+ self._bind_key('Alt-v', self.ipython_paste)
+ self._bind_key('Control-y', self.yank)
+ self._bind_key('Control-k', self.kill_line)
+ self._bind_key('Control-m', self.set_mark)
+ self._bind_key('Control-q', self.copy_region_to_clipboard)
+# self._bind_key('Control-shift-k', self.kill_whole_line)
+ self._bind_key('Control-Shift-v', self.paste_mulitline_code)
+ self._bind_key("Control-Right", self.forward_word_end)
+ self._bind_key("Control-Left", self.backward_word)
+ self._bind_key("Shift-Right",
+ self.forward_char_extend_selection)
+ self._bind_key("Shift-Left",
+ self.backward_char_extend_selection)
+ self._bind_key("Shift-Control-Right",
+ self.forward_word_end_extend_selection)
+ self._bind_key("Shift-Control-Left",
+ self.backward_word_extend_selection)
+ self._bind_key("Shift-Home",
+ self.beginning_of_line_extend_selection)
+ self._bind_key("Shift-End",
+ self.end_of_line_extend_selection)
+ self._bind_key("numpad0", self.self_insert)
+ self._bind_key("numpad1", self.self_insert)
+ self._bind_key("numpad2", self.self_insert)
+ self._bind_key("numpad3", self.self_insert)
+ self._bind_key("numpad4", self.self_insert)
+ self._bind_key("numpad5", self.self_insert)
+ self._bind_key("numpad6", self.self_insert)
+ self._bind_key("numpad7", self.self_insert)
+ self._bind_key("numpad8", self.self_insert)
+ self._bind_key("numpad9", self.self_insert)
+ self._bind_key("add", self.self_insert)
+ self._bind_key("subtract", self.self_insert)
+ self._bind_key("multiply", self.self_insert)
+ self._bind_key("divide", self.self_insert)
+ self._bind_key("vk_decimal", self.self_insert)
+ log("RUNNING INIT EMACS")
+ for i in range(0, 10):
+ self._bind_key("alt-%d"%i, self.digit_argument)
+ self._bind_key("alt--", self.digit_argument)
+
+
+# make it case insensitive
+def commonprefix(m):
+ "Given a list of pathnames, returns the longest common leading component"
+ if not m:
+ return ''
+ prefix = m[0]
+ for item in m:
+ for i in range(len(prefix)):
+ if prefix[:i + 1].lower() != item[:i + 1].lower():
+ prefix = prefix[:i]
+ if i == 0:
+ return ''
+ break
+ return prefix
diff --git a/pyreadline/modes/notemacs.py b/pyreadline/modes/notemacs.py
new file mode 100644
index 0000000..c182a6d
--- /dev/null
+++ b/pyreadline/modes/notemacs.py
@@ -0,0 +1,602 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import os
+import pyreadline.logger as logger
+from pyreadline.logger import log
+import pyreadline.lineeditor.lineobj as lineobj
+import pyreadline.lineeditor.history as history
+from . import basemode
+
+class NotEmacsMode(basemode.BaseMode):
+ mode="notemacs"
+ def __init__(self,rlobj):
+ super(NotEmacsMode,self).__init__(rlobj)
+
+ def __repr__(self):
+ return "<NotEmacsMode>"
+
+ def _readline_from_keyboard(self):
+ c=self.console
+ while 1:
+ self._update_line()
+ event = c.getkeypress()
+ if self.next_meta:
+ self.next_meta = False
+ control, meta, shift, code = event.keyinfo
+ event.keyinfo = (control, True, shift, code)
+
+ #Process exit keys. Only exit on empty line
+ if event.keyinfo in self.exit_dispatch:
+ if lineobj.EndOfLine(self.l_buffer) == 0:
+ raise EOFError
+
+ dispatch_func = self.key_dispatch.get(event.keyinfo,self.self_insert)
+ log("readline from keyboard:%s"%(event.keyinfo,))
+ r = None
+ if dispatch_func:
+ r = dispatch_func(event)
+ self.l_buffer.push_undo()
+
+ self.previous_func = dispatch_func
+ if r:
+ self._update_line()
+ break
+
+ def readline(self, prompt=''):
+ '''Try to act like GNU readline.'''
+ # handle startup_hook
+ if self.first_prompt:
+ self.first_prompt = False
+ if self.startup_hook:
+ try:
+ self.startup_hook()
+ except:
+ print('startup hook failed')
+ traceback.print_exc()
+
+ c = self.console
+ self.l_buffer.reset_line()
+ self.prompt = prompt
+ self._print_prompt()
+
+ if self.pre_input_hook:
+ try:
+ self.pre_input_hook()
+ except:
+ print('pre_input_hook failed')
+ traceback.print_exc()
+ self.pre_input_hook = None
+
+ log("in readline: %s"%self.paste_line_buffer)
+ if len(self.paste_line_buffer)>0:
+ self.l_buffer=lineobj.ReadlineTextBuffer(self.paste_line_buffer[0])
+ self._update_line()
+ self.paste_line_buffer=self.paste_line_buffer[1:]
+ c.write('\r\n')
+ else:
+ self._readline_from_keyboard()
+ c.write('\r\n')
+
+ self.add_history(self.l_buffer.copy())
+
+ log('returning(%s)' % self.l_buffer.get_line_text())
+ return self.l_buffer.get_line_text() + '\n'
+
+ ### Methods below here are bindable emacs functions
+
+ def beginning_of_line(self, e): # (C-a)
+ '''Move to the start of the current line. '''
+ self.l_buffer.beginning_of_line()
+
+ def end_of_line(self, e): # (C-e)
+ '''Move to the end of the line. '''
+ self.l_buffer.end_of_line()
+
+ def forward_char(self, e): # (C-f)
+ '''Move forward a character. '''
+ self.l_buffer.forward_char()
+
+ def backward_char(self, e): # (C-b)
+ '''Move back a character. '''
+ self.l_buffer.backward_char()
+
+ def forward_word(self, e): # (M-f)
+ '''Move forward to the end of the next word. Words are composed of
+ letters and digits.'''
+ self.l_buffer.forward_word()
+
+ def backward_word(self, e): # (M-b)
+ '''Move back to the start of the current or previous word. Words are
+ composed of letters and digits.'''
+ self.l_buffer.backward_word()
+
+ def clear_screen(self, e): # (C-l)
+ '''Clear the screen and redraw the current line, leaving the current
+ line at the top of the screen.'''
+ self.console.page()
+
+ def redraw_current_line(self, e): # ()
+ '''Refresh the current line. By default, this is unbound.'''
+ pass
+
+ def accept_line(self, e): # (Newline or Return)
+ '''Accept the line regardless of where the cursor is. If this line
+ is non-empty, it may be added to the history list for future recall
+ with add_history(). If this line is a modified history line, the
+ history line is restored to its original state.'''
+ return True
+
+######### History commands
+ def previous_history(self, e): # (C-p)
+ '''Move back through the history list, fetching the previous command. '''
+ self._history.previous_history(self.l_buffer)
+
+ def next_history(self, e): # (C-n)
+ '''Move forward through the history list, fetching the next command. '''
+ self._history.next_history(self.l_buffer)
+
+ def beginning_of_history(self, e): # (M-<)
+ '''Move to the first line in the history.'''
+ self._history.beginning_of_history()
+
+ def end_of_history(self, e): # (M->)
+ '''Move to the end of the input history, i.e., the line currently
+ being entered.'''
+ self._history.end_of_history(self.l_buffer)
+
+ def _i_search(self, searchfun, direction, init_event):
+ c = self.console
+ line = self.get_line_buffer()
+ query = ''
+ hc_start = self._history.history_cursor #+ direction
+ while 1:
+ x, y = self.prompt_end_pos
+ c.pos(0, y)
+ if direction < 0:
+ prompt = 'reverse-i-search'
+ else:
+ prompt = 'forward-i-search'
+
+ scroll = c.write_scrolling("%s`%s': %s" % (prompt, query, line))
+ self._update_prompt_pos(scroll)
+ self._clear_after()
+
+ event = c.getkeypress()
+ if event.keysym == 'BackSpace':
+ if len(query) > 0:
+ query = query[:-1]
+ self._history.history_cursor = hc_start
+ else:
+ self._bell()
+ elif event.char in string.letters + string.digits + string.punctuation + ' ':
+ self._history.history_cursor = hc_start
+ query += event.char
+ elif event.keyinfo == init_event.keyinfo:
+ self._history.history_cursor += direction
+ line=searchfun(query)
+ pass
+ else:
+ if event.keysym != 'Return':
+ self._bell()
+ break
+ line=searchfun(query)
+
+ px, py = self.prompt_begin_pos
+ c.pos(0, py)
+ self.l_buffer.set_line(line)
+ self._print_prompt()
+ self._history.history_cursor=len(self._history.history)
+
+ def reverse_search_history(self, e): # (C-r)
+ '''Search backward starting at the current line and moving up
+ through the history as necessary. This is an incremental search.'''
+# print "HEJ"
+# self.console.bell()
+ self._i_search(self._history.reverse_search_history, -1, e)
+
+ def forward_search_history(self, e): # (C-s)
+ '''Search forward starting at the current line and moving down
+ through the the history as necessary. This is an incremental search.'''
+# print "HEJ"
+# self.console.bell()
+ self._i_search(self._history.forward_search_history, 1, e)
+
+
+ def non_incremental_reverse_search_history(self, e): # (M-p)
+ '''Search backward starting at the current line and moving up
+ through the history as necessary using a non-incremental search for
+ a string supplied by the user.'''
+ self._history.non_incremental_reverse_search_history(self.l_buffer)
+
+ def non_incremental_forward_search_history(self, e): # (M-n)
+ '''Search forward starting at the current line and moving down
+ through the the history as necessary using a non-incremental search
+ for a string supplied by the user.'''
+ self._history.non_incremental_reverse_search_history(self.l_buffer)
+
+ def history_search_forward(self, e): # ()
+ '''Search forward through the history for the string of characters
+ between the start of the current line and the point. This is a
+ non-incremental search. By default, this command is unbound.'''
+ self.l_buffer=self._history.history_search_forward(self.l_buffer)
+
+ def history_search_backward(self, e): # ()
+ '''Search backward through the history for the string of characters
+ between the start of the current line and the point. This is a
+ non-incremental search. By default, this command is unbound.'''
+ self.l_buffer=self._history.history_search_backward(self.l_buffer)
+
+ def yank_nth_arg(self, e): # (M-C-y)
+ '''Insert the first argument to the previous command (usually the
+ second word on the previous line) at point. With an argument n,
+ insert the nth word from the previous command (the words in the
+ previous command begin with word 0). A negative argument inserts the
+ nth word from the end of the previous command.'''
+ pass
+
+ def yank_last_arg(self, e): # (M-. or M-_)
+ '''Insert last argument to the previous command (the last word of
+ the previous history entry). With an argument, behave exactly like
+ yank-nth-arg. Successive calls to yank-last-arg move back through
+ the history list, inserting the last argument of each line in turn.'''
+ pass
+
+ def delete_char(self, e): # (C-d)
+ '''Delete the character at point. If point is at the beginning of
+ the line, there are no characters in the line, and the last
+ character typed was not bound to delete-char, then return EOF.'''
+ self.l_buffer.delete_char()
+
+ def backward_delete_char(self, e): # (Rubout)
+ '''Delete the character behind the cursor. A numeric argument means
+ to kill the characters instead of deleting them.'''
+ self.l_buffer.backward_delete_char()
+
+ def forward_backward_delete_char(self, e): # ()
+ '''Delete the character under the cursor, unless the cursor is at
+ the end of the line, in which case the character behind the cursor
+ is deleted. By default, this is not bound to a key.'''
+ pass
+
+ def quoted_insert(self, e): # (C-q or C-v)
+ '''Add the next character typed to the line verbatim. This is how to
+ insert key sequences like C-q, for example.'''
+ e = self.console.getkeypress()
+ self.insert_text(e.char)
+
+ def tab_insert(self, e): # (M-TAB)
+ '''Insert a tab character. '''
+ cursor = min(self.l_buffer.point, len(self.l_buffer.line_buffer))
+ ws = ' ' * (self.tabstop - (cursor % self.tabstop))
+ self.insert_text(ws)
+
+ def self_insert(self, e): # (a, b, A, 1, !, ...)
+ '''Insert yourself. '''
+ if ord(e.char)!=0: #don't insert null character in buffer, can happen with dead keys.
+ self.insert_text(e.char)
+
+ def transpose_chars(self, e): # (C-t)
+ '''Drag the character before the cursor forward over the character
+ at the cursor, moving the cursor forward as well. If the insertion
+ point is at the end of the line, then this transposes the last two
+ characters of the line. Negative arguments have no effect.'''
+ self.l_buffer.transpose_chars()
+
+ def transpose_words(self, e): # (M-t)
+ '''Drag the word before point past the word after point, moving
+ point past that word as well. If the insertion point is at the end
+ of the line, this transposes the last two words on the line.'''
+ self.l_buffer.transpose_words()
+
+ def upcase_word(self, e): # (M-u)
+ '''Uppercase the current (or following) word. With a negative
+ argument, uppercase the previous word, but do not move the cursor.'''
+ self.l_buffer.upcase_word()
+
+ def downcase_word(self, e): # (M-l)
+ '''Lowercase the current (or following) word. With a negative
+ argument, lowercase the previous word, but do not move the cursor.'''
+ self.l_buffer.downcase_word()
+
+ def capitalize_word(self, e): # (M-c)
+ '''Capitalize the current (or following) word. With a negative
+ argument, capitalize the previous word, but do not move the cursor.'''
+ self.l_buffer.capitalize_word()
+
+ def overwrite_mode(self, e): # ()
+ '''Toggle overwrite mode. With an explicit positive numeric
+ argument, switches to overwrite mode. With an explicit non-positive
+ numeric argument, switches to insert mode. This command affects only
+ emacs mode; vi mode does overwrite differently. Each call to
+ readline() starts in insert mode. In overwrite mode, characters
+ bound to self-insert replace the text at point rather than pushing
+ the text to the right. Characters bound to backward-delete-char
+ replace the character before point with a space.'''
+ pass
+
+ def kill_line(self, e): # (C-k)
+ '''Kill the text from point to the end of the line. '''
+ self.l_buffer.kill_line()
+
+ def backward_kill_line(self, e): # (C-x Rubout)
+ '''Kill backward to the beginning of the line. '''
+ self.l_buffer.backward_kill_line()
+
+ def unix_line_discard(self, e): # (C-u)
+ '''Kill backward from the cursor to the beginning of the current line. '''
+ # how is this different from backward_kill_line?
+ self.l_buffer.unix_line_discard()
+
+ def kill_whole_line(self, e): # ()
+ '''Kill all characters on the current line, no matter where point
+ is. By default, this is unbound.'''
+ self.l_buffer.kill_whole_line()
+
+ def kill_word(self, e): # (M-d)
+ '''Kill from point to the end of the current word, or if between
+ words, to the end of the next word. Word boundaries are the same as
+ forward-word.'''
+ self.l_buffer.kill_word()
+
+ def backward_kill_word(self, e): # (M-DEL)
+ '''Kill the word behind point. Word boundaries are the same as
+ backward-word. '''
+ self.l_buffer.backward_kill_word()
+
+ def unix_word_rubout(self, e): # (C-w)
+ '''Kill the word behind point, using white space as a word
+ boundary. The killed text is saved on the kill-ring.'''
+ self.l_buffer.unix_word_rubout()
+
+ def delete_horizontal_space(self, e): # ()
+ '''Delete all spaces and tabs around point. By default, this is unbound. '''
+ pass
+
+ def kill_region(self, e): # ()
+ '''Kill the text in the current region. By default, this command is unbound. '''
+ pass
+
+ def copy_region_as_kill(self, e): # ()
+ '''Copy the text in the region to the kill buffer, so it can be
+ yanked right away. By default, this command is unbound.'''
+ pass
+
+ def copy_region_to_clipboard(self, e): # ()
+ '''Copy the text in the region to the windows clipboard.'''
+ if self.enable_win32_clipboard:
+ mark=min(self.l_buffer.mark,len(self.l_buffer.line_buffer))
+ cursor=min(self.l_buffer.point,len(self.l_buffer.line_buffer))
+ if self.l_buffer.mark==-1:
+ return
+ begin=min(cursor,mark)
+ end=max(cursor,mark)
+ toclipboard="".join(self.l_buffer.line_buffer[begin:end])
+ clipboard.SetClipboardText(str(toclipboard))
+
+ def copy_backward_word(self, e): # ()
+ '''Copy the word before point to the kill buffer. The word
+ boundaries are the same as backward-word. By default, this command
+ is unbound.'''
+ pass
+
+ def copy_forward_word(self, e): # ()
+ '''Copy the word following point to the kill buffer. The word
+ boundaries are the same as forward-word. By default, this command is
+ unbound.'''
+ pass
+
+ def paste(self,e):
+ '''Paste windows clipboard'''
+ if self.enable_win32_clipboard:
+ txt=clipboard.get_clipboard_text_and_convert(False)
+ self.insert_text(txt)
+
+ def paste_mulitline_code(self,e):
+ '''Paste windows clipboard'''
+ reg=re.compile("\r?\n")
+ if self.enable_win32_clipboard:
+ txt=clipboard.get_clipboard_text_and_convert(False)
+ t=reg.split(txt)
+ t=[row for row in t if row.strip()!=""] #remove empty lines
+ if t!=[""]:
+ self.insert_text(t[0])
+ self.add_history(self.l_buffer.copy())
+ self.paste_line_buffer=t[1:]
+ log("multi: %s"%self.paste_line_buffer)
+ return True
+ else:
+ return False
+
+ def ipython_paste(self,e):
+ '''Paste windows clipboard. If enable_ipython_paste_list_of_lists is
+ True then try to convert tabseparated data to repr of list of lists or
+ repr of array'''
+ if self.enable_win32_clipboard:
+ txt=clipboard.get_clipboard_text_and_convert(
+ self.enable_ipython_paste_list_of_lists)
+ if self.enable_ipython_paste_for_paths:
+ if len(txt)<300 and ("\t" not in txt) and ("\n" not in txt):
+ txt=txt.replace("\\", "/").replace(" ", r"\ ")
+ self.insert_text(txt)
+
+ def yank(self, e): # (C-y)
+ '''Yank the top of the kill ring into the buffer at point. '''
+ pass
+
+ def yank_pop(self, e): # (M-y)
+ '''Rotate the kill-ring, and yank the new top. You can only do this
+ if the prior command is yank or yank-pop.'''
+ pass
+
+
+ def digit_argument(self, e): # (M-0, M-1, ... M--)
+ '''Add this digit to the argument already accumulating, or start a
+ new argument. M-- starts a negative argument.'''
+ pass
+
+ def universal_argument(self, e): # ()
+ '''This is another way to specify an argument. If this command is
+ followed by one or more digits, optionally with a leading minus
+ sign, those digits define the argument. If the command is followed
+ by digits, executing universal-argument again ends the numeric
+ argument, but is otherwise ignored. As a special case, if this
+ command is immediately followed by a character that is neither a
+ digit or minus sign, the argument count for the next command is
+ multiplied by four. The argument count is initially one, so
+ executing this function the first time makes the argument count
+ four, a second time makes the argument count sixteen, and so on. By
+ default, this is not bound to a key.'''
+ pass
+
+ def delete_char_or_list(self, e): # ()
+ '''Deletes the character under the cursor if not at the beginning or
+ end of the line (like delete-char). If at the end of the line,
+ behaves identically to possible-completions. This command is unbound
+ by default.'''
+ pass
+
+ def start_kbd_macro(self, e): # (C-x ()
+ '''Begin saving the characters typed into the current keyboard macro. '''
+ pass
+
+ def end_kbd_macro(self, e): # (C-x ))
+ '''Stop saving the characters typed into the current keyboard macro
+ and save the definition.'''
+ pass
+
+ def call_last_kbd_macro(self, e): # (C-x e)
+ '''Re-execute the last keyboard macro defined, by making the
+ characters in the macro appear as if typed at the keyboard.'''
+ pass
+
+ def re_read_init_file(self, e): # (C-x C-r)
+ '''Read in the contents of the inputrc file, and incorporate any
+ bindings or variable assignments found there.'''
+ pass
+
+ def abort(self, e): # (C-g)
+ '''Abort the current editing command and ring the terminals bell
+ (subject to the setting of bell-style).'''
+ self._bell()
+
+ def do_uppercase_version(self, e): # (M-a, M-b, M-x, ...)
+ '''If the metafied character x is lowercase, run the command that is
+ bound to the corresponding uppercase character.'''
+ pass
+
+ def prefix_meta(self, e): # (ESC)
+ '''Metafy the next character typed. This is for keyboards without a
+ meta key. Typing ESC f is equivalent to typing M-f. '''
+ self.next_meta = True
+
+ def undo(self, e): # (C-_ or C-x C-u)
+ '''Incremental undo, separately remembered for each line.'''
+ self.l_buffer.pop_undo()
+
+ def revert_line(self, e): # (M-r)
+ '''Undo all changes made to this line. This is like executing the
+ undo command enough times to get back to the beginning.'''
+ pass
+
+ def tilde_expand(self, e): # (M-~)
+ '''Perform tilde expansion on the current word.'''
+ pass
+
+ def set_mark(self, e): # (C-@)
+ '''Set the mark to the point. If a numeric argument is supplied, the
+ mark is set to that position.'''
+ self.l_buffer.set_mark()
+
+ def exchange_point_and_mark(self, e): # (C-x C-x)
+ '''Swap the point with the mark. The current cursor position is set
+ to the saved position, and the old cursor position is saved as the
+ mark.'''
+ pass
+
+ def character_search(self, e): # (C-])
+ '''A character is read and point is moved to the next occurrence of
+ that character. A negative count searches for previous occurrences.'''
+ pass
+
+ def character_search_backward(self, e): # (M-C-])
+ '''A character is read and point is moved to the previous occurrence
+ of that character. A negative count searches for subsequent
+ occurrences.'''
+ pass
+
+ def insert_comment(self, e): # (M-#)
+ '''Without a numeric argument, the value of the comment-begin
+ variable is inserted at the beginning of the current line. If a
+ numeric argument is supplied, this command acts as a toggle: if the
+ characters at the beginning of the line do not match the value of
+ comment-begin, the value is inserted, otherwise the characters in
+ comment-begin are deleted from the beginning of the line. In either
+ case, the line is accepted as if a newline had been typed.'''
+ pass
+
+ def dump_functions(self, e): # ()
+ '''Print all of the functions and their key bindings to the Readline
+ output stream. If a numeric argument is supplied, the output is
+ formatted in such a way that it can be made part of an inputrc
+ file. This command is unbound by default.'''
+ pass
+
+ def dump_variables(self, e): # ()
+ '''Print all of the settable variables and their values to the
+ Readline output stream. If a numeric argument is supplied, the
+ output is formatted in such a way that it can be made part of an
+ inputrc file. This command is unbound by default.'''
+ pass
+
+ def dump_macros(self, e): # ()
+ '''Print all of the Readline key sequences bound to macros and the
+ strings they output. If a numeric argument is supplied, the output
+ is formatted in such a way that it can be made part of an inputrc
+ file. This command is unbound by default.'''
+ pass
+
+
+ #Create key bindings:
+
+ def init_editing_mode(self, e): # (C-e)
+ '''When in vi command mode, this causes a switch to emacs editing
+ mode.'''
+
+ self._bind_exit_key('Control-d')
+ self._bind_exit_key('Control-z')
+
+ # I often accidentally hold the shift or control while typing space
+ self._bind_key('Shift-space', self.self_insert)
+ self._bind_key('Control-space', self.self_insert)
+ self._bind_key('Return', self.accept_line)
+ self._bind_key('Left', self.backward_char)
+ self._bind_key('Control-b', self.backward_char)
+ self._bind_key('Right', self.forward_char)
+ self._bind_key('Control-f', self.forward_char)
+ self._bind_key('BackSpace', self.backward_delete_char)
+ self._bind_key('Home', self.beginning_of_line)
+ self._bind_key('End', self.end_of_line)
+ self._bind_key('Delete', self.delete_char)
+ self._bind_key('Control-d', self.delete_char)
+ self._bind_key('Clear', self.clear_screen)
+
+
+# make it case insensitive
+def commonprefix(m):
+ "Given a list of pathnames, returns the longest common leading component"
+ if not m: return ''
+ prefix = m[0]
+ for item in m:
+ for i in range(len(prefix)):
+ if prefix[:i+1].lower() != item[:i+1].lower():
+ prefix = prefix[:i]
+ if i == 0: return ''
+ break
+ return prefix
+
diff --git a/pyreadline/modes/vi.py b/pyreadline/modes/vi.py
new file mode 100644
index 0000000..8adedac
--- /dev/null
+++ b/pyreadline/modes/vi.py
@@ -0,0 +1,1174 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Michael Graz. <mgraz@plan10.com>
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import os
+import pyreadline.logger as logger
+from pyreadline.logger import log
+import pyreadline.lineeditor.lineobj as lineobj
+import pyreadline.lineeditor.history as history
+from . import basemode
+
+class ViMode(basemode.BaseMode):
+ mode="vi"
+ def __init__(self,rlobj):
+ super(ViMode,self).__init__(rlobj)
+ self.__vi_insert_mode = None
+
+ def __repr__(self):
+ return "<ViMode>"
+
+ def process_keyevent(self, keyinfo):
+ def nop(e):
+ pass
+ keytuple=keyinfo.tuple()
+
+ #Process exit keys. Only exit on empty line
+ if keytuple in self.exit_dispatch:
+ if lineobj.EndOfLine(self.l_buffer) == 0:
+ raise EOFError
+
+ dispatch_func = self.key_dispatch.get(keytuple,self.vi_key)
+ log("readline from keyboard:%s->%s"%(keytuple,dispatch_func))
+ r = None
+ if dispatch_func:
+ r = dispatch_func(keyinfo)
+ self.l_buffer.push_undo()
+
+ self.previous_func = dispatch_func
+ if r:
+ self._update_line()
+ return True
+ return False
+
+ ### Methods below here are bindable emacs functions
+
+ def init_editing_mode(self, e): # (M-C-j)
+ '''Initialize vi editingmode'''
+ self.show_all_if_ambiguous = 'on'
+ self.key_dispatch = {}
+ self.__vi_insert_mode = None
+ self._vi_command = None
+ self._vi_command_edit = None
+ self._vi_key_find_char = None
+ self._vi_key_find_direction = True
+ self._vi_yank_buffer = None
+ self._vi_multiplier1 = ''
+ self._vi_multiplier2 = ''
+ self._vi_undo_stack = []
+ self._vi_undo_cursor = -1
+ self._vi_current = None
+ self._vi_search_text = ''
+ self.vi_save_line ()
+ self.vi_set_insert_mode (True)
+ # make ' ' to ~ self insert
+ for c in range(ord(' '), 127):
+ self._bind_key('%s' % chr(c), self.vi_key)
+ self._bind_key('BackSpace', self.vi_backspace)
+ self._bind_key('Escape', self.vi_escape)
+ self._bind_key('Return', self.vi_accept_line)
+
+ self._bind_key('Left', self.backward_char)
+ self._bind_key('Right', self.forward_char)
+ self._bind_key('Home', self.beginning_of_line)
+ self._bind_key('End', self.end_of_line)
+ self._bind_key('Delete', self.delete_char)
+
+ self._bind_key('Control-d', self.vi_eof)
+ self._bind_key('Control-z', self.vi_eof)
+ self._bind_key('Control-r', self.vi_redo)
+ self._bind_key('Up', self.vi_arrow_up)
+ self._bind_key('Control-p', self.vi_up)
+ self._bind_key('Down', self.vi_arrow_down)
+ self._bind_key('Control-n', self.vi_down)
+ self._bind_key('Tab', self.vi_complete)
+# self._bind_key('Control-e', self.emacs)
+
+ def vi_key (self, e):
+ if not self._vi_command:
+ self._vi_command = ViCommand (self)
+ elif self._vi_command.is_end:
+ if self._vi_command.is_edit:
+ self._vi_command_edit = self._vi_command
+ self._vi_command = ViCommand (self)
+ self._vi_command.add_char (e.char)
+
+ def vi_error (self):
+ self._bell ()
+
+ def vi_get_is_insert_mode (self):
+ return self.__vi_insert_mode
+ vi_is_insert_mode = property (vi_get_is_insert_mode)
+
+ def vi_escape (self, e):
+ if self.vi_is_insert_mode:
+ if self._vi_command:
+ self._vi_command.add_char (e.char)
+ else:
+ self._vi_command = ViCommand (self)
+ self.vi_set_insert_mode (False)
+ self.l_buffer.point=lineobj.PrevChar
+ elif self._vi_command and self._vi_command.is_replace_one:
+ self._vi_command.add_char (e.char)
+ else:
+ self.vi_error ()
+
+ def vi_backspace (self, e):
+ if self._vi_command:
+ self._vi_command.add_char (e.char)
+ else:
+ self._vi_do_backspace (self._vi_command)
+
+ def _vi_do_backspace (self, vi_cmd):
+ if self.vi_is_insert_mode or (self._vi_command and self._vi_command.is_search):
+ if self.l_buffer.point > 0:
+ self.l_buffer.point -= 1
+ if self.l_buffer.overwrite:
+ try:
+ prev = self._vi_undo_stack [self._vi_undo_cursor][1][self.l_buffer.point ]
+ self.l_buffer.line_buffer [self.l_buffer.point] = prev
+ except IndexError:
+ del self.l_buffer.line_buffer [self.l_buffer.point ]
+ else:
+ self.vi_save_line ()
+ del self.l_buffer.line_buffer [self.l_buffer.point ]
+
+ def vi_accept_line (self, e):
+ if self._vi_command and self._vi_command.is_search:
+ self._vi_command.do_search ()
+ return False
+ self._vi_command = None
+ self.vi_set_insert_mode (True)
+ self._vi_undo_stack = []
+ self._vi_undo_cursor = -1
+ self._vi_current = None
+ return self.accept_line (e)
+
+ def vi_eof (self, e):
+ raise EOFError
+
+ def vi_set_insert_mode (self, value):
+ if self.__vi_insert_mode == value:
+ return
+ self.__vi_insert_mode = value
+ if value:
+ self.vi_save_line ()
+ self.cursor_size=25
+ else:
+ self.cursor_size=100
+
+ def vi_undo_restart (self):
+ tpl_undo = (self.l_buffer.point, self.l_buffer.line_buffer[:], )
+ self._vi_undo_stack = [tpl_undo]
+ self._vi_undo_cursor = 0
+
+ def vi_save_line (self):
+ if self._vi_undo_stack and self._vi_undo_cursor >= 0:
+ del self._vi_undo_stack [self._vi_undo_cursor + 1 : ]
+ # tpl_undo = (self.l_buffer.point, self.l_buffer[:], )
+ tpl_undo = (self.l_buffer.point, self.l_buffer.line_buffer[:], )
+ if not self._vi_undo_stack or self._vi_undo_stack[self._vi_undo_cursor][1] != tpl_undo[1]:
+ self._vi_undo_stack.append (tpl_undo)
+ self._vi_undo_cursor += 1
+
+ def vi_undo_prepare (self):
+ if self._vi_undo_cursor == len(self._vi_undo_stack)-1:
+ self.vi_save_line ()
+
+ def vi_undo (self, do_pop=True):
+ self.vi_undo_prepare ()
+ if not self._vi_undo_stack or self._vi_undo_cursor <= 0:
+ self.vi_error ()
+ return
+ self._vi_undo_cursor -= 1
+ self.vi_undo_assign ()
+
+ def vi_undo_all (self):
+ self.vi_undo_prepare ()
+ if self._vi_undo_cursor > 0:
+ self._vi_undo_cursor = 0
+ self.vi_undo_assign ()
+ else:
+ self.vi_error ()
+
+ def vi_undo_assign (self):
+ tpl_undo = self._vi_undo_stack [self._vi_undo_cursor]
+ self.l_buffer.line_buffer = tpl_undo [1][:]
+ self.l_buffer.point = tpl_undo [0]
+
+ def vi_redo (self, e):
+ if self._vi_undo_cursor >= len(self._vi_undo_stack)-1:
+ self.vi_error ()
+ return
+ self._vi_undo_cursor += 1
+ self.vi_undo_assign ()
+
+ def vi_search (self, rng):
+ for i in rng:
+ line_history = self._history.history [i]
+ pos = line_history.get_line_text().find (self._vi_search_text)
+ if pos >= 0:
+ self._history.history_cursor = i
+ self.l_buffer.line_buffer = list (line_history.line_buffer)
+ self.l_buffer.point = pos
+ self.vi_undo_restart ()
+ return True
+ self._bell ()
+ return False
+
+ def vi_search_first (self):
+ text = ''.join (self.l_buffer.line_buffer [1:])
+ if text:
+ self._vi_search_text = text
+ position = len (self._history.history) - 1
+ elif self._vi_search_text:
+ position = self._history.history_cursor - 1
+ else:
+ self.vi_error ()
+ self.vi_undo ()
+ return
+ if not self.vi_search (list(range(position, -1, -1))):
+ # Here: search text not found
+ self.vi_undo ()
+
+ def vi_search_again_backward (self):
+ self.vi_search (list(range(self._history.history_cursor-1, -1, -1)))
+
+ def vi_search_again_forward (self):
+ self.vi_search (list(range(self._history.history_cursor+1, len(self._history.history))))
+
+ def vi_up (self, e):
+ if self._history.history_cursor == len(self._history.history):
+ self._vi_current = self.l_buffer.line_buffer [:]
+ # self._history.previous_history (e)
+ self._history.previous_history (self.l_buffer)
+ if self.vi_is_insert_mode:
+ self.end_of_line (e)
+ else:
+ self.beginning_of_line (e)
+ self.vi_undo_restart ()
+
+ def vi_down (self, e):
+ if self._history.history_cursor >= len(self._history.history):
+ self.vi_error ()
+ return
+ if self._history.history_cursor < len(self._history.history) - 1:
+ # self._history.next_history (e)
+ self._history.next_history (self.l_buffer)
+ if self.vi_is_insert_mode:
+ self.end_of_line (e)
+ else:
+ self.beginning_of_line (e)
+ self.vi_undo_restart ()
+ elif self._vi_current is not None:
+ self._history.history_cursor = len(self._history.history)
+ self.l_buffer.line_buffer = self._vi_current
+ self.end_of_line (e)
+ if not self.vi_is_insert_mode and self.l_buffer.point > 0:
+ self.l_buffer.point -= 1
+ self._vi_current = None
+ else:
+ self.vi_error ()
+ return
+
+ def vi_arrow_up (self, e):
+ self.vi_set_insert_mode (True)
+ self.vi_up (e)
+ self.vi_save_line ()
+
+ def vi_arrow_down (self, e):
+ self.vi_set_insert_mode (True)
+ self.vi_down (e)
+ self.vi_save_line ()
+
+ def vi_complete (self, e):
+ text = self.l_buffer.get_line_text ()
+ if text and not text.isspace ():
+ return self.complete (e)
+ else:
+ return self.vi_key (e)
+
+# vi input states
+# sequence of possible states are in the order below
+_VI_BEGIN = 'vi_begin'
+_VI_MULTI1 = 'vi_multi1'
+_VI_ACTION = 'vi_action'
+_VI_MULTI2 = 'vi_multi2'
+_VI_MOTION = 'vi_motion'
+_VI_MOTION_ARGUMENT = 'vi_motion_argument'
+_VI_REPLACE_ONE = 'vi_replace_one'
+_VI_TEXT = 'vi_text'
+_VI_SEARCH = 'vi_search'
+_VI_END = 'vi_end'
+
+# vi helper class
+class ViCommand:
+ def __init__ (self, readline):
+ self.readline = readline
+ self.lst_char = []
+ self.state = _VI_BEGIN
+ self.action = self.movement
+ self.motion = None
+ self.motion_argument = None
+ self.text = None
+ self.pos_motion = None
+ self.is_edit = False
+ self.is_overwrite = False
+ self.is_error = False
+ self.is_star = False
+ self.delete_left = 0
+ self.delete_right = 0
+ self.readline._vi_multiplier1 = ''
+ self.readline._vi_multiplier2 = ''
+ self.set_override_multiplier (0)
+ self.skip_multipler = False
+ self.tabstop = 4
+ self.dct_fcn = {
+ ord('$') : self.key_dollar,
+ ord('^') : self.key_hat,
+ ord(';') : self.key_semicolon,
+ ord(',') : self.key_comma,
+ ord('%') : self.key_percent,
+ ord('.') : self.key_dot,
+ ord('/') : self.key_slash,
+ ord('*') : self.key_star,
+ ord('|') : self.key_bar,
+ ord('~') : self.key_tilde,
+ 8 : self.key_backspace,
+ }
+
+ def add_char (self, char):
+ self.lst_char.append (char)
+ if self.state == _VI_BEGIN and self.readline.vi_is_insert_mode:
+ self.readline.vi_save_line ()
+ self.state = _VI_TEXT
+ if self.state == _VI_SEARCH:
+ if char == '\x08': # backspace
+ self.key_backspace (char)
+ else:
+ self.set_text (char)
+ return
+ if self.state == _VI_TEXT:
+ if char == '\x1b': # escape
+ self.escape (char)
+ elif char == '\x09': # tab
+ ts = self.tabstop
+ ws = ' ' * (ts - (self.readline.l_buffer.point%ts))
+ self.set_text (ws)
+ elif char == '\x08': # backspace
+ self.key_backspace (char)
+ else:
+ self.set_text (char)
+ return
+ if self.state == _VI_MOTION_ARGUMENT:
+ self.set_motion_argument (char)
+ return
+ if self.state == _VI_REPLACE_ONE:
+ self.replace_one (char)
+ return
+ try:
+ fcn_instance = self.dct_fcn [ord(char)]
+ except:
+ fcn_instance = getattr (self, 'key_%s' % char, None)
+ if fcn_instance:
+ fcn_instance (char)
+ return
+ if char.isdigit ():
+ self.key_digit (char)
+ return
+ # Here: could not process key
+ self.error ()
+
+ def set_text (self, text):
+ if self.text is None:
+ self.text = text
+ else:
+ self.text += text
+ self.set_buffer (text)
+
+ def set_buffer (self, text):
+ for char in text:
+ if not self.char_isprint (char):
+ continue
+# self.readline.l_buffer.insert_text(char)
+# continue
+# #overwrite in l_buffer obj
+ if self.is_overwrite:
+ if self.readline.l_buffer.point < len (self.readline.l_buffer.line_buffer):
+ # self.readline.l_buffer[self.l_buffer.point]=char
+ self.readline.l_buffer.line_buffer [self.readline.l_buffer.point] = char
+ else:
+ # self.readline.l_buffer.insert_text(char)
+ self.readline.l_buffer.line_buffer.append (char)
+ else:
+ # self.readline.l_buffer.insert_text(char)
+ self.readline.l_buffer.line_buffer.insert (self.readline.l_buffer.point, char)
+ self.readline.l_buffer.point += 1
+
+ def replace_one (self, char):
+ if char == '\x1b': # escape
+ self.end ()
+ return
+ self.is_edit = True
+ self.readline.vi_save_line ()
+ times = self.get_multiplier ()
+ cursor = self.readline.l_buffer.point
+ self.readline.l_buffer.line_buffer [cursor : cursor + times] = char * times
+ if times > 1:
+ self.readline.l_buffer.point += (times - 1)
+ self.end ()
+
+ def char_isprint (self, char):
+ return ord(char) >= ord(' ') and ord(char) <= ord('~')
+
+ def key_dollar (self, char):
+ self.motion = self.motion_end_in_line
+ self.delete_right = 1
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_hat (self, char):
+ self.motion = self.motion_beginning_of_line
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_0 (self, char):
+ if self.state in [_VI_BEGIN, _VI_ACTION]:
+ self.key_hat (char)
+ else:
+ self.key_digit (char)
+
+ def key_digit (self, char):
+ if self.state in [_VI_BEGIN, _VI_MULTI1]:
+ self.readline._vi_multiplier1 += char
+ self.readline._vi_multiplier2 = ''
+ self.state = _VI_MULTI1
+ elif self.state in [_VI_ACTION, _VI_MULTI2]:
+ self.readline._vi_multiplier2 += char
+ self.state = _VI_MULTI2
+
+ def key_w (self, char):
+ if self.action == self.change:
+ self.key_e (char)
+ return
+ self.motion = self.motion_word_short
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_W (self, char):
+ if self.action == self.change:
+ self.key_E (char)
+ return
+ self.motion = self.motion_word_long
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_e (self, char):
+ self.motion = self.motion_end_short
+ self.state = _VI_MOTION
+ self.delete_right = 1
+ self.apply ()
+
+ def key_E (self, char):
+ self.motion = self.motion_end_long
+ self.state = _VI_MOTION
+ self.delete_right = 1
+ self.apply ()
+
+ def key_b (self, char):
+ self.motion = self.motion_back_short
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_B (self, char):
+ self.motion = self.motion_back_long
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_f (self, char):
+ self.readline._vi_key_find_direction = True
+ self.motion = self.motion_find_char_forward
+ self.delete_right = 1
+ self.state = _VI_MOTION_ARGUMENT
+
+ def key_F (self, char):
+ self.readline._vi_key_find_direction = False
+ self.motion = self.motion_find_char_backward
+ self.delete_left = 1
+ self.state = _VI_MOTION_ARGUMENT
+
+ def key_t (self, char):
+ self.motion = self.motion_to_char_forward
+ self.delete_right = 1
+ self.state = _VI_MOTION_ARGUMENT
+
+ def key_T (self, char):
+ self.motion = self.motion_to_char_backward
+ self.state = _VI_MOTION_ARGUMENT
+
+ def key_j (self, char):
+ self.readline.vi_down (ViEvent (char))
+ self.state = _VI_END
+
+ def key_k (self, char):
+ self.readline.vi_up (ViEvent (char))
+ self.state = _VI_END
+
+ def key_semicolon (self, char):
+ if self.readline._vi_key_find_char is None:
+ self.error ()
+ return
+ if self.readline._vi_key_find_direction:
+ self.motion = self.motion_find_char_forward
+ else:
+ self.motion = self.motion_find_char_backward
+ self.set_motion_argument (self.readline._vi_key_find_char)
+
+ def key_comma (self, char):
+ if self.readline._vi_key_find_char is None:
+ self.error ()
+ return
+ if self.readline._vi_key_find_direction:
+ self.motion = self.motion_find_char_backward
+ else:
+ self.motion = self.motion_find_char_forward
+ self.set_motion_argument (self.readline._vi_key_find_char)
+
+ def key_percent (self, char):
+ '''find matching <([{}])>'''
+ self.motion = self.motion_matching
+ self.delete_right = 1
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_dot (self, char):
+ vi_cmd_edit = self.readline._vi_command_edit
+ if not vi_cmd_edit:
+ return
+ if vi_cmd_edit.is_star:
+ self.key_star (char)
+ return
+ if self.has_multiplier ():
+ count = self.get_multiplier ()
+ else:
+ count = 0
+ # Create the ViCommand object after getting multipler from self
+ # Side effect of the ViCommand creation is resetting of global multipliers
+ vi_cmd = ViCommand (self.readline)
+ if count >= 1:
+ vi_cmd.set_override_multiplier (count)
+ vi_cmd_edit.set_override_multiplier (count)
+ elif vi_cmd_edit.override_multiplier:
+ vi_cmd.set_override_multiplier (vi_cmd_edit.override_multiplier)
+ for char in vi_cmd_edit.lst_char:
+ vi_cmd.add_char (char)
+ if vi_cmd_edit.is_overwrite and self.readline.l_buffer.point > 0:
+ self.readline.l_buffer.point -= 1
+ self.readline.vi_set_insert_mode (False)
+ self.end ()
+
+ def key_slash (self, char):
+ self.readline.vi_save_line ()
+ self.readline.l_buffer.line_buffer=['/']
+ self.readline.l_buffer.point= 1
+ self.state = _VI_SEARCH
+
+ def key_star (self, char):
+ self.is_star = True
+ self.is_edit = True
+ self.readline.vi_save_line ()
+ completions = self.readline._get_completions()
+ if completions:
+ text = ' '.join (completions) + ' '
+ self.readline.l_buffer.line_buffer [self.readline.begidx : self.readline.endidx + 1] = list (text)
+ prefix_len = self.readline.endidx - self.readline.begidx
+ self.readline.l_buffer.point += len(text) - prefix_len
+ self.readline.vi_set_insert_mode (True)
+ else:
+ self.error ()
+ self.state = _VI_TEXT
+
+ def key_bar (self, char):
+ self.motion = self.motion_column
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_tilde (self, char):
+ self.is_edit = True
+ self.readline.vi_save_line ()
+ for i in range (self.get_multiplier()):
+ try:
+ c = self.readline.l_buffer.line_buffer [self.readline.l_buffer.point]
+ if c.isupper ():
+ self.readline.l_buffer.line_buffer [self.readline.l_buffer.point] = c.lower()
+ elif c.islower ():
+ self.readline.l_buffer.line_buffer [self.readline.l_buffer.point] = c.upper()
+ self.readline.l_buffer.point += 1
+ except IndexError:
+ break
+ self.end ()
+
+ def key_h (self, char):
+ self.motion = self.motion_left
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_backspace (self, char):
+ if self.state in [_VI_TEXT, _VI_SEARCH]:
+ if self.text and len(self.text):
+ self.text = self.text [:-1]
+ try:
+ # Remove backspaces for potential dot command
+ self.lst_char.pop ()
+ self.lst_char.pop ()
+ except IndexError:
+ pass
+ else:
+ self.key_h (char)
+ self.readline._vi_do_backspace (self)
+ if self.state == _VI_SEARCH and not (self.readline.l_buffer.line_buffer):
+ self.state = _VI_BEGIN
+
+ def key_l (self, char):
+ self.motion = self.motion_right
+ self.state = _VI_MOTION
+ self.apply ()
+
+ def key_i (self, char):
+ self.is_edit = True
+ self.state = _VI_TEXT
+ self.readline.vi_set_insert_mode (True)
+
+ def key_I (self, char):
+ self.is_edit = True
+ self.state = _VI_TEXT
+ self.readline.vi_set_insert_mode (True)
+ self.readline.l_buffer.point = 0
+
+ def key_a (self, char):
+ self.is_edit = True
+ self.state = _VI_TEXT
+ self.readline.vi_set_insert_mode (True)
+ if len (self.readline.l_buffer.line_buffer):
+ self.readline.l_buffer.point += 1
+
+ def key_A (self, char):
+ self.is_edit = True
+ self.state = _VI_TEXT
+ self.readline.vi_set_insert_mode (True)
+ self.readline.l_buffer.point = len (self.readline.l_buffer.line_buffer)
+
+ def key_d (self, char):
+ self.is_edit = True
+ self.state = _VI_ACTION
+ self.action = self.delete
+
+ def key_D (self, char):
+ self.is_edit = True
+ self.state = _VI_ACTION
+ self.action = self.delete_end_of_line
+ self.apply ()
+
+ def key_x (self, char):
+ self.is_edit = True
+ self.state = _VI_ACTION
+ self.action = self.delete_char
+ self.apply ()
+
+ def key_X (self, char):
+ self.is_edit = True
+ self.state = _VI_ACTION
+ self.action = self.delete_prev_char
+ self.apply ()
+
+ def key_s (self, char):
+ self.is_edit = True
+ i1 = self.readline.l_buffer.point
+ i2 = self.readline.l_buffer.point + self.get_multiplier ()
+ self.skip_multipler = True
+ self.readline.vi_set_insert_mode (True)
+ del self.readline.l_buffer.line_buffer [i1 : i2]
+ self.state = _VI_TEXT
+
+ def key_S (self, char):
+ self.is_edit = True
+ self.readline.vi_set_insert_mode (True)
+ self.readline.l_buffer.line_buffer = []
+ self.readline.l_buffer.point = 0
+ self.state = _VI_TEXT
+
+ def key_c (self, char):
+ self.is_edit = True
+ self.state = _VI_ACTION
+ self.action = self.change
+
+ def key_C (self, char):
+ self.is_edit = True
+ self.readline.vi_set_insert_mode (True)
+ del self.readline.l_buffer.line_buffer [self.readline.l_buffer.point : ]
+ self.state = _VI_TEXT
+
+ def key_r (self, char):
+ self.state = _VI_REPLACE_ONE
+
+ def key_R (self, char):
+ self.is_edit = True
+ self.is_overwrite = True
+ self.readline.l_buffer.overwrite=True
+ self.readline.vi_set_insert_mode (True)
+ self.state = _VI_TEXT
+
+ def key_y (self, char):
+ self._state = _VI_ACTION
+ self.action = self.yank
+
+ def key_Y (self, char):
+ self.readline._vi_yank_buffer = self.readline.l_buffer.get_line_text()
+ self.end ()
+
+ def key_p (self, char):
+ if not self.readline._vi_yank_buffer:
+ return
+ self.is_edit = True
+ self.readline.vi_save_line ()
+ self.readline.l_buffer.point += 1
+ self.readline.l_buffer.insert_text (self.readline._vi_yank_buffer * self.get_multiplier ())
+ self.readline.l_buffer.point -= 1
+ self.state = _VI_END
+
+ def key_P (self, char):
+ if not self.readline._vi_yank_buffer:
+ return
+ self.is_edit = True
+ self.readline.vi_save_line ()
+ self.readline.l_buffer.insert_text (self.readline._vi_yank_buffer * self.get_multiplier ())
+ self.readline.l_buffer.point -= 1
+ self.state = _VI_END
+
+ def key_u (self, char):
+ self.readline.vi_undo ()
+ self.state = _VI_END
+
+ def key_U (self, char):
+ self.readline.vi_undo_all ()
+ self.state = _VI_END
+
+ def key_v (self, char):
+ editor = ViExternalEditor (self.readline.l_buffer.line_buffer)
+ self.readline.l_buffer.line_buffer = list (editor.result)
+ self.readline.l_buffer.point = 0
+ self.is_edit = True
+ self.state = _VI_END
+
+ def error (self):
+ self.readline._bell ()
+ self.is_error = True
+
+ def state_is_end (self):
+ return self.state == _VI_END
+ is_end = property (state_is_end)
+
+ def state_is_search (self):
+ return self.state == _VI_SEARCH
+ is_search = property (state_is_search)
+
+ def state_is_replace_one (self):
+ return self.state == _VI_REPLACE_ONE
+ is_replace_one = property (state_is_replace_one)
+
+ def do_search (self):
+ self.readline.vi_search_first ()
+ self.state = _VI_END
+
+ def key_n (self, char):
+ self.readline.vi_search_again_backward ()
+ self.state = _VI_END
+
+ def key_N (self, char):
+ self.readline.vi_search_again_forward ()
+ self.state = _VI_END
+
+ def motion_beginning_of_line (self, line, index=0, count=1, **kw):
+ return 0
+
+ def motion_end_in_line (self, line, index=0, count=1, **kw):
+ return max (0, len (self.readline.l_buffer.line_buffer)-1)
+
+ def motion_word_short (self, line, index=0, count=1, **kw):
+ return vi_pos_word_short (line, index, count)
+
+ def motion_word_long (self, line, index=0, count=1, **kw):
+ return vi_pos_word_long (line, index, count)
+
+ def motion_end_short (self, line, index=0, count=1, **kw):
+ return vi_pos_end_short (line, index, count)
+
+ def motion_end_long (self, line, index=0, count=1, **kw):
+ return vi_pos_end_long (line, index, count)
+
+ def motion_back_short (self, line, index=0, count=1, **kw):
+ return vi_pos_back_short (line, index, count)
+
+ def motion_back_long (self, line, index=0, count=1, **kw):
+ return vi_pos_back_long (line, index, count)
+
+ def motion_find_char_forward (self, line, index=0, count=1, char=None):
+ self.readline._vi_key_find_char = char
+ return vi_pos_find_char_forward (line, char, index, count)
+
+ def motion_find_char_backward (self, line, index=0, count=1, char=None):
+ self.readline._vi_key_find_char = char
+ return vi_pos_find_char_backward (line, char, index, count)
+
+ def motion_to_char_forward (self, line, index=0, count=1, char=None):
+ return vi_pos_to_char_forward (line, char, index, count)
+
+ def motion_to_char_backward (self, line, index=0, count=1, char=None):
+ return vi_pos_to_char_backward (line, char, index, count)
+
+ def motion_left (self, line, index=0, count=1, char=None):
+ return max (0, index - count)
+
+ def motion_right (self, line, index=0, count=1, char=None):
+ return min (len(line), index + count)
+
+ def motion_matching (self, line, index=0, count=1, char=None):
+ return vi_pos_matching (line, index)
+
+ def motion_column (self, line, index=0, count=1, char=None):
+ return max (0, count-1)
+
+ def has_multiplier (self):
+ return self.override_multiplier or self.readline._vi_multiplier1 or self.readline._vi_multiplier2
+
+ def get_multiplier (self):
+ if self.override_multiplier:
+ return int (self.override_multiplier)
+ if self.readline._vi_multiplier1 == '': m1 = 1
+ else: m1 = int(self.readline._vi_multiplier1)
+ if self.readline._vi_multiplier2 == '': m2 = 1
+ else: m2 = int(self.readline._vi_multiplier2)
+ return m1 * m2
+
+ def set_override_multiplier (self, count):
+ self.override_multiplier = count
+
+ def apply (self):
+ if self.motion:
+ self.pos_motion = self.motion (self.readline.l_buffer.line_buffer, self.readline.l_buffer.point,
+ self.get_multiplier(), char=self.motion_argument)
+ if self.pos_motion < 0:
+ self.error ()
+ return
+ self.action ()
+ if self.state != _VI_TEXT:
+ self.end ()
+
+ def movement (self):
+ if self.pos_motion <= len(self.readline.l_buffer.line_buffer):
+ self.readline.l_buffer.point = self.pos_motion
+ else:
+ self.readline.l_buffer.point = len(self.readline.l_buffer.line_buffer) - 1
+
+ def yank (self):
+ if self.pos_motion > self.readline.l_buffer.point:
+ s = self.readline.l_buffer.line_buffer [self.readline.l_buffer.point : self.pos_motion + self.delete_right]
+ else:
+ index = max (0, self.pos_motion - self.delete_left)
+ s = self.readline.l_buffer.line_buffer [index : self.readline.l_buffer.point + self.delete_right]
+ self.readline._vi_yank_buffer = s
+
+ def delete (self):
+ self.readline.vi_save_line ()
+ self.yank ()
+# point=lineobj.Point(self.readline.l_buffer)
+# pm=self.pos_motion
+# del self.readline.l_buffer[point:pm]
+# return
+ if self.pos_motion > self.readline.l_buffer.point:
+ del self.readline.l_buffer.line_buffer [self.readline.l_buffer.point : self.pos_motion + self.delete_right]
+ if self.readline.l_buffer.point > len (self.readline.l_buffer.line_buffer):
+ self.readline.l_buffer.point = len (self.readline.l_buffer.line_buffer)
+ else:
+ index = max (0, self.pos_motion - self.delete_left)
+ del self.readline.l_buffer.line_buffer [index : self.readline.l_buffer.point + self.delete_right]
+ self.readline.l_buffer.point = index
+
+ def delete_end_of_line (self):
+ self.readline.vi_save_line ()
+ # del self.readline.l_buffer [self.readline.l_buffer.point : ]
+ line_text = self.readline.l_buffer.get_line_text ()
+ line_text = line_text [ : self.readline.l_buffer.point]
+ self.readline.l_buffer.set_line (line_text)
+ if self.readline.l_buffer.point > 0:
+ self.readline.l_buffer.point -= 1
+
+ def delete_char (self):
+# point=lineobj.Point(self.readline.l_buffer)
+# del self.readline.l_buffer[point:point+self.get_multiplier ()]
+# return
+ self.pos_motion = self.readline.l_buffer.point + self.get_multiplier ()
+ self.delete ()
+ end = max (0, len (self.readline.l_buffer) - 1)
+ if self.readline.l_buffer.point > end:
+ self.readline.l_buffer.point = end
+
+ def delete_prev_char (self):
+ self.pos_motion = self.readline.l_buffer.point - self.get_multiplier ()
+ self.delete ()
+
+ def change (self):
+ self.readline.vi_set_insert_mode (True)
+ self.delete ()
+ self.skip_multipler = True
+ self.state = _VI_TEXT
+
+ def escape (self, char):
+ if self.state == _VI_TEXT:
+ if not self.skip_multipler:
+ times = self.get_multiplier ()
+ if times > 1 and self.text:
+ extra = self.text * (times - 1)
+ self.set_buffer (extra)
+ self.state = _VI_END
+
+ def set_motion_argument (self, char):
+ self.motion_argument = char
+ self.apply ()
+
+ def end (self):
+ self.state = _VI_END
+ if self.readline.l_buffer.point >= len(self.readline.l_buffer.line_buffer):
+ self.readline.l_buffer.point = max (0, len(self.readline.l_buffer.line_buffer) - 1)
+
+class ViExternalEditor:
+ def __init__ (self, line):
+ if type(line) is type([]):
+ line = ''.join (line)
+ file_tmp = self.get_tempfile ()
+ fp_tmp = self.file_open (file_tmp, 'w')
+ fp_tmp.write (line)
+ fp_tmp.close ()
+ self.run_editor (file_tmp)
+ fp_tmp = self.file_open (file_tmp, 'r')
+ self.result = fp_tmp.read ()
+ fp_tmp.close ()
+ self.file_remove (file_tmp)
+
+ def get_tempfile (self):
+ import tempfile
+ return tempfile.mktemp (prefix='readline-', suffix='.py')
+
+ def file_open (self, filename, mode):
+ return file (filename, mode)
+
+ def file_remove (self, filename):
+ os.remove (filename)
+
+ def get_editor (self):
+ try:
+ return os.environ ['EDITOR']
+ except KeyError:
+ return 'notepad' # ouch
+
+ def run_editor (self, filename):
+ cmd = '%s %s' % (self.get_editor(), filename, )
+ self.run_command (cmd)
+
+ def run_command (self, command):
+ os.system (command)
+
+class ViEvent:
+ def __init__ (self, char):
+ self.char = char
+
+# vi standalone functions
+def vi_is_word (char):
+ log ('xx vi_is_word: type(%s), %s' % (type(char), char, ))
+ return char.isalpha() or char.isdigit() or char == '_'
+
+def vi_is_space (char):
+ return char.isspace ()
+
+def vi_is_word_or_space (char):
+ return vi_is_word (char) or vi_is_space (char)
+
+def vi_pos_word_short (line, index=0, count=1):
+ try:
+ for i in range(count):
+ in_word = vi_is_word (line[index])
+ if not in_word:
+ while not vi_is_word (line[index]):
+ index += 1
+ else:
+ while vi_is_word (line[index]):
+ index += 1
+ while vi_is_space (line[index]):
+ index += 1
+ return index
+ except IndexError:
+ return len(line)
+
+def vi_pos_word_long (line, index=0, count=1):
+ try:
+ for i in range(count):
+ in_space = vi_is_space (line[index])
+ if not in_space:
+ while not vi_is_space (line[index]):
+ index += 1
+ while vi_is_space (line[index]):
+ index += 1
+ return index
+ except IndexError:
+ return len(line)
+
+def vi_pos_end_short (line, index=0, count=1):
+ try:
+ for i in range(count):
+ index += 1
+ while vi_is_space (line[index]):
+ index += 1
+ in_word = vi_is_word (line[index])
+ if not in_word:
+ while not vi_is_word_or_space (line[index]):
+ index += 1
+ else:
+ while vi_is_word (line[index]):
+ index += 1
+ return index - 1
+ except IndexError:
+ return max (0, len(line)-1)
+
+def vi_pos_end_long (line, index=0, count=1):
+ try:
+ for i in range(count):
+ index += 1
+ while vi_is_space (line[index]):
+ index += 1
+ while not vi_is_space (line[index]):
+ index += 1
+ return index - 1
+ except IndexError:
+ return max (0, len(line)-1)
+
+class vi_list (list):
+ '''This is a list that cannot have a negative index'''
+ def __getitem__ (self, key):
+ try:
+ if int(key) < 0:
+ raise IndexError
+ except ValueError:
+ pass
+ return list.__getitem__ (self, key)
+
+def vi_pos_back_short (line, index=0, count=1):
+ line = vi_list (line)
+ try:
+ for i in range(count):
+ index -= 1
+ while vi_is_space (line[index]):
+ index -= 1
+ in_word = vi_is_word (line[index])
+ if in_word:
+ while vi_is_word (line[index]):
+ index -= 1
+ else:
+ while not vi_is_word_or_space (line[index]):
+ index -= 1
+ return index + 1
+ except IndexError:
+ return 0
+
+def vi_pos_back_long (line, index=0, count=1):
+ line = vi_list (line)
+ try:
+ for i in range(count):
+ index -= 1
+ while vi_is_space (line[index]):
+ index -= 1
+ while not vi_is_space (line[index]):
+ index -= 1
+ return index + 1
+ except IndexError:
+ return 0
+
+def vi_pos_find_char_forward (line, char, index=0, count=1):
+ try:
+ for i in range(count):
+ index += 1
+ while line [index] != char:
+ index += 1
+ return index
+ except IndexError:
+ return -1
+
+def vi_pos_find_char_backward (line, char, index=0, count=1):
+ try:
+ for i in range(count):
+ index -= 1
+ while 1:
+ if index < 0:
+ return -1
+ if line[index] == char:
+ break
+ index -= 1
+ return index
+ except IndexError:
+ return -1
+
+def vi_pos_to_char_forward (line, char, index=0, count=1):
+ index = vi_pos_find_char_forward (line, char, index, count)
+ if index > 0:
+ return index - 1
+ return index
+
+def vi_pos_to_char_backward (line, char, index=0, count=1):
+ index = vi_pos_find_char_backward (line, char, index, count)
+ if index >= 0:
+ return index + 1
+ return index
+
+_vi_dct_matching = {
+ '<': ('>', +1), '>': ('<', -1),
+ '(': (')', +1), ')': ('(', -1),
+ '[': (']', +1), ']': ('[', -1),
+ '{': ('}', +1), '}': ('{', -1),
+}
+
+def vi_pos_matching (line, index=0):
+ '''find matching <([{}])>'''
+ anchor = None
+ target = None
+ delta = 1
+ count = 0
+ try:
+ while 1:
+ if anchor is None:
+ # first find anchor
+ try:
+ target, delta = _vi_dct_matching [line [index]]
+ anchor = line [index]
+ count = 1
+ except KeyError:
+ index += 1
+ continue
+ else:
+ # Here the anchor has been found
+ # Need to get corresponding target
+ if index < 0:
+ return -1
+ if line [index] == anchor:
+ count += 1
+ elif line [index] == target:
+ count -= 1
+ if count == 0:
+ return index
+ index += delta
+ except IndexError:
+ return -1
+
diff --git a/pyreadline/release.py b/pyreadline/release.py
new file mode 100644
index 0000000..625fc93
--- /dev/null
+++ b/pyreadline/release.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+"""Release data for the pyreadline project.
+
+$Id$"""
+
+#*****************************************************************************
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+
+# Name of the package for release purposes. This is the name which labels
+# the tarballs and RPMs made by distutils, so it's best to lowercase it.
+name = 'pyreadline'
+
+# For versions with substrings (like 0.6.16.svn), use an extra . to separate
+# the new substring. We have to avoid using either dashes or underscores,
+# because bdist_rpm does not accept dashes (an RPM) convention, and
+# bdist_deb does not accept underscores (a Debian convention).
+
+branch = ''
+
+version = '2.0'
+
+revision = '$Revision$'
+
+description = "A python implmementation of GNU readline."
+
+long_description = \
+"""
+The pyreadline package is a python implementation of GNU readline functionality
+it is based on the ctypes based UNC readline package by Gary Bishop.
+It is not complete. It has been tested for use with windows 2000 and windows xp.
+
+Version 1.7.1 includes fixes for 64-bit windows, thanks to Christoph Gohlke for
+helping out.
+
+Version 1.7 will be the last release with compatibility with 2.4 and 2.5. The next
+major release will target 2.6, 2.7 and 3.x. The 1.7 series will only receive bugfixes
+from now on.
+
+Features:
+ * keyboard text selection and copy/paste
+ * Shift-arrowkeys for text selection
+ * Control-c can be used for copy activate with allow_ctrl_c(True) in config file
+ * Double tapping ctrl-c will raise a KeyboardInterrupt, use ctrl_c_tap_time_interval(x)
+ where x is your preferred tap time window, default 0.3 s.
+ * paste pastes first line of content on clipboard.
+ * ipython_paste, pastes tab-separated data as list of lists or numpy array if all data is numeric
+ * paste_mulitline_code pastes multi line code, removing any empty lines.
+
+
+ The latest development version is always available at the IPython subversion
+ repository_.
+
+.. _repository:
+ """
+
+license = 'BSD'
+
+authors = {'Jorgen' : ('Jorgen Stenarson','jorgen.stenarson@bostream.nu'),
+ 'Gary': ('Gary Bishop', ''),
+ 'Jack': ('Jack Trainor', ''),
+ }
+
+url = 'http://ipython.scipy.org/moin/PyReadline/Intro'
+
+download_url = 'https://launchpad.net/pyreadline/+download'
+
+platforms = ['Windows XP/2000/NT',
+ 'Windows 95/98/ME']
+
+keywords = ['readline',
+ 'pyreadline']
+
+classifiers = ['Development Status :: 5 - Production/Stable',
+ 'Environment :: Console',
+ 'Operating System :: Microsoft :: Windows',
+ 'License :: OSI Approved :: BSD License',
+ 'Programming Language :: Python :: 2.4',
+ 'Programming Language :: Python :: 2.5',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3.2',
+ ]
+
+
diff --git a/pyreadline/rlmain.py b/pyreadline/rlmain.py
new file mode 100644
index 0000000..443a74b
--- /dev/null
+++ b/pyreadline/rlmain.py
@@ -0,0 +1,591 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2003-2006 Gary Bishop.
+# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+''' an attempt to implement readline for Python in Python using ctypes'''
+import sys,os,re,time
+from glob import glob
+
+from . import release
+
+import pyreadline.lineeditor.lineobj as lineobj
+import pyreadline.lineeditor.history as history
+import pyreadline.clipboard as clipboard
+import pyreadline.console as console
+import pyreadline.logger as logger
+
+from pyreadline.keysyms.common import make_KeyPress_from_keydescr
+from pyreadline.unicode_helper import ensure_unicode
+from .logger import log
+from .modes import editingmodes
+from .error import ReadlineError, GetSetError
+import collections
+
+in_ironpython = "IronPython" in sys.version
+if in_ironpython:#ironpython does not provide a prompt string to readline
+ import System
+ default_prompt = ">>> "
+else:
+ default_prompt = ""
+ import pdb
+
+
+class MockConsoleError(Exception):
+ pass
+
+class MockConsole(object):
+ """object used during refactoring. Should raise errors when someone tries to use it.
+ """
+ def __setattr__(self, x):
+ raise MockConsoleError("Should not try to get attributes from MockConsole")
+
+ def cursor(self, size=50):
+ pass
+
+class BaseReadline(object):
+ def __init__(self):
+ self.allow_ctrl_c = False
+ self.ctrl_c_tap_time_interval = 0.3
+
+ self.debug = False
+ self.bell_style = 'none'
+ self.mark = -1
+ self.console=MockConsole()
+ self.disable_readline = False
+ # this code needs to follow l_buffer and history creation
+ self.editingmodes = [mode(self) for mode in editingmodes]
+ for mode in self.editingmodes:
+ mode.init_editing_mode(None)
+ self.mode = self.editingmodes[0]
+
+ self.read_inputrc()
+ log("\n".join(self.mode.rl_settings_to_string()))
+
+ self.callback = None
+
+ def parse_and_bind(self, string):
+ '''Parse and execute single line of a readline init file.'''
+ try:
+ log('parse_and_bind("%s")' % string)
+ if string.startswith('#'):
+ return
+ if string.startswith('set'):
+ m = re.compile(r'set\s+([-a-zA-Z0-9]+)\s+(.+)\s*$').match(string)
+ if m:
+ var_name = m.group(1)
+ val = m.group(2)
+ try:
+ setattr(self.mode, var_name.replace('-','_'), val)
+ except AttributeError:
+ log('unknown var="%s" val="%s"' % (var_name, val))
+ else:
+ log('bad set "%s"' % string)
+ return
+ m = re.compile(r'\s*(.+)\s*:\s*([-a-zA-Z]+)\s*$').match(string)
+ if m:
+ key = m.group(1)
+ func_name = m.group(2)
+ py_name = func_name.replace('-', '_')
+ try:
+ func = getattr(self.mode, py_name)
+ except AttributeError:
+ log('unknown func key="%s" func="%s"' % (key, func_name))
+ if self.debug:
+ print('pyreadline parse_and_bind error, unknown function to bind: "%s"' % func_name)
+ return
+ self.mode._bind_key(key, func)
+ except:
+ log('error')
+ raise
+
+ def _set_prompt(self, prompt):
+ self.mode.prompt = prompt
+
+ def _get_prompt(self):
+ return self.mode.prompt
+
+ prompt = property(_get_prompt, _set_prompt)
+
+
+ def get_line_buffer(self):
+ '''Return the current contents of the line buffer.'''
+ return self.mode.l_buffer.get_line_text()
+
+ def insert_text(self, string):
+ '''Insert text into the command line.'''
+ self.mode.insert_text(string)
+
+ def read_init_file(self, filename=None):
+ '''Parse a readline initialization file. The default filename is the last filename used.'''
+ log('read_init_file("%s")' % filename)
+
+ #History file book keeping methods (non-bindable)
+
+ def add_history(self, line):
+ '''Append a line to the history buffer, as if it was the last line typed.'''
+ self.mode._history.add_history(line)
+
+ def get_current_history_length(self ):
+ '''Return the number of lines currently in the history.
+ (This is different from get_history_length(), which returns
+ the maximum number of lines that will be written to a history file.)'''
+ return self.mode._history.get_current_history_length()
+
+ def get_history_length(self ):
+ '''Return the desired length of the history file.
+
+ Negative values imply unlimited history file size.'''
+ return self.mode._history.get_history_length()
+
+ def set_history_length(self, length):
+ '''Set the number of lines to save in the history file.
+
+ write_history_file() uses this value to truncate the history file
+ when saving. Negative values imply unlimited history file size.
+ '''
+ self.mode._history.set_history_length(length)
+
+ def get_history_item(self, index):
+ '''Return the current contents of history item at index.'''
+ return self.mode._history.get_history_item(index)
+
+ def clear_history(self):
+ '''Clear readline history'''
+ self.mode._history.clear_history()
+
+ def read_history_file(self, filename=None):
+ '''Load a readline history file. The default filename is ~/.history.'''
+ if filename is None:
+ filename = self.mode._history.history_filename
+ log("read_history_file from %s"%ensure_unicode(filename))
+ self.mode._history.read_history_file(filename)
+
+ def write_history_file(self, filename=None):
+ '''Save a readline history file. The default filename is ~/.history.'''
+ self.mode._history.write_history_file(filename)
+
+ #Completer functions
+
+ def set_completer(self, function=None):
+ '''Set or remove the completer function.
+
+ If function is specified, it will be used as the new completer
+ function; if omitted or None, any completer function already
+ installed is removed. The completer function is called as
+ function(text, state), for state in 0, 1, 2, ..., until it returns a
+ non-string value. It should return the next possible completion
+ starting with text.
+ '''
+ log('set_completer')
+ self.mode.completer = function
+
+ def get_completer(self):
+ '''Get the completer function.
+ '''
+ log('get_completer')
+ return self.mode.completer
+
+ def get_begidx(self):
+ '''Get the beginning index of the readline tab-completion scope.'''
+ return self.mode.begidx
+
+ def get_endidx(self):
+ '''Get the ending index of the readline tab-completion scope.'''
+ return self.mode.endidx
+
+ def set_completer_delims(self, string):
+ '''Set the readline word delimiters for tab-completion.'''
+ self.mode.completer_delims = string
+
+ def get_completer_delims(self):
+ '''Get the readline word delimiters for tab-completion.'''
+ return self.mode.completer_delims.encode("ascii")
+
+ def set_startup_hook(self, function=None):
+ '''Set or remove the startup_hook function.
+
+ If function is specified, it will be used as the new startup_hook
+ function; if omitted or None, any hook function already installed is
+ removed. The startup_hook function is called with no arguments just
+ before readline prints the first prompt.
+
+ '''
+ self.mode.startup_hook = function
+
+ def set_pre_input_hook(self, function=None):
+ '''Set or remove the pre_input_hook function.
+
+ If function is specified, it will be used as the new pre_input_hook
+ function; if omitted or None, any hook function already installed is
+ removed. The pre_input_hook function is called with no arguments
+ after the first prompt has been printed and just before readline
+ starts reading input characters.
+
+ '''
+ self.mode.pre_input_hook = function
+
+#Functions that are not relevant for all Readlines but should at least have a NOP
+
+ def _bell(self):
+ pass
+
+#
+# Standard call, not available for all implementations
+#
+
+ def readline(self, prompt=''):
+ raise NotImplementedError
+
+#
+# Callback interface
+#
+ def process_keyevent(self, keyinfo):
+ return self.mode.process_keyevent(keyinfo)
+
+ def readline_setup(self, prompt=""):
+ return self.mode.readline_setup(prompt)
+
+ def keyboard_poll(self):
+ return self.mode._readline_from_keyboard_poll()
+
+ def callback_handler_install(self, prompt, callback):
+ '''bool readline_callback_handler_install ( string prompt, callback callback)
+ Initializes the readline callback interface and terminal, prints the prompt and returns immediately
+ '''
+ self.callback = callback
+ self.readline_setup(prompt)
+
+ def callback_handler_remove(self):
+ '''Removes a previously installed callback handler and restores terminal settings'''
+ self.callback = None
+
+ def callback_read_char(self):
+ '''Reads a character and informs the readline callback interface when a line is received'''
+ if self.keyboard_poll():
+ line = self.get_line_buffer() + '\n'
+ # however there is another newline added by
+ # self.mode.readline_setup(prompt) which is called by callback_handler_install
+ # this differs from GNU readline
+ self.add_history(self.mode.l_buffer)
+ # TADA:
+ self.callback(line)
+
+ def read_inputrc(self, #in 2.4 we cannot call expanduser with unicode string
+ inputrcpath=os.path.expanduser("~/pyreadlineconfig.ini")):
+ modes = dict([(x.mode,x) for x in self.editingmodes])
+ mode = self.editingmodes[0].mode
+
+ def setmode(name):
+ self.mode = modes[name]
+
+ def bind_key(key, name):
+ import types
+ if isinstance(name, collections.Callable):
+ modes[mode]._bind_key(key, types.MethodType(name, modes[mode]))
+ elif hasattr(modes[mode], name):
+ modes[mode]._bind_key(key, getattr(modes[mode], name))
+ else:
+ print("Trying to bind unknown command '%s' to key '%s'"%(name, key))
+
+ def un_bind_key(key):
+ keyinfo = make_KeyPress_from_keydescr(key).tuple()
+ if keyinfo in modes[mode].key_dispatch:
+ del modes[mode].key_dispatch[keyinfo]
+
+ def bind_exit_key(key):
+ modes[mode]._bind_exit_key(key)
+
+ def un_bind_exit_key(key):
+ keyinfo = make_KeyPress_from_keydescr(key).tuple()
+ if keyinfo in modes[mode].exit_dispatch:
+ del modes[mode].exit_dispatch[keyinfo]
+
+ def setkill_ring_to_clipboard(killring):
+ import pyreadline.lineeditor.lineobj
+ pyreadline.lineeditor.lineobj.kill_ring_to_clipboard = killring
+
+ def sethistoryfilename(filename):
+ self.mode._history.history_filename=os.path.expanduser(filename)
+
+ def setbellstyle(mode):
+ self.bell_style = mode
+
+ def disable_readline(mode):
+ self.disable_readline = mode
+
+ def sethistorylength(length):
+ self.mode._history.history_length = int(length)
+
+ def allow_ctrl_c(mode):
+ log("allow_ctrl_c:%s:%s"%(self.allow_ctrl_c, mode))
+ self.allow_ctrl_c = mode
+
+ def setbellstyle(mode):
+ self.bell_style = mode
+
+ def show_all_if_ambiguous(mode):
+ self.mode.show_all_if_ambiguous = mode
+
+ def ctrl_c_tap_time_interval(mode):
+ self.ctrl_c_tap_time_interval = mode
+
+ def mark_directories(mode):
+ self.mode.mark_directories = mode
+
+ def completer_delims(delims):
+ self.mode.completer_delims = delims
+
+ def complete_filesystem(delims):
+ self.mode.complete_filesystem = delims.lower()
+
+ def enable_ipython_paste_for_paths(boolean):
+ self.mode.enable_ipython_paste_for_paths = boolean
+
+ def debug_output(on, filename="pyreadline_debug_log.txt"): #Not implemented yet
+ if on in ["on", "on_nologfile"]:
+ self.debug=True
+
+ if on == "on":
+ logger.start_file_log(filename)
+ logger.start_socket_log()
+ logger.log("STARTING LOG")
+ elif on == "on_nologfile":
+ logger.start_socket_log()
+ logger.log("STARTING LOG")
+ else:
+ logger.log("STOPING LOG")
+ logger.stop_file_log()
+ logger.stop_socket_log()
+
+ _color_trtable={"black":0, "darkred":4, "darkgreen":2,
+ "darkyellow":6, "darkblue":1, "darkmagenta":5,
+ "darkcyan":3, "gray":7, "red":4+8,
+ "green":2+8, "yellow":6+8, "blue":1+8,
+ "magenta":5+8, "cyan":3+8, "white":7+8}
+
+ def set_prompt_color(color):
+ self.prompt_color = self._color_trtable.get(color.lower(),7)
+
+ def set_input_color(color):
+ self.command_color=self._color_trtable.get(color.lower(),7)
+
+ loc = {"branch":release.branch,
+ "version":release.version,
+ "mode":mode,
+ "modes":modes,
+ "set_mode":setmode,
+ "bind_key":bind_key,
+ "disable_readline":disable_readline,
+ "bind_exit_key":bind_exit_key,
+ "un_bind_key":un_bind_key,
+ "un_bind_exit_key":un_bind_exit_key,
+ "bell_style":setbellstyle,
+ "mark_directories":mark_directories,
+ "show_all_if_ambiguous":show_all_if_ambiguous,
+ "completer_delims":completer_delims,
+ "complete_filesystem":complete_filesystem,
+ "debug_output":debug_output,
+ "history_filename":sethistoryfilename,
+ "history_length":sethistorylength,
+ "set_prompt_color":set_prompt_color,
+ "set_input_color":set_input_color,
+ "allow_ctrl_c":allow_ctrl_c,
+ "ctrl_c_tap_time_interval":ctrl_c_tap_time_interval,
+ "kill_ring_to_clipboard":setkill_ring_to_clipboard,
+ "enable_ipython_paste_for_paths":enable_ipython_paste_for_paths,
+ }
+ if os.path.isfile(inputrcpath):
+ try:
+ exec(compile(open(inputrcpath).read(), inputrcpath, 'exec'), loc, loc)
+ except Exception as x:
+ raise
+ import traceback
+ print("Error reading .pyinputrc", file=sys.stderr)
+ filepath,lineno=traceback.extract_tb(sys.exc_info()[2])[1][:2]
+ print("Line: %s in file %s"%(lineno, filepath), file=sys.stderr)
+ print(x, file=sys.stderr)
+ raise ReadlineError("Error reading .pyinputrc")
+
+
+
+class Readline(BaseReadline):
+ """Baseclass for readline based on a console
+ """
+ def __init__(self):
+ BaseReadline.__init__(self)
+ self.console = console.Console()
+ self.selection_color = self.console.saveattr<<4
+ self.command_color = None
+ self.prompt_color = None
+ self.size = self.console.size()
+
+ # variables you can control with parse_and_bind
+
+# To export as readline interface
+
+
+## Internal functions
+
+ def _bell(self):
+ '''ring the bell if requested.'''
+ if self.bell_style == 'none':
+ pass
+ elif self.bell_style == 'visible':
+ raise NotImplementedError("Bellstyle visible is not implemented yet.")
+ elif self.bell_style == 'audible':
+ self.console.bell()
+ else:
+ raise ReadlineError("Bellstyle %s unknown."%self.bell_style)
+
+ def _clear_after(self):
+ c = self.console
+ x, y = c.pos()
+ w, h = c.size()
+ c.rectangle((x, y, w+1, y+1))
+ c.rectangle((0, y+1, w, min(y+3,h)))
+
+ def _set_cursor(self):
+ c = self.console
+ xc, yc = self.prompt_end_pos
+ w, h = c.size()
+ xc += self.mode.l_buffer.visible_line_width()
+ while(xc >= w):
+ xc -= w
+ yc += 1
+ c.pos(xc, yc)
+
+ def _print_prompt(self):
+ c = self.console
+ x, y = c.pos()
+
+ n = c.write_scrolling(self.prompt, self.prompt_color)
+ self.prompt_begin_pos = (x, y - n)
+ self.prompt_end_pos = c.pos()
+ self.size = c.size()
+
+ def _update_prompt_pos(self, n):
+ if n != 0:
+ bx, by = self.prompt_begin_pos
+ ex, ey = self.prompt_end_pos
+ self.prompt_begin_pos = (bx, by - n)
+ self.prompt_end_pos = (ex, ey - n)
+
+ def _update_line(self):
+ c = self.console
+ l_buffer = self.mode.l_buffer
+ c.cursor(0) #Hide cursor avoiding flicking
+ c.pos(*self.prompt_begin_pos)
+ self._print_prompt()
+ ltext = l_buffer.quoted_text()
+ if l_buffer.enable_selection and (l_buffer.selection_mark >= 0):
+ start = len(l_buffer[:l_buffer.selection_mark].quoted_text())
+ stop = len(l_buffer[:l_buffer.point].quoted_text())
+ if start > stop:
+ stop,start = start,stop
+ n = c.write_scrolling(ltext[:start], self.command_color)
+ n = c.write_scrolling(ltext[start:stop], self.selection_color)
+ n = c.write_scrolling(ltext[stop:], self.command_color)
+ else:
+ n = c.write_scrolling(ltext, self.command_color)
+
+ x, y = c.pos() #Preserve one line for Asian IME(Input Method Editor) statusbar
+ w, h = c.size()
+ if (y >= h - 1) or (n > 0):
+ c.scroll_window(-1)
+ c.scroll((0, 0, w, h), 0, -1)
+ n += 1
+
+ self._update_prompt_pos(n)
+ if hasattr(c, "clear_to_end_of_window"): #Work around function for ironpython due
+ c.clear_to_end_of_window() #to System.Console's lack of FillFunction
+ else:
+ self._clear_after()
+
+ #Show cursor, set size vi mode changes size in insert/overwrite mode
+ c.cursor(1, size=self.mode.cursor_size)
+ self._set_cursor()
+
+
+ def callback_read_char(self):
+ #Override base to get automatic newline
+ '''Reads a character and informs the readline callback interface when a line is received'''
+ if self.keyboard_poll():
+ line = self.get_line_buffer() + '\n'
+ self.console.write("\r\n")
+ # however there is another newline added by
+ # self.mode.readline_setup(prompt) which is called by callback_handler_install
+ # this differs from GNU readline
+ self.add_history(self.mode.l_buffer)
+ # TADA:
+ self.callback(line)
+
+
+ def event_available(self):
+ return self.console.peek() or (len(self.paste_line_buffer) > 0)
+
+
+ def _readline_from_keyboard(self):
+ while 1:
+ if self._readline_from_keyboard_poll():
+ break
+
+ def _readline_from_keyboard_poll(self):
+ pastebuffer = self.mode.paste_line_buffer
+ if len(pastebuffer) > 0:
+ #paste first line in multiline paste buffer
+ self.l_buffer = lineobj.ReadLineTextBuffer(pastebuffer[0])
+ self._update_line()
+ self.mode.paste_line_buffer = pastebuffer[1:]
+ return True
+
+ c = self.console
+ def nop(e):
+ pass
+ try:
+ event = c.getkeypress()
+ except KeyboardInterrupt:
+ event = self.handle_ctrl_c()
+ try:
+ result = self.mode.process_keyevent(event.keyinfo)
+ except EOFError:
+ logger.stop_logging()
+ raise
+ self._update_line()
+ return result
+
+ def readline_setup(self, prompt=''):
+ BaseReadline.readline_setup(self, prompt)
+ self._print_prompt()
+ self._update_line()
+
+ def readline(self, prompt=''):
+ self.readline_setup(prompt)
+ self.ctrl_c_timeout = time.time()
+ self._readline_from_keyboard()
+ self.console.write('\r\n')
+ log('returning(%s)' % self.get_line_buffer())
+ return self.get_line_buffer() + '\n'
+
+ def handle_ctrl_c(self):
+ from pyreadline.keysyms.common import KeyPress
+ from pyreadline.console.event import Event
+ log("KBDIRQ")
+ event = Event(0,0)
+ event.char = "c"
+ event.keyinfo = KeyPress("c", shift=False, control=True,
+ meta=False, keyname=None)
+ if self.allow_ctrl_c:
+ now = time.time()
+ if (now - self.ctrl_c_timeout) < self.ctrl_c_tap_time_interval:
+ log("Raise KeyboardInterrupt")
+ raise KeyboardInterrupt
+ else:
+ self.ctrl_c_timeout = now
+ else:
+ raise KeyboardInterrupt
+ return event
+
diff --git a/pyreadline/unicode_helper.py b/pyreadline/unicode_helper.py
new file mode 100644
index 0000000..b848d60
--- /dev/null
+++ b/pyreadline/unicode_helper.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+#*****************************************************************************
+# Copyright (C) 2007 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+import sys
+
+try:
+ pyreadline_codepage = sys.stdout.encoding
+except AttributeError:
+ # This error occurs when pdb imports readline and doctest has replaced
+ # stdout with stdout collector. We will assume ascii codepage
+ pyreadline_codepage = "ascii"
+
+if pyreadline_codepage is None:
+ pyreadline_codepage = "ascii"
+
+if sys.version_info < (2, 6):
+ bytes = str
+
+PY3 = (sys.version_info >= (3, 0))
+
+def ensure_unicode(text):
+ """helper to ensure that text passed to WriteConsoleW is unicode"""
+ if isinstance(text, bytes):
+ try:
+ return text.decode(pyreadline_codepage, "replace")
+ except (LookupError, TypeError):
+ return text.decode("ascii", "replace")
+ return text
+
+def ensure_str(text):
+ """Convert unicode to str using pyreadline_codepage"""
+ if isinstance(text, str):
+ try:
+ return text.encode(pyreadline_codepage, "replace")
+ except (LookupError, TypeError):
+ return text.encode("ascii", "replace")
+ return text
+
+def biter(text):
+ if PY3 and isinstance(text, bytes):
+ return (s.to_bytes(1, 'big') for s in text)
+ else:
+ return iter(text)
diff --git a/pyreadline/windows_readline.py b/pyreadline/windows_readline.py
new file mode 100644
index 0000000..2d70def
--- /dev/null
+++ b/pyreadline/windows_readline.py
@@ -0,0 +1,79 @@
+# -*- coding: UTF-8 -*-
+#this file is needed in site-packages to emulate readline
+#necessary for rlcompleter since it relies on the existance
+#of a readline module
+from pyreadline.rlmain import Readline
+
+__all__ = [ 'parse_and_bind',
+ 'get_line_buffer',
+ 'insert_text',
+ 'clear_history',
+ 'read_init_file',
+ 'read_history_file',
+ 'write_history_file',
+ 'get_current_history_length',
+ 'get_history_length',
+ 'get_history_item',
+ 'set_history_length',
+ 'set_startup_hook',
+ 'set_pre_input_hook',
+ 'set_completer',
+ 'get_completer',
+ 'get_begidx',
+ 'get_endidx',
+ 'set_completer_delims',
+ 'get_completer_delims',
+ 'add_history',
+ 'callback_handler_install',
+ 'callback_handler_remove',
+ 'callback_read_char',] #Some other objects are added below
+
+
+# create a Readline object to contain the state
+rl = Readline()
+
+if rl.disable_readline:
+ def dummy(completer=""):
+ pass
+ for funk in __all__:
+ globals()[funk] = dummy
+else:
+ def GetOutputFile():
+ '''Return the console object used by readline so that it can be used for printing in color.'''
+ return rl.console
+ __all__.append("GetOutputFile")
+
+ import pyreadline.console as console
+
+ # make these available so this looks like the python readline module
+ read_init_file = rl.read_init_file
+ parse_and_bind = rl.parse_and_bind
+ clear_history = rl.clear_history
+ add_history = rl.add_history
+ insert_text = rl.insert_text
+
+ write_history_file = rl.write_history_file
+ read_history_file = rl.read_history_file
+
+ get_completer_delims = rl.get_completer_delims
+ get_current_history_length = rl.get_current_history_length
+ get_history_length = rl.get_history_length
+ get_history_item = rl.get_history_item
+ get_line_buffer = rl.get_line_buffer
+ set_completer = rl.set_completer
+ get_completer = rl.get_completer
+ get_begidx = rl.get_begidx
+ get_endidx = rl.get_endidx
+
+ set_completer_delims = rl.set_completer_delims
+ set_history_length = rl.set_history_length
+ set_pre_input_hook = rl.set_pre_input_hook
+ set_startup_hook = rl.set_startup_hook
+
+ callback_handler_install=rl.callback_handler_install
+ callback_handler_remove=rl.callback_handler_remove
+ callback_read_char=rl.callback_read_char
+
+ console.install_readline(rl.readline)
+
+__all__.append("rl")
\ No newline at end of file
diff --git a/queryfilters/__init__.py b/queryfilters/__init__.py
index dbd3c6c..f785286 100644
--- a/queryfilters/__init__.py
+++ b/queryfilters/__init__.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
def register_query_filter(name, class_):
"""Register a query filter.
diff --git a/queryfilters/base.py b/queryfilters/base.py
index df914db..797cdfa 100644
--- a/queryfilters/base.py
+++ b/queryfilters/base.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import copy
diff --git a/queryfilters/genericfilters.py b/queryfilters/genericfilters.py
index ce09c56..17095ff 100644
--- a/queryfilters/genericfilters.py
+++ b/queryfilters/genericfilters.py
@@ -16,9 +16,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import random, re
from moleexceptions import FilterConfigException, FilterCreationError
diff --git a/requestfilters/__init__.py b/requestfilters/__init__.py
index 8259659..1c4702b 100644
--- a/requestfilters/__init__.py
+++ b/requestfilters/__init__.py
@@ -16,9 +16,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
def register_request_filter(name, class_):
diff --git a/requestfilters/base.py b/requestfilters/base.py
index 10c5f63..164b0d5 100644
--- a/requestfilters/base.py
+++ b/requestfilters/base.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import copy
diff --git a/requestfilters/urichanger.py b/requestfilters/urichanger.py
index 3007181..d1cc197 100644
--- a/requestfilters/urichanger.py
+++ b/requestfilters/urichanger.py
@@ -16,9 +16,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from urllib.parse import parse_qs, quote
diff --git a/responsefilters/__init__.py b/responsefilters/__init__.py
index 51a5277..facec61 100644
--- a/responsefilters/__init__.py
+++ b/responsefilters/__init__.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
def register_response_filter(name, class_):
"""Register a response filter.
diff --git a/responsefilters/base.py b/responsefilters/base.py
index 215c37a..b0aa9b5 100644
--- a/responsefilters/base.py
+++ b/responsefilters/base.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import copy
diff --git a/responsefilters/regexfilters.py b/responsefilters/regexfilters.py
index add15c5..841ab1a 100644
--- a/responsefilters/regexfilters.py
+++ b/responsefilters/regexfilters.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
import re, sre_constants
from moleexceptions import FilterCreationError
diff --git a/themole.py b/themole.py
index fd5a1e6..92d28de 100644
--- a/themole.py
+++ b/themole.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from moleexceptions import MoleAttributeRequired, PageNotFound, ConnectionException
from moleexceptions import DbmsDetectionFailed, CommentNotFound, ColumnNumberNotFound
@@ -175,8 +175,8 @@ class TheMole:
return
raise
- if self._dbms_mole is None:
- raise DbmsDetectionFailed()
+# if self._dbms_mole is None:
+# raise DbmsDetectionFailed()
if self._dbms_mole.is_string_query():
output_manager.advance('Using string union technique.').line_break()
@@ -207,6 +207,7 @@ class TheMole:
output_manager.echo_output = False
try:
if len(self.dumper.get_databases(self, field, 1)) > 0:
+ self.injectable_field = field
return True
except QueryError:
return False
diff --git a/threader.py b/threader.py
index 235574a..4b256d9 100644
--- a/threader.py
+++ b/threader.py
@@ -18,9 +18,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from threading import Thread, Event, Lock
from moleexceptions import ConnectionException, QueryError
diff --git a/xmlexporter.py b/xmlexporter.py
index e0f834d..d241ac5 100644
--- a/xmlexporter.py
+++ b/xmlexporter.py
@@ -17,9 +17,9 @@
# Developed by: Nasel(http://www.nasel.com.ar)
#
# Authors:
-# Matías Fontanini
+# Matias Fontanini
# Santiago Alessandri
-# Gastón Traberg
+# Gaston Traberg
from pickle import dumps, loads
Debdiff
File lists identical (after any substitutions)
No differences were encountered in the control files