Skip to content

Commit

Permalink
Match fenced code blocks with language and info
Browse files Browse the repository at this point in the history
Closes #184
  • Loading branch information
jrblevin committed May 12, 2017
1 parent 0b6df0f commit e4c331e
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 18 deletions.
47 changes: 37 additions & 10 deletions markdown-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -1234,10 +1234,14 @@ Groups 1 and 3 match the opening and closing tags.
Group 2 matches the key sequence.")

(defconst markdown-regex-gfm-code-block-open
"^[[:blank:]]*\\(```\\)[ ]?\\([^[:space:]]+\\|{[^}]*}\\)?\\([[:space:]]*?\\)$"
"^[[:blank:]]*\\(```\\)[[:blank:]]*\\({\\)?[[:blank:]]*\\([^[:space:]]+?\\)?\\(?:[[:blank:]]+\\(.+?\\)\\)?[[:blank:]]*\\(}\\)?[[:blank:]]*$"
"Regular expression matching opening of GFM code blocks.
Group 1 matches the opening three backticks.
Group 2 matches the language identifier (optional).")
Group 2 matches the opening brace (optional).
Group 3 matches the language identifier (optional).
Group 4 matches the info string (optional).
Group 5 matches the closing brace (optional).
Groups need to agree with `markdown-regex-tilde-fence-begin'.")

(defconst markdown-regex-gfm-code-block-close
"^[[:blank:]]*\\(```\\)\\s *?$"
Expand Down Expand Up @@ -1361,15 +1365,22 @@ Groups 1 and 3 match the opening and closing delimiters.
Group 2 matches the mathematical expression contained within.")

(defsubst markdown-make-tilde-fence-regex (num-tildes &optional end-of-line)
"Return regexp matching a Pandoc code fence at least NUM-TILDES long.
"Return regexp matching a tilde code fence at least NUM-TILDES long.
END-OF-LINE is the regexp construct to indicate end of line; $ if
missing."
(format "%s%d%s%s" "^\\([~]\\{" num-tildes ",\\}\\)" (or end-of-line "$")))
(format "%s%d%s%s" "^[[:blank:]]*\\([~]\\{" num-tildes ",\\}\\)"
(or end-of-line "$")))

(defconst markdown-regex-tilde-fence-begin
(markdown-make-tilde-fence-regex
3 "[ ]?\\([^[:space:]]+\\|{[^}]*}\\)?\\([[:space:]]*?\\)$")
"Regular expression for matching Pandoc tildes.")
3 "[[:blank:]]*\\({\\)?[[:blank:]]*\\([^[:space:]]+?\\)?\\(?:[[:blank:]]+\\(.+?\\)\\)?[[:blank:]]*\\(}\\)?[[:blank:]]*$")
"Regular expression for matching tilde-fenced code blocks.
Group 1 matches the opening tildes.
Group 2 matches the opening brace (optional).
Group 3 matches the language identifier (optional).
Group 4 matches the info string (optional).
Group 5 matches the closing brace (optional).
Groups need to agree with `markdown-regex-gfm-code-block-open'.")

(defconst markdown-regex-declarative-metadata
"^\\([[:alpha:]][[:alpha:] _-]*?\\)\\([:=][ \t]*\\)\\(.*\\)$"
Expand Down Expand Up @@ -1971,6 +1982,9 @@ START and END delimit region to propertize."
(defvar markdown-language-keyword-face 'markdown-language-keyword-face
"Face name to use for programming language identifiers.")

(defvar markdown-language-info-face 'markdown-language-info-face
"Face name to use for programming info strings.")

(defvar markdown-link-face 'markdown-link-face
"Face name to use for links.")

Expand Down Expand Up @@ -2073,6 +2087,11 @@ START and END delimit region to propertize."
"Face for programming language identifiers."
:group 'markdown-faces)

(defface markdown-language-info-face
'((t (:inherit font-lock-string-face)))
"Face for programming language info strings."
:group 'markdown-faces)

(defface markdown-link-face
'((t (:inherit font-lock-keyword-face)))
"Face for links."
Expand Down Expand Up @@ -2215,11 +2234,17 @@ See `font-lock-syntactic-face-function' for details."
(2 markdown-markup-face)
(3 markdown-metadata-value-face)))
(markdown-match-gfm-open-code-blocks . ((1 markdown-markup-face)
(2 markdown-language-keyword-face nil t)))
(2 markdown-markup-face nil t)
(3 markdown-language-keyword-face nil t)
(4 markdown-language-info-face nil t)
(5 markdown-markup-face nil t)))
(markdown-match-gfm-close-code-blocks . ((1 markdown-markup-face)))
(markdown-match-gfm-code-blocks . ((0 markdown-pre-face)))
(markdown-match-fenced-start-code-block . ((1 markdown-markup-face)
(2 markdown-language-keyword-face nil t)))
(2 markdown-markup-face nil t)
(3 markdown-language-keyword-face nil t)
(4 markdown-language-info-face nil t)
(5 markdown-markup-face nil t)))
(markdown-match-fenced-end-code-block . ((0 markdown-markup-face)))
(markdown-match-fenced-code-blocks . ((0 markdown-pre-face)))
(markdown-match-pre-blocks . ((0 markdown-pre-face)))
Expand Down Expand Up @@ -3938,9 +3963,11 @@ automatically in order to have the correct markup."
(goto-char (car pos-prop))
(save-match-data
(set-match-data (get-text-property (point) prop))
(when (and (match-beginning 2) (match-end 2))
;; Note: Hard-coded group number assumes tilde
;; and GFM fenced code regexp groups agree.
(when (and (match-beginning 3) (match-end 3))
(buffer-substring-no-properties
(match-beginning 2) (match-end 2)))))
(match-beginning 3) (match-end 3)))))
do (progn (when lang (markdown-gfm-add-used-language lang))
(goto-char (next-single-property-change (point) prop)))))))

