aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Vilcans <martin@librador.com>2012-02-08 00:05:01 +0100
committerMartin Vilcans <martin@librador.com>2012-02-08 00:05:10 +0100
commit429e3a428d3bd7f681bea753525dcffba078c4bf (patch)
tree1155c601202b7a2576a5a1edcfa90bb94bbffec0
parentb2dbd04a50a159ba18caa152ae98267a16f89c28 (diff)
downloadscreenplain-429e3a428d3bd7f681bea753525dcffba078c4bf.tar.gz
Added support for sections
-rw-r--r--screenplain/export/fdx.py4
-rw-r--r--screenplain/export/html.py31
-rw-r--r--screenplain/parsers/spmd.py15
-rw-r--r--screenplain/types.py9
-rw-r--r--tests/files/sections.spmd13
-rw-r--r--tests/files/sections.spmd.html7
-rw-r--r--tests/files_test.py6
-rw-r--r--tests/spmd_test.py16
8 files changed, 83 insertions, 18 deletions
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(
' </Content>\n'
'</FinalDraft>\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):
'</html>\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 @@
+<h1>First level</h1>
+<h2>Second level</h2>
+<h6>EXT. STREET - DAWN</h6>
+<div class="action"><p>DAWN walks down the street.</p></div>
+<h2>Second level again</h2>
+<h6>EXT. ALLEY - CONTINUOUS</h6>
+<div class="action"><p>Dawn walks into this place.</p></div>
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):