Wordwrap in Edita.
==================

Version 0.2.9 and later of Edita allow wordwrapping.

Wordwrap is manually applied to individual files. There is a Toolbar button and
a checkable Options/Wordwrap menu entry, plus the equivalent shortcut key Alt W.
The wordwrap state is automatically saved/restored when switching filetabs, and 
also saved on a file-by-file basis (asuming you have Options/Edit Restore files 
on open checked) for each file in edita.ini. See also point b below. 

The old edit options "show wordwrap line" and "word wrap column", (which were a 
bit rubbish really,) have been removed.

Suppose that line 8 is a long (word-wrapped) line displayed in 5 segments, ie 
occupying 5 lines on the screen. Call these "chunks" 8.1 to 8.5.

When line numbers are being displayed (Options/Display) an "8" is shown next to 
chunk 8.1 and a blank margin is shown next to chunks 8.2..8.5. Alternately, if
there is a bookmark on line 8, an asterisk("*") is displayed instead. If a fold
begins on line 8 then a ">" is displayed instead - but since the top line of a 
fold is not wordwrapped, for obvious reasons, a bit of a moot point anyway, and
the text beyond one screenwidth/in chunks 8.2..5 would not be visible.

When editing a wordwrapped line, Up and Down move to the previous and next chunk 
if any, or to previous and next line if already on the first/last chunk.
Likewise, Home and End jump to the start/end of the current chunk respectively. 
Ctrl Alt [Shift] Home and End jump to the very start and very end [with block 
selection] of the whole line (/paragraph).

The Edit/Clipboard menu has two new entries:

  Copy with CR	Line breaks are inserted into the selected text according to the
		wordwrap control table. Works with any selection, with WYSIWYG
		word wrapping. Trailing spaces are stripped.

  Copy w/o CR	Multiple selected lines are joined, with the addition of a single
		space if required, except for blank lines (left as a double CR).
		Note that a single wordwrapped line is just one long text string, 
		block selection and copying such with this function is exactly 
		the same as a normal copy operation.

These entries are disabled unless there is a current open file, displayed in wordwrap 
mode, with an active multi-line block selection. They can be used to manually toggle 
between hard and soft line breaks. For example (soft->hard) select a wordwrapped line 
via <Ctrl Alt Home>, <Ctrl Alt Shift End>, Copy with CR, Paste. To reverse, reselect 
the text just pasted via <Ctrl Z> <Ctrl Shift Y>, then Copy w/o CR, Paste. Assuming 
the initial selection is plain text, just like this paragraph, without any tabs or 
consecutive spaces, and is otherwise fully wordwrapped to begin with, then the only 
things which should change are the line numbers.

There are also two new entries not specifically related to wordwrap:
  Copy as html	Copies selected text with html tags for colouring etc. This may
		be useful for documentation/examples. (This option uses the same 
		code as File/Code Publisher but to clipboard not browser.)
		Can take a few moments to process massive files.
  Strip html	Copy selected text with any html tags removed to the clipboard.
		An alternative name for this function would be "Copy w/o Html".
		Can take a few more moments to process massive files.

Column selection is to all intents and purposes completely unaffected by wordwrap.
If you column select columns 1..3 of lines 7 through 9 then only columns 1..3 of
chunk 8.1 are selected, and nothing in chunks 8.2..5. This is intentional; were
it other, then the actual text selected would change as the screen is resized.
Menu options described above could/should be used instead. Also note that column 
selection does not "smooth scroll", ie Alt up from {9,3} jumps to {8,3} rather 
than somewhere in chunk 8.5 and similarly Alt up/down on the first/last line may 
jump the entire view by several lines.


A pop-up warning appears if the number of wordwrapped lines exceeds 2000.
This message exists solely to prevent large fonts/small window sizes from 
appearing to hang the program or otherwise cause slow response times.

