Content deleted Content added
save |
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
<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 -->}}
<!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) -->
{{TOC right}}
This module was originally developed to optimize string concatenation in [[Module:Asbox]] but can be used in any module.
Line 25 ⟶ 26:
{{luaself|\|args=sep, i, j}}{{anchor|call Buffer object}}
:''See also {{luaself|:_str|plain=y}} for advanced string conversion.
Get Buffer as type {{luaref|string||y}} by performing a function call on the Buffer ''object'' (as opposed to [[#require'Module:Buffer'|a call on the ''Module'']]). Calling a Buffer is basically shorthand for {{luaref|table.concat|args=Buffer, ...}}, or, with no args, {{luaref|tostring|args=Buffer}}.
Line 31 ⟶ 32:
However, if your Buffer contains [[#raw|raw]] objects or out-of-sequence values, then the return string would be the result of {{luaself|Buffer|:_all|empty-buffer:_all|args=Buffer )( ...}} instead.<ref group=note name=raw />
When strung without a separator, the result may be retrieved via {{code|Buffer.last_concat}}. Future tostring operations on the Buffer will return Buffer.last_concat until it is modified.
===Buffer:_===
{{luaself|:_|args='string'|args2=value, pos, raw|args3=value, raw}}
:''See also {{luaself|:stream|
Appends a value to the Buffer. In rough terms, {{code|lang=lua|Buffer:_'string1':_'string2'}} is the same as {{code|lang=lua|Buffer {{=}} Buffer..'string1'..'string2'}}. (It may help to imagine {{code|:_}} as a {{code|..}} that has stood up and is now casting a shadow.)
Line 49 ⟶ 50:
* {{luaref|nil||y}}
* empty {{luaref|string||y}}
* {{luaref|table||y}} without a {{luaref|Metatables|__tostring metamethod}} and which
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}}. In fact, assuming a [[#valid|valid value]], {{luaself|:_|args='string', 1}} is exactly the same as {{luaref|table.insert|args=Buffer, 1, 'string'}}.
Unconventionally, a ''pos'' of type {{luaref|string|plain=y}} is treated as relative to length; that is, {{luaself|:_|args='string', '-1'}} is equivalent to {{luaself|:_|args='string', #Buffer - 1}} (obviating the need to set a {{luaref|Local variable declarations|local|y}} Buffer to use the {{luaref|Length operator|length operator|y}})
{{anchor|raw}}
Set {{code|raw}} to true to force ''value'' in Buffer without tostring coercion, including [[#no-op|invalid]] values.<ref group=note name=raw>Setting a Buffer to ''raw'' incurs [[#performance|performance]] penalty for all future tostring ops as it must re-validate each indexed value through {{luaself|:_all}} to 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===
Line 76 ⟶ 75:
===Buffer:_all===
{{luaself|:_all|args={ value, ... }|args2={
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}},
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|:_}}.
{{anchor|nanKeys}}
If
{{#tag:syntaxhighlight|Buffer:_(1):_(2):_(3)
if variable then
Line 94 ⟶ 93:
Buffer:_'... done'|lang=lua}}
If
{{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}}
Line 106 ⟶ 105:
Creates and returns a new Buffer object. This does not <u>not</u> append the new Buffer to the parent. (See next section)
More precisely, it re-calls the
<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. (No, {{u|Codehydro}} did not get lazy. Rather, this allows {{luaref|Garbage collection|garbage collection|y}} on children with no further purpose.)</ref> Do not pass _G if the Module:Buffer instance was not initialized with [[#Global functions|global functions]] enabled.
Note that all Buffer parent references are {{luaref|weak tables|weak|y}}. If a variable is set to the parent and then set to something else, such may trigger immediate garbage collection.
===Buffer:_out===
{{luaself|:_out|args=sep|args2=outs, sep
{{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.
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
If the last vararg is a table,
===Buffer:_str===
{{luaself|:_str|args=sep|args2=generations, sep
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).
Unlike :_out, this does not append the child into the parent. As such, even with the same arguments, it may return a different result than would be obtained from stringing the return of :_out since each parents' ''sep'' is not used to join parent and child. Furthermore, the number of ''generations'' counted includes the current Buffer, whereas the number of "''outs''" in Buffer:_out does not.
===Buffer:_parent===
{{luaself|:_parent|args=outs, sep
:''To skip generations without breaking the Buffer chain, see [[#global functions]].
Similar to {{luaself|:_out}} except, instead of apending the Buffer to its parent, this calls {{luaself|:_str}} on the parent(s) and appends the result. The parent is unaffected by this operation and may still be retrieved via {{luaself|:_out}}.
===Buffer:getParent===
{{luaself|:getParent|args=
Returns parent Buffer, or, if none exists, sets a newly created Buffer as the 'parent' and returns the adopted parent.
Following this Module's waste no {{code|()}} philosophy, arguments are passed to the parent. If passed only one {{code|value}}, this is equivalent to {{luaself|Buffer|:_|***:getParent():_|args=value}}.
If any {{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.
Also know that here is no 'getChild' method.<ref group='note' name='in-dependents'/>
===Buffer:killParent===
{{luaself|:killParent|args=...}}
Unsets the parent reference, allowing {{luaref|garbage collection||y}} unless there are non-weak references to the parent.
If passed any args, they will be passed to the current parent via Buffer:getParent as a "parting gift". In either case, returns to current Buffer.
===Buffer:_c===
Line 142 ⟶ 158:
{{luaself|:_c|args=clear|args=clear, copy}}
Nils all keys of the table referenced by {{code|clear}} and unsets its metatable. If
If given a table to {{code|copy}},
If ''copy'' is not a table, then it will be inserted as the first index of the ''clear''ed table so long as ''copy'' is not nil or false.
==Stream mode==
===Buffer:stream===
{{luaself|:stream|args='string'|args2={concat
Switches the Buffer to stream mode. In this mode, the [[#call Buffer object|Buffer call]] operation, instead of returning a string, now acts as streamlined version of {{luaself|:_}}.
When streaming, you may append a sequence of
{{#tag:syntaxhighlight|local A = p: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 =
:_'or even tab and line-break characters':_'and continue to append individually':_'for use with a joiner
table#1 {
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}}
Aside from saving two
Also, Lua numbers
<ref group=note>It is best practice to pass number ''strings'' instead of a passing a number literals (i.e. {{code|lang=lua|Buffer:stream'1'}} instead of {{code|lang=lua|Buffer:stream(1)}}). Such improves performance (and is 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}}.
No
Line 178 ⟶ 196:
===Buffer:_inHTML===
{{luaself|:_inHTML|args=tagName, args|args2={ args{{ndash}}list } }}
Creates and returns a [[#Buffer-HTML|modified mw.html object]]. Accepts the same parameters as {{luaref|mw.html.create}}.
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.
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.
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.
<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>
Note that other functions in section [[#HTML extension]] are only available after Buffer:_inHTML is used for the first time.
===Buffer-HTML===
{{luaself|\-HTML|args='string'|args2={ wikitext{{ndash}}string, { tag = { tagName, arg = value, wikitext, htmlFunction = args, cssName = cssValue } }, ... } }}<br />
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.)
Call the object as a function to return its {{luaself|pre=Element{{ndash}}|\|plain=y}}, which is the table found at {{code|mw.html{{ndash}}object.nodes}} converted into a Module:Buffer object.
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{{ndash}}|:_add}}.
Most Buffer object functions are either unavailable for use directly on the Buffer-HTML object. Those listed below have been modified so that
===Element-Buffer===
{{luaself|pre=Element{{ndash}}|\|args=sep, i , j}}
Element-Buffers have the same metatable as normal Buffer objects, so [[#call Buffer object|calling it]] will string it in the same manner.
The string returned is analogous to the value returned by the [http://www.w3schools.com/jsref/prop_html_innerhtml.asp "innerHTML"] DOM property in [[JavaScript]]. In other words, when strung, it is the contents of the Buffer-HTML object without the "outerHTML" or tag.
You may use most Buffer object function 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.
Additionally, you may chain any mw.html object function directly on an Element-Buffer. With the exception of {{luaself|pre=Element{{ndash}}|:tag}} and {{luaself|pre=Element{{ndash}}|: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>
====Element-Buffer:done====
{{luaself|pre=Element{{ndash}}|\|args=dones}}
When called without arguments, this behaves just like {{luaref|mw.html:done}} as called on the outer HTML object.
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}}.
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{{ndash}}|:node|args=()}}, though that example does not work for selfClosing tags.)
===ipairs with HTML-Buffer===
:''See also [[#Using 'all' pairs outside of buffer]] for more details about Module:Buffer's custom iterator.
<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>
==Global functions==
Line 197 ⟶ 270:
==Modified {{code|..}} operator==
{{code|Buffer .. value}}<br />
<code>''
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.
<code>''
Line 209 ⟶ 282:
==Using 'all' pairs outside of buffer==
{{anchor|library}}
==String, mw.ustring, and mw.text functions==
Line 214 ⟶ 288:
==Tips and style recommendations==
* 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 {{
Line 226 ⟶ 300:
* Appending values in multiple locations is one of the primary reasons why the [[#nanKeys|nanKeys]] argument exists. While passing a boolean directly will cause an error, you can do something like...
::this:{{sp|3}}{{code
::versus: {{code|lang=lua|Buffer:_nil('0', condition and 'replacement' or false):_(condition and 'Front', 1):getParent(condition and 'from child'):_B(child)}}.
Line 234 ⟶ 308:
* Inserting a [[#Buffer:_G|named]] empty table is raw as a placeholder to be populated later via this function may be easier than calculating ''pos'' argument of {{luaself|:_}}.
'''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. Also note that Buffer:_inHTML is slower on the first run due to initialization. After the first run, the efficiency ratio of using mw.html.create directly over Buffer:_inHTML drops to 2.)
{{anchor|performance}}
|