diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,17 @@
 # Revision history for WEditor
 
+## 0.2.0.0  -- 2020-04-17
+
+* **[breaking]** Adds line-splitting to `FixedFontParser` requirements. This
+  ensures that a custom parser that formats the front of the line (e.g., adding
+  block indentation) works properly when paragraph breaks are inserted. This
+  does not affect compilation or behavior of code that isn't using custom
+  line-wrapping policies.
+
+* **[breaking]** Adds vertical shifting of the viewable text area. This only
+  affects compilation of custom `FixedFontViewer`, and otherwise extends the
+  available functionality of existing code.
+
 ## 0.1.0.0  -- 2020-04-14
 
 * First version. Released on an unsuspecting world.
diff --git a/WEditor.cabal b/WEditor.cabal
--- a/WEditor.cabal
+++ b/WEditor.cabal
@@ -1,5 +1,5 @@
 name:                WEditor
-version:             0.1.0.0
+version:             0.2.0.0
 synopsis:            Generic text-editor logic for use with fixed-width fonts.
 
 description:
@@ -10,14 +10,15 @@
   .
   * Dynamic line wrapping that preserves the cursor and edit positions.
   * Customizable line-wrapping policies and word-hyphenation policies.
-  * Automatic management of a fixed-size viewable area.
+  * Automatic management of a dynamically-sized viewable area.
   * Support for novel character types.
 
 homepage:            https://github.com/ta0kira/wrapping-editor
 license:             Apache-2.0
 license-file:        LICENSE
 author:              Kevin P. Barry
-maintainer:          ta0kira@gmail.com
+maintainer:          Kevin P. Barry <ta0kira@gmail.com>
+copyright:           (c) Kevin P. Barry 2020
 category:            Text
 build-type:          Simple
 
diff --git a/WEditor/Base/Line.hs b/WEditor/Base/Line.hs
--- a/WEditor/Base/Line.hs
+++ b/WEditor/Base/Line.hs
@@ -32,8 +32,3 @@
     vlText :: [c] -- ^ The complete data of the line.
   }
   deriving (Eq,Ord,Show)
-
--- | Break type that has a default.
-class DefaultBreak b where
-  -- | The default break for the break type.
-  defaultBreak :: b
diff --git a/WEditor/Base/Parser.hs b/WEditor/Base/Parser.hs
--- a/WEditor/Base/Parser.hs
+++ b/WEditor/Base/Parser.hs
@@ -26,7 +26,6 @@
 
 module WEditor.Base.Parser (
   FixedFontParser(..),
-  emptyLine,
 ) where
 
 import WEditor.Base.Line
@@ -36,8 +35,6 @@
 class FixedFontParser a c | a -> c where
   -- | Type used to differentiate between line-break types.
   type BreakType a :: *
-  -- | An acceptable fallback line-break type.
-  defaultBreak :: a -> BreakType a
   -- | Change the max line width used for parsing. A width of zero must result
   --   in breakLines skipping line breaks.
   setLineWidth :: a -> Int -> a
@@ -51,13 +48,17 @@
   --   Implement 'renderLine' and 'tweakCursor' to make visual adjustments (such
   --   as adding hyphens or indentation) if necessary.
   breakLines :: a -> [c] -> [VisibleLine c (BreakType a)]
+  -- | A place-holder line for empty paragraphs.
+  emptyLine :: a -> VisibleLine c (BreakType a)
   -- | Render the line for viewing. Implement 'tweakCursor' if 'renderLine'
   --   changes the positions of any characters on the line.
   renderLine :: a -> VisibleLine c (BreakType a) -> [c]
   -- | Adjust the horizontal cursor position.
   tweakCursor :: a -> VisibleLine c (BreakType a) -> Int -> Int
   tweakCursor _ _ = id
