Module:Buffer/doc: Difference between revisions

Content deleted Content added
full draft save
Line 1:
{{Tmbox|image=[[File:Stop_hand_nuvola.svg|40px]]|text=Do not "beautify" the source of this metamodule. Its unconventional syntax was developed using the [[scientific method]] for performance. For example, {{code|1=v2~=true and v2~=false}}, though longer than {{code|1=type(v2)~='boolean'|lang=lua}}, is about 8 times faster per op. Though the {{luaref|type}} op is normally trivial, the way this is used could cause any inefficiency to be multiplied by over a '''b'''illion.<ref group=note>
<ref group=note>For instance, [[Module:Asbox]] is transcluded on about 2 million pages, which each have Asbox using Buffer functions on 10-30 variables,
some of which may be strings generated by other Modules that may eventually use Module:Buffer several times. Finally, throw in the fact
that many pages transclude Asbox multiple times, and you can see how a few microseconds per op could translate to hours for the [[Help:Job queue|job queue]].</ref>}}
 
{{Module rating |release<!-- Values: pre-alpha • alpha • beta • release • protected -- If a rating not needed/relevant, delete this template call -->}}
Line 7 ⟶ 9:
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: [[#String, mw.ustring, and mw.text libraries]]
 
Additionally, there are several specialized forms and extended objects, described further in their respective sections:
Line 32 ⟶ 36:
Creates a new Module:Buffer object when 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|:_}};
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,
Line 45 ⟶ 51:
{{anchor|recycling}}
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|1=--:_in == indirect require Module:Buffer}}
so future editors won't hunt for {{code|lang=lua|function ...:_in}} at the other module)
 
Line 56 ⟶ 62:
 
