diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,28 @@
 # Changelog for commonmark
 
+## 0.3.2
+
+  * Replace duplicate block specs in place (#180). When several
+    block specs have the same blockType,
+    `removeDuplicateBlockSpecs` still keeps the first, but now
+    puts it in the position of the last. So an extension that
+    overrides a default spec (e.g. `fancy_lists`' ListItem) takes
+    the default's slot, instead of moving ahead of other
+    extensions' specs and of the remaining default specs. This
+    makes task_lists work regardless of whether it is combined
+    before or after fancy_lists, and it fixes thematic breaks
+    like `* * *` being parsed as nested lists when fancy_lists is
+    enabled.
+
+  * delimsMatch: disallow match for 1-length delims when there is no
+    single-delim contsructor defined (e.g., with strikeout) (#181).
+
+  * Test: read test file as bytestring, then convert.
+
+  * Ensure that token stream always ends with a LineEnd (#179).
+    If the input does not end with a line ending, we still add
+    a LineEnd token at the end (with null text content).
+
 ## 0.3.1
 
   * Export `getPrecedingTokType` from Commonmark.Inlines [API change].
diff --git a/commonmark.cabal b/commonmark.cabal
--- a/commonmark.cabal
+++ b/commonmark.cabal
@@ -1,6 +1,6 @@
 cabal-version:  2.2
 name:           commonmark
-version:        0.3.1
+version:        0.3.2
 synopsis:       Pure Haskell commonmark parser.
 description:
    This library provides the core data types and functions
@@ -97,6 +97,7 @@
       base >= 4.9 && <5
     , commonmark
     , text
+    , bytestring
     , unicode-transforms
     , tasty
     , tasty-quickcheck
diff --git a/src/Commonmark/Inlines.hs b/src/Commonmark/Inlines.hs
--- a/src/Commonmark/Inlines.hs
+++ b/src/Commonmark/Inlines.hs
@@ -734,7 +734,19 @@
                 then delimLength close `mod` 3 == 0 ||
                      (delimLength open + delimLength close) `mod` 3 /= 0
                 else True) &&
+    hasConstructor &&
     opents /= closets
+ where
+  -- Don't match if the spec provides no constructor for delimiters
+  -- of these lengths (e.g. single ~ with a strikethrough spec that
+  -- only allows ~~), since otherwise we'd consume intervening
+  -- delimiters (#181).
+  hasConstructor =
+    case delimSpec close of
+      Just spec -> isJust (formattingSingleMatch spec) ||
+                   (isJust (formattingDoubleMatch spec) &&
+                    min (length opents) (length closets) >= 2)
+      Nothing -> True
 delimsMatch _ _ = False
 
 -- check for balanced `[]` brackets
diff --git a/src/Commonmark/Syntax.hs b/src/Commonmark/Syntax.hs
--- a/src/Commonmark/Syntax.hs
+++ b/src/Commonmark/Syntax.hs
@@ -39,10 +39,16 @@
   mempty = SyntaxSpec mempty mempty mempty mempty mempty mempty
   mappend = (<>)
 
+-- | When several specs have the same 'blockType', keep the first
+-- one, but put it in the position of the last one.  This way an
+-- extension's spec replaces a default spec in place, rather than
+-- moving ahead of other extensions' specs (#180).
 removeDuplicateBlockSpecs :: [BlockSpec m il bl] -> [BlockSpec m il bl]
 removeDuplicateBlockSpecs []     = []
 removeDuplicateBlockSpecs (b:bs) =
-  b : removeDuplicateBlockSpecs (filter ((/= blockType b) . blockType) bs)
+  case break ((== blockType b) . blockType) bs of
+    (_, [])      -> b : removeDuplicateBlockSpecs bs
+    (xs, _:ys)   -> removeDuplicateBlockSpecs (xs ++ b : ys)
 
 -- | Standard commonmark syntax.
 defaultSyntaxSpec :: (Monad m, IsBlock il bl, IsInline il)
diff --git a/src/Commonmark/Tokens.hs b/src/Commonmark/Tokens.hs
--- a/src/Commonmark/Tokens.hs
+++ b/src/Commonmark/Tokens.hs
@@ -49,7 +49,10 @@
 
     isWordChar c  = isAlphaNum c || isMark c
 
-    go !_pos [] = []
+    go !pos []
+      | sourceColumn pos > 1 = [Tok LineEnd pos ""]
+        -- ensure we always have a completed line
+      | otherwise = []
     go !pos (!t:ts) = -- note that t:ts are guaranteed to be nonempty
       case T.head t of
          ' ' ->  Tok Spaces pos t :
diff --git a/test/test-commonmark.hs b/test/test-commonmark.hs
--- a/test/test-commonmark.hs
+++ b/test/test-commonmark.hs
@@ -8,22 +8,19 @@
 import           Data.List             (groupBy)
 import           Data.Text             (Text)
 import qualified Data.Text             as T
-import qualified Data.Text.IO          as T
-import           System.IO             (hSetEncoding, utf8, openFile,
-                                        IOMode(..))
 import qualified Data.Text.Lazy        as TL
+import           Data.Text.Encoding         (decodeUtf8With)
+import           Data.Text.Encoding.Error   (lenientDecode)
 import           Data.Text.Normalize   (normalize, NormalizationMode(NFC))
 import           Test.Tasty
 import           Test.Tasty.HUnit
 import           Test.Tasty.QuickCheck
 import           Text.Parsec
 import           Text.Parsec.Pos
+import qualified Data.ByteString as BS
 
 readTextFile :: FilePath -> IO Text
-readTextFile fp = do
-  h <- openFile fp ReadMode
-  hSetEncoding h utf8
-  T.hGetContents h
+readTextFile fp = decodeUtf8With lenientDecode <$> BS.readFile fp
 
 main :: IO ()
 main = do
@@ -34,6 +31,8 @@
              ]
   defaultMain $ testGroup "Tests"
      (testProperty "tokenize/untokenize roundtrip" tokenize_roundtrip
+      : testCase "tokenize always ends with LineEnd"
+           (map tokType (tokenize "input" "hi") @?= [WordChars, LineEnd])
       : pathologicalTests defaultParser
       : toSpecTest defaultParser
         SpecTest
