diff --git a/docs/database/dbolio.lib b/docs/database/dbolio.lib new file mode 100644 index 0000000000000000000000000000000000000000..2ee721a1c062eaa2222b5e8220d28849e95140e4 --- /dev/null +++ b/docs/database/dbolio.lib @@ -0,0 +1,324 @@ +@Marker(Library, DBOLIO, Press, Dover, Postscript) + + +@Modify(Chapter,TitleForm +{@Begin(Hd1A, Below 0.8 in)@Skip(0.8 in)@*Chapter @Parm(Numbered)@*@Skip(0.3 in)@*@Parm(Title)@End(Hd1A)}, + ContentsForm +{@Begin(Tc1)@Rfstr(@Parm(Page))@Parm(Referenced)@.@ @Parm(Title)@End(Tc1)}, + Numbered [@1],IncrementedBy Use,Referenced [@1],Announced) +@Modify(Appendix,TitleEnv HD1,ContentsEnv tc1,Numbered [@A.], + ContentsForm "@Tc1(Appendix @Parm(Referenced)@.@ @Rfstr(@Parm(Page))@parm(Title))", + TitleForm "@Hd1(@=Appendix @Parm(Referenced)@*@=@Parm(Title))", + IncrementedBy,Referenced [@A],Announced) @Comment{Alias Chapter} +@Define(F, Facecode F) +@Define(Sail, Facecode U) +@Define(Arrow, Facecode N) +@Commandstring(Minussign = "") +@Commandstring(Centerdot = "@Begin(W, script +0.4 quad).@End(W)") +@Commandstring(Tilde = "@Begin(W, script +0.4 quad)~@End(W)") +@Commandstring(Underscore = "_") +@Commandstring(Bq = "@;`") +@Form(Complex = "@f[#C(@Parm(R) @Parm(I))]") +@Textform(Altmode = "") +@Define(DenseDescription = Description, Spread .3) +@Define(Defenvironment = Text, Need 4, Nofill, Justification Off, Break, + Above 2, Below 2, Spread 0.5) +@Define(Defbody = Text, Leftmargin +.6in, Indent 0, Spread 0.5, Break) +@Define(Undefbody = Text, Nofill, Justification Off, Leftmargin -.6in, + Above 0, Below 0, Group, Break) +@Define(Lispenvironment = Verbatim, Group, Leftmargin +.3in, Above .5, Below .5) +@Commandstring(Nopara = "@Begin(Text, Continue Force)@End(Text)") +@Commandstring(Optional = "@f[&optional]") +@Commandstring(Rest = "@f[&rest]") +@Commandstring(Key = "@f[&key]") +@Commandstring(allowotherkeys = "@f[&allow-other-keys]") +@Commandstring(Aux = "@f[&aux]") +@Commandstring(Body = "@f[&body]") +@Commandstring(Whole = "@f[&whole]") +@Commandstring(Special = "@f[&special]") +@Commandstring(Local = "@f[&local]") +@Textform(Mopt = "@r{[}@Parm(Text)@r{]}") +@Textform(Mgroup = "@r[{]@Parm(Text)@r[}]") +@Textform(Mstar = "@r[{]@Parm(Text)@r[}*]") +@Textform(Mplus = "@r[{]@Parm(Text)@r[}@+[+]]") +@Commandstring(Mor = "@r[|]") +@Commandstring(Lisp = "@Begin(Lispenvironment)@~") +@Commandstring(Endlisp = "@End(Lispenvironment)") +@Commandstring(Lispx = "@Begin(Text, Indent 0, Above .5)For example:@End(Text)@Lisp") +@Comment{ + @Commandstring(EQ = "@Arrow[J]") + @Commandstring(EV = "@Arrow[A]") + @Commandstring(EX = "@Arrow[G]") +} + @Commandstring(EQ = "@f[<=>]") + @Commandstring(EV = "@f[=>]") + @Commandstring(EX = "@f[==>]") +@Commandstring(lbracket = "@f{[}") +@Commandstring(rbracket = "@f{]}") +@Commandstring(lbrace = "@f[{]") +@Commandstring(rbrace = "@f[}]") +@define(subi = i) +@define(subr = r) +@define(superi = i) +@define(supersail = sail) +@define(superg = g) +@commandstring(superminussign = "@minussign@;") +@commandstring(supercenterdot = "@centerdot@;") +@Commandstring(false = "@f[nil]") +@Commandstring(nil = "@f[nil]") +@Commandstring(empty = "@f[()]") +@Commandstring(true = "@f[t]") +@Commandstring(xlisp = "@r[L@c[isp]]") +@Commandstring(clisp = "@r[C@c[ommon]] @xlisp") +@Commandstring(lmlisp = "@PossiblyIndexedRef(K , + E <@r[Z@c[eta]@xlisp]>, F <@r[Z@c[eta]@xlisp]>)") +@Commandstring(newlisp = "@PossiblyIndexedRef(K , + E <@r[N@c[il]]>, F <@r[N@c[il] (New Implementation of @xlisp]>)") +@Commandstring(slisp = "@PossiblyIndexedRef(K , + E <@r[S@c[pice]] @xlisp>, F <@r[S@c[pice]] @xlisp>)") +@Commandstring(lisp15 = "@PossiblyIndexedRef(K , + E <@xlisp @r[1.5]>, F <@xlisp @r[1.5]>)") +@Commandstring(maclisp = "@PossiblyIndexedRef(K , + E <@r[M@c[ac]]@xlisp>, F <@r[M@c[ac]]@xlisp>)") +@Commandstring(franzlisp = "@PossiblyIndexedRef(K , + E <@r[F@c[ranz]] @xlisp>, F <@r[F@c[ranz]] @xlisp>)") +@Commandstring(interlisp = "@PossiblyIndexedRef(K , + E <@r[I@c[nter]]@xlisp>, F <@r[I@c[nter]]@xlisp>)") +@Commandstring(stdlisp = "@PossiblyIndexedRef(K , + E <@r[S@c[tandard]] @xlisp>, F <@r[S@c[tandard]] @xlisp>)") +@Commandstring(psl = "@PossiblyIndexedRef(K , + E <@r[P@c[ortable] S@c[tandard]] @xlisp>, F <@r[P@c[ortable] S@c[tandard]] @xlisp>)") +@Commandstring(s1lisp = "@PossiblyIndexedRef(K , + E <@r[S-1] @xlisp>, F <@r[S-1] @xlisp>)") +@Commandstring(scheme = "@PossiblyIndexedRef(K , + E <@r[S@c[cheme]]>, F <@r[S@c[cheme]]>)") +@Commandstring(fortran = "@PossiblyIndexedRef(K , + E <@c[fortran]>, F <@c[fortran]>)") +@Commandstring(algol = "@PossiblyIndexedRef(K , + E <@c[algol]>, F <@c[algol]>)") +@Commandstring(pascal = "@PossiblyIndexedRef(K , + E <@c[pascal]>, F <@c[pascal]>)") +@Commandstring(ada = "@PossiblyIndexedRef(K , + E <@c[ada]>, F <@c[ada]>)") +@Commandstring(apl = "@PossiblyIndexedRef(K , + E <@c[apl]>, F <@c[apl]>)") +@Commandstring(pl1 = "@PossiblyIndexedRef(K , + E <@c[pl/i]>, F <@c[pl/i]>)") +@Commandstring(clanguage = "@PossiblyIndexedRef(K , + E <@c[c]>, F <@c[c] language>)") +@Define(Smallitemize=Itemize, Spread 0.5, Above 0.5, Below 0.5) + +@Marker(Library, DBOLIO, File) + + +@Style(Justification Off) +@Style(Linewidth 7.2 Inches) +@Style(Indentation 0) +@Modify(Chapter,TitleForm +{@Begin(Hd1A, Below 0.8 in)@Skip(0.8 in)@*Chapter @Parm(Numbered)@*@Skip(0.3 in)@*@Parm(Title)@End(Hd1A)}, + ContentsForm +{@Begin(Tc1)@Rfstr(@Parm(Page))@Parm(Referenced)@.@ @Parm(Title)@End(Tc1)}, + Numbered [@1],IncrementedBy Use,Referenced [@1],Announced) +@Modify(Appendix,TitleEnv HD1,ContentsEnv tc1,Numbered [@A.], + ContentsForm "@Tc1(Appendix @Parm(Referenced)@.@ @Rfstr(@Parm(Page))@parm(Title))", + TitleForm "@Hd1(@=Appendix @Parm(Referenced)@*@=@Parm(Title))", + IncrementedBy,Referenced [@A],Announced) @Comment{Alias Chapter} +@Define(F, Capitalized) +@Define(A, Initialize "[APL]") +@Define(Sail = R) +@Define(B = R) +@Commandstring(Minussign = "-") +@Commandstring(Tilde = "~") +@Commandstring(Centerdot = ".") +@Commandstring(Underscore = "_") +@Commandstring(Bq = "@;`") +@Form(Complex = "@f[#C(@Parm(R) @Parm(I))]") +@Textform(Altmode = "$") +@Define(DenseDescription = Description, Spread 0) +@Define(Defenvironment = Text, Need 4, Nofill, Justification Off, Break, + Leftmargin +12, Indent -12, Above 2, Below 2, Spread 0.5) +@Define(Defbody = Text, Leftmargin -6in, Indent 0, Spread 0.5, Break) +@Define(Undefbody = Text, Justification Off, Leftmargin +6, Indent -12, + Above 0, Below 0, Group, Break) +@Define(Lispenvironment = Verbatim, Group, Leftmargin +.5in, Above 1, Below 1) +@Commandstring(Nopara = "@Begin(Text, Continue Force)@End(Text)") +@Commandstring(Optional = "@f[&optional]") +@Commandstring(Rest = "@f[&rest]") +@Commandstring(Key = "@f[&key]") +@Commandstring(allowotherkeys = "@f[&allow-other-keys]") +@Commandstring(Aux = "@f[&aux]") +@Commandstring(Body = "@f[&body]") +@Commandstring(Whole = "@f[&whole]") +@Commandstring(Special = "@f[&special]") +@Commandstring(Local = "@f[&local]") +@Textform(Mopt = "@r{[}@Parm(Text)@r{]}") +@Textform(Mgroup = "@r[{]@Parm(Text)@r[}]") +@Textform(Mstar = "@r[{]@Parm(Text)@r[}*]") +@Textform(Mplus = "@r[{]@Parm(Text)@r[}@+[+]]") +@Commandstring(Mor = "@r[|]") +@Commandstring(Lisp = "@Begin(Lispenvironment)@~") +@Commandstring(Endlisp = "@End(Lispenvironment)") +@Commandstring(Lispx = "@Begin(Text, Indent 0, Above 0)For example:@End(Text)@Lisp") +@Commandstring(EQ = "==") +@Commandstring(EV = "->") +@Commandstring(EX = "<=>") +@Commandstring(lbracket = "@f{[}") +@Commandstring(rbracket = "@f{]}") +@Commandstring(lbrace = "@f[{]") +@Commandstring(rbrace = "@f[}]") +@define(subi = i) +@define(subr = r) +@define(superi = i) +@define(supersail = sail) +@define(superg = g) +@commandstring(superminussign = "@minussign@;") +@commandstring(supercenterdot = "@centerdot@;") +@Commandstring(false = "@f[nil]") +@Commandstring(nil = "@f[nil]") +@Commandstring(empty = "@f[()]") +@Commandstring(true = "@f[t]") +@Commandstring(xlisp = "@r[L@c[isp]]") +@Commandstring(clisp = "@r[C@c[ommon]] @xlisp") +@Commandstring(lmlisp = "@PossiblyIndexedRef(K , + E <@r[Z@c[eta]@xlisp]>, F <@r[Z@c[eta]@xlisp]>)") +@Commandstring(newlisp = "@PossiblyIndexedRef(K , + E <@r[N@c[il]]>, F <@r[N@c[il] (New Implementation of @xlisp]>)") +@Commandstring(slisp = "@PossiblyIndexedRef(K , + E <@r[S@c[pice]] @xlisp>, F <@r[S@c[pice]] @xlisp>)") +@Commandstring(lisp15 = "@PossiblyIndexedRef(K , + E <@xlisp @r[1.5]>, F <@xlisp @r[1.5]>)") +@Commandstring(maclisp = "@PossiblyIndexedRef(K , + E <@r[M@c[ac]]@xlisp>, F <@r[M@c[ac]]@xlisp>)") +@Commandstring(franzlisp = "@PossiblyIndexedRef(K , + E <@r[F@c[ranz]] @xlisp>, F <@r[F@c[ranz]] @xlisp>)") +@Commandstring(interlisp = "@PossiblyIndexedRef(K , + E <@r[I@c[nter]]@xlisp>, F <@r[I@c[nter]]@xlisp>)") +@Commandstring(stdlisp = "@PossiblyIndexedRef(K , + E <@r[S@c[tandard]] @xlisp>, F <@r[S@c[tandard]] @xlisp>)") +@Commandstring(psl = "@PossiblyIndexedRef(K , + E <@r[P@c[ortable] S@c[tandard]] @xlisp>, F <@r[P@c[ortable] S@c[tandard]] @xlisp>)") +@Commandstring(s1lisp = "@PossiblyIndexedRef(K , + E <@r[S-1] @xlisp>, F <@r[S-1] @xlisp>)") +@Commandstring(scheme = "@PossiblyIndexedRef(K , + E <@c[scheme]>, F <@c[scheme]>)") +@Commandstring(fortran = "@PossiblyIndexedRef(K , + E <@c[fortran]>, F <@c[fortran]>)") +@Commandstring(algol = "@PossiblyIndexedRef(K , + E <@c[algol]>, F <@c[algol]>)") +@Commandstring(pascal = "@PossiblyIndexedRef(K , + E <@c[pascal]>, F <@c[pascal]>)") +@Commandstring(ada = "@PossiblyIndexedRef(K , + E <@c[ada]>, F <@c[ada]>)") +@Commandstring(apl = "@PossiblyIndexedRef(K , + E <@c[apl]>, F <@c[apl]>)") +@Commandstring(pl1 = "@PossiblyIndexedRef(K , + E <@c[pl/i]>, F <@c[pl/i]>)") +@Commandstring(clanguage = "@PossiblyIndexedRef(K , + E <@c[c]>, F <@c[c] language>)") +@Define(Smallitemize=Itemize, Spread 0, Above 0, Below 0) + +@Marker(Library, DBOLIO) + + +@Modify(Chapter,TitleForm +{@Begin(Hd1A, Below 0.8 in)@Skip(0.8 in)@*Chapter @Parm(Numbered)@*@Skip(0.3 in)@*@Parm(Title)@End(Hd1A)}, + ContentsForm +{@Begin(Tc1)@Rfstr(@Parm(Page))@Parm(Referenced)@.@ @Parm(Title)@End(Tc1)}, + Numbered [@1],IncrementedBy Use,Referenced [@1],Announced) +@Modify(Appendix,TitleEnv HD1,ContentsEnv tc1,Numbered [@A.], + ContentsForm "@Tc1(Appendix @Parm(Referenced)@.@ @Rfstr(@Parm(Page))@parm(Title))", + TitleForm "@Hd1(@=Appendix @Parm(Referenced)@*@=@Parm(Title))", + IncrementedBy,Referenced [@A],Announced) @Comment{Alias Chapter} +@Define(F, Capitalized) +@Define(A, Initialize "[APL]") +@Define(Sail = R) +@Define(B = R) +@Commandstring(Minussign = "-") +@Commandstring(Tilde = "~") +@Commandstring(Centerdot = ".") +@Commandstring(Underscore = "_") +@Commandstring(Bq = "@;`") +@Form(Complex = "@f[#C(@Parm(R) @Parm(I))]") +@Textform(Altmode = "") +@Define(DenseDescription = Description, Spread 0) +@Define(Defenvironment = Text, Need 4, Nofill, Justification Off, Break, + Leftmargin +12, Indent -12, Above 2, Below 2, Spread 0.5) +@Define(Defbody = Text, Leftmargin -6, Indent 0, Spread 0.5, Break) +@Define(Undefbody = Text, Justification Off, Leftmargin +6, Indent -12, + Above 0, Below 0, Group, Break) +@Define(Lispenvironment = Verbatim, Group, Leftmargin +4, Above 1, Below 1) +@Commandstring(Nopara = "@Begin(Text, Continue Force)@End(Text)") +@Commandstring(Optional = "@f[&optional]") +@Commandstring(Rest = "@f[&rest]") +@Commandstring(Key = "@f[&key]") +@Commandstring(allowotherkeys = "@f[&allow-other-keys]") +@Commandstring(Aux = "@f[&aux]") +@Commandstring(Body = "@f[&body]") +@Commandstring(Whole = "@f[&whole]") +@Commandstring(Special = "@f[&special]") +@Commandstring(Local = "@f[&local]") +@Textform(Mopt = "@r{[}@Parm(Text)@r{]}") +@Textform(Mgroup = "@r[{]@Parm(Text)@r[}]") +@Textform(Mstar = "@r[{]@Parm(Text)@r[}*]") +@Textform(Mplus = "@r[{]@Parm(Text)@r[}@+[+]]") +@Commandstring(Mor = "@r[|]") +@Commandstring(Lisp = "@Begin(Lispenvironment)@~") +@Commandstring(Endlisp = "@End(Lispenvironment)") +@Commandstring(Lispx = "@Begin(Text, Indent 0, Above 0)For example:@End(Text)@Lisp") +@Commandstring(EQ = "==") +@Commandstring(EV = "->") +@Commandstring(EX = "<=>") +@Commandstring(lbracket = "@f{[}") +@Commandstring(rbracket = "@f{]}") +@Commandstring(lbrace = "@f[{]") +@Commandstring(rbrace = "@f[}]") +@define(subi = i) +@define(subr = r) +@define(superi = i) +@define(supersail = sail) +@define(superg = g) +@commandstring(superminussign = "@minussign@;") +@commandstring(supercenterdot = "@centerdot@;") +@Commandstring(false = "@f[nil]") +@Commandstring(nil = "@f[nil]") +@Commandstring(empty = "@f[()]") +@Commandstring(true = "@f[t]") +@Commandstring(xlisp = "@r[L@c[isp]]") +@Commandstring(clisp = "@r[C@c[ommon]] @xlisp") +@Commandstring(lmlisp = "@PossiblyIndexedRef(K , + E <@r[Z@c[eta]@xlisp]>, F <@r[Z@c[eta]@xlisp]>)") +@Commandstring(newlisp = "@PossiblyIndexedRef(K , + E <@r[N@c[il]]>, F <@r[N@c[il] (New Implementation of @xlisp)]>)") +@Commandstring(slisp = "@PossiblyIndexedRef(K , + E <@r[S@c[pice]] @xlisp>, F <@r[S@c[pice]] @xlisp>)") +@Commandstring(lisp15 = "@PossiblyIndexedRef(K , + E <@xlisp @r[1.5]>, F <@xlisp @r[1.5]>)") +@Commandstring(maclisp = "@PossiblyIndexedRef(K , + E <@r[M@c[ac]]@xlisp>, F <@r[M@c[ac]]@xlisp>)") +@Commandstring(franzlisp = "@PossiblyIndexedRef(K , + E <@r[F@c[ranz]] @xlisp>, F <@r[F@c[ranz]] @xlisp>)") +@Commandstring(interlisp = "@PossiblyIndexedRef(K , + E <@r[I@c[nter]]@xlisp>, F <@r[I@c[nter]]@xlisp>)") +@Commandstring(stdlisp = "@PossiblyIndexedRef(K , + E <@r[S@c[tandard]] @xlisp>, F <@r[S@c[tandard]] @xlisp>)") +@Commandstring(psl = "@PossiblyIndexedRef(K , + E <@r[P@c[ortable] S@c[tandard]] @xlisp>, F <@r[P@c[ortable] S@c[tandard]] @xlisp>)") +@Commandstring(s1lisp = "@PossiblyIndexedRef(K , + E <@r[S-1] @xlisp>, F <@r[S-1] @xlisp>)") +@Commandstring(scheme = "@PossiblyIndexedRef(K , + E <@c[scheme]>, F <@c[scheme]>)") +@Commandstring(fortran = "@PossiblyIndexedRef(K , + E <@c[fortran]>, F <@c[fortran]>)") +@Commandstring(algol = "@PossiblyIndexedRef(K , + E <@c[algol]>, F <@c[algol]>)") +@Commandstring(pascal = "@PossiblyIndexedRef(K , + E <@c[pascal]>, F <@c[pascal]>)") +@Commandstring(ada = "@PossiblyIndexedRef(K , + E <@c[ada]>, F <@c[ada]>)") +@Commandstring(apl = "@PossiblyIndexedRef(K , + E <@c[apl]>, F <@c[apl]>)") +@Commandstring(pl1 = "@PossiblyIndexedRef(K , + E <@c[pl/i]>, F <@c[pl/i]>)") +@Commandstring(clanguage = "@PossiblyIndexedRef(K , + E <@c[c]>, F <@c[c] language>)") +@Define(Smallitemize=Itemize, Spread 0, Above 0, Below 0) diff --git a/docs/database/hem.lib b/docs/database/hem.lib new file mode 100644 index 0000000000000000000000000000000000000000..f3b5721e89c5bd1d263bf88d74badc0ab3ccad7f --- /dev/null +++ b/docs/database/hem.lib @@ -0,0 +1,71 @@ +@marker(library, Hem, Press, Dover, Postscript) + +@Form(Defhvar = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @hid[@Parm(Var)]@imbed(val, def <@ @ (initial value @f[@parm(val)])>)@>[@i[@Hemlock Variable]]@\@~ + @Send(FunList {@hid[@Parm(Var)] @>[@i[@Hemlock Variable]]@\})@~ + @HVxindex@Parmquote(Var)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-hvar}>, + undef <@Label{@Parm(Var)-hvar}>)')@~ + @Begin(Defbody)@Tabclear ") + +@Form(Defhvar1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @hid[@Parm(Var)]@imbed(val, def <@ @ (initial value @f[@parm(val)])>)@>[@i[@hemlock Variable]]@\@~ + @Send(FunList {@hid[@Parm(Var)] @>[@i[@hemlock Variable]]@\})@~ + @HVxindex@Parmquote(Var)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-hvar}>, + undef <@Label{@Parm(Var)-hvar}>)')@~ + @End(Undefbody)") + +@Commandstring(Enddefhvar = "@End(Defbody)@End(Defenvironment)") +@Textform(HVxindex = "@Xindex(T {Hemlock variable}, X {@Parm(Text)}, P {@hid[@Parm(Text)]})@'") +@Textform(HVindexref = "@Xindexref(T {Hemlock variable}, X {@Parm(Text)}, P {@hid[@Parm(Text)]})@'") +@Textform(HVarref = '@hid[@Parm(Text)] @~ + @r[(page @Pageref(@Parm(Text)-hvar))]@HVindexref@Parmquote(Text)') + + +@Form(Defcom = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @hid[@Parm(Com)]@imbed(bind, def <@ @ (bound to @bf[@parm(bind)])>)@~ + @imbed(Stuff, def <@ @ (@Parm(Stuff))>)@>[@i[Command]]@\@~ + @Send(FunList {@hid[@Parm(Com)] @>[@i[Command]]@\})@~ + @HCxindex@Parmquote(Com)@~ + @Imbed(Nolabel, undef '@Imbed(Comlabel, + def <@Label{@Parm(Comlabel)-com}>, + undef <@Label{@Parm(Com)-com}>)')@~ + @Begin(Defbody)@Tabclear ") + + +@Form(Defcom1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @hid[@Parm(Com)]@imbed(bind, def <@ @ (bound to @bf[@parm(bind)])>)@~ + @imbed(Stuff, def <@ @ (@Parm(Stuff))>)@>[@i[Command]]@\@~ + @Send(FunList {@hid[@Parm(Com)] @>[@i[Command]]@\})@~ + @HCxindex@Parmquote(Com)@~ + @Imbed(Nolabel, undef '@Imbed(Comlabel, + def <@Label{@Parm(Comlabel)-com}>, + undef <@Label{@Parm(Com)-com}>)')@~ + @End(Undefbody)") + + +@Commandstring(Enddefcom = "@End(Defbody)@End(Defenvironment)") +@Textform(HCxindex = "@Xindex(T {Command}, X {@Parm(Text)}, P {@hid[@Parm(Text)]})@'") +@Textform(HCindexref = "@Xindexref(T {Command}, X {@Parm(Text)}, P {@hid[@Parm(Text)]})@'") +@Textform(Comref = '@hid[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-com))]@HCindexref@Parmquote(Text)') + + +@String(IndexFuns "Yes") +@string(supresskeyindex "Yes") + +@specialfont(f1 = helvetica) +@commandstring(Hemlock = "@f1(Hemlock)") +@textform(hid = '@w{@f1{@parm(Text)}}') +@commandstring[emacs = "@f1"] + +@specialfont(f2 = "BookmanDemi") +@textform +@textform + +@commandstring[llisp = "L@c(isp)"] + +@commandstring[windows = "X windows"] diff --git a/docs/database/outtir.lib b/docs/database/outtir.lib new file mode 100644 index 0000000000000000000000000000000000000000..428d1a416142c743844dab57252b9c1f6ef5769b --- /dev/null +++ b/docs/database/outtir.lib @@ -0,0 +1,329 @@ +@Comment{Version of BOLIO.LIB that provides a single unified index +instead of separate indexes for functions, variables, constants, +keywords, and concepts.} + +@Marker(Library, Uttir, Press, Dover, Postscript) + +@Libraryfile(DBolio) + + +@Comment{Constraints on the definition of IndexEnvironment: + Indent = - LeftMargin + (Linewidth + Leftmargin + Columnmargin) * Columns - Columnmargin + = global line width +} +@Define(IndexEnvironment, Boxed, Columns 2, Columnmargin 0.5in, + Linewidth 2.7in, Leftmargin +0.3in, Indent -0.3in) + +@Define(KeySpreadEnvironment, Facecode F) +@Textform(SpreadKeys = "@KeySpreadEnvironment<@SpreadKeysLoop@Parm(Text)[]>") +@Textform(SpreadKeysLoop = + "@String(SpreadKeysTemp = @Parmquote(Text))@Case(SpreadKeysTemp, + null <>, else < :@Parm(Text)@SpreadKeysLoop>)") + +@Form(IndexKeys = "@Textform(IndexKeysLoop = + '@String(IndexKeysTemp = @Quote{@Parmquote(Text)})@Case(IndexKeysTemp, + null <>, else [@Kindex2(P = @Quote{@Parmquote(Text)}, + S = {for@f( @Parm(Fun))})@IndexKeysLoop])')@IndexKeysLoop@Parm(Keys)[]@~") + +@Counter(UnNumberedIndex,TitleEnv HD1A,ContentsEnv tc1,Announced,Alias Chapter) + +@Textform(IndexHeadings = "@UnnumberedIndex@Parmquote(Text) +@String(ChapterTitle = @Parmquote(Text)) +@Pageheading(Immediate, Left = <>, Right = <>) +@Pagefooting(Immediate, Center = <@Value(Page)>) +@Pageheading(Odd, Left = <@c[@Value(ChapterTitle)]>, Right = <@Value(Page)>) +@Pageheading(Even, Left = <@Value(Page)>, Right = <@c[@Value(ReportTitle)]>) +@Pagefooting(Center = <>) +") + +@Textform(Incompatibility = "@Begin(Quotation, Font Smallbodyfont, Indent +0, Spread 0.5, need 4) + @b[Compatibility note:] @Index(@b[Compatibility note]) + @Parm(Text) @End(Quotation)") +@Textform(Implementation = "@Begin(Quotation, Font Smallbodyfont, Indent +0, Spread 0.5, need 4) + @b[Implementation note:] @Index(@b[Implementation note]) + @Parm(Text) @End(Quotation)") +@Textform(Rationale = "@Begin(Quotation, Font Smallbodyfont, Indent +0, Spread 0.5, need 4) + @b[Rationale:] @Index(@b[Rationale]) @Parm(Text) @End(Quotation)") +@Comment{ +@Textform(Query = "@Begin(Quotation, Font Smallbodyfont, Indent +0, Spread 0.5, need 4) + @f[???] @b[Query:] @Index(@b[Query]) @Parm(Text) @End(Quotation)") +} + +@Textform(Kwd = "@f[:@Parm(Text)]") +@Textform(KeywordList = " + @Begin(Description) + @Textform(Keyword = '@Begin(Multiple)@~ +@f[:@Quote<@Parm(Text)> ]@\@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote<@Parm(Text)>-kwd}@~') + @Textform(FirstKeyword = '@Begin(Multiple)@~ +@f[:@Quote<@Parm(Text)> @r[or] ]@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote<@Parm(Text)>-kwd}@~') + @Textform(NextKeyword = '@~ +@f[:@Quote<@Parm(Text)> @r[or] ]@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote<@Parm(Text)>-kwd}@~') + @Textform(LastKeyword = '@~ +@f[:@Quote<@Parm(Text)> ]@\@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote<@Parm(Text)>-kwd}@~') + @Textform(SubKeywordList = ' + @Begin(Description) + @Textform(SubKeyword = <@Begin(Multiple)@~ +@f[:@Quote{@Quote[@Parm(Text)]} ]@\@Kindex2(P = @Quote{@Quote[@Parmquote(Text)]}, + S = {for@f[ @Quote[@Parm(Text)] ]option @~ + to@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote[@Parm(Text)]-@Quote{@Quote[@Parm(Text)]}-kwd}@~>)') +") +@Textform(PseudoKeyword = "@Begin(Multiple)@Parm(Text) @\@~") +@Commandstring(Endkeyword = "@End(Multiple)") +@Commandstring(Endkeywordlist = "@End(Description)") +@Textform(PseudoSubKeyword = "@Begin(Multiple)@Parm(Text) @\@~") +@Commandstring(EndSubKeyword = "@End(Multiple)") +@Commandstring(EndSubKeywordlist = "@End(Description)") +@Textform(RandomKeywordList = " + @Begin(Description) + @Index2(P = {Keywords}, S = {for@f[ ]@Parm(Text)}) + @Textform(RandomKeyword = '@Begin(Multiple) +@f[:@Quote<@Parm(Text)> ]@\@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ ]@Parm(Text)})@~') +") +@Textform(PseudoRandomKeyword = "@Begin(Multiple)@Parm(Text) @\@~") +@Commandstring(Endrandomkeyword = "@End(Multiple)") +@Commandstring(Endrandomkeywordlist = "@End(Description)") + +@Form(Defvar = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Imbed(Nostar, undef '*')@Parm(Var)@Imbed(Nostar, undef '*')] @>[@i[Variable]]@\@~ + @Send(FunList {@f[@Imbed(Nostar, undef '*')@Parm(Var)@Imbed(Nostar, undef '*')] @>[@i[Variable]]@\})@~ + @Imbed(Nostar, undef<@Vindex@Parmquote(Var)>, def<@Vxindex@Parmquote(Var)>)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-var}>, + undef <@Label{@Parm(Var)-var}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defvar1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Imbed(Nostar, undef '*')@Parm(Var)@Imbed(Nostar, undef '*')] @>[@i[Variable]]@\@~ + @Send(FunList {@f[@Imbed(Nostar, undef '*')@Parm(Var)@Imbed(Nostar, undef '*')] @>[@i[Variable]]@\})@~ + @Imbed(Nostar, undef<@Vindex@Parmquote(Var)>, def<@Vxindex@Parmquote(Var)>)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-var}>, + undef <@Label{@Parm(Var)-var}>)')@~ + @End(Undefbody)") +@Commandstring(Enddefvar = "@End(Defbody)@End(Defenvironment)") + +@Form(Defcon = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Parm(Var)] @>[@i[Constant]]@\@~ + @Send(FunList {@f[@Parm(Var)] @>[@i[Constant]]@\})@~ + @Conindex@Parmquote(Var)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-con}>, + undef <@Label{@Parm(Var)-con}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defcon1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Parm(Var)] @>[@i[Constant]]@\@~ + @Send(FunList {@f[@Parm(Var)] @>[@i[Constant]]@\})@~ + @Conindex@Parmquote(Var)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-con}>, + undef <@Label{@Parm(Var)-con}>)')@~ + @End(Undefbody)") +@Commandstring(Enddefcon = "@End(Defbody)@End(Defenvironment)") + +@Form(Defun = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Parm(Fun) @Parm(Args, default <>)@Imbed(Keys, def < @key@!@Spreadkeys(@Parm(Keys))>)] @>[@i[Function]]@\ +@Imbed(MoreKeys, def <@/@Spreadkeys(@Parm(MoreKeys)) +>)@~ +@Imbed(YetMoreKeys, def <@/@Spreadkeys(@Parm(YetMoreKeys)) +>)@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)@Imbed(Keys, + def < @key@!@Spreadkeys(@Parm(Keys))>)] @>[@i[Function]]@\@Imbed(MoreKeys, + def < +@/@f[@Spreadkeys(@Parm(MoreKeys))]>)@Imbed(YetMoreKeys, + def < +@/@f[@Spreadkeys(@Parm(YetMoreKeys))]>)})@~ + @Findex@Parmquote(Fun)@~ + @Imbed(SuppressKeyIndex, undef < + @Imbed(Keys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(Keys))')@~ + @Imbed(MoreKeys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(MoreKeys))')@~ + @Imbed(YetMoreKeys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(YetMoreKeys))')>)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-fun}>, + undef <@Label{@Parm(Fun)-fun}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defun1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Parm(Fun) @Parm(Args, default <>)@Imbed(Keys, def < @key@!@Spreadkeys(@Parm(Keys))>)] @>[@i[Function]]@\ +@Imbed(MoreKeys, def <@/@Spreadkeys(@Parm(MoreKeys)) +>)@~ +@Imbed(YetMoreKeys, def <@/@Spreadkeys(@Parm(YetMoreKeys)) +>)@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)@Imbed(Keys, + def < @key@!@Spreadkeys(@Parm(Keys))>)] @>[@i[Function]]@\@Imbed(MoreKeys, + def < +@/@f[@Spreadkeys(@Parm(MoreKeys))]>)@Imbed(YetMoreKeys, + def < +@/@f[@Spreadkeys(@Parm(YetMoreKeys))]>)})@~ + @Findex@Parmquote(Fun)@~ + @Imbed(SuppressKeyIndex, undef < + @Imbed(Keys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(Keys))')@~ + @Imbed(MoreKeys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(MoreKeys))')@~ + @Imbed(YetMoreKeys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(YetMoreKeys))')>)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def [@Label{@Parm(Funlabel)-fun}], + undef [@Label{@Parm(Fun)-fun}])')@~ + @End(Undefbody)") +@Commandstring(Enddefun = "@End(Defbody)@End(Defenvironment)") + +@Form(Defmac = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Macro]]@\@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Macro]]@\})@~ + @Mindex@Parmquote(Fun)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-mac}>, + undef <@Label{@Parm(Fun)-mac}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defmac1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Macro]]@\@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Macro]]@\})@~ + @Mindex@Parmquote(Fun)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-mac}>, + undef <@Label{@Parm(Fun)-mac}>)')@~ + @End(Undefbody)") +@Commandstring(Enddefmac = "@End(Defbody)@End(Defenvironment)") + +@Form(Defspec = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Special form]]@\@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Special form]]@\})@~ + @Sindex@Parmquote(Fun)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-spec}>, + undef <@Label{@Parm(Fun)-spec}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defspec1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Special form]]@\@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Special form]]@\})@~ + @Sindex@Parmquote(Fun)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-spec}>, + undef <@Label{@Parm(Fun)-spec}>)')@~ + @End(Undefbody)") +@Commandstring(Enddefspec = "@End(Defbody)@End(Defenvironment)") + +@Textform(Xindent = "@hsp(2.0em)") +@Textform(Xsepr = "@hsp(0.5em)") +@Textform(Def = "@Index@Parmquote(Text)@i@Parmquote(Text)") +@Textform(Index = "@Indexentry{Key {@Parm(Text) !1}, + Entry {@r[@Parm(Text)]@Xsepr()}, Number {@r[@Parmvalue(Page)]}}@'") +@Form(PossiblyIndexedRef = "@Indexentry{Key {@Parm(K) !1}, + Entry {@r[@Parm(F)]@Xsepr()}, Number {@r[@Parmvalue(Page)]}}@Parm(E)") +@Textform(Index1 = "@Indexentry{Key {@Parm(Text) !1}, + Entry {@r[@Parm(Text)]@Xsepr()}}@'") +@Form(Index2 = "@Index1@Parmquote(P)@Indexentry{Key {@Parm(P) !2 @Parm(S)}, + Entry {@Xindent()@r[@Parm(S)]@Xsepr()}, Number {@Parmvalue(Page)}}@'") +@Form(Seealso = "@Index1@Parmquote(P)@Indexentry{Key {@Parm(P) !3 @Parm(S)}, + Entry {@Xindent()@Xindent()@r[See also:]}, Number @Parmquote(S)}@'") +@Form(Xindex = "@Indexentry{Key {@Parm(X) @Parm(T) 1}, + Entry {@Parm(Q, default <>)@Parm(P)@Parm(R, default <>)@f[ ]@r[@Parm(T)]@Xsepr()}, + Number {@b[@Parmvalue(Page)]}}@'") +@Form(Xindexref = "@Indexentry{Key {@Parm(X) @Parm(T) 1}, + Entry {@Parm(Q, default <>)@Parm(P)@Parm(R, default <>)@f[ ]@r[@Parm(T)]@Xsepr()}, + Number {@Parmvalue(Page)}}@'") +@Form(Xindex1 = "@Indexentry{Key {@Parm(X) @Parm(T) 1}, + Entry {@Parm(Q, default <>)@Parm(P)@Parm(R, default <>)@f[ ]@r[@Parm(T)]@Xsepr()}}@'") +@Form(Xindex2 = "@Xindex1(X @Parmquote(X), T @Parmquote(T), + P @Parmquote(P)@Imbed(Q, def <, Q @Parmquote(Q)>)@Imbed(R, def <, R @Parmquote(R)>))@~ + @Indexentry{Key {@Parm(X) @Parm(T) 2 @Parm(S)}, + Entry {@Xindent()@Parm(S)@Xsepr()}, + Number {@b[@Parmvalue(Page)]}}@'") +@Form(Xindexref2 = "@Xindex1(X @Parmquote(X), T @Parmquote(T), + P @Parmquote(P)@Imbed(Q, def <, Q @Parmquote(Q)>)@Imbed(R, def <, R @Parmquote(R)>))@~ + @Indexentry{Key {@Parm(X) @Parm(T) 2 @Parm(S)}, + Entry {@Xindent()@Parm(S)@Xsepr()}, + Number {@Parmvalue(Page)}}@'") +@Form(Xseealso = "@Xindex1(X @Parmquote(X), T @Parmquote(T), + P @Parmquote(P)@Imbed(Q, def <, Q @Parmquote(Q)>)@Imbed(R, def <, R @Parmquote(R)>))@~ + @Indexentry{Key {@Parm(X) @Parm(T) 3 @Parm(S)}, + Entry {@Xindent()@Xindent()@r[See also:]}, + Number @Parmquote(S)}}@'") + +@Textform(Cindex = "@Index@Parmquote(Text)@'") + +@Textform(Findex = "@Case(IndexFuns, Yes '@Xindex(T {function}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Textform(Findexref = "@Case(IndexFuns, Yes '@Xindexref(T {function}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Form(Findex2 = "@Case(IndexFuns, Yes '@Xindex2(T {function}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") +@Form(Fseealso = "@Case(IndexFuns, Yes '@Xseealso(T {function}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") + +@Textform(Mindex = "@Case(IndexFuns, Yes '@Xindex(T {macro}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Textform(Mindexref = "@Case(IndexFuns, Yes '@Xindexref(T {macro}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Form(Mindex2 = "@Case(IndexFuns, Yes '@Xindex2(T {macro}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") +@Form(Mseealso = "@Case(IndexFuns, Yes '@Xseealso(T {macro}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") + +@Textform(Sindex = "@Case(IndexFuns, Yes '@Xindex(T {special form}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Textform(Sindexref = "@Case(IndexFuns, Yes '@Xindexref(T {special form}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Form(Sindex2 = "@Case(IndexFuns, Yes '@Xindex2(T {special form}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") +@Form(Sseealso = "@Case(IndexFuns, Yes '@Xseealso(T {special form}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") + +@Textform(Vindex = "@Xindex(T {variable}, X {@Parm(Text)}, P {@f[@Parm(Text)]}, Q {@f[*]}, R{@f[*]})@'") +@Textform(Vxindex = "@Xindex(T {variable}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Textform(Vindexref = "@Xindexref(T {variable}, X {@Parm(Text)}, P {@f[@Parm(Text)]}, Q {@f[*]}, R{@f[*]})@'") +@Form(Vindex2 = "@Xindex2(T {variable}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S), Q {@f[*]}, R{@f[*]})@'") +@Form(Vseealso = "@Xseealso(T {variable}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S), Q {@f[*]}, R{@f[*]})@'") + +@Textform(Conindex = "@Xindex(T {constant}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Textform(Conindexref = "@Xindexref(T {constant}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Form(Conindex2 = "@Xindex2(T {constant}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") +@Form(Conseealso = "@Xseealso(T {constant}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") + +@Textform(Kindex = "@Xindex(T {keyword}, X {@Parm(Text)}, P {@f[@Parm(Text)]}, Q {@f[:]})@'") +@Textform(Kindexref = "@Xindexref(T {keyword}, X {@Parm(Text)}, P {@f[@Parm(Text)]}, Q {@f[:]})@'") +@Form(Kindex2 = "@Xindex2(T {keyword}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S), Q {@f[:]})@'") +@Form(Kindexref2 = "@Xindexref2(T {keyword}, X {@Parm(K)}, P {@f[@Parm(K)]}, S @Parmquote(S), Q {@f[:]})@'") +@Form(Kseealso = "@Xseealso(T {keyword}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S), Q {@f[:]})@'") + +@Textform(Declindex = "@Xindex(T {declaration}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Textform(Declindexref = "@Xindexref(T {declaration}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Form(Declindex2 = "@Xindex2(T {declaration}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") +@Form(Declseealso = "@Xseealso(T {declaration}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") + +@Textform(Typeindex = "@Xindex(T {type specifier}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Textform(Typeindexref = "@Xindexref(T {type specifier}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Form(Typeindex2 = "@Xindex2(T {type specifier}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") +@Form(Typeseealso = "@Xseealso(T {type specifier}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") + +@Textform(Funref = "@f[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-fun))]@Findexref@Parmquote(Text)@;") +@Textform(Funreftab = "@f[@Parm(Text) ]@\@r[(page @Pageref(@Parm(Text)-fun))]@Findexref@Parmquote(Text)@;") +@Form(Xfunref = "@f[@Parm(X) ]@~ + @r[(page @Pageref(@Parm(L)-fun))]@Findexref@Parmquote(X)@;") +@Textform(Macref = "@f[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-mac))]@Mindexref@Parmquote(Text)@;") +@Textform(Macreftab = "@f[@Parm(Text) ]@\@r[(page @Pageref(@Parm(Text)-mac))]@Mindexref@Parmquote(Text)@;") +@Form(Xmacref = "@f[@Parm(X) ]@~ + @r[(page @Pageref(@Parm(L)-mac))]@Mindexref@Parmquote(X)@;") +@Textform(Specref = "@f[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-spec))]@Sindexref@Parmquote(Text)@;") +@Textform(Specreftab = "@f[@Parm(Text) ]@\@r[(page @Pageref(@Parm(Text)-spec))]@Sindexref@Parmquote(Text)@;") +@Form(XSpecref = "@f[@Parm(X) ]@~ + @r[(page @Pageref(@Parm(L)-spec))]@Sindexref@Parmquote(X)@;") +@Textform(Var = "@f[*@Parm(Text)*]") +@Textform(Varref = "@f[*@Parm(Text)* ]@~ + @r[(page @Pageref(@Parm(Text)-var))]@Vindexref@Parmquote(Text)@;") +@Form(Xvarref = "@f[*@Parm(X)* ]@~ + @r[(page @Pageref(@Parm(L)-var))]@Vindexref@Parmquote(X)@;") +@Textform(Conref = "@f[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-con))]@Conindexref@Parmquote(Text)@;") +@Form(Xconref = "@f[@Parm(X) ]@~ + @r[(page @Pageref(@Parm(L)-con))]@Conindexref@Parmquote(X)@;") +@Form(Kwdref = "@f[:@Parm(K) ]@~ + @r[(page @Pageref(@Parm(F)-@Parm(K)-kwd))]@Kindexref2(S {for@f[ @Parm(F)]}, + K @Parmquote(K))@;") +@Form(SubKwdref = "@f[:@Parm(K)] @~ + @r[(page @Pageref(@Parm(F)-@Parm(O)-@Parm(K)-kwd))]@~ + @Kindexref2(S {for@f[ :@Parm(O) ]option to@f[ @Parm(F)]}, + K @Parmquote(K))") + +@Indexentry(Key { 1}, Entry {@IndexHeadings[Index]@Blankspace(1)@Begin(IndexEnvironment)}) +@Indexentry(Key {~~~ }, Entry {@End(IndexEnvironment)}) + +@Textform(IndexCleanup = " ") diff --git a/docs/database/spice.lib b/docs/database/spice.lib new file mode 100644 index 0000000000000000000000000000000000000000..a6520620e2c2b17d1fe1359e1183eac8639f30b7 --- /dev/null +++ b/docs/database/spice.lib @@ -0,0 +1,67 @@ +@Marker(Library, Spice, Press, Dover, Postscript) + +@libraryfile(table) +@modify(HD3, above 0.3inch, below 0.15inch) +@modify(HD2, below 0.25inch) +@modify[HD4, above .3inch, below 0, break before] +@modify(paragraph, + ContentsForm=" ", + TitleForm="@HD4[@parm(title)@hsp(1quad)]", + numbered [], referenced []) +@textform(mline="@enter(liner,script +5pts)@tabclear()@&@ @\@leave(liner)") + +@define(twocolumn,columns 2,columnmargin 1cm,spacing 12pts,boxed, + linewidth 3inch) + +@form(SpiceTitlePage={ +@begin(titlepage) +@begin(titlebox,centered) +@majorheading(CARNEGIE-MELLON UNIVERSITY) +@heading(DEPARTMENT@ OF@ COMPUTER@ SCIENCE) +@heading[SPICE@ PROJECT] +@begin(format, leftmargin +1.10inch, rightmargin +1.10inch) +@mline() +@begin(center, spacing 1) +@heading[@Parm(Title)] + +@Parm(Author) + +@Parm(Date,default="@value(date)") +@end(center) +@mline() +@end(format) +@end(titlebox) + +@Parm(cruft, default="") + +@begin(center,spacing 1.3, above .5inch) +Keywords and index categories: @Parm(Index,default="") +Location of machine-readable File: @parm(File,default="@value(Manuscript) @@ @value(site)") +@end(center) + +@Copyrightnotice(@Parm(Copyrightholder, default )) +@begin(researchcredit) +@imbed( internal, +def "This is an internal working document of the Computer Science + Department, Carnegie-Mellon University, Schenley Park, Pittsburgh, + PA 15213. Some of the ideas expressed in this document may be + only partially developed or erroneous. Distribution of this document + outside the immediate working community is discouraged; publication + of this document is forbidden.") + +Supported by the Defense Advanced Research Projects Agency, Department +of Defense, ARPA Order 3597, monitored by the Air Force Avionics +Laboratory under contract F33615-78-C-1551. The views +and conclusions contained in this document are those of the authors +and should not be interpreted as representing the official policies, +either expressed or implied, of the Defense Advanced Research +Projects Agency or the U.S. Government. +@end(researchcredit) +@end(titlepage) + +@newpage + +@comment[ Leave larger blank area at top of first page ] +@format[ @blankspace( 1inch ) ] +}) @comment[ End of SpiceTitlePage form ] + @Marker(Library,Spice) diff --git a/docs/database/spicer.fon b/docs/database/spicer.fon new file mode 100644 index 0000000000000000000000000000000000000000..5244c3d385c5055d57c5a2cf953e15144938166c --- /dev/null +++ b/docs/database/spicer.fon @@ -0,0 +1,230 @@ +@Marker(Font,SpiceRoman10,Press) +@DefineFont(BodyFont, + R=, + I=, + B=, + P=, + F=, + C=, + Z=, + G=, + Y=, + X=, + U=, + A=, + T=, + K=, + L=, + M=, + N=) + +@comment(Shrunk: 14-12,10-9,9-8,8-7,6-5 except K and G) +@DefineFont(SmallBodyFont, + R=, + B=, + I=, + P=, + G=, + F=, + Z=, + Y=, + X=, + U=, + A=, + C=, + T=, + K=, + L=, + M=, + N=) @Comment{GLS 5/17/81: AllArrows7R nonexistent} + +@DefineFont(LargeBodyFont, + R=, + I=, + B=, + C=) +@DefineFont(SquareRootFont, + R=) + +@DefineFont(TitleFont1, + R=, + I=, + F=, + C=) +@DefineFont(TitleFont2, + R=, + I=, + F=, + C=) +@DefineFont(TitleFont3, + R=, + I=, + F=, + C=) +@DefineFont(TitleFont4, + R=, + I=, + F=, + C=) +@DefineFont(TitleFont5, + R=, + I=, + C=) + +@Equate(DisplayFont=BodyFont,TitleFont0=BodyFont,HeadingFont=TitleFont3) + @Marker(Font,SpiceRoman11,Press) +@DefineFont(BodyFont, + R=, + I=, + B=, + P=, + F=, + C=, + Z=, + G=, + Y=, + X=, + U=, + A=, + T=) + +@DefineFont(SmallBodyFont, + R=, + B=, + I=, + P=, + G=, + F=, + Z=, + Y=, + X=, + C=, + U=, + A=, + T=) + +@DefineFont(TitleFont1, + R=, + I=, + C=) +@DefineFont(TitleFont2, + R=, + I=, + C=) +@DefineFont(TitleFont3, + R=, + I=, + C=) +@DefineFont(TitleFont4, + R=, + I=, + C=) +@DefineFont(TitleFont5, + R=, + I=, + C=) + +@Equate(DisplayFont=BodyFont,TitleFont0=BodyFont,HeadingFont=TitleFont3) + @Marker(Font,SpiceRoman12,Press) +@DefineFont(BodyFont, + R=, + I=, + B=, + P=, + F=, + C=, + Z=, + G=, + Y=, + X=, + U=, + T=) + +@DefineFont(SmallBodyFont, + R=, + B=, + I=, + P=, + G=, + F=, + Z=, + Y=, + X=, + C=, + U=, + A=, + T=) + +@DefineFont(TitleFont1, + R=, + I=, + C=) +@DefineFont(TitleFont2, + R=, + I=, + C=) +@DefineFont(TitleFont3, + R=, + I=, + C=) +@DefineFont(TitleFont4, + R=, + I=, + C=) +@DefineFont(TitleFont5, + R=, + I=, + C=) + +@Equate(DisplayFont=BodyFont,TitleFont0=BodyFont,HeadingFont=TitleFont3) + @Marker(Font,SpiceRoman14,Press) +@DefineFont(BodyFont, + R=, + I=, + B=, + P=, + F=, + C=, + Z=, + G=, + Y=, + X=, + U=, + T=) + +@DefineFont(SmallBodyFont, + R=, + B=, + I=, + P=, + G=, + F=, + Z=, + Y=, + X=, + C=, + U=, + T=) + +@DefineFont(TitleFont1, + R=, + I=, + C=) +@DefineFont(TitleFont2, + R=, + I=, + C=) +@DefineFont(TitleFont3, + R=, + I=, + C=) +@DefineFont(TitleFont4, + R=, + I=, + C=) +@DefineFont(TitleFont5, + R=, + I=, + C=) + +@Equate(DisplayFont=BodyFont,TitleFont0=BodyFont,HeadingFont=TitleFont3) diff --git a/docs/database/table.lib b/docs/database/table.lib new file mode 100644 index 0000000000000000000000000000000000000000..0d572fe8305de202a7683bdeaef38f62b1d567e1 --- /dev/null +++ b/docs/database/table.lib @@ -0,0 +1,27 @@ +@Marker(Library,Table,Dover,Postscript) + +@Comment{New library definitions for tables.} + +@Style(StringMax=10000) +@Modify(Table, Spacing 1) + +@Define(Liner, underline all, tabexport off, script -1pt, + rightmargin +1pts, leftmargin +2pts) +@Define(up6, script +6pts) + +@textform(VB={@string(NID = "@parm(text)")@case(NID, + 1 "@hsp(1pt)@ovp[|]@up6[|]", + 2 "@hsp(1pt)@ovp[|]@up6[|]@>@ovp[|]@up6[|]@\", + 3 "@hsp(1pt)@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@\", + 4 "@hsp(1pt)@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@\", + 5 "@hsp(1pt)@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@\", + 6 "@hsp(1pt)@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@\", + 7 "@hsp(1pt)@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@>@ovp[|]@up6[|]@\", + else "@hsp(1pt)@ovp[|]@up6[|]")}) + +@Form(TLine="@enter(Liner, script @parm)@~ + @tabclear()@&@ @\@leave(Liner)") +@TextForm(BLine="@ovp<@Liner(@tabclear()@&@ @\)>@VB(@parm(text))") +@TextForm(TL="@hsp(1)@parm(text)@>@ovp[|]@up6[|]@\") +@TextForm(TC="@=@parm(text)@>@ovp[|]@up6[|]@\") +@TextForm(TR="@>@parm(text)@hsp(1)@ovp[|]@up6[|]@\") diff --git a/docs/database/uttir.lib b/docs/database/uttir.lib new file mode 100644 index 0000000000000000000000000000000000000000..63bdfdca67ef1eb5f48026028813ec89f5890ac4 --- /dev/null +++ b/docs/database/uttir.lib @@ -0,0 +1,329 @@ +@Comment{Version of BOLIO.LIB that provides a single unified index +instead of separate indexes for functions, variables, constants, +keywords, and concepts.} + +@Marker(Library, Uttir, Press, Dover, Postscript) + +@Libraryfile(DBolio) + + +@Comment{Constraints on the definition of IndexEnvironment: + Indent = - LeftMargin + (Linewidth + Leftmargin + Columnmargin) * Columns - Columnmargin + = global line width +} +@Define(IndexEnvironment, Boxed, Columns 2, Columnmargin 0.5in, + Linewidth 2.7in, Leftmargin +0.3in, Indent -0.3in) + +@Define(KeySpreadEnvironment, Facecode F) +@Textform(SpreadKeys = "@KeySpreadEnvironment<@SpreadKeysLoop@Parm(Text)[]>") +@Textform(SpreadKeysLoop = + "@String(SpreadKeysTemp = @Parmquote(Text))@Case(SpreadKeysTemp, + null <>, else < :@Parm(Text)@SpreadKeysLoop>)") + +@Form(IndexKeys = "@Textform(IndexKeysLoop = + '@String(IndexKeysTemp = @Quote{@Parmquote(Text)})@Case(IndexKeysTemp, + null <>, else [@Kindex2(P = @Quote{@Parmquote(Text)}, + S = {for@f( @Parm(Fun))})@IndexKeysLoop])')@IndexKeysLoop@Parm(Keys)[]@~") + +@Counter(UnNumberedIndex,TitleEnv HD1A,ContentsEnv tc1,Announced,Alias Chapter) + +@Textform(IndexHeadings = "@UnnumberedIndex@Parmquote(Text) +@String(ChapterTitle = @Parmquote(Text)) +@Pageheading(Immediate, Left = <>, Right = <>) +@Pagefooting(Immediate, Center = <@Value(Page)>) +@Pageheading(Odd, Left = <@c[@Value(ChapterTitle)]>, Right = <@Value(Page)>) +@Pageheading(Even, Left = <@Value(Page)>, Right = <@c[@Value(ReportTitle)]>) +@Pagefooting(Center = <>) +") + +@Textform(Incompatibility = "@Begin(Quotation, Font Smallbodyfont, Indent +0, Spread 0.5, need 4) + @b[Compatibility note:] @Index(@b[Compatibility note]) + @Parm(Text) @End(Quotation)") +@Textform(Implementation = "@Begin(Quotation, Font Smallbodyfont, Indent +0, Spread 0.5, need 4) + @b[Implementation note:] @Index(@b[Implementation note]) + @Parm(Text) @End(Quotation)") +@Textform(Rationale = "@Begin(Quotation, Font Smallbodyfont, Indent +0, Spread 0.5, need 4) + @b[Rationale:] @Index(@b[Rationale]) @Parm(Text) @End(Quotation)") +@Comment{ +@Textform(Query = "@Begin(Quotation, Font Smallbodyfont, Indent +0, Spread 0.5, need 4) + @f[???] @b[Query:] @Index(@b[Query]) @Parm(Text) @End(Quotation)") +} + +@Textform(Kwd = "@f[:@Parm(Text)]") +@Textform(KeywordList = " + @Begin(Description) + @Textform(Keyword = '@Begin(Multiple)@~ +@f[:@Quote<@Parm(Text)> ]@\@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote<@Parm(Text)>-kwd}@~') + @Textform(FirstKeyword = '@Begin(Multiple)@~ +@f[:@Quote<@Parm(Text)> @r[or] ]@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote<@Parm(Text)>-kwd}@~') + @Textform(NextKeyword = '@~ +@f[:@Quote<@Parm(Text)> @r[or] ]@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote<@Parm(Text)>-kwd}@~') + @Textform(LastKeyword = '@~ +@f[:@Quote<@Parm(Text)> ]@\@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote<@Parm(Text)>-kwd}@~') + @Textform(SubKeywordList = ' + @Begin(Description) + @Textform(SubKeyword = <@Begin(Multiple)@~ +@f[:@Quote{@Quote[@Parm(Text)]} ]@\@Kindex2(P = @Quote{@Quote[@Parmquote(Text)]}, + S = {for@f[ @Quote[@Parm(Text)] ]option @~ + to@f[ @Parm(Text)]})@~ + @Label{@Parm(Text)-@Quote[@Parm(Text)]-@Quote{@Quote[@Parm(Text)]}-kwd}@~>)') +") +@Textform(PseudoKeyword = "@Begin(Multiple)@Parm(Text) @\@~") +@Commandstring(Endkeyword = "@End(Multiple)") +@Commandstring(Endkeywordlist = "@End(Description)") +@Textform(PseudoSubKeyword = "@Begin(Multiple)@Parm(Text) @\@~") +@Commandstring(EndSubKeyword = "@End(Multiple)") +@Commandstring(EndSubKeywordlist = "@End(Description)") +@Textform(RandomKeywordList = " + @Begin(Description) + @Index2(P = {Keywords}, S = {for@f[ ]@Parm(Text)}) + @Textform(RandomKeyword = '@Begin(Multiple) +@f[:@Quote<@Parm(Text)> ]@\@Kindex2(P = @Quote<@Parmquote(Text)>, + S = {for@f[ ]@Parm(Text)})@~') +") +@Textform(PseudoRandomKeyword = "@Begin(Multiple)@Parm(Text) @\@~") +@Commandstring(Endrandomkeyword = "@End(Multiple)") +@Commandstring(Endrandomkeywordlist = "@End(Description)") + +@Form(Defvar = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Imbed(Nostar, undef '*')@Parm(Var)@Imbed(Nostar, undef '*')] @>[@i[Variable]]@\@~ + @Send(FunList {@f[@Imbed(Nostar, undef '*')@Parm(Var)@Imbed(Nostar, undef '*')] @>[@i[Variable]]@\})@~ + @Imbed(Nostar, undef<@Vindex@Parmquote(Var)>, def<@Vxindex@Parmquote(Var)>)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-var}>, + undef <@Label{@Parm(Var)-var}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defvar1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Imbed(Nostar, undef '*')@Parm(Var)@Imbed(Nostar, undef '*')] @>[@i[Variable]]@\@~ + @Send(FunList {@f[@Imbed(Nostar, undef '*')@Parm(Var)@Imbed(Nostar, undef '*')] @>[@i[Variable]]@\})@~ + @Imbed(Nostar, undef<@Vindex@Parmquote(Var)>, def<@Vxindex@Parmquote(Var)>)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-var}>, + undef <@Label{@Parm(Var)-var}>)')@~ + @End(Undefbody)") +@Commandstring(Enddefvar = "@End(Defbody)@End(Defenvironment)") + +@Form(Defcon = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Parm(Var)] @>[@i[Constant]]@\@~ + @Send(FunList {@f[@Parm(Var)] @>[@i[Constant]]@\})@~ + @Conindex@Parmquote(Var)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-con}>, + undef <@Label{@Parm(Var)-con}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defcon1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Parm(Var)] @>[@i[Constant]]@\@~ + @Send(FunList {@f[@Parm(Var)] @>[@i[Constant]]@\})@~ + @Conindex@Parmquote(Var)@~ + @Imbed(Nolabel, undef '@Imbed(Varlabel, + def <@Label{@Parm(Varlabel)-con}>, + undef <@Label{@Parm(Var)-con}>)')@~ + @End(Undefbody)") +@Commandstring(Enddefcon = "@End(Defbody)@End(Defenvironment)") + +@Form(Defun = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Parm(Fun) @Parm(Args, default <>)@Imbed(Keys, def < @key@!@Spreadkeys(@Parm(Keys))>)] @>[@i[Function]]@\ +@Imbed(MoreKeys, def <@/@Spreadkeys(@Parm(MoreKeys)) +>)@~ +@Imbed(YetMoreKeys, def <@/@Spreadkeys(@Parm(YetMoreKeys)) +>)@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)@Imbed(Keys, + def < @key@!@Spreadkeys(@Parm(Keys))>)] @>[@i[Function]]@\@Imbed(MoreKeys, + def < +@/@f[@Spreadkeys(@Parm(MoreKeys))]>)@Imbed(YetMoreKeys, + def < +@/@f[@Spreadkeys(@Parm(YetMoreKeys))]>)})@~ + @Findex@Parmquote(Fun)@~ + @Imbed(SuppressKeyIndex, undef < + @Imbed(Keys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(Keys))')@~ + @Imbed(MoreKeys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(MoreKeys))')@~ + @Imbed(YetMoreKeys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(YetMoreKeys))')>)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-fun}>, + undef <@Label{@Parm(Fun)-fun}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defun1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Parm(Fun) @Parm(Args, default <>)@Imbed(Keys, def < @key@!@Spreadkeys(@Parm(Keys))>)] @>[@i[Function]]@\ +@Imbed(MoreKeys, def <@/@Spreadkeys(@Parm(MoreKeys)) +>)@~ +@Imbed(YetMoreKeys, def <@/@Spreadkeys(@Parm(YetMoreKeys)) +>)@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)@Imbed(Keys, + def < @key@!@Spreadkeys(@Parm(Keys))>)] @>[@i[Function]]@\@Imbed(MoreKeys, + def < +@/@f[@Spreadkeys(@Parm(MoreKeys))]>)@Imbed(YetMoreKeys, + def < +@/@f[@Spreadkeys(@Parm(YetMoreKeys))]>)})@~ + @Findex@Parmquote(Fun)@~ + @Imbed(SuppressKeyIndex, undef < + @Imbed(Keys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(Keys))')@~ + @Imbed(MoreKeys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(MoreKeys))')@~ + @Imbed(YetMoreKeys, def '@IndexKeys(Fun = @Parmquote(Fun), Keys = @Parmquote(YetMoreKeys))')>)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def [@Label{@Parm(Funlabel)-fun}], + undef [@Label{@Parm(Fun)-fun}])')@~ + @End(Undefbody)") +@Commandstring(Enddefun = "@End(Defbody)@End(Defenvironment)") + +@Form(Defmac = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Parm(Fun) @Parm(Args, default <>)] @>[@i[Macro]]@\@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Macro]]@\})@~ + @Mindex@Parmquote(Fun)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-mac}>, + undef <@Label{@Parm(Fun)-mac}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defmac1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Parm(Fun) @Parm(Args, default <>)] @>[@i[Macro]]@\@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Macro]]@\})@~ + @Mindex@Parmquote(Fun)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-mac}>, + undef <@Label{@Parm(Fun)-mac}>)')@~ + @End(Undefbody)") +@Commandstring(Enddefmac = "@End(Defbody)@End(Defenvironment)") + +@Form(Defspec = "@Begin(Defenvironment)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Parm(Fun) @Parm(Args, default <>)] @>[@i[Special form]]@\@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Special form]]@\})@~ + @Sindex@Parmquote(Fun)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-spec}>, + undef <@Label{@Parm(Fun)-spec}>)')@~ + @Begin(Defbody)@Tabclear ") +@Form(Defspec1 = "@Begin(Undefbody)@Comment(Tabset?)@~ + @f[@Imbed(Package, def '@Parm(Package):')@Parm(Fun) @Parm(Args, default <>)] @>[@i[Special form]]@\@~ + @Send(FunList {@f[@Parm(Fun) @Parm(Args, default <>)] @>[@i[Special form]]@\})@~ + @Sindex@Parmquote(Fun)@~ + @Imbed(Nolabel, undef '@Imbed(Funlabel, + def <@Label{@Parm(Funlabel)-spec}>, + undef <@Label{@Parm(Fun)-spec}>)')@~ + @End(Undefbody)") +@Commandstring(Enddefspec = "@End(Defbody)@End(Defenvironment)") + +@Textform(Xindent = "@hsp(2.0em)") +@Textform(Xsepr = "@hsp(0.5em)") +@Textform(Def = "@Index@Parmquote(Text)@i@Parmquote(Text)") +@Textform(Index = "@Indexentry{Key {@Parm(Text) !1}, + Entry {@r[@Parm(Text)]@Xsepr()}, Number {@r[@Parmvalue(Page)]}}@'") +@Form(PossiblyIndexedRef = "@Indexentry{Key {@Parm(K) !1}, + Entry {@r[@Parm(F)]@Xsepr()}, Number {@r[@Parmvalue(Page)]}}@Parm(E)") +@Textform(Index1 = "@Indexentry{Key {@Parm(Text) !1}, + Entry {@r[@Parm(Text)]@Xsepr()}}@'") +@Form(Index2 = "@Index1@Parmquote(P)@Indexentry{Key {@Parm(P) !2 @Parm(S)}, + Entry {@Xindent()@r[@Parm(S)]@Xsepr()}, Number {@Parmvalue(Page)}}@'") +@Form(Seealso = "@Index1@Parmquote(P)@Indexentry{Key {@Parm(P) !3 @Parm(S)}, + Entry {@Xindent()@Xindent()@r[See also:]}, Number @Parmquote(S)}@'") +@Form(Xindex = "@Indexentry{Key {@Parm(X) @Parm(T) 1}, + Entry {@Parm(Q, default <>)@Parm(P)@Parm(R, default <>)@f[ ]@r[@Parm(T)]@Xsepr()}, + Number {@b[@Parmvalue(Page)]}}@'") +@Form(Xindexref = "@Indexentry{Key {@Parm(X) @Parm(T) 1}, + Entry {@Parm(Q, default <>)@Parm(P)@Parm(R, default <>)@f[ ]@r[@Parm(T)]@Xsepr()}, + Number {@Parmvalue(Page)}}@'") +@Form(Xindex1 = "@Indexentry{Key {@Parm(X) @Parm(T) 1}, + Entry {@Parm(Q, default <>)@Parm(P)@Parm(R, default <>)@f[ ]@r[@Parm(T)]@Xsepr()}}@'") +@Form(Xindex2 = "@Xindex1(X @Parmquote(X), T @Parmquote(T), + P @Parmquote(P)@Imbed(Q, def <, Q @Parmquote(Q)>)@Imbed(R, def <, R @Parmquote(R)>))@~ + @Indexentry{Key {@Parm(X) @Parm(T) 2 @Parm(S)}, + Entry {@Xindent()@Parm(S)@Xsepr()}, + Number {@b[@Parmvalue(Page)]}}@'") +@Form(Xindexref2 = "@Xindex1(X @Parmquote(X), T @Parmquote(T), + P @Parmquote(P)@Imbed(Q, def <, Q @Parmquote(Q)>)@Imbed(R, def <, R @Parmquote(R)>))@~ + @Indexentry{Key {@Parm(X) @Parm(T) 2 @Parm(S)}, + Entry {@Xindent()@Parm(S)@Xsepr()}, + Number {@Parmvalue(Page)}}@'") +@Form(Xseealso = "@Xindex1(X @Parmquote(X), T @Parmquote(T), + P @Parmquote(P)@Imbed(Q, def <, Q @Parmquote(Q)>)@Imbed(R, def <, R @Parmquote(R)>))@~ + @Indexentry{Key {@Parm(X) @Parm(T) 3 @Parm(S)}, + Entry {@Xindent()@Xindent()@r[See also:]}, + Number @Parmquote(S)}}@'") + +@Textform(Cindex = "@Index@Parmquote(Text)@'") + +@Textform(Findex = "@Case(IndexFuns, Yes '@Xindex(T {function}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Textform(Findexref = "@Case(IndexFuns, Yes '@Xindexref(T {function}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Form(Findex2 = "@Case(IndexFuns, Yes '@Xindex2(T {function}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") +@Form(Fseealso = "@Case(IndexFuns, Yes '@Xseealso(T {function}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") + +@Textform(Mindex = "@Case(IndexFuns, Yes '@Xindex(T {macro}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Textform(Mindexref = "@Case(IndexFuns, Yes '@Xindexref(T {macro}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Form(Mindex2 = "@Case(IndexFuns, Yes '@Xindex2(T {macro}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") +@Form(Mseealso = "@Case(IndexFuns, Yes '@Xseealso(T {macro}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") + +@Textform(Sindex = "@Case(IndexFuns, Yes '@Xindex(T {special form}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Textform(Sindexref = "@Case(IndexFuns, Yes '@Xindexref(T {special form}, X {@Parm(Text)}, P {@f[@Parm(Text)]})')@'") +@Form(Sindex2 = "@Case(IndexFuns, Yes '@Xindex2(T {special form}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") +@Form(Sseealso = "@Case(IndexFuns, Yes '@Xseealso(T {special form}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))')@'") + +@Textform(Vindex = "@Xindex(T {variable}, X {@Parm(Text)}, P {@f[@Parm(Text)]}, Q {@f[*]}, R{@f[*]})@'") +@Textform(Vxindex = "@Xindex(T {variable}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Textform(Vindexref = "@Xindexref(T {variable}, X {@Parm(Text)}, P {@f[@Parm(Text)]}, Q {@f[*]}, R{@f[*]})@'") +@Form(Vindex2 = "@Xindex2(T {variable}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S), Q {@f[*]}, R{@f[*]})@'") +@Form(Vseealso = "@Xseealso(T {variable}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S), Q {@f[*]}, R{@f[*]})@'") + +@Textform(Conindex = "@Xindex(T {constant}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Textform(Conindexref = "@Xindexref(T {constant}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Form(Conindex2 = "@Xindex2(T {constant}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") +@Form(Conseealso = "@Xseealso(T {constant}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") + +@Textform(Kindex = "@Xindex(T {keyword}, X {@Parm(Text)}, P {@f[@Parm(Text)]}, Q {@f[:]})@'") +@Textform(Kindexref = "@Xindexref(T {keyword}, X {@Parm(Text)}, P {@f[@Parm(Text)]}, Q {@f[:]})@'") +@Form(Kindex2 = "@Xindex2(T {keyword}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S), Q {@f[:]})@'") +@Form(Kindexref2 = "@Xindexref2(T {keyword}, X {@Parm(K)}, P {@f[@Parm(K)]}, S @Parmquote(S), Q {@f[:]})@'") +@Form(Kseealso = "@Xseealso(T {keyword}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S), Q {@f[:]})@'") + +@Textform(Declindex = "@Xindex(T {declaration}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Textform(Declindexref = "@Xindexref(T {declaration}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Form(Declindex2 = "@Xindex2(T {declaration}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") +@Form(Declseealso = "@Xseealso(T {declaration}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") + +@Textform(Typeindex = "@Xindex(T {type specifier}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Textform(Typeindexref = "@Xindexref(T {type specifier}, X {@Parm(Text)}, P {@f[@Parm(Text)]})@'") +@Form(Typeindex2 = "@Xindex2(T {type specifier}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") +@Form(Typeseealso = "@Xseealso(T {type specifier}, X {@Parm(P)}, P {@f[@Parm(P)]}, S @Parmquote(S))@'") + +@Textform(Funref = "@f[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-fun))]@Findexref@Parmquote(Text)@;") +@Textform(Funreftab = "@f[@Parm(Text) ]@\@r[(page @Pageref(@Parm(Text)-fun))]@Findexref@Parmquote(Text)@;") +@Form(Xfunref = "@f[@Parm(X) ]@~ + @r[(page @Pageref(@Parm(L)-fun))]@Findexref@Parmquote(X)@;") +@Textform(Macref = "@f[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-mac))]@Mindexref@Parmquote(Text)@;") +@Textform(Macreftab = "@f[@Parm(Text) ]@\@r[(page @Pageref(@Parm(Text)-mac))]@Mindexref@Parmquote(Text)@;") +@Form(Xmacref = "@f[@Parm(X) ]@~ + @r[(page @Pageref(@Parm(L)-mac))]@Mindexref@Parmquote(X)@;") +@Textform(Specref = "@f[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-spec))]@Sindexref@Parmquote(Text)@;") +@Textform(Specreftab = "@f[@Parm(Text) ]@\@r[(page @Pageref(@Parm(Text)-spec))]@Sindexref@Parmquote(Text)@;") +@Form(XSpecref = "@f[@Parm(X) ]@~ + @r[(page @Pageref(@Parm(L)-spec))]@Sindexref@Parmquote(X)@;") +@Textform(Var = "@f[*@Parm(Text)*]") +@Textform(Varref = "@f[*@Parm(Text)* ]@~ + @r[(page @Pageref(@Parm(Text)-var))]@Vindexref@Parmquote(Text)@;") +@Form(Xvarref = "@f[*@Parm(X)* ]@~ + @r[(page @Pageref(@Parm(L)-var))]@Vindexref@Parmquote(X)@;") +@Textform(Conref = "@f[@Parm(Text) ]@~ + @r[(page @Pageref(@Parm(Text)-con))]@Conindexref@Parmquote(Text)@;") +@Form(Xconref = "@f[@Parm(X) ]@~ + @r[(page @Pageref(@Parm(L)-con))]@Conindexref@Parmquote(X)@;") +@Form(Kwdref = "@f[:@Parm(K) ]@~ + @r[(page @Pageref(@Parm(F)-@Parm(K)-kwd))]@Kindexref2(S {for@f[ @Parm(F)]}, + K @Parmquote(K))@;") +@Form(SubKwdref = "@f[:@Parm(K)] @~ + @r[(page @Pageref(@Parm(F)-@Parm(O)-@Parm(K)-kwd))]@~ + @Kindexref2(S {for@f[ :@Parm(O) ]option to@f[ @Parm(F)]}, + K @Parmquote(K))") + +@Indexentry(Key { 1}, Entry {@IndexHeadings[Index]@Blankspace(1)@Begin(IndexEnvironment)}) +@Indexentry(Key {~~~ }, Entry {@End(IndexEnvironment)}) + +@Textform(IndexCleanup = " ") diff --git a/docs/doc-diff.lisp b/docs/doc-diff.lisp new file mode 100644 index 0000000000000000000000000000000000000000..7f232bb68f2d9ec72bfa50a76ef747258a3fc47e --- /dev/null +++ b/docs/doc-diff.lisp @@ -0,0 +1,384 @@ +;;; -*- Package: Hemlock -*- +;;; +;;; A hack to compare the functions and variables defined by the hemlock +;;; documents with the ones defined in the core. +;;; +;;; Use GROVEL-LABELS. +;;; + +(in-package "HEMLOCK") + + +(defvar *defined-labels* (make-hash-table :test #'equal)) + +;;; Ignore these because they would be internal (not for the user) if Hemlock +;;; had that kind of definition power. +;;; +(defvar *hvars-to-ignore* + '(auto-save-state current-package draft-information headers-buffer + headers-information message-buffer message-information spell-information + default-message-modeline-fields current-compile-server current-eval-server)) + +(defvar *cmds-to-ignore* + '("Beginning Of Parse" "Echo Area Backward Character" + "Echo Area Backward Word" "Echo Area Delete Previous Character" + "Echo Area Kill Previous Word" "Do Nothing" "Illegal" "Insert Parse Default" + "Italic Comment Mode" "Kill Parse" "Lisp Insert )" "Next Parse" + "Previous Parse" "Start Italic Comment" "Insert ()" "Move over )" + "Current Compile Server" "Current Eval Server" "Defhvar" "Defindent")) + +;;; These do not get removed from *defined-labels* because they are not +;;; command names, variable names, or "HI" function names. These are now +;;; documented in the Command Implementor's Manual, but we don't want to call +;;; FIND-UNDOCUMENTED-SYMBOLS on these packages due to all the uninteresting +;;; symbols they hold. In the case of routines defined in the "ED" package, +;;; they aren't exported anyway. +;;; +;;; Do not add names to this list that occur in the ED package and have +;;; asterisks (e.g., specials like *kill-ring* and *last-search-string*). Use +;;; the variable below, *unimplemented-strings-to-ignore*. +;;; +(defvar *unimplemented-to-ignore* + '(spell:spell-try-word spell:maybe-read-spell-dictionary spell:spell-root-word + spell:max-entry-length spell:spell-read-dictionary + spell:spell-collect-close-words spell:correct-spelling + spell:spell-add-entry spell:spell-remove-entry spell:spell-root-flags + + ext:translate-character ext:define-keyboard-modifier + ext:define-mouse-code ext:translate-mouse-character ext:define-keysym + + dired:find-file dired:make-directory dired:delete-file + dired:pathnames-from-pattern dired:copy-file dired:rename-file + + get-search-pattern current-mark file-compile kill-characters + indent-region-for-commands display-page-directory previous-buffer + sentence-offset interactive buffer-default-pathname + add-definition-dir-translation push-buffer-mark do-active-group + paragraph-offset word-offset create-slave make-region-undo + process-file-options pre-command-parse-check top-level-offset fill-region + pop-buffer-mark region-eval get-current-compile-server mark-top-level-form + ed page-directory find-file-buffer deactivate-region valid-spot + buffer-history kill-region string-eval backward-up-list + define-file-type-hook buffer-history check-region-query-size + change-to-buffer region-compile current-region mark-paragraph form-offset + check-region-active read-buffer-file fill-region-by-paragraphs + forward-up-list define-file-option buffer-mark region-active-p + inside-defun-p activate-region start-defun-p delete-buffer-if-possible + get-current-eval-server goto-page write-buffer-file save-for-undo + eval-form-in-server indent-region in-lisp pathname-to-buffer-name + page-offset defun-region delete-definition-dir-translation + delete-horizontal-space supply-generic-pointer-up-function)) + +;;; This is just like *unimplemented-to-ignore*, but these names are hard to +;;; deal with in *unimplemented-to-ignore* due to one of the following reasons: +;;; Scribe, +;;; The name is an example and truly unimplemented, or +;;; The name has asterisks in core but not in the Scribe label name. +;;; +(defvar *unimplemented-strings-to-ignore* + '("SAMPLECOMMAND" "SAMPLEVARIABLE" + + "MARK-GTR" "MARK-NEQ" "MARK-LSS" "MARK-LEQ" "MARK-GEQ" "MARK-EQL" + "LINE-GEQ" "LINE-LSS" "LINE-GTR" "LINE-LEQ" + + "KILL-RING" "LAST-SEARCH-STRING" "EPHEMERALLY-ACTIVE-COMMAND-TYPES" + "HEMLOCK-BEEP" "LAST-SEARCH-PATTERN" "ACTIVE-FILE-GROUP" + + "ERROR-FUNCTION" "REPORT-FUNCTION" "UPDATE-DEFAULT" "YESP-FUNCTION" + "CLOBBER-DEFAULT" "RECURSIVE-DEFAULT")) + + +(defun grovel-labels (aux-files output-file) + "Read each of the files in the list Aux-Files to find what commands are + documented, then compare it with the commands defined in core. We write + documentation forms to the output-file for things defined but not documented, + and we put a list of things documented but not implemented in a comment." + (clrhash *defined-labels*) + (dolist (labels-file aux-files) + (with-open-file (s labels-file :direction :input) + (loop + (let ((l (read-line s nil nil))) + (unless l (return)) + (multiple-value-bind (kind label) + (parse-label l) + (when kind + (let ((old (gethash label *defined-labels*))) + (when (and old + (not (eq old :hemlock-variable)) + (not (eq kind :hemlock-variable))) + (format t "~S multiply defined as ~S and ~S.~%" + label old kind)) + (setf (gethash label *defined-labels*) kind)))))))) + (with-open-file (s output-file :direction :output + :if-exists :new-version) + (map-undocumented-hemlock-things *command-names* :command s + #'document-command *cmds-to-ignore*) + (terpri s) + (map-undocumented-hemlock-things *global-variable-names* :hemlock-variable s + #'document-variable *hvars-to-ignore*) + (terpri s) + (find-undocumented-symbols "HEMLOCK-INTERNALS" s) + (terpri s) + (write-line "@begin[comment]" s) + (let ((ignored-symbols (copy-list *unimplemented-to-ignore*)) + (ignored-strings (copy-list *unimplemented-strings-to-ignore*))) + (maphash #'(lambda (name type) + (cond ((member name ignored-symbols + :test #'string= :key #'symbol-name) + (setf ignored-symbols + (delete name ignored-symbols + :test #'string= :key #'symbol-name))) + ((member name ignored-strings :test #'string=) + (setf ignored-strings + (delete name ignored-strings :test #'string=))) + (t + (format s "~A ~S is not implemented.~%" type name)))) + *defined-labels*) + (when ignored-symbols + (format s + "~&******************* These ignored \"unimplemented\" symbols ~ + were not used.~%~S~%********************~2%" + ignored-symbols)) + (when ignored-strings + (format s + "~&******************* These ignored \"unimplemented\" strings ~ + were not used.~%~S~%********************~2%" + ignored-strings))) + (write-line "@end[comment]" s) + (values))) + + +;;; Iterate over a string table, checking that each thing has a corresponding +;;; label of the specified kind. If there is no label, then call the function +;;; with the value and stream. If the label is the wrong kind, print a comment +;;; on Stream before calling the function. We also blast the label so we will +;;; know that it was defined. +;;; +(defun map-undocumented-hemlock-things (table kind stream function ignore-stuff) + (do-strings (string value table) + (let* ((lab (nstring-upcase (remove #\space string))) + (lkind (gethash lab *defined-labels*))) + (cond ((and (eq kind :command) + (member (command-name value) ignore-stuff + :test #'string-equal)) + (setf ignore-stuff + (remove (command-name value) ignore-stuff + :test #'string-equal))) + ((member value ignore-stuff) + (setf ignore-stuff (remove value ignore-stuff))) + (t + (unless (eq lkind kind) + (when lkind + (format stream + "@comment{~S documented as a ~A, ~ + but defined as a ~A.}~2%" + string lkind kind)) + (funcall function value stream)))) + (remhash lab *defined-labels*))) + (when ignore-stuff + (format stream + "~&******************** These ignored ~Ss were not used.~%~ + ~S~%********************~2%" + kind ignore-stuff))) + + + +(defvar *undocumented-symbols-to-ignore* + '(make-xwindow-like-hwindow mark/= default-font input-waiting mark= + modify-kbdmac-stream delete-line-font-marks font-mark hemlock-output-stream + command reprompt store-cut-string make-kbdmac-stream window window-font + delete-font-mark fetch-cut-string fun-defined-from-pathname + hemlock-region-stream line< buffer mark< move-font-mark + editor-describe-function enter-window-autoraise ring mark<= search-pattern + *print-region* mark>= string-table line mark> line> line>= line<= + after-editor-initializations *invoke-hook* defhvar)) + +(defun find-undocumented-symbols (package stream) + (let ((ignore-symbols *undocumented-symbols-to-ignore*)) + (do-external-symbols (sym package) + (let* ((name (string-trim "*" (symbol-name sym))) + (kind (gethash name *defined-labels*))) + (ecase kind + ((nil) + (if (member sym ignore-symbols) + (setf ignore-symbols (remove sym ignore-symbols)) + (let ((*standard-output* stream)) + ;; Bind this to squelch CLOS/DESCRIBE bad interaction. + (describe sym) + (terpri) + (terpri)))) + ((:function :macro :special-form) + (let ((def (cond ((macro-function sym) :macro) + ((special-form-p sym) :special-form) + ((fboundp sym) :function)))) + (unless (eq kind def) + (format stream + "@comment{~S is ~:[not defined~;~:*defined as a ~A~]~ + , but is documented as a ~A}~%" sym def kind)))) + (:constant + (unless (constantp sym) + (format stream + "@comment{~S is documented as a constant, but isn't ~ + defined.}~%" + sym))) + (:variable + (unless (or (get sym 'lisp::globally-special) + (string= name (symbol-name sym))) + (format stream + "@comment{~S is documented as a special, but isn't ~ + declared.}~%" + sym)))) + (remhash name *defined-labels*))) + (when ignore-symbols + (format stream + "~&******************** These ignored symbols were not used.~%~ + ~S~%********************~2%" + ignore-symbols)))) + + +(defvar *suffix-codes* (make-hash-table :test #'equal)) +(setf (gethash "COM" *suffix-codes*) :command) +(setf (gethash "HVAR" *suffix-codes*) :hemlock-variable) +(setf (gethash "FUN" *suffix-codes*) :function) +(setf (gethash "MAC" *suffix-codes*) :macro) +(setf (gethash "SPEC" *suffix-codes*) :special-form) +(setf (gethash "VAR" *suffix-codes*) :variable) +(setf (gethash "CON" *suffix-codes*) :constant) + + +;;; Parse a line from a Scribe .Aux file, returning the kind of the thing +;;; documented and its name. +;;; +(defun parse-label (entry) + (let* ((end (search "), Value" entry :start2 28)) + (hpos (position #\- entry :start 28 :end end :from-end t))) + (if hpos + (let* ((suffix (subseq entry (1+ hpos) end)) + (found (gethash suffix *suffix-codes*))) + (if found + (values found (subseq entry 28 hpos)) + (values nil nil))) + (values nil nil)))) + + +(defun document-command (command stream) + (format stream "@defcom[com ~S" (command-name command)) + (let ((binds (command-bindings command))) + (when binds + (format stream ", bind (") + (print-command-bindings binds stream) + (format stream ")")) + (format stream "]~%~A~%@enddefcom~2%" + (command-documentation command)))) + + +(defun document-variable (var stream) + (let* ((name (variable-name var :global)) + (len (length name))) + (unless (string= name "Mode Hook" :start1 (- len 9)) + (format stream "@defhvar[var ~S~@[, val {~(~S~)}~]]~%~A~%@enddefhvar~2%" + name (variable-value var :global) + (variable-documentation var :global))))) + + +(defvar *definition-pattern* + (new-search-pattern :string-insensitive :forward " +@def")) + +(defvar *insert-pattern* + (new-search-pattern :string-insensitive :backward " + +")) + +(defvar *definition-macros* (make-hash-table :test #'equal)) +(setf (gethash "COM" *definition-macros*) :command) +(setf (gethash "HVAR" *definition-macros*) :hemlock-variable) +(setf (gethash "UN" *definition-macros*) :function) +(setf (gethash "MAC" *definition-macros*) :macro) +(setf (gethash "SPEC" *definition-macros*) :special-form) +(setf (gethash "VAR" *definition-macros*) :variable) +(setf (gethash "CON" *definition-macros*) :constant) +(setf (gethash "COM1" *definition-macros*) :command) +(setf (gethash "HVAR1" *definition-macros*) :hemlock-variable) +(setf (gethash "UN1" *definition-macros*) :function) +(setf (gethash "MAC1" *definition-macros*) :macro) +(setf (gethash "SPEC1" *definition-macros*) :special-form) +(setf (gethash "VAR1" *definition-macros*) :variable) +(setf (gethash "CON1" *definition-macros*) :constant) + +(defun parse-doc-macro (line) + (let* ((bracket (or (position #\[ line) + (error "No opening #\[ ???"))) + (name (nstring-upcase (subseq line 4 bracket))) + (kind (gethash name *definition-macros*)) + (nend (case (char line (+ bracket 5)) + (#\" + (position #\" line :start (+ bracket 6))) + (#\{ + (position #\} line :start (+ bracket 6))) + (t nil)))) + (cond ((not kind) + (format t "Unknown definition macro:~%~A~%" line) + (values nil nil)) + ((not nend) + (format t "Can't parse name:~%~A~%" line) + (values nil nil)) + (t + (values kind (subseq line (+ bracket 6) nend)))))) + + +(defun annotate-with-online-documentation (input-file output-file) + "Take a Scribe input file and produce a Scribe output file with the online + documentation for each thing inserted before the offline documentation." + (let* ((temp-buffer (make-buffer "Annotate Temporary")) + (point (buffer-point temp-buffer))) + (unwind-protect + (progn + (read-file input-file point) + (buffer-start point) + (loop + (unless (find-pattern point *definition-pattern*) + (return)) + (line-offset point 1) + (multiple-value-bind + (kind name) + (parse-doc-macro (line-string (mark-line point))) + (when kind + (with-mark ((insert point :left-inserting)) + (unless (find-pattern insert *insert-pattern*) + (buffer-start insert)) + (line-offset insert 2 0) + (with-output-to-mark (stream insert :full) + (ecase kind + ((:function :macro :special-form :constant) + (format stream "@begin[format]~%") + (let ((*standard-output* stream)) + (describe (intern (string-upcase name)))) + (format stream "~&@end[format]~2%")) + (:variable + (format stream "@begin[format]~%") + (let ((*standard-output* stream)) + (describe (intern (concatenate 'string "*" + (string-upcase name) + "*")))) + (format stream "~&@end[format]~2%")) + (:command + (let ((command (getstring name *command-names*))) + (when command + (format stream "@begin[verse]~%Command @hid[~A]: (" + (command-name command)) + (print-command-bindings (command-bindings command) + stream) + (format stream ")~%@end[verse]~%~A~2&" + (command-documentation command))))) + (:hemlock-variable + (let ((var (getstring name *global-variable-names*))) + (when var + (format stream "@begin[verse]~%Variable @hid[~A]: ~ + (~(~S~))~%@end[verse]~%~A~2&" + (variable-name var :global) + (variable-value var :global) + (variable-documentation var :global)))))) + ))))) + (write-file (buffer-region temp-buffer) output-file)) + (delete-buffer temp-buffer)))) diff --git a/docs/hem/cim/aux-sys.mss b/docs/hem/cim/aux-sys.mss new file mode 100644 index 0000000000000000000000000000000000000000..15e215eaf9fe1c5bc17bfdbe49c68eba2c3c8eae --- /dev/null +++ b/docs/hem/cim/aux-sys.mss @@ -0,0 +1,682 @@ +@comment{-*- Dictionary: /usr/lisp/scribe/hem/hem; Mode: spell; Package: Hemlock; Log: /usr/lisp/scribe/hem/hem-docs.log -*-} +@chapter (Auxiliary Systems) +This chapter describes utilities that some implementations of @hemlock may +leave unprovided or unsupported. + + +@section[Key-events] +@index(I/O) +@index[keyboard input] +@index[input, keyboard] +@index[mouse input] +@index[input, mouse] +@label[key-events] + +These routines are defined in the @f["EXTENSIONS"] package since other projects +have often used @hemlock's input translations for interfacing to CLX. + + +@subsection[Introduction] + +The canonical representation of editor input is a key-event structure. Users +can bind commands to keys (see section @ref[key-bindings]), which are non-zero +length sequences of key-events. A key-event consists of an identifying token +known as a @i[keysym] and a field of bits representing modifiers. Users define +keysyms, integers between 0 and 65535 inclusively, by supplying names that +reflect the legends on their keyboard's keys. Users define modifier names +similarly, but the system chooses the bit and mask for recognizing the +modifier. You can use keysym and modifier names to textually specify +key-events and Hemlock keys in a @f[#k] syntax. The following are some +examples: +@begin[programexample] + #k"C-u" + #k"Control-u" + #k"c-m-z" + #k"control-x meta-d" + #k"a" + #k"A" + #k"Linefeed" +@end[programexample] +This is convenient for use within code and in init files containing +@f[bind-key] calls. + +The @f[#k] syntax is delimited by double quotes, but the system parses the +contents rather than reading it as a Common Lisp string. Within the double +quotes, spaces separate multiple key-events. A single key-event optionally +starts with modifier names terminated by hyphens. Modifier names are +alphabetic sequences of characters which the system uses case-insensitively. +Following modifiers is a keysym name, which is case-insensitive if it consists +of multiple characters, but if the name consists of only a single character, +then it is case-sensitive. + +You can escape special characters @dash hyphen, double quote, open angle +bracket, close angle bracket, and space @dash with a backslash, and you can +specify a backslash by using two contiguously. You can use angle brackets to +enclose a keysym name with many special characters in it. Between angle +brackets appearing in a keysym name position, there are only two special +characters, the closing angle bracket and backslash. + + + +@subsection[Interface] + +All of the following routines and variables are exported from the "EXTENSIONS" +("EXT") package. + + +@defun[fun {define-keysym}, args {@i[keysym] @i[preferred-name] @rest @i[other-names]}] +This function establishes a mapping from @i[preferred-name] to @i[keysym] for +purposes of @f[#k] syntax. @i[Other-names] also map to @i[keysym], but the +system uses @i[preferred-name] when printing key-events. The names are +case-insensitive simple-strings; however, if the string contains a single +character, then it is used case-sensitively. Redefining a keysym or re-using +names has undefined effects. + +You can use this to define unused keysyms, but primarily this defines keysyms +defined in the @i[X Window System Protocol, MIT X Consortium Standard, X +Version 11, Release 4]. @f[translate-key-event] uses this knowledge to +determine what keysyms are modifier keysyms and what keysym stand for +alphabetic key-events. +@enddefun + + +@defun[fun {define-mouse-keysym}, args {@i[button] @i[keysym] @i[name] @i[shifted-bit] @i[event-key]}] +This function defines @i[keysym] named @i[name] for key-events representing the +X @i[button] cross the X @i[event-key] (@kwd[button-press] or +@kwd[button-release]). @i[Shifted-bit] is a defined modifier name that +@f[translate-mouse-key-event] sets in the key-event it returns whenever the X +shift bit is set in an incoming event. + +Note, by default, there are distinct keysyms for each button distinguishing +whether the user pressed or released the button. + +@i[Keysym] should be an one unspecified in @i[X Window System Protocol, MIT X +Consortium Standard, X Version 11, Release 4]. +@enddefun + + +@defun[fun {name-keysym}, args {@i[name]}] +This function returns the keysym named @i[name]. If @i[name] is unknown, this +returns @nil. +@enddefun + +@defun[fun {keysym-names}, args {@i[keysym]}] +This function returns the list of all names for @i[keysym]. If @i[keysym] is +undefined, this returns @nil. +@enddefun + +@defun[fun {keysym-preferred-name}, args {@i[keysym]}] +This returns the preferred name for @i[keysym], how it is typically printed. +If @i[keysym] is undefined, this returns @nil. +@enddefun + +@defun[fun {define-key-event-modifier}, args {@i[long-name] @i[short-name]}] +This establishes @i[long-name] and @i[short-name] as modifier names for +purposes of specifying key-events in @f[#k] syntax. The names are +case-insensitive simple-strings. If either name is already defined, this +signals an error. + +The system defines the following default modifiers (first the long name, +then the short name): +@begin[Itemize] +@f["Hyper"], @f["H"] + +@f["Super"], @f["S"] + +@f["Meta"], @f["M"] + +@f["Control"], @f["C"] + +@f["Shift"], @f["Shift"] + +@f["Lock"], @f["Lock"] +@end[Itemize] +@enddefun + + +@defvar[var {all-modifier-names}] +This variable holds all the defined modifier names. +@enddefvar + + +@defun[fun {define-clx-modifier}, args {@i[clx-mask] @i[modifier-name]}] +This function establishes a mapping from @i[clx-mask] to a defined key-event +@i[modifier-name]. @f[translate-key-event] and @f[translate-mouse-key-event] +can only return key-events with bits defined by this routine. + +The system defines the following default mappings between CLX modifiers and +key-event modifiers: +@begin[Itemize] +@f[(xlib:make-state-mask :mod-1) --> "Meta"] + +@f[(xlib:make-state-mask :control) --> "Control"] + +@f[(xlib:make-state-mask :lock) --> "Lock"] + +@f[(xlib:make-state-mask :shift) --> "Shift"] +@end[Itemize] +@enddefun + + +@defun[fun {make-key-event-bits}, args {@rest @i[modifier-names]}] +This function returns bits suitable for @f[make-key-event] from the supplied +@i[modifier-names]. If any name is undefined, this signals an error. +@enddefun + +@defun[fun {key-event-modifier-mask}, args {@i[modifier-name]}] +This function returns a mask for @i[modifier-name]. This mask is suitable for +use with @f[key-event-bits]. If @i[modifier-name] is undefined, this signals +an error. +@enddefun + +@defun[fun {key-event-bits-modifiers}, args {@i[bits]}] +This returns a list of key-event modifier names, one for each modifier +set in @i[bits]. +@enddefun + + +@defun[fun {translate-key-event}, args {@i[display] @i[scan-code] @i[bits]}] +This function translates the X @i[scan-code] and X @i[bits] to a key-event. +First this maps @i[scan-code] to an X keysym using @f[xlib:keycode->keysym] +looking at @i[bits] and supplying index as @f[1] if the X shift bit is on, +@f[0] otherwise. + +If the resulting keysym is undefined, and it is not a modifier keysym, +then this signals an error. If the keysym is a modifier key, then this +returns @nil. + +If these conditions are satisfied +@begin[Itemize] +The keysym is defined. + +The X shift bit is off. + +The X lock bit is on. + +The X keysym represents a lowercase letter. +@end[Itemize] +then this maps the @i[scan-code] again supplying index as @f[1] this time, +treating the X lock bit as a caps-lock bit. If this results in an undefined +keysym, this signals an error. Otherwise, this makes a key-event with the +keysym and bits formed by mapping the X bits to key-event bits. + +Otherwise, this makes a key-event with the keysym and bits formed by +mapping the X bits to key-event bits. +@enddefun + + +@defun[fun {translate-mouse-key-event}, args {@i[scan-code] @i[bits] @i[event-key]}] +This function translates the X button code, @i[scan-code], and modifier bits, +@i[bits], for the X @i[event-key] into a key-event. See +@f[define-mouse-keysym]. +@enddefun + +@defun[fun {make-key-event}, args {@i[object] @i[bits]}] +This function returns a key-event described by @i[object] with @i[bits]. +@i[Object] is one of keysym, string, or key-event. When @i[object] is a +key-event, this uses @f[key-event-keysym]. You can form @i[bits] with +@f[make-key-event-bits] or @f[key-event-modifier-mask]. +@enddefun + +@defun[fun {key-event-p}, args {@i[object]}] +This function returns whether @i[object] is a key-event. +@enddefun + +@defun[fun {key-event-bits}, args {@i[key-event]}] +This function returns the bits field of a @i[key-event]. +@enddefun + +@defun[fun {key-event-keysym}, args {@i[key-event]}] +This function returns the keysym field of a @i[key-event]. +@enddefun + +@defun[fun {char-key-event}, args {@i[character]}] +This function returns the key-event associated with @i[character]. You can +associate a key-event with a character by @f[setf]'ing this form. +@enddefun + +@defun[fun {key-event-char}, args {@i[key-event]}] +This function returns the character associated with @i[key-event]. You can +associate a character with a key-event by @f[setf]'ing this form. The system +defaultly translates key-events in some implementation dependent way for text +insertion; for example, under an ASCII system, the key-event @f[#k"C-h"], as +well as @f[#k"backspace"] would map to the Common Lisp character that causes a +backspace. +@enddefun + +@defun[fun {key-event-bit-p}, args {@i[key-event] @i[bit-name]}] +This function returns whether @i[key-event] has the bit set named by +@i[bit-name]. This signals an error if @i[bit-name] is undefined. +@enddefun + +@defmac[fun {do-alpha-key-events}, args +{(@i[var] @i[kind] @optional @i[result]) @mstar<@i[form]>}] + This macro evaluates each @i[form] with @i[var] bound to a key-event +representing an alphabetic character. @i[Kind] is one of @kwd[lower], +@kwd[upper], or @kwd[both], and this binds @i[var] to each key-event in order +as specified in @i[X Window System Protocol, MIT X Consortium Standard, X +Version 11, Release 4]. When @kwd[both] is specified, this processes lowercase +letters first. +@enddefmac + +@defun[fun {print-pretty-key}, args {@i[key] @optional @i[stream] @i[long-names-p]}] +This prints @i[key], a key-event or vector of key-events, in a user-expected +fashion to @i[stream]. @i[Long-names-p] indicates whether modifiers should +print with their long or short name. @i[Stream] defaults to +@var[standard-output]. +@enddefun + +@defun[fun {print-pretty-key-event}, args {@i[key-event] @optional @i[stream] @i[long-names-p]}] +This prints @i[key-event] to @i[stream] in a user-expected fashion. +@i[Long-names-p] indicates whether modifier names should appear using the long +name or short name. @i[Stream] defaults to @var[standard-output]. +@enddefun + + + +@section (CLX Interface) + +@subsection (Graphics Window Hooks) +This section describes a few hooks used by Hemlock's internals to handle +graphics windows that manifest Hemlock windows. Some heavy users of Hemlock as +a tool have needed these in the past, but typically functions that replace the +default values of these hooks must be written in the "@f[HEMLOCK-INTERNALS]" +package. All of these symbols are internal to this package. + +If you need this level of control for your application, consult the current +implementation for code fragments that will be useful in correctly writing your +own window hook functions. + +@defvar[var {create-window-hook}] +This holds a function that @Hemlock calls when @f[make-window] executes under +CLX. @Hemlock passes the CLX display and the following arguments from +@f[make-window]: starting mark, ask-user, x, y, width, height, and modelinep. +The function returns a CLX window or nil indicating one could not be made. +@enddefvar + +@defvar[var {delete-window-hook}] +This holds a function that @hemlock calls when @f[delete-window] executes under +CLX. @hemlock passes the CLX window and the @hemlock window to this function. +@enddefvar + +@defvar[var {random-typeout-hook}] +This holds a function that @hemlock calls when random typeout occurs under CLX. +@hemlock passes it a @hemlock device, a pre-existing CLX window or @nil, and +the number of pixels needed to display the number of lines requested in the +@f[with-pop-up-display] form. It should return a window, and if a new window +is created, then a CLX gcontext must be the second value. +@enddefvar + +@defvar[var {create-initial-windows-hook}] +This holds a function that @hemlock calls when it initializes the screen +manager and makes the first windows, typically windows for the @hid[Main] and +@hid[Echo Area] buffers. @hemlock passes the function a @hemlock device. +@enddefvar + + +@subsection (Entering and Leaving Windows) + +@defhvar[var "Enter Window Hook"] +When the mouse enters an editor window, @hemlock invokes the functions in this +hook. These functions take a @Hemlock window as an argument. +@enddefhvar + +@defhvar[var "Exit Window Hook"] +When the mouse exits an editor window, @hemlock invokes the functions in this +hook. These functions take a @Hemlock window as an argument. +@enddefhvar + + +@subsection (How to Lose Up-Events) +Often the only useful activity user's design for the mouse is to click on +something. @Hemlock sees a character representing the down event, but what do +you do with the up event character that you know must follow? Having the +command eat it would be tasteless, and would inhibit later customizations that +make use of it, possibly adding on to the down click command's functionality. +Bind the corresponding up character to the command described here. + +@defcom[com "Do Nothing"] +This does nothing as many times as you tell it. +@enddefcom + + +@section (Slave Lisps) +@index (Slave lisp interface functions) +Some implementations of @hemlock feature the ability to manage multiple slave +Lisps, each connected to one editor Lisp. The routines discussed here spawn +slaves, send evaluation and compilation requests, return the current server, +etc. This is very powerful because without it you can lose your editing state +when code you are developing causes a fatal error in Lisp. + +The routines described in this section are best suited for creating editor +commands that interact with slave Lisps, but in the past users implemented +several independent Lisps as nodes communicating via these functions. There is +a better level on which to write such code that avoids the extra effort these +routines take for the editor's sake. See the @i[CMU Common Lisp User's Manual] +for the @f[remote] and @f[wire] packages. + + +@subsection (The Current Slave) +There is a slave-information structure that these return which is suitable for +passing to the routines described in the following subsections. + +@defun[fun {create-slave}, args {@optional @i[name]}] +This creates a slave that tries to connect to the editor. When the slave +connects to the editor, this returns a slave-information structure, and the +interactive buffer is the buffer named @i[name]. This generates a name if +@i[name] is @nil. In case the slave never connects, this will eventually +timeout and signal an editor-error. +@enddefun + +@defun[fun {get-current-eval-server}, args {@optional @i[errorp]}] +@defhvar1[var {Current Eval Server}] +This returns the server-information for the @hid[Current Eval Server] after +making sure it is valid. Of course, a slave Lisp can die at anytime. If this +variable is @nil, and @i[errorp] is non-@nil, then this signals an +editor-error; otherwise, it tries to make a new slave. If there is no current +eval server, then this tries to make a new slave, prompting the user based on a +few variables (see the @i[Hemlock User's Manual]). +@enddefun + +@defun[fun {get-current-compile-server}] +@defhvar1[var {Current Compile Server}] +This returns the server-information for the @hid[Current Compile Server] after +making sure it is valid. This may return nil. Since multiple slaves may +exist, it is convenient to use one for developing code and one for compiling +files. The compilation commands that use slave Lisps prefer to use the current +compile server but will fall back on the current eval server when necessary. +Typically, users only have separate compile servers when the slave Lisp can +live on a separate workstation to save cycles on the editor machine, and the +@hemlock commands only use this for compiling files. +@enddefun + + +@subsection (Asynchronous Operation Queuing) +The routines in this section queue requests with an eval server. Requests are +always satisfied in order, but these do not wait for notification that the +operation actually happened. Because of this, the user can continue editing +while his evaluation or compilation occurs. Note, these usually execute in the +slave immediately, but if the interactive buffer connected to the slave is +waiting for a form to return a value, the operation requested must wait until +the slave is free again. + +@defun[fun {string-eval}, args {@i[string]}, keys {[server][package][context]}] +@defun1[fun {region-eval}, args {@i[region]}, keys {[server][package][context]}] +@defun1[fun {region-compile}, args {@i[region]}, keys {[server][package]}] +@f[string-eval] queues the evaluation of the form read from @i[string] on eval +server @i[server]. @i[Server] defaults to the result of +@f[get-current-server], and @i[string] is a simple-string. The evaluation +occurs with @var[package] bound in the slave to the package named by +@i[package], which defaults to @hid[Current Package] or the empty string; the +empty string indicates that the slave should evaluate the form in its current +package. The slave reads the form in @i[string] within this context as well. +@i[Context] is a string to use when reporting start and end notifications in +the @hid[Echo Area] buffer; it defaults to the concatenation of @f["evaluation +of "] and @i[string]. + +@f[region-eval] is the same as @f[string-eval], but @i[context] defaults +differently. If the user leaves this unsupplied, then it becomes a string +involving part of the first line of region. + +@f[region-compile] is the same as the above. @i[Server] defaults the same; it +does not default to @f[get-current-compile-server] since this compiles the +region into the slave Lisp's environment, to affect what you are currently +working on. +@enddefun + +@defun[fun {file-compile}, args {@i[file]}, + keys {[output-file][error-file][load][server]}, + morekeys {[package]}] +@defhvar1[var {Remote Compile File}, val {nil}] +This compiles @i[file] in a slave Lisp. When @i[output-file] is @true (the +default), this uses a temporary output file that is publicly writable in case +the client is on another machine, which allows for file systems that do not +permit remote write access. This renames the temporary file to the appropriate +binary name or deletes it after compilation. Setting @hid[Remote Compile File] +to @nil, inhibits this. If @i[output-file] is non-@nil and not @true, then it +is the name of the binary file to write. The compilation occurs with +@var[package] bound in the slave to the package named by @i[package], which +defaults to @hid[Current Package] or the empty string; the empty string +indicates that the slave should evaluate the form in its current package. +@i[Error-file] is the file in which to record compiler output, and a @nil value +inhibits this file's creation. @i[Load] indicates whether to load the +resulting binary file, defaults to @nil. @i[Server] defaults to +@f[get-current-compile-server], but if this returns nil, then @i[server] +defaults to @f[get-current-server]. +@enddefun + +@subsection (Synchronous Operation Queuing) +The routines in this section queue requests with an eval server and wait for +confirmation that the evaluation actually occurred. Because of this, the user +cannot continue editing while the slave executes the request. Note, these +usually execute in the slave immediately, but if the interactive buffer +connected to the slave is waiting for a form to return a value, the operation +requested must wait until the slave is free again. + +@defun[fun {eval-form-in-server}, + args {@i[server-info] @i[string] @optional @i[package]}] + This function queues the evaluation of a form in the server associated with +@i[server-info] and waits for the results. The server @f[read]'s the form from +@i[string] with @var[package] bound to the package named by @i[package]. This +returns the results from the slave Lisp in a list of string values. You can +@f[read] from the strings or simply display them depending on the @f[print]'ing +of the evaluation results. + +@i[Package] defaults to @hid[Current Package]. If this is @nil, the server +uses the value of @var[package] in the server. + +While the slave executes the form, it binds @var[terminal-io] to a stream that +signals errors when read from and dumps output to a bit-bucket. This prevents +the editor and slave from dead locking by waiting for each other to reply. +@enddefun + +@defun[fun {eval-form-in-server-1}, + args {@i[server-info] @i[string] @optional @i[package]}] + This function calls @f[eval-form-in-server] and @f[read]'s the result in the +first string it returns. This result must be @f[read]'able in the editor's +Lisp. +@enddefun + + +@section (Spelling) +@index (Spelling checking) +@hemlock supports spelling checking and correcting commands based on the ITS +Ispell dictionary. These commands use the following routines which include +adding and deleting entries, reading the Ispell dictionary in a compiled binary +format, reading user dictionary files in a text format, and checking and +correcting possible spellings. + +@defun[fun {maybe-read-spell-dictionary}, package {spell}] +This reads the default binary Ispell dictionary. Users must call this before +the following routines will work. +@enddefun + +@defun[fun {spell-read-dictionary}, package {spell}, args {@i[filename]}] +This adds entries to the dictionary from the lines in the file @i[filename]. +Dictionary files contain line oriented records like the following: +@begin[programexample] +entry1/flag1/flag2 +entry2 +entry3/flag1 +@end[programexample] +The flags are the Ispell flags indicating which endings are appropriate for the +given entry root, but these are unnecessary for user dictionary files. You can +consult Ispell documentation if you want to know more about them. +@enddefun + +@defun[fun {spell-add-entry}, package {spell}, + args {@i[line] @optional @i[word-end]}] +This takes a line from a dictionary file, and adds the entry described by +@i[line] to the dictionary. @i[Word-end] defaults to the position of the first +slash character or the length of the line. @i[Line] is destructively modified. +@enddefun + +@defun[fun {spell-remove-entry}, package {spell}, args {@i[entry]}] +This removes entry, a simple-string, from the dictionary, so it will be an +unknown word. This destructively modifies @i[entry]. If it is a root word, +then all words derived with @i[entry] and its flags will also be deleted. If +@i[entry] is a word derived from some root word, then the root and any words +derived from it remain known words. +@enddefun + +@defun[fun {correct-spelling}, package {spell}, args {@i[word]}] +This checks the spelling of @i[word] and outputs the results. If this finds +@i[word] is correctly spelled due to some appropriate suffix on a root, it +generates output indicating this. If this finds @i[word] as a root entry, it +simply outputs that it found @i[word]. If this cannot find @i[word] at all, +then it outputs possibly correct close spellings. This writes to +@var[standard-output], and it calls @f[maybe-read-spell-dictionary] before +attempting any lookups. +@enddefun + +@defun[fun {spell-try-word}, package {spell}, args {@i[word] @i[word-len]}] +@defcon1[var {max-entry-length}, val {31}] +This returns an index into the dictionary if it finds @i[word] or an +appropriate root. @i[Word-len] must be inclusively in the range 2 through +@f[max-entry-length], and it is the length of @i[word]. @i[Word] must be +uppercase. This returns a second value indicating whether it found @i[word] +due to a suffix flag, @nil if @i[word] is a root entry. +@enddefun + +@defun[fun {spell-root-word}, package {spell}, args {@i[index]}] +This returns a copy of the root word at dictionary entry @i[index]. This index +is the same as returned by @f[spell-try-word]. +@enddefun + +@defun[fun {spell-collect-close-words}, package {spell}, args {@i[word]}] +This returns a list of words correctly spelled that are @i[close] to @i[word]. +@i[Word] must be uppercase, and its length must be inclusively in the range 2 +through @f[max-entry-length]. Close words are determined by the Ispell rules: +@begin[enumerate] +Two adjacent letters can be transposed to form a correct spelling. + +One letter can be changed to form a correct spelling. + +One letter can be added to form a correct spelling. + +One letter can be removed to form a correct spelling. +@end[enumerate] +@enddefun + +@defun[fun {spell-root-flags}, package {spell}, args {@i[index]}] +This returns a list of suffix flags as capital letters that apply to the +dictionary root entry at @i[index]. This index is the same as returned by +@f[spell-try-word]. +@enddefun + + +@section (File Utilities) +Some implementations of @hemlock provide extensive directory editing commands, +@hid[Dired], including a single wildcard feature. An asterisk denotes a +wildcard. + +@defun[fun {copy-file}, package {dired}, + args {@i[spec1] @i[spec2]}, keys {[update][clobber][directory]}] + This function copies @i[spec1] to @i[spec2]. It accepts a single wildcard in +the filename portion of the specification, and it accepts directories. This +copies files maintaining the source's write date. + +If @i[spec1] and @i[spec2] are both directories, this recursively copies the +files and subdirectory structure of @i[spec1]; if @i[spec2] is in the +subdirectory structure of @i[spec1], the recursion will not descend into it. +Use @f["/spec1/*"] to copy only the files from @i[spec1] to directory +@i[spec2]. + +If @i[spec2] is a directory, and @i[spec1] is a file, then this copies +@i[spec1] into @i[spec2] with the same @f[pathname-name]. + +When @kwd[update] is non-@nil, then the copying process only copies files if the +source is newer than the destination. + +When @kwd[update] and @kwd[clobber] are @nil, and the destination exists, the +copying process stops and asks the user whether the destination should be +overwritten. + +When the user supplies @kwd[directory], it is a list of pathnames, directories +excluded, and @i[spec1] is a pattern containing one wildcard. This then copies +each of the pathnames whose @f[pathname-name] matches the pattern. @i[Spec2] +is either a directory or a pathname whose @f[pathname-name] contains a +wildcard. +@enddefun + +@defun[fun {rename-file}, package {dired}, + args {@i[spec1] @i[spec2]}, keys {[clobber][directory]}] + This function renames @i[spec1] to @i[spec2]. It accepts a single wildcard in +the filename portion of the specification, and @i[spec2] may be a directory +with the destination specification resulting in the merging of @i[spec2] with +@i[spec1]. If @kwd[clobber] is @nil, and @i[spec2] exists, then this asks the +user to confirm the renaming. When renaming a directory, end the specification +without the trailing slash. + +When the user supplies @kwd[directory], it is a list of pathnames, directories +excluded, and @i[spec1] is a pattern containing one wildcard. This then copies +each of the pathnames whose @f[pathname-name] matches the pattern. @i[Spec2] +is either a directory or a pathname whose @f[pathname-name] contains a +wildcard. +@enddefun + +@defun[fun {delete-file}, package {dired}, + args {@i[spec]}, keys {[recursive][clobber]}] + This function deletes @i[spec]. It accepts a single wildcard in the filename +portion of the specification, and it asks for confirmation on each file if +@kwd[clobber] is @nil. If @kwd[recursive] is non-@nil, then @i[spec] may be a +directory to recursively delete the entirety of the directory and its +subdirectory structure. An empty directory may be specified without +@kwd[recursive] being non-@nil. Specify directories with the trailing +slash. +@enddefun + +@defun[fun {find-file}, package {dired}, + args {@i[name] @optional @i[directory] @i[find-all]}] + This function finds the file with @f[file-namestring] @i[name], recursively +looking in @i[directory]. If @i[find-all] is non-@nil (defaults to @nil), then +this continues searching even after finding a first occurrence of file. +@i[Name] may contain a single wildcard, which causes @i[find-all] to default to +@true instead of @nil. +@enddefun + +@defun[fun {make-directory}, package {dired}, args {@i[name]}] +This function creates the directory with @i[name]. If it already exists, this +signals an error. +@enddefun + +@defun[fun {pathnames-from-pattern}, package {dired}, + args {@i[pattern] @i[files]}] +This function returns a list of pathnames from the list @i[files] whose +@f[file-namestring]'s match @i[pattern]. @i[Pattern] must be a non-empty +string and contain only one asterisk. @i[Files] contains no directories. +@enddefun + +@defvar[var {update-default}, package {dired}] +@defvar1[var {clobber-default}, package {dired}] +@defvar1[var {recursive-default}, package {dired}] +These are the default values for the keyword arguments above with corresponding +names. These default to @nil, @true, and @nil respectively. +@enddefvar + +@defvar[var {report-function}, package {dired}] +@defvar1[var {error-function}, package {dired}] +@defvar1[var {yesp-function}, package {dired}] +These are the function the above routines call to report progress, signal +errors, and prompt for @i[yes] or @i[no]. These all take format strings and +arguments. +@enddefvar + + +@section (Beeping) + +@defun[fun {hemlock-beep}] +@Hemlock binds @f[system:*beep-function*] to this function to beep the device. +It is different for different devices. +@enddefvar + +@defhvar[var "Bell Style", val {:border-flash}] +@defhvar1[var "Beep Border Width", val {20}] +@hid[Bell Style] determines what @var[hemlock-beep] does in @hemlock under CLX. +Acceptable values are @kwd[border-flash], @kwd[feep], +@kwd[border-flash-and-feep], @kwd[flash], @kwd[flash-and-feep], and @nil (do +nothing). + +@hid[Beep Border Width] is the width in pixels of the border flashed by border +flash beep styles. +@enddefhvar diff --git a/docs/hem/cim/cim.mss b/docs/hem/cim/cim.mss new file mode 100644 index 0000000000000000000000000000000000000000..0fc5c0cedf333f733dd61138fcb8266dd5c71ea4 --- /dev/null +++ b/docs/hem/cim/cim.mss @@ -0,0 +1,4003 @@ +@make[Manual] @comment{-*- Dictionary: /usr/lisp/scribe/hem/hem; Mode: spell; Package: Hemlock; Log: /usr/lisp/scribe/hem/hem-docs.log -*-} +@Device[postscript] +@style(FontFamily = TimesRoman) +@Style(Spacing = 1.2 lines) +@Style(StringMax = 5000) +@style(Hyphenation = On) +@style(Date="March 1952") +@use(database "/usr/lisp/scribe/database/") +@Style [DoubleSided] +@Libraryfile[ArpaCredit] +@Libraryfile[Hem] +@Libraryfile[Spice] +@Libraryfile[Uttir] + +@String(ReportTitle "Hemlock Command Implementor's Manual") + +@comment< +@begin[TitlePage] +@begin[TitleBox] +> +@blankspace(1.3inches) +@heading[Hemlock Command Implementor's Manual] + +@center[ +@b +@b + +@b<@value[date]> + +@b +] +@comment<@end[TitleBox]> +@blankspace(2lines) +@begin[Center] +School of Computer Science +Carnegie Mellon University +Pittsburgh, PA 15213 +@end[Center] + +@blankspace(2lines) +@begin[Center] +This is a revised version of Technical Report CMU-CS-87-159. +@end[Center] +@heading[Abstract] +@begin(Text, indent 0) +This document describes how to write commands for the @Hemlock text editor, as +of version M3.2. @Hemlock is a customizable, extensible text editor whose +initial command set closely resembles that of ITS/TOPS-20 @Emacs. @Hemlock is +written in the CMU Common Lisp and has been ported to other implementations. +@end(Text) + +@blankspace(0.5in) +@begin[ResearchCredit] +@arpacredit[Contract=Basic87-90] +@end[ResearchCredit] +@comment<@end[TitlePage]> + + +@commandstring(dash = "@Y[M]") + + +@Tabclear + +@chapter(Introduction) + + @hemlock is a text editor which follows in the tradition of editors +such as EMACS and the Lisp Machine editor ZWEI. In its basic form, +@hemlock has almost the same command set as EMACS, and similar +features such as multiple buffers and windows, extended commands, +and built in documentation. + +Both user extensions and the original commands are written in Lisp, +therefore a command implementor will have a working knowledge of this +language. Users not familiar with Lisp need not despair however. Many +users of Multics EMACS, another text editor written in Lisp, came to learn +Lisp simply for the purpose of writing their own editor extensions, and +found, to their surprise, that it was really pretty easy to write simple +commands. + +This document describes the Common Lisp functions, macros and data structures +that are used to implement new commands. The basic editor consists of a set of +Lisp utility functions for manipulating buffers and the other data structures +of the editor as well as handling the display. All user level commands are +written in terms of these functions. To find out how to define commands see +chapter @ref[commands]. + +@chapter(Representation of Text) +@index (Lines) +@section(Lines) +In @hemlock all text is in some @i[line]. Text is broken into lines wherever +it contains a newline character; newline characters are never stored, but are +assumed to exist between every pair of lines. The implicit newline character +is treated as a single character by the text primitives. + +@defun[fun {linep}, args {@i[line]}] +This function returns @true if @i[line] is a @f[line] object, otherwise @nil. +@enddefun + +@defun[fun {line-string}, args {@i[line]}] +Given a @i(line), this function returns as a simple string the characters in +the line. This is @f[setf]'able to set the @f[line-string] to any string that +does not contain newline characters. It is an error to destructively modify +the result of @f[line-string] or to destructively modify any string after the +@f[line-string] of some line has been set to that string. +@enddefun + +@defun[fun {line-previous}, args {@i[line]}] +@defun1[fun {line-next}, args {@i[line]}] +Given a @i(line), @f[line-previous] returns the previous line or @nil if there +is no previous line. Similarly, @f[line-next] returns the line following +@i[line] or @nil. +@enddefun + +@defun[fun {line-buffer}, args {@i[line]}] +This function returns the buffer which contains this @i(line). Since a +line may not be associated with any buffer, in which case @f[line-buffer] +returns @nil. +@enddefun + +@defun[fun {line-length}, args {@i[line]}] +This function returns the number of characters in the @i(line). This excludes +the newline character at the end. +@enddefun + +@defun[fun {line-character}, args {@i[line] @i[index]}] +This function returns the character at position @i[index] within @i[line]. It +is an error for @i[index] to be greater than the length of the line or less +than zero. If @i[index] is equal to the length of the line, this returns a +@f[#\newline] character. +@enddefun + +@defun[fun {line-plist}, args {@i[line]}] +This function returns the property-list for @i[line]. @f[setf], @f[getf], +@f[putf] and @f[remf] can be used to change properties. This is typically used +in conjunction with @f[line-signature] to cache information about the line's +contents. +@enddefun + +@defun[fun {line-signature}, args {@i[line]}] +This function returns an object that serves as a signature for a @i[line]'s +contents. It is guaranteed that any modification of text on the line will +result in the signature changing so that it is not @f[eql] to any previous +value. The signature may change even when the text remains unmodified, but +this does not happen often. +@enddefun + + +@section(Marks) +@label[marks] +@index (Marks) +A mark indicates a specific position within the text represented by a line and +a character position within that line. Although a mark is sometimes loosely +referred to as pointing to some character, it in fact points between +characters. If the @f[charpos] is zero, the previous character is the newline +character separating the previous line from the mark's @f[line]. If the +charpos is equal to the number of characters in the line, the next character is +the newline character separating the current line from the next. If the mark's +line has no previous line, a mark with @f[charpos] of zero has no previous +character; if the mark's line has no next line, a mark with @f[charpos] equal +to the length of the line has no next character. + +This section discusses the very basic operations involving marks, but a lot of +@hemlock programming is built on altering some text at a mark. For more +extended uses of marks see chapter @ref[doing-stuff]. + + +@subsection(Kinds of Marks) +@index (Permanent marks) +@index (Temporary marks) +A mark may have one of two lifetimes: @i[temporary] or @i[permanent]. +Permanent marks remain valid after arbitrary operations on the text; temporary +marks do not. Temporary marks are used because less bookkeeping overhead is +involved in their creation and use. If a temporary mark is used after the text +it points to has been modified results will be unpredictable. Permanent marks +continue to point between the same two characters regardless of insertions and +deletions made before or after them. + +There are two different kinds of permanent marks which differ only in their +behavior when text is inserted @i(at the position of the mark); text is +inserted to the left of a @i[left-inserting] mark and to the right of +@i[right-inserting] mark. + + +@subsection(Mark Functions) +@defun[fun {markp}, args {@i[mark]}] +This function returns @true if @i[mark] is a @f[mark] object, otherwise @nil. +@enddefun + +@defun[fun {mark-line}, args {@i[mark]}] +This function returns the line to which @i(mark) points. +@enddefun + +@defun[fun {mark-charpos}, args {@i[mark]}] +This function returns the character position of the character after @i(mark). +If @i[mark]'s line has no next line, this returns the length of the line as +usual; however, there is actually is no character after the mark. +@enddefun + +@defun[fun {mark-kind}, args {@i[mark]}] +This function returns one of @kwd[right-inserting], @kwd[left-inserting] or +@kwd[temporary] depending on the mark's kind. A corresponding @f[setf] form +changes the mark's kind. +@enddefun + +@defun[fun {previous-character}, args {@i[mark]}] +@defun1[fun {next-character}, args {@i[mark]}] +This function returns the character immediately before (after) the position of +the @i[mark], or @nil if there is no previous (next) character. These +characters may be set with @f[setf] when they exist; the @f[setf] methods for +these forms signal errors when there is no previous or next character. +@enddefun + + +@subsection(Making Marks) +@defun[fun {mark}, args {@i[line] @i[charpos] @optional @i[kind]}] +This function returns a mark object that points to the @i(charpos)'th character +of the @i(line). @i(Kind) is the kind of mark to create, one of +@kwd[temporary], @kwd[left-inserting], or @kwd[right-inserting]. The default +is @kwd[temporary]. +@enddefun + +@defun[fun {copy-mark}, args {@i[mark] @optional @i[kind]}] +This function returns a new mark pointing to the same position and of the same +kind, or of kind @i[kind] if it is supplied. +@enddefun + +@defun[fun {delete-mark}, args {@i[mark]}] +This function deletes @i(mark). Delete any permanent marks when you are +finished using it. +@enddefun + +@Defmac[Fun {with-mark}, Args + {(@Mstar<(@i[mark] @i[pos] @mopt[@i(kind)])>) @Mstar<@i[form]>}] + This macro binds to each variable @i[mark] a mark of kind @i[kind], which +defaults to @kwd[temporary], pointing to the same position as the mark @i[pos]. +On exit from the scope the mark is deleted. The value of the last @i[form] is +the value returned. +@enddefmac + + +@subsection(Moving Marks) +@index(Moving marks) +These functions destructively modify marks to point to new positions. Other +sections of this document describe mark moving routines specific to higher +level text forms than characters and lines, such as words, sentences, +paragraphs, Lisp forms, etc. + +@defun[fun {move-to-position}, args {@i[mark] @i[charpos] @optional @i[line]}] +This function changes the @i(mark) to point to the given character position on +the line @i(line). @i(Line) defaults to @i[mark]'s line. +@enddefun + +@defun[fun {move-mark}, args {@i[mark] @i[new-position]}] +This function moves @i[mark] to the same position as the mark @i[new-position] +and returns it. +@enddefun + +@defun[fun {line-start}, args {@i[mark] @optional @i[line]}] +@defun1[fun {line-end}, args {@i[mark] @optional @i[line]}] +This function changes @i[mark] to point to the beginning or the end of @i(line) +and returns it. @i[Line] defaults to @i[mark]'s line. +@enddefun + +@defun[fun {buffer-start}, args {@i[mark] @optional @i[buffer]}] +@defun1[fun {buffer-end}, args {@i[mark] @optional @i[buffer]}] +These functions change @i[mark] to point to the beginning or end of @i[buffer], +which defaults to the buffer @i[mark] currently points into. If @i[buffer] is +unsupplied, then it is an error for @i[mark] to be disassociated from any +buffer. +@enddefun + +@defun[fun {mark-before}, args {@i[mark]}] +@defun1[fun {mark-after}, args {@i[mark]}] +These functions change @i[mark] to point one character before or after the +current position. If there is no character before/after the current position, +then they return @nil and leave @i[mark] unmodified. +@enddefun + +@defun[fun {character-offset}, args {@i[mark] @i[n]}] +This function changes @i[mark] to point @i[n] characters after (@i[n] before if +@i[n] is negative) the current position. If there are less than @i[n] +characters after (before) the @i[mark], then this returns @nil and @i[mark] is +unmodified. +@enddefun + +@defun[fun {line-offset}, args {@i[mark] @i[n] @optional @i[charpos]}] +This function changes @i[mark] to point @i[n] lines after (@i[n] before if +@i[n] is negative) the current position. The character position of the +resulting mark is +@lisp +(min (line-length @i(resulting-line)) (mark-charpos @i(mark))) +@endlisp +if @i[charpos] is unspecified, or +@lisp +(min (line-length @i(resulting-line)) @i(charpos)) +@endlisp +if it is. As with @t(character-offset), if there are not @i[n] lines then +@nil is returned and @i[mark] is not modified. +@enddefun + + +@section(Regions) +@index (Regions) +A region is simply a pair of marks: a starting mark and an ending mark. +The text in a region consists of the characters following the starting +mark and preceding the ending mark (keep in mind that a mark points between +characters on a line, not at them). + +By modifying the starting or ending mark in a region it is possible to +produce regions with a start and end which are out of order or even in +different buffers. The use of such regions is undefined and may +result in arbitrarily bad behavior. + + +@subsection(Region Functions) +@defun[fun {region}, args {@i[start] @i[end]}] +This function returns a region constructed from the marks @i[start] and +@i[end]. It is an error for the marks to point to non-contiguous lines or for +@i(start) to come after @i(end). +@enddefun + +@defun[fun {regionp}, args {@i[region]}] +This function returns @true if @i[region] is a @f[region] object, otherwise +@nil. +@enddefun + +@defun[fun {make-empty-region}] +This function returns a region with start and end marks pointing to the start +of one empty line. The start mark is a @kwd[right-inserting] mark, and the end +is a @kwd[left-inserting] mark. +@enddefun + +@defun[fun {copy-region}, args {@i[region]}] +This function returns a region containing a copy of the text in the specified +@i[region]. The resulting region is completely disjoint from @i[region] with +respect to data references @dash marks, lines, text, etc. +@enddefun + +@defun[fun {region-to-string}, args {@i[region]}] +@defun1[fun {string-to-region}, args {@i[string]}] +These functions coerce regions to Lisp strings and vice versa. Within the +string, lines are delimited by newline characters. +@enddefun + +@defun[fun {line-to-region}, args {@i[line]}] +This function returns a region containing all the characters on @i[line]. The +first mark is @kwd[right-inserting] and the last is @kwd[left-inserting]. +@enddefun + +@defun[fun {region-start}, args {@i[region]}] +@defun1[fun {region-end}, args {@i[region]}] +This function returns the start or end mark of @i(region). +@enddefun + +@defun[fun {region-bounds}, args {@i[region]}] +This function returns as multiple-values the starting and ending marks of +@i[region]. +@enddefun + +@defun[fun {set-region-bounds}, args {@i[region] @i[start] @i[end]}] +This function sets the start and end of region to @i[start] and @i[end]. It is +an error for @i[start] to be after or in a different buffer from @i[end]. +@enddefun + +@index(Counting lines and characters) +@defun[fun {count-lines}, args {@i[region]}] +This function returns the number of lines in the @i(region), first and last +lines inclusive. A newline is associated with the line it follows, thus a +region containing some number of non-newline characters followed by one newline +is one line, but if a newline were added at the beginning, it would be two +lines. +@enddefun + +@defun[fun {count-characters}, args {@i[region]}] +This function returns the number of characters in a given @i(region). This +counts line breaks as one character. +@enddefun + +@defun[fun {check-region-query-size}, args {@i[region]}] +@defhvar1[var {Region Query Size}, val {30}] +@f[check-region-query-size] counts the lines in @i[region], and if their number +exceeds the @hid[Region Query Size] threshold, it prompts the user for +confirmation. This should be used in commands that perform destructive +operations and are not undoable. If the user responds negatively, then this +signals an editor-error, aborting whatever command was in progress. +@enddefun + + + +@chapter(Buffers) +@index (Buffers) +@label[buffers] +A buffer is an environment within @hemlock consisting of: +@begin(enumerate) +A name. + +A piece of text. + +A current focus of attention, the point. + +An associated file (optional). + +A write protect flag. + +Some variables (page @pageref[variables]). + +Some key bindings (page @pageref[key-bindings]). + +Some collection of modes (page @pageref[modes]). + +Some windows in which it is displayed (page @pageref[windows]). + +A list of modeline fields (optional). +@end(enumerate) + + +@section (The Current Buffer) +@index (Current buffer) +@defun[fun {current-buffer}] +@defhvar1[var {Set Buffer Hook}] +@defhvar1[var {After Set Buffer Hook}] +@f[current-buffer] returns the current buffer object. Usually this is the +buffer that @funref[current-window] is displaying. This value may be changed +with @f[setf], and the @f[setf] method invokes @hid[Set Buffer Hook] before the +change occurs with the new value. After the change occurs, the method invokes +@hid[After Set Buffer Hook] with the old value. +@enddefun + +@defun[fun {current-point}] +This function returns the @f[buffer-point] of the current buffer. +This is such a common idiom in commands that it is defined despite +its trivial implementation. +@enddefun + +@defun[fun {current-mark}] +@defun1[fun {pop-buffer-mark}] +@defun1[fun {push-buffer-mark}, args {@i[mark] @optional @i[activate-region]}] +@index(Buffer mark stack) +@index(Mark stack) +@label(mark-stack) +@f[current-mark] returns the top of the current buffer's mark stack. There +always is at least one mark at the beginning of the buffer's region, and all +marks returned are right-inserting. + +@f[pop-buffer-mark] pops the current buffer's mark stack, returning the mark. +If the stack becomes empty, this pushes a new mark on the stack pointing to the +buffer's start. This always deactivates the current region (see section +@ref[active-regions]). + +@f[push-buffer-mark] pushes @i[mark] into the current buffer's mark stack, +ensuring that the mark is right-inserting. If @i[mark] does not point into the +current buffer, this signals an error. Optionally, the current region is made +active, but this never deactivates the current region (see section +@ref[active-regions]). @i[Mark] is returned. +@enddefun + +@defvar[var {buffer-list}] +This variable holds a list of all the buffer objects made with @f[make-buffer]. +@enddefvar + +@defvar[var {buffer-names}] +This variable holds a @f[string-table] (page @pageref(string-tables)) of all the +names of the buffers in @var[buffer-list]. The values of the entries are the +corresponding buffer objects. +@enddefvar + +@defvar[var {buffer-history}] +This is a list of buffer objects ordered from those most recently selected to +those selected farthest in the past. When someone makes a buffer, an element +of @hid[Make Buffer Hook] adds this buffer to the end of this list. When +someone deletes a buffer, an element of @hid[Delete Buffer Hook] removes the +buffer from this list. Each buffer occurs in this list exactly once, but it +never contains the @var[echo-area-buffer]. +@enddefvar + +@defun[fun {change-to-buffer}, args {@i[buffer]}] +This switches to @i[buffer] in the @f[current-window] maintaining +@f[buffer-history]. +@enddefun + +@defun[fun {previous-buffer}] +This returns the first buffer from @var[buffer-history] that is not the +@f[current-buffer]. If none can be found, then this returns @nil. +@enddefun + + +@section(Buffer Functions) +@defun[fun {make-buffer}, args {@i[name]}, keys {[modes][modeline-fields][delete-hook]}] +@defhvar1[var {Make Buffer Hook}] +@defhvar1[var {Default Modeline Fields}] +@f[make-buffer] creates and returns a buffer with the given @i(name). If a +buffer named @i[name] already exists, @nil is returned. @i[Modes] is a list of +modes which should be in effect in the buffer, major mode first, followed by +any minor modes. If this is omitted then the buffer is created with the list +of modes contained in @hvarref[Default Modes]. @i[Modeline-fields] is a list +of modeline-field objects (see section @ref[modelines]) which may be @nil. +@f[delete-hook] is a list of delete hooks specific to this buffer, and +@f[delete-buffer] invokes these along with @hid[Delete Buffer Hook]. + +Buffers created with @f[make-buffer] are entered into the list +@var[buffer-list], and their names are inserted into the +string-table @var[buffer-names]. When a buffer is created the hook +@hid[Make Buffer Hook] is invoked with the new buffer. +@enddefun + +@defun[fun {bufferp}, args {@i[buffer]}] +Returns @true if @i[buffer] is a @f[buffer] object, otherwise @nil. +@enddefun + +@defun[fun {buffer-name}, args {@i[buffer]}] +@defhvar1[var {Buffer Name Hook}] +@f[buffer-name] returns the name, which is a string, of the given @i(buffer). +The corresponding @f[setf] method invokes @hid[Buffer Name Hook] with +@i[buffer] and the new name and then sets the buffer's name. When the user +supplies a name for which a buffer already exists, the @f[setf] method signals +an error. +@enddefun + +@defun[fun {buffer-region}, args {@i[buffer]}] +Returns the @i[buffer]'s region. This can be set with @f[setf]. Note, this +returns the region that contains all the text in a buffer, not the +@funref[current-region]. +@enddefun + +@defun[fun {buffer-pathname}, args {@i[buffer]}] +@defhvar1[var {Buffer Pathname Hook}] +@f[buffer-pathname] returns the pathname of the file associated with +the given @i(buffer), or nil if it has no associated file. This is +the truename of the file as of the most recent time it was read or +written. There is a @f[setf] form to change the pathname. When the +pathname is changed the hook @hid[Buffer Pathname Hook] is invoked +with the buffer and new value. +@enddefun + +@defun[fun {buffer-write-date}, args {@i[buffer]}] +Returns the write date for the file associated with the buffer in universal +time format. When this the @f[buffer-pathname] is set, use @f[setf] to set +this to the corresponding write date, or to @nil if the date is unknown or +there is no file. +@enddefun + +@defun[fun {buffer-point}, args {@i[buffer]}] +Returns the mark which is the current location within @i[buffer]. To +move the point, use @f[move-mark] or @funref[move-to-position] rather +than setting @f[buffer-point] with @f[setf]. +@enddefun + +@defun[fun {buffer-mark}, args {@i[buffer]}] +@index(Buffer mark stack) +@index(Mark stack) +This function returns the top of @i[buffer]'s mark stack. There always +is at least one mark at the beginning of @i[buffer]'s region, and all marks +returned are right-inserting. +@enddefun + +@defun[fun {buffer-start-mark}, args {@i[buffer]}] +@defun1[fun {buffer-end-mark}, args {@i[buffer]}] +These functions return the start and end marks of @i[buffer]'s region: +@Begin[ProgramExample] +(buffer-start-mark buffer) <==> + (region-start (buffer-region buffer)) +and +(buffer-end-mark buffer) <==> + (region-end (buffer-region buffer)) +@End[ProgramExample] +@enddefun + +@defun[fun {buffer-writable}, args {@i[buffer]}] +@defhvar1[var "Buffer Writable Hook"] +This function returns @true if you can modify the @i(buffer), @nil if you +cannot. If a buffer is not writable, then any attempt to alter text in the +buffer results in an error. There is a @f[setf] method to change this value. + +The @f[setf] method invokes the functions in @hid[Buffer Writable Hook] on the +buffer and new value before storing the new value. +@enddefun + +@defun[fun {buffer-modified}, args {@i[buffer]}] +@defhvar1[var "Buffer Modified Hook"] +@f[buffer-modified] returns @true if the @i[buffer] has been modified, @nil if +it hasn't. This attribute is set whenever a text-altering operation is +performed on a buffer. There is a @f[setf] method to change this value. + +The @f[setf] method invokes the functions in @hid[Buffer Modified Hook] with +the buffer whenever the value of the modified flag changes. +@enddefun + +@defmac[fun {with-writable-buffer}, args {(@i[buffer]) @rest @i[forms]}] +This macro executes @i[forms] with @i[buffer]'s writable status set. After +@i[forms] execute, this resets the @i[buffer]'s writable and modified status. +@enddefmac + +@defun[fun {buffer-signature}, args {@i[buffer]}] +This function returns an arbitrary number which reflects the buffer's current +@i[signature]. The result is @f[eql] to a previous result if and only if the +buffer has not been modified between the calls. +@enddefun + +@defun[fun {buffer-variables}, args {@i[buffer]}] +This function returns a string-table (page @pageref[string-tables]) containing +the names of the buffer's local variables. See chapter @ref[variables]. +@enddefun + +@defun[fun {buffer-modes}, args {@i[buffer]}] +This function returns the list of the names of the modes active in @i[buffer]. +The major mode is first, followed by any minor modes. See chapter @ref[modes]. +@enddefun + +@defun[fun {buffer-windows}, args {@i[buffer]}] +This function returns the list of all the windows in which the buffer may be +displayed. This list may include windows which are not currently visible. See +page @pageref[windows] for a discussion of windows. +@enddefun + +@defun[fun {buffer-delete-hook}, args {@i[buffer]}] +This function returns the list of buffer specific functions @f[delete-buffer] +invokes when deleting a buffer. This is @f[setf]'able. +@enddefun + +@defun[fun {delete-buffer}, args {@i[buffer]}] +@defhvar1[var {Delete Buffer Hook}] +@f[delete-buffer] removes @i[buffer] from @varref[buffer-list] and its name +from @varref[buffer-names]. Before @i[buffer] is deleted, this invokes the +functions on @i[buffer] returned by @f[buffer-delete-hook] and those found in +@hid[Delete Buffer Hook]. If @i[buffer] is the @f[current-buffer], or if it is +displayed in any windows, then this function signals an error. +@enddefun + +@defun[fun {delete-buffer-if-possible}, args {@i[buffer]}] +This uses @f[delete-buffer] to delete @i[buffer] if at all possible. If +@i[buffer] is the @f[current-buffer], then this sets the @f[current-buffer] to +the first distinct buffer in @f[buffer-history]. If @i[buffer] is displayed in +any windows, then this makes each window display the same distinct buffer. +@enddefun + + +@section(Modelines) +@index(Modelines) +@label(modelines) + +A Buffer may specify a modeline, a line of text which is displayed across the +bottom of a window to indicate status information. Modelines are described as +a list of @f[modeline-field] objects which have individual update functions and +are optionally fixed-width. These have an @f[eql] name for convenience in +referencing and updating, but the name must be unique for all created +modeline-field objects. When creating a modeline-field with a specified width, +the result of the update function is either truncated or padded on the right to +meet the constraint. All modeline-field functions must return simple strings +with standard characters, and these take a buffer and a window as arguments. +Modeline-field objects are typically shared amongst, or aliased by, different +buffers' modeline fields lists. These lists are unique allowing fields to +behave the same wherever they occur, but different buffers may display these +fields in different arrangements. + +Whenever one of the following changes occurs, all of a buffer's modeline fields +are updated: +@Begin[Itemize] +A buffer's major mode is set. + +One of a buffer's minor modes is turned on or off. + +A buffer is renamed. + +A buffer's pathname changes. + +A buffer's modified status changes. + +A window's buffer is changed. +@End[Itemize] + +The policy is that whenever one of these changes occurs, it is guaranteed that +the modeline will be updated before the next trip through redisplay. +Furthermore, since the system cannot know what modeline-field objects the +user has added whose update functions rely on these values, or how he has +changed @hid[Default Modeline Fields], we must update all the fields. When any +but the last occurs, the modeline-field update function is invoked once for +each window into the buffer. When a window's buffer changes, each +modeline-field update function is invoked once; other windows' modeline +fields should not be affected due to a given window's buffer changing. + +The user should note that modelines can be updated at any time, so update +functions should be careful to avoid needless delays (for example, waiting for +a local area network to determine information). + +@defun[fun {make-modeline-field}, keys {[name][width][function]}] +@defun1[fun {modeline-field-p}, args @i(modeline-field)] +@defun1[fun {modeline-field-name}, args @i(modeline-field)] +@f[make-modeline-field] returns a modeline-field object with @i[name], +@i[width], and @i[function]. @i[Width] defaults to @nil meaning that the field +is variable width; otherwise, the programmer must supply this as a positive +integer. @i[Function] must take a buffer and window as arguments and return a +@f[simple-string] containing only standard characters. If @i[name] already +names a modeline-field object, then this signals an error. + +@f[modeline-field-name] returns the name field of a modeline-field object. If +this is set with @f[setf], and the new name already names a modeline-field, +then the @f[setf] method signals an error. + +@f[modeline-field-p] returns @true or @nil, depending on whether its argument +is a @f[modeline-field] object. +@enddefun + +@defun[fun {modeline-field}, args {@i[name]}] +This returns the modeline-field object named @i[name]. If none exists, this +returns nil. +@enddefun + +@defun[fun {modeline-field-function}, args {@i[modeline-field]}] +Returns the function called when updating the @i[modeline-field]. When this is +set with @f[setf], the @f[setf] method updates @i[modeline-field] for all +windows on all buffers that contain the given field, so the next trip through +redisplay will reflect the change. All modeline-field functions must return +simple strings with standard characters, and they take a buffer and a window +as arguments. +@enddefun + +@defun[fun {modeline-field-width}, args {@i[modeline-field]}] +Returns the width to which @i[modeline-field] is constrained, or @nil +indicating that it is variable width. When this is set with @f[setf], the +@f[setf] method updates all modeline-fields for all windows on all buffers that +contain the given field, so the next trip through redisplay will reflect the +change. All the fields for any such modeline display must be updated, which is +not the case when setting a modeline-field's function. +@enddefun + +@defun[fun {buffer-modeline-fields}, args {@i[buffer]}] +Returns a copy of the list of @i[buffer]'s modeline-field objects. This list +can be destructively modified without affecting display of @i[buffer]'s +modeline, but modifying any particular field's components (for example, width +or function) causes the changes to be reflected the next trip through redisplay +in every modeline display that uses the modified modeline-field. When this is +set with @f[setf], @f[update-modeline-fields] is called for each window into +@i[buffer]. +@enddefun + +@defun[fun {buffer-modeline-field-p}, args {@i[buffer] @i[field]}] +If @i[field], a modeline-field or the name of one, is in buffer's list of +modeline-field objects, it is returned; otherwise, this returns nil. +@enddefun + +@defun[fun {update-modeline-fields}, args {@i[buffer] @i[window]}] +This invokes each modeline-field object's function from @i[buffer]'s list, +passing @i[buffer] and @i[window]. The results are collected regarding each +modeline-field object's width as appropriate, and the window is marked so +the next trip through redisplay will reflect the changes. If window does not +display modelines, then no computation occurs. +@enddefun + +@defun[fun {update-modeline-field}, args {@i[buffer] @i[window] @i[field-or-name}] +This invokes the modeline-field object's function for @i[field-or-name], which +is a modeline-field object or the name of one for @i[buffer]. This passes +@i[buffer] and @i[window] to the update function. The result is applied to the +@i[window]'s modeline display using the modeline-field object's width, and the +window is marked so the next trip through redisplay will reflect the changes. +If the window does not display modelines, then no computation occurs. If +@i[field-or-name] is not found in @i[buffer]'s list of modeline-field objects, +then this signals an error. See @f[buffer-modeline-field-p] above. +@enddefun + + + +@chapter(Altering and Searching Text) +@label[doing-stuff] + +@section(Altering Text) +@index(Altering text) +@index(Inserting) +@index(Deleting) +A note on marks and text alteration: @kwd[temporary] marks are invalid +after any change has been made to the text the mark points to; it is an +error to use a temporary mark after such a change has been made. If +text is deleted which has permanent marks pointing into it then they +are left pointing to the position where the text was. + +@defun[fun {insert-character}, args {@i[mark] @i[character]}] +@defun1[fun {insert-string}, args {@i[mark] @i[string]}] +@defun1[fun {insert-region}, args {@i[mark] @i[region]}] +Inserts @i[character], @i[string] or @i[region] at @i[mark]. +@f[insert-character] signals an error if @i[character] is not +@f[string-char-p]. If @i[string] or @i[region] is empty, and @i[mark] is in +some buffer, then @hemlock leaves @f[buffer-modified] of @i[mark]'s buffer +unaffected. +@enddefun + +@defun[fun {ninsert-region}, args {@i[mark] @i[region]}] +Like @f[insert-region], inserts the @i[region] at the @i[mark]'s position, +destroying the source region. This must be used with caution, since if anyone +else can refer to the source region bad things will happen. In particular, one +should make sure the region is not linked into any existing buffer. If +@i[region] is empty, and @i[mark] is in some buffer, then @hemlock leaves +@f[buffer-modified] of @i[mark]'s buffer unaffected. +@enddefun + +@defun[fun {delete-characters}, args {@i[mark] @i[n]}] +This deletes @i[n] characters after the @i[mark] (or -@i[n] before if @i[n] is +negative). If @i[n] characters after (or -@i[n] before) the @i[mark] do not +exist, then this returns @nil; otherwise, it returns @true. If @i[n] is zero, +and @i[mark] is in some buffer, then @hemlock leaves @f[buffer-modified] of +@i[mark]'s buffer unaffected. +@enddefun + +@defun[fun {delete-region}, args {@i[region]}] +This deletes @i[region]. This is faster than @f[delete-and-save-region] +(below) because no lines are copied. If @i[region] is empty and contained in +some buffer's @f[buffer-region], then @hemlock leaves @f[buffer-modified] of +the buffer unaffected. +@enddefun + +@defun[fun {delete-and-save-region}, args {@i[region]}] +This deletes @i[region] and returns a region containing the original +@i[region]'s text. If @i[region] is empty and contained in some buffer's +@f[buffer-region], then @hemlock leaves @f[buffer-modified] of the buffer +unaffected. In this case, this returns a distinct empty region. +@enddefun + +@defun[fun {filter-region}, args {@i[function] @i[region]}] +Destructively modifies @i[region] by replacing the text +of each line with the result of the application of @i[function] to a +string containing that text. @i[Function] must obey the following +restrictions: +@begin[enumerate] +The argument may not be destructively modified. + +The return value may not contain newline characters. + +The return value may not be destructively modified after it is +returned from @i[function]. +@end[enumerate] +The strings are passed in order, and are always simple strings. + +Using this function, a region could be uppercased by doing: +@lisp +(filter-region #'string-upcase region) +@endlisp +@enddefun + + +@section(Text Predicates) +@defun[fun {start-line-p}, args {@i[mark]}] +Returns @true if the @i(mark) points before the first character in a line, +@nil otherwise. +@enddefun + +@defun[fun {end-line-p}, args {@i[mark]}] +Returns @true if the @i(mark) points after the last character in a line and +before the newline, @nil otherwise. +@enddefun + +@defun[fun {empty-line-p}, args {@i[mark]}] +Return @true of the line which @i[mark] points to contains no characters. +@enddefun + +@defun[fun {blank-line-p}, args {@i[line]}] +Returns @true if @i[line] contains only characters with a +@hid[Whitespace] attribute of 1. See chapter @ref[character-attributes] for +discussion of character attributes. +@enddefun + +@defun[fun {blank-before-p}, args {@i[mark]}] +@defun1[fun {blank-after-p}, args {@i[mark]}] +These functions test if all the characters preceding or following +@i[mark] on the line it is on have a @hid[Whitespace] attribute of @f[1]. +@enddefun + +@defun[fun {same-line-p}, args {@i[mark1] @i[mark2]}] +Returns @true if @i(mark1) and @i(mark2) point to the same line, or @nil +otherwise; That is, +@example[(same-line-p a b) <==> (eq (mark-line a) (mark-line b))] +@enddefun + +@defun[fun {mark<}, funlabel {mark-LSS}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark<=}, funlabel {mark-LEQ}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark=}, funlabel {mark-EQL}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark/=}, funlabel {mark-NEQ}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark>=}, funlabel {mark-GEQ}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark>}, funlabel {mark-GTR}, args {@i[mark1] @i[mark2]}] +These predicates test the relative ordering of two marks in a piece of +text, that is a mark is @f[mark>] another if it points to a position +after it. If the marks point into different, non-connected pieces of +text, such as different buffers, then it is an error to test their +ordering; for such marks @f[mark=] is always false and @f[mark/=] is +always true. +@enddefun + +@defun[fun {line<}, funlabel {line-LSS}, args {@i[line1] @i[line2]}] +@defun1[fun {line<=}, funlabel {line-LEQ}, args {@i[line1] @i[line2]}] +@defun1[fun {line>=}, funlabel {line-GEQ}, args {@i[line1] @i[line2]}] +@defun1[fun {line>}, funlabel {line-GTR}, args {@i[line1] @i[line2]}] +These predicates test the ordering of @i[line1] and @i[line2]. If the +lines are in unconnected pieces of text it is an error to test their +ordering. +@enddefun + +@defun[fun {lines-related}, args {@i[line1] @i[line2]}] +This function returns @true if @i[line1] and @i[line2] are in the same +piece of text, or @nil otherwise. +@enddefun + +@defun[fun {first-line-p}, args {@i[mark]}] +@defun1[fun {last-line-p}, args {@i[mark]}] +@f[first-line-p] returns @true if there is no line before the line +@i[mark] is on, and @nil otherwise. @i[Last-line-p] similarly tests +tests whether there is no line after @i[mark]. +@enddefun + + +@section(Kill Ring) +@index(Kill ring) +@label(kill-ring) + +@defvar[var {kill-ring}] +This is a ring (see section @ref[rings]) of regions deleted from buffers. +Some commands save affected regions on the kill ring before performing +modifications. You should consider making the command undoable (see section +@ref[undo]), but this is a simple way of achieving a less satisfactory means +for the user to recover. +@enddefvar + +@defun[fun {kill-region}, args {@i[region] @i[current-type]}] +This kills @i[region] saving it in @var[kill-ring]. @i[Current-type] is either +@kwd[kill-forward] or @kwd[kill-backward]. When the @funref[last-command-type] +is one of these, this adds @i[region] to the beginning or end, respectively, of +the top of @var[kill-ring]. The result of calling this is undoable using the +command @hid[Undo] (see the @i[Hemlock User's Manual]). This sets +@f[last-command-type] to @i[current-type], and it interacts with +@f[kill-characters]. +@enddefun + +@defun[fun {kill-characters}, args {@i[mark] @i[count]}] +@defhvar1[var {Character Deletion Threshold}, val {5}] +@f[kill-characters] kills @i[count] characters after @i[mark] if @i[count] is +positive, otherwise before @i[mark] if @i[count] is negative. When @i[count] +is greater than or equal to @hid[Character Deletion Threshold], the killed +characters are saved on @var[kill-ring]. This may be called multiple times +contiguously (that is, without @funref[last-command-type] being set) to +accumulate an effective count for purposes of comparison with the threshold. + +This sets @f[last-command-type], and it interacts with @f[kill-region]. When +this adds a new region to @var[kill-ring], it sets @f[last-command-type] to +@kwd[kill-forward] (if @i[count] is positive) or @kwd[kill-backward] (if +@i[count] is negative). When @f[last-command-type] is @kwd[kill-forward] or +@kwd[kill-backward], this adds the killed characters to the beginning (if +@i[count] is negative) or the end (if @i[count] is positive) of the top of +@var[kill-ring], and it sets @f[last-command-type] as if it added a new region +to @var[kill-ring]. When the kill ring is unaffected, this sets +@f[last-command-type] to @kwd[char-kill-forward] or @kwd[char-kill-backward] +depending on whether @i[count] is positive or negative, respectively. + +This returns mark if it deletes characters. If there are not @i[count] +characters in the appropriate direction, this returns nil. +@enddefun + + +@section(Active Regions) +@index(Active regions) +@label(active-regions) + +Every buffer has a mark stack (page @pageref[mark-stack]) and a mark known as +the point where most text altering nominally occurs. Between the top of the +mark stack, the @f[current-mark], and the @f[current-buffer]'s point, the +@f[current-point], is what is known as the @f[current-region]. Certain +commands signal errors when the user tries to operate on the @f[current-region] +without its having been activated. If the user turns off this feature, then +the @f[current-region] is effectively always active. + +When writing a command that marks a region of text, the programmer should make +sure to activate the region. This typically occurs naturally from the +primitives that you use to mark regions, but sometimes you must explicitly +activate the region. These commands should be written this way, so they do not +require the user to separately mark an area and then activate it. Commands +that modify regions do not have to worry about deactivating the region since +modifying a buffer automatically deactivates the region. Commands that insert +text often activate the region ephemerally; that is, the region is active for +the immediately following command, allowing the user wants to delete the region +inserted, fill it, or whatever. + +Once a marking command makes the region active, it remains active until: +@begin[itemize] +a command uses the region, + +a command modifies the buffer, + +a command changes the current window or buffer, + +a command signals an editor-error, + +or the user types @binding[C-g]. +@end[itemize] + +@defhvar[var "Active Regions Enabled", val {t}] +When this variable is non-@nil, some primitives signal an editor-error if +the region is not active. This may be set to @nil for more traditional @emacs +region semantics. +@enddefhvar + +@defvar[var {ephemerally-active-command-types}] +This is a list of command types (see section @ref[command-types]), and its +initial value is the list of @kwd[ephemerally-active] and @kwd[unkill]. When +the previous command's type is one of these, the @f[current-region] is active +for the currently executing command only, regardless of whether it does +something to deactivate the region. However, the current command may activate +the region for future commands. @kwd[ephemerally-active] is a default command +type that may be used to ephemerally activate the region, and @kwd[unkill] is +the type used by two commands, @hid[Un-kill] and @hid[Rotate Kill Ring] (what +users typically think of as @binding[C-y] and @binding[M-y]). +@enddefvar + +@defun[fun {activate-region}] +This makes the @f[current-region] active. +@enddefun + +@defun[fun {deactivate-region}] +After invoking this the @f[current-region] is no longer active. +@enddefun + +@defun[fun {region-active-p}] +Returns whether the @f[current-region] is active, including ephemerally. This +ignores @hid[Active Regions Enabled]. +@enddefun + +@defun[fun {check-region-active}] +This signals an editor-error when active regions are enabled, and the +@f[current-region] is not active. +@enddefun + +@defun[fun {current-region}, + args {@optional @i[error-if-not-active] @i[deactivate-region]}] +This returns a region formed with @f[current-mark] and @f[current-point], +optionally signaling an editor-error if the current region is not active. +@i[Error-if-not-active] defaults to @true. Each call returns a distinct region +object. Depending on @i[deactivate-region] (defaults to @true), fetching the +current region deactivates it. @hemlock primitives are free to modify text +regardless of whether the region is active, so a command that checks for this +can deactivate the region whenever it is convenient. +@enddefun + + +@section(Searching and Replacing) +@index(Searching) +@index(Replacing) + +Before using any of these functions to do a character search, look at character +attributes (page @pageref[character-attributes]). They provide a facility +similar to the syntax table in real EMACS. Syntax tables are a powerful, +general, and efficient mechanism for assigning meanings to characters in +various modes. + +@defcon[var {search-char-code-limit}] +An exclusive upper limit for the char-code of characters given to the searching +functions. The result of searches for characters with a char-code greater than +or equal to this limit is ill-defined, but it is @i[not] an error to do such +searches. +@enddefcon + +@defun[fun {new-search-pattern}, +args {@i[kind] @i[direction] @i[pattern] @optional @i[result-search-pattern]}] + +Returns a @i[search-pattern] object which can be given to the @f[find-pattern] +and @f[replace-pattern] functions. A search-pattern is a specification of a +particular sort of search to do. @i[direction] is either @kwd[forward] or +@kwd[backward], indicating the direction to search in. @i[kind] specifies the +kind of search pattern to make, and @i[pattern] is a thing which specifies what +to search for. + +The interpretation of @i[pattern] depends on the @i[kind] of pattern being +made. Currently defined kinds of search pattern are: +@begin(description) +@kwd[string-insensitive]@\Does a case-insensitive string search, +@i[pattern] being the string to search for. + +@kwd[string-sensitive]@\Does a case-sensitive string search for +@i[pattern]. + +@kwd[character]@\Finds an occurrence of the character @i[pattern]. +This is case sensitive. + +@kwd[not-character]@\Find a character which is not the character +@i[pattern]. + +@kwd[test]@\Finds a character which satisfies the function @i[pattern]. +This function may not be applied an any particular fashion, so it +should depend only on what its argument is, and should have no +side-effects. + +@kwd[test-not]@\Similar to as @kwd[test], except it finds a character that +fails the test. + +@kwd[any]@\Finds a character that is in the string @i[pattern]. + +@kwd[not-any]@\Finds a character that is not in the string @i[pattern]. +@end(description) + +@i[result-search-pattern], if supplied, is a search-pattern to +destructively modify to produce the new pattern. Where reasonable +this should be supplied, since some kinds of search patterns may +involve large data structures. +@enddefun + +@defun[fun {search-pattern-p}, args {@i[search-pattern]}] +Returns @true if @i[search-pattern] is a @f[search-pattern] object, otherwise +@nil. +@enddefun + +@defun[fun {get-search-pattern}, args {@i[string] @i[direction]}] +@defvar1[var {last-search-pattern}] +@defvar1[var {last-search-string}] +@f[get-search-pattern] interfaces to a default search string and pattern that +search and replacing commands can use. These commands then share a default +when prompting for what to search or replace, and save on consing a search +pattern each time they execute. This uses @hid[Default Search Kind] (see the +@i[Hemlock User's Manual]) when updating the pattern object. This returns the +pattern, so you probably don't need to refer to @var[last-search-pattern], but +@var[last-search-string] is useful when prompting. +@enddefun + +@defun[fun {find-pattern}, args {@i[mark] @i[search-pattern]}] +Find the next match of @i[search-pattern] starting at @i[mark]. If a +match is found then @i[mark] is altered to point before the matched text +and the number of characters matched is returned. If no match is +found then @nil is returned and @i[mark] is not modified. +@enddefun + +@defun[fun {replace-pattern}, args + {@i[mark] @i[search-pattern] @i[replacement] @optional @i[n]}] +Replace @i[n] matches of @i[search-pattern] with the string +@i[replacement] starting at @i[mark]. If @i[n] is @nil (the default) +then replace all matches. A mark pointing before the last replacement +done is returned. +@enddefun + + + +@Chapter(The Current Environment) +@label(current-environment) +@index(Current environment) + +@section(Different Scopes) + In @hemlock the values of @i[variables] (page @pageref[variables]), +@i[key-bindings] (page @pageref(key-bindings)) and +@i[character-attributes] (page @pageref[character-attributes]) may +depend on the @funref(current-buffer) and the modes +active in it. There are three possible scopes for +@hemlock values: +@begin(description) +@i[buffer local]@\The value is present only if the buffer it is local +to is the @f[current-buffer]. + +@i[mode local]@\The value is present only when the mode it is local to +is active in the @f[current-buffer]. + +@i[global]@\The value is always present unless shadowed by a buffer or +mode local value. +@end(description) + + +@section(Shadowing) + It is possible for there to be a conflict between different values +for the same thing in different scopes. For example, there be might a +global binding for a given variable and also a local binding in the +current buffer. Whenever there is a conflict shadowing occurs, +permitting only one of the values to be visible in the current +environment. + + The process of resolving such a conflict can be described as a +search down a list of places where the value might be defined, returning +the first value found. The order for the search is as follows: +@begin(enumerate) +Local values in the current buffer. + +Mode local values in the minor modes of the current buffer, in order +from the highest precedence mode to the lowest precedence mode. The +order of minor modes with equal precedences is undefined. + +Mode local values in the current buffer's major mode. + +Global values. +@end(enumerate) + + + +@chapter(Hemlock Variables) +@index (Hemlock variables) +@label(variables) +@hemlock implements a system of variables separate from normal Lisp variables +for the following reasons: +@begin(enumerate) +@hemlock has different scoping rules which are useful in an editor. @hemlock +variables can be local to a @i(buffer) (page @pageref[buffers]) or a @i(mode) +(page @pageref[modes]). + +@hemlock variables have @i(hooks) (page @pageref[hooks]), lists of functions +called when someone sets the variable. See @f[variable-value] for the +arguments @hemlock passes to these hook functions. + +There is a database of variable names and documentation which makes it easier +to find out what variables exist and what their values mean. +@end(enumerate) + + +@section(Variable Names) +To the user, a variable name is a case insensitive string. This +string is referred to as the @i[string name] of the variable. A +string name is conventionally composed of words separated by spaces. + +In Lisp code a variable name is a symbol. The name of this symbol is +created by replacing any spaces in the string name with hyphens. This +symbol name is always interned in the @hemlock package and referring +to a symbol with the same name in the wrong package is an error. + +@defvar[var {global-variable-names}] +This variable holds a string-table of the names of all the global @hemlock +variables. The value of each entry is the symbol name of the variable. +@enddefvar + +@defun[fun {current-variable-tables}] +This function returns a list of variable tables currently established, +globally, in the @f[current-buffer], and by the modes of the +@f[current-buffer]. This list is suitable for use with +@f[prompt-for-variable]. +@enddefun + + +@section(Variable Functions) +In the following descriptions @i[name] is the symbol name of the variable. + +@defun[fun {defhvar}, args {@i[string-name] @i[documentation]}, + keys {[mode][buffer][hooks][value]}] + This function defines a @hemlock variable. Functions that take a variable +name signal an error when the variable is undefined. +@begin(description) +@i[string-name]@\The string name of the variable to define. + +@i[documentation]@\The documentation string for the variable. + +@multiple{ +@kwd[mode], +@kwd[buffer]}@\ + If @i[buffer] is supplied, the variable is local to that buffer. If @i[mode] +is supplied, it is local to that mode. If neither is supplied, it is global. + +@kwd[value]@\ + This is the initial value for the variable, which defaults to @nil. + +@kwd[hooks]@\ + This is the initial list of functions to call when someone sets the variable's +value. These functions execute before @hemlock establishes the new value. See +@f[variable-value] for the arguments passed to the hook functions. +@end(description) +If a variable with the same name already exists in the same place, then +@f[defhvar] sets its hooks and value from @i[hooks] and @i[value] if the user +supplies these keywords. +@enddefun + +@defun[fun {variable-value}, args {@i[name] @optional @i[kind] @i[where]}] +This function returns the value of a @hemlock variable in some place. +The following values for @i[kind] are defined: +@begin[description] +@kwd[current]@\ + Return the value present in the current environment, taking into consideration +any mode or buffer local variables. This is the default. + +@kwd[global]@\ + Return the global value. + +@kwd[mode]@\ + Return the value in the mode named @i[where]. + +@kwd[buffer]@\ + Return the value in the buffer @i[where]. +@end[description] +When set with @f[setf], @hemlock sets the value of the specified variable and +invokes the functions in its hook list with @i[name], @i[kind], @i[where], and +the new value. +@enddefun + +@defun[fun {variable-documentation}, args + {@i[name] @optional @i[kind] @i[where]}] +@defun1[fun {variable-hooks}, args + {@i[name] @optional @i[kind] @i[where]}] +@defun1[fun {variable-name}, args + {@i[name] @optional @i[kind] @i[where]}] +These function return the documentation, hooks and string name of a +@hemlock variable. The @i[kind] and @i[where] arguments are the same +as for @f[variable-value]. The documentation and hook list may be set +using @f[setf]. +@enddefun + +@defun[fun {string-to-variable}, args {@i[string]}] +This function converts a string into the corresponding variable symbol +name. @i[String] need not be the name of an actual @hemlock variable. +@enddefun + +@defmac[fun {value}, args {@i[name]}] +@defmac1[fun {setv}, args {@i[name] @i[new-value]}] +These macros get and set the current value of the @hemlock variable +@i[name]. @i[Name] is not evaluated. There is a @f[setf] form for +@f[value]. +@enddefmac + +@Defmac[Fun {hlet}, Args {(@Mstar<(@i[var] @i[value])>) @Mstar<@i[form]>}] +This macro is very similar to @f[let] in effect; within its scope each +of the @hemlock variables @i[var] have the respective @i[value]s, but +after the scope is exited by any means the binding is removed. This +does not cause any hooks to be invoked. The value of the last +@i[form] is returned. +@enddefmac + +@defun[fun {hemlock-bound-p}, args {@i[name] @optional @i[kind] @i[where]}] +Returns @true if @i[name] is defined as a @hemlock variable in the +place specified by @i[kind] and @i[where], or @nil otherwise. +@enddefun + +@defun[fun {delete-variable}, args {@i(name) @optional @i[kind] @i[where]}] +@defhvar1[var {Delete Variable Hook}] +@f[delete-variable] makes the @hemlock variable @i[name] no longer +defined in the specified place. @i[Kind] and @i[where] have the same +meanings as they do for @f[variable-value], except that @kwd[current] +is not available, and the default for @i[kind] is @kwd[global] + +An error will be signaled if no such variable exists. The hook, +@hid[Delete Variable Hook] is invoked with the same arguments before the +variable is deleted. +@enddefun + + +@section(Hooks) +@index(Hooks) +@label[hooks] +@hemlock actions such as setting variables, changing buffers, changing windows, +turning modes on and off, etc., often have hooks associated with them. A hook +is a list of functions called before the system performs the action. The +manual describes the object specific hooks with the rest of the operations +defined on these objects. + +Often hooks are stored in @hemlock variables, @hid[Delete Buffer Hook] and +@hid[Set Window Hook] for example. This leads to a minor point of confusion +because these variables have hooks that the system executes when someone +changes their values. These hook functions @hemlock invokes when someone sets +a variable are an example of a hook stored in an object instead of a @hemlock +variable. These are all hooks for editor activity, but @hemlock keeps them in +different kinds of locations. This is why some of the routines in this section +have a special interpretation of the hook @i[place] argument. + +@defmac[fun {add-hook}, args {@i[place] @i[hook-fun]}] +@defmac1[fun {remove-hook}, args {@i[place] @i[hook-fun]}] +These macros add or remove a hook function in some @i[place]. If @i[hook-fun] +already exists in @i[place], this call has no effect. If @i[place] is a +symbol, then it is a @hemlock variable; otherwise, it is a generalized variable +or storage location. Here are two examples: +@Begin[ProgramExample] +(add-hook delete-buffer-hook 'remove-buffer-from-menu) + +(add-hook (variable-hooks 'check-mail-interval) + 'reschedule-mail-check) +@End[ProgramExample] +@enddefmac + +@defmac[fun {invoke-hook}, args {@i[place] @rest @i[args]}] +This macro calls all the functions in @i[place]. If @i[place] is a symbol, +then it is a @hemlock variable; otherwise, it is a generalized variable. +@enddefun + + + +@chapter(Commands) +@index (Commands) +@label[commands] + + +@section(Introduction) +The way that the user tells @hemlock to do something is by invoking a +@i(command). Commands have three attributes: +@begin(description) +@i[name]@\A command's name provides a way to refer to it. Command +names are usually capitalized words separated by spaces, such as +@hid[Forward Word]. + +@i[documentation]@\The documentation for a command is used by +on-line help facilities. + +@i[function]@\A command is implemented by a Lisp function, which is callable +from Lisp. +@end(description) + +@defvar[var {command-names}] +Holds a string-table (page @pageref[string-tables]) associating +command names to command objects. Whenever a new command is defined +it is entered in this table. +@enddefvar + + +@subsection(Defining Commands) + +@defmac[fun {defcommand}, args +{@^@mgroup<@i[command-name] @MOR (@i[command-name] @i[function-name])> @i[lambda-list] +@\@i[command-doc] @i[function-doc] @mstar<@i[form]>}] + +Defines a command named @i[name]. @f[defcommand] creates a function to +implement the command from the @i[lambda-list] and @i[form]'s supplied. The +@i[lambda-list] must specify one required argument, see section +@ref[invoking-commands-as-functions], which by convention is typically named +@f[p]. If the caller does not specify @i[function-name], @f[defcommand] +creates the command name by replacing all spaces with hyphens and appending +"@f[-command]". @i[Function-doc] becomes the documentation for the function +and should primarily describe issues involved in calling the command as a +function, such as what any additional arguments are. @i[Command-doc] becomes +the command documentation for the command. @enddefmac + +@defun[fun {make-command}, args + {@i[name] @i[documentation] @i[function]}] +Defines a new command named @i[name], with command documentation +@I[documentation] and function @i[function]. The command in entered +in the string-table @varref[command-names], with the command object as +its value. Normally command implementors will use the @f[defcommand] +macro, but this permits access to the command definition mechanism at +a lower level, which is occasionally useful. +@enddefun + +@defun[fun {commandp}, args {@i[command]}] +Returns @true if @i[command] is a @f[command] object, otherwise @nil. +@enddefun + +@defun[fun {command-documentation}, args {@i[command]}] +@defun1[fun {command-function}, args {@i[command]}] +@defun1[fun {command-name}, args {@i[command]}] +Returns the documentation, function, or name for @i[command]. These +may be set with @f[setf]. +@enddefun + + +@subsection(Command Documentation) +@i[Command documentation] is a description of what the command does +when it is invoked as an extended command or from a key. Command +documentation may be either a string or a function. If the +documentation is a string then the first line should briefly summarize +the command, with remaining lines filling the details. Example: +@lisp +(defcommand "Forward Character" (p) + "Move the point forward one character. + With prefix argument move that many characters, with negative + argument go backwards." + "Move the point of the current buffer forward p characters." + . . .) +@endlisp + +Command documentation may also be a function of one argument. The +function is called with either @kwd[short] or @kwd[full], indicating +that the function should return a short documentation string or do +something to document the command fully. + + +@section(The Command Interpreter) +@index[Interpreter, command] +@index[Invocation, command] +@index[Command interpreter] + +The @i[command interpreter] is a function which reads key-events (see section +@ref[key-events-intro]) from the keyboard and dispatches to different commands +on the basis of what the user types. When the command interpreter executes a +command, we say it @i[invokes] the command. The command interpreter also +provides facilities for communication between commands contiguously running +commands, such as a last command type register. It also takes care of +resetting communication mechanisms, clearing the echo area, displaying partial +keys typed slowly by the user, etc. + +@defvar[var {invoke-hook}] +This variable contains a function the command interpreter calls when it wants +to invoke a command. The function receives the command and the prefix argument +as arguments. The initial value is a function which simply funcalls the +@f[command-function] of the command with the supplied prefix argument. This is +useful for implementing keyboard macros and similar things. +@enddefhvar + +@defhvar[var "Command Abort Hook"] +The command interpreter invokes the function in this variable whenever someone +aborts a command (for example, if someone called @f[editor-error]). +@enddefhvar + +When @hemlock initially starts the command interpreter is in control, but +commands may read from the keyboard themselves and assign whatever +interpretation they will to the key-events read. Commands may call the command +interpreter recursively using the function @funref[recursive-edit]. + + +@subsection(Editor Input) +@label[key-events-intro] +@index[key-events] + +The canonical representation of editor input is a key-event structure. Users +can bind commands to keys (see section @ref[key-bindings]), which are non-zero +length sequences of key-events. A key-event consists of an identifying token +known as a @i[keysym] and a field of bits representing modifiers. Users define +keysyms, integers between 0 and 65535 inclusively, by supplying names that +reflect the legends on their keyboard's keys. Users define modifier names +similarly, but the system chooses the bit and mask for recognizing the +modifier. You can use keysym and modifier names to textually specify +key-events and Hemlock keys in a @f[#k] syntax. The following are some +examples: +@begin[programexample] + #k"C-u" + #k"Control-u" + #k"c-m-z" + #k"control-x meta-d" + #k"a" + #k"A" + #k"Linefeed" +@end[programexample] +This is convenient for use within code and in init files containing +@f[bind-key] calls. + +The @f[#k] syntax is delimited by double quotes, but the system parses the +contents rather than reading it as a Common Lisp string. Within the double +quotes, spaces separate multiple key-events. A single key-event optionally +starts with modifier names terminated by hyphens. Modifier names are +alphabetic sequences of characters which the system uses case-insensitively. +Following modifiers is a keysym name, which is case-insensitive if it consists +of multiple characters, but if the name consists of only a single character, +then it is case-sensitive. + +You can escape special characters @dash hyphen, double quote, open angle +bracket, close angle bracket, and space @dash with a backslash, and you can +specify a backslash by using two contiguously. You can use angle brackets to +enclose a keysym name with many special characters in it. Between angle +brackets appearing in a keysym name position, there are only two special +characters, the closing angle bracket and backslash. + +For more information on key-events see section @ref[key-events]. + + + +@subsection(Binding Commands to Keys) +@label[Key-Bindings] +@Index[Key Bindings] + +The command interpreter determines which command to invoke on the basis of +@i[key bindings]. A key binding is an association between a command and a +sequence of key-events (see section @ref[key-events-intro]. A sequence of +key-events is called a @i[key] and is represented by a single key-event or a +sequence (list or vector) of key-events. + +Since key bindings may be local to a mode or buffer, the current environment +(page @pageref[current-environment]) determines the set of key bindings in +effect at any given time. When the command interpreter tries to find the +binding for a key, it first checks if there is a local binding in the +@w[@funref[current-buffer]], then if there is a binding in each of the minor +modes and the major mode for the current buffer @w[(page @pageref[modes])], and +finally checks to see if there is a global binding. If no binding is found, +then the command interpreter beeps or flashes the screen to indicate this. + +@defun[fun {bind-key}, args + {@i(name) @i(key) @optional @i[kind] @i[where]}] + This function associates command @i[name] and @i[key] in some environment. +@i[Key] is either a key-event or a sequence of key-events. There are three +possible values of @i[kind]: +@begin(description) +@kwd[global]@\ + The default, make a global key binding. + +@kwd[mode]@\ + Make a mode specific key binding in the mode whose name is @i[where]. + +@kwd[buffer]@\ + Make a binding which is local to buffer @i[where]. +@end(description) + +This processes @i[key] for key translations before establishing the binding. +See section @ref[key-trans]. + +If the key is some prefix of a key binding which already exists in the +specified place, then the new one will override the old one, effectively +deleting it. + +@f[ext:do-alpha-key-events] is useful for setting up bindings in certain new +modes. +@enddefun + +@defun[fun {command-bindings}, args {@i[command]}] +This function returns a list of the places where @i[command] is bound. A place +is specified as a list of the key (always a vector), the kind of binding, and +where (either the mode or buffer to which the binding is local, or @nil if it +is a global). +@enddefun + +@defun[fun {delete-key-binding}, args {@i[key] @optional @i[kind] @i[where]}] +This function removes the binding of @i[key] in some place. @i[Key] is either +a key-event or a sequence of key-events. @i[kind] is the kind of binding to +delete, one of @kwd[global] (the default), @kwd[mode] or @kwd[buffer]. If +@i[kind] is @kwd[mode], @i[where] is the mode name, and if @i[kind] is +@kwd[buffer], then @i[where] is the buffer. + +This function signals an error if @i[key] is unbound. + +This processes @i[key] for key translations before deleting the binding. See +section @ref[key-trans]. +@enddefun + +@defun[fun {get-command}, args {@i[key] @optional @i[kind] @i[where]}] +This function returns the command bound to @i[key], returning @nil if it is +unbound. @i[Key] is either a key-event or a sequence of key-events. If +@i[key] is an initial subsequence of some keys, then this returns the keyword +@kwd[prefix]. There are four cases of @i[kind]: +@begin(description) +@kwd[current]@\ + Return the current binding of @i[key] using the current buffer's search list. +If there are any transparent key bindings for @i[key], then they are returned +in a list as a second value. + +@kwd[global]@\ + Return the global binding of @i[key]. This is the default. + +@kwd[mode]@\ + Return the binding of @i[key] in the mode named @i[where]. + +@kwd[buffer]@\ + Return the binding of @i[key] local to the buffer @i[where]. +@end(description) + +This processes @i[key] for key translations before looking for any binding. +See section @ref[key-trans]. +@enddefun + +@defun[fun {map-bindings}, Args {@i[function] @i[kind] @optional @i[where]}] +This function maps over the key bindings in some place. For each binding, this +passes @i[function] the key and the command bound to it. @i[Kind] and +@i[where] are the same as in @f[bind-key]. The key is not guaranteed to remain +valid after a given iteration. +@enddefmac + + +@subsection[Key Translation] +@index[bit-prefix keys] +@index[key translation] +@index[translating keys] +@label[key-trans] +Key translation is a process that the command interpreter applies to keys +before doing anything else. There are two kinds of key translations: +substitution and bit-prefix. In either case, the command interpreter +translates a key when a specified key-event sequence appears in a key. + +In a substitution translation, the system replaces the matched subsequence with +another key-event sequence. Key translation is not recursively applied to the +substituted key-events. + +In a bit-prefix translation, the system removes the matched subsequence and +effectively sets the specified bits in the next key-event in the key. + +While translating a key, if the system encounters an incomplete final +subsequence of key-events, it aborts the translation process. This happens +when those last key-events form a prefix of some translation. It also happens +when they translate to a bit-prefix, but there is no following key-event to +which the system can apply the indicated modifier. If there is a binding for +this partially untranslated key, then the command interpreter will invoke that +command; otherwise, it will wait for the user to type more key-events. + +@defun[fun {key-translation}, args {@i[key]}] +This form is @f[setf]'able and allows users to register key translations that +the command interpreter will use as users type key-events. + +This function returns the key translation for @i[key], returning @nil if there +is none. @i[Key] is either a key-event or a sequence of key-events. If +@i[key] is a prefix of a translation, then this returns @kwd[prefix]. + +A key translation is either a key or modifier specification. The bits +translations have a list form: @w<@f[(:bits {]@i[bit-name]@f[}*)]>. + +Whenever @i[key] appears as a subsequence of a key argument to the binding +manipulation functions, that portion will be replaced with the translation. +@enddefun + + + +@subsection[Transparent Key Bindings] +@label[transparent-key-bindings] +@index[Transparent key bindings] + +Key bindings local to a mode may be @i[transparent]. A transparent key +binding does not shadow less local key bindings, but rather indicates that +the bound command should be invoked before the first normal key binding. +Transparent key bindings are primarily useful for implementing minor modes +such as auto fill and word abbreviation. There may be several transparent +key bindings for a given key, in which case all of the commands bound are +invoked in the order they were found. If there no normal key binding for a +key typed, then the command interpreter acts as though the key is unbound +even if there are transparent key bindings. + +The @kwd[transparent-p] argument to @funref[defmode] determines whether the +key bindings in a mode are transparent or not. + + +@subsection (Interactive) +@index (Keyboard macro vs. interactive) +@index (Interactive vs. keyboard macro) +@Hemlock supports keyboard macros. A user may enter a mode where the editor +records his actions, and when the user exits this mode, the command @hid[Last +Keyboard Macro] plays back the actions. Some commands behave differently when +invoked as part of the definition of a keyboard macro. For example, when used +in a keyboard macro, a command that @f[message]'s useless user confirmation +will slow down the repeated invocations of @hid[Last Keyboard Macro] because +the command will pause on each execution to make sure the user sees the +message. This can be eliminated with the use of @f[interactive]. As another +example, some commands conditionally signal an editor-error versus simply +beeping the device depending on whether it executes on behalf of the user or a +keyboard macro. + +@defun[fun {interactive}] +This returns @true when the user invoked the command directly. +@enddefun + + +@section(Command Types) +@index(Command types) +@label(command-types) +In many editors the behavior of a command depends on the kind of command +invoked before it. @hemlock provides a mechanism to support this known as +@i(command type). + +@defun[fun {last-command-type}] +This returns the command type of the last command invoked. If this is set with +@f[setf], the supplied value becomes the value of @f[last-command-type] until +the next command completes. If the previous command did not set +@f[last-command-type], then its value is @nil. Normally a command type is a +keyword. The command type is not cleared after a command is invoked due to a +transparent key binding. +@enddefun + + +@section(Command Arguments) +@label[invoking-commands-as-functions] +There are three ways in which a command may be invoked: It may be bound to a +key which has been typed, it may be invoked as an extended command, or it may +be called as a Lisp function. Ideally commands should be written in such a way +that they will behave sensibly no matter which way they are invoked. The +functions which implement commands must obey certain conventions about argument +passing if the command is to function properly. + + +@subsection(The Prefix Argument) +@index(Prefix arguments) +Whenever a command is invoked it is passed as its first argument what +is known as the @i[prefix argument]. The prefix argument is always +either an integer or @nil. When a command uses this value it is +usually as a repeat count, or some conceptually similar function. + +@defun[fun {prefix-argument}] +This function returns the current value of the prefix argument. When +set with @f[setf], the new value becomes the prefix argument for the +next command. +@enddefun + +If the prefix argument is not set by the previous command then the +prefix argument for a command is @nil. The prefix argument is not cleared +after a command is invoked due to a transparent key binding. + + +@subsection(Lisp Arguments) +It is often desirable to call commands from Lisp code, in which case +arguments which would otherwise be prompted for are passed as optional +arguments following the prefix argument. A command should prompt for +any arguments not supplied. + + +@section(Recursive Edits) +@index(Recursive edits) +@defmac[fun {use-buffer}, args {@i[buffer] @mstar<@i[form]>}] +The effect of this is similar to setting the current-buffer to @i[buffer] +during the evaluation of @i[forms]. There are restrictions placed on what the +code can expect about its environment. In particular, the value of any global +binding of a @hemlock variable which is also a mode local variable of some mode +is ill-defined; if the variable has a global binding it will be bound, but the +value may not be the global value. It is also impossible to nest +@f[use-buffer]'s in different buffers. The reason for using @f[use-buffer] is +that it may be significantly faster than changing @f[current-buffer] to +@i[buffer] and back. +@enddefmac + +@defun[fun {recursive-edit}, args {@optional @i[handle-abort]}] +@defhvar1[var {Enter Recursive Edit Hook}] +@index[aborting] +@f[recursive-edit] invokes the command interpreter. The command interpreter +will read from the keyboard and invoke commands until it is terminated with +either @f[exit-recursive-edit] or @f[abort-recursive-edit]. + +Normally, an editor-error or @bf[C-g] aborts the command in progress and +returns control to the top-level command loop. If @f[recursive-edit] is used +with @i[handle-abort] true, then @f[editor-error] or @bf[C-g] will only abort +back to the recursive command loop. + +Before the command interpreter is entered the hook +@hid[Enter Recursive Edit Hook] is invoked. +@enddefun + +@defun[fun {in-recursive-edit}] +This returns whether the calling point is dynamically within a recursive edit +context. +@enddefun + +@defun[fun {exit-recursive-edit}, args {@optional @i[values-list]}] +@defhvar1[var {Exit Recursive Edit Hook}] +@f[exit-recursive-edit] exits a recursive edit returning as multiple values +each element of @i[values-list], which defaults to @nil. This invokes +@hid[Exit Recursive Edit Hook] after exiting the command interpreter. If no +recursive edit is in progress, then this signals an error. +@enddefun + +@defun[fun {abort-recursive-edit}, args {@rest @i[args]}] +@defhvar1[var {Abort Recursive Edit Hook}] +@f[abort-recursive-edit] terminates a recursive edit by applying +@funref[editor-error] to @i[args] after exiting the command interpreter. This +invokes @hid[Abort Recursive Edit Hook] with @i[args] before aborting the +recursive edit . If no recursive edit is in progress, then this signals an +error. +@enddefun + + + +@Chapter(Modes) +@label[modes] +@index (Modes) +A mode is a collection of @hemlock values which may be present in the current +environment @w<(page @pageref(current-environment))> depending on the editing +task at hand. Examples of typical modes are @hid[Lisp], for editing Lisp code, +and @hid[Echo Area], for prompting in the echo area. + + +@section(Mode Hooks) + When a mode is added to or removed from a buffer, its @i[mode hook] +is invoked. The hook functions take two arguments, the buffer +involved and @true if the mode is being added or @nil if it is being +removed. + +Mode hooks are typically used to make a mode do something additional to +what it usually does. One might, for example, make a text mode hook +that turned on auto-fill mode when you entered. + + +@section(Major and Minor Modes) +There are two kinds of modes, @i[major] modes and @i[minor] modes. A buffer +always has exactly one major mode, but it may have any number of minor modes. +Major modes may have mode character attributes while minor modes may not. + +A major mode is usually used to change the environment in some major way, such +as to install special commands for editing some language. Minor modes +generally change some small attribute of the environment, such as whether lines +are automatically broken when they get too long. A minor mode should work +regardless of what major mode and minor modes are in effect. + +@defhvar[var {Default Modes}, val {("Fundamental" "Save")}] +This variable contains a list of mode names which are instantiated in a +buffer when no other information is available. +@enddefhvar + +@defvar[var {mode-names}] +Holds a string-table of the names of all the modes. +@enddefvar + +@defcom[com "Illegal"] +This is a useful command to bind in modes that wish to shadow global bindings +by making them effectively illegal. Also, although less likely, minor modes +may shadow major mode bindings with this. This command calls @f[editor-error]. +@enddefcom + + +@section(Mode Functions) + +@defun[fun {defmode}, args {@i[name]}, + keys {[setup-function][cleanup-function][major-p]}, + morekeys {[precedence][transparent-p][documentation]}] +This function defines a new mode named @i[name], and enters it in +@varref[mode-names]. If @i[major-p] is supplied and is not @nil +then the mode is a major mode; otherwise it is a minor mode. + +@i[Setup-function] and @i[cleanup-function] are functions which are +invoked with the buffer affected, after the mode is turned on, and +before it is turned off, respectively. These functions typically are +used to make buffer-local key or variable bindings and to remove them +when the mode is turned off. + +@i[Precedence] is only meaningful for a minor mode. The precedence of a +minor mode determines the order in which it in a buffer's list of modes. +When searching for values in the current environment, minor modes are +searched in order, so the precedence of a minor mode determines which value +is found when there are several definitions. + +@i[Transparent-p] determines whether key bindings local to the defined mode +are transparent. Transparent key bindings are invoked in addition to the +first normal key binding found rather than shadowing less local key bindings. + +@i[Documentation] is some introductory text about the mode. Commands such as +@hid[Describe Mode] use this. +@enddefun + +@defun[fun {mode-documentation}, args {@i[name]}] +This function returns the documentation for the mode named @i[name]. +@enddefun + +@defun[fun {buffer-major-mode}, args {@i[buffer]}] +@defhvar1[var {Buffer Major Mode Hook}] +@f[buffer-major-mode] returns the name of @i[buffer]'s major mode. +The major mode may be changed with @f[setf]; then + @hid[Buffer Major Mode Hook] is invoked with +@i[buffer] and the new mode. +@enddefun + +@defun[fun {buffer-minor-mode}, args {@i[buffer] @i[name]}] +@defhvar1[var {Buffer Minor Mode Hook}] +@f[buffer-minor-mode] returns @true if the minor mode @i[name] is active +in @i[buffer], @nil otherwise. A minor mode may be turned on or off +by using @f[setf]; then @hid[Buffer Minor Mode Hook] is +invoked with @i[buffer], @i[name] and the new value. +@enddefun + +@defun[fun {mode-variables}, args {@i[name]}] +Returns the string-table of mode local variables. +@enddefun + +@defun[fun {mode-major-p}, args {@i[name]}] +Returns @true if @i[name] is the name of a major mode, or @nil if +it is the name of a minor mode. It is an error for @i[name] not to be +the name of a mode. +@enddefun + + + +@chapter(Character Attributes) +@label(character-attributes) +@index(Character attributes) +@index(Syntax tables) + +@section(Introduction) +Character attributes provide a global database of information about characters. +This facility is similar to, but more general than, the @i[syntax tables] of +other editors such as @f[EMACS]. For example, you should use character +attributes for commands that need information regarding whether a character is +@i[whitespace] or not. Use character attributes for these reasons: +@begin(enumerate) +If this information is all in one place, then it is easy the change the +behavior of the editor by changing the syntax table, much easier than it would +be if character constants were wired into commands. + +This centralization of information avoids needless duplication of effort. + +The syntax table primitives are probably faster than anything that can be +written above the primitive level. +@end(enumerate) + +Note that an essential part of the character attribute scheme is that +@i[character attributes are global and are there for the user to change.] +Information about characters which is internal to some set of commands (and +which the user should not know about) should not be maintained as a character +attribute. For such uses various character searching abilities are provided by +the function @funref[find-pattern]. + +@defcon[var {syntax-char-code-limit}] +The exclusive upper bound on character codes which are significant in +the character attribute functions. Font and bits are always ignored. +@enddefcon + + +@section(Character Attribute Names) + +As for @hemlock variables, character attributes have a user visible +string name, but are referred to in Lisp code as a symbol. The string +name, which is typically composed of capitalized words separated by +spaces, is translated into a keyword by replacing all spaces with +hyphens and interning this string in the keyword package. The +attribute named @hid[Ada Syntax] would thus become @kwd[ada-syntax]. + +@defvar[var {character-attribute-names}] +Whenever a character attribute is defined, its name is entered in +this string table (page @pageref[string-tables]), with the +corresponding keyword as the value. +@enddefvar + + +@section(Character Attribute Functions) + +@defun[fun {defattribute}, args + {@i[name] @i[documentation] @optional @i[type] @i[initial-value]}] + This function defines a new character attribute with @i[name], a +simple-string. Character attribute operations take attribute arguments as a +keyword whose name is @i[name] uppercased with spaces replaced by hyphens. + +@i[Documentation] describes the uses of the character attribute. + +@i[Type], which defaults to @w<@f[(mod 2)]>, specifies what type the values of +the character attribute are. Values of a character attribute may be of any +type which may be specified to @f[make-array]. @i[Initial-value] (default +@f[0]) is the value which all characters will initially have for this +attribute. +@enddefun + +@defun[fun {character-attribute-name}, args {@i[attribute]}] +@defun1[fun {character-attribute-documentation}, args {@i[attribute]}] +These functions return the name or documentation for @i[attribute]. +@enddefun + +@defun[fun {character-attribute}, args {@i[attribute] @i[character]}] +@defhvar1[var {Character Attribute Hook}] +@f[character-attribute] returns the value of @i[attribute] for @i[character]. +This signals an error if @i[attribute] is undefined. + +@f[setf] will set a character's attributes. This @f[setf] method invokes the +functions in @hid[Character Attribute Hook] on the attribute and character +before it makes the change. + +If @i[character] is @nil, then the value of the attribute for the beginning or +end of the buffer can be accessed or set. The buffer beginning and end thus +become a sort of fictitious character, which simplifies the use of character +attributes in many cases. +@enddefun + +@defun[fun {character-attribute-p}, args {@i[symbol]}] +This function returns @true if @i[symbol] is the name of a character attribute, +@nil otherwise. +@enddefun + +@defun[fun {shadow-attribute}, args +{@i[attribute] @i[character] @i[value] @i[mode]}] +@defhvar1[var {Shadow Attribute Hook}] +This function establishes @i[value] as the value of @i[character]'s +@i[attribute] attribute when in the mode @i[mode]. @i[Mode] must be the name +of a major mode. @hid[Shadow Attribute Hook] is invoked with the same +arguments when this function is called. If the value for an attribute is set +while the value is shadowed, then only the shadowed value is affected, not the +global one. +@enddefun + +@defun[fun {unshadow-attribute}, args {@i[attribute] @i[character] @i[mode]}] +@defhvar1[var {Unshadow Attribute Hook}] +Make the value of @i[attribute] for @i[character] no longer be shadowed in +@i[mode]. @hid[Unshadow Attribute Hook] is invoked with the same arguments +when this function is called. +@enddefun + +@defun[fun {find-attribute}, + args {@i[mark] @i[attribute] @optional @i[test]}] +@defun1[fun {reverse-find-attribute}, + args {@i[mark] @i[attribute] @optional @i[test]}] + These functions find the next (or previous) character with some value for the +character attribute @i[attribute] starting at @i[mark]. They pass @i[Test] one +argument, the value of @i[attribute] for the character tested. If the test +succeeds, then these routines modify @i[mark] to point before (after for +@f[reverse-find-attribute]) the character which satisfied the test. If no +characters satisfy the test, then these return @nil, and @i[mark] remains +unmodified. @i[Test] defaults to @f[not zerop]. There is no guarantee that +the test is applied in any particular fashion, so it should have no side +effects and depend only on its argument. +@enddefun + + +@section(Character Attribute Hooks) + +It is often useful to use the character attribute mechanism as an abstract +interface to other information about characters which in fact is stored +elsewhere. For example, some implementation of @hemlock might decide to define +a @hid[Print Representation] attribute which controls how a character is +displayed on the screen. + +To make this easy to do, each attribute has a list of hook functions +which are invoked with the attribute, character and new value whenever +the current value changes for any reason. + +@defun[fun {character-attribute-hooks}, args {@i[attribute]}] +Return the current hook list for @i[attribute]. This may be set with +@f[setf]. The @f[add-hook] and @macref[remove-hook] macros should +be used to manipulate these lists. +@enddefun + + +@section (System Defined Character Attributes) +@label(sys-def-chars) +These are predefined in @hemlock: +@begin[description] +@hid[Whitespace]@\ +A value of @f[1] indicates the character is whitespace. + +@hid[Word Delimiter]@\ +A value of @f[1] indicates the character separates words (see section +@ref[text-functions]). + +@hid[Digit]@\ +A value of @f[1] indicates the character is a base ten digit. This may be +shadowed in modes or buffers to mean something else. + +@hid[Space]@\ +This is like @hid[Whitespace], but it should not include @binding[Newline]. +@hemlock uses this primarily for handling indentation on a line. + +@hid[Sentence Terminator]@\ +A value of @f[1] indicates these characters terminate sentences (see section +@ref[text-functions]). + +@hid[Sentence Closing Char]@\ +A value of @f[1] indicates these delimiting characters, such as @binding["] +or @binding[)], may follow a @hid[Sentence Terminator] (see section +@ref[text-functions]). + +@hid[Paragraph Delimiter]@\ +A value of @f[1] indicates these characters delimit paragraphs when they begin +a line (see section @ref[text-functions]). + +@hid[Page Delimiter]@\ +A value of @f[1] indicates this character separates logical pages (see section +@ref[logical-pages]) when it begins a line. + +@hid[Scribe Syntax]@\ +This uses the following symbol values: +@begin[multiple] +@begin[description] +@nil@\These characters have no interesting properties. + +@kwd[escape]@\This is @binding[@@] for the Scribe formatting language. + +@kwd[open-paren]@\These characters begin delimited text. + +@kwd[close-paren]@\These characters end delimited text. + +@kwd[space]@\These characters can terminate the name of a formatting command. + +@kwd[newline]@\These characters can terminate the name of a formatting command. +@end[description] +@end[multiple] + + +@hid[Lisp Syntax]@\ +This uses symbol values from the following: +@begin[multiple] +@begin[description] +@nil@\These characters have no interesting properties. + +@kwd[space]@\These characters act like whitespace and should not include +@binding[Newline]. + +@kwd[newline]@\This is the @binding[Newline] character. + +@kwd[open-paren]@\This is @binding[(] character. + +@kwd[close-paren]@\This is @binding[)] character. + +@kwd[prefix]@\This is a character that is a part of any form it precedes @dash +for example, the single quote, @binding[']. + +@kwd[string-quote]@\This is the character that quotes a string literal, +@binding["].@comment["] + +@kwd[char-quote]@\This is the character that escapes a single character, +@binding[\]. + +@kwd[comment]@\This is the character that makes a comment with the rest of the +line, @binding[;]. + +@kwd[constituent]@\These characters are constitute symbol names. +@end[description] +@end[multiple] + +@end[description] + + + +@chapter (Controlling the Display) +@section (Windows) +@tag[windows] +@index(Windows) +@index(modelines) + +A window is a mechanism for displaying part of a buffer on some physical +device. A window is a way to view a buffer but is not synonymous with one; a +buffer may be viewed in any number of windows. A window may have a +@i[modeline] which is a line of text displayed across the bottom of a window to +indicate status information, typically related to the buffer displayed. + + +@section (The Current Window) +@index (Current window) +@defun[fun {current-window}, args {}] +@defhvar1[var {Set Window Hook}] +@f[current-window] returns the window in which the cursor is currently +displayed. The cursor always tracks the buffer-point of the corresponding +buffer. If the point is moved to a position which would be off the screen the +recentering process is invoked. Recentering shifts the starting point of the +window so that the point is once again displayed. The current window may be +changed with @f[setf]. Before the current window is changed, the hook @hid[Set +Window Hook] is invoked with the new value. +@enddefun + +@defvar[var {window-list}] +Holds a list of all the window objects made with @funref[make-window]. +@enddefvar + + +@section(Window Functions) + +@defun[fun {make-window}, args {@i[mark]}, + keys {[modelinep][window][ask-user]}, + morekeys {[x][y][width][height]}] +@defhvar1[var {Default Window Width}] +@defhvar1[var {Default Window Height}] +@defhvar1[var {Make Window Hook}] +@f[make-window] returns a window displaying text starting at @i[mark], which +must point into a buffer. If it could not make a window on the device, it +returns nil. The default action is to split the current window to make room +for the new window. + +@i[Modelinep] specifies whether the window should display buffer modelines. + +Window is a X window to be used with the Hemlock window. + +@i[Window] is a device dependent window to be used with the Hemlock window. +The device may not support this argument. @i[Window] becomes the parent window +for a new group of windows that behave in a stack orientation as windows do on +the terminal. + +If @i[ask-user] is non-@nil, @hemlock prompts the user for the missing +dimensions (@i[x], @i[y], @i[width], and @i[height]) to make a new group of +windows, as with the @i[window] argument. The device may not support this +argument. Non-null values other than @f[t] may have device dependent meanings. +@i[X] and @i[y] are in pixel units, but @i[width] and @i[height] are characters +units. @hid[Default Window Width] and @hid[Default Window Height] are the +default values for the @i[width] and @i[height] arguments. + +This invokes @hid[Make Window Hook] with the new window. +@enddefun + +@defun[fun {windowp}, args {@i[window]}] +This function returns @true if @i[window] is a @f[window] object, otherwise +@nil. +@enddefun + +@defun[fun {delete-window}, args {@i[window]}] +@defhvar1[var {Delete Window Hook}] +@f[delete-window] makes @i[window] go away, first invoking @hid[Delete Window +Hook] with @i[window]. +@enddefun + +@defun[fun {window-buffer}, args {@i[window]}] +@defhvar1[var {Window Buffer Hook}] +@f[window-buffer] returns the buffer from which the window displays +text. This may be changed with @f[setf], in which case the hook +@hid[Window Buffer Hook] is invoked beforehand with the window and the +new buffer. +@enddefun + +@defun[fun {window-display-start}, args {@i[window]}] +@defun1[fun {window-display-end}, args {@i[window]}] +@f[window-display-start] returns the mark that points before the first +character displayed in @i[window]. Note that if @i[window] is the current +window, then moving the start may not prove much, since recentering may move it +back to approximately where it was originally. + +@f[window-display-end] is similar, but points after the last character +displayed. Moving the end is meaningless, since redisplay always moves it to +after the last character. +@enddefun + +@defun[fun {window-point}, args {@i[window]}] +This function returns as a mark the position in the buffer where the cursor is +displayed. This may be set with @f[setf]. If @i[window] is the current +window, then setting the point will have little effect; it is forced to track +the buffer point. When the window is not current, the window point is the +position that the buffer point will be moved to when the window becomes +current. +@enddefun + +@defun[fun {center-window}, args {@i[window] @i[mark]}] +This function attempts to adjust window's display start so the that @i[mark] is +vertically centered within the window. +@enddefun + +@defun[fun {scroll-window}, args {@i[window] @i[n]}] +This function scrolls the window down @i[n] display lines; if @i[n] is negative +scroll up. Leave the cursor at the same text position unless we scroll it off +the screen, in which case the cursor is moved to the end of the window closest +to its old position. +@enddefun + +@defun[fun {displayed-p}, args {@i[mark] @i[window]}] +Returns @true if either the character before or the character after @i[mark] +is being displayed in @i[window], or @nil otherwise. +@enddefun + +@defun[fun {window-height}, args {@i[window]}] +@defun1[fun {window-width}, args {@i[window]}] +Height or width of the area of the window used for displaying the +buffer, in character positions. These values may be changed with +@f[setf], but the setting attempt may fail, in which case nothing is done. +@enddefun + +@defun[fun {next-window}, args {@i[window]}] +@defun1[fun {previous-window}, args {@i[window]}] +Return the next or previous window of @i[window]. The exact meaning of next +and previous depends on the device displaying the window. It should be +possible to cycle through all the windows displayed on a device using either +next or previous (implying that these functions wrap around.) +@enddefun + + +@section(Cursor Positions) +@index(Cursor positions) +A cursor position is an absolute position within a window's coordinate +system. The origin is in the upper-left-hand corner and the unit +is character positions. + +@defun[fun {mark-to-cursorpos}, args {@i[mark] @i[window]}] +Returns as multiple values the @f[X] and @f[Y] position on which +@i[mark] is being displayed in @i[window], or @nil if it is not within the +bounds displayed. +@enddefun + +@defun[fun {cursorpos-to-mark}, args {@i[X] @i[Y] @i[window]}] +Returns as a mark the text position which corresponds to the given +(@i[X], @i[Y]) position within window, or @nil if that +position does not correspond to any text within @i[window]. +@enddefun + +@defun[fun {last-key-event-cursorpos}] +Interprets mouse input. It returns as multiple values the (@i[X], @i[Y]) +position and the window where the pointing device was the last time some key +event happened. If the information is unavailable, this returns @nil. +@enddefun + +@defun[fun {mark-column}, args {@i[mark]}] +This function returns the @i[X] position at which @i[mark] would be displayed, +supposing its line was displayed on an infinitely wide screen. This takes into +consideration strange characters such as tabs. +@enddefun + +@defun[fun {move-to-column}, args {@i[mark] @i[column] @optional @i[line]}] +This function is analogous to @funref[move-to-position], except that +it moves @i[mark] to the position on @i[line] which corresponds to the +specified @i[column]. @i[Line] defaults to the line that @i[mark] is +currently on. If the line would not reach to the specified column, +then @nil is returned and @i[mark] is not modified. Note that since a +character may be displayed on more than one column on the screen, +several different values of @i[column] may cause @i[mark] to be moved +to the same position. +@enddefun + +@defun[fun {show-mark}, args {@i[mark] @i[window] @i[time]}] +This function highlights the position of @i[mark] within @i[window] for +@i[time] seconds, possibly by moving the cursor there. The wait may be aborted +if there is pending input. If @i[mark] is positioned outside the text +displayed by @i[window], then this returns @nil, otherwise @true. +@enddefun + + +@section(Redisplay) +Redisplay is the process by which the editor translates changes in the internal +representation of text into changes on the screen. Ideally this process finds +the minimal transformation of the screen that brings it into correspondence +with the text, in order to maximize the speed of redisplay. + +@defun[fun {redisplay}] +@defhvar1[var "Redisplay Hook"] +@f[redisplay] invokes the redisplay process, and the command interpreter +typically causes this after the completion of each command. The redisplay +process repeatedly checks for input, and if any is detected, redisplay aborts. +This function invokes the functions in @hid[Redisplay Hook] on the current +window after completely preparing for but not executing redisplay. After +invoking the hook, it recomputes the redisplay again and then finally executes +it on the current window. +@enddefun + +@defun[fun {redisplay-all}] +This causes all editor windows to be completely redisplayed. +@enddefun + +@defun[fun {editor-finish-output}, args {@i[window]}] +This makes sure the editor is synchronized with respect to redisplay output to +@i[window]. This may do nothing on some devices. +@enddefun + + + +@chapter(Logical Key-Events) +@label[logical-key-events] +@index[Logical key-events] + + +@section[Introduction] +Some primitives such as @funref[prompt-for-key] and commands such as EMACS +query replace read key-events directly from the keyboard instead of using the +command interpreter. To encourage consistency between these commands and to +make them portable and easy to customize, there is a mechanism for defining +@i[logical key-events]. + +A logical key-event is a keyword which stands for some set of key-events. The +system globally interprets these key-events as indicators a particular action. +For example, the @kwd[help] logical key-event represents the set of key-events +that request help in a given @hemlock implementation. This mapping is a +many-to-many mapping, not one-to-one, so a given logical key-event may have +multiple corresponding actual key-events. Also, any key-event may represent +different logical key-events. + + +@section[Logical Key-Event Functions] + +@defvar[var {logical-key-event-names}] +This variable holds a string-table mapping all logical key-event names to the +keyword identifying the logical key-event. +@enddefvar + +@defun[fun {define-logical-key-event}, args {@i[string-name] @i[documentation]}] + This function defines a new logical key-event with name @i[string-name], a +simple-string. Logical key-event operations take logical key-events arguments +as a keyword whose name is @i[string-name] uppercased with spaces replaced by +hyphens. + +@i[Documentation] describes the action indicated by the logical key-event. +@enddefun + +@defun[fun {logical-key-event-key-events}, args {@i[keyword]}] +This function returns the list of key-events representing the logical key-event +@i[keyword]. +@enddefun + +@defun[fun {logical-key-event-name}, args {@i[keyword]}] +@defun1[fun {logical-key-event-documentation}, args {@i[keyword]}] +These functions return the string name and documentation given to +@f[define-logical-key-event] for logical key-event @i[keyword]. +@enddefun + +@defun[fun {logical-key-event-p}, args {@i[key-event] @i[keyword]}] +This function returns @f[t] if @i[key-event] is the logical key-event +@i[keyword]. This is @f[setf]'able establishing or disestablishing key-events +as particular logical key-events. It is a error for @i[keyword] to be an +undefined logical key-event. +@enddefun + + +@section[System Defined Logical Key-Events] +There are many default logical key-events, some of which are used by functions +documented in this manual. If a command wants to read a single key-event +command that fits one of these descriptions then the key-event read should be +compared to the corresponding logical key-event instead of explicitly +mentioning the particular key-event in the code. In many cases you can use the +@macref[command-case] macro. It makes logical key-events easy to use and takes +care of prompting and displaying help messages. + +@begin[description] +@kwd[yes]@\ + Indicates the prompter should take the action under consideration. + +@kwd[no]@\ + Indicates the prompter should NOT take the action under consideration. + +@kwd[do-all]@\ + Indicates the prompter should repeat the action under consideration as many +times as possible. + +@kwd[do-once]@\ + Indicates the prompter should execute the action under consideration once and +then exit. + +@kwd[exit]@\ + Indicates the prompter should terminate its activity in a normal fashion. + +@kwd[abort]@\ + Indicates the prompter should terminate its activity without performing any +closing actions of convenience, for example. + +@kwd[keep]@\ + Indicates the prompter should preserve something. + +@kwd[help]@\ + Indicates the prompter should display some help information. + +@kwd[confirm]@\ + Indicates the prompter should take any input provided or use the default if +the user entered nothing. + +@kwd[quote]@\ + Indicates the prompter should take the following key-event as itself without +any sort of command interpretation. + +@kwd[recursive-edit]@\ + Indicates the prompter should enter a recursive edit in the current context. + +@kwd[cancel]@\ + Indicates the prompter should cancel the effect of a previous key-event input. + +@kwd[forward-search]@\ + Indicates the prompter should search forward in the current context. + +@kwd[backward-search]@\ + Indicates the prompter should search backward in the current context. +@end[description] + +@blankspace(1 line) +Define a new logical key-event whenever: +@begin[enumerate] +The key-event concerned represents a general class of actions, and +several commands may want to take a similar action of this type. + +The exact key-event a command implementor chooses may generate violent taste +disputes among users, and then the users can trivially change the command in +their init files. + +You are using @f[command-case] which prevents implementors from specifying +non-standard characters for dispatching in otherwise possibly portable code, +and you can define and set the logical key-event in a site dependent file where +you can mention implementation dependent characters. +@end[enumerate] + + + +@chapter(The Echo Area) + +@hemlock provides a number of facilities for displaying information and +prompting the user for it. Most of these work through a small window displayed +at the bottom of the screen. This is called the echo area and is supported by +a buffer and a window. This buffer's modeline (see section @ref[modelines]) is +referred to as the status line, which, unlike other buffers' modelines, is used +to show general status about the editor, Lisp, or world. + +@defhvar[var {Default Status Line Fields}] +This is the initial list of modeline-field objects stored in the echo area +buffer. +@enddefhvar + +@defhvar[var "Echo Area Height", val {3}] +This variable determines the initial height in lines of the echo area window. +@enddefhvar + + +@section(Echo Area Functions) +It is considered poor taste to perform text operations on the echo area buffer +to display messages; the @f[message] function should be used instead. A +command must use this function or set @funref[buffer-modified] for the +@hid[Echo Area] buffer to @nil to cause @hemlock to leave text in the echo area +after the command's execution. + +@defun[fun {clear-echo-area}] +Clears the echo area. +@enddefun + +@defun[fun {message}, args {@i[control-string] @rest @i[format-arguments]}] +@defun1[fun {loud-message}, args {@i[control-string] @rest @i[format-arguments]}] +@defhvar1[var {Message Pause}, val {0.5}] +Displays a message in the echo area. The message is always displayed on a +fresh line. @f[message] pauses for @hid[Message Pause] seconds before +returning to assure that messages are not displayed too briefly to be seen. +Because of this, @f[message] is the best way to display text in the echo area. + +@f[loud-message] is like @f[message], but it first clears the echo area and +beeps. +@enddefun + +@defvar[var {echo-area-window}] +@defvar1[var {echo-area-buffer}] +@f[echo-area-buffer] contains the buffer object for the echo area, which is +named @hid[Echo Area]. This buffer is usually in @hid[Echo Area] mode. +@f[echo-area-window] contains a window displaying @f[echo-area-buffer]. Its +modeline is the status line, see the beginning of this chapter. +@enddefvar + +@defvar[var {echo-area-stream}] +@index (Echo area) +This is a buffered @hemlock output stream +(@pageref[make-hemlock-output-stream-fun]) which inserts text written to it at +the point of the echo area buffer. Since this stream is buffered a +@f[force-output] must be done when output is complete to assure that it is +displayed. +@enddefvar + + +@section(Prompting Functions) +@index(Prompting functions) +Most of the prompting functions accept the following keyword arguments: +@begin(description) +@kwd[must-exist] @\If @kwd[must-exist] has a non-@nil value then the +user is prompted until a valid response is obtained. If +@kwd[must-exist] is @nil then return as a string whatever is input. +The default is @true. + +@kwd[default] @\If null input is given when the user is prompted +then this value is returned. If no default is given then +some input must be given before anything interesting will happen. + +@kwd[default-string] @\If a @kwd[default] is given then this is a +string to be printed to indicate what the default is. The default is +some representation of the value for @kwd[default], for example for a +buffer it is the name of the buffer. + +@kwd[prompt] @\This is the prompt string to display. + +@kwd[help] @\@multiple{ +This is similar to @kwd[prompt], except that it is displayed when +the help command is typed during input. @comment{If there is some known number +of options as in keyword parses, then they may be displayed, depending +on the setting of @hvarref[Help Show Options].} + +This may also be a function. When called with no arguments, it should either +return a string which is the help text or perform some action to help the user, +returning @Nil.} +@end(description) + +@defun[fun {prompt-for-buffer}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] +Prompts with completion for a buffer name and returns the corresponding buffer. +If @i[must-exist] is @nil, then it returns the input string if it is not a +buffer name. This refuses to accept the empty string as input when +@kwd[default] and @kwd[default-string] are @nil. @kwd[default-string] may be +used to supply a default buffer name when @kwd[default] is @nil, but when +@kwd[must-exist] is non-@nil, it must name an already existing buffer. +@enddefun + +@defmac[fun {command-case}, Args {(@mstar<@i[key] @i[value]>) @Mstar<(@Mgroup"(@MSTAR'@i[tag]') @MOR @i[tag]" @i[help] @MSTAR'@i[form]')>}] + This macro is analogous to the Common Lisp @f[case] macro. Commands such as +@hid[Query Replace] use this to get a key-event, translate it to a character, +and then to dispatch on the character to some case. In addition to character +dispatching, this supports logical key-events @w<(page +@pageref[logical-key-events])> by using the input key-event directly without +translating it to a character. Since the description of this macro is rather +complex, first consider the following example: +@lisp +(defcommand "Save All Buffers" (p) + "Give the User a chance to save each modified buffer." + "Give the User a chance to save each modified buffer." + (dolist (b *buffer-list*) + (select-buffer-command () b) + (when (buffer-modified b) + (command-case (:prompt "Save this buffer: [Y] " + :help "Save buffer, or do something else:") + ((:yes :confirm) + "Save this buffer and go on to the next." + (save-file-command () b)) + (:no "Skip saving this buffer, and go on to the next.") + (:recursive-edit + "Go into a recursive edit in this buffer." + (do-recursive-edit) (reprompt)) + ((:exit #\p) "Punt this silly loop." + (return nil)))))) +@endlisp + +@f[command-case] prompts for a key-event and then executes the code in the +first branch with a logical key-event or a character (called @i[tags]) matching +the input. Each character must be a standard-character, one that satisfies the +Common Lisp @f[standard-char-p] predicate, and the dispatching mechanism +compares the input key-event to any character tags by mapping the key-event to +a character with @f[ext:key-event-char]. If the tag is a logical key-event, +then the search for an appropriate case compares the key-event read with the +tag using @f[logical-key-event-p]. + +All uses of @f[command-case] have two default cases, @kwd[help] and +@kwd[abort]. You can override these easily by specifying your own branches +that include these logical key-event tags. The @kwd[help] branch displays in a +pop-up window the a description of the valid responses using the variously +specified help strings. The @kwd[abort] branch signals an editor-error. + +The @i[key]/@i[value] arguments control the prompting. The following are valid +values: +@begin[description] +@kwd[help]@\ + The default @kwd[help] case displays this string in a pop-up window. In +addition it formats a description of the valid input including each case's +@i[help] string. + +@kwd[prompt]@\ + This is the prompt used when reading the key-event. + +@kwd[change-window]@\ + If this is non-nil (the default), then the echo area window becomes the +current window while the prompting mechanism reads a key-event. Sometimes it +is desirable to maintain the current window since it may be easier for users to +answer the question if they can see where the current point is. + +@kwd[bind]@\ + This specifies a variable to which the prompting mechanism binds the input +key-event. Any case may reference this variable. If you wish to know what +character corresponds to the key-event, use @f[ext:key-event-char]. +@end(description) + +Instead of specifying a tag or list of tags, you may use @true. This becomes +the default branch, and its forms execute if no other branch is taken, +including the default @kwd[help] and @kwd[abort] cases. This option has no +@i[help] string, and the default @kwd[help] case does not describe the default +branch. Every @f[command-case] has a default branch; if none is specified, the +macro includes one that @f[system:beep]'s and @f[reprompt]'s (see below). + +Within the body of @f[command-case], there is a defined @f[reprompt] macro. +It causes the prompting mechanism and dispatching mechanism to immediately +repeat without further execution in the current branch. +@enddefmac + + +@defun[fun {prompt-for-key-evnet}, keys {[prompt][change-window]}] +This function prompts for a key-event returning immediately when the user types +the next key-event. @macref[command-case] is more useful for most purposes. +When appropriate, use logical key-events @w<(page +@pageref[logical-key-events])>. +@enddefun + +@defun[fun {prompt-for-key}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function prompts for a @i[key], a vector of key-events, suitable for +passing to any of the functions that manipulate key bindings @w<(page +@pageref[key-bindings])>. If @i[must-exist] is true, then the key must be +bound in the current environment, and the command currently bound is returned +as the second value. +@enddefun + +@defun[fun {prompt-for-file}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function prompts for an acceptable filename in some system dependent +fashion. "Acceptable" means that it is a legal filename, and it exists if +@i[must-exist] is non-@nil. @f[prompt-for-file] returns a Common Lisp +pathname. + +If the file exists as entered, then this returns it, otherwise it is merged +with @i[default] as by @f[merge-pathnames]. +@enddefun + +@defun[fun {prompt-for-integer}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function prompts for a possibly signed integer. If @i[must-exist] is +@nil, then @f[prompt-for-integer] returns the input as a string if it is not a +valid integer. +@enddefun + +@defun[fun {prompt-for-keyword}, args {@i[string-tables]}, + keys {[prompt][help][must-exist]}, + morekeys {[default][default-string]}] + This function prompts for a keyword with completion, using the string tables +in the list @i[string-tables]. If @I[must-exist] is non-@nil, then the result +must be an unambiguous prefix of a string in one of the @i[string-tables], and +the returns the complete string even if only a prefix of the full string was +typed. In addition, this returns the value of the corresponding entry in the +string table as the second value. + +If @i[must-exist] is @nil, then this function returns the string exactly as +entered. The difference between @f[prompt-for-keyword] with @i[must-exist] +@nil, and @f[prompt-for-string], is the user may complete the input using the +@hid and @hid commands. +@enddefun + +@defun[fun {prompt-for-expression}, + keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function reads a Lisp expression. If @i[must-exist] is @nil, and a read +error occurs, then this returns the string typed. +@enddefun + +@defun[fun {prompt-for-string}, keys +{[prompt][help][default][default-string]}] + This function prompts for a string; this cannot fail. +@enddefun + +@defun[fun {prompt-for-variable}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function prompts for a variable name. If @i[must-exist] is non-@nil, +then the string must be a variable @i[defined in the current environment], in +which case the symbol name of the variable found is returned as the second +value. +@enddefun + +@defun[fun {prompt-for-y-or-n}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This prompts for @binding[y], @binding[Y], @binding[n], or @binding[N], +returning @true or @nil without waiting for confirmation. When the user types +a confirmation key, this returns @i[default] if it is supplied. If +@i[must-exist] is @nil, this returns whatever key-event the user first types; +however, if the user types one of the above key-events, this returns @true or +@nil. This is analogous to the Common Lisp function @f[y-or-n-p]. +@enddefun + +@defun[fun {prompt-for-yes-or-no}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function is to @f[prompt-for-y-or-n] as @f[yes-or-no-p] is to +@f[y-or-n-p]. "Yes" or "No" must be typed out in full and +confirmation must be given. +@enddefun + + +@section(Control of Parsing Behavior) + +@defhvar[var {Beep On Ambiguity}, val {@true}] +If this variable is true, then an attempt to complete a parse which is +ambiguous will result in a "beep". +@enddefhvar + + +@begin(comment) +@hemlock provides for limited control of parsing routine behaviour The +character attribute @hid[Parse Field Separator] is a boolean attribute, a value +of @f[1] indicating that the character is a field separator recognized by the +@hid command. +@end(comment) + +@begin(comment) +@defhvar[var {Help Show Options}] +During a keyword or similar parse, typing the help command may cause a +list of options to be displayed. If displaying the help would take up +more lines than the value of this variable then confirmation will be +asked for before they will be displayed. +@enddefhvar +@end(comment) + + + +@section(Defining New Prompting Functions) +Prompting functions are implemented as a recursive edit in the +@hid[Echo Area] buffer. Completion, help, and other parsing features +are implemented by commands which are bound in @hid[Echo Area Mode]. + +A prompting function passes information down into the recursive edit +by binding a collection of special variables. + +@defvar[var {parse-verification-function}] +The system binds this to a function that @comref[Confirm Parse] calls. It does +most of the work when parsing prompted input. @comref[Confirm Parse] passes +one argument, which is the string that was in @var when the +user invokes the command. The function should return a list of values which +are to be the result of the recursive edit, or @nil indicating that the parse +failed. In order to return zero values, a non-@nil second value may be +returned along with a @nil first value. +@enddefvar + +@defvar[var {parse-string-tables}] +This is the list of @f[string-table]s, if any, that pertain to this parse. +@enddefvar + +@defvar[var {parse-value-must-exist}] +This is bound to the value of the @kwd[must-exist] argument, and is +referred to by the verification function, and possibly some of the +commands. +@enddefvar + +@defvar[var {parse-default}] +When prompting the user, this is bound to a string representing the default +object, the value supplied as the @kwd[default] argument. @hid +supplies this to the parse verification function when the +@var is empty. +@enddefvar + +@defvar[var {parse-default-string}] +When prompting the user, if @var[parse-default] is @nil, @hemlock displays this +string as a representation of the default object; for example, when prompting +for a buffer, this variable would be bound to the buffer name. +@enddefvar + +@defvar[var {parse-type}] +The kind of parse in progress, one of @kwd[file], @kwd[keyword] or +@kwd[string]. This tells the completion commands how to do completion, with +@kwd[string] disabling completion. +@enddefvar + +@defvar[var {parse-prompt}] +The prompt being used for the current parse. +@enddefvar + +@defvar[var {parse-help}] +The help string or function being used for the current parse. +@enddefvar + +@defvar[var {parse-starting-mark}] +This variable holds a mark in the @varref[echo-area-buffer] which +is the position at which the parse began. +@enddefvar + +@defvar[var {parse-input-region}] +This variable holds a region with @var[parse-starting-mark] as its +start and the end of the echo-area buffer as its end. When +@hid[Confirm Parse] is called, the text in this region is the text +that will be parsed. +@enddefvar + + +@section(Some Echo Area Commands) + +These are some of the @hid[Echo Area] commands that coordinate with the +prompting routines. @Hemlock binds other commands specific to the @hid[Echo +Area], but they are uninteresting to mention here, such as deleting to the +beginning of the line or deleting backwards a word. + +@defcom[com {Help On Parse}, + stuff (bound to @bf[Home, C-_] in @hid[Echo Area] mode)] +Display the help text for the parse currently in progress. +@enddefcom + +@defcom[com {Complete Keyword}, + stuff (bound to @bf[Escape] in @hid[Echo Area] mode)] +This attempts to complete the current region as a keyword in +@var[string-tables]. It signals an editor-error if the input is ambiguous +or incorrect. +@enddefcom + +@defcom[com {Complete Field}, + stuff (bound to @bf[Space] in @hid[Echo Area] mode)] +Similar to @hid[Complete Keyword], but only attempts to complete up to and +including the first character in the keyword with a non-zero +@kwd[parse-field-separator] attribute. If +there is no field separator then attempt to complete the entire keyword. +If it is not a keyword parse then just self-insert. +@enddefcom + +@defcom[com {Confirm Parse}, + stuff (bound to @bf[Return] in @hid[Echo Area] mode)] +If @var[string-tables] is non-@nil find the string in the region in +them. Call @var[parse-verification-function] with the current input. +If it returns a non-@nil value then that is returned as the value of +the parse. A parse may return a @nil value if the verification +function returns a non-@nil second value. +@enddefcom + + + +@chapter (Files) +@index (Files) +This chapter discusses ways to read and write files at various levels @dash at +marks, into regions, and into buffers. This also treats automatic mechanisms +that affect the state of buffers in which files are read. + +@section (File Options and Type Hooks) +@index (File options) +@index (Type hooks) +@index (File type hooks) +The user specifies file options with a special syntax on the first line of a +file. If the first line contains the string "@f[-*-]", then @hemlock +interprets the text between the first such occurrence and the second, which +must be contained in one line , as a list of @w{"@f<@i[option]: @i[value]>"} +pairs separated by semicolons. The following is a typical example: +@begin[programexample] +;;; -*- Mode: Lisp, Editor; Package: Hemlock -*- +@end[programexample] +See the @i[Hemlock User's Manual] for more details and predefined options. + +File type hooks are executed when @hemlock reads a file into a buffer based on +the type of the pathname. When the user specifies a @hid[Mode] file option +that turns on a major mode, @hemlock ignores type hooks. This mechanism is +mostly used as a simple means for turning on some appropriate default major +mode. + +@defmac[fun {define-file-option}, args +{@i[name] (@i[buffer] @i[value]) @mstar<@i[declaration]> @mstar<@i[form]>}] +This defines a new file option with the string name @i[name]. @i[Buffer] and +@i[value] specify variable names for the buffer and the option value string, +and @i[form]'s are evaluated with these bound. +@enddefmac + +@defmac[fun {define-file-type-hook}, args +{@i[type-list] (@i[buffer] @i[type]) @mstar<@i[declaration]> @mstar<@i[form]>}] + +This defines some code that @f[process-file-options] (below) executes when the +file options fail to set a major mode. This associates each type, a +@f[simple-string], in @i[type-list] with a routine that binds @i[buffer] to the +buffer the file is in and @i[type] to the type of the pathname. +@enddefmac + +@defun[fun {process-file-options}, args {@i[buffer] @optional @i[pathname]}] +This checks for file options in buffer and invokes handlers if there are any. +@i[Pathname] defaults to @i[buffer]'s pathname but may be @nil. If there is no +@hid[Mode] file option that specifies a major mode, and @i[pathname] has a +type, then this tries to invoke the appropriate file type hook. +@f[read-buffer-file] calls this. +@enddefun + + +@section (Pathnames and Buffers) +There is no good way to uniquely identify buffer names and pathnames. However, +@hemlock has one way of mapping pathnames to buffer names that should be used +for consistency among customizations and primitives. Independent of this, +@hemlock provides a means for consistently generating prompting defaults when +asking the user for pathnames. + +@defun[fun {pathname-to-buffer-name}, args {@i[pathname]}] +This function returns a string of the form "@f[file-namestring] +@f[directory-namestring]". +@enddefun + +@defhvar[var "Pathname Defaults", val {(pathname "gazonk.del")}] +@defhvar1[var "Last Resort Pathname Defaults Function"] +@defhvar1[var "Last Resort Pathname Defaults", val {(pathname "gazonk")}] +These variables control the computation of default pathnames when needed for +promting the user. @hid[Pathname Defaults] is a @i[sticky] default. +See the @i[Hemlock User's Manual] for more details. +@enddefhvar + +@defun[fun {buffer-default-pathname}, args {@i[buffer]}] +This returns @hid[Buffer Pathname] if it is bound. If it is not bound, and +@i[buffer]'s name is composed solely of alphnumeric characters, then return a +pathname formed from @i[buffer]'s name. If @i[buffer]'s name has other +characters in it, then return the value of @hid[Last Resort Pathname Defaults +Function] called on @i[buffer]. +@enddefun + +@section (File Groups) +@index (File groups) +File groups provide a simple way of collecting the files that compose a system +and naming that collection. @Hemlock supports commands for searching, +replacing, and compiling groups. + +@defvar[var {active-file-group}] +This is the list of files that constitute the currently selected file group. +If this is @nil, then there is no current group. +@enddefvar + +@defmac[fun {do-active-group}, args {@mstar<@i[form]>}] +@defhvar1[var "Group Find File", val {nil}] +@defhvar1[var "Group Save File Confirm", val {t}] +@f[do-active-group] iterates over @var[active-file-group] executing the forms +once for each file. While the forms are executing, the file is in the current +buffer, and the point is at the beginning. If there is no active group, this +signals an editor-error. + +This reads each file into its own buffer using @f[find-file-buffer]. Since +unwanted buffers may consume large amounts of memory, @hid[Group Find File] +controls whether to delete the buffer after executing the forms. When the +variable is false, this deletes the buffer if it did not previously exist; +however, regardless of this variable, if the user leaves the buffer modified, +the buffer persists after the forms have completed. Whenever this processes a +buffer that already existed, it saves the location of the buffer's point before +and restores it afterwards. + +After processing a buffer, if it is modified, @f[do-active-group] tries to save +it. If @hid[Group Save File Confirm] is non-@nil, it asks for confirmation. +@enddefmac + + +@section (File Reading and Writing) +Common Lisp pathnames are used by the file primitives. For probing, checking +write dates, and so forth, all of the Common Lisp file functions are available. + +@defun[fun {read-file}, args {@i[pathname] @i[mark]}] +This inserts the file named by @i[pathname] at @i[mark]. +@enddefun + +@defun[fun {write-file}, args {@i[region] @i[pathname]}, + keys {[keep-backup][access]}] +@defhvar1[var {Keep Backup Files}, val {@nil}] +@f[write-file] writes the contents of @i[region] to the file named by +@i[pathname]. This writes @i[region] using a stream as if it were opened with +@kwd[if-exists] supplied as @kwd[rename-and-delete]. When @i[keep-backup], +which defaults to the value of @hid[Keep Backup Files], is non-nil, it is as if +the stream were opened with @kwd[if-exists] supplied as @kwd[rename]. +@i[Access] is an implementation dependent value that is suitable for setting +@i[pathname]'s access or protection bits. +@enddefun + + +@defun[fun {write-buffer-file}, args {@i[buffer] @i[pathname]}] +@defhvar1[var {Write File Hook}] +@defhvar1[var {Add Newline at EOF on Writing File}, val {@kwd[ask-user]}] +@f[write-buffer-file] writes @i[buffer] to the file named by @i[pathname] +including the following: +@begin[itemize] +It assumes pathname is somehow related to @i[buffer]'s pathname: if the +@i[buffer]'s write date is not the same as @i[pathname]'s, then this prompts +the user for confirmation before overwriting the file. + +It consults @hid[Add Newline at EOF on Writing File] (see @i[Hemlock User's +Manual] for possible values) and interacts with the user if necessary. + +It sets @hid[Pathname Defaults], and after using @f[write-file], marks +@i[buffer] unmodified. + +It updates @i[Buffer]'s pathname and write date. + +It renames the buffer according to the new pathname if possible. + +It invokes @hid[Write File Hook]. +@end[itemize] + +@hid[Write File Hook] is a list of functions that take the newly written buffer +as an argument. +@enddefun + + +@defun[fun {read-buffer-file}, args {@i[pathname] @i[buffer]}] +@defhvar1[var {Read File Hook}] +@f[read-buffer-file] deletes @i[buffer]'s region and uses @f[read-file] to read +@i[pathname] into it, including the following: +@begin[itemize] +It sets @i[buffer]'s write date to the file's write date if the file exists; +otherwise, it @f[message]'s that this is a new file and sets @i[buffer]'s write +date to @nil. + +It moves @i[buffer]'s point to the beginning. + +It sets @i[buffer]'s unmodified status. + +It sets @i[buffer]'s pathname to the result of probing @i[pathname] if the file +exists; otherwise, this function sets @i[buffer]'s pathname to the result of +merging @i[pathname] with @f[default-directory]. + +It sets @hid[Pathname Defaults] to the result of the previous item. + +It processes the file options. + +It invokes @hid[Read File Hook]. +@end[itemize] + +@hid[Read File Hook] is a list functions that take two arguments @dash the +buffer read into and whether the file existed, @true if so. +@enddefun + + +@defun[fun {find-file-buffer}, args {@i[pathname]}] +This returns a buffer assoicated with the @i[pathname], reading the file into a +new buffer if necessary. This returns a second value indicating whether a new +buffer was created, @true if so. If the file has already been read, this +checks to see if the file has been modified on disk since it was read, giving +the user various recovery options. This is the basis of the @hid[Find File] +command. +@enddefun + + + +@chapter (Hemlock's Lisp Environment) + +@index (Lisp environment) +This chapter is sort of a catch all for any functions and variables +which concern @hemlock's interaction with the outside world. + +@section(Entering and Leaving the Editor) + +@defun[fun {ed}, args {@optional @i[x]}] +@defhvar1[var "Entry Hook"] +@f[ed] enters the editor. It is basically as specified in Common Lisp. If +@i[x] is supplied and is a symbol, the definition of @i[x] is put into a +buffer, and that buffer is selected. If @i[x] is a pathname, the file +specified by @i[x] is visited in a new buffer. If @i[x] is not supplied or +@nil, the editor is entered in the same state as when last exited. + +The @hid[Entry Hook] is invoked each time the editor is entered. +@enddefhvar + +@defun[fun {exit-hemlock}, args {@optional @i[value]}] +@defhvar1[var {Exit Hook}] +@f[exit-hemlock] leaves @hemlock and return to Lisp; @i[value] is the +value to return, which defaults to @true. The hook +@hvarref[Exit Hook] is invoked before this is done. +@enddefun + +@defun[fun {pause-hemlock}] +@f[pause-hemlock] suspends the editor process and returns control to the shell. +When the process is resumed, it will still be running @hemlock. +@enddefun + + +@section(Keyboard Input) +@index(I/O) +@index[keyboard input] +@index[input, keyboard] + +Keyboard input interacts with a number of other parts of the editor. Since the +command loop works by reading from the keyboard, keyboard input is the initial +cause of everything that happens. Also, @hemlock redisplays in the low-level +input loop when there is no available input from the user. + + +@defvar[var {editor-input}] +@defvar1[var {real-editor-input}] +@defhvar1[var "Input Hook"] +@defhvar1[var "Abort Hook"] +@index[aborting] +@var[editor-input] is an object on which @hemlock's I/O routines operate. You +can get input, clear input, return input, and listen for input. Input appears +as key-events. + +@var[real-editor-input] holds the initial value of @var[editor-input]. This is +useful for reading from the user when @var[editor-input] is rebound (such as +within a keyboard macro.) + +@Hemlock invokes the functions in @hid[Input Hook] each time someone reads a +key-event from @var[real-editor-input]. These take no arguments. +@enddefvar + +@defun[fun {get-key-event}, args {@i[editor-input] @optional @i[ignore-abort-attempts-p]}] +This function returns a key-event as soon as it is available on +@i[editor-input]. @i[Editor-input] is either @var[editor-input] or +@var[real-editor-input]. @i[Ignore-abort-attempts-p] indicates whether +@binding[C-g] and @binding[C-G] throw to the editor's top-level command loop; +when this is non-nil, this function returns those key-events when the user +types them. Otherwise, it aborts the editor's current state, returning to the +command loop. + +When the user aborts, @Hemlock invokes the functions in @hid[Abort Hook]. +These functions take no arguments. When aborting, @Hemlock ignores the +@hid[Input Hook]. +@enddefun + + +@defun[fun {unget-key-event}, args {@i[key-event] @i[editor-input]}] +This function returns @i[key-event] to @i[editor-input], so the next invocation +of @f[get-key-event] will return @i[key-event]. If @i[key-event] is +@f[#k"C-g"] or @f[#k"C-G"], then whether @f[get-key-event] returns it depends +on that function's second argument. @i[Editor-input] is either +@var[editor-input] or @var[real-editor-input]. +@enddefun + +@defun[fun {clear-editor-input}, args {@i[editor-input]}] +This function flushes any pending input on @i[editor-input]. @i[Editor-input] +is either @var[editor-input] or @var[real-editor-input]. +@enddefun + +@defun[fun {listen-editor-input}, args {@i[editor-input]}] +This function returns whether there is any input available on @i[editor-input]. +@i[Editor-input] is either @var[editor-input] or @var[real-editor-input]. +@enddefun + +@defun[fun {editor-sleep}, args {@i[time]}] +Return either after @i[time] seconds have elapsed or when input is available on +@var[editor-input]. +@enddefun + +@defvar[var {key-event-history}] +This is a @hemlock ring buffer (see page @pageref[rings]) that holds the last +60 key-events read from the keyboard. +@enddefvar + +@defvar[var {last-key-event-typed}] +Commands use this variable to realize the last key-event the user typed to +invoke the commands. Before @hemlock ever reads any input, the value is @nil. +This variable usually holds the last key-event read from the keyboard, but it +is also maintained within keyboard macros allowing commands to behave the same +on each repetition as they did in the recording invocation. +@enddefvar + +@defvar[var {input-transcript}] +If this is non-@nil then it should be an adjustable vector with a fill-pointer. +When it is non-@nil, @hemlock pushes all input read onto this vector. +@enddefvar + + + +@section(Hemlock Streams) +It is possible to create streams which output to or get input from a buffer. +This mechanism is quite powerful and permits easy interfacing of @hemlock to +Lisp. + +@defun[fun {make-hemlock-output-stream}, args + {@i[mark] @optional @i[buffered]}] +@defun1[fun {hemlock-output-stream-p}, args {@i[object]}] +@f[make-hemlock-output-stream] returns a stream that inserts at the permanent +mark @i[mark] all output directed to it. @i[Buffered] controls whether the +stream is buffered or not, and its valid values are the following keywords: +@begin[description] +@kwd[none]@\No buffering is done. This is the default. + +@kwd[line]@\The buffer is flushed whenever a newline is written or +when it is explicitly done with @f[force-output]. + +@kwd[full]@\The screen is only brought up to date when it is +explicitly done with @f[force-output] +@end[description] + +@f[hemlock-output-stream-p] returns @true if @i[object] is a +@f[hemlock-output-stream] object. +@enddefun + +@defun[fun {make-hemlock-region-stream}, args {@i[region]}] +@defun1[fun {hemlock-region-stream-p}, args {@i[object]}] +@f[make-hemlock-region-stream] returns a stream from which the text in +@i[region] can be read. @f[hemlock-region-stream-p] returns @true if +@i[object] is a @f[hemlock-region-stream] object. +@enddefun + +@defmac[fun {with-input-from-region}, args +{(@i[var] @i[region]) @mstar<@i[declaration]> @mstar<@i[form]>}] +While evaluating @i[form]s, binds @i[var] to a stream which returns input +from @i[region]. +@enddefmac + +@defmac[fun {with-output-to-mark}, args +{(@i[var] @i[mark] @mopt<@i"buffered">) @mstar<@i[declaration]> @mstar<@i[form]>}] + During the evaluation of the @i[form]s, binds @i[var] to a stream which +inserts output at the permanent @i[mark]. @i[Buffered] has the same meaning as +for @f[make-hemlock-output-stream]. +@enddefmac + +@defmac[fun {with-pop-up-display}, args {(@i[var] @key @i[height name]) @mstar<@i[declaration]> @mstar<@i[form]>}] +@defvar1[var {random-typeout-buffers}] + This macro executes @i[forms] in a context with @i[var] bound to a stream. +@Hemlock collects output to this stream and tries to pop up a display of the +appropriate height containing all the output. When @i[height] is supplied, +@Hemlock creates the pop-up display immediately, forcing output on line breaks. +The system saves the output in a buffer named @i[name], which defaults to +@hid[Random Typeout]. When the window is the incorrect height, the display +mechanism will scroll the window with more-style prompting. This is useful +for displaying information of temporary interest. + +When a buffer with name @i[name] already exists and was not previously created +by @f[with-pop-up-display], @Hemlock signals an error. + +@var[random-typeout-buffers] is an association list mapping random typeout +buffers to the streams that operate on the buffers. +@enddefmac + + +@section (Interface to the Error System) +The error system interface is minimal. There is a simple editor-error +condition which is a subtype of error and a convenient means for signaling +them. @Hemlock also provides a standard handler for error conditions while in +the editor. + +@defun[fun {editor-error-format-string}, args {@i[condition]}] +@defun1[fun {editor-error-format-arguments}, args {@i[condition]}] +Handlers for editor-error conditions can access the condition object with +these. +@enddefun + +@defun[fun {editor-error}, args {@rest @i[args]}] +This function is called to signal minor errors within Hemlock; these are errors +that a normal user could encounter in the course of editing such as a search +failing or an attempt to delete past the end of the buffer. This function +@f[signal]'s an editor-error condition formed from @i[args], which are @nil or +a @f[format] string possibly followed by @f[format] arguments. @Hemlock +invokes commands in a dynamic context with an editor-error condition handler +bound. This default handler beeps or flashes (or both) the display. If the +condition passed to the handler has a non-@nil string slot, the handler also +invokes @f[message] on it. The command in progress is always aborted, and this +function never returns. +@enddefun + +@defmac[fun {handle-lisp-errors}, args {@mstar<@i[form]>}] +Within the body of this macro any Lisp errors that occur are handled in some +fashion more gracefully than simply dumping the user in the debugger. This +macro should be wrapped around code which may get an error due to some action +of the user @dash for example, evaluating code fragments on the behalf of and +supplied by the user. Using this in a command allows the established handler +to shadow the default editor-error handler, so commands should take care to +signal user errors (calls to @f[editor-errors]) outside of this context. +@enddefmac + + +@section (Definition Editing) +@index (Definition editing) +@hemlock provides commands for finding the definition of a function, macro, or +command and placing the user at the definition in a buffer. This, of course, +is implementation dependent, and if an implementation does not associate a +source file with a routine, or if @hemlock cannot get at the information, then +these commands do not work. If the Lisp system does not store an absolute +pathname, independent of the machine on which the maintainer built the system, +then users need a way of translating a source pathname to one that will be able +to locate the source. + +@defun[fun {add-definition-dir-translation}, args {@i[dir1] @i[dir2]}] +This maps directory pathname @i[dir1] to @i[dir2]. Successive invocations +using the same @i[dir1] push into a translation list. When @hemlock seeks a +definition source file, and it has a translation, then it tries the +translations in order. This is useful if your sources are on various machines, +some of which may be down. When @hemlock tries to find a translation, it first +looks for translations of longer directory pathnames, finding more specific +translations before shorter, more general ones. +@enddefun + +@defun[fun {delete-definition-dir-translation}, args {@i[dir]}] +This deletes the mapping of @i[dir] to all directories to which it has been +mapped. +@enddefun + + +@section (Event Scheduling) +@index (Event scheduling) +@index (Scheduling events) +The mechanism described in this chapter is only operative when the Lisp process +is actually running inside of @hemlock, within the @f[ed] function. The +designers intended its use to be associated with the editor, such as with +auto-saving files, reminding the user, etc. + +@defun[fun {schedule-event}, args {@i[time] @i[function] @optional @i[repeat]}] +This causes @hemlock to call @i[function] after @i[time] seconds have passed, +optionally repeating every @i[time] seconds. @i[Repeat] defaults to @true. +This is a rough mechanism since commands can take an arbitrary amount of time +to run; @hemlock invokes @i[function] at the first possible moment after +@i[time] has elapsed. @i[Function] takes the time in seconds that has elapsed +since the last time it was called (or since it was scheduled for the first +invocation). +@enddefun + +@defun[fun {remove-scheduled-event}, args {@i[function]}] +This removes @i[function] from the scheduling queue. @i[Function] does not +have to be in the queue. +@enddefun + + +@section (Miscellaneous) + +@defun[fun {in-lisp}, args {@mstar<@i[form]>}] +@index[Evaluating Lisp code] +This evaluates @i[form]'s inside @f[handle-lisp-errors]. It also binds +@var[package] to the package named by @hid[Current Package] if it is non-@nil. +Use this when evaluating Lisp code on behalf of the user. +@enddefun + +@defmac[fun {do-alpha-chars}, args {(@i[var] @i[kind] [@i[result]]) @mstar<@i[form]>}] +This iterates over alphabetic characters in Common Lisp binding @i[var] to each +character in order as specified under character relations in @i[Common Lisp the +Language]. @i[Kind] is one of @kwd[lower], @kwd[upper], or @kwd[both]. When +the user supplies @kwd[both], lowercase characters are processed first. +@enddefmac + + + +@chapter (High-Level Text Primitives) +This chapter discusses primitives that operate on higher level text forms than +characters and words. For English text, there are functions that know about +sentence and paragraph structures, and for Lisp sources, there are functions +that understand this language. This chapter also describes mechanisms for +organizing file sections into @i[logical pages] and for formatting text forms. + + +@section (Indenting Text) +@index (Indenting) +@label(indenting) + +@defhvar[var "Indent Function", val {tab-to-tab-stop}] +The value of this variable determines how indentation is done, and it is a +function which is passed a mark as its argument. The function should indent +the line that the mark points to. The function may move the mark around on +the line. The mark will be @f[:left-inserting]. The default simply inserts a +@binding[tab] character at the mark. A function for @hid[Lisp] mode probably +moves the mark to the beginning of the line, deletes horizontal whitespace, and +computes some appropriate indentation for Lisp code. +@enddefhvar + +@defhvar[var "Indent with Tabs", val {indent-using-tabs}] +@defhvar1[var "Spaces per Tab", val {8}] +@hid[Indent with Tabs] holds a function that takes a mark and a number of +spaces. The function will insert a maximum number of tabs and a minimum number +of spaces at mark to move the specified number of columns. The default +definition uses @hid[Spaces per Tab] to determine the size of a tab. @i[Note,] +@hid[Spaces per Tab] @i[is not used everywhere in @hemlock yet, so changing +this variable could have unexpected results.] +@enddefhvar + +@defun[fun {indent-region}, args {@i[region]}] +@defun1[fun {indent-region-for-commands}, args {@i[region]}] +@f[indent-region] invokes the value of @hid[Indent Function] on every line of +region. @f[indent-region-for-commands] uses @f[indent-region] but first saves +the region for the @hid[Undo] command. +@enddefun + +@defun[fun {delete-horizontal-space}, args {@i[mark]}] +This deletes all characters with a @hid[Space] attribute (see section +@ref[sys-def-chars]) of @f[1]. +@enddefun + + +@section (Lisp Text Buffers) +@index (Lisp text functions) +@hemlock bases its Lisp primitives on parsing a block of the buffer and +annotating lines as to what kind of Lisp syntax occurs on the line or what kind +of form a mark might be in (for example, string, comment, list, etc.). These +do not work well if the block of parsed forms is exceeded when moving marks +around these forms, but the block that gets parsed is somewhat programmable. + +There is also a notion of a @i[top level form] which this documentation often +uses synonymously with @i[defun], meaning a Lisp form occurring in a source +file delimited by parentheses with the opening parenthesis at the beginning of +some line. The names of the functions include this inconsistency. + +@defun[fun {pre-command-parse-check}, args {@i[mark] @i[for-sure]}] +@defhvar1[var {Parse Start Function}, val {start-of-parse-block}] +@defhvar1[var {Parse End Function}, val {end-of-parse-block}] +@defhvar1[var {Minimum Lines Parsed}, val {50}] +@defhvar1[var {Maximum Lines Parsed}, val {500}] +@defhvar1[var {Defun Parse Goal}, val {2}] +@f[pre-command-parse-check] calls @hid[Parse Start Function] and @hid[Parse End +Function] on @i[mark] to get two marks. It then parses all the lines between +the marks including the complete lines they point into. When @i[for-sure] is +non-@nil, this parses the area regardless of any cached information about the +lines. Every command that uses the following routines calls this before doing +so. + +The default values of the start and end variables use @hid[Minimum Lines +Parsed], @hid[Maximum Lines Parsed], and @hid[Defun Parse Goal] to determine +how big a region to parse. These two functions always include at least the +minimum number of lines before and after the mark passed to them. They try to +include @hid[Defun Parse Goal] number of top level forms before and after the +mark passed them, but these functions never return marks that include more than +the maximum number of lines before or after the mark passed to them. +@enddefun + +@defun[fun {form-offset}, args {@i[mark] @i[count]}] +This tries to move @i[mark] @i[count] forms forward if positive or -@i[count] +forms backwards if negative. @i[Mark] is always moved. If there were enough +forms in the appropriate direction, this returns @i[mark], otherwise nil. +@enddefun + +@defun[fun {top-level-offset}, args {@i[mark] @i[count]}] +This tries to move @i[mark] @i[count] top level forms forward if positive or +-@i[count] top level forms backwards if negative. If there were enough top +level forms in the appropriate direction, this returns @i[mark], otherwise nil. +@i[Mark] is moved only if this is successful. +@enddefun + +@defun[fun {mark-top-level-form}, args {@i[mark1] @i[mark2]}] +This moves @i[mark1] and @i[mark2] to the beginning and end, respectively, of +the current or next top level form. @i[Mark1] is used as a reference to start +looking. The marks may be altered even if unsuccessful. If successful, return +@i[mark2], else nil. @i[Mark2] is left at the beginning of the line following +the top level form if possible, but if the last line has text after the closing +parenthesis, this leaves the mark immediately after the form. +@enddefun + +@defun[fun {defun-region}, args {@i[mark]}] +This returns a region around the current or next defun with respect to +@i[mark]. @i[Mark] is not used to form the region. If there is no appropriate +top level form, this signals an editor-error. This calls +@f[pre-command-parse-check] first. +@enddefun + +@defun[fun {inside-defun-p}, args {@i[mark]}] +@defun1[fun {start-defun-p}, args {@i[mark]}] +These return, respectively, whether @i[mark] is inside a top level form or at +the beginning of a line immediately before a character whose @hid[Lisp Syntax] +(see section @ref[sys-def-chars]) value is @kwd[opening-paren]. +@enddefun + +@defun[fun {forward-up-list}, args {@i[mark]}] +@defun1[fun {backward-up-list}, args {@i[mark]}] +Respectively, these move @i[mark] immediately past a character whose @hid[Lisp +Syntax] (see section @ref[sys-def-chars]) value is @kwd[closing-paren] or +immediately before a character whose @hid[Lisp Syntax] value is +@kwd[opening-paren]. +@enddefun + +@defun[fun {valid-spot}, args {@i[mark] @i[forwardp]}] +This returns @true or @nil depending on whether the character indicated by +@i[mark] is a valid spot. When @i[forwardp] is set, use the character after +mark and vice versa. Valid spots exclude commented text, inside strings, and +character quoting. +@enddefun + +@defun[fun {defindent}, args {@i[name] @i[count]}] +This defines the function with @i[name] to have @i[count] special arguments. +@f[indent-for-lisp], the value of @hid[Indent Function] (see section +@ref[indenting]) in @hid[Lisp] mode, uses this to specially indent these +arguments. For example, @f[do] has two, @f[with-open-file] has one, etc. +There are many of these defined by the system including definitions for special +@hemlock forms. @i[Name] is a simple-string, case insensitive and purely +textual (that is, not read by the Lisp reader); therefore, @f["with-a-mumble"] +is distinct from @f["mumble:with-a-mumble"]. +@enddefun + + +@section (English Text Buffers) +@index (English text functions) +@label(text-functions) +This section describes some routines that understand basic English language +forms. + +@defun[fun {word-offset}, args {@i[mark] @i[count]}] +This moves @i[mark] @i[count] words forward (if positive) or backwards (if +negative). If @i[mark] is in the middle of a word, that counts as one. If +there were @i[count] (-@i[count] if negative) words in the appropriate +direction, this returns @i[mark], otherwise nil. This always moves @i[mark]. +A word lies between two characters whose @hid[Word Delimiter] attribute value +is @f[1] (see section @ref[sys-def-chars]). +@enddefun + +@defun[fun {sentence-offset}, args {@i[mark] @i[count]}] +This moves @i[mark] @i[count] sentences forward (if positive) or backwards (if +negative). If @i[mark] is in the middle of a sentence, that counts as one. If +there were @i[count] (-@i[count] if negative) sentences in the appropriate +direction, this returns @i[mark], otherwise nil. This always moves @i[mark]. + +A sentence ends with a character whose @hid[Sentence Terminator] attribute is +@f[1] followed by two spaces, a newline, or the end of the buffer. The +terminating character is optionally followed by any number of characters whose +@hid[Sentence Closing Char] attribute is @f[1]. A sentence begins after a +previous sentence ends, at the beginning of a paragraph, or at the beginning of +the buffer. +@enddefun + +@defun[fun {paragraph-offset}, args {@i[mark] @i[count] @optional @i[prefix]}] +@defhvar1[var {Paragraph Delimiter Function}, var {default-para-delim-function}] +This moves @i[mark] @i[count] paragraphs forward (if positive) or backwards (if +negative). If @i[mark] is in the middle of a paragraph, that counts as one. +If there were @i[count] (-@i[count] if negative) paragraphs in the appropriate +direction, this returns @i[mark], otherwise nil. This only moves @i[mark] if +there were enough paragraphs. + +@hid[Paragraph Delimiter Function] holds a function that takes a mark, +typically at the beginning of a line, and returns whether or not the current +line should break the paragraph. @f[default-para-delim-function] returns @true +if the next character, the first on the line, has a @hid[Paragraph Delimiter] +attribute value of @f[1]. This is typically a space, for an indented +paragraph, or a newline, for a block style. Some modes require a more +complicated determinant; for example, @hid[Scribe] modes adds some characters +to the set and special cases certain formatting commands. + +@i[Prefix] defaults to @hid[Fill Prefix] (see section @ref[filling]), and the +right prefix is necessary to correctly skip paragraphs. If @i[prefix] is +non-@nil, and a line begins with @i[prefix], then the scanning process skips +the prefix before invoking the @hid[Paragraph Delimiter Function]. +Note, when scanning for paragraph bounds, and @i[prefix] is non-@nil, lines are +potentially part of the paragraph regardless of whether they contain the prefix; +only the result of invoking the delimiter function matters. + +The programmer should be aware of an idiom for finding the end of the current +paragraph. Assume @f[paragraphp] is the result of moving @f[mark] one +paragraph, then the following correctly determines whether there actually is a +current paragraph: +@begin[programexample] +(or paragraphp + (and (last-line-p mark) + (end-line-p mark) + (not (blank-line-p (mark-line mark))))) +@end[programexample] +In this example @f[mark] is at the end of the last paragraph in the buffer, and +there is no last newline character in the buffer. @f[paragraph-offset] would +have returned @nil since it could not skip any paragraphs since @f[mark] was at +the end of the current and last paragraph. However, you still have found a +current paragraph on which to operate. @f[mark-paragraph] understands this +problem. +@enddefun + +@defun[fun {mark-paragraph}, args {@f[mark1] @f[mark2]}] +This marks the next or current paragraph, setting @i[mark1] to the beginning +and @i[mark2] to the end. This uses @hid[Fill Prefix] (see section +@ref[filling]). @i[Mark1] is always on the first line of the paragraph, +regardless of whether the previous line is blank. @i[Mark2] is typically at +the beginning of the line after the line the paragraph ends on, this returns +@i[mark2] on success. If this cannot find a paragraph, then the marks are left +unmoved, and @nil is returned. +@enddefun + + +@section (Logical Pages) +@index (Logical pages) +@index (Page functions) +@label(logical-pages) +Logical pages are a way of dividing a file into coarse divisions. This is +analogous to dividing a paper into sections, and @hemlock provides primitives +for moving between the pages of a file and listing a directory of the page +titles. Pages are separated by @hid[Page Delimiter] characters (see section +@ref[sys-def-chars]) that appear at the beginning of a line. + +@defun[fun {goto-page}, args {@i[mark] @i[n]}] +This moves @i[mark] to the absolute page numbered @i[n]. If there are less +than @i[n] pages, it signals an editor-error. If it returns, it returns +@i[mark]. @hemlock numbers pages starting with one for the page delimited by +the beginning of the buffer and the first @hid[Page Delimiter] (or the end of +the buffer). +@enddefun + +@defun[fun {page-offset}, args {@i[mark] @i[n]}] +This moves mark forward @i[n] (-@i[n] backwards, if @i[n] is negative) +@hid[Page Delimiter] characters that are in the zero'th line position. If a +@hid[Page Delimiter] is the immediately next character after mark (or before +mark, if @i[n] is negative), then skip it before starting. This always moves +@i[mark], and if there were enough pages to move over, it returns @i[mark]; +otherwise, it returns @nil. +@enddefun + +@defun[fun {page-directory}, args {@i[buffer]}] +This returns a list of each first non-blank line in @i[buffer] that follows a +@hid[Page Delimiter] character that is in the zero'th line position. This +includes the first line of the @i[buffer] as the first page title. If a page +is empty, then its title is the empty string. +@enddefun + +@defun[fun {display-page-directory}, args {@i[stream] @i[directory]}] +This writes the list of strings, @i[directory], to @i[stream], enumerating them +in a field three wide. The number and string are separated by two spaces, and +the first line contains headings for the page numbers and title strings. +@enddefun + + +@section (Filling) +@index (filling) +@label(filling) +Filling is an operation on text that breaks long lines at word boundaries +before a given column and merges shorter lines together in an attempt to make +each line roughly the specified length. This is different from justification +which tries to add whitespace in awkward places to make each line exactly the +same length. @Hemlock's filling optionally inserts a specified string at the +beginning of each line. Also, it eliminates extra whitespace between lines and +words, but it knows two spaces follow sentences (see section +@ref[text-functions]). + +@defhvar[var "Fill Column", val {75}] +@defhvar1[var "Fill Prefix", val {nil}] +These variables hold the default values of the prefix and column arguments to +@hemlock's filling primitives. If @hid[Fill Prefix] is @nil, then there is no +fill prefix. +@enddefhvar + +@defun[fun {fill-region}, args {@i[region] @optional @i[prefix] @i[column]}] +This deletes any blank lines in region and fills it according to prefix and +column. @i[Prefix] and @i[column] default to @hid[Fill Prefix] and @hid[Fill +Column]. +@enddefun + +@defun[fun {fill-region-by-paragraphs}, + args {@i[region] @optional @i[prefix] @i[column]}] +This finds paragraphs (see section @ref[text-functions]) within region and +fills them with @f[fill-region]. This ignores blank lines between paragraphs. +@i[Prefix] and @i[column] default to @hid[Fill Prefix] and @hid[Fill Column]. +@enddefun + + + +@chapter (Utilities) +@index (Utilities) +This chapter describes a number of utilities for manipulating some types of +objects @hemlock uses to record information. String-tables are used to store +names of variables, commands, modes, and buffers. Ring lists can be used to +provide a kill ring, recent command history, or other user-visible features. + + +@section(String-table Functions) +@index (String-tables) +@label(string-tables) + +String tables are similar to Common Lisp hash tables in that they associate a +value with an object. There are a few useful differences: in a string table +the key is always a case insensitive string, and primitives are provided to +facilitate keyword completion and recognition. Any type of string may be added +to a string table, but the string table functions always return +@f[simple-string]'s. + +A string entry in one of these tables may be thought of as being separated into +fields or keywords. The interface provides keyword completion and recognition +which is primarily used to implement some @hid[Echo Area] commands. These +routines perform a prefix match on a field-by-field basis allowing the +ambiguous specification of earlier fields while going on to enter later fields. +While string tables may use any @f[string-char] as a separator, the use of +characters other than @binding[space] may make the @hid[Echo Area] commands +fail or work unexpectedly. + +@defun[fun {make-string-table}, keys {[separator][initial-contents]}] +This function creates an empty string table that uses @i[separator] as the +character, which must be a @f[string-char], that distinguishes fields. +@i[Initial-contents] specifies an initial set of strings and their values in +the form of a dotted @f[a-list], for example: +@Begin[ProgramExample] +'(("Global" . t) ("Mode" . t) ("Buffer" . t)) +@End[ProgramExample] +@enddefun + +@defun[fun {string-table-p}, args {@i[string-table]}] +This function returns @true if @i[string-table] is a @f[string-table] object, +otherwise @nil. +@enddefun + +@defun[fun {string-table-separator}, args {@i[string-table]}] +This function returns the separator character given to @f[make-string-table]. +@enddefun + +@defun[fun {delete-string}, args {@i[string] @i[table]}] +@defun1[fun {clrstring}, args {@i[table]}] +@f[delete-string] removes any entry for @i[string] from the @f[string-table] +@i[table], returning @true if there was an entry. @f[clrstring] removes all +entries from @i[table]. +@enddefun + +@defun[fun {getstring}, args {@i[string] @i[table]}] +This function returns as multiple values, first the value corresponding to the +string if it is found and @nil if it isn't, and second @true if it is found and +@nil if it isn't. + +This may be set with @f[setf] to add a new entry or to store a new value for a +string. It is an error to try to insert a string with more than one +field separator character occurring contiguously. +@enddefun + +@defun[fun {complete-string}, args {@i[string] @i[tables]}] +This function completes @i[string] as far as possible over the list of +@i[tables], returning five values. It is an error for @i[tables] to have +different separator characters. The five return values are as follows: +@begin[itemize] +The maximal completion of the string or @nil if there is none. + +An indication of the usefulness of the returned string: +@begin[description] +@kwd[none]@\ +There is no completion of @i[string]. + +@kwd[complete]@\ +The completion is a valid entry, but other valid completions exist too. This +occurs when the supplied string is an entry as well as initial substring of +another entry. + +@kwd[unique]@\ +The completion is a valid entry and unique. + +@kwd[ambiguous]@\ +The completion is invalid; @f[get-string] would return @nil and @nil if given +the returned string. +@end[description] + +The value of the string when the completion is @kwd[unique] or @kwd[complete], +otherwise @nil. + +An index, or nil, into the completion returned, indicating where the addition +of a single field to @i[string] ends. The command @hid[Complete Field] uses +this when the completion contains the addition to @i[string] of more than one +field. + +An index to the separator following the first ambiguous field when the +completion is @kwd[ambiguous] or @kwd[complete], otherwise @nil. +@end[itemize] +@enddefun + +@defun[fun {find-ambiguous}, args {@i[string] @i[table]}] +@defun1[fun {find-containing}, args {@i[string] @i[table]}] +@f[find-ambiguous] returns a list in alphabetical order of all the +strings in @i[table] matching @i[string]. This considers an entry as matching +if each field in @i[string], taken in order, is an initial substring of the +entry's fields; entry may have fields remaining. + +@f[find-containing] is similar, but it ignores the order of the fields in +@i[string], returning all strings in @i[table] matching any permutation of the +fields in @i[string]. +@enddefun + +@defmac[fun {do-strings}, args {(@i[string-var] @i[value-var] @i[table] @MOPT<@i[result]>) @mstar<@i[declaration]> @mstar<@i[tag] @MOR @i[statement]>}] +This macro iterates over the strings in @i[table] in alphabetical order. On +each iteration, it binds @i[string-var] to an entry's string and @i[value-var] +to an entry's value. +@enddefmac + + +@section (Ring Functions) +@index (Rings) +@label[rings] +There are various purposes in an editor for which a ring of values can be used, +so @hemlock provides a general ring buffer type. It is used for maintaining a +ring of killed regions (see section @ref[kill-ring]), a ring of marks (see +section @ref[mark-stack]), or a ring of command strings which various modes and +commands maintain as a history mechanism. + +@defun[fun {make-ring}, args {@i[length] @optional @i[delete-function]}] +Makes an empty ring object capable of holding up to @i[length] Lisp objects. +@i[Delete-function] is a function that each object is passed to before it falls +off the end. @i[Length] must be greater than zero. +@enddefun + +@defun[fun {ringp}, args {@i[ring]}] +Returns @true if @i[ring] is a @f[ring] object, otherwise @nil. +@enddefun + +@defun[fun {ring-length}, args {@i[ring]}] +Returns as multiple-values the number of elements which @i[ring] +currently holds and the maximum number of elements which it may hold. +@enddefun + +@defun[fun {ring-ref}, args {@i[ring] @i[index]}] +Returns the @i[index]'th item in the @i[ring], where zero is the index +of the most recently pushed. This may be set with @f[setf]. +@enddefun + +@defun[fun {ring-push}, args {@i[object] @i[ring]}] +Pushes @i[object] into @i[ring], possibly causing the oldest item to +go away. +@enddefun + +@defun[fun {ring-pop}, args {@i[ring]}] +Removes the most recently pushed object from @i[ring] and returns it. +If the ring contains no elements then an error is signalled. +@enddefun + +@defun[fun {rotate-ring}, args {@i[ring] @i[offset]}] +With a positive @i[offset], rotates @i[ring] forward that many times. +In a forward rotation the index of each element is reduced by one, +except the one which initially had a zero index, which is made the +last element. A negative offset rotates the ring the other way. +@enddefun + + +@section (Undoing commands) +@index (Undo functions) +@label(undo) + +@defun[fun {save-for-undo}, args {@i[name] @i[method] @optional @i[cleanup] @i[method-undo] @i[buffer]}] +This saves information to undo a command. @i[Name] is a string to display when +prompting the user for confirmation when he invokes the @hid[Undo] command (for +example, @f["kill"] or @f["Fill Paragraph"]). @i[Method] is the function to +invoke to undo the effect of the command. @i[Method-undo] is a function that +undoes the undo function, or effectively re-establishes the state immediately +after invoking the command. If there is any existing undo information, this +invokes the @i[cleanup] function; typically @i[method] closes over or uses +permanent marks into a buffer, and the @i[cleanup] function should delete such +references. @i[Buffer] defaults to the @f[current-buffer], and the @hid[Undo] +command only invokes undo methods when they were saved for the buffer that is +current when the user invokes @hid[Undo]. +@enddefun + +@defun[fun {make-region-undo}, args {@i[kind] @i[name] @i[region] @optional @i[mark-or-region]}] +This handles three common cases that commands fall into when setting up undo +methods, including cleanup and method-undo functions (see @f[save-for-undo]). +These cases are indicated by the @i[kind] argument: +@begin[description] +@kwd[twiddle]@\ +Use this kind when a command modifies a region, and the undo information +indicates how to swap between two regions @dash the one before any modification +occurs and the resulting region. @i[Region] is the resulting region, and it +has permanent marks into the buffer. @i[Mark-or-region] is a region without +marks into the buffer (for example, the result of @f[copy-region]). As a +result of calling this, a first invocation of @hid[Undo] deletes @i[region], +saving it, and inserts @i[mark-or-region] where @i[region] used to be. The +undo method sets up for a second invocation of @hid[Undo] that will undo the +effect of the undo; that is, after two calls, the buffer is exactly as it was +after invoking the command. This activity is repeatable any number of times. +This establishes a cleanup method that deletes the two permanent marks into the +buffer used to locate the modified region. + +@kwd[insert]@\ +Use this kind when a command has deleted a region, and the undo information +indicates how to re-insert the region. @i[Region] is the deleted and saved +region, and it does not contain marks into any buffer. @i[Mark-or-region] is a +permanent mark into the buffer where the undo method should insert @i[region]. +As a result of calling this, a first invocation of @hid[Undo] inserts +@i[region] at @i[mark-or-region] and forms a region around the inserted text +with permanent marks into the buffer. This allows a second invocation of +@hid[Undo] to undo the effect of the undo; that is, after two calls, the buffer +is exactly as it was after invoking the command. This activity is repeatable +any number of times. This establishes a cleanup method that deletes either the +permanent mark into the buffer or the two permanent marks of the region, +depending on how many times the user used @hid[Undo]. + +@kwd[delete]@\ +Use this kind when a command has inserted a block of text, and the undo +information indicates how to delete the region. @i[Region] has permanent marks +into the buffer and surrounds the inserted text. Leave @i[Mark-or-region] +unspecified. As a result of calling this, a first invocation of @hid[Undo] +deletes @i[region], saving it, and establishes a permanent mark into the buffer +to remember where the @i[region] was. This allows a second invocation of +@hid[Undo] to undo the effect of the undo; that is, after two calls, the buffer +is exactly as it was after invoking the command. This activity is repeatable +any number of times. This establishes a cleanup method that deletes either the +permanent mark into the buffer or the two permanent marks of the region, +depending on how many times the user used @hid[Undo]. +@end[description] + +@blankspace(1 line) +@i[Name] in all cases is an appropriate string indicating what the command did. +This is used by @hid[Undo] when prompting the user for confirmation before +calling the undo method. The string used by @hid[Undo] alternates between this +argument and something to indicate that the user is undoing an undo. +@enddefun + + + +@chapter (Miscellaneous) +This chapter is somewhat of a catch-all for comments and features that don't +fit well anywhere else. + + +@section (Generic Pointer Up) +@hid[Generic Pointer Up] is a @hemlock command bound to mouse up-clicks. It +invokes a function supplied with the interface described in this section. This +command allows different commands to be bound to the same down-click in various +modes with one command bound to the corresponding up-click. + +@defun[fun {supply-generic-pointer-up-function}, args {@i[function]}] +@index[Generic Pointer Up] +This function supplies a function that @hid[Generic Pointer Up] invokes the +next time it executes. +@enddefun + + +@section (Using View Mode) +@hid[View] mode supports scrolling through files automatically terminating the +buffer at end-of-file as well as commands for quitting the mode and popping +back to the buffer that spawned the @hid[View] mode buffer. Modes such as +@hid[Dired] and @hid[Lisp-Lib] use this to view files and description of +library entries. + +Modes that want similar commands should use @f[view-file-command] to view a +file and get a handle on the view buffer. To allow the @hid[View Return] and +@hid[View Quit] commands to return to the originating buffer, you must set the +variable @hid[View Return Function] in the viewing buffer to a function that +knows how to do this. Furthermore, since you now have a reference to the +originating buffer, you must add a buffer local delete hook to it that will +clear the view return function's reference. This needs to happen for two +reasons in case the user deletes the originating buffer: +@Begin[Enumerate] +You don't want the return function to go to a non-existing, invalid buffer. + +Since the viewing buffer still exists, its @hid[View Return Function] buffer +local variable still exists. This means the function still references the +deleted originating buffer, and garbage collection cannot reclaim the memory +locked down by the deleted buffer. +@End[Enumerate] + +The following is a piece of code that could implement part of @hid[Dired View +File] that uses two closures to accomplish that described above: +@Begin[ProgramExample] +(let* ((dired-buf (current-buffer)) + (buffer (view-file-command nil pathname))) + (push #'(lambda (buffer) + (declare (ignore buffer)) + (setf dired-buf nil)) + (buffer-delete-hook dired-buf)) + (setf (variable-value 'view-return-function :buffer buffer) + #'(lambda () + (if dired-buf + (change-to-buffer dired-buf) + (dired-from-buffer-pathname-command nil))))) +@End[ProgramExample] + +The @hid[Dired] buffer's delete hook clears the return function's reference to +the @hid[Dired] buffer. The return function tests the variable to see if it +still holds a buffer when the function executes. + + + +@comment[@chapter (Auxiliary Systems)] +@include(aux-sys) diff --git a/docs/hem/hem-docs.log b/docs/hem/hem-docs.log new file mode 100644 index 0000000000000000000000000000000000000000..25528a02ec961d09ec75b75e10fde41a4ef6e139 --- /dev/null +++ b/docs/hem/hem-docs.log @@ -0,0 +1,43 @@ +/usr1/lisp/scribe/hem/user/lisp.mss, 01-Sep-89 10:27:47, Edit by Chiles. + Modifying slave interaction description. + +/usr1/lisp/scribe/hem/cim/cim.mss, 01-Sep-89 10:25:15, Edit by Chiles. + Added description of LOUD-MESSAGE. + +/usr1/lisp/scribe/hem/cim/aux-sys.mss, 01-Sep-89 10:22:22, Edit by Chiles. + Updating slave Lisp interaction functions. + +/usr1/lisp/scribe/hem/cim.mss, 27-Jun-89 15:19:47, Edit by Chiles. + Moved "Auxiliary Systems" chapter into its own file, and qualified lots of + the described symbols with their home package. + +/usr1/lisp/scribe/hem/hemimpl.mss, 22-Jun-89 12:56:23, Edit by Chiles. + Added new WITH-POP-UP-DISPLAY. + + Updated all the English in the prompting functions section. + + Added the "Keep" logical character description. + + +/usr1/lisp/scribe/hem/hemuser.mss, 22-Jun-89 12:55:03, Edit by Chiles. + Added new pop-up windows section. + + Modified modelines section, talked about status line. + + Added completion section. + + +/usr1/lisp/scribe/hem/hemuser.mss, 20-May-89 18:03:57, Edit by Chiles. + Modified example after "Name Keyboard Macro". Scribe was screwing up a paren + in the @binding. I changed it to a programexample, but check the result. + +/usr1/lisp/scribe/hemimpl.mss, 18-May-89 13:00:09, Edit by Chiles. + Addes sections to miscellaneous chapter for View mode interaction and + SUPPLY-GENERIC-POINTER-UP-FUNCTION + +/usr1/lisp/scribe/hemimpl.mss, 15-May-89 17:40:09, Edit by Chiles. + Added new Miscellaneous chapter. + +/usr1/lisp/scribe/hemuser.mss, 15-May-89 14:14:37, Edit by Chiles. + Modified "View" mode section. + diff --git a/docs/hem/hem.dict b/docs/hem/hem.dict new file mode 100644 index 0000000000000000000000000000000000000000..312c9d2258d3ffbe0e90d757a7d0f5db82d345fc --- /dev/null +++ b/docs/hem/hem.dict @@ -0,0 +1,446 @@ +NEWLINE +WHITESPACE +MISINTERPRETED +ENDCOMMANDS +RUBOUT +COMMANDBODY +DEFCOMMAND +ENDDEFCOMMAND +ALT +BF +LINEFEED +TRANSPOSITION +SPICETITLEPAGE +CONTENTSMACROS +TIMESROMANKERN +NEWFACECODE +COPYFONT +PRESSFILE +BINDINGFONT +TIMESROMAN +SUBSTITUTECHAR +ASSOCFONT +SETFONTINFO +STANDARDR +STANDARDBBI +SSF +HELVETICA +BOLDPAGENO +PAGENO +BEGINENTRY +TABCLEAR +TABSET +EXTRALEFTMARGIN +ENDENTRY +ININIT +COMMANDENV +CM +COMMANDTEXT +LEFTMARGIN +RIGHTMARGIN +ARGS +TABDIVIDE +CHPGNO +NCONV +CHAPTERNO +CHAPID +PAGEHEADING +BORDERSTYLE +SPICETP +MACLACHLAN +ABS +WHIZZY +SCREENFULL +REF +RIGHTDOWN +LEFTDOWN +MIDDLEDOWN +MIDDLEUP +BIZZARE +FORMFEED +NEWLINES +UN +YANKS +YANKED +OOPS +TYPOS +LOWERCASE +UPPERCASE +TRANSPOSING +TRANSPOSE +EMACS +UNINITIATED +TRANSPOSITIONS +TRANSPOSES +LF +PAREN +COMMANDEND +BOYER +MOORE +UNREADING +FOO +FILENAME +BUFFER'S +SYS +TECO +PROGRAMEXAMPLE +TRASHED +SEMI +PATHNAME +UPD +SRCCOM +LOSSAGE +DEFUN +PARENS +DEFSTRUCT +DEFMACRO +UNREADABLE +ED +RECOMPILATION +RECOMPILE +SFASL +EVAL +UNDAMAGED +NEWPAGE +INDEXINCLUDE +NLM +EMS +RLM +CENUMERATE +LEFTMPOSN +BULLETPOSN +NRM +CDESCRIPTION +RRM +DEFHVAR +ENDDEFHVAR +HVAR +SLISP +PICE +ISP +CLISP +OMMON +MACS +DOC +PORTED +ZWEI +'S +BLOTCH +PERQ +META +KEYPAD +LEFTUP +COMMAND +'S +FACECODE +VARS +SOS +MODELESS +MODELINE +MSS +UNDERRATED +MALCOORDINATION +RIGHTARROW +LEFTARROW +DOWNARROW +UPARROW +SPASTIC +EOF +PATHNAMES +GAZONK +DEL +IO +INOPERATIVE +TYPESCRIPT +KEYTRAN +REDIRECTED +BACKTRACE +CONDITIONALIZE +INIT +'HEMLOCK +SETV +DEFINDENT +ABBREVIATORY +NEUTRALIZES +UNEXPAND +RESYNCHRONIZING +FORMFEEDS +COMMAND'S +HEMLOCKMAN +READDEFS +RESHAPE +IDIOM +CURVACEOUS +PERCENTAGEWISE +ABBREVS +ABBREV +PROB +BINARIES +ENDDEFVAR +FEEP +KWD +DEFUNS +VAL +DEFCOM +ENDDEFCOM +LLISP +REBINDS +TYPESCRIPTS +SYNTAXES +COMREF +MAKUNBOUND +DEFVAR +ENDDEFUN +VAR +ARGS +DEFUN +DEFVAR +ENDDEFVAR +CHECKIN +EXT +GETSTRING +CLRSTRING +HASHTABLES +TYPEOUT +ERRORP +REPROMPT +DOLIST +CURSORPOS +RECENTERING +MACREF +ZEROP +UNSHADOW +MOD +DEFATTRIBUTE +ADA +INTERNING +MOREKEYS +CLEANUP +DEFMODE +FUNCALLS +MGROUP +HLET +ENDDEFCON +DEFCON +UPCASE +'STRING +UPPERCASED +NINSERT +FUNLABEL +EQ +VARREF +TRUENAME +HVARREF +FUNREF +ENDLISP +MIN +ENDDEFMAC +MOPT +POS +MSTAR +DEFMAC +'TH +MARK'S +CHARPOS +EQL +LINE'S +REMF +PUTF +GETF +PLIST +SETF +MULTICS +WHOLEY +SKEF +UTTIR +SINGLESIDED +HYPHENATION +NAMESTRING +NAMESTRINGS +FASL +DEF +DEFANYTHING +CRYPTIC +WILDCARDS +FILESYSTEM +PAGEREF +XTERM +TERMCAP +UNIX +LIB +MISC +VERBATIM +MODELINES +PIXELS +BITMAP +WORKSTATION +USR +COM +PREFIXING +HYPER +CRUFT +LIBRARYFILE +FONTFAMILY +STRINGMAX +BEEPING +AUTORAISE +TELNET +SLAVE'S +SERVER'S +PREPENDING +OFFLOADING +ICONIFY +LPR +INFO +CHECKPOINTED +ROBERT +MUNGING +DEACTIVATED +EPHEMERALLY +DEACTIVATE +HQB +MODELINEP +RINGP +SUBTYPES +SUBTYPE +MSTAR' +WINDOWP +COMMANDP +MOR +GTR +GEQ +NEQ +LEQ +LSS +FIELD'S +WINDOWS' +BUFFERS' +BUFFERP +DEACTIVATES +REGIONP +MARKP +LINEP +COMMANDSTRING +DOD +RESEARCHCREDIT +CS +CMU +CHILES +BLANKSPACE +TITLEBOX +TITLEPAGE +REPORTTITLE +ARPACREDIT +DOUBLESIDED +UNDOABLE +LISPS +CLX +SPEC +DISASSOCIATED +MAILERS +RAND +AFS +SUPFILE +STEPPENWOLF +GPA +CRYPT +HOSTBASE +LOGIN +RT'S +FRAME'S +ARPANET +SETENV +FORWCOMPS +REPLCOMPS +RMMPROC +RM +FORMFILE +NOCC +FCC +REPL +PREVS +RFSLINK +HOSTNAME +XBIFF +RFS +GROUPNAME +ACCOUNTNAME +BLURBS +WORKSTATION'S +PROFILENAME +USERNAME +MAILDROP +MACHINENAME +WORKSTATIONS +HEADERS' +LISPY +FIELDS' +INBOX +ANNO +NOTATING +MH +ID +MH'S +MSGS +EXP +CC +DATEFIELD +BB +MSG +FOLDER'S +COMP +FORW +DRAFTFOLDER +DRAFTMESSAGE +INC +HEMLOCKDELETED +RMM +NOTATED +UNDELETE +ID'S +RMF +REFILE +REFILING +REFILED +DEACTIVATING +UNKILL +ZERO'TH +DIR +VS +KEYSYM +KEYCODE +XLIB +KEYSYM'S +FORWARDP +HI +ISPELL +LEN +LISP'S +TWIDDLE +RIGHTUP +HEMUSER +MACROEXPAND +UNPROVIDED +DIRED +WILDCARD +UNDELETING +TXT +PATHNAME'S +SUBDIRECTORIES +SUBDIRECTORY +FILENAMES +BUFED +HINIT +WINDOWING +UNSUPPLIED +CALLABLE +'ABLE +OP +FORGOES +AUX +'RESCHEDULE +'CHECK +'REMOVE +BITCHFUCKSALL +SUBPROCESS +KEYSYMS diff --git a/docs/hem/user/commands.mss b/docs/hem/user/commands.mss new file mode 100644 index 0000000000000000000000000000000000000000..7d547bd04f4a89a5f4db8050f4d6b4eb07a12192 --- /dev/null +++ b/docs/hem/user/commands.mss @@ -0,0 +1,822 @@ +@comment{-*- Dictionary: hem; Mode: spell; Package: Hemlock -*-} +@chap[Basic Commands] +@section[Motion Commands] + +@index[commands, basic]@index[motion]There is a fairly small number of +basic commands for moving around in the buffer. While there are many other +more complex motion commands, these are by far the most commonly used and +the easiest to learn. + +@defcom[com "Forward Character", bind (C-f, Rightarrow)] +@defcom1[com "Backward Character", bind (C-b, Leftarrow)] +@index[character, motion] +@hid[Forward Character] moves the point forward by one character. If a prefix +argument is supplied, then the point is moved by that many characters. +@hid[Backward Character] is identical, except that it moves the point +backwards. +@enddefcom + +@defcom[com "Forward Word", bind {M-f}] +@defcom1[com "Backward Word", bind {M-b}] +@index[word, motion]These commands move the point forward and backward +over words. The point is always left between the last word and first +non-word character in the direction of motion. This means that after moving +backward the cursor appears on the first character of the word, while after +moving forward, the cursor appears on the delimiting character. Supplying +a prefix argument moves the point by that many words. +@enddefcom + +@defcom[com "Next Line", bind (C-n, Downarrow)] +@defcom1[com "Previous Line", bind (C-p, Uparrow)] +@defcom1[com "Goto Absolute Line"] +@index[line, motion] +@hid[Next Line] and @hid[Previous Line] move to adjacent lines, while remaining +the same distance within a line. Note that this motion is by logical lines, +each of which may take up many lines on the screen if it wraps. If a prefix +argument is supplied, then the point is moved by that many lines. + +The position within the line at the start is recorded, and each successive +use of @binding[C-p] or @binding[C-n] attempts to move the point to that +position on the new line. If it is not possible to move to the recorded +position because the line is shorter, then the point is left at the end of +the line. + +@hid[Goto Absolute Line] moves to the indicated line, as if you counted them +starting at the beginning of the buffer with number one. If the user supplies +a prefix argument, it is the line number; otherwise, @Hemlock prompts the user +for the line. +@enddefcom + +@defcom[com "End of Line", bind {C-e}] +@defcom1[com "Beginning of Line", bind {C-a}] +@hid[End of Line] moves the point to the end of the current line, while +@hid[Beginning of Line] moves to the beginning. If a prefix argument is +supplied, then the point is moved to the end or beginning of the line that +many lines below the current one. +@enddefcom + +@defcom[com "Scroll Window Down", bind {C-v}] +@defcom1[com "Scroll Window Up", bind {M-v}] +@index[scrolling] +@hid[Scroll Window Down] moves forward in the buffer by one screenful of text, +the exact amount being determined by the size of the window. If a prefix +argument is supplied, then this scrolls the screen that many lines. When this +action scrolls the line with the point off the screen, it this command moves +the point to the vertical center of the window. @hid[Scroll Window Up] is +identical to @hid[Scroll Window Down], except that it moves backwards. +@enddefcom + +@defhvar[var "Scroll Overlap", val {2}] +This variable is used by @hid[Scroll Window Down] and @hid[Scroll Window Up] to +determine the number of lines by which the new and old screen should overlap. +@enddefhvar + +@defcom[com "End of Buffer", bind (M-<)] +@defcom1[com "Beginning of Buffer", bind (@bf[M->])] +These commands are used to conveniently get to the very beginning and end of the +text in a buffer. Before the point is moved, its position is saved by +pushing it on the mark stack (see page @pageref[marks]). +@enddefcom + +@defcom[com "Top of Window", bind (M-,)] +@defcom1[com "Bottom of Window", bind (M-.)] +@index[window, motion]@hid[Top of Window] moves the point to the beginning of +the first line displayed in the current window. @hid[Bottom of Window] moves +to the beginning of the last line displayed. +@enddefcom + + +@section[The Mark and The Region] + +@label[marks]@index[marks]@index[region]@index[selection]Each buffer has a +distinguished position known as the @i[mark]. The mark initially points to the +beginning of the buffer. The area between the mark and the point is known as +the @i[region]. Many @hemlock commands which manipulate large pieces of text +use the text in the region. To use these commands, one must first use some +command to mark the region. + +@index[active regions]Although the mark is always pointing somewhere (initially +to the beginning of the buffer), region commands insist that the region be made +@i[active] before it can be used. This prevents accidental use of a region +command from mysteriously mangling large amounts of text. + +@defhvar[var "Active Regions Enabled", val {t}] +When this variable is true, region commands beep unless the region is active. +This may be set to @false for more traditional @emacs region semantics. +@enddefhvar + +Once a marking command makes the region active, it remains active until: +@begin[itemize] +a command uses the region, + +a command modifies the buffer, + +a command changes the current window or buffer, + +a command signals an editor error, + +or the user types @binding[C-g]. +@end[itemize] +Motion commands have the effect of redefining the region, since they move the +point and leave the region active. + +@index[ephemerally active regions]Commands that insert a large chunk of +text into the buffer usually set an @i[ephemerally active] region around +the inserted text. An ephemerally active region is always deactivated by +the next command, regardless of the kind of command. The ephemerally +active region allows an immediately following region command to manipulate +the inserted text, but doesn't persist annoyingly. This is also very +useful with active region highlighting, since it visibly marks the inserted +text. + + +@defhvar[var "Highlight Active Region", val {t}] +@defhvar1[var "Active Region Highlighting Font", val {nil}] +When @hid[Highlight Active Region] is true, @hemlock displays the text in the +region in a different font whenever the region is active. This provides a +visible indication of what text will be manipulated by a region command. +Active region highlighting is only supported under @windows. + +@hid[Active Region Highlighting Font] is the name of the font to use for active +region highlighting. If unspecified, @hemlock uses an underline font. +@enddefhvar + + +@defcom[com "Set/Pop Mark", bind (C-@@)] +This command moves the mark to the point (saving the old mark on the mark +stack) and activates the region. After using this command to mark one end of +the region, use motion commands to move to the other end, then do the region +command. This is the traditional @emacs marking command; when running under a +windowing system with mouse support, it is usually easier to use the mouse with +the @comref[Point to Here] and @comref[Generic Pointer Up]. + +For historical reasons, the prefix argument causes this command to do things +that are distinct commands in @Hemlock. A prefix argument of four does +@hid[Pop and Goto Mark], and a prefix argument of @f[16] does +@hid[Pop Mark]. +@enddefcom + +@defcom[com "Mark Whole Buffer", bind (C-x h)] +@defcom1[com "Mark to Beginning of Buffer", bind (C-<)] +@defcom1[com "Mark to End of Buffer", bind (C->)] +@hid[Mark Whole Buffer] sets the region around the whole buffer, with the point +at the beginning and the mark at the end. If a prefix argument is supplied, +then the mark is put at the beginning and the point at the end. The mark is +pushed on the mark stack beforehand, so popping the stack twice will restore +it. + +@hid[Mark to Beginning of Buffer] sets the current region from point to the +beginning of the buffer. + +@hid[Mark to End of Buffer] sets the current region from the end of the buffer +to point. +@enddefcom + +@defcom[com "Activate Region", bind (C-x C-Space, C-x C-@@)] +This command makes the region active, using whatever the current position of +the mark happens to be. This is useful primarily when the region is +accidentally deactivated. +@enddefcom + + +@subsection[The Mark Stack] + +@index[mark stack]As was hinted at earlier, each buffer has a @i[mark stack], +providing a history of positions in that buffer. The current mark is the mark +on the top of the stack; earlier values are recovered by popping the stack. +Since commands that move a long distance save the old position on the mark +stack, the mark stack commands are useful for jumping to interesting places in +a buffer without having to do a search. + +@defcom[com "Pop Mark", bind (C-M-Space)] +@defcom1[com "Pop and Goto Mark", bind (M-@@, M-Space)] +@hid[Pop Mark] pops the mark stack, restoring the current mark to the next most +recent value. @hid[Pop and Goto Mark] also pops the mark stack, but instead of +discarding the current mark, it moves the point to that position. Both +commands deactivate the region. +@enddefcom + +@defcom[com "Exchange Point and Mark", bind (C-x C-x)] +This command interchanges the position of the point and the mark, thus moving +to where the mark was, and leaving the mark where the point was. This command +can be used to switch between two positions in a buffer, since repeating it +undoes its effect. The old mark isn't pushed on the mark stack, since it is +saved in the point. +@enddefcom + + +@subsection[Using The Mouse] + +@index[mouse]It can be convenient to use the mouse to point to positions in +text, especially when moving large distances. @hemlock defines several +commands for using the mouse. These commands can only be used when running +under @windows (see page @pageref[using-x].) + +@defcom[com "Here to Top of Window", bind (Rightdown)] +@defcom1[com "Top Line to Here", bind (Leftdown)] +@index[window, motion]@hid[Here to Top of Window] scrolls the window so as to +move the line which is under the mouse cursor to the top of the window. This +has the effect of moving forward in the buffer by the distance from the top of +the window to the mouse cursor. @hid[Top Line to Here] is the inverse +operation, it scrolls backward, moving current the top line underneath the +mouse. + +If the mouse is near the left edge of a window, then these commands do smooth +scrolling. @hid[Here To Top of Window] repeatedly scrolls the window up by one +line until the mouse button is released. Similarly, @hid[Top Line to Here] +smoothly scrolls down. +@enddefcom + +@defcom[com "Point to Here", bind (Middledown, S-Leftdown)] +This command moves the point to the position of the mouse, changing to a +different window if necessary. + +When used in a window's modeline, this moves the point of the window's buffer +to the position within the file that is the same percentage, start to end, as +the horizontal position of the mouse within the modeline. This also makes this +window current if necessary. + +This command supplies a function @hid[Generic Pointer Up] invokes if it runs +without any intervening generic pointer up predecessors executing. If the +position of the pointer is different than the current point when the user +invokes @hid[Generic Pointer Up], then this function pushes a buffer mark at +point and moves point to the pointer's position. This allows the user to mark +off a region with the mouse. +@enddefcom + +@defcom[com "Generic Pointer Up", bind (Middleup, S-Leftup)] +Other commands determine this command's action by supplying functions that +this command invokes. The following built-in commands supply the following +generic up actions: +@Begin[Description] +@hid[Point to Here]@\ + When the position of the pointer is different than the current point, the +action pushes a buffer mark at point and moves point to the pointer's position. + +@hid[Bufed Goto and Quit]@\ + The action is a no-op. +@End[Description] +@enddefcom + +@defcom[com "Insert Kill Buffer", bind (S-Rightdown)] +This command is a combination of @hid[Point to Here] and @comref[Un-Kill]. It +moves the point to the mouse location and inserts the most recently killed +text. +@enddefcom + + +@section[Modification Commands] +@index[commands, modification] + +There is a wide variety of basic text-modification commands, but once again the +simplest ones are the most often used. + +@subsection[Inserting Characters] +@index[character, insertion] +@index[insertion, character] + +In @hemlock, you can insert characters with graphic representations by typing +the corresponding key-event which you normally generate with the obvious +keyboard key. You can only insert characters whose codes correspond to ASCII +codes. To insert those without graphic representations, use @hid[Quoted +Insert]. + +@defcom[com "Self Insert"] +@hid[Self Insert] inserts into the buffer the character corresponding to the +key-event typed to invoke the command. This command is normally bound to all +such key-events @binding[Space]. If a prefix argument is supplied, then this +inserts the character that many times. +@enddefcom + +@defcom[com "New Line", bind (Return)] +This command, which has roughly the same effect as inserting a @bf[Newline], +is used to move onto a new blank line. If there are at least two blank +lines beneath the current one then @binding[Return] cleans off any +whitespace on the next line and uses it, instead of inserting a newline. +This behavior is desirable when inserting in the middle of text, because +the bottom half of the screen does not scroll down each time @hid[New Line] +is used. +@enddefcom + +@defcom[com "Quoted Insert", bind {C-q}] +Many key-events have corresponding ASCII characters, but these key-events are +bound to commands other than @hid[Self Insert]. Sometimes they are otherwise +encumbered such as with @binding[C-g]. @hid[Quoted Insert] prompts for a +key-event, without any command interpretation semantics, and inserts the +corresponding character. If the appropriate character has some code other than +an ASCII code, this will beep and abort the command. A common use for this +command is inserting a @bf[Formfeed] by typing @binding[C-q C-l]. If a prefix +argument is supplied, then the character is inserted that many times. +@enddefcom + +@defcom[com "Open Line", bind {C-o}] +This command inserts a newline into the buffer without moving the point. +This command may also be given a prefix argument to insert a number of +newlines, thus opening up some room to work in the middle of a screen of +text. See also @comref[Delete Blank Lines]. +@enddefcom + + +@subsection[Deleting Characters] +@index[deletion, character] +@index[character, deletion] +There are a number of commands for deleting characters as well. + +@defhvar[var "Character Deletion Threshold", val {5}] +If more than this many characters are deleted by a character deletion command, +then the deleted text is placed in the kill ring. +@enddefhvar + +@defcom[com "Delete Next Character", bind {C-d}] +@defcom1[com "Delete Previous Character", bind (Delete, Backspace)] +@hid[Delete Next Character] deletes the character immediately following the +point, that is, the character which appears under the cursor. When given a +prefix argument, @binding[C-d] deletes that many characters after the +point. @hid[Delete Previous Character] is identical, except that it +deletes characters before the point. +@enddefcom + +@defcom[com "Delete Previous Character Expanding Tabs"] +@hid[Delete Previous Character Expanding Tabs] is identical to +@hid[Delete Previous Character], except that it treats tabs as the +equivalent number of spaces. Various language modes that use tabs for +indentation bind @binding[Delete] to this command. +@enddefcom + + +@subsection[Killing and Deleting] + +@index[killing]@index[cutting]@index[pasting]@index[kill ring]@hemlock has many +commands which kill text. Killing is a variety of deletion which saves the +deleted text for later retrieval. The killed text is saved in a ring buffer +known as the @i[kill ring]. Killing has two main advantages over deletion: +@begin[enumerate] +If text is accidentally killed, a not uncommon occurrence, then it can be +restored. + +Text can be moved from one place to another by killing it and then +restoring it in the new location. +@end[enumerate] + +Killing is not the same as deleting. When a command is said to delete +text, the text is permanently gone and is not pushed on the kill ring. +Commands which delete text generally only delete things of little +importance, such as single characters or whitespace. + +@subsection[Kill Ring Manipulation] +@defcom[com "Un-Kill", bind {C-y}] +@index[kill ring, manipulation]This command "yanks" back the most +recently killed piece of text, leaving the mark before the inserted text +and the point after. If a prefix argument is supplied, then the text that +distance back in the kill ring is yanked. +@enddefcom + +@defcom[com "Rotate Kill Ring", bind {M-y}] +This command rotates the kill ring forward, replacing the most recently +yanked text with the next most recent text in the kill ring. @binding[M-y] +may only be used immediately after a use of @binding[C-y] or a previous +use of @binding[M-y]. This command is used to step back through the text +in the kill ring if the desired text was not the most recently killed, and +thus could not be retrieved directly with a @binding[C-y]. If a prefix +argument is supplied, then the kill ring is rotated that many times. +@enddefcom + +@defcom[com "Kill Region", bind {C-w}] +@index[region, killing]This command kills the text between the point and +mark, pushing it onto the kill ring. This command is usually the best way +to move or remove large quantities of text. +@enddefcom + +@defcom[com "Save Region", bind {M-w}] +This command pushes the text in the region on the kill ring, but doesn't +actually kill it, giving an effect similar to typing @binding[C-w C-y]. +This command is useful for duplicating large pieces of text. +@enddefcom + +@subsection[Killing Commands] + +@index[commands, killing]Most commands which kill text append into the +kill ring, meaning that consecutive uses of killing commands will insert +all text killed into the top entry in the kill ring. This allows large +pieces of text to be killed by repeatedly using a killing command. + +@defcom[com "Kill Line", bind {C-k}] +@defcom1[com "Backward Kill Line"] +@index[line, killing]@hid[Kill Line] kills the text from the point to the +end of the current line, deleting the line if it is empty. If a prefix +argument is supplied, then that many lines are killed. Note that a prefix +argument is not the same as a repeat count. + +@hid[Backward Kill Line] is similar, except that it kills from the point to the +beginning of the line. If it is called at the beginning of the line, it kills +the newline and any trailing whitespace on the previous line. With a prefix +argument, this command is the same as @hid[Kill Line] with a negated argument. +@enddefcom + +@defcom[com "Kill Next Word", bind {M-d}] +@defcom1[com "Kill Previous Word", bind (M-Backspace, M-Delete)] +@index[word, killing]@hid[Kill Next Word] kills from the point to the end +of the current or next word. If a prefix argument is supplied, then that +many words are killed. @hid[Kill Previous Word] is identical, except that +it kills backward. +@enddefcom + +@subsection[Case Modification Commands] + +@index[case modification]@hemlock provides a few case modification +commands, which are often useful for correcting typos. + +@defcom[com "Capitalize Word", bind {M-c}] +@defcom1[com "Lowercase Word", bind {M-l}] +@defcom1[com "Uppercase Word", bind {M-u}] +@index[word, case modification]These commands modify the case of the +characters from the point to the end of the current or next word, leaving +the point after the end of the word affected. A positive prefix argument +modifies that many words, moving forward. A negative prefix argument +modifies that many words before the point, but leaves the point unmoved. +@enddefcom + +@defcom[com "Lowercase Region", bind (C-x C-l)] +@defcom1[com "Uppercase Region", bind (C-x C-u)] +@index[region, case modification]These commands case-fold the text in the +region. Since these commands can damage large amounts of text, they ask for +confirmation before modifying large regions and can be undone with @hid[Undo]. +@enddefcom + +@subsection[Transposition Commands] + +@index[transposition]@index[commands, transposition]@hemlock provides a +number of transposition commands. A transposition command swaps the +"things" before and after the point and moves forward one "thing". Just +how a "thing" is defined depends on the particular transposition command. +Transposition commands, particularly +@hid[Transpose Characters] and @hid[Transpose Words], are useful for +correcting typos. More obscure transposition commands can be used to amaze +your friends and demonstrate your immense knowledge of exotic @emacs +commands. + +To the uninitiated, the behavior of transposition commands may seem mysterious; +this has led some implementors to attempt to improve the definition of +transposition, but right-thinking people will accept no substitutes. The +@emacs transposition definition used in @hemlock has two useful properties: +@begin[enumerate] +Repeated applications of a transposition command have a useful effect. The +way to visualize this effect is that each use of the transposition command +drags the previous thing over the next thing. It is possible to correct +double transpositions easily using @hid[Transpose Characters]. + +Transposition commands move backward with a negative prefix argument, thus +undoing the effect of the equivalent positive argument. +@end[enumerate] + +@defcom[com "Transpose Characters", bind {C-t}] +@index[character, transposition]This command exchanges the characters on +either side of the point and moves forward, unless at the end of a line, in +which case it transposes the previous two characters without moving. +@enddefcom + +@defcom[com "Transpose Lines", bind (C-x C-t)] +@index[line, transposition]This command transposes the previous and +current line, moving down to the next line. With a zero argument, it +transposes the current line and the line the mark is on. +@enddefcom + +@defcom[com "Transpose Words", bind {M-t}] +@index[word, transposition]This command transposes the previous word and +the current or next word. +@enddefcom + + +@defcom[com "Transpose Regions", bind (C-x t)] +This command transposes two regions with endpoints defined by the mark stack +and point. To use this command, place three marks (in order) at the start and +end of the first region, and at the start of the second region, then place the +point at the end of the second region. Unlike the other transposition +commands, a second use will simply undo the effect of the first use, and to do +even this, you must reactivate the current region. +@enddefcom + + +@subsection[Whitespace Manipulation] +These commands change the amount of space between words. See also the +indentation commands in section @ref[indentation]. + +@defcom[com "Just One Space", bind (M-|)] +@index[whitespace, manipulation]@index[indentation, manipulation]This +command deletes all whitespace characters before and after the point and then +inserts one space. If a prefix argument is supplied, then that number of +spaces is inserted. +@enddefcom + +@defcom[com "Delete Horizontal Space", bind (M-\)] +This command deletes all blank characters around the point. +@enddefcom + +@defcom[com "Delete Blank Lines", bind (C-x C-o)] +This command deletes all blank lines surrounding the current line, leaving the +point on a single blank line. If the point is already on a single blank line, +then that line is deleted. If the point is on a non-blank line, then all blank +lines immediately following that line are deleted. This command is often used +to clean up after @comref[Open Line]. +@enddefcom + +@section[Filtering] + +@i[Filtering] is a simple way to perform a fairly arbitrary transformation +on text. Filtering text replaces the string in each line with the result +of applying a @llisp function of one argument to that string. The function must +neither destructively modify the argument nor the return value. It is an +error for the function to return a string containing newline characters. + +@defcom[com "Filter Region"] +This function prompts for an expression which is evaluated to obtain a +function to be used to filter the text in the region. For example, to +capitalize all the words in the region one could respond: +@begin[programexample] +Function: #'@comment<>string-capitalize +@end[programexample] +Since the function may be called many times, it should probably be +compiled. Functions for one-time use can be compiled using the compile +function as in the following example which removes all the semicolons on any line +which contains the string "@f[PASCAL]": +@begin[programexample] +Function: (compile nil '(lambda (s) + (if (search "PASCAL" s) + (remove #\; s) + s))) +@end[programexample] +@enddefcom + +@section[Searching and Replacing] +@index[searching]@index[replacing] +Searching for some string known to appear in the text is a commonly used method +of moving long distances in a file. Replacing occurrences of one pattern with +another is a useful way to make many simple changes to text. @hemlock provides +powerful commands for doing both of these operations. + +@defhvar[var "String Search Ignore Case", val {t}] +@index[case sensitivity] +This variable determines the kind of search done by searching and replacing +commands. +@enddefhvar + +@defcom[com "Incremental Search", bind {C-s}] +@defcom1[com "Reverse Incremental Search", bind {C-r}] +@hid[Incremental Search] searches for an occurrence of a string after the +current point. It is known as an incremental search because it reads +key-events form the keyboard one at a time and immediately searches for the +pattern of corresponding characters as you type. This is useful because +it is possible to initially type in a very short pattern and then add more +characters if it turns out that this pattern has too many spurious matches. + +This command dispatches on the following key-events as sub-commands: +@begin[description] +@binding[C-s]@\ + Search forward for an occurrence of the current pattern. This can be used +repeatedly to skip from one occurrence of the pattern to the next, or it can be +used to change the direction of the search if it is currently a reverse search. +If @binding[C-s] is typed when the search string is empty, then a search is +done for the string that was used by the last searching command. + +@binding[C-r]@\ + Similar to @binding[C-s], except that it searches backwards. + +@binding[Delete, Backspace]@\ + Undoes the effect of the last key-event typed. If that key-event simply added +to the search pattern, then this removes the character from the pattern, moving +back to the last match found before entering the removed character. If the +character was a @binding[C-s] or @binding[C-r], then this moves back to the +previous match and possibly reverses the search direction. + +@binding[C-g]@\ + If the search is currently failing, meaning that there is no occurrence of the +search pattern in the direction of search, then @binding[C-g] deletes enough +characters off the end of the pattern to make it successful. If the search +is currently successful, then @binding[C-g] causes the search to be aborted, +leaving the point where it was when the search started. Aborting the search +inhibits the saving of the current search pattern as the last search string. + +@binding[Escape]@\ + Exit at the current position in the text, unless the search string is empty, +in which case a non-incremental string search is entered. + +@binding[C-q]@\ + Search for the character corresponding to the next key-event, rather than +treating it as a command. +@end[description] +Any key-event not corresponding to a graphic character, except those just +described, causes the search to exit. @hemlock then uses the key-event in it +normal command interpretation. + +For example, typing @binding[C-a] will exit the search @i[and] go to the +beginning of the current line. When either of these commands successfully +exits, they push the starting position (before the search) on the mark stack. +If the current region was active when the search started, this foregoes pushing +a mark. +@enddefcom + +@defcom[com "Forward Search", bind (M-s)] +@defcom1[com "Reverse Search", bind (M-r)] +These commands do a normal dumb string search, prompting for the search +string in a normal dumb fashion. One reason for using a non-incremental +search is that it may be faster since it is possible to specify a long +search string from the very start. Since @hemlock uses the Boyer--Moore +search algorithm, the speed of the search increases with the size of the +search string. +When either of these commands successfully exits, they push the starting +position (before the search) on the mark stack. This is inhibited when the +current region is active. +@enddefcom + +@defcom[com "Query Replace", bind (M-%)] +This command prompts in the echo area for a target string and a replacement +string. It then searches for an occurrence of the target after the point. +When it finds a match, it prompts for a key-event indicating what action to +take. The following are valid responses: +@begin[description] +@binding[Space, y]@\ + Replace this occurrence of the target with the replacement string, and search +again. + +@binding[Delete, Backspace, n]@\ + Do not replace this occurrence, but continue the search. + +@binding[!]@\ + Replace this and all remaining occurrences without prompting again. + +@binding[.]@\ + Replace this occurrence and exit. + +@binding[C-r]@\ + Go into a recursive edit (see page @pageref[recursive-edits]) at the current +location. The search will be continued from wherever the point is left when +the recursive edit is exited. This is useful for handling more complicated +cases where a simple replacement will not achieve the desired effect. + +@binding[Escape]@\ + Exit without doing any replacement. + +@binding[Home, C-_, ?, h]@\ + Print a list of all the options available. +@end[description] +Any other key-event causes the command to exit, returning the key-event to the +input stream; thus, @hemlock will interpret it normally for a command binding. + +When the current region is active, this command uses it instead of the region +from point to the end of the buffer. This is especially useful when you expect +to use the @binding[!] option. + +If the replacement string is all lowercase, then a heuristic is used that +attempts to make the case of the replacement the same as that of the +particular occurrence of the target pattern. If "@f[foo]" is being +replaced with "@f[bar]" then "@f[Foo]" is replaced with "@f[Bar]" and +"@f[FOO]" with "@f[BAR]". + +This command may be undone with @hid[Undo], but its undoing may not be undone. +On a successful exit from this command, the starting position (before the +search) is pushed on the mark stack. +@enddefcom + +@defhvar[var "Case Replace", val {t}] +@index[case sensitivity] +If this variable is true then the case preserving heuristic in +@hid[Query Replace] is enabled, otherwise all replacements are done with +the replacement string exactly as specified. +@enddefhvar + +@defcom[com "Replace String"] +This command is the same as @hid[Query Replace] except it operates without ever +querying the user before making replacements. After prompting for a target and +replacement string, it replaces all occurrences of the target string following +the point. If a prefix argument is specified, then only that many occurrences +are replaced. When the current region is active, this command uses it instead +of the region from point to the end of the buffer. +@enddefcom + +@defcom[com "List Matching Lines"] +This command prompts for a search string and displays in a pop-up window all +the lines containing the string that are after the point. If a prefix argument +is specified, then this displays that many lines before and after each matching +line. When the current region is active, this command uses it instead of the +region from point to the end of the buffer. +@enddefcom + +@defcom[com "Delete Matching Lines"] +@defcom1[com "Delete Non-Matching Lines"] +@hid[Delete Matching Lines] prompts for a search string and deletes all lines +containing the string that are after the point. Similarly, @hid[Delete +Non-Matching Lines] deletes all lines following the point that do not contain +the specified string. When the current region is active, these commands uses +it instead of the region from point to the end of the buffer. +@enddefcom + + +@section[Page Commands] +@index[page commands] +Another unit of text recognized by @hemlock is the page. A @i[page] is a piece +of text delimited by formfeeds (@f[^L]'s.) The first non-blank line after the +page marker is the @i[page title]. The page commands are quite useful when +logically distinct parts of a file are put on separate pages. See also +@comref[Count Lines Page]. These commands only recognize @f[^L]'s at the +beginning of a lines, so those quoted in string literals do not get in the way. + +@defcom[com "Previous Page", bind (C-x @bf<]>)] +@defcom1[com "Next Page", bind (C-x [)] +@hid[Previous Page] moves the point to the previous page delimiter, while +@hid[Next Page] moves to the next one. Any page delimiters next to the point +are skipped. The prefix argument is a repeat count. +@enddefcom + +@defcom[com "Mark Page", bind (C-x C-p)] +This command puts the point at the beginning of the current page and the mark +at the end. If given a prefix argument, marks the page that many pages from the +current one. +@enddefcom + +@defcom[com "Goto Page"] +This command does various things, depending on the prefix argument: +@begin[description] +@i[no argument]@\goes to the next page. + +@i[positive argument]@\goes to an absolute page number, moving that many pages +from the beginning of the file. + +@i[zero argument]@\prompts for string and goes to the page with that string in +its title. Repeated invocations in this manner continue searching from the +point of the last find, and a first search with a particular pattern pushes a +buffer mark. + +@i[negative argument]@\moves backward by that many pages, if possible. +@end[description] +@enddefcom + +@defcom[com "View Page Directory"] +@defcom1[com "Insert Page Directory"] +@hid[View Page Directory] uses a pop-up window to display the number and title +of each page in the current buffer. @hid[Insert Page Directory] is the same +except that it inserts the text at the beginning of the buffer. With a prefix +argument, @hid[Insert Page Directory] inserts at the point. +@enddefcom + + +@section[Counting Commands] + +@defcom[com "Count Words"] +This command counts the number of words from the current point to the end of +the buffer, displaying a message in the echo area. When the current region is +active, this uses it instead of the region from the point to the end of the +buffer. Word delimiters are determined by the current major mode. +@enddefcom + +@defcom[com "Count Lines"] +This command counts the number of lines from the current point to the end of +the buffer, displaying a message in the echo area. When the current region is +active, this uses it instead of the region from the point to the end of the +buffer. +@enddefcom + +@defcom[com "Count Lines Page", bind (C-x l)] +This command displays the number of lines in the current page and the number of +lines before and after the point within that page. If given a prefix argument, +the entire buffer is counted instead of just the current page. +@enddefcom + +@defcom[com "Count Occurrences"] +This command prompts for a search string and displays the number of occurrences +of that string in the text from the point to the end of the buffer. When the +current region is active, this uses it instead of the region from the point to +the end of the buffer. +@enddefcom + + +@section[Registers] +@index[registers] +Registers allow you to save a text position or chunk of text associated with a +key-event. This is a convenient way to repeatedly access a commonly-used +location or text fragment. The concept and key bindings should be familiar to +TECO users. + +@defcom[com "Save Position", bind (C-x s)] +@defcom1[com "Jump to Saved Position", bind (C-x j)] +These commands manipulate registers containing textual positions. +@hid[Save Position] prompts for a register and saves the location of the +current point in that register. @hid[Jump to Saved Position] prompts for a +register and moves the point to the position saved in that register. If the +saved position is in a different buffer, then that buffer is made current. +@enddefcom + +@defcom[com "Put Register", bind (C-x x)] +@defcom1[com "Get Register", bind (C-x g)] +These commands manipulate registers containing text. @hid[Put Register] +prompts for a register and puts the text in the current region into the +register. @hid[Get Register] prompts for a register and inserts the text in +that register at the current point. +@enddefcom + +@defcom[com "List Registers"] +@defcom1[com "Kill Register"] +@hid[List Registers] displays a list of all the currently defined registers in +a pop-up window, along with a brief description of their contents. +@hid[Kill Register] prompts for the name of a register and deletes that +register. +@enddefcom diff --git a/docs/hem/user/intro.mss b/docs/hem/user/intro.mss new file mode 100644 index 0000000000000000000000000000000000000000..8049fc62f2f1e05011d68bf011bde8736289ab8c --- /dev/null +++ b/docs/hem/user/intro.mss @@ -0,0 +1,1053 @@ +@comment{-*- Dictionary: bld:scribe/hem/hem; Mode: spell; Package: Hemlock -*-} +@chap[Introduction] + +@hemlock is a text editor which follows in the tradition of @emacs +and the Lisp Machine editor ZWEI. In its basic form, @hemlock has almost +the same command set as ITS/TOPS-20 @emacs@foot[In this document, "Emacs" +refers to this, the original version, rather than to any of the large +numbers of text editors inspired by it which may go by the same name.], +and similar features such as multiple windows and extended commands, as +well as built in documentation features. The reader should bear in mind +that whenever some powerful feature of @hemlock is described, it has +probably been directly inspired by @emacs. + +This manual describes @hemlock@comment{}'s commands and other user visible +features and then goes on to tell how to make simple customizations. For +complete documentation of the @hemlock primitives with which commands are +written, the @i[Hemlock Command Implementor's Manual] is also available. + +@section[The Point and The Cursor] + +@index[point]@index[cursor]The @i[point] is the current focus of editing +activity. Text typed in by the user is inserted at the point. Nearly all +commands use the point as a indication of what text to examine or modify. +Textual positions in @hemlock are between characters. This may seem a bit +curious at first, but it is necessary since text must be inserted between +characters. Although the point points between characters, it is sometimes said +to point @i[at] a character, in which case the character after the point is +referred to. + +The @i[cursor] is the visible indication of the current focus of attention: a +rectangular blotch under @windows, or the hardware cursor on a terminal. The +cursor is usually displayed on the character which is immediately after the +point, but it may be displayed in other places. Wherever the cursor is +displayed it indicates the current focus of attention. When input is being +prompted for in the echo area, the cursor is displayed where the input is to +go. Under @windows the cursor is only displayed when @hemlock is waiting +for input. + + +@section[Notation] + +There are a number of notational conventions used in this manual which need +some explanation. + + +@subsection[Key-events] + +@label[key-events] +@index[key-events, notation] +@index[bits, key-event] +@index[modifiers, key-event] +The canonical representation of editor input is a @i[key-event]. When you type +on the keyboard, @hemlock receives key-events. Key-events have names for their +basic form, and we refer to this name as a @i[keysym]. This manual displays +keysyms in a @bf[Bold] font. For example, @bf[a] and @bf[b] are the keys that +normally cause the editor to insert the characters @i[a] and @i[b]. + +Key-events have @i[modifiers] or @i[bits] indicating a special interpretation +of the root key-event. Although the keyboard places limitations on what +key-events you can actually type, @hemlock understands arbitrary combinations +of the following modifiers: @i[Control], @i[Meta], @i[Super], @i[Hyper], +@i[Shift], and @i[Lock]. This manual represents the bits in a key-event by +prefixing the keysym with combinations of @bf[C-], @bf[M-], @bf[S-], @bf[H-], +@bf[Shift-], and @bf[Lock]. For example, @bf[a] with both the control and meta +bits set appears as @bf[C-M-a]. In general, ignore the shift and lock +modifiers since this manual never talks about keysyms that explicitly have +these bits set; that is, it may talk about the key-event @bf[A], but it would +never mention @bf[Shift-a]. These are actually distinct key-events, but +typical input coercion turns presents @hemlock with the former, not the latter. + +Key-event modifiers are totally independent of the keysym. This may be new to +you if you are used to thinking in terms of ASCII character codes. For +example, with key-events you can distinctly identify both uppercase and +lowercase keysyms with the control bit set; therefore, @bf[C-a] and @bf[C-A] +may have different meanings to @hemlock. + +Some keysyms' names consist of more than a single character, and these usually +correspond to the legend on the keyboard. For example, some keyboards let you +enter @bf[Home], @bf[Return], @bf[F9], etc. + +In addition to a keyboard, you may have a mouse or pointer device. Key-events +also represent this kind of input. For example, the down and up transitions of +the @i[left button] correspond to the @bf[Leftdown] and @bf[Leftup] keysyms. + +See sections @ref[key-bindings], @ref[using-x], @ref[using-terminals] + + +@subsection[Commands] + +@index[commands]@label[commands]Nearly everything that can be done in +@hemlock is done using a command. Since there are many things worth +doing, @hemlock provides many commands, currently nearly two hundred. +Most of this manual is a description of what commands exist, how they are +invoked, and what they do. This is the format of a command's +documentation: + +@defcom[com "Sample Command", bind (C-M-q, C-`)] +@begin[quotation, facecode i, leftmargin 8ems, rightmargin 3.5ems, +below 0.8 lines] +This command's name is @hid[Sample Command], and it is bound to +@w(@bf(C-M-q)) and @bf[C-`], meaning that typing either of these will +invoke it. After this header comes a description of what the command does: +@end[quotation] + +This command replaces all occurrences following the point of the string +"@f[Pascal]" with the string "@f[Lisp]". If a prefix argument is supplied, +then it is interpreted as the maximum number of occurrences to replace. If +the prefix argument is negative then the replacements are done backwards +from the point. +@comment< +@begin[quotation, facecode i, leftmargin 8ems, rightmargin 3.5ems, +above 0.8 lines, below 0.8 lines] +Toward the end of the description there may be information primarily of +interest to customizers and command implementors. If you don't understand +this information, don't worry, the writer probably forgot to speak English. +@end[quotation] + +@b[Arguments:] +@begin[description] +@i[target]@\The string to replace with "@f[Lisp]". + +@i[buffer]@\The buffer to do the replacement in. If this is @f[:all] then +the replacement is done in all buffers. +@end[description]> +@enddefcom + + +@subsection[Hemlock Variables] + +@index[variables, hemlock]@hemlock variables supply a simple +customization mechanism by permitting commands to be parameterized. For +details see page @pageref[vars]. + +@defhvar[var "Sample Variable", val {36}] +@begin[quotation, facecode i, leftmargin 8ems, below 0.8 lines] +The name of this variable is @hid[Sample Variable] and its initial value is +36. +@end[quotation] +This variable sets a lower limit on the number of replacements that be done +by @hid[Sample Command]. If the prefix argument is supplied, and smaller +in absolute value than @hid[Sample Variable], then the user is prompted as +to whether that small a number of occurrences should be replaced, so as to +avoid a possibly disastrous error. +@enddefhvar + + +@section[Invoking Commands] +@index[invocation, command] +In order to get a command to do its thing, it must be invoked. The user can do +this two ways, by typing the @i[key] to which the command is @i[bound] or by +using an @i[extended command]. Commonly used commands are invoked via their +key bindings since they are faster to type, while less used commands are +invoked as extended commands since they are easier to remember. + + +@subsection[Key Bindings] +@index[bindings, key] +@index[key bindings] +@label[key-bindings] +A key is a sequence of key-events (see section @ref[key-events]) typed on the +keyboard, usually only one or two in length. Sections @ref[using-x] and +@ref[using-terminals] contain information on particular input devices. + +When a command is bound to a key, typing the key causes @hemlock to invoke the +command. When the command completes its job, @hemlock returns to reading +another key, and this continually repeats. + +Some commands read key-events interpreting them however each command desires. +When commands do this, key bindings have no effect, but you can usually abort +@hemlock whenever it is waiting for input by typing @binding[C-g] (see section +@ref[aborting]). You can usually find out what options are available by typing +@binding[C-_] or @binding[Home] (see section @ref[help]). + +The user can easily rebind keys to different commands, bind new keys to +commands, or establish bindings for commands never bound before (see section +@ref[binding-keys]). + +In addition to the key bindings explicitly listed with each command, there are +some implicit bindings created by using key translations@foot[Key translations +are documented in the @i[Hemlock Command Implementor's Manual].]. These +bindings are not displayed by documentation commands such as @hid[Where Is]. +By default, there are only a few key translations. The modifier-prefix +characters @bf[C-^], @bf[Escape], @bf[C-z], or @bf[C-c] may be used when typing +keys to convert the following key-event to a control, meta, control-meta, or +hyper key-event. For example, @bf[C-x Escape b] invokes the same commands as +@bf[C-x M-b], and @bf[C-z u] is the same as @bf[C-M-u]. This allows user to +type more interesting keys on limited keyboards that lack control, meta, and +hyper keys. +@index[bit-prefix key-events] + + +@defhvar[var "Key Echo Delay", val {1.0}] +A key binding may be composed of several key-events, especially when you enter +it using modifier-prefix key-events. @hemlock provides feedback for partially +entered keys by displaying the typed key-events in the echo area. In order to +avoid excessive output and clearing of the echo area, this display is delayed +by @hid[Key Echo Delay] seconds. If this variable is set to @nil, then +@hemlock foregoes displaying initial subsequences of keys. +@enddefhvar + + +@subsection[Extended Commands] + +@index[commands, extended]A command is invoked as an extended command by +typing its name to the @hid[Extended Command] command, which is invoked +using its key binding, @binding[M-x]. + +@defcom[com "Extended Command", bind {M-x}] +This command prompts in the echo area for the name of a command, and then +invokes that command. The prefix argument is passed through to the command +invoked. The command name need not be typed out in full, as long as enough +of its name is supplied to uniquely identify it. Completion is available +using @binding[Escape] and @binding[Space], and a list of possible completions +is given by @binding[Home] or @binding[C-_]. +@enddefcom + + +@section[The Prefix Argument] + +@index[prefix argument]The prefix argument is an integer argument which +may be supplied to a command. It is known as the prefix argument because +it is specified by invoking some prefix argument setting command +immediately before the command to be given the argument. The following +statements about the interpretation of the prefix argument are true: +@begin[itemize] +When it is meaningful, most commands interpret the prefix argument as a +repeat count, causing the same effect as invoking the command that many +times. + +When it is meaningful, most commands that use the prefix argument interpret +a negative prefix argument as meaning the same thing as a positive +argument, but the action is done in the opposite direction. + +Most commands treat the absence of a prefix argument as meaning the same +thing as a prefix argument of one. + +Many commands ignore the prefix argument entirely. + +Some commands do none of the above. +@end[itemize] +The following commands are used to set the prefix argument: + +@defcom[com "Argument Digit", stuff (bound to all control or meta digits)] +Typing a number using this command sets the prefix argument to that number, +for example, typing @binding[M-1 M-2] sets the prefix argument to twelve. +@enddefcom + +@defcom[com "Negative Argument", bind {M--}] +This command negates the prefix argument, or if there is none, sets it to +negative one. For example, typing @binding[M-- M-7] sets the prefix +argument to negative seven. +@enddefcom + +@defcom[com "Universal Argument", bind {C-u}] +@defhvar1[var "Universal Argument Default", val {4}] +This command sets the prefix argument or multiplies it by four. If digits +are typed immediately afterward, they are echoed in the echo area, and the +prefix argument is set to the specified number. If no digits are typed +then the prefix argument is multiplied by four. @binding[C-u - 7] sets the +prefix argument to negative seven. @binding[C-u C-u] sets the prefix +argument to sixteen. @binding[M-4 M-2 C-u] sets the prefix argument to one +hundred and sixty-eight. @binding[C-u M-0] sets the prefix argument to +forty. + +@hid[Universal Argument Default] determines the default value and multiplier +for the @hid[Universal Argument] command. +@enddefcom + + +@section[Modes] + +@label[modes]@index[modes]A mode provides a way to change @hemlock@comment{}'s +behavior by specifying a modification to current key bindings, values of +variables, and other things. Modes are typically used to adjust @hemlock +to suit a particular editing task, e.g. @hid[Lisp] mode is used for editing +@llisp code. + +Modes in @hemlock are not like modes in most text editors; @hemlock is really a +"modeless" editor. There are two ways that the @hemlock mode concept differs +from the conventional one: +@begin[enumerate] +Modes do not usually alter the environment in a very big way, i.e. replace +the set of commands bound with another totally disjoint one. When a mode +redefines what a key does, it is usually redefined to have a slightly +different meaning, rather than a totally different one. For this reason, +typing a given key does pretty much the same thing no matter what modes are +in effect. This property is the distinguishing characteristic of a +modeless editor. + +Once the modes appropriate for editing a given file have been chosen, they +are seldom, if ever, changed. One of the advantages of modeless editors is +that time is not wasted changing modes. +@end[enumerate] + +@index[major mode]A @i[major mode] is used to make some big change in the +editing environment. Language modes such as @hid[Pascal] mode are major +modes. A major mode is usually turned on by invoking the command +@i{mode-name}@hid[ Mode] as an extended command. There is only one major +mode present at a time. Turning on a major mode turns off the one that is +currently in effect. + +@index[minor mode]A @i[minor mode] is used to make a small change in the +environment, such as automatically breaking lines if they get too long. +Unlike major modes, any number of minor modes may be present at once. +Ideally minor modes should do the "right thing" no matter what major and +minor modes are in effect, but this is may not be the case when key +bindings conflict. + +Modes can be envisioned as switches, the major mode corresponding to one big +switch which is thrown into the correct position for the type of editing being +done, and each minor mode corresponding to an on-off switch which controls +whether a certain characteristic is present. + +@defcom[com "Fundamental Mode"] +This command puts the current buffer into @hid[Fundamental] mode. +@hid[Fundamental] mode is the most basic major mode: it's the next best thing +to no mode at all. +@enddefcom + + +@section[Display Conventions] +@index[display conventions] +There are two ways that @hemlock displays information on the screen; one is +normal @i[buffer display], in which the text being edited is shown on the +screen, and the other is a @i[pop-up window]. + + +@subsection[Pop-Up Windows] +@index[pop-up windows] +Some commands print out information that is of little permanent value, and +these commands use a @i[pop-up] window to display the information. It is known +as a @i[pop-up] window because it temporarily appears on the screen overlaying +text already displayed. Most commands of this nature can generate their output +quickly, but in case there is a lot of output, or the user wants to repeatedly +refer to the same output while editing, @hemlock saves the output in a buffer. +Different commands may use different buffers to save their output, and we refer +to these as @i[random typeout] buffers. + +If the amount of output exceeds the size of the pop-up window, @Hemlock +displays the message @w<"@f[--More--]"> after each window full. The following +are valid responses to this prompt: +@Begin[Description] +@bf[Space], @bf[y]@\ + Display the next window full of text. + +@bf[Delete], @bf[Backspace], @bf[n]@\ + Abort any further output. + +@bf[Escape], @bf[!]@\ + Remove the window and continue saving any further output in the buffer. + +@bf[k]@\ + This is the same as @bf[!] or @bf[escape], but @hemlock makes a normal window +over the pop-up window. +@End[Description] +Any other input causes the system to abort using the key-event to determine +the next command to execute. + +When the output is complete, @hemlock displays the string @w<"@f[--Flush--]"> +in the pop-up window's modeline, indicating that the user may flush the +temporary display. Typing any of the key-events described above removes the +pop-up window, but typing @bf[k] still produces a window suitable for normal +editing. Any other input also flushes the display, but @hemlock uses the +key-event to determine the next command to invoke. + +@defcom[com "Select Random Typeout Buffer", bind {H-t}] +This command makes the most recently used random typeout buffer the current +buffer in the current window. +@enddefcom + +Random typeout buffers are always in @hid[Fundamental] mode. + + +@subsection[Buffer Display] +@index[buffer, display] +@index[display, buffer] + +If a line of text is too long to fit within the screen width it is @i[wrapped], +with @hemlock displaying consecutive pieces of the text line on as many screen +lines as needed to hold the text. @hemlock indicates a wrapped line by placing +a line-wrap character in the last column of each screen line. Currently, the +line-wrap character is an exclamation point (@f[!]). It is possible for a line +to wrap off the bottom of the screen or on to the top. + +@hemlock wraps screen lines when the line is completely full regardless of the +line-wrap character. Most editors insert the line-wrap character and wrap a +single character when a screen line would be full if the editor had avoided +wrapping the line. In this situation, @hemlock would leave the screen line +full. This means there are always at least two characters on the next screen +line if @hemlock wraps a line of display. When the cursor is at the end of a +line which is the full width of the screen, it is displayed in the last column, +since it cannot be displayed off the edge. + +@hemlock displays most characters as themselves, but it treats some +specially: +@begin[itemize] +Tabs are treated as tabs, with eight character tab-stops. + +Characters corresponding to ASCII control characters are printed as +@f[^]@i[char]; for example, a formfeed is @f[^L]. + +Characters with the most-significant bit on are displayed as +@f[<]@i[hex-code]@f[>]; for example, @f[]. +@end[itemize] +Since a character may be displayed using more than one printing character, +there are some positions on the screen which are in the middle of a character. +When the cursor is on a character with a multiple-character representation, +@hemlock always displays the cursor on the first character. + + +@subsection[Modelines] +@label[modelines] +@index[modeline] +A modeline is the line displayed at the bottom of each window where @hemlock +shows information about the buffer displayed in that window. Here is a typical +modeline: +@begin[programexample] +Hemlock USER: (Fundamental Fill) /usr/slisp/hemlock/user.mss +@end[programexample] +This tells us that the file associated with this buffer is +"@f[/usr/slisp/hemlock/user.mss]", and the @hid[Current Package] for Lisp +interaction commands is the @f["USER"] package. The modes currently present +are @hid[Fundamental] and @hid[Fill]; the major mode is always displayed first, +followed by any minor modes. If the buffer has no associated file, then the +buffer name will be present instead: +@begin[programexample] +Hemlock PLAY: (Lisp) Silly: +@end[programexample] +In this case, the buffer is named @hid[Silly] and is in @hid[Lisp] mode. The +user has set @hid[Current Package] for this buffer to @f["PLAY"]. + +@defhvar[var "Maximum Modeline Pathname Length", val {nil}] +This variable controls how much of a pathname @hemlock displays in a modeline. +Some distributed file systems can have very long pathnames which leads to the +more particular information in a pathname running off the end of a modeline. +When set, the system chops off leading directories until the name is less than +the integer value of this variable. Three dots, @f[...], indicate a truncated +name. The user can establish this variable buffer locally with the +@hid[Defhvar] command. +@enddefhvar + +If the user has modified the buffer since the last time it was read from or +save to a file, then the modeline contains an asterisk (@f[*]) between the +modes list and the file or buffer name: +@begin[programexample] +Hemlock USER: (Fundamental Fill) * /usr/slisp/hemlock/user.mss +@end[programexample] +This serves as a reminder that the buffer should be saved eventually. + +@index[status line] +There is a special modeline known as the @i[status line] which appears as the +@hid[Echo Area]'s modeline. @Hemlock and user code use this area to display +general information not particular to a buffer @dash recursive edits, whether +you just received mail, etc. + + +@section[Use with X Windows] +@label[using-x] +@index[X windows, use with] +@index[window management] +It is preferable to use @hemlock on a workstation with a bitmap display and a +window manager since @hemlock makes good use of the window manager and +non-ASCII input devices such as the mouse and modifier keys. This section +deals with using @hemlock under X windows, which is currently the only +supported window manager. + + +@subsection[Event Translation] +@index[keyboard use under X] +@index[translation of keys under X] +Each X key event is translated into a canonical input representation, a +key-event. The X key event consists of a scan-code and modifier bits, and +these translate to an X keysym. This keysym and the modifier bits map to a +key-event. + +If you type a key with a shift key held down, this typically maps to a distinct +X keysym. For example, the shift of @bf[3] is @bf[#], and these have different +X keysyms. Some keys map to the same X keysym regardless of the shift bit, +such as @bf[Tab], @bf[Space], @bf[Return], etc. When the X lock bit is on, the +system treats this as a caps-lock, only mapping keysyms for lowercase letters +to shifted keysyms. + +The key-event has a keysym and a field of bits. The X keysyms map directly to +the key-event keysyms. There is a distinct mapping for each CLX modifier bit +to a key-event bit. This tends to eliminate shift and lock modifiers, so +key-events usually only have control, meta, hyper, and super bits on. Hyper +and super usually get turned on with prefix key-events that set them on the +following key-event, but you can turn certain keys on the keyboard into hyper +and super keys. See the X manuals and the @i[Hemlock Command Implementor's +Manual] for details. + +The system also maps mouse input to key-events. Each mouse button has distinct +key-event keysyms for whether the user pressed or released it. For +convenience, @hemlock makes use of an odd property of converting mouse events +to key-events. If you enter a mouse event with the shift key held down, +@hemlock sees the key-event keysym for the mouse event, but the key-event has +the super bit turned on. For example, if you press the left button with the +shift key pressed, @hemlock sees @bf[S-Leftdown]. + +Note that with the two button mouse on the IBM RT PC, the only way to to send +@bf[Middledown] is to press both the left and right buttons simultaneously. +This is awkward, and it often confuses the X server. For this reason, the +commands bound to the middle button are also bound to the shifted left button, +@bf[S-Leftdown], which is much easier to type. + + +@subsection[Cut Buffer Commands] +@index[cutting]@index[pasting] These commands allow the X cut buffer to be +used from @hemlock . Although @hemlock can cut arbitrarily large regions, +a bug in the standard version 10 xterm prevents large regions from being +pasted into an xterm window. + +@defcom[com "Region to Cut Buffer", bind {M-Insert}] +@defcom1[com "Insert Cut Buffer", bind {Insert}] +These commands manipulate the X cut buffer. @hid[Region to Cut Buffer] puts +the text in the region into the cut buffer. @hid[Insert Cut Buffer] inserts +the contents of the cut buffer at the point. +@enddefcom + +@subsection[Redisplay and Screen Management] + +These variables control a number of the characteristics of @hemlock bitmap +screen management. + +@defhvar[var "Bell Style", val {:border-flash}] +@defhvar1[var "Beep Border Width", val {20}] +@hid[Bell Style] determines what beeps do in @hemlock. Acceptable values are +@kwd[border-flash], @kwd[feep], @kwd[border-flash-and-feep], @kwd[flash], +@kwd[flash-and-feep], and @nil (do nothing). + +@hid[Beep Border Width] is the width in pixels of the border flashed by border +flash beep styles. +@enddefhvar + +@defhvar[var "Reverse Video", val {nil}] +If this variable is true, then @hemlock paints white on black in window +bodies, black on white in modelines. +@enddefhvar + +@defhvar[var "Thumb Bar Meter", val {t}] +If this variable is true, then windows will be created to be displayed with a +ruler in the bottom border of the window. +@enddefhvar + +@defhvar[var "Set Window Autoraise", val {:echo-only}] +When true, changing the current window will automatically raise the new current +window. If the value is @kwd[echo-only], then only the echo area window will +be raised automatically upon becoming current. +@enddefhvar + +@defhvar[var "Default Initial Window Width", val {80}] +@defhvar1[var "Default Initial Window Height", val {24}] +@defhvar1[var "Default Initial Window X"] +@defhvar1[var "Default Initial Window Y"] +@defhvar1[var "Default Window Height", val {24}] +@defhvar1[var "Default Window Width", val {80}] +@index[window placement] +@Hemlock uses the variables with "@hid[Initial]" in their names when it first +starts up to make its first window. The width and height are specified in +character units, but the x and y are specified in pixels. The other variables +determine the width and height for interactive window creation, such as making +a window with @comref[New Window]. +@enddefhvar + +@defhvar[var "Cursor Bitmap File", val {"/usr/misc/.lisp/lib/hemlock.cursor"}] +This variable determines where the mouse cursor bitmap is read from when +@hemlock starts up. The mask is found by merging this name with "@f[.mask]". +This has to be a full pathname for the C routine. +@enddefhvar + + +@defhvar[var "Default Font"] +This variable holds the string name of the font to be used for normal text +display: buffer text, modelines, random typeout, etc. The font is loaded at +initialization time, so this variable must be set before entering @hemlock. +When @nil, the display type is used to choose a font. +@enddefhvar + + +@section[Use With Terminals] +@label[using-terminals]@index[terminals, use with] @hemlock can also be used +with ASCII terminals and terminal emulators. Capabilities that depend on +@windows (such as mouse commands) are not available, but nearly everything else +can be done. + +@subsection[Terminal Input] +@index[ASCII keyboard translation] +@index[bit-prefix key-events] +@index[prefix key-events] +@index[key-event, prefix] +The most important limitation of a terminal is its input capabilities. On a +workstation with function keys and independent control, meta, and shift +modifiers, it is possible to type 800 or so distinct single keystrokes. +Although by default, @hemlock uses only a fraction of these combinations, there +are many more than the 128 key-events available in ASCII. + +On a terminal, @hemlock attempts to translate ASCII control characters into the +most useful key-event: +@begin[itemize] +On a terminal, control does not compose with shift. If the control key is down +when you type a letter keys, the terminal always sends one code regardless of +whether the shift key is held. Since @hemlock primarily binds commands to +key-events with keysyms representing lowercase letters regardless of what bits +are set in the key-event, the system translates the ASCII control codes to a +keysym representing the appropriate lowercase characters. This keysym then +forms a key-event with the control bit set. Users can type @bf[C-c] followed +by an uppercase character to form a key-event with a keysym representing an +uppercase character and bits with the control bit set. + +On a terminal, some of the named keys generate an ASCII control code. For +example, @f[Return] usually sends a @f[C-m]. The system translates these ASCII +codes to a key-event with an appropriate keysym instead of the keysym named by +the character which names the ASCII code. In the above example, typing the +@f[Return] key would generate a key-event with the @bf[Return] keysym and no +bits. It would NOT translate to a key-event with the @bf[m] keysym and the +control bit. +@end[itemize] + +Since terminals have no meta key, you must use the @bf[Escape] and @bf[C-Z] +modifier-prefix key-events to invoke commands bound to key-events with the meta +bit or meta and control bits set. ASCII terminals cannot generate all +key-events which have the control bit on, so you can use the @bf[C-^] +modifier-prefix. The @bf[C-c] prefix sets the hyper bit on the next key-event +typed. + +When running @hemlock from a terminal @f[^\] is the interrupt key-event. +Typing this will place you in the Lisp debugger. + +When using a terminal, pop-up output windows cannot be retained after the +completion of the command. + + +@subsection[Terminal Redisplay] + +Redisplay is substantially different on a terminal. @Hemlock uses different +algorithms, and different parameters control redisplay and screen management. + +Terminal redisplay uses the Unix termcap database to find out how to use a +terminal. @hemlock is useful with terminals that lack capabilities for +inserting and deleting lines and characters, and some terminal emulators +implement these operations very inefficiently (such as xterm). +If you realize poor performance when scrolling, create a termcap entry that +excludes these capabilities. + +@defhvar[var "Scroll Redraw Ratio", val {nil}] +This is a ratio of "inserted" lines to the size of a window. When this ratio +is exceeded, insert/delete line terminal optimization is aborted, and every +altered line is simply redrawn as efficiently as possible. For example, +setting this to 1/4 will cause scrolling commands to redraw the entire window +instead of moving the bottom two lines of the window to the top (typically 3/4 +of the window is being deleted upward and inserted downward, hence a redraw); +however, commands like @hid[New Line] and @hid[Open Line] will still work +efficiently, inserting a line and moving the rest of the window's text +downward. +@enddefhvar + + +@section[The Echo Area] + +@index[echo area] +@index[prompting] +The echo area is the region which occupies the bottom few lines on the screen. +It is used for two purposes: displaying brief messages to the user and +prompting. + +When a command needs some information from the user, it requests it by +displaying a @i[prompt] in the echo area. The following is a typical prompt: +@begin[programexample] +Select Buffer: [hemlock-init.lisp /usr/foo/] +@end[programexample] +The general format of a prompt is a one or two word description of the input +requested, possibly followed by a @i[default] in brackets. The default is a +standard response to the prompt that @hemlock uses if you type @bf[Return] +without giving any other input. + +There are four general kinds of prompts: @comment +@begin[description] +@i[key-event]@\ + The response is a single key-event and no confirming @binding[Return] is +needed. + +@i[keyword]@\ + The response is a selection from one of a limited number of choices. +Completion is available using @binding[Space] and @binding[Escape], and you +only need to supply enough of the keyword to distinguish it from any other +choice. In some cases a keyword prompt accepts unknown input, indicating the +prompter should create a new entry. If this is the case, then you must enter +the keyword fully specified or completed using @binding[Escape]; this +distinguishes entering an old keyword from making a new keyword which is a +prefix of an old one since the system completes partial input automatically. + +@i[file]@\ + The response is the name of a file, which may have to exist. Unlike other +prompts, the default has some effect even after the user supplies some input: +the system @i[merges] the default with the input filename. See page +@pageref(merging) for a description of filename merging. @bf[Escape] and +@bf[Space] complete the input for a file parse. + +@i[string]@\ + The response is a string which must satisfy some property, such as being the +name of an existing file. +@end[description] + +@index[history, echo area] +These key-events have special meanings when prompting: +@begin[description] +@binding[Return]@\ + Confirm the current parse. If no input has been entered, then use the +default. If for some reason the input is unacceptable, @hemlock does two +things: +@Begin[enumerate] +beeps, if the variable @hid[Beep on Ambiguity] set, and + +moves the point to the end of the first word requiring disambiguation. +@End[enumerate] +This allows you to add to the input before confirming the it again. + +@binding[Home, C-_]@\ + Print some sort of help message. If the parse is a keyword parse, then print +all the possible completions of the current input in a pop-up window. + +@binding[Escape]@\ + Attempt to complete the input to a keyword or file parse as far as possible, +beeping if the result is ambiguous. When the result is ambiguous, @hemlock +moves the point to the first ambiguous field, which may be the end of the +completed input. + +@binding[Space]@\ + In a keyword parse, attempt to complete the input up to the next space. This +is useful for completing the names of @hemlock commands and similar things +without beeping a lot, and you can continue entering fields while leaving +previous fields ambiguous. For example, you can invoke @hid[Forward Word] as +an extended command by typing @binding[M-X f Space w Return]. Each time the +user enters space, @Hemlock attempts to complete the current field and all +previous fields. + +@binding[C-i, Tab]@\ + In a string or keyword parse, insert the default so that it may be edited. + +@binding[C-p]@\ + Retrieve the text of the last string input from a history of echo area inputs. +Repeating this moves to successively earlier inputs. + +@binding[C-n]@\ + Go the other way in the echo area history. + +@binding[C-q]@\ + Quote the next key-event so that it is not interpreted as a command. +@end[description] + +@defhvar[var "Ignore File Types"] +This variable is a list of file types (or extensions), represented as a string +without the dot, e.g. @f["fasl"]. Files having any of the specified types will +be considered nonexistent for completion purposes, making an unambiguous +completion more likely. The initial value contains most common binary and +output file types. +@enddefhvar + + +@section[Online Help] + +@label[help] +@index[online help] +@index[documentation, hemlock] +@hemlock has a fairly good online documentation facility. You can get brief +documentation for every command, variable, character attribute, and key +by typing a key. + +@defcom[com "Help", bind (Home, C-_)] +This command prompt for a key-event indicating one of a number of other +documentation commands. The following are valid responses: +@begin[description] +@bf[a]@\ + List commands and other things whose names contain a specified keyword. + +@bf[d]@\ + Give the documentation and bindings for a specified command. + +@bf[g]@\ + Give the documentation for any @hemlock thing. + +@bf[v]@\ + Give the documentation for a @hemlock variable and its values. + +@bf[c]@\ + Give the documentation for a command bound to some key. + +@bf[l]@\ + List the last sixty key-events typed. + +@bf[m]@\ + Give the documentation for a mode followed by a short description of its +mode-specific bindings. + +@bf[p]@\ + Give the documentation and bindings for commands that have at least one +binding involving a mouse/pointer key-event. + +@bf[w]@\ + List all the key bindings for a specified command. + +@bf[t]@\ + Describe a @llisp object. + +@binding[q]@\ + Quit without doing anything. + +@binding[Home, C-_, ?, h]@\ + List all of the options and what they do. +@end[description] +@enddefcom + +@defcom[com "Apropos", bind (Home a, C-_ a)] +This command prints brief documentation for all commands, variables, and +character attributes whose names match the input. This performs a prefix match +on each supplied word separately, intersecting the names in each word's result. +For example, giving @hid[Apropos] "@f[f m]" causes it to tersely describe +following commands and variables: +@Begin[Itemize] +@hid[Auto Fill Mode] + +@hid[Fundamental Mode] + +@hid[Mark Form] + +@hid[Default Modeline Fields] + +@hid[Fill Mode Hook] + +@hid[Fundamental Mode Hook] +@End[Itemize] +Notice @hid[Mark Form] demonstrates that the "@f[f]" words may follow the +"@f[m]" order of the fields does not matter for @hid[Apropos]. + +The bindings of commands and values of variables are printed with the +documentation. +@enddefcom + +@defcom[com "Describe Command", bind (Home d, C-_ d)] +This command prompts for a command and prints its full documentation and all +the keys bound to it. +@enddefcom + +@defcom[com "Describe Key", bind (Home c, C-_ c, M-?)] +This command prints full documentation for the command which is bound to +the specified key in the current environment. +@enddefcom + +@defcom[com "Describe Mode", bind (Home m, C-_ m)] +This command prints the documentation for a mode followed by a short +description of each of its mode-specific bindings. +@enddefcom + +@defcom[com "Show Variable"] +@defcom1[com "Describe and Show Variable"] +@hid[Show Variable] prompts for the name of a variable and displays +the global value of the variable, the value local to the current buffer (if +any), and the value of the variable in all defined modes that have it as a +local variable. @hid[Describe and Show Variable] displays the variable's +documentation in addition to the values. +@enddefcom + +@defcom[com "What Lossage", bind (Home l, C-_ l)] +This command displays the last sixty key-events typed. This can be useful +if, for example, you are curious what the command was that you typed by +accident. +@enddefcom + +@defcom[com "Describe Pointer"] +This command displays the documentation and bindings for commands that have +some binding involving a mouse/pointer key-event. It will not show the +documentation for the @hid[Illegal] command regardless of whether it has a +pointer binding. +@enddefcom + +@defcom[com "Where Is", bind (Home w, C-_ w)] +This command prompts for the name of a command and displays its key +bindings in a pop-up window. If a key binding is not global, the +environment in which it is available is displayed. +@enddefcom + +@defcom[com "Generic Describe", bind (Home g, C-_ g)] +This command prints full documentation for any thing that has +documentation. It first prompts for the kind of thing to document, the +following options being available: +@begin[description] +@i[attribute]@\Describe a character attribute, given its name. + +@i[command]@\Describe a command, given its name. + +@i[key]@\Describe a command, given a key to which it is bound. + +@i[variable]@\Describe a variable, given its name. This is the default. +@end[description] +@enddefcom + + +@section[Entering and Exiting] + +@index[entering hemlock]@hemlock is entered by using the @clisp @f[ed] +function. Simply typing @f[(ed)] will enter @hemlock, leaving you in the state +that you were in when you left it. If @hemlock has never been entered before +then the current buffer will be @hid[Main]. The @f[-edit] command-line switch +may also be used to enter @hemlock: see page @pageref[edit-switch]. + +@f[ed] may optionally be given a file name or a symbol argument. Typing +@f[(ed @i[filename])] will cause the specified file to be read into @hemlock, +as though by @hid[Find File]. Typing @w<@f[(ed @i[symbol])]> will pretty-print +the definition of the symbol into a buffer whose name is obtained by adding +"@f[Edit ]" to the beginning of the symbol's name. + +@defcom[com "Exit Hemlock", bind (C-c, C-x C-z)] +@defcom1[com "Pause Hemlock"] +@index[exiting hemlock]@hid[Exit Hemlock] exits @hemlock, returning @f[t]. +@hid[Exit Hemlock] does not by default save modified buffers, or do +anything else that you might think it should do; it simply exits. At any time +after exiting you may reenter by typing @f[(ed)] to @llisp without losing +anything. Before you quit from @llisp using @f[(quit)], you should +save any modified files that you want to be saved. + +@hid[Pause Hemlock] is similar, but it suspends the @llisp process and returns +control to the shell. When the process is resumed, it will still be running +@hemlock. +@enddefcom + + +@section[Helpful Information] + +@label[aborting] +@index[aborting] +@index[undoing] +@index[error recovery] +This section contains assorted helpful information which may be useful in +staying out of trouble or getting out of trouble. + +@begin[itemize] +It is possible to get some sort of help nearly everywhere by typing +@binding[Home] or @binding[C-_]. + +Various commands take over the keyboard and insist that you type the key-events +that they want as input. If you get in such a situation and want to get out, +you can usually do so by typing @bf[C-g] some small number of times. If this +fails you can try typing @binding[C-x C-z] to exit @hemlock and then "@f[(ed)]" +to re-enter it. + +Before you quit, make sure you have saved all your changes. @binding[C-u C-x +C-b] will display a list of all modified buffers. If you exit using @bf[C-x +M-z], then @hemlock will save all modified buffers with associated files. + +If you lose changes to a file due to a crash or accidental failure to save, +look for backup ("@i[file]@f[.BAK]") or checkpoint ("@i[file]@f[.CKP]") files +in the same directory where the file was. + +If the screen changes unexpectedly, you may have accidentally typed an +incorrect command. Use @binding[Home l] to see what it was. If you are +not familiar with the command, use @binding[Home c] to see what it is so that +you know what damage has been done. Many interesting commands can be found +in this fashion. This is an example of the much-underrated learning +technique known as "Learning by serendipitous malcoordination". Who would +ever think of looking for a command that deletes all files in the current +directory? + +If you accidentally type a "killing" command such as @binding[C-w], you can +get the lost text back using @binding[C-y]. The @hid[Undo] command is also +useful for recovering from this sort of problem. +@end[itemize] + +@defhvar[var "Region Query Size", val {30}] +@index[large region] +Various commands ask for confirmation before modifying a region containing more +than this number of lines. If this is @nil, then these commands refrain from +asking, no matter how large the region is. +@enddefhvar + +@defcom[com "Undo"] +This command undoes the last major modification. Killing commands and some +other commands save information about their modifications, so accidental uses +may be retracted. This command displays the name of the operation to be undone +and asks for confirmation. If the affected text has been modified between the +invocations of @hid[Undo] and the command to be undone, then the result may be +somewhat incorrect but useful. Often @hid[Undo] itself can be undone by +invoking it again. +@enddefcom + + +@section[Recursive Edits] +@label[recursive-edits] +@index[recursive edits] +Some sophisticated commands, such as @hid[Query Replace], can place you in a +@i[recursive edit]. A recursive edit is simply a recursive invocation of +@hemlock done within a command. A recursive edit is useful because it allows +arbitrary editing to be done during the execution of a command without losing +any state that the command might have. When the user exits a recursive edit, +the command that entered it proceeds as though nothing happened. @Hemlock +notes recursive edits in the @hid[Echo Area] modeline, or status line. A +counter reflects the number of pending recursive edits. + +@defcom[com "Exit Recursive Edit", bind (C-M-z)] +This command exits the current recursive edit, returning @nil. If invoked when +not in a recursive edit, then this signals an user error. +@enddefcom + +@defcom[com "Abort Recursive Edit", bind (@bf)] +This command causes the command which invoked the recursive edit to get an +error. If not in a recursive edit, this signals an user error. +@enddefcom + + +@section[User Errors] +@index[beeping] +@index[errors, user] +When in the course of editing, @hemlock is unable to do what it thinks you want +to do, then it brings this to your attention by a beep or a screen flash +(possibly accompanied by an explanatory echo area message such as @w<"@f[No +next line.]">.) Although the exact attention-getting mechanism may vary on the +output device and variable settings, this is always called @i[beeping]. + +Whatever the circumstances, you had best try something else since @hemlock, +being far more stupid than you, is far more stubborn. @hemlock is an +extensible editor, so it is always possible to change the command that +complained to do what you wanted it to do. + +@section[Internal Errors] + +@index[errors, internal]A message of this form may appear in the echo +area, accompanied by a beep: +@begin[programexample] +Internal error: +Wrong type argument, NIL, should have been of type SIMPLE-VECTOR. +@end[programexample] +If the error message is a file related error such as the following, then +you have probably done something illegal which @hemlock did not catch, +but was detected by the file system: +@begin[programexample] +Internal error: +No access to "/lisp2/emacs/teco.mid" +@end[programexample] +Otherwise, you have found a bug. Try to avoid the behavior that resulted +in the error and report the problem to your system maintainer. Since @llisp +has fairly robust error recovery mechanisms, probably no damage has been +done. + +If a truly abominable error from which @hemlock cannot recover occurs, +then you will be thrown into the @llisp debugger. At this point it would be +a good idea to save any changes with @f[save-all-buffers] and then start +a new @llisp. + +@index[save-all-buffers, function]The @llisp function @f[save-all-buffers] may +be used to save modified buffers in a seriously broken @hemlock. To use this, +type "@f[(save-all-buffers)]" to the top-level ("@f[* ]") or debugger +("@f<1] >") prompt and confirm saving of each buffer that should be saved. +Since this function will prompt in the "@f[Lisp]" window, it isn't very useful +when called inside of @hemlock. diff --git a/docs/hem/user/lisp.mss b/docs/hem/user/lisp.mss new file mode 100644 index 0000000000000000000000000000000000000000..004e6b9ed2732d12966ce5c45c7998f316357962 --- /dev/null +++ b/docs/hem/user/lisp.mss @@ -0,0 +1,697 @@ +@comment{-*- Dictionary: bld:scribe/hem/hem; Mode: spell; Package: Hemlock; Log: /usr/lisp/scribe/hem/hem-docs.log -*-} +@chap[Interacting With Lisp] +@index[lisp, interaction with] + +Lisp encourages highly interactive programming environments by requiring +decisions about object type and function definition to be postponed until run +time. @hemlock supports interactive programming in @llisp by providing +incremental redefinition and environment examination commands. @hemlock also +uses Unix TCP sockets to support multiple Lisp processes, each of which may be +on any machine. + + +@section[Eval Servers] +@label[eval-servers] +@index[eval servers] + +@hemlock runs in the editor process and interacts with other Lisp processes +called @i[eval servers]. A user's Lisp program normally runs in an eval +server process. The separation between editor and eval server has several +advantages: +@begin[itemize] +The editor is protected from any bad things which may happen while debugging a +Lisp program. + +Editing may occur while running a Lisp program. + +The eval server may be on a different machine, removing the load from the +editing machine. + +Multiple eval servers allow the use of several distinct Lisp environments. +@end[itemize] +Instead of providing an interface to a single Lisp environment, @hemlock +coordinates multiple Lisp environments. + + +@subsection[The Current Eval Server] +@index[current eval server] +Although @hemlock can be connected to several eval servers simultaneously, one +eval server is designated as the @i[current eval server]. This is the eval +server used to handle evaluation and compilation requests. Eval servers are +referred to by name so that there is a convenient way to discriminate between +servers when the editor is connected to more than one. The current eval server +is normally globally specified, but it may also be shadowed locally in specific +buffers. + +@defcom[com "Set Eval Server"] +@defcom1[com "Set Buffer Eval Server"] +@defcom1[com "Current Eval Server"] +@hid[Set Eval Server] prompts for the name of an eval server and makes it the +the current eval server. @hid[Set Buffer Eval Server] is the same except that +is sets the eval server for the current buffer only. @hid[Current Eval Server] +displays the name of the current eval server in the echo area, taking any +buffer eval server into consideration. See also @comref[Set Compile Server]. +@enddefcom + + +@subsection[Slaves] +@index[slave buffers] +@index[slaves] +For now, all eval servers are @i[slaves]. A slave is a Lisp process that uses +a typescript (see page @pageref[typescripts]) to run its top-level +@f[read-eval-print] loop in a @hemlock buffer. We refer to the buffer that a +slave uses for I/O as its @i[interactive] or @i[slave] buffer. The name of the +interactive buffer is the same as the eval server's name. + +@index[background buffers] +@hemlock creates a @i[background] buffer for each eval server. The background +buffer's name is @w<@hid[Background ]@i{name}>, where @i[name] is the name of +the eval server. Slaves direct compiler warning output to the background +buffer to avoid cluttering up the interactive buffer. + +@hemlock locally sets @hid[Current Eval Server] in interactive and background +buffers to their associated slave. When in a slave or background buffer, eval +server requests will go to the associated slave, regardless of the global value +of @hid[Current Eval Server]. + +@defcom[com "Select Slave", bind (C-M-c)] +This command changes the current buffer to the current eval server's +interactive buffer. If the current eval server is not a slave, then it beeps. +If there is no current eval server, then this creates a slave (see section +@ref[slave-creation]). If a prefix argument is supplied, then this creates a +new slave regardless of whether there is a current eval server. This command +is the standard way to create a slave. + +The slave buffer is a typescript (see page @pageref[typescripts]) the slave +uses for its top-level @f[read-eval-print] loop. +@enddefcom + +@defcom[com "Select Background", bind (C-M-C)] +This command changes the current buffer to the current eval server's background +buffer. If there is no current eval server, then it beeps. +@enddefcom + + +@subsection[Slave Creation and Destruction] +@label[slave-creation] +When @hemlock first starts up, there is no current eval server. If there is no +a current eval server, commands that need to use the current eval server will +create a slave as the current eval server. + +If an eval server's Lisp process terminates, then we say the eval server is +dead. @hemlock displays a message in the echo area, interactive, and +background buffers whenever an eval server dies. If the user deletes an +interactive or background buffer, the associated eval server effectively +becomes impotent, but @hemlock does not try to kill the process. If a command +attempts to use a dead eval server, then the command will beep and display a +message. + +@defhvar[var "Confirm Slave Creation", val {t}] +If this variable is true, then @hemlock always prompts the user for +confirmation before creating a slave. +@enddefhvar + +@defhvar[var "Ask About Old Servers", val {t}] +If this variable is true, and some slave already exists, @hemlock prompts the +user for the name of an existing server when there is no current server, +instead of creating a new one. +@enddefhvar + +@defcom[com "Editor Server Name"] +This command echos the editor server's name, the machine and port of the +editor, which is suitable for use with the Lisp processes -slave switch. +See section @ref[slave-switch]. +@enddefcom + +@defcom[com "Accept Slave Connections"] +This command cause @hemlock to accept slave connections, and it displays the +editor server's name, which is suitable for use with the Lisp processes -slave +switch. See section @ref[slave-switch]. Supplying an argument causes this +command to inhibit slave connections. +@enddefcom + +@defhvar[var "Slave Utility", val {"/usr/misc/.lisp/bin/lisp"}] +@defhvar1[var "Slave Utility Switches"] +A slave is started by running the program @hid[Slave Utility Name] with +arguments specified by the list of strings @hid[Slave Utility Switches]. This +is useful primarily when running customized Lisp systems. For example, +setting @hid[Slave Utility Switches] to @f[("-core" "my.core")] will cause +"@f[/usr/hqb/my.core]" to be used instead of the default core image. + +The @f[-slave] switch and the editor name are always supplied as arguments, and +should remain unspecified in @hid[Slave Utility Switches]. +@enddefhvar + +@defcom[com "Kill Slave"] +@defcom1[com "Kill Slave and Buffers"] +@hid[Kill Slave] prompts for a slave name, aborts any operations in the slave, +tells the slave to @f[quit], and shuts down the connection to the specified +eval server. This makes no attempt to assure the eval server actually dies. + +@hid[Kill Slave and Buffers] is the same as @hid[Kill Slave], but it also +deletes the interactive and background buffers. +@enddefcom + + +@subsection[Eval Server Operations] + +@label[operations] +@index[eval server operations]@index[operations, eval server] +@hemlock handles requests for compilation or evaluation by queuing an +@i[operation] on the current eval server. Any number of operations may be +queued, but each eval server can only service one operation at a time. +Information about the progress of operations is displayed in the echo area. + +@defcom[com "Abort Operations", bind (C-c a)] +This command aborts all operations on the current eval server, either queued or +in progress. Any operations already in the @f[Aborted] state will be flushed. +@enddefcom + +@defcom[com "List Operations", bind (C-c l)] +This command lists all operations which have not yet completed. Along with a +description of the operation, the state and eval server is displayed. The +following states are used: +@begin[description] +@f[Unsent]@\The operation is in local queue in the editor, and hasn't been sent +yet. + +@f[Pending]@\The operation has been sent, but has not yet started execution. + +@f[Running]@\The operation is currently being processed. + +@f[Aborted]@\The operation has been aborted, but the eval server has not yet +indicated termination. +@end[description] +@enddefcom + + +@section[Typescripts] +@label[typescripts] +@index[typescripts] + +Both slave buffers and background buffers are typescripts. The typescript +protocol allows other processes to do stream-oriented interaction in a @hemlock +buffer similar to that of a terminal. When there is a typescript in a buffer, +the @hid[Typescript] minor mode is present. Some of the commands described in +this section are also used by @hid[Eval] mode (page @pageref[eval-mode].) + +Typescripts are simple to use. @hemlock inserts output from the process into +the buffer. To give the process input, use normal editing to insert the input +at the end of the buffer, and then type @bf[Return] to confirm sending the +input to the process. + +@defcom[com "Confirm Typescript Input", + stuff (bound to @bf[Return] in @hid[Typescript] mode)] +@defhvar1[var "Unwedge Interactive Input Confirm", val {t}] +This command sends text that has been inserted at the end of the current buffer +to the process reading on the buffer's typescript. Before sending the text, +@hemlock moves the point to the end of the buffer and inserts a newline. + +Input may be edited as much as is desired before it is confirmed; the result +of editing input after it has been confirmed is unpredictable. For this reason, +it is desirable to postpone confirming of input until it is actually complete. +The @hid[Indent New Line] command is often useful for inserting newlines +without confirming the input. + +If the process reading on the buffer's typescript is not waiting for input, +then the text is queued instead of being sent immediately. Any number of +inputs may be typed ahead in this fashion. @hemlock makes sure that the inputs +and outputs get interleaved correctly so that when all input has been read, the +buffer looks the same as it would have if the input had not been typed ahead. + +If the buffer's point is before the start of the input area, then various +actions can occur. When set, @hid[Unwedge Interactive Input Confirm] causes +@hemlock to ask the user if it should fix the input buffer which typically +results in ignoring any current input and refreshing the input area at the end +of the buffer. This also has the effect of throwing the slave Lisp to top +level, which aborts any pending operations or queued input. This is the only +way to be sure the user is cleanly set up again after messing up the input +region. When this is @nil, @hemlock simply beeps and tells the user in the +@hid[Echo Area] that the input area is invalid. +@enddefcom + +@defcom[com "Kill Interactive Input", + stuff (bound to @bf[M-i] in @hid[Typescript] and @hid[Eval] modes)] +This command kills any input that would have been confirmed by @bf[Return]. +@enddefcom + +@defcom[com "Next Interactive Input", + stuff (bound to @bf[M-n] in @hid[Typescript] and @hid[Eval] modes)] +@defcom1[com "Previous Interactive Input", + stuff (bound to @bf[M-p] in @hid[Typescript] and @hid[Eval] modes)] +@defcom1[com "Search Previous Interactive Input", + stuff (bound to @bf[M-P] in @hid[Typescript] and @hid[Eval] modes)] +@defhvar1[var "Interactive History Length", val {10}] +@defhvar1[var "Minimum Interactive Input Length", val {2}] +@index[history, typescript] +@Hemlock maintains a history of interactive inputs. @hid[Next Interactive +Input] and @hid[Previous Interactive Input] step forward and backward in the +history, inserting the current entry in the buffer. The prefix argument is +used as a repeat count. + +@hid[Search Previous Interactive Input] searches backward through the +interactive history using the current input as a search string. Consecutive +invocations repeat the previous search. + +@hid[Interactive History Length] determines the number of entries with which +@hemlock creates the buffer-specific histories. @Hemlock only adds an input +region to the history if its number of characters exceeds @hid[Minimum +Interactive Input Length]. +@enddefcom + +@defcom[com "Reenter Interactive Input", + stuff (bound to @bf[C-Return] in @hid[Typescript] and @hid[Eval] modes)] + This copies to the end of the buffer the form to the left of the buffer's +point. When the current region is active, this copies it instead. This is +sometimes easier to use to get a previous input that is either so far back that +it has fallen off the history or is visible and more readily @i[yanked] than +gotten with successive invocations of the history commands. +@enddefcom + +@defcom[com "Interactive Beginning of Line", + stuff (bound to @bf[C-a] in @hid[Typescript] and @hid[Eval] modes)] +This command is identical to @hid[Beginning of Line] unless there is no +prefix argument and the point is on the same line as the start of the current +input; then it moves to the beginning of the input. This is useful since it +skips over any prompt which may be present. +@enddefcom + +@defhvar[var "Input Wait Alarm", val {:loud-message}] +This variable determines what action is taken when a process goes into an input +wait on a typescript that isn't currently displayed in any window. These +values are legal: +@begin[description] +@kwd[loud-message]@\Beep and display a message in the echo area indicating +which buffer is waiting for input. + +@kwd[message]@\Display a message, but don't beep. + +@nil@\Don't do anything. +@end[description] +@enddefhvar + +@defcom[com "Typescript Slave BREAK", bind (Typescript: H-b)] +@defcom1[com "Typescript Slave to Top Level", bind (Typescript: H-g)] +Some typescripts have associated information which these commands access +allowing @hemlock to control the process which uses the typescript. + +@hid[Typescript Slave BREAK] puts the current process in a break loop so that +you can be debug it. This is similar in effect to an interrupt signal (@f[^C] +or @f[^\] in the editor process). + +@hid[Typescript Slave to Top Level] causes the current process to throw to the +top-level @f[read-eval-print] loop. This is similar in effect to a quit signal +(@f[^\]). +@enddefcom + + +@section[The Current Package] +@label[lisp-package] +@index[package] +The current package is the package which Lisp interaction commands use. The +current package is specified on a per-buffer basis, and defaults to "@f[USER]". +If the current package does not exist in the eval server, then it is created. +If evaluation is being done in the editor process and the current package +doesn't exist, then the value of @f[*package*] is used. The current package is +displayed in the modeline (see section @ref[modelines].) Normally the package +for each file is specified using the @f[Package] file option (see page +@pageref[file-options].) + +When in a slave buffer, the current package is controlled by the value of +@var[package] in that Lisp process. Modeline display of the current package +is inhibited in this case. + +@defcom[com "Set Buffer Package"] +This command prompts for the name of a package to make the local package in the +current buffer. If the current buffer is a slave, background, or eval buffer, +then this sets the current package in the associated eval server or editor +Lisp. When in an interactive buffer, do not use @f[in-package]; use this +command instead. +@enddefcom + + +@section[Compiling and Evaluating Lisp Code] + +@index[compilation]@index[evaluation]These commands can greatly speed up +the edit/debug cycle since they enable incremental reevaluation or +recompilation of changed code, avoiding the need to compile and load an +entire file. + +@defcom[com "Evaluate Expression", bind (M-Escape)] +This command prompts for an expression and prints the result of its evaluation +in the echo area. If an error happens during evaluation, the evaluation is +simply aborted, instead of going into the debugger. This command doesn't +return until the evaluation is complete. +@enddefcom + +@defcom[com "Evaluate Defun", bind (C-x C-e)] +@defcom1[com "Evaluate Region"] +@defcom1[com "Evaluate Buffer"] +These commands evaluate text out of the current buffer, reading the current +defun, the region and the entire buffer, respectively. The result of the +evaluation of each form is displayed in the echo area. If the region is +active, then @hid[Evaluate Defun] evaluates the current region, just like +@hid[Evaluate Region]. +@enddefcom + +@defcom[com "Macroexpand Expression", bind (C-M)] +This command shows the macroexpansion of the next expression in the null +environment in a pop-up window. With an argument, it uses @f[macroexpand] +instead of @f[macroexpand-1]. +@enddefcom + +@defcom[com "Re-evaluate Defvar"] +This command is similar to @hid[Evaluate Defun]. It is used for force the +re-evaluation of a @f[defvar] init form. If the current top-level form is a +@f[defvar], then it does a @f[makunbound] on the variable, and evaluates the +form. +@enddefcom + +@defcom[com "Compile Defun", bind (C-x C-c)] +@defcom1[com "Compile Region"] +These commands compile the text in the current defun and the region, +respectively. If the region is active, then @hid[Compile Defun] compiles the +current region, just like @hid[Compile Region]. +@enddefcom + +@defcom[com "Load File"] +@defhvar1[var "Load Pathname Defaults", val {nil}] +This command prompts for a file and loads it into the current eval server using +@f[load]. @hid[Load Pathname Defaults] contains the default pathname for this +command. This variable is set to the file loaded; if it is @nil, then there is +no default. This command also uses the @hid[Remote Compile File] variable. +@enddefcom + + +@section[Compiling Files] +These commands are used to compile source ("@f[.lisp]") files, producing binary +("@f[.fasl]") output files. Note that unlike the other compiling and evalating +commands, this does not have the effect of placing the definitions in the +environment; to do so, the binary file must be loaded. + +@defcom[com "Compile Buffer File", bind (C-x c)] +@defhvar1[var "Compile Buffer File Confirm", val {t}] +This command asks for confirmation, then saves the current buffer (when +modified) and compiles the associated file. The confirmation prompt indicates +intent to save and compile or just compile. If the buffer wasn't modified, and +a comparison of the write dates for the source and corresponding binary +("@f[.fasl]") file suggests that recompilation is unnecessary, the confirmation +also indicates this. A prefix argument overrides this test and forces +recompilation. Since there is a complete log of output in the background +buffer, the creation of the normal error output ("@f[.err]") file is inhibited. + +Setting @hid[Compile Buffer File Confirm] to @nil inhibits confirmation, except +when the binary is up to date and a prefix argument is not supplied. +@enddefcom + +@defcom[com "Compile File"] +This command prompts for a file and compiles that file, providing a convenient +way to compile a file that isn't in any buffer. Unlike +@hid[Compile Buffer File], this command doesn't do any consistency checks such +as checking whether the source is in a modified buffer or the binary is up to +date. +@enddefcom + +@defcom[com "Compile Group"] +@defcom1[com "List Compile Group"] +@label[compile-group-command]@index[group, compilation]@hid[Compile Group] does +a @hid[Save All Files] and then compiles every "@f[.lisp]" file for which the +corresponding "@f[.fasl]" file is older or nonexistent. The files are compiled +in the order in which they appear in the group definition. A prefix argument +forces compilation of all "@f[.lisp]" files. + +@hid[List Compile Group] lists any files that would be compiled by +@hid[Compile Group]. All Modified files are saved before checking to generate +a consistent list. +@enddefcom + +@defcom[com "Set Compile Server"] +@defcom1[com "Set Buffer Compile Server"] +@defcom1[com "Current Compile Server"] +These commands are analogous to @hid[Set Eval Server], @comref[Set Buffer Eval +Server] and @hid[Current Eval Server], but they determine the eval server used +for file compilation requests. If the user specifies a compile server, then +the file compilation commands send compilation requests to that server instead +of the current eval server. + +Having a separate compile server makes it easy to do compilations in the +background while continuing to interact with your eval server and editor. The +compile server can also run on a remote machine relieving your active +development machine of the compilation effort. +@enddefcom + +@defcom[com "Next Compiler Error", bind (H-n)] +@defcom1[com "Previous Compiler Error", bind (H-p)] +These commands provides a convenient way to inspect compiler errors. First it +splits the current window if there is only one window present. @hemlock +positions the current point in the first window at the erroneous source code +for the next (or previous) error. Then in the second window, it displays the +error beginning at the top of the window. Given an argument, this command +skips that many errors. +@enddefcom + +@defcom[com "Flush Compiler Error Information"] +This command relieves the current eval server of all infomation about errors +encountered while compiling. This is convenient if you have been compiling a +lot, but you were ignoring errors and warnings. You don't want to step through +all the old errors, so you can use this command immediately before compiling a +file whose errors you intend to edit. +@enddefcom + + +@defhvar[var "Remote Compile File", val {nil}] +When true, this variable causes file compilations to be done using the RFS +remote file system mechanism by prepending "@f[/../]@i[host]" to the file being +compiled. This allows the compile server to be run on a different machine, but +requires that the source be world readable. If false, commands use source +filenames directly. Do NOT use this to compile files in AFS. +@enddefhvar + + +@section[Querying the Environment] +@index[documentation, lisp] +These commands are useful for obtaining various random information from the +Lisp environment. + +@defcom[com "Describe Function Call", bind (C-M-A)] +@defcom1[com "Describe Symbol", bind (C-M-S)] +@hid[Describe Function Call] uses the current eval server to describe the +symbol found at the head of the currently enclosing list, displaying the output +in a pop-up window. @hid[Describe Symbol] is the same except that it describes +the symbol at or before the point. These commands are primarily useful for +finding the documentation for functions and variables. If there is no +currently valid eval server, then this command uses the editor Lisp's +environment instead of trying to spawn a slave. +@enddefcom + + +@section[Editing Definitions] +The Lisp compiler annotates each compiled function object with the source +file that the function was originally defined from. The definition editing +commands use this information to locate and edit the source for functions +defined in the environment. + +@defcom[com "Edit Definition"] +@defcom1[com "Goto Definition", bind (C-M-F)] +@defcom1[com "Edit Command Definition"] +@hid[Edit Definition] prompts for the name of a function, and then uses the +current eval server to find out in which file the function is defined. If +something other than @f[defun] or @f[defmacro] defined the function, then this +simply reads in the file, without trying to find its definition point within +the file. If the function is uncompiled, then this looks for it in the current +buffer. If there is no currently valid eval server, then this command uses the +editor Lisp's environment instead of trying to spawn a slave. + +@hid[Goto Definition] edits the definition of the symbol at the beginning of +the current list. + +@hid[Edit Command Definition] edits the definition of a @hemlock command. By +default, this command does a keyword prompt for the command name (as in an +extended command). If a prefix argument is specified, then instead prompt for +a key and edit the definition of the command bound to that key. +@enddefcom + +@defcom[com "Add Definition Directory Translation"] +@defcom1[com "Delete Definition Directory Translation"] +The defining file is recorded as an absolute pathname. The definition editing +commands have a directory translation mechanism that allow the sources to be +found when they are not in the location where compilation was originally done. +@hid[Add Definition Directory Translation] prompts for two directory +namestrings and causes the first to be mapped to the second. Longer (more +specific) directory specifications are matched before shorter (more general) +ones. + +@hid[Delete Definition Directory Translation] prompts for a directory +namestring and deletes it from the directory translation table. +@enddefcom + +@defhvar[var "Editor Definition Info", val {nil}] +When this variable is true, the editor Lisp is used to determine definition +editing information, otherwise the current eval server is used. This variable +is true in @hid[Eval] and @hid[Editor] modes. +@enddefhvar + + +@section[Manipulating the Editor Process] +When developing @hemlock customizations, it is useful to be able to manipulate +the editor Lisp environment from @hemlock. + +@defcom[com "Editor Describe", bind (Home t, C-_ t)] +This command prompts for an expression, and then evaluates and describes it +in the editor process. +@enddefcom + +@defcom[com "Room"] +Call the @f[room] function in the editor process, displaying information +about allocated storage in a pop-up window. +@enddefcom + +@defcom[com "Editor Load File"] +This command is analogous to @comref[Load File], but loads the file into the +editor process. +@enddefcom + + +@subsection[Editor Mode] +When @hid[Editor] mode is on, alternate versions of the Lisp interaction +commands are bound in place of the eval server based commands. These commands +manipulate the editor process instead of the current eval server. Turning on +editor mode in a buffer allows incremental development of code within the +running editor. + +@defcom[com "Editor Mode"] +This command turns on @hid[Editor] minor mode in the current buffer. If it is +already on, it is turned off. @hid[Editor] mode may also be turned on using +the @f[Mode] file option (see page @pageref[file-options].) +@enddefcom + +@defcom[com "Editor Compile Defun", + stuff (bound to @bf[C-x C-c] in @hid[Editor] mode)] +@defcom1[com "Editor Compile Region"] +@defcom1[com "Editor Evaluate Buffer"] +@defcom1[com "Editor Evaluate Defun", + stuff (bound to @bf[C-x C-e] in @hid[Editor] mode)] +@defcom1[com "Editor Evaluate Region"] +@defcom1[com "Editor Macroexpand Expression", bind (Editor: C-M)] +@defcom1[com "Editor Re-evaluate Defvar"] +@defcom1[com "Editor Describe Function Call", + stuff (bound to @bf[C-M-A] in @hid[Editor] mode)] +@defcom1[com "Editor Describe Symbol", + stuff (bound to @bf[C-M-S] in @hid[Editor] mode)] +These commands are similar to the standard commands, but modify or examine the +Lisp process that @hemlock is running in. Terminal I/O is done on the +initial window for the editor's Lisp process. Output is directed to a pop-up +window or the editor's window instead of to the background buffer. +@enddefcom + +@defcom[com "Editor Compile Buffer File"] +@defcom1[com "Editor Compile File"] +@defcom1[com "Editor Compile Group"] +In addition to compiling in the editor process, these commands differ from the +eval server versions in that they direct output to the the +@hid[Compiler Warnings] buffer. +@enddefcom + +@defcom[com "Editor Evaluate Expression", + stuff (bound to @bf[M-Escape] in @hid[Editor] mode and @bf[C-M-Escape])] +This command prompts for an expression and evaluates it in the editor process. +The results of the evaluation are displayed in the echo area. +@enddefcom + + +@subsection[Eval Mode] +@label[eval-mode] +@index[modes, eval]@hid[Eval] mode is a minor mode that simulates a @f[read] +@f[eval] @f[print] loop running within the editor process. Since Lisp +program development is usually done in a separate eval server process (see page +@pageref[eval-servers]), @hid[Eval] mode is used primarily for debugging code +that must run in the editor process. @hid[Eval] mode shares some commands with +@hid[Typescript] mode: see section @ref[typescripts]. + +@hid[Eval] mode doesn't completely support terminal I/O: it binds +@var[standard-output] to a stream that inserts into the buffer and +@var[standard-input] to a stream that signals an error for all operations. +@hemlock cannot correctly support the interactive evaluation of forms that read +from the @hid[Eval] interactive buffer. + +@defcom[com "Select Eval Buffer"] +This command changes to the @hid[Eval] buffer, creating one if it doesn't +already exist. The @hid[Eval] buffer is created with @hid[Lisp] as the major +mode and @hid[Eval] and @hid[Editor] as minor modes. +@enddefcom + +@defcom[com "Confirm Eval Input", + stuff (bound to @bf[Return] in @hid[Eval] mode)] +This command evaluates all the forms between the end of the last output and +the end of the buffer, inserting the results of their evaluation in the buffer. +This beeps if the form is incomplete. Use @binding[Linefeed] to insert line +breaks in the middle of a form. + +This command uses @hid[Unwedge Interactive Input Confirm] in the same way +@hid[Confirm Interactive Input] does. +@enddefcom + +@defcom[com "Abort Eval Input", + stuff (bound to @bf[M-i] in @hid[Eval] mode)] +This command moves the the end of the buffer and prompts, ignoring any +input already typed in. +@enddefcom + + +@subsection[Error Handling] +@index[error handling] +When an error happens inside of @hemlock, @hemlock will trap the error and +display the error message in the echo area, possibly along with the +"@f[Internal error:]" prefix. If you want to debug the error, type @bf[?]. +This causes the prompt "@f[Debug:]" to appear in the echo area. The following +commands are recognized: +@begin[description] +@bf[d]@\Enter a break-loop so that you can use the Lisp debugger. +Proceeding with "@f[go]" will reenter @hemlock and give the "@f[Debug:]" +prompt again. + +@bf[e]@\Display the original error message in a pop-up window. + +@bf[b]@\Show a stack backtrace in a pop-up window. + +@bf[q, Escape]@\Quit from this error to the nearest command loop. + +@bf[r]@\Display a list of the restart cases and prompt for the number of a +@f[restart-case] with which to continue. Restarting may result in prompting in +the window in which Lisp started. +@end[description] + +Only errors within the editor process are handled in this way. Errors during +eval server operations are handled using normal terminal I/O on a typescript in +the eval server's slave buffer or background buffer (see page +@pageref[operations]). Errors due to interaction in a slave buffer will cause +the debugger to be entered in the slave buffer. + + +@section[Command Line Switches] +@label[slave-switch] +Two command line switches control the initialization of editor and eval servers +for a Lisp process: +@begin[description] +@f<-edit>@\ +@label[edit-switch] +This switch starts up @hemlock. If there is a non-switch command line word +immediately following the program name, then the system interprets it as a file +to edit. For example, given +@Begin[ProgramExample] +lisp file.txt -edit +@End[ProgramExample] +Lisp will go immediately into @hemlock finding the file @f[file.txt]. + +@f<-slave [>@i[name]@f<]>@\ + This switch causes the Lisp process to become a slave of the editor process +@i[name]. An editor Lisp determines @i[name] when it allows connections from +slaves. Once the editor chooses a name, it keeps the same name until the +editor's Lisp process terminates. Since the editor can automatically create +slaves on its own machine, this switch is useful primarily for creating slaves +that run on a different machine. @f[hqb]'s machine is @f[ME.CS.CMU.EDU], and +he wants want to run a slave on @f[SLAVE.CS.CMU.EDU], then he should use the +@hid[Accept Slave Connections] command, telnet to the machine, and invoke Lisp +supplying @f[-slave] and the editor's name. The command displays the editor's +name. +@end[description] diff --git a/docs/hem/user/mail.mss b/docs/hem/user/mail.mss new file mode 100644 index 0000000000000000000000000000000000000000..a82c77368105dd219ef5046fff8f1a191e501acb --- /dev/null +++ b/docs/hem/user/mail.mss @@ -0,0 +1,1346 @@ +@comment{-*- Dictionary: bld:scribe/hem/hem; Mode: spell; Package: Hemlock -*-} +@chap[The Hemlock Mail Interface] +@section[Introduction to Mail in Hemlock] + +@index[MH interface]@label[introduction] +@hemlock provides an electronic mail handling facility via an interface to the +public domain @i[Rand MH Message Handling System]. This chapter assumes that +the user is familiar with the basic features and operation of @mh, but it +attempts to make allowances for beginners. Later sections of this chapter +discuss setting up @mh, profile components and special files for formatting +outgoing mail headers, and backing up protected mail directories on a +workstation. For more information on @mh, see the @i[Rand MH Message Handling +System Tutorial] and the @i[Rand MH Message Handling System Manual]. + +The @hemlock interface to @mh provides a means for generating header (@f[scan]) +lines for messages and displaying these headers in a @hid[Headers] buffer. +This allows the user to operate on the @i[current message] as indicated by the +position of the cursor in the @hid[Headers] buffer. The user can read, reply +to, forward, refile, or perform various other operations on the current +message. A user typically generates a @hid[Headers] buffer with the commands +@hid[Message Headers] or @hid[Incorporate and Read New Mail], and multiple such +buffers may exist simultaneously. + +Reading a message places its text in a @hid[Message] buffer. In a manner +similar to a @hid[Headers] buffer, this allows the user to operate on that +message. Most @hid[Headers] buffer commands behave the same in a @hid[Message] +buffer. For example, the @hid[Reply to Message] command has the same effect in +both @hid[Headers] mode and @hid[Message] mode. It creates a @hid[Draft] +buffer and makes it the current buffer so that the user may type a reply to the +current message. + +The @hid[Send Message] command originates outgoing mail. It generates a +@hid[Draft] buffer in which the user composes a mail message. Each @hid[Draft] +buffer has an associated pathname, so the user can save the buffer to a file as +necessary. Invoking @hid[Send Message] in a @hid[Headers] or @hid[Message] +buffer associates the @hid[Draft] buffer with a @hid[Message] buffer. This +allows the user to easily refer to the message being replied to with the +command @hid[Goto Message Buffer]. After the user composes a draft message, he +can deliver the message by invoking the @hid[Deliver Message] command in the +@hid[Draft] buffer (which deletes both the this buffer and any associated +@hid[Message] buffer), or he can delay this action. Invoking @hid[Deliver +Message] when not in a @hid[Draft] buffer causes it to prompt for a draft +message ID, allowing previously composed and saved messages to be delivered +(even across distinct Lisp invocations). + +@index[virtual message deletion] +The @hemlock mail system provides a mechanism for @i[virtual message deletion]. +That is, the @hid[Delete Message] command does not immediately delete a message +but merely flags the message for future deletion. This allows the user to +undelete the messages with the @hid[Undelete Message] command. The +@hid[Expunge Messages] command actually removes messages flagged for deletion. +After expunging a deleted message, @hid[Undelete Messages] can no longer +retrieve it. Commands that read messages by sequencing through a @hid[Headers] +buffer typically ignore those marked for deletion, which makes for more fluid +reading if a first pass has been made to delete uninteresting messages. + +After handling messages in a @hid[Headers] buffer, there may be messages +flagged for deletion and possibly multiple @hid[Message] buffers lying around. +There is a variety of commands that help @i[terminate] a mail session. +@hid[Expunge Messages] will flush the messages to be deleted, leaving the +buffer in an updated state. @hid[Delete Headers Buffer and Message Buffers] +will delete the @hid[Headers] buffer and its corresponding @hid[Message] +buffers. @hid[Quit Headers] is a combination of these two commands in that it +first expunges messages and then deletes all the appropriate buffers. + +One does not have to operate only on messages represented in a @hid[Headers] +buffer. This is merely the nominal mode of interaction. There are commands +that prompt for a folder, an @mh message specification (for example, "@f[1 3 6 +last]", "@f[1-3 5 6]", "@f[all]", "@f[unseen]"), and possibly a @f[pick] +expression. @f[Pick] expressions allow messages to be selected based on header +field pattern matching, body text searching, and date comparisons; these can be +specified using either a Unix shell-like/switch notation or a Lisp syntax, +according to one's preference. See section @ref[scanning] for more details. + +A @i[mail-drop] is a file where a Unix-based mail system stores all messages a +user receives. The user's mail handling program then fetches these from the +mail-drop, allowing the user to operate on them. Traditionally one locates his +mail-drop and mail directory on a mainframe machine because the information on +mainframes is backed up on magnetic tape at least once per day. Since @hemlock +only runs under CMU @clisp on workstations, and one's mail directory is not +usually world writable, it is not possible to adhere to a standard arrangement. +Since @mh provides for a remote mail-drop, and CMU's Remote File System has a +feature allowing authentication across a local area network, one can use +@hemlock to fetch his mail from a mainframe mail-drop (where it is backed up +before @hemlock grabs it) and store it on his workstation. Reading mail on a +workstation is often much faster and more comfortable because typically it is a +single user machine. Section @ref[backing-up] describes how to back up one's +mail directory from a workstation to a mainframe. + + +@section[Constraints on MH to use Hemlock's Interface] + +@index[constraints for mail interface]@label[constraints] +There are a couple constaints placed on the user of the @hemlock interface to +@mh. The first is that there must be a draft folder specified in one's @mh +profile to use any command that sends mail. Also, to read new mail, there must +be an @f[Unseen-Sequence:] component in one's @mh profile. The default @mh +profile does not specify these components, so they must be added by the user. +The next section of this chapter describes how to add these components. +Another constraint is that @hemlock requires its own @f[scan] line format to +display headers lines in a @hid[Headers] buffer. See the description of the +variable @hid[MH Scan Line Form] for details. + + +@section[Setting up MH] + +@index[setting up the mail interface]@label[setting-up] +@index[mail profile]@index[MH profile] +Get an @mh default profile and mail directory by executing the @mh @f[folder] +utility in a Unix shell. When it asks if it should make the "@f[inbox]" +folder, answer "@b[yes]". This creates a file called "@f[.mh_profile]" in the +user's home directory and a directory named "@f[Mail]". + +Edit the "@f[.mh_profile]" file inserting two additional lines. To send mail +in @hemlock, the user must indicate a draft folder by adding a +@f[Draft-Folder:] line with a draft folder name @dash "@f[drafts]" is a common +name: +@begin[example] +Draft-Folder: drafts +@end[example] + +Since the mail-drop exists on a remote machine, the following line must +also be added: +@begin[example] +MailDrop: /..//usr/spool/mail/ +@end[example] + +Since the user's mail-drop is on a separate machine from his mail directory +(and where the user runs @hemlock), it is necessary to issue the following +command from the Unix shell (on the workstation). This only needs to be done +once. +@begin[programexample] +/usr/cs/etc/rfslink -host /usr/spool/mail/ +@end[programexample] +Note that @b[] is not a full ARPANET domain-style name. Use an +abbreviated CMU host name (for example, "@b[spice]" not +"@b[spice.cs.cmu.edu]"). + + +@section[Profile Components and Customized Files] + +@subsection[Profile Components] + +@label[Profile] +The following are short descriptions about profile components that are either +necessary to using @hemlock's interface to @mh or convenient for using @mh in +general: + +@begin[description] +@f[Path:]@\ +This specifies the user's mail directory. It can be either a full pathname or +a pathname relative to the user's home directory. This component is +@i[necessary] for using @mh. + +@f[MailDrop:]@\ +This is used to specify one's remote mail-drop. It is @i[necessary] for +@hemlock only when using a mail-drop other than "@f[/usr/spool/mail/]" on +the local machine. + +@f[Folder-Protect:], @f[Msg-Protect:]@\ +These are set to 700 and 600 respectively to keep others from reading one's +mail. At one time the default values were set for public visibility of mail +folders. Though this is no longer true, these can be set for certainty. The +700 protection allows only user read, write, and execute (list access for +directories), and 600 allows only user read and write. These are not necessary +for either @mh or the @hemlock interface. + +@f[Unseen-Sequence:]@\ +When mail is incorporated, new messages are added to this sequence, and as +these messages are read they are removed from it. This allows the user at any +time to invoke an @mh program on all the unseen messges of a folder easily. An +example definition is: +@begin[example] +Unseen-Sequence: unseen +@end[example] +Specifying an unseen-sequence is @i[necessary] to use @hemlock's +interface to @mh. + +@f[Alternate-Mailboxes:]@\ +This is not necessary for either @mh or the @hemlock interface. This +component tells @mh which addresses that it should recognize as the user. This +is used for @f[scan] output formatting when the mail was sent by the user. It +is also used by @f[repl] when it sets up headers to know who the user is for +inclusion or exclusion from @b[cc]: lists. This is case sensitive and takes +wildcards. One example is: +@begin[example] +Alternate-Mailboxes: *FRED*, *Fred*, *fred* +@end[example] + +@f[Draft-Folder:]@\ +This makes multiple draft creation possible and trivial to use. Just supply a +folder name (for example, "@f[drafts]"). Specifying a draft-folder is +@i[necessary] to use @hemlock's interface to @mh. + +@f[repl: -cc all -nocc me -fcc out-copy]@\ +This tells the @f[repl] utility to include everyone but the user in the +@b[cc:] list when replying to mail. It also makes @f[repl] keep an copy of the +message the user sends. This is mentioned because one probably wants to reply +to everyone receiving a piece of mail except oneself. Unlike other utilities +that send mail, @f[repl] stores personal copies of outgoing mail based on a +command line switch. Other @mh utilities use different mechanisms. This line +is not necessary to use either @mh or the @hemlock interface. + +@f[rmmproc: /usr/cs/bin/rm]@\ +This is not necessary to use @hemlock's interface to @mh, but due to +@hemlock's virtual message deletion feature, this causes messages to be deleted +from folder directories in a cleaner fashion when they actually get removed. +Note that setting this makes @f[rmm] more treacherous if used in the Unix +shell. +@end[description] +@; + + +@subsection[Components Files] +@index[components] +@label[components-files] +@i[Components] files are templates for outgoing mail header fields that specify +position and sometimes values for specified fields. Example files are shown +for each one discussed here. These should exist in the user's mail directory. + +For originating mail there is a components file named "@f[components]", and it +is used by the @mh utility @f[comp]. An example follows: +@begin[example] + To: + cc: + fcc: out-copy + Subject: + -------- +@end[example] +This example file differs from the default by including the @f[fcc:] line. +This causes @mh to keep a copy of the outgoing draft message. Also, though it +isn't visible here, the @f[To:], @f[cc:], and @f[Subject:] lines have a space +at the end. + +@index[forwarding components] +The "@f[forwcomps]" components file is a template for the header fields of any +forwarded message. Though it may be different, our example is the same as the +previous one. These are distinct files for @mh's purposes, and it is more +flexible since the user might not want to keep copies of forwarded messages. + +@index[reply components] +The "@f[replcomps]" components file is a template for the header fields of any +draft message composed when replying to a message. An example +follows: +@begin[example] + %(lit)%(formataddr %<{reply-to}%|%<{from}%|%{sender}%>%>)\ + %<(nonnull)%(void(width))%(putaddr To: )\n%>\ + %(lit)%(formataddr{to})%(formataddr{cc})%(formataddr(me))\ + %(formataddr{resent-to})\ + %<(nonnull)%(void(width))%(putaddr cc: )\n%>\ + %<{fcc}Fcc: %{fcc}\n%>\ + %<{subject}Subject: Re: %{subject}\n%>\ + %<{date}In-reply-to: Your message of \ + %<(nodate{date})%{date}%|%(tws{date})%>.%<{message-id} + %{message-id}%>\n%>\ + -------- +@end[example] +This example file differs from the default by including the @b[resent-to:] +field (in addition to the @b[to:] and @b[cc:] fields) of the message being +replied to in the @b[cc:] field of the draft. This is necessary for replying +to all recipients of a distributed message. Keeping a copy of the outgoing +draft message works a little differently with reply components. @mh expects a +switch which the user can put in his profile (see section @ref[Profile] of this +chapter), and using the @mh formatting language, this file tests for the +@f[fcc] value as does the standard file. + + +@section[Backing up the Mail Directory] +@index[backing up mail directories] +@label[backing-up] +The easiest method of backing up a protected mail directory is to copy it into +an Andrew File System (AFS) directory since these are backed up daily as with +mainframes. The only problem with this is that the file servers may be down +when one wants to copy his mail directory since, at the time of this writing, +these servers are still under active development; however, they are becoming +more robust daily. One can read about the current AFS status in the file +@f[/../fac/usr/gripe/doc/vice/status]. + +Using AFS, one could keep his actual mail directory (not a copy thereof) in his +AFS home directory which eliminates the issue of backing it up. This is +additionally beneficial if the user does not use the same workstation everyday +(that is, he does not have his own but shares project owned machines). Two +problems with this arrangement result from the AFS being a distributed file +system. Besides the chance that the server will be down when the user wants to +read mail, performance degrades since messages must always be referenced across +the local area network. + +Facilities' official mechanism for backing up protected directories is called +@f[sup]. This is awkward to use and hard to set up, but a subsection here +describes a particular arrangement suitable for the user's mail directory. + + +@subsection[Andrew File System] +If the user choses to use AFS, he should get copies of @i[Getting Started with +the Andrew File System] and @i[Protecting AFS files and directories]. To use +AFS, send mail to Gripe requesting an account. When Gripe replies with a +password, change it to be the same as the account's password on the +workstation. This causes the user to be authenticated into AFS when he logs +into his workstation (that is, he is automatically logged into his AFS +account). To change the password, first log into the AFS account: +@begin[programexample] +log +@end[programexample] +Then issue the @f[vpasswd] command. + +All of the example command lines in this section assume the user has +@f[/usr/misc/bin] on his Unix shell @f[PATH] environment variable. + +@paragraph[Copy into AFS:] + +Make an AFS directory to copy into: +@begin[programexample] +mkdir /afs/cs.cmu.edu/user//mail-backup +@end[programexample] + +This will be readable by everyone, so protect it with the following: +@begin[programexample] +fs sa /afs/cs.cmu.edu/user//mail-backup System:AnyUser none +@end[programexample] + +Once the AFS account and directory to backup into have been established, the +user needs a means to recursively copy his mail directory updating only those +file that have changed and deleting those that no longer exist. To do this, +issue the following command: +@begin[programexample] +copy -2 -v -R +@end[programexample] +Do not terminate either of these directory specifications with a @f[/]. The +@f[-v] switch causes @f[copy] to output a line for copy and deletion, so this +may be eliminated if the user desires. + +@paragraph[Mail Directory Lives in AFS:] + +Assuming the AFS account has been established, and the user has followed the +directions in @ref[setting-up], now make an AFS directory to serve as the mail +directory: +@begin[programexample] +mkdir /afs/cs.cmu.edu/user//Mail +@end[programexample] + +This will be readable by everyone, so protect it with the following: +@begin[programexample] +fs sa /afs/cs.cmu.edu/user//Mail System:AnyUser none +@end[programexample] + +Tell @mh where the mail directory is by modifying the profile's +"@f[.mh_profile]" (see section @ref[setting-up]) @f[Path:] component (see +section @ref[Profile]): +@begin[programexample] +Path: /afs/cs.cmu.edu/user//Mail +@end[programexample] + + +@subsection[Sup to a Mainframe] +To use @f[sup] the user must set up a directory named "@f[sup]" on the +workstation in the user's home directory. This contains different directories +for the various trees that will be backed up, so there will be a "@f[Mail]" +directory. This directory will contain two files: "@f[crypt]" and "@f[list]". +The "@f[crypt]" file contains one line, terminated with a new line, that +contains a single word @dash an encryption key. "@f[list]" contains one line, +terminated with a new line, that contains two words @dash "@b[upgrade Mail]". + +On the user's mainframe, a file must be created that will be supplied to the +@f[sup] program. It should contain the following line to backup the mail +directory: + +@begin[example] +Mail delete host= hostbase=/usr/ base=/usr/ \ +crypt=WordInCryptFile login= password=LoginPasswordOnWorkstation +@end[example] +Warning: @i[This file contains the user's password and should be +protected appropriately.] + +The following Unix shell command issued on the mainframe will backup the +mail directory: + +@begin[programexample] + sup +@end[programexample] + +As a specific example, assume user "@f[fred]" has a workstation called +"@f[fred]", and his mainframe is the "@f[gpa]" machine where he has another +user account named "@f[fred]". The password on his workstation is +"@f[purple]". On his workstation, he creates the directory +"@f[/usr/fred/sup/Mail/]" with the two files "@f[crypt]" and "@f[list]". +The file "@f[/usr/fred/sup/Mail/crypt]" contains only the encryption key: +@programexample[steppenwolf] +The file "@f[/usr/fred/sup/Mail/list]" contains the command to upgrade the +"@f[Mail]" directory: +@programexample[upgrade Mail] + +On the "@f[gpa]" machine, the file "@f[/usr/fred/supfile]" contains the +following line: +@begin[programexample] +Mail delete host=fred hostbase=/usr/fred base=/usr/fred \ +crypt=steppenwolf login=fred password=purple +@end[programexample] +This file is protected on "@f[gpa]", so others cannot see @f[fred's] password +on his workstation. + +On the gpa-vax, issuing +@begin[programexample] + sup /usr/fred/supfile +@end[programexample] +to the Unix shell will update the @mh mail directory from @f[fred's] +workstation deleting any files that exist on the gpa that do not exist on the +workstation. + +For a more complete description of the features of @f[sup], see the @i[UNIX +Workstation Owner's Guide] and @i[The SUP Software Upgrade Protocol]. + +@section[Introduction to Commands and Variables] + +@index[mail commands]@index[mail variables]@label[mhcommands] +Unless otherwise specified, any command which prompts for a folder name will +offer the user a default. Usually this is @mh's idea of the current folder, +but sometimes it is the folder name associated with the current buffer if there +is one. When prompting for a message, any valid @mh message expression may be +entered (for example, "@f[1 3 6]", "@f[1-3 5 6]", "@f[unseen]", "@f[all]"). +Unless otherwise specified, a default will be offered (usually the current +message). + +Some commands mention specific @mh utilities, so the user knows how the +@hemlock command affects the state of @mh and what profile components and +special formatting files will be used. @hemlock runs the @mh utility programs +from a directory indicated by the following variable: + +@defhvar[var "MH Utility Pathname", val {"/usr/misc/.mh/bin/"}] +@mh utility names are merged with this pathname to find the executable +files. +@enddefhvar + + +@section[Scanning and Picking Messages] +@label[scanning] +As pointed out in the introduction of this chapter, users typically generate +headers or @f[scan] listings of messages with @hid[Message Headers], using +commands that operate on the messages represented by the headers. @hid[Pick +Headers] (bound to @bf[h] in @hid[Headers] mode) can be used to narrow down (or +further select over) the headers in the buffer. + +A @f[pick] expression may be entered using either a Lisp syntax or a Unix +shell-like/switch notation as described in the @mh documentation. The Lisp +syntax is as follows: + +@begin[example] + ::= {(not ) | (and *) | (or *) + | (cc ) | (date ) + | (from ) | (search ) + | (subject ) | (to ) + | (-- ) + | (before ) | (after ) + | (datefield )} + + ::= { | } + + ::= { | } + + ::= { | | } + + ::= +@end[example] + +Anywhere the user enters a @f[], its symbol name is used as a string. +Since @hemlock @f[read]s the expression without evaluating it, single quotes +("@bf[']") are unnecessary. From the @mh documentation, + +@begin[itemize] + A @f[] is a Unix @f[ed] regular expression. When using a string to + input these, remember that @f[\] is an escape character in Common Lisp. + + A @f[] is a header field name (for example, @b[reply-to] or + @b[resent-to]). + + A @f[] is an @i[822]-style specification, a day of the week, + "@b[today]", "@b[yesterday]", "@b[tomorrow]", or a number indicating @i[n] + days ago. The @i[822] standard is basically: + @begin[example] + dd mmm yy hh:mm:ss zzz + @end[example] + which is a two digit day, three letter month (first letter capitalized), two + digit year, two digit hour (@f[00] through @f[23]), two digit minute, two + digit second (this is optional), and a three letter zone (all capitalized). + For + example: + @begin[example] + 21 Mar 88 16:00 EST + @end[example] + + A @f[] is an alternate @f[Date:] field to use with @f[(before + )] and @f[(after )] such as @f[BB-Posted:] or + @f[Delivery-Date:]. + + Using @f[(before )] and @f[(after )] causes date field parsing, + while @f[(date )] does string pattern matching. +@end[itemize] + +Since a @f[] may be a symbol or string, it should be noted that the +symbol name is probably all uppercase characters, and @mh will match these +only against upper case. @mh will match lowercase characters against lower +and upper case. Some examples are: +@begin[example] + ;;; All messages to Gripe. + (to "gripe") + + ;;; All messages to Gripe or about Hemlock. + (or (to "gripe") (subject "hemlock")) + + ;;; All messages to Gripe with "Hemlock" in the body. + (and (to "gripe") (search "hemlock")) +@end[example] + +Matching of @f[] fields is case sensitive, so this example will +@f[pick] over all messages that have been replied to. +@example[(or (-- "replied" "") (-- "Replied" ""))] + + +@defhvar[var "MH Scan Line Form", val {"/usr/misc/.lisp/lib/mh-scan"}] + This is a pathname of a file containing an @mh format expression used for + header lines. + + The header line format must display the message ID as the first + non-whitespace item. If the user uses the virtual message deletion feature + which is on by default, there must be a space three characters to the right + of the message ID. This location is used on header lines to note that a + message is flagged for deletion. The second space after the message ID is + used for notating answered or replied-to messages. +@enddefhvar + +@defcom[com "Message Headers", bind (C-x r)] + This command prompts for a folder, message (defaulting to "@b[all]"), and an + optional @f[pick] expression. Typically this will simply be used to + generate headers for an entire folder or sequence, and the @f[pick] + expression will not be used. A new @hid[Headers] buffer is made, and the + output of @f[scan] on the messages indicated is inserted into the buffer. + The current window is used, the buffer's point is moved to the first header, + and the @hid[Headers] buffer becomes current. The current value of the + @hemlock @hid[Fill Column] variable is supplied to @f[scan] as the + @f[-width] switch. The buffer name is set to a string of the form + @w<"@f[Headers ]">, so the modeline will + show what is in the buffer. If no @f[pick] expression was supplied, none + will be shown in the buffer's name. As described in the introduction to + this section, the expression may be entered using either a Lisp syntax or a + Unix shell-like/switch notation. +@enddefcom + +@defhvar[var "MH Lisp Expression", val {t}] + When this is set, @mh expression prompts are read in a Lisp syntax. + Otherwise, the input is of the form of a Unix shell-like/switch notation as + described in the @mh documentation. +@enddefhvar + +@defcom[com "Pick Headers", stuff (bound to @bf[h] in @hid[Headers] mode) ] + This command is only valid in a @hid[Headers] buffer. It prompts for a + @f[pick] expression, and the messages shown in the buffer are supplied to + @f[pick] with the expression. The resulting messages are @f[scan]'ed, + deleting the previous contents of the buffer. The current value of + @hid[Fill Column] is used for the @f[scan]'ing. The buffer's point is moved + to the first header. The buffer's name is set to a string of the form + @w<"@f[Headers ]">, so the + modeline will show what is in the buffer. As described in the introduction + to this section, the expression may be entered using either a Lisp syntax or + a Unix shell-like/switch notation. +@enddefcom + +@defcom[com "Headers Help", bind (Headers: ?)] +This command displays documentation on @hid[Headers] mode. +@enddefcom + + +@section[Reading New Mail] + +@index[reading new mail]@label[reading-new-mail] + +@defcom[com "Incorporate and Read New Mail", stuff (bound to @bf[C-x i] globally and @bf[i] in @hid[Headers] and @hid[Message] modes) ] + This command incorporates new mail into @hid[New Mail Folder] and creates a + @hid[Headers] buffer with the new messages. An unseen-sequence must be + define in the user's @mh profile to use this. Any headers generated due to + @hid[Unseen Headers Message Spec] are inserted as well. The buffer's point + is positioned on the headers line representing the first unseen message of + the newly incorporated mail. +@enddefcom + +@defcom[com "Incorporate New Mail" ] + This command incorporates new mail into @hid[New Mail Folder], displaying + @f[inc] output in a pop-up window. This is similar to @hid[Incorporate and + Read New Mail] except that no @hid[Headers] buffer is generated. +@enddefcom + +@defhvar[var "New Mail Folder", val {"+inbox"}] + This is the folder into which @mh incorporates new mail. +@enddefhvar + +@defhvar[var "Unseen Headers Message Spec", val {nil}] + This is an @mh message specification that is suitable for any message + prompt. When incorporating new mail and after expunging messages, @hemlock + uses this specification in addition to the unseen-sequence name that is + taken from the user's @mh profile to generate headers for the unseen + @hid[Headers] buffer. This value is a string. +@enddefhvar + +@defhvar[var "Incorporate New Mail Hook", val {nil}] + This is a list of functions which are invoked immediately after new mail is + incorporated. The functions should take no arguments. +@enddefhvar + +@defhvar[var "Store Password", val {nil}] + When this is set, the user is only prompted once for his password, and the + password is stored for future use. +@enddefhvar + +@defhvar[var "Authenticate Incorporation", val {t}] +@defhvar1[var "Authentication User Name", val {nil}] +When @hid[Authenticate Incorporation] is set, incorporating new mail prompts +for a password to access a remote mail-drop. + +When incorporating new mail accesses a remote mail-drop, @hid[Authentication +User Name] is the user name supplied for authentication on the remote machine. +If this is @nil, @hemlock uses the local name. +@enddefhvar + + +@section[Reading Messages] +@index[reading messages] +@label[reading-messages] +This section describes basic commands that show the current, next, and previous +messages, as well as a couple advanced commands. @hid[Show Message] (bound to +@bf[SPACE] in @hid[Headers] mode) will display the message represented by the +@f[scan] line the @hemlock cursor is on. Deleted messages are considered +special, and the more conveniently bound commands for viewing the next and +previous messages (@hid[Next Undeleted Message] bound to @bf[n] and +@hid[Previous Undeleted Message] bound to @bf[p], both in @hid[Headers] and +@hid[Message] modes) will ignore them. @hid[Next Message] and @hid[Previous +Message] (bound to @bf[M-n] and @bf[M-p] in @hid[Headers] and @hid[Message] +modes) may be invoked if reading a message is desired regardless of whether it +has been deleted. + + +@defcom[com "Show Message", stuff (bound to @bf[SPACE] and @bf[.] in @hid[Headers] mode) ] + This command, when invoked in a @hid[Headers] buffer, displays the current +message (the message the cursor is on), by replacing any previous message that +has not been preserved with @hid[Keep Message]. The current message is also +removed from the unseen sequence. The @hid[Message] buffer becomes the current +buffer using the current window. The buffer's point will be moved to the +beginning of the buffer, and the buffer's name will be set to a string of the +form @w<"@f[Message ]">. + +The @hid[Message] buffer is read-only and may not be modified. The command +@hid[Goto Headers Buffer] issued in the @hid[Message] buffer makes the +associated @hid[Headers] buffer current. + +When not in a @hid[Headers] buffer, this command prompts for a folder and +message. A unique @hid[Message] buffer is obtained, and its name is set to a +string of the form @w<"@f[Message ]">. The buffer's point is +moved to the beginning of the buffer, and the current window is used to display +the message. + +Specifying multiple messages inserts all the messages into the same buffer. If +the user wishes to show more than one message, it is expected that he will +generate a @hid[headers] buffer with the intended messages, and then use the +message sequencing commands described below. +@enddefcom + +@defcom[com "Next Message", stuff (bound to @bf[M-n] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful in a @hid[Headers] buffer or a @hid[Message] +buffer associated with a @hid[Headers] buffer. In a @hid[Headers] buffer, the +point is moved to the next message, and if there is one, it is shown as +described in the @hid[Show Message] command. + +In a @hid[Message] buffer, the message after the currently visible message is +displayed. This clobbers the buffer's contents. Note, if the @hid[Message] +buffer is associated with a @hid[Draft] buffer, invoking this command breaks +that association. Using @hid[Keep Message] preserves the @hid[Message] buffer +and any association with a @hid[Draft] buffer. + +The @hid[Message] buffer's name is set as described in the @hid[Show Message] +command. +@enddefcom + +@defcom[com "Previous Message", stuff (bound to @bf[M-p] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful in a @hid[Headers] buffer or a @hid[Message] +buffer associated with a @hid[Headers] buffer. In a @hid[Headers] buffer, the +point is moved to the previous message, and if there is one, it is shown as +described in the @hid[Show Message] command. + +In a @hid[Message] buffer, the message before the currently visible message is +displayed. This clobbers the buffer's contents. Note, if the @hid[Message] +buffer is associated with a @hid[Draft] buffer, invoking this command breaks +that association. Using @hid[Keep Message] preserves the @hid[Message] buffer +and any association with a @hid[Draft] buffer. + +The @hid[Message] buffer's name is set as described in the @hid[Show Message] +command. +@enddefcom + +@defcom[com "Next Undeleted Message", stuff (bound to @bf[n] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful in a @hid[Headers] buffer or a @hid[Message] +buffer associated with a @hid[Headers] buffer. In a @hid[Headers] buffer, the +point is moved to the next undeleted message, and if there is one, it is shown +as described in the @hid[Show Message] command. + +In a @hid[Message] buffer, the first undeleted message after the currently +visible message is displayed. This clobbers the buffer's contents. Note, if +the @hid[Message] buffer is associated with a @hid[Draft] buffer, invoking this +command breaks that association. The @hid[Keep Message] command preserves the +@hid[Message] buffer and any association with a @hid[Draft] buffer. + +The @hid[Message] buffer's name is set as described in the @hid[Show Message] +command. +@enddefcom + +@defcom[com "Previous Undeleted Message", stuff (bound to @bf[p] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful in a @hid[Headers] buffer or a @hid[Message] +buffer associated with a @hid[Headers] buffer. In a @hid[Headers] buffer, the +point is moved to the previous undeleted message, and if there is one, it is +shown as described in the @hid[Show Message] command. + +In a @hid[Message] buffer, the first undeleted message before the currently +visible message is displayed. This clobbers the buffer's contents. Note, if +the @hid[Message] buffer is associated with a @hid[Draft] buffer, invoking this +command breaks that association. The @hid[Keep Message] command preserves the +@hid[Message] buffer and any association with a @hid[Draft] buffer. + +The @hid[Message] buffer's name is set as described in the @hid[Show Message] +command. +@enddefcom + +@defcom[com "Scroll Message", stuff (bound to @bf[SPACE] and @bf[C-v] in @hid[Message] mode) ] +@defhvar1[var "Scroll Message Showing Next", val {t}] + This command scrolls the current window down through the current message. If +the end of the message is visible and @hid[Scroll Message Showing Next] is not +@nil, then show the next undeleted message. +@enddefcom + +@defcom[com "Keep Message" ] +This command can only be invoked in a @hid[Message] buffer. It causes the +@hid[Message] buffer to continue to exist when the user invokes commands to +view other messages either within the kept @hid[Message] buffer or its +associated @hid[Headers] buffer. This is useful for getting two messages into +different buffers. It is also useful for retaining @hid[Message] buffers which +would otherwise be deleted when an associated draft message is delivered. +@enddefcom + +@defcom[com "Message Help", bind (Message: ?)] +This command displays documentation on @hid[Message] mode. +@enddefcom + + +@section[Sending Messages] +@index[sending messages] +@label[sending-messages] +The most useful commands for sending mail are @hid[Send Message] (bound to +@bf[m] and @bf[s] in @hid[Headers] and @hid[Message] modes), @hid[Reply to +Message] (bound to @bf[r] in @hid[Headers] mode), and @hid[Reply to Message in +Other Window] (bound to @bf[r] in @hid[Message] mode). These commands set up a +@hid[Draft] buffer and associate a @hid[Message] buffer with the draft when +possible. To actually deliver the message to its recipient(s), use +@hid[Deliver Message] (bound to @bf[H-s] in @hid[Draft] mode). To abort +sending mail, use @hid[Delete Draft and Buffer] (bound to @bf[H-q] in +@hid[Draft] mode). If one wants to temporarily stop composing a draft with the +intention of finishing it later, then the @hid[Save File] command (bound to +@bf[C-x C-s]) will save the draft to the user's draft folder. + +@hid[Draft] buffers have a special @hemlock minor mode called @hid[Draft] mode. +The major mode of a @hid[Draft] buffer is taken from the @hid[Default Modes] +variable. The user may wish to arrange that @hid[Text] mode (and possibly +@hid[Fill] mode or @hid[Save] mode) be turned on whenever @hid[Draft] mode is +set. For a further description of how to manipulate modes in @hemlock see the +@i[Hemlock Command Implementor's Manual]. + + +@defcom[com "Send Message", stuff (bound to @bf[s] and @bf[m] in @hid[Headers] and @hid[Message] modes and @bf[C-x m] globally) ] + This command, when invoked in a @hid[Headers] buffer, creates a unique +@hid[Draft] buffer and a unique @hid[Message] buffer. The current message is +inserted in the @hid[Message] buffer, and the @hid[Draft] buffer is displayed +in the current window. The @hid[Draft] buffer's point is moved to the end of +the line containing @f[To:] if it exists. The name of the draft message file +is used to produce the buffer's name. A pathname is associated with the +@hid[Draft] buffer so that @hid[Save File] can be used to incrementally save a +composition before delivering it. The @f[comp] utility will be used to +allocate a draft message in the user's @mh draft folder and to insert the +proper header components into the draft message. Both the @hid[Draft] and +@hid[Message] buffers are associated with the @hid[Headers] buffer, and the +@hid[Draft] buffer is associated with the @hid[Message] buffer. + +When invoked in a @hid[Message] buffer, a unique @hid[Draft] buffer is created, +and these two buffers are associated. If the @hid[Message] buffer is +associated with a @hid[Headers] buffer, this association is propagated to the +@hid[Draft] buffer. Showing other messages while in this @hid[Headers] buffer +will not affect this @hid[Message] buffer. + +When not in a @hid[Headers] or @hid[Message] buffer, this command does the same +thing as described in the previous two cases, but there are no @hid[Message] or +@hid[Headers] buffer manipulations. + +@hid[Deliver Message] will deliver the draft to its intended recipient(s). + +The @hid[Goto Headers Buffer] command, when invoked in a @hid[Draft] or +@hid[Message] buffer, makes the associated @hid[Headers] buffer current. The +@hid[Goto Message Buffer] command, when invoked in a @hid[Draft] buffer, makes +the associated @hid[Message] buffer current. +@enddefcom + +@defcom[com "Reply to Message", stuff (bound to @bf[r] in @hid[Headers] mode) ] +@defcom1[com "Reply to Message in Other Window", stuff (bound to @bf[r] in @hid[Message] mode) ] +@defhvar1[var "Reply to Message Prefix Action"] + @hid[Reply to Message], when invoked in a @hid[Headers] buffer, creates a +unique @hid[Draft] buffer and a unique @hid[Message] buffer. The current +message is inserted in the @hid[Message] buffer, and the @hid[Draft] buffer is +displayed in the current window. The draft components are set up in reply to +the message, and the @hid[Draft] buffer's point is moved to the end of the +buffer. The name of the draft message file is used to produce the buffer's +name. A pathname is associated with the @hid[Draft] buffer so that @hid[Save +File] can be used to incrementally save a composition before delivering it. +The @f[repl] utility will be used to allocate a draft message file in the +user's @mh draft folder and to insert the proper header components into the +draft message. Both the @hid[Draft] and @hid[Message] buffers are associated +with the @hid[Headers] buffer, and the @hid[Draft] buffer is associated with +the @hid[Message] buffer. + +When invoked in a @hid[Message] buffer, a unique @hid[Draft] buffer is set up +using the message in the buffer as the associated message. Any previous +association between the @hid[Message] buffer and a @hid[Draft] buffer is +removed. Any association of the @hid[Message] buffer with a @hid[Headers] +buffer is propagated to the @hid[Draft] buffer. + +When not in a @hid[Headers] buffer or @hid[Message] buffer, this command +prompts for a folder and message to reply to. This message is inserted into a +unique @hid[Message] buffer, and a unique @hid[Draft] buffer is created as in +the previous two cases. There is no association of either the @hid[Message] +buffer or the @hid[Draft] buffer with a @hid[Headers] buffer. + +When a prefix argument is supplied, @hid[Reply to Message Prefix Action] is +considered with respect to supplying carbon copy switches to @f[repl]. This +variable's value is one of @b[:cc-all], :@b[no-cc-all], or @nil. See section +@ref[Styles] for examples of how to use this. + +@hid[Reply to Message in Other Window] is identical to @hid[Reply to Message], +but the current window is split showing the @hid[Draft] buffer in the new +window. The split window displays the @hid[Message] buffer. + +@hid[Deliver Message] will deliver the draft to its intended recipient(s). + +The @hid[Goto Headers Buffer] commmand, when invoked in a @hid[Draft] or +@hid[Message] buffer, makes the associated @hid[Headers] buffer current. The +@hid[Goto Message Buffer] command, when invoked in a @hid[Draft] buffer, makes +the associated @hid[Message] buffer current. +@enddefcom + +@defcom[com "Forward Message", stuff (bound to @bf[f] in @hid[Headers] and @hid[Message] modes) ] + This command, when invoked in a @hid[Headers] buffer, creates a unique +@hid[Draft] buffer. The current message is inserted in the draft by using the +@f[forw] utility, and the @hid[Draft] buffer is shown in the current window. +The name of the draft message file is used to produce the buffer's name. A +pathname is associated with the @hid[Draft] buffer so that @hid[Save File] can +be used to incrementally save a composition before delivering it. The +@hid[Draft] buffer is associated with the @hid[Headers] buffer, but no +@hid[Message] buffer is created since the message is already a part of the +draft. + +When invoked in a @hid[Message] buffer, a unique @hid[Draft] buffer is set up +inserting the message into the @hid[Draft] buffer. The @hid[Message] buffer is +not associated with the @hid[Draft] buffer because the message is already a +part of the draft. However, any association of the @hid[Message] buffer with a +@hid[Headers] buffer is propagated to the @hid[Draft] buffer. + +When not in a @hid[Headers] buffer or @hid[Message] buffer, this command +prompts for a folder and message to forward. A @hid[Draft] buffer is created +as described in the previous two cases. + +@hid[Deliver Message] will deliver the draft to its intended recipient(s). +@enddefcom + +@defcom[com "Deliver Message", stuff (bound to @bf[H-s] and @bf[H-c] in @hid[Draft] mode) ] +@defhvar1[var "Deliver Message Confirm", val {nil}] + This command, when invoked in a @hid[Draft] buffer, saves the file and uses +the @mh @f[send] utility to deliver the draft. If the draft is a reply to some +message, then @f[anno] is used to annotate that message with a "@f[replied]" +component. Any @hid[Headers] buffers containing the replied-to message are +updated with an "@b[A]" placed in the appropriate headers line two characters +after the message ID. Before doing any of this, confirmation is asked for +based on @hid[Deliver Message Confirm]. + +When not in a @hid[Draft] buffer, this prompts for a draft message ID and +invokes @f[send] on that draft message to deliver it. Sending a draft in this +way severs any association that draft may have had with a message being replied +to, so no annotation will occur. +@enddefcom + +@defcom[com "Delete Draft and Buffer", stuff (bound to @bf[H-q] in @hid[Draft] mode) ] +This command, when invoked in a @hid[Draft] buffer, deletes the draft message +file and the buffer. This also deletes any associated message buffer unless +the user preserved it with @hid[Keep Message]. +@enddefcom + +@defcom[com "Remail Message", stuff (bound to @bf[H-r] in @hid[Headers] and @hid[Message] modes) ] + This command, when invoked in a @hid[Headers] or @hid[Message] buffer, prompts +for resend @f[To:] and resend @f[Cc:] addresses, remailing the current message. +When invoked in any other kind of buffer, this command prompts for a folder and +message as well. @mh's @f[dist] sets up a draft folder message which is then +modified. The above mentioned addresses are inserted on the @f[Resent-To:] and +@f[Resent-Cc:] lines. Then the message is delivered. + +There is no mechanism for annotating messages as having been remailed. +@enddefcom + +@defcom[com "Draft Help", bind (Draft: H-?)] +This command displays documentation on @hid[Draft] mode. +@enddefcom + + +@section[Convenience Commands for Message and Draft Buffers] +@index[message buffer commands] +@index[draft buffer commands] +@index[convenience commands for mail interface] +@label[convenience-coms] +This section describes how to switch from a @hid[Message] or @hid[Draft] buffer +to its associated @hid[Headers] buffer, or from a @hid[Draft] buffer to its +associated @hid[Message] buffer. There are also commands for various styles of +inserting text from a @hid[Message] buffer into a @hid[Draft] buffer. + +@defcom[com "Goto Headers Buffer", stuff (bound to @bf[^] in @hid[Message] mode and @bf[H-^] in @hid[Draft] mode) ] + This command, when invoked in a @hid[Message] or @hid[Draft] buffer with an + associated @hid[Headers] buffer, places the associated @hid[Headers] buffer + in the current window. + + The cursor is moved to the headers line of the associated message. +@enddefcom + +@defcom[com "Goto Message Buffer", stuff (bound to @bf[H-m] in @hid[Draft] mode) ] + This command, when invoked in a @hid[Draft] buffer with an associated + @hid[Message] buffer, places the associated @hid[Message] buffer in the + current window. +@enddefcom + +@defcom[com "Insert Message Region", stuff (bound to @bf[H-y] in @hid[Message] mode) ] +@defhvar1[var "Message Insertion Prefix", val {" "}] +@defhvar1[var "Message Insertion Column", val {75}] + This command, when invoked in a @hid[Message] buffer that has an associated + @hid[Draft] buffer, copies the current active region into the @hid[Draft] + buffer. It is filled using @hid[Message Insertion Prefix] (which defaults + to three spaces) and @hid[Message Insertion Column]. If an argument is + supplied, the filling is inhibited. +@enddefcom + +@defcom[com "Insert Message Buffer", stuff (bound to @bf[H-y] in @hid[Draft] mode) ] +@defhvar1[var "Message Buffer Insertion Prefix", val {" "}] + This command, when invoked in a @hid[Draft] buffer with an associated + @hid[Message] buffer, or when in a @hid[Message] buffer that has an + associated @hid[Draft] buffer, inserts the @hid[Message] buffer into the + @hid[Draft] buffer. Each inserted line is modified by prefixing it with + @hid[Message Buffer Insertion Prefix] (which defaults to four spaces) . If + an argument is supplied, the prefixing is inhibited. +@enddefcom + +@defcom[com "Edit Message Buffer", stuff (bound to @bf[e] in @hid[Message] mode) ] + This command puts the current @hid[Message] buffer in @hid[Text] mode and + makes it writable (@hid[Message] buffers are normally read-only). The + pathname of the file which the message is in is associated with the buffer + making saving possible. A recursive edit is entered, and the user is + allowed to make changes to the message. When the recursive edit is exited, + if the buffer is modified, the user is asked if the changes should be saved. + The buffer is marked unmodified, and the pathname is disassociated from the + buffer. The buffer otherwise returns to its previous state as a + @hid[Message] buffer. If the recursive edit is aborted, the user is not + asked to save the file, and the buffer remains changed though it is marked + unmodified. +@enddefcom + + +@section[Deleting Messages] +@index[deleting messages] +@label[deleting] +The main command described in this section is @hid[Headers Delete Message] +(bound to @bf[k] in @hid[Headers] and @hid[Message] modes). A useful command +for reading new mail is @hid[Delete Message and Show Next] (bound to @bf[d] in +@hid[Message] mode) which deletes the current message and shows the next +undeleted message. + +Since messages are by default deleted using a virtual message deletion +mechanism, @hid[Expunge Messages] (bound to @bf[!] in @hid[Headers] mode) +should be mentioned here. This is described in section @ref[terminating]. + + +@defhvar[var "Virtual Message Deletion", val {t}] +When set, @hid[Delete Message] adds a message to the "@f[hemlockdeleted]" +sequence; otherwise, @f[rmm] is invoked on the message immediately. +@enddefhvar + +@defcom[com "Delete Message" ] +This command prompts for a folder, messages, and an optional @f[pick] +expression. When invoked in a @hid[Headers] buffer of the specified folder, +the prompt for a message specification will default to the those messages in +that @hid[Headers] buffer. + +When the variable @hid[Virtual Message Deletion] is set, this command merely +flags the messages for deletion by adding them to the "@f[hemlockdeleted]" +sequence. Then this updates any @hid[Headers] buffers representing the folder. +It notates each headers line referring to a deleted message with a "@b[D]" in +the third character position after the message ID. + +When @hid[Virtual Message Deletion] is not set, @f[rmm] is invoked on the +message, and each headers line referring to the deleted message is deleted from +its buffer +@enddefcom + +@defcom[com "Headers Delete Message", stuff (bound to @bf[k] in @hid[Headers] and @hid[Message] modes) ] + This command, when invoked in a @hid[Headers] buffer, deletes the message on + the current line as described in @hid[Delete Message]. + + When invoked in a @hid[Message] buffer, the message displayed in it is + deleted as described in @hid[Delete Message]. +@enddefcom + +@defcom[com "Delete Message and Show Next", stuff (bound to @bf[k] in @hid[Headers] and @hid[Message] modes) ] + This command is only valid in a @hid[Headers] buffer or a @hid[Message] + buffer associated with some @hid[Headers] buffer. The current message is + deleted as with the @hid[Delete Message] command. Then the next message is + shown as with @hid[Next Undeleted Message]. +@enddefcom + +@defcom[com "Delete Message and Down Line", stuff (bound to @bf[d] in @hid[Headers mode])] + This command, when invoked in a @hid[Headers] buffer, deletes the message on + the current line. Then the point is moved to the next non-blank line. +@enddefcom + +@defcom[com "Undelete Message" ] + This command is only meaningful when @hid[Virtual Message Deletion] is set. + This prompts for a folder, messages, and an optional @f[pick] expression. When + in a @hid[Headers] buffer of the specified folder, the messages prompt + defaults to those messages in the buffer. All @hid[Headers] buffers + representing the folder are updated. Each headers line referring to an + undeleted message is notated by replacing the "@b[D]" in the third character + position after the message ID with a space. +@enddefcom + +@defcom[com "Headers Undelete Message", stuff (bound to @bf[u] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful when @hid[Virtual Message Deletion] is set. + When invoked in a @hid[Headers] buffer, the message on the current line is + undeleted as described in @hid[Undelete Message]. + + When invoked in a @hid[Message] buffer, the message displayed in it is + undeleted as described in @hid[Undelete Message]. +@enddefcom + + +@section[Folder Operations] + +@index[folder operations]@label[folder] +@defcom[com "List Folders" ] + This command displays a list of all current mail folders in the user's + top-level mail directory in a @hemlock pop-up window. +@enddefcom + +@defcom[com "Create Folder"] + This command prompts for and creates a folder. If the folder already + exists, an error is signaled. +@enddefcom + +@defcom[com "Delete Folder" ] + This command prompts for a folder and uses @f[rmf] to delete it. Note that + no confirmation is asked for. +@enddefcom + + +@section[Refiling Messages] + +@index[refiling messages]@label[refiling] +@defcom[com "Refile Message" ] + This command prompts for a folder, messages, an optional @f[pick] + expression, and a destination folder. When invoked in a @hid[Headers] + buffer of the specified folder, the message prompt offers a default of those + messages in the buffer. If the destination folder does not exist, the user + is asked to create it. The resulting messages are refiled with the + @f[refile] utility. All @hid[Headers] buffers for the folder are updated. + Each line referring to a refiled message is deleted from its buffer. +@enddefcom + +@defcom[com "Headers Refile Message", stuff (bound to @bf[o] in @hid[Headers] and @hid[Message] modes) ] + This command, when invoked in a @hid[Headers] buffer, prompts for a + destination folder, refiling the message on the current line with + @f[refile]. If the destination folder does not exist, the user is asked to + create it. Any @hid[Headers] buffers containing messages for that folder + are updated. Each headers line referring to the refiled message is deleted + from its buffer. + + When invoked in a @hid[Message] buffer, that message is refiled as described + above. +@enddefcom + + +@section[Marking Messages] +@index[marking messages] +@label[marking] +@defcom[com "Mark Message" ] +This command prompts for a folder, message, and sequence and adds (deletes) the +message specification to (from) the sequence. By default this adds the +message, but if an argument is supplied, this deletes the message. When +invoked in a @hid[Headers] buffer or @hid[Message] buffer, this only prompts +for a sequence and uses the current message. +@enddefcom + + +@section[Terminating Headers Buffers] +@label[terminating] +The user never actually @i[exits] the mailer. He can leave mail buffers lying +around while conducting other editing tasks, selecting them and continuing his +mail handling whenever. There still is a need for various methods of +terminating or cleaning up @hid[Headers] buffers. The two most useful commands +in this section are @hid[Expunge Messages] and @hid[Quit Headers]. + + +@defhvar[var "Expunge Messages Confirm", val {t}] + When this is set, @hid[Quit Headers] and @hid[Expunge Messages] will ask for + confirmation before expunging messages and packing the folder's message ID's. +@enddefhvar + +@defhvar[var "Temporary Draft Folder", val {nil}] + This is a folder name where @mh @f[fcc:] messages are kept with the + intention that this folder's messages will be deleted and expunged whenever + messages from any folder are expunged (for example, when @hid[Expunge + Messages] or @hid[Quit Headers] is invoked. +@enddefhvar + +@defcom[com "Expunge Messages", stuff (bound to @bf[!] in @hid[Headers] mode) ] +This command deletes messages @f[mark]'ed for deletion, and compacts the +folder's message ID's. If there are messages to expunge, ask the user for +confirmation, displaying the folder name. This can be inhibited by setting +@hid[Expunge Messages Confirm] to @nil. When @hid[Temporary Draft Folder] is +not @nil, this command deletes and expunges that folder's messages regardless +of the folder in which the user invokes it, and a negative response to the +request for confirmation inhibits this. + +When invoked in a @hid[Headers] buffer, the messages in that folder's +"@f[hemlockdeleted]" sequence are deleted by invoking @f[rmm]. Then the ID's +of the folder's remaining messages are compacted using the @f[folder] utility. +Since headers must be regenerated due to renumbering or reassigning message +ID's, and because @hid[Headers] buffers become inconsistent after messages are +deleted, @hemlock must regenerate all the headers for the folder. Multiple +@hid[Headers] buffers for the same folder are then collapsed into one buffer, +deleting unnecessary duplicates. Any @hid[Message] buffers associated with +these @hid[Headers] buffers are deleted. + +If there is an unseen @hid[Headers] buffer for the folder, it is handled +separately from the @hid[Headers] buffers described above. @hemlock tries to +update it by filling it only with remaining unseen message headers. +Additionally, any headers generated due to @hid[Unseen Headers Message Spec] +are inserted. If there are no headers, unseen or otherwise, the buffer is left +blank. + +Any @hid[Draft] buffer set up as a reply to a message in the folder is affected +as well since the associated message has possibly been deleted. When a draft +of this type is delivered, no message will be annotated as having been replied +to. + +When invoked in a @hid[Message] buffer, this uses its corresponding folder as +the folder argument. The same updating as described above occurs. + +In any other type of buffer, a folder is prompted for. +@enddefcom + +@defcom[com "Quit Headers", stuff (bound to @bf[q] in @hid[Headers] and @hid[Message] modes) ] +This command affects the current @hid[Headers] buffer. When there are deleted +messages, ask the user for confirmation on expunging the messages and packing +the folder's message ID's. This prompting can be inhibited by setting +@hid[Expunge Messages Confirm] to @nil. After deleting and packing, this +deletes the buffer and all its associated @hid[Message] buffers. + +Other @hid[Headers] buffers regarding the same folder are handled as described +in @hid[Expunge Messages], but the buffer this command is invoked in is always +deleted. + +When @hid[Temporary Draft Folder] is not @nil, this folder's messages are +deleted and expunged regardless of the folder in which the user invokes this +command. A negative response to the above mentioned request for confirmation +inhibits this. +@enddefcom + +@defcom[com "Delete Headers Buffer and Message Buffers" ] + This command prompts for a @hid[Headers] buffer to delete along with its + associated @hid[Message] buffers. Any associated @hid[Draft] buffers are + left intact, but their corresponding @hid[Message] buffers will be deleted. + When invoked in a @hid[Headers] buffer or a @hid[Message] buffer associated + with a @hid[Headers] buffer, that @hid[Headers] buffer is offered as a + default. +@enddefcom + + +@section[Miscellaneous Commands] +@label[miscellaneous mail commands] +@label[miscellaneous] + +@defcom[com "List Mail Buffers", stuff (bound to @bf[l] in @hid[Headers] and @hid[Message] modes @bf[H-l] in @hid[Draft] mode) ] +This command shows a list of all mail @hid[Message], @hid[Headers], and +@hid[Draft] buffers. + +If a @hid[Message] buffer has an associated @hid[Headers] buffer, it is +displayed to the right of the @hid[Message] buffer's name. + +If a @hid[Draft] buffer has an associated @hid[Message] buffer, it is displayed +to the right of the @hid[Draft] buffer's name. If a @hid[Draft] buffer has no +associated @hid[Message] buffer, but it is associated with a @hid[Headers] +buffer, then the name of the @hid[Headers] buffer is displayed to the right of +the @hid[Draft] buffer. + +For each buffer listed, if it is modified, then an asterisk is displayed before +the name of the buffer. +@enddefcom + + +@section[Styles of Usage] +@index[styles of mail interface usage] +@label[Styles] +This section discusses some styles of usage or ways to make use of some of the +features of @hemlock's interface to @mh that might not be obvious. In each +case, setting some variables and/or remembering an extra side effect of a +command will lend greater flexibility and functionality to the user. + +@subsection[Unseen Headers Message Spec] +The unseen @hid[Headers] buffer by default only shows unseen headers which is +adequate for one folder, simple mail handling. Some people use their @hid[New +Mail Folder] only for incoming mail, refiling or otherwise dispatching a +message immediately. Under this mode it is easy to conceive of the user not +having time to respond to a message, but he would like to leave it in this +folder to remind him to take care of it. Using the @hid[Unseen Headers Message +Spec] variable, the user can cause all the messages the @hid[New Mail Folder] to +be inserted into the unseen @hid[Headers] buffer whenever just unseen headers +would be. This way he sees all the messages that require immediate attention. + +To achieve the above effect, @hid[Unseen Headers Message Spec] should be set to +the string @f["all"]. This variable can be set to any general @mh message +specification (see section @ref[mhcommands] of this chapter), so the user can +include headers of messages other than those that have not been seen without +having to insert all of them. For example, the user could set the variable to +@f["flagged"] and use the @hid[Mark Message] command to add messages he's +concerned about to the @f["flagged"] sequence. Then the user would see new +mail and interesting mail in his unseen @hid[Headers] buffer, but he doesn't +have to see everything in his @hid[New Mail Folder]. + + +@subsection[Temporary Draft Folder] +Section @ref[components-files] of this chapter discusses how to make @mh keep +personal copies of outgoing mail. The method described will cause a copy of +every outgoing message to be saved forever and requires the user to go through +his @f[Fcc:] folder, weeding out those he does not need. The @hid[Temporary +Draft Folder] variable can name a folder whose messages will be deleted and +expunged whenever any folder's messages are expunged. By naming this folder in +the @mh profile and components files, copies of outgoing messages can be saved +temporarily. They will be cleaned up automatically, but the user still has a +time frame in which he can permanently save a copy of an outgoing message. +This folder can be visited with @hid[Message Headers], and messages can be +refiled just like any other folder. + + +@subsection[Reply to Message Prefix Action] +Depending on the kinds of messages one tends to handle, the user may find +himself usually replying to everyone who receives a certain message, or he may +find that this is only desired occasionally. In either case, the user +can set up his @mh profile to do one thing by default, using the @hid[Reply +to Message Prefix Action] variable in combination with a prefix argument to the +@hid[Reply to Message] command to get the other effect. + +For example, the following line in one's @mh profile will cause @mh to reply to +everyone receiving a certain message (except for the user himself since he +saves personal copies with the @f[-fcc] switch): +@begin[programexample] +repl: -cc all -nocc me -fcc out-copy +@end[programexample] +This user can set @hid[Reply to Message Prefix Action] to be @f[:no-cc-all]. +Then whenever he invokes @hid[Reply to Message] with a prefix argument, instead +of replying to everyone, the draft will be set up in reply only to the person +who sent the mail. + +As an alternative example, not specifying anything in one's @mh profile and +setting this variable to @f[:cc-all] will have a default effect of replying +only to the sender of a piece of mail. Then invoking @hid[Reply to Message] +with a prefix argument will cause everyone who received the mail to get a copy +of the reply. If the user does not want a @f[cc:] copy, then he can add +@f[-nocc me] as a default switch and value in his @mh profile. + + +@newpage +@section[Wallchart] + +@tabclear +@tabdivide(3) + +@begin[format, spacing 1.5] + +@Begin[Center] @b[Global bindings:] @End[Center] + +@hid[Incorporate and Read New Mail]@\@\@bf[C-x i] +@hid[Send Message]@\@\@bf[C-x m] +@hid[Message Headers]@\@\@bf[C-x r] + + +@Begin[Center] @b[Headers and Message modes bindings:] @End[Center] + +@hid[Next Undeleted Message]@\@\@bf[n] +@hid[Previous Undeleted Message]@\@\@bf[p] +@hid[Send Message]@\@\@bf[s], @bf[m] +@hid[Forward Message]@\@\@bf[f] +@hid[Headers Delete Message]@\@\@bf[k] +@hid[Headers Undelete Message]@\@\@bf[u] +@hid[Headers Refile Message]@\@\@bf[o] +@hid[List Mail Buffers]@\@\@bf[l] +@hid[Quit Headers]@\@\@bf[q] +@hid[Incorporate and Read New Mail]@\@\@bf[i] +@hid[Next Message]@\@\@bf[M-n] +@hid[Previous Message]@\@\@bf[M-p] +@hid[Beginning of Buffer]@\@\@bf[<] +@hid[End of Buffer]@\@\@bf[>] + + +@Begin[Center] @b[Headers mode bindings:] @End[Center] + +@hid[Delete Message and Down Line]@\@\@bf[d] +@hid[Pick Headers]@\@\@bf[h] +@hid[Show Message]@\@\@bf[space], @bf[.] +@hid[Reply to Message]@\@\@bf[r] +@hid[Expunge Messages]@\@\@bf[!] + + +@Begin[Center] @b[Message mode bindings:] @End[Center] + +@hid[Delete Message and Show Next]@\@\@bf[d] +@hid[Goto Headers Buffer]@\@\@bf[^] +@hid[Scroll Message]@\@\@bf[space] +@hid[Scroll Message]@\@\@bf[C-v] +@hid[Scroll Window Up]@\@\@bf[backspace], @bf[delete] +@hid[Reply to Message in Other Window]@\@bf[r] +@hid[Edit Message Buffer]@\@\@bf[e] +@hid[Insert Message Region]@\@\@bf[H-y] + + +@Begin[Center] @b[Draft mode bindings:] @End[Center] + +@hid[Goto Headers Buffer]@\@\@bf[H-^] +@hid[Goto Message Buffer]@\@\@bf[H-m] +@hid[Deliver Message]@\@\@bf[H-s], @bf[H-c] +@hid[Insert Message Buffer]@\@\@bf[H-y] +@hid[Delete Draft and Buffer]@\@\@bf[H-q] +@hid[List Mail Buffers]@\@\@bf[H-l] + +@end[format] +@tabclear diff --git a/docs/hem/user/special-modes.mss b/docs/hem/user/special-modes.mss new file mode 100644 index 0000000000000000000000000000000000000000..bbb30ed69e0be2b0182709fb1e11bcb6cefa810d --- /dev/null +++ b/docs/hem/user/special-modes.mss @@ -0,0 +1,725 @@ +@comment{-*- Dictionary: bld:scribe/hem/hem; Mode: spell; Package: Hemlock -*-} +@chap[Special Modes] + +@section[Dired Mode] +@label[dired] +@index[directory editing] + +@hemlock provides a directory editing mechanism. The user can flag files and +directories for deletion, undelete flagged files, and with a keystroke read in +files and descend into directories. In some implementations, it also supports +copying, renaming, and a simple wildcard feature. + + +@subsection[Inspecting Directories] +@defcom[com "Dired", bind (C-x C-M-d)] +This command prompts for a directory and fills a buffer with a verbose listing +of that directory. When the prefix argument is supplied, this includes Unix +dot files. If a dired buffer already exists for the directory, this switches +to the buffer and makes sure it displays dot files if appropriate. +@enddefcom + +@defcom[com "Dired with Pattern", bind (C-x C-M-d)] +This command prompts for a directory and a pattern that may contain at most one +wildcard, an asterisk, and it fills a buffer with a verbose listing of the +files in the directory matching the pattern. When the prefix argument is +supplied, this includes Unix dot files. If a dired buffer already exists for +this directory, this switches to the buffer and makes sure it displays dot +files if appropriate. +@enddefcom + +@defcom[com "Dired from Buffer Pathname"] +This command invokes @hid[Dired] on the directory name of the current buffer's +pathname. +@enddefcom + +@defcom[com "Dired Help", bind (Dired: ?)] +This command pops up a help window listing the various @hid[Dired] commands. +@enddefcom + +@defcom[com "Dired View File", bind (Dired: Space)] +@defcom1[com "Dired Edit File", bind (Dired: e)] +These command read in the file on the current line with the point. If the line +describes a directory instead of a file, then this command effectively invokes +@hid[Dired] on the specification. This associates the file's buffer with the +@hid[Dired] buffer. + +@hid[Dired View File] reads in the file as if by @hid[View File], and +@hid[Dired Edit File] as if by @hid[Find File]. + +@hid[Dired View File] always reads into a newly created buffer, warning if the +file already exists in some buffer. +@enddefcom + +@defcom[com "Dired Up Directory", bind (Dired: ^)] +This command invokes @hid[Dired] on the directory up one level from the current +@hid[Dired] buffer. This is useful for going backwards after repeatedly +invoking @hid[Dired View File] and descending into a series of subdirectories. +Remember, @hid[Dired] only generates directory listings when no buffer contains +a dired for the specified directory. +@enddefcom + +@defcom[com "Dired Update Buffer", bind (Dired: H-u)] +This command is useful when the user knows the directory in the current +@hid[Dired] buffer has changed. @hemlock cannot know the directory structure +has changed, but the user can explicitly update the buffer with this command +instead of having to delete it and invoke @hid[Dired] again. +@enddefcom + +@defcom[com "Dired Next File"] +@defcom1[com "Dired Previous File"] +These commands move to next or previous undeleted file. +@enddefcom + + +@subsection[Deleting Files] +@defcom[com "Dired Delete File and Down Line", bind (Dired: d)] +This command marks for deletion the file on the current line with the point and +moves point down a line. +@enddefcom + +@defcom[com "Dired Delete File with Pattern", bind (Dired: D)] +This command prompts for a name pattern that may contain at most one wildcard, +an asterisk, and marks for deletion all the names matching the pattern. +@enddefcom + +@defcom[com "Dired Delete File", bind (Dired: C-d)] +This command marks for deletion the file on the current line with the point +without moving the point. +@enddefcom + + +@subsection[Undeleting Files] +@defcom[com "Dired Undelete File and Down Line", bind (Dired: u)] +This command unmarks for deletion the file on the current line with the point +and moves point down a line. +@enddefcom + +@defcom[com "Dired Undelete File with Pattern", bind (Dired: U)] +This command prompts for a name pattern that may contain at most one wildcard, +an asterisk, and unmarks for deletion all the names matching the pattern. +@enddefcom + +@defcom[com "Dired Undelete File", bind (Dired: C-u)] +This command unmarks for deletion the file on the current line with the point +without moving the point. +@enddefcom + + +@subsection[Expunging and Quitting] +@defcom[com "Dired Expunge Files", bind (Dired: !)] +@defhvar1[var "Dired File Expunge Confirm", val {t}] +@defhvar1[var "Dired Directory Expunge Confirm", val {t}] +This command deletes files marked for deletion, asking the user for +confirmation once for all the files flagged. It recursively deletes any marked +directories, asking the user for confirmation once for all those marked. +@hid[Dired File Expunge Confirm] and @hid[Dired Directory Expunge Confirm] when +set to @nil individually inhibit the confirmation prompting for the appropriate +deleting. +@enddefcom + +@defcom[com "Dired Quit", bind (Dired: q)] +This command expunges any marked files or directories as if by @hid[Expunge +Dired Files] before deleting the @hid[Dired] buffer. +@enddefcom + + +@subsection[Copying Files] +@defcom[com "Dired Copy File", bind (Dired: c)] +This command prompts for a destination specification and copies the file on the +line with the point. When prompting, the current line's specification is the +default, which provides some convenience in supplying the destination. The +destination is either a directory specification or a file name, and when it is +the former, the source is copied into the directory under its current file name +and extension. +@enddefcom + +@defcom[com "Dired Copy with Wildcard", bind (Dired: C)] +This command prompts for a name pattern that may contain at most one wildcard, +an asterisk, and copies all the names matching the pattern. When prompting for +a destination, this provides the @hid[Dired] buffer's directory as a default. +The destination is either a directory specification or a file name with a +wildcard. When it is the former, all the source files are copied into the +directory under their current file names and extensions. When it is the later, +each sources file's substitution for the wildcard causing it to match the first +pattern replaces the wildcard in the destination pattern; for example, you +might want to copy @f["*.txt"] to @f["*.text"]. +@enddefcom + +@defhvar[var "Dired Copy File Confirm", val {t}] +@label[copy-confirm] +This variable controls interaction with the user when it is not obvious what +the copying process should do. This takes one of the following values: +@Begin[Description] +@true@\ +When the destination specification exists, the copying process stops and asks +the user if it should overwrite the destination. + +@nil@\ +The copying process always copies the source file to the destination +specification without interacting with the user. + +@kwd[update]@\ +When the destination specification exists, and its write date is newer than +the source's write date, then the copying process stops and asks the user if it +should overwrite the destination. +@End[Description] +@enddefhvar + + +@subsection[Renaming Files] +@defcom[com "Dired Rename File", bind (Dired: r)] +Rename the file or directory under the point +@enddefcom + +@defcom[com "Dired Rename with Wildcard", bind (Dired: R)] +Rename files that match a pattern containing ONE wildcard. +@enddefcom + +@defhvar[var "Dired Rename File Confirm", val {t}] +When non-nil, @hid[Dired] will query before clobbering an existing file. +@enddefhvar + + +@section[View Mode] +@hid[View] mode provides for scrolling through a file read-only, terminating +the buffer upon reaching the end. + +@defcom[com "View File"] +This command reads a file into a new buffer as if by "Visit File", but +read-only. Bindings exist for scrolling and backing up in a single key stroke. +@enddefcom + +@defcom[com "View Help", bind (View: ?)] +This command shows a help message for @hid[View] mode. +@enddefcom + +@defcom[com "View Edit File", bind (View: e)] +This commands makes a buffer in @hid[View] mode a normal editing buffer, +warning if the file exists in another buffer simultaneously. +@enddefcom + +@defcom[com "View Scroll Down", bind (View: Space)] +@defhvar1[var "View Scroll Deleting Buffer", val {t}] +This command scrolls the current window down through its buffer. If the end of +the file is visible, then this deletes the buffer if @hid[View Scroll Deleting +Buffer] is set. If the buffer is associated with a @hid[Dired] buffer, this +returns there instead of to the previous buffer. +@enddefcom + +@defcom[com "View Return", bind (View: ^)] +@defcom1[com "View Quit", bind (View: q)] +These commands invoke a function that returns to the buffer that created the +current buffer in @hid[View] mode. Sometimes this function does nothing, but +it is useful for returning to @hid[Dired] buffers and similar @hemlock +features. + +After invoking the viewing return function if there is one, @hid[View Quit] +deletes the buffer that is current when the user invokes it. +@enddefcom + +Also, bound in @hid[View] mode are the following commands: +@Begin[Description] +@binding[backspace], @binding[delete]@\Scrolls the window up. + +@binding[<]@\Goes to the beginning of the buffer. + +@binding[>]@\Goes to the end of the buffer. +@End[Description] + + +@section[Process Mode] +@Label[process] +@Index[shells] +@Index[processes] + +@hid[Process] mode allows the user to execute a Unix process within a @hemlock +buffer. These commands and default bindings cater to running Unix shells in +buffers. For example, @hid[Stop Buffer Subprocess] is bound to @binding[H-z] +to stop the process you are running in the shell instead of binding @hid[Stop +Main Process] to this key which would stop the shell itself. + +@defcom[com "Shell", bind (C-M-s)] +@defhvar1[var "Shell Utility", val {"/bin/csh"}] +@defhvar1[var "Shell Utility Switches", val {@nil}] +@defhvar1[var "Current Shell"] +@defhvar1[var "Ask about Old Shells"] +This command executes the process determined by the values of @hid(Shell +Utility) and @hid(Shell Utility Switches) in a new buffer named @f["Shell n"] +where @f["n"] is some distinguishing integer. + +@hid[Current Shell] is a @hemlock variable that holds to the current shell +buffer. When @hid[Shell] is invoked, if there is a @hid[Current Shell], the +command goes to that buffer. + +When there is no @hid[Current Shell], but shell buffers do exist, if @hid[Ask +about Old Shells] is set, the @hid[Shell] command prompts for one of them, +setting @hid[Current Shell] to the indicated shell, and goes to the buffer. + +Invoking @hid[Shell] with an argument forces the creation of a new shell +buffer. + +@hid[Shell Utility] is the string name of the process to execute. + +@hid[Shell Utility Switches] is a string containing the default command line +arguments to @hid[Shell Utility]. This is a string since the utility is +typically @f["/bin/csh"], and this string can contain I/O redirection and other +shell directives. +@enddefcom + +@defcom[com "Shell Command Line in Buffer"] +This command prompts for a buffer and a shell command line. It then runs a +shell, giving it the command line, in the buffer. +@enddefcom + +@defcom[com "Stop Main Process"] +This command stops the process running in the current buffer by sending a +@f[:SIGTSTP] to that process. With an argument, stops the process using +@f[:SIGSTOP]. +@enddefcom + +@defcom[com "Continue Main Process"] +If the process in the current buffer is stopped, this command continues it. +@enddefcom + +@defcom[com "Kill Main Process"] +This command kills the process running in the current buffer. +@enddefcom + +@defcom[com "Stop Buffer Subprocess", stuff (bound to @bf[H-z] in @hid[Process] mode)] +This command stops the foreground subprocess of the process in the current +buffer, similar to the effect of @binding[C-Z] in a shell. +@enddefcom + +@defcom[com "Interrupt Buffer Subprocess", stuff (bound to @bf[H-c] in @hid[Process] mode)] +This command interrupts the foreground subprocess of the process in the +current buffer, similar to the effect of @binding[C-C] in a shell. +@enddefcom + +@defcom[com "Quit Buffer Subprocess", stuff (bound to @bf[H-\] in @hid[Process] mode)] +This command dumps the core of the foreground subprocess of the processs in +the current buffer, similar to the effect of @binding[C-\] in a shell. +@enddefcom + +@defcom[com "Send EOF to Process", stuff (bound to @bf[H-d] in @hid[Process] mode)] +This command sends the end of file character to the process in the current +buffer, similar to the effect of @binding[C-D] in a shell. +@enddefcom + +@defcom[com "Confirm Process Input", stuff (bound to @bf[Return] in @hid[Process] mode)] +This command sends the text the user has inserted at the end of a process +buffer to the process in that buffer. Resulting output is inserted at the end +of the process buffer. +@enddefcom + +The user may edit process input using commands that are shared with +@hid[Typescript] mode, see section @ref[typescripts]. + + +@section[Bufed Mode] +@hemlock provides a mechanism for managing buffers as an itemized list. +@hid[Bufed] supports conveniently deleting several buffers at once, saving +them, going to one, etc., all in a key stroke. + +@defcom[com "Bufed", bind (C-x C-M-b)] +This command creates a list of buffers in a buffer supporting operations such +as deletion, saving, and selection. If there already is a @hid[Bufed] buffer, +this just goes to it. +@enddefcom + +@defcom[com "Bufed Help"] +This command pops up a display of @hid[Bufed] help. +@enddefcom + +@defcom[com "Bufed Delete", bind (Bufed: C-d, C-D, D, d)] +@defhvar1[var "Virtual Buffer Deletion", val {t}] +@defhvar1[var "Bufed Delete Confirm", val {t}] +@hid[Bufed Delete] deletes the buffer on the current line. + +When @hid[Virtual Buffer Deletion] is set, this merely flags the buffer for +deletion until @hid[Bufed Expunge] or @hid[Bufed Quit] executes. + +Whenever these commands actually delete a buffer, if @hid[Bufed Delete Confirm] +is set, then @hemlock prompts the user for permission; if more than one buffer +is flagged for deletion, this only prompts once. For each modified buffer, +@hemlock asks to save the buffer before deleting it. +@enddefcom + +@defcom[com "Bufed Undelete", bind (Bufed: U, u)] +This command undeletes the buffer on the current line. +@enddefcom + +@defcom[com "Bufed Expunge", bind (Bufed: !)] +This command expunges any buffers marked for deletion regarding @hid[Bufed +Delete Confirm]. +@enddefcom + +@defcom[com "Bufed Quit", bind (Bufed: q)] +This command kills the @hid[Bufed] buffer, expunging any buffers marked for +deletion. +@enddefcom + +@defcom[com "Bufed Goto", bind (Bufed: Space)] +This command selects the buffer on the current line, switching to it. +@enddefcom + +@defcom[com "Bufed Goto and Quit", bind (Bufed: S-leftdown)] +This command goes to the buffer under the pointer, quitting @hid[Bufed]. It +supplies a function for @hid[Generic Pointer Up] which is a no-op. +@enddefcom + +@defcom[com "Bufed Save File", bind (Bufed: s)] +This command saves the buffer on the current line. +@enddefcom + + +@section[Completion] +This is a minor mode that saves words greater than three characters in length, +allowing later completion of those words. This is very useful for the often +long identifiers used in Lisp programs. As you type a word, such as a Lisp +symbol when in @hid[Lisp] mode, and you progress to typing the third letter, +@hemlock displays a possible completion in the status line. You can then +rotate through the possible completions or type some more letters to narrow +down the possibilities. If you choose a completion, you can also rotate +through the possibilities in the buffer instead of in the status line. +Choosing a completion or inserting a character that delimits words moves the +word forward in the ring of possible completions, so the next time you enter +its initial characters, @hemlock will prefer it over less recently used +completions. + +@defcom[com "Completion Mode"] +This command toggles @hid[Completion] mode in the current buffer. +@enddefcom + +@defcom[com "Completion Self Insert"] +This command is like @hid[Self Insert], but it also checks for possible +completions displaying any result in the status line. This is bound to most of +the key-events with corresponding graphic characters. +@enddefcom + +@defcom[com "Completion Complete Word", bind (Completion: End)] +This command selects the currently displayed completion if there is one, +guessing the case of the inserted text as with @hid[Query Replace]. Invoking +this immediately in succession rotates through possible completions in the +buffer. If there is no currently displayed completion on a first invocation, +this tries to find a completion from text immediately before the point and +displays the completion if found. +@enddefcom + +@defcom[com "Completion Rotate Completions", bind (Completion: M-End)] +This command displays the next possible completion in the status line. If +there is no currently displayed completion, this tries to find a completion +from text immediately before the point and displays the completion if found. +@enddefcom + +@defcom[com "List Possible Completions"] +This command lists all the possible completions for the text immediately before +the point in a pop-up display. Sometimes this is more useful than rotating +through several completions to see if what you want is available. +@enddefcom + +@defhvar[var "Completion Bucket Size", val {20}] +Completions are stored in buckets determined by the first three letters of a +word. This variable limits the number of completions saved for each combination +of the first three letters of a word. If you have many identifier in some +module beginning with the same first three letters, you'll need increase this +variable to accommodate all the names. +@enddefhvar + + +@defcom[com "Save Completions"] +@defcom1[com "Read Completions"] +@defhvar1[var "Completion Database Filename", val {nil}] +@hid[Save Completions] writes the current completions to the file +@hid[Completion Database Filename]. It writes them, so @hid[Read Completions] +can read them back in preserving the most-recently-used order. If the user +supplies an argument, then this prompts for a pathname. + +@hid[Read Completions] reads completions saved in @hid[Completion Database +Filename]. It moves any current completions to a less-recently-used status, +and it removes any in a given bucket that exceed the limit @hid[Completion +Bucket Size]. +@enddefcom + +@defcom[com "Parse Buffer for Completions"] +This command passes over the current buffer putting each valid completion word +into the database. This is a good way of picking up many useful completions +upon visiting a new file for which there are no saved completions. +@enddefcom + + +@section[CAPS-LOCK Mode] + +@hid[CAPS-LOCK] is a minor mode in which @hemlock that inserts all alphabetic +characters as uppercase letters. + +@defcom[com "Caps Lock Mode"] +This command toggles @hid[CAPS-LOCK] mode for the current buffer; it is most +useful when bound to a key, so you can enter and leave @hid[CAPS-LOCK] mode +casually. +@enddefcom + +@defcom[com "Self Insert Caps Lock"] +This command inserts the uppercase version of the character corresponding to +the last key-event typed. +@enddefcom + + + +@section[Overwrite Mode] + +@hid[Overwrite] mode is a minor mode which is useful for creating figures and +tables out of text. In this mode, typing a key-event with a corresponding +graphic character replaces the character at the point instead of inserting the +character. @hid[Quoted Insert] can be used to insert characters normally. + +@defcom[com "Overwrite Mode"] +This command turns on @hid[Overwrite] mode in the current buffer. If it is +already on, then it is turned off. A positive argument turns @hid[Overwrite] +mode on, while zero or a negative argument turns it off. +@enddefcom + +@defcom[com "Self Overwrite"] +This command replaces the next character with the character corresponding to +the key-event used to invoke the command. After replacing the character, this +moves past it. If the next character is a tab, this first expands the tab into +the appropriate number of spaces, replacing just the next space character. +At the end of the line, it inserts the +character instead of clobbering the newline. + +This is bound to key-events with corresponding graphic characters in +@hid[Overwrite] mode. +@enddefcom + +@defcom[com "Overwrite Delete Previous Character", + stuff (bound to @bf[Delete] and @bf[Backspace] in @hid[Overwrite] mode)] +This command replaces the previous character with a space and moves backwards. +This deletes tabs and newlines. +@enddefcom + + +@section[Word Abbreviation] +@index[word abbreviation] +Word abbreviation provides a way to speed the typing of frequently used words +and phrases. When in @hid[Abbrev] mode, typing a word delimiter causes the +previous word to be replaced with its @i[expansion] if there is one currently +defined. The expansion for an abbrev may be any string, so this mode can be +used for abbreviating programming language constructs and other more obscure +uses. For example, @hid[Abbrev] mode can be used to automatically correct +common spelling mistakes and to enforce consistent capitalization of +identifiers in programs. + +@i[Abbrev] is an abbreviation for @i[abbreviation], which is used for +historical reasons. Obviously the original writer of @hid[Abbrev] mode hated +to type long words and could hardly use @hid[Abbrev] mode while writing +@hid[Abbrev] mode. + +A word abbrev can be either global or local to a major mode. A global word +abbrev is defined no matter what the current major mode is, while a mode word +abbrev is only defined when its mode is the major mode in the current buffer. +Mode word abbrevs can be used to prevent abbrev expansion in inappropriate +contexts. + + +@subsection[Basic Commands] + +@defcom[com "Abbrev Mode"] +This command turns on @hid[Abbrev] mode in the current buffer. If @hid[Abbrev] +mode is already on, it is turned off. @hid[Abbrev] mode must be on for the +automatic expansion of word abbrevs to occur, but the abbreviation commands are +bound globally and may be used at any time. +@enddefcom + +@defcom[com "Abbrev Expand Only", + stuff (bound to word-delimiters in @hid[Abbrev] mode)] +This is the word abbrev expansion command. If the word before the point is a +defined word abbrev, then it is replaced with its expansion. The replacement +is done using the same case-preserving heuristic as is used by +@hid[Query Replace]. This command is globally bound to @binding[M-Space] so +that abbrevs can be expanded when @hid[Abbrev] mode is off. An undesirable +expansion may be inhibited by using @binding[C-q] to insert the delimiter. +@enddefcom + +@defcom[com "Inverse Add Global Word Abbrev", bind (C-x -)] +@defcom1[com "Inverse Add Mode Word Abbrev", bind (C-x C-h, C-x Backspace)] +@hid[Inverse Add Global Word Abbrev] prompts for a string and makes it the +global word abbrev expansion for the word before the point. + +@hid[Inverse Add Mode Word Abbrev] is identical to +@hid[Inverse Add Global Word Abbrev] except that it defines an expansion which +is local to the current major mode. +@enddefcom + +@defcom[com "Make Word Abbrev"] +This command defines an arbitrary word abbreviation. It prompts for the mode, +abbreviation and expansion. If the mode @f["Global"] is specified, then it +makes a global abbrev. +@enddefcom + +@defcom[com "Add Global Word Abbrev", bind (C-x +)] +@defcom1[com "Add Mode Word Abbrev", bind (C-x C-a)] +@hid[Add Global Word Abbrev] prompts for a word and defines it to be a global +word abbreviation. The prefix argument determines which text is used as the +expansion: +@begin[description] +@i[no prefix argument]@\The word before the point is used as the expansion of +the abbreviation. + +@i[zero prefix argument]@\The text in the region is used as the expansion of the +abbreviation. + +@i[positive prefix argument]@\That many words before the point are made the +expansion of the abbreviation. + +@i[negative prefix argument]@\Do the same thing as +@hid[Delete Global Word Abbrev] instead of defining an abbreviation. +@end[description] + +@hid[Add Mode Word Abbrev] is identical to @hid[Add Global Word Abbrev] except +that it defines or deletes mode word abbrevs in the current major mode. +@enddefcom + +@defcom[com "Word Abbrev Prefix Mark", bind (M-")] +This command allows @hid[Abbrev Expand Only] to recognize abbreviations when +they have prefixes attached. First type the prefix, then use this command. A +hyphen (@f[-]) will be inserted in the buffer. Now type the abbreviation and +the word delimiter. @hid[Abbrev Expand Only] will expand the abbreviation and +remove the hyphen. + +Note that there is no need for a suffixing command, since +@hid[Abbrev Expand Only] may be used explicitly by typing @binding[M-Space]. +@enddefcom + +@defcom[com "Unexpand Last Word", bind (C-x u)] +This command undoes the last word abbrev expansion. If repeated, undoes its +own effect. +@enddefcom + + +@subsection[Word Abbrev Files] +A word abbrev file is a file which holds word abbrev definitions. Word abbrev +files allow abbrevs to be saved so that they may be used across many editing +sessions. + +@defhvar[var "Abbrev Pathname Defaults", val {(pathname "abbrev.defns")}] +This is sticky default for the following commands. When they prompt for a file +to write, they offer this and set it for the next time one of them executes. +@enddefhvar + +@defcom[com "Read Word Abbrev File"] +This command reads in a word abbrev file, adding all the definitions to those +currently defined. If a definition in the file is different from the current +one, the current definition is replaced. +@enddefcom + +@defcom[com "Write Word Abbrev File"] +This command prompts for a file and writes all currently defined word abbrevs +out to it. +@enddefcom + +@defcom[com "Append to Word Abbrev File"] +This command prompts for a word abbrev file and appends any new definitions to +it. An abbrev is new if it has been defined or redefined since the last use of +this command. Definitions made by reading word abbrev files are not +considered. +@enddefcom + + +@subsection[Listing Word Abbrevs] +@defcom[com "List Word Abbrevs"] +@defcom1[com "Word Abbrev Apropos"] +@hid[List Word Abbrevs] displays a list of each defined word abbrev, with its +mode and expansion. + +@hid[Word Abbrev Apropos] is similar, except that it only displays abbrevs +which contain a specified string, either in the definition, expansion or mode. +@enddefcom + +@subsection[Editing Word Abbrevs] +Word abbrev definition lists are edited by editing the text representation +of the definitions. Word abbrev files may be edited directly, like any other +text file. The set of abbrevs currently defined in @hemlock may be edited +using the commands described in this section. + +The text representation of a word abbrev is fairly simple. Each definition +begins at the beginning of a line. Each line has three fields which are +separated by ASCII tab characters. The fields are the abbreviation, the mode +of the abbreviation and the expansion. The mode is represented as the mode +name inside of parentheses. If the abbrev is global, then the mode field is +empty. The expansion is represented as a quoted string since it may contain +any character. The string is quoted with double-quotes (@f["]); double-quotes +in the expansion are represented by doubled double-quotes. The expansion may +contain newline characters, in which case the definition will take up more than +one line. + +@defcom[com "Edit Word Abbrevs"] +This command inserts the current word abbrev definitions into the +@hid[Edit Word Abbrevs] buffer and then enters a recursive edit on the buffer. +When the recursive edit is exited, the definitions in the buffer become the new +current abbrev definitions. +@enddefcom + +@defcom[com "Insert Word Abbrevs"] +This command inserts at the point the text representation of the currently +defined word abbrevs. +@enddefcom + +@defcom[com "Define Word Abbrevs"] +This command interprets the text of the current buffer as a word abbrev +definition list, adding all the definitions to those currently defined. +@enddefcom + + +@subsection[Deleting Word Abbrevs] +The user may delete word abbrevs either individually or collectively. +Individual abbrev deletion neutralizes single abbrevs which have outlived their +usefulness; collective deletion provides a clean slate from which to initiate +abbrev definitions. + +@defcom[com "Delete All Word Abbrevs"] +This command deletes all word abbrevs which are currently defined. +@enddefcom + +@defcom[com "Delete Global Word Abbrev"] +@defcom1[com "Delete Mode Word Abbrev"] +@hid[Delete Global Word Abbrev] prompts for a word abbreviation and deletes its +global definition. If given a prefix argument, deletes all global abbrev +definitions. + +@hid[Delete Mode Word Abbrev] is identical to @hid[Delete Global Word Abbrev] +except that it deletes definitions in the current major mode. +@enddefcom + + +@section[Lisp Library] +This is an implementation dependent feature. The Lisp library is a collection +of local hacks that users can submit and share that is maintained by the Lisp +group. These commands help peruse the catalog or description files and figure +out how to load the entries. + +@defcom[com "Lisp Library"] +This command finds all the library entries and lists them in a buffer. The +following commands describe and load those entries. +@enddefcom + +@defcom[com "Describe Library Entry", bind (Lisp-Lib: space)] +@defcom1[com "Describe Pointer Library Entry", bind (Lisp-Lib: leftdown)] +@defcom1[com "Load Library Entry", bind (Lisp-Lib: rightdown)] +@defcom1[com "Load Pointer Library Entry", bind (Lisp-Lib: l)] +@defcom1[com "Editor Load Library Entry"] +@defcom1[com "Editor Load Pointer Library Entry"] +@hid[Load Library Entry] and @hid[Load Pointer Library Entry] load the library +entry indicated by the line on which the point lies or where the user clicked +the pointer, respectively. These load the entry into the current slave Lisp. + +@hid[Editor Load Library Entry] and @hid[Editor Load Pointer Library Entry] are +the same, but they load the entry into the editor Lisp. +@enddefcom + +@defcom[com "Exit Lisp Library", bind (Lisp-Lib: q)] +This command deletes the @hid[Lisp Library] buffer. +@enddefcom + +@defcom[com "Lisp Library Help", bind (Lisp-Lib: ?)] +This command pops up a help window listing @hid[Lisp-Lib] commands. +@enddefcom diff --git a/docs/hem/user/user.mss b/docs/hem/user/user.mss new file mode 100644 index 0000000000000000000000000000000000000000..37574d183f322d4bc4b6cee3afe1931b23979dd8 --- /dev/null +++ b/docs/hem/user/user.mss @@ -0,0 +1,1958 @@ +@make[Manual] @comment{-*- Dictionary: bld:scribe/hem/hem; Mode: spell; Package: Hemlock; Log: /usr/lisp/scribe/hem/hem-docs.log -*-} +@Device[postscript] +@Style(Spacing = 1.2 lines) +@Style(StringMax = 5000) +@Use(Database "/usr/lisp/scribe/database/") +@Style(FontFamily=TimesRoman) +@Style(Date="March 1952") +@style(DOUBLESIDED) +@Libraryfile[ArpaCredit] +@libraryfile[hem] +@Libraryfile[Spice] +@Libraryfile[Uttir] + +@String(REPORTTITLE "Hemlock User's Manual") + +@comment<@begin[TitlePage] +@begin[TitleBox] +> +@blankspace(1.3inches) +@heading[Hemlock User's Manual] + +@center[@b +@b + + +@b<@value[date]> + +@b +] +@comment<@end[TitleBox]> +@blankspace(2lines) +@begin[Center] +School of Computer Science +Carnegie Mellon University +Pittsburgh, PA 15213 +@end[Center] +@blankspace[2lines] + +@begin[Center] +This is a revised version of Technical Report CMU-CS-87-158. +@end[Center] + +@heading +@begin(Text, indent 0) +This document describes the @Hemlock text editor, version M3.2. @Hemlock is a +customizable, extensible text editor whose initial command set closely +resembles that of ITS/TOPS-20 @Emacs. @Hemlock is written in CMU Common Lisp +and has been ported to other implementations. +@end(Text) + +@begin[ResearchCredit] +@ArpaCredit[Contract=Basic87-90] +@end[ResearchCredit] +@comment<@end[TitlePage]> + + +@commandstring(mh = "@f1(MH)") +@commandstring(dash = "@Y[M]") + +@comment[This tabclear is necessary since the definition macros don't + take care of the own tabbing needs] +@tabclear + + +@comment[@chap (Introduction)] +@include(intro) + + +@comment[@chap (Basic Commands)] +@include(commands) + + + +@chap[Files, Buffers, and Windows] + +@section[Introduction] + +@index[files]@index[buffers]@index[windows]@hemlock provides three +different abstractions which are used in combination to solve the +text-editing problem, while other editors tend to mash these ideas together +into two or even one. +@begin[description] +File@\A file provides permanent storage of text. @hemlock has commands +to read files into buffers and write buffers out into files. + +Buffer@\A buffer provides temporary storage of text and a capability to +edit it. A buffer may or may not have a file associated with it; if it +does, the text in the buffer need bear no particular relation to the text +in the file. In addition, text in a buffer may be displayed in any number +of windows, or may not be displayed at all. + +Window@\A window displays some portion of a buffer on the screen. There +may be any number of windows on the screen, each of which may display any +position in any buffer. It is thus possible, and often useful, to have +several windows displaying different places in the same buffer. +@end[description] + + +@section[Buffers] +In addition to some text, a buffer has several other user-visible attributes: +@begin[description] +A name@\ +A buffer is identified by its name, which allows it to be selected, destroyed, +or otherwise manipulated. + +A collection of modes@\ +The modes present in a buffer alter the set of commands available and +otherwise alter the behavior of the editor. For details see page +@pageref[modes]. + +A modification flag @\ +This flag is set whenever the text in a buffer is modified. It is often +useful to know whether a buffer has been changed, since if it has it should +probably be saved in its associated file eventually. + +A write-protect flag @\ +If this flag is true, then any attempt to modify the buffer will result in an +error. +@end[description] + +@defcom[com "Select Buffer", bind (C-x b)] +This command prompts for the name of a existing buffer and makes that buffer +the @i[current buffer]. The newly selected buffer is displayed in the +current window, and editing commands now edit the text in that buffer. +Each buffer has its own point, thus the point will be in the place it was +the last time the buffer was selected. When prompting for the buffer, the +default is the buffer that was selected before the current one. +@enddefcom + +@defcom[com "Select Previous Buffer", bind (C-M-l)] +@defcom1[com "Circulate Buffers", bind (C-M-L)] +With no prefix argument, @hid[Select Previous Buffer] selects the buffer that +has been selected most recently, similar to @binding[C-x b Return]. If given a +prefix argument, then it does the same thing as @hid[Circulate Buffers]. + +@hid[Circulate Buffers] moves back into successively earlier buffers in the +buffer history. If the previous command was not @hid[Circulate Buffers] or +@hid[Select Previous Buffer], then it does the same thing as +@hid[Select Previous Buffer], otherwise it moves to the next most recent +buffer. The original buffer at the start of the excursion is made the previous +buffer, so @hid[Select Previous Buffer] will always take you back to where you +started. + +These commands are generally used together. Often @hid[Select Previous Buffer] +will take you where you want to go. If you don't end up there, then using +@hid[Circulate Buffers] will do the trick. +@enddefcom + +@defcom[com "Create Buffer", bind (C-x M-b)] +This command is very similar to @hid[Select Buffer], but the buffer need not +already exist. If the buffer does not exist, a new empty buffer is created +with the specified name. +@enddefcom + +@defcom[com "Kill Buffer", bind (C-x k)] +This command is used to make a buffer go away. There is no way to restore +a buffer that has been accidentally deleted, so the user is given a chance +to save the hapless buffer if it has been modified. This command is poorly +named, since it has nothing to do with killing text. +@enddefcom + +@defcom[com "List Buffers", bind (C-x C-b)] +This command displays a list of all existing buffers in a pop-up window. A +"@f[*]" is displayed before the name of each modified buffer. A buffer with no +associated file is represented by the buffer name followed by the number of +lines in the buffer. A buffer with an associated file are is represented by +the name and type of the file, a space, and the device and directory. If the +buffer name doesn't match the associated file, then the buffer name is also +displayed. When given a prefix argument, this command lists only the modified +buffers. +@enddefcom + +@defcom[com "Buffer Not Modified", bind (M-~)] +This command resets the current buffer's modification flag @dash @i[it does not +save any changes]. This is primarily useful in cases where a user accidentally +modifies a buffer and then undoes the change. Resetting the modified flag +indicates that the buffer has no changes that need to be written out. +@enddefcom + +@defcom[com "Check Buffer Modified", bind (C-x ~)] +This command displays a message indicating whether the current buffer is modified. +@enddefcom + +@defcom[com "Set Buffer Read-Only"] +This command changes the flag that allows the current buffer to be modified. +If a buffer is read-only, any attempt to modify it will result in an error. The +buffer may be made writable again by repeating this command. +@enddefcom + +@defcom[com "Insert Buffer"] +This command prompts for the name of a buffer and inserts its contents at the +point, pushing a buffer mark before inserting. The buffer inserted is +unaffected. +@enddefcom + +@defcom[com "Rename Buffer"] +This command prompts for a new name for the current buffer, which defaults +to a name derived from the associated filename. +@enddefcom + + +@section[Files] +@index[files] +These commands either read a file into the current buffer or write it out to +some file. Various other bookkeeping operations are performed as well. + +@defcom[com "Find File", bind (C-x C-f)] +This is the command normally used to get a file into @hemlock. It prompts +for the name of a file, and if that file has already been read in, selects +that buffer; otherwise, it reads file into a new buffer whose name is +derived from the name of the file. If the file does not exist, then the +buffer is left empty, and @w<"@f[(New File)]"> is displayed in the echo area; +the file may then be created by saving the buffer. + +The buffer name created is in the form @w<"@i[name] @i[type] @i[directory]">. +This means that the filename "@f[/sys/emacs/teco.mid]" has +@w<"@f[Teco Mid /Sys/Emacs/]"> as its the corresponding buffer name. The +reason for rearranging the fields in this fashion is that it facilitates +recognition since the components most likely to differ are placed first. If +the buffer cannot be created because it already exists, but has another file in +it (an unlikely occurrence), then the user is prompted for the buffer to use, +as by @hid[Create Buffer]. + +@hid[Find File] takes special action if the file has been modified on disk +since it was read into @hemlock. This usually happens when several people are +simultaneously editing a file, an unhealthy circumstance. If the buffer is +unmodified, @hid[Find File] just asks for confirmation before reading in the +new version. If the buffer is modified, then @hid[Find File] beeps and prompts +for a single key-event to indicate what action to take. It recognizes +the following key-events: +@begin[description] +@binding[Return, Space, y]@\ + Prompt for a file in which to save the current buffer and then read in the +file found to be modified on disk. + +@binding[Delete, Backspace, n]@\ + Forego reading the file. + +@binding[r]@\ + Read the file found to be modified on disk into the buffer containing the +earlier version with modifications. This loses all changes you had in the +buffer. +@end[description] +@enddefcom + +@defcom[com "Save File", bind (C-x C-s)] +This command writes the current buffer out to its associated file and +resets the buffer modification flag. If there is no associated file, then +the user is prompted for a file, which is made the associated file. If +the buffer is not modified, then the user is asked whether to actually +write it or not. + +If the file has been modified on disk since the last time it was read, +@hid[Save File] prompts for confirmation before overwriting the file. +@enddefcom + +@defcom[com "Save All Files", bind (C-x C-m)] +@defcom1[com "Save All Files and Exit", bind (C-x M-z)] +@defhvar1[var "Save All Files Confirm", val {t}] +@hid[Save All Files] does a @hid[Save File] on all buffers which have an +associated file. @hid[Save All Files and Exit] does the same thing and then +exits @hemlock. + +When @hid[Save All Files Confirm] is true, these commands will ask for +confirmation before saving a file. +@enddefcom + +@defcom[com "Visit File", bind (C-x C-v)] +This command prompts for a file and reads it into the current buffer, +setting the associated filename. Since the old contents of the buffer are +destroyed, the user is given a chance to save the buffer if it is modified. +As for @hid[Find File], the file need not actually exist. This command warns +if some other buffer also contains the file. +@enddefcom + +@defcom[com "Write File", bind (C-x C-w)] This command prompts for a file +and writes the current buffer out to it, changing the associated filename +and resetting the modification flag. When the buffer's associated file is +specified this command does the same thing as @hid[Save File]. @enddefcom + +@defcom[com "Backup File"] +This command is similar to @hid[Write File], but it neither sets the +associated filename nor clears the modification flag. This is useful for +saving the current state somewhere else, perhaps on a reliable machine. + +Since @hid[Backup File] doesn't update the write date for the buffer, +@hid[Find File] and @hid[Save File] will get all upset if you back up +a buffer on any file that has been read into @hemlock. +@enddefcom + +@defcom[com "Revert File"] +@defhvar1[var "Revert File Confirm", val {t}] +This command replaces the text in the current buffer with the contents of the +associated file or the checkpoint file for that file, whichever is more recent. +The point is put in approximately the same place that it was before the file +was read. If the original file is reverted to, then clear the modified flag, +otherwise leave it set. If a prefix argument is specified, then always revert +to the original file, ignoring any checkpoint file. + +If the buffer is modified and @hid[Revert File Confirm] is true, then the user +is asked for confirmation. +@enddefcom + +@defcom[com "Insert File", bind (C-x C-r)] +This command prompts for a file and inserts it at the point, pushing a buffer +mark before inserting. +@enddefcom + +@defcom[com "Write Region"] +This command prompts for a file and writes the text in the region out to it. +@enddefcom + +@defhvar[var "Add Newline at EOF on Writing File", val {:ask-user}] +This variable controls whether some file writing commands add a newline at the +end of the file if the last line is non-empty. +@begin[description] +@f[:ask-user]@\Ask the user whether to add a newline. + +@f[t]@\Automatically add a newline and inform the user. + +@nil@\Never add a newline and do not ask. +@end[description] +Some programs will lose the text on the last line or get an +error when the last line does not have a newline at the end. +@enddefhvar + +@defhvar[var "Keep Backup Files", val {nil}] +Whenever a file is written by @hid[Save File] and similar commands, the old +file is renamed by appending "@f[.BAK]" to the name, ensuring that some version +of the file will survive a system crash during the write. If set to true, this +backup file will not deleted even when the write successfully completes. +@enddefhvar + + +@subsection[Auto Save Mode] + +@hid[Save] mode protects against loss of work in system crashes by periodically +saving modified buffers in checkpoint files. + +@defcom[com "Auto Save Mode"] +This command turns on @hid[Save] mode if it is not on, and turns off when it is +on. @hid[Save] mode is on by default. +@enddefcom + +@defhvar[var "Auto Save Checkpoint Frequency", val {120}] +@defhvar1[var "Auto Save Key Count Threshold", val {256}] +These variables determine how often modified buffers in @hid[Save] mode will be +checkpointed. Checkpointing is done after +@hid[Auto Save Checkpoint Frequency] seconds, or after +@hid[Auto Save Key Count Threshold] keystrokes that modify the buffer +(whichever comes first). Either kind of checkpointing may be disabled by +setting the corresponding variable to @nil. +@enddefhvar + +@defhvar[var "Auto Save Cleanup Checkpoints", val {t}] +If this variable is true, then any checkpoint file for a buffer will be deleted +when the buffer is successfully saved in its associated file. +@enddefhvar + +@defhvar[var "Auto Save Filename Pattern", val {"~A~A.CKP"}] +@defhvar1[var "Auto Save Pathname Hook", val {make-unique-save-pathname}] +These variables determine the naming of checkpoint files. +@hid[Auto Save Filename Pattern] is a format string used to name the checkpoint +files for buffers with associated files. Format is called with two arguments: +the directory and file namestrings of the associated file. + +@hid[Auto Save Pathname Hook] is a function called by @hid[Save] mode to get a +checkpoint pathname when there is no pathname associated with a buffer. It +should take a buffer as its argument and return either a pathname or @nil. If +a pathname is returned, then it is used as the name of the checkpoint file. If +the function returns @nil, or if the hook variable is @nil, then @hid[Save] +mode is turned off in the buffer. The default value for this variable returns +a pathname in the default directory of the form "@w<@f[save-]@i[number]>", +where @i[number] is a number used to make the file unique. +@enddefhvar + + +@subsection[Filename Defaulting and Merging] +@index[merging, filename] +@index[defaulting, filename] +@index[filename defaulting] +@label[merging] +@index[pathnames] +When @hemlock prompts for the name of a file, it always offers a default. +Except for a few commands that have their own defaults, filename defaults are +computed in a standard way. If it exists, the associated file for the current +buffer is used as the default, otherwise a more complex mechanism creates a +default. + +@defhvar[var "Pathname Defaults", val {(pathname "gazonk.del")}] +@defhvar1[var "Last Resort Pathname Defaults Function"] +@defhvar1[var "Last Resort Pathname Defaults", val {(pathname "gazonk")}] +These variables control the computation of default filename defaults when the +current buffer has no associated file. + +@hid[Pathname Defaults] holds a "sticky" filename default. Commands that +prompt for files set this to the file specified, and the value is used as a +basis for filename defaults. It is undesirable to offer the unmodified value +as a default, since it is usually the name of an existing file that we don't +want to overwrite. If the current buffer's name is all alphanumeric, then the +default is computed by substituting the buffer name for the the name portion of +@hid[Pathname Defaults]. Otherwise, the default is computed by calling +@hid[Last Resort Pathname Defaults Function] with the buffer as an argument. + +The default value of @hid[Last Resort Pathname Defaults Function] merges +@hid[Last Resort Pathname Defaults] with @hid[Pathname Defaults]. +Unlike @hid[Pathname Defaults], @hid[Last Resort Pathname Defaults] is not +modified by file commands, so setting it to a silly name ensures that real +files aren't inappropriately offered as defaults. +@enddefhvar + +When a default is present in the prompt for a file, @hemlock @i[merges] the +given input with the default filename. The semantics of merging, described in +the Common Lisp manual, is somewhat involved, but @hemlock has a few rules it +uses: +@begin[enumerate] +If @hemlock can find the user's input as a file on the @f["default:"] search +list, then it forgoes merging with the displayed default. Basically, the +system favors the files in your current working directory over those found by +merging with the defaults offered in the prompt. + +Merging comes in two flavors, just merge with the displayed default's directory +or just merge with the displayed default's @f[file-namestring]. If the user +only responds with a directory specification, without any name or type +information, then @hemlock merges the default's @f[file-namestring]. If the +user responds with any name or type information, then @hemlock only merges with +the default's directory. Specifying relative directories in this second +situation coordinates with the displayed defaults, not the current working +directory. +@end[enumerate] + + +@subsection[Type Hooks and File Options] +@index[mode comment] +@index[type hooks] +When a file is read either by @hid[Find File] or @hid[Visit File], @hemlock +attempts to guess the correct mode in which to put the buffer, based on the +file's @i[type] (the part of the filename after the last dot). Any default +action may be overridden by specifying the mode in the file's @i[file +options].@index[modes]@index[package] + +@label[file-options]@index[file options] +The user specifies file options with a special syntax on the first line of a +file. If the first line contains the string "@f[-*-]", then @hemlock +interprets the text between the first such occurrence and the second, which +must be contained in one line , as a list of @w{"@f<@i[option]: @i[value]>"} +pairs separated by semicolons. The following is a typical example: +@begin[programexample] +;;; -*- Mode: Lisp, Editor; Package: Hemlock -*- +@end[programexample] + +These options are currently defined: +@begin[description] +Dictionary@\The argument is the filename of a spelling dictionary associated +with this file. The handler for this option merges the argument with the +name of this file. See @comref[Set Buffer Spelling Dictionary]. + +Log@\The argument is the name of the change log file associated with this file +(see page @pageref[log-files]). The handler for this option merges the +argument with the name of this file. + +Mode@\The argument is a comma-separated list of the names of modes to turn on +in the buffer that the file is read into. + +Package@\The argument is the name of the package to be used for reading code in +the file. This is only meaningful for @llisp code (see page +@pageref[lisp-package].) + +Editor@\The handler for this option ignores its argument and turns on +@hid[Editor] mode (see @comref[Editor Mode]). + +@end[description] +If the option list contains no "@f[:]" then the entire string is used as +the name of the major mode for the buffer. + +@defcom[com "Process File Options"] +This command processes the file options in the current buffer as described +above. This is useful when the options have been changed or when a file is +created. +@enddefcom + +@section[Windows] + +@index[windows]When running under @windows, each @hemlock window is a +separate window, so window manager commands can be used to move and reshape +windows. + +@defcom[com "New Window", bind (C-x C-n)] +@defcom1[com "Split Window", bind (C-x 2)] +@defcom1[com "Stack Window"] +@hid[New Window] creates a new window on the screen that displays the current +window. The dimensions of the window are determined by asking the user. + +@hid[Split Window] is the same as @hid[New Window] except that it creates the +new window by splitting the current window in half. If the current window is +too small to be reasonably split, the command may fail; however, on some +devices, @hemlock will make a window the same size as the current window, +placing it on the screen shifted downward vertically a small amount. + +@hid[Stack Window] is the same as @i[New Window], except that it makes a window +exactly superimposed on the current window. This is not supported on on all +devices. +@enddefcom + +@defcom[com "Next Window", bind (C-x n)] +@defcom1[com "Previous Window", bind (C-x p)] +These commands make the next or previous window the new current window, often +changing the current buffer in the process. When a window is created, it is +arbitrarily made the next window of the current window. The location of the +next window is, in general, unrelated to that of the current window. +@enddefcom + +@defcom[com "Delete Window", bind (C-x C-d, C-x d)] +@defcom1[com "Delete Next Window", bind (C-x 1)] +@hid[Delete Window] makes the current window go away, making the next window +current. @hid[Delete Next Window] deletes the next window, leaving the current +window unaffected. +@enddefcom + +@defcom[com "Line to Top of Window", bind (M-!)] +@defcom1[com "Line to Center of Window", bind (M-#)] +@index[scrolling]@hid[Line to Top of Window] scrolls the current window up +until the current line is at the top of the screen. + +@hid[Line to Center of Window] attempts to scroll the current window so that +the current line is vertically centered. +@enddefcom + +@defcom[com "Scroll Next Window Down", bind (C-M-v)] +@defcom1[com "Scroll Next Window Up", bind (C-M-V)] +These commands are the same as @hid[Scroll Window Up] and +@hid[Scroll Window Down] except that they operate on the next window. +@enddefcom + +@defcom[com "Refresh Screen", bind {C-l}] +This command refreshes all windows, which is useful if the screen got trashed, +centering the current window about the current line. When the user supplies a +positive argument, it scrolls that line to the top of the window. When the +argument is negative, the line that far from the bottom of the window is moved +to the bottom of the window. In either case when an argument is supplied, this +command only refreshes the current window. +@enddefcom + + +@chap[Editing Documents] +@index[documents, editing] +Although @hemlock is not dedicated to editing documents as word processing +systems are, it provides a number of commands for this purpose. If @hemlock is +used in conjunction with a text-formatting program, then its lack of complex +formatting commands is no liability. + + +@defcom[com "Text Mode"] +This commands puts the current buffer into "Text" mode. +@enddefcom + + +@section[Sentence Commands] +@index[sentence commands] +A sentence is defined as a sequence of characters ending with a period, +question mark or exclamation point, followed by either two spaces or a newline. +A sentence may also be terminated by the end of a paragraph. Any number of +closing delimiters, such as brackets or quotes, may be between the punctuation +and the whitespace. This somewhat complex definition of a sentence is used so +that periods in abbreviations are not misinterpreted as sentence ends. + +@defcom[com "Forward Sentence", bind {M-a}] +@defcom1[com "Backward Sentence", bind {M-e}] +@index[motion, sentence]@hid[Forward Sentence] moves the point forward +past the next sentence end. @hid[Backward Sentence] moves to the beginning +of the current sentence. A prefix argument may be used as a repeat count. +@enddefcom + +@defcom[com "Forward Kill Sentence", bind {M-k}] +@defcom1[com "Backward Kill Sentence", bind (C-x Delete, C-x Backspace)] +@index[killing, sentence]@hid[Forward Kill Sentence] kills text from the +point through to the end of the current sentence. @hid[Backward Kill Sentence] +kills from the point to the beginning of the current sentence. A +prefix argument may be used as a repeat count. +@enddefcom + +@defcom[com "Mark Sentence"] +This command puts the point at the beginning and the mark at the end of the +next or current sentence. +@enddefcom + + +@section[Paragraph Commands] + +@index[paragraph commands]A paragraph may be delimited by a blank line or a +line beginning with "@f[']" or "@f[.]", in which case the delimiting line is +not part of the paragraph. Other characters may be paragraph delimiters in +some modes. A line with at least one leading whitespace character may also +introduce a paragraph and is considered to be part of the paragraph. Any +fill-prefix which is present on a line is disregarded for the purpose of +locating a paragraph boundary. + +@defcom[com "Forward Paragraph", bind (@bf)] +@defcom1[com "Backward Paragraph", bind (M-[)] +@index[motion, paragraph]@index[paragraph, motion]@hid[Forward Paragraph] +moves to the end of the current or next paragraph. @hid[Backward Paragraph] +moves to the beginning of the current or previous paragraph. A prefix +argument may be used as a repeat count. +@enddefcom + +@defcom[com "Mark Paragraph", bind {M-h}] +This command puts the point at the beginning and the mark at the end of the +current paragraph. +@enddefcom + +@defhvar[var "Paragraph Delimiter Function", val {default-para-delim-function}] +This variable holds a function that takes a mark as its argument and returns +true when the line it points to should break the paragraph. +@enddefhvar + +@section[Filling] + +@index[filling]@index[formatting]Filling is a coarse text-formatting +process which attempts to make all the lines roughly the same length, but +doesn't vary the amount of space between words. Editing text may leave +lines with all sorts of strange lengths; filling this text will return it +to a moderately aesthetic form. + +@defcom[com "Set Fill Column", bind (C-x f)] +This command sets the fill column to the column that the point is currently at, +or the one specified by the absolute value of prefix argument, if it is +supplied. The fill column is the column past which no text is permitted to +extend. +@enddefcom + +@defcom[com "Set Fill Prefix", bind (C-x .)] +This command sets the fill prefix to the text from the beginning of the +current line to the point. The fill-prefix is a string which filling commands +leave at the beginning of every line filled. This feature is useful for +filling indented text or comments. +@enddefcom + +@defhvar[var "Fill Column", val {75}] +@defhvar1[var "Fill Prefix", val {nil}] +These variables hold the value of the fill prefix and fill column, thus +setting these variables will change the way filling is done. If +@hid[Fill Prefix] is @nil, then there is no fill prefix. +@enddefcom + +@defcom[com "Fill Paragraph", bind {M-q}] +@index[paragraph, filling]This command fills the text in the current or next +paragraph. The point is not moved. +@enddefcom + +@defcom[com "Fill Region", bind {M-g}] +@index[region, filling]This command fills the text in the region. Since +filling can mangle a large quantity of text, this command asks for confirmation +before filling a large region (see @hid[Region Query Size].) +@enddefcom + + +@defcom[com "Auto Fill Mode"] +@index[modes, auto fill]This command turns on or off the @hid[Fill] +minor mode in the current buffer. When in @hid[Fill] mode, @bf[Space], +@bf[Return] and @bf[Linefeed] are rebound to commands that check whether +the point is past the fill column and fill the current line if it is. +This enables typing text without having to break the lines manually. + +If a prefix argument is supplied, then instead of toggling, the sign +determines whether @hid[Fill] mode is turned off; a positive argument +argument turns in on, and a negative one turns it off. +@enddefcom + +@defcom[com "Auto Fill Linefeed", stuff (bound to @bf[Linefeed] in @hid[Fill] mode)] +@defcom1[com "Auto Fill Return", stuff (bound to @bf[Return] in @hid[Fill] mode)] +@hid[Auto Fill Linefeed] fills the current line if it needs it and then goes to +a new line and inserts the fill prefix. @hid[Auto Fill Return] is similar, but +does not insert the fill prefix on the new line. +@enddefcom + +@defcom[com "Auto Fill Space", stuff (bound to @bf[Space] in @hid[Fill] mode)] +If no prefix argument is supplied, this command inserts a space and +fills the current line if it extends past the fill column. If the argument is +zero, then it fills the line if needed, but does not insert a space. If the +argument is positive, then that many spaces are inserted without filling. +@enddefcom + +@defhvar[var "Auto Fill Space Indent", val {nil}] +This variable determines how lines are broken by the auto fill commands. If it +is true, new lines are created using the @hid[Indent New Comment Line] command, +otherwise the @hid[New Line] command is used. Language modes should define +this variable to be true so that auto fill mode can be used on code. +@enddefhvar + + +@section[Scribe Mode] + +@hid[Scribe] mode provides a number of facilities useful for editing Scribe +documents. It is also sufficiently parameterizable to be adapted to other +similar syntaxes. + +@defcom[com "Scribe Mode"] +@index[modes, scribe]This command puts the current buffer in @hid[Scribe] mode. +Except for special Scribe commands, the only difference between @hid[Scribe] +mode and @hid[Text] mode is that the rules for determining paragraph breaks are +different. In @hid[Scribe] mode, paragraphs delimited by Scribe commands are +normally placed on their own line, in addition to the normal paragraph breaks. +The main reason for doing this is that it prevents @hid[Fill Paragraph] from +mashing these commands into the body of a paragraph. +@enddefcom + +@defcom[com "Insert Scribe Directive", stuff (@bf[C-h] in @hid[Scribe] mode)] +This command prompts for a key-event to determine which Scribe directive to +insert. Directives are inserted differently depending on their kind: +@begin[description] +@i[environment]@\ +The current or next paragraph is enclosed in a begin-end pair: +@f<@@begin[@i{directive}]> @i[paragraph] @f<@@end[@i{directive}]>. If the +current region is active, then this command encloses the region instead of the +paragraph it would otherwise chose. + +@i[command]@\ +The previous word is enclosed by @f<@@@i[directive][@i[word]]>. If the +previous word is already enclosed by a use of the same command, then the +beginning of the command is extended backward by one word. +@end[description] + +Typing @bf[Home] or @bf[C-_] to this command's prompt will display a list of +all the defined key-events on which it dispatches. +@enddefcom + +@defcom[com "Add Scribe Directive"] +This command adds to the database of directives recognized by the +@hid[Insert Scribe Directive] command. It prompts for the directive's name, +the kind of directive (environment or command) and the key-event on which to +dispatch. +@enddefcom + +@defcom[com "Add Scribe Paragraph Delimiter"] +@defcom1[com "List Scribe Paragraph Delimiters"] +@hid[Add Scribe Paragraph Delimiter] prompts for a string to add to the list of +formatting commands that delimit paragraphs in @hid[Scribe] mode. If the user +supplies a prefix argument, then this command removes the string as a +delimiter. + +@hid[List Scribe Paragraph Delimiters] displays in a pop-up window the Scribe +commands that delimit paragraphs. +@enddefcom + +@defhvar[var "Escape Character", val {#\@@}] +@defhvar1[var "Close Paren Character", val {#\]}] +@defhvar1[var "Open Paren Character", val {#\[}] +These variables determine the characters used when a Scribe directive is +inserted. +@enddefhvar + +@defcom[com "Scribe Insert Bracket"] +@defhvar1[var "Scribe Bracket Table"] +@hid[Scribe Insert Bracket] inserts a bracket (@bf[>], @bf[}], @bf[)], or +@bf<]>), that caused its invocation, and then shows the matching bracket. + +@hid[Scribe Bracket Table] holds a @f[simple-vector] indexed by character +codes. If a character is a bracket, then the entry for its @f[char-code] +should be the opposite bracket. If a character is not a bracket, then the +entry should be @nil. +@enddefcom + + +@section[Spelling Correction] +@index[spelling correction] +@hemlock has a spelling correction facility based on the dictionary for the ITS +spell program. This dictionary is fairly small, having only 45,000 word or so, +which means it fits on your disk, but it also means that many reasonably common +words are not in the dictionary. A correct spelling for a misspelled word will +be found if the word is in the dictionary and is only erroneous in that it has +a wrong character, a missing character, an extra character or a transposition +of two characters. + + +@defcom[com "Check Word Spelling", bind (M-$)] +This command looks up the previous or current word in the dictionary and +attempts to correct the spelling if it is misspelled. There are four possible +results of invoking this command: +@begin[enumerate] +This command displays the message "@f[Found it.]" in the echo area. This means +it found the word in the dictionary exactly as given. + +This command displays the message "@f[Found it because of @i[word].]", where +@i[word] is some other word with the same root but a different ending. The +word is no less correct than if the first message is given, but an additional +piece of useless information is supplied to make you feel like you are using a +computer. + +The command prompts with "@f[Correction choice:]" in the echo area and lists +possible correct spellings associated with numbers in a pop-up display. Typing +a number selects the corresponding correction, and the command replaces the +erroneous word, preserving case as though by @hid[Query Replace]. Typing +anything else rejects all the choices. + +This commands displays the message "@f[Word not found.]". The word is not in +the dictionary and possibly spelled correctly anyway. Furthermore, no +similarly spelled words were found to offer as possible corrections. If this +happens, it is worth trying some alternate spellings since one of them might +be close enough to some known words that this command could display. +@end[enumerate] +@enddefcom + +@defcom[com "Correct Buffer Spelling"] +This command scans the entire buffer looking for misspelled words and offers to +correct them. It creates a window into the @hid[Spell Corrections] buffer, and +in this buffer it maintains a log of any actions taken by the user. When this +finds an unknown word, it prompts for a key-event. The user has the following +options: +@begin[description] +@bf[a]@\ + Ignore this word. If the command finds the word again, it will prompt again. + +@bf[i]@\ + Insert this word in the dictionary. + +@bf[c]@\ + Choose one of the corrections displayed in the @hid[Spell Corrections] window +by specifying the correction number. If the same misspelling is encountered +again, then the command will make the same correction automatically, leaving a +note in the log window. + +@bf[r]@\ + Prompt for a word to use instead of the misspelled one, remembering the +correction as with @bf[c]. + +@binding[C-r]@\ + Go into a recursive edit at the current position, and resume checking when the +recursive edit is exited. +@end[description] +After this command completes, it deletes the log window leaving the buffer +around for future reference. +@enddefcom + +@defhvar[var "Spell Ignore Uppercase", val {nil}] +@index[case sensitivity] +If this variable is true, then @hid[Auto Check Word Spelling] and @hid[Correct +Buffer Spelling] will ignore unknown words that are all uppercase. This is +useful for acronyms and cryptic formatter directives. +@enddefhvar + +@defcom[com "Add Word to Spelling Dictionary", bind (C-x $)] +This command adds the previous or current word to the spelling dictionary. +@enddefcom + +@defcom[com "Remove Word from Spelling Dictionary"] +This command prompts for a word to remove from the spelling dictionary. Due to +the dictionary representation, removal of a word in the initial spelling +dictionary will remove all words with the same root. The user is asked for +confirmation before removing a root word with valid suffix flags. +@enddefcom + +@defcom[com "List Incremental Spelling Insertions"] +This command displays the incremental spelling insertions for the current +buffer's associated spelling dictionary file. +@enddefcom + +@defcom[com "Read Spelling Dictionary"] +This command adds some words from a file to the spelling dictionary. The +format of the file is a list of words, one on each line. +@enddefcom + +@defcom[com "Save Incremental Spelling Insertions"] +This command appends incremental dictionary insertions to a file. Any words +added to the dictionary since the last time this was done will be appended to +the file. Except for @hid[Augment Spelling Dictionary], all the commands that +add words to the dictionary put their insertions in this list. The file is +prompted for unless @hid[Set Buffer Spelling Dictionary] has been executed in +the buffer. +@enddefcom + +@defcom[com "Set Buffer Spelling Dictionary"] +This command Prompts for the dictionary file to associate with the current +buffer. If the specified dictionary file has not been read for any other +buffer, then it is read. Incremental spelling insertions from this buffer +can be appended to this file with @hid[Save Incremental Spelling +Insertions]. If a buffer has an associated spelling dictionary, then +saving the buffer's associated file also saves any incremental dictionary +insertions. The @w<"@f[Dictionary: ]@i[file]"> file option may also be +used to specify the dictionary for a buffer (see section +@ref[file-options]). +@enddefcom + +@defhvar[var "Default User Spelling Dictionary", val {nil}] +This variable holds the pathname of a dictionary to read the first time +@hid[Spell] mode is entered in a given editing session. When +@hid[Set Buffer Spelling Dictionary] or the "@f[dictionary]" file option is +used to specify a dictionary, this default one is read also. It defaults to +nil. +@enddefhvar + + +@subsection[Auto Spell Mode] +@hid[Auto Spell Mode] checks the spelling of each word as it is typed. +When an unknown word is typed the user is notified and allowed to take a +number of actions to correct the word. + +@defcom[com "Auto Spell Mode"] +This command turns @hid[Spell] mode on or off in the current buffer. +@enddefcom + +@defcom[com "Auto Check Word Spelling", + stuff (bound to word delimiters in @hid[Spell] mode)] +@defhvar1[var "Check Word Spelling Beep", val {t}] +@defhvar1[var "Correct Unique Spelling Immediately", val {t}] +This command checks the spelling of the word before the point, doing nothing if +the word is in the dictionary. If the word is misspelled but has a known +correction previously supplied by the user, then this command corrects the +spelling. If there is no correction, then this displays a message in the echo +area indicating the word is unknown. An unknown word detected by this command +may be corrected using the @hid[Correct Last Misspelled Word] command. This +command executes in addition to others bound to the same key; for example, if +@hid[Fill] mode is on, any of its commands bound to the same keys as this +command also run. + +If @hid[Check Word Spelling Beep] is true, then this command will beep when an +unknown word is found. If @hid[Correct Unique Spelling Immediately] is true, +then this command will immediately attempt to correct any unknown word, +automatically making the correction if there is only one possible. +@enddefhvar + +@defcom[com "Undo Last Spelling Correction", bind (C-x a)] +@defhvar1[var "Spelling Un-Correct Prompt for Insert", val {nil}] +@hid[Undo Last Spelling Correction] undoes the last incremental spelling +correction. The "correction" is replaced with the old word, and the old word +is inserted in the dictionary. Any automatic replacement for the old word is +eliminated. When @hid[Spelling Un-Correct Prompt for Insert] is true, the user +is asked to confirm the insertion into the dictionary. +@enddefcom + +@defcom[com "Correct Last Misspelled Word", bind (M-:)] +This command places the cursor after the last misspelled word detected by the +@hid[Auto Check Word Spelling] command and then prompts for a key-event on +which it dispatches: +@begin[description] +@bf[c]@\ + Display possible corrections in a pop-up window, and prompt for the one to +make according to the corresponding displayed digit or letter. + +@i[any digit]@\ + Similar to @bf[c] @i[digit], but immediately makes the correction, dispensing +with display of the possible corrections. This is shorter, but only works when +there are less than ten corrections. + +@bf[i]@\ + Insert the word in the dictionary. + +@bf[r]@\ + Replace the word with another. + +@binding[Backspace, Delete, n]@\ + Skip this word and try again on the next most recently misspelled word. + +@binding[C-r]@\ + Enter a recursive edit at the word, exiting the command when the recursive +edit is exited. + +@binding[Escape]@\ + Exit and forget about this word. +@end[description] +As in @hid[Correct Buffer Spelling], the @bf[c] and @bf[r] commands add the +correction to the known corrections. +@enddefcom + + + +@chap[Managing Large Systems] + +@hemlock provides three tools which help to manage large systems: +@begin[enumerate] +File groups, which provide several commands that operate on all the files +in a possibly large collection, instead of merely on a single buffer. + +A source comparison facility with semi-automatic merging, which can be used +to compare and merge divergent versions of a source file. + +A change log facility, which maintains a single file containing a record of the +edits done on a system. +@end[enumerate] + + +@section[File Groups] + +@index[file groups]@index[searching, group]@index[replacing, group] +A file group is a set of files, upon which various editing operations can be +performed. The files in a group are specified by a file in the following +format: +@begin[itemize] +Any line which begins with one "@f[@@]" is ignored. + +Any line which does not begin with an "@f[@@]" is the name of a file in the +group. + +A line which begins with "@f[@@@@]" specifies another file having this +syntax, which is recursively examined to find more files in the group. +@end[itemize] +This syntax is used for historical reasons. Although any number of file groups +may be read into @hemlock, there is only one @i[active group], which is the +file group implicitly used by all of the file group commands. +Page @pageref[compile-group-command] describes the @hid[Compile Group] command. + +@defcom[com "Select Group"] +This command prompts for the name of a file group to make the active group. +If the name entered is not the name of a group whose definition has been +read, then the user is prompted for the name of a file to read the group +definition from. The name of the default pathname is the name of the +group, and the type is "@f[upd]". +@enddefcom + +@defcom[com "Group Query Replace"] +This command prompts for target and replacement strings and then executes an +interactive string replace on each file in the active group. This reads in +each file as if @hid[Find File] were used and processes it as if @hid[Query +Replace] were executing. +@enddefcom + +@defcom[com "Group Replace"] +This is like @hid[Group Query Replace] except that it executes a +non-interactive replacement, similar to @hid[Replace String]. +@enddefcom + +@defcom[com "Group Search"] +This command prompts for a string and then searches for it in each file in the +active group. This reads in each file as if @hid[Find File] were used. When +it finds an occurrence, it prompts the user for a key-event indicating what +action to take. The following commands are defined: +@begin[description] +@binding[Escape, Space, y]@\ + Exit @hid[Group Search]. + +@binding[Delete, Backspace, n]@\ + Continue searching for the next occurrence of the string. + +@binding[!]@\ + Continue the search at the beginning of the next file, skipping the remainder +of the current file. + +@binding[C-r]@\ + Go into a recursive edit at the current location, and continue the search when +it is exited. +@end[description] +@enddefcom + +@defhvar[var "Group Find File", val {nil}] +The group searching and replacing commands read each file into its own buffer +using @hid[Find File]. Since this may result in large amounts of memory being +consumed by unwanted buffers, this variable controls whether to delete the +buffer after processing it. When this variable is false, the default, the +commands delete the buffer if it did not previously exist; however, regardless +of this variable, if the user leaves the buffer modified, the commands will not +delete it. +@enddefhvar + +@defhvar[var "Group Save File Confirm", val {t}] +If this variable is true, the group searching and replacing commands ask for +confirmation before saving any modified file. The commands attempt to save +each file processed before going on to the next one in the group. +@enddefhvar + + +@section[Source Comparison] +@index[buffer, merging] +@index[buffer, comparison] +@index[source comparison] + +These commands can be used to find exactly how the text in two buffers differs, +and to generate a new version that combines features of both versions. + +@defhvar[var "Source Compare Default Destination", val {"Differences"}] +This is a sticky default buffer name to offer when comparison commands prompt +for a buffer in which to insert the results. +@enddefhvar + +@defcom[com "Compare Buffers"] +This command prompts for three buffers and then does a buffer comparison. +The first two buffers must exist, as they are the buffers to be compared. +The last buffer, which is created if it does not exist, is the buffer to +which output is directed. The output buffer is selected during the +comparison so that its progress can be monitored. There are various variables +that control exactly how the comparison is done. + +If a prefix argument is specified, then only only the lines in the the regions +of the two buffers are compared. +@enddefcom + +@defcom[com "Buffer Changes"] +This command compares the contents of the current buffer with the disk version +of the associated file. It reads the file into the buffer +@hid[Buffer Changes File], and generates the comparison in the buffer +@hid[Buffer Changes Result]. As with @hid[Compare Buffers], the output buffer +is displayed in the current window. +@enddefcom + +@defcom[com "Merge Buffers"] +This command functions in a very similar fashion to @hid[Compare Buffers], the +difference being that a version which is a combination of the two buffers being +compared is generated in the output buffer. This copies text that is identical +in the two comparison buffers to the output buffer. When it encounters a +difference, it displays the two differing sections in the output buffer and +prompts the user for a key-event indicating what action to take. The following +commands are defined: +@begin[description] +@bf[1]@\ + Use the first version of the text. + +@bf[2]@\ + Use the second version. + +@bf[b]@\ + Insert the string @w<"@f[**** MERGE LOSSAGE ****]"> followed by both versions. +This is useful if the differing sections are too complex, or it is unclear +which is the correct version. If you cannot make the decision conveniently at +this point, you can later search for the marking string above. + +@binding[C-r]@\ + Do a recursive edit and ask again when the edit is exited. +@end[description] +@enddefcom + + +@defhvar[var "Source Compare Ignore Case", val {nil}] +@index[case sensitivity] +If this variable is non-@nil, @hid[Compare Buffers] and @hid[Merge Buffers] +will do comparisons case-insensitively. +@enddefhvar + +@defhvar[var "Source Compare Ignore Indentation", val {nil}] +If this variable is non-@nil, @hid[Compare Buffers] and @hid[Merge Buffers] +ignore initial whitespace when comparing lines. +@enddefhvar + +@defhvar[var "Source Compare Ignore Extra Newlines", val {t}] +If this variable is true, @hid[Compare Buffers] and @hid[Merge Buffers] +will treat all groups of newlines as if they were a single newline. +@enddefhvar + +@defhvar[var "Source Compare Number of Lines", val {3}] +This variable controls the number of lines @hid[Compare Buffers] and +@hid[Merge Buffers] will compare when resynchronizing after a difference +has been encountered. +@enddefhvar + + +@section[Change Logs] +@label[log-files] +@index[edit history] +@index[change log] + +The @hemlock change log facility encourages the recording of changes to a +system by making it easy to do so. The change log is kept in a separate file +so that it doesn't clutter up the source code. The name of the log for a file +is specified by the @f[Log] file option (see page @pageref[file-options].) + +@defcom[com "Log Change"] +@defhvar1[var "Log Entry Template"] +@hid[Log Change] makes a new entry in the change log associated with the file. +Any changes in the current buffer are saved, and the associated log file is +read into its own buffer. The name of the log file is determined by merging +the name specified in the @f[Log] option with the current buffer's file name, +so it is not usually necessary to put the full name there. After inserting a +template for the log entry at the beginning of the buffer, the command enters a +recursive edit (see page @pageref[recursive-edits]) so that the text of the +entry may be filled in. When the user exits the recursive edit, the log file +is saved. + +The variable @hid[Log Entry Template] determines the format of the change log +entry. Its value is a @clisp @f[format] control string. The format string is +passed three string arguments: the full name of the file, the creation date for +the file and the name of the file author. If the creation date is not +available, the current date is used. If the author is not available then @nil +is passed. If there is an @f[@@] in the template, then it is deleted and the +point is left at that position. +@enddefcom + + + +@comment[@chap (Special Modes)] +@include(special-modes) + + + +@chap[Editing Programs] + + +@section[Comment Manipulation] +@index[comment manipulation] +@hemlock has commenting commands which can be used in almost any language. The +behavior of these commands is determined by several @hemlock variables which +language modes should define appropriately. + +@defcom[com "Indent for Comment", bind (M-;)] +@index[indentation, comment]@label[comment-indentation] +This is the most basic commenting command. If there is already a comment on +the current line, then this moves the point to the start of the comment. If +there no comment, this creates an empty one. + +This command normally indents the comment to start at @hid[Comment Column]. +The comment indents differently in the following cases: +@begin[enumerate] +If the comment currently starts at the beginning of the line, or if the last +character in the @hid[Comment Start] appears three times, then the comment +remains unmoved. + +If the last character in the @hid[Comment Start] appears two times, then the +comment is indented like a line of code. + +If text on the line prevents the comment occurring in the desired position, +this places the comment at the end of the line, separated from the text by a +space. +@end[enumerate] +Although the rules about replication in the comment start are oriented toward +Lisp commenting styles, you can exploit these properties in other languages. + +When given a prefix argument, this command indents any existing comment on that +many consecutive lines. This is useful for fixing up the indentation of a +group of comments. +@enddefcom + +@defcom[com "Indent New Comment Line", bind {M-j, M-Linefeed}] +This commend ends the current comment and starts a new comment on a blank line, +indenting the comment the same way that @hid[Indent for Comment] does. +When not in a comment, this command is the same as @hid[Indent New Line]. +@enddefcom + +@defcom[com "Up Comment Line", bind {M-p}] +@defcom1[com "Down Comment Line", bind {M-n}] +These commands are similar to @hid[Previous Line] or @hid[Next Line] +followed by @hid[Indent for Comment]. Any empty comment on the current line is +deleted before moving to the new line. +@enddefcom + +@defcom[com "Kill Comment", bind (C-M-;)] +This command kills any comment on the current line. When given a prefix +argument, it kills comments on that many consecutive lines. @hid[Undo] will +restore the unmodified text. +@enddefcom + +@defcom[com "Set Comment Column", bind (C-x ;)] +This command sets the comment column to its prefix argument. If used without a +prefix argument, it sets the comment column to the column the point is at. +@enddefcom + +@defhvar[var "Comment Start", val {nil}] +@defhvar1[var "Comment End", val {nil}] +@defhvar1[var "Comment Begin", val {nil}] +@defhvar1[var "Comment Column", val {0}] +These variables determine the behavior of the comment commands. +@begin[description] +@hid[Comment Start]@\The string which indicates the start of a comment. If +this is @nil, then there is no defined comment syntax. + +@hid[Comment End]@\The string which ends a comment. If this is @nil, then +the comment is terminated by the end of the line. + +@hid[Comment Begin]@\The string inserted to begin a new comment. + +@hid[Comment Column]@\The column that normal comments start at. +@end[description] +@enddefcom + + +@section[Indentation] +@label[indentation] +@index[indentation] +Nearly all programming languages have conventions for indentation or leading +whitespace at the beginning of lines. The @hemlock indentation facility is +integrated into the command set so that it interacts well with other features +such as filling and commenting. + +@defcom[com "Indent", bind (Tab, C-i)] +This command indents the current line. With a prefix argument, indents that +many lines and moves down. Exactly what constitutes indentation depends on the +current mode (see @hid[Indent Function]). +@enddefcom + +@defcom[com "Indent New Line", bind (Linefeed)] +This command starts a new indented line. Deletes any whitespace before the +point and inserts indentation on a blank line. The effect of this is similar +to @binding[Return] followed by @binding[Tab]. The prefix argument is passed +to @hid[New Line], which is used to insert the blank line. +@enddefcom + +@defcom[com "Indent Region", bind (C-M-\)] +This command indents every line in the region. It may be undone with +@hid[Undo]. +@enddefcom + +@defcom[com "Back to Indentation", bind {M-m, C-M-m}] +@index[motion, indentation] +This command moves point to the first non-whitespace character on the current +line. +@enddefcom + +@defcom[com "Delete Indentation", bind (M-^, C-M-^)] +@hid[Delete Indentation] joins the current line with the previous one, deleting +excess whitespace. This operation is the inverse of the @bf[Linefeed] command +in most modes. Usually this leaves one space between the two joined lines, but +there are several exceptions. + +The non-whitespace immediately surrounding the deleted line break determine the +amount of space inserted. +@begin[enumerate] +If the preceding character is an "@f[(]" or the following character is a +"@f[)]", then this inserts no space. + +If the preceding character is a newline, then this inserts no space. This will +happen if the previous line was blank. + +If the preceding character is a sentence terminator, then this inserts two +spaces. +@end[enumerate] + +When given a prefix argument, this command joins the current and next lines, +rather than the previous and current lines. +@enddefcom + +@defcom[com "Quote Tab", bind (M-Tab)] +This command inserts a tab character. +@enddefcom + +@defcom[com "Indent Rigidly", bind (C-x Tab, C-x C-i)] +This command changes the indentation of all the lines in the region. Each +line is moved to the right by the number of spaces specified by the prefix +argument, which defaults to eight. A negative prefix argument moves lines +left. +@enddefcom + +@defcom[com "Center Line"] +This indents the current line so that it is centered between the left margin +and @hvarref[Fill Column]. If a prefix argument is supplied, then it is used +as the width instead of @hid[Fill Column]. +@enddefcom + +@defhvar[var "Indent Function", val {tab-to-tab-stop}] +The value of this variable determines how indentation is done, and it is a +function which is passed a mark as its argument. The function should indent +the line which the mark points to. The function may move the mark around on +the line. The mark will be @f[:left-inserting]. The default simply inserts a +tab character at the mark. +@enddefhvar + +@defhvar[var "Indent with Tabs", val {indent-using-tabs}] +@defhvar1[var "Spaces per Tab", val {8}] +@hid[Indent with Tabs] holds a function that takes a mark and a number of +spaces. The function will insert a maximum number of tabs and a minimum number +of spaces at mark to move the specified number of columns. The default +definition uses @hid[Spaces per Tab] to determine the size of a tab. @i[Note,] +@hid[Spaces per Tab] @i[is not used everywhere in @hemlock yet, so changing +this variable could have unexpected results.] +@enddefhvar + + +@section[Language Modes] + +@hemlock@comment{}'s language modes are currently fairly crude, but probably +provide better programming support than most non-extensible editors. + +@defcom[com "Pascal Mode"] +@index[indentation, pascal]@index[modes, pascal]This command sets the current +buffer's major mode to @hid[Pascal]. @hid[Pascal] mode borrows parenthesis +matching from Scribe mode and indents lines under the previous line. +@enddefcom + + +@chap[Editing Lisp] +@index[lisp, editing] +@hemlock provides a large number of powerful commands for editing @llisp code. +It is possible for a text editor to provide a much higher level of support for +editing @llisp than ordinary programming languages, since its syntax is much +simpler. + + +@section[Lisp Mode] +@index[lisp mode] +@index[modes, lisp] +@hid[Lisp] mode is a major mode used for editing @llisp code. Although most +@llisp specific commands are globally bound, @hid[Lisp] mode is necessary to +enable @llisp indentation, commenting, and parenthesis-matching. Whenever the +user or some @hemlock mechanism turns on @hid[Lisp] mode, the mode's setup +includes locally setting @hid[Current Package] (see section @ref[lisp-package]) +in that buffer if its value is non-existent there; the value used is +@f["USER"]. + +@defcom[com "Lisp Mode"] +This command sets the major mode of the current buffer to @hid[Lisp]. +@enddefcom + + +@section[Form Manipulation] +@index[form manipulation] +These commands manipulate @llisp forms, the printed representations of @llisp +objects. A form is either an expression balanced with respect to parentheses +or an atom such as a symbol or string. + +@defcom[com "Forward Form", bind (C-M-f)] +@defcom1[com "Backward Form", bind (C-M-b)] +@index[motion, form]@hid[Forward Form] moves to the end of the current or +next form, while @hid[Backward Form] moves to the beginning of the current +or previous form. A prefix argument is treated as a repeat count. +@enddefcom + +@defcom[com "Forward Kill Form", bind (C-M-k)] +@defcom1[com "Backward Kill Form", bind (C-M-Delete, C-M-Backspace)] +@index[killing, form]@hid[Forward Kill Form] kills text from the point to +the end of the current form. If at the end of a list, but inside the close +parenthesis, then kill the close parenthesis. @hid[Backward Kill Form] is +the same, except it goes in the other direction. A prefix argument is +treated as a repeat count. +@enddefcom + +@defcom[com "Mark Form", bind (C-M-@@)] +This command sets the mark at the end of the current or next form. +@enddefcom + +@defcom[com "Transpose Forms", bind (C-M-t)] +This command transposes the forms before and after the point and moves +forward. A prefix argument is treated as a repeat count. If the prefix +argument is negative, then the point is moved backward after the +transposition is done, reversing the effect of the equivalent positive +argument. +@enddefcom + +@defcom[com "Insert ()", bind {M-(}] +This command inserts an open and a close parenthesis, leaving the point +inside the open parenthesis. If a prefix argument is supplied, then the +close parenthesis is put at the end of the form that many forms from the +point. +@enddefcom + +@defcom[com "Extract Form"] +This command replaces the current containing list with the next form. The +entire affected area is pushed onto the kill ring. If an argument is supplied, +that many upward levels of list nesting is replaced by the next form. This is +similar to @hid[Extract List], but this command is more generally useful since +it works on any kind of form; it is also more intuitive since it operates on +the next form as many @hid[Lisp] mode commands do. +@enddefcom + + +@section[List Manipulation] + +@index[list manipulation]List commands are similar to form commands, but +they only pay attention to lists, ignoring any atomic objects that may +appear. These commands are useful because they can skip over many symbols +and move up and down in the list structure. + +@defcom[com "Forward List", bind (C-M-n)] +@defcom1[com "Backward List", bind (C-M-p)] +@index[motion, list]@hid[Forward List] moves the point to immediately +after the end of the next list at the current level of list structure. If +there is not another list at the current level, then it moves up past +the end of the containing list. +@hid[Backward List] is identical, except that it moves backward and leaves +the point at the beginning of the list. The prefix argument is used as a +repeat count. +@enddefcom + +@defcom[com "Forward Up List", bind {C-M-@bf<)>}] +@defcom1[com "Backward Up List", bind (C-M-@bf<(>, C-M-u)] +@hid[Forward Up List] moves to after the end of the enclosing list. +@hid[Backward Up List] moves to the beginning. The prefix argument is used +as a repeat count. +@enddefcom + +@defcom[com "Down List", bind (C-M-d)] +This command moves to just after the beginning of the next list. The +prefix argument is used as a repeat count. +@enddefcom + +@defcom[com "Extract List", bind (C-M-x)] +This command "extracts" the current list from the list which contains it. +The outer list is deleted, leaving behind the current list. The entire +affected area is pushed on the kill ring, so that this possibly catastrophic +operation can be undone. The prefix argument is used as a repeat count. +@enddefcom + + + +@section[Defun Manipulation] + +@index[defun manipulation]A @i[defun] is a list whose open parenthesis is +against the left margin. It is called this because an occurrence of the +@f[defun] top level form usually satisfies this definition, but +other top level forms such as a @f[defstruct] and @f[defmacro] work just as +well. + +@defcom[com "End of Defun", bind (@bf)] +@defcom1[com "Beginning of Defun", bind (C-M-a, C-M-[)] +@index[motion, defun]@hid[End of Defun] moves to the end of the current +or next defun. @hid[Beginning of Defun] moves to the beginning of the +current or previous defun. @hid[End of Defun] will not work if the +parentheses are not balanced. +@enddefcom + +@defcom[com "Mark Defun", bind (C-M-h)] +This command puts the point at the beginning and the mark at the end of the +current or next defun. +@enddefcom + +@section[Indentation] + +@index[indentation, lisp]One of the most important features provided by +@hid[Lisp] mode is automatic indentation of @llisp code, since unindented +@llisp is unreadable, poorly indented @llisp is ugly, and inconsistently +indented @llisp is subtly misleading. See section @ref[indentation] for a +description of the general-purpose indentation commands. These are the +indentation rules used: +@begin[itemize] +If in a semicolon (@f[;]) comment, then the standard comment indentation rules +are used. See page @pageref[comment-indentation]. + +If in a quoted string, copy the indentation from the previous line. + +If there is no enclosing list, then use no indentation. + +If enclosing list resembles a call to a known macro or special-form, then the +first few arguments are given greater indentation and the first body form is +indented two spaces. If the first special argument is on the same line as the +beginning of the form, then following special arguments will be indented to the +start of the first special argument, otherwise all special arguments are +indented four spaces. + +If the previous form starts on its own line, then the indentation is copied +from that form. This rule allows the default indentation to be overridden: +once a form has been manually indented to the user's satisfaction, subsequent +forms will be indented in the same way. + +If the enclosing list has some arguments on the same line as the form start, +then subsequent arguments will be indented to the start of the first argument. + +If the enclosing list has no argument on the same line as the form start, then +arguments will be indented one space. +@end[itemize] + + +@defcom[com "Indent Form", bind (C-M-q)] +This command indents all the lines in the current form, leaving the point +unmoved. This is undo-able. +@enddefcom + +@defcom[com "Defindent", bind (C-M-#)] +This command prompts for the number of special arguments to associate with +the symbol at the beginning of the current or containing list. +@enddefcom + +@defhvar[var "Indent Defanything", val {2}] +This is the number of special arguments implicitly assumed to be supplied in +calls to functions whose names begin with "@f[def]". If set to @nil, this +feature is disabled. +@enddefhvar + +@defcom[com "Move Over )", bind {M-)}] +This command moves past the next close parenthesis and then does the equivalent +of @hid[Indent New Line]. +@enddefcom + + +@section[Parenthesis Matching] + +@index[parenthesis matching]Another very important facility provided by +@hid[Lisp] mode is @i[parenthesis matching]. Two different styles of +parenthesis matching are supported: highlighting and pausing. + +@defhvar[var "Highlight Open Parens", val {t}] +@defhvar1[var "Open Paren Highlighting Font", val {nil}] +When @hid[Highlight Open Parens] is true, and a close paren is immediately +before the point, then @hemlock displays the matching open paren in @hid[Open +Paren Highlighting Font]. + +@hid[Open Paren Highlighting Font] is the string name of the font used for +paren highlighting. Only the "@f[(]" character is used in this font. If null, +then a reasonable default is chosen. The highlighting font is read at +initialization time, so this variable must be set before the editor is first +entered to have any effect. +@enddefhvar + +@defcom[com "Lisp Insert )", stuff ] +@defhvar1[var "Paren Pause Period", val {0.5}] +This command inserts a close parenthesis and then attempts to display the +matching open parenthesis by placing the cursor on top of it for +@hid[Paren Pause Period] seconds. If there is no matching parenthesis then +beep. If the matching parenthesis is off the top of the screen, then the line +on which it appears is displayed in the echo area. Paren pausing may be +disabled by setting @hid[Paren Pause Period] to @nil. +@enddefcom + +The initial values shown for @hid[Highlight Open Parens] and @hid[Paren Pause +Period] are only approximately correct. Since paren highlighting is only +meaningful in @llisp mode, @hid[Highlight Open Parens] is false globally, and +has a mode-local value of @true in @llisp mode. It it redundant to do both +kinds of paren matching, so there is also a binding of @hid[Paren Pause Period] +to @false in @llisp mode. + +Paren highlighting is only supported under @windows, so the above defaults are +conditional on the device type. If @hemlock is started on a terminal, the +initialization code makes @llisp mode bindings of @false and @f[0.5] for +@hid[Highlight Open Parens] and @hid[Paren Pause Period]. Since these +alternate default bindings are made at initialization time, the only way to +affect them is to use the @f[after-editor-initializations] macro. + + +@section[Parsing Lisp] +@llisp mode has a fairly complete knowledge of @llisp syntax, but since it does +not use the reader, and must work incrementally, it can be confused by legal +constructs. @llisp mode totally ignores the read-table, so user-defined read +macros have no effect on the editor. In some cases, the values the @hid[Lisp +Syntax] character attribute can be changed to get a similar effect. + +@llisp commands consistently treat semicolon (@f[;]) style comments as +whitespace when parsing, so a @llisp command used in a comment will affect the +next (or previous) form outside of the comment. Since @f[#| ... |#] comments +are not recognized, they can used to comment out code, while still allowing +@llisp editing commands to be used. + +Strings are parsed similarly to symbols. When within a string, the next form +is after the end of the string, and the previous form is the beginning of the +string. + + +@defhvar[var "Defun Parse Goal", val {2}] +@defhvar1[var "Maximum Lines Parsed", val {500}] +@defhvar1[var "Minimum Lines Parsed", val {50}] +In order to save time, @llisp mode does not parse the entire buffer every time +a @llisp command is used. Instead, it uses a heuristic to guess the region of +the buffer that is likely to be interesting. These variables control the +heuristic. + +Normally, parsing begins and ends on defun boundaries (an open parenthesis at +the beginning of a line). @hid[Defun Parse Goal] specifies the number of +defuns before and after the point to parse. If this parses fewer lines than +@hid[Minimum Lines Parsed], then parsing continues until this lower limit is +reached. If we cannot find enough defuns within @hid[Maximum Lines Parsed] +lines then we stop on the farthest defun found, or at the point where we +stopped if no defuns were found. + +When the heuristic fails, and does not parse enough of the buffer, then +commands usually act as though a syntax error was detected. If the parse +starts in a bad place (such as in the middle of a string), then @llisp commands +will be totally confused. Such problems can usually be eliminated by +increasing the values of some of these variables. +@enddefhvar + +@defhvar[var "Parse Start Function", val {start-of-parse-block}] +@defhvar1[var "Parse End Function", val {end-of-parse-block}] +These variables determine the region of the buffer parsed. The values are +functions that take a mark and move it to the start or end of the parse region. +The default values implement the heuristic described above. +@enddefhvar + + + +@comment[@chap(Interacting With Lisp)] +@include(lisp) + + +@comment[@chap(Mail Interface)] +@include(mail) + + + +@chap[System Interface] + +@hemlock provides a number of commands that access operating system resources +such as the filesystem and print servers. These commands offer an alternative +to leaving the editor and using the normal operating system command language +(such as the Unix shell), but they are implementation dependent. Therefore, +they might not even exist in some implementations. + + +@section[File Utility Commands] +This section describes some general file operation commands and quick directory +commands. + +See section @ref[dired] for a description @hemlock's directory editing +mechanism, @hid[Dired] mode. + +@defcom[com "Copy File"] +This command copies a file, allowing one wildcard in the filename. It prompts +for source and destination specifications. + +If these are both directories, then the copying process is recursive on the +source, and if the destination is in the subdirectory structure of the source, +the recursion excludes this portion of the directory tree. Use +@f[dir-spec-1/*] to copy only the files in a source directory without +recursively descending into subdirectories. + +If the destination specification is a directory, and the source is a file, then +it is copied into the destination with the same filename. + +The copying process copies files maintaining the source's write date. + +See the description of @hid[Dired Copy File Confirm], page +@pageref[copy-confirm], for controlling user interaction when the destination +exists. +@enddefcom + +@defcom[com "Rename File"] +This command renames a file, allowing one wildcard in the filename. It prompts +for source and destination specifications. + +If the destination is a directory, then the renaming process moves file(s) +indicated by the source into the directory with their original filenames. + +For Unix-based implementations, if you want to rename a directory, do not +specify the trailing slash in the source specification. +@enddefcom + +@defcom[com "Delete File"] +This command prompts for the name of a file and deletes it. +@enddefcom + +@defcom[com "Directory", bind (C-x C-d)] +@defcom1[com "Verbose Directory", bind (C-x C-D)] +These commands prompt for a pathname (which may contain wildcards), and display +a directory listing in a pop-up window. If a prefix argument is supplied, then +normally hidden files such as Unix dot-files will also be displayed. +@hid[Directory] uses a compact, multiple-column format; +@hid[Verbose Directory] displays one file on a line, with information about +protection, size, etc. +@enddefcom + + +@section[Printing] + +@defcom[com "Print Region"] +@defcom1[com "Print Buffer"] +@defcom1[com "Print File"] +@hid[Print Region] and @hid[Print Buffer] print the contents of the current +region and the current buffer, respectively. @hid[Print File] prompts for a +the name of a file and prints that file. Any error messages will be displayed +in the echo area. +@enddefcom + +@defhvar[var "Print Utility", val {"/usr/cs/bin/lpr"}] +@defhvar1[var "Print Utility Switches", val {()}] +@hid[Print Utility] is the program the print commands use to send files to the +printer. The program should act like @f[lpr]: if a filename is given as an +argument, it should print that file, and if no name appears, standard input +should be assumed. @hid[Print Utility Switches] is a list of strings +specifying the options to pass to the program. +@enddefhvar + + +@section[Scribe] +@defcom[com "Scribe Buffer File", + stuff (bound to @bf[C-x c] in @hid[Scribe] mode)] +@defhvar1[var "Scribe Buffer File Confirm", val {t}] +@defcom1[com "Scribe File"] +@hid[Scribe Buffer File] invokes @hid[Scribe Utility] on the file associated +with the current buffer. That process's default directory is the directory of +the file. The process sends its output to the @hid[Scribe Warnings] buffer. +Before doing anything, this asks the user to confirm saving and formatting the +file. This prompting can be inhibited with "Scribe Buffer File Confirm". + +@hid[Scribe File] invokes @hid[Scribe Utility] on a file supplied by the user +in the same manner as describe above. +@enddefcom + +@defhvar[var "Scribe Utility", val {"/usr/misc/bin/scribe"}] +@defhvar1[var "Scribe Utility Switches"] +@hid[Scribe Utility] is the program the Scribe commands use to compile the text +formatting. @hid[Scribe Utility Switches] is a list of strings whose contents +would be contiguous characters, other than space, had the user invoked this +program on a command line outside of @hemlock. Do not include the name of the +file to compile in this variable; the Scribe commands supply this. +@enddefhvar + +@defcom[com "Select Scribe Warnings", bind (Scribe: C-M-C)] +This command makes the @hid[Scribe Warnings] buffer current if it exists. +@enddefcom + + +@section[Unix Filtering] + +@defcom[com "Unix Filter Region"] +This command prompts for a UNIX program and then passes the current region to +the program as standard input. The standard output from the program is used to +replace the region. This command is undoable. +@enddefcom + + + +@chap[Simple Customization] + +@index[customization]@hemlock can be customized and extended to a very +large degree, but in order to do much of this a knowledge of @llisp is +required. These advanced aspects of customization are discussed in the +@i[Hemlock Command Implementor's Manual], while simpler methods of +customization are discussed here. + + +@section[Keyboard Macros] +@index[keyboard macros] +Keyboard macros provide a facility to turn a sequence of commands into one +command. + +@defcom[com "Define Keyboard Macro", bind {C-x (}] +@defcom1[com "End Keyboard Macro", bind {C-x )}] +@hid[Define Keyboard Macro] starts the definition of a keyboard macro. The +commands which are invoked up until @hid[End Keyboard Macro] is invoked +become the definition for the keyboard macro, thus replaying the keyboard +macro is synonymous with invoking that sequence of commands. +@enddefcom + +@defcom[com "Last Keyboard Macro", bind (C-x e)] +This command is the keyboard macro most recently defined; invoking it will +replay the keyboard macro. The prefix argument is used as a repeat count. +@enddefcom + +@defcom[com "Define Keyboard Macro Key", bind (C-x M-(; )] +@defhvar1[var "Define Keyboard Macro Key Confirm", val {t}] +This command prompts for a key before going into a mode for defining keyboard +macros. After defining the macro @hemlock binds it to the key. If the key is +already bound, @hemlock asks for confirmation before clobbering the binding; +this prompting can be inhibited by setting @hid[Define Keyboard Macro Key +Confirm] to @nil. +@enddefcom + +@defcom[com "Keyboard Macro Query", bind (C-x q)] +This command conditionalizes the execution of a keyboard macro. When invoked +during the definition of a macro, it does nothing. When the macro replays, it +prompts the user for a key-event indicating what action to take. The following +commands are defined: +@begin[description] +@binding[Escape]@\ + Exit all repetitions of this keyboard macro. More than one may have been +specified using a prefix argument. + +@binding[Space, y]@\ + Proceed with the execution of the keyboard macro. + +@binding[Delete, Backspace, n]@\ + Skip the remainder of the keyboard macro and go on to the next repetition, if +any. + +@binding[!]@\ + Do all remaining repetitions of the keyboard macro without prompting. + +@binding[.]@\ + Complete this repetition of the macro and then exit without doing any of the +remaining repetitions. + +@binding[C-r]@\ + Do a recursive edit and then prompt again. +@end[description] +@enddefcom + +@defcom[com "Name Keyboard Macro"] +This command prompts for the name of a command and then makes the +definition for that command the same as @hid[Last Keyboard Macro]'s current +definition. The command which results is not clobbered when another +keyboard macro is defined, so it is possible to keep several keyboard +macros around at once. The resulting command may also be bound to a key +using @hid[Bind Key], in the same way any other command is. +@enddefcom + +Many keyboard macros are not for customization, but rather for one-shot +use, a typical example being performing some operation on each line of a file. +To add "@f[del ]" to the beginning and "@f[.*]" to the end of every line in +in a buffer, one could do this: +@begin[programexample] +C-x ( d e l Space C-e . * C-n C-a C-x ) C-u 9 9 9 C-x e +@end[programexample] +First a keyboard macro is defined which performs the desired operation on +one line, and then the keyboard macro is invoked with a large prefix +argument. The keyboard macro will not actually execute that many times; +when the end of the buffer is reached the @binding[C-n] will get an error +and abort the execution. + + +@section[Binding Keys] +@index[key bindings] +@label[binding-keys] + +@defcom[com "Bind Key"] +This command prompts for a command, a key and a kind of binding to make, +and then makes the specified binding. The following kinds of bindings are +allowed: +@begin[description] +@i[buffer]@\Prompts for a buffer and then makes a key binding which is +only present when that buffer is the current buffer. + +@i[mode]@\Prompts for the name of a mode and then makes a key binding which +is only in present when that mode is active in the current buffer. + +@i[global]@\Makes a global key binding which is in effect when there is +no applicable mode or buffer key binding. This is the default. +@end[description] +@enddefcom + +@defcom[com "Delete Key Binding"] +This command prompts for a key binding the same way that @hid[Bind Key] +does and makes the specified binding go away. +@enddefcom + +@section[Hemlock Variables] + +@label[vars]@index[variables, hemlock]@index[hemlock variables]A number +of commands use @hemlock variables as flags to control their behavior. Often +you can get a command to do what you want by setting a variable. Generally the +default value for a variable is chosen to be the safest value for novice users. + +@defcom[com "Set Variable"] +This command prompts for the name of a @hemlock variable and an expression, +then sets the current value of the variable to the result of the evaluation of +the expression. +@enddefcom + + +@defcom[com "Defhvar"] +Like @hid[Set Variable], this command prompts for the name of a @hemlock +variable and an expression. Like @hid[Bind Key], this command prompts for a +place: mode, buffer or local. The result of evaluating the expression is +defined to be the value of the named variable in the specified place. + +This command is most useful for making mode or buffer local bindings of +variables. Redefining a variable in a mode or buffer will create a +customization that takes effect only when in that mode or buffer. + +Unlike @hid[Set Variable], the variable name need not be the name of an +existing variable: new variables may be defined. If the variable is already +defined in the current environment, @hemlock copies the documentation and hooks +to the new definition. +@enddefcom + + +@section[Init Files] +@index[init files] +@hemlock customizations are normally put in @hemlock's initialization file, +"@f[hemlock-init.lisp]", or when compiled "@f[hemlock-init.fasl]". When +starting up Lisp, use the @f[-hinit] switch to indicate a particular file. The +contents of the init file must be @llisp code, but there is a fairly +straightforward correspondence between the basic customization commands and the +equivalent @llisp code. Rather than describe these functions in depth here, a +brief example follows: +@begin[programexample] +;;; -*- Mode: Lisp; Package: Hemlock -*- + +;;; It is necessary to specify that the customizations go in +;;; the hemlock package. +(in-package 'hemlock) + +;;; Bind @hid[Kill Previous Word] to @binding[M-h]. +(bind-key "Kill Previous Word" '#(#\m-h)) +;;; +;;; Bind @hid[Extract List] to @binding[C-M-?] when in @hid[Lisp] mode. +(bind-key "Extract List" '#(#\c-m-?) :mode "Lisp") + +;;; Make @binding[C-w] globally unbound. +(delete-key-binding '#(#\c-w)) + +;;; Make string searches case-sensitive. +(setv string-search-ignore-case nil) +;;; +;;; Make "Query Replace" replace strings literally. +(setv case-replace nil) +@end[programexample] +For a detailed description of these functions, see the @i[Hemlock Command +Implementor's Manual]. diff --git a/docs/rtguts.mss b/docs/rtguts.mss new file mode 100644 index 0000000000000000000000000000000000000000..38f08e396776e027dbba559dcaa59d9555ff95ad --- /dev/null +++ b/docs/rtguts.mss @@ -0,0 +1,4150 @@ +@make [Manual] +@device [PostScript] +@use (database "/usr/lisp/scribe/database/") +@libraryfile [Mathematics10] +@libraryfile [ArpaCredit] +@libraryfile [table] +@libraryfile [spice] +@style(FontFamily=TimesRoman) +@style(Date="March 1952") + +@commandstring(pusharrow = "@jsym") +@define(f, facecode f) + +@commandstring(InstrSection = "@tabclear@tabset[.5 in, 3.0 in]") +@form(Instr = "@*@\@Parm[name]@\") +@form(BInstr ="@*@\@Parm[name]@+[*]@\") +@string(DinkyMachine = "IBM RT PC") +@begin[TitlePage] +@begin[TitleBox] +@blankspace(0.25in) +@heading[Internal Design of CMU Common Lisp +on the IBM RT PC] +@begin[Center] +@b{David B. McDonald +Scott E. Fahlman +Skef Wholey + +@value[Date] + +CMU-CS-87-157 +} +@end[Center] +@end[TitleBox] +@center[@b] +@begin[Text] +CMU Common Lisp is an implementation of Common Lisp that currently runs on +the IBM RT PC under Mach, a Berkeley Unix 4.3 binary compatible operating +system. This document describes low level +details of the implementation. In particular, it describes the data +formats used for all Lisp objects, the assembler language routines +(miscops) used to support compiled code, the function call and return +mechanism, and other design information necessary to understand the +underlying structure of the CMU Common Lisp implementation on the IBM RT PC +under the Mach operating system. +@end[Text] + +@begin[ResearchCredit] +@ArpaCredit[Contract=Strategic87-90] +@end[ResearchCredit] +@end[TitlePage] + +@heading [Acknowledgments] + +This document is based heavily on the document @i[Revised Internal Design +of Spice Lisp] by Skef Wholey, Scott Fahlman, and Joseph Ginder. + +The FASL file format was designed by Guy L. Steele Jr. and Walter van +Roggen, and the appendix on this subject is their document with very few +modifications. + +@chapter [Introduction] + +@section [Scope and Purpose] + +This document describes a new implementation of CMU Common Lisp (nee Spice +Lisp) as it is implemented on the @value(DinkyMachine) running Mach, a +Berkeley Unix 4.3 binary compatible operating system. This design is +undergoing rapid change, and for the present is not guaranteed to +accurately describe any past, present, or future implementation of CMU +Common Lisp. All questions and comments on this material should be +directed to David B. McDonald (David.McDonald@@CS.CMU.EDU). + +This document specifies the hand-coded assembler routines (miscops) and +virtual memory architecture of the @value(DinkyMachine) CMU Common Lisp system. +This is a working document, and it will change frequently as the system is +developed and maintained. If some detail of the system does not agree with +what is specified here, it is to be considered a bug. + +@section [Notational Conventions] +@index [Bit numbering] +@index [Byte numbering] +CMU Common Lisp objects are 32 bits long. The high-order bit of each word is +numbered 0; the low-order bit is numbered 31. If a word is broken into smaller +units, these are packed into the word from left to right. For example, if we +break a word into bytes, byte 0 would occupy bits 0-7, byte 1 would occupy +8-15, byte 2 would occupy 16-23, and byte 3 would occupy 24-31. + +All CMU Common Lisp documentation uses decimal as the default radix; other +radices will be indicated by a subscript (as in 77@-[8]) or by a clear +statement of what radix is in use. + +@chapter [Data Types and Object Formats] + +@section [Lisp Objects] +@index [Lisp objects] + +Lisp objects are 32 bits long. They come in 32 basic types, divided into three +classes: immediate data types, pointer types, and forwarding pointer types. +The storage formats are as follows: + +@index [Immediate object format] +@index [Pointer object format] +@begin [verbatim, group] + +@b[Immediate Data Types:] + 0 4 5 31 +------------------------------------------------------------------------ +| Type Code (5) | Immediate Data (27) | +------------------------------------------------------------------------ + +@b[Pointer and Forwarding Types:] + 0 4 5 6 7 29 31 +------------------------------------------------------------------------ +| Type Code (5) | Space Code (2) | Pointer (23) | Unused (2) | +------------------------------------------------------------------------ +@end [verbatim] + +@section [Table of Type Codes] +@index [Type codes] + +@begin [verbatim, group] + +Code Type Class Explanation +---- ---- ----- ----------- +0 + Fixnum Immediate Positive fixnum, miscop code, etc. +1 GC-Forward Pointer GC forward pointer, used during GC. +4 Bignum Pointer Bignum. +5 Ratio Pointer Two words: numerator, denominator. +6 + Short Float Immediate Positive short flonum. +7 - Short Float Immediate Negative short flonum. +8 Single Float Pointer Single precision float. +9 Double Float Pointer Double precision float (?). +9 Long Float Pointer Long float. +10 Complex Pointer Two words: real, imaginary parts. +11 String Pointer Character string. +12 Bit-Vector Pointer Vector of bits +13 Integer-Vector Pointer Vector of integers +14 General-Vector Pointer Vector of Lisp objects. +15 Array Pointer Array header. +16 Function Pointer Compiled function header. +17 Symbol Pointer Symbol. +18 List Pointer Cons cell. +20 C. S. Pointer Pointer Pointer into control stack. +21 B. S. Pointer Pointer Pointer into binding stack. +26 Interruptible Immediate Marks a miscop as interruptible. +27 Character Immediate Character object. +28 Values-Marker Immediate Multiple values marker. +29 Catch-All Immediate Catch-All object. +30 Trap Immediate Illegal object trap. +31 - Fixnum Immediate Negative fixnum. +@end [verbatim] + +@section [Table of Space Codes] +@index [Space codes] + +@begin [verbatim, group] + +Code Space Explanation +---- ----- ----------- +0 Dynamic-0 Storage normally garbage collected, space 0. +1 Dynamic-1 Storage normally garbage collected, space 1. +2 Static Permanent objects, never moved or reclaimed. +3 Read-Only Objects never moved, reclaimed, or altered. +@end [verbatim] + +@section [Immediate Data Type Descriptions] + +@begin [description] + +@index [Fixnum format] +Fixnum@\A 28-bit two's complement integer. The sign bit is stored redundantly +in the top 5 bits of the word. + +@index [Short float format] +Short-Float@\The sign bit is stored as part of the type code, +allowing a 28 bit signed short float format. The format of short floating +point numbers is: +@begin [verbatim] + 0 3 4 5 12 13 31 +--------------------------------------------------------------- +| Type code (4) | Sign (1) | Exponent (8) | Mantissa (19) | +--------------------------------------------------------------- +@end [verbatim] +The floating point number is the same format as the @value(DinkyMachine) +supports for single precision numbers, except it has been shifted right +by four bits for the type code. The result of any operation is therefore +truncated. Long floating point numbers are also available if you need +more accuracy and better error propagation properties. + +@index [Character object] +Character@\A character object holding a character code, control bits, and font +in the following format: +@begin [verbatim, group] + 0 4 6 7 8 15 16 23 24 31 +--------------------------------------------------------------- +| Type code (5) | Unused (3) | Font (8) | Bits (8) | Code (8) | +--------------------------------------------------------------- +@end [verbatim] + +@index [Values-Marker] +Values-Marker@\Used to mark the presence of multiple values on the stack. The +low 16 bits indicate how many values are being returned. Note that only 65535 +values can be returned from a multiple-values producing form. These are pushed +onto the stack in order, and the Values-Marker is returned in register A0. + +@index [Catch-All object] +Catch-All@\Object used as the catch tag for unwind-protects. Special things +happen when a catch frame with this as its tag is encountered during a throw. +See section @ref[Catch] for details. + +@index[Trap] +@index[Illegal object trap] +Trap@\Illegal object trap. This value is used in symbols to signify an +undefined value or definition. + +@index[Interruptible Marker] +Interruptible-Marker@\Object used to mark a miscop as interruptible. This +object is put in one of the registers and signals to the interrupt handler +that the miscop can be interrupted safely. Only miscops that can take a long +time (e.g., length when passed a circular list, system call miscops that +may wait indefinitely) are marked this way. +@end [description] + +@section [Pointer-Type Objects and Spaces] +@index [Pointer object format] +@index [Virtual memory] + +Each of the pointer-type lisp objects points into a different space in virtual +memory. There are separate spaces for Bit-Vectors, Symbols, Lists, and so on. +The 5-bit type-code provides the high-order virtual address bits for the +object, followed by the 2-bit space code, followed by the 25-bit pointer +address. This gives a 30-bit virtual address to a 32-bit word; since the +@value(DinkyMachine) is a byte-addressed machine, the two low-order +bits are 0. In effect we have carved a 30-bit space into a fixed set +of 23-bit subspaces, not all of which are used. + +@index [Space codes] +The space code divides each of the type spaces into four sub-spaces, +as shown in the table above. At any given time, one of the dynamic +spaces is considered newspace, while the other is oldspace. +During a stop and copy garbage collection, a ``flip'' can be done, turning the +old newspace into the new oldspace. All type-spaces are flipped at once. +Allocation of new dynamic objects always occurs in newspace. + +@index [Static space] +@index [Read-only space] +Optionally, the user (or system functions) may allocate objects in +static or read-only space. Such objects are never reclaimed once they +are allocated -- they occupy the space in which they were initially +allocated for the lifetime of the Lisp process. The advantage of +static allocation is that the GC never has to move these objects, +thereby saving a significant amount of work, especially if the objects +are large. Objects in read-only space are static, in that they are +never moved or reclaimed; in addition, they cannot be altered once +they are set up. Pointers in read-only space may only point to +read-only or static space, never to dynamic space. This saves even +more work, since read-only space does not need to be scavenged, and +pages of read-only material do not need to be written back onto the +disk during paging. + +Objects in a particular type-space will contain either pointers to +garbage-collectible objects or words of raw non-garbage-collectible bits, but +not both. Similarly, a space will contain either fixed-length objects or +variable-length objects, but not both. A variable-length object always +contains a 24-bit length field right-justified in the first word, with +the positive fixnum type-code in the high-order five bits. The remaining three +bits can be used for sub-type information. The length field gives the +size of the object in 32-bit words, including the header word. The +garbage collector needs this information when the object is moved, and +it is also useful for bounds checking. + +The format of objects in each space are as follows: + +@begin [description] +@index [Symbol] +@index [Value cell] +@index [Definition cell] +@index [Property list cell] +@index [Plist cell] +@index [Print name cell] +@index [Pname cell] +@index [Package cell] +Symbol@\Each symbol is represented as a +fixed-length block of boxed Lisp cells. The number of cells +per symbol is 5, in the following order: +@begin [verbatim, group] +0 Value cell for shallow binding. +1 Definition cell: a function or list. +2 Property list: a list of attribute-value pairs. +3 Print name: a string. +4 Package: the obarray holding this symbol. +@end [verbatim] + +@index [List cell] +List@\A fixed-length block of two boxed Lisp cells, the CAR and the CDR. + +@index [General-Vector format] +@index [G-Vector format] +@index [Vector format] +General-Vector@\Vector of lisp objects, any length. The first word is a fixnum +giving the number of words allocated for the vector (up to 24 bits). The +highest legal index is this number minus 2. The second word is vector entry 0, +and additional entries are allocated contiguously in virtual memory. General +vectors are sometimes called G-Vectors. (See section @ref[Vectors] for further +details.) + +@index [Integer-Vector format] +@index [I-Vector format] +@index [Vector format] +Integer-Vector@\Vector of integers, any length. The 24 low bits of the first +word give the allocated length in 32-bit words. The low-order 28 bits of the +second word gives the length of the vector in entries, whatever the length of +the individual entries may be. The high-order 4 bits of the second word +contain access-type information that yields, among other things, the number of +bits per entry. Entry 0 is left-justified in the third word of the vector. +Bits per entry will normally be powers of 2, so they will fit neatly into +32-bit words, but if necessary some empty space may be left at the low-order +end of each word. Integer vectors are sometimes called I-Vectors. (See +section @ref[Vectors] for details.) + +@index [Bit-Vector format] +@index [Vector format] +Bit-Vector@\Vector of bits, any length. Bit-Vectors are represented in a form +identical to I-Vectors, but live in a different space for efficiency reasons. + +@index [Bignum format] +@label [Bignums] +Bignum@\Bignums are infinite-precision integers, represented in a format +identical to G-Vectors. Each bignum is stored as a series of 32-bit words, +with the low-order word stored first. The representation is two's complement, +but the sign of the number is redundantly encoded in the type field of the +fixnum in the header word. If this fixnum is non-negative, then so is the +bignum, if it is negative, so is the bignum. + +@index [Flonum format] +@index [Flonum formats] +@index [Floating point formats] +Floats@\Floats are stored as two or more consecutive words of bits, in the +following format: +@begin [verbatim, group] +--------------------------------------------------------------- +| Header word, used only for GC forward pointers. | +--------------------------------------------------------------- +| Appropriate number of 32-bit words in machine format | +--------------------------------------------------------------- +@end [verbatim] +The number of words used to represent a floating point number is one plus the +size of the floating point number being stored. The floating point numbers +will be represented in whatever format the @value(DinkyMachine) expects. The +extra header word is needed so that a valid floating point number is not +mistaken for a gc-forward pointer during a garbage collection. + +@index [Ratio format] +Ratio@\Ratios are stored as two consecutive words of Lisp objects, which should +both be integers. + +@index [Complex number format] +Complex@\Complex numbers are stored as two consecutive words of Lisp objects, +which should both be numbers. + +@index [Array format] +Array@\This is actually a header which holds the accessing and +other information about the array. The actual array contents are held in a +vector (either an I-Vector or G-Vector) pointed to by an entry in +the header. The header is identical in format to a G-Vector. For +details on what the array header contains, see section @ref[Arrays]. + +@index [String format] +String@\A vector of bytes. Identical in form to I-Vectors with the access type +always 8-Bit. However, instead of accepting and returning fixnums, string +accesses accept and return character objects. Only the 8-bit code field is +actually stored, and the returned character object always has bit and font +values of 0. + +@index [Function object format] +Function @\A compiled CMU Common Lisp function consists of both lisp +objects and raw bits for the code. The Lisp objects are stored in +the Function space in a format identical to that used for general +vectors, with a 24-bit length field in the first word. This object +contains assorted parameters needed by the calling machinery, a +pointer to an 8-bit I-Vector containing the compiled code, a number +of pointers to symbols used as special variables within the function, +and a number of lisp objects used as constants by the function. +@end [description] + +@section [Forwarding Pointers] +@index [Forwarding pointers] + +@begin [description] +@index [GC-Forward pointer] +GC-Forward@\When a data structure is transported into newspace, a GC-Forward +pointer is left behind in the first word of the oldspace object. This points +to the same type-space in which it is found. For example, a GC-Forward in +G-Vector space points to a structure in the G-Vector newspace. GC-Forward +pointers are only found in oldspace. +@end [description] + +@section [System and Stack Spaces] +@index [System table space] +@index [Stack spaces] +@index [Control stack space] +@index [Binding stack space] +@index [Special binding stack space] + +The virtual addresses below 08000000@-[16] are not occupied by Lisp objects, +since Lisp objects with type code 0 are positive fixnums. Some of this space +is used for other purposes by Lisp. A couple of pages (4096 byte pages) +at address 00100000@-[16] contain tables that Lisp needs to access +frequently. These include the allocation table, the active-catch-frame, +information to link to C routines, etc. Memory at location 00200000@-[16] +contains code for various miscops. Also, any C code loaded into a running +Lisp process is loaded after the miscops. The format of the allocation +table is described in chapter @ref[Alloc-Chapter]. + +The control stack grows upward (toward higher addresses) in memory, +and is a framed stack. It contains only general Lisp objects (with +some random things encoded as fixnums). Every object +pointed to by an entry on this stack is kept alive. The frame for a +function call contains an area for the function's arguments, an area +for local variables, a pointer to the caller's frame, and a pointer +into the binding stack. The frame for a Catch form contains similar +information. The precise stack format can be found in chapter +@ref[Runtime]. + +The special binding stack grows downward. This stack is used to hold +previous values of special variables that have been bound. It grows and +shrinks with the depth of the binding environment, as reflected in the +control stack. This stack contains symbol-value pairs, with only boxed +Lisp objects present. + +All Lisp objects are allocated on word boundaries, since the +@value(DinkyMachine) can only access words on word boundaries. + +@section [Vectors and Arrays] +@label [Vectors] +@index [Vectors] + +Common Lisp arrays can be represented in a few different ways in CMU Common +Lisp -- different representations have different performance advantages. +Simple general vectors, simple vectors of integers, and simple strings are +basic CMU Common Lisp data types, and access to these structures is quicker +than access to non-simple (or ``complex'') arrays. However, all +multi-dimensional arrays in CMU Common Lisp are complex arrays, so +references to these are always through a header structure. + +@subsection [General Vectors] +@index [General-Vector format] + +G-Vectors contain Lisp objects. The format is as follows: + +@begin [verbatim, group] +------------------------------------------------------------------ +| Fixnum code (5) | Subtype (3) | Allocated length (24) | +------------------------------------------------------------------ +| Vector entry 0 (Additional entries in subsequent words) | +------------------------------------------------------------------ +@end [verbatim] + +The first word of the vector is +a header indicating its length; the remaining words hold the boxed entries of +the vector, one entry per 32-bit word. The header word is of type fixnum. It +contains a 3-bit subtype field, which is used to indicate several special types +of general vectors. At present, the following subtype codes are defined: + +@index [DEFSTRUCT] +@index [Hash tables] +@begin [itemize, spread 0, spacing 1] +0 Normal. Used for assorted things. + +1 Named structure created by DEFSTRUCT, with type name in entry 0. + +2 EQ Hash Table, last rehashed in dynamic-0 space. + +3 EQ Hash Table, last rehashed in dynamic-1 space. + +4 EQ Hash Table, must be rehashed. +@end [itemize] + +Following the subtype is a 24-bit field indicating how many 32-bit words are +allocated for this vector, including the header word. Legal indices into the +vector range from zero to the number in the allocated length field minus 2, +inclusive. Normally, the index is checked on every access to the vector. +Entry 0 is stored in the second word of the vector, and subsequent entries +follow contiguously in virtual memory. + +Once a vector has been allocated, it is possible to reduce its length by using +the Shrink-Vector miscop, but never to increase its length, even back to +the original size, since the space freed by the reduction may have been +reclaimed. This reduction simply stores a new smaller value in the length +field of the header word. + +It is not an error to create a vector of length 0, though it will always be an +out-of-bounds error to access such an object. The maximum possible length for +a general vector is 2@+[24]-2 entries, and that can't fit in the available +space. The maximum length is 2@+[23]-2 entries, and that is only possible if +no other general vectors are present in the space. + +@index [Bignum Format] +Bignums are identical in format to G-Vectors although each entry is a 32-bit +integer, and thus only assembler routines should ever access an entry. + +@index [Function object format] +@index [Array format] +Objects of type Function and Array are identical in format to +general vectors, though they have their own spaces. + +@subsection [Integer Vectors] +@index [Integer-Vector format] + +I-Vectors contain unboxed items of data, and their format is more complex. The +data items come in a variety of lengths, but are of constant length within a +given vector. Data going to and from an I-Vector are passed as Fixnums, right +justified. Internally these integers are stored in packed form, filling 32-bit +words without any type-codes or other overhead. The format is as follows: + +@begin [verbatim, group] +---------------------------------------------------------------- +| Fixnum code (5) | Subtype (3) | Allocated length (24) | +---------------------------------------------------------------- +| Access type (4) | Number of entries (28) | +---------------------------------------------------------------- +| Entry 0 left justified | +---------------------------------------------------------------- +@end [verbatim] + +The first word of an I-Vector +contains the Fixnum type-code in the top 5 bits, a 3-bit subtype code in the +next three bits, and the total allocated length of the vector (in 32-bit words) +in the low-order 24 bits. At present, the following subtype codes are defined: +@begin [itemize, spread 0, spacing 1] +0 Normal. Used for assorted things. + +1 Code. This is the code-vector for a function object. +@end [itemize] + +The second word of the vector is the one that is looked at every +time the vector is accessed. The low-order 28 bits of this word +contain the number of valid entries in the vector, regardless of how +long each entry is. The lowest legal index into the vector is always +0; the highest legal index is one less than this number-of-entries +field from the second word. These bounds are checked on every access. +Once a vector is allocated, it can be reduced in size but not increased. +The Shrink-Vector miscop changes both the allocated length field +and the number-of-entries field of an integer vector. + +@index [Access-type codes] +The high-order 4 bits of the second word contain an access-type code +which indicates how many bits are occupied by each item (and therefore +how many items are packed into a 32-bit word). The encoding is as follows: +@begin [verbatim, group] +0 1-Bit 8 Unused +1 2-Bit 9 Unused +2 4-Bit 10 Unused +3 8-Bit 11 Unused +4 16-Bit 12 Unused +5 32-Bit 13 Unused +6 Unused 14 Unused +7 Unused 15 Unused +@end [verbatim] + +In I-Vectors, the data items are packed into the third and subsequent +words of the vector. Item 0 is left justified in the third word, +item 1 is to its right, and so on until the allocated number of items +has been accommodated. All of the currently-defined access types +happen to pack neatly into 32-bit words, but if this should not be +the case, some unused bits would remain at the right side of each +word. No attempt will be made to split items between words to use up +these odd bits. When allocated, an I-Vector is initialized to all +0's. + +As with G-Vectors, it is not an error to create an I-Vector of length +0, but it will always be an error to access such a vector. The +maximum possible length of an I-Vector is 2@+[28]-1 entries or +2@+[23]-3 words, whichever is smaller. + +@index [String format] +Objects of type String are identical in format to I-Vectors, though they have +their own space. Strings always have subtype 0 and access-type 3 (8-Bit). +Strings differ from normal I-Vectors in that the accessing miscops accept +and return objects of type Character rather than Fixnum. + +@subsection [Arrays] +@label [Arrays] +@index [Arrays] + +An array header is identical in form to a G-Vector. Like any G-Vector, its +first word contains a fixnum type-code, a 3-bit subtype code, and a 24-bit +total length field (this is the length of the array header, not of the vector +that holds the data). At present, the subtype code is always 0. The entries +in the header-vector are interpreted as follows: + +@index [Array header format] +@begin [description] +0 Data Vector @\This is a pointer to the I-Vector, G-Vector, or string that +contains the actual data of the array. In a multi-dimensional array, the +supplied indices are converted into a single 1-D index which is used to access +the data vector in the usual way. + +1 Number of Elements @\This is a fixnum indicating the number of elements for +which there is space in the data vector. + +2 Fill Pointer @\This is a fixnum indicating how many elements of the data +vector are actually considered to be in use. Normally this is initialized to +the same value as the Number of Elements field, but in some array applications +it will be given a smaller value. Any access beyond the fill pointer is +illegal. + +3 Displacement @\This fixnum value is added to the final code-vector index +after the index arithmetic is done but before the access occurs. Used for +mapping a portion of one array into another. For most arrays, this is 0. + +4 Range of First Index @\This is the number of index values along the first +dimension, or one greater than the largest legal value of this index (since the +arrays are always zero-based). A fixnum in the range 0 to 2@+[24]-1. If any +of the indices has a range of 0, the array is legal but will contain no data +and accesses to it will always be out of range. In a 0-dimension array, this +entry will not be present. + +5 - N Ranges of Subsequent Dimensions +@end [description] + +The number of dimensions of an array can be determined by looking at the length +of the array header. The rank will be this number minus 6. The maximum array +rank is 65535 - 6, or 65529. + +The ranges of all indices are checked on every access, during the conversion to +a single data-vector index. In this conversion, each index is added to the +accumulating total, then the total is multiplied by the range of the following +dimension, the next index is added in, and so on. In other words, if the data +vector is scanned linearly, the last array index is the one that varies most +rapidly, then the index before it, and so on. + +@section [Symbols Known to the Assembler Routines] +@label [Known-Objects] + +A large number of symbols will be pre-defined when a CMU Common Lisp system +is fired up. A few of these are so fundamental to the operation of the +system that their addresses have to be known to the assembler routines. +These symbols are listed here. All of these symbols are in static space, +so they will not move around. + +@begin [description] +@index [NIL] +NIL @\94000000@-[16] The value of NIL is always NIL; it is an error +to alter it. The plist of NIL is always NIL; it is an error to alter +it. NIL is unique among symbols in that it is stored in Cons cell +space and thus you can take its CAR and CDR, yielding NIL in either +case. NIL has been placed in Cons cell space so that the more common +operations on lists will yield the desired results. This slows down +some symbol operations but this should be insignificant compared to +the savings in list operations. A test for NIL for the +@value(DinkyMachine) is: +@begin(Example) + xiu R0,P,X'9400' + bz IsNIL or bnz IsNotNIL +@end(Example) + +@index [T] +T @\8C000000@-[16] The value of T is always T; it is an error +to alter it. A similar sequence of code as for NIL above can test for T, +if necessary. + +@index [%SP-Internal-Apply] +%SP-Internal-Apply @\8C000014@-[16] The function stored in the definition cell +of this symbol is called by an assembler routine whenever compiled code calls +an interpreted function. + +@index [%SP-Internal-Error] +%SP-Internal-Error @\8C000028@-[16] The function stored in the definition cell +of this symbol is called whenever an error is detected during the execution of +an assembler routine. See section @ref[Errors] for details. + +@index [%SP-Software-Interrupt-Handler] +%SP-Software-Interrupt-Handler @\8C00003C@-[16] The function stored in the +definition cell of this symbol is called whenever a software interrupt occurs. +See section @ref[Interrupts] for details. + +@index [%SP-Internal-Throw-Tag] +%SP-Internal-Throw-Tag @\8C000050@-[16] This symbol is bound to the tag being +thrown when a Catch-All frame is encountered on the stack. See section +@ref[Catch] for details. + +@index [%Initial-function] +%Initial-function@\8c000064@-[16] This symbol's function cell should contain +a function that is called when the initial core image is started. This +function should initialize all the data structures that Lisp needs to run. + +@index [%Link-table-header] +%Link-table-header@\8c000078@-[16] This symbol's value cell contains a pointer +to the link table information. + +@index [Current-allocation-space] +Current-allocation-space@\8c00008c@-[16] This symbol's value cell contains +an encoded form of the current space that new lisp objects are to be allocated +in. + +@index [%SP-bignum/fixnum] +%SP-bignum/fixnum@\8c0000a0@-[16] This function is invoked by the miscops +when a division of a bignum by a fixnum results in a ratio. + +@index [%SP-fixnum/bignum] +%SP-bignum/bignum@\8c0000b4@-[16] This +function is invoked by the miscops when a division of a fixnum by a +bignum results in a ratio. + +@index [%SP-bignum/bignum] +%SP-bignum/bignum@\8c0000c8@-[16] This function is invoked by the miscops +when a division of a bignum by a bignum results in a ratio. + +@index [%SP-abs-ratio] +%SP-abs-ratio@\8c0000dc@-[16] This function is invoked by the miscops +when the absolute value of a ratio is taken. + +@index [%SP-abs-complex] +%SP-abs-complex@\8c0000f0@-[16] This function is invoked by the miscops +when the absolute value of a complex is taken. + +@index [%SP-negate-ratio] +%SP-negate-ratio@\8c000104@-[16] This function is invoked by the miscops +when a ratio is to be negated. + +@index [%SP-negate-complex] +%SP-negate-ratio@\8c000118@-[16] This function is invoked by the miscops +when a complex is to be negated. + +@index[%SP-integer+ratio] +%SP-integer+ratio@\8c00012c@-[16] This function is invoked by the miscops +when a fixnum or bignum is added to a ratio. + +@index[%SP-ratio+ratio] +%SP-ratio+ratio@\8c000140@-[16] This function is invoked by the miscops +when a ratio is added to a ratio. + +@index[%SP-complex+number] +%SP-complex+number@\8c000154@-[16] This function is invoked by the miscops +when a complex is added to a number. + +@index[%SP-number+complex] +%SP-number+complex@\8c000168@-[16] This function is invoked by the miscops +when a number is added to a complex. + +@index[%SP-complex+complex] +%SP-complex+complex@\8c00017c@-[16] This function is invoked by the miscops +when a number is added to a complex. + +@index[%SP-1+ratio] +%SP-1+ratio@\8c000190@-[16] This function is invoked by the miscops when +1 is added to a ratio. + +@index[%SP-1+complex] +%SP-1+complex@\8c000190@-[16] This function is invoked by the miscops when +1 is added to a complex. + +@index[%SP-ratio-integer] +%SP-ratio-integer@\8c0001b8@-[16] This function is invoked by the miscops +when an integer is subtracted from a ratio. + +@index[%SP-ratio-ratio] +%SP-ratio-ratio@\8c0001cc@-[16] This function is invoked by the miscops +when an ratio is subtracted from a ratio. + +@index[%SP-complex-number] +%SP-complex-number@\8c0001e0@-[16] This function is invoked by the miscops +when a complex is subtracted from a number. + +@index[%SP-number-complex] +%SP-number-complex@\8c0001f4@-[16] This function is invoked by the miscops +when a number is subtracted from a complex. + +@index[%SP-complex-complex] +%SP-complex-complex@\8c000208@-[16] This function is invoked by the miscops +when a complex is subtracted from a complex. + +@index[%SP-1-complex] +%SP-1-complex@\8c000230@-[16] This function is invoked by the miscops when +1 is subtracted from a complex. + +@index[%SP-ratio*ratio] +%SP-ratio*ratio@\8c000244@-[16] This function is invoked by the miscops to +multiply two ratios. + +@index[%SP-number*complex] +%SP-number*complex@\8c000258@-[16] This function is invoked by the miscops to +multiply a number by a complex. + +@index[%SP-complex*number] +%SP-complex*number@\8c00026c@-[16] This function is invoked by the miscops to +multiply a complex by a number. + +@index[%SP-complex*complex] +%SP-complex*complex@\8c000280@-[16] This function is invoked by the miscops +to multiply a complex by a complex. + +@index[%SP-integer/ratio] +%SP-integer/ratio@\8c000294@-[16] This function is invoked by the miscops to +divide an integer by a ratio. + +@index[%SP-ratio/integer] +%SP-ratio/integer@\8c0002a8@-[16] This function is invoked by the miscops to +divide a ratio by an integer. + +@index[%SP-ratio/ratio] +%SP-ratio/ratio@\8c0002bc@-[16] This function is invoked by the miscops to +divide a ratio by a ratio. + +@index[%SP-number/complex] +%SP-number/complex@\8c0002d0@-[16] This function is invoked by the miscops to +divide a number by a complex. + +@index[%SP-complex/number] +%SP-complex/number@\8c0002e4@-[16] This function is invoked by the miscops to +divide a complex by a number. + +@index[%SP-complex/complex] +%SP-complex/complex@\8c0002f8@-[16] This function is invoked by the miscops +to divide a complex by a complex. + +@index[%SP-integer-truncate-ratio] +%SP-integer-truncate-ratio@\8c00030c@-[16] This function is invoked by the +miscops to truncate an integer by a ratio. + +@index[%SP-ratio-truncate-integer] +%SP-ratio-truncate-integer@\8c000320@-[16] This function is invoked by the +miscops to truncate a ratio by an integer. + +@index[%SP-ratio-truncate-ratio] +%SP-ratio-truncate-ratio@\8c000334@-[16] This function is invoked by the +miscops to truncate a ratio by a ratio. + +@index[%SP-number-truncate-complex] +%SP-number-truncate-complex@\8c000348@-[16] This function is invoked by the +miscops to truncate a number by a complex. + +@index[%SP-complex-truncate-number] +%SP-complex-truncate-number@\8c00035c@-[16] This function is invoked by the +miscops to truncate a complex by a number. + +@index[%SP-complex-truncate-complex] +%SP-complex-truncate-complex@\8c000370@-[16] This function is invoked by +the miscops to truncate a complex by a complex. + +@index[maybe-gc] +Maybe-GC@\8c000384@-[16] This function may be invoked by any miscop that +does allocation. This function determines whether it is time to garbage +collect or not. If it is it performs a garbage collection. Whether it +invokes a garbage collection or not, it returns the single argument passed +to it. + +@index[Lisp-environment-list] +Lisp-environment-list@\8c000398@-[16] The value of this symbol is +set to the a list of the Unix environment strings passed into the Lisp +process. This list by Lisp to obtain various environment information, such +as the user's home directory, etc. + +@index[Call-lisp-from-c] +Call-lisp-from-C@\8c0003ac@-[16] This function is called whenever a +C function called by Lisp tries to call a Lisp function. + +@index[Lisp-command-line-list] +Lisp-command-line-list@\8c0003c0@-[16] The value of this symbol is +set to the list of strings passed into the Lisp process as the command +line. + +@index[*Nameserverport*] +*Nameserverport*@\8c0003d4@-[16] The value of this symbol is set to +the C global variable name_server_port. This allows Lisp to access the +name server. + +@index[*Ignore-Floating-Point-Underflow*] +*Ignore-Floating-Point-Underflow*@\8c0003e8@-[16] If the the value of this +symbol is NIL then an error is signalled when floating point underflow +occurs, otherwise the operation quietly returns zero. +@End[description] + +@chapter [Runtime Environment] +@index [Runtime Environment] +@label [Runtime] + +@section [Register Allocation] +@index [Register allocation] +To describe the assembler support routines in chapter @ref[Instr-Chapter] and +the complicated +control conventions in chapter @ref[Control-Conventions] requires that we talk +about the allocation of the 16 32-bit general purpose registers provided +by the @value(DinkyMachine). +@begin [description] +@index [Program-Counter register] +Program-Counter (PC) [R15]@\This register contains an index into the current +code vector when a Lisp function is about to be called. When a miscop is +called, it contains the return address. It may be used as a super temporary +between miscop and function calls. + +@index [Active-Function-Pointer register] +Active-Function-Pointer (AF) [R14]@\This register contains a pointer to the +active function object. It is used to access the symbol and constant area for +the currently running function. + +@index [Active-Frame-Pointer register] +Active-Frame-Pointer (FP) [R13]@\This register contains a pointer to the +current active frame on the control stack. It is used to access the arguments +and local variables stored on the control stack. + +@index [Binding-Stack-Pointer register] +Binding-Stack-Pointer (BS) [R12]@\This register contains the current binding +stack pointer. The binding stack is a downward growing stack and follows +a decrement-write/increment-read discipline. + +@index [Local registers] +Local registers (L0-L4) [R7-R11]@\These registers contain locals and saved +arguments for the currently executing function. Functions may use these +registers, so that stack accesses can be reduced, since a stack access is +relatively expensive compared to a register access. + +@index [Argument registers] +Argument register (A0, A1, A2) [R1, R3, R5]@\These registers contain arguments +to a function or miscop that has just been called. On entry to a function +or miscop, they contain the first three arguments. The first thing a function +does is to move the contents of these registers into the local registers. + +@index [Miscop argument register] +Miscop argument register (A3) [R4]@\This register is used to pass a fourth +argument to miscops requiring four or more arguments. It is also used as a +super temporary by the compiler. + +@index [Control-Stack-Pointer register] +Control-Stack-Pointer (CS) [R6]@\The stack pointer for the control stack, an +object of type Control-Stack-Pointer. Points to the last used word in +Control-Stack space; this upward growing stack uses a +increment-write/read-decrement discipline. + +@index [Non-Lisp temporary registers] +Non-Lisp temporary registers (NL0, NL1) [R0, R2]@\These registers are used to +contain non-Lisp values. They will normally be used during miscop calls, but +may also be used in in-line code to contain temporary data. These are the only +two registers never examined by the garbage collector, so no pointers to Lisp +objects should be stored here (since they won't get updated during a garbage +collection). +@end [description] + +@section [Function Object Format] +@label [Fn-Format] + +Each compiled function is represented in the machine as a Function +Object. This is identical in form to a G-Vector of lisp objects, and +is treated as such by the garbage collector, but it exists in a +special function space. (There is no particular reason for this +distinction. We may decide later to store these things in G-Vector +space, if we become short on spaces or have some reason to believe +that this would improve paging behavior.) Usually, the function +objects and code vectors will be kept in read-only space, but nothing +should depend on this; some applications may create, compile, and +destroy functions often enough to make dynamic allocation of function +objects worthwhile. + +@index [Code vector] +@index [Constants in code] The function object contains a vector of +header information needed by the function-calling mechanism: a +pointer to the I-Vector that holds the actual code. Following this +is the so-called ``symbols and constants'' area. The first few +entries in this area are fixnums that give the offsets into the code +vector for various numbers of supplied arguments. Following this +begin the true symbols and constants used by the function. Any +symbol used by the code as a special variable. +Fixnum constants can be generated faster +with in-line code than they can be accessed from the function-object, +so they are not stored in the constants area. + +The subtype of the G-Vector header indicates the type of the function: +@begin(Itemize, spacing 1, spread 0) +0 - A normal function (expr). + +1 - A special form (fexpr). + +2 - A defmacro macroexpansion function. + +3 - An anonymous expr. The name is the name of the parent function. + +4 - A compiled top-level form. +@end(Itemize) +Only the fexpr information has any real meaning to the system. The rest +is there for the printer and anyone else who cares. + + +After the one-word G-Vector header, the entries of the function object +are as follows: + +@begin [verbatim, group] +0 Name of the innermost enclosing named function. +1 Pointer to the unboxed Code vector holding the instructions. +2 A fixnum with bit fields as follows: + 24 - 31: The minimum legal number of args (0 to 255). + 16 - 23: The maximum number of args, not counting &rest (0 to 255). + The fixnum has a negative type code, if the function accepts a &rest + arg and a positive one otherwise. +3 A string describing the source file from which the function was defined. + See below for a description of the format. +4 A string containing a printed representation of the argument list, for + documentation purposes. If the function is a defmacro macroexpansion + function, the argument list will be the one originally given to defmacro + rather than the actual arglist to the expansion function. +5 The symbols and constants area starts here. + This word is entry 0 of the symbol/constant area. + The first few entries in this area are fixnums representing the + code-vector entry points for various numbers of optional arguments. +@end [verbatim] + +@section [Defined-From String Format] +@label [Defined-From-String-Format] +@index [Defined-From String Format] + +The defined-from string may have any of three different formats, depending +on which of the three compiling functions compiled it: +@begin(Description) +compile-file "@i[filename user-time universal-time]"@\ The @i[filename] is +the namestring of the truename of the file the function was defined from. +The time is the file-write-date of the file. + +compile "Lisp on @i[user-time], machine @i[machine universal-time]"@\ +The time is the time that the function was compiled. @i[Machine] is the +machine-instance of the machine on which the compilation was done. + +compile-from-stream "@i[stream] on @i[user-time], machine @i[machine-instance +universal-time]"@\@i[Stream] is the printed representation of the stream +compiled from. The time is the time the compilation started. +@end(Description) + +An example of the format of @i[user-time] is 6-May-86 1:04:44. The +@i[universal-time] is the same time represented as a decimal integer. +It should be noted that in each case, the universal time is the last +thing in the string. + +@section [Control-Stack Format] +@label [Control-Stack-Format] +@index [Control-stack format] + +The CMU Common Lisp control stack is a framed stack. Call frames, which hold +information for function calls, are intermixed with catch frames, which hold +information used for non-local exits. In addition, the control stack is used +as a scratchpad for random computations. + +@subsection [Call Frames] +@index [Open frame] +@index [Active frame] + +At any given time, the machine contains pointers to the current top +of the control stack and the start of the current active frame (in +which the current function is executing). In addition, there is a +pointer to the current top of the special binding stack. CMU Common Lisp +on the Perq also has a pointer to an open frame. An open frame is +one which has been partially built, but which is still having +arguments for it computed. When all the arguments have been computed +and saved on the frame, the function is then started. This means +that the call frame is completed, becomes the current active frame, +and the function is executed. At this time, special variables may be +bound and the old values are saved on the binding stack. Upon +return, the active frame is popped away and the result is either sent +as an argument to some previously opened frame or goes to some other +destination. The binding stack is popped and old values are +restored. + +On the @value(DinkyMachine), open frames still exist, however, no register is +allocated to point at the most recent one. Instead, a count of the arguments +to the function is kept. In most cases, a known fixed number of arguments are +passed to a function, and this is all that is needed to calculate the correct +place to set the active frame pointer. +In some cases, it is not as simple, and runtime calculations are necessary to +set up the frame pointer. These calculations are simple except in some very +strange cases. + +The active frame contains pointers to the previously-active frame and +to the point to which the binding stack will be popped +on exit, among other things. Following this is a vector of storage locations +for the function's arguments and local variables. Space is allocated for the +maximum number of arguments that the function can take, regardless of how many +are actually supplied. + +In an open frame, stack space is allocated up to the point where the arguments +are stored. Nothing is stored in the frame +at this time. Thus, as arguments are computed, they can simply be pushed on +the stack. Since the first three arguments are passed in registers, it is +sometimes necessary to save these values when succeeding arguments are +complicated. When the function is finally started, the remainder of the frame +is built (including storing all the +registers that must be saved). A call frame looks like this: +@begin [verbatim, group] +0 Saved local 0 register. +1 Saved local 1 register. +2 Saved local 2 register. +3 Saved local 3 register. +4 Saved local 4 register. +5 Pointer to previous binding stack. +6 Pointer to previous active frame. +7 Pointer to previous active function. +8 Saved PC of caller. A fixnum. +9 Args-and-locals area starts here. This is entry 0. +@end [verbatim] +The first slot is pointed to by the Active-Frame register if this frame is +currently active. + +@subsection [Catch Frames] +@index [Catch] +@index [Catch frames] + +Catch frames contain much of the same information that call frames +do, and have a very similar format. A catch frame holds the function +object for the current function, a stack pointer to the current +active frame, a pointer to the current top of the binding stack, and +a pointer to the previous catch frame. When a Throw occurs, an +operation similar to returning from this catch frame (as if it +were a call frame) is performed, and the stacks are unwound to the +proper place for continued execution in the current function. A +catch frame looks like this: +@begin [verbatim, group] +0 Pointer to current binding stack. +1 Pointer to current active frame. +2 Pointer to current function object. +3 Destination PC for a Throw. +4 Tag caught by this catch frame. +5 Pointer to previous catch frame. +@end [verbatim] +The conventions used to manipulate call and catch frames are described in +chapter @ref[Control-Conventions]. + +@section [Binding-Stack Format] +@index [Binding stack format] + +Each entry of the binding-stack consists of two boxed (32-bit) words. Pushed +first is a pointer to the symbol being bound. Pushed second is the symbol's +old value (any boxed item) that is to be restored when the binding stack is +popped. + +@chapter [Storage Management] +@index [Storage management] +@index [Garbage Collection] +@label [Alloc-Chapter] + +@index [Free-Storage pointer] +@index [Clean-Space pointer] +New objects are allocated from the lowest unused addresses within the specified +space. Each allocation call specifies how many words are wanted, and a +Free-Storage pointer is incremented by that amount. There is one of these +Free-Storage pointers for each space, and it points to the lowest free address +in the space. There is also a Clean-Space pointer associated with each space +that is used during garbage collection. These pointers are stored in a table +which is indexed by the type and space code. The +address of the Free-Storage pointer for a given space is +@begin[verbatim] + (+ alloc-table-base (lsh type 5) (lsh space 3)). +@end[verbatim] +The address of the Clean-Space pointer is +@begin[verbatim] + (+ alloc-table-base (lsh type 5) (lsh space 3) 4). +@end[verbatim] + +Common Lisp on the @value(DinkyMachine) uses a stop-and-copy garbage collector +to reclaim storage. The Collect-Garbage miscop performs a full GC. The +algorithm used is a degenerate form of Baker's incremental garbage collection +scheme. When the Collect-Garbage miscop is executed, the following +happens: +@begin[enumerate] +The current newspace becomes oldspace, and the current oldspace becomes +newspace. + +The newspace Free-Storage and Clean-Space pointers are initialized to point to +the beginning of their spaces. + +The objects pointed at by contents of all the registers containing Lisp objects +are transported if necessary. + +The control stack and binding stack are scavenged. + +Each static pointer space is scavenged. + +Each new dynamic space is scavenged. The scavenging of the dynamic spaces +continues until an entire pass through all of them does not result in anything +being transported. At this point, every live object is in newspace. +@end[enumerate] +A Lisp-level GC function returns the oldspace pages to Mach. + +@index [Transporter] +@section [The Transporter] +The transporter moves objects from oldspace to newspace. It is given an +address @i[A], which contains the object to be transported, @i[B]. If @i[B] is +an immediate object, a pointer into static space, a pointer into read-only +space, or a pointer into newspace, the transporter does nothing. + +If @i[B] is a pointer into oldspace, the object it points to must be +moved. It may, however, already have been moved. Fetch the first +word of @i[B], and call it @i[C]. If @i[C] is a GC-forwarding +pointer, we form a new pointer with the type code of @i[B] and the +low 27 bits of @i[C]. Write this into @i[A]. + +If @i[C] is not a GC-forwarding pointer, we must copy the object that +@i[B] points to. Allocate a new object of the same size in newspace, +and copy the contents. Replace @i[C] with a GC-forwarding pointer to +the new structure, and write the address of the new structure back +into @i[A]. + +Hash tables maintained with an EQ relation need special treatment by the +transporter. Whenever a G-Vector with subtype 2 or 3 is transported to +newspace, its subtype code is changed to 4. The Lisp-level hash-table +functions will see that the subtype code has changed, and re-hash the entries +before any access is made. + +@index [Scavenger] +@section [The Scavenger] The scavenger looks through an area of +pointers for pointers into oldspace, transporting the objects they +point to into newspace. The stacks and static spaces need to be +scavenged once, but the new dynamic spaces need to be scavenged +repeatedly, since new objects will be allocated while garbage +collection is in progress. To keep track of how much a dynamic space +has been scavenged, a Clean-Space pointer is maintained. The +Clean-Space pointer points to the next word to be scavenged. Each +call to the scavenger scavenges the area between the Clean-Space +pointer and the Free-Storage pointer. The Clean-Space pointer is +then set to the Free-Storage pointer. When all Clean-Space pointers +are equal to their Free-Storage pointers, GC is complete. + +To maintain (and create) locality of list structures, list space is +treated specially. When a list cell is transported, if the cdr points +to oldspace, it is immediately transported to newspace. This continues until +the end of the list is encountered or a non-oldspace pointer occurs in the cdr +position. This linearizes lists in the cdr direction which should +improve paging performance. + +@section [Purification] +@index [Purification] +@label [PURIFY] + +Garbage is created when the files that make up a CMU Common Lisp system are +loaded. Many functions are needed only for initialization and +bootstrapping (e.g. the ``one-shot'' functions produced by the compiler for +random forms between function definitions), and these can be thrown away +once a full system is built. Most of the functions in the system, however, +will be used after initialization. Rather than bend over backwards to make +the compiler dump some functions in read-only space and others in dynamic +space (which involves dumping their constants in the proper spaces, also), +@i[everything] is dumped into dynamic space. A purify miscop is provided +that does a garbage collection and moves accessible information in dynamic +space into read-only or static space. + +@chapter [Assembler Support Routines] +@label [Instr-Chapter] +@index [Assembler Support Routines] + +To support compiled Common Lisp code many hand coded assembler +language routines (miscops) are required. These routines accept +arguments in the three argument registers, the special miscop +argument register, and in a very few cases on the stack. The current +register assignments are: +@begin(Itemize, spread 0, spacing 1) +A0 contains the first argument. + +A1 contains the second argument. + +A2 contains the third argument. + +A3 contains the fourth argument. +@end(itemize) +The rest of the arguments are passed on the stack with the last +argument at the end of the stack. All arguments on the stack must be +popped off the stack by the miscop. All miscops return their +values in register A0. A few miscops return two or three values, +these are all placed in the argument registers. The main return +value is stored in register A0, the others in A1 and A2. The +compiler must generate code to use the multiple values correctly, +i.e., place the return values on the stack and put a values marker in +register A0 if multiple-values are wanted. Otherwise the compiler +can use the value(s) it needs and ignore the rest. NB: Most of the +miscops follow this scheme, however, a few do not. Any +discrepancies are explained in the description of particular +miscops. + +Several of the instructions described in the Perq Internal Design Document do +not have associated miscops, rather they have been code directly in-line. +Examples of these instructions include push, pop, bind, bind-null, many of the +predicates, and a few other instructions. Most of these instructions can be +performed in 4 or fewer @value(DinkyMachine) instructions and the overhead of +calling a miscop seemed overly expensive. Some instructions are encoded +in-line or as a miscop call depending on settings of compiler optimization +switches. If space is more important than speed, then some Perq instructions +are compiled as calls to out of line miscops rather than generating in-line +code. + +@section [Miscop Descriptions] +@label[macro-codes] + +There are 10 classes of miscops: allocation, stack manipulation, +list manipulation, symbol manipulation, array manipulation, type predicate, +arithmetic and logical, function call and return, +miscellaneous, and system hacking. + +@subsection [Allocation] +@instrsection +All non-immediate objects are allocated in the ``current allocation space,'' +which is dynamic space, static space, or read-only space. The current +allocation space is initially dynamic space, but can be changed by using the +Set-Allocation-Space miscop below. The current allocation space can be +determined by using the Get-Allocation-Space miscop. One usually wants to +change the allocation space around some section of code; an unwind protect +should be used to insure that the allocation space is restored to some safe +value. + +@begin(Description) +@index [Get-Allocation-Space] +Get-Allocation-Space (@i[])@\returns 0, 2, or 3 if the current allocation +space is dynamic, static, or read-only, respectively. + +@index [Set-Allocation-Space] +Set-Allocation-Space (@i[X])@\sets the current allocation space to dynamic, +static, or read-only if @i[X] is 0, 2, or 3 respectively. Returns @i[X]. + +@index [Alloc-Bit-Vector] +Alloc-Bit-Vector (Length)@\returns a new bit-vector @i[Length] bits long, +which is allocated in the current allocation space. @i[Length] must be a +positive fixnum. + +@index [Alloc-I-Vector] +Alloc-I-Vector (@i[Length A])@\returns a new I-Vector @i[Length] +bytes long, with the access code specified by @i[A]. @i[Length] and +@i[A] must be positive fixnums. + +@index [Alloc-String] +Alloc-String (@i[Length])@\ returns a new string @i[Length] characters long. +@i[Length] must be a fixnum. + +@index [Alloc-Bignum] +Alloc-Bignum (@i[Length])@\returns a new bignum @i[Length] 32-bit words long. +@i[Length] must be a fixnum. + +@index [Make-Complex] +Make-Complex (@i[Realpart Imagpart])@\returns a new complex number with the +specified @i[Realpart] and @i[Imagpart]. @i[Realpart] and @i[Imagpart] should +be the same type of non-complex number. + +@index [Make-Ratio] +Make-Ratio (@i[Numerator Denominator])@\returns a new ratio with the +specified @i[Numerator] and @i[Denominator]. @i[Numerator] and +@i[Denominator] should be integers. + +@index [Alloc-G-Vector] +Alloc-G-Vector (@i[Length Initial-Element])@\returns a new G-Vector +with @i[Length] elements initialized to @i[Initial-Element]. +@i[Length] should be a fixnum. + +@index [Static-Alloc-G-Vector] +Static-G-Vector (@i[Length Initial-Element])@\returns a new G-Vector in +static allocation space with @i[Length] elements initialized to +@i[Initial-Element]. + +@index [Vector] +Vector (@i[Elt@-[0] Elt@-[1] ... Elt@-[Length - 1] Length])@\returns a new +G-Vector containing the specified @i[Length] elements. @i[Length] should be a +fixnum and is passed in register A0. The rest of the arguments are passed on +the stack. + +@index [Alloc-Function] +Alloc-Function (@i[Length])@\returns a new function with @i[Length] elements. +@i[Length] should be a fixnum. + +@index [Alloc-Array] +Alloc-Array (@i[Length])@\returns a new array with @i[Length] elements. +@i[Length] should be a fixnum. + +@index [Alloc-Symbol] +Alloc-Symbol (@i[Print-Name])@\returns a new symbol with the print-name as +@i[Print-Name]. The value is initially Trap, the definition is Trap, +the property list and the package are initially NIL. The symbol is +not interned by this operation -- that is done in Lisp code. +@i[Print-Name] should be a simple-string. + +@index [Cons] +Cons (@i[Car Cdr])@\returns a new cons with the specified @i[Car] and @i[Cdr]. + +@index [List] +List (@i[Elt@-[0] Elt@-[1] ... Elt@-[CE - 1] Length])@\returns a new list +containing the @i[Length] elements. @i[Length] should be fixnum and is +passed in register NL0. The first three arguments are passed in A0, A1, and +A2. The rest of the arguments are passed on the stack. + +@index [List*] +List* (@i[Elt@-[0] Elt@-[1] ... Elt@-[CE - 1] Length])@\returns a list* formed +by the @i[Length-1] elements. The last element is placed in the cdr of the +last element of the new list formed. @i[Length] should be a fixnum and is +passed in register NL0. The first three arguments are passed in A0, A1, and +A2. The rest of the arguments are passed on the stack. + +@index[mv-list] +MV-List (@i[Elt@-<0> Elt@-<1> ... Elt@- Length])@\returns a list +formed from the elements, all of which are on the stack. @i[Length] is +passed in register A0. This miscop is invoked when multiple values from +a function call are formed into a list. +@end(Description) + +@subsection [Stack Manipulation] +@instrsection + +@begin(Description) +@index [Push] +Push (@i[E])@\pushes E on to the control stack. + +@index [Pop] +Pop (@i[E])@\pops the top item on the control stack into @i[E]. + +@index [NPop] +NPop (@i[N])@\If @i[N] is positive, @i[N] items are popped off of the stack. +If @i[N] is negative, NIL is pushed onto the stack -@i[N] times. @i[N] must be +a fixnum. + +@index [Bind-Null] +Bind-Null (@i[E])@\pushes @i[E] (which must be a symbol) and its current value +onto the binding stack, and sets the value of @i[E] to NIL. Returns NIL. + +@index [Bind] +Bind (Value Symbol)@\pushes @i[Symbol] (which must be a symbol) and its current +value onto the binding stack, and sets the value cell of @i[Symbol] to +@i[Value]. Returns @i[Symbol]. + +@index [Unbind] +Unbind (@i[N])@\undoes the top @i[N] bindings on the binding stack. +@end(Description) + +@subsection [List Manipulation] +@instrsection + +@begin(Description) +@index [Car] +@index [Cdr] +@index [Caar] +@index [Cadr] +@index [Cdar] +@index [Cddr] +Car, Cdr, Caar, Cadr, Cdar, Cddr (@i[E])@\returns the car, cdr, caar, cadr, +cdar, or cddr of @i[E] respectively. + +@index [Set-Cdr] +@index [Set-Cddr] +Set-Cdr, Set-Cddr (@i[E])@\The cdr or cddr of the contents of @i[E] is stored +in @i[E]. The contents of @i[E] should be either a list or NIL. + +@index [Set-Lpop] +Set-Lpop (@i[E])@\The car of the contents of @i[E] is returned; +the cdr of the contents of @i[E] is stored in @i[E]. The contents of @i[E] +should be a list or NIL. + +@index [Spread] +Spread (@i[E])@\pushes the elements of the list @i[E] onto the stack in +left-to-right order. + +@index [Replace-Car] +@index [Replace-Cdr] +Replace-Car, Replace-Cdr (@i[List Value])@\sets the car or cdr of the @i[List] +to @i[Value] and returns @i[Value]. + +@index [Endp] +Endp (X)@\sets the condition code eq bit to 1 if @i[X] is NIL, or 0 if @i[X] is +a cons cell. Otherwise an error is signalled. + +@index [Assoc] +@index [Assq] +Assoc, Assq (@i[List Item])@\returns the first cons in the association-list +@i[List] whose car is EQL to @i[Item]. If the = part of the EQL comparison +bugs out (and it can if the numbers are too complicated), a Lisp-level Assoc +function is called with the current cdr of the @i[List]. Assq returns the +first cons in the association-list @i[List] whose car is EQ to @i[Item]. + +@index [Member] +@index [Memq] Member, Memq (@i[List Item])@\returns the first cons in +the list @i[List] whose car is EQL to @i[Item]. If the = part of the +EQL comparison bugs out, a Lisp-level Member function is called with +the current cdr of the @i[List]. Memq returns the first cons in +@i[List] whose car is EQ to the @i[Item]. + +@index [GetF] + +GetF (@i[List Indicator Default])@\searches for the @i[Indicator] in +the list @i[List], cddring down as the Common Lisp form GetF would. +If @i[Indicator] is found, its associated value is returned, +otherwise @i[Default] is returned. +@end(Description) + +@subsection [Symbol Manipulation] +@instrsection + +Most of the symbol manipulation miscops are compiled in-line rather than +actual calls. + +@begin(Description) +@index [Get-Value] +Get-Value (@i[Symbol])@\returns the value of @i[Symbol] (which must be a +symbol). An error is signalled if @i[Symbol] is unbound. + +@index [Set-Value] +Set-Value (@i[Symbol Value])@\sets the value cell of the symbol @i[Symbol] to +@i[Value]. @i[Value] is returned. + +@index [Get-Definition] +Get-Definition (@i[Symbol])@\returns the definition of the symbol +@i[Symbol]. If @i[Symbol] is undefined, an error is signalled. + +@index [Set-Definition] +Set-Definition (@i[Symbol Definition])@\sets the definition of the symbol +@i[Symbol] to @i[Definition]. @i[Definition] is returned. + +@index [Get-Plist] +Get-Plist (@i[Symbol])@\returns the property list of the symbol @i[Symbol]. + +@index [Set-Plist] +Set-Plist (@i[Symbol Plist])@\sets the property +list of the symbol @i[Symbol] to +@i[Plist]. @i[Plist] is returned. + +@index [Get-Pname] +Get-Pname (@i[Symbol])@\returns the print name of the symbol @i[Symbol]. + +@index [Get-Package] +Get-Package (@i[Symbol])@\returns the package cell of the symbol @i[Symbol]. + +@index [Set-Package] +Set-Package (@i[Symbol Package])@\sets the package cell of the symbol +@i[Symbol] to @i[Package]. @i[Package] is returned. + +@index [Boundp] +Boundp (@i[Symbol])@\sets the eq condition code bit to 1 if the symbol +@i[Symbol] is bound; sets it to 0 otherwise. + +@index [FBoundp] +FBoundp (@i[Symbol])@\sets the eq condition code bit to 1 if the symbol +@i[Symbol] is defined; sets it to 0 otherwise. + +@index [Get] +Get (@i[Symbol] @i[Indicator] @i[Default])@\searches the property list of +@i[Symbol] for @i[Indicator] and returns the associated value. If +@i[Indicator] is not found, @i[Default] is returned. + +@index [Put] +Put (@i[Symbol] @i[Indicator] @i[Value])@\searches the property list of +@i[Symbol] for @i[Indicator] and replaces the associated value with @i[Value]. +If @i[Indicator] is not found, the @i[Indicator] @i[Value] pair are consed onto +the front of the property list. +@end(Description) + +@subsection [Array Manipulation] +@instrsection + +Common Lisp arrays have many manifestations in CMU Common Lisp. The CMU +Common Lisp data types Bit-Vector, Integer-Vector, String, General-Vector, +and Array are used to implement the collection of data types the Common +Lisp manual calls ``arrays.'' + +In the following miscop descriptions, ``simple-array'' means an array +implemented in CMU Common Lisp as a Bit-Vector, I-Vector, String, or +G-Vector. ``Complex-array'' means an array implemented as a CMU Common Lisp +Array object. ``Complex-bit-vector'' means a bit-vector implemented as a +CMU Common Lisp array; similar remarks apply for ``complex-string'' and so +forth. + +@begin(Description) +@index [Vector-Length] @index [G-Vector-Length] @index +[Simple-String-Length] @index [Simple-Bit-Vector-Length] Vector-Length +(@i[Vector])@\returns the length of the one-dimensional Common Lisp array +@i[Vector]. G-Vector-Length, Simple-String-Length, and +Simple-Bit-Vector-Length return the lengths of G-Vectors, CMU Common Lisp +strings, and CMU Common Lisp Bit-Vectors respectively. @i[Vector] should +be a vector of the appropriate type. + +@index [Get-Vector-Subtype] +Get-Vector-Subtype (@i[Vector])@\returns the subtype field of the vector +@i[Vector] as an integer. @i[Vector] should be a vector of some sort. + +@index [Set-Vector-Subtype] +Set-Vector-Subtype (@i[Vector A])@\sets the subtype field of the vector +@i[Vector] to @i[A], which must be a fixnum. + +@index [Get-Vector-Access-Code] +Get-Vector-Access-Code (@i[Vector])@\returns the access code of the I-Vector +(or Bit-Vector) @i[Vector] as a fixnum. + +@index [Shrink-Vector] +Shrink-Vector (@i[Vector Length])@\sets the length field and the +number-of-entries field of the vector @i[Vector] to @i[Length]. If the vector +contains Lisp objects, entries beyond the new end are set to Trap. +Returns the shortened vector. @i[Length] should be a fixnum. One cannot +shrink array headers or function headers. + +@index [Typed-Vref] +Typed-Vref (@i[A Vector I])@\returns the @i[I]'th element of the I-Vector +@i[Vector] by indexing into it as if its access-code were @i[A]. @i[A] and +@i[I] should be fixnums. + +@index [Typed-Vset] +Typed-Vset (@i[A Vector I Value])@\sets the @i[I]'th element of the I-Vector +@i[Vector] to @i[Value] indexing into @i[Vector] as if its access-code were +@i[A]. @i[A], @i[I], and @i[Value] should be fixnums. @i[Value] is returned. + +@index [Header-Length] +Header-Length (@i[Object])@\returns the number of Lisp objects in the header of +the function or array @i[Object]. This is used to find the number of +dimensions of an array or the number of constants in a function. + +@index [Header-Ref] +Header-Ref (@i[Object I])@\returns the @i[I]'th element of the function or +array header @i[Object]. @i[I] must be a fixnum. + +@index [Header-Set] +Header-Set (@i[Object I Value])@\sets the @i[I]'th element of the function of +array header @i[Object] to @i[Value], and pushes @i[Value]. @i[I] must be a +fixnum. +@end(Description) + +The names of the miscops used to reference and set elements of arrays are +based somewhat on the Common Lisp function names. The SVref, SBit, and SChar +miscops perform the same operation as their Common Lisp namesakes -- +referencing elements of simple-vectors, simple-bit-vectors, and simple-strings +respectively. Aref1 references any kind of one dimensional array. +The names of setting functions are derived by replacing ``ref'' with ``set'', +``char'' with ``charset'', and ``bit'' with ``bitset.'' + +@begin(Description) +@index [Aref1] +@index [SVref] +@index [SChar] +@index [SBit] +Aref1, SVref, SChar, SBit (@i[Array I])@\returns the @i[I]'th element of the +one-dimensional +array @i[Array]. SVref pushes an element of a G-Vector; SChar an element of a +string; Sbit an element of a Bit-Vector. @i[I] should be a fixnum. + +@index [Aset1] +@index [SVset] +@index [SCharset] +@index [SBitset] +Aset1, SVset, SCharset, SBitset (@i[Array I Value])@\sets the @i[I]'th element +of the one-dimensional +array @i[Array] to @i[Value]. SVset sets an element of a G-Vector; SCharset an +element of a string; SBitset an element of a Bit-Vector. @i[I] should be a +fixnum and @i[Value] is returned. + +@index [CAref2] +@index [CAref3] +CAref2, CAref3 (@i[Array I1 I2])@\returns the element (@i[I1], @i[I2]) of the +two-dimensional array @i[Array]. @i[I1] and @i[I2] should be +fixnums. CAref3 pushes the element (@i[I1], @i[I2], @i[I3]). + +@index [CAset2] +@index [CAset3] +CAset2, CAset3 (@i[Array I1 I2 Value]) @\sets the element (@i[I1], @i[I2]) of +the two-dimensional array @i[Array] to @i[Value] and returns @i[Value]. +@i[I1] and @i[I2] should be fixnums. CAset3 sets the element (@i[I1], @i[I2], +@i[I3]). + +@index [Bit-Bash] +Bit-Bash (@i[V1 V2 V3 Op])@\@i[V1], @i[V2], and @i[V3] should be bit-vectors +and @i[Op] should be a fixnum. The elements of the bit vector @i[V3] are +filled with the result of @i[Op]'ing the corresponding elements of @i[V1] and +@i[V2]. @i[Op] should be a Boole-style number (see the Boole miscop in +section @ref[Boole-Section]). +@end(Description) + +The rest of the miscops in this section implement special cases of sequence or +string operations. Where an operand is referred to as a string, it may +actually be an 8-bit I-Vector or system area pointer. + +@begin(Description) +@index [Byte-BLT] +Byte-BLT (@i[Src-String Src-Start Dst-String Dst-Start Dst-End])@\ +moves bytes from @i[Src-String] into @i[Dst-String] between @i[Dst-Start] +(inclusive) and @i[Dst-End] (exclusive). @i[Dst-Start] - @i[Dst-End] bytes are +moved. If the substrings specified overlap, ``the right thing happens,'' i.e. +all the characters are moved to the right place. This miscop corresponds +to the Common Lisp function REPLACE when the sequences are simple-strings. + +@index [Find-Character] +Find-Character (@i[String Start End Character])@\ +searches @i[String] for the @i[Character] from @i[Start] to @i[End]. If the +character is found, the corresponding index into @i[String] is returned, +otherwise NIL is returned. This miscop corresponds to the Common Lisp +function FIND when the sequence is a simple-string. + +@index [Find-Character-With-Attribute] +Find-Character-With-Attribute (@i[String Start End Table Mask])@\ +The codes of the characters of @i[String] from @i[Start] to @i[End] are used as +indices into the @i[Table], which is an I-Vector of 8-bit bytes. When the +number picked up from the table bitwise ANDed with @i[Mask] is non-zero, the +current index into the @i[String] is returned. + +@index [SXHash-Simple-String] +SXHash-Simple-String (@i[String Length])@\Computes the hash code of the first +@i[Length] characters of @i[String] and pushes it on the stack. This +corresponds to the Common Lisp function SXHASH when the object is a +simple-string. The @i[Length] operand can be Nil, in which case the length of +the string is calculated in assembler. +@end(Description) + +@subsection [Type Predicates] +@instrsection + +Many of the miscops described in this sub-section can be coded in-line rather +than as miscops. In particular, all the predicates on basic types are coded +in-line with default optimization settings in the compiler. Currently, all of +these predicates set the eq condition code bit to return an indication of +whether the predicate is true or false. This is so that the +@value(DinkyMachine) branch instructions can be used directly without having to +test for NIL. However, this only works if the value of the predicate is needed +for a branching decision. In the cases where the value is actually needed, T +or NIL is generated in-line according to whether the predicate is true or +false. At some point it might be worthwhile having two versions of these +predicates, one which sets the eq condition code bit, and one which returns T +or NIL. This is especially true if space becomes an issue. + +@begin(Description) +@index [Bit-Vector-P] +Bit-Vector-P (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is +a Common Lisp bit-vector or 0 if it is not. + +@index [Simple-Bit-Vector-P] +Simple-Bit-Vector-P (@i[Object])@\sets the eq condition code bit to 1 if +@i[Object] is a CMU Common Lisp bit-vector or 0 if it is not. + +@index [Simple-Integer-Vector-P] +Simple-Integer-Vector-P (@i[Object])@\sets the eq condition code bit to 1 +if @i[Object] is a CMU Common Lisp I-Vector or 0 if it is not. + +@index [StringP] +StringP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +Common Lisp string or 0 if it is not. + +@index [Simple-String-P] +Simple-String-P (@i[Object])@\sets the eq condition code bit to 1 if +@i[Object] is a CMU Common Lisp string or 0 if it is not. + +@index [BignumP] +BignumP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +bignum or 0 if it is not. + +@index [Long-Float-P] +Long-Float-P (@i[Object])@\sets the eq condition code bit to 1 if +@i[Object] is a long-float or 0 if it is not. + +@index [ComplexP] +ComplexP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +complex number or 0 if it is not. + +@index [RatioP] +RatioP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +ratio or 0 if it is not. + +@index [IntegerP] +IntegerP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +fixnum or bignum or 0 if it is not. + +@index [RationalP] +RationalP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +fixnum, bignum, or ratio or 0 if it is not. + +@index [FloatP] +FloatP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +short-float or long-float or 0 if it is not. + +@index [NumberP] +NumberP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +number or 0 if it is not. + +@index [General-Vector-P] +General-Vector-P (@i[Object])@\sets the eq condition code bit to 1 if +@i[Object] is a Common Lisp general vector or 0 if it is not. + +@index [Simple-Vector-P] +Simple-Vector-P (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] +is a CMU Common Lisp G-Vector or 0 if it is not. + +@index [Compiled-Function-P] +Compiled-Function-P (@i[Object])@\sets the eq condition code bit to 1 if +@i[Object] is a compiled function or 0 if it is not. + +@index [ArrayP] +ArrayP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +Common Lisp array or 0 if it is not. + +@index [VectorP] +VectorP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +Common Lisp vector of 0 if it is not. + +@index [Complex-Array-P] +Complex-Array-P (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] +is a CMU Common Lisp array or 0 if it is not. + +@index [SymbolP] +SymbolP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +symbol or 0 if it is not. + +@index [ListP] +ListP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a cons +or NIL or 0 if it is not. + +@index [ConsP] +ConsP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a cons +or 0 if it is not. + +@index [FixnumP] +FixnumP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is a +fixnum or 0 if it is not. + +@index [Single-Float-P] +Single-Float-P (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] +is a single-float or 0 if it is not. + +@index [CharacterP] +CharacterP (@i[Object])@\sets the eq condition code bit to 1 if @i[Object] is +a character or 0 if it is not. +@end(Description) + +@subsection [Arithmetic] +@instrsection + +@begin(Description) +@index [Integer-Length] +Integer-Length (@i[Object])@\returns the integer-length (as defined in the +Common Lisp manual) of the integer @i[Object]. + +@index [Logcount] +Logcount (@i[Object])@\returns the number of 1's if @i[object] is a +positive integer, the number of 0's if @i[object] is a negative integer, +and signals an error otherwise. + +@index [Float-Short] +Float-Short (@i[Object])@\returns a short-float corresponding to the number +@i[Object]. + +@index [Float-Long] +Float-Long (@i[Number])@\returns a long float formed by coercing @i[Number] to +a long float. This corresponds to the Common Lisp function Float when given a +long float as its second argument. + +@index [Realpart] +Realpart (@i[Number])@\returns the realpart of the @i[Number]. + +@index [Imagpart] +Imagpart (@i[Number])@\returns the imagpart of the @i[Number]. + +@index [Numerator] +Numerator (@i[Number])@\returns the numerator of the rational @i[Number]. + +@index [Denominator] +Denominator (@i[Number])@\returns the denominator of the rational @i[Number]. + +@index [Decode-Float] +Decode-Float (@i[Number])@\performs the Common Lisp Decode-Float function, +returning 3 values. + +@index [Scale-Float] +Scale-Float (@i[Number X])@\performs the Common Lisp Scale-Float function, +returning the result. + +@index[=] += (@i[X Y])@\sets the condition codes according to whether @i[X] is equal +to @i[Y]. Both @i[X] and @i[Y] must be numbers, otherwise an error is +signalled. If a rational is compared with a flonum, the rational is +converted to a flonum of the same type first. If a short flonum is compared +with a long flonum, the short flonum is converted to a long flonum. +Flonums must be exactly equal (after conversion) for the condition codes to +be set to equality. This miscop also deals with complex numbers. + +@index [Compare] +Compare (@i[X Y])@\sets the condition codes according to +whether @i[X] is less than, equal to, or greater than @i[Y]. @i[X] +and @i[Y] must be numbers. Conversions as described in = above are done +as necessary. This miscop replaces the < and > instructions on the Perq, +so that the branch on condition instructions can be used more +effectively. The value of < and > as defined for the Perq are +only generated if necessary, i.e., the result is saved. If @i[X] or @i[Y] +is a complex number, an error is signalled. + +@index [Truncate] +Truncate (@i[N X])@\performs the Common Lisp TRUNCATE operation. There are 3 +cases depending on @i[X]: +@Begin[Itemize] +If @i[X] is fixnum 1, return two items: a fixnum or bignum +representing the integer part of @i[N] (rounded toward 0), then either 0 if +@i[N] was already an integer or the fractional part of @i[N] represented as a +flonum or ratio with the same type as @i[N]. + +If @i[X] and @i[N] are both fixnums or bignums and @i[X] is not 1, divide +@i[N] by @i[X]. Return two items: the integer quotient (a fixnum or +bignum) and the integer remainder. + +If either @i[X] or @i[N] is a flonum or ratio, return a fixnum or bignum +quotient (the true quotient rounded toward 0), then a flonum or ratio +remainder. The type of the remainder is determined by the same type-coercion +rules as for +. The value of the remainder is equal to @i[N] - @i[X] * +@i[Quotient]. +@End[Itemize] +On the @value(DinkyMachine), the integer part is returned in register A0, and +the remainder in A1. + +@index [+] +@index [-] +@index [*] +@index [/] ++, -, *, / (@i[N X])@\returns @i[N] + @i[X]. -, *, and / are similar. + +@index [Fixnum*Fixnum] +@index [Fixnum/Fixnum] +Fixnum*Fixnum, Fixnum/Fixnum (@i[N X])@\returns @i[N] * @i[X], where +both @i[N] and @i[X] are fixnums. Fixnum/ is similar. + +@index [1+] +1+ (@i[E])@\returns @i[E] + 1. + +@index [1-] +1- (@i[E])@\returns @i[E] - 1. + +@index [Negate] +Negate (@i[N])@\returns -@i[N]. + +@index [Abs] +Abs (@i[N])@\returns |@i[N]|. + +@index [GCD] +GCD (@i[N X])@\returns the greatest common divisor of the integers @i[N] and @i[X]. + +@index [Logand] +@index [Logior] +@index [Logxor] +Logand (@i[N X])@\returns the bitwise and of the integers @i[N] and @i[X]. +Logior and Logxor are analogous. + +@index [Lognot] +Lognot (@i[N])@\returns the bitwise complement of @i[N]. + +@index [Boole] +@label [Boole-Section] +Boole (@i[Op X Y])@\performs the Common Lisp Boole operation @i[Op] on @i[X], +and @i[Y]. The Boole constants for CMU Common Lisp are these: +@begin [verbatim, group] + boole-clr 0 + boole-set 1 + boole-1 2 + boole-2 3 + boole-c1 4 + boole-c2 5 + boole-and 6 + boole-ior 7 + boole-xor 8 + boole-eqv 9 + boole-nand 10 + boole-nor 11 + boole-andc1 12 + boole-andc2 13 + boole-orc1 14 + boole-orc2 15 +@end [verbatim] + +@index [Ash] +Ash (@i[N X])@\performs the Common Lisp ASH operation on @i[N] and @i[X]. + +@index [Ldb] +Ldb (@i[S P N])@\All args are integers; @i[S] and @i[P] are non-negative. +Performs the Common Lisp LDB operation with @i[S] and @i[P] being the size and +position of the byte specifier. + +@index [Mask-Field] +Mask-Field (@i[S P N])@\performs the Common Lisp Mask-Field operation with +@i[S] and @i[P] being the size and position of the byte specifier. + +@index [Dpb] +Dpb (@i[V S P N])@\performs the Common Lisp DPB operation with @i[S] and @i[P] +being the size and position of the byte specifier. + +@index [Deposit-Field] +Deposit-Field (@i[V S P N])@\performs the Common Lisp Deposit-Field operation +with @i[S] and @i[P] as the size and position of the byte specifier. + +@index [Lsh] +Lsh (@i[N X])@\returns a fixnum that is @i[N] shifted left by @i[X] bits, with +0's shifted in on the right. If @i[X] is negative, @i[N] is shifted to the +right with 0's coming in on the left. Both @i[N] and @i[X] should be fixnums. + +@index [Logldb] +Logldb (@i[S P N])@\All args are fixnums. @i[S] and @i[P] specify a ``byte'' +or bit-field of any length within @i[N]. This is extracted and is returned +right-justified as a fixnum. @i[S] is the length of the field in bits; @i[P] +is the number of bits from the right of @i[N] to the beginning of the +specified field. @i[P] = 0 means that the field starts at bit 0 of @i[N], and +so on. It is an error if the specified field is not entirely within the 26 +bits of @i[N] + +@index [Logdpb] +Logdpb (@i[V S P N])@\All args are fixnums. Returns a number equal to @i[N], +but with the field specified by @i[P] and @i[S] replaced by the @i[S] low-order +bits of @i[V]. It is an error if the field does not fit into the 26 bits of +@i[N]. + +@index[Sin]@index[Cos]@index[Tan]@index[Atan] +Sin(@i[X]), Cos(@i[X]), Tan(@i[X]), and Atan(@i[X])@\accept a single number +@i[X] as argument and return the sine, cosine, tangent, and arctangent of +the number respectively. These miscops take advantage of the hardware +support provided on the IBM RT PC if it is available, otherwise they escape +to Lisp code to calculate the appropriate result. + +@index[Log] +Log(@i[X])@\returns the natural log of the number @i[X]. This miscop uses +the hardware operation if it is available, otherwise it escapes to Lisp +code to calculate the result. + +@index[Exp] +Exp(@i[X])@\returns e raised to the power @i[X]. This miscop uses the +hardware operation if it is available, otherwise it escapes to Lisp code to +calculate the result. + +@index[Sqrt] +Sqrt(@i[X])@\returns the square root of @i[X]. This miscop uses the +hardware operation if it is available, otherwise it escapes to Lisp code to +calculate the result. +@end(Description) + +@subsection [Branching] +All branching is done with @value(DinkyMachine) branch instructions. +Instructions are generated to set the condition code bits appropriately, and +a branch which tests the appropriate condition code bit is generated. + +@subsection [Function Call and Return] +@instrsection + +@begin(Description) +@index [Call] +Call()@\A call frame for a function is opened. This is explained in +more detail in the next chapter. + +@index [Call-0] +Call-0 (@i[F])@\@i[F] must be an executable function, but is a +function of 0 arguments. Thus, there is no need to collect arguments. The +call frame is opened and activated in a single miscop. + +@index [Call-Multiple] +Call-Multiple ()@\Just like a Call miscop, but it marks the frame +to indicate that multiple values will be accepted. See +section @ref[Multi]. + +@index[Set-Up-Apply-Args] +Set-Up-Apply-Args ()@\is called to handle the last argument of a +function called by apply. All the other arguments will have been +properly set up by this time. Set-up-apply-args places the values of +the list passed as the last argument to apply in their proper +locations, whether they belong in argument registers or on the stack. +It updates the NArgs register with the actual count of the arguments +being passed to the function. When Set-up-apply-args returns, all the +arguments to the function being applied are in their correct +locations, and the function can be invoked normally. + +@index[Start-Call-Interpreter] +Start-Call-Interpreter (@i[NArgs])@\is called from the interpreter to +start a function call. It accepts the number of arguments that are +pushed on the stack in register A0. Just below the arguments is the +function to call; just below the function is the area to store the +preserved registers. This miscop sets up the argument registers +correctly, moves any other arguments down on the stack to their +proper place, and invokes the function. + +@index[Invoke1] +Invoke1 (@i[Function] @i[Argument])@\is similar to Start-Call-Interpreter, +but is simpler, since the @i[Function] is being called with only a +single @i[Argument]. + +@index[Invoke1*] +Invoke1* (@i[Function] @i[Argument])@\is similar to Invoke1, but the +@i[Function] being called is called for one value, rather than multiple ones. + +@index [Start-call-mc] +Start-call-mc ()@\is called when the compiler generates code for the +form multiple-value-call. Register A0 contains the function to be +called, A1 contains a 0 if the call if for a single value, and 1 +otherwise, NArgs contains the number of arguments that are stored on +the stack. The argument registers are set up correctly, and the +excess values moved down on the stack if necessary. Finally, the +function is actually invoked. + +@index [Push-Last] +Push-Last ()@\closes the currently open call frame, and initiates a function +call. + +@index [Return] +Return (@i[X])@\Return from the current function call. After the current +frame is popped off the stack, @i[X] is returned in register A0 as the result +Being returned. See section @ref[Return] for more details. + +@index [Return-From] +Return-From (@i[X] @i[F])@\is similar to Return, except it accepts the frame +to return from as an additional argument. + +@index [Return-1-Value-Any-Bind] +Return-1-Value-Any-Bind (@i[X])@\is similar to return, except only +one value is returned. Any number of bindings are undone during the +return operation. + +@index [Return-Mult-Value-0-Bind] +Return-Mult-Value-0-Bind (@i[X])@\is similar to return, except multiple values +may be returned, but the binding stack does not have to be popped. + +@index [Link-Address-Fixup] +Link-Address-Fixup (@i[Symbol NArgs Code-Vector Offset])@\finds the +correct link table entry for @i[Symbol] with @i[NArgs] (@i[NArgs] +specifies the fixed number of arguments and a flag if more may be +passed). It smashes the @i[Code-Vector] at @i[Offset] to generate +code to point at the absolute address of the link table entry. + +@index [Miscop-Fixup] +Miscop-Fixup (@i[Code-Vector Offset Index])@\smashes @i[Code-Vector] at +@i[Offset] with the correct value for the miscop specified by @i[Index] in a +transfer vector of all the miscops. + +@index [Make-Compiled-Closure] +Make-Compiled-Closure (@i[env fcn offset])@\returns a new function object +that is a copy of the function object @i[fcn] which has the @i[env] +information stored at @i[offset]. Compiled lexical closures are now +represented as real function objects rather than as lists. This miscop is +necessary to support this change. + +@index [Reset-link-table] +Reset-link-table (@i[function])@\resets all the link table entries for +@i[function] to the default action. This is necessary because Portable +Commonloops updates generic function objects by copying new information +into the function object. The link table must be updated to reflect this +or the wrong function will be called. + +@index[Interrupt-Handler] +@begin[Multiple] +Interrupt-Handler (@i[Signal Code Signal-Context])@\gets the first +indication that a Unix signal has occurred. This miscop does not follow +the normal Lisp calling conventions at all. Instead it follows the +standard IBM RT PC calling conventions for C or other algorithmic +languages. On entry the registers are as follows: +@begin(Description) +R0@\Pointer to C data area for Interrupt-Handler. Currently this data area +only holds a pointer to the entry point for Interrupt-Handler and nothing +else. + +R1@\Pointer to a C stack that contains information about the signal. + +R2@\Contains the @i[Signal] number that caused the interrupt to happen. + +R3@\Contains the @i[Code] that further specifies what caused the interrupt +(if necessary). + +R4@\Contains a pointer to the @i[signal-context] which contains +information about where the interrupt occurred, the saved registers, etc. + +R5-R14@\Contain unknown values. + +R15@\is the return PC which will return from the interrupt handler and +restart the computation. +@end(Description) +Interrupt-Handler determines whether it is safe to take the interrupt now, +i.e., it is executing in Lisp code, C code, or an interruptible miscop. An +interruptible miscop is one that has been specially written to make sure +that it is safe to interrupt it at any point and is possible that it will +never return of its own accord (e.g., length which could be passed a +circular list, some of the system call miscops, etc.). If it is safe to +take the interrupt, the signal-context is modified so that control will +transfer to the miscop interrupt-routine when the interrupt-handler returns +normally (i.e., after the kernel has done the necessary bookkeeping). If +it is unsafe to take the interrupt (i.e., it is executing in an +non-interruptible miscop), then the return PC of the miscop is modified to +be interrupt-routine and interrupt-handler returns to the kernel. In +either case interrupts are disabled and information is stored in a global +Lisp data area, so that the interrupt-routine miscop can retrieve the +important information about the interrupt. +@end[Multiple] + +Interrupt-Routine ()@\gets control when it is safe to take an interrupt. +It saves the current state of the computation on the appropriate stack (on +the C stack if it was executing in C or on the Lisp stack if in Lisp) +including all the registers, some control information specifying whether +the computation was in C code, Lisp code, whether it should form a PC in +register R15. When a PC has to be formed in R15, R14 will contain a pointer +to the active function and R15 will contain an index into the code vector +associated with the active function. Reforming the PC is necessary so +it is possible to restart a computation even after a garbage collection +may have moved the function. Once this information is stored, +interrupt-routine invokes the Lisp function %sp-software-interrupt-routine +which moves the processing of the interrupt to Lisp code. + +@index [Break-Return] +Break-Return (@i[])@\returns from a function called by the +interrupt-routine miscop. The only function that should ever do this is +%sp-software-interrupt-routine. This miscop expects the stack to be in a +format that is generated during an interrupt and should not be used for +anything else. + +@index [Catch] +Catch (@i[Tag PC])@\builds a catch frame. @i[Tag] is the tag caught by this +catch frame, @i[PC] is a saved-format PC (i.e., an index into the current code +vector). See section @ref[Catch] for details. + +@index [Catch-Multiple] +Catch-Multiple (@i[Tag PC])@\builds a multiple-value catch frame. @i[Tag] is +the tag caught by this catch frame, and @i[PC] is a saved-format PC. See +section @ref[Catch] for details. + +@index [Catch-All] +Catch-All (@i[PC])@\builds a catch frame whose tag is the special Catch-All +object. @i[PC] is the saved-format PC, which is the address to branch to if +this frame is thrown through. See section @ref[Catch] for details. + +@index [Throw] +Throw (@i[X Tag])@\@i[Tag] is the throw-tag, normally a symbol. @i[X] is the +value to be returned. See section @ref[Catch] for a description of how this +miscop works. + +@index[Rest-Entry-0]@index[Rest-Entry-1]@index[Rest-Entry-2]@index[Rest-Entry] +Rest-Entry-0, Rest-Entry-1, Rest-Entry-2, Rest-Entry@\are miscops +that do the processing for a function at its &rest entry point. +Rest-Entry-@i[i] are miscops that are invoked by functions that have +0, 1, or 2 arguments before the &rest argument. Rest-entry is +invoked for all other cases, and is passed an additional argument in +A3 which is the number of non-&rest arguments. These miscops form +the &rest arg list and set up all the registers to have the +appropriate values. In particular, the non-&rest arguments are copied +into preserved registers, and the &rest arg list is built and stored +in the appropriate preserved register or on the stack as appropriate. + +@index[Call-Foreign] +Call-Foreign (@i[C-Function Arguments NArgs])@\establishes the C +environment so that C code can be called correctly. @i[C-Function] is a +pointer to the data area for a C function, the first word of which is a +pointer to the entry point of the C function. @i[Arguments] is a block of +storage that contains the @i[NArgs] arguments to be passed to the C +function. The first four of these arguments are passed in registers R2 +through R5 respectively, the rest are moved onto the C stack in the proper +location. When the C function returns, Call-Foreign restores the Lisp +environment and returns as its value the integer in R2. + +@index[Call-Lisp] +Call-Lisp (@i[Arg@-<1> ... Arg@-<2>])@\is a Lisp miscop that gets control +when a C function needs to call a Lisp function. Lisp provides a mechanism +for setting up an object that looks like a C procedure pointer. The code +pointer in this object always points at Call-Lisp. Additional data in this +procedure pointer is the Lisp function to call and the number of arguments +that it should be called with. Call-Lisp restores the Lisp environment, +saves the state of the C computation, moves the C arguments into the +correct places for a call to a Lisp function and then invokes the special +Lisp function call-lisp-from-c. This Lisp function actually invokes the +correct Lisp function. Call-Lisp never regains control. + +@index[Return-To-C] +Return-To-C (@i[C-Stack-Pointer Value])@\is used in the +function call-lisp-from-c to return control to C from a Lisp function +called by C. @i[C-Stack-Pointer] is the C stack pointer when the call-lisp +miscop got control. The C stack pointer argument is used to restore the C +environment to what it was at the time the call to Lisp was made. +@i[Value] is the value returned from Lisp and is passed back to C in +register R2. Currently, it is not possible to return other than a single +32 bit quantity. + +@index[Reset-C-Stack] +Reset-C-Stack ()@\is invoked when a Lisp function called by C throws out +past where it should return to C. Reset-C-Stack restores the C stack to +what it was before the original call to C happened. This is so that in the +future, the C stack will not contain any garbage that should not be there. + +@index[Set-C-Procedure-Pointer] +Set-C-Procedure-Pointer (@i[Sap] @i[I] @I[Proc])@\sets the @i[I/2]'th +element of @i[sap] to be the data part of the statically allocated g-vector +@i[Proc]. This is used to set up a C procedure argument in the argument +block that is passed to call-foreign. + +@end(Description) + +@subsection [Miscellaneous] +@instrsection + +@begin(Description) +@index [Eq] +Eq (@i[X Y])@\sets the eq condition code bit to 1 if @i[X] and @i[Y] are the +same object, 0 otherwise. + +@index [Eql] +Eql (@i[X Y])@\sets the eq condition code bit to 1 if @i[X] and @i[Y] are the +same object or if +@i[X] and @i[Y] are numbers of the same type with the same value, 0 otherwise. + +@index [Make-Predicate] +Make-Predicate (@i[X])@\returns NIL if @i[X] is NIL or T if it is not. + +@index [Not-Predicate] +Not-Predicate (@i[X])@\returns T if @i[X] is NIL or NIL if it is not. + +@index [Values-To-N] +Values-To-N (@i[V])@\@i[V] must be a Values-Marker. Returns the number +of values indicated in the low 24 bits of @i[V] as a fixnum. + +@index [N-To-Values] +N-To-Values (@i[N])@\@i[N] is a fixnum. Returns a Values-Marker with the +same low-order 24 bits as @i[N]. + +@index [Force-Values] +Force-Values (@i[VM])@\If the @i[VM] is a Values-Marker, do +nothing; if not, push @i[VM] and return a Values-Marker 1. + +@index [Flush-Values] +Flush-Values (@i[])@\is a no-op for the @value(DinkyMachine), since the only +time that a Flush-Values miscop is generated is in some well-defined cases +where all the values are wanted on the stack. +@end(Description) + +@subsection [System Hacking] +@label [System-Hacking-Instructions] +@instrsection + +@begin(Description) +@index [Get-Type] +Get-Type (@i[Object])@\returns the five type bits of the @i[Object] as a +fixnum. + +@index [Get-Space] +Get-Space (@i[Object])@\returns the two space bits of @i[Object] as a +fixnum. + +@index [Make-Immediate-Type] +Make-Immediate-Type (@i[X A])@\returns an object whose type bits are the +integer @i[A] and whose other bits come from the immediate object or pointer +@i[X]. @i[A] should be an immediate type code. + +@index [8bit-System-Ref] +8bit-System-Ref (@i[X I])@\@i[X] must be a system area pointer, returns +the @i[I]'th byte of @i[X], indexing into @i[X] directly. @i[I] +must be a fixnum. + +@index [8bit-System-Set] +8bit-System-Set (@i[X I V])@\@i[X] must be a system area pointer, sets the +@i[I]'th element of @i[X] to @i[V], indexing into @i[X] directly. + +@index [16bit-System-Ref] +16bit-System-Ref (@i[X I])@\@i[X] must be a system area pointer, returns the +@i[I]'th 16-bit word of @i[X], indexing into @i[X] directly. + +@index [Signed-16bit-System-Ref] +Signed-16bit-System-Ref (@i[X I])@\@i[X] must be a system area pointer, +returns the @i[I]'th 16-bit word of @i[X] extending the high order bit as +the sign bit. + +@Index [16bit-System-Set] +16bit-System-Set (@i[X I V])@\@i[X] must be a system area pointer, sets the +@i[I]'th element of @i[X] to @i[V], indexing into @i[X] directly. + +@Index [Signed-32bit-System-Ref] +Signed-32bit-System-Ref (@i[X I])@\@i[X] must be a system area pointer and +@i[I] an even fixnum, returns the @i[I]/2'th 32 bit word as a signed +quantity. + +@Index [Unsigned-32bit-System-Ref] +Unsigned-32bit-System-Ref (@i[X I])@\@i[X] must be a system area pointer and +@i[I] an even fixnum, returns the @i[I]/2'th 32 bit word as an unsigned +quantity. + +@Index [Signed-32bit-System-Set] +Signed-32bit-System-Set (@i[X I V])@\@i[X] must be a system area pointer, +@i[I] an even fixnum, and @i[V] an integer, sets the @i[I]/2'th element of +@i[X] to @i[V]. + +@index[Sap-System-Ref] +Sap-System-Ref (@i[X I])@\@i[X] must be a system area pointer and @i[I] and +even fixnum, returns the @i[I]/2'th element of @i[X] as a system area +pointer. + +@index[Sap-System-Set] +Sap-System-Set (@i[X I V])@\@i[X] and @i[V] must be a system area pointers +and @i[I] an even fixnum, sets the @i[I]/2'th element of @i[X] to @i[V]. + +@index[Pointer-System-Set] +Pointer-System-Set (@i[X I])@\@i[X] must be a system area pointer, @i[I] an +even fixnum, and @i[V] a pointer (either system area pointer or Lisp +pointer), sets the @i[I]/2'th element of @i[X] to the pointer @i[V]. If +the pointer is a Lisp pointer, the pointer stored is to the first word of +data (i.e., the header word(s) are bypassed). + +@index[Sap-Int] +Sap-Int (@i[X])@\@i[X] should be a system area pointer, returns a Lisp +integer containing the system area pointer. This miscop is useful when it +is necessary to do arithmetic on system area pointers. + +@index[Int-Sap] +Int-Sap (@i[X])@\@i[X] should be an integer (fixnum or bignum), returns a +system area pointer. This miscop performs the inverse operation of sap-int. + +@index[Check-<=] +Check-<= (@i[X] @i[Y])@\checks to make sure that @i[X] is less than or +equal to @i[Y]. If not, then check-<= signals an error, otherwise it just +returns. + +@index [Collect-Garbage] +Collect-Garbage (@i[])@\causes a stop-and-copy GC to be performed. + +@index [Purify] +Purify (@i[])@\is similar to collect-garbage, except it copies Lisp objects +into static or read-only space. This miscop needs Lisp level code to get +the process started by putting some root structures into the correct space. + +@index [Newspace-Bit] +Newspace-Bit (@i[])@\returns 0 if newspace is currently space 0 or 1 if it is +1. + +@index [Save] +Save (@i[*current-alien-free-pointer*] @i[Checksum] @I[memory])@\Save takes +a snap short of the current state of the Lisp computation. The value of +the symbol *Current-alien-free-pointer* must be passed to save, so that it +can save the static alien data structures. The parameter @i[checksum] +specifies whether a checksum should be generated for the saved image. +Currently, this parameter is ignored and no checksum is generated. The +parameter @i[memory] should be be a pointer to a block of memory where the +saved core image will be stored. Save returns the size of the core image +generated. + +@index [Syscall0] +@index [Syscall1] +@index [Syscall2] +@index [Syscall3] +@index [Syscall4] +@index [Syscall] +Syscall0 Syscall1 Syscall2 Syscall3 Syscall4 Syscall (@i[number] +@i[arg@-<1> ... arg@-])@\is for making syscalls to the Mach kernel. The +argument @i[number] should be the number of the syscall. Syscall0 accepts +no arguments to the syscall; syscall1 accepts one argument to the syscall, +etc. Syscall accepts five or more arguments to the syscall. + +@index[Unix-write] +Unix-Write (@i[fd buffer offset length])@\performs a Unix write syscall to +the file descriptor @i[fd]. @i[Buffer] should contain the data to be +written; @i[Offset] should be an offset into buffer from which to start +writing; and @i[length] is the number of bytes of data to write. + +@index[Unix-fork] +Unix-Fork ()@\performs a Unix fork operation returning one or two values. +If an error occurred, the value -1 and the error code is returned. If no +error occurred, 0 is returned in the new process and the process id of the +child process is returned in the parent process. + +@index [Arg-In-Frame] Arg-In-Frame (@i[N F])@\@i[N] is a fixnum, @i[F] is a +control stack pointer as returned by the Active-Call-Frame miscop. It +returns the item in slot @i[N] of the args-and-locals area of call frame +@i[F]. + +@index [Active-Call-Frame] +Active-Call-Frame (@i[])@\returns a control-stack pointer to the start of the +currently active call frame. This will be of type Control-Stack-Pointer. + +@index [Active-Catch-Frame] +Active-Catch-Frame (@i[])@\returns the control-stack pointer to the start of +the currently active catch frame. This is Nil if there is no active catch. + +@index [Set-Call-Frame] +Set-Call-Frame (@i[P])@\@i[P] must be a control stack pointer. This becomes +the current active call frame pointer. + +@index [Current-Stack-Pointer] +Current-Stack-Pointer (@i[])@\returns the Control-Stack-Pointer that points +to the current top of the stack (before the result of this operation is +pushed). Note: by definition, this points to the +to the last thing pushed. + +@index [Current-Binding-Pointer] +Current-Binding-Pointer (@i[])@\returns a Binding-Stack-Pointer that points +to the first word above the current top of the binding stack. + +@index [Read-Control-Stack] +Read-Control-Stack (@i[F])@\@i[F] must be a control stack pointer. Returns +the Lisp object that resides at this location. If the addressed object is +totally outside the current stack, this is an error. + +@index [Write-Control-Stack] +Write-Control-Stack (@i[F V])@\@i[F] is a stack pointer, @i[V] is any Lisp +object. Writes @i[V] into the location addressed. If the addressed cell is +totally outside the current stack, this is an error. Obviously, this should +only be used by carefully written and debugged system code, since you can +destroy the world by using this miscop. + +@index [Read-Binding-Stack] +Read-Binding-Stack (@i[B])@\@i[B] must be a binding stack pointer. Reads and +returns the Lisp object at this location. An error if the location specified +is outside the current binding stack. + +@index [Write-Binding-Stack] +Write-Binding-Stack (@i[B V])@\@i[B] must be a binding stack pointer. Writes +@i[V] into the specified location. An error if the location specified is +outside the current binding stack. +@end(Description) + +@chapter [Control Conventions] +@label [Control-Conventions] +@index [Hairy stuff] + +@section [Function Calls] +@index [Call] +@index [Call-0] +@index [Call-Multiple] + +On the Perq function calling is done by micro-coded instructions. The +instructions perform a large number of operations, including determining +whether the function being called is compiled or interpreted, determining that +a legal number of arguments are passed, and branching to the correct entry +point in the function. To do all this on the @value(DinkyMachine) would +involve a large amount of computation. In the general case, it is necessary to +do all this, but in some common cases, it is possible to short circuit most of +this work. + +To perform a function call in the general case, the following steps occur: +@begin(Enumerate) + +Allocate space on the control stack for the fix-sized part of a call +frame. This space will be used to store all the registers that must +be preserved across a function call. + +Arguments to the function are now evaluated. The first three +arguments are stored in the argument registers A0, A1, and A2. The +rest of the arguments are stored on the stack as they are evaluated. +Note that during the evaluation of arguments, the argument registers +may be used and may have to be stored in local variables and restored +just before the called function is invoked. + +Load R0 with the argument count. + +Load the PC register with the offset into the current code vector of +the place to return to when the function call is complete. + +If this call is for multiple values, mark the frame as accepting +multiple values, by making the fixnum offset above negative by oring +in the negative fixnum type code. + +Store all the registers that must be preserved over the function call in the +current frame. +@end(Enumerate) + +At this point, all the arguments are set up and all the registers have been +saved. All the code to this point is done inline. If the object being called +as a function is a symbol, we get the definition from the definition cell of +the symbol. If this definition is the trap object, an undefined symbol error +is generated. The function calling mechanism diverges at this point depending +on the type of function being called, i.e., whether it is a compiled function +object or a list. + +If we have a compiled function object, the following steps are performed (this +code is out of line): +@begin(Enumerate) +Load the active function register with a pointer to the compiled function +object. + +The active frame register is set to the start of the current frame. + +Note the number of arguments evaluated. Let this be K. The correct +entry point in the called function's code vector must be computed as +a function of K and the number of arguments the called function +wants: +@begin(Enumerate, spread 0, spacing 1) +If K < minimum number of arguments, signal an error. + +If K > maximum number of arguments and there is no &rest argument, +signal an error. + +If K > maximum number of arguments and there is a &rest argument, +start at offset 0 in the code vector. This entry point must collect +the excess arguments into a list and leave the &rest argument in the +appropriate argument register or on the stack as appropriate. + +If K is between the minimum and maximum arguments (inclusive), get +the starting offset from the appropriate slot of the called +function's function object. This is stored as a fixnum in slot K - +MIN + 6 of the function object. +@end(Enumerate) + +Load one of the Non-Lisp temporary registers with the address of the +code vector and add in the offset calculated above. Then do a branch +register instruction with this register as the operand. The called +function is now executing at the appropriate place. +@end(enumerate) + +If the function being called is a list, %SP-Internal-Apply must be called to +interpret the function with the given arguments. Proceed as follows: +@begin(Enumerate) +Note the number of arguments evaluated for the current open frame (call this N) +and the frame pointer for the frame (call it F). Also remember the lambda +expression in this frame (call it L). + +Load the active function register with the list L. + +Load the PC register with 0. + +Allocate a frame on the control stack for the call to %SP-Internal-Apply. + +Move the contents of the argument registers into the local registers L0, L1, +and L2 respectively. + +Store all the preserved registers in the frame. + +Place N, F, and L into argument registers A0, A1, and A2 respectively. + +Do the equivalent of a start call on %SP-Internal-Apply. +@end(Enumerate) %SP-Internal-Apply, a function of three arguments, +now evaluates the call to the lambda-expression or interpreted +lexical closure L, obtaining the arguments from the frame pointed to +by F. The first three arguments must be obtained from the frame that +%SP-Internal-Apply runs in, since they are stored in its stack frame +and not on the stack as the rest of the arguments are. Prior to +returning %SP-Internal-Apply sets the Active-Frame register to F, so +that it returns from frame F. + +The above is the default calling mechanism. However, much of the +overhead can be reduced. Most of the overhead is incurred by having +to check the legality of the function call everytime the function is +called. In many situations where the function being called is a +symbol, this checking can be done only once per call site by +introducing a data structure called a link table. The one exception +to this rule is when the function apply is used with a symbol. In +this situation, the argument count checks are still necessary, but +checking for whether the function is a list or compiled function +object can be bypassed. + +The link table is a hash table whose key is based on the name of the +function, the number of arguments supplied to the call and a flag +specifying whether the call is done through apply or not. Each entry +of the link table consists of two words: +@begin(Enumerate) +The address of the function object associated with the symbol being +called. This is here, so that double indirection is not needed to +access the function object which must be loaded into the active +function register. Initially, the symbol is stored in this slot. + +The address of the instruction in the function being called to start +executing when this table entry is used. Initially, this points to +an out of line routine that checks the legality of the call and +calculates the correct place to jump to in the called function. This +out of line routine replaces the contents of this word with the +correct address it calculated. In the case when the call is caused +by apply, this will often be an out of line routine that checks the +argument count and calculates where to jump. In the case where the +called function accepts &rest arguments and the minimum number of +arguments passed is guaranteed to be greater than the maximum number +of arguments, then a direct branch to the &rest arg entry point is +made. +@end(Enumerate) + +When a compiled file is loaded into the lisp environment, all the +entries for the newly loaded functions will be set to an out of line +routine mentioned above. Also, during a garbage collection the +entries in this table must be updated when a function object for a +symbol is moved. + +The @value(DinkyMachine) code to perform a function call using the link table +becomes: +@begin(Example) + cal CS,CS,%Frame-Size ; Alloc. space on control st. + + + + cau NL1,0,high-half-word(lte(function nargs flag)) + oil NL1,0,low-half-word(lte(function nargs flag)) + cal PC,0,return-tag ; Offset into code vector. + + stm L0,CS,-(%Frame-Size-4) ; Save preserved regs. + lm AF,NL1,0 ; Link table entry contents. + bnbrx pz,R15 ; Branch to called routine. + cal FP,CS,-(%Frame-Size-4) ; Get pointer to frame. +return-tag: +@end(Example) +The first two instructions after the arguments are evaled get the +address of the link table entry into a register. The two 16-bit half +word entries are filled in at load time. The rest of the +instructions should be fairly straight forward. + +@section(Returning from a Function Call) +@label(Return) +@index(Return) + +Returning from a function call on the Perq is done by a micro-coded +instruction. On the @value(DinkyMachine), return has to do the following: +@begin(enumerate) +Pop the binding stack back to the binding stack pointer stored in the frame +we're returning from. For each symbol/value pair popped of the binding stack, +restore that value for the symbol. + +Save the current value of the frame pointer in a temporary registers. This +will be used to restore the control stack pointer at the end. + +Restore all the registers that are preserved across a function call. + +Get a pointer to the code vector for the function we're returning to. This is +retrieved from the code slot of what is now the active function. + +Make sure the relative PC (which is now in a register) is positive and add it +to the code vector pointer above, giving the address of the instruction to +return to. + +If the function is returning multiple values do a block transfer of all the +return values down over the stack frame just released, i.e., the first return +value should be stored where the temporarily saved frame pointer points to. +In effect the return values can be pushed onto the stack using the saved frame +pointer above as a stack pointer that is incremented everytime a value is +pushed. Register A0 can be examined to determine the number of values that +must be transferred. + +Set the control stack register to the saved frame pointer above. NB: it may +have been updated if multiple values are being returned. + +Resume execution of the calling function. +@end(enumerate) + +Again, it is not always necessary to use the general return code. At compile +time it is often possible to determine that no special symbols have to be +unbound and/or only one value is being returned. For example the code to +perform a return when only one value is returned and it is unnecessary to +unbind any special symbols is: +@begin(Example) + cas NL1,FP,0 ; Save frame register. + lm L0,FP,0 ; Restore all preserved regs. + ls A3,AF,%function-code ; Get pointer to code vector. + niuo PC,PC,#x07FF ; Make relative PC positive. + cas PC,A3,PC ; Get addr. of instruction + bnbrx pz,PC ; to return to and do so while + cas CS,NL1,0 ; updating control stack reg. +@end(Example) + + +@subsection [Returning Multiple-Values] +@label [Multi] +@index [Multiple values] + +If the current frame can accept multiple values and a values marker is in +register A0 indicating N values on top of the stack, it is necessary to copy +the N return values down to the top of the control stack after the current +frame is popped off. Thus returning multiple values is similar to the +above, but a block transfer is necessary to move the returned values down to +the correct location on the control stack. + +In tail recursive situations, such as in the last form of a PROGN, one +function, FOO, may want to call another function, BAR, and return ``whatever +BAR returns.'' Call-Multiple is used in this case. If BAR returns multiple +values, they will all be passed to FOO. If FOO's caller wants multiple values, +the values will be returned. If not, FOO's Return instruction will see that +there are multiple values on the stack, but that multiple values will not be +accepted by FOO's caller. So Return will return only the first value. + +@section [Non-Local Exits] +@label [Catch] +@index [Catch] +@index [Throw] +@index [Catch-All object] +@index [Unwind-Protect] +@index [Non-Local Exits] + +The Catch and Unwind-Protect special forms are implemented using +catch frames. Unwind-Protect builds a catch frame whose tag is the +Catch-All object. The Catch miscop creates a catch frame for a +given tag and PC to branch to in the current instruction. The Throw +miscop looks up the stack by following the chain of catch frames +until it finds a frame with a matching tag or a frame with the +Catch-All object as its tag. If it finds a frame with a matching +tag, that frame is ``returned from,'' and that function is resumed. +If it finds a frame with the Catch-All object as its tag, that frame +is ``returned from,'' and in addition, %SP-Internal-Throw-Tag is set +to the tag being searched for. So that interrupted cleanup forms +behave correctly, %SP-Internal-Throw-Tag should be bound to the +Catch-All object before the Catch-All frame is built. The protected +forms are then executed, and if %SP-Internal-Throw-Tag is not the +Catch-All object, its value is thrown to. Exactly what we do is +this: +@begin [enumerate] +Put the contents of the Active-Catch register into a register, A. +Put NIL into another register, B. + +If A is NIL, the tag we seek isn't on the stack. Signal an +Unseen-Throw-Tag error. + +Look at the tag for the catch frame in register A. If it's the tag +we're looking for, go to step 4. If it's the Catch-All object and B +is NIL, copy A to B. Set A to the previous catch frame and go back +to step 2. + +If B is non-NIL, we need to execute some cleanup forms. Return into +B's frame and bind %SP-Internal-Throw-Tag to the tag we're searching +for. When the cleanup forms are finished executing, they'll throw to +this tag again. + +If B is NIL, return into this frame, pushing the return value (or +BLTing the multiple values if this frame accepts multiple values and +there are multiple values). +@end [enumerate] + +If no form inside of a Catch results in a Throw, the catch frame +needs to be removed from the stack before execution of the function +containing the throw is resumed. For now, the value produced by the +forms inside the Catch form are thrown to the tag. Some sort of +specialized miscop could be used for this, but right now we'll +just go with the throw. The branch PC specified by a Catch +miscop is part of the constants area of the function object, +much like the function's entry points. + +@section [Escaping to Lisp code] +@label [Escape] +@index [Escape to Lisp code convention] + +Escaping to Lisp code is fairly straight forward. If a miscop discovers that +it needs to call a Lisp function, it creates a call frame on the control +stack and sets it up so that the called function returns to the function that +called the miscop. This means it is impossible to return control to a miscop +from a Lisp function. + +@section [Errors] +@label [Errors] +@index [Errors] + +When an error occurs during the execution of a miscop, a call +to %SP-Internal-Error is performed. This call is a break-type call, +so if the error is proceeded (with a Break-Return instruction), no +value will be returned. + + +%SP-Internal-Error is passed a fixnum error code as its first +argument. The second argument is a fixnum offset into the current +code vector that points to the location immediately following the +instruction that encountered the trouble. From this offset, the +Lisp-level error handler can reconstruct the PC of the losing +instruction, which is not readily available in the micro-machine. +Following the offset, there may be 0 - 2 additional arguments that +provide information of possible use to the error handler. For +example, an unbound-symbol error will pass the symbol in question as +the third arg. + +The following error codes are currently defined. Unless otherwise +specified, only the error code and the code-vector offset are passed +as arguments. + +@begin +[description] +1 Object Not List@\The object is passed as the third argument. + +2 Object Not Symbol@\The object is passed as the third argument. + +3 Object Not Number@\The object is passed as the third argument. + +4 Object Not Integer@\The object is passed as the third argument. + +5 Object Not Ratio@\The object is passed as the third argument. + +6 Object Not Complex@\The object is passed as the third argument. + +7 Object Not Vector@\The object is passed as the third argument. + +8 Object Not Simple Vector@\The object is passed as the third argument. + +9 Illegal Function Object@\The object is passed as the third argument. + +10 Object Not Header@\The object (which is not an array or function header) +is passed as the third argument. + +11 Object Not I-Vector@\The object is passed as the third argument. + +12 Object Not Simple Bit Vector@\The object is passed as the third argument. + +13 Object Not Simple String@\The object is passed as the third argument. + +14 Object Not Character@\The object is passed as the third argument. + +15 Object Not Control Stack Pointer@\The object is passed as the third +argument. + +16 Object Not Binding Stack Pointer@\The object is passed as the third +argument. + +17 Object Not Array@\The object is passed as the third argument. + +18 Object Not Non-negative Fixnum@\The object is passed as the third +argument. + +19 Object Not System Area Pointer@\The object is passed as the third +argument. + +20 Object Not System Pointer@\The object is passed as the third argument. + +21 Object Not Float@\The object is passed as the third argument. + +22 Object Not Rational@\The object is passed as the third argument. + +23 Object Not Non-Complex Number@\A complex number has been passed to +the comparison routine for < or >. The complex number is passed as the +third argument. + +25 Unbound Symbol @\Attempted access to the special value of an unbound +symbol. Passes the symbol as the third argument to %Sp-Internal-Error. + +26 Undefined Symbol @\Attempted access to the definition cell of an undefined +symbol. Passes the symbol as the third argument to %Sp-Internal-Error. + +27 Altering NIL @\Attempt to bind or setq the special value of NIL. + +28 Altering T @\Attempt to bind or setq the special value of T. + +30 Illegal Vector Access Type @\The specified access type is returned as the +third argument. + +31 Illegal Vector Size @\Attempt to allocate a vector with negative size or +size too large for vectors of this type. Passes the requested size as the +third argument. + +32 Vector Index Out of Range @\The specified index is out of bounds for +this vector. The bad index is passed as the third argument. + +33 Illegal Vector Index@\The specified index is not a positive fixnum. The +bad index is passed as the third argument. + +34 Illegal Shrink Vector Value@\The specified value to shrink a vector to is +not a positive fixnum. The bad value is passed as the third argument. + +35 Not A Shrink@\The specified value is greater than the current size of the +vector being shrunk. The bad value is passed as the third argument. + +36 Illegal Data Vector@\The data vector of an array is illegal. The bad +vector is passed as the third value. + +37 Array has Too Few Indices@\An attempt has been made to access +an array as a two or three dimensional array when it has fewer than two +or three dimensions, respectively. + +38 Array has Too Many Indices@\An attempt has been made to access an array +as a two or three dimensional array when it has more than two or three +dimensions, respectively. + +40 Illegal Byte Specifier@\A bad byte specifier has been passed to one +of the byte manipulation miscops. The offending byte specifier is passed +as the third argument. + +41 Illegal Position in Byte Specifier@\A bad position has been given in a +byte specifier that has been passed to one of the byte manipulation +miscops. The offending byte specifier is passed as the third +argument. + +42 Illegal Size in Byte Specifier@\A bad size has been given in a +byte specifier that has been passed to one of the byte manipulation +miscops. The offending byte specifier is passed as the third +argument. + +43 Illegal Shift Count@\A shift miscop has encountered non fixnum shift +count. The offending shift count is passed as the third argument. + +44 Illegal Boole Operation@\The operation code passed to the boole miscop +is either not a fixnum or is out of range. The operation code is passed as +the third argument. + +50 Too Few Arguments@\Too few arguments have been passed to a function. The +number of arguments actually passed is passed as the third argument, and the +function is passed as the fourth. + +51 Too Many Arguments@\Too many arguments have been passed to a function. +The number of arguments actually passed is passed as the third argument, and +the function is passed as the fourth. + +52 Last Apply Arg Not a List@\The last argument to a function being +invoked by apply is not a list. The last argument is passed as the third +argument. + +53 Deleted Link Table Entry@\An attempt has been made to call a function +through a link table entry which no longer exists. This is a serious +internal error and should never happen. + +55 Error Not <=@\The check-<= miscop will invoke this error if the condition +is false. The two arguments are passed as the third and fourth arguments +to %SP-internal-error. + +60 Divide by 0@\An division operation has done a division by zero. The +two operands are passed as the third and fourth arguments. + +61 Unseen Throw Tag@\An attempt has been made to throw to a tag that is +not in the current catch hierarchy. The offending tag is passed as the +third argument. + +62 Short Float Underflow@\A short float operation has resulted in +underflow. The two arguments to the operation are passed as the third +and fourth arguments. + +63 Short Float Overflow@\A short float operation has resulted in +overflow. The two arguments to the operation are passed as the third +and fourth arguments. + +64 Single Float Underflow@\A single float operation has resulted in +underflow. The two arguments to the operation are passed as the third +and fourth arguments. + +65 Single Float Overflow@\A single float operation has resulted in +overflow. The two arguments to the operation are passed as the third +and fourth arguments. + +66 Long Float Underflow@\A long float operation has resulted in +underflow. The two arguments to the operation are passed as the third +and fourth arguments. + +67 Long Float Overflow@\A long float operation has resulted in +overflow. The two arguments to the operation are passed as the third +and fourth arguments. + +68 Monadic Short Float Underflow@\A short float operation has resulted in +underflow. The argument to the operation is passed as the third argument. + +69 Monadic Short Float Overflow@\A short float operation has resulted in +overflow. The argument to the operation is passed as the third argument. + +70 Monadic Long Float Underflow@\A long float operation has resulted in +underflow. The argument to the operation is passed as the third argument. + +71 Monadic Long Float Overflow@\A long float operation has resulted in +overflow. The argument to the operation is passed as the third argument. +@end [description] + +@section [Trapping to the Mach Kernel] +@label [Trap] +@index [Trapping to the kernel] +@index [Kernel traps] + +Trapping to the Mach kernel is done through one of the syscall0, syscall1, +syscall2, syscall3, syscall4, or syscall miscops. The first argument to +these miscops is the number of the Unix syscall that is to be invoked. Any +other arguments the syscall requires are passed in order after the first +one. Syscall0 accepts only the syscall number and no other arguments; +syscall1 accepts the syscall number and a single argument to the syscall; +etc. Syscall accepts the syscall number and five or more arguments to the +Unix syscall. These syscalls generally return two values: the result twice +if the syscall succeeded and a -1 and the Unix error code if the syscall +failed. + +@section [Interrupts] +@label [Interrupts] +@index [Interrupts] + +An interface has been built to the general signal mechanism defined by the +Unix operating system. As mentioned in the section on function call and +return miscops, several miscops are defined that support the lowest level +interface to the Unix signal mechanism. The manual @I[CMU Common Lisp +User's Manual, Mach/IBM RT PC Edition] contains descriptions of functions +that allow a user to set up interrupt handlers for any of the Unix signals +from within Lisp. + +@appendix [Fasload File Format] +@section [General] + +The purpose of Fasload files is to allow concise storage and rapid +loading of Lisp data, particularly function definitions. The intent +is that loading a Fasload file has the same effect as loading the +ASCII file from which the Fasload file was compiled, but accomplishes +the tasks more efficiently. One noticeable difference, of course, is +that function definitions may be in compiled form rather than +S-expression form. Another is that Fasload files may specify in what +parts of memory the Lisp data should be allocated. For example, +constant lists used by compiled code may be regarded as read-only. + +In some Lisp implementations, Fasload file formats are designed to +allow sharing of code parts of the file, possibly by direct mapping +of pages of the file into the address space of a process. This +technique produces great performance improvements in a paged +time-sharing system. Since the Mach project is to produce a +distributed personal-computer network system rather than a +time-sharing system, efficiencies of this type are explicitly @i[not] +a goal for the CMU Common Lisp Fasload file format. + +On the other hand, CMU Common Lisp is intended to be portable, as it will +eventually run on a variety of machines. Therefore an explicit goal +is that Fasload files shall be transportable among various +implementations, to permit efficient distribution of programs in +compiled form. The representations of data objects in Fasload files +shall be relatively independent of such considerations as word +length, number of type bits, and so on. If two implementations +interpret the same macrocode (compiled code format), then Fasload +files should be completely compatible. If they do not, then files +not containing compiled code (so-called "Fasdump" data files) should +still be compatible. While this may lead to a format which is not +maximally efficient for a particular implementation, the sacrifice of +a small amount of performance is deemed a worthwhile price to pay to +achieve portability. + +The primary assumption about data format compatibility is that all +implementations can support I/O on finite streams of eight-bit bytes. +By "finite" we mean that a definite end-of-file point can be detected +irrespective of the content of the data stream. A Fasload file will +be regarded as such a byte stream. + +@section [Strategy] + +A Fasload file may be regarded as a human-readable prefix followed by +code in a funny little language. When interpreted, this code will +cause the construction of the encoded data structures. The virtual +machine which interprets this code has a @i[stack] and a @i[table], +both initially empty. The table may be thought of as an expandable +register file; it is used to remember quantities which are needed +more than once. The elements of both the stack and the table are +Lisp data objects. Operators of the funny language may take as +operands following bytes of the data stream, or items popped from the +stack. Results may be pushed back onto the stack or pushed onto the +table. The table is an indexable stack that is never popped; it is +indexed relative to the base, not the top, so that an item once +pushed always has the same index. + +More precisely, a Fasload file has the following macroscopic +organization. It is a sequence of zero or more groups concatenated +together. End-of-file must occur at the end of the last group. Each +group begins with a series of seven-bit ASCII characters terminated +by one or more bytes of all ones (FF@-(16)); this is called the +@i[header]. Following the bytes which terminate the header is the +@i[body], a stream of bytes in the funny binary language. The body +of necessity begins with a byte other than FF@-(16). The body is +terminated by the operation @f[FOP-END-GROUP]. + +The first nine characters of the header must be "@f[FASL FILE]" in +upper-case letters. The rest may be any ASCII text, but by +convention it is formatted in a certain way. The header is divided +into lines, which are grouped into paragraphs. A paragraph begins +with a line which does @i[not] begin with a space or tab character, +and contains all lines up to, but not including, the next such line. +The first word of a paragraph, defined to be all characters up to but +not including the first space, tab, or end-of-line character, is the +@i[name] of the paragraph. A Fasload file header might look something like +this: +@begin(verbatim) +FASL FILE >SteelesPerq>User>Guy>IoHacks>Pretty-Print.Slisp +Package Pretty-Print +Compiled 31-Mar-1988 09:01:32 by some random luser +Compiler Version 1.6, Lisp Version 3.0. +Functions: INITIALIZE DRIVER HACK HACK1 MUNGE MUNGE1 GAZORCH + MINGLE MUDDLE PERTURB OVERDRIVE GOBBLE-KEYBOARD + FRY-USER DROP-DEAD HELP CLEAR-MICROCODE + %AOS-TRIANGLE %HARASS-READTABLE-MAYBE +Macros: PUSH POP FROB TWIDDLE +@r[] +@end(verbatim) +The particular paragraph names and contents shown here are only intended as +suggestions. + +@section [Fasload Language] + +Each operation in the binary Fasload language is an eight-bit +(one-byte) opcode. Each has a name beginning with "@f[FOP-]". In +the following descriptions, the name is followed by operand +descriptors. Each descriptor denotes operands that follow the opcode +in the input stream. A quantity in parentheses indicates the number +of bytes of data from the stream making up the operand. Operands +which implicitly come from the stack are noted in the text. The +notation "@PushArrow stack" means that the result is pushed onto the +stack; "@PushArrow table" similarly means that the result is added to the +table. A construction like "@i[n](1) @i[value](@i[n])" means that +first a single byte @i[n] is read from the input stream, and this +byte specifies how many bytes to read as the operand named @i[value]. +All numeric values are unsigned binary integers unless otherwise +specified. Values described as "signed" are in two's-complement form +unless otherwise specified. When an integer read from the stream +occupies more than one byte, the first byte read is the least +significant byte, and the last byte read is the most significant (and +contains the sign bit as its high-order bit if the entire integer is +signed). + +Some of the operations are not necessary, but are rather special +cases of or combinations of others. These are included to reduce the +size of the file or to speed up important cases. As an example, +nearly all strings are less than 256 bytes long, and so a special +form of string operation might take a one-byte length rather than a +four-byte length. As another example, some implementations may +choose to store bits in an array in a left-to-right format within +each word, rather than right-to-left. The Fasload file format may +support both formats, with one being significantly more efficient +than the other for a given implementation. The compiler for any +implementation may generate the more efficient form for that +implementation, and yet compatibility can be maintained by requiring +all implementations to support both formats in Fasload files. + +Measurements are to be made to determine which operation codes are +worthwhile; little-used operations may be discarded and new ones +added. After a point the definition will be "frozen", meaning that +existing operations may not be deleted (though new ones may be added; +some operations codes will be reserved for that purpose). + +@begin(description) +0 @f[ ] @f[FOP-NOP] @\ +No operation. (This is included because it is recognized +that some implementations may benefit from alignment of operands to some +operations, for example to 32-bit boundaries. This operation can be used +to pad the instruction stream to a desired boundary.) + +1 @f[ ] @f[FOP-POP] @f[ ] @PushArrow @f[ ] table @\ +One item is popped from the stack and added to the table. + +2 @f[ ] @f[FOP-PUSH] @f[ ] @i[index](4) @f[ ] @PushArrow @f[ ] stack @\ +Item number @i[index] of the table is pushed onto the stack. +The first element of the table is item number zero. + +3 @f[ ] @f[FOP-BYTE-PUSH] @f[ ] @i[index](1) @f[ ] @PushArrow @f[ ] stack @\ +Item number @i[index] of the table is pushed onto the stack. +The first element of the table is item number zero. + +4 @f[ ] @f[FOP-EMPTY-LIST] @f[ ] @PushArrow @f[ ] stack @\ +The empty list (@f[()]) is pushed onto the stack. + +5 @f[ ] @f[FOP-TRUTH] @f[ ] @PushArrow @f[ ] stack @\ +The standard truth value (@f[T]) is pushed onto the stack. + +6 @f[ ] @f[FOP-SYMBOL-SAVE] @f[ ] @i[n](4) @f[ ] @i[name](@i[n]) +@f[ ] @PushArrow @f[ ] stack & table@\ +The four-byte operand @i[n] specifies the length of the print name +of a symbol. The name follows, one character per byte, +with the first byte of the print name being the first read. +The name is interned in the default package, +and the resulting symbol is both pushed onto the stack and added to the table. + +7 @f[ ] @f[FOP-SMALL-SYMBOL-SAVE] @f[ ] @i[n](1) @f[ ] @i[name](@i[n]) @f[ ] @PushArrow @f[ ] stack & table@\ +The one-byte operand @i[n] specifies the length of the print name +of a symbol. The name follows, one character per byte, +with the first byte of the print name being the first read. +The name is interned in the default package, +and the resulting symbol is both pushed onto the stack and added to the table. + +8 @f[ ] @f[FOP-SYMBOL-IN-PACKAGE-SAVE] @f[ ] @i[index](4) +@f[ ] @i[n](4) @f[ ] @i[name](@i[n]) +@f[ ] @PushArrow @f[ ] stack & table@\ +The four-byte @i[index] specifies a package stored in the table. +The four-byte operand @i[n] specifies the length of the print name +of a symbol. The name follows, one character per byte, +with the first byte of the print name being the first read. +The name is interned in the specified package, +and the resulting symbol is both pushed onto the stack and added to the table. + +9 @f[ ] @f[FOP-SMALL-SYMBOL-IN-PACKAGE-SAVE] @f[ ] @i[index](4) +@f[ ] @i[n](1) @f[ ] @i[name](@i[n]) @f[ ] +@PushArrow @f[ ] stack & table@\ +The four-byte @i[index] specifies a package stored in the table. +The one-byte operand @i[n] specifies the length of the print name +of a symbol. The name follows, one character per byte, +with the first byte of the print name being the first read. +The name is interned in the specified package, +and the resulting symbol is both pushed onto the stack and added to the table. + +10 @f[ ] @f[FOP-SYMBOL-IN-BYTE-PACKAGE-SAVE] @f[ ] @i[index](1) +@f[ ] @i[n](4) @f[ ] @i[name](@i[n]) +@f[ ] @PushArrow @f[ ] stack & table@\ +The one-byte @i[index] specifies a package stored in the table. +The four-byte operand @i[n] specifies the length of the print name +of a symbol. The name follows, one character per byte, +with the first byte of the print name being the first read. +The name is interned in the specified package, +and the resulting symbol is both pushed onto the stack and added to the table. + +11@f[ ] @f[FOP-SMALL-SYMBOL-IN-BYTE-PACKAGE-SAVE] @f[ ] @i[index](1) +@f[ ] @i[n](1) @f[ ] @i[name](@i[n]) @f[ ] +@PushArrow @f[ ] stack & table@\ +The one-byte @i[index] specifies a package stored in the table. +The one-byte operand @i[n] specifies the length of the print name +of a symbol. The name follows, one character per byte, +with the first byte of the print name being the first read. +The name is interned in the specified package, +and the resulting symbol is both pushed onto the stack and added to the table. + +12 Unused. + +13 @f[ ] @f[FOP-DEFAULT-PACKAGE] @f[ ] @i[index](4) @\ +A package stored in the table entry specified by @i[index] is made +the default package for future @f[FOP-SYMBOL] and @f[FOP-SMALL-SYMBOL] +interning operations. (These package FOPs may change or disappear +as the package system is determined.) + +14 @f[ ] @f[FOP-PACKAGE] @f[ ] @PushArrow @f[ ] table @\ +An item is popped from the stack; it must be a symbol. The package of +that name is located and pushed onto the table. + +15 @f[ ] @f[FOP-LIST] @f[ ] @i[length](1) @f[ ] @PushArrow @f[ ] stack @\ +The unsigned operand @i[length] specifies a number of +operands to be popped from the stack. These are made into a list +of that length, and the list is pushed onto the stack. +The first item popped from the stack becomes the last element of +the list, and so on. Hence an iterative loop can start with +the empty list and perform "pop an item and cons it onto the list" +@i[length] times. +(Lists of length greater than 255 can be made by using @f[FOP-LIST*] +repeatedly.) + +16 @f[ ] @f[FOP-LIST*] @f[ ] @i[length](1) @f[ ] @PushArrow @f[ ] stack @\ +This is like @f[FOP-LIST] except that the constructed list is terminated +not by @f[()] (the empty list), but by an item popped from the stack +before any others are. Therefore @i[length]+1 items are popped in all. +Hence an iterative loop can start with +a popped item and perform "pop an item and cons it onto the list" +@i[length]+1 times. + +17-24 @f[ ] @f[FOP-LIST-1], @f[FOP-LIST-2], ..., @f[FOP-LIST-8] @\ +@f[FOP-LIST-@i{k}] is like @f[FOP-LIST] with a byte containing @i[k] +following it. These exist purely to reduce the size of Fasload files. +Measurements need to be made to determine the useful values of @i[k]. + +25-32 @f[ ] @f[FOP-LIST*-1], @f[FOP-LIST*-2], ..., @f[FOP-LIST*-8] @\ +@f[FOP-LIST*-@i{k}] is like @f[FOP-LIST*] with a byte containing @i[k] +following it. These exist purely to reduce the size of Fasload files. +Measurements need to be made to determine the useful values of @i[k]. + +33 @f[ ] @f[FOP-INTEGER] @f[ ] @i[n](4) @f[ ] @i[value](@i[n]) @f[ ] +@PushArrow @f[ ] stack @\ +A four-byte unsigned operand specifies the number of following +bytes. These bytes define the value of a signed integer in two's-complement +form. The first byte of the value is the least significant byte. + +34 @f[ ] @f[FOP-SMALL-INTEGER] @f[ ] @i[n](1) @f[ ] @i[value](@i[n]) +@f[ ] @PushArrow @f[ ] stack @\ +A one-byte unsigned operand specifies the number of following +bytes. These bytes define the value of a signed integer in two's-complement +form. The first byte of the value is the least significant byte. + +35 @f[ ] @f[FOP-WORD-INTEGER] @f[ ] @i[value](4) @f[ ] @PushArrow @f[ ] stack @\ +A four-byte signed integer (in the range -2@+[31] to 2@+[31]-1) follows the +operation code. A LISP integer (fixnum or bignum) with that value +is constructed and pushed onto the stack. + +36 @f[ ] @f[FOP-BYTE-INTEGER] @f[ ] @i[value](1) @f[ ] @PushArrow @f[ ] stack @\ +A one-byte signed integer (in the range -128 to 127) follows the +operation code. A LISP integer (fixnum or bignum) with that value +is constructed and pushed onto the stack. + +37 @f[ ] @f[FOP-STRING] @f[ ] @i[n](4) @f[ ] @i[name](@i[n]) +@f[ ] @PushArrow @f[ ] stack @\ +The four-byte operand @i[n] specifies the length of a string to +construct. The characters of the string follow, one per byte. +The constructed string is pushed onto the stack. + +38 @f[ ] @f[FOP-SMALL-STRING] @f[ ] @i[n](1) @f[ ] @i[name](@i[n]) @f[ ] @PushArrow @f[ ] stack @\ +The one-byte operand @i[n] specifies the length of a string to +construct. The characters of the string follow, one per byte. +The constructed string is pushed onto the stack. + +39 @f[ ] @f[FOP-VECTOR] @f[ ] @i[n](4) @f[ ] @PushArrow @f[ ] stack @\ +The four-byte operand @i[n] specifies the length of a vector of LISP objects +to construct. The elements of the vector are popped off the stack; +the first one popped becomes the last element of the vector. +The constructed vector is pushed onto the stack. + +40 @f[ ] @f[FOP-SMALL-VECTOR] @f[ ] @i[n](1) @f[ ] @PushArrow @f[ ] stack @\ +The one-byte operand @i[n] specifies the length of a vector of LISP objects +to construct. The elements of the vector are popped off the stack; +the first one popped becomes the last element of the vector. +The constructed vector is pushed onto the stack. + +41 @f[ ] @f[FOP-UNIFORM-VECTOR] @f[ ] @i[n](4) @f[ ] @PushArrow @f[ ] stack @\ +The four-byte operand @i[n] specifies the length of a vector of LISP objects +to construct. A single item is popped from the stack and used to initialize +all elements of the vector. The constructed vector is pushed onto the stack. + +42 @f[ ] @f[FOP-SMALL-UNIFORM-VECTOR] @f[ ] @i[n](1) @f[ ] @PushArrow @f[ ] stack @\ +The one-byte operand @i[n] specifies the length of a vector of LISP objects +to construct. A single item is popped from the stack and used to initialize +all elements of the vector. The constructed vector is pushed onto the stack. + +43 @f[ ] @f[FOP-INT-VECTOR] @f[ ] @i[n](4) @f[ ] @i[size](1) @f[ ] @i[count](1) @f[ ] +@i[data](@ceiling<@i[n]/@i[count]>@ceiling<@i[size]*@i[count]/8>) @f[ ] +@PushArrow @f[ ] stack @\ +The four-byte operand @i[n] specifies the length of a vector of +unsigned integers to be constructed. Each integer is @i[size] +bits big, and are packed in the data stream in sections of +@i[count] apiece. Each section occupies an integral number of bytes. +If the bytes of a section are lined up in a row, with the first +byte read at the right, and successive bytes placed to the left, +with the bits within a byte being arranged so that the low-order bit +is to the right, then the integers of the section are successive +groups of @i[size] bits, starting from the right and running across +byte boundaries. (In other words, this is a consistent +right-to-left convention.) Any bits wasted at the left end of +a section are ignored, and any wasted groups in the last section +are ignored. +It is permitted for the loading implementation to use a vector +format providing more precision than is required by @i[size]. +For example, if @i[size] were 3, it would be permitted to use a vector +of 4-bit integers, or even vector of general LISP objects filled +with integer LISP objects. However, an implementation is expected +to use the most restrictive format that will suffice, and is expected +to reconstruct objects identical to those output if the Fasload file +was produced by the same implementation. +(For the PERQ U-vector formats, one would have +@i[size] an element of {1, 2, 4, 8, 16}, and @i[count]=32/@i[size]; +words could be read directly into the U-vector. +This operation provides a very general format whereby almost +any conceivable implementation can output in its preferred packed format, +and another can read it meaningfully; by checking at the beginning +for good cases, loading can still proceed quickly.) +The constructed vector is pushed onto the stack. + +44 @f[ ] @f[FOP-UNIFORM-INT-VECTOR] @f[ ] @i[n](4) @f[ ] @i[size](1) @f[ ] +@i[value](@ceiling<@i[size]/8>) @f[ ] @PushArrow @f[ ] stack @\ +The four-byte operand @i[n] specifies the length of a vector of unsigned +integers to construct. +Each integer is @i[size] bits big, and is initialized to the value +of the operand @i[value]. +The constructed vector is pushed onto the stack. + +45 @f[ ] @f[FOP-FLOAT] @f[ ] @i[n](1) @f[ ] @i[exponent](@ceiling<@i[n]/8>) @f[ ] +@i[m](1) @f[ ] @i[mantissa](@ceiling<@i[m]/8>) @f[ ] @PushArrow @f[ ] stack @\ +The first operand @i[n] is one unsigned byte, and describes the number of +@i[bits] in the second operand @i[exponent], which is a signed +integer in two's-complement format. The high-order bits of +the last (most significant) byte of @i[exponent] shall equal the sign bit. +Similar remarks apply to @i[m] and @i[mantissa]. The value denoted by these +four operands is @i[mantissa]@f[x]2@+{@i[exponent]-length(@i[mantissa])}. +A floating-point number shall be constructed which has this value, +and then pushed onto the stack. That floating-point format should be used +which is the smallest (most compact) provided by the implementation which +nevertheless provides enough accuracy to represent both the exponent +and the mantissa correctly. + +46-51 Unused + +52 @f[ ] @f[FOP-ALTER] @f[ ] @i[index](1) @\ +Two items are popped from the stack; call the first @i[newval] and +the second @i[object]. The component of @i[object] specified by +@i[index] is altered to contain @i[newval]. The precise operation +depends on the type of @i[object]: +@begin(description) +List @\ A zero @i[index] means alter the car (perform @f[RPLACA]), +and @i[index]=1 means alter the cdr (@f[RPLACD]). + +Symbol @\ By definition these indices have the following meaning, +and have nothing to do with the actual representation of symbols +in a given implementation: +@begin(description) +0 @\ Alter value cell. + +1 @\ Alter function cell. + +2 @\ Alter property list (!). +@end(description) + +Vector (of any kind) @\ Alter component number @i[index] of the vector. + +String @\ Alter character number @i[index] of the string. +@end(description) + +53 @f[ ] @f[FOP-EVAL] @f[ ] @PushArrow @f[ ] stack @\ +Pop an item from the stack and evaluate it (give it to @f[EVAL]). +Push the result back onto the stack. + +54 @f[ ] @f[FOP-EVAL-FOR-EFFECT] @\ +Pop an item from the stack and evaluate it (give it to @f[EVAL]). +The result is ignored. + +55 @f[ ] @f[FOP-FUNCALL] @f[ ] @i[nargs](1) @f[ ] @PushArrow @f[ ] stack @\ +Pop @i[nargs]+1 items from the stack and apply the last one popped +as a function to +all the rest as arguments (the first one popped being the last argument). +Push the result back onto the stack. + +56 @f[ ] @f[FOP-FUNCALL-FOR-EFFECT] @f[ ] @i[nargs](1) @\ +Pop @i[nargs]+1 items from the stack and apply the last one popped +as a function to +all the rest as arguments (the first one popped being the last argument). +The result is ignored. + +57 @f[ ] @f[FOP-CODE-FORMAT] @f[ ] @i[id](1) @\ +The operand @i[id] is a unique identifier specifying the format +for following code objects. The operations @f[FOP-CODE] +and its relatives may not +occur in a group until after @f[FOP-CODE-FORMAT] has appeared; +there is no default format. This is provided so that several +compiled code formats may co-exist in a file, and so that a loader +can determine whether or not code was compiled by the correct +compiler for the implementation being loaded into. +So far the following code format identifiers are defined: +@begin(description) +0 @\ PERQ + +1 @\ VAX + +3 @\ @value(DinkyMachine) +@end(description) + +58 @f[ ] @f[FOP-CODE] @f[ ] @i[nitems](4) @f[ ] @i[size](4) @f[ ] +@i[code](@i[size]) @f[ ] @PushArrow @f[ ] stack @\ +A compiled function is constructed and pushed onto the stack. +This object is in the format specified by the most recent +occurrence of @f[FOP-CODE-FORMAT]. +The operand @i[nitems] specifies a number of items to pop off +the stack to use in the "boxed storage" section. The operand @i[code] +is a string of bytes constituting the compiled executable code. + +59 @f[ ] @f[FOP-SMALL-CODE] @f[ ] @i[nitems](1) @f[ ] @i[size](2) @f[ ] +@i[code](@i[size]) @f[ ] @PushArrow @f[ ] stack @\ +A compiled function is constructed and pushed onto the stack. +This object is in the format specified by the most recent +occurrence of @f[FOP-CODE-FORMAT]. +The operand @i[nitems] specifies a number of items to pop off +the stack to use in the "boxed storage" section. The operand @i[code] +is a string of bytes constituting the compiled executable code. + +60 @f[ ] @f[FOP-STATIC-HEAP] @\ +Until further notice operations which allocate data structures +may allocate them in the static area rather than the dynamic area. +(The default area for allocation is the dynamic area; this +default is reset whenever a new group is begun. +This command is of an advisory nature; implementations with no +static heap can ignore it.) + +61 @f[ ] @f[FOP-DYNAMIC-HEAP] @\ +Following storage allocation should be in the dynamic area. + +62 @f[ ] @f[FOP-VERIFY-TABLE-SIZE] @f[ ] @i[size](4) @\ +If the current size of the table is not equal to @i[size], +then an inconsistency has been detected. This operation +is inserted into a Fasload file purely for error-checking purposes. +It is good practice for a compiler to output this at least at the +end of every group, if not more often. + +63 @f[ ] @f[FOP-VERIFY-EMPTY-STACK] @\ +If the stack is not currently empty, +then an inconsistency has been detected. This operation +is inserted into a Fasload file purely for error-checking purposes. +It is good practice for a compiler to output this at least at the +end of every group, if not more often. + +64 @f[ ] @f[FOP-END-GROUP] @\ +This is the last operation of a group. If this is not the +last byte of the file, then a new group follows; the next +nine bytes must be "@f[FASL FILE]". + +65 @f[ ] @f[FOP-POP-FOR-EFFECT] @f[ ] stack @f[ ] @PushArrow @f[ ] @\ +One item is popped from the stack. + +66 @f[ ] @f[FOP-MISC-TRAP] @f[ ] @PushArrow @f[ ] stack @\ +A trap object is pushed onto the stack. + +67 @f[ ] @f[FOP-READ-ONLY-HEAP] @\ +Following storage allocation may be in a read-only heap. +(For symbols, the symbol itself may not be in a read-only area, +but its print name (a string) may be. +This command is of an advisory nature; implementations with no +read-only heap can ignore it, or use a static heap.) + +68 @f[ ] @f[FOP-CHARACTER] @f[ ] @i[character](3) @f[ ] @PushArrow @f[ ] stack @\ +The three bytes specify the 24 bits of a CMU Common Lisp character object. +The bytes, lowest first, represent the code, control, and font bits. +A character is constructed and pushed onto the stack. + +69 @f[ ] @f[FOP-SHORT-CHARACTER] @f[ ] @i[character](1) @f[ ] +@PushArrow @f[ ] stack @\ +The one byte specifies the lower eight bits of a CMU Common Lisp character +object (the code). A character is constructed with zero control +and zero font attributes and pushed onto the stack. + +70 @f[ ] @f[FOP-RATIO] @f[ ] @PushArrow @f[ ] stack @\ +Creates a ratio from two integers popped from the stack. +The denominator is popped first, the numerator second. + +71 @f[ ] @f[FOP-COMPLEX] @f[ ] @PushArrow @f[ ] stack @\ +Creates a complex number from two numbers popped from the stack. +The imaginary part is popped first, the real part second. + +72 @f[ ] @f[FOP-LINK-ADDRESS-FIXUP] @f[ ] @i[nargs](1) @f[ ] @i[restp](1) +@f[ ] @i[offset](4) @f[ ] @PushArrow @f[ ] stack @\ +Valid only for when FOP-CODE-FORMAT corresponds to the Vax or the +@value(DinkyMachine). +This operation pops a symbol and a code object from the stack and pushes +a modified code object back onto the stack according to the needs of the +runtime code linker on the Vax or @value(DinkyMachine). + +73 @f[ ] @f[FOP-LINK-FUNCTION-FIXUP] @f[ ] @i[offset](4) @f[ ] +@PushArrow @f[ ] stack @\ +Valid only for when FOP-CODE-FORMAT corresponds to the Vax or the +@value(DinkyMachine). +This operation pops a symbol and a code object from the stack and pushes +a modified code object back onto the stack according to the needs of the +runtime code linker on the Vax or the @value(DinkyMachine). + +74 @f[ ] @f[FOP-FSET] @f[ ] @\ +Pops the top two things off of the stack and uses them as arguments to FSET +(i.e. SETF of SYMBOL-FUNCTION). + +128 @f[ ] @f[FOP-LINK-ADDRESS-FIXUP] @f[ ] @i[nargs] @f[ ] @i[flag] @f[ ] +@i[offset] @f[ ]@\Valid only when FOP-CODE-FORMAT corresponds to the +@value(DinkyMachine). This operation pops a symbol and a function object +off the stack. The code vector in the function object is modified +according to the needs of the runtime code linker of the @value(DinkyMachine) +and pushed back on the stack. This FOP links in calls to other functions. + +129 @f[ ] @f[FOP-MISCOP-FIXUP] @f[ ] @i[index](2) @f[ ] @i[offset](4) @f[ ]@\ +Valid only when FOP-CODE-FORMAT corresponds to the @value(DinkyMachine). +This operation pops a code object from the stack and pushes a +modified code object back onto the stack according to the needs of +the runtime code linker on the @value(DinkyMachine). This FOP links in +calls to the assembler language support routines. + +130 @f[ ] @f[FOP-ASSEMBLER-ROUTINE] @f[ ] @i[code-length] @f[ ] @\ +Valid only when FOP-CODE-FORMAT corresponds to the @value(DinkyMachine). +This operation loads assembler code into the assembler code space of the +currently running Lisp. + +131 @f[ ] @f[FOP-FIXUP-MISCOP-ROUTINE] @f[ ]@\Valid only when FOP-CODE-FORMAT +corresponds to the @value(DinkyMachine). This operation pops a list of +external references, a list of external labels defined, the name, and the +code address off the stack. This information is saved, so that after +everything is loaded, all the external references can be resolved. + +132 @f[ ] @f[FOP-FIXUP-ASSEMBLER-ROUTINE] @f[ ]@\is similar to +FOP-FIXUP-MISCOP-ROUTINE, except it is for internal assembler routines +rather than ones visible to Lisp. + +133 @f[ ] @f[FOP-FIXUP-USER-MISCOP-ROUTINE] @f[ ]@\is similar to +FOP-FIXUP-MISCOP-ROUTINE, except it is for routines written by users who +have an extremely good understanding of the system internals. + +134 @f[ ] @f[FOP-USER-MISCOP-FIXUP] @f[ ] @i[offset](4) @f[ ]@\is similar +to FOP-MISCOP-FIXUP, but is used to link in user defined miscops. + +255 @f[ ] @f[FOP-END-HEADER] @\ Indicates the end of a group header, as described above. +@end(description) + +@Appendix[Building CMU Common Lisp] + +@section(Introduction) +This document explains how to build a working Common Lisp from source code on +the IBM RT PC under the Mach operating system. You should already have a +working Common Lisp running on an IBM RT PC before trying to build a new Common +Lisp. + +Throughout this document the following terms are used: +@begin(Description) +Core file@\A core file is a file containing an image of a Lisp system. The +core file contains header information describing where the data in the rest of +the file should be placed in memory. There is a simple C program which reads a +core file into memory at the correct locations and then jumps to a location +determined by the contents of the core file. The C code includes the X +window system version 10 release 4 which may be called from Lisp. + + +Cold core file @\A cold core file contains enough of the Lisp system to make it +possible to load in the rest of the code necessary to generate a full Common +Lisp. A cold core file is generated by the program Genesis. + +Miscops@\Miscops are assembler language routines that are used to support +compiled Lisp code. A Lisp macro assembler provides a +convenient mechanism for writing these assembler language routines. + +Matchmaker@\Matchmaker is a program developed to automatically generate +remote procedure call interfaces between programs. Matchmaker accepts +a description of a remote procedure call interface and generates code +that implements it. +@end(Description) + +There are many steps required to go from sources to a working Common Lisp +system. Each step will be explained in detail in the following sections. +It is possible to perform more than one step with one invocation of Lisp. +However, I recommend that each step be started with a fresh Lisp. There +is some small chance that something done in one step will adversely affect +a following step if the same Lisp is used. The scripts for each +step assume that you are in the user package which is the default when +Lisp first starts up. If you change to some other package, some of these +steps may not work correctly. + +In many of the following steps, there are lines setting up search lists so that +command files know where to find the sources. What I have done is create a +init.lisp file that sets up these search lists for me. This file is +automatically loaded from the user's home directory (as determined by the +@b[HOME] environment variable) when you start up Lisp. Note that my init.lisp +file is included with the sources. You may have to modify it, if you change +where the lisp sources are. + +@section(Installing Source Code) +With this document, you should also have received a tape cartridge in tar +format containing the complete Common Lisp source code. You should create +some directory where you want to put the source code. For the following +discussion, I will assume that the source code lives in the directory +/usr/lisp. To install the source code on your machine, issue the following +commands: +@begin(Example) +cd /usr/lisp +tar xvf +@end(Example) +The first command puts you into the directory where you want the source code, +and the second extracts all the files and sub-directories from the tape into +the current directory. should be the name of the tape device on +your machine, usually /dev/st0. + +The following sub-directories will be created by tar: +@begin(Description) +bin@\contains a single executable file, lisp, which is a C program +used to start up Common Lisp. + +clc@\contains the Lisp source code for the Common Lisp compiler and assembler. + +code@\contains the Lisp source code that corresponds to all the functions, +variables, macros, and special forms described in @i[Common Lisp: The Language] +by Guy L. Steele Jr., as well as some Mach specific files. + +hemlock@\contains the Lisp source code for Hemlock, an emacs-like editor +written completely in Common Lisp. + +icode@\contains Matchmaker generated code for interfaces to Inter Process +Communication (IPC) routines. This code is used to communicate with other +processes using a remote procedure call mechanism. Under Mach, all the +facilities provided by Mach beyond the normal Berkeley Unix 4.3 system +calls are accessed from Lisp using this IPC mechanism. Currently, the +code for the Mach, name server, Lisp typescript, and Lisp eval server +interfaces reside in this directory. + +idefs@\contains the Matchmaker definition files used to generate the Lisp +code in the icode directory. + +lib@\contains files needed to run Lisp. The file lisp.core is known as a +Lisp core file and is loaded into memory by the lisp program mentioned +above in the entry for the bin directory. This file has a format which +allows it to be mapped into memory at the correct locations. The files +spell-dictionary.text and spell-dictionary.bin are the text and binary +form of a dictionary, respectively, used by Hemlock's spelling checker and +corrector. The two files hemlock.cursor and hemlock.mask are used by +Hemlock when running under the X window system. + +miscops@\contains the Lisp assembler source code for all the miscops +that support low level Lisp functions, such as storage allocation, +complex operations that can not performed in-line, garbage collection, and +other operations. These routines are written in assembler, so that they +are as efficient as possible. These routines use a very short calling +sequence, so calling them is very cheap compared to a normal Lisp +function call. + +mm@\contains the Lisp source code for the Matchmaker program. This program +is used to generate the Lisp source code files in icode from the corresponding +matchmaker definitions in idefs. + +pcl@\contains the Lisp source code for a version of the Common Lisp Object +System (originally Portable Common Loops), +an object oriented programming language built on top of Common Lisp. + +X@\contains the C object files for X version 10 release 4 C library +routines. These are linked with the lisp startup code, so that X is +available from Lisp. + +scribe@\contains Scribe source and postscript output for the manuals +describing various aspects of the CMU Common Lisp implementation. + +demos@\contains the Lisp source code for various demonstration programs. +This directory contains the Gabriel benchmark set (bmarks.lisp) and +a sub-directory containing the Soar program which is also used for +benchmarking purposes. There may be other programs and/or sub-directories +here that you may look at. +@end(Description) +These directories contain source files as well as Lisp object files. +This means it is not necessary to go through all the steps to +build a new a Common Lisp, only those steps that are affected by +a modification to the sources. For example, modifying the compiler +will require recompiling everything. Modifying a miscop file should +require only reassembling that particular file and rebuilding the +cold core file and full core file. + +As well as the directories mentioned above, there are also several files +contained in the top-level directory. These are: +@begin(Description) +init.lisp@\is a Lisp init file I use. This sets up some standard search +lists, as well as defines a Hemlock mode for editing miscop +source files. + +lisp.c@\contains the C code used to start up the lisp core image under Mach. + +lispstart.s@\contains some assembler language code that is invoked by lisp.c +to finish the process of starting up the lisp process. + +makefile@\contains make definitions for compiling lisp.c and lispstart.s +into the lisp program. + +rg@\contains some adb commands that can be read into adb while debugging a lisp +process. It prints out all the registers, the name of the currently +executing Lisp function, and sets an adb variable to the current stack frame +which is used by the following file. + +st@\contains some adb commands that can be read into adb while debugging +a lisp process. It prints out a Lisp stack frame and the name of the +function associated with the stack frame. It also updates the adb variable +mentioned above to point to the next stack frame. Repeatedly reading this +file into adb will produce a backtrace of all the active call frames +on the Lisp stack. + +ac@\contains some adb commands that print out the current values of the +active catch pointer. This points to the head of a list of catch frames +that exist on the control stack. + +cs@\contains some adb commands that print out the contents of a catch +frame. Reading cs into adb several times in a row (after reading ac once) +will print out the catch frames in order. +@end(Description) + +@section(Compiling the Lisp Startup Program) +To compile the lisp start up program, you should be in the top level directory +of the sources (/usr/lisp) and type: +@begin(Example) +make lisp +@end(Example) +This will compile the file lisp.c, assemble the file lispstart.s and produce +an executable file lisp. Currently the default location for the lisp core +file is /usr/misc/.lisp/lib/lisp.core. If you want to change this default +location, edit the file lisp.c and change the line +@begin(Example) +#define COREFILE "/usr/misc/.lisp/lib/lisp.core" +@end(Example) +to refer to the file where you intend to put the core file. + +This step takes a few seconds. + +@section(Assembling Assembler routines) +The standard core image includes a Lisp macro assembler. To assemble all +the miscops, the following steps should be performed: +@begin(Example) +(compile-file "/usr/lisp/clc/miscasm.lisp") +(load "/usr/lisp/clc/miscasm.fasl") +(setf (search-list "msc:") '("/usr/lisp/miscops/")) +(clc::asm-files) +@end(Example) +The first line compiles a file that contains a couple of functions used to +assemble miscop source files. The second line loads the resulting compiled +file into the currently executing core image. The third line defines the +msc search list which is used by the function clc::asm-files to locate +the miscop sources. The terminal will display information as each file +is assembled. For each file a .fasl, a .list, and an .err file will be +generated in /usr/lisp/miscops. + +This step takes about half an hour. + +@section(Compiling the Compiler) + +To compile the compiler is simple: +@begin(Example) +(setf (search-list "clc:") '("/usr/lisp/clc/")) +(load "clc:compclc.lisp") +@end(Example) +The first line just sets up a search list variable clc, so that the file +compclc.lisp can find the compiler sources. The terminal will display +information as each file is assembled. For each file a .fasl and an .err file +will be generated. A log of the compiler output is also displayed on the +terminal. + +This step takes about forty-five minutes. + +@section(Compiling the Lisp Sources) + +Compiling the Lisp source code is also easy: +@begin(Example) +(setf (search-list "code:") '("/usr/lisp/code/")) +(load "code:worldcom.lisp") +@end(Example) +Again, the first line defines a search list variable, so that the file +worldcom.lisp can find the Lisp sources. As each file is compiled, the +name of the file is printed on the terminal. For each file a .fasl will be +generated. Also, a single error log will be generated in the file +code:compile-lisp.log. + +This step takes about an hour and a half. + +@section(Compiling Hemlock) + +Compiling the Hemlock source code is done as follows: +@begin(Example) +(setf (search-list "hem:") '("/usr/lisp/hemlock/")) +(load "hem:ctw.lisp") +@end(Example) +Again, the first line defines a search list variable, so that ctw.lisp can +find the Hemlock sources. As each file is compiled, the name of the file is +printed on the terminal. For each file a .fasl will be generated. Also, a +single error log will be generated in the file hem:lossage.log. + +This step takes about forty-five minutes. + +@section(Compiling Matchmaker) +Compiling the matchmaker sources is done as follows: +@begin(Example) +(setf (search-list "mm:") '("/usr/lisp/mm")) +(compile-file "mm:mm.lisp") +(load "mm:mm.fasl") +(compile-mm) +@end(Example) +The first line sets up a search list, so that the matchmaker sources can be +found. The second line compiles the file containing a function for compiling +the matchmaker sources. The third line loads the file just +compiled, and the final line invokes the function compile-mm which compiles the +matchmaker sources. For each file, a .fasl and .err file is generated. Also, +a log of the compiler output is printed to the terminal. + +This step takes about 15 minutes + +@section(Generating Lisp Source Files from Matchmaker Definition Files) +The following sequence of commands is necessary to generate the Lisp +files for the Mach interface: +@begin(Example) +(setf (search-list "mm:") '("/usr/lisp/mm/")) +(setf (search-list "idefs:") '("/usr/lisp/idefs/")) +(setf (search-list "icode:") '("/usr/lisp/icode/")) +(setf (search-list "code:") '("/usr/lisp/code/")) +(setf (default-directory) "/usr/lisp/icode/") +(load "code:mm-interfaces.lisp") +@end(Example) +The first four lines set up search lists for mm (matchmaker sources), idefs +(matchmaker interface definition files), icode (Lisp matchmaker interface +sources), and code (Lisp code sources). The fifth line changes the current +working directory to be /usr/lisp/icode. This is where the output from +matchmaker will be placed. And finally, the last line invokes matchmaker on +the matchmaker definition files for all the interfaces. + +Matchmaker generates three files for each interface XXX: +@begin(Description) +XXXdefs.lisp@\contains constants and record definitions for the interface. + +XXXmsgdefs.lisp@\contains definitions of offsets to important fields in the +messages that are sent to and received from the interface. + +XXXuser.lisp@\contains code for each remote procedure, that sends a message +to the server and receives the reply from the server (if appropriate). +Each of these functions returns one or more values. The first value +returned is a general return which specifies whether the remote procedure +call succeeded or gives an indication of why it failed. Other values may +be returned depending on the particular remote procedure. These values are +returned using the multiple value mechanism of Common Lisp. +@end(Description) + +This step takes about five minutes. + +@section(Compiling Matchmaker Generated Lisp Files) +To compile the matchmaker generated Lisp files the following steps should +be performed: +@begin(Example) +(setf (search-list "code:") '("/usr/lisp/code/")) +(setf (search-list "icode:") '("/usr/lisp/icode/")) +(load "code:comutil.lisp") +@end(Example) +The first two lines set up search lists for the code and icode directory. +The final line loads a command file that compiles the Mach interface +definition in the correct order. Note that once the files are compiled, +the XXXmsgdefs files are no longer needed. The file +/usr/lisp/icode/lossage.log contains a listing of all the error messages +generated by the compiler. + +This step takes about fifteen minutes. + +@section(Compiling the Common Lisp Object System) + +To compile the Common Lisp Object System (CLOS) do the following: +@begin(Example) +(setf (search-list "pcl:") '("/usr/lisp/pcl/")) +(rename-package (find-package "CLOS") "OLD-CLOS") +(compile-file "pcl:defsys.lisp") +(load "pcl:defsys.fasl") +(clos::compile-pcl) +@end(Example) +The first line sets up a search list as usual. The second line renames the +CLOS package to be the OLD-CLOS package. This is so that the current version +of CLOS doesn't interfere with the compilation process. The third line +compiles a file containing some functions for building CLOS. The fourth +line loads in the result of the previous compilation. The final line +compiles all the CLOS files necessary for a working CLOS system. + +The file /usr/lisp/pcl/test.lisp is a file that contains some test functions. +To run it through CLOS build a new Lisp and start up a fresh Lisp +resulting from the build and do the following: +@begin(Example) +(in-package 'clos) +(compile-file "/usr/lisp/pcl/test.lisp") +(load "/usr/lisp/pcl/test.fasl") +@end(Example) +This sequence of function calls puts you in the CLOS package, compiles the +test file and then loads it. As the test file is loaded, it executes several +tests. It will print out a message specifying whether each test passed or +failed. + +Currently, CLOS is built into the standard core. + +This step takes about 30 minutes. + +@section(Compiling Genesis) +To compile genesis do the following: +@begin(Example) +(compile-file "/usr/lisp/clc/genesis.lisp") +@end(Example) +Genesis is used to build a cold core file. Compiling Genesis takes about five +minutes. + +@section(Building a Cold Core File) +Once all the files have been assembled or compiled as described above, it is +necessary to build a cold core file as follows: +@begin(Example) +(setf (search-list "code:") '("/usr/lisp/code/")) +(setf (search-list "icode:") '("/usr/lisp/icode/")) +(setf (search-list "msc:") '("/usr/lisp/miscops/")) +(load "/usr/lisp/clc/genesis.fasl") +(load "code:worldbuild.lisp") +@end(Example) +The first three lines set up search lists for the code, icode, and miscops +subdirectories. The fourth line loads in the program Genesis which builds +the cold core file. The last line calls Genesis on a list of the files that +are necessary to build the cold core file. As each file is being processed, +its name is printed to the terminal. Genesis generates two files: +/usr/lisp/ilisp.core and /usr/lisp/lisp.map. Ilisp.core is the cold core +file and lisp.map is a file containing the location of all the functions +and miscops in the cold core file. Lisp.map is useful for debugging the +cold core file. + +This step takes from about fifteen minutes. + +@section(Building a Full Common Lisp) +The cold core file built above does not contain some of the more useful +programs such as the compiler and hemlock. To build these into a core, it is +necessary to do the following: +@begin(Example) +lisp -c /usr/lisp/ilisp.core +(in-package "USER") +(load (open "/usr/lisp/code/worldload.lisp")) +@end(Example) +The first line invokes the lisp startup program specifying the cold core +file just built as the core file to load. This cold core file is set up +to do a significant amount of initialization and it is quite possible that +some bug will occur during this initialization process. After about a +minute, you should get a prompt of the form: +@begin(Example) +CMU Common Lisp kernel core image 2.7(?). +[You are in the Lisp Package.] +* +@end(Example) +The following two lines should then be entered. The first of these puts +you into the User package which is the package you should be in when the +full core is first started up. It is necessary to add this line, because +the current package is rebound while a file is loaded. The last line loads +in a file that loads in the compiler, hemlock, and some other files not yet +loaded. The open call is @b[essential] otherwise when the full core is +started up, load will try to close the file and probably invalidate memory +that is needed. When load is passed a stream, it does not automatically +close the stream. With a file name it now does after a recent bug fix. +This file prompts for the versions of the Lisp system, the compiler, and +hemlock. You should enter versions that make sense for your installation. +It then purifies the core image. Up to this point most of the Lisp system +has been loaded into dynamic space. Only a few symbols and some other data +structures are in static space. The process of purification moves Lisp +objects into static and read-only space, leaving very little in dynamic +space. Having the Lisp system in static and read-only space reduces the +amount of work the garbage collector has to do. Only those objects needed +in the final core file are retained. Finally, a new core file is generated +and is written to the file /usr/lisp/nlisp.core. Also, the currently +running Lisp should go through the default initialization process, finally +prompting for input with an asterisk. At this point you have successfully +built a new core file containing a complete Common Lisp implementation. + +This step takes about thirty minutes. + +@section(Debugging) +Debugging Lisp code is much easier with a fully functional Lisp. However, it +is quite possible that a change made in the system can cause a bug to happen +while running the cold core file. If this happens, it is best to use adb to +track down the problem. Unfortunately, the core file (i.e., the +remains of a process normally created by Unix when a process dies) generated by +such a bug will be of no use. To get some useful information, follow these +steps: +@begin(Enumerate) +Look at the file /usr/lisp/lisp.map and find the entry points for the +miscop routines error0, error1, and error2. These entry points are +used to invoke the Lisp error system from the miscops. Write down +the numbers beside these names. They are the addresses (in hex) of where +the miscops are located when the cold core file is loaded into memory. + +Run adb on the lisp file, i.e.: +@begin(example) +adb lisp +@end(Example) + +Set a breakpoint at the lispstart entry point: +@begin(Example) +lispstart:b +@end(Example) + +Start the lisp program running, telling it to use ilisp.core (I'm +assuming you're in /usr/lisp): +@begin(Example) +:r -c ilisp.core +@end(Example) + +After a while, you will hit the lispstart breakpoint. The core file has been +mapped into memory, but control is still in the C startup code. At this point, +you should enter breakpoints for all the error entry points described above. + +Continue running the program by typing :c. Shortly after this, the C lisp +program will give up control to Lisp proper. Lisp will start doing its +initialization and will probably hit one of the error break points. +At that point you can look around at the state and try and discover +what has gone wrong. Note that the two files rg and st are useful at this +point. Also, you should look at the document @i[Internal Design of Common +Lisp on the IBM RT PC] by David B. McDonald, Scott E. Fahlman, and Skef +Wholey so that you know the internal data structures. +@end(Enumerate) + +@section(Running the Soar Benchmark) +To compile the soar benchmark, you should do the following: +@begin(Example) +(compile-file "/usr/lisp/demos/soar/soar.lisp") +@end(Example) + +To run the benchmark, you should start up a fresh Lisp and do the following: +@begin(Example) +(load "/usr/lisp/demos/soar/soar.fasl") +(load "/usr/lisp/demos/soar/default.soar") +(load "/usr/lisp/demos/soar/eight.soar") +(user-select 'first) +(init-soar) +(time (run)) +@end(Example) +The first two lines load in the standard Soar system. The third line loads in +information about the eight puzzle which is a standard Soar puzzle that has +been run on several different machines. The fourth line sets up the puzzle +conditions so that it will select a goal to work on automatically. The fifth +line initializes Soar's working memory, etc. The final line is the one that +actually runs the benchmark. Soar prints out a fair amount of information as +it solves the puzzle. The final state should be numbered 143 when it finishes. +The time macro prints out information about information various resources after +the eight puzzle has run. + +@section(Summary) +I have tried to present sufficient information here to allow anyone to be +able to build a Common Lisp system under Mach from the sources. I am sure +there are many tricks that I have learned to use to reduce the amount of grief +necessary to build a system. My best recommendation is to go slowly. Start +by building a system from the sources provided on the tape. Make sure you +are comfortable doing that before you try modifying anything. + +Some hints on building the system which you may find useful: +@begin(Itemize) +If you change the compiler, you will have to recompile all the sources before +the change is reflected in a system. Changing the compiler is probably the +most dangerous change you can make, since an error here means that +nothing will work. In particular, this is the time you are going to need +to get familiar with adb and the internal structure of the Lisp, since a +serious error in the compiler will show up during the initialization of the +cold core file. + +Changing the miscops should be done with care. They follow a fairly rigid +convention and you should understand all the information provided in +@i[Internal Design of Common Lisp on the IBM RT PC] before making any changes +to miscops. You will probably need to get familiar with adb to debug some of +the changes. Note that this requires building a new cold core file and a final +core file before the change is reflected in the system. + +Changing sources in the code directory should be fairly straight forward. The +only time this will cause trouble is if you change something that a lot of +files depend on in which case you will have to recompile everything and build +a new cold core file and a core file. + +Changing hemlock should have no adverse effect on system integrity. + +If you make a fairly major change, it is a good idea to go through the complete +process of building a core file at least two or three times. If things are +still working at the end of this, your change is probably correct and shouldn't +cause any serious trouble. + +Finally, always keep at least one backup copy of a good core image around. +If you build a bad core file over an existing one and can't back up, it is +possible that you may not be able to recover from a serious error. +@end(Itemize)