Skip to content

Commit

Permalink
feat: add include & enhance snippet
Browse files Browse the repository at this point in the history
  • Loading branch information
zyao89 committed Oct 25, 2021
1 parent 3e4fbbd commit 62f6dd1
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 3 deletions.
2 changes: 2 additions & 0 deletions docs/zh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ features:
footer: true
---

<<< config/svg-icon.md

::: warning
请确保你的 Node.js 版本 >= 8.6。
:::
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"generate-robotstxt": "^8.0.3",
"markdown-it-footnote": "^3.0.3",
"markdown-it-imsize": "^2.0.1",
"markdown-it-include": "^2.0.0",
"markdown-it-mark": "^3.0.1",
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0",
Expand Down
5 changes: 3 additions & 2 deletions src/commands/createConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ module.exports = function createConfig(api, args, opts) {
const path = require('path');

const vuepressConfig = api.vuepressConfig;
vuepressConfig.sourceDir = args._ && args._[1] || vuepressConfig.sourceDir || '.';
const sourceDir = args._ && args._[1] || vuepressConfig.sourceDir || '.';
const root = vuepressConfig.root;
vuepressConfig.sourceDir = path.resolve(root, sourceDir);

const loadConfig = require('@vuepress/core/lib/node/loadConfig');

// const vuepressDir = api.resolveWorkspace('.vuepress'); // 不能使用,vuepress内部代码太乱
const vuepressDir = path.resolve(root, vuepressConfig.sourceDir, '.vuepress');
const vuepressDir = path.resolve(vuepressConfig.sourceDir, '.vuepress');
const siteConfig = loadConfig(vuepressDir) || {};

const customConfig = require('../config');
Expand Down
6 changes: 6 additions & 0 deletions theme/components/SidebarGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ export default {
extends: ParentLayout,
};
</script>

<style>
.sidebar-heading {
border-left: 0;
}
</style>
18 changes: 17 additions & 1 deletion theme/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = (options, ctx) => {

const themeConfig = ctx.themeConfig = ctx.themeConfig || {};

const sourceDir = ctx.sourceDir;
const vuepressDir = ctx.vuepressDir;
const iconsDir = path.resolve(vuepressDir, 'public', 'icons');
const iconsLibDir = path.resolve(__dirname, 'icons');
Expand Down Expand Up @@ -115,7 +116,22 @@ module.exports = (options, ctx) => {
config.plugin('imsize').use(require('markdown-it-imsize'));
config.plugin('task-lists').use(require('markdown-it-task-lists'), [{ label: true, labelAfter: true }]);

// const { PLUGINS } = require('@vuepress/markdown/lib/constant.js');
// include
config.plugin('include').use(require('markdown-it-include'), [ {
root: sourceDir, // root path
includeRe: /<<<include(.+)/i,
bracesAreOptional: true,
getRootDir(pluginOptions, state, startLine, endLine) {
// const pos = state.bMarks[startLine] + state.tShift[startLine]
// const max = state.eMarks[startLine]
return pluginOptions.root;
},
} ])

const { PLUGINS } = require('@vuepress/markdown/lib/constant.js');
config.plugin(PLUGINS.SNIPPET).use(require('./plugins/markdown/snippet'), [ {
root: sourceDir, // root path
} ]);
// config.plugin(PLUGINS.ANCHOR).tap(options => {
// return options.map(opt => {
// // opt.permalinkSymbol = '#$';
Expand Down
162 changes: 162 additions & 0 deletions theme/plugins/markdown/snippet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
const { fs, logger, path } = require('@vuepress/shared-utils')

function dedent (text) {
const wRegexp = /^([ \t]*)(.*)\n/gm
let match; let minIndentLength = null

while ((match = wRegexp.exec(text)) !== null) {
const [indentation, content] = match.slice(1)
if (!content) continue

const indentLength = indentation.length
if (indentLength > 0) {
minIndentLength
= minIndentLength !== null
? Math.min(minIndentLength, indentLength)
: indentLength
} else break
}

if (minIndentLength) {
text = text.replace(
new RegExp(`^[ \t]{${minIndentLength}}(.*)`, 'gm'),
'$1'
)
}

return text
}

function testLine (line, regexp, regionName, end = false) {
const [full, tag, name] = regexp.exec(line.trim()) || []

return (
full
&& tag
&& name === regionName
&& tag.match(end ? /^[Ee]nd ?[rR]egion$/ : /^[rR]egion$/)
)
}

function findRegion (lines, regionName) {
const regionRegexps = [
/^\/\/ ?#?((?:end)?region) ([\w*-]+)$/, // javascript, typescript, java
/^\/\* ?#((?:end)?region) ([\w*-]+) ?\*\/$/, // css, less, scss
/^#pragma ((?:end)?region) ([\w*-]+)$/, // C, C++
/^<!-- #?((?:end)?region) ([\w*-]+) -->$/, // HTML, markdown
/^#((?:End )Region) ([\w*-]+)$/, // Visual Basic
/^::#((?:end)region) ([\w*-]+)$/, // Bat
/^# ?((?:end)?region) ([\w*-]+)$/ // C#, PHP, Powershell, Python, perl & misc
]

let regexp = null
let start = -1

for (const [lineId, line] of lines.entries()) {
if (regexp === null) {
for (const reg of regionRegexps) {
if (testLine(line, reg, regionName)) {
start = lineId + 1
regexp = reg
break
}
}
} else if (testLine(line, regexp, regionName, true)) {
return { start, end: lineId, regexp }
}
}

return null
}

module.exports = function snippet (md, options = {}) {
const fence = md.renderer.rules.fence
const root = options.root || process.cwd()

md.renderer.rules.fence = (...args) => {
const [tokens, idx, , { loader }] = args
const token = tokens[idx]
const [src, regionName] = token.src ? token.src.split('#') : ['']
if (src) {
if (loader) {
loader.addDependency(src)
}
const isAFile = fs.lstatSync(src).isFile()
if (fs.existsSync(src) && isAFile) {
let content = fs.readFileSync(src, 'utf8')

if (regionName) {
const lines = content.split(/\r?\n/)
const region = findRegion(lines, regionName)

if (region) {
content = dedent(
lines
.slice(region.start, region.end)
.filter(line => !region.regexp.test(line.trim()))
.join('\n')
)
}
}

token.content = content
} else {
token.content = isAFile ? `Code snippet path not found: ${src}` : `Invalid code snippet option`
token.info = ''
logger.error(token.content)
}
}
return fence(...args)
}

function parser (state, startLine, endLine, silent) {
const CH = '<'.charCodeAt(0)
const pos = state.bMarks[startLine] + state.tShift[startLine]
const max = state.eMarks[startLine]

// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) {
return false
}

for (let i = 0; i < 3; ++i) {
const ch = state.src.charCodeAt(pos + i)
if (ch !== CH || pos + i >= max) return false
}

// must be `<<< /ab/b`
if (state.src.charCodeAt(pos + 3) !== ' '.charCodeAt(0)) {
return false;
}

if (silent) {
return true
}

const start = pos + 3
const end = state.skipSpacesBack(max, pos)

/**
* raw path format: "/path/to/file.extension#region {meta}"
* where #region and {meta} are optionnal
*
* captures: ['/path/to/file.extension', 'extension', '#region', '{meta}']
*/
const rawPathRegexp = /^(.+?(?:\.([a-z]+))?)(?:(#[\w-]+))?(?: ?({\d+(?:[,-]\d+)*}))?$/

const rawPath = state.src.slice(start, end).trim().replace(/^@/, root).trim()
const [filename = '', extension = '', region = '', meta = ''] = (rawPathRegexp.exec(rawPath) || []).slice(1)

state.line = startLine + 1

const token = state.push('fence', 'code', 0)
token.info = extension + meta
token.src = path.resolve(root, filename) + region
token.markup = '```'
token.map = [startLine, startLine + 1]

return true
}

md.block.ruler.before('fence', 'snippet', parser)
}
4 changes: 4 additions & 0 deletions theme/styles/code.styl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ div[class*="language-"]
.custom-style-wrapper.window-controls
padding: 10px 0 0 10px;

.highlight-lines {
padding-top: 4rem;
}

div[class*="language-"].line-numbers-mode {
.line-numbers-wrapper {
top: 28px;
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8105,6 +8105,11 @@ markdown-it-imsize@^2.0.1:
resolved "https://registry.yarnpkg.com/markdown-it-imsize/-/markdown-it-imsize-2.0.1.tgz#cca0427905d05338a247cb9ca9d968c5cddd5170"
integrity sha1-zKBCeQXQUziiR8ucqdloxc3dUXA=

markdown-it-include@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/markdown-it-include/-/markdown-it-include-2.0.0.tgz#e86e3b3c68c8f0e0437e179ba919ffd28443127a"
integrity sha512-wfgIX92ZEYahYWiCk6Jx36XmHvAimeHN420csOWgfyZjpf171Y0xREqZWcm/Rwjzyd0RLYryY+cbNmrkYW2MDw==

markdown-it-mark@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/markdown-it-mark/-/markdown-it-mark-3.0.1.tgz#51257db58787d78aaf46dc13418d99a9f3f0ebd3"
Expand Down

0 comments on commit 62f6dd1

Please sign in to comment.