Closed ZTiKnl closed 2 years ago
Out of the box, showdown does not support that functionality. However, it's very easy to create an extension that does that for you.
You can use a listener extension, listening to the makehtml.codeBlocks.after
event and change each line in the output prepending <code>
and appending </code>
Thank you for your quick response, and glad to hear I'm not asking for the impossible!
I have one follow-up question:
Is there any documentation or a working example/extension making use of the listener event?
edit: found that here, testing when I get back home!
Perhaps such an example could be added to the wiki for other (blind) people like me ;)
According to the extension wiki it is an upcoming feature, and unless I misunderstand, it is a seperate entity from the lang
& output
sub-extentions.
To be clear, I'm not asking to make the extention for me, happy to do the work myself, but am not sure how the syntax of a listener is supposed to work.
Almost there (I think):
Creating extension:
var codetageachline = function () {
console.log('test'); // works, runs once each script execution, seems alright to me so far
var myext1 = {
type: 'listener',
listeners: {
'makehtml.codeBlocks.after': function (event, text, converter, options, globals) {
console.log('test: ' + text); // doesnt work
text = text.replace(/\Memory/g, 'jbsgfjfdhbg');
return text;
}
}
};
return myext1;
}
Initializing showdown with options and load & convert content:
var markdownconverter = new showdown.Converter({extensions: [codetageachline], ellipsis:false});
markdownconverter.setOption('simpleLineBreaks', 'true');
markdownconverter.setOption('omitExtraWLInCodeBlocks', 'true');
markdownconverter.setOption('parseImgDimensions', 'true');
markdownconverter.setOption('strikethrough', 'true');
markdownconverter.setOption('tables', 'true');
markdownconverter.setOption('tasklists','true');
var input = fs.readFileSync(filePath, 'utf8');
var html = markdownconverter.makeHtml(input, {mode: 'nonAsciiPrintable', level: 'html5'});
File has the word Memory
in it multiple times, but doesn't seem to even reach that point.
The console.log('test');
at the start of the extension does work, so the extension is being triggered.
Further testing/experimenting required, to be continued!
codetageachline
should return an array of extensions:
return [myext1];
Updated, but afraid to say it doesn't seem to solve it
var codetageachline = function () {
console.log('test1'); // works
var myext1 = {
type: 'listener',
listeners: {
'makehtml.codeBlocks.after': function (event, text, converter, options, globals) {
console.log('test2: ' + text); // doesn't work, would at least expect 'test2: ' on console log here
sdjnfkjnfdsdkf; // undefined, doesn't trigger error
text = text.replace(/e/g, 'jbsgfjfdhbg');
return text;
}
}
};
return [myext1];
}
It appears as the function linked to the listener is never triggered
The .md file I am reading contains code blocks (in markdown syntax) When viewing the resulting HTML, there is a pre/code block in the source code at the right spot, but no modifications are made inside it.
I have changed the regex to be as simple as I can think of, replace all letter e
with a string, to be sure the regex isnt where I'm failing, but no luck so far :x
I tried another approach, using a seperate extension file, by making a copy/clone of the prettify extension:
e
with the string jbsgfjfdhbg
, but only inside a code block
This because it is easier to spot the changes and to avoid newline special case issues if any.The replace isn't being executed, neither is the console.log just in front of it.
Anyway, just reporting my progress / documenting attempts, I will continue to test and experiment (as free time allows me to)
Main script:
const showdown = require('showdown');
const codetagseachline = require('./codetagseachline.js');
showdown.extension('codetagseachline', codetagseachline);
var markdownconverter = new showdown.Converter({extensions: ['codetagseachline']});
var input = 'Test line 1\n' + 'Code block line 1\n' + '```Code block line 2\n' + 'Code block line 3\n```' + 'Test line 5';
var html = markdownconverter.makeHtml(input, {mode: 'nonAsciiPrintable', level: 'html5'});
console.log(html);
codetagseachline.js:
(function (extension) {
'use strict';
if (typeof showdown !== 'undefined') {
// global (browser or nodejs global)
extension(showdown);
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['showdown'], extension);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = extension(require('showdown'));
} else {
// showdown was not found so we throw
throw Error('Could not find showdown library');
}
}(function (showdown) {
'use strict';
showdown.extension('codetagseachline', function () {
console.log('test1'); // This line is printed at the moment this extension file is requested by main script
return [{
type: 'listener',
listeners: {
'makehtml.codeBlocks.after': function (source) {
console.log('test2: ' + source); // doesn't work, would at least expect 'test2: ' on console log here
source = source.replace(/e/g, 'jbsgfjfdhbg');
return source;
}
}
}];
});
}));
output
$ node test.js
test1
<p>Test line 1
<code>Code block line 1
Code block line 2
Code block line 3
</code>Test line 5</p>
I think you're forgetting to register the extension. There's an extension boilerplate you can use.
Regardless, I tested your extension code in jsfiddle and it works https://jsfiddle.net/eyjpzxr0/
Our documentation really needs to be improved
I'm really sorry, but I'm probably just having a breakdown or something..
I don't see the same as you do... Tried 3 different browsers, but the fiddle doesnt work in any
No errors, true but no text replacement either no console output
If I hit run, I see
<p>foo</p>
<pre><code>Memory
second line
third line
</code></pre>
<p>bar</p>
What I would expect with that fiddle:
<p>foo</p>
<pre><code>jbsgfjfdhbg
second line
third line
</code></pre>
<p>bar</p>
Are we still talking about the same thing here?
Our documentation really needs to be improved
Hehe, Work In Progress, one more thing on the todo list ;)
Oh and I really do appreciate you taking the time to reply and test, but don't feel obligated to help me, as I'm probably making some silly beginner mistake or something like that
sorry. I'm the one with the mental breakdown. I just finished a 24 hour shift in the ER and I'm not thinking straight! I was using the dev build (in my machine) and forgot that it was different.
Yeah, it wasn't working, but should be working now. Check this fiddle: https://jsfiddle.net/eyjpzxr0/2/
I just finished a 24 hour shift in the ER
I think the technical term is 'absolute legend'!
Not only a real-life hero but fixing my problems on the side... expect a donation coming up soon ;)
I had some time to do some more work on the original goal.
I am working under the assumption that I should be manipulating the globals.ghCodeBlocks object, as this holds an array with each gh code block in an array like such:
[
{
text: '\n' +
'```\n' +
'Sense: Prompt\n' +
'Sense: Memory\n' +
'Device: Sense\n' +
'\n' +
'Prompt->Color\n' +
'Prompt->Person\n' +
'Color-Person\n' +
'Voice->Person\n' +
'SFX->Person\n' +
'Person->Hearing\n' +
'Person->Vision\n' +
'```',
codeblock: '\n\n¨K0K\n\n'
}
]
I think I should be listening for the event githubCodeBlocks.after
, and not the before
as the globals.ghCodeBlocks isnt populated before this point.
showdown.extension('codetageachline', function() {
console.log('extension registered!');
var myext = {
type: 'listener',
listeners: {
'githubCodeBlocks.after': function(event, text, converter, options, globals) {
// console.info(globals.ghCodeBlocks);
let i = 0;
while (i < globals.ghCodeBlocks.length) {
globals.ghCodeBlocks[i].text = globals.ghCodeBlocks[i].text.replace(/Memory/g, 'sjhfbsjdhbfsd');
i++;
}
// console.info(globals.ghCodeBlocks); // changes appear here as expected
return text;
}
}
};
return [myext];
});
This function does seem to update the globals.ghCodeBlocks, but the changes are not reflected in the final output.
Starting to think I'm trying to manipulate the wrong variable/array by updating globals.ghCodeBlocks.text?
As always, not expecting help, mostly documenting my path to finding a solution, perhaps it helps someone else on a similar path ;)
I think for your use case it might be easer with an output extension. https://jsfiddle.net/30w9m6ef/
showdown.extension('codetageachline', function() {
return [{
type: 'output',
filter: function (text, converter, options) {
return text.replace(/<pre><code>([\s\S]+?)<\/code><\/pre>/g, function (fullMatch, inCode) {
// first split by newline, so we have an array of code lines
var codeLines = inCode.split('\n');
// pop the last element since it's an empty line
if (codeLines[codeLines.length - 1] === '') {
codeLines.pop();
}
codeLines = codeLines
// then loop through the array of lines of code and wrap it in code tags
.map(function(line) {
return '<code>' + line + '</code>';
})
// then rejoin the array into a string
.join('\n');
// lastly wrap everything in pre tags again
return '<pre>' + codeLines + '</pre>';
});
}
}];
});
Can't believe I didn't see this from the start...
Below you'll find the complete working script
I modified your example slightly, to also incorporate codeblock classes where needed
CSS:
pre {
counter-reset: line;
font-family: Menlo, Monaco, monospace;
background-color: #333;
padding: 5px;
padding-left: 15px;
color: #CCC;
border-radius: 3px;
word-break: keep-all;
white-space: pre-wrap;
}';
pre code:before {
content: counter(line);
counter-increment: line;
padding-right: 1em;
width: 1.5em;
text-align: right;
opacity: 0.5;
}
code {
display: inline;
background-color:#333;
color:#CCC;
}
Extension 'codetageachline':
showdown.extension('codetageachline', function() {
return [{
type: 'output',
filter: function (text, converter, options) {
text = text.replace(/<pre><code>([\s\S]+?)<\/code><\/pre>/g, function (fullMatch, inCode) {
// first split by newline, so we have an array of code lines
var codeLines = inCode.split('\n');
// pop the last element since it's an empty line
if (codeLines[codeLines.length - 1] === '') {
codeLines.pop();
}
codeLines = codeLines
// then loop through the array of lines of code and wrap it in code tags
.map(function(line) {
return '<code>' + line + '</code>';
})
// then rejoin the array into a string
.join('\n');
// lastly wrap everything in pre tags again
return '<pre>' + codeLines + '</pre>';
});
text = text.replace(/<pre><code class=\"([\s\S]+?)\">([\s\S]+?)?<\/code><\/pre>/g, function (fullMatch, codeClass, inCode) {
// first split by newline, so we have an array of code lines
var codeLines = inCode.split('\n');
// pop the last element since it's an empty line
if (codeLines[codeLines.length - 1] === '') {
codeLines.pop();
}
codeLines = codeLines
// then loop through the array of lines of code and wrap it in code tags
.map(function(line) {
return '<code class="' + codeClass + '">' + line + '</code>';
})
// then rejoin the array into a string
.join('\n');
// lastly wrap everything in pre tags again
return '<pre>' + codeLines + '</pre>';
});
return text;
}
}];
});
Yes, there is probably a better/cleaner way to do this, but this is a simple working solution everyone can reuse All credits to @tivie, all I did here was some CSS ;)
Updated opening post, closing ticket
Thanks again for all the time you spent on my sillyness ;)
Update: Solution here
Is it possible to have showdown add
<code>
tags for every line in a multiline code block?How it works now (not sure how to format example properly, sry):
Results in:
What I am hoping to get is:
Reason: I have some CSS code that allows for line numbering in code blocks This works using a counter for
<code>
tags, and a reset at each<pre>
tag Since all lines are placed in a single<code>
tag, it results in counting only a single lineMy working CSS (resulting in only recognizing a single line):
I have tried to find a solution using google and the issue tracker, but wasn't able to find it. Apologies if I have overlooked something obvious ;)