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 "
   

Element #1

" - (entry-function "
\n
\n

Element #1

\n
\n
")))) + (entry-function "
\n
\n

Element #1

\n
\n
"))) + (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

"