-
--- | Create an empty line.
-emptyLine :: FixedFontParser a c => a -> VisibleLine c (BreakType a)
-emptyLine p = VisibleLine (defaultBreak p) []
+  -- | Split the line to create a paragraph break.
+  splitLine :: a
+            -> Int                           -- ^ Index to split at.
+            -> VisibleLine c (BreakType a)   -- ^ Line to split.
+            -> (VisibleLine c (BreakType a),
+                VisibleLine c (BreakType a)) -- ^ End of original paragraph and beginning of next paragraph.
diff --git a/WEditor/Base/Viewer.hs b/WEditor/Base/Viewer.hs
--- a/WEditor/Base/Viewer.hs
+++ b/WEditor/Base/Viewer.hs
@@ -25,25 +25,48 @@
 
 module WEditor.Base.Viewer (
   FixedFontViewer(..),
+  ViewAction(..),
   ViewerAction,
+  viewerFillAction,
   viewerResizeAction,
+  viewerShiftUpAction,
+  viewerShiftDownAction,
 ) where
 
 
 -- | Generic editor viewport for fixed-width fonts.
 class FixedFontViewer a c | a -> c where
-  -- | Sets the (width,height) size of the viewport. A width < 0 must disable
+  -- | Set the (width,height) size of the viewport. A width < 0 must disable
   --   line wrapping, and a height < 0 must disable vertical bounding.
   setViewSize :: a -> (Int,Int) -> a
-  -- | Gets the (width,height) size of the viewport.
+  -- | Get the (width,height) size of the viewport.
   getViewSize :: a -> (Int,Int)
-  -- | Gets the visible lines in the viewport. This does not need to completely
+  -- | Get the visible lines in the viewport. This does not need to completely
   --   fill the viewport area, but it must not exceed it.
   getVisible :: a -> [[c]]
+  -- | Apply a view change.
+  updateView :: a -> ViewAction -> a
 
+-- | Actions that modify the view without affecting editing.
+data ViewAction =
+  ShiftVertical Int | -- ^ Shift the vertical offset. Negative values shift up.
+  FillView            -- ^ Attempt to fill the entire viewport.
+    deriving (Eq,Show)
+
 -- | Any action that updates a 'FixedFontViewer'.
 type ViewerAction c = forall a. FixedFontViewer a c => a -> a
 
 -- | Action to resize the viewport.
 viewerResizeAction :: (Int,Int) -> ViewerAction c
 viewerResizeAction s v = setViewSize v s
+
+-- | Action to shift the view upward.
+viewerShiftUpAction :: Int -> ViewerAction c
+viewerShiftUpAction n v = updateView v (ShiftVertical (-n))
+
+-- | Action to shift the view downward.
+viewerShiftDownAction :: Int -> ViewerAction c
+viewerShiftDownAction n v = updateView v (ShiftVertical n)
+
+viewerFillAction :: ViewerAction c
+viewerFillAction v = updateView v FillView
diff --git a/WEditor/Document.hs b/WEditor/Document.hs
--- a/WEditor/Document.hs
+++ b/WEditor/Document.hs
@@ -51,7 +51,10 @@
   editorRightAction,
   editorSetPositionAction,
   editorUpAction,
+  viewerFillAction,
   viewerResizeAction,
+  viewerShiftUpAction,
+  viewerShiftDownAction,
   -- <<< From Base
 ) where
 
@@ -61,6 +64,9 @@
 
 
 -- | Generic document editor with a dynamic viewport.
