Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Paste
P192
Automatic line numbering for Poem, based on core merge patch https://gerrit.wikimedia.org/r/#/c/106861/
Active
Public
Actions
Authored by
TTO
on Jan 4 2015, 5:03 AM.
Edit Paste
Archive Paste
View Raw File
Subscribe
Mute Notifications
Tags
MediaWiki-extensions-Poem
Referenced Files
F26515: Automatic_line_numbering_for_Poem,_based_on_core_merge_patch_https:_gerrit.wikimedia.org_r_c_106861
Jan 4 2015, 5:03 AM
2015-01-04 05:03:11 (UTC+0)
Subscribers
Ricordisamoa
diff --git a/includes/parser/CoreTagHooks.php b/includes/parser/CoreTagHooks.php
index 2e249d4..bd1f401 100644
--- a/includes/parser/CoreTagHooks.php
+++ b/includes/parser/CoreTagHooks.php
@@ -126,9 +126,35 @@ class CoreTagHooks {
* Core parser tag hook function for 'lines', aka 'poem'.
*
* Outputs text, maintaining line breaks and providing a smaller, line-based
- * indentation for line-initial colons instead of the default <dl>. Suitable
+ * indentation for line-initial colons instead of the default <dl>. Suitable
* for line-based text such as poetry and similar content.
*
+ * Valid attributes:
+ * <lines number-start="1"
+ * number-step="5|first|last|stanza[s]|stanza[s]-first|stanza[s]-last"
+ * number-show-first
+ * number-style="color: red">
+ *
+ * - number-start: The line number assigned to the first non-blank line of text,
+ * or the stanza number assigned to the first stanza. Integer >= 0, default 1.
+ * - number-step:
+ * - If an integer >= 1: Display line numbers every X lines (default 1)
+ * - first: Display a line number next to the first line of each stanza
+ * - last: Display a line number next to the last line of each stanza
+ * - stanza, stanzas, stanza-first, stanzas-first: Number stanzas
+ * (paragraphs) instead of lines, and number the first line of each
+ * stanza
+ * - stanza-last, stanzas-last: Number stanzas instead of lines, and number
+ * the last line of each stanza
+ * - number-show-first: If set, show a line number on the first line when
+ * number-step > 1. For example, <lines number-step=5> gives 5, 10, 15...,
+ * while <lines number-step=5 number-show-first> goes 1, 6, 11, 16... .
+ * This attribute is ignored when number-step is not an integer > 1.
+ * - number-style: A custom CSS style to be applied to line numbers.
+ *
+ * To enable line numbering, simply specify at least one of the attributes
+ * with any valid value.
+ *
* @param string $content The text inside the <lines> tag
* @param array $attributes
* @param Parser $parser
@@ -138,19 +164,183 @@ class CoreTagHooks {
static function lines( $content, $attributes, $parser, $frame = false ) {
// replace colons with indented spans
$text = preg_replace_callback( '/^(:+)(.+)$/m', array( __CLASS__, 'linesIndentVerse' ), $content );
+
+ // clear out lines containing whitespace only (needed to make line
+ // numbering work, but also good to keep HTML small)
+ $text = preg_replace( '/^[ \t\xC\xD\xA0]+$/mu', '', $text );
// replace spaces at the beginning of a line with non-breaking spaces
$text = preg_replace_callback( '/^( +)/m', array( __CLASS__, 'linesReplaceSpaces' ), $text );
+ // parse the wikitext inside the tag
$text = $parser->recursiveTagParse( $text, $frame );
+ // add line numbers
+ $useNumbering = isset( $attributes['number-start'] ) ||
+ isset( $attributes['number-step'] ) ||
+ isset( $attributes['number-show-first'] ) ||
+ isset( $attributes['number-style'] );
+ if ( $useNumbering ) {
+ $numberStart = isset( $attributes['number-start'] ) ?
+ intval( $attributes['number-start'] ) :
+ 1;
+ $numberStep = isset( $attributes['number-step'] ) ?
+ $attributes['number-step'] :
+ 1;
+ $numberShowFirst = isset( $attributes['number-show-first'] );
+
+ // validate possible values for number-step
+ $numberStepFirst = $numberStepLast = $numberStanzas = false;
+ if ( is_numeric( $numberStep ) ) {
+ $numberStep = intval( $numberStep );
+ if ( $numberStep <= 0 ) {
+ $numberStep = 1;
+ }
+ } else {
+ switch ( trim( $numberStep ) ) {
+ case 'first':
+ $numberStep = 0;
+ $numberStepFirst = true;
+ break;
+ case 'last':
+ $numberStep = 0;
+ $numberStepLast = true;
+ break;
+ case 'stanza':
+ case 'stanzas':
+ case 'stanza-first':
+ case 'stanzas-first':
+ $numberStep = 0;
+ $numberStepFirst = true;
+ $numberStanzas = true;
+ break;
+ case 'stanza-last':
+ case 'stanzas-last':
+ $numberStep = 0;
+ $numberStepLast = true;
+ $numberStanzas = true;
+ break;
+ default:
+ $numberStep = 1;
+ break;
+ }
+ }
+
+ // number-show-first doesn't make sense when number-step is 1,
+ // force it to false to get correct behavior (every line is numbered)
+ if ( $numberStep == 1 ) {
+ $numberShowFirst = false;
+ }
+
+ $numberTagAttribs = array(
+ 'class' => 'mw-lines-linenumber'
+ );
+ if ( isset( $attributes['number-style'] ) ) {
+ $numberTagAttribs['style'] = Sanitizer::checkCss( $attributes['number-style'] );
+ }
+
+ // iterate through the text, line by line, and add line numbers where needed
+ // Note: this loop is not multi-byte aware, but that does not matter:
+ // we are only inserting text before or after \n, so we run no risk of
+ // splitting up UTF-8 characters. $index is therefore a byte index
+ $index = 0;
+ $currentLineNum = $numberStart - 1;
+ $currentStanzaNum = $numberStart - 1;
+ do {
+ // the character index of the \n that ends the current line, or false
+ // if there are no more newlines
+ $nextNewlinePos = strpos( $text, "\n", $index );
+
+ // the character index at which to insert the line number
+ $lineNumIndex = -1;
+ // the line number to be shown
+ $lineNumToPrint = 0;
+
+ if ( $numberStepFirst && $index == 0 && $nextNewlinePos != 0 ) {
+ // the first line always needs a number if not blank
+ $lineNumIndex = 0;
+ $lineNumToPrint = $numberStanzas ? ++$currentStanzaNum : ++$currentLineNum;
+ } elseif (
+ $numberStepLast &&
+ ( $nextNewlinePos === false || $nextNewlinePos == strlen( $text ) - 1 ) &&
+ isset( $text[ $index - 2 ] ) &&
+ $text[ $index - 2 ] != "\n"
+ ) {
+ // the last line always needs a number if not blank
+ // (or the second-last line, if it itself is not blank but the last is blank)
+ $lineNumIndex = $index;
+ $lineNumToPrint = $numberStanzas ? ++$currentStanzaNum : ++$currentLineNum;
+ } elseif ( $nextNewlinePos != $index ) {
+ // this line has text on it, we need to count it
+ ++$currentLineNum;
+
+ // display a line number at the start of the line, if needed
+ if (
+ $numberStep &&
+ ( $currentLineNum % $numberStep == ( $numberShowFirst ? 1 : 0 ) )
+ ) {
+ $lineNumIndex = $index;
+ $lineNumToPrint = $currentLineNum;
+ }
+ } else {
+ // we're on a blank line, see if we need to add a number for the
+ // previous or next line
+ if (
+ $numberStepFirst &&
+ isset( $text[ $nextNewlinePos + 1 ] ) &&
+ $text[ $nextNewlinePos + 1 ] != "\n"
+ ) {
+ // place a line number at the start of the next line
+ $lineNumIndex = $nextNewlinePos + 1;
+ $lineNumToPrint = $numberStanzas ? ++$currentStanzaNum : ($currentLineNum + 1);
+ } elseif (
+ $numberStepLast &&
+ isset( $text[ $index - 2 ] ) &&
+ $text[ $index - 2 ] != "\n"
+ ) {
+ // place a line number at the start of the previous line
+ $lineNumIndex = strrpos( $text, "\n", $index - strlen( $text ) - 2 ) + 1;
+ $lineNumToPrint = $numberStanzas ? ++$currentStanzaNum : $currentLineNum;
+ }
+ }
+
+ if ( $lineNumIndex > -1 ) {
+ $numberTagAttribs['id'] = $lineNumToPrint;
+ $tagToAdd = Html::rawElement( 'span', $numberTagAttribs, "$lineNumToPrint " );
+ $text = substr_replace( $text, $tagToAdd, $lineNumIndex, 0 );
+
+ // if we are inserting the line number before the current position, move
+ // the current position along, to avoid infinite loops
+ if ( $lineNumIndex <= $index ) {
+ $index += strlen( $tagToAdd );
+ }
+ }
+
+ // sanity check? maybe not needed
+ if ( $index >= strlen( $text ) ) {
+ break;
+ }
+
+ // advance the counter variable. Needs to be at the end of the loop
+ // because the first iteration is a special case
+ $index = strpos( $text, "\n", $index );
+ } while ( $index !== false && ++$index < strlen( $text ) );
+ }
+
$attribs = Sanitizer::validateTagAttributes( $attributes, 'div' );
// Wrap output in a <pre> with appropriate class names.
- $attribs['class'] = 'poem mw-lines';
+ $className = 'poem mw-lines';
+ if ( $useNumbering ) {
+ $className .= ' mw-lines-numbered';
+ if ( $currentLineNum > 9999 || $currentStanzaNum > 9999 ) {
+ $className .= ' mw-lines-numbered-wide';
+ }
+ }
if ( isset( $attribs['class'] ) ) {
- $attribs['class'] .= ' ' . $attribs['class'];
+ $className .= ' ' . $attribs['class'];
}
+ $attribs['class'] = $className;
return Html::rawElement( 'pre', $attribs, $text );
}
diff --git a/skins/common/shared.css b/skins/common/shared.css
index f70299f..b925746 100644
--- a/skins/common/shared.css
+++ b/skins/common/shared.css
@@ -973,10 +973,33 @@ pre.mw-lines {
/* see also p, pre.mw-lines declaration in commonElements.css */
}
+pre.mw-lines.mw-lines-numbered {
+ margin-left: 2.64em; /* 88% (font size of line numbers) * 3em */
+}
+
+/* -wide is used for big line numbers (5 digits or more) */
+pre.mw-lines.mw-lines-numbered.mw-lines-numbered-wide {
+ margin-left: 4.4em; /* 88% * 5em */
+}
+
.mw-lines-indented {
display: inline-block;
}
+.mw-lines-linenumber {
+ float: left;
+ width: 2em;
+ margin-left: -3em;
+ font-size: 88%;
+ text-align: right;
+ color: #4E5A66;
+}
+
+.mw-lines-numbered-wide .mw-lines-linenumber {
+ width: 4em;
+ margin-left: -5em;
+}
+
/* AJAX loader */
.mw-ajax-loader {
/* @embed */
@@ -1239,7 +1262,8 @@ table.floatleft {
.mw-editsection,
.toctoggle,
-#jump-to-nav {
+#jump-to-nav,
+.mw-poem-linenumber {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
diff --git a/tests/parser/parserTests.txt b/tests/parser/parserTests.txt
index b691879..8fd28ff 100644
--- a/tests/parser/parserTests.txt
+++ b/tests/parser/parserTests.txt
@@ -15734,6 +15734,476 @@ test
!! end
!! test
+<lines> with line numbering enabled
+!! input
+<lines number-start=1>
+Tra
+la
+
+la.
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+<span class="mw-poem-linenumber" id="1">1 </span>Tra
+<span class="mw-poem-linenumber" id="2">2 </span>la
+
+<span class="mw-poem-linenumber" id="3">3 </span>la.
+</pre>
+
+!! end
+
+!! test
+<lines> with line numbering enabled and blank/whitespace-filled lines
+!! input
+<lines number-start=1>
+Tra
+
+
+la
+
+la.
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+<span class="mw-poem-linenumber" id="1">1 </span>Tra
+
+
+<span class="mw-poem-linenumber" id="2">2 </span>la
+
+<span class="mw-poem-linenumber" id="3">3 </span>la.
+</pre>
+
+!! end
+
+!! test
+<lines> with line numbering enabled and blank lines at start/end
+!! input
+<lines number-start=1>
+
+
+Tra
+la
+
+la.
+
+
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+
+
+<span class="mw-poem-linenumber" id="1">1 </span>Tra
+<span class="mw-poem-linenumber" id="2">2 </span>la
+
+<span class="mw-poem-linenumber" id="3">3 </span>la.
+
+
+</pre>
+
+!! end
+
+!! test
+<lines> with line numbering enabled and content on same line as tag
+!! input
+<lines number-start=1>Tra
+la
+la.</lines>
+!! result
+<pre class="poem mw-poem-numbered"><span class="mw-poem-linenumber" id="1">1 </span>Tra
+<span class="mw-poem-linenumber" id="2">2 </span>la
+<span class="mw-poem-linenumber" id="3">3 </span>la.</pre>
+
+!! end
+
+!! test
+<lines> with line numbering enabled and custom start/step values
+!! input
+<lines number-start=400 number-step=4>
+Tra
+la
+di
+
+da
+de
+la
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+<span class="mw-poem-linenumber" id="400">400 </span>Tra
+la
+di
+
+da
+<span class="mw-poem-linenumber" id="404">404 </span>de
+la
+</pre>
+
+!! end
+
+
+!! test
+<lines> with line numbering enabled and number-show-first set
+!! input
+<lines number-step=3 number-show-first>
+Tra
+la ha
+di
+da ba
+he.
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+<span class="mw-poem-linenumber" id="1">1 </span>Tra
+la ha
+di
+<span class="mw-poem-linenumber" id="4">4 </span>da ba
+he.
+</pre>
+
+!! end
+
+!! test
+<lines> with line numbering enabled and large line number values
+!! input
+<lines number-start=123456>
+Hey
+ho
+</lines>
+!! result
+<pre class="poem mw-poem-numbered mw-poem-numbered-wide">
+<span class="mw-poem-linenumber" id="123456">123456 </span>Hey
+<span class="mw-poem-linenumber" id="123457">123457 </span>ho
+</pre>
+
+!! end
+
+!! test
+<lines> with number-step=first line numbering
+!! input
+<lines number-step=first>
+John Dryden
+ascended
+
+and fell to
+earth
+
+like an
+eagle
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+<span class="mw-poem-linenumber" id="1">1 </span>John Dryden
+ascended
+
+<span class="mw-poem-linenumber" id="3">3 </span>and fell to
+earth
+
+<span class="mw-poem-linenumber" id="5">5 </span>like an
+eagle
+</pre>
+
+!! end
+
+!! test
+<lines> with number-step=last line numbering
+!! input
+<lines number-step=last>
+John Dryden
+ascended
+
+and fell to
+earth
+
+like an
+eagle
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+John Dryden
+<span class="mw-poem-linenumber" id="2">2 </span>ascended
+
+and fell to
+<span class="mw-poem-linenumber" id="4">4 </span>earth
+
+like an
+<span class="mw-poem-linenumber" id="6">6 </span>eagle
+</pre>
+
+!! end
+
+!! test
+<lines> with number-step=first line numbering and content on same line as tag
+!! input
+<lines number-step=first>John Dryden
+ascended
+
+and fell to
+earth
+
+like an
+eagle</lines>
+!! result
+<pre class="poem mw-poem-numbered"><span class="mw-poem-linenumber" id="1">1 </span>John Dryden
+ascended
+
+<span class="mw-poem-linenumber" id="3">3 </span>and fell to
+earth
+
+<span class="mw-poem-linenumber" id="5">5 </span>like an
+eagle</pre>
+
+!! end
+
+!! test
+<lines> with number-step=last line numbering and content on same line as tag
+!! input
+<lines number-step=last>John Dryden
+ascended
+
+and fell to
+earth
+
+like an
+eagle</lines>
+!! result
+<pre class="poem mw-poem-numbered">John Dryden
+<span class="mw-poem-linenumber" id="2">2 </span>ascended
+
+and fell to
+<span class="mw-poem-linenumber" id="4">4 </span>earth
+
+like an
+<span class="mw-poem-linenumber" id="6">6 </span>eagle</pre>
+
+!! end
+
+!! test
+<lines> with number-step=first line numbering and extra blank lines
+!! input
+<lines number-step=first>
+
+
+John Dryden
+ascended
+
+and fell to
+earth
+
+like an
+eagle
+
+
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+
+
+<span class="mw-poem-linenumber" id="1">1 </span>John Dryden
+ascended
+
+<span class="mw-poem-linenumber" id="3">3 </span>and fell to
+earth
+
+<span class="mw-poem-linenumber" id="5">5 </span>like an
+eagle
+
+
+</pre>
+
+!! end
+
+!! test
+<lines> with number-step=last line numbering and extra blank lines
+!! input
+<lines number-step=last>
+
+
+John Dryden
+ascended
+
+and fell to
+earth
+
+like an
+eagle
+
+
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+
+
+John Dryden
+<span class="mw-poem-linenumber" id="2">2 </span>ascended
+
+and fell to
+<span class="mw-poem-linenumber" id="4">4 </span>earth
+
+like an
+<span class="mw-poem-linenumber" id="6">6 </span>eagle
+
+
+</pre>
+
+!! end
+
+!! test
+<lines> with number-step=stanza-first line numbering
+!! input
+<lines number-step=stanza-first>
+John Dryden
+ascended
+
+and fell to
+earth
+
+like an
+eagle
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+<span class="mw-poem-linenumber" id="1">1 </span>John Dryden
+ascended
+
+<span class="mw-poem-linenumber" id="2">2 </span>and fell to
+earth
+
+<span class="mw-poem-linenumber" id="3">3 </span>like an
+eagle
+</pre>
+
+!! end
+
+!! test
+<lines> with number-step=stanza-last line numbering
+!! input
+<lines number-step=stanza-last>
+John Dryden
+ascended
+
+and fell to
+earth
+
+like an
+eagle
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+John Dryden
+<span class="mw-poem-linenumber" id="1">1 </span>ascended
+
+and fell to
+<span class="mw-poem-linenumber" id="2">2 </span>earth
+
+like an
+<span class="mw-poem-linenumber" id="3">3 </span>eagle
+</pre>
+
+!! end
+
+!! test
+<lines> with number-step=stanza-first line numbering and number-start specified
+!! input
+<lines number-start=567 number-step=stanza-first>
+John Dryden
+ascended
+
+and fell to
+earth
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+<span class="mw-poem-linenumber" id="567">567 </span>John Dryden
+ascended
+
+<span class="mw-poem-linenumber" id="568">568 </span>and fell to
+earth
+</pre>
+
+!! end
+
+!! test
+<lines> with number-step=stanza-first line numbering and number-start specified and large stanza number values
+!! input
+<lines number-start=234567 number-step=stanza-first>
+John Dryden
+ascended
+
+and fell to
+earth
+</lines>
+!! result
+<pre class="poem mw-poem-numbered mw-poem-numbered-wide">
+<span class="mw-poem-linenumber" id="234567">234567 </span>John Dryden
+ascended
+
+<span class="mw-poem-linenumber" id="234568">234568 </span>and fell to
+earth
+</pre>
+
+!! end
+
+!! test
+<lines> with line numbering enabled but no content
+!! input
+<lines number-start=1></lines>
+!! result
+<pre class="poem mw-poem-numbered"></pre>
+
+!! end
+
+!! test
+<lines> with line numbering enabled but just a blank line inside
+!! input
+<lines number-start=1>
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+</pre>
+
+!! end
+
+!! test
+<lines> with stanza-first numbering enabled but no content
+!! input
+<lines number-step=stanza-first></lines>
+!! result
+<pre class="poem mw-poem-numbered"></pre>
+
+!! end
+
+!! test
+<lines> with stanza-first numbering enabled but just a blank line inside
+!! input
+<lines number-step=stanza-first>
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+</pre>
+
+!! end
+
+!! test
+<lines> with stanza-last numbering enabled but no content
+!! input
+<lines number-step=stanza-last></lines>
+!! result
+<pre class="poem mw-poem-numbered"></pre>
+
+!! end
+
+!! test
+<lines> with stanza-last numbering enabled but just a blank line inside
+!! input
+<lines number-step=stanza-last>
+</lines>
+!! result
+<pre class="poem mw-poem-numbered">
+</pre>
+
+!! end
+
+!! test
HTML Hex character encoding (spells the word "JavaScript")
!! options
parsoid=wt2html,wt2wt,html2html
Event Timeline
TTO
edited the content of this paste.
(Show Details)
Jan 4 2015, 5:03 AM
2015-01-04 05:03:11 (UTC+0)
TTO
changed the title of this paste from untitled to
Automatic line numbering for Poem, based on core merge patch https://gerrit.wikimedia.org/r/#/c/106861/
.
TTO
updated the paste's language from
autodetect
to
diff
.
TTO
added a project:
MediaWiki-extensions-Poem
.
TTO
mentioned this in
T15644: Add automatic line numbering capability to <poem> tag
.
Jan 4 2015, 5:08 AM
2015-01-04 05:08:30 (UTC+0)
Ricordisamoa
awarded a token.
Jan 5 2015, 12:14 PM
2015-01-05 12:14:16 (UTC+0)
Ricordisamoa
subscribed.
Log In to Comment