| @@ -162,14 +162,8 @@ class Builder: | |||||
| self._push() | self._push() | ||||
| elif isinstance(token, tokens.WikilinkClose): | elif isinstance(token, tokens.WikilinkClose): | ||||
| if title is not None: | if title is not None: | ||||
| wikilink = Wikilink(title, self._pop()) | |||||
| else: | |||||
| wikilink = Wikilink(self._pop()) | |||||
| # MediaWiki treats [[]] and [[|...]] (empty title) as plain | |||||
| # text rather than actual wikilinks. | |||||
| if not str(wikilink.title): | |||||
| return Text(str(wikilink)) | |||||
| return wikilink | |||||
| return Wikilink(title, self._pop()) | |||||
| return Wikilink(self._pop()) | |||||
| else: | else: | ||||
| self._write(self._handle_token(token)) | self._write(self._handle_token(token)) | ||||
| raise ParserError("_handle_wikilink() missed a close token") | raise ParserError("_handle_wikilink() missed a close token") | ||||
| @@ -761,8 +761,12 @@ class Wikicode(StringMixIn): | |||||
| This is equivalent to :meth:`ifilter` with *forcetype* set to | This is equivalent to :meth:`ifilter` with *forcetype* set to | ||||
| :class:`~wikilink.Wikilink`. | :class:`~wikilink.Wikilink`. | ||||
| """ | """ | ||||
| return self.ifilter( | |||||
| recursive=recursive, matches=matches, flags=flags, forcetype=Wikilink | |||||
| return ( | |||||
| node | |||||
| for node in self.ifilter( | |||||
| recursive=recursive, matches=matches, flags=flags, forcetype=Wikilink | |||||
| ) | |||||
| if str(node.title) | |||||
| ) | ) | ||||
| @overload | @overload | ||||
| @@ -934,8 +938,8 @@ class Wikicode(StringMixIn): | |||||
| This is equivalent to :meth:`filter` with *forcetype* set to | This is equivalent to :meth:`filter` with *forcetype* set to | ||||
| :class:`~wikilink.Wikilink`. | :class:`~wikilink.Wikilink`. | ||||
| """ | """ | ||||
| return self.filter( | |||||
| recursive=recursive, matches=matches, flags=flags, forcetype=Wikilink | |||||
| return list( | |||||
| self.ifilter_wikilinks(recursive=recursive, matches=matches, flags=flags) | |||||
| ) | ) | ||||
| def get_sections( | def get_sections( | ||||
| @@ -113,13 +113,15 @@ def test_skip_style_tags(pyparser): | |||||
| "[[|||]]", | "[[|||]]", | ||||
| ], | ], | ||||
| ) | ) | ||||
| def test_empty_title_wikilink_is_text(text): | |||||
| """[[]] and [[|...]] have an empty title and should not be wikilinks. | |||||
| def test_empty_title_wikilink_filter_wikilinks(text): | |||||
| """[[]] and [[|...]] have an empty title and should not be links. | |||||
| MediaWiki does not render these as hyperlinks; the parser must treat them | |||||
| as plain text so that filter_wikilinks() does not return false positives. | |||||
| MediaWiki does not render these as hyperlinks; filter_wikilinks() should | |||||
| not return false positives, while the parsed Wikilink node remains | |||||
| available to tools that want to detect and fix invalid wikilinks. | |||||
| Regression test for https://github.com/earwig/mwparserfromhell/issues/292. | Regression test for https://github.com/earwig/mwparserfromhell/issues/292. | ||||
| """ | """ | ||||
| parsed = parser.Parser().parse(text) | parsed = parser.Parser().parse(text) | ||||
| assert parsed.filter_wikilinks() == [] | assert parsed.filter_wikilinks() == [] | ||||
| assert isinstance(parsed.get(0), Wikilink) | |||||
| assert str(parsed) == text | assert str(parsed) == text | ||||