Pressing OK continues to build the table and increases the test by 2000, Cancel 
aborts the build and turns off wordwrap. Bookmarks, folds, and wordwraps are 
implemented in a "packed table", which is intended to be considerably shorter 
than the file being edited. Hence insertions of the form:
    table=table[1..i]&newBit&table[i+1..length(table)], and deletions of the
    form table=table[1..i-1]&table[i+1..length(table)]
of necessity involve (at some low level) a copy of every element and become very
expensive on massive tables. For comparison, I estimate that a "soft-wrapped" 
text of War and Peace should need slightly less than 6000 wordwrap entries.

The wordwrap entries are also calculated in blocks of 2000 source file lines, 
and at the point of this message the status bar (if visible) usually shows the
approximate place in the file where the current limit was exceeded. In most 
cases, pressing Cancel, then reducing the font size (Ctrl R) or increasing the 
window size, and attempting wordwrap again (Alt W), will require fewer entries.

See also the "sparse table" suggestion below.


Technical details.
==================

When wordwrap mode is enabled then code folding of single lines is permitted. 
I assume this makes perfect sense on the above example, hiding chunks 8.2..5, 
however some explanation is probably warranted on shorter lines. Granted, it 
makes little sense to permit this on a blank, empty line; but consider the case 
of "the fat cat, or perhaps a bit longer than that". At first glance, this fits 
easily on a single line and there should be no need to allow a fold on it. 
However during resizing such a line can wordwrap as the window gets quite small. 
If there is a single line fold on that line, then it will not wrap no matter how 
small the window becomes. You might, for instance, want to enable word wrap for
the very obvious reason that you want to be able to see all the code, but after
reading a line and realising it is not pertinent to the problem at hand, put a
single line fold on it to stop it mangling the display. Alternatively, you might
want to use single line folds to reduce entire paragraphs to bullet points.

The only point at which folds on single lines theoretically cease to have any 
value is lines of less than two characters, and as there is no practical value 
in that, it is not coded. I would certainly never consider auto-deleting single 
line folds once the line fits on screen - I am quite sure such behaviour would 
be most annoying if, say, you accidentally maximised the window. Conversely, 
there is no reason to prohibit folds on lines that "fit", since there is no 
certainty that such folds will not be useful if the window is later resized.
There is no problem with any of this, I just want you to understand why I allow
folding of single short lines, as it can look a bit daft, if you don't know.

To implement "smooth scrolling", ie cursor up/down moves one line or chunk at
a time, and the top line of the screen does likewise, a new variable TopChunk
has been added which is a CursorX value rather than say 1..5 for the five
(original) chunks of line 8. Resizing the window may change the wordwrapping
and hence change the set of legal places which can be shown at screen(/client)
position (1,1). Suppose our line 8 is "aaa bbb ccc ddd eee" and we have resized
such that we get one word per line. The top line of the screen can then start
at any of {8,1}, {8,5}, {8,9}, {8,13}, {8,17} (or somewhere on another line).
If we ctrl up/down so that the display starts at {8,5}, ie TopChunk=5, and then 
resize so that we get two words per line, display can now only validly start at 
{8,1}, {8,9}, or {8,17}. To avoid problems, such as miscalculating the screen
co-ordinates of the cursor, TopChunk is reset to a (lower) legal value from the
new set of {1,9,17}. In this way, resizing a window may creep back the topline
to column 1 of a wordwrapped line. (Obviously were TopChunk an index say 1..5 
then such a resize would exhibit a similar problem looking for the 5th entry
of {1,9,17}.)

The current wordwrapping algorithm is extremely naieve, for example the code
    while not find(ch,{'\r','\n',-1}) do
is treated as four words, ie "find(ch,{'\r','\n',-1})" wraps as a single word,
since there is no space in it. Should anyone care to experiment, the decision 
about where to break such a line is entirely contained in the routine calcnWrap() 
in edita.exw, for example, if 'w' is in column 17 and it returns {29,45} then 
Edita will (erm, yes, _will_, by which I mean the result is an order to break 
the line, not a wimpy "break here or here if you want to" thing), now where was
I? Oh yea, will wrap the above line as:
 "    while not fin"	    -- ie chars [1..29]
 "d(ch,{'\r','\n',"	    -- ie chars [30..45]
 "-1}) do"		    -- ie chars [46..$]