Expand Down
32 changes: 24 additions & 8 deletions tests/markdown-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -2259,7 +2259,10 @@ puts markdown.to_html
"Test GFM-style fenced code blocks (2)."
(markdown-test-string "```{r sum}\n2+2\n```"
(markdown-test-range-has-face 1 3 markdown-markup-face) ; ```
(markdown-test-range-has-face 4 10 markdown-language-keyword-face) ; {r sum}
(markdown-test-range-has-face 4 4 markdown-markup-face) ; {
(markdown-test-range-has-face 5 5 markdown-language-keyword-face) ; r
(markdown-test-range-has-face 7 9 markdown-language-info-face) ; sum
(markdown-test-range-has-face 10 10 markdown-markup-face) ; }
(markdown-test-range-has-face 12 14 markdown-pre-face) ; 2+2
(markdown-test-range-has-face 16 18 markdown-markup-face))) ; ```

Expand All @@ -2277,6 +2280,15 @@ for (var i = 0; i < 10; i++) {
(markdown-test-range-has-face 17 68 markdown-pre-face)
(markdown-test-range-has-face 70 72 markdown-markup-face)))

(ert-deftest test-markdown-font-lock/gfm-fenced-4 ()
"Test GFM-style fenced code blocks (2)."
(markdown-test-string "```scalaFiddle libraries=\"Java8 Time-0.1.0\"\nimport java.time._\n\nval hour = LocalTime.now().getHour()\n\nprintln(hour)\n```"
(markdown-test-range-has-face 1 3 markdown-markup-face) ; ```
(markdown-test-range-has-face 4 14 markdown-language-keyword-face) ; scalaFiddle
(markdown-test-range-has-face 16 43 markdown-language-info-face) ; libraries="Java8 Time-0.1.0"
(markdown-test-range-has-face 45 115 markdown-pre-face) ; [code]
(markdown-test-range-has-face 117 119 markdown-markup-face))) ; ```

(ert-deftest test-markdown-font-lock/atx-no-spaces ()
"Test font-lock for atx headers with no spaces."
(markdown-test-string "##abc##"
Expand Down Expand Up @@ -2636,7 +2648,7 @@ echo \"Hello, world v2!\"
(marker-position end-top-1) (marker-position start-top-1)))
;; check top language specifier
(should (markdown-test-check-match-limits
'markdown-tilde-fence-begin 2 (marker-position start-lang-1)
'markdown-tilde-fence-begin 3 (marker-position start-lang-1)
(marker-position end-lang-1) (marker-position start-lang-1)))
;; check text in between
(should (markdown-test-check-match-limits
Expand All @@ -2663,7 +2675,7 @@ echo \"Hello, world v2!\"
'markdown-tilde-fence-begin 1 (marker-position start-top-2)
(marker-position end-top-2) (marker-position start-top-2)))
(should (markdown-test-check-match-limits
'markdown-tilde-fence-begin 2 (marker-position start-lang-2)
'markdown-tilde-fence-begin 3 (marker-position start-lang-2)
(marker-position end-lang-2) (marker-position start-lang-2)))
(should (markdown-test-check-match-limits
'markdown-fenced-code 0 (marker-position start-mid-2)
Expand All @@ -2682,7 +2694,7 @@ echo \"Hello, world v2!\"
'markdown-tilde-fence-begin 1 (marker-position start-top-1)
(marker-position end-top-1) (marker-position start-top-1)))
(should (markdown-test-check-match-limits
'markdown-tilde-fence-begin 2 (marker-position start-lang-1)
'markdown-tilde-fence-begin 3 (marker-position start-lang-1)
(marker-position end-lang-1) (marker-position start-lang-1)))
(should (markdown-test-check-match-limits
'markdown-fenced-code 0 (marker-position start-mid-1)
Expand All @@ -2698,7 +2710,7 @@ echo \"Hello, world v2!\"
'markdown-tilde-fence-begin 1 (marker-position start-top-2)
(marker-position end-top-2) (marker-position start-top-2)))
(should (markdown-test-check-match-limits
'markdown-tilde-fence-begin 2 (marker-position start-lang-2)
'markdown-tilde-fence-begin 3 (marker-position start-lang-2)
(marker-position end-lang-2) (marker-position start-lang-2)))
(should (markdown-test-check-match-limits
'markdown-fenced-code 0 (marker-position start-mid-2)
Expand Down Expand Up @@ -2728,7 +2740,7 @@ echo \"Hello, world!\"
'markdown-tilde-fence-begin 1 1 4 1))
;; check language
(should (markdown-test-check-match-limits
'markdown-tilde-fence-begin 2 5 10 5))
'markdown-tilde-fence-begin 3 5 10 5))
;; middle should not be propertized
(should-not (get-text-property 11 'markdown-fenced-code))
;; neither should end
Expand All @@ -2739,7 +2751,7 @@ echo \"Hello, world!\"
(should (markdown-test-check-match-limits
'markdown-tilde-fence-begin 1 1 4 1))
(should (markdown-test-check-match-limits
'markdown-tilde-fence-begin 2 5 10 5))
'markdown-tilde-fence-begin 3 5 10 5))
;; check middle
(should (markdown-test-check-match-limits 'markdown-fenced-code 0 10 43 10))
;; check ending tildes
Expand Down Expand Up @@ -3853,10 +3865,14 @@ this is not header line
(markdown-test-range-has-face 119 152 markdown-header-face-1)
(markdown-test-range-has-face 129 129 markdown-markup-face)
(markdown-test-range-has-face 136 136 markdown-markup-face)

(markdown-test-range-has-face 174 177 markdown-markup-face)
(markdown-test-range-has-face 179 188 markdown-language-keyword-face)
(markdown-test-range-has-face 179 179 markdown-markup-face)
(markdown-test-range-has-face 180 187 markdown-language-keyword-face)
(markdown-test-range-has-face 188 188 markdown-markup-face)
(markdown-test-range-has-face 190 211 markdown-pre-face)
(markdown-test-range-has-face 212 215 markdown-markup-face)

(markdown-test-range-has-face 218 218 markdown-markup-face)
(markdown-test-range-has-face 219 223 markdown-math-face)
(markdown-test-range-has-face 224 224 markdown-markup-face)
Expand Down

0 comments on commit e4c331e

Please sign in to comment.