diff options
author | Jakob Kaivo <jkk@ung.org> | 2022-03-04 12:32:20 -0500 |
---|---|---|
committer | Jakob Kaivo <jkk@ung.org> | 2022-03-04 12:32:20 -0500 |
commit | 55f277e77428d7423ae906a8e1f1324d35b07a7d (patch) | |
tree | 5c1c04703dff89c46b349025d2d3ec88ea9b3819 /miralib/ex/just.m |
import Miranda 2.066 from upstream
Diffstat (limited to 'miralib/ex/just.m')
-rw-r--r-- | miralib/ex/just.m | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/miralib/ex/just.m b/miralib/ex/just.m new file mode 100644 index 0000000..9dca373 --- /dev/null +++ b/miralib/ex/just.m @@ -0,0 +1,98 @@ +||Text formatting program (DT) +||Reformats text to a specified width, with line-fill + +%export just + +||To try this out from within a Miranda session, say e.g. +|| just 60 (read "file") +||where "file" contains some text you want to reformat. + +||You could also make it into a UNIX filter -- see the example `mrev'. + +||----------------------------------------------------------------------|| +|| in this program we move between three different representations of || +|| text - as a flat list of characters, including spaces and newlines || +|| - as a list of lines (containing spaces but not newlines) || +|| - and as a list of list of words. || +||----------------------------------------------------------------------|| + +text == [char] +line == [char] +word == [char] + +just::num->text->text ||the main function +just n = concat.map(reformat n).paras.map words.lines + +||lines::text->[line] +||lines is defined in <stdenv> - it breaks a string into lines, +||removing the newline characters + +paras::[[word]]->[[word]] +||make each paragraph into one long line, by joining adjacent +||non-blank lines +paras (a:b:x) = paras ((a++b):x), if a~=[]~=b + = a:paras (b:x), otherwise +paras (a:[]) = a:[] +paras [] = [] + +reformat::num->[word]->text +||reformat a paragraph to width n +reformat n [] = "\n" ||the empty paragraph represents a blank line +reformat n x = lay(justify n (partition n x)) + +||lay::[line]->text +||lay is defined in <stdenv> - it is the inverse of lines + +justify::num->[[word]]->[line] +justify n para = map(fill_line n)(init para)++[unwords(last para)] + +partition::num->[word]->[[word]] +||break a paragraph into lines, with as many words as will fit in width +||n on each line (except the last) +partition n [] = [] +partition n x = x1 : partition n rest + where + (x1,rest) = grab [] x + grab y (w:x) = grab (w:y) x, if sum(map(#)y)+#y+#w <= n + = (reverse y,w:x), otherwise + grab y [] = (reverse y,[]) + +fill_line :: num->[word]->line +||make words into a line of length n exactly, by inserting enough spaces +fill_line n words + = (concat.concat) (transpose [words,mkspaces (w-1) (n-sw)]) + where + w = #words + sw = sum(map (#) words) + +mkspaces :: num->num->[[char]] +||return s spaces broken into n groups +mkspaces n s = map f [1..n], if n mod 2=0 ||see note + = map f [n,n-1..1], otherwise + where + f i = rep (s div n + delta) ' ' + where + delta = 1, if i<=s mod n + = 0, otherwise +||note: we put the extra spaces in sometimes from the left and sometimes +||from the right, depending on the parity of n. This is to avoid +||visually unbalancing the text by having all the extra spaces towards +||one margin. Using the parity of n to decide this is arbitrary. + +words :: line->[word] +||break a line into words +words = filter (~=[]) . foldr (breakon ' ') [[]] + +unwords :: [word]->line +||join words to make a line, inserting one space as separator +unwords = foldr1 (insert ' ') + +insert :: *->[*]->[*]->[*] +insert a x y = x ++ [a] ++ y + +breakon :: *->*->[[*]]->[[*]] +breakon c a x = []:x, if a=c + = (a:hd x):tl x, otherwise + +||These definitions of `words' and `unwords' are due to Richard Bird, see +||Bird and Wadler (1988), page 91. |