FW: Text reflow woes (or: I want bullets back!)y

solderpunk solderpunk at SDF.ORG
Mon Jan 20 21:26:52 GMT 2020


Thanks for taking the time to write this!

There are a few details that could be nitpicked (e.g. a lot of this code
seems to assume that *s or #s at the start of lines are followed by
whitespace, which hasn't been specced), but I'm totally happy that this
code is representative of the complexity involved in handling everything
proposed so far.

If a bare-minimum renderer implementing only the compulsory core line
types can be done in ~10 lines and a full-strength renderer implementing
everything to the max can be done in ~100 lines then I'm totally happy
with that - in terms of implementation difficulty.

I still want to think very carefully about graceful degradation, to make
sure those ~10 line renderers still yield usable results.  I still have
real concerns about ordered lists in that regard.

Cheers,
Solderpunk



On Mon, Jan 20, 2020 at 11:53:51AM -0800, Aaron Janse wrote:
> Attached is a <100 line python gemini renderer with the following features:
> * no external dependencies
> * only two state variables
> * unlimited-depth ordered lists that rotates through
>   * numbers
>   * letters (does `az` after `z`, `aaa` after `zz`, etc)
>   * roman numerals
> * unlimited-depth unordered lists
> * unlimited-depth headers with rotating colors
> * wraps at word boundaries, with fancy indents for lists and quotes
> * colors for all special syntax
> * horizontal rules that span the width of the display
> * preformatted text
> * links
> 
> To use this script, pipe text/gemini into stdin.
> 
> I hope this makes a strong case that these features aren't too complex to
> implement.

> #!/usr/bin/env python3
> 
> import sys
> import textwrap
> 
> def int2roman(number):
>     numerals = { 1 : "I", 4 : "IV", 5 : "V", 9 : "IX", 10 : "X", 40 : "XL", 
>         		 50 : "L", 90 : "XC", 100 : "C", 400 : "CD", 500 : "D",
>         		 900 : "CM", 1000 : "M" }
>     result = ""
>     for value, numeral in sorted(numerals.items(), reverse=True):
>         while number >= value:
>             result += numeral
>             number -= value
>     return result
> 
> width = 80
> 
> # only two state variables
> preformatted = False
> list_counter = [0]
> 
> for line in sys.stdin:
> 	if line.startswith('```'):
> 		preformatted = not preformatted
> 		continue
> 
> 	if preformatted:
> 		print('\033[37m'+line+'\033[m', end='')
> 		continue
> 
> 	line = line.rstrip()
> 
> 	if line.startswith('=>'):
> 		parts = line[2:].strip().split(maxsplit=1)
> 		print('\033[36m'+parts[0]) # url
> 		print(parts[1]+'\033[m') # text
> 	elif line.startswith('#'):
> 		parts = line.split(maxsplit=1)
> 		depth = len(parts[0]) - 1
> 		colors = ['31', '93', '92', '34']
> 		color = colors[depth % len(colors)]
> 		print('\033['+color+'m'+line+'\033[m')
> 	elif line.startswith('*'):
> 		parts = line.split(maxsplit=1)
> 		depth = len(parts[0])
> 		text = textwrap.fill(parts[1], width)
> 		text = textwrap.indent(text, ' '*(2*depth)).lstrip()
> 		print(2*(depth-1)*' '+'\033[93m•\033[m '+text)
> 	elif line.startswith('+'):
> 		list_counter = list_counter if len(list_counter) > 0 else [0]
> 
> 		parts = line.split(maxsplit=1)
> 		depth = len(parts[0])
> 
> 		if depth > len(list_counter):
> 			list_counter += [0]
> 		elif depth < len(list_counter):
> 			list_counter = list_counter[:depth]
> 
> 		assert len(list_counter) == depth
> 
> 		marker = ''
> 
> 		counter_type = (len(list_counter) - 1) % 3
> 		list_counter[-1] += 1
> 		count = list_counter[-1]
> 		if counter_type == 0:
> 			marker = str(count)
> 		elif counter_type == 1:
> 			while True:
> 				count -= 1
> 				marker = chr(97+(count%26)) + marker
> 				count = count // 26
> 				if count == 0:
> 					break
> 		else:
> 			marker = int2roman(count)
> 
> 		text = textwrap.fill(parts[1], width)
> 		text = textwrap.indent(text, ' '*(3*depth)+' '*(len(marker)-1)).lstrip()
> 		print('\033[93m'+(depth-1)*3*' ' + marker + '. \033[m' + text)
> 	elif line.startswith('>'):
> 		depth = 0
> 		while True:
> 			if line.startswith('>'):
> 				line = line[1:].lstrip()
> 				depth += 1
> 			else:
> 				break
> 		text = textwrap.fill(line, width)
> 		text = textwrap.indent(text, '\033[93m>\033[m '*depth)
> 		print(text)
> 	elif line.startswith('---'):
> 		print('\033[37m'+'-'*width+'\033[m')
> 	else:
> 		print(textwrap.fill(line, width))
> 



More information about the Gemini mailing list