Get a Buffer as a {{luaref|string||y}} via a function call on the Buffer ''object'' (as opposed to [[#require'Module:Buffer'|a call on the ''module'']]).
This is basically shorthand for {{luaref|table.concat|args=Buffer, ...sep, i, j}}, or, with no args, {{luaref|tostring|args=Buffer}}.
However, if this Buffer holdsis in [[#raw|raw valuesmode]]<ref group=note name=raw /> or contains at least one {{luaref|sequence|non-sequential values|y}}, index,
this reconstructs the Buffer by creating a new table, coercing it contents to strings and appending them sequentially to the temporary "buffer" via
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).
<code>new-{{luaself|:_all|plain=yes|args=Buffer}}</code> (or a similar process).
{{anchor|relative}}
 
Unconventionally, any {{luaref|string||y}}-type position passed as <code>i</code> or <code>j<code> of type {{luaref|string|plain=y}} would beare treated as ''relative to length'';
that is, {{luaself|\|args='-', -1, '-3'}} is equivalent to {{luaself|\|args='-', -1, #Buffer - 3}} (which obviates the need to {{luaref|Local variable declarations|declare a local|y}} ''Buffer''
(obviating the need to set a {{luaref|Local variable declarations|local|y}} Bufferjust to use the {{luaref|Length operator|length operator|y}}). Moreover, unlike table.concat, this automatically adjusts numerical<ref group=note>
Later sections may describe values as being ''numerical'' or ''numeric''. Though perfect synonyms in normal usage, these adjectives are not interchangeable here.
For the purposes of documenting Module:Buffer, ''numerical'' includes both actual number values and string values which {{luaref|tonumber|args=value}} does not return nil
(and often involves Buffer-style length relativity); ''numeric'' describes values of number type only.</ref> positions to be within the range of the lowest and greatest indicies.
 
Note you may append a Buffer object without tostring coercion to an {{luaref|HTML library|mw.html|y}} object via {{luaref|mw.html:node}} (though not mw.html:wikitext because of type checking).
Line 213 ⟶ 222:
====Buffer:_cc====
 
{{luaself|:_cc|DUPLICATE-args=clear|args=clear, copy, meta|args2=0, true}}
 
Nils all keys of the table referenced by {{code|clear}} and unsets its metatable. If ''clear'' evaluates false, this simply purges the cache at {{luaself|.last_concat}}.
Line 248 ⟶ 257:
:''Note that there is no 'getChild' method<ref group='note' name='in-dependents'/>
 
Returns parent Buffer, or, if none exists, creates a new Buffer and returns it as the adopted parent.
As with {{luaself|:_in}}, this does not automatically append the adoptive child to the new parent.
 
Pass a non-false <code>value</code> and this performs an op on the parent object.
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}}.
 
If passed anything other than ''value'' (including nil), this requires that ''value'' names a function available to the parent object,
When passed exactly one argument that is not a Buffer function name that starts with {{code|_}}, this calls {{luaself|:_|args=value}} on the parent.
which this calls and forwards the additional {{luaself|varargs|plain=yes}}.
 
Pass only a table ''value'' which has no metatable and this forwards ''value'' to the parent which calls {{luaself|:_all|args=value}}.
 
Given only a string starting with {{code|_}} and naming a parent function, this calls it on the parent without arguments.
Any other [[#valid|valid]] singular argument [[#Buffer:|appends]] to the end of the parent Buffer.<ref group=note>
In other words, {{luaself|:getParent|args='_nil'}} is shorthand for {{luaself|:getParent|args=():_nil()}}, however,
{{luaself|:getParent|args='match'}} simply appends "match" to the parent. Note that you may still call {{luaself|Buffer|library|***:match|args=pattern}}
on a parent via {{luaself|:getParent|args='match', pattern}}.</ref>
 
====Buffer:killParent====
Line 323 ⟶ 342:
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.
 
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 adjectiveadverb 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]].
Line 492 ⟶ 511:
 
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''. (withoutNames string coercion).of Functionsfunctions not yet [[#loadable|loaded]] also end up there.
 
For convenience, any <code>_</code> character in the key string is automatically substituted with the <code>-</code> character;
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;
thus {{code|lang=lua|1=border_bottom_style =}} is equivalent to {{code|lang=lua|1=['border-bottom-style'] =}}.
see example at [[#args.htmlFunction]] for more details).
 
The form {{code|lang=lua|1=Element-Buffer:_add{ css = { cssName = cssValue } } }} also works if sacrificing performance to reduce ambiguity is your thing (or to clear a previously set value;
see example at [[#args.htmlFunction|args.htmlFunction]] for more details).
 
{{anchor|args.tag}}
======args.tag<sup>[[#endnote_skip1|†]]</sup>======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ tag = tagName } }}<br />
Line 508 ⟶ 531:
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 />
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.
Line 519 ⟶ 541:
so use mw.html:tag outside of Element-Buffer:_add if such is not the case.
 
{{anchor|args.done}}
======args.done<sup>[[#endnote_skip1|†]]</sup>======
{{code|lang=lua|1=Element{{ndash}}Buffer:_add{ done = wikitext } }}<br />
Line 555 ⟶ 578:
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 numbernumeric indiciesindices, 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{ cssattr = { 'width', width = '1em'20 } } }} is equivalent to
{{code|lang=lua|Element{{ndash}}Buffer:cssattr( 'width', '1em'20 ):cssattr( 'width' )()}}, setting then unsetting the width attribute, thenand unsettingreturning itthe Element-Buffer for a net no-op, and returning the Element-Buffer
(though the practical purpose of such is a mystery for this developer).
 
For <code>args.css</code> only, this auto-replaces underscores with hypens for string keys{{--}}i.e.,
you may save four keystrokes/pair by typing <code>css_property =</code> instead of <code>["css-property"] =</code>.
This does not apply to strings indexed at number keys.
 
<!--//redundant?
Line 593 ⟶ 620:
{{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:
 
Methods such as {{luaref|mw.html:done}} and {{luaself|:getParent}} traverse a node tree in only one direction.
{{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>
While fine for returning to an ancestor, they do not provide navigation to a non-ancestor
(often necessary for templates with co-dependent parameters). Yet, repeated breaks in call chains to set local variables
for several nodes of the same branch can look choppy if not confusing for nodes many generations removed from its declaration statement.<ref group=note>
i.e., does ''x'', in the following, reference the TD or some other node hidden within an [[ellipsis]]?:
{{code|lang=lua|local x {{=}} mw.html.create():tag ... :tag'td' ... :tag(arg and 'div' or 'p'):wikitext( ... ):tag'br':done():done() }}</ref>
 
Templates with several conditionally-appended nodes with similar, but not identical, parts may present another conundrum for coders
The global functions, as described further below, were developed to simplify the construction and maintenance
who must decide between having awkward call chain interruptions to store potentially repeated components as local variables
of multi-conditional structures by providing in-chain variable declaration and navigation.
or constructing a somewhat redundant module that is more susceptible to maintenance errors by future editors
These methods are enabled when your global variable is passed to the module{{--}}either in the initial call to [[#initialize|require'Module:Buffer']]
who may patch one code segment but miss the sibling buried within a convoluted nesting of {{luaref|Logical operators|logical operators|y}}.
(further instructions in that section) or to {{luaself|:_in}} which calls the Module:Buffer metatable to produce a new Buffer object.<ref group=note>
 
This module's global functions and added [[syntactic sugar]] for the [[#G object|_G object]] were formulated
to simplify such node trees with multi-conditional or repeating structures by providing concise in-chain variable declaration.
The extension is enabled by passing your global table to the module{{--}}either in the initial call to [[#initialize|require'Module:Buffer']]
(more instructions in that section) or to {{luaself|:_in}} which forwards arguments to Module:Buffer.<ref group=note>
Global function are not enabled by default for various reasons:
* Relatively few modules would benefit from these methods since mostMost templates are one-dimensional (i.e. contain few if any nested conditional statements) and thus would not benefit from these methods.
* 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.
* IfExcess useduse tomay excess,clutter the global scope may get so cluttered asenough to slow theaccess retrieval ofto basic Lua functions (e.g. {{luaref|type}} or {{luaref|pairs}}) even after Buffer functionsmethods 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
Line 619 ⟶ 648:
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====
Line 706 ⟶ 734:
When passed a variable that does not exist, this returns the Buffer nil object:
 
{{anchor|Buffer-nil}}
=====Buffer-nil object=====
 
Line 711 ⟶ 740:
{{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).
 
Line 809 ⟶ 838:
{{luaself|:_var|args=var, change|args2={ ... }|args3=()}}
 
Raw appends a Buffer-variable object, which may appear as a different value each time the object (or its container) is converted to a string.<ref group=example>
The following contrived example demonstrates most features of {{luaself|:_var|plain=y}}:
:{|
Line 816 ⟶ 845:
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
Line 831 ⟶ 860:
* 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 firstonly argument.
For non-table-based variables, you may specify ''change'' to append a sister version object
which transforms the value at the rate specified. Sister changesChanges 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 and vice versa.
 
Apply a ''change'' without appending a new variable object to the Buffer by passing {{code|lang=lua|false}}.
Line 853 ⟶ 882:
===String, mw.ustring, and mw.text libraries===
 
====Basic usage====
 
{{code|lang=lua|Buffer:functionName( ... )}}
 
You may directly chain any function from the following libraries on Buffer objects:
:{|
|{{collist|3|colwidth=15em|style=width:49em|
* {{luaref|String library|string|y}}
* {{luaref|Text library|mw.text|y}}
* {{luaref|Ustring library|mw.ustring|y}} }}
|}
 
Functions from these libraries added to the Module:Buffer metatable on-demand and placed within a wrapper method
that strings the Buffer object for the first argument and then forwards the remaining arguments.
 
Thus, the following are equivalent: {{code|lang=lua|Buffer:nowiki( ... )}} and {{luaref|mw.text.nowiki|args=tostring(Buffer), ...}}
 
If a name exists in both the string and mw.ustring libraries, the string version takes precedence.
You may [[prefix]] the letter ''u'' on any mw.ustring function{{--}}e.g. Buffer:ulen returns the number of unicode characters
and Buffer:len returns the number of bytes.
 
Buffer:gsub and Buffer:ugsub have a slightly different wrapper which substitutes the <code>repl</code> argument of {{luaref|string.gsub||y}} and {{luaref|mw.ustring.gsub||y}}
when it evaluates false or is omitted with an empty string (otherwise the originals would throw an error).
This saves a few keystrokes when removing characters via {{code|lang=lua|Buffer:gsub'[pattern]'}} as opposed to {{code|lang=lua|Buffer:gsub( '[pattern]', '' )}}.
All other arguments are handled the same as with the other on-demand methods.
 
Library functions which take a non-string as the first argument are not supported.
 
====Empty Buffer interface====
 
{{luaself|:_in|args=():functionName( ... )}}
 
To obtain the first return value as a Buffer object (as opposed to whatever type the original normally returns),
simply chain the imported method immediately after creating the new Buffer via [[#Buffer:_in|Buffer:_in]].
Only empty Buffer objects which have a parent object will append the result of their parent in this manner.
 
This syntactic sugar allows two things:
* For appending additional objects after the op via Buffer object methods.
* For chaining multiple {{luaref|_|Scribunto|y}} methods not chainable to strings{{--}}e.g., this: {{code|lang=lua|1=Buffer:_in():uformat( ... ):_in():toNFD():encode'[<>#]':match'^(.-)==='}}
: vs. the following which has the same order of operations albeit harder to see: {{code|lang=lua|1=mw.text.encode( mw.ustring.toNFD( Buffer:uformat( ... ) ), '[<>#]' ):match'^(.-)==='}}
 
:{|
|
=====Special case: Element-Buffer=====
 
{{code|lang=lua|empty{{ndash}}Element{{ndash}}Buffer:functionName( ... )}}<br />
{{code|lang=lua|Element{{ndash}}Buffer:_in():functionName( ... )}}
 
The 'empty' behavior is different when chained to empty Element-Buffer or an empty child Buffer of an Element-Buffer.
 
Library methods chained to an empty Buffer which parent is an [[#Element-Buffer|Element-Buffer object]] will instead string
the grandparent [[#Buffer-HTML|Buffer-HTML object]] for use as the first argument before appending the result to the new Buffer.
This interface is provided because Buffer-HTML objects, which are not true Buffer objects, are unable to load these functions,
making this the only chainable option for Scribunto methods that includes the outer tag of non-empty Element-Buffers.
 
Chained on an empty Element-Buffer, these methods will string the Buffer object which created its HTML tree via {{luaself|:_inHTML}}<ref group=note>
Though this strings the same object returned by {{luaself|\-HTML:getParent}}, that function is not used to avoid setting a "lastHTML" reference.</ref>
and append the result to the Element-Buffer.<ref group=example>Compare the comment and source:
:{|
|{{#tag:syntaxhighlight|--[[--= Result: ===>
 
This is just a quick example to demonstrate a neat concept:
<div>Notice how the same object is strung which allows you to recycle boilerplate text.
<p>3 is 1 added to 2.</p>
<span>Though this is not really realistic to be fair...
Edit: not realistic at all</span>
I hope such is much to your liking.</div>
 
--]]--= Source: ===>
require'Module:Buffer'
'%s is %s to %s.'--A
:_inHTML'div'():format('Notice how the same object', 'strung which allows you', 'recycle boilerplate text')
:tag'p'():format(3, '1 added', 2)
:done()
:tag'span'():format('Though this', 'not really realistic', 'be fair..')
:_in():sub(21, 29)
:_('\nEdit:', 1)
:_'istic at all'
:_out()
:done()
:_parent()--B
:_out'\n'
:format('This', 'just a quick example', 'demonstrate a neat trick',--A
'I hope such', 'much', 'your liking')--B
:gsub('trick.', 'concept:\n')|lang=lua}}
|}</ref>
|}
 
==Modified {{code|..}} operator==
{{code|Buffer .. value}}<br />
<code>''Buffer-HTML'' .. value</code><br />
This is akin to {{luaref|self=Buffer/doc|:_all|'''new-buffer'':_all|args={ Buffer, value} }} or <code>{{luaref|tostring||p|args=Buffer}} .. value</code>. HTML objects created by a Buffer may also be concatenated in this manner.
 
All "true" Buffer objects{{--}}e.g., the [[#Buffer object|regular]], [[#Stream-Buffer|stream]],
<code>''Buffer-HTML'' .. value</code><br />
and [[#Element-Buffer|element]] varieties{{--}}share the same {{luaref|metatables|__concat metamethod|y}}.
Some Buffer-''like'' classes, namely {{luaself|\-HTML object|plain=y}}s and the {{luaself|\-nil object|plain=y}}, also have this same metamethod.
 
The extended {{luaref|Concatenation operator|..}} operator does not append to Buffer objects. In other words,
Buffers generally remain the same as before the op excepting those effects that apply whenever Buffers are strung
(See {{luaself|.last_concat|plain=y}}, {{luaself|stream mode|plain=y}}, and {{luaself|\-variable|plain=y}}).
 
===with non-tables===
<code>value .. ''Element-Buffer''</code><br />
 
{{code|lang=lua|Buffer .. value}}<br />
Concatenate an Element-Buffer to another value to return the result inside the tag, such that:
{{code|lang=lua|value .. Buffer}}
{{#tag:syntaxhighlight|
local Buff = require'Module:Buffer':_inHTML'div'{'Section ',color='red'}
return {Buff..1,Buff..2,Buff..3}
|lang=lua}}
 
Any non-table <code>value</code> may be joined a Buffer object with the concatenation operator {{code|lang=lua|..}} without error.
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}}
 
With the exception of Element-Buffers (which are a special case), the op passes each object, ordered left-to-right,
to {{luaself|:_}} which inserts [[#valid|valid]]ated items in a new table, which this returns through {{luaref|table.concat||y}}.
 
Concatenating an [[#valid|invalid]] ''value'' and a Buffer has generally the same effect as {{luaref|tostring|args=Buffer}} unless such involves:
* the Buffer-nil object {{--}} which produces an empty string (instead of nil)
* Element-Buffer objects {{--}} which returns the string of the parent Buffer-HTML{{see|#for Element-Buffer objects}}
 
===with tables===
 
{{code|lang=lua|Buffer .. table}}<br />
{{code|lang=lua|table .. Buffer}}
 
The same general operation applies for tables as with non-tables{{--}}i.e., [[#valid|validated]] values are inserted left-to-right
into a new table to be joined by table.concat. In fact, tables which have a metatable (including Buffer objects which are not an Element-Buffer)
are forwarded to Buffer:_ and processed the same way as non-tables.
 
Given a <code>table</code> for which {{luaref|getmetatable||y}} returns nil or false, this instead forwards the table to {{luaself|:_all}},
which iterates every value indexed at a number key in sequential order, inserting those which are valid in the new table.
 
As a reminder, Buffer:_ validates tables with metatables that lack a __tostring method through table.concat,
which throws an error on sequences containing one or more value that is neither a string nor a number.
Such accounts for nearly all cases of breaking errors involving this op.
 
Note that the ''valKey'' parameter of Buffer:_all is not triggered.
 
===for Element-Buffers===
 
{{code|lang=lua|Element-Buffer .. value}}<br />
{{code|lang=lua|value .. Element-Buffer}}<br />
{{code|lang=lua|Element-Buffer .. Element-Buffer}}
 
To recap and expand upon [[#Element-Buffer|&sect; Element-Buffer-object]], the behavior of this op depends on whether its parent Buffer-HTML is {{luaref|mw.html.create|selfClosing|y}}
or if the other ''value'' is also an Element-Buffer. Also, the final result always includes the outer HTML object (i.e., the tag) in some manner.
 
For Element-Buffers of "open" tags, this op creates a table with a metaindex that references the parent of the Element-Buffer.
The table is then effectively a "mirror" of the parent Buffer-HTML object except that it contains an empty table at <code>table.nodes</code>{{--}}
the index of the Element-Buffer within its parent. This then populates the mirror's inner table with the string of the Element-Buffer and the other value,
validated left-to-right, in a manner not unlike what this does with the temporary table it creates when concatenating non-element Buffers to another value.
This then returns the mirror table through the __tostring metamethod of the mw.html library, yielding a string which resembles that of the parent tag
but with ''value'' inserted in front of or behind the original inner text depending on whether ''value'' was to the left or right of the <code>..</code> operator, respectively.
 
When the selfClosing property of the parent evaluates true, this operates on the parent instead of the Element-Buffer{{--}}i.e., the resulting string will have ''value'' on the outside
as opposed to within the tag (placing it inside would be pointless since selfClosing tags do not show inner contents).
 
If both operated objects are Element-Buffers, this mirrors the parent of the first. The inner table of the mirror is then populated by inserting the string of the first Element-Buffer
followed by the string the parent Buffer-HMTL of the second. The resulting string would be as though the parent of the second were the last node of the first parent.
Note that this Element-to-Element rule does not apply when the first Buffer belongs to a selfClosing tag (in which case, this behaves as though the selfClosing parent were
to the left of the operator, returning a string with the selfClosing tag inside the tag of the second Element-Buffer in front the latter's inner contents.)
 
Finally, this combines an Element-Buffer and a table ''value'' which has no metatable by passing the table as ''args'' for {{luaself|pre=Element-|:_add}}
with the mirror of the Element-Buffer as the "self". This avoids permanently changing the parent Buffer-HTML by setting a new table
at <code>table.attributes</code> or <code>table.styles</code> in the mirror the first time methods such as {{luaref|mw.html:css|plain=y}},
{{luaref|mw.html:attr|plain=y}}, {{luaref|mw.html:addClass|plain=y}}, etc. attempt to access those tables, copying the original's via the recursive form of {{luaself|:_cc}}.
Note however that permanent changes may be made to other objects whenever methods such as via {{luaself|args.done}} or {{luaself|args.globalFunction}} are keyed
to navigate beyond the mirror or "sandbox".
 
{{anchor|MBpairs}}
==require'Module:Buffer'.__pairs==
 
[[#MBpairs|{{code|lang=lua|require'Module:Buffer'.__pairs( table, flag, ext )}}]]
 
Returns two values: an iterator function and the <code>table</code>. This is intended for use in the {{luaref|iterators|iterator form of|y}}&nbsp;<code>for</code>.
 
One distinctive feature of this pairs method is that it splits keys into two groups: {{luaref|number||y}}s and non-numbers. This indexes each group of keys in its own "map" object,
traversed by its own iterator function{{--}}i.e, iterating both sets of keys requires two separate for loops. Numeric keys are served in an orderly fashion
as with {{luaref|ipairs}} except that those which are negative, non-consecutive, and non-integer may be included. Moreover, this can find some keys paired with explicitly nil values.<ref group=example>
Take a moment to look at the following tables ''X'' and ''Y'':
:{|style=width:50%
|{{#tag:syntaxhighlight|local X = { [5] = 5 }
local Y = { nil, nil, nil, nil, 5 }
|lang=lua}}
|}
These tables are indistinguishable to {{luaref|ipairs}} and {{luaref|pairs}} (ipairs iterates nothing and pairs yields only one key-value pair for either table).
 
While this module's __pairs method also gives only one pair for table ''X'', it loops all five explicitly declared indicies for table ''Y'',
as shown in the console input below with Module:Buffer as ''p'':
:{|style=width:50%
|{{#tag:syntaxhighlight|for k, v in p.__pairs{ [5] {{=}} 5 } do mw.log(k, v) end
5 5
for k, v in p.__pairs{ nil, nil, nil, nil, 5 } do mw.log(k, v) end
1 nil
2 nil
3 nil
4 nil
5 5|lang=lua}}
|}
Detecting nil values is actually a side effect of trying to improve performance by avoiding type checking on some keys when sorting them
(see stage one of [[#mapping|&sect; Mapping process]]), but, if pressed for a practical use, let's just say this can be a means
to force the inclusion of keys from the table's meta __index or to allow keys to be unset without excluding them from the iteration.</ref>
 
The <code>flag</code> argument selects the iterator method returned for that loop. When ''flag'' is an explicit nil or omitted, this returns an iterator for number keys.
If given any non-nil ''flag'' (i.e., false or any value that evaluates true), this returns a method for looping non-numeric keys. Because both sets are mapped at the same time,
you may avoid a redundant mapping op in a subsequent loop by passing an explicit nil or false as ''flag''{{--}}i.e., omitting ''flag'' or passing true indicate that re-mapping is desired.
 
This automatically selects certain tables for "mapless" iteration. Typically, mapless differs from mapped only in that it uses fewer server resources, though,
as explained in the next section on mapping, it may "miss" keys in some cases.
 
Mapping behavior may be modified or extended by <code>ext</code>. To disable mapless iteration for the table, you may pass false as ''ext''. If not nil or false,
''ext'' must be a pairs method that takes the table as its only argument and returns a function that may iterate its keys for mapping purposes.
Note that re-mapping avoidance via ''flag'' does not apply if ''ext'' is explicitly given, though a nil ''ext'' does not disqualify a table from mapless iteration.
{{anchor|mapping}}
 
===Mapping process===
 
Tables are mapped in two stages.
 
The initial stage is a {{luaref|for|numerical for loop|y}} which inserts integers between {{code|lang=lua|1}} and {{code|lang=lua|#table}} in the number key map.
Because nothing is checked in this step, this may map keys which the [[#Iterators|numeric map iterator]] would pair with nil values or with values from the table's {{luaref|Metatables|meta __index|y}}.
 
The second stage explores the table's keys with an {{luaref|iterators|iterative for loop|y}} and {{luaref|next|next, table}} as the default ''expression-list'', or,
if ''ext'' evaluates true, the expression returned by {{code|lang=lua|ext( table )}}. This ignores keys already mapped in the first stage and checks if any unmapped key is a number
before indexing it in the appropriate map group. Upon completion, if any new number key were found in the second stage, this runs the numeric map through {{luaref|table.sort||y}}.
No order is imposed on the non-numeric map.
 
Alternatively, a table may qualify for "mapless" iteration if {{luaref|rawget|args=table, 1}} is not nil, and {{luaref|next|args=table, #table}} returns nil.
If either ''flag'' or ''ext'' are not nil, or if the table was previously mapped, such permanently disqualifies a table for mapless processing.<ref group=note>
Mapless iteration is intended to improve performance for tables which were constructed sequentially and which have only numeric indicies (e.g., any Buffer-object built
without using the ''pos'' argument of methods such as {{luaself|:_}}; note that {{luaself|.last_concat}} may be temporarily uset when the Buffer is strung in a way that would
involve this method to avoid disqualification from mapless iteration due to non-sequential indexing). Though "trick" tables have been made to qualify for
but yet contain keys not covered by mapless iteration, such involved both intent and an exceptional understanding of lua table, making it difficult to imagine that
mis-qualified tables may arrise accidentally.</ref>
 
As a side note, if mapless numeric iteration occurs, this returns {{code|lang=lua|iterator, table, nil}}. In other words, you may use {{luaref|select}} to confirm that the table qualifies
for mapless iteration when it has a third explicit return (for debugging).
 
===Iterators===
 
{{luaself|iterator|args=table, key}}
 
One of four functions may be provided in the ''{{luaref|iterators|expression-list|y}}'' returned by this pairs method, depending on which group of keys (numeric or non-numeric)
and which iteration process (map-based or mapless) is indicated.
 
When <code>key</code> is nil or unspecified, map iterators will return the key object referenced by the first index of the relevant map along with the value it indexes.
If passed the first mapped key, these iterators then return the second index mapped, which if passed in turn may retrieve the third and so on until the last mapped key has been served.
 
For numeric iteration, the mapless method returns {{code|lang=lua|1, table[1]}} when ''key'' is unspecified. If a ''key'' is given, it returns {{code|lang=lua|key + 1, table[ key + 1 ]}}
unless ''key'' is greater or equal to the length of the table, upon which it returns nil. For non-numeric keys, the mapless "iterator" is actually a no-op (empty) function
which takes nothing, does nothing, and returns nothing{{--}}provided only to prevent an error when the for loop expects a function.
 
As mentioned (using different words), key-value pairs are served independently of whether or not {{code|lang=lua|table[key]}} exists and retrieved without using {{luaref|rawget}}.
 
For example, take a look at table ''x'' as declared in the following statement: {{code|lang=lua|local x = {1,nil,nil,nil,nil,nil,nil,8} }}.
Table ''x'' has a length equal to 8. With ipairs, the for loop stops after the first pair. In contrast, this module's __pairs method will loop all 8 keys
declared{{--}}i.e., (1, 1), (2, nil), ... (7, nil), (8, 8). That said, this only iterates two keys if table ''x'' were declared as {{code|lang=lua|{ 1, [8] = 8} }} instead
even though such is indistinguishable to
Finally, the loop would continue to include any keys set to nil after the mapping process.</ref>
 
You may assign these iterators to a local variable to use them directly. If an unmapped table is given to a map iterator, it will forward the table to this pairs method for immediate mapping.
Though no map table is produced for the mapless iteration, the pairs method does cache the length of the table at a map reference, which the iterator compares against ''key'' to determine when to stop.
Unlike the map methods, the mapless iterator does not call the pairs method when such has been bypassed and instead compares ''key'' to the value returned by the {{luaref|length operator||y}},
which may be unstable if the loop includes code that sets or unsets indicies within the table. Also, the mapless method will throw an error if given a table that has been mapped
(when it attempts to compare ''key'' to a map object).
 
==Appendix==
{{anchor|tips|Tips}}
===Tips and style recommendations===
{{incomplete|section}}
 
* If [[#Buffer|joining Buffer]] with a string immediately after <code>:_'<i>text</i>'</code>, place a space between 'string' and the [[#Buffer|separator]] and use double/single quote marks to . (i.e. <code>:_'<i>text</i>' " "</code> instead of <code>:_'<i>text</i>'{{`}} '</code> or <code>:_'<i>text</i>'(' ')</code>)
* Saving Module:Buffer locally, e.g. <code>local Buffer = {{luaref|require|args='Module:Buffer'|plain=y}}</code>, though fine, is often unnecessary since all Buffer objects can create new buffers via {{luaself|:_in}}.
 
 
'''For {{luaself|:_}}'''
Line 918 ⟶ 1,175:
 
{{anchor|performance}}
===Performance===
{{ombox|text=Due to the non-essential nature of this section, the sharing of performance test procedures and results has been postponed to a later date.}}
 
 
==Examples==
 
 
===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.
Line 935 ⟶ 1,196:
 
{{reflist|group=note}}
 
==See also==
* [[Module:Escape]], a lightweight metamodule for customized string character escaping
 
<includeonly>{{#ifeq:{{SUBPAGENAME}}|sandbox | |