diff --git a/README.rst b/README.rst index cd6ee5a..04552af 100644 --- a/README.rst +++ b/README.rst @@ -25,8 +25,6 @@ What is left to do * Make a clean interface to transmit them; - Check why exceptions on raw tags effectively escape the content, as it shouldn't…? -- Implement the ``inline`` tweak in order not to read blocks in the - translator. - Look for security flaws (we really don't want stored XSS flaws!). - Manage keywords with tags such as ``[tag key=value other="something else"]``. diff --git a/docs/usage.rst b/docs/usage.rst index d0edc89..6a23607 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -29,6 +29,8 @@ equivalent. The following tweaks are read by the translator and built-in tags: +- ``inline``: if ``True``, only use inline tags, not blocks (for inline + contexts such as instant messaging or one-line comments). - ``label_prefix`` (HTML): prefix to be used by the ``[label]`` and ``[target]`` tags, e.g. ``msg45529-``. Defaults to `""` for PCv42 compatibility; diff --git a/test/test_html.py b/test/html.py similarity index 96% rename from test/test_html.py rename to test/html.py index 3327a1d..a37a512 100755 --- a/test/test_html.py +++ b/test/html.py @@ -7,8 +7,10 @@ Uses the builtin `unittest` module. """ -import unittest -import textoutpc +import unittest as _unittest +import textoutpc as _textoutpc + +__all__ = ["TextoutHTMLTest"] # Define the tests. @@ -187,7 +189,7 @@ _cnt = 0 _len = len(str(len(__test_cases))) _templ = """\ def test_html{n:0>{l}}(self): - self.assertEqual(textoutpc.tohtml({i}), {r}, \\ + self.assertEqual(_textoutpc.tohtml({i}), {r}, \\ "for the following text: " + {i}) """ @@ -197,13 +199,13 @@ def _wrap_test(inp, res): _cnt += 1 return _templ.format(n = _cnt, l = _len, i = repr(inp), r = repr(res)) -exec("class TextoutHTMLTest(unittest.TestCase):\n maxDiff = None\n" + \ +exec("class TextoutHTMLTest(_unittest.TestCase):\n maxDiff = None\n" + \ '\n'.join(map(lambda args: _wrap_test(*args), __test_cases.items())), globals()) # If run as main script, run the test function. if __name__ == '__main__': - unittest.main() + çunittest.main() # End of file. diff --git a/test/htmli.py b/test/htmli.py new file mode 100755 index 0000000..08754e8 --- /dev/null +++ b/test/htmli.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +#****************************************************************************** +# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey +# This file is part of the textoutpc project, which is MIT-licensed. +#****************************************************************************** +""" Unit tests for the Python version of textout. + Uses the builtin `unittest` module. +""" + +import unittest as _unittest +import textoutpc as _textoutpc + +__all__ = ["TextoutHTMLiTest"] + +# Define the tests. + +__test_cases = { + # Basic text. + '': '', + 'lol': 'lol', + + # Basic text styling. + '[u][b]a[/]mdr': 'amdr', + + # Links. + '[url=https://thomas.touhey.fr/]': '' \ + 'https://thomas.touhey.fr/', +} + +# Define the tests wrapper, and define the classes. + +_cnt = 0 +_len = len(str(len(__test_cases))) +_templ = """\ + def test_htmli{n:0>{l}}(self): + self.assertEqual(_textoutpc.tohtml({i}, inline = True), {r}, \\ + "for the following text: " + {i}) +""" + +def _wrap_test(inp, res): + global _cnt + + _cnt += 1 + return _templ.format(n = _cnt, l = _len, i = repr(inp), r = repr(res)) + +exec("class _TextoutHTMLiTest(_unittest.TestCase):\n maxDiff = None\n" + \ + '\n'.join(map(lambda args: _wrap_test(*args), __test_cases.items())), + globals()) + +# If run as main script, run the test function. + +if __name__ == '__main__': + _unittest.main() + +# End of file. diff --git a/test/test_ls.py b/test/ls.py similarity index 79% rename from test/test_ls.py rename to test/ls.py index 128e279..e6bf883 100755 --- a/test/test_ls.py +++ b/test/ls.py @@ -7,8 +7,10 @@ Uses the builtin `unittest` module. """ -import unittest -import textoutpc +import unittest as _unittest +import textoutpc as _textoutpc + +__all__ = ["TextoutLSTest"] # Define the tests. @@ -23,7 +25,7 @@ _cnt = 0 _len = len(str(len(__test_cases))) _templ = """\ def test_lgsp{n:0>{l}}(self): - self.assertEqual(textoutpc.tolightscript({i}), {r}, {i}) + self.assertEqual(_textoutpc.tolightscript({i}), {r}, {i}) """ def _wrap_test(inp, res): @@ -32,13 +34,13 @@ def _wrap_test(inp, res): _cnt += 1 return _templ.format(n = _cnt, l = _len, i = repr(inp), r = repr(res)) -exec("class TextoutLSTest(unittest.TestCase):\n maxDiff = None\n" + \ +exec("class TextoutLSTest(_unittest.TestCase):\n maxDiff = None\n" + \ '\n'.join(map(lambda args: _wrap_test(*args), __test_cases.items())), globals()) # If run as main script, run the test function. if __name__ == '__main__': - unittest.main() + _unittest.main() # End of file. diff --git a/textoutpc/_translate.py b/textoutpc/_translate.py index 73b5d93..423c771 100755 --- a/textoutpc/_translate.py +++ b/textoutpc/_translate.py @@ -178,6 +178,20 @@ class Translator: self.raw_mode = False self.raw_deg = 0 + # `inline_mode` is whether the inline mode is on or not. + # Actually, for now, this mode is only global and cannot be enabled + # by tags. + + self.inline_mode = bool(self.tweak("inline", False)) + + def tweak(self, key, default = None): + """ Get a tweak from the tweaks dictionary. """ + + try: + return self.tweaks[key] + except KeyError: + return default + # --- # Text outputting utilities. # --- @@ -240,7 +254,8 @@ class Translator: # --- def add_text(self, text, process_func = lambda x: x, start_tags = True, - superblocks_only = False, next_block_is_super = False): + superblocks_only = False, next_block_is_super = False, + skip_first = False): """ Add text to the higher blocks if available. """ # The last queue is composed of booleans (does the group contain @@ -264,6 +279,12 @@ class Translator: else: continue + # Check if it is the first tag we want to skip. + + if skip_first: + skip_first = False + continue + # Contribute to it, either by or-ing the content if it is # a boolean (but anything or True == True), or by contributing # to the buffer otherwise. @@ -305,7 +326,8 @@ class Translator: self.outp.write(message) def put_code(self, code, start_tags = True, flush_text = True, - superblocks_only = True, next_block_is_super = False): + superblocks_only = True, next_block_is_super = False, + skip_first = False): """ Put some code. """ # We don't want to mix text and code, so we'll flush to be sure that @@ -323,7 +345,8 @@ class Translator: self.add_text(code, start_tags = start_tags, superblocks_only = superblocks_only, - next_block_is_super = next_block_is_super) + next_block_is_super = next_block_is_super, + skip_first = skip_first) def put_newline(self): """ Put a newline. """ @@ -575,7 +598,7 @@ class Translator: if hasattr(dat.tag, 'begin'): self.put_code(dat.tag.begin(), start_tags = False, - flush_text = False) + flush_text = False, skip_first = dat == self.queue[0]) dat.started = True def close_inline_tags(self): @@ -603,8 +626,9 @@ class Translator: # By default, everything is in a paragraph. # Other blocks will supplant this by being further in the queue. - self.push_tag(_TagData(_TextoutParagraphTag(None, None, - self.output_type, self.tweaks), None, '')) + if not self.inline_mode: + self.push_tag(_TagData(_TextoutParagraphTag(None, None, + self.output_type, self.tweaks), None, '')) # We want to get our elements out of the element stream (Lephe # told me that the `TextoutStream` class was actually a lexer, @@ -721,9 +745,15 @@ class Translator: self.put_text(tagdata.full) continue - # And don't forget to push the tag. + # Check if it is a block tag. dat = _TagData(tag, tagdata.name, tagdata.full) + if self.inline_mode and dat.type == dat.BLOCK: + self.put_text(tagdata.full) + continue + + # And don't forget to push the tag (through its data). + self.push_tag(dat) # Push a paragraph tag if the block is a superblock. @@ -733,11 +763,12 @@ class Translator: self.push_tag(_TagData(_TextoutParagraphTag(None, None, self.output_type, self.tweaks), None, '')) - # End of file, it seems! Let's close the tags and just resume our - # lives from there. + # End of file, it seems! Let's close the tags, flush the text + # and just resume our lives from there. while self.queue: self.pop_tag() + self.flush_text() # And don't forget to return the output for the user to chain # stuff easily ;)