+--
+--   This editor bounds vertical view scrolling (see 'ViewAction') to keep the
+--   cursor within view.
 data EditingDocument c =
   forall a. FixedFontParser a c => EditingDocument {
     edBefore :: [VisibleParaBefore c (BreakType a)],  -- Reversed.
@@ -108,6 +114,7 @@
     | otherwise = d
   getViewSize d = (edWidth d,edHeight d)
   getVisible = getVisibleLines
+  updateView = flip updateDocView
 
 instance FixedFontEditor (EditingDocument c) c where
   editText da m d = storeCursor $ modifyDoc m d da
@@ -162,14 +169,19 @@
 resizeHeight h da@(EditingDocument bs e as w _ k c p) =
   (EditingDocument bs e as w h offset c p) where
     offset
-      | h < 1 = countLinesAbove da
-      | otherwise = boundOffset h $ min (countLinesAbove da) k
+      | h < 1 = paraLinesBefore da
+      | otherwise = boundOffset h $ min (paraLinesBefore da) k
 
-countLinesAbove :: EditingDocument c -> Int
-countLinesAbove (EditingDocument bs e _ _ _ _ _ _) = total where
+paraLinesBefore :: EditingDocument c -> Int
+paraLinesBefore (EditingDocument bs e _ _ _ _ _ _) = total where
   total = countLinesBefore bs'
   bs' = getBeforeLines e:bs
 
+paraLinesAfter :: EditingDocument c -> Int
+paraLinesAfter (EditingDocument _ e as _ _ _ _ _) = total where
+  total = countLinesAfter as'
+  as' = getAfterLines e:as
+
 getVisibleLines :: EditingDocument c -> [[c]]
 getVisibleLines (EditingDocument bs e as _ h k _ p) = visible where
   visible = map (renderLine p) $ bs2 ++ [e2] ++ as2
@@ -260,3 +272,17 @@
 
 setDocEditPoint :: (Int,Int) -> EditingDocument c -> EditingDocument c
 setDocEditPoint (p,c) = setEditChar c . setEditPara p
+
+updateDocView :: ViewAction -> EditingDocument c -> EditingDocument c
+updateDocView _ da@(EditingDocument _ _ _ _ h _ _ _) | h <= 0 = da
+updateDocView FillView da@(EditingDocument bs e as w h k c p) = revised where
+  revised = EditingDocument bs e as w h (boundOffset h k2) c p
+  before = paraLinesBefore da
+  after  = paraLinesAfter  da
+  k2 = if after >= h-k-1  -- The view is already full.
+          then k
+          else h-after-1  -- k2+after=h-1, i.e., a full viewport.
+updateDocView (ShiftVertical n) da@(EditingDocument bs e as w h k c p) = revised where
+  revised = EditingDocument bs e as w h (boundOffset h k2) c p
+  before = paraLinesBefore da
+  k2 = min before (k-n)
diff --git a/WEditor/Internal/Line.hs b/WEditor/Internal/Line.hs
--- a/WEditor/Internal/Line.hs
+++ b/WEditor/Internal/Line.hs
@@ -33,7 +33,7 @@
   moveLineCursor,
   prependToLine,
   setLineCursor,
-  splitLine,
+  splitLineAtCursor,
   viewLine,
 ) where
 
@@ -69,10 +69,9 @@
     | k > n && not (null as) = seek (n+1) (head as:bs) (tail as)
     | otherwise = (bs,as)
 
-splitLine :: b -> EditingLine c b -> (VisibleLine c b,VisibleLine c b)
-splitLine b0 (EditingLine bs as b) =
-  (VisibleLine b0 (reverse bs),
-   VisibleLine b as)
+splitLineAtCursor :: (Int -> VisibleLine c b -> (VisibleLine c b,VisibleLine c b))
+                  -> EditingLine c b -> (VisibleLine c b,VisibleLine c b)
+splitLineAtCursor f l@(EditingLine bs _ _) = f (length bs) (viewLine l)
 
 lineCursorMovable :: MoveDirection -> EditingLine c b -> Bool
 lineCursorMovable MovePrev (EditingLine (_:_) _ _) = True
diff --git a/WEditor/Internal/Para.hs b/WEditor/Internal/Para.hs
--- a/WEditor/Internal/Para.hs
+++ b/WEditor/Internal/Para.hs
@@ -154,9 +154,11 @@
 takeLinesAfter :: Int -> [VisibleParaAfter c b] -> [VisibleLine c b]
 takeLinesAfter n = take n . concat . map vpaLines
 
+-- TODO: Add an upper bound, to avoid unnecessary traversal.
 countLinesBefore :: [VisibleParaBefore c b] -> Int
 countLinesBefore = length . concat . map vpbLines
 
+-- TODO: Add an upper bound, to avoid unnecessary traversal.
 countLinesAfter :: [VisibleParaAfter c b] -> Int
 countLinesAfter = length . concat . map vpaLines
 
