本站相关

关于本站扩展Markdown语法

简介:关于本站扩展Markdown语法

本博客Markdown编辑器在基本语法上使用PyMdown和Pygments扩展了一些高级语法,并对其进行了特定的功能优化改进,下面就一些扩展语法进行说明。

1. Details语法

Details语法用于生成HTML5中的detailsummary标签,它的用法如下:

  • ???+ info "这是一个Info类型的Detail面板"
  • ```java
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • ```
  • ???+ success "这是一个Success类型的Detail面板"
  • ```java
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • ```
  • ???+ warning "这是一个Warning类型的Detail面板"
  • ```java
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • ```
  • ??? danger "这是一个折叠的Danger类型的Detail面板"
  • ```java
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • ```

渲染效果如下:

这是一个Info类型的Detail面板
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
这是一个Success类型的Detail面板
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
这是一个Warning类型的Detail面板
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
这是一个折叠的Danger类型的Detail面板
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }

其中第1行的???表示使用Details扩展,后面跟上的+表示该面板默认是展开的,如果不添加+面板默认为折叠状态,接下来的info表示面板的类型,目前支持info / success / warning / danger四种类型,最后双引号中的内容表示面板的标题。

第2行至第6行为面板的内容,可以是任意内容,但目前Details语法扩展只对内容为代码块的情况下样式友好,其他样式将陆续支持。

2. 扩展的代码行号

可以在代码块上使用linenums和hl_lines来指定显示行号和某行高亮,如:

  • ```java linenums="1 1 2" hl_lines="2"
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • ```

显示效果如下:

  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }

linenums选项可以输入最多三个数字,分别代表:

  • 起始行号:设置该值将从该值所指定的行开始进行编号;
  • 间隔行号:设置了该值,会每隔所指定行数进行一次编号;
  • 特殊间隔行号:设置了该值会每隔所指定行数进行一次特殊样式调整。

hl_lines选项用于指定高亮的行号,可以是一串数字序列,如“1 3 7 8”,则会高亮第1、3、7、8行;另外我对本选项进行了扩展,你甚至可以传一个Json串表明要高亮的行号和背景色,如:

  • hl_lines='{"5": "orange", "12": "green"}'

表示高亮第5行和第12行,第5行背景色为橙色,第12行背景色为绿色。效果如下:

  • ```java linenums="2 4 2" hl_lines='{"5": "red", "12": "green"}'
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • System.out.println("---- this.getClass().getResource(\"/\") ");
  • System.out.println(this.getClass().getResource("/"));
  • System.out.println(this.getClass().getResource("/").getPath());
  • System.out.println();
  • }
  • }
  • ```
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • System.out.println("---- this.getClass().getResource(\"/\") ");
  • System.out.println(this.getClass().getResource("/"));
  • System.out.println(this.getClass().getResource("/").getPath());
  • System.out.println();
  • }
  • }

我们可以通过传入shownum选项来指定是否显示行号,传入“1”为显示,“0”为不显示,不传默认显示行号:

  • ```java linenums="2 4 2" hl_lines='{"5": "red", "12": "green"}' shownum="0"
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • System.out.println("---- this.getClass().getResource(\"/\") ");
  • System.out.println(this.getClass().getResource("/"));
  • System.out.println(this.getClass().getResource("/").getPath());
  • System.out.println();
  • }
  • }
  • ```
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • System.out.println("---- this.getClass().getResource(\"/\") ");
  • System.out.println(this.getClass().getResource("/"));
  • System.out.println(this.getClass().getResource("/").getPath());
  • System.out.println();
  • }
  • }

3. 扩展的代码操作面板

我对Pygments的代码高亮处理做了很多优化,原始的Pygments只支持tableinline模式的代码高亮及行号显示,但是这两者都有弊端,table模式下,代码和行号分别在两个td中展示,这种方式的优点是代码易于复制,但缺点却非常明显,当需要代码折行显示时,代码块和行号可能由于行高问题对不齐;inline方式虽然没有这个问题,但这种方式下行号和代码放在一起,复制出来的代码会相当混乱。

