This Lua module is used in system messages, and on approximately 19,100,000 pages, or roughly 29% of all pages. Changes to it can cause immediate changes to the Wikipedia user interface. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Please discuss changes on the talk page before implementing them.
This module is rated as ready for general use. It has reached a mature state, is considered relatively stable and bug-free, and may be used wherever appropriate. It can be mentioned on help pages and other Wikipedia resources as an option for new users. To minimise server load and avoid disruptive output, improvements should be developed through sandbox testing rather than repeated trial-and-error editing.
This module provides functions to help with the complex edge cases involved in modules like Module:Template parameter value which intend to process the raw wikitext of a page while respecting nowiki tags or similar content reliably. This module is designed to be called by other modules, and does not support invoking.
PrepareText
PrepareText(text, keepComments) will run any content within certain tags that normally disable processing (, , , , ) through mw.text.nowiki and remove HTML comments. This allows for tricky syntax to be parsed through more basic means such as %b{} by other modules without worrying about edge cases.
If the second parameter, keepComments, is set to true, the content of HTML comments will be passed through mw.text.nowiki instead of being removed entirely.
Any code using this function directly should consider using mw.text.decode to correct the output at the end if part of the processed text is returned, though this will also decode any input that was encoded but not inside a no-processing tag, which likely isn't a significant issue but still something worth noting.
require("strict")-- We're calling up to thousands of string methods each time we run, so-- do this very simple micro optimisation to help a tiny bitlocalstring=string-- Helper functions for PrepareText --localfunctionendswith(text,subtext)returnstring.sub(text,-#subtext,-1)==subtextendlocalfunctionallcases(s)returnstring.gsub(s,"%a",function(c)return"["..string.upper(c)..string.lower(c).."]"end)end--[=[ Implementation notes---- NORMAL HTML TAGS ----Tags are very strict on how they want to start, but loose on how they end.The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace inthe tag's name, but may then flow as they want afterwards, making
valid. If a tag has no end, it will consume all
text instead of not processing.There's no sense of escaping < or >E.g.
will end at \> despite it being inside a quote
error"> will not process the larger div
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source, etc.) ----(Note: is the deprecated version of )No-processing tags have some differences to the above rules. Specifically, theirsyntax is a lot stricter. While an opening tag follows the same set of rules, Aclosing tag can't have any sort of extra formatting.
is valid, is not. Only newlines and spaces/tabs are allowed in closing tags.Note that, even though tags may cause a visual change without an ending tag like
, one is required for the no-processing effects.
Both the content inside the tag pair and the text in the tags will not beprocessed. E.g. |}} would have both of the |}} escaped.Since we only care about these no-processing tags, we can ignore the idea of anintercepting tag messing us up, and just go for the first ending we can find. Ifthere is no ending, the tag will NOT consume the rest of the text. Even if thereis no ending tag, the content inside the opening tag will still be unprocessed,meaning {{X20|}} wouldn't end at the first }} despite there being noending tag.There are some tags, like purposes, and are handled here. Some other tags, like , have more complexbehaviour that can't be reasonably implemented in this, and so are ignored. Isuspect that every tag listed in [[Special:Version]] may behave somewhat likethis, but that's far too many cases worth checking for rarely used tags that maynot even have a good reason to contain {{ or }} anyways, so we leave them alone.---- INCLUDEONLY ----While includeonly tags do technically serve the same purpose as a nowiki tag forwhat this module does, contextually they don't always make sense to escape, andas such are ignored by this module entirely.--]=]-- This function expects the string to start with the taglocalvalidtags={nowiki=1,pre=1,syntaxhighlight=1,source=1,math=1}localfunctionTestForNowikiTag(text,scanPosition)localtagName=string.match(text,"^<([^\n />]+)",scanPosition)ifnottagNameornotvalidtags[string.lower(tagName)]thenreturnnilendlocalnextOpener=string.find(text,"<",scanPosition+1,true)localnextCloser=string.find(text,">",scanPosition+1,true)ifnextCloserand(notnextOpenerornextCloser<nextOpener)thenlocalstartingTag=string.sub(text,scanPosition,nextCloser)-- We have our starting tag (E.g. '
')
-- Now find our ending...ifendswith(startingTag,"/>")then-- self-closing tag (we are our own ending)return{Tag=tagName,Start=startingTag,Content="",End="",Length=#startingTag}elselocalendingTagStart,endingTagEnd=string.find(text,""..allcases(tagName).."[ \t\n]*>",scanPosition)ifendingTagStartthen--Regular tag formationlocalendingTag=string.sub(text,endingTagStart,endingTagEnd)localtagContent=string.sub(text,nextCloser+1,endingTagStart-1)return{Tag=tagName,Start=startingTag,Content=tagContent,End=endingTag,Length=#startingTag+#tagContent+#endingTag}else-- Content inside still needs escaping (also linter error!)return{Tag=tagName,Start=startingTag,Content="",End="",Length=#startingTag}endendendreturnnilend--[=[ Implementation NotesHTML Comments are about as basic as it gets. Start at , no extraconditions. If a comment has no end, it will eat all the text ahead.--]=]localfunctionTestForComment(text,scanPosition)ifstring.match(text,"^,scanPosition)thenlocalcommentEnd=string.find(text,"-->",scanPosition+4,true)ifcommentEndthenreturn{Start="",Content=string.sub(text,scanPosition+4,commentEnd-1),Length=commentEnd-scanPosition+3}else-- Consumes all text if not given an endingreturn{Start="Should see|Shouldn't see}}]=]local out = p.PrepareText(s)mw.logObject(out)local s = [=[BA]=]local out = p.TestForComment(s, 2)mw.logObject(out); mw.log(string.sub(s, 2, out.Length))]==]