@@ -182,7 +184,7 @@
   | otherwise = p
 
 splitPara :: FixedFontParser a c => a -> EditingPara c (BreakType a) -> (UnparsedPara c,UnparsedPara c)
-splitPara parser (EditingPara bs l as _) = let (b,a) = splitLine (defaultBreak parser) l in
+splitPara parser (EditingPara bs l as _) = let (b,a) = splitLineAtCursor (splitLine parser) l in
   (unparseParaBefore $ VisibleParaBefore (b:bs) 0,
    unparseParaAfter  $ VisibleParaAfter  (a:as))
 
diff --git a/WEditor/LineWrap.hs b/WEditor/LineWrap.hs
--- a/WEditor/LineWrap.hs
+++ b/WEditor/LineWrap.hs
@@ -154,9 +154,12 @@
 
 instance FixedFontParser (BreakWords c) c where
   type BreakType (BreakWords c) = LineBreak
-  defaultBreak _ = lineBreakEnd
   setLineWidth (BreakWords _ s) w = BreakWords w s
   breakLines (BreakWords w s) = breakAllLines w s
+  splitLine _ k (VisibleLine b cs) =
+    (VisibleLine lineBreakEnd (take k cs),
+     VisibleLine b            (drop k cs))
+  emptyLine _ = VisibleLine lineBreakEnd []
   renderLine (BreakWords w _) (VisibleLine ParagraphEnd cs)
     | w < 1 = cs
     | otherwise = take w cs
diff --git a/test/TestDocument.hs b/test/TestDocument.hs
--- a/test/TestDocument.hs
+++ b/test/TestDocument.hs
@@ -77,6 +77,63 @@
            editorPageUpAction,
            editorAppendAction "XYZ"
          ]),
+    ("fill view ignores already full", checkEditView
+       "original-long.txt"
+       "fill-noop-view.txt" $
+       composeActions [
+           editorPageDownAction,
+           repeatAction 4 editorDownAction,
+           viewerFillAction
+         ]),
+    ("fill view with short text", checkEditView
+       "original-short.txt"
+       "fill-short-view.txt" $
+       composeActions [
+           editorPageDownAction,
+           -- Manually shift the view up by one line.
+           editorUpAction,
+           editorDownAction,
+           viewerFillAction
+         ]),
+    ("fill view starting past end", checkEditView
+       "original-long.txt"
+       "fill-end-view.txt" $
+       composeActions [
+           repeatAction 5 editorPageDownAction,
+           repeatAction 2 editorUpAction,
+           viewerFillAction
+         ]),
+    ("shift view up within bounds", checkEditView
+       "original-long.txt"
+       "shift-up-bounded-view.txt" $
+       composeActions [
+           editorPageDownAction,
+           repeatAction 4 editorDownAction,
+           viewerShiftUpAction 4
+         ]),
+    ("shift view down within bounds", checkEditView
+       "original-long.txt"
+       "shift-down-bounded-view.txt" $
+       composeActions [
+           editorPageDownAction,
+           repeatAction 4 editorDownAction,
+           viewerShiftDownAction 4
+         ]),
+    ("shift view up past doc front", checkEditView
+       "original-long.txt"
+       "shift-up-unbounded-view.txt" $
+       composeActions [
+           repeatAction 4 editorDownAction,
+           viewerShiftUpAction 4
+         ]),
+    ("shift view down past doc back", checkEditView
+       "original-long.txt"
+       "shift-down-unbounded-view.txt" $
+       composeActions [
+           repeatAction 5 editorPageDownAction,
+           repeatAction 4 editorUpAction,
+           viewerShiftDownAction 7
+         ]),
     ("move previous at doc front", checkEditView
        "original-long.txt"
        "insert-front-view.txt" $
diff --git a/test/TestLine.hs b/test/TestLine.hs
--- a/test/TestLine.hs
+++ b/test/TestLine.hs
@@ -139,27 +139,6 @@
            moveLineCursor MoveEnd,
            modifyLine DeleteText EditAfter
          ]),
