mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-06 09:20:57 +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()
|
||||
|
||||
|
||||
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.
|
||||
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
|
||||
|
@ -242,33 +252,69 @@ def populate_console(reader, fd, buffer, chunksize, queue):
|
|||
# care about without a noticible performance hit.
|
||||
# b. Even with this huge size, it is still possible to write more lines than
|
||||
# this, so we should scroll along with the console.
|
||||
winutils.get_console_screen_buffer_info(1)
|
||||
x, y, cols, lins = posize = winutils.get_position_size(fd)
|
||||
# Unfortnately, because we do not have control over the terminal emulator,
|
||||
# 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
|
||||
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'):
|
||||
pass
|
||||
while True:
|
||||
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):
|
||||
# already at the current cursor position.
|
||||
# already at or ahead of the current cursor position.
|
||||
if reader.closed:
|
||||
break
|
||||
else:
|
||||
time.sleep(reader.timeout * 10**reader.sleepscale)
|
||||
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.
|
||||
pass
|
||||
else:
|
||||
# screen size changed, which is offset preserving
|
||||
offset = (cols*y) + x
|
||||
orig_posize = posize
|
||||
cols, lins = posize[2:]
|
||||
cols, rows = posize[2:]
|
||||
x = offset % cols
|
||||
y = offset // cols
|
||||
pre_x = pre_y = -1
|
||||
max_offset = (rows - 1) * cols
|
||||
continue
|
||||
try:
|
||||
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
|
||||
beg_offset = (cols*y) + x
|
||||
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]
|
||||
# convert to lines and add to queue
|
||||
# convert to lines
|
||||
lines = [buf[:(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))]
|
||||
|
@ -297,6 +343,7 @@ def populate_console(reader, fd, buffer, chunksize, queue):
|
|||
if not lines:
|
||||
time.sleep(reader.timeout * 10**reader.sleepscale)
|
||||
continue
|
||||
# put lines in the queue
|
||||
nl = b'\n'
|
||||
for line in lines[:-1]:
|
||||
queue.put(line.rstrip() + nl)
|
||||
|
@ -305,9 +352,10 @@ def populate_console(reader, fd, buffer, chunksize, queue):
|
|||
else:
|
||||
queue.put(lines[-1])
|
||||
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:
|
||||
f.write(repr(line) + '\n')
|
||||
# update x and y locations
|
||||
if (beg_offset + len(buf))%cols == 0:
|
||||
new_offset = beg_offset + len(buf)
|
||||
else:
|
||||
|
|
|
@ -249,19 +249,29 @@ def set_console_mode(mode, fd=1):
|
|||
hcon = STDHANDLES[fd]
|
||||
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):
|
||||
"""Struct from the winapi, representing coordinates in the console.
|
||||
class _COORD(ctypes.Structure):
|
||||
"""Struct from the winapi, representing coordinates in the console.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
X : int
|
||||
Column position
|
||||
Y : int
|
||||
Row position
|
||||
"""
|
||||
_fields_ = [("X", SHORT),
|
||||
("Y", SHORT)]
|
||||
Attributes
|
||||
----------
|
||||
X : int
|
||||
Column position
|
||||
Y : int
|
||||
Row position
|
||||
"""
|
||||
_fields_ = [("X", SHORT),
|
||||
("Y", SHORT)]
|
||||
|
||||
return _COORD
|
||||
|
||||
@lazyobject
|
||||
def ReadConsoleOutputCharacterA():
|
||||
|
@ -415,6 +425,9 @@ def get_console_screen_buffer_info(fd=1):
|
|||
GetConsoleScreenBufferInfo(hcon, byref(csbi))
|
||||
return csbi
|
||||
|
||||
#
|
||||
# end colorama forked section
|
||||
#
|
||||
|
||||
def get_cursor_position(fd=1):
|
||||
"""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)
|
||||
return (info.dwCursorPosition.X, info.dwCursorPosition.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