| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -118,3 +118,38 @@ For known bugs please see bpython's issue tracker at | |||
| 118 | 118 | bitbucket: | |
| 119 | 119 | ||
| 120 | 120 | http://bitbucket.org/bobf/bpython/issues/ | |
| 121 | + | ||
| 122 | + CLI Windows Support | ||
| 123 | + =================== | ||
| 124 | + | ||
| 125 | + Dependencies | ||
| 126 | + ------------ | ||
| 127 | + Curses | ||
| 128 | + Use the appropriate version compiled by Christoph Gohlke | ||
| 129 | + http://www.lfd.uci.edu/~gohlke/pythonlibs/ | ||
| 130 | + | ||
| 131 | + pyreadline | ||
| 132 | + Use the version in the cheeseshop | ||
| 133 | + http://pypi.python.org/pypi/pyreadline/ | ||
| 134 | + | ||
| 135 | + Recommended | ||
| 136 | + ----------- | ||
| 137 | + Obtain the less program from GnuUtils. This makes the pager work as intended. | ||
| 138 | + It can be obtained from cygwin or GnuWin32 or msys | ||
| 139 | + | ||
| 140 | + Current version is tested with | ||
| 141 | + ------------------------------ | ||
| 142 | + Curses 2.2 | ||
| 143 | + pyreadline 1.7 | ||
| 144 | + | ||
| 145 | + Curses Notes | ||
| 146 | + ------------ | ||
| 147 | + The curses used has a bug where the colours are displayed incorrectly: | ||
| 148 | + red is swapped with blue | ||
| 149 | + cyan is swapped with yellow | ||
| 150 | + | ||
| 151 | + To correct this I have provided my windows.theme file. | ||
| 152 | + | ||
| 153 | + This curses implementation has 16 colors (dark and light versions of the colours) | ||
| 154 | + | ||
| 155 | + | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,18 +22,38 @@ | |||
| 22 | 22 | # THE SOFTWARE. | |
| 23 | 23 | # | |
| 24 | 24 | ||
| 25 | + # Modified by Brandon Navra | ||
| 26 | + # Notes for Windows | ||
| 27 | + # Prerequsites | ||
| 28 | + # - Curses | ||
| 29 | + # - pyreadline | ||
| 30 | + # | ||
| 31 | + # Added | ||
| 32 | + # | ||
| 33 | + # - Support for running on windows command prompt | ||
| 34 | + # - input from numpad keys | ||
| 35 | + # | ||
| 36 | + # Issues | ||
| 37 | + # | ||
| 38 | + # - Suspend doesn't work nor does detection of resizing of screen | ||
| 39 | + # - Instead the suspend key exits the program | ||
| 40 | + # - View source doesn't work on windows unless you install the less program (From GnuUtils or Cygwin) | ||
| 41 | + | ||
| 25 | 42 | from __future__ import division, with_statement | |
| 26 | 43 | ||
| 44 | + import platform | ||
| 27 | 45 | import os | |
| 28 | 46 | import sys | |
| 29 | 47 | import curses | |
| 30 | 48 | import math | |
| 31 | 49 | import re | |
| 32 | 50 | import time | |
| 33 | - import signal | ||
| 51 | + | ||
| 34 | 52 | import struct | |
| 35 | - import termios | ||
| 36 | - import fcntl | ||
| 53 | + if platform.system() != 'Windows': | ||
| 54 | + import signal #Windows does not have job control | ||
| 55 | + import termios #Windows uses curses | ||
| 56 | + import fcntl #Windows uses curses | ||
| 37 | 57 | import unicodedata | |
| 38 | 58 | import errno | |
| 39 | 59 | ||
@@ -76,6 +96,7 @@ | |||
| 76 | 96 | DO_RESIZE = False | |
| 77 | 97 | # --- | |
| 78 | 98 | ||
| 99 | + | ||
| 79 | 100 | def getpreferredencoding(): | |
| 80 | 101 | return locale.getpreferredencoding() or sys.getdefaultencoding() | |
| 81 | 102 | ||
@@ -136,7 +157,7 @@ def readline(self, size=-1): | |||
| 136 | 157 | ||
| 137 | 158 | curses.raw(True) | |
| 138 | 159 | try: | |
| 139 | - while not buffer.endswith('\n'): | ||
| 160 | + while not (buffer.endswith('\n') or buffer.endswith('\r')): | ||
| 140 | 161 | key = self.interface.get_key() | |
| 141 | 162 | if key in [curses.erasechar(), 'KEY_BACKSPACE']: | |
| 142 | 163 | y, x = self.interface.scr.getyx() | |
@@ -147,7 +168,7 @@ def readline(self, size=-1): | |||
| 147 | 168 | elif key == chr(4) and not buffer: | |
| 148 | 169 | # C-d | |
| 149 | 170 | return '' | |
| 150 | - elif (key != '\n' and | ||
| 171 | + elif (key not in ('\n', '\r') and | ||
| 151 | 172 | (len(key) > 1 or unicodedata.category(key) == 'Cc')): | |
| 152 | 173 | continue | |
| 153 | 174 | sys.stdout.write(key) | |
@@ -224,6 +245,21 @@ def make_colors(config): | |||
| 224 | 245 | 'w': 7, | |
| 225 | 246 | 'd': -1, | |
| 226 | 247 | } | |
| 248 | + | ||
| 249 | + if platform.system() == 'Windows': | ||
| 250 | + c = dict(c.items() + | ||
| 251 | + [ | ||
| 252 | + ('K', 8), | ||
| 253 | + ('R', 9), | ||
| 254 | + ('G', 10), | ||
| 255 | + ('Y', 11), | ||
| 256 | + ('B', 12), | ||
| 257 | + ('M', 13), | ||
| 258 | + ('C', 14), | ||
| 259 | + ('W', 15), | ||
| 260 | + ] | ||
| 261 | + ) | ||
| 262 | + | ||
| 227 | 263 | for i in range(63): | |
| 228 | 264 | if i > 7: | |
| 229 | 265 | j = i // 8 | |
@@ -773,14 +809,22 @@ def p_key(self, key): | |||
| 773 | 809 | ||
| 774 | 810 | config = self.config | |
| 775 | 811 | ||
| 776 | - if key == chr(8): # C-Backspace (on my computer anyway!) | ||
| 812 | + if platform.system() == 'Windows': | ||
| 813 | + C_BACK = chr(127) | ||
| 814 | + BACKSP = chr(8) | ||
| 815 | + else: | ||
| 816 | + C_BACK = chr(8) | ||
| 817 | + BACKSP = chr(127) | ||
| 818 | + | ||
| 819 | + if key == C_BACK: # C-Backspace (on my computer anyway!) | ||
| 777 | 820 | self.clrtobol() | |
| 778 | 821 | key = '\n' | |
| 779 | 822 | # Don't return; let it get handled | |
| 780 | - if key == chr(27): | ||
| 823 | + | ||
| 824 | + if key == chr(27): #Escape Key | ||
| 781 | 825 | return '' | |
| 782 | 826 | ||
| 783 | - if key in (chr(127), 'KEY_BACKSPACE'): | ||
| 827 | + if key in (BACKSP, 'KEY_BACKSPACE'): | ||
| 784 | 828 | self.bs() | |
| 785 | 829 | self.complete() | |
| 786 | 830 | return '' | |
@@ -893,7 +937,7 @@ def p_key(self, key): | |||
| 893 | 937 | self.statusbar.message(_('Cannot show source.')) | |
| 894 | 938 | return '' | |
| 895 | 939 | ||
| 896 | - elif key == '\n': | ||
| 940 | + elif key in ('\n', '\r', 'PADENTER'): | ||
| 897 | 941 | self.lf() | |
| 898 | 942 | return None | |
| 899 | 943 | ||
@@ -904,9 +948,25 @@ def p_key(self, key): | |||
| 904 | 948 | return self.tab(back=True) | |
| 905 | 949 | ||
| 906 | 950 | elif key in key_dispatch[config.suspend_key]: | |
| 907 | - self.suspend() | ||
| 908 | - return '' | ||
| 951 | + if platform.system() != 'Windows': | ||
| 952 | + self.suspend() | ||
| 953 | + return '' | ||
| 954 | + else: | ||
| 955 | + self.do_exit = True | ||
| 956 | + return None | ||
| 909 | 957 | ||
| 958 | + elif key[0:3] == 'PAD' and not key in ('PAD0', 'PADSTOP'): | ||
| 959 | + pad_keys = { | ||
| 960 | + 'PADMINUS': '-', | ||
| 961 | + 'PADPLUS': '+', | ||
| 962 | + 'PADSLASH': '/', | ||
| 963 | + 'PADSTAR': '*', | ||
| 964 | + } | ||
| 965 | + try: | ||
| 966 | + self.addstr(pad_keys[key]) | ||
| 967 | + self.print_line(self.s) | ||
| 968 | + except KeyError: | ||
| 969 | + return '' | ||
| 910 | 970 | elif len(key) == 1 and not unicodedata.category(key) == 'Cc': | |
| 911 | 971 | self.addstr(key) | |
| 912 | 972 | self.print_line(self.s) | |
@@ -1296,8 +1356,9 @@ def size(self): | |||
| 1296 | 1356 | ||
| 1297 | 1357 | def suspend(self): | |
| 1298 | 1358 | """Suspend the current process for shell job control.""" | |
| 1299 | - curses.endwin() | ||
| 1300 | - os.kill(os.getpid(), signal.SIGSTOP) | ||
| 1359 | + if platform.system() != 'Windows': | ||
| 1360 | + curses.endwin() | ||
| 1361 | + os.kill(os.getpid(), signal.SIGSTOP) | ||
| 1301 | 1362 | ||
| 1302 | 1363 | def tab(self, back=False): | |
| 1303 | 1364 | """Process the tab key being hit. If there's only whitespace | |
@@ -1575,9 +1636,32 @@ def gethw(): | |||
| 1575 | 1636 | So I'm not going to ask any questions. | |
| 1576 | 1637 | ||
| 1577 | 1638 | """ | |
| 1578 | - h, w = struct.unpack( | ||
| 1579 | - "hhhh", | ||
| 1580 | - fcntl.ioctl(sys.__stdout__, termios.TIOCGWINSZ, "\000" * 8))[0:2] | ||
| 1639 | + | ||
| 1640 | + if platform.system() != 'Windows': | ||
| 1641 | + h, w = struct.unpack( | ||
| 1642 | + "hhhh", | ||
| 1643 | + fcntl.ioctl(sys.__stdout__, termios.TIOCGWINSZ, "\000" * 8))[0:2] | ||
| 1644 | + else: | ||
| 1645 | + from ctypes import windll, create_string_buffer | ||
| 1646 | + | ||
| 1647 | + # stdin handle is -10 | ||
| 1648 | + # stdout handle is -11 | ||
| 1649 | + # stderr handle is -12 | ||
| 1650 | + | ||
| 1651 | + h = windll.kernel32.GetStdHandle(-12) | ||
| 1652 | + csbi = create_string_buffer(22) | ||
| 1653 | + res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) | ||
| 1654 | + | ||
| 1655 | + if res: | ||
| 1656 | + import struct | ||
| 1657 | + (bufx, bufy, curx, cury, wattr, | ||
| 1658 | + left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) | ||
| 1659 | + sizex = right - left + 1 | ||
| 1660 | + sizey = bottom - top + 1 | ||
| 1661 | + else: | ||
| 1662 | + sizex, sizey = stdscr.getmaxyx()# can't determine actual size - return default values | ||
| 1663 | + | ||
| 1664 | + h, w = sizey, sizex | ||
| 1581 | 1665 | return h, w | |
| 1582 | 1666 | ||
| 1583 | 1667 | ||
@@ -1679,10 +1763,11 @@ def main_curses(scr, args, config, interactive=True, locals_=None, | |||
| 1679 | 1763 | global colors | |
| 1680 | 1764 | DO_RESIZE = False | |
| 1681 | 1765 | ||
| 1682 | - old_sigwinch_handler = signal.signal(signal.SIGWINCH, | ||
| 1683 | - lambda *_: sigwinch(scr)) | ||
| 1684 | - # redraw window after being suspended | ||
| 1685 | - old_sigcont_handler = signal.signal(signal.SIGCONT, lambda *_: sigcont(scr)) | ||
| 1766 | + if platform.system() != 'Windows': | ||
| 1767 | + old_sigwinch_handler = signal.signal(signal.SIGWINCH, | ||
| 1768 | + lambda *_: sigwinch(scr)) | ||
| 1769 | + # redraw window after being suspended | ||
| 1770 | + old_sigcont_handler = signal.signal(signal.SIGCONT, lambda *_: sigcont(scr)) | ||
| 1686 | 1771 | ||
| 1687 | 1772 | stdscr = scr | |
| 1688 | 1773 | try: | |
@@ -1733,8 +1818,9 @@ def main_curses(scr, args, config, interactive=True, locals_=None, | |||
| 1733 | 1818 | curses.raw(False) | |
| 1734 | 1819 | ||
| 1735 | 1820 | # Restore signal handlers | |
| 1736 | - signal.signal(signal.SIGWINCH, old_sigwinch_handler) | ||
| 1737 | - signal.signal(signal.SIGCONT, old_sigcont_handler) | ||
| 1821 | + if platform.system() != 'Windows': | ||
| 1822 | + signal.signal(signal.SIGWINCH, old_sigwinch_handler) | ||
| 1823 | + signal.signal(signal.SIGCONT, old_sigcont_handler) | ||
| 1738 | 1824 | ||
| 1739 | 1825 | return clirepl.getstdout() | |
| 1740 | 1826 | ||
@@ -1743,6 +1829,7 @@ def main(args=None, locals_=None, banner=None): | |||
| 1743 | 1829 | locale.setlocale(locale.LC_ALL, "") | |
| 1744 | 1830 | translations.init() | |
| 1745 | 1831 | ||
| 1832 | + | ||
| 1746 | 1833 | config, options, exec_args = bpython.args.parse(args) | |
| 1747 | 1834 | ||
| 1748 | 1835 | # Save stdin, stdout and stderr for later restoration | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,28 @@ | |||
| 1 | + # Each letter represents a colour marker: | ||
| 2 | + # k, r, g, y, b, m, c, w, d | ||
| 3 | + # which stands for: | ||
| 4 | + # blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default | ||
| 5 | + # | ||
| 6 | + # But in Windows (for now) | ||
| 7 | + # K, R, G, Y, B, M, C, W, k | ||
| 8 | + # blacK, blue, Green, cyan, red, Magenta, yellow, hite, grey | ||
| 9 | + # Capital letters represent bold (brighter) | ||
| 10 | + # Copy to %USERPROFILE%\.bpython\windows.theme and set "color_scheme = windows" in %USERPROFILE%\.bpython\config | ||
| 11 | + | ||
| 12 | + [syntax] | ||
| 13 | + keyword = G | ||
| 14 | + name = Y | ||
| 15 | + comment = c | ||
| 16 | + string = M | ||
| 17 | + error = B | ||
| 18 | + number = C | ||
| 19 | + operator = G | ||
| 20 | + punctuation = C | ||
| 21 | + token = M | ||
| 22 | + | ||
| 23 | + [interface] | ||
| 24 | + background = r | ||
| 25 | + output = w | ||
| 26 | + main = W | ||
| 27 | + prompt = W | ||
| 28 | + prompt_more = K | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments