Module:Buffer/doc: Difference between revisions

Content deleted Content added
save
save
Line 7:
This was originally developed to optimize string concatenation as a helper method within [[Module:Asbox]], but has been generalized for all modules.
 
The interface for [[#Buffer object|Module:Buffer objects]] is similar to that of {{Luaref|HTML_library|mw.html}} objects in that you may build complex strings with independent child nodes. In most cases, you may use Buffer objects like a normal string, including using {{code|..}} operator (though {{luaself|:_}} has the same role, but potentially [[#performance|over 10 times faster than {{code|..}}]]). See also: [[#Calling stringString, mw.ustring, and mw.text libraries]]
 
Additionally, there are fourseveral specialized forms and extended objects, described further in their respective sections: {{luaself|:stream|plain=y}}, {{luaself|\-HTML|plain=y}}, {{luaself|pre=Element-|\|plain=y}} and {{luaself|\-variable|plain=y}}.
:{|
|-
|<div style="margin:0; column-width: 15em; -moz-column-width: 15em; -webkit-column-width: 15em; width:49em">
*{{luaself|:stream|plain=y}}
*{{luaself|\-HTML|plain=y}}
*{{luaself|pre=Element-|\|plain=y}}
*{{luaself|\-variable|plain=y}}
*{{luaself|\-nil|plain=y}}
*[[#_G object|_G (callable)]]
|}
 
Last but not least, this module has an ordered [[#require'Module:Buffer'.__pairs|__pairs]] which can be more thorough than {{luaref|ipairs||y}} and {{luaref|pairs||y}}. (Even reads nil keys!)
The <span title='This is a bad Star Trek joke'>logical uniqueness</span> of this iterator may be reason enough to {{luaref|require|assimilate|y}} Module:Buffer.
 
==Basic usageUsage==
{{anchor|module|initialize}}
===require'Module:Buffer'===
{{luaself|Buffer|\|require'Module:***'|subpage= |args=...|args2=_G, name, save, ...}}
 
Creates a new Module:Buffer object when it (the module) returned by {{luaref|require||y}} is called as a function{{--}}i.e., there is no 'main'.
 
Because <span title='i.e. never accept arguments'>{{luaref|mw.html:done|public methods with useless parenthesis|y}}</span> are a [[pet peeve]] of this developer, this forwards arguments to {{luaself|:_}};
Pass the module (or {{luaself|:_in}}) your global variable {{luaref|_G}} to enable [[#Global functions|global functions]]. If passed _G then the next two {{luaref|varargs|plain=y}} will pass to {{luaself|:_G}} and any extra will pass to {{luaself|:_}}. If initialized without _G then all varargs will pass to {{luaself|:_|args=...}}.
if that op is not desired, you may chain a Buffer object function directly to the module and the self-action will be redirected to a new Buffer object{{--}}
i.e [[#Buffer:_inHTML|{{code|lang=lua|require'Module:Buffer':_inHTML'table'}}]] is equivalent to {{code|lang=lua|require'Module:Buffer'():_inHTML'table'}}.<ref group=note>For your convience, the self operator {{code|:}} and {{code|.}} are interchangable when used on the Module directly, though the self-op is required for nearly all other interactions with objects created by the Module.</ref>
 
The global variable {{luaref|_G}} is "magic" when passed as the first arg. Such enables the [[#Global functions|global functions]] and,
You may also use most Buffer object functions directly on the module{{--}}i.e <code style='white-space:nowrap'>require'Module:Buffer':''function''{...}</code> is equivalent to <code style='white-space:nowrap'>require'Module:Buffer'():''function''{...}</code>.<ref group=note>For your convience, the self operator {{code|:}} and {{code|.}} are interchangable when used on the Module directly, though the self-op is required for nearly all interactions with objects created by the Module.</ref>
if followed by a <code>name<code> string, will declare the new Buffer with that name in the global scope. If the argument following ''name'' is a table with no metatable,
it and any other {{luaref|varargs||y}} are forwarded to {{luaself|:_all}}; otherwise, as long as the first vararg is not nil or false, this passes them to {{luaself|:_}}.<ref group=note>
Passing arguments after ''name'' is primary used when {{luaself|:_in}} is indirectly called by {{luaself|:_all}} or {{luaself|pre=Element-|:_add}}. For example:<br />
{{code|1=require'Module:Buffer'(_G,'arg'):_all({'arg',arg and {' was saved:' ,_in={_G, 't', ' awesome'}{{))}}, true):_(t and {t(), t..'r', t..'st'})|lang=lua}}<br />
produces: {{code|arg was saved: awesome awesomer awesomest|lang=lua}}</ref> The _G passed may also gain a __call {{luaref|Metatables|metamethod|y}} (details at [[#G object|_G object]]).
 
{{anchor|recycling}}
===Buffer===
As a final note, if you {{luaref|require|plain=y}} text from a module which returns a Buffer object, it may be more efficient to create a new Buffer via chaining {{luaself|Buffer|:_in|:_in|args=()}}
after a require statement for the other module and use {{luaself|:_parent|args=()}} at the point where you would append the required text. (Best add {{code|lang=lua|--:_in == indirect require Module:Buffer}}
so future editors won't hunt for {{code|lang=lua|function ...:_in}} at the other module)
 
===Basic functions===
{{luaself|\|args=sep, i, j}}{{anchor|call Buffer object}}
{{anchor|Buffer}}
====Buffer object====
 
{{luaself|\|args=sep, i, j}}
:''See also {{luaself|:_str|plain=y}} for advanced string conversion.
 
Get a Buffer as typea {{luaref|string||y}} by performingvia a function call on the Buffer ''object'' (as opposed to [[#require'Module:Buffer'|a call on the ''Modulemodule'']]).
Calling a BufferThis is basically shorthand for {{luaref|table.concat|args=Buffer, ...}}, or, with no args, {{luaref|tostring|args=Buffer}}.
However, if this Buffer holds [[#raw|raw values]]<ref group=note name=raw /> or {{luaref|sequence|non-sequential values|y}},
However, if your Buffer contains [[#raw|raw]] objects or out-of-sequence values, then the return string would be result of {{luaself|Buffer|:_all|empty-buffer:_all|args=Buffer )( ...}} instead.<ref group=note name=raw />
then the return is built from an iteration of its values through <code>new-{{luaself|:_all|plain=yes|args=Buffer}}</code> (or a similar process).
{{anchor|relative}}
 
Unconventionally, any <code>i</code> or <code>j<code> of type {{luaref|string|plain=y}} would be treated as ''relative to length'';
Line 38 ⟶ 65:
(obviating the need to set a {{luaref|Local variable declarations|local|y}} Buffer to use the {{luaref|Length operator|length operator|y}}).
 
Note you domay not need to stringappend a Buffer object towithout appendtostring itcoercion to an {{luaref|HTML library|mw.html|y}} object via {{luaref|mw.html:node}} (though not mw.html:wikitext because of type checking).
 
=====Buffer.last_concat=====
 
When strung without a separator([[#valid|valid]]) <code>sep</code>, the result mayis becached retrievedat via {{<code|>Buffer.last_concat}}</code>. Future tostring operations on the Buffer will return Buffer.last_concat until it is modified.
Until purged, future calls to return that Buffer as a string will return this index instead.<ref group=note>
Using {{luaself|:_var|plain=y}} prevents future caching on all Buffers, though Buffers which already
unmodified Buffers will continue to return their cached version</ref>
 
This should clear automatically whenever a Buffer object function changes the contents of a Buffer.
You may purge the cache by setting this key to nil, by appending a [[#valid|valid value]] and/or removing a value: <code>{{luaself|:_|args=(0)|plain=y}}[[#Buffer:_nil|:_nil()]]</code>, as well as by passing nothing to {{luaself|:_c|args=() }}.
You may manually purge the cache by setting this key to nil, as well as by passing nothing to {{luaself|:_cc|args=()}}.
 
====Buffer:_====
 
{{luaself|:_|args='string'|args2=value, pos, raw|args3=value, raw}}
Line 58 ⟶ 89:
* {{luaref|boolean||y}}
* {{luaref|nil||y}}
* empty {{luaref|string||y}}, or any table such that {{luaref|tostring|args=table}} returns an empty string (or nil/false)
* {{luaref|table||y}} without a {{luaref|Metatables|__tostring metamethod}} and which {{code|table[1]|lang=lua}} is nil or false.
 
A table with no __tostring will pass through {{luaref|table.concat}} before insertion. An {{luaref|error|plain=y}} may be thrown if the table would cause table.concat to error.
(Use {{luaself|:_all}} instead for such tables.) For all other ''value'', the result of {{luaref|tostring|args=value}} would be inserted so long as it is not an empty string.
 
When passed {{code|pos}} of type {{luaref|number|plain=y}}, the argument is identical to ''pos'' for {{luaref|table.insert|args=table, pos, value}}.
Line 70 ⟶ 101:
{{anchor|raw}}
 
Set {{code|raw}} to true to force append a ''value'' without tostring coercion, including [[#no-op|invalid]] values.<ref group=note name=raw>
<refAppending group=notea value name="raw>Setting"{{--}}i.e. awithout Bufferstring totype ''coercion{{--}} sets "raw'' mode", which incurs a [[#performance|performance]] penalty for all future tostring ops as it must re-validate each indexed value
throughbecause future tostring ops would have to [[#valid|re-validate]] every value in the Buffer via {{luaself|:_all}} to insert them in a new table before passing that to table.concat (vs. passing itself directly).
That said, re-stringing a raw Buffer is still usually several times faster than using the {{luaref|Concatenation operator|..}} op to join an equivalent number of strings.
(See [[#Tips]] for ways to avoid using raw)</ref>
If given only two (non-self) arguments with the second being a boolean, then the second is read as ''raw'' instead.
 
====Buffer:_nil====
 
{{luaself|:_nil|args=pos, replacement|args2=replacement}}
 
Removes the value buffered at {{code|pos}}. As with {{luaself|:_}}, a string ''pos'' string is treated as {{code|lang=lua|#Buffer + pos}}.
 
If a non-boolean {{code|replacement}} is provided, then thisit will replace the value indexed at the ''pos''. index as long as ''replacement'' is notPassing a boolean, inas whichthe case,second thisargument is a no-op.
 
When ''replacement'' is nil, the op is simplybasically {{luaref|table.remove|args=Buffer, pos}}. As with stringthe ''pos''positional relativearguments toof length.other Buffer methods,
any numerical string ''pos'' is added to length, such that {{luaself|:_nil'0'}} removes the last item.
Note however there is no further type checking on replacement, so, if nil nor boolean, then Buffer will be set to [[#raw|raw mode]].
Note the only type check on replacement is a direct comparison to both booleans (nil is implied).
Any other type, including strings, sets the Buffer to [[#raw|raw mode]].
 
A ''pos'' that is omitted, nil, or false has the same effect as though <code>'0'</code> (or {{luaself|pre=#|\}}) were passed.
Pos cannot be omitted if replacement is passed, though a ''pos'' that is nil will be treated as <code>{{#tag:syntaxhighlight|#Buffer|lang=lua|enclose='none'}}</code>.
Given only one non-numerical argument which evaluates true but {{luaref|tonumber||y}} would return it as nil , this treats it as though it were passed as the second.
If passed a non-numerical ''pos'' with any other argument, including nil, this is effectively a no op (though may still purge the [[#Buffer.last_concat|cache]]).
 
====Buffer:_all====
 
{{luaself|:_all|args={ value, ... }|args2={ ..., value = pos, functionName = args, ... }, nanKeys}}
 
Takes a table {{code|value}}, iterates through all number keys {{luaref|table.sort|in order|plain=yes}}, appending each [[#valid|valid]] value to the end of the Buffer.
In contrast to {{luaref|ipairs}}, this starts at the most negative key (down to {{luaref|math.huge|-inf|y}}), continuesand throughends anyat nilthe keysmost positive index,
untilcontinuing itthrough reachesany thenil most positive indexkeys and includes [[Double-precision floating-point format|non-integer number keys]]. (Note: despite more thorough iteration,
the runtime of Module:Buffer.__pairs is almost statisically indistinguishable from that of ipairs in most cases. Details at [[#Performance|Performance]] and [[#MBpairs|require'Module:Buffer'.__pairs]].)
(Note: despite Module:Buffer.__pairs having a more thorough iteration than ipairs, the difference in their runtimes is almost statistically insignificant.
Details at [[#Performance]] and [[#Using the iterator outside of buffer]].)
 
A table ''value'' that has no metatable will have its contents iterated by this function before moving on to the next value. All other data types are processed by {{luaself|:_}}.
Line 106 ⟶ 140:
Keep in mind such keys are iterated in {{luaref|next|no particular|y}} order, though an order may be imposed by wrapping each pair in a table indexed at a number key.
 
If given a <code>''value'' = ''pos''</code> pair, defined as a number or number string indexed at a non-number key,
then they will be passed as the {{code|value}} and {{code|pos}} arguments for {{luaself|:_|plain=y}}. Thus,
:{{luaself|:_all|args=({1,2,3,'... done',[3.5]=variable and 4 or {four='1',zero=1{{))}},true)}}
 
Thus, {{luaself|:_all|args=({1,2,3,'... done',[3.5]=variable and 4 or {four='1',zero=1{{))}},true)}} produces the same result as:
:{|
{{#tag:syntaxhighlight|Buffer:_(1):_(2):_(3)
|{{#tag:syntaxhighlight|Buffer:_(1):_(2):_(3)
if variable then
Buffer:_(4)
else
Buffer:_'four':_('zero',1)--vs :_all{four='1',zero=1}; less redundant would be
end -- :_all{'four',zero=1}, but that doesn't demo string numbers
end
Buffer:_'... done'|lang=lua}}
--returns "1234... done" if variable evaluates true or "zero123four... done" if false|lang=lua}}
|}
 
If a non-number key points to a value that cannot be coerced into a {{luaref|tonumber|coerced into a number|y}} then the pair may be treated as <code>''functionName'' = ''args''</code>,
Line 122 ⟶ 161:
<ref group=note>In other words, if ''args'' is a string or a table without [1] set, it will be passed as the only argument.
Further note it is not possible to pass a <code>''functionName'' = ''args''</code> pair where ''args'' is numerical since such would be read as <code>''value'' = ''pos''</code>.
Finally, passing a function type as ''args'' will throw an error message.</ref>
For example:
 
====Buffer:_in====
{{code|1=p(_G,'arg', true):_all({'arg',arg==true and {'==true: ' ,_in={_G, 't', nil, ' awesome'}{{))}}, true):_(t and {t(), t..'r', t..'st'})|lang=lua}}
 
produces: {{code|1='arg==true: awesome awesomer awesomest'|lang=lua}}
 
===Buffer:_in===
 
{{luaself|:_in|args=...|args2=_G, name, save, ...}}
 
Passes any arguments to [[#require'Module:Buffer'|Module:Buffer]] to create a new Buffer object and then, sets an external reference to the parent Buffer and returns the child.<ref group='note' name='in-dependents'>
<ref group='note' name='in-dependents'>There is no 'getChild' method. If a child needed after returning to the parent, set it {{luaref|Local_variable_declarations|locally|y}}
or use {{luaself|:_G}} prior to returning or it may become irretrievable. (No, {{u|Codehydro}} did not get lazy.
Rather, this allows {{luaref|Garbage collection|garbage collection|y}} on children with no further purpose.)</ref>
 
This does <u>not</u> append the child to the parent. (See {{luaself|:_out|plain=y}})
Line 141 ⟶ 176:
Also, be aware that Buffer parent references are {{luaref|weak tables|weak|y}}. Thus, if you were to (re-)set a local variable that is currently set to the parent, such could trigger immediate garbage collection on the parent.
 
====Buffer:_out====
 
{{luaself|:_out|args=sep|args2=ops, sep{{ndash}}list, { default{{ndash}}sep, ..., [#] = sep }|args3=0, sep}}
 
[[#Buffer object|Joins a Buffer]] with {{code|sep}} and appends result to its parent, which is returned.
If no parent is found, this is a no-op and returns the same Buffer.
 
When given two or more arguments, this reads the first as {{code|ops}}{{--}}the number of :_out() operations to perform.<ref group=note>
For [[#performance]], the first arg is not type checked but read as ''ops'' only when multiple args are present (or if it is the number 0);
i.e., <code>Buffer:_out(2)</code> uses <code>2</code> as a separator. To append to the ''N''th ancestor with no separator,
use <code>Buffer:_outs(''N'', nil)</code>.</ref> This applies the first ''sep'' in {{code|sep-list}} for the current Buffer,
the second for its parent, the third for its grandparent, and so on.
 
If the last item is a {{luaref|table||y}}, then any ''nil'' in ''sep-list'' default to <code>''table''[1]</code>;
{{luaself|:_out|args=sep|args2=outs, sep{{ndash}}list, { default{{ndash}}sep, ..., [out] = sep } }}
any ''false'' mean "no-sep".<ref group=note>Empty strings would produce the same output as false, however,
Lua string literals create objects that take up memory until garbage collected.</ref> The table may be the second arg (i.e. ''sep-list'' may be omitted).
If it has other keys, then ''table[n]'' would apply instead of ''table[1]'', making these synonymous:<br />
{{luaself|:_out|args=4, nil, nil, nil, ' and ', {', '} }} and {{luaself|:_out|args=4, {', ', [4] = ' and '} }}.
 
The number {{code|lang=lua|0}} is "magic" when passed as the first arg (even by itself),
{{luaref|table.concat|Joins|y}} Buffer with {{code|sep}} and appends result to the parent Buffer. Returns the parent. If no parent is found, this is a no-op and returns the same Buffer.
joining and appending to the same Buffer ''after'' it has been {{luaself|Buffer|:_cc|emptied|y}}.
This is the only method by which a Buffer in [[#raw|raw mode]] may lose that status. Parent references are preserved.
 
====Buffer:_str====
If given more than one (non-self) argument, then the first is read as {{code|outs}}{{--}}the number of :_out() operations to perform.<ref group=note>The first argument is not type checked. For [[#performance]], it is read as ''outs'' only when there are multiple varargs. In other words, <code>Buffer:_out(2)</code> will use <code>2</code> as the separator. To append ''N'' generations to their parent with no separator, use <code>Buffer:_outs(''N'', nil)</code>.</ref> Each additional argument in ''{{code|sep-list}}'' is applied as ''sep'' for that :_out operation. That is, the first ''sep'' applies to the current Buffer, the second to its parent Buffer, the third to its grandparent, and so on.
 
{{luaself|:_str|args=sep|args2=ops, sep{{ndash}}list, { default{{ndash}}sep, ..., [#] = sep } }}
If the last vararg is a table, then ''table''[1] will be applied as the default separator for all nil varargs in ''sep-list''. This table may directly follow ''outs'' (i.e. ''sep-list'' may be omitted). If it contains other keys, then the ''sep'' at key ''N'' would apply for the ''N''th :_out() instead of ''default-sep''. Thus, these two snippets are synonymous: {{luaself|:_out|args=4, nil, nil, nil, ' and ', {', '} }} and {{luaself|:_out|args=4, {', ', [4] = ' and '} }}. Include false in ''sep-list'' to indicate no separator applies for that ''out''.<ref group=note>An empty string would produce the same output as false, however, Lua strings, even empty ones, take up memory until garbage collected.</ref>
 
Joins a Buffer with {{code|sep}} and returns the string.
===Buffer:_str===
 
This uses the same helper method as {{luaself|:_out}} to handle multiple arguments, with which, if provided, this creates a new temporary Buffer to which this appends the results of the number of generations specified by <code>ops</code>, with each ancestor in front of its descendants. This then performs one additional concat op using the ''sep'' at {{code|ops + 1|lang=lua}} and returns the result. If a parent-less Buffer is reached before ''ops'', then the sep that follows its op number will separate the generations.
{{luaself|:_str|args=sep|args2=generations, sep{{ndash}}list, { default{{ndash}}sep, ..., [gen] = sep } }}
 
When no [[#valid|valid]] ''sep'' is given, this returns a string identical to what would append to the next ancestor, if those arguments were passed to Buffer:_out instead and one additional :_out() op performed.
Joins a Buffer with {{code|sep}} and returns the string. Varargs are handled by the same function as {{luaself|:_out}}, which, if provided, this will create a new temporary Buffer and backtrack the number of {{code|generations}} specified, inserting each ancestor in front of its descendants in the temporary Buffer. The ''sep'' indexed at {{code|generations + 1|lang=lua}} will be used as the joiner for the temporary Buffer (unless the first ancestor is reached before the specified number of generations, in which case it is the index following that of the original generation).
 
UnlikeHowever, :_out,because this does not actually append the child intoBuffers theto their parent. As such, even with the same arguments, itresult may returndiffer awhere different result thanthis would be obtained from stringinginsert the returnsep ofat :_out since''ops each+ parents' ''sep1'' isand notBuffer:_out usedwould toplace jointhe parent and child. Furthermore, the number of ''generations''s countedsep includesbetween theit currentand Buffer,its whereaschild the number of "''outs''" in Buffer:_out does notinstead.
 
====Buffer:_cc====
 
{{luaself|:_cc|args=clear|args=clear, copy, meta|args2=0, true}}
Line 174 ⟶ 226:
This replicates length by filling clear halfway to the length of copy (the minimum needed to 'trick' Lua) and then setting nil every key that would not trigger recalculation.
As a result, keys that would resize ''clear'' when set nil are left as empty strings. Such should be fairly rare;
given tables representing every possible way to position a single nil key for all lengths between 2 and 32 (inclusive), only 8.39 percent of such tables would have its nil copied as an empty string instead.
Also note that tables returned from {{code|lang=lua|Buffer:_(0, copy)}} have length declared on creation instead, and thus won't have extra strings attached.
The odds can be estimated using <math>y {{=}} 0.5 / \sqrt{x}</math>, where <math>y</math> is the upper limit that an arbitrary nil key from ''copy'' of length ranging from 1 to <math>x</math> is imaged as an empty string.</ref>)
Line 180 ⟶ 232:
* Includes Buffer parent and [[#raw|raw]] attributes (stored externally)
 
To obtain the table of key-value pairs left as empty strings in the previous copy op in a table, simply call this again such with anyvalue samesuch valuethat passed<code>rawequal(clear, tocopy)</code> bothis ''clear''true; andcall :getParent() on this table returns ''copyclear'' (asuseful longafter asleeaving theythe dolocal notscope evaluatewhich falsereferenced ''clear'').
 
====Buffer:_parent====
 
{{luaself|:_parent|args=outssep|args2=ops, sep{{ndash}}list, { default{{ndash}}sep, ..., [out#] = sep, ...} }}
:''To skip generations without breaking the Buffer chain, see [[#global functions]].
 
SimilarResembling tothe reverse of {{luaself|:_out}} except, instead of apending the Buffer to its parent, this calls {{luaself|:_str}} on the Buffer's parent with the arguments given and appends the strung ancestor(s). to the current Buffer, which is returned.
 
The parent is unaffected by this operation and may still be retrieved via {{luaself|:_out}} or re-appended again with this function.
 
====Buffer:getParent====
 
{{luaself|:getParent|args=value|args2=functionName, ...}}
:''Note that there is no 'getChild' method<ref group='note' name='in-dependents'/>
 
Returns parent Buffer, or, if none exists, setscreates a newly creatednew Buffer and returns it as the 'adopted parent'. andAs returnswith {{luaself|:_in}}, this does not automatically append the adoptedadoptive child to the new parent.
 
If the first argument is a string matching the name of a Buffer object (or [[#library]]) function, this calls the function on the parent and forwards any additional {{luaself|varargs|plain=yes}}.
 
InWhen accordingpassed withexactly theone "wasteargument nothat {{code|()}}is philosophy"not ofa thisBuffer Module,function argumentsname arethat passedstarts to the parent. If passed only onewith {{code|value_}}, this is equivalent tocalls {{luaself|Buffer|:_|***:getParent():_|args=value}} on the parent.
If additional {{luaself|varargs|plain=yes}} are given, {{code|functionName}} must be a string naming a Buffer object function (or [[#library]]) to be called on the parent using the varargs.
 
====Buffer:killParent====
 
{{luaself|:killParent|args=...}}
 
Unsets Buffer parent reference.
Unsets the parent reference, allowing {{luaref|garbage collection||y}} unless there are non-weak references to the parent.
 
If passed any args, they willare be passedforwarded to the current parent, if one exists, via Buffer:getParent as a "parting gift". In either case, returns tothe current Buffer.
 
This is not necessary for {{luaref|garbage collection||y}} since Buffer parent references are weak. Rather, use this when it is desirable to assign a new parent via Buffer:getParent or, for example, to prevent the inclusion of an ancestor when passing {{luaref|math.huge}} as ''ops'' for functions such as {{luaself|:_out|plain=y}} (more useful when [[#recycling|recycling Module:Buffer]] from another Module).
==Stream mode==
 
===Buffer:stream===
===Stream mode===
====Buffer:stream====
 
{{luaself|:stream|args=...}}
 
Switches a Buffer to [[#Stream-Buffer|stream mode]]. While streaming, the __call metamethod will append values to the end of the Buffer
Switches the Buffer to stream mode, in which the [[#call Buffer object|__call metamethod]], instead of returning a string, now acts as streamlined version of {{luaself|:_}}. Though using the same helper method as :_ to [[#valid|validate]] values, the stream call op performs [[#performance|50 percent faster]].
instead of the [[#Buffer object|usual op]].
 
NoteAside from that, anythere argsis givenonly areone passedother tofunction: {{luaself|pre=Stream{{ndash}}|\:each}}. rather than to __call for a reason that should be evident after you read that section.
Any args passed to Buffer:stream are forwarded to that function for a reason that should be evident when you finish reading this very short section.
 
No special action is needed to exit this mode. The normal call to string op is restored upon the use of any regular Buffer function
===Stream-Buffer===
or any operation which coerces the Buffer into a string (e.g. the <code>..</code> operator).
 
{{anchor|Stream-Buffer}}
====Stream-Buffer object====
 
{{luaself|pre=Stream-|\|args='string'|args2={concat{{ndash}}list}|args3=(var)}}{{anchor|streaming|calling a Buffer:stream}}
 
Stream-Buffer objects accept only one argument which they append if [[#valid|valid]]. That is, the op is a streamlined version of {{luaself|:_}}
When streaming, you can append string (and table) literals with nothing between them (or only {{luaref|Character class|ASCII space chars|y}} if desired). For example, both A and B will produce identical strings:
sans the ''pos'' and ''raw'' args, improving [[#performance|performance]] by about 50 percent (or, in one test versus <code>..</code>, over 2000 percent).
{{#tag:syntaxhighlight|local A = require'Module:Buffer':stream'A string of text may flow''with nothing between each string' 'or perhaps only a space'
 
This also exploits {{luaref|named arguments|the syntactic sugar of function calls|y}} to append a series of string literals (and tables) with nothing between them
(or only {{luaref|Character class|ASCII space chars|y}} if desired).
 
For example, both A and B will produce identical strings:
:{|
|{{#tag:syntaxhighlight|local A = require'Module:Buffer':stream'A string of text may flow''with nothing between each string' 'or perhaps only a space'
'or even tab and line-break characters''and continue to append individually''for use with a joiner'
local B = require'Module:Buffer':_'A string of text may flow':_'with nothing between each string' :_ 'or perhaps only a space'
Line 229 ⟶ 298:
true A string of text may flow with nothing between each string or perhaps only a space or even tab and line-break characters and continue to append individually for use with a joiner
|lang=lua}}
|}
 
====Stream-Buffer:each====
Keep in mind that Lua numbers<ref group=note>It is best practice to pass number ''strings'' instead of number literals (i.e. {{code|lang=lua|Buffer:stream'1'}} instead of {{code|lang=lua|Buffer:stream(1)}}).
Such improves performance (and is perhaps more aesthetically pleasing in this mode).</ref>
and named variables are too shy to [[skinny dip]] in a Buffer stream and must wear parenthesis {{code|lang=lua|()}} as with any {{luaref|Function calls|function call|y}}.
 
{{luaself|pre=Stream-|:each|args=...}}
No special action is needed to exit this mode. The normal call to string op is restored upon the use of any regular Buffer function or any operation which coerces the Buffer into a string.
<ref group=note>No explicit trigger to exit stream mode has been programmed for {{luaself|pre=Element-|\}} functions (including Buffer-HTML redirects).
Stringing an Element-Buffer without its outer HTML-Buffer was deemed uncommon enough that the [[#performance]] penalty from modifying those specialized functions
would outweigh the inconvience of having to exit via typing <code>[[#Buffer:|:_( ... )]]</code> around the last item.</ref>
 
Appends an undetermined number of [[#valid|valid]] values to the Buffer object.<ref group=note>This is no different than calling the Stream-Buffer object directly
===Stream-Buffer:each===
with each item in the {{luaref|expression list}}; however, after noting how numbers and variables (too shy to [[skinny dip]] without parenthesis)
could look rather odd swimming fully clothed in a stream of naked strings, this was made for those whose aesthetics prefer
{{luaself|pre=Stream-|:each|args=('A',arg,'B',arg2)}} over {{code|lang=lua|Stream{{ndash}}Buffer'A'(arg)'B'(arg2)}}.
As a side note, it is best practice to pass number ''strings'' instead of number literals (i.e. {{code|lang=lua|Buffer:stream'1'}} instead of {{code|lang=lua|Buffer:stream(1)}})
as such improves performance and is perhaps more aesthetically pleasing in this mode.</ref>
 
If the above line gives you [[Déjà vu]], that is because it is drawn from {{luaref|mw.html:wikitext|plain=y}}.
{{luaself|pre=Stream-|\stream:each|args=...}}
However, unlike mw.html:wikitext, this does ''not'' stop at the first ''nil'' value.<ref group=note>
If you want something like {{code|lang=lua|:wikitext('string1', varName, 'string2')}} such that ''varName'' is shorthand for an {{code|lang=lua|if}} statement that
appends ''varName'' and 'string2' when the former is not nil, use {{code|lang=lua|:each('string1', {varName, 'string2'})}} instead.</ref>
 
Also, despite a more nuanced handling of non-string objects, this is [[#performance|over twice as fast]] as its mw.html analog and should be preferred
Appends an undetermined number of [[#valid|valid]] values.
for appending multiple strings to (Stream-)[[#Element-Buffer|Element-Buffers]].
 
==HTML library extension==
While analogous to {{luaref|mw.html:wikitext|plain=y}}, one distinguishing point (other than being [[#performance|twice as fast]] and able to [[#Buffer:_|handle tables and booleans]]) is that this does ''not'' stop at the first ''nil'' value.
In short, something like {{code|lang=lua|:wikitext('string1', varName, 'string2')}} can be replaced with {{code|lang=lua|:each('string1', {varName, 'string2'})}} when <code>varName</code> is either a string or nil.
 
Upon the first call to {{luaself|:_inHTML}}, Module:Buffer clones the {{luaref|HTML library|mw.html object metatable|y}}, adding Module:Buffer's __eq and __concat metamethods along with a few additional functions.
==HTML extension==
 
===Buffer:_inHTML===
Objects with this modified metatable are referred to as '''Buffer-HTML objects'''. Yet, though dressed in bells and whistles, they are only named with ''Buffer'' as an adjective since they lack most Buffer object functions.
 
In contrast, the '''Element-Buffer''' (returned by [[#Buffer-HTML|the function call on a Buffer-HTML object]]) is a true Buffer object with specialized "tricks" allowing complex structures to be built via both mw.html and Buffer object methods as well as through a [[#Element-Buffer:_add|builder that perhaps marries the best of both worlds]].
 
===Buffer functions for HTML===
 
====Buffer:_inHTML====
 
{{luaself|:_inHTML|args=tagName, args|args2={ args{{ndash}}list } }}
 
Accepts the same parameters as {{luaref|mw.html.create||y}} to create and return a [[#Buffer-HTML|modified mw.html object]]. As with {{luaself|:_in}}, this does ''not'' append the child object to the parent Buffer and instead sets a Buffer-style parent reference.
Creates and returns a [[#Buffer-HTML|modified mw.html object]]. Accepts the same parameters as {{luaref|mw.html.create}}.
 
An exception to the above is when chaining this to an [[#Element-Buffer|''Element-''Buffer]] and such produces a ''selfClosing'' tag; when both conditions are met, this appends the tag and returns to the same Buffer.<ref group=note>That is, Element-Buffer{{code|lang=lua|:_inHTML'br'}} may be shorthand for {{code|lang=lua|:tag'br':done()()}} when planning to continue using Buffer object functions. This "auto-done and back" for selfClosing tags does not apply to non-element Buffers in order not to encourage the use of Buffer:_inHTML for simple tags as per [[#Tips and style recommendations]].</ref>
 
Unlike mw.html.create, if {{code|args}} has keys other than <code>args.parent</code> and <code>args.selfClosing</code>, it will pass for further processing through {{luaself|pre=Element-Buffer|:_add|plain=y}} (a cousin of {{luaself|:_all|plain=y}} that handles mw.html functions). Moreover, if passed a table where mw.html.create expects ''tagName'', this treats it as ''args'' instead.
 
Finally, this does not automatically set the <code>mw.html.parent</code> reference, making this an alternative to mw.html:tag
 
====Buffer:getHTML====
 
{{luaself|:getHTML|args=value|args2=functionName, ...}}
 
Available only after {{luaself|:_inHTML}} is used the first time.
{{anchor|lastHTML}}
 
Accepts the same arguments as {{luaself|:getParent}}, however this instead return the last Buffer-HTML object ("lastHTML") created, or, if available, the lastHTML passed to any of the following functions:
* {{luaself|\-HTML:getParent|plain=y}}
* {{luaself|\-HTML:getBuffer|plain=y}}
* {{luaself|\-HTML:_out|plain=y}} (except when passed {{code|lang=lua|0}})
 
====Buffer:_html====
 
{{luaself|:_html|args = at, raw|args2 = raw}}
 
Available only after {{luaself|:_inHTML}} is used the first time.
 
This (re-)appends the [[#lastHTML|last Buffer-HTML object]] to the current Buffer object. The <code>raw</code> and <code>pos</code> args are generally the same as those in {{luaself|:_}}.
 
When called with no arguments on an ''Element-''Buffer, this appends lastHTML without [[#raw|string coercion]]. Be warned however that if the Element-Buffer belongs to lastHTML or one of its tags, such will cause an [[infinite loop]], which can be avoided by passing an explicit nil to append lastHTML as a string.<ref group=note>{{luaself|\-HTML:_parent}} serves a similar role, but appends Buffer objects rather than HTML objects. Also Buffer-HTML:_parent only appends the string form of its ancestor object(s).</ref>
 
===HTML object functions===
Modifications are summarized below:
* The {{code|..}} may be used on Buffer-mw.html objects directly (no {{luaref|tostring}} needed).
* If [[#HTML-Buffer|initialized]], will store tags and wikitext in an Element-Buffer, with which you may use Module:Buffer object functions to append (and remove, etc.) values.
* Element-Buffer objects may use {{luaself|pre=Element-|:_add}}, which greatly reduces the code size needed to build an equivalent mw.html object.
 
Buffer-HTML objects may be used like any mw.html object. (In fact, merely replacing {{code|mw.html.create}} with {{code|require'Module:Buffer':_inHTML}} in an existing Module should produce the same output.)
Unlike mw.html.create, if {{code|args}} has keys other than <code>args.parent</code> and <code>args.selfClosing</code>, it will pass through {{luaself|pre=Element-Buffer|:_add}} for further processing. Moreover, if passed a table where mw.html.create expects ''tagName'', this treats it as ''args'' instead.
 
{{anchor|wrapper}}
Most mw.html functions are unchanged, except {{luaref|mw.html:tag|:tag}}, {{luaref|mw.html:done|:done}}, and {{luaref|mw.html:allDone|:allDone}} are embedded in a wrapper function that checks whether they return a normal mw.html object. If so, converts it to a Buffer-HTML object and sets a parent reference.
Most mw.html functions are unchanged, except {{luaref|mw.html:tag|:tag}}, {{luaref|mw.html:done|:done}}, and {{luaref|mw.html:allDone|:allDone}} are embedded in a wrapper function that checks whether they return a normal mw.html object. If so, switches the metatable to convert it to a Buffer-HTML object and sets a parent reference.
<ref group=note>Buffer(-HTML) objects reference their parent differently from mw.html objects. Passing a normal mw.html object to Buffer:_inHTML as {{code|args.parent}} and then calling {{luaref|mw.html:done|:done}} the object created, followed by {{luaself|:getParent}} on the adopted parent, may return the "child." This is a feature rather than a bug.</ref>
 
As a side bonus, the {{code|..}} may be used on Buffer-mw.html objects directly (no {{luaref|tostring}} needed).
Note that other functions in section [[#HTML extension]] are only available after Buffer:_inHTML is used for the first time.
 
==={{anchor|Buffer-HTML===}}
====Buffer-HTML object====
 
{{luaself|\-HTML|args='string'|args2={ wikitextargs{{ndash}}string, { tag = { tagName, arg = value, wikitext, htmlFunction = args, cssName = cssValuelist } }, ... } }}<br />
 
Call this object as a function to return its {{code|lang=lua|.nodes}} index<ref group=note>{{--}}the internal table which holds elements appended via {{luaref|mw.html:wikitext||y}}, {{luaref|mw.html:tag||y}}, and {{luaref|mw.html:node||y}}</ref>, which this converts to an {{luaself|pre=Element-|\|plain=y}} object, granting it the same metatable as regular Module:Buffer objects (as well as several additional "tricks") and assigning the Buffer-HTML as its parent Buffer.<ref group=note>Passing arguments for {{luaself|pre=Element-|:_add}} via the ''args'' parameter of {{luaself|:_inHTML}} and {{luaself|\-HTML:tag}} during the creation of a new Buffer-HTML object is the only way to use most Buffer object functions on the new HTML object without converting its <code>.nodes</code> into an Element-Buffer.</ref>
Buffer-HTML objects may be used like any mw.html object. (In fact, if the only change were to substitute {{code|mw.html.create}} with {{code|require'Module:Buffer':_inHTML}} in an existing Module, its output should remain the same.)
 
This takes one argument which is forwarded to its Element-Buffer. Tables pass to the specialized HTML builder {{luaself|pre=Element-|:_add}}. All other [[#valid]] values are appended to its Element-Buffer via {{luaself|:_}}.
Call the object as a function to return its {{luaself|pre=Element-|\|plain=y}}, which is the table found at {{code|mw.html{{ndash}}object.nodes}} converted into a Module:Buffer object.
 
{|style='float:right'
Strings are passed to the Element-Buffer via {{luaself|:_}} which basically has the same effect as though <code>:wikitext</code> were between {{code|Buffer-HTML}} and {{code|lang=lua|'string'}}, the only difference being the object returned. Tables are passed to {{luaself|pre=Element-|:_add}}.
|{{TOC tab|{{TOC tab|{{TOC tab|{{TOC tab|{{TOC tab||Buffer-HTML:_parent|depth=2}}|Buffer-HTML:_str|depth=2}}|Buffer-HTML:_out|depth=2}}|Buffer-HTML:killParent|depth=2}}|Buffer-HTML:getParent|depth=2}}
|}{{anchor|HTML version}}
You cannot chain call regular Buffer functions on a Buffer-HTML object; however, since mw.html functions cannot read Buffer-style parent references, modified versions of methods that return [[#Buffer:_inHTML|the parent Buffer]] are available to Buffer-HTML without having to call into the Element-Buffer. For convenience, {{luaref|mw.html:allDone}} is called automatically prior to the op, though after the <code>lastHTML</code> reference has been set for {{luaself|:getHTML}}.<ref group=note>That is, Buffer:getHTML may be used to return to the child node, though the trade off is that you may still need to call mw.html:allDone before using these methods in order to append the full HTML tree via [[#Buffer:_html|Buffer:_html]].</ref>
:{|
|-
|<div style="margin:0; column-width: 15em; -moz-column-width: 15em; -webkit-column-width: 15em; width:49em">
*{{replace|'''[[#Buffer:${{!}}:$]]'''|$|getParent}}
*{{replace|'''[[#Buffer:${{!}}:$]]'''|$|killParent}}
*{{replace|'''[[#Buffer:${{!}}:$]]'''|$|_out}} {{ref label|html-0-out|0}}{{ref label|html-ops-out|$}}
*{{replace|'''[[#Buffer:${{!}}:$]]'''|$|_str}} {{ref label|html-0-str|0}}{{ref label|html-ops-str|$}}
*{{replace|'''[[#Buffer:${{!}}:$]]'''|$|_parent}} {{ref label|html-parent|^}}
</div>
 
<div style='font-size:smaller'>
Most Buffer object functions are either unavailable for use directly on the Buffer-HTML object. Those listed below have been modified so that
{{note label|html-0-out|||{{note label|html-0-str|0||&nbsp;if passed {{code|lang=lua|0}} as the first argument, these do not redirect the self-action via <code>:allDone()</code>, however, these always return a Buffer-HTML object (think <code>{{luaself|pre=Element-|:done|args=(0)|plain=y}}</code>). {{note label|html-0-out-perm|||For :_out only, the "magic" number zero ''permanently'' consolidates <code>Buffer-HTML.nodes</code> (whether or not converted)}} just like <code>:_out(0)</code> on a normal Buffer except it would not unset raw mode on an Element-Buffer.}}}}
 
{{note label|html-ops-out|||{{note label|html-ops-str|$||&nbsp;When passed a [[#valid|valid]] sep for the first op, these temporarily{{ref label||0|html-0-out-perm}} switch the Element-Buffer (or unconverted .nodes) with a table containing only the concatenated string before stringing the Buffer-HTML in the first op. As with the non-HTML versions, only :_out appends the string to the parent Buffer.}}}}
===Element-Buffer===
 
{{note label|html-parent|^||&nbsp;This takes arguments for {{luaself|:_str}} and calls it on the [[#Buffer:_inHTML|parent Buffer of the HTML tree]], returning and appending the result to the same Buffer-HTML object. In other words, the "auto-allDone" doesn't really apply here, or at least not in the same sense as with the other functions. (As a reminder, Buffer:_inHTML does not append to the parent Buffer those Buffer-HTML objects which it returns.)}}
</div>
|}
 
In addition to the above, [[#gfuncs|global functions]] may be available to Buffer-HTML if enabled; these functions are the same for all Module:Buffer objects{{--}}i.e. the self action is never redirected.
 
===Element-Buffer functions===
{{anchor|Element-Buffer}}
====Element-Buffer object====
 
{{luaself|pre=Element-|\|args=sep, i , j}}
 
Element-Buffers haveSharing the same metatable as normalwith regular Buffer objects, soElement-Buffers concatenate the same way when [[#call Buffer object|calling itcalled]] will string it in the same manner.
to produce a string analogous to the [[JavaScript]] DOM [http://www.w3schools.com/jsref/prop_html_innerhtml.asp "innerHTML"] property.
In other words, when strung, it is generally the contents of the Buffer-HTML object without the "outerHTML" or tag.
 
There are exceptions to this "innerHTML" behavior. For instance, as appended to another object via {{luaref|mw.html:node}}, an Element-Buffer and its Buffer-HTML
The string returned is analogous to that returned by the [[JavaScript]] DOM method [http://www.w3schools.com/jsref/prop_html_innerhtml.asp "innerHTML"]. In other words, when strung, it is the contents of the Buffer-HTML object without the "outerHTML" or tag (though it will include the outer Buffer-HTML when appended via {{luaref|mw.html:node}}).
are interchangeable (though appending the former via {{luaself|:_}} only includes the inner result).
 
Also, using the concatenation operator {{code|lang=lua|..}} on an Element-Buffer includes its tag in a manner depending on if it is selfClosing:
You may use most Buffer object functions normally, however those which have a Buffer-HTML version (such as {{luaself|\-HTML:_out}}) will instead behave as though used on the [[#Buffer-HTML|outer HTML object]].<ref group=note>While Buffer-HTML objects may use [[#global functions]], there is no separate Buffer-HTML version. In other words, the self-action of a global function on an Element-Buffer is <u>not</u> redirected to the outer Buffer-HTML object.</ref> Also, {{luaself|pre-Element|:_inHTML}} has been modified as described in that section.
*For most tags, the conjoined string is placed inside the tag, e.g. {{code|lang=lua|Buffer:_inHTML'p' 'inner text' .. 1}} returns {{code|lang=lua|'<p>inner text1</p>'}}.
*For selfClosers, the {{code|lang=lua|..}} op redirects to its Buffer-HTML, e.g. insert [[#Element-Buffer:_add|{{code|lang=lua|1=:_add{selfClosing=true} }}]] in the above example before {{code|lang=lua|.. 1}} to produce {{code|lang=lua|'<p />1'}}.
:{{see|#Modified .. operator}}
 
You may use most Buffer object functions normally, however if there is a [[#HTML version|Buffer-HTML version]],
Additionally, you may chain any mw.html object function directly on an Element-Buffer. With the exception of {{luaself|pre=Element-|:tag}} and {{luaself|pre=Element-|:done}}, the mw.html function has been placed in a wrapper function that merely redirects the self-action to the outside Buffer-HTML.<ref group=note>{{luaref|mw.html:allDone}} is doubly wrapped for Element-Buffers. The inner wrapper sets a Buffer parent reference as described at {{luaself|:_inHTML}}.</ref>
it instead behaves as though chained on the [[#Buffer-HTML|outer HTML object]].<ref group=note>While Buffer-HTML objects may use [[#global functions]],
there is no separate Buffer-HTML version. In other words, the self-action of a global function on an Element-Buffer is <u>not</u> redirected.</ref>
You may also chain any mw.html object function. Unless otherwise indicated, such returns a [[#wrapper|wrapper]] method that merely redirects the self-action to the outside Buffer-HTML.<ref group=note>
{{luaref|mw.html:allDone}} is doubly wrapped for Element-Buffers, with the other wrapper setting a Buffer parent reference as described at {{luaself|:_inHTML}}.
Furthermore, {{luaself|pre=Element-|:tag}} and {{luaself|pre=Element-|:done}} do not call their mw.html namesakes at all, as detailed in their respective sections.</ref>
 
As a final note, Element-Buffers are in permanent [[#raw|raw mode]] since it is expected that some mw.html method (e.g. :tag and :node) may or will append non-string elements.<ref group=note>However,
Concatenate an Element-Buffer to another value with the {{code|lang=lua|..}} operator to return the result inside the tag, such that:
since the mw.html __tostring method never calls the Element-Buffer as a function, raw mode is not expected to reduce performance when appending the Buffer-HTML (or Element-Buffer via mw.html:node)
{{#tag:syntaxhighlight|
to another object.</ref>
local Buff = require'Module:Buffer':_inHTML'div'{'Section ',color='red'}
return {Buff..1,Buff..2,Buff..3}
|lang=lua}}
 
====Element-Buffer:done====
Can be a rapid way of generating:
 
{{#tag:syntaxhighlight|
{{luaself|pre=Element-|\|args=ops}}
local section = {}
 
for k = 1, 3 do
When passed nothing, this should behave just like {{luaref|mw.html:done}} as called the "outer" HTML object{{--}}returning
table.insert(section, tostring(mw.html.create'div':css{color='red'}:wikitext('Section ', k)))
<code>Buffer-HTML.parent</code>, if available, or Buffer-HTML if not.
end
 
return section
However, this has been re-designed to accept {{code|ops}}, the number of :done() operations to perform. Thus,
{{code|Element{{ndash}}Buffer:done(4)|lang=lua}} is equivalent to {{code|Buffer{{ndash}}HTML:done():done():done():done()|lang=lua}}.
 
Pass {{code|lang=lua|0}} (zero) as ''dones'' to return to the Element-Buffer's direct HTML container.
 
Finally, keep in mind that Buffer-HTML objects use the original mw.html:done (albeit in a light [[#wrapper|wrapper]]).
 
====Element-Buffer:tag====
 
{{luaself|pre=Element-|:tag|args=tagName, args|args2={ args{{ndash}}list } }}
 
This uses the same helper method as {{luaself|:_inHTML|plain=y}} to handle arguments and produce new Buffer-HTML objects,
selectively passing ''args'' to {{luaself|:_add|plain=y}} when it contains keys not used by {{luaref|mw.html.create||y}}.
 
As may be expected, this differs from Buffer:_inHTML in that this actually appends the tag and will set a mw.html-style parent reference.
This also lacks the other function's "auto-done" feature for selfClosing tags.
 
As with the other Element-Buffer remake of an mw.html method, the features described here do not apply to the version used by Buffer-HTML objects.
 
====Element-Buffer:_add====
 
{{luaself|pre=Element-|:_add|args=args|args2={ wikitext{{ndash}}list, { args{{ndash}}list }, ..., arg {{=}} value, functionName {{=}} args } }}
 
Takes a table as its only argument. This then thoroughly iterates all number keys from lowest<sup>[[#endnote_skip1|†]]</sup> to highest using [[#MBpairs|this module's custom __pairs]] method.
Most values append as wikitext if [[#valid|valid]]. If a table is indexed at a number key, this [[Recursion (computer science)|recursively]] iterates the table before moving on to the next key.
 
After processing all number key-value pairs, this then iterates the other (non-number) keys. For those naming a [[#Basic functions|core Buffer object function]],
this selectively unpacks {{code|args}} in a manner described at {{luaself|:_all}} when that function is passed the ''nanKey'' parameter (excepting that this does not read
numbers as ''pos'', i.e. treats them the same way as strings).
This also accepts keys naming [[#HTML object functions|HTML]] and [[#Global functions|global functions]] as well as mw.html arguments.
Thus, {{code|lang=lua|Element{{ndash}}Buffer:_add{ tag {{=}} 'br', 'text'} }} appends a BR tag ''after'' the text and
{{code|lang=lua|Element{{ndash}}Buffer:_add{ {tag {{=}} 'br' }, 'text'} }} appends the BR before the text.
Note however that how this handles ''args'' for such keys depends on the particular function or argument named:
:{|
|
======args.''argName''======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ arg = value } }}<br />
The effect of passing ''args'' with keys such as <code>args.selfClosing</code> and <code>args.parent</code> is the same as though ''args'' were passed to {{luaref|mw.html.create}}.
This also takes one additional ''arg'', i.e. <code>args.tagName</code>, which value replaces the original ''tagName'' of the HTML object (or, if false, removes the tag).
 
Note that these are the only keys for which a boolean ''arg'' would result in a op. (For Buffer object functions that do not no-op when passed only a boolean, place the boolean in an ''args'' table for unpacking.)
 
======args.''cssName''======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ cssName = cssValue } }}<br />
A non-number ''key'' and ''value'' pair may default as the <code>cssName</code> and <code>cssValue</code> parameters for {{luaref|mw.html:css}} when
the key matches none of the three [[#args.argsName|argName]] keys nor the name of any available function for Buffer and mw.html objects.
 
This sends non-boolean ''cssValue'' though {{luaref|tostring||y}} prior to forwarding it to mw.html:css. Because this is the default,
any typoed or non-string key goes to mw.html:css as ''cssName'' (without string coercion). Functions not yet [[#loadable|loaded]] also end up there.
 
The form {{code|lang=lua|1=Element-Buffer:_add{ css = { cssName = cssValue } } }} also works if sacrificing performance to reduce ambiguity is your thing (or if clearing a previously set value;
see example at [[#args.htmlFunction]] for more details).
 
======args.tag<sup>[[#endnote_skip1|†]]</sup>======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ tag = tagName } }}<br />
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ tag = { tagName, args{{ndash}}list } } }}<br />
Set the key <code>args.tag</code> to a string and this calls {{luaref|mw.html.create||y}} with it as the ''tagName'' argument.
This then raw inserts the returned mw.html object ("tag"), emulating the effect of {{luaref|mw.html:tag||y}} minus parent references, which are unnecessary.
 
Pair the ''args.tag'' key with a table value and this calls mw.html.create with {{code|lang=lua|table[1]}} as ''tagName'' (or nil if [[#invalid]]), appending it as described above for string values,
but also pointing <code>tag.parent</code> to the [[#Buffer-HTML|Element-Buffer's parent]] as well as temporarily setting the tag as the parent Buffer of <code>tag.nodes</code>.
This then treats tag.nodes as a ''[[pseudo]]-''Element-Buffer, [[Recursion (computer science)|recursing]] tag.nodes as "self" and the table as ''args'',
though only iterating keys not equal to {{code|lang=lua|1}} (or less).[[#endnote_skip1-tag|<sup>[*]</sup>]]
 
Note this appends normal mw.html objects. That said, most Buffer functions named in ''args-list'' should still work as though the tag and <code>tag.nodes</code> were Buffer objects.<ref group=note>However,
some Buffer methods may not work properly after appending objects via mw.html functions to the psuedo-Buffer.<br />
For example, {{code|lang=lua|1={ tag = {'div', 'List:', foo1, foo2, foo3, _out = { 0, '\n*' } } } }} could produce a div with each ''foo'' as [[Bullet_(typography)|bulleted]] item.
But, if ''foo1'' were {{code|lang=lua|1={ tag = { 'b', 'text' } } }}, then {{luaself|:_out|plain=y}} may fail when appending {{luaref|table.concat||y}} with the non-string/number element.
A workaround is to add the pair {{code|lang=lua|1=_ = {true, true} }} to set [[#raw|raw mode]] on the div's tag.nodes; another is to replace ''foo1'' with {{code|lang=lua|mw.html.create'b':wikitext'text'}},
which appends in string form.</ref>
 
Upon completing a recursive iteration for args.tag, this checks if the tag is selfClosing, in which case, this saves memory (which can improve performance) by setting tag.nodes to nil.
Likewise, if its tagName property evaluates false, this nils tag.styles and tag.attributes. Such presumes those properties will not be modified afterwards,
so use mw.html:tag outside of Element-Buffer:_add if such is not the case.
 
======args.done<sup>[[#endnote_skip1|†]]</sup>======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ done = wikitext } }}<br />
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ done = { ops, args{{ndash}}list } } }}<br />
Similar to args.tag, this treats the first index of the table as the ''ops'' argument of {{luaself|pre=Element-|:done}}.
After calling that function, this then iterates all subsequent keys in a recursive call on the Element-Buffer of the Buffer-HTML object returned.
 
======args.allDone======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ allDone = wikitext } }}<br />
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ allDone = { args{{ndash}}list } } }}<br />
Similar to the previous two, except that the first index is not used as an argument; thus, the entire table is iterated.
 
{{anchor|args.globalFunction}}
======args.''globalFunction''<sup>[[#endnote_skip2|‡]]</sup>======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ globalFunction = name } }}<br />
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ globalFunction = { name, save, args{{ndash}}list } } }}<br />
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ _B = { var, args{{ndash}}list } } }}<br />
If the [[#gfuncs|global functions]] have been loaded and a key matches one, this calls the function with the first two arguments {{luaref|unpack||y}}ed from the paired ''args-list'' table.
This then recursively iterates the list, excluding keys less than or equal to {{code|lang=lua|2}}, with whatever object is returned as ''self''.
However, if the returned object has a metatable and <code>object.nodes</code> exists, the ''self'' will be object.nodes instead.
 
Because {{luaself|:_B}} takes only one argument, args._B only unpacks the first index and starts the iteration after that key.
 
If neither of the first two keys evaluate true, this assumes the paired value is a string for use as the ''name'' argument for the function matching its key.<ref group=note>
A caveat of this unconventional type checking is that pairing an args.globalFunction with a number value will throw an error
(which shouldn't be a problem since numbers make poor names for global variables).</ref> In that case, the current call stack's ''self''
(an Element-Buffer or [[#args.tag|tag.nodes]] if this was called indirectly) is ''self' for the global function.
 
======args.''htmlFunction''======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ htmlFunction = object } }}<br />
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ htmlFunction = { arg-list, name = value } } }}<br />
If args.''key'' matches an {{luaref|HTML library|mw.html object function|y}} that does not have its own args section,
this checks if the associated ''object'' is table. If not a table, or if <code>object.nodes</code> evaluates true,
this calls the mw.html function with the object as the only argument.
 
For table objects without an object.nodes, this iterates the table (non-recursively), repeatedly calling the named mw.html function
with one or two arguments depending on key's type in each loop. Non-number key-value pairs are both passed as arguments.
For number indicies, only the value is passed. Boolean values are a no-op.
 
Unlike with most implementions of [[#MBpairs|Module:Buffer's __pairs]], this first loops through ''non-''number keys,
followed by number keys (still ordered from lowest to highest). Thus, something like {{code|lang=lua|1=Element{{ndash}}Buffer:_add{ css = { 'width', width = '1em' } } }} is equivalent to
{{code|lang=lua|Element{{ndash}}Buffer:css( 'width', '1em' ):css( 'width' )()}}, setting the width attribute, then unsetting it for a net no-op, and returning the Element-Buffer
(though the practical purpose of such is a mystery for this developer).
 
<!--//redundant?
======args.''bufferFunction''======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ bufferFunction = object } }}<br />
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ bufferFunction = { expression{{ndash}}list } } }}<br />
 
If passed a table object that has no metatable such that {{code|lang=lua|object[1]}} exists, this {{luaref|unpack}}s the table from {{code|lang=lua|1}} and {{luaref|table.maxn|args=table}}
for use as arguments for the Buffer object function named. Other objects are passed as the only argument to the designated function.
//!-->
 
<div style='font-size:smaller'>
----
{{note label|skip1|†||&nbsp;Unlike with tables passed directly, [[Recursion (computer science)|recursive]] iterations
for functions marked with <sup>†</sup> start at the smallest number greater than {{code|lang=lua|1}} instead of negative infinity.
Such was decided for performance and coding simplicity on the reasoning that any practical purpose for indexing at a key less than one may be accomplished
by instead indexing it at any of the trillion or so [[floating point]] values between one and two{{--}}e.g. {{code|lang=lua|1.01}}.}}
 
{{note label|skip2|‡||&nbsp;The iteration may start after {{code|lang=lua|2}} for some global functions.}}
</div>
|}
 
{{anchor|loadable}}
==Loadable convenience extensions==
The methods here are loaded on demand or depend on subroutines which need initialization.
 
Whereas most Module:Buffer methods are built for performance, these methods perform tasks
which generally require less CPU usage if handled outside of Module:Buffer (i.e. via local variable declarations).
That said, they can greatly simplify the structure of the modules which employ them by doing, in a fluent manner,
many tasks which may otherwise force an awkward interruption in Buffer call chains.
 
{{anchor|gfuncs}}
===Global functions===
The nodes of many chainable constructors are traversed by methods such as {{luaref|HTML library||y}}'s {{luaref|mw.html:done}}
or this module's {{luaself|getParent}} which only go in one direction. While fine for returning to an ancestor,
such functions are unable to navigate to nodes which are a child, sibling, or cousin.
When multiple variables may each affect multiple nodes, a common solution is to assign a local variable
to each node and then break out of call chain to switch objects; the alternative being a convoluted series of {{luaref|Logical operators|logical operators|y}}.<ref group=note>
For example, mw.html objects may append a node that isn't actually "done" if a condition which affects the node also appends text after the node;
i.e. the node must be appended prematurely so that it appears before the text. Occasionally, this can get confusing for future editors
when a node is several generations removed from the declaration statement:
 
{{code|lang=lua|local x {{=}} mw.html.create():tag ... :tag'td' ... :tag'p':wikitext( ... ):tag'br':done():done() }}{{--}}i.e. is ''x'' the TD, or did I miss an element in the [[ellipsis]]?</ref>
 
The global functions, as described further below, were developed to simplify the construction and maintenance
of multi-conditional structures by providing in-chain variable declaration and navigation.
These methods are enabled when your global variable is passed to the module{{--}}either in the initial call to [[#initialize|require'Module:Buffer']]
(further instructions in that section) or to {{luaself|:_in}} which calls the Module:Buffer metatable to produce a new Buffer object.<ref group=note>
Global function are not enabled by default for various reasons:
* Relatively few modules would benefit from these methods since most templates are one-dimensional (i.e. contain few if any nested conditional statements).
* Loading them to the Module:Buffer meta index means more items that must be sifted through each time a specific function has to be retrieved for execution.
* Lua checks the global scope last; thus retrieving values from that scope takes longer than it would if they were stored in the local scope.
* If used to excess, the global scope may get so cluttered as to slow the retrieval of basic Lua functions (e.g. {{luaref|type}} or {{luaref|pairs}}) even after Buffer functions are no longer used.
 
It should be mentioned however that variable retrieval even even in a relatively cluttered global scope is fairly trivial. In fact, early versions of Module:Buffer
used globals extensively (and actually had no locals declared before the final return, or rather the entire module was just one long return statement).
In contrast, the current version nests many {{code|lang=lua|do ... end}} blocks to limit scope size.
Yet, {{luaself|:_}}, a core function which has changed little, is only a modest 10 percent faster than itself in the last unscoped version (not published);
then again, perhaps the benefit of scope dieting has been masked by much greater total number of variables required by new features?</ref>
Initializing this extension also adds a [[#_G object|__call metamethod to the global _G]].
 
====Buffer:_G====
 
{{luaself|:_G|args=name, save}}
{{Distinguish2|the [[#G object|&sect; _G object]], which chain call looks the same but may behave differently.}}
 
Pass <code>name</code> and <code>save</code> to assign the object passed as ''save'' to a global variable via {{luaref|rawset|args=_G, name, save}}.<ref group=note>
Actually, the first argument to rawset is a local variable <code>[[#new_G|new_G]]</code> which generally equals _G but not always, to be detailed in a later section.</ref>
 
Pass only ''name'' and this substitutes ''self'' for ''save'' to assign the Buffer object to <code>_G[name]</code> instead.
Give an explicit nil as ''save'' to unset a global. This returns the Buffer object as well as any argument given after ''name''.
 
This is a no-op when ''name'' is nil or a boolean, or, when ''save'' (eventually) evaluates true and {{luaref|rawequal|args=save, rawget(new_G, name)}} also returns true.
{{anchor|metaglobal}}
 
If the named global already exists, this "backs up" the old value by moving it to the meta __index of the global table,
setting a new metatable if none exists.<ref group=note>If the meta global has an __index which is a function (as is the case after requiring [[Module:No globals]]),
the back-up op aborts without throwing an error.</ref> Retrieving the old value requires unsetting the new one via {{luaself|:_R}} (more details in that section).
If overwritten a third time, the first value is discarded and the second takes its place in the back up.
 
If a metaglobal variable exists but the global is nil, this sets the global without unsetting the metaglobal (i.e. does not back up a nil global).
An exception is when this is given an explicit nil as ''save'' and only the metaglobal exists;
thus, passing nil twice for the same ''name'', unsets the key in both the global table and its metaindex.
 
====Buffer:_R====
 
{{luaself|:_R|args=name, save|args2='new_G', var, metaindex}}
 
This {{luaref|rawset||y}} with the global table as the first argument and <code>name</code> and <code>save</code> as the second and third, respectively,
returning the Buffer object for call chaining.<ref group=example>
The following demonstates how, by combining Buffer:_R and Buffer:_G, the global variable ''v'' can be declared, backed-up and replaced, replaced without back-up, restored from back-up, and removed completely:
:{|
|{{#tag:syntaxhighlight|require'Module:Buffer'
(_G,'v') -- call module with global functions enabled and declare new buffer as v
:_'A' -- append 'A' to the returned buffer
:_G('v', 1):_(v) -- _G.v = 1, shift old value (the buffer) to metaglobal.__index
:_R('v', 2):_(v) -- _G.v = 2, discard old value (1) without back-up
:_R'v':_(v) -- unset _G.v, which now defaults to metaglobal.__index.v (the buffer)
:_G('v', nil)", " -- remove back-up and join the buffer with a separator
..' and '..tostring(v)-- returns 'A, 1, 2, A12 and nil'|lang=lua}}
|}</ref> This is a no-op if ''name'' is nil or a boolean.
 
{{anchor|new_G}}
{{see|#new_G object}}
Note that Buffer methods use a local variable <code>new_G</code> as a proxy for the global table _G;
though not a global index, the string {{code|lang=lua|'new_G'}} is a "magic word"
that changes the destination for future ''save'' for this and Buffer:_G.
 
Pass a table as <code>var</code> (same place as ''save'') to set as the ''new'' new_G.
Any table such that {{code|lang=lua|1=var._G == _G}} is treated as a (former) new_G object.
This {{luaref|getmetatable|gets the metatable|y}} of former proxies and {{luaref|setmetatable|sets a new table|y}}
with the {{luaself|_G object|plain=y}} __call method on non-new_G tables. Then, this, if third parameter <code>metaindex</code> equals:
* nil {{--}} backs up the current proxy as the metaindex of the next (though this no-ops if ''var'' equals new_G to avoid cyclical indexing).
* false {{--}} leaves the metaindex intact (replacing the current proxy without back-up)
* true {{--}} unsets the metaindex of the next proxy
* any other value {{--}} sets that value as the metaindex of the next proxy. (Note new_G._G is not set until it is returned by {{luaself|:_2}})
 
To omit or to pass nil/false as ''var'' has the same effect as {{luaself|:_R|args='new_G', {} }}.
Pass true instead and this treats it as though passed as ''metaindex'', creating a new proxy without backing up the incumbent.
 
====Buffer:_2====
 
{{luaself|:_2|args=name, save|args2='new_G', ...}}
 
This returns the value indexed at key <code>name</code> in the global table. If it does not exist, this forwards both arguments to {{luaself|:_G}} and returns the saved value (which may be itself).
 
In other words, {{luaself|:_2|args=name, save}} is roughly equivalent to {{code|lang=lua|1=_G[name] = _G[name] or save or save==nil and Buffer}}.
 
The string {{code|lang=lua|'new_G'}} will return the Module:Buffer local variable <code>[[#new_G|new_G]]</code>,
used as a proxy for the global variable _G. Given more than one argument, this forwards arguments to {{luaself|:_R}}
to assign another proxy global before returning the (newly-deposed) proxy. This then sets <code>new_G._G</code> to the
original _G object for call chaining. (See &sect; chain call in [[#G object|_G object]]).
 
====Buffer:_B====
 
{{luaself|:_B|args=var}}
 
Takes only one argument and returns that argument.
Assuming the only ''X'' declared is {{luaref|_G|_G.X}} and new_G equals _G, then {{luaself|:_B|args=(X)}} and {{luaself|:_2|args='X'}} are equivalent.<ref group=note>
Dubbing this a "global function" is bit of a misnomer since this never retrieves anything from the global table.
While designed for in-chain navigation to Buffer objects that were [[#Buffer:_G|self-declared as globals]],
this returns any local reference or literal passed as well (allowing {{luaself|pre=Element-|:_add}} to execute Buffer methods on non-Buffer objects <code>[[#args.globalFunction|args._B]]</code>).</ref>
 
When passed a variable that does not exist, this returns the Buffer nil object:
 
=====Buffer-nil object=====
 
{{luaself|\-nil()}}<br />
{{luaself|\-nil:anyName|args=():_B( var )}}
 
The Buffer nil object is unique. Calling this as a function returns nothing (in contrast, calling an empty Buffer object returns an empty string).
This does however have the Module:Buffer __concat metamethod, which treats this the same way as any [[#invalid|invalid]] object (i.e. ignores it).
 
Appending this via {{luaref|mw.html:node}} or {{luaself|:_}} has the same effect as appending nil.
Passing this to {{luaref|tostring||y}} returns nil instead of the string 'nil'.
 
The only real Buffer method in its meta __index is {{luaself|:_B}}, however, any non-numerical key string retrieves a function that only returns the Buffer nil object for call chaining.
In a sense, you can think of {{code|lang=lua|Buffer:_B(var):...}} as an {{code|lang=lua|1=if var~=nil then var:...}} block around the following chain that ends in the the next :_B().
 
If {{luaref|mw.clone|cloned|y}}, the clone will be a normal Buffer object.
 
{{anchor|_G object}}<!--anchor below excludes the _ -->
====_G object====
 
The first _G variable [[#initialize|passed to this module]] is given a __call metamethod that self-{{luaref|rawset||y}}s and returns in a manner
that depends on whether it was called directly or from a chain.<ref group=example>Saving a new_G object globally via a chain call
can prevent conflict. The follow example has a hypothetical "Module:X" that may overwrite globals declared by your module or
unwittingly discard your new_G when it passes _G to Module:Buffer (passing _G to this module resets new_G to the global table
even when the global functions are already enabled):
:{|
|{{#tag:syntaxhighlight|
return require'Module:Buffer'(_G)--Return before require to show intent to return a Buffer object; chain cannot be broken
:_R(frame.args.title and --store values outside global scope if invoked with title parameter
'new_G')
:_G'myBuff' --save current Buffer in new_G
:_2'new_G' --retrieve new_G
:_G'my_G' --save new_G as global my_G
:_G('t', --save title object as my_G.t for later re-use
mw.title.new(frame.args.title or frame.args.page)
).myBuff --go to my_G.myBuff (my_G lacks the Buffer:_2 method, but doesn't need it)
:stream(my_G.t.exists --just arbitrary code to show how in-line storage may be used without breaking the chain
or warning(my_G.t), --local function warning() declared before return
require'Module:X'.main(my_G.t),
my_G.t.isSubpage and subpage(my_G.t),
... )
:_R('new_G', my_G) --set my_G as new_G again and have the new_G from Module:X as its metaindex
:_(frame.args.detail and
my_G.info
:_(frame.args.detail)--append Buffer object from Module:X's new_G.info if args.details and it exists; append detail param to result
or my_G.summary) --just append summary from Module:X if not invoked with detail param.
:_B(t and --use global t as a shorthand for "if not frame.args.title then" (t only declared a global in line 2 if no title given)
myBuff
:stream(frame.args.page,
frame.args.details2,
mw.html.create'br',
require'Module:Y'.main(frame))
or my_G.myBuff --place results in a table if invoked with title param (alternative chain call branches within Buffer:_B)
:_inHTML'table'(_addArgs)
:_parent()
)
|lang=lua}}
|}</ref> This module conserves any pre-existing metatable and alters no metamethod other than __call.
 
:{|
====Element-Buffer:done====
|
=====direct call=====
 
<code>_G( k, v )</code><br />
{{luaself|pre=Element-|\|args=dones}}
<code>_G'string'</code>
 
When called, the _G object self-sets any string passed as <code>k</code> with whatever is passed as <code>v</code>.
When called without arguments, this behaves just like {{luaref|mw.html:done}} as called on the outer HTML object.
This returns ''v'', or nil if omitted (unlike with rawset, an explicit nil is not necessary to unset a variable with direct calls).
 
Note that ''k'' must be a string to declare or unset a global in this op.
However, it has been modified to accept {{code|dones}}, the number of :done() operations to perform. Thus, {{code|Element{{ndash}}Buffer:done(4)|lang=lua}} is equivalent to {{code|Buffer{{ndash}}HTML:done():done():done():done()|lang=lua}}.
Tables passed as the first argument are treated as though this were executed via a call chain (discussed shortly).
Passing ''k'' which is not one of those two types will throw an error.
 
=====chain call=====
Pass zero ({{code|lang=lua|0}}) as ''dones'' to return to the Element-Buffer's direct HTML container. (Using an mw.html function to no-op is another way to return to the Buffer-HTML object, e.g. {{luaself|pre=Element-|:node|args=()}}, though that example does not work for selfClosing tags.)
 
<code>chained-object:_G( k, v )</code><br />
===ipairs with HTML-Buffer===
<code>chained-object:_G'string'</code>
:''See also [[#Using _pairs outside of buffer]] for more details about Module:Buffer's custom iterator.
 
{{Distinguish2|[[#Buffer:_G|&sect; Buffer:_G]], the Buffer object function, which differences are noted in the final paragraph of this section.}}
<pre>
BufferHTML = p:_inHTML'td'{1,2,nil, '', true, 3,4,tag='br'}:done(0)
mw.log(BufferHTML)
for k, v in ipairs(BufferHTML) do mw.log(k,v) end
for k, v in ipairs(BufferHTML) do if v=='3' then BufferHTML():_nil(k) end end
mw.log(BufferHTML)
<td>1234<br /></td>
1 1
2 2
3 3
4 4
5 <br />
<td>124<br /></td>
</pre>
 
When used in a call chain, this rawsets the ''k''ey-''v''alue pair in the chained object and returns that object.
The _G object may chain itself when returning _G is desired for another op instead of ''v''.
 
In contrast to the direct op, the in-chain op will index non-string ''k'' values. Moreover, this only unsets object[k] when passed an explicitly nil ''v''.
==Global functions==
 
If ''v'' is omitted in-chain, this uses the chained object as the missing argument; thus, (chained) <code>object:_G'string'</code>
has identical effect and return to <code>_G('string', object)</code>.
|}
 
The same __call method is given to [[#new_G|new_G]] objects created by Buffer:_R, however the direct call only works if its metaindex is the _G object.
Any table such that <code>table._G</code> points to the _G object may chain save to itself regardless of metaindex.
 
Though the behavior of the chain op when ''v'' is omitted may be a [[Dead ringer (idiom)|dead ringer]] to that of [[#Buffer:_G|Buffer:_G]]
when ''save'' is omitted and [[#new_G|new_G]] is the chained object, mind that the Buffer object function sets keys in new_G variable
rather than the chained (Buffer) object; in other words, this is unaffected by Buffer:_R re-assigning new_G to another table.
Also, this does not have the back up behavior of Buffer:_G.
 
{{anchor|Buffer-variable}}
===Buffer-variable objectsobject===
 
====Buffer:_var====
 
{{luaself|:_var|args=initial{{ndash}}valuevar, namechange|args2={ ... }|args3=()}}
 
AppendsRaw appends a Buffer-variable object., Thiswhich alsomay disablesappear futureas cachinga atdifferent {{luaself|.last_concat|plain=y}}value foreach alltime Bufferthe objectsobject inis yourconverted moduleto (anda anystring.<ref module which may require it).group=example>
The following contrived example demonstrates most features of {{luaself|:_var|plain=y}}:
:{|
|{{#tag:syntaxhighlight|local H, sep = require'Module:Buffer':_inHTML('div',{'Heading ',_var={nil,'odd','even'},color='blue',['text-decoration']='underline'})
:_out():_html(true):_html(true):_html(true)
sep = H:_in'This is ':_var():_' - ':_var'A':_var(3,-1):_'\n'
return H:_in(H(sep)):_(sep)
:_'math:':_var():_'+ 5 =':_var(true,5):_';':_var():_out(0,' '):_var(false):_'- 1 = ':_var()
--[[ produces:
<div style="color:blue;text-decoration:underline">Heading odd</div>This is odd - A3
<div style="color:blue;text-decoration:underline">Heading even</div>This is even - B2
<div style="color:blue;text-decoration:underline">Heading odd</div>This is odd - C1
<div style="color:blue;text-decoration:underline">Heading even</div> This is even - D0
math: 0 + 5 = 5 ; 5 - 1 = 4 --]]|lang=lua}}
|}</ref>
Initialize a Buffer-variable object by passing as <code>var</code> a:
* number - which, when strung the first time, appears as that number and reappears as <code>var + change</code> the next time it is strung.
* string - that transforms into the next [[ASCII]] character via {{luaref|string.char||args=var:byte() + change}}.
* table - to return the first (non-nil) item, then the second, and so on as determined by {{luaref|next|args=table}}, looping back to the first item after reaching the last. (Note the ''change'' argument does not apply to table-based Buffer-variables.)
* custom function - to be set as the _build and __tostring method of a variable-object, though instructions for coding such functions are beyond the scope of this manual.
 
Re-append the same variable object by passing {{code|lang=lua|true}} as the first argument.
If passed a number as <code>initial-value</code>, the number will increase by one each time the Buffer-variable is strung.
For non-table-based variables, you may specify ''change'' to append a sister version
Similarily, if it is a string, then it reappear as the next ASCII character each time.
which transforms the value at the rate specified. Sister changes are cumulative. Thus,
if the original is re-strung after a sister, its value will differ from that of its last appearance
by the sum of the original and sister rates.
 
Apply a ''change'' without appending a new variable object to the Buffer by passing {{code|lang=lua|false}}.
When passed a table, the first item will be used, then the second, and so on. This loops back to the first after reaching the last item.
The shift is effective immediately and may affect previously appended variable objects not yet [[#Buffer:_out|finalized]].
You may also pass your own custom function, though how to code one is beyond the scope of this manual.
Pass only false (i.e., omit ''change'') to produce the same effect as stringing the original once.
Note that the false-change is the only ''change'' table-based Buffer variables will honor.<ref group=note>
False cycles tables based on {{luaref|Length operator|#|y}} instead of {{luaref|next}}, which may diverge or error if the table contains nil items.</ref>
 
Pass nothing to append a version which simply repeats the result of the last stringing. While generally identical in effect to
For example:
the object generated by <code>:_var(true, 0)<code>, the Buffer-variable will return nothing if strung before any of its sisters.
{{#tag:syntaxhighlight|require'Module:Buffer'
:_inHTML'div'
{{((}}tag='br'},'Heading ',color='blue',['text-decoration']='underline'}
:_var'A':_' - ':_var{'odd', 'even'}
:_out()
:_in(_G, 'bod'):_'Body ':_var(1):_'\n':_out():_html():_(bod):_html():_(bod)
--[[ Produces:
<div style="color:blue;text-decoration:underline"><br />Heading A - odd</div>Body 1
<div style="color:blue;text-decoration:underline"><br />Heading B - even</div>Body 2
<div style="color:blue;text-decoration:underline"><br />Heading C - odd</div>Body 3 --]]|lang=lua}}
 
If passed an explicit nil as the first argument, this is no-op. If passed a boolean before any Buffer-variable has been initialized,
Pass a boolean to re-append the last Buffer-variable object created. Anything that is not false or nil will append the object [[#raw]]. False appends the previous value as a string without incrementing the object. If passed nil, this is a no-op.
this is also a no-op. Note that any op disables future caching at {{luaself|.last_concat|plain=y}} for all Buffer objects
in your module (and in any module which may require it).
 
{{anchor|library}}
===String, mw.ustring, and mw.text libraries===
 
If given <code>name</code>, then, if [[#global functions|global functions]] are enabled, the Buffer-variable will be saved via {{luaself|:_G}}.
 
==Modified {{code|..}} operator==
Line 378 ⟶ 864:
<code>value .. ''Element-Buffer''</code><br />
 
Concatenate an Element-Buffer to another value to return the result inside the tag, such that:
{{#tag:syntaxhighlight|
local Buff = require'Module:Buffer':_inHTML'div'{'Section ',color='red'}
return {Buff..1,Buff..2,Buff..3}
|lang=lua}}
 
Can be a rapid way of generating:
{{#tag:syntaxhighlight|
local section = {}
for k = 1, 3 do
table.insert(section, tostring(mw.html.create'div':css{color='red'}:wikitext('Section ', k)))
end
return section
|lang=lua}}
 
 
{{anchor|MBpairs}}
==require'Module:Buffer'.__pairs==
 
 
{{anchor|library}}
==Calling string, mw.ustring, and mw.text libraries==
 
{{anchor|tips|Tips}}
Line 394 ⟶ 896:
* Most uses of {{code|raw}} can be avoided through careful planning with the {{code|pos}} argument. That said, the performance decrease from ''raw'' is unlikely to be significant for modules transcluded on less 100,000 pages. In short, reduction in server load from avoiding ''raw'' may not be worth it if such makes the code harder to maintain.
* To insert an empty string as a placeholder for a [[#Buffer|separator]] without setting {{code|raw}}, pass a table containing only a empty string, like so: {{luaself|:_|args={''} }}.
* Raw appending a non-table is pointless since no other Scribunto type can tostring differently afterwards. However, this developer believes you are smart enough that {{code|lang=lua|1=raw and type(v)=='table'}} is a waste of server resources. (Such checks are why {{luaref|mw.html:wikitext||y}} takes twice as much time to append a list of strings as {{luaself|pre=Stream-|:each|plain=y}} despite their near-identical roles in an Element-Stream-Buffer).
 
'''For {{luaself|:_out}} and {{luaself|:_str}}'''
* Something like {{code|lang=lua|Buffer_str(2, false, A, false, {C, B})}} will use variable <code>A</code> as the parent's separator, or <code>B</code> instead if ''A'' is nil, or <code>C</code> if both A and B are nil.
 
'''For {{luaself|:_all}}'''
Line 408 ⟶ 914:
'''For {{luaself|:_inHTML}} '''
 
* When appending simple HTML structures, something like {{luaself|:_|args=mw.html.create'br'}} is roughly 6 times more efficient than <code>{{luaself|:_inHTML|plain=y}}<syntaxhighlight lang=lua enclose=none>'br':_out()</syntaxhighlight></code>, at least in terms of server CPU usage. (Though {{code|lang=lua|Buffer:_'<br />'}} is 25 and 4 times more efficient, respectively.than Alsoboth note thatexamples, respectively.)
:Buffer:_inHTML is slower on the first run due to initialization. After the first run, the efficiency ratioof Buffer:_inHTML improves by a factor of using4 mw.html.create(though directlynot overquite Buffer:_inHTMLas dropsfast toas 2.the other two).
 
{{anchor|performance}}
==Performance==
 
==Examples==
 
{{reflist|group=example}}
 
==Notes==
Non-literal interpretations of the source code (that is, more opinion than fact) are provided here to offer additional clarity.
Overly technical details may be found here as well when including such caveats appears more likely to confuse than help those advanced-but-not-quite-fluent in Lua.
 
Though commentary was deemed unneccessary for the {{Scribunto}}, Scribunto methods are "safe" for beginners and
offer extensive in-code help in the form of {{luaref|error||y}} messages. In contrast, Module:Buffer methods,
intended for intermediate-advanced coders, are built with "safeties off"{{--}}i.e., minimal type filtering and custom error warnings{{--}}to maximize performance
(though the rare {{luaref|assert}} may be found in methods which seldom need to be used more than once and where the default error message
seemed exceptionally vague or difficult to trace).
 
{{reflist|group=note}}