在思索一番之后,我即手动扩展了Pygments的代码行号模块,使用ulli的方式实现了代码行号,这种方式可以兼顾易于复制和代码折行显示。

另外我对Pygments的代码块做了扩充,允许传入一个Json串用于生成操作面板,这个功能基于PyMdown SuperFences实现,在使用非Python Markdown Codehilite的情况下生效,使用方式如下:

3.1. 定义全局操作面板

我们可以通过在渲染Markdown文档时传入配置来添加操作面板,如:

Python
  • MARKDOWN_EXTENSION_CONFIGS = {
  • 'pymdownx.superfences': {
  • 'global_toolsbar': """
  • {
  • "shownum": {
  • "class": "shownum-class ivu-icon",
  • "id": "button-shownum",
  • "title": "显示或隐藏行号",
  • "text": "",
  • "icon": "i-icon-shownum",
  • "event": "onclick='toggleCodeNum(this)'"},
  • "theme": {
  • "class": "theme-class ivu-icon",
  • "id": "button-theme",
  • "title": "切换代码明暗显示",
  • "text": "",
  • "icon": "i-icon-theme",
  • "event": "onclick='toggleCodeTheme(this)'"},
  • "copy": {
  • "class": "copy-class ivu-icon",
  • "id": "button-copy",
  • "title": "复制代码到剪切板",
  • "text": "",
  • "icon": "i-icon-copy",
  • "event": "onclick='copyCode(this)'"},
  • "break": {
  • "class": "break-class ivu-icon",
  • "id": "button-break",
  • "title": "过长代码换号显示",
  • "text": "",
  • "icon": "i-icon-break",
  • "event": "onclick='toggleBreakCode(this)'"},
  • "fold": {
  • "class": "fold-class ivu-icon",
  • "id": "button-fold",
  • "title": "点击收起代码",
  • "text": "",
  • "icon": "i-icon-fold",
  • "event": "onclick='toggleFoldCode(this)'"
  • }
  • }
  • """
  • }
  • }

然后在渲染文档时,传入参数:

Python
  • self.formatted_content = markdown.markdown(self.origin_content, extensions=MARKDOWN_EXTENSIONS, extension_configs=MARKDOWN_EXTENSION_CONFIGS, lazy_ol=False)

在使用Markdown编写代码块时,如果要使用上面定义的全局操作面板,可以直接传入toolsbar="global"即可:

  • ```java linenums="1 1 2" toolsbar="global"
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • }
  • }
  • ```
Java
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • }
  • }