(Note that calcnWrap() will not be called on the above line unless charsWide<56)
As noted in the comments of that routine, it should probably use charMap, which
will have been properly loaded from the appropriate .syn file beforehand.

A wordwrapped line is kept both on disk and in memory as a single line; the task 
of displaying it in several chunks and calculating where the chunks are is 
performed directly within the display routine (IdleHandler). While that always 
recalculates wrapping for each line it displays, a more permanent copy of this 
information is saved, eg for cursor up/down, alongside the bookmarks and folds.
Bit #01 indicates a bookmark, bit #02 a fold, and now bit #04 a wordwrapped line.
The [bfEnd] slot contains a list of "chunk starts", ie column numbers. Wordwrap 
is never applied to the first line of a fold, hence there is no clash over this 
re-use of [bfEnd]. An entry in the bookmarks table can therefore be:
  #01: bookmark-only	#02 fold-only
  #03: bookmark+fold			#04 wrap-only
  #05: bookmark+wrap
(use and_bits though) but not fold+wrap or bookmark+fold+wrap.

There are no plans to ever store wrap info in edita.edb, since all wrap info
becomes incorrect on a window resize anyway.

Significant revisions have been made to the display code (in IdleHandler())
as well as routines such as adjustLine() to utilise the new wrap information,
plus there have been numerous smaller changes throughout the code.
New constants, functions, and fields include: WRAP, distanceFromTopLine(), 
landingChunk, endChunk, isWordWrapON, TopChunk, nacX, conti, wrapIdx, nWrap,
charsLeft, csl, and wasnWrap. Also, getScreenLine() has been removed.


Futures / Troubleshooting notes.
================================

a) Intermittent 'invalid key' sound when pressing Alt-W. I get the same with
Alt-U/L (upper/lower). I have added some return {0} which sometimes help, but
I have no clue as to why this would be intermittent in the first place.
[I think this is fixed now]

b) As well as a flag in in [session cursel] in edita.ini for currently open 
files, it should also be possible to store wordwrap toggle(s) in edita.edb.
However I do not consider this a high priority.

c) I have so far made only one small change to mouse handling. So sue me, I 
just don't mouse much. I suspect that the right click/context help will get 
the wrong word on a wordwrapped line, and other (equally untested) bugs.

d) forceCursorOnScreen() has proved problematical and deserves your attention
in terms of a code review and walkthrough. Well actually, it has been hugely 
simplified recently but heck, I still challenge you to find a bug in those 39 
lines, and I know (or strongly suspect) that 1 or 2 still exist. ;-((
Update: I just found and fixed another bug, probably/hopefully that was it.

e) There seems to be a slight miscalculation in the vertical scrollbar. The
main symptom is that the thumb does not reach the bottom when paging all the
way down the file. There isn't any code at all that involves TopChunk with
the scrollbar, so obviously you'll get the same thumb position whether the
topline is at the start or end of a very long word-wrapped line. I guess I
just feel it is "close enough" but don't let me stop you wading in ;-)).

f) Another small one I noticed is that if you should key "file:" (etc) on
a new line, it is underlined as a hyperlink, but the underline runs on into
whitespace on the highlit line. This does not happen for non-current-lines.
Minor, trivial, but nevertheless wrong. [FIXED]

g) Re-implement bookmark/fold/wordwrap using a "sparse table", ie start with
each bookmarks[c] set to repeat(0,length(filetext[c])). For a file such as
the 1MB win32lib.ew this adds around 140K, which we can easily live with.
Alot of that dodgy "nextImportantLine" stuff can just go, as we just check
for atom(bookmarks[c][lineno]). For sanity, retain the [bfStart] value and
crash if any bookmarks[c][line][bfStart]!=line.
(This could avoid some significant overheads in rebuildWraps().)

