mirror of
https://github.com/erkin/ponysay.git
synced 2025-03-29 14:57:43 +01:00
fix balloon cowsay + fix mini balloons + fix wrapping bugs + inform user of exception in wrapping and do skip wrapping
This commit is contained in:
parent
86b983ec7d
commit
e23628daf9
2 changed files with 171 additions and 124 deletions
|
@ -6,7 +6,7 @@ s:-
|
||||||
w:|
|
w:|
|
||||||
e: |
|
e: |
|
||||||
|
|
||||||
ww:<
|
ww:<
|
||||||
ee: >
|
ee: >
|
||||||
|
|
||||||
nw: _
|
nw: _
|
||||||
|
|
293
ponysay.py
293
ponysay.py
|
@ -79,7 +79,7 @@ Checks whether a text ends with a specific text, but has more
|
||||||
@return :bool The result of the test
|
@return :bool The result of the test
|
||||||
'''
|
'''
|
||||||
def endswith(text, ending):
|
def endswith(text, ending):
|
||||||
return text.endswith(ending) and not (text == ending);
|
return text.endswith(ending) and not (text == ending)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -407,10 +407,13 @@ class Ponysay():
|
||||||
'''
|
'''
|
||||||
def __gettermsize(self):
|
def __gettermsize(self):
|
||||||
## Call `stty` to determine the size of the terminal, this way is better then using python's ncurses
|
## Call `stty` to determine the size of the terminal, this way is better then using python's ncurses
|
||||||
termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=sys.stderr).communicate()[0]
|
for channel in (sys.stderr, sys.stdout, sys.stdin):
|
||||||
termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n
|
termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0]
|
||||||
termsize = [int(item) for item in termsize]
|
if len(termsize) > 0:
|
||||||
return termsize
|
termsize = termsize.decode('utf8', 'replace')[:-1].split(' ') # [:-1] removes a \n
|
||||||
|
termsize = [int(item) for item in termsize]
|
||||||
|
return termsize
|
||||||
|
return (24, 80) # fall back to minimal sane size
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -706,8 +709,8 @@ class Ponysay():
|
||||||
## Use default balloon if none is specified
|
## Use default balloon if none is specified
|
||||||
if balloonfile is None:
|
if balloonfile is None:
|
||||||
if isthink:
|
if isthink:
|
||||||
return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ')
|
return Balloon('o', 'o', '( ', ' )', [' _'], ['_'], ['_'], ['_'], ['_ '], ' )', ' )', ' )', ['- '], ['-'], ['-'], ['-'], [' -'], '( ', '( ', '( ')
|
||||||
return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ')
|
return Balloon('\\', '/', '< ', ' >', [' _'], ['_'], ['_'], ['_'], ['_ '], ' \\', ' |', ' /', ['- '], ['-'], ['-'], ['-'], [' -'], '\\ ', '| ', '/ ')
|
||||||
|
|
||||||
## Initialise map for balloon parts
|
## Initialise map for balloon parts
|
||||||
map = {}
|
map = {}
|
||||||
|
@ -716,7 +719,8 @@ class Ponysay():
|
||||||
|
|
||||||
## Read all lines in the balloon file
|
## Read all lines in the balloon file
|
||||||
with open(balloonfile, 'rb') as balloonstream:
|
with open(balloonfile, 'rb') as balloonstream:
|
||||||
data = [line.replace('\n', '') for line in balloonstream.read().decode('utf8', 'replace').split('\n')]
|
data = balloonstream.read().decode('utf8', 'replace')
|
||||||
|
data = [line.replace('\n', '') for line in data.split('\n')]
|
||||||
|
|
||||||
## Parse the balloon file, and fill the map
|
## Parse the balloon file, and fill the map
|
||||||
last = None
|
last = None
|
||||||
|
@ -1380,7 +1384,7 @@ class Balloon():
|
||||||
for j in range(0, len(self.n)):
|
for j in range(0, len(self.n)):
|
||||||
outer = UCS.dispLen(self.nw[j]) + UCS.dispLen(self.ne[j])
|
outer = UCS.dispLen(self.nw[j]) + UCS.dispLen(self.ne[j])
|
||||||
inner = UCS.dispLen(self.nnw[j]) + UCS.dispLen(self.nne[j])
|
inner = UCS.dispLen(self.nnw[j]) + UCS.dispLen(self.nne[j])
|
||||||
if outer + inner >= w:
|
if outer + inner <= w:
|
||||||
rc.append(self.nw[j] + self.nnw[j] + self.n[j] * (w - outer - inner) + self.nne[j] + self.ne[j])
|
rc.append(self.nw[j] + self.nnw[j] + self.n[j] * (w - outer - inner) + self.nne[j] + self.ne[j])
|
||||||
else:
|
else:
|
||||||
rc.append(self.nw[j] + self.n[j] * (w - outer) + self.ne[j])
|
rc.append(self.nw[j] + self.n[j] * (w - outer) + self.ne[j])
|
||||||
|
@ -1391,7 +1395,7 @@ class Balloon():
|
||||||
for j in range(0, len(self.s)):
|
for j in range(0, len(self.s)):
|
||||||
outer = UCS.dispLen(self.sw[j]) + UCS.dispLen(self.se[j])
|
outer = UCS.dispLen(self.sw[j]) + UCS.dispLen(self.se[j])
|
||||||
inner = UCS.dispLen(self.ssw[j]) + UCS.dispLen(self.sse[j])
|
inner = UCS.dispLen(self.ssw[j]) + UCS.dispLen(self.sse[j])
|
||||||
if outer + inner >= w:
|
if outer + inner <= w:
|
||||||
rc.append(self.sw[j] + self.ssw[j] + self.s[j] * (w - outer - inner) + self.sse[j] + self.se[j])
|
rc.append(self.sw[j] + self.ssw[j] + self.s[j] * (w - outer - inner) + self.sse[j] + self.se[j])
|
||||||
else:
|
else:
|
||||||
rc.append(self.sw[j] + self.s[j] * (w - outer) + self.se[j])
|
rc.append(self.sw[j] + self.s[j] * (w - outer) + self.se[j])
|
||||||
|
@ -1440,17 +1444,41 @@ class Backend():
|
||||||
'''
|
'''
|
||||||
def parse(self):
|
def parse(self):
|
||||||
self.__expandMessage()
|
self.__expandMessage()
|
||||||
|
self.__unpadMessage()
|
||||||
self.__loadFile()
|
self.__loadFile()
|
||||||
|
|
||||||
if self.pony.startswith('$$$\n'):
|
if self.pony.startswith('$$$\n'):
|
||||||
self.pony = self.pony[4:]
|
self.pony = self.pony[4:]
|
||||||
infoend = self.pony.index('\n$$$\n')
|
infoend = self.pony.index('\n$$$\n')
|
||||||
printinfo(self.pony[:infoend])
|
printinfo(self.pony[:infoend])
|
||||||
self.pony = self.pony[infoend + 5:]
|
self.pony = self.pony[infoend + 5:]
|
||||||
self.pony = mode + self.pony
|
self.pony = mode + self.pony
|
||||||
|
|
||||||
self.__processPony()
|
self.__processPony()
|
||||||
self.__truncate()
|
self.__truncate()
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
Remove padding spaces fortune cookies are padded with whitespace (damn featherbrains)
|
||||||
|
'''
|
||||||
|
def __unpadMessage(self):
|
||||||
|
lines = self.message.split('\n')
|
||||||
|
for spaces in (8, 4, 2, 1):
|
||||||
|
padded = True
|
||||||
|
for line in lines:
|
||||||
|
if not line.startswith(' ' * spaces):
|
||||||
|
padded = False
|
||||||
|
break
|
||||||
|
if padded:
|
||||||
|
for i in range(0, len(lines)):
|
||||||
|
line = lines[i]
|
||||||
|
while line.startswith(' ' * spaces):
|
||||||
|
line = line[spaces:]
|
||||||
|
lines[i] = line
|
||||||
|
lines = [line.rstrip(' ') for line in lines]
|
||||||
|
self.message = '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Converts all tabs in the message to spaces by expanding
|
Converts all tabs in the message to spaces by expanding
|
||||||
'''
|
'''
|
||||||
|
@ -1728,131 +1756,149 @@ class Backend():
|
||||||
@return :str The message wrapped
|
@return :str The message wrapped
|
||||||
'''
|
'''
|
||||||
def __wrapMessage(self, message, wrap):
|
def __wrapMessage(self, message, wrap):
|
||||||
AUTO_PUSH = '\033[0101y0~'
|
|
||||||
AUTO_POP = '\033[10101~'
|
|
||||||
msg = message.replace('\n', AUTO_PUSH + '\n' + AUTO_POP);
|
|
||||||
cstack = ColourStack(AUTO_PUSH, AUTO_POP)
|
|
||||||
buf = ''
|
buf = ''
|
||||||
for c in msg:
|
try:
|
||||||
buf += c + cstack.feed(c)
|
AUTO_PUSH = '\033[01010~'
|
||||||
lines = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n')
|
AUTO_POP = '\033[10101~'
|
||||||
buf = ''
|
msg = message.replace('\n', AUTO_PUSH + '\n' + AUTO_POP);
|
||||||
for line in lines:
|
cstack = ColourStack(AUTO_PUSH, AUTO_POP)
|
||||||
b = [None] * len(line)
|
for c in msg:
|
||||||
map = {}
|
buf += c + cstack.feed(c)
|
||||||
(bi, cols, w) = (0, 0, wrap)
|
lines = buf.replace(AUTO_PUSH, '').replace(AUTO_POP, '').split('\n')
|
||||||
(indent, indentc) = (-1, 0)
|
buf = ''
|
||||||
|
|
||||||
(i, n) = (0, len(line))
|
for line in lines:
|
||||||
while i <= n:
|
b = [None] * len(line)
|
||||||
d = None
|
map = {0 : 0}
|
||||||
if i < n:
|
(bi, cols, w) = (0, 0, wrap)
|
||||||
d = line[i]
|
(indent, indentc) = (-1, 0)
|
||||||
i += 1
|
|
||||||
if d == '\033': # TODO this should use self.__getcolour()
|
(i, n) = (0, len(line))
|
||||||
## Invisible stuff
|
while i <= n:
|
||||||
b[bi] = d
|
d = None
|
||||||
bi += 1
|
if i < n:
|
||||||
b[bi] = line[i]
|
d = line[i]
|
||||||
d = line[i]
|
|
||||||
bi += 1
|
|
||||||
i += 1
|
i += 1
|
||||||
if d == '[':
|
if d == '\033': # TODO this should use self.__getcolour()
|
||||||
while True:
|
## Invisible stuff
|
||||||
b[bi] = line[i]
|
b[bi] = d
|
||||||
d = line[i]
|
bi += 1
|
||||||
bi += 1
|
|
||||||
i += 1
|
|
||||||
if (('a' <= d) and (d <= 'z')) or (('A' <= d) and (d <= 'Z')) or (d == '~'):
|
|
||||||
break
|
|
||||||
elif d == ']':
|
|
||||||
b[bi] = line[i]
|
b[bi] = line[i]
|
||||||
d = line[i]
|
d = line[i]
|
||||||
bi += 1
|
bi += 1
|
||||||
i += 1
|
i += 1
|
||||||
if d == 'P':
|
if d == '[':
|
||||||
for j in range(0, 7):
|
while True:
|
||||||
b[bi] = line[i]
|
b[bi] = line[i]
|
||||||
|
d = line[i]
|
||||||
bi += 1
|
bi += 1
|
||||||
i += 1
|
i += 1
|
||||||
elif (d is not None) and (d != ' '):
|
if (('a' <= d) and (d <= 'z')) or (('A' <= d) and (d <= 'Z')) or (d == '~'):
|
||||||
## Fetch word
|
break
|
||||||
if indent == -1:
|
elif d == ']':
|
||||||
indent = i - 1
|
b[bi] = line[i]
|
||||||
for j in range(0, indent):
|
d = line[i]
|
||||||
if line[j] == ' ':
|
bi += 1
|
||||||
indentc += 1
|
i += 1
|
||||||
b[bi] = d
|
if d == 'P':
|
||||||
bi += 1
|
for j in range(0, 7):
|
||||||
if (not UCS.isCombining(d)) and (d != ''):
|
b[bi] = line[i]
|
||||||
cols += 1
|
bi += 1
|
||||||
map[cols] = bi
|
i += 1
|
||||||
else:
|
elif (d is not None) and (d != ' '):
|
||||||
## Wrap?
|
## Fetch word
|
||||||
mm = 0
|
if indent == -1:
|
||||||
bisub = 0
|
indent = i - 1
|
||||||
iwrap = wrap - (0 if indent == 1 else indentc)
|
for j in range(0, indent):
|
||||||
|
if line[j] == ' ':
|
||||||
while ((w > 8) and (cols > w + 5)) or (cols > iwrap): # TODO make configurable
|
indentc += 1
|
||||||
## wrap
|
b[bi] = d
|
||||||
x = w;
|
bi += 1
|
||||||
nbsp = b[map[mm + x]] == ' '
|
if (not UCS.isCombining(d)) and (d != ''):
|
||||||
m = map[mm + x]
|
cols += 1
|
||||||
|
map[cols] = bi
|
||||||
if ('' in b[bisub : m]) and not nbsp:
|
|
||||||
hyphen = m - 1
|
|
||||||
while b[hyphen] != '':
|
|
||||||
hyphen -= 1
|
|
||||||
while map[mm + x] > hyphen: ## Only looking backward, if foreward is required the word is probabily not hyphenated correctly
|
|
||||||
x -= 1
|
|
||||||
x += 1
|
|
||||||
m = map[mm + x]
|
|
||||||
|
|
||||||
mm += x - (0 if nbsp else 1) ## − 1 so we have space for a hythen
|
|
||||||
|
|
||||||
for bb in b[bisub : m]:
|
|
||||||
buf += bb
|
|
||||||
buf += '\n' if nbsp else '\0\n'
|
|
||||||
cols -= x - (0 if nbsp else 1)
|
|
||||||
bisub = m
|
|
||||||
|
|
||||||
w = iwrap
|
|
||||||
if indent != -1:
|
|
||||||
buf += line[:indent]
|
|
||||||
|
|
||||||
for j in range(bisub, bi):
|
|
||||||
b[j - bisub] = b[j]
|
|
||||||
bi -= bisub
|
|
||||||
|
|
||||||
if cols > w:
|
|
||||||
buf += '\n'
|
|
||||||
w = wrap
|
|
||||||
if indent != -1:
|
|
||||||
buf += line[:indent]
|
|
||||||
w -= indentc
|
|
||||||
for bb in b[:bi]:
|
|
||||||
buf += bb
|
|
||||||
w -= cols
|
|
||||||
cols = 0
|
|
||||||
bi = 0
|
|
||||||
if d is None:
|
|
||||||
i += 1
|
|
||||||
else:
|
else:
|
||||||
if w > 0:
|
## Wrap?
|
||||||
buf += ' '
|
mm = 0
|
||||||
w -= 1
|
bisub = 0
|
||||||
else:
|
iwrap = wrap - (0 if indent == 1 else indentc)
|
||||||
|
|
||||||
|
while ((w > 8) and (cols > w + 5)) or (cols > iwrap): # TODO make configurable
|
||||||
|
## wrap
|
||||||
|
x = w;
|
||||||
|
if mm + x not in map: # Too much whitespace ?
|
||||||
|
cols = 0
|
||||||
|
break
|
||||||
|
nbsp = b[map[mm + x]] == ' '
|
||||||
|
m = map[mm + x]
|
||||||
|
|
||||||
|
if ('' in b[bisub : m]) and not nbsp:
|
||||||
|
hyphen = m - 1
|
||||||
|
while b[hyphen] != '':
|
||||||
|
hyphen -= 1
|
||||||
|
while map[mm + x] > hyphen: ## Only looking backward, if foreward is required the word is probabily not hyphenated correctly
|
||||||
|
x -= 1
|
||||||
|
x += 1
|
||||||
|
m = map[mm + x]
|
||||||
|
|
||||||
|
mm += x - (0 if nbsp else 1) ## − 1 so we have space for a hythen
|
||||||
|
|
||||||
|
for bb in b[bisub : m]:
|
||||||
|
buf += bb
|
||||||
|
buf += '\n' if nbsp else '\0\n'
|
||||||
|
cols -= x - (0 if nbsp else 1)
|
||||||
|
bisub = m
|
||||||
|
|
||||||
|
w = iwrap
|
||||||
|
if indent != -1:
|
||||||
|
buf += line[:indent]
|
||||||
|
|
||||||
|
for j in range(bisub, bi):
|
||||||
|
b[j - bisub] = b[j]
|
||||||
|
bi -= bisub
|
||||||
|
|
||||||
|
if cols > w:
|
||||||
buf += '\n'
|
buf += '\n'
|
||||||
w = wrap
|
w = wrap
|
||||||
if indent != -1:
|
if indent != -1:
|
||||||
buf + line[:indent]
|
buf += line[:indent]
|
||||||
w -= indentc
|
w -= indentc
|
||||||
buf += '\n'
|
for bb in b[:bi]:
|
||||||
|
buf += bb
|
||||||
rc = '\n'.join(line.rstrip() for line in buf[:-1].split('\n'));
|
w -= cols
|
||||||
rc = rc.replace('', ''); # remove soft hyphens
|
cols = 0
|
||||||
rc = rc.replace('\0', '%s%s%s' % (AUTO_PUSH, self.hyphen, AUTO_POP))
|
bi = 0
|
||||||
return rc
|
if d is None:
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
if w > 0:
|
||||||
|
buf += ' '
|
||||||
|
w -= 1
|
||||||
|
else:
|
||||||
|
buf += '\n'
|
||||||
|
w = wrap
|
||||||
|
if indent != -1:
|
||||||
|
buf + line[:indent]
|
||||||
|
w -= indentc
|
||||||
|
buf += '\n'
|
||||||
|
|
||||||
|
rc = '\n'.join(line.rstrip() for line in buf[:-1].split('\n'));
|
||||||
|
rc = rc.replace('', ''); # remove soft hyphens
|
||||||
|
rc = rc.replace('\0', '%s%s%s' % (AUTO_PUSH, self.hyphen, AUTO_POP))
|
||||||
|
return rc
|
||||||
|
except Exception as err:
|
||||||
|
import traceback
|
||||||
|
errormessage = ''.join(traceback.format_exception(type(err), err, None))
|
||||||
|
rc = '\n'.join(line.rstrip() for line in buf.split('\n'));
|
||||||
|
rc = rc.replace('\0', '%s%s%s' % (AUTO_PUSH, self.hyphen, AUTO_POP))
|
||||||
|
errormessage += '\n---- WRAPPING BUFFER ----\n\n' + rc
|
||||||
|
try:
|
||||||
|
if os.readlink('/proc/self/fd/2') != os.readlink('/proc/self/fd/1'):
|
||||||
|
printerr(errormessage, end='')
|
||||||
|
return message
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return message + '\n\n\033[0;1;31m---- EXCEPTION IN PONYSAY WHILE WRAPPING ----\033[0m\n\n' + errormessage
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -1884,7 +1930,7 @@ class ColourStack():
|
||||||
@return :str String that should be inserted into your buffer
|
@return :str String that should be inserted into your buffer
|
||||||
'''
|
'''
|
||||||
def push(self):
|
def push(self):
|
||||||
self.stack = [[self.bufproto, None, None, [False] * 9]] + self.stack
|
self.stack.insert(0, [self.bufproto, None, None, [False] * 9])
|
||||||
if len(self.stack) == 1:
|
if len(self.stack) == 1:
|
||||||
return None
|
return None
|
||||||
return '\033[0m'
|
return '\033[0m'
|
||||||
|
@ -1896,9 +1942,10 @@ class ColourStack():
|
||||||
@return :str String that should be inserted into your buffer
|
@return :str String that should be inserted into your buffer
|
||||||
'''
|
'''
|
||||||
def pop(self):
|
def pop(self):
|
||||||
old = self.stack[0]
|
old = self.stack.pop(0)
|
||||||
self.stack = self.stack[1:]
|
|
||||||
rc = '\033[0;'
|
rc = '\033[0;'
|
||||||
|
if len(self.stack) == 0: # last resort in case something made it pop too mush
|
||||||
|
push()
|
||||||
new = self.stack[0]
|
new = self.stack[0]
|
||||||
if new[1] is not None: rc += new[1] + ';'
|
if new[1] is not None: rc += new[1] + ';'
|
||||||
if new[2] is not None: rc += new[2] + ';'
|
if new[2] is not None: rc += new[2] + ';'
|
||||||
|
|
Loading…
Add table
Reference in a new issue