可以看到,扩展的面板已经显示出来了,想要面板的效果生效,我们只需要根据定义的全局配置在前端添加相应的JavaScript方法即可:

  • window.toggleCodeNum = function(target) {
  • let wrapDiv = target.parentNode.parentNode;
  • if (wrapDiv !== 'undefined') {
  • if (wrapDiv.classList.contains('shownum')) {
  • wrapDiv.classList.remove('shownum');
  • } else {
  • wrapDiv.classList.add('shownum');
  • }
  • }
  • let ul = target.parentNode.parentNode.querySelector('ul');
  • if (ul !== 'undefined') {
  • if (ul.classList.contains('numbered')) {
  • ul.classList.remove('numbered');
  • } else {
  • ul.classList.add('numbered');
  • }
  • }
  • };
  • window.toggleCodeTheme = function(target) {
  • let wrapDiv = target.parentNode.parentNode;
  • if (wrapDiv !== 'undefined') {
  • if (wrapDiv.classList.contains('dark')) {
  • wrapDiv.classList.remove('dark');
  • } else {
  • wrapDiv.classList.add('dark');
  • }
  • }
  • let highlight = target.parentNode.parentNode.querySelector('.highlight');
  • if (highlight !== 'undefined') {
  • if (highlight.classList.contains('dark')) {
  • highlight.classList.remove('dark');
  • } else {
  • highlight.classList.add('dark');
  • }
  • }
  • };
  • function copyInnerText(el) {
  • let text = '';
  • if (el.nodeName === 'SELECT') {
  • el.focus();
  • text = el.value;
  • } else if (el.nodeName === 'INPUT' || el.nodeName === 'TEXTAREA') {
  • let readonly = el.hasAttribute('readonly');
  • readonly || el.setAttribute('readonly', '');
  • el.select();
  • el.setSelectionRange(0, el.value.length);
  • readonly || el.removeAttribute('readonly');
  • text = el.value;
  • } else {
  • el.hasAttribute('contenteditable') && el.focus();
  • let selection = window.getSelection();
  • let range = document.createRange();
  • range.selectNodeContents(el);
  • selection.removeAllRanges();
  • selection.addRange(range);
  • text = selection.toString();
  • }
  • try {
  • // 复制
  • document.execCommand('Copy');
  • } catch(e) {
  • console.log('failed', e);
  • return false;
  • } finally {
  • // 清除选中
  • window.getSelection().removeAllRanges();
  • }
  • // 返回选中的值
  • return text;
  • }
  • window.copyCode = function(target) {
  • let preEle = target.parentNode.parentNode.querySelector('pre');
  • if (preEle !== 'undefined') {
  • let result = copyInnerText(preEle);
  • if (!result) {
  • Notice.error({
  • title: '复制出错',
  • desc: '请手动选择复制'
  • });
  • } else {
  • Notice.success({
  • title: '复制成功',
  • desc: '代码已复制到剪切板'
  • });
  • }
  • }
  • };
  • window.toggleBreakCode = function(target) {
  • let wrapDiv = target.parentNode.parentNode;
  • if (wrapDiv !== 'undefined') {
  • if (wrapDiv.classList.contains('linefeed')) {
  • wrapDiv.classList.remove('linefeed');
  • } else {
  • wrapDiv.classList.add('linefeed');
  • }
  • }
  • let highlight = target.parentNode.parentNode.querySelector('.highlight');
  • if (highlight !== 'undefined') {
  • if (highlight.classList.contains('linefeed')) {
  • highlight.classList.remove('linefeed');
  • } else {
  • highlight.classList.add('linefeed');
  • }
  • }
  • };
  • window.toggleFoldCode = function(target) {
  • let wrapDiv = target.parentNode.parentNode;
  • if (wrapDiv !== 'undefined') {
  • if (wrapDiv.classList.contains('folded')) {
  • wrapDiv.classList.remove('folded');
  • } else {
  • wrapDiv.classList.add('folded');
  • }
  • }
  • };

我们还可以为工具栏提供一个标题,此时则需要同时传入titleglobal参数:

  • ```java linenums="0 5" toolsbar='{"title": "Java代码", "global":true}'
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • }
  • }
  • ```

渲染出来的效果如下:

Java代码
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • }

