From 429e3a428d3bd7f681bea753525dcffba078c4bf Mon Sep 17 00:00:00 2001 From: Martin Vilcans Date: Wed, 8 Feb 2012 00:05:01 +0100 Subject: Added support for sections --- screenplain/export/fdx.py | 4 +++- screenplain/export/html.py | 31 ++++++++++++++++++------------- screenplain/parsers/spmd.py | 15 ++++++++++++++- screenplain/types.py | 9 +++++++-- tests/files/sections.spmd | 13 +++++++++++++ tests/files/sections.spmd.html | 7 +++++++ tests/files_test.py | 6 ++++++ tests/spmd_test.py | 16 +++++++++++++++- 8 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 tests/files/sections.spmd create mode 100644 tests/files/sections.spmd.html diff --git a/screenplain/export/fdx.py b/screenplain/export/fdx.py index 2c4b92a..f476468 100644 --- a/screenplain/export/fdx.py +++ b/screenplain/export/fdx.py @@ -91,7 +91,9 @@ def to_fdx(screenplay, out): elif isinstance(para, Transition): write_paragraph(out, 'Transition', para.lines) else: - raise RuntimeError('Unknown type: %s' % type(para)) + # Ignore unknown types + pass + out.write( ' \n' '\n' diff --git a/screenplain/export/html.py b/screenplain/export/html.py index e003bb8..886318c 100644 --- a/screenplain/export/html.py +++ b/screenplain/export/html.py @@ -92,6 +92,11 @@ def format_slug(slug, out): out.write(to_html(slug.scene_number)) +def format_section(section, out): + with tags(out, 'h%d' % section.level): + out.write(to_html(section.text)) + + def format_action(para, out): if para.centered: tag = 'div class="action centered"' @@ -153,6 +158,15 @@ def convert_full(screenplay, out): '\n' ) +_format_functions = { + Slug: format_slug, + Action: format_action, + Dialog: format_dialog, + DualDialog: format_dual, + Transition: format_transition, + Section: format_section, +} + def convert_bare(screenplay, out): """Convert the screenplay into HTML, written to the file-like object `out`. @@ -161,16 +175,7 @@ def convert_bare(screenplay, out): """ for para in screenplay: - if isinstance(para, Slug): - format_slug(para, out) - elif isinstance(para, Action): - format_action(para, out) - elif isinstance(para, Dialog): - format_dialog(para, out) - elif isinstance(para, DualDialog): - format_dual(para, out) - elif isinstance(para, Transition): - format_transition(para, out) - else: - assert False, 'Unknown type: %s' % type(para) - out.write('\n') + format_function = _format_functions.get(type(para), None) + if format_function: + format_function(para, out) + out.write('\n') diff --git a/screenplain/parsers/spmd.py b/screenplain/parsers/spmd.py index c1b9202..2940a8c 100644 --- a/screenplain/parsers/spmd.py +++ b/screenplain/parsers/spmd.py @@ -6,7 +6,7 @@ import itertools import re from screenplain.types import ( - Slug, Action, Dialog, DualDialog, Transition + Slug, Action, Dialog, DualDialog, Transition, Section ) from screenplain.richstring import parse_emphasis, plain @@ -22,6 +22,7 @@ centered_re = re.compile(r'\s*>\s*(.*?)\s*<\s*$') dual_dialog_re = re.compile(r'^(.+?)(\s*\^)$') slug_re = re.compile(r'(?:(\.)\s*)?(\S.*?)\s*$') scene_number_re = re.compile(r'(.*?)\s*(?:#([\w\-.]+)#)\s*$') +section_re = re.compile(r'^(#{1,6})\s*([^#].*)$') transition_re = re.compile(r'(>?)\s*(.+?)(:?)$') @@ -45,6 +46,7 @@ class InputParagraph(object): Modifies the `previous_paragraphs` list. """ previous_paragraphs.append( + self.as_section() or self.as_slug() or self.as_centered_action() or self.as_dialog(previous_paragraphs) or @@ -72,6 +74,17 @@ class InputParagraph(object): else: return Slug(_to_rich(text)) + def as_section(self): + if len(self.lines) != 1: + return None + + match = section_re.match(self.lines[0]) + if not match: + return None + + hashes, text = match.groups() + return Section(_to_rich(text), len(hashes)) + def as_centered_action(self): if not all(centered_re.match(line) for line in self.lines): return None diff --git a/screenplain/types.py b/screenplain/types.py index fb21c1b..d814af3 100644 --- a/screenplain/types.py +++ b/screenplain/types.py @@ -6,8 +6,6 @@ import textwrap class Slug(object): - indent = '' - top_margin = 1 def __init__(self, line, scene_number=None): """Creates a scene heading (slug). @@ -23,6 +21,13 @@ class Slug(object): return [self.line] +class Section(object): + """A section heading.""" + def __init__(self, text, level): + self.text = text + self.level = level + + class Dialog(object): def __init__(self, character, lines): self.character = character diff --git a/tests/files/sections.spmd b/tests/files/sections.spmd new file mode 100644 index 0000000..96a2266 --- /dev/null +++ b/tests/files/sections.spmd @@ -0,0 +1,13 @@ +# First level + +## Second level + +EXT. STREET - DAWN + +DAWN walks down the street. + +## Second level again + +EXT. ALLEY - CONTINUOUS + +Dawn walks into this place. diff --git a/tests/files/sections.spmd.html b/tests/files/sections.spmd.html new file mode 100644 index 0000000..d06decb --- /dev/null +++ b/tests/files/sections.spmd.html @@ -0,0 +1,7 @@ +

First level

+

Second level

+
EXT. STREET - DAWN
+

DAWN walks down the street.

+

Second level again

+
EXT. ALLEY - CONTINUOUS
+

Dawn walks into this place.

diff --git a/tests/files_test.py b/tests/files_test.py index 1637dda..41d17bb 100644 --- a/tests/files_test.py +++ b/tests/files_test.py @@ -64,3 +64,9 @@ class ParseTests(unittest2.TestCase): 'scene-numbers.spmd', 'scene-numbers.html', 'scene-numbers.spmd.html', '--bare') self.assertMultiLineEqual(expected, actual) + + def test_sections(self): + actual, expected = self.convert( + 'sections.spmd', 'sections.html', + 'sections.spmd.html', '--bare') + self.assertMultiLineEqual(expected, actual) diff --git a/tests/spmd_test.py b/tests/spmd_test.py index 21f39ae..aac8df9 100644 --- a/tests/spmd_test.py +++ b/tests/spmd_test.py @@ -4,7 +4,9 @@ import unittest2 from screenplain.parsers.spmd import parse -from screenplain.types import Slug, Action, Dialog, DualDialog, Transition +from screenplain.types import ( + Slug, Action, Dialog, DualDialog, Transition, Section +) from screenplain.richstring import plain, italic, empty_string @@ -80,6 +82,18 @@ class ParseTests(unittest2.TestCase): paras[0].line ) + def test_section_parsed_correctly(self): + paras = parse([ + '# first level', + '', + '## second level', + ]) + self.assertEquals([Section, Section], [type(p) for p in paras]) + self.assertEquals(1, paras[0].level) + self.assertEquals(plain('first level'), paras[0].text) + self.assertEquals(2, paras[1].level) + self.assertEquals(plain('second level'), paras[1].text) + # A Character element is any line entirely in caps, with one empty # line before it and without an empty line after it. def test_all_caps_is_character(self): -- cgit