diff --git a/docs/embed-files.md b/docs/embed-files.md
index 175e95d2d..13f9d8a75 100644
--- a/docs/embed-files.md
+++ b/docs/embed-files.md
@@ -65,7 +65,7 @@ Sometimes you don't want to embed a whole file. Maybe because you need just a fe
```
In your code file you need to surround the fragment between `/// [demo]` lines (before and after the fragment).
-Alternatively you can use `### [demo]`.
+Alternatively you can use `### [demo]`. If you want the full line containing the identifier to be omitted in the fragment output you can add the option `:fragmentFullLine`.
Example:
diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js
index 23bc540be..363bfc2d1 100644
--- a/src/core/render/compiler.js
+++ b/src/core/render/compiler.js
@@ -135,6 +135,7 @@ export class Compiler {
}
embed.fragment = config.fragment;
+ embed.fragmentFullLine = config.fragmentFullLine;
return embed;
}
diff --git a/src/core/render/compiler/media.js b/src/core/render/compiler/media.js
index d12b1fb0d..3fa3cd799 100644
--- a/src/core/render/compiler/media.js
+++ b/src/core/render/compiler/media.js
@@ -18,12 +18,12 @@ export const compileMedia = {
},
video(url, title) {
return {
- html: ``,
+ html: ``,
};
},
audio(url, title) {
return {
- html: ``,
+ html: ``,
};
},
code(url, title) {
diff --git a/src/core/render/embed.js b/src/core/render/embed.js
index 95055e9fa..d8e745e22 100644
--- a/src/core/render/embed.js
+++ b/src/core/render/embed.js
@@ -12,16 +12,22 @@ const cached = {};
*
* @param {string} text - The input text that may contain embedded fragments.
* @param {string} fragment - The fragment identifier to search for.
- * @returns {string} - The extracted and demented content, or an empty string if not found.
+ * @param {boolean} fullLine - Boolean flag to enable full-line matching of fragment identifiers.
+ * @returns {string} - The extracted and dedented content, or an empty string if not found.
*/
-function extractFragmentContent(text, fragment) {
+function extractFragmentContent(text, fragment, fullLine) {
if (!fragment) {
return text;
}
-
+ let fragmentRegex = `(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`;
+ const contentRegex = `[\\s\\S]*?`;
+ if (fullLine) {
+ // Match full line for fragment
+ fragmentRegex = `.*${fragmentRegex}.*\n`;
+ }
const pattern = new RegExp(
- `(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*?)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`,
- );
+ `(?:${fragmentRegex})(${contentRegex})(?:${fragmentRegex})`,
+ ); // content is the capture group
const match = text.match(pattern);
return stripIndent((match || [])[1] || '').trim();
}
@@ -68,13 +74,21 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
}
if (currentToken.embed.fragment) {
- text = extractFragmentContent(text, currentToken.embed.fragment);
+ text = extractFragmentContent(
+ text,
+ currentToken.embed.fragment,
+ currentToken.embed.fragmentFullLine,
+ );
}
embedToken = compile.lexer(text);
} else if (currentToken.embed.type === 'code') {
if (currentToken.embed.fragment) {
- text = extractFragmentContent(text, currentToken.embed.fragment);
+ text = extractFragmentContent(
+ text,
+ currentToken.embed.fragment,
+ currentToken.embed.fragmentFullLine,
+ );
}
embedToken = compile.lexer(
diff --git a/test/integration/example.test.js b/test/integration/example.test.js
index c7e9ca4aa..ff306209e 100644
--- a/test/integration/example.test.js
+++ b/test/integration/example.test.js
@@ -173,6 +173,48 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
);
});
+ test('embed file full line fragment identifier', async () => {
+ await docsifyInit({
+ markdown: {
+ homepage: `
+ # Embed Test
+
+ [filename](_media/example1.html ':include :type=code :fragment=demo :fragmentFullLine')
+ `,
+ },
+ routes: {
+ '_media/example1.html': `
+
+ `,
+ },
+ });
+
+ // Wait for the embedded fragment to be fetched and rendered into #main
+ expect(
+ await waitForText('#main', 'console.log(JSON.stringify(myJson));'),
+ ).toBeTruthy();
+
+ const mainText = document.querySelector('#main').textContent;
+ expect(mainText).not.toContain('https://api.example.com/data');
+ expect(mainText).not.toContain('Full line fragment identifier');
+ expect(mainText).not.toContain('-->');
+ expect(mainText).not.toContain(
+ 'result.then(console.log).catch(console.error);',
+ );
+ });
+
test('embed multiple file code fragments', async () => {
await docsifyInit({
markdown: {
@@ -186,7 +228,9 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
# Text between
[filename](_media/example3.js ':include :fragment=something_else_not_code')
-
+
+ [filename](_media/example4.js ':include :fragment=demo')
+
# Text after
`,
},
@@ -209,6 +253,12 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
example3 += 10;
/// [something_else_not_code]
console.log(example3);`,
+ '_media/example4.js': `
+ let example4 = 1;
+ ### No fragment here
+ example4 += 10;
+ /// No fragment here
+ console.log(example4);`,
},
});
@@ -225,5 +275,8 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
expect(mainText).not.toContain('console.log(example1);');
expect(mainText).not.toContain('console.log(example2);');
expect(mainText).not.toContain('console.log(example3);');
+ expect(mainText).not.toContain('console.log(example4);');
+ expect(mainText).not.toContain('example4 += 10;');
+ expect(mainText).not.toContain('No fragment here');
});
});