diff --git a/docs/userGuide/syntax/code.mbdf b/docs/userGuide/syntax/code.mbdf index 8e0ddefa6a..7a561845e5 100644 --- a/docs/userGuide/syntax/code.mbdf +++ b/docs/userGuide/syntax/code.mbdf @@ -53,11 +53,27 @@ function add(a, b) { ##### Line highlighting -To highlight lines, add the attribute `highlight-lines` with the line numbers as value, as shown below. You can specify ranges or individual line numbers. +To highlight lines, add the attribute `highlight-lines` as shown below. + +You can specify highlighting in many different ways, depending on how you want it to be. There are two main variants: + +**Text-only highlighting** + +To highlight only the text portion of the line, you can just use the line numbers as is. + +For ranges of lines, join the two line numbers with a dash sign (`-`). + +**Whole-line highlighting** + +If you wish to highlight a full line (including whitespaces) or ranges of it, you can leverage MarkBind's own _line-slice_ syntax. Line-slices are in the form of `lineNumber[:]`, e.g. `2[:]`. + +This variant's format is very similar to the previous, but instead use line-slices rather than line numbers. + +For ranges, you only need to use line-slices on either ends. -```java {highlight-lines="2,4,6-8"} +```java {highlight-lines="1,3[:],6-8,10[:]-12"} import java.util.List; public class Inventory { @@ -66,6 +82,10 @@ public class Inventory { public int getItemCount(){ return items.size(); } + + public bool isEmpty() { + return items.isEmpty(); + } //... } diff --git a/packages/cli/test/functional/test_site/expected/markbind/css/codeblock-dark.min.css b/packages/cli/test/functional/test_site/expected/markbind/css/codeblock-dark.min.css index 25af447195..1a1f424be5 100644 --- a/packages/cli/test/functional/test_site/expected/markbind/css/codeblock-dark.min.css +++ b/packages/cli/test/functional/test_site/expected/markbind/css/codeblock-dark.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;background:#2b2b2b;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute,.hljs-builtin-name{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} +/* MarkBind-customized styles */.hljs span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} diff --git a/packages/cli/test/functional/test_site/expected/markbind/css/codeblock-light.min.css b/packages/cli/test/functional/test_site/expected/markbind/css/codeblock-light.min.css index 72a97c76ab..5e20b4e805 100644 --- a/packages/cli/test/functional/test_site/expected/markbind/css/codeblock-light.min.css +++ b/packages/cli/test/functional/test_site/expected/markbind/css/codeblock-light.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} +/* MarkBind-customized styles */.hljs span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} diff --git a/packages/cli/test/functional/test_site/expected/testCodeBlocks.html b/packages/cli/test/functional/test_site/expected/testCodeBlocks.html index bd07920f15..fd837dec02 100644 --- a/packages/cli/test/functional/test_site/expected/testCodeBlocks.html +++ b/packages/cli/test/functional/test_site/expected/testCodeBlocks.html @@ -49,28 +49,36 @@ -----

highlight-lines attr causes corresponding lines to have 'highlighted' class

-
1  highlighted
+        
1  highlighted
 2
-3  highlighted
+3  highlighted
 4
-5  highlighted
-6  highlighted
-7  highlighted
-8  highlighted
+5  highlighted
+6  highlighted
+7  highlighted
+8  highlighted
 9
 10
 

highlight-lines attr with start-from attr should cause corresponding lines to have 'highlighted' class based on start-from

-
11  highlighted
+        
11  highlighted
 12
-13  highlighted
+13  highlighted
 14
-15  highlighted
-16  highlighted
-17  highlighted
-18  highlighted
+15  highlighted
+16  highlighted
+17  highlighted
+18  highlighted
 19
 20
+
+

**highlight-lines attr with line-slice syntax of empty indices should highlight leading/trailing spaces

+
<foo>
+  <bar type="name">goo</bar>
+  <baz type="name">goo</baz>
+  <qux type="name">goo</qux>
+  <quux type="name">goo</quux>
+</foo>
 

Should render correctly with heading

diff --git a/packages/cli/test/functional/test_site/testCodeBlocks.md b/packages/cli/test/functional/test_site/testCodeBlocks.md index a5605b5b13..89ba1f2451 100644 --- a/packages/cli/test/functional/test_site/testCodeBlocks.md +++ b/packages/cli/test/functional/test_site/testCodeBlocks.md @@ -56,6 +56,16 @@ Content in a fenced code block 20 ``` +**`highlight-lines` attr with line-slice syntax of empty indices should highlight leading/trailing spaces +```xml {highlight-lines="2[:],4[:]-5[:]"} + + goo + goo + goo + goo + +``` + **Should render correctly with heading** ```{heading="A heading"} diff --git a/packages/cli/test/functional/test_site_algolia_plugin/expected/markbind/css/codeblock-dark.min.css b/packages/cli/test/functional/test_site_algolia_plugin/expected/markbind/css/codeblock-dark.min.css index 25af447195..1a1f424be5 100644 --- a/packages/cli/test/functional/test_site_algolia_plugin/expected/markbind/css/codeblock-dark.min.css +++ b/packages/cli/test/functional/test_site_algolia_plugin/expected/markbind/css/codeblock-dark.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;background:#2b2b2b;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute,.hljs-builtin-name{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} +/* MarkBind-customized styles */.hljs span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} diff --git a/packages/cli/test/functional/test_site_algolia_plugin/expected/markbind/css/codeblock-light.min.css b/packages/cli/test/functional/test_site_algolia_plugin/expected/markbind/css/codeblock-light.min.css index 72a97c76ab..5e20b4e805 100644 --- a/packages/cli/test/functional/test_site_algolia_plugin/expected/markbind/css/codeblock-light.min.css +++ b/packages/cli/test/functional/test_site_algolia_plugin/expected/markbind/css/codeblock-light.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} +/* MarkBind-customized styles */.hljs span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} diff --git a/packages/cli/test/functional/test_site_convert/expected/markbind/css/codeblock-dark.min.css b/packages/cli/test/functional/test_site_convert/expected/markbind/css/codeblock-dark.min.css index 25af447195..1a1f424be5 100644 --- a/packages/cli/test/functional/test_site_convert/expected/markbind/css/codeblock-dark.min.css +++ b/packages/cli/test/functional/test_site_convert/expected/markbind/css/codeblock-dark.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;background:#2b2b2b;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute,.hljs-builtin-name{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} +/* MarkBind-customized styles */.hljs span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} diff --git a/packages/cli/test/functional/test_site_convert/expected/markbind/css/codeblock-light.min.css b/packages/cli/test/functional/test_site_convert/expected/markbind/css/codeblock-light.min.css index 72a97c76ab..5e20b4e805 100644 --- a/packages/cli/test/functional/test_site_convert/expected/markbind/css/codeblock-light.min.css +++ b/packages/cli/test/functional/test_site_convert/expected/markbind/css/codeblock-light.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} +/* MarkBind-customized styles */.hljs span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} diff --git a/packages/cli/test/functional/test_site_expressive_layout/expected/markbind/css/codeblock-dark.min.css b/packages/cli/test/functional/test_site_expressive_layout/expected/markbind/css/codeblock-dark.min.css index 25af447195..1a1f424be5 100644 --- a/packages/cli/test/functional/test_site_expressive_layout/expected/markbind/css/codeblock-dark.min.css +++ b/packages/cli/test/functional/test_site_expressive_layout/expected/markbind/css/codeblock-dark.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;background:#2b2b2b;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute,.hljs-builtin-name{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} +/* MarkBind-customized styles */.hljs span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} diff --git a/packages/cli/test/functional/test_site_expressive_layout/expected/markbind/css/codeblock-light.min.css b/packages/cli/test/functional/test_site_expressive_layout/expected/markbind/css/codeblock-light.min.css index 72a97c76ab..5e20b4e805 100644 --- a/packages/cli/test/functional/test_site_expressive_layout/expected/markbind/css/codeblock-light.min.css +++ b/packages/cli/test/functional/test_site_expressive_layout/expected/markbind/css/codeblock-light.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} +/* MarkBind-customized styles */.hljs span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} diff --git a/packages/cli/test/functional/test_site_special_tags/expected/markbind/css/codeblock-dark.min.css b/packages/cli/test/functional/test_site_special_tags/expected/markbind/css/codeblock-dark.min.css index 25af447195..1a1f424be5 100644 --- a/packages/cli/test/functional/test_site_special_tags/expected/markbind/css/codeblock-dark.min.css +++ b/packages/cli/test/functional/test_site_special_tags/expected/markbind/css/codeblock-dark.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;background:#2b2b2b;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute,.hljs-builtin-name{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} +/* MarkBind-customized styles */.hljs span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} diff --git a/packages/cli/test/functional/test_site_special_tags/expected/markbind/css/codeblock-light.min.css b/packages/cli/test/functional/test_site_special_tags/expected/markbind/css/codeblock-light.min.css index 72a97c76ab..5e20b4e805 100644 --- a/packages/cli/test/functional/test_site_special_tags/expected/markbind/css/codeblock-light.min.css +++ b/packages/cli/test/functional/test_site_special_tags/expected/markbind/css/codeblock-light.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} +/* MarkBind-customized styles */.hljs span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} diff --git a/packages/cli/test/functional/test_site_templates/test_default/expected/markbind/css/codeblock-dark.min.css b/packages/cli/test/functional/test_site_templates/test_default/expected/markbind/css/codeblock-dark.min.css index 25af447195..1a1f424be5 100644 --- a/packages/cli/test/functional/test_site_templates/test_default/expected/markbind/css/codeblock-dark.min.css +++ b/packages/cli/test/functional/test_site_templates/test_default/expected/markbind/css/codeblock-dark.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;background:#2b2b2b;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute,.hljs-builtin-name{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} +/* MarkBind-customized styles */.hljs span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} diff --git a/packages/cli/test/functional/test_site_templates/test_default/expected/markbind/css/codeblock-light.min.css b/packages/cli/test/functional/test_site_templates/test_default/expected/markbind/css/codeblock-light.min.css index 72a97c76ab..5e20b4e805 100644 --- a/packages/cli/test/functional/test_site_templates/test_default/expected/markbind/css/codeblock-light.min.css +++ b/packages/cli/test/functional/test_site_templates/test_default/expected/markbind/css/codeblock-light.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} +/* MarkBind-customized styles */.hljs span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} diff --git a/packages/cli/test/functional/test_site_templates/test_minimal/expected/markbind/css/codeblock-dark.min.css b/packages/cli/test/functional/test_site_templates/test_minimal/expected/markbind/css/codeblock-dark.min.css index 25af447195..1a1f424be5 100644 --- a/packages/cli/test/functional/test_site_templates/test_minimal/expected/markbind/css/codeblock-dark.min.css +++ b/packages/cli/test/functional/test_site_templates/test_minimal/expected/markbind/css/codeblock-dark.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;background:#2b2b2b;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute,.hljs-builtin-name{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} +/* MarkBind-customized styles */.hljs span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} diff --git a/packages/cli/test/functional/test_site_templates/test_minimal/expected/markbind/css/codeblock-light.min.css b/packages/cli/test/functional/test_site_templates/test_minimal/expected/markbind/css/codeblock-light.min.css index 72a97c76ab..5e20b4e805 100644 --- a/packages/cli/test/functional/test_site_templates/test_minimal/expected/markbind/css/codeblock-light.min.css +++ b/packages/cli/test/functional/test_site_templates/test_minimal/expected/markbind/css/codeblock-light.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} +/* MarkBind-customized styles */.hljs span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} diff --git a/packages/core-web/asset/css/codeblock-dark.min.css b/packages/core-web/asset/css/codeblock-dark.min.css index 25af447195..1a1f424be5 100644 --- a/packages/core-web/asset/css/codeblock-dark.min.css +++ b/packages/core-web/asset/css/codeblock-dark.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;background:#2b2b2b;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute,.hljs-builtin-name{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} +/* MarkBind-customized styles */.hljs span.highlighted{background:#474949}.hljs.inline{background:#444}.hljs:not(.inline){border:1px solid #1e1e1e}.code-block-heading{background:#3f3f3f;color:#dcdcdc} diff --git a/packages/core-web/asset/css/codeblock-light.min.css b/packages/core-web/asset/css/codeblock-light.min.css index 72a97c76ab..5e20b4e805 100644 --- a/packages/core-web/asset/css/codeblock-light.min.css +++ b/packages/core-web/asset/css/codeblock-light.min.css @@ -1,2 +1,2 @@ .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -/* MarkBind-customized styles */.hljs>span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} +/* MarkBind-customized styles */.hljs span.highlighted{background:#e6e6fa}.hljs.inline{background:#f8f8f8}.hljs.inline.no-lang{color:#e83e8c}.hljs:not(.inline){border:1px solid #c8c8c8;border:1px solid rgba(200,200,200,.3)}.code-block-heading{background:#f2f2ff;color:#8787a5} diff --git a/packages/core-web/src/styles/markbind.css b/packages/core-web/src/styles/markbind.css index fde7418016..8cb7bad42c 100644 --- a/packages/core-web/src/styles/markbind.css +++ b/packages/core-web/src/styles/markbind.css @@ -61,7 +61,7 @@ pre > code.hljs[heading] { color: #333; } - code > span.highlighted { + code span.highlighted { background: lavender; } } diff --git a/packages/core/src/lib/markdown-it/highlight/HighlightRule.js b/packages/core/src/lib/markdown-it/highlight/HighlightRule.js new file mode 100644 index 0000000000..2602b12353 --- /dev/null +++ b/packages/core/src/lib/markdown-it/highlight/HighlightRule.js @@ -0,0 +1,73 @@ +const { HighlightRuleComponent } = require('./HighlightRuleComponent.js'); + +class HighlightRule { + constructor(ruleComponents) { + /** + * @type Array + */ + this.ruleComponents = ruleComponents; + } + + static parseRule(ruleString) { + const components = ruleString.split('-').map(HighlightRuleComponent.parseRuleComponent); + return new HighlightRule(components); + } + + offsetLines(offset) { + this.ruleComponents.forEach(comp => comp.offsetLineNumber(offset)); + } + + shouldApplyHighlight(lineNumber) { + const compares = this.ruleComponents.map(comp => comp.compareLine(lineNumber)); + if (this.isLineRange()) { + const withinRangeStart = compares[0] <= 0; + const withinRangeEnd = compares[1] >= 0; + return withinRangeStart && withinRangeEnd; + } + + const atLineNumber = compares[0] === 0; + return atLineNumber; + } + + applyHighlight(line) { + const isLineSlice = this.ruleComponents.length === 1 && this.ruleComponents[0].isSlice; + + if (this.isLineRange()) { + const shouldWholeLine = this.ruleComponents.some(comp => comp.isUnboundedSlice()); + return shouldWholeLine + ? HighlightRule._highlightWholeLine(line) + : HighlightRule._highlightTextOnly(line); + } + + if (isLineSlice) { + // TODO: Implement slice-index based highlighting + return HighlightRule._highlightWholeLine(line); + } + + return HighlightRule._highlightTextOnly(line); + } + + static _highlightWholeLine(codeStr) { + return `${codeStr}\n`; + } + + static _splitCodeAndIndentation(codeStr) { + const codeStartIdx = codeStr.search(/\S|$/); + const indents = codeStr.substr(0, codeStartIdx); + const content = codeStr.substr(codeStartIdx); + return [indents, content]; + } + + static _highlightTextOnly(codeStr) { + const [indents, content] = HighlightRule._splitCodeAndIndentation(codeStr); + return `${indents}${content}\n` + } + + isLineRange() { + return this.ruleComponents.length === 2; + } +} + +module.exports = { + HighlightRule +}; diff --git a/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.js b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.js new file mode 100644 index 0000000000..63e0073028 --- /dev/null +++ b/packages/core/src/lib/markdown-it/highlight/HighlightRuleComponent.js @@ -0,0 +1,53 @@ +const LINESLICE_REGEX = new RegExp('(\\d+)\\[(\\d*):(\\d*)]'); + +class HighlightRuleComponent { + constructor(lineNumber, isSlice, bounds) { + this.lineNumber = lineNumber; + this.isSlice = isSlice || false; + this.bounds = bounds || []; + } + + static parseRuleComponent(compString) { + // tries to match with the line slice pattern + const matches = compString.match(LINESLICE_REGEX); + if (matches) { + const groups = matches.slice(1); // keep the capturing group matches only + const lineNumber = parseInt(groups.shift(), 10); + + const isUnbounded = groups.every(x => x === ''); + if (isUnbounded) { + return new HighlightRuleComponent(lineNumber, true); + } + + const bounds = groups.map(x => x !== '' ? parseInt(x, 10) : -1); + return new HighlightRuleComponent(lineNumber, true, bounds); + } + + // match fails, so it is just line numbers + const lineNumber = parseInt(compString, 10); + return new HighlightRuleComponent(lineNumber); + } + + offsetLineNumber(offset) { + this.lineNumber += offset; + } + + /** + * Compares the component's line number to a given line number. + * + * @param lineNumber The line number to compare + * @returns {number} A negative number, zero, or a positive number when the given line number + * is after, at, or before the component's line number + */ + compareLine(lineNumber) { + return this.lineNumber - lineNumber; + } + + isUnboundedSlice() { + return this.isSlice && this.bounds.length === 0; + } +} + +module.exports = { + HighlightRuleComponent +}; diff --git a/packages/core/src/lib/markdown-it/index.js b/packages/core/src/lib/markdown-it/index.js index e6718e879e..74da059fa6 100644 --- a/packages/core/src/lib/markdown-it/index.js +++ b/packages/core/src/lib/markdown-it/index.js @@ -5,6 +5,8 @@ const markdownIt = require('markdown-it')({ }); const slugify = require('@sindresorhus/slugify'); +const { HighlightRule } = require('./highlight/HighlightRule.js'); + // markdown-it plugins markdownIt.use(require('markdown-it-mark')) .use(require('markdown-it-ins')) @@ -87,43 +89,27 @@ markdownIt.renderer.rules.fence = (tokens, idx, options, env, slf) => { // counter is incremented on each span, so we need to subtract 1 token.attrJoin('style', `counter-reset: line ${startFromZeroBased};`); } - + const highlightLinesInput = getAttributeAndDelete(token, 'highlight-lines'); - let lineNumbersAndRanges = []; + let highlightRules = []; if (highlightLinesInput) { - // example input format: "1,4-7,8,11-55" - // output: [[1],[4,7],[8],[11,55]] - // the output is an array contaning either single line numbers [lineNum] or ranges [start, end] - // ',' delimits either single line numbers (eg: 1) or ranges (eg: 4-7) const highlightLines = highlightLinesInput.split(','); - // if it's the single number, it will just be parsed as an int, (eg: ['1'] --> [1] ) - // if it's a range, it will be parsed as as an array of two ints (eg: ['4-7'] --> [4,6]) - function parseAndZeroBaseLineNumber(numberString) { - // authors provide line numbers to highlight based on the 'start-from' attribute if it exists - // so we need to shift them all back down to start at 0 - return parseInt(numberString, 10) - startFromZeroBased; - } - lineNumbersAndRanges = highlightLines.map(elem => elem.split('-').map(parseAndZeroBaseLineNumber)); + highlightRules = highlightLines.map(HighlightRule.parseRule); + // Note: authors provide line numbers based on the 'start-from' attribute if it exists, + // so we need to shift line numbers back down to start at 0 + highlightRules.forEach(rule => rule.offsetLines(-startFromZeroBased)); } - + lines.pop(); // last line is always a single '\n' newline, so we remove it // wrap all lines with so we can number them str = lines.map((line, index) => { const currentLineNumber = index + 1; - // check if there is at least one range or line number that matches the current line number - // Note: The algorithm is based off markdown-it-highlight-lines (https://github.com/egoist/markdown-it-highlight-lines/blob/master/src/index.js) - // This is an O(n^2) solution wrt to the number of lines - // I opt to use this approach because it's simple, and it is unlikely that the number of elements in `lineNumbersAndRanges` will be large - // There is possible room for improvement for a more efficient algo that is O(n). - const inRange = lineNumbersAndRanges.some(([start, end]) => { - if (start && end) { - return currentLineNumber >= start && currentLineNumber <= end; - } - return currentLineNumber === start; - }); - if (inRange) { - return `${line}\n`; + const rule = highlightRules.find(rule => rule.shouldApplyHighlight(currentLineNumber)) + if (rule) { + return rule.applyHighlight(line); } + + // not highlighted return `${line}\n`; }).join('');