mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-06 17:30:59 +01:00
more efforts
This commit is contained in:
parent
076110785b
commit
fc012ede84
2 changed files with 155 additions and 26 deletions
|
@ -218,7 +218,17 @@ class BufferedFDParallelReader:
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
|
|
||||||
def populate_console(reader, fd, buffer, chunksize, queue):
|
def _expand_console_buffer(cols, max_offset, expandsize, orig_posize, fd):
|
||||||
|
# if we are getting close to the end of the console buffer,
|
||||||
|
# expand it so that we can read from it successfully.
|
||||||
|
rows = ((max_offset + expandsize)//cols)# + 1
|
||||||
|
winutils.set_console_screen_buffer_size(cols, rows, fd=fd)
|
||||||
|
orig_posize = orig_posize[:3] + (rows,)
|
||||||
|
max_offset = (rows - 1) * cols
|
||||||
|
return rows, max_offset, orig_posize
|
||||||
|
|
||||||
|
|
||||||
|
def populate_console(reader, fd, buffer, chunksize, queue, expandsize=None):
|
||||||
"""Reads bytes from the file descriptor and puts lines into the queue.
|
"""Reads bytes from the file descriptor and puts lines into the queue.
|
||||||
The reads happend in parallel, using xonsh.winutils.read_console_output_character(),
|
The reads happend in parallel, using xonsh.winutils.read_console_output_character(),
|
||||||
and is thus only available on windows. If the read fails for any reason, the reader is
|
and is thus only available on windows. If the read fails for any reason, the reader is
|
||||||
|
@ -242,33 +252,69 @@ def populate_console(reader, fd, buffer, chunksize, queue):
|
||||||
# care about without a noticible performance hit.
|
# care about without a noticible performance hit.
|
||||||
# b. Even with this huge size, it is still possible to write more lines than
|
# b. Even with this huge size, it is still possible to write more lines than
|
||||||
# this, so we should scroll along with the console.
|
# this, so we should scroll along with the console.
|
||||||
winutils.get_console_screen_buffer_info(1)
|
# Unfortnately, because we do not have control over the terminal emulator,
|
||||||
x, y, cols, lins = posize = winutils.get_position_size(fd)
|
# It is not possible to compute how far back we should set the begining
|
||||||
|
# read position because we don't know how many characters have been popped
|
||||||
|
# off the top of the buffer. If we did somehow know this number we could do
|
||||||
|
# something like the following:
|
||||||
|
#
|
||||||
|
# new_offset = (y*cols) + x
|
||||||
|
# if new_offset == max_offset:
|
||||||
|
# new_offset -= scolled_offset
|
||||||
|
# x = new_offset%cols
|
||||||
|
# y = new_offset//cols
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# So this method is imperfect and only works as long as the sceen has
|
||||||
|
# room to expand to. Thus the trick here is to expand the screen size
|
||||||
|
# when we get close enough to the end of the screen. There remain some
|
||||||
|
# async issues related to not being able to set the cursor position.
|
||||||
|
# but they just affect the alignment / capture of the output of the
|
||||||
|
# first command run after a screen resize.
|
||||||
|
if expandsize is None:
|
||||||
|
expandsize = 100 * chunksize
|
||||||
|
x, y, cols, rows = posize = winutils.get_position_size(fd)
|
||||||
pre_x = pre_y = -1
|
pre_x = pre_y = -1
|
||||||
orig_posize = posize
|
orig_posize = posize
|
||||||
|
offset = (cols*y) + x
|
||||||
|
max_offset = (rows - 1) * cols
|
||||||
|
# I believe that there is a bug in PTK that if we reset the
|
||||||
|
# cursor position, the cursor on the next prompt is accidentally on
|
||||||
|
# the next line. If this is fixed, uncomment the following line.
|
||||||
|
#if max_offset < offset + expandsize:
|
||||||
|
# rows, max_offset, orig_posize = _expand_console_buffer(
|
||||||
|
# cols, max_offset, expandsize,
|
||||||
|
# orig_posize, fd)
|
||||||
|
# winutils.set_console_cursor_position(x, y, fd=fd)
|
||||||
with open('buf.txt', 'w'):
|
with open('buf.txt', 'w'):
|
||||||
pass
|
pass
|
||||||
while True:
|
while True:
|
||||||
posize = winutils.get_position_size(fd)
|
posize = winutils.get_position_size(fd)
|
||||||
if ((posize[1], posize[0]) <= (y, x) and posize[2:] == (cols, lins)) or \
|
offset = (cols*y) + x
|
||||||
|
if ((posize[1], posize[0]) <= (y, x) and posize[2:] == (cols, rows)) or \
|
||||||
(pre_x == x and pre_y == y):
|
(pre_x == x and pre_y == y):
|
||||||
# already at the current cursor position.
|
# already at or ahead of the current cursor position.
|
||||||
if reader.closed:
|
if reader.closed:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
time.sleep(reader.timeout * 10**reader.sleepscale)
|
time.sleep(reader.timeout * 10**reader.sleepscale)
|
||||||
continue
|
continue
|
||||||
elif posize[2:] == (cols, lins):
|
elif max_offset <= offset + expandsize:
|
||||||
|
rows, max_offset, orig_posize = _expand_console_buffer(cols,
|
||||||
|
max_offset, expandsize,
|
||||||
|
orig_posize, fd)
|
||||||
|
continue
|
||||||
|
elif posize[2:] == (cols, rows):
|
||||||
# cursor updated but screen size is the same.
|
# cursor updated but screen size is the same.
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# screen size changed, which is offset preserving
|
# screen size changed, which is offset preserving
|
||||||
offset = (cols*y) + x
|
|
||||||
orig_posize = posize
|
orig_posize = posize
|
||||||
cols, lins = posize[2:]
|
cols, rows = posize[2:]
|
||||||
x = offset % cols
|
x = offset % cols
|
||||||
y = offset // cols
|
y = offset // cols
|
||||||
pre_x = pre_y = -1
|
pre_x = pre_y = -1
|
||||||
|
max_offset = (rows - 1) * cols
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
buf = winutils.read_console_output_character(x=x, y=y, fd=fd,
|
buf = winutils.read_console_output_character(x=x, y=y, fd=fd,
|
||||||
|
@ -287,9 +333,9 @@ def populate_console(reader, fd, buffer, chunksize, queue):
|
||||||
cur_offset = (cols*cur_y) + cur_x
|
cur_offset = (cols*cur_y) + cur_x
|
||||||
beg_offset = (cols*y) + x
|
beg_offset = (cols*y) + x
|
||||||
end_offset = beg_offset + nread
|
end_offset = beg_offset + nread
|
||||||
if end_offset > cur_offset:
|
if end_offset > cur_offset and cur_offset != max_offset:
|
||||||
buf = buf[:cur_offset-end_offset]
|
buf = buf[:cur_offset-end_offset]
|
||||||
# convert to lines and add to queue
|
# convert to lines
|
||||||
lines = [buf[:(cols-x)]]
|
lines = [buf[:(cols-x)]]
|
||||||
lines += [buf[l*cols+(cols-x):(l+1)*cols+(cols-x)]
|
lines += [buf[l*cols+(cols-x):(l+1)*cols+(cols-x)]
|
||||||
for l in range((nread//cols) + (1 if nread%cols > 0 else 0))]
|
for l in range((nread//cols) + (1 if nread%cols > 0 else 0))]
|
||||||
|
@ -297,6 +343,7 @@ def populate_console(reader, fd, buffer, chunksize, queue):
|
||||||
if not lines:
|
if not lines:
|
||||||
time.sleep(reader.timeout * 10**reader.sleepscale)
|
time.sleep(reader.timeout * 10**reader.sleepscale)
|
||||||
continue
|
continue
|
||||||
|
# put lines in the queue
|
||||||
nl = b'\n'
|
nl = b'\n'
|
||||||
for line in lines[:-1]:
|
for line in lines[:-1]:
|
||||||
queue.put(line.rstrip() + nl)
|
queue.put(line.rstrip() + nl)
|
||||||
|
@ -305,9 +352,10 @@ def populate_console(reader, fd, buffer, chunksize, queue):
|
||||||
else:
|
else:
|
||||||
queue.put(lines[-1])
|
queue.put(lines[-1])
|
||||||
with open('buf.txt', 'a+') as f:
|
with open('buf.txt', 'a+') as f:
|
||||||
f.write("{} {} {}\n----------\n".format(x, y, cols))
|
f.write("{} {} {} {}\n----------\n".format(x, y, cols, rows))
|
||||||
for line in lines:
|
for line in lines:
|
||||||
f.write(repr(line) + '\n')
|
f.write(repr(line) + '\n')
|
||||||
|
# update x and y locations
|
||||||
if (beg_offset + len(buf))%cols == 0:
|
if (beg_offset + len(buf))%cols == 0:
|
||||||
new_offset = beg_offset + len(buf)
|
new_offset = beg_offset + len(buf)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -249,8 +249,16 @@ def set_console_mode(mode, fd=1):
|
||||||
hcon = STDHANDLES[fd]
|
hcon = STDHANDLES[fd]
|
||||||
SetConsoleMode(hcon, mode)
|
SetConsoleMode(hcon, mode)
|
||||||
|
|
||||||
|
@lazyobject
|
||||||
|
def COORD():
|
||||||
|
if platform.has_prompt_toolkit():
|
||||||
|
# turns out that PTK has a separate ctype wrapper
|
||||||
|
# for this struct and also wraps similar function calls
|
||||||
|
# we need to use the same struct to prevent clashes.
|
||||||
|
import prompt_toolkit.win32_types
|
||||||
|
return prompt_toolkit.win32_types.COORD
|
||||||
|
|
||||||
class COORD(ctypes.Structure):
|
class _COORD(ctypes.Structure):
|
||||||
"""Struct from the winapi, representing coordinates in the console.
|
"""Struct from the winapi, representing coordinates in the console.
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -263,6 +271,8 @@ class COORD(ctypes.Structure):
|
||||||
_fields_ = [("X", SHORT),
|
_fields_ = [("X", SHORT),
|
||||||
("Y", SHORT)]
|
("Y", SHORT)]
|
||||||
|
|
||||||
|
return _COORD
|
||||||
|
|
||||||
@lazyobject
|
@lazyobject
|
||||||
def ReadConsoleOutputCharacterA():
|
def ReadConsoleOutputCharacterA():
|
||||||
rcoc = ctypes.windll.kernel32.ReadConsoleOutputCharacterA
|
rcoc = ctypes.windll.kernel32.ReadConsoleOutputCharacterA
|
||||||
|
@ -415,6 +425,9 @@ def get_console_screen_buffer_info(fd=1):
|
||||||
GetConsoleScreenBufferInfo(hcon, byref(csbi))
|
GetConsoleScreenBufferInfo(hcon, byref(csbi))
|
||||||
return csbi
|
return csbi
|
||||||
|
|
||||||
|
#
|
||||||
|
# end colorama forked section
|
||||||
|
#
|
||||||
|
|
||||||
def get_cursor_position(fd=1):
|
def get_cursor_position(fd=1):
|
||||||
"""Gets the current cursor position as an (x, y) tuple."""
|
"""Gets the current cursor position as an (x, y) tuple."""
|
||||||
|
@ -438,3 +451,71 @@ def get_position_size(fd=1):
|
||||||
info = get_console_screen_buffer_info(fd)
|
info = get_console_screen_buffer_info(fd)
|
||||||
return (info.dwCursorPosition.X, info.dwCursorPosition.Y,
|
return (info.dwCursorPosition.X, info.dwCursorPosition.Y,
|
||||||
info.dwSize.X, info.dwSize.Y)
|
info.dwSize.X, info.dwSize.Y)
|
||||||
|
|
||||||
|
|
||||||
|
@lazyobject
|
||||||
|
def SetConsoleScreenBufferSize():
|
||||||
|
"""Set screen buffer dimensions."""
|
||||||
|
scsbs = ctypes.windll.kernel32.SetConsoleScreenBufferSize
|
||||||
|
scsbs.errcheck = check_zero
|
||||||
|
scsbs.argtypes = (
|
||||||
|
HANDLE, # _In_ HANDLE hConsoleOutput
|
||||||
|
COORD, # _In_ COORD dwSize
|
||||||
|
)
|
||||||
|
scsbs.restype = BOOL
|
||||||
|
return scsbs
|
||||||
|
|
||||||
|
|
||||||
|
def set_console_screen_buffer_size(x, y, fd=1):
|
||||||
|
"""Sets the console size for a standard buffer.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
x : int
|
||||||
|
Number of columns.
|
||||||
|
y : int
|
||||||
|
Number of rows.
|
||||||
|
fd : int, optional
|
||||||
|
Standard buffer file descriptor, 0 for stdin, 1 for stdout (default),
|
||||||
|
and 2 for stderr.
|
||||||
|
"""
|
||||||
|
coord = COORD()
|
||||||
|
coord.X = x
|
||||||
|
coord.Y = y
|
||||||
|
hcon = STDHANDLES[fd]
|
||||||
|
rtn = SetConsoleScreenBufferSize(hcon, coord)
|
||||||
|
return rtn
|
||||||
|
|
||||||
|
|
||||||
|
@lazyobject
|
||||||
|
def SetConsoleCursorPosition():
|
||||||
|
"""Set cursor position in console."""
|
||||||
|
sccp = ctypes.windll.kernel32.SetConsoleCursorPosition
|
||||||
|
sccp.errcheck = check_zero
|
||||||
|
sccp.argtypes = (
|
||||||
|
HANDLE, # _In_ HANDLE hConsoleOutput
|
||||||
|
COORD, # _In_ COORD dwCursorPosition
|
||||||
|
)
|
||||||
|
sccp.restype = BOOL
|
||||||
|
return sccp
|
||||||
|
|
||||||
|
|
||||||
|
def set_console_cursor_position(x, y, fd=1):
|
||||||
|
"""Sets the console cursor position for a standard buffer.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
x : int
|
||||||
|
Number of columns.
|
||||||
|
y : int
|
||||||
|
Number of rows.
|
||||||
|
fd : int, optional
|
||||||
|
Standard buffer file descriptor, 0 for stdin, 1 for stdout (default),
|
||||||
|
and 2 for stderr.
|
||||||
|
"""
|
||||||
|
coord = COORD()
|
||||||
|
coord.X = x
|
||||||
|
coord.Y = y
|
||||||
|
hcon = STDHANDLES[fd]
|
||||||
|
rtn = SetConsoleCursorPosition(hcon, coord)
|
||||||
|
return rtn
|
||||||
|
|
Loading…
Add table
Reference in a new issue