上面这段代码渲染的结果HTML和定制的基本CSS结果如下:

  • <div class="toolsbar">
  • <span class="language">Java代码</span>
  • <button class="theme-class ivu-icon" id="button-theme" value="切换代码明暗显示" title="切换代码明暗显示" onclick="toggleCodeTheme(this)" type="button"></button>
  • <button class="break-class ivu-icon" id="button-break" value="代码自动换行" title="代码自动换行" onclick="toggleBreakCode(this)" type="button"></button>
  • <button class="shownum-class ivu-icon" id="button-shownum" value="显示或隐藏行号" title="显示或隐藏行号" onclick="toggleCodeNum(this)" type="button"></button>
  • <button class="copy-class ivu-icon" id="button-copy" value="复制代码到剪切板" title="复制代码到剪切板" onclick="copyCode(this)" type="button"></button>
  • <button class="fold-class ivu-icon" id="button-fold" value="点击收起代码" title="点击收起代码" onclick="toggleFoldCode(this)" type="button"></button>
  • </div>
  • .toolsbar {
  • display:flex;
  • justify-content:flex-end;
  • height:25px;
  • border:1px solid #ddd;
  • background:#fff;
  • padding:2px;
  • }
  • .toolsbar > span.language {
  • flex-grow:1;
  • color:#444;
  • font-size:14px;
  • font-weight:300;
  • padding-left:4px;
  • line-height:130%;
  • }
  • .toolsbar > button {
  • border:1px solid #2c2c2c;
  • background:#4f4f4f;
  • color:#ccc;
  • margin-left:2px;
  • outline:none;
  • font-size:13px;
  • min-width:20px;
  • padding:2px 4px;
  • cursor:pointer;

3.2. 定义局部操作面板

我们甚至可以直接给代码块的toolsbar传入一个Json串(该Json串需要和上面的Python格式的配置项结构一致),来定制单独代码块的操作面板:

  • ```java linenums="0 5" linenums="1 1 2" toolsbar='{"copy":{"class":"class_1","id":"id_1","title":"自定义按钮1","text":"自定义按钮1","icon":"i-icon-1","event":"onclick='alert(1)'"},"wrap":{"class":"class_2","id":"id_2","title":"自定义按钮2","text":"自定义按钮2","icon":"i-icon-2","event":"onclick='alert(2)'"},"fold":{"class":"class_3","id":"id_3","title":"自定义按钮3","text":"自定义按钮3","icon":"i-icon-3","event":"onclick='alert(3)'"}}'
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • }
  • }
  • ```
Java
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • }
  • }

注:当不传入toolsbar参数时即关闭操作面板。

4. 扩展的代码折叠和自动换行

在代码控制面板的基础上,我对Pygments和PyMdown进行了扩展,可以传入linefeedfolded来使代码换行或者关闭代码,folded配置必须依赖代码操作面板(否则无法控制关闭和打开),使用方式如下:

  • ```java linenums="0 5" toolsbar='{"title": "Java代码", "global":true}' shownum="0" linefeed="0" folded="1"
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • }
  • }
  • ```

渲染出来的效果如下:

Java代码【如折叠请点击右方按钮】
  • package com.coderap.foundation.resource;
  • public class ResourceTest {
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • public void testGetPath() {
  • System.out.println("---- this.getClass().getResource(\"\") ");
  • System.out.println(this.getClass().getResource(""));
  • System.out.println(this.getClass().getResource("").getPath());
  • System.out.println();
  • }

5. Code Tabs语法

Code Tabs语法可以方便将多份代码语言放在一个可切换的Tab面板中,比如最前面的效果即是Code Tabs语法,它的格式如下:

  • ```c linenums="1 1 2" tab="C代码"
  • #include
  • int main(void) {
  • printf("hello, world\n");
  • }
  • ```
  • ```java linenums="1 1 2" tab="Java代码"
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }
  • ```

上面的两段代码将会合在一个面板中显示:

  • #include
  • int main(void) {
  • printf("hello, world\n");
  • }
  • public static void main(String[] args) {
  • new ResourceTest().testGetPath();
  • }

6. 键盘按键语法

扩展的键盘按键语法更好地显示按键效果:

  • ++ctrl+alt+delete++

显示效果如下:

Ctrl+Alt+Del

7. 进度条语法

进度条可以使用下面的语法进行编写:

  • [=0% "0%"]
  • [=5% "5%"]
  • [=25% "25%"]
  • [=45% "45%"]
  • [=65% "65%"]
  • [=85% "85%"]
  • [=100% "100%"]
  • [=0%]{: .thin}
  • [=5%]{: .thin}
  • [=25%]{: .thin}
  • [=45%]{: .thin}
  • [=65%]{: .thin}
  • [=85%]{: .thin}
  • [=100%]{: .thin}

显示效果如下:

0%


5%


25%


45%


65%


85%


100%