Skip to content

Instantly share code, notes, and snippets.

@ADHdev
Last active January 20, 2018 17:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ADHdev/0f16992826cac3bcd00266f33dbceb87 to your computer and use it in GitHub Desktop.
Save ADHdev/0f16992826cac3bcd00266f33dbceb87 to your computer and use it in GitHub Desktop.
+help system using TinyMUX Data-agnostic storage system (AKA Cheapass Clusters)
//------------------------------------------------------------------------------
// Updates:
// 2018-01-20: Altered the break-out function AGAIN - it really didn't like
// when there were /'s and ='s in the code. Also took out the special
// formatting that occurs when there's a | in the display. Instead,
// you'll need to explicitly declare the name or key as "COLUMNIZE".
// Added COLUMNIZE to tags automatically so user won't have to think
// about that.
//------------------------------------------------------------------------------
// Requires: Cheapass Clusters, header(), footer(), alert(), boxtext(), and
// isstaff().
// -
// If you want to use the +help/stats function, be sure to change &VF to your
// Data Storage Functions module.
// -
// Also requires a boxtext() function which is something like this:
//------------------------------------------------------------------------------
// Function: get the user's screen width, min 50, max 80.
// Arguments:
// %0 - the user to get the width of
&f.get-width Global Functions Object=max(min(width(%0), 80), 50)
// Function: wrap text for display, optionally columnizing it.
// Arguments:
// %0 - the text to box
// %1 - the delimiter to split it by if a table is desired
// %2 - the number of columns to display in a table
// %3 - the user this is getting shown to (optional)
&f.globalpp.boxtext Global Functions Object=strcat(
setq(0, u(f.get-width, if(t(%3), %3, %#))),
setq(1, sub(%q0, 2)),
if(
or(t(%1), t(%2)),
strcat(
setq(2, %q1),
setq(3, if(t(%2), %2, 3)),
setq(4, sub(%q3, 1)),
wrap(table(%0, div(sub(%q2, %q4), %q3), %q2, %1 ), %q1, left, %b)
),
wrap(%0, %q1, left, %b)
)
)
//------------------------------------------------------------------------------
// COMMANDS
//------------------------------------------------------------------------------
// +help - list all help data (might be spammy)
// /find - hunt down a specific bit of data
// /tags - list all help tags - same as +help Tags
//
// Help data is stored like so: Name/Key(/Key2/Key3/etc). You can view it by
// typing out the full "Name/Key" sequence, or by getting at least the first
// part correct - +help Name/K would return either the only Name key that starts
// with K, or a list of possible Name/Keys starting with K.
//
// +help/stats - display basic data about the Help system.
//
//Finding entries:
//
// /find will let you dig a little deeper - you can search for all names
// containing the text "A" with the following:
//
// +help/find =A or +help/find NULL=A
//
// NULL means you aren't entering a value for that specific search term. You
// can also search for all names containing A with a key containing B like so:
//
// +help/find =A/=B or +help/find NULL=A/NULL=B
//
// Or you can just search for all names starting with A like so:
//
// +help/find A
//
// You can also search for a key starting with A like so:
//
// +help/find NULL/A
//
//Special Names and Keys:
//
// <anything> - Code: The value will be shown "as is", with no substitutions
// made. This is useful if you want to store code. Yes, you need the
// dash and spaces in exactly the format above.
//
// The following only apply when they are used as key names:
//
// NOINDEX: The "More Information" section of the entry will not appear. Value
// of this key must evaluate to 1 if passed to a t() function - so "1" would
// work, as would "True" or "no" or "piano".
//
// NOPARSE: The value of the name this key belongs to will not be parsed. Same
// t() test here.
//
// COLUMNIZE: Display the content of this name/key as 3 columns separated by the
// | character. When shown on one line, as in the More Information section,
// COLUMNIZE becomes itemize().
//
//
// Code: The value will be shown "as is", with no substitutions made.
//
// Nested keys: A key with a slash in it will be "nested" when displayed. For
// example, a key under "Cats" named "Goldie/Favorite Food" will only show up
// if the user types "+help Cats/Goldie", and will appear to be named "Favorite
// Food". That way you can nest data without a lot of repetitiveness in the
// main "Cats" entry.
//
//Special Values:
//
// Link:Name[/Key] - links to another name or name/key value. When displayed,
// that entry's text and name will be used instead. This can be helpful in
// only writing something ONCE but allowing it to be looked up in multiple ways.
// Note that linked entries still show up in searches, so that may be confusing
// for the user.
//
//Staff commands:
//
// +help/set Name=Value - every entry begins with a Name and a Value. The name
// must be unique - if it isn't, you're just overwriting another entry. The
// value can be anything - literally anything, including code. (Any code will
// be parsed through s() unless it is stored in a key named "Code".)
//
// +help/name Name=New Name - later, you might want to rename your entry to
// something new. Note that you'll have to change (or unlink) other entries
// that are linked to the one you want to rename first.
//
// +help/tag Name=Tag - think of tags as similar to hashtags, without the #.
// It's just a way of grouping things into arbitrary collections that can be
// listed under +help/tags (or +help Tags) or even +help <tag name>. What
// happens when tag names conflict with data names? Confusion. Don't do it if
// you can help it.
//
// +help/untag Name=Tag - opposite of /tag.
//
// +help/link Name=Target - creates a new entry named "Name" that has the value
// "Link:Target", which just shows the same entry as Target when +help Name is
// searched. Same with +help/link Name/Key=Target - Target can be a full entry
// or a sub-entry under another name and key.
//
// To unlink an entry, just set its text to a new value - including, if you
// really want to, Link:New Name. +help/set Name=Link:New Name would not check
// for the existence of an entry named "New Name" before changing, unlike /link.
//
// All of the above take a /quiet flag after the first /switch, which will
// prevent them from displaying any output at all.
//
// +help/wipe Name[/Key] - Delete an entry. You'll get a warning unless you pass
// the /unsafe flag after the /wipe. You must type out the full name and key
// you want to delete. This nukes the data right out of the database.
//
//------------------------------------------------------------------------------
@create Help Files <HF>=10
@set Help Files <HF>=safe
@create +help Commands <HC>=10
@set HC=safe inherit
@fo me=&vD HC=[num(Help Files <HF>)]
@fo me=&vF HC=[num(DSF)]
@tel Help Files <HF>=HC
//------------------------------------------------------------------------------
// FUNCTIONS
//------------------------------------------------------------------------------
// Locks.
&l.canset HC=isstaff(%0)
&l.canget HC=1
// Filter a list of keys to get only the current level.
// Args:
// %0 - the item from the list
// %1 - the current level of the call
&filter.is-same-level HC=or(and(not(t(strmatch(%1, */*))), not(strmatch(%0, */*))), and(strmatch(%0, %1/*), not(strmatch(rest(%0, %1/), */*))))
// Get all the data objects.
// -
// Args:
// %0 - target object
// Results: List of dbrefs.
&f.list-parents HC=if(t(parent(%0)), parent(%0) [ulocal(f.list-parents, parent(%0))])
// Expects %0 to be something like:
// name/key=value
// name=value
// name
// name/key
// name=value/key=value
// name=value/key
// %1 - is this a search?
// Produces:
// %qN: Name
// %qK: Key
// %qC: Name/Key
// %qV: Value
// %qF: Find value
// %qP: Second find value, if given
&f.break-out-values HC=strcat(
setq(A, 0),
if(
not(t(%1)),
strcat(
setq(V, rest(%0, =)),
setq(0, first(%0, =))
),
setq(0, %0)
),
if(t(%1),
switch(%q0,
*=*/*=*,
strcat(
setq(F, first(%qV, /)),
setq(P, rest(rest(%q0, /), =)),
setq(N, first(%q0, =)),
setq(K, first(rest(%q0, /), =)),
setq(A, 1)
),
)
),
if(not(t(%qA)),
switch(%q0,
*/*=*/*,
strcat(
setq(P, rest(%q0, /)),
setq(F, first(rest(%q0, =), /)),
setq(N, first(%q0, /)),
setq(K, first(rest(%q0, /), =)),
setq(A, 2)
),
*/*=*,
strcat(
setq(P, rest(%q0, =)),
setq(N, first(%q0, /)),
setq(K, first(rest(%q0, /), =)),
setq(A, 3)
),
*=*/*,
strcat(
setq(F, first(rest(%q0, =), /)),
setq(N, first(%q0, =)),
setq(K, if(t(%1), rest(%q0, =))),
setq(A, 4)
),
*=*,
strcat(
setq(F, %qV),
setq(N, first(%q0, =)),
setq(A, 5)
),
*/*,
strcat(
setq(N, first(%q0, /)),
setq(K, rest(%q0, /)),
setq(A, 6)
),
setq(N, %q0),
setq(A, 7)
)
),
setq(N, trim(%qN)),
setq(K, trim(%qK)),
if(not(t(%qV)), setq(V, NULL)),
setq(C, %qN),
if(t(%qK), setq(C, %qC/%qK))
)
// Takes %qN and %qK and validates that they exist.
// Produces:
// %qN: Name
// %qK: Key
// %qC: Name/Key
// %qE: Error
&f.validate-NK HC=strcat(
setq(E, getname(%vD, %0)),
if(
not(strlen(%qE)),
setr(E, #-1 NO NAME),
if(
not(t(%qE)),
%qE,
strcat(
setq(N, %qE),
if(
t(%1),
strcat(
setq(E, getname(%vD, %qN, %1)),
if(
not(strlen(%qE)),
setr(E, #-1 NO KEY),
if(
not(t(%qE)),
%qE,
setq(K, %qE)1
)
)
),
1)
)
)
),
if(t(%qN), setq(C, %qN)),
if(t(%qK), setq(C, %qC/%qK))
)
// Takes %qN and %qK and validates that they exist.
// Produces:
// %qF: Name
// %qP: Key
// %qJ: Name/Key
// %qD: Error
&f.validate-FP HC=strcat(
setq(D, getname(%vD, %0)),
if(
not(strlen(%qD)),
setr(D, #-1 NO TARGET NAME),
if(
not(t(%qD)),
%qD,
strcat(
setq(F, %qD),
if(
t(%1),
strcat(
setq(D, getname(%vD, %qF, %1)),
if(
not(strlen(%qD)),
setr(D, #-1 NO TARGET KEY),
if(
not(t(%qD)),
%qD,
setq(P, %qD)1
)
)
),
1)
)
)
),
if(t(%qF), setq(J, %qF)),
if(t(%qP), setq(J, %qJ/%qP))
)
//------------------------------------------------------------------------------
// VIEWS
//------------------------------------------------------------------------------
// View a specific name or name/key combo.
// %0: Name
// %1: Key
// %2: Display data
// %3: Person to display data to
// %4: Footer text, if any
// WARNING: There is nesting here - you can name a key "test/Test2" - Test2 will
// only show up if you're looking at the key "test". However, changing the
// names of the parent keys will break the automatic listing. Better to
// manually rename them, or just never change them. I thought about adding
// recursive checks on the setname functions, but decided it would be a lot of
// extra work for a very rare use-case.
&view.data HC=strcat(
if(
strmatch(%2, Link:*),
strcat(
setq(N, getname(%vD, first(rest(%2, :), /))),
if(t(rest(%2, /)), setq(K, getname(%vD, %qN, rest(%2, /)))),
setq(0, getdata(%vD, %qN, %qK)),
),
strcat(
setq(N, getname(%vD, %0)),
if(t(%1), setq(K, getname(%vD, %qN, %1))),
setq(0, %2)
)
),
setq(C, %qN),
if(t(%qK), setq(C, %qC/%qK)),
setq(L, filter(filter.is-same-level, listdata(%vD, %qN, if(t(%qK), %qK, NULL)), |, |, %qN[if(t(%qK), /%qK)])),
setq(M, t(getdata(%vD, %qN, if(t(%qK), %qK/)NOINDEX))),
setq(Q, t(getdata(%vD, %qN, if(t(%qK), %qK/)NOPARSE))),
setq(I, t(getdata(%vD, %qN, if(t(%qK), %qK/)COLUMNIZE))),
header(strcat(
%qN,
if(t(%1), strcat(
%b-%b,
%qK
))
), %3),
%R%R,
boxtext(case(1,
or(
strmatch(%qN, * - Code),
strmatch(%qK, * - Code),
strmatch(%qK, */Code),
match(%qK, Code),
%qQ
),
%q0,
s(%q0)
),if(t(%qI), |),,%3),
%R,
if(
and(
t(%qL),
not(%qM)
),
strcat(
setq(X, 20),
setq(W, sub(width(if(t(%3), %3, %#)), add(%qX, 2))),
%R,
header(More information, %3),
%R,
iter(%qL,
strcat(
%b,
ljust(left(rest(itext(0), %qC/), sub(%qX, 2)):%b, %qX),
setq(3, first(%qC, /)),
setq(2, rest(itext(0), first(%qC, /)/)),
setq(1,
case(1,
strmatch(setr(0, getdata(%vD, %q3, %q2)), Link:*),
getdata(%vD, first(rest(%q0, :), /), rest(%q0, /)),
or(
strmatch(%q2, * - Code),
strmatch(%q2, */Code),
match(%q2, Code),
t(getdata(%vD, %q3, if(t(%q2), %q2/)NOPARSE))
),
%q0,
t(getdata(%vD, %q3, if(t(%q2), %q2/)COLUMNIZE)),
itemize(%q0, |),
first(s(%q0), %r)
)
),
if(gt(strlen(%q1), add(%qW, 3)), setq(1, left(%q1, sub(%qW, 3))...)),
%q1
),
|, %r)
)
),
%R,
footer(%4, %3)
)
// View: list of data.
// Arguments:
// %0 - person to show list to
// %1 - list to display
// %2 - name searched
// %3 - value searched
// %4 - key searched
// %5 - key value searched
// %6 - replacement title if there is a preferred one
// %7 - footer text if desired
&view.list HC=strcat(
header(if(t(%6), %6, squish(trim(strcat(
Search,
%b,
if(t(%2), names starting with '%2', all names),
%b,
if(t(%3), containing text '%3'),
if(
or(t(%4), t(%5)),
strcat(
%b, for, %b,
if(t(%4), keys starting with '%4', all keys),
%b,
if(t(%5), containing text '%5')
)
)
)))), %0),
%R,
boxtext(%1, |,, %0),
%R,
footer(%7, %0)
)
//View - show stats
&view.stats HC=strcat(
[header()],
%R,
Base object: %vD,
if(
xget(%vF, vS),
strcat(
%R,
header(SQL mode),
%R,
Total DBref/Name combinations stored:,
%b,
sql(SELECT COUNT(DISTINCT Name, DBref) FROM tblDataValues WHERE DBref='%vD'),
%RTotal DBref/Name/Key combinations stored:,
%b,
sql(SELECT COUNT(DISTINCT Name, DataName, DBref) FROM tblKeyValues),
%R
),
strcat(
%R,
header(Parent mode),
%R,
Parents:,
%b,
if(t(setr(P, ulocal(f.list-parents, %vD))), %qP, None),
%R,
iter(%vD %qP, if(
t(itext(0)),
[header()]
%RName: [name(itext(0))] ([itext(0)])
%RAttributes: [attrcnt(itext(0))]
%RObject memory used: [objmem(itext(0))]
%RTotal names stored: [words(lattr(itext(0)/_N.*))]
%RTotal keys stored: [words(lattr(itext(0)/_F.*))]%R
),,@@)
)
),
[footer()]
)
//------------------------------------------------------------------------------
// CONTROLLER
//------------------------------------------------------------------------------
&cmd-+help HC=$+help*:@break ulocal(l.can[switch(%0, *=*, set, get)], %#)={
@switch/first 1=
not(strlen(%0)),
{@trigger me/switch.default=%#;},
and(strmatch(%0, /*), t(setr(S, first(lattr(me/switch.[first(rest(%0, /), if(strmatch(first(%0), /*/*), /))]*))))),
{
@trigger me/%qS=first(%0), rest(%0), %#;
},
strmatch(%0, %b*),
{@trigger me/switch.view=[trim(%0)], %#;},
{
@pemit %#=alert(+help) Command '[first(%0)]' not found. Available commands are: [itemize(iter(setdiff(lattr(me/switch.*), SWITCH.DEFAULT SWITCH.VIEW), +help/[lcstr(rest(itext(0), .))]))];
};
}; @pemit %#=alert(+help) You are not permitted to [switch(%0, *=*, set, get)] data.;
@set HC/cmd-+help=no_parse
//------------------------------------------------------------------------------
// SWITCHES
//------------------------------------------------------------------------------
// Show the data.
// Format: +help
// Result: list of data or error.
&switch.default HC=@assert t(setr(0, listdata(%vD)))={@pemit %0=alert(+help) No data found. %q0;}; @pemit %0=u(view.list,%0,%q0,,,,,All data)
// Set the data.
// Format: +help/set[/quiet] name[/key]=value
// Result: +help of the new data or nothing if quiet.
&switch.set HC=@eval u(f.break-out-values, %1); @assert t(%qN)={@pemit %2=alert(+help/set) Could not find the name from '%1'.;}; @assert or(t(setr(0, setdata(%vD, %qN, if(t(%qK), %qK, %qV), if(t(%qK), %qV)))), eq(strlen(%q0), 0))={@pemit %2=alert(+help/set) Set failed. %q0;}; @if not(strmatch(%0, */q*))={@trigger me/switch.view=[escape(%qC)], %2;};
// Set the data's new name.
// Format: +help/name[/quiet] name[/key]=new name
// Result: +help of the new data or nothing if quiet.
&switch.name HC=@eval u(f.break-out-values, %1); @assert t(%qN)={@pemit %2=alert(+help/name) Could not find the name from '%1'.;}; @assert and(not(t(setr(L, listdata(%vD,,, Link:%qN)))), not(t(setr(M, listdata(%vD,,,, Link:%qC)))))={@pemit %2=alert(+help/name) You cannot change the name of this [if(t(%qK), key, entry)] because other entries link to it by name. You will need to change these entries first: [itemize(setunion(if(t(%qL), %qL), if(t(%qM), %qM), |), |)];}; @assert or(t(setr(0, setname(%vD, %qN, if(t(%qK), %qK, %qV), if(t(%qK), %qV)))), eq(strlen(%q0), 0))={@pemit %2=alert(+help/name) Name set failed. %q0;}; @if not(strmatch(%0, */q*))={@trigger me/switch.view=[escape(if(t(%qK), %qN/%qV, %qV))], %2;};
// Get the data.
// Format: +help name or +help name/key
// Result: Data or a list of possible names it could be.
&switch.view HC=@eval u(f.break-out-values, %0); @assert t(setr(0, getdata(%vD, setr(N, first(%0, /)), setr(K, rest(%0, /)))))={@trigger me/tr.viewfail=%0, %1;}; @pemit %1=u(view.data, %qN, %qK, %q0, %1);
// If the view switch fails...
&tr.viewfail HC=@eval u(f.break-out-values, %0);
@break or(
and(eq(words(setr(L, listdata(%vD, %qN, %qK)), |), 1), t(%qL)),
and(
t(%qL),
eq(words(setr(L, squish(trim(iter(%qL, if(eq(words(%0, /), words(itext(0), /)), itext(0)), |, |), b, |), |)), |), 1)
)
)={@pemit %1=u(view.data, first(%qL, /), rest(%qL, /), getdata(%vD, first(%qL, /), rest(%qL, /)), %1, One item found - displaying data.);};
@break t(setr(0, listdata(%vD, Tags, %0)))={@pemit %1=u(view.list, %1, getdata(%vD, Tags, rest(%q0, /)),,,,, getname(%vD, Tags, rest(%q0, /)), All [getname(%vD, Tags, rest(%q0, /))] tags);};
@pemit %1=strcat(
alert(+help),
%b,
%0 not found.,
if(
t(%qL),
strcat(
%b,
Did you mean,
%b,
itemize(%qL, |, or),
?
)
)
);
// Wipe data.
// Format: +help/wipe[/unsafe] name[/key][=YES]
// Result: Error, "are you sure", or "your data has been wiped"
&switch.wipe HC=@eval u(f.break-out-values, %1);
@assert t(u(f.validate-NK, %qN, %qK))={@pemit %2=alert(+help/wipe) Could not find the name '%qC': %qE.;};
@assert and(not(t(setr(L, listdata(%vD,,, Link:%qN)))), not(t(setr(M, listdata(%vD,,,, Link:%qC)))))={@pemit %2=alert(+help/name) You cannot wipe this [if(t(%qK), key, entry)] because other entries link to it by name. You will need to wipe these entries first: [itemize(setunion(if(t(%qL), %qL), if(t(%qM), %qM), |), |)];};
@break or(
match(first(%0), /wipe/unsafe),
and(
match(%qV, YES),
t(setr(0, xget(%2, _wipedata))),
match(first(%q0, |), %qC, |),
lt(sub(secs(), rest(%q0, |)), 300)
)
)={
@assert or(t(setr(0, wipedata(%vD, %qN, %qK))), eq(strlen(%q0), 0))={@pemit %2=alert(+help/wipe) %q0;};
@pemit %2=alert(+help/wipe) The data on %qC has been wiped.;
};
@pemit %2=alert(WARNING) You are about to delete %qC. [if(not(t(%qK)), if(t(setr(1, listdata(%vD, %qN, NULL))), %bThis data has [words(%q1, |)] keys.))] Are you sure? If so, type +help/wipe %qC=YES;
&_wipedata %2=%qC|[secs()];
// Switch: +help/find
// Format:
// +help/find
// +help/find <name>
// +help/find <name>/<key>
// +help/find <name>=<value>
// +help/find <name>/<key>=<value>
// +help/find <name>=<value>/<key>
// +help/find <name>=<value>/<key>=<value>
// Result:
// List of data matching full or partial criteria
&switch.find HC=@eval strcat(u(f.break-out-values, %1, 1), if(not(t(%qN)), setq(N, NULL))); @assert t(setr(0, listdata(%vD, %qN, %qK, %qF, %qP)))={@pemit %2=alert(+help/find) Data not found. %q0;}; @pemit %2=if(eq(words(%q0, |), 1), u(view.data, setr(N, first(%q0, /)), setr(K, rest(%q0, /)), getdata(%vD, %qN, %qK), %2, One item found - showing data.), u(view.list, %2, %q0, case(NULL, %qN,, %qN), case(NULL, %qF,, %qF), case(NULL, %qK,, %qK), case(NULL, %qP,, %qP)));
// Show some data stats.
// Format: +help/stats
// Result: The stats.
&switch.stats HC=@pemit %2=u(view.stats)
// Link two entries
// Format: +help/link <name>[/<key>]=<target>
// Result: Nothing if /quiet, otherwise a view of the linked data.
&switch.link HC=@eval u(f.break-out-values, %1); @assert t(u(f.validate-NK, %qN, %qK))={@pemit %2=alert(+help/link) Could not find the name '%qC': %qE.;}; @assert t(u(f.validate-FP, %qF, %qP))={@pemit %2=alert(+help/link) Could not find the target name '%qJ': %qD.;}; @assert or(t(setr(0, setdata(%vD, %qN, if(t(%qK), %qK, Link:%qJ), if(t(%qK), Link:%qJ)))), eq(strlen(%q0), 0))={@pemit %2=alert(+help/link) Link failed. %q0;}; @if not(strmatch(%0, */q*))={@trigger me/switch.view=[escape(%qC)], %2;};
// Switch: Tags
// Format: +help/tag <name>[/<key>]=<tag>
// Result: Either nothing or the altered data.
&switch.tag HC=@eval strcat(u(f.break-out-values, %1), setq(R, getname(%vD, Tags, %qV)), if(not(t(%qR)), setq(R, %qV))); @assert t(u(f.validate-NK, %qN, %qK))={@pemit %2=alert(+help/tag) Could not find the name '%qC': %qE.;}; @assert and(or(t(setr(0, setdata(%vD, %qN, if(t(%qK), %qK/Tags, Tags), setunion(getdata(%vD, %qN, if(t(%qK), %qK/Tags, Tags)), %qR, |)))), eq(strlen(%q0), 0)), or(t(setr(1, setdata(%vD, Tags, %qR, setunion(getdata(%vD, Tags, %qR), %qC, |)))), eq(strlen(%q1), 0)), or(t(setr(2, setdata(%vD, Tags, %qR/COLUMNIZE, 1))), eq(strlen(%q2), 0)), or(t(setr(3, setdata(%vD, %qN, if(t(%qK), %qK/)Tags/COLUMNIZE, 1))), eq(strlen(%q3), 0)))={@pemit %2=alert(+help/tag) Tag set failed. %q0%q1%q2%q3;}; @if not(strmatch(%0, */q*))={@trigger me/switch.view=[escape(%qC)], %2;};
&switch.untag HC=@eval strcat(u(f.break-out-values, %1), setq(R, getname(%vD, Tags, %qV)), if(not(t(%qR)), setq(R, %qV)), setr(W, setdiff(getdata(%vD, Tags, %qR), %qC, |)), setr(V, setdiff(getdata(%vD, %qN, if(t(%qK), %qK/Tags, Tags)), %qR, |))); @assert t(u(f.validate-NK, %qN, %qK))={@pemit %2=alert(+help/untag) Could not find the name '%qC': %qE.;}; @assert and(or(t(setr(0, setdata(%vD, %qN, if(t(%qK), %qK/Tags, Tags), if(t(%qV), %qV, NULL)))), eq(strlen(%q0), 0)), or(t(setr(1, setdata(%vD, Tags, %qR, if(t(%qW), %qW, NULL)))), eq(strlen(%q1), 0)), if(not(t(%qW)), eq(strlen(setr(2, wipedata(%vD, Tags, %qR))), 0), 1), if(not(t(%qV)), eq(strlen(setr(3, wipedata(%vD, %qN, if(t(%qK), %qK/Tags, Tags)))), 0), 1))={@pemit %2=alert(+help/untag) Tag unset failed. %q0%q1%q2%q3;}; @if not(strmatch(%0, */q*))={@trigger me/switch.view=[escape(%qC)], %2;};
// Shortcut: Effectively the same as "+help <shortcut name>".
// Format: +help/<shortcut>
// Result: The special data view.
&switch.tags HC=@pemit %2=u(view.data, Tags,, getdata(%vD, Tags), %2, +help Tags/<tag> for more information)
&CMD-+JHELP HC=$+jhelp:@force %#=+help Jobs;
&CMD-+JHELP_STUFF HC=$+jhelp *:@force %#=+help Jobs/%0;
&CMD-+BBHELP HC=$+bbhelp:@force %#=+help BBoards;
&CMD-+BBHELP_STUFF HC=$+bbhelp *:@force %#=+help BBoards/%0;
// -----------------------------------------------------------------------------
// Tags placeholder
// -----------------------------------------------------------------------------
+help/set Tags=This is a list of tags to help with grouping help files. With tags, you can type +help <tag name> and get a list of the tagged files.
+help/set Help=+help - list all help data (might be spammy)%R %b/find - hunt down a specific bit of data%R %b/tags - list all help tags - same as +help Tags%R%RHelp data is stored like so: Name/Key(/Key2/Key3/etc). You can view it by typing out the full "Name/Key" sequence, or by getting at least the first part correct - +help Name/K would return either the only Name key that starts with K, or a list of possible Name/Keys starting with K.%R%R+help/stats - display basic data about the help system.
+help/set Help/Finding Data=/find will let you dig a little deeper - you can search for all names containing the text "A" with the following:%R%R %b+help/find =A or +help/find NULL=A%R%RNULL means you aren't entering a value for that specific search term. You can also search for all names containing A with a key containing B like so:%R%R %b+help/find =A/=B or +help/find NULL=A/NULL=B%R%ROr you can just search for all names starting with A like so:%R%R %b+help/find A%R%RYou can also search for a key starting with A like so:%R%R %b+help/find NULL/A
+help/set Help/Special Names and Keys=<anything> - Code: The value will be shown "as is", with no substitutions made. This is useful if you want to store code. Yes, you need the dash and spaces in exactly the format above.%R%RThe following only apply when they are used as key names:%R%RNOINDEX: The "More Information" section of the entry will not appear. Value of this key must evaluate to 1 if passed to a t() function - so "1" would %bwork, as would "True" or "no" or "piano".%R%RNOPARSE: The value of the name this key belongs to will not be parsed. Same%Rt() test here.%R%RCOLUMNIZE: Display the content of this name/key as 3 columns separated by the | character. When shown on one line, as in the More Information section, COLUMNIZE becomes itemize().%R%RCode: The value will be shown "as is", with no substitutions made.%R%RNested keys: A key with a slash in it will be "nested" when displayed. For example, a key under "Cats" named "Goldie/Favorite Food" will only show up if the user types "+help Cats/Goldie", and will appear to be named "Favorite Food". That way you can nest data without a lot of repetitiveness in the main "Cats" entry.
+help/set Help/Special Values="Link:Name[/Key]" - links to another name or name/key value. When displayed, that entry's text and name will be used instead. This can be helpful in only writing something ONCE but allowing it to be looked up in multiple ways. Note that linked entries still show up in searches, so that may be confusing for the user.
+help/set Help/Staff Commands=+help/set Name=Value - every entry begins with a Name and a Value. The name must be unique - if it isn't, you're just overwriting another entry. The value can be anything - literally anything, including code. (Any code will be parsed through s() unless it is stored in a key named "Code".)%R%R+help/name Name=New Name - later, you might want to rename your entry to something new. Note that you'll have to change (or unlink) other entries that are linked to the one you want to rename first.%R%R+help/tag Name=Tag - think of tags as similar to hashtags, without the #. It's just a way of grouping things into arbitrary collections that can be listed under +help/tags (or +help Tags) or even +help <tag name>. What happens when tag names conflict with data names? Confusion. Don't do it if you can help it.%R%R+help/untag Name=Tag - opposite of /tag.%R%R+help/link Name=Target - creates a new entry named "Name" that has the value "Link:Target", which just shows the same entry as Target when +help Name is searched. Same with +help/link Name/Key=Target - Target can be a full entry or a sub-entry under another name and key.%R%RTo unlink an entry, just set its text to a new value - including, if you really want to, Link:New Name. +help/set Name=Link:New Name would not check for the existence of an entry named "New Name" before changing, unlike /link.%R%RAll of the above take a /quiet flag after the first /switch, which will prevent them from displaying any output at all.%R%R+help/wipe Name[/Key] - Delete an entry. You'll get a warning unless you pass the /unsafe flag after the /wipe. You must type out the full name and key you want to delete. This nukes the data right out of the database.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment