From 1967db22de958c6eb653b0968d99527ac0a4f75d Mon Sep 17 00:00:00 2001
From: Odd A
Date: Thu, 10 Mar 2022 19:18:35 +0100
Subject: [PATCH] Fix issue with unparagraphed text following code indent
(#178)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Fix issue with unparagraphed text following code indent
Text that immediately follows a code block sometimes does not
get transformed, but instead is stuck as plain text behind the
ending code- and pre-tags
* cleanups
Co-authored-by: Odd Andreas Sørsæther
---
.gitignore | 2 +
src/clj/markdown/core.clj | 13 +++---
src/cljc/markdown/common.cljc | 2 +-
src/cljc/markdown/transformers.cljc | 65 +++++++++++++++++------------
test/markdown/md_test.cljc | 14 ++++++-
5 files changed, 59 insertions(+), 37 deletions(-)
diff --git a/.gitignore b/.gitignore
index 8e31878..b1525d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,5 @@ target
.cpcache
cljs-test-runner-out
node_modules
+.clj-kondo/
+.calva/
\ No newline at end of file
diff --git a/src/clj/markdown/core.clj b/src/clj/markdown/core.clj
index 4e00626..02c1ecf 100644
--- a/src/clj/markdown/core.clj
+++ b/src/clj/markdown/core.clj
@@ -1,17 +1,16 @@
(ns markdown.core
(:require [clojure.java.io :as io]
- [clojure.string :as string]
[markdown.common
- :refer [*substring* *inhibit-separator*]]
+ :refer [*inhibit-separator*]]
[markdown.links
- :refer [parse-reference parse-reference-link parse-footnote-link]]
+ :refer [parse-reference-link parse-footnote-link]]
[markdown.transformers
:refer [transformer-vector footer parse-metadata-headers]])
(:import [java.io BufferedReader
- BufferedWriter
- StringReader
- StringWriter
- Writer]))
+ BufferedWriter
+ StringReader
+ StringWriter
+ Writer]))
(defn- write [^Writer writer ^String text]
(doseq [c text] (.write writer (int c))))
diff --git a/src/cljc/markdown/common.cljc b/src/cljc/markdown/common.cljc
index cc3d7f2..8c2f859 100644
--- a/src/cljc/markdown/common.cljc
+++ b/src/cljc/markdown/common.cljc
@@ -176,7 +176,7 @@
(defn heading-level [text]
(let [num-hashes (count (filter #(not= \space %) (take-while #(or (= \# %) (= \space %)) (seq text))))]
- (if (pos? num-hashes) num-hashes)))
+ (when (pos? num-hashes) num-hashes)))
(defn make-heading [text heading-anchors]
(when-let [heading (heading-level text)]
diff --git a/src/cljc/markdown/transformers.cljc b/src/cljc/markdown/transformers.cljc
index afecb85..d6a6fbb 100644
--- a/src/cljc/markdown/transformers.cljc
+++ b/src/cljc/markdown/transformers.cljc
@@ -15,7 +15,6 @@
[escape-code
escaped-chars
freeze-string
- separator
thaw-strings
strong
bold
@@ -34,7 +33,7 @@
(defn heading? [text type]
(when-not (every? #{\space} (take 4 text))
- (let [trimmed (if text (string/trim text))]
+ (let [trimmed (some-> text string/trim)]
(and (not-empty trimmed) (every? #{type} trimmed)))))
(defn h1? [text]
@@ -154,39 +153,51 @@
[text state]))
(defn close-paragraph [text {:keys [next-line paragraph] :as state}]
- (if (and paragraph (= [\` \` \`] (take-last 3 (some-> next-line string/trim))))
+ (if (and paragraph (some-> next-line string/trim (string/ends-with? "```")))
[(str text "
") (dissoc state :paragraph)]
[text state]))
(defn paragraph [text state]
(apply close-paragraph (open-paragraph text state)))
-(defn code [text {:keys [eof lists code codeblock paragraph] :as state}]
- (cond
- (or lists codeblock)
- [text state]
+(defn code [text {:keys [eof indent-code-end next-line lists code codeblock paragraph] :as state}]
+ (let [should-close? (or eof
+ (not (or (string/blank? next-line)
+ (string/starts-with? next-line " "))))]
+ (cond
+ (or lists codeblock)
+ [text state]
- code
- (if (or eof (not= " " (string/join (take 4 text))))
- [(str "" text) (dissoc state :indented-code :code :last-line-empty?)]
- [(str "\n" (escape-code (string/replace-first text #" " ""))) state])
+ indent-code-end
+ [text (-> state
+ (dissoc :code :indent-code-end :indented-code)
+ (assoc :last-line-empty? true))]
- paragraph
- [text state]
+ code
+ [(str (escape-code (string/replace-first text #" " "\n"))
+ (when should-close? ""))
+ (cond-> state
+ should-close? (assoc :indent-code-end true))]
- (empty? (string/trim text))
- [text state]
+ paragraph
+ [text state]
- :default
- (let [num-spaces (count (take-while (partial = \space) text))]
- (if (> num-spaces 3)
- [(str "" (escape-code (string/replace-first text #" " "")))
- (assoc state :code true :indented-code true)]
- [text state]))))
+ (empty? (string/trim text))
+ [text state]
+
+ :default
+ (let [num-spaces (count (take-while (partial = \space) text))]
+ (if (>= num-spaces 4)
+ [(str ""
+ (escape-code (string/replace-first text #" " ""))
+ (when should-close? "
"))
+ (cond-> (assoc state :code true :indented-code true)
+ should-close? (assoc :indent-code-end true))]
+ [text state])))))
(defn codeblock [text {:keys [codeblock codeblock-end indented-code next-line lists] :as state}]
(let [trimmed (string/trim text)
- next-line-closes? (= [\` \` \`] (take-last 3 (some-> next-line string/trim)))]
+ next-line-closes? (some-> next-line string/trim (string/ends-with? "```"))]
(cond
(and lists codeblock-end)
["" (dissoc state :code :codeblock :codeblock-end)]
@@ -197,19 +208,19 @@
(dissoc :code :codeblock :codeblock-end))]
(and next-line-closes? codeblock)
- [(str (escape-code (str text "\n" (apply str (first (string/split next-line #"```"))))) "
")
+ [(str (escape-code (str text \newline (apply str (first (string/split next-line #"```"))))) "")
(assoc state :skip-next-line? (not lists)
:codeblock-end true
:last-line-empty? (not lists))]
(and
(not indented-code)
- (= [\` \` \`] (take 3 trimmed)))
+ (string/starts-with? trimmed "```"))
(let [[lang code] (split-with (partial not= \newline) (drop 3 trimmed))
lang (string/trim (string/join lang))
s (apply str (rest code))
formatter (:code-style state)]
- [(str "foo
bar
"
(entry-function " foo\n\nbar")))
- (is (= "foo
bar"
+ (is (= "foo
bar
"
(entry-function " foo\nbar")))
(is (= "baz foo
bar
"
(entry-function "baz\n foo\n\nbar")))
(is (= ""
- (entry-function ""))))
+ (entry-function "")))
+ (is (= "Random text. Random text.
"
+ (entry-function "Random text.\nRandom text.\n"))
+ "A single newline is interpreted as space in the same paragraph")
+ (is (= "Random text.
code block
Random text. Random text.
"
+ (entry-function "Random text.\n\n code block\n\nRandom text.\nRandom text.\n"))
+ "Two newlines after a code block is interpreted as starting a new paragraph")
+ (is (= "Random text.
code block\n \nwith spaces\n
Random text. Random text.
"
+ (entry-function
+ "Random text.\n\n code block\n \n with spaces\n \nRandom text.\nRandom text.\n"))
+ "Two newlines after a indented code block where one contains spaces is still interpreted as a new paragraph"))
(deftest strikethrough
(is (= "foo
"