-    ("split line middle", do
-       let line = editLine $ innerLine "this is a test line"
-       let (left,right) = splitLine lineBreakEnd $ setLineCursor 5 line
-       checkConditions [
-           ("Left: " ++ show left,left == (endLine "this ")),
-           ("Right: " ++ show right,right == (innerLine "is a test line"))
-         ]),
-    ("split line front", do
-       let line = editLine $ innerLine "this is a test line"
-       let (left,right) = splitLine lineBreakEnd $ moveLineCursor MoveUp line
-       checkConditions [
-           ("Left: " ++ show left,left == (endLine "")),
-           ("Right: " ++ show right,right == (innerLine "this is a test line"))
-         ]),
-    ("split line back", do
-       let line = editLine $ innerLine "this is a test line"
-       let (left,right) = splitLine lineBreakEnd $ moveLineCursor MoveDown line
-       checkConditions [
-           ("Left: " ++ show left,left == (endLine "this is a test line")),
-           ("Right: " ++ show right,right == (innerLine ""))
-         ]),
     ("prepend preserves cursor", checkLineEdit
        "this is a test line"
        "XYthis Zis a test line" $
diff --git a/test/testfiles/fill-end-view.txt b/test/testfiles/fill-end-view.txt
new file mode 100644
--- /dev/null
+++ b/test/testfiles/fill-end-view.txt
@@ -0,0 +1,10 @@
+d.
+
+I can't seem to stop
+ writing paragraphs,
+ even though this is
+ just a test file.
+Isn't it annoying ho
+w each paragraph onl
+y has a single sente
+nce in it?
diff --git a/test/testfiles/fill-noop-view.txt b/test/testfiles/fill-noop-view.txt
new file mode 100644
--- /dev/null
+++ b/test/testfiles/fill-noop-view.txt
@@ -0,0 +1,10 @@
+is all very contrive
+d.
+It would certainly s
+uffice to just fill
+it with random chara
+cters, but this will
+ be easier to read i
+n test output.
+
+Since the view buffe
diff --git a/test/testfiles/fill-short-view.txt b/test/testfiles/fill-short-view.txt
new file mode 100644
--- /dev/null
+++ b/test/testfiles/fill-short-view.txt
@@ -0,0 +1,6 @@
+This is a shorter te
+st doc.
+It has 3 paragraphs,
+ just in case.
+Definitely a struggl
+e.
diff --git a/test/testfiles/shift-down-bounded-view.txt b/test/testfiles/shift-down-bounded-view.txt
new file mode 100644
--- /dev/null
+++ b/test/testfiles/shift-down-bounded-view.txt
@@ -0,0 +1,10 @@
+it with random chara
+cters, but this will
+ be easier to read i
+n test output.
+
+Since the view buffe
+r has five parts to
+it, we need at least
+ five paragraphs.
+But, that five numbe
diff --git a/test/testfiles/shift-down-unbounded-view.txt b/test/testfiles/shift-down-unbounded-view.txt
new file mode 100644
--- /dev/null
+++ b/test/testfiles/shift-down-unbounded-view.txt
@@ -0,0 +1,5 @@
+ just a test file.
+Isn't it annoying ho
+w each paragraph onl
+y has a single sente
+nce in it?
diff --git a/test/testfiles/shift-up-bounded-view.txt b/test/testfiles/shift-up-bounded-view.txt
new file mode 100644
--- /dev/null
+++ b/test/testfiles/shift-up-bounded-view.txt
@@ -0,0 +1,10 @@
+ some are out of vie
+w, and thus remain u
+nparsed.
+Honestly, this file
+is all very contrive
+d.
+It would certainly s
+uffice to just fill
+it with random chara
+cters, but this will
diff --git a/test/testfiles/shift-up-unbounded-view.txt b/test/testfiles/shift-up-unbounded-view.txt
new file mode 100644
--- /dev/null
+++ b/test/testfiles/shift-up-unbounded-view.txt
@@ -0,0 +1,10 @@
+I need a lot more te
+xt here in order to
+test out viewing.
+
+I also need some mor
+e paragraphs so that
+ some are out of vie
+w, and thus remain u
+nparsed.
+Honestly, this file
