/* --- Copyright University of Sussex 2001. All rights reserved. ---------- > File: $poplocal/local/auto/ved_html.p > Purpose: Assist in editing HTML documents > Author: David S Young, Sep 2 1997 (see revisions) > Documentation: HELP * VED_HTML */ /* CONTENTS - (Use g to access required sections) 1 Global variables and constants 1.1 Tag end markers 1.2 User-accessible variables and constants 1.3 Private global variables 1.4 Macros 2 Tag properties 2.1 Working out permitted abbreviations for tags 2.2 Setting up the tag data structures 2.3 Tag utilities 2.4 Switching on and off P closers 3 Ved utilities 3.1 General utilities 3.2 Ved search utilities 4 Tag location 5 URL recognition 6 Text Scope 6.1 Initial scope definition procedures 6.2 Scope adjustment to allow for existing elements 7 Tag insertion 7.1 Tag highlighting 7.2 Main insertion procedure 7.3 Keyboard insertion 8 Tag deletion 9 Splicing deleted text 10 Preprocessing routines 10.1 Character attributes 10.2 Special characters such as < and & 10.3 Indexes 10.4 REF/HELP/TEACH file headers 10.5 Lists 10.6 Paragraph separation 10.7 Wrappers 10.8 Graphics characters and special spaces 10.9 Overall preprocessing 11 Entry points 11.1 Keyboard string interface 11.2 Command line interface 12 Customisation 13 Set up the tag list */ compile_mode:pop11 +strict; section; vedputmessage('Compiling ved_html - please wait'); /* ----------------------------------------------------------------------- 1 Global variables and constants ----------------------------------------------------------------------- 1.1 Tag end markers -------------------- */ /* Make the tag end markers active so that a list of them can be maintained, and right markers that are the same are also identical */ lvars htmlopenleft, htmlopenright, htmlcloseleft, htmlcloseright, htmlmarklist; define lconstant setupmarklist; [% htmlopenleft, htmlopenright, htmlcloseleft, if htmlcloseright = htmlopenright then htmlopenright -> htmlcloseright ;;; ensure identical else htmlcloseright ;;; different so in list endif %] -> htmlmarklist enddefine; define active vedhtmlopenleft; htmlopenleft enddefine; define active vedhtmlopenright; htmlopenright enddefine; define active vedhtmlcloseleft; htmlcloseleft enddefine; define active vedhtmlcloseright; htmlcloseright enddefine; define updaterof active vedhtmlopenleft with_nargs 1; -> htmlopenleft; setupmarklist() enddefine; define updaterof active vedhtmlopenright with_nargs 1; -> htmlopenright; setupmarklist() enddefine; define updaterof active vedhtmlcloseleft with_nargs 1; -> htmlcloseleft; setupmarklist() enddefine; define updaterof active vedhtmlcloseright with_nargs 1; -> htmlcloseright; setupmarklist() enddefine; '<' -> vedhtmlopenleft; '>' -> vedhtmlopenright; ' vedhtmlcloseleft; '>' -> vedhtmlcloseright; ;;; vedhtmlpunctchars is a string of characters to be treated as ;;; punctuation when inside ved_html - see vedhtmlchartype. Needs ;;; to be updated manually if the markers are changed. vars vedhtmlpunctchars = '<>'; /* 1.2 User-accessible variables and constants -------------------------------------------- */ global vars vedhtmltagchars = '.-!', vedhtmltagtermin = [`.` `\s` `\r`], vedhtmlscopeattr = `\[7]`, vedhtmltagattr =`\[2b]`, vedhtmluppercase = true, vedhtmlcursormark = '^', vedhtmlurlmark = '*', vedhtmlurlstarts = ['http:' 'https:' 'file:' 'ftp:' 'mailto:' 'news:' 'nntp:' 'wais:' 'gopher:' 'telnet:' 'cid:' 'mid:' 'afs:' 'prospero:' 'x-exec:'], vedhtmlurlchars = '/.:@?#&', vedhtmlglobalreplace = ['/&/&' '/>/>' '/ disamb */; ;;; Takes a list of words or strings. ;;; Returns a procedure which takes as its argument a character ;;; repeater. The procedure reads characters until it has enough ;;; to unambiguously identify one of the words or strings in the list, ;;; then returns that word or string. If the first character read is not ;;; the first character of a word in the list, returns false. If a word ;;; is an initial subword of another word, then any character in ;;; termchars can be used to terminate it. define lconstant disambtable(wordlist, i) -> prop; lvars wordlist, i, prop = newanyproperty([], 4, 1, 4, false, false, "perm", false, false); ;;; To be called with a list of words that are identical for all ;;; characters before position i. Returns a property tree. ;;; Collect together words with same character at position i. lvars w, c, wds, lw; for w in wordlist do if i > (length(w) ->> lw) then if i > lw + 1 then mishap(w, 1, 'Same word occurs twice') else termin -> c endif else w(i) -> c endif; if prop(c) ->> wds then if wds.ispair then conspair(w, wds) -> prop(c) else conspair(w, conspair(wds, [])) -> prop(c) endif else w -> prop(c) endif endfor; ;;; Make characters from termchars work as terminators. if prop(termin) ->> w then for c in termchars do if prop(c) then mishap(c, 1, 'Terminator occurs inside tag name') else w -> prop(c) endif endfor endif; ;;; Build new tree for words identical before i+1 for c, wds in_property prop do if wds.ispair then disambtable(wds, i+1) -> prop(c) endif endfor enddefine; define lconstant procedure disamb(rep, p) -> result; lvars procedure rep, p, result = p(rep()); while result.isproperty do result(rep()) -> result endwhile enddefine; disamb(% disambtable(words, 1) %) enddefine; define lconstant disambig_abbrevs(words) /* -> abbrs */; ;;; Returns a list of the abbreviations disambig will use, as strings. ;;; Relies on fact that disambiguating procedure does not use ;;; stack to remember anything between calls. lconstant termins = [^(consref(termin))]; lvars w, d = disambig(words, termins); [% for w in words do consstring( #| d(stringin(w) <> dup) -> ; if dup() == termin then erase(); `.` endif |#) endfor %] enddefine; /* 2.2 Setting up the tag data structures --------------------------------------- */ lconstant ;;; tag properties (set up later) tagprop = newanyproperty([], 4, 1, 4, false, false, "perm", false, false); defclass lconstant tagrec {tag_name, tag_quals, tag_attribs, tag_cursor, tag_url}; lvars procedure tagabbrevs, ;;; abbreviation recogniser taglist, ;;; raw form of tag list - see format below tagnamelist; ;;; full names of tags define lconstant removesubstr(str, substr) -> (str, pos); ;;; If substr is a substring of str, returns a string ;;; with substr deleted and the number of characters between ;;; the position of substr and the end of str. str and issubstring(substr, str) -> pos; if pos then lvars i, ls = length(str), lm = length(substr); for i from 1 to pos-1 do str(i) endfor; for i from pos+lm to ls do str(i) endfor; consstring(ls - lm) -> str; ls - pos - lm + 1 -> pos; ;;; from end endif enddefine; define lconstant attribmarks(str) -> (str, curpos, urlpos); ;;; Returns a new string with vedhtmlcursormark and ;;; vedhtmlurlmark removed, and the number of characters ;;; the marks and the end of the output str. removesubstr(str, vedhtmlcursormark) -> (str, curpos); removesubstr(str, vedhtmlurlmark) -> (str, urlpos); if curpos and urlpos and urlpos < curpos then curpos - length(vedhtmlurlmark) -> curpos endif enddefine; define lconstant fixtagvec(v) -> (name, tagrec); ;;; Convert vector tag representation as in list below to record, ;;; separating out cursor and url position in attributes. unless length(v) == 3 then mishap(v, 1, 'Wrong length for tag property vector') endunless; lvars (name, quals, attrib) = explode(v); unless name.isword then mishap(name, 1, 'Expecting word in tag description') endunless; unless quals.islist then mishap(quals, 1, 'Expecting list in tag description') endunless; not(attrib == "N") and attrib -> attrib; unless attrib.isstring or not(attrib) then mishap(attrib, 1, 'Expecting string or N in tag description') endunless; constagrec(name, quals, attribmarks(attrib)) -> tagrec enddefine; define active vedhtmltags; ;;; Active variable returning taglist, and on updating ;;; updating tagnamelist, tagprop and tagabbrevs. taglist enddefine; define updaterof active vedhtmltags(l); l -> taglist; clearproperty(tagprop); [] -> tagnamelist; lvars v, w; for v in l do fixtagvec(v) -> (w, v); w :: tagnamelist -> tagnamelist; v -> tagprop(w) endfor; disambig(tagnamelist, vedhtmltagtermin) -> tagabbrevs enddefine; define lconstant vedhtmlreadtags(file); ;;; Read in a set of tags from a file. unless trycompile(file) then vederror('Tag file ' sys_>< file sys_>< ' not found') endunless -> vedhtmltags; enddefine; define lconstant vedhtmlprinttagnames; ;;; Prints the tag names and their abbreviations dlocal pop_charout_device = consveddevice('ved_html\'s tag abbreviations', 1, true); lvars a, w; lconstant printvec = initv(2); format_print('~%Abbreviation~15TTag name~2%', []); for a, w in ncrev(disambig_abbrevs(tagnamelist)), rev(tagnamelist) do a, w -> explode(printvec); format_print('~15A~A~%', printvec) endfor enddefine; define lconstant vedhtmlprinttags; ;;; Pretty prints the tag list in a working buffer. dlocal pop_charout_device = consveddevice('ved_html\'s tag list', 1, true), pop_=>_flag = nullstring, pop_pr_quotes = true; pretty(taglist) enddefine; /* 2.3 Tag utilities ------------------ */ ;;; Next three routines just test for attributes define lconstant htmlseparates(tagrec) /* -> bool */; if tagrec.isword then tagprop(tagrec) -> tagrec endif; lmember("separates", tagrec.tag_quals) enddefine; define lconstant htmlhascloser(tagrec) /* -> bool */; if tagrec.isword then tagprop(tagrec) -> tagrec endif; lmember("hascloser", tagrec.tag_quals) enddefine; define lconstant htmlkeepspaces(tagrec) /* -> bool */; if tagrec.isword then tagprop(tagrec) -> tagrec endif; lmember("keepspaces", tagrec.tag_quals) enddefine; define lconstant vedhtmltagname /* -> tag */; ;;; Return the word from the cursor position to the next character ;;; that is illegal in a tag name (not a letter, digit, full stop ;;; or hyphen). Skips to first character of name if necessary. ;;; Used to get tag name if cursor just after tag left opener. ;;; Moves cursor to end of name. lvars c; until isalphacode(vedcurrentchar() ->> c) or locchar(c, 1, vedhtmltagchars) do vedcharnext() enduntil; consword( #| while isalphacode(vedcurrentchar() ->> c) or isnumbercode(c) or locchar(c, 1, vedhtmltagchars) do vedhtmluppercase and lowertoupper(c) or c; vedcharright() endwhile |# ) enddefine; /* 2.4 Switching on and off P closers ----------------------------------- */ ;;; A nasty hack really - but this is something users might well want to ;;; do, and it's not worth rebuilding the whole property and abbreviation ;;; structures to do it. define lconstant taglistPentry -> Pentry; define lconstant istaglistPentry(v) /* -> bool */; v(1) == "P" enddefine; unless taglist = [=** =?Pentry:istaglistPentry =**] then vederror('No entry for P in tag list') endunless; enddefine; define lconstant vedhtmlPclosersoff; lvars Pentry = taglistPentry(); delete("hascloser", Pentry(2)) -> Pentry(2); delete("hascloser", "P".tagprop.tag_quals) -> "P".tagprop.tag_quals enddefine; define lconstant vedhtmlPcloserson; lvars Pentry = taglistPentry(), l; unless lmember("hascloser", Pentry(2) ->> l) then "hascloser" :: l -> Pentry(2) endunless; unless lmember("hascloser", "P".tagprop.tag_quals ->> l) then "hascloser" :: l -> "P".tagprop.tag_quals endunless enddefine; lvars htmlPclosers = undef; ;;; so overriden by file read in define active vedhtmlPclosers /* -> bool */; lvars trec = tagprop("P"); if trec then htmlhascloser(trec) ->> htmlPclosers else htmlPclosers endif enddefine; define updaterof active vedhtmlPclosers(Pclosers); if tagprop("P") then if Pclosers then vedhtmlPcloserson() else vedhtmlPclosersoff() endif endif; Pclosers -> htmlPclosers enddefine; define vedhtmlswitchPclosers(arg); lconstant son = 'on', soff = 'off'; if arg /== [] then hd(arg) -> arg; if arg = son or arg = soff then arg = son -> vedhtmlPclosers endif endif; vedputmessage('Closers for P tag are ' sys_>< if vedhtmlPclosers then son else soff endif) enddefine; /* ----------------------------------------------------------------------- 3 Ved utilities ----------------------------------------------------------------------- 3.1 General utilities ---------------------- Some Ved utilities are here, but some (e.g. vedsplice) occur later either because they are only used locally or because they need tag location routines. */ define lconstant vedbefore(l1, c1, l2, c2) /* -> bool */; ;;; True if first coordinates earlier than second l1 < l2 or (l1 == l2 and c1 < c2) enddefine; define lconstant vedinside(l0, c0, l1, c1, l2, c2) /* -> bool */; ;;; True if first coords after second coords and before ;;; third coords. vedbefore(l1, c1, l0, c0) and vedbefore(l0, c0, l2, c2) enddefine; include ast ;;; for TIMER_CANCEL define lconstant vedinascii_withprompt(prompt) /* -> char */; ;;; Calls vedinascii and puts the prompt on the status line if a ;;; character has not been typed after a second. ;;; Used later by key insert routine. define lconstant putmessage; vedputmessage(prompt) enddefine; lconstant wait = 1e6; ;;; microseconds to wait lvars msg = vedmessage; wait -> sys_timer(putmessage); vedinascii() /* -> char */; unless sys_timer(putmessage, TIMER_CANCEL) then vedputmessage(msg) endunless; enddefine; define lconstant vedleftcol /* -> col */; ;;; Returns the column of the leftmost text in the current line. dlocal vedcolumn; vedtextleft(); vedcolumn enddefine; define lconstant vedadjustpos(l, c, l0, c0, text) -> (l, c); ;;; Adjusts the position of l and c to allow for the insertion ;;; of the text at l0, c0 unless vedbefore(l, c, l0, c0) then ;;; adjust cursor position if l == l0 then c + length(last(text)) -> c; unless tl(text) == [] then c - c0 + 1 -> c endunless endif; l + length(text) - 1 -> l; endunless; enddefine; define lconstant veddeletewholespan(l0, c0, l1, c1) /* -> text */; ;;; Deletes from l0,c0 to l1,c1 and adjusts cursor position ;;; to stay on same character (or move to just after deleted ;;; portion if it was in it). (Note: ved_cut does not seem ;;; to be consistent, so not used - specifically, if cut ;;; section is on more than one line, it replaces it with a ;;; line break.) ;;; Returns the text deleted as a list of lines. There is taken ;;; to be a line break between every pair of lines in this list, ;;; but not at either end of it. dlocal vveddump = [], vvedworddump = nullstring, 0 % vedmarkpush(), vedmarkpop() %; lvars lg = vedline, cg = vedcolumn, sstring = false, estring = nullstring; ;;; First find final cursor position if vedbefore(l1, c1, lg, cg) then if l1 == lg then cg + c0 - c1 -> cg endif; lg + l0 - l1 -> lg; elseif vedbefore(l0, c0, lg, cg) then (l0, c0) -> (lg, cg) endif; if l1 > l0+1 then ;;; delete intermediate lines in a chunk false -> vvedmarkprops; vedjumpto(l0+1, 1); vedmarklo(); vedjumpto(l1-1, 1); vedmarkhi(); ved_d(); l0 + 1 -> l1; endif; if l0 == l1 then vedjumpto(l0, 1); if c1 > c0 then vedspandelete(c0, c1, true) -> estring endif else ;;; successive lines vedjumpto(l0, c0); vedcleartail(); vvedworddump -> sstring; vedjumpto(l1, c1); unless c1 == 1 then vedclearhead(); vvedworddump -> estring endunless; vedchardelete(); ;;; delete line break ;;; may have to insert or remove spaces until vedcolumn <= c0 do vedchardelete() ;;; as vedchardelete may insert a space enduntil; until vedcolumn >= c0 do vedcharinsert(`\s`) ;;; in case start was past end of line enduntil endif; vedjumpto(lg, cg); [% if sstring then sstring endif, explode(vveddump), estring %] /* -> text */ enddefine; define vars vedhtmlchartype(char) /* -> type */; ;;; Needed so that eg >< is split into two. checkinteger(char, 0, false); char fi_&& 16:FF -> char; ;;; get rid of attributes if locchar(char, 1, vedhtmlpunctchars) then `.` else vedchartype_orig(char) endif enddefine; define lconstant vedatstring(str) /* -> bool */; issubstring_lim(str, vedcolumn, vedcolumn, false, vedthisline()) enddefine; define lconstant vedhtmlatsep /* -> bool */; ;;; Is current line an HTML separator tag? ;;; Does not use vedhtmlnexttag in order to avoid errors if ;;; vedhtmlinparagraph is used outside html context. VED_DLOCAL_POS lvars t; vedtextleft(); vedatstring(htmlopenleft) and not(vedatstring(htmlcloseleft)) and tagprop(vedhtmltagname() ->> t) and htmlseparates(t) enddefine; define vars vedhtmlinparagraph /* -> bool */; ;;; Everything except a blank line is part of a paragraph. ;;; A line with . in the first column or with an HTML separator ;;; as the first text is a paragraph start. if vvedlinesize == 0 then false elseif vedlinestart('.') then 1 ;;; roff-type macro else if vedhtmlatsep() then 1 else true endif endif enddefine; define lconstant vedwriteplain(filename); ;;; Write the current file without special Ved characters dlocal vedwriteoutplain = 1, vedargument = filename or nullstring; if filename then ved_w() else ved_w1() endif enddefine; /* 3.2 Ved search utilities ------------------------- Best to have own search routines - can search for one of a set of strings efficiently and can control scope more exactly. The routines search for one of the strings in the list (if only one string is to be used, it need not be in a list) and return the first one found searching in the current direction up to the limit given. In these two routines, cursor position is considered to be between characters, just to the left of the position of the highlighted character. String start and end points are the cursor positions surrounding the string - in terms of character positions, start point has coords of first character of string, but end point has coords of next character after end of string. The routines assume that no search string contains a newline. If the cursor is inside a search string, this string is returned; otherwise search proceeds forwards or backwards from the cursor position up to the limit given. If two strings match at a particular position, the longest is returned. The algorithm may carry out repeated searches for a given string on one line. This is certainly simpler and probably more efficient than keeping a record of all search results. */ define lconstant vednextof(list, l1, c1) -> (s, l0s, c0s, l0s, c1s); ;;; Cursor left at end of string. ;;; Strings must start before l1, c1; l1 or c1 false means end of file. lvars s = false, l0s = false, c0s = false, c1s = false; lconstant singlist = [^false]; if list.isstring then list -> front(singlist); singlist -> list endif; unless l1 and c1 then (vvedbuffersize + 1, 1) -> (l1, c1) endunless; vedtrimline(); lvars l0 = vedline, s0 = vvedlinesize, startlim, line, ss, m, c; for vedline from l0 to l1 do vedsetlinesize(); (vedline == l1) and c1-1 -> startlim; unless startlim and startlim <= 0 then vedthisline() -> line; for ss in list do (vedline == l0 and max(1, vedcolumn-length(ss)+1)) or 1 -> c; if (issubstring_lim(ss, c, startlim, false, line) ->> m) and (not(s) or m < c0s or (m == c0s and length(ss) > length(s)) ) then m -> c0s; ss -> s endif endfor; quitif(s) endunless endfor; if s then vedline -> l0s; c0s + length(s) -> c1s; c1s -> vedcolumn else l0 -> vedline; s0 -> vvedlinesize endif enddefine; define lconstant vedlastof(list, l0, c0) -> (s, l0s, c0s, l0s, c1s); ;;; Cursor left at start of string. ;;; Strings must end after l0, c0; l0 or c0 false means start of file. lvars s = false, l0s = false, c0s = false, c1s = false; lconstant singlist = [^false]; if list.isstring then list -> front(singlist); singlist -> list endif; unless l0 and c0 then (1, 1) -> (l0, c0) endunless; vedtrimline(); lvars l1 = vedline, c = vedcolumn - 1, s0 = vvedlinesize, line, ss, m, m0, strt; for vedline from l1 by -1 to l0 do vedsetlinesize(); c or vvedlinesize -> c; unless c <= 0 then vedthisline() -> line; for ss in list do (vedline == l0 and max(1, c0-length(ss)+1)) or 1 -> strt; false -> m; while issubstring_lim(ss, strt, c, false, line) ->> m0 do m0 -> m; m0 + 1 -> strt endwhile; if m and (not(s) or m > c0s or (m == c0s and length(ss) > length(s)) ) then m -> c0s; ss -> s endif endfor; quitif(s) endunless; false -> c endfor; if s then vedline -> l0s; c0s + length(s) -> c1s; c0s -> vedcolumn else l1 -> vedline; s0 -> vvedlinesize endif enddefine; /* ----------------------------------------------------------------------- 4 Tag location ----------------------------------------------------------------------- */ define lconstant vedhtmlnexttag(ls, cs) -> (tag, opener, l0, c0, l1, c1); ;;; Searches forward for a tag ending after the cursor position ;;; and starting before ls, cs. Leaves the cursor at the end of the tag. ;;; If inside a tag starting before the current line and ending ;;; outside the search range and after the current line, will not see it. ;;; tag is returned as false if no tag found - other results ;;; undefined in this case. lvars tag = false, opener = false; unless ls and cs then (vvedbuffersize + 1, 1) -> (ls, cs) endunless; lvars s0, s1, c01, lg = vedline, cg = vedcolumn; vednextof(htmlmarklist, ls+1, 1) -> (s0, l0, c0, l1, c1); unless s0 then ;;; May be inside a tag - check back to start of line vedlastof([% htmlopenleft, htmlcloseleft %], vedline, 1) -> (s0, l0, c0, l1, c1); if s0 then c1 -> vedcolumn endif endunless; if s0 then if s0 == vedhtmlopenright or s0 == vedhtmlcloseright then s0 -> s1; c0 -> vedcolumn; vedlastof(htmlmarklist, false, false) -> (s0, l0, c0, , c01) else c1 -> c01; vednextof(htmlmarklist, false, false) -> (s1, , , l1, c1) endif; unless ((s0 == htmlopenleft ->> opener) and s1 = htmlopenright) or (s0 == htmlcloseleft and s1 = htmlcloseright) then vederror('Incomplete tag or extra tag marker') endunless; vedjumpto(l0, c01); vedhtmltagname() -> tag; unless tagprop(tag) then vederror('Unrecognised tag ' sys_>< tag) endunless; ;;; As search was extended to check for case of being inside ;;; a long tag, must now check that this tag really is in ;;; the proper search area. if vedbefore(lg, cg, l1, c1) and vedbefore(l0, c0, ls, cs) then vedjumpto(l1, c1) else ;;; tag outside range vedjumpto(lg, cg); false -> tag endif endif enddefine; define lconstant vedhtmlprevtag(ls, cs) -> (tag, opener, l0, c0, l1, c1); ;;; Searches backwards for a tag starting before the cursor position ;;; and ending after ls, cs. Leaves cursor at the start of tag. ;;; Same restriction on tag size if inside a tag as vedhtmlnexttag. lvars tag = false, opener = false; unless ls and cs then (1, 1) -> (ls, cs) endunless; lvars s0, s1, c01, lg = vedline, cg = vedcolumn; vedlastof(htmlmarklist, ls, 1) -> (s0, l0, c0, l1, c1); unless s0 then ;;; check to end of line vednextof([% vedhtmlopenright, vedhtmlcloseright %], vedline+1, 1) -> (s0, l0, c0, l1, c1); if s0 then c0 -> vedcolumn endif endunless; if s0 then if s0 == vedhtmlopenright or s0 == vedhtmlcloseright then s0 -> s1; vedlastof(htmlmarklist, false, false) -> (s0, l0, c0, , c01) else c1 ->> c01 -> vedcolumn; vednextof(htmlmarklist, false, false) -> (s1, , , l1, c1) endif; unless ((s0 == htmlopenleft ->> opener) and s1 = htmlopenright) or (s0 == htmlcloseleft and s1 = htmlcloseright) then vederror('Incomplete tag or extra tag marker') endunless; vedjumpto(l0, c01); vedhtmltagname() -> tag; unless tagprop(tag) then vederror('Unrecognised tag ' sys_>< tag) endunless; if vedbefore(ls, cs, l1, c1) and vedbefore(l0, c0, lg, cg) then vedjumpto(l0, c0) else ;;; tag outside range vedjumpto(lg, cg); false -> tag endif endif enddefine; define lconstant vedhtmlopener(tag) -> (t, l0, c0, l1, c1); ;;; Finds the opening tag for the element containing the cursor. ;;; Element contains cursor if cursor is after first character of ;;; opener and on or before last character of closer. ;;; If tag is non-false, ensures the tag found matches. ;;; Tags without closers ignored unless called with cursor inside ;;; when they form an element by themselves. ;;; Leaves cursor at l0, c0. lvars lg = vedline, cg = vedcolumn; define lconstant prevopener(tag); lvars opener, atcursor; repeat vedhtmlprevtag(false, false) -> (t, opener, l0, c0, l1, c1); quitunless (t); lg and vedinside(lg, cg, l0, c0, l1, c1) -> atcursor; false -> lg; ;;; only check first tag found if opener then quitif (atcursor or htmlhascloser(t)) elseunless atcursor then prevopener(t) ;;; skip element endif endrepeat; if tag and not(t) then vederror('Found start of file, wanted opener for ' sys_>< tag) endif; if tag and t /== tag then vederror('Found opener for ' sys_>< t sys_>< ', wanted ' sys_>< tag) endif enddefine; prevopener(tag) enddefine; define lconstant vedhtmlcloser(tag) -> (t, l0, c0, l1, c1); ;;; Finds the closing tag for the element containing the cursor. lvars lg = vedline, cg = vedcolumn; define lconstant nextcloser(tag); lvars opener, atcursor, hascloser; repeat vedhtmlnexttag(false, false) -> (t, opener, l0, c0, l1, c1); quitunless (t); lg and vedinside(lg, cg, l0, c0, l1, c1) -> atcursor; false -> lg; ;;; only check first tag found if opener then htmlhascloser(t) -> hascloser; if atcursor and not(hascloser) then quitloop ;;; special case - inside empty tag elseif not(atcursor) and hascloser then nextcloser(t) ;;; skip element ;;; elseif atcursor and hascloser or ;;; not(atcursor) and not(hascloser) then skip tag endif else ;;; at closer quitloop endif endrepeat; if tag and not(t) then vederror('Found end of file, wanted closer for ' sys_>< tag) endif; if tag and t /== tag then vederror('Found closer for ' sys_>< t sys_>< ', wanted ' sys_>< tag) endif enddefine; nextcloser(tag) enddefine; define lconstant vedhtmltagparts(withcloser) -> (tag, lt0, ct0, lt1, ct1, lc0, cc0, lc1, cc1); ;;; Returns locations of opener and closer of the element containing ;;; the cursor. If withcloser is then the element can be an ;;; empty tag (one without a closer); otherwise a surrounding tag with ;;; content will be found. For a tag without a closer, the two sets of ;;; results are the same. Returns for tag if not in a tag. ;;; Moves cursor to end of closer. repeat vedhtmlopener(false) -> (tag, lt0, ct0, lt1, ct1); quitunless(tag and withcloser and not(htmlhascloser(tag))) endrepeat; if tag then vedcharright(); ;;; move into opener vedhtmlcloser(tag) -> (tag, lc0, cc0, lc1, cc1); endif; enddefine; /* ----------------------------------------------------------------------- 5 URL recognition ----------------------------------------------------------------------- */ define lconstant vedhtmlinurl -> (l0, c0, l1, c1); ;;; Returns the coordinates of a URL surrounding the cursor. ;;; A URL is any string starting with something in urlstarts ;;; and terminated by a nonalphanumeric character not in urlchars. VED_DLOCAL_POS lvars cg = vedcolumn; vedcharright(); ;;; in case on first character vedlastof(vedhtmlurlstarts, vedline, 1) -> ( , l0, c0, l1, c1); if l0 then c1 -> vedcolumn; lvars c; while (vedcurrentchar() ->> c).isalphacode or c.isnumbercode or locchar(c, 1, vedhtmlurlchars) do vedcharright(); endwhile; if vedcolumn > cg then vedcolumn -> c1 else false ->> l0 ->> c0 ->> l1 -> c1 endif endif enddefine; /* ----------------------------------------------------------------------- 6 Text Scope ----------------------------------------------------------------------- 6.1 Initial scope definition procedures ---------------------------------------- */ lblock; ;;; Scopes for different kind of structures. l0, c0 are coords of first ;;; character in scope; l1, c0 are for first character after scope. ;;; In an lblock so that they can only be accessed through ;;; vedhtmlscopeprocs. define lconstant cursorscope /* -> (l0, c0, l1, c1) */; vedline, vedcolumn, vedline, vedcolumn enddefine; define lconstant charscope /* -> (l0, c0, l1, c1) */; vedline, vedcolumn, vedline, vedcolumn + 1 enddefine; define lconstant wordscope /* -> (l0, c0, l1, c1) */; VED_DLOCAL_POS lvars lg = vedline, cg = vedcolumn; vedcharright(); vedstartwordleft(); vedline, vedcolumn; /* -> l0, c0 */; vedendwordright(); vedline, vedcolumn; /* -> l1, c1 */ unless vedbefore(lg, cg, vedline, vedcolumn) then vederror('Cursor not in word') endunless enddefine; define lconstant urlscope -> (l0, c0, l1, c1); vedhtmlinurl() -> (l0, c0, l1, c1); unless l0 then vederror('Not in a URL') endunless; enddefine; define lconstant linescope /* -> (l0, c0, l1, c1) */; VED_DLOCAL_POS vedtextleft(); vedline, vedcolumn; /* -> l0, c0 */; vedtextright(); vedline, vedcolumn; /* -> l1, c1 */ enddefine; define lconstant sentscope /* -> (l0, c0, l1, c1) */; VED_DLOCAL_POS lvars lg = vedline, cg = vedcolumn; vedcharright(); vedprevsent(); vedline, vedcolumn; /* -> l0, c0 */; vednextsentend(); vedline, vedcolumn + 1; /* -> l1, c1 */ if vedbefore(vedline, vedcolumn, lg, cg) then vederror('Cursor not in sentence') endif enddefine; define lconstant parascope /* -> (l0, c0, l1, c1) */; VED_DLOCAL_POS lvars lg = vedline, cg = vedcolumn; vedcharright(); vedprevpara(); vedline, vedcolumn; /* -> l0, c0 */; vednextparaend(); vedline, vedcolumn; /* -> l1, c1 */ unless vedbefore(lg, cg, vedline+1, 1) then vederror('Cursor not in paragraph') endunless enddefine; define lconstant rangescope /* -> (l0, c0, l1, c1) */; VED_DLOCAL_POS vedmarkfind(); vedline, vedcolumn; /* -> l0, c0 */; vedendrange(); vedtextright(); vedline, vedcolumn; /* -> l1, c1 */ enddefine; define lconstant filescope /* -> (l0, c0, l1, c1) */; 1, 1, vvedbuffersize + 1, 1 enddefine; define lconstant procscope -> (l0, c0, l1, c1); VED_DLOCAL_POS dlocal 0 % vedmarkpush(), vedmarkpop() %; lvars lg = vedline, cg = vedcolumn; ved_mcp(); rangescope() -> (l0, c0, l1, c1); unless vedbefore(lg, cg, l1, c1) then vederror('Cursor not in procedure') endunless enddefine; define lconstant elemscope -> (l0, c0, l1, c1); VED_DLOCAL_POS lvars tag; vedhtmltagparts(false) -> (tag, l0, c0, , , , , l1, c1); unless tag then vederror('Not in an HTML element') endunless; enddefine; define lconstant inelemscope -> (l0, c0, l1, c1); VED_DLOCAL_POS lvars tag; vedhtmltagparts(true) -> (tag, , , l0, c0, l1, c1, , ); unless tag then vederror('Not in an HTML element') endunless; enddefine; define lconstant xselscope /* -> (l0, c0, l1, c1) */; ;;; This uses an undocumented system variable, so maybe ;;; not reliable. It is about time this sort of thing was available ;;; properly documented. lconstant noseln = {1 1 1 1}; ;;; who decided on this?? if vedusewindows == "x" then lvars v = valof("vedselectioncoords"); if v = noseln then vederror('No X selection made') else explode(v) endif else vederror('X selection only available in xved') endif enddefine; define lconstant stackscope -> (l0, c0, l1, c1); ;;; Top two elements of position stack used. Stack is popped ;;; (can dlocal vedpositionstack to preserve it, but it gets ;;; meaningless anyway after the insertions). VED_DLOCAL_POS vedpositionpop(); (vedline, vedcolumn) -> (l0, c0); vedpositionpop(); (vedline, vedcolumn) -> (l1, c1); if vedbefore(l1, c1, l0, c0) then (l1, c1, l0, c0) -> (l0, c0, l1, c1) endif enddefine; [ [. ^cursorscope] [c ^charscope] [w ^wordscope] [u ^urlscope] [l ^linescope] [s ^sentscope] [p ^parascope] [o ^procscope] [r ^rangescope] [x ^xselscope] [k ^stackscope] [i ^inelemscope] [e ^elemscope] [f ^filescope] ]; ;;; on stack to escape from lblock endlblock; ;;; vedhtmlscopeprocs is central property - maps scope characters to ;;; scope procedure defined above. vedhtmlscopechars is for information. lconstant vedhtmlscopelist = identfn(), ;;; get list from lblock vedhtmlscopeprocs = newproperty(vedhtmlscopelist, 20, false, "perm"), vedhtmlscopechars = consstring(#| applist(vedhtmlscopelist, hd <> explode) |#); /* 6.2 Scope adjustment to allow for existing elements ---------------------------------------------------- */ define lconstant vedhtmladjustscope(l0, c0, l1, c1) -> (l0, c0, l1, c1); ;;; Adjust the scope to avoid overlaps between elements. ;;; As much of the current scope as possible is included, working ;;; from the cursor position. VED_DLOCAL_POS if vedbefore(vedline, vedcolumn, l0, c0) then vedjumpto(l0, c0) elseif vedbefore(l1, c1, vedline, vedcolumn) then vedjumpto(l1, c1) endif; lvars lg = vedline, cg = vedcolumn, tag, opener, l0i, c0i, l1i, c1i, l0e, c0e, l1e, c1e; ;;; Go left from cursor, finding restrictions on scope repeat vedhtmlprevtag(l0, c0) -> (tag, opener, l0e, c0e, l0i, c0i); quitunless(tag); if opener and htmlhascloser(tag) then vedcharright(); vedhtmlcloser(tag) -> ( , l1i, c1i, l1e, c1e); if vedbefore(l0e, c0e, l0, c0) or vedbefore(l1, c1, l1e, c1e) then (l0i, c0i) -> (l0, c0); ;;; stay inside element if vedbefore(l1i, c1i, l1, c1) then (l1i, c1i) -> (l1, c1) ;;; stay inside endif; quitloop elseif vedbefore(lg, cg, l1e, c1e) then (l1e, c1e) -> (lg, cg) ;;; stay outside endif; vedjumpto(l0e, c0e); else vedcharright(); ;;; in case of empty tag vedhtmlopener(tag) -> ( , l1i, c1i, l1e, c1e); if vedbefore(lg, cg, l0i, c0i) then (l0i, c0i) -> (lg, cg) ;;; stay to right of element endif; if vedbefore(l1i, c1i, l0, c0) then (l0i, c0i) -> (l0, c0); ;;; stay to right quitloop endif endif endrepeat; ;;; Go right from cursor vedjumpto(lg, cg); repeat vedhtmlnexttag(l1, c1) -> (tag, opener, l0e, c0e, l0i, c0i); quitunless(tag); if opener then vedcharleft(); ;;; in case of empty tag vedhtmlcloser(tag) -> ( , , , l1e, c1e); if vedbefore(l1, c1, l1e, c1e) then (l0e, c0e) -> (l1, c1); ;;; stay to left of element quitloop endif else ;;; Closer - must be operating inside this element (l0e, c0e) -> (l1, c1); ;;; stay inside element quitloop endif endrepeat; if vedbefore(l1, c1, l0, c0) then vederror('Scope error - cursor or start or end of scope inside tag?') endif enddefine; define lconstant vedhtmlscope(scope) /* -> (l0, c0, l1, c1) */; ;;; Specify scope as 4 integers, list or vector with 4 integers, ;;; or word giving scope character. Returns adjusted scope. if scope.isinteger then ;;; should be 3 more integers on stack scope elseif scope.islist or scope.isvector then unless length(scope) == 4 then mishap(scope, 1, 'Scope list or vector should have length 4') endunless; explode(scope) elseif scope.isword then lvars p = vedhtmlscopeprocs(scope); if p then p() else vederror('Unrecognised scope option: ' sys_>< scope) endif else mishap(scope, 1, 'Expecting integer, list, vector or word') endif; vedhtmladjustscope() enddefine; /* ----------------------------------------------------------------------- 7 Tag insertion ----------------------------------------------------------------------- 7.1 Tag highlighting --------------------- */ define lconstant vedsetattr(attr, l0, c0, l1, c1); ;;; Sets the attributes of all the characters in the current range. ;;; Throws away the old attributes (Cf setattribs) ;;; If first arg is false, unsets attributes. VED_DLOCAL_POS unless attr then 0 -> attr endunless; lvars line, col0, col1; for line from l0 to l1 do vedjumpto(line, 1); if line == l0 then c0 else 1 endif -> col0; if line == l1 then c1-1 else vvedlinesize endif -> col1; for vedcolumn from col0 to col1 do attr || vedcurrentchar() -> vedcurrentdchar() endfor endfor enddefine; define lconstant vedhtmlsettagattrs(attr, l0, c0, l1, c1); ;;; Set the attributes of all tags in the range given. VED_DLOCAL_POS dlocal vedediting = false; vedjumpto(l0, c0); repeat lvars (tag, , tl0, tc0, tl1, tc1) = vedhtmlnexttag(l1, c1); quitunless(tag); vedsetattr(attr, tl0, tc0, tl1, tc1) endrepeat; true -> vedediting; vedrefresh(); enddefine; define lconstant vedhtmlsetallattrs; dlocal vedautowrite = false; vedhtmlsettagattrs(vedhtmlhighlight and vedhtmltagattr, 1, 1, vvedbuffersize+1, 1); enddefine; define lconstant vedhtmlshowtags; ;;; Toggles display of tags. not(vedhtmlhighlight) -> vedhtmlhighlight; vedputmessage('Changing tag characters - please wait'); vedhtmlsetallattrs(); vedputmessage(if vedhtmlhighlight then 'Tag highlighting is on - use "html write" to write file' else 'Tag highlighting is off' endif) enddefine; /* 7.2 Main insertion procedure ----------------------------- */ define lconstant vedhtmlinsert(scope, tag); ;;; Insert tag round scope. Scope can be specified by line ;;; and column numbers or as a word specifying one of the ;;; scope procedures above. dlocal vvedworddump; ;;; because uses vedspandelete lvars (l0, c0, l1, c1) = vedhtmlscope(scope); lvars tagrec = tagprop(tag), issep = htmlseparates(tagrec), hascloser = htmlhascloser(tagrec), keepspaces = htmlkeepspaces(tagrec), attr = tagrec.tag_attribs, curspos = tagrec.tag_cursor, urlpos = tagrec.tag_url, linecurs = vedline, colcurs = vedcolumn; ;;; Opening tag vedjumpto(l0, c0); ;;; Put separator tags on new lines to help find paragraphs if issep then lvars lcol = vedleftcol(); if keepspaces and c0 > 1 and c0 <= lcol then ;;; put tag at left margin to preserve initial indent in ;;; formatted text 1 ->> c0 -> vedcolumn; elseif c0 > lcol then ;;; separator tag being put in middle of text if linecurs == l0 and colcurs >= c0 then colcurs - c0 + 1 -> colcurs endif; linecurs + 1 -> linecurs; if l1 == l0 then c1 - c0 + 1 -> c1 endif; l1 + 1 -> l1; 1 -> c0; l0 + 1 -> l0; vedcharinsert(`\n`); endif endif; ;;; Get an opening URL if there is one lvars urlc0, urlc1, url = false; if urlpos then vedhtmlinurl() -> ( , urlc0, , urlc1); if urlc0 == vedcolumn then if urlpos == curspos then false -> curspos elseif curspos and urlpos < curspos then curspos + urlc1 - urlc0 -> curspos endif; vedspandelete(urlc0, urlc1, true) -> url endif endif; ;;; Insert the opening tag (assume vedbreak set false by caller) vedinsertstring(htmlopenleft); vedinsertstring(tag); if attr then vedcharinsert(`\s`); vedinsertstring(attr); if url then vedcolumn - urlpos -> vedcolumn; vedinsertstring(url); vedcolumn + urlpos -> vedcolumn endif endif; vedinsertstring(htmlopenright); ;;; Highlight opening tag if required if vedhtmlhighlight then vedsetattr(vedhtmltagattr, l0, c0, vedline, vedcolumn) endif; ;;; Adjust other positions. lenopen is length of opening tag. lvars colopenright = vedcolumn, lenopen = colopenright - c0; if url then lenopen - urlc1 + urlc0 -> lenopen endif; if l0 == linecurs and colcurs >= c0 then if url and colcurs < urlc1 then ;;; cursor was in URL colcurs - urlpos - length(htmlopenright) -> colcurs endif; colcurs + lenopen -> colcurs; endif; if l0 == l1 then c1 + lenopen -> c1 endif; ;;; Closing tag if hascloser then vedjumpto(l1, c1); vedinsertstring(htmlcloseleft); vedinsertstring(tag); vedinsertstring(htmlcloseright); ;;; Highlight closing tag if required if vedhtmlhighlight then vedsetattr(vedhtmltagattr, l1, c1, vedline, vedcolumn) endif; ;;; Adjust cursor position for closing tag if l1 == linecurs and colcurs > c1 then colcurs + vedcolumn - c1 -> colcurs endif; elseif issep then l0 -> l1 ;;; closer and opener same endif; ;;; Line break and maybe blank line after closing separator if issep then if vedusedsize(vedthisline()) >= vedcolumn then if linecurs == vedline and colcurs >= vedcolumn then linecurs + 1 -> linecurs; colcurs - vedcolumn + 1 -> colcurs elseif linecurs > l1 then linecurs + 1 -> linecurs endif; vedcharinsert(`\n`); else vedchardown() endif; if vedinparagraph() == true then ;;; not 1 or false vedlineabove(); if linecurs > l1 then linecurs + 1 -> linecurs endif; endif endif; ;;; Set final cursor position if curspos then vedjumpto(l0, colopenright - length(htmlopenright) - curspos) else vedjumpto(linecurs, colcurs) endif enddefine; /* 7.3 Keyboard insertion ----------------------- */ define lconstant vedsplice(l, c, text, upd_attr); ;;; Insert the text, in the form returned by veddeletewholespan, ;;; into the file at l,c. ;;; If upd_attr is true, set attributes of any tags in the spliced text. ;;; Restores the cursor. lvars (lg, cg) = vedadjustpos(vedline, vedcolumn, l, c, text); vedjumpto(l, c); vedinsertstring(dest(text) -> text); lvars line; for line in text do vedcharinsert(`\n`); vedinsertstring(line) endfor; if upd_attr then vedhtmlsettagattrs(vedhtmlhighlight and vedhtmltagattr, l, c, vedline, vedcolumn) endif; vedjumpto(lg, cg) enddefine; define lconstant setattribs(attrib, l0, c0, l1, c1) -> oldtext; ;;; Sets attributes in the given scope to attrib. ;;; The original text is returned for reinstatement later (Cf vedsetattr). ;;; This together with restoretext preserve both the original ;;; attributes and embedded data. VED_DLOCAL_POS dlocal vedediting = false; veddeletewholespan(l0, c0, l1, c1) -> oldtext; vedsplice(l0, c0, [% lvars str; for str in oldtext do lvars l = length(str), i; consdstring( for i from 1 to l do attrib || (str(i) && 16:FFFF) endfor, l) endfor %], false); true -> vedediting; vedrefresh() enddefine; define lconstant restoretext(oldtext, l0, c0, l1, c1); ;;; Deletes the given scope and restores the text ;;; (including attributes and embedded data). VED_DLOCAL_POS dlocal vedediting = false; veddeletewholespan(l0, c0, l1, c1) -> ; vedsplice(l0, c0, oldtext, false); true -> vedediting; vedrefresh() enddefine; lvars procedure vedhtmldeletescope; ;;; forward ref define lconstant vedhtmlkeyinsert(scope); ;;; Show scope, then get tag and do vedhtmlinsert. lvars (l0, c0, l1, c1) = vedhtmlscope(scope); lvars saved_text = false; if vedhtmlscopeattr then ;;; change the scope setattribs(vedhtmlscopeattr, l0, c0, l1, c1) -> saved_text endif; ;;; Get tag lvars firsttagchar = false; define lconstant readtag -> ch; ;;; repeater for reading tag unless firsttagchar then vedinascii_withprompt( 'Start of tag name, or tag termination character, or ' ) ->> ch -> firsttagchar else vedinascii_withprompt( 'Continue tag name or type a tag termination character' ) -> ch endunless; if vedhtmluppercase then lowertoupper(ch) -> ch endif enddefine; lvars tag = tagabbrevs(readtag); ;;; try to read tag ;;; Unmark the scope if saved_text then restoretext(saved_text, l0, c0, l1, c1); endif; if tag then ;;; if a tag read then insert it vedhtmlinsert(l0, c0, l1, c1, tag) else if firsttagchar == `\^?` then ;;; delete scope vedhtmldeletescope(l0, c0, l1, c1) elseif not(lmember(firsttagchar, vedhtmltagtermin)) then vederror('Unrecognised tag') endif endif enddefine; /* ----------------------------------------------------------------------- 8 Tag deletion ----------------------------------------------------------------------- */ define lconstant vedhtmldel(l, c) -> tag; ;;; Deletes the tag and its closer that surround (l, c), ;;; restoring the cursor position. ;;; Uses current cursor position if l false. ;;; Stores deleted text and positions in vedhtmldump - position ;;; for closer is position before closer restored. lvars lg = vedline, cg = vedcolumn; if l then vedjumpto(l, c) endif; lvars (tag, lt0,ct0,lt1,ct1, lc0,cc0,lc1,cc1) = vedhtmltagparts(false); unless tag then vederror('Not in an HTML element') endunless; if htmlhascloser(tag) then if lt1 == lc0 then cc0 + ct0 - ct1 -> cc0 endif; if lt1 == lc1 then cc1 + ct0 - ct1 -> cc1 endif; lc0 + lt0 - lt1 -> lc0; lc1 + lt0 - lt1 -> lc1; endif; vedjumpto(lg, cg); lt0, ct0, veddeletewholespan(lt0, ct0, lt1, ct1); ;;; on stack if htmlhascloser(tag) then lc0, cc0, veddeletewholespan(lc0, cc0, lc1, cc1) else false, false, false endif; -> explode(vedhtmldump) enddefine; define lvars procedure vedhtmldeletescope(scope); ;;; Deletes the entire scope. lvars (l0, c0, l1, c1) = vedhtmlscope(scope); l0, c0, veddeletewholespan(l0, c0, l1, c1), false, false, false -> explode(vedhtmldump) enddefine; /* ----------------------------------------------------------------------- 9 Splicing deleted text ----------------------------------------------------------------------- */ define lconstant vedhtmlundo; ;;; Splice back deleted text in the positions it came from. ;;; May not work if edits have been made since deletion. lvars (lt, ct, textt, lc, cc, textc) = explode(vedhtmldump); if lt then if lc then vedhtmladjustscope(lt, ct, lc, cc) -> (lt, ct, lc, cc); endif; vedsplice(lt, ct, textt, true) else vederror('Nothing to undo') endif; if lc then vedadjustpos(lc, cc, lt, ct, textt) -> (lc, cc); vedsplice(lc, cc, textc, true) endif; enddefine; define lconstant vedhtmlsplice(scope); ;;; Splice text back round current scope, or in front of it if ;;; no closer. lvars (l0, c0, l1, c1) = vedhtmlscope(scope), textt = vedhtmldump(3), textc = vedhtmldump(6), lg = vedline, cg = vedcolumn; if textt then vedsplice(l0, c0, textt, true) else vederror('Nothing to splice') endif; if textc then vedadjustpos(l1, c1, l0, c0, textt) -> (l1, c1); vedsplice(l1, c1, textc, true) endif enddefine; /* ----------------------------------------------------------------------- 10 Preprocessing routines ----------------------------------------------------------------------- 10.1 Character attributes -------------------------- */ define lconstant attribstotags(str) -> str; ;;; Converts some character attributes into tags and ;;; ignores the rest. Ensures tags nest, using special purpose code. lconstant attrnames = {b i u}; ;;; must match vedhtmlattribtags lconstant macro b = 1, i = 2, u = 3; lconstant testbits = { ^( integer_leastbit(`\[b]`), integer_leastbit(`\[i]`), integer_leastbit(`\[u]`) ) }; define lconstant tag(name, sw); if sw then explode(htmlopenleft) else explode(htmlcloseleft) endif; explode(name); if sw then explode(htmlopenright) else explode(htmlcloseright) endif enddefine; lconstant switchedon = initv(3); (false, false, false) -> explode(switchedon); ;;; in case of mishap lvars onlist = []; ;;; stack of atrributes switched on define lconstant puton(attr); lvars tagname = vedhtmlattribtags(attrnames(attr)); if tagname then attr :: onlist -> onlist; true -> switchedon(attr); tag(tagname, true) endif enddefine; define lconstant procedure putoff(attr); repeat lvars a = (dest(onlist) -> onlist); false -> switchedon(a); tag(vedhtmlattribtags(attrnames(a)), false); quitif(a == attr) endrepeat enddefine; if str.isdstring then lvars bld, ita, und; consstring( #| lvars ind, c; for ind from 1 to vedusedsize(str) do str(ind) -> c; unless vedchartype(c) == `\s` then testbit(c, testbits(b)) -> bld; testbit(c, testbits(i)) -> ita; if not(bld) and switchedon(b) then putoff(b) endif; if not(ita) and switchedon(i) then putoff(i) endif; endunless; testbit(c, testbits(u)) -> und; if not(und) and switchedon(u) then putoff(u) endif; unless vedchartype(c) == `\s` then if bld and not(switchedon(b)) then puton(b) endif; if ita and not(switchedon(i)) then puton(i) endif; endunless; if und and not(switchedon(u)) then puton(u) endif; c && 16:FF ;;; ordinary char endfor; if switchedon(b) then putoff(b) endif; if switchedon(i) then putoff(i) endif; if switchedon(u) then putoff(u) endif; |# ) -> str endif enddefine; define lconstant vedhtmlattribs; ;;; Replace character attributes with tags vedtopfile(); until vedatend() do attribstotags(vedthisline()) -> vedthisline(); vedchardown() enduntil enddefine; /* 10.2 Special characters such as < and & ---------------------------------------- */ define lconstant vedhtmlspecchars; ;;; Replaces special characters, using vedhtmlglobalreplace dlocal vedargument; for vedargument in vedhtmlglobalreplace do ved_sgs(); endfor enddefine; /* 10.3 Indexes ------------- */ define lconstant parseindex(indstr) -> (type, headstr); ;;; Tries to identify the type of a string from an index lvars type = false; lvars pd, ps = issubstring('\s\s', 1, indstr), l = length(indstr); if isstartstring('...', indstr) and ps == 4 then 3 -> type; substring(6, l - 5, indstr) -> headstr else if (locchar(`.`, 2, indstr) ->> pd) and pd < ps then 2 -> type else 1 -> type endif; substring(ps + 2, l - ps - 1, indstr) -> headstr endif enddefine; define lconstant concatstrings(strlist) /* -> string */; ;;; Joins strings (and words) together. Avoids intermediate string ;;; construction at the expense of the list to pass the strings in. lvars s, n = 0; consstring( #| for s in strlist do explode(s) endfor |# ) enddefine; uses ved_newindex define lconstant get_next_indexline -> string; lvars string; ;;; Like $-ved$-find_new_indexline except will not go past a ;;; non-index line (so can ignore embedded index fragments as found, ;;; for example, in REF * VEDCOMMS) until ($-ved$-is_new_indexline(vedthisline()) ->> string) or vvedlinesize /== 0 or vedline >= vvedbuffersize do vedchardown() enduntil enddefine; define lconstant reindexify /* -> indexline */; ;;; Reconstruct headers and index in standard form ;;; Actually, ref files sometimes have out of date indexes, so quite ;;; a good idea to renumber and redo index anyway. dlocal vedargument = 'r ref'; ved_newheading(); ;;; replace all headings in new ref style vedtopfile(); 'nosp' -> vedargument; ved_newindex(); ;;; new index without spacing vedline enddefine; define lconstant vedhtmlfixindex(indexline); ;;; Rather than rewriting most of the indexing material, ;;; call it. May redo some work, but reliable as long as ;;; styles implemented by ved_newindex and ved_newheading do not change. vedjumpto(indexline, 1); ;;; The CONTENTS line will not exist if there was an index ;;; without one previously, so put in an extra line for it. unless issubstring('CONTENTS', 1, vedthisline()) then vedchardown(); vedlinebelow(); vedcharup() endunless; lvars lcont = vedline, ccont = vedcolumn, iline; ;;; Delete old_type index entries (not done when index rebuilt) unless ved_g_string = nullstring then lvars s = '@^\s' sys_>< ved_g_string; while ved_try_search(s, [nowrap]) do vedlinedelete(); vedcharup() endwhile endunless; vedjumpto(lcont+1, 1); ;;; Do each index entry. Index entries and headers can point to ;;; each other mutually to make it easy to jump between ;;; index and text. 1 ->> gensym("index") -> gensym("heading"); nullstring -> vedargument; lvars hasindex = false; while get_next_indexline() ->> iline do true -> hasindex; lvars li = vedline, lc = vedcolumn, ;;; index line (htype, hstring) = parseindex(iline), nameh = gensym("heading"), namei = gensym("index"); ved_g(); ;;; to heading if htype == 1 then ;;; delete over- and underlining vedcharup(); vedlinedelete(); endif; vedchardown(); nullstring -> vedthisline(); vedcharup(); concatstrings([% htmlopenleft, vedhtmlheaders(htype), htmlopenright, htmlopenleft, 'A NAME="', nameh, '"', if vedhtmljumptoindex then ' HREF="#', namei, '"' endif, htmlopenright, hstring, htmlcloseleft, 'A', htmlcloseright, htmlcloseleft, vedhtmlheaders(htype), htmlcloseright %]) -> vedthisline(); vedjumpto(li, lc); ;;; back to index concatstrings([% htmlopenleft, vedhtmlindexentry, htmlopenright, htmlopenleft, 'A HREF="#', nameh, '"', if vedhtmljumptoindex then ' NAME="', namei, '"' endif, htmlopenright, vedhtmlindexmarks(htype), hstring, htmlcloseleft, 'A', htmlcloseright, htmlcloseleft, vedhtmlindexentry, htmlcloseright %]) -> vedthisline(); vedchardown() endwhile; ;;; Terminate index if hasindex then vedjumpto(li, 1); vedlinebelow(); htmlcloseleft sys_>< vedhtmlindextype sys_>< htmlcloseright -> vedthisline() endif; ;;; Some REF files have mini-indices in the text (e.g. REF * VEDCOMMS) ;;; Ideally these ought to become references too, but for now just ;;; remove their numbers which have become meaningless and make ;;; them separate objects. while $-ved$-find_new_indexline() ->> iline do parseindex(iline) -> (htype, hstring); concatstrings([% htmlopenleft, vedhtmlembeddedindextag, htmlopenright, vedhtmlindexmarks(htype), hstring, htmlcloseleft, vedhtmlembeddedindextag, htmlcloseright %]) -> vedthisline() endwhile; ;;; Start index or delete contents line vedjumpto(lcont, 1); ;;; back to contents line if hasindex then concatstrings([% htmlopenleft, vedhtmlheaders(1), htmlopenright, vedhtmlcontents, htmlcloseleft, vedhtmlheaders(1), htmlcloseright %]) -> vedthisline(); if vedhtmljumptoindex.isstring then vedlinebelow(); vedlinebelow(); vedhtmljumptoindex -> vedthisline(); vedlinebelow(); endif; vedchardown(); htmlopenleft sys_>< vedhtmlindextype sys_>< htmlopenright -> vedthisline() else vedlinedelete(); vedlinedelete() endif enddefine; /* 10.4 REF/HELP/TEACH file headers --------------------------------- */ define lconstant vedhtmlsimplepara /* -> inpara */; ;;; For recognising paragraphs simply separated by blank lines vvedlinesize /== 0 enddefine; define lconstant noattribs(dstr) /* -> str */; ;;; Strips attributes from string mapdata(dstr, nonop fi_&&(% 16:FF %)) enddefine; uses int_parameters define lconstant vedhtmldocheads -> (title, authors, heading); ;;; Deletes the name, authors, and heading box of a ref file and ;;; returns them. ;;; Must be called before special characters are replaced. lvars title = false, authors = false, heading = false; dlocal vedlinemax = pop_max_int, vedargument, vedinparagraph = vedhtmlsimplepara, 0 %vedmarkpush(), vedmarkpop() %; lconstant startheader = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', endheader = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<', maxhdsize = 10; ;;; max number of lines in a header vedtopfile(); lvars first = vednextitem(); if lmember(first, [REF TEACH DOC HELP]) then vedwordrightdelete(); first sys_>< '\s' sys_>< vednextitem() -> title; vedwordrightdelete(); vedmarkparagraph(); ved_al(); ved_fill(); noattribs(vedthisline()) -> authors; vedlinedelete(); if ved_try_search(startheader, []) then vedmarklo(); vedline + maxhdsize -> vedline; vedsetlinesize(); vedmarkhi(); vedline - maxhdsize + 1 -> vedline; vedsetlinesize(); if ved_try_search(endheader, [range]) then vedmarkhi(); '/>//' -> vedargument; ved_sgsr(); '/ vedargument; ved_sgsr(); ved_al(); ved_fill(); vedprevpara(); noattribs(vedthisline()) -> heading; vedlinedelete() endif endif endif enddefine; /* 10.5 Lists ----------- */ define lconstant parastart -> found; ;;; Goes forwards until vedinparagraph true (or end of file) until (vedinparagraph() ->> found) or vedatend() do vedchardown() enduntil enddefine; define lconstant paraend; ;;; Goes forwards while vedinparagraph true vedchardown(); until vedinparagraph() /== true do vedchardown() enduntil; vedcharup() enddefine; define lconstant listline -> (indent, startitem, startlen); ;;; Tests whether a line is a potential list item first line. ;;; ;;; Call with the cursor on the first line of the potential para. ;;; ;;; Is a candidate if the characters before the first space on ;;; the first line are ;;; ;;; a single non-alphanumeric character or "o" or "x" ;;; or a single letter followed by "." or ")" ;;; or an integer optionally followed by ".", ")" or ".)" ;;; or two integers separated by "." optionally followed ditto ;;; ;;; Returns false for all results if condition not met; otherwise gives ;;; the indent of the first line, the item before the punctuation, and the ;;; length of the stuff up to the first space. Two numbers separated by ;;; a dot are returned as a pair. VED_DLOCAL_POS define :inline lconstant nonneg(x) /* -> x */; (x).isreal and (x) >= 0 and (x) enddefine; define splitstring(str) /* -> strings on stack */; ;;; like sys_parse_string but uses `.` instead of white space lvars i = 1, j; while locchar(`.`, i, str) ->> j do substring(i, j-i, str); ;;; on stack j+1 -> i endwhile; substring(i, datalength(str)-i+1, str) enddefine; false ->> indent ->> startitem -> startlen; vedtextleft(); lvars c0 = vedcolumn; lvars str, len; consstring(#| repeat lvars ch = vedcurrentchar(), chtype = vedchartype(ch); quitif (chtype == `\s` or chtype == `\t` or chtype == `\n`); ch; vedcharright(); endrepeat |# ->> len) -> str; returnif (str = nullstring); ;;; line starting with special space if len == 1 then lvars ch = str(1); if ch == `o` or ch == `x` or not(ch.isalphacode or ch.isnumbercode) then consword(str) -> startitem endif elseif len == 2 then lvars ch = str(1), ch2 = str(2); if (ch2 == `.` or ch2 == `)`) and ch.isalphacode then ch -> startitem ;;; can treat as if a number (at least if ASCII!) endif endif; unless startitem then ;;; maybe it is a number or two lvars l = len, ch = str(len); if ch == `.` or ch == `)` then allbutlast(1, str) -> str; l - 1 -> l endif; lvars nitems = #| splitstring(str) |#; if nitems == 1 then strnumber() -> startitem; nonneg(startitem) -> startitem; elseif nitems == 2 then lvars i2 = strnumber(), i1 = strnumber(); nonneg(i1) -> i1; nonneg(i2) -> i2; if i1 and i2 then conspair(i1, i2) -> startitem endif else erasenum(nitems) endif endunless; if startitem then c0 -> indent; len -> startlen endif enddefine; define lconstant testlistline(indent1, startitem1) -> (indent, startitem, startlen); ;;; If arguments not false, then tests whether current line ;;; is the first line of a possible continuation paragraph. ;;; A para can continue a list if its indent is the same as the ;;; previous one, and its startitem is the same if a word or ;;; increments appropriately if a number. In this case indent is . ;;; A para can start a sublist if it changes from a number to two ;;; numbers, or if its indent increases. In this case indent is an int. ;;; Otherwise indent is false. listline() -> (indent, startitem, startlen); if indent1 then if indent == indent1 and ( (startitem.isword and startitem == startitem1) or (startitem.isinteger and startitem1.isinteger and startitem == startitem1 + 1) or (startitem.ispair and startitem1.ispair and front(startitem) == front(startitem1) and back(startitem) == back(startitem1) + 1) ) then true -> indent elseif indent and ( (indent > indent1 and startitem.isword and startitem == startitem1) or (indent >= indent1 and startitem.ispair and startitem1.isinteger and front(startitem) == startitem1) ) then ;;; leave it alone else false -> indent endif endif enddefine; define lconstant marklist(indent1, startitem1, startlen1) -> lend; ;;; Called with cursor on first line of para. ;;; Three cases when called: ;;; not already in a list - all args ;;; already in a list but current para not looked at yet ;;; - startlen1 only ;;; first para in a sublist - no args ;;; ;;; Returns line no of last line of list, or false if not ;;; a list. lvars lg = vedline; define lconstant restoreline; vedjumpto(lg, 1) enddefine; define lconstant markthispara(indent, startlen); dlocal 0 % vedmarkpush(), vedmarkpop() %; vedmarkparagraph(); vedspandelete(indent, indent+startlen+1, false); ved_al(); ;;; remove indentation vedhtmlinsert("r", vedhtmllistentry) enddefine; define lconstant markthislist(startitem, lend); VED_DLOCAL_POS dlocal 0 % vedmarkpush(), vedmarkpop() %; vedmarklo(); vedjumpto(lend, 1); vedmarkhi(); vedhtmlinsert("r", if startitem.ispair or startitem.isinteger then vedhtmlnumlist elseif startitem then vedhtmlsymlist else vedhtmllistentry ;;; for wrapping sublist endif) enddefine; define lconstant nextpara -> (lastline, found); ;;; Jumps to start of next para; returns last line of this one paraend(); vedline -> lastline; vedchardown(); parastart() -> found enddefine; lvars indent, startitem, startlen, more; if indent1 and startlen1 then ;;; at first para in sublist (indent1, startitem1, startlen1) -> (indent, startitem, startlen); nextpara() -> (lend, more); if more then marklist(indent, startitem, false) or lend -> lend endif; restoreline(); markthispara(indent, startlen); markthislist(startitem, lend); elseif indent1 then ;;; in a list already testlistline(indent1, startitem1) -> (indent, startitem, startlen); if indent == true then ;;; current para is a continuation nextpara() -> (lend, more); if more then marklist(indent1, startitem, false) or lend -> lend endif; restoreline(); markthispara(indent1, startlen); elseif indent then ;;; current para starts a sublist marklist(indent, startitem, startlen) -> lend; restoreline(); markthislist(false, lend); ;;; wrap whole sublist as entry vedjumpto(lend, 1); nextpara() -> (lend, more); if more then marklist(indent1, startitem1, false) or lend -> lend endif; else false -> lend endif else ;;; may be at start of a list listline() -> (indent, startitem, startlen); if indent then nextpara() -> (lend, more); ;;; next line different to above as lone list-type para not ;;; a list more and marklist(indent, startitem, false) -> lend; if lend then restoreline(); markthispara(indent, startlen); markthislist(startitem, lend); endif else false -> lend endif endif; restoreline(); enddefine; define lconstant vedhtmlmarklists; ;;; Put list element tags round things that look like lists. define dlocal vedinparagraph; lconstant lline = listline <> erase <> erase; (lline() and 1) or vedhtmlinparagraph() enddefine; lvars lend; vedtopfile(); while parastart() do if marklist(false, false, false) ->> lend then vedjumpto(lend+1, 1) else paraend(); vedchardown() endif endwhile enddefine; /* 10.6 Paragraph separation -------------------------- */ define lconstant identifierline(str) /* -> bool */; ;;; Tries to recognise ref-file style identifier lines lvars c, l, b; (length(str) ->> l) > 4 and last(str) == `]` ;;; ends with [ ... ] and ((vedhtmlchartype(str(1)) ->> c) == `a` ;;; starts in col 1 with or c == `+` ;;; with an identifier of or c == `_`) ;;; some sort and (locchar_back(`[`, l, str) ->> b) ;;; has a [ and (issubstring('constant', b, str) ;;; and has a suitable word or issubstring('procedure', b, str) ;;; between the brackets or issubstring('operator', b, str) or issubstring('variable', b, str) or issubstring('syntax', b, str) or issubstring('macro', b, str) or issubstring('datatype', b, str) or issubstring('property', b, str)) enddefine; define lconstant vedhtmlsplitidlines; ;;; Puts a blank line below each ref-file identifier line. Best ;;; done before inserting attribute tags. lvars onidline; vedtopfile(); until vedatend() do until (identifierline(vedthisline()) ->> onidline) or vedatend() do vedchardown() enduntil; if onidline then repeat vedchardown(); vedtextleft(); if vedcolumn == 1 then vedlineabove(); vedchardown() endif; quitif (vedcolumn == 9) endrepeat; ;;; go to following text vedlineabove() endif enduntil enddefine; define vars vedhtmlparatype(l0, l1) -> tag; ;;; Returns a tag for the type of the paragraph from lines l0 to l1. VED_DLOCAL_POS l0 -> vedline; if vedhtmlatsep() then false -> tag ;;; already separated else vedtextleft(); lvars c, c0 = vedcolumn; if c0 == 1 then if (vedcurrentchar() ->> c) == `\Sf` then vedhtmlformatpara elseif c == `\Sp` then vedhtmlpromptpara else vedhtmlnormpara endif else vedhtml1linepara endif -> tag; for vedline from l0 + 1 to l1 do vedtextleft(); if vedcolumn /== c0 then vedhtmlraggedpara -> tag; quitloop elseif vedcolumn > 1 then vedhtmlindentpara -> tag endif endfor endif enddefine; define lconstant vedhtmlinsertseps; ;;; Insert separators to break up text into paragraphs. Do after ;;; inserting attribute tags. ;;; Uses vedhtmlparatype to decide which tag to insert. vedtopfile(); while parastart() do lvars l0 = vedline; paraend(); lvars l1 = vedline; lvars tag = vedhtmlparatype(l0, l1); vedtextleft(); if tag then vedhtmlinsert("p", tag); if htmlhascloser(tag) then vedhtmlcloser(tag) -> ( , , , l1, ); vedjumpto(l1+1, 1) else vednextline() endif else vednextline() endif endwhile enddefine; /* 10.7 Wrappers -------------- */ define lconstant vedhtmlwrappers(title, authors, heading); ;;; Put in body, head and html wrappers heading or title -> heading; ;;; use heading if no title vedtopfile(); if heading then vedlineabove(); heading -> vedthisline(); vedhtmlinsert("l", vedhtmlmainhead); vedchardown(); endif; if authors then vedlineabove(); authors -> vedthisline(); vedhtmlinsert("l", vedhtmlauthors); vedchardown(); endif; if (heading or authors) and vedhtmlmainheadsep then vedlineabove(); vedhtmlinsert(".", vedhtmlmainheadsep) endif; vedhtmlinsert("f", "BODY"); vedtopfile(); vedlineabove(); if title then title -> vedthisline() endif; vedhtmlinsert("l", "TITLE"); vedhtmlinsert("e", "HEAD"); vedhtmlinsert("f", "HTML") enddefine; /* 10.8 Graphics characters and special spaces -------------------------------------------- */ define vedhtmlgraphchars; ;;; Replace graphics characters and special spaces with their ;;; nearest ASCII equivalents. Could do this on writing more ;;; easily but logically part of preparation procedure. dlocal vedscreengraphtrans; vednographics(); ;;; set vedscreengraphtrans to default lvars c; for vedline from 1 to vvedbuffersize do vedsetlinesize(); for vedcolumn from 1 to vvedlinesize do if vedhtmlchartype(vedcurrentchar() ->> c) == `\s` then `\s` -> vedcurrentchar() elseif c fi_>= 16:80 then if c fi_<= 16:9C and c /== 16:80 then vedscreengraphtrans(c) -> (vedcurrentchar(), ) else `\s` -> vedcurrentchar() endif endif endfor endfor enddefine; /* 10.9 Overall preprocessing --------------------------- */ define lconstant vedhtmlprep(args); ;;; Runs each of the preprocessing actions in a sensible order. ;;; args is a list of strings. If empty everything is done, ;;; if it starts with 'not' then everything apart from the actions ;;; identified is done, otherwise only the actions identified are done. dlocal vedautowrite = false, vedediting = false; ;;; Sort out arguments lvars todo = [], nottodo = []; ncmaplist(args, consword) -> args; if args /== [] and hd(args) == "not" then tl(args) ->> nottodo -> args ;;; reassign to args for check below else args -> todo endif; lvars doall = todo == []; ;;; check args were all legal lconstant ops = [titles htmlchars headers attributes lists paragraphs graphchars wrappers]; lvars w; for w in args do unless lmember(w, ops) then vederror('Illegal prep option: ', sys_>< w) endunless endfor; define lconstant putmessage(str); dlocal vedediting = true; vedputmessage(str) enddefine; define lconstant macro allor word; ;;; (doall and not(lmember(word,nottodo))) or lmember(word,todo) then "(", "doall", "and", "not", "(", "lmember", "(", """, word, """, ",", "nottodo", ")", ")", ")", "or", "lmember", "(", """, word, """, ",", "todo", ")", "then" enddefine; ;;; Tags are inserted in various ways, so highlighting will not work false -> vedhtmlhighlight; ;;; These may need remembering between calls lconstant title = consref(false), authors = consref(false), heading = consref(false); ;;; Order of these matters if allor titles putmessage('Extracting titles'); vedhtmldocheads() -> (cont(title), cont(authors), cont(heading)) endif; if allor headers putmessage('Reindexing'); lvars indexline = reindexify(); endif; if allor htmlchars putmessage('Replacing HTML special characters'); vedhtmlspecchars() endif; if allor headers putmessage('Doing headers and index'); vedhtmlfixindex(indexline) endif; if allor paragraphs putmessage('Separating out REF-file identifier lines'); vedhtmlsplitidlines() endif; if allor attributes putmessage('Tagging characters with ved attributes'); vedhtmlattribs() endif; if allor lists putmessage('Doing lists'); vedhtmlmarklists() endif; if allor paragraphs putmessage('Inserting paragraph separators'); vedhtmlinsertseps() endif; if allor graphchars putmessage('Replacing graphics and special space characters'); vedhtmlgraphchars() endif; if allor wrappers putmessage('Inserting overall wrappers'); vedhtmlwrappers(cont(title), cont(authors), cont(heading)) endif; vedtopfile(); ;;; might as well end up here putmessage('HTML preprocessing done'); true -> vedediting; vedrefresh(); enddefine; /* ----------------------------------------------------------------------- 11 Entry points ----------------------------------------------------------------------- */ ;;; Next macro needs to be in every entry point lconstant macro TOP_LEVEL_DLOCAL = [ dlocal ved_search_state, vedbreak = false, vvedpromptchar = false, vedleftmargin = 0, vedinparagraph = vedhtmlinparagraph, vedchartype_orig = vedchartype, vedchartype = vedhtmlchartype; ]; /* 11.1 Keyboard string interface ------------------------------- */ define lconstant vedhtmlkeyaction; ;;; To be called when vedhtmlkeys typed. TOP_LEVEL_DLOCAL ;;; x scope does not work when vedinascii called lconstant xscopeerr = 'Use html commands for x scope'; lconstant deletecmd = consword(`\^?`, 1), prompt = 'Enter scope from ' sys_>< vedhtmlscopechars sys_>< ', or or one of DUS', Dprompt = 'Enter scope to delete from ' sys_>< vedhtmlscopechars, Sprompt = 'Enter scope to splice round from ' sys_>< vedhtmlscopechars; lvars cmd = consword(vedinascii_withprompt(prompt), 1); if cmd == deletecmd then vedputmessage('Deleted tag ' sys_>< vedhtmldel(false, false)) elseif cmd == "D" then lvars scope = consword(vedinascii_withprompt(Dprompt), 1); if scope == "x" then vederror(xscopeerr) endif; vedhtmldeletescope(scope) elseif cmd == "U" then vedhtmlundo() elseif cmd == "S" then lvars scope = consword(vedinascii_withprompt(Sprompt), 1); if scope == "x" then vederror(xscopeerr) endif; vedhtmlsplice(scope) elseif cmd == "x" then vederror(xscopeerr) else vedhtmlkeyinsert(cmd) endif enddefine; lvars htmlkeys = false; define active vedhtmlkeys; ;;; Returns string value; on update stores string and does vedsetkey ;;; to make string initiate vedhtmlkeyaction. Initialised at end of file. htmlkeys enddefine; define updaterof active vedhtmlkeys(s); if htmlkeys.isstring then vedsetkey(htmlkeys, undef) endif; s -> htmlkeys; if s then vedsetkey(s, vedhtmlkeyaction) endif enddefine; /* 11.2 Command line interface ---------------------------- */ define lconstant tagfromargs(arglist) -> tag; ;;; tag from string or from string in hd of list if arglist.islist then hd(arglist) -> arglist endif; lvars tagreader = stringin(arglist); if vedhtmluppercase then tagreader <> lowertoupper -> tagreader endif; tagabbrevs(tagreader) -> tag; unless tag then vederror('Unrecognised tag: ' sys_>< arglist) endunless enddefine; define lconstant vedhtmlkeystring /* -> str */; ;;; Stripped down vedinkeys. Prints message and returns ;;; keys typed up to 3 ESCs. vedputmessage( 'Type keyboard sequence for html, then 3 times'); lvars x, z; consstring( #| repeat vedinascii() -> z; if z == `\e` then ;;; esc typed vedinascii() -> x; if x == z then ;;; second esc typed vedinascii() -> x; quitif (x == z); ;;; quit if third esc typed z; z; x else z; x endif else z endif endrepeat |#); vedputmessage(nullstring) enddefine; define ved_html; ;;; Command line driver procedure. TOP_LEVEL_DLOCAL lvars args = sysparse_string(vedargument, true), key = args /== [] and consword(dest(args) -> args); if key == "prep" then vedhtmlprep(args) elseif key == "keys" then vedhtmlkeystring() -> vedhtmlkeys elseif key == "del" then if args == [] then vedhtmldel(false, false) else vedhtmldeletescope(consword(hd(args))) endif elseif key == "undo" then vedhtmlundo() elseif key == "splice" then if args == [] then vederror('Need scope for splicing round after "splice"') else vedhtmlsplice(consword(hd(args))) endif elseif key == "show" then vedhtmlshowtags() elseif key == "write" then vedwriteplain(args /== [] and hd(args)) elseif key == "printtags" then vedhtmlprinttags() elseif key == "abbrevs" then vedhtmlprinttagnames() elseif key == "readtags" then vedhtmlreadtags(args /== [] and hd(args) or vedhtmltagfile) elseif key == "pclosers" then vedhtmlswitchPclosers(args) elseif args /== [] then vedhtmlinsert(key, tagfromargs(args)) elseif key then vederror('ved_html command not recognised: ' sys_>< key) else vedputmessage('ved_html loaded') endif enddefine; /* ----------------------------------------------------------------------- 12 Customisation ----------------------------------------------------------------------- */ ;;; Do this at end so that variables can be updated trycompile('$poplib/ved_html_init.p') -> ; ;;; Set up keyboard string if not done from init file. unless vedhtmlkeys then '\e\eh' -> vedhtmlkeys endunless; /* ----------------------------------------------------------------------- 13 Set up the tag list ----------------------------------------------------------------------- */ ;;; Needs to come after the customisation in case the name of ;;; the tag file has been changed lvars Pclosers = vedhtmlPclosers; ;;; record Pcloser state #_IF sys_file_exists(vedhtmltagfile) vedhtmlreadtags(vedhtmltagfile); #_ELSE /* Adapted from the w3 list at http://www.w3.org/TR/REC-html40/index/elements.html hascloser is set for all elements for which an ending tag is not forbidden separates is set for all %block elements plus others which I think are structural, plus list elements keepspaces is set for PRE, also for CODE and SAMP which may be wrong as they are not block elements - see how it works out tag_name qualities attributes */ [ {!-- [] '^ --'} {A [hascloser] 'HREF="^*"'} {ABBR [hascloser] N} {ACRONYM [hascloser] N} {ADDRESS [hascloser separates] N} {APPLET [hascloser] 'CODE="^" WIDTH="" HEIGHT=""'} {AREA [] 'COORDS="^" ALT=""'} {B [hascloser] N} {BASE [] 'HREF="^*"'} {BASEFONT [] 'SIZE=^'} {BDO [hascloser] 'DIR=^'} {BIG [hascloser] N} {BLOCKQUOTE [hascloser separates] N} {BODY [hascloser separates] N} {BR [] N} {BUTTON [hascloser] N} {CAPTION [hascloser separates] N} {CENTER [hascloser separates] N} {CITE [hascloser] N} {CODE [hascloser keepspaces] N} {COL [] N} {COLGROUP [hascloser] N} {DD [hascloser separates] N} {DEL [hascloser] N} {DFN [hascloser] N} {DIR [hascloser separates] N} {DIV [hascloser separates] N} {DL [hascloser separates] N} {DT [hascloser separates] N} {EM [hascloser] N} {FIELDSET [hascloser separates] N} {FONT [hascloser] 'SIZE=^'} {FORM [hascloser separates] 'ACTION="^*"'} {FRAME [] N} {FRAMESET [hascloser separates] N} {H1 [hascloser separates] N} {H2 [hascloser separates] N} {H3 [hascloser separates] N} {H4 [hascloser separates] N} {H5 [hascloser separates] N} {H6 [hascloser separates] N} {HEAD [hascloser separates] N} {HR [separates] N} {HTML [hascloser separates] N} {I [hascloser] N} {IFRAME [hascloser] N} {IMG [] 'SRC="^" ALT=""'} {INPUT [] N} {INS [hascloser] N} {ISINDEX [separates] N} {KBD [hascloser] N} {LABEL [hascloser] N} {LEGEND [hascloser] N} {LI [hascloser separates] N} {LINK [] 'HREF="^*"'} {MAP [hascloser] 'NAME="^"'} {MENU [hascloser separates] N} {META [] 'CONTENT="^"'} {NOFRAMES [hascloser separates] N} {NOSCRIPT [hascloser separates] N} {OBJECT [hascloser] N} {OL [hascloser separates] N} {OPTGROUP [hascloser] N} {OPTION [hascloser] N} {P [hascloser separates] N} {PARAM [] 'NAME="^" VALUE=""'} {PRE [hascloser separates keepspaces] N} {Q [hascloser] N} {S [hascloser] N} {SAMP [hascloser keepspaces] N} {SCRIPT [hascloser] 'TYPE="^" SRC=""'} {SELECT [hascloser] 'NAME="^"'} {SMALL [hascloser] N} {SPAN [hascloser] N} {STRIKE [hascloser] N} {STRONG [hascloser] N} {STYLE [hascloser separates] 'TYPE="^"'} {SUB [hascloser] N} {SUP [hascloser] N} {TABLE [hascloser separates] N} {TBODY [hascloser separates] N} {TD [hascloser] N} {TEXTAREA [hascloser] 'NAME="^" ROWS="" COLS=""'} {TFOOT [hascloser separates] N} {TH [hascloser] N} {THEAD [hascloser separates] N} {TITLE [hascloser separates] N} {TR [hascloser] N} {TT [hascloser] N} {U [hascloser] N} {UL [hascloser separates] N} {VAR [hascloser] N} ] -> vedhtmltags; #_ENDIF if Pclosers /== undef then Pclosers -> vedhtmlPclosers; ;;; restore P closer state endif; endsection; /* --- Revision History --------------------------------------------------- --- David Young, Jun 4 2001 Changed vedhtmlauthors from AU (obsolete) to H2. --- David Young, Oct 21 1999 Updated default tag list to HTML 4.0 --- David S Young, Jan 21 1999 - Added "!" to vedhtmltagchars and modified vedhtmltagname so that comments are seen as valid tags. - Changed test in parascope so that cursor can be after end of text on last line of paragraph. --- David S Young, Sep 20 1997 Added delete option to vedhtmlinsertkey. --- David S Young, Sep 18 1997 Erroneous lconstant removed from vedhtmltags definition. --- David S Young, Sep 16 1997 - Added URL scope and made leading URLs move into attributes when tags such as inserted. - Added scope highlighting during tag insertion. - Added "html show" and "html write" commands. - Added options to "html prep" command. - Changed escape sequences: all now start with the same string to allow more commands. This changes the sequences for deletion. Sequences for undo and splice added. ikeys, dkeys and Dkeys commands all replaced by single keys command. - Some reordering and tidying of procedures. --- David S Young, Sep 10 1997 Switched off vedediting in setattribs and restoretext. --- David S Young, Sep 9 1997 Added scope highlighting during keyboard insertion. Some consequent reordering of procedures. Removed position restore and vedrefresh from vedhtmlattribs. --- David S Young, Sep 4 1997 Changed vedhtmlfixindex to leave blank lines under headings when deleting underlining to maintain paragraph separation. */