diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6d70c05..2a5ee3f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,3 +5,7 @@
- ENHANCEMENT: you can now disable the automatic inclusion of our default theme and provide one of the other highlight.js themes. See "Theming Support" in the README.
- ENHANCEMENT: detect nested code snippets (PR #42 by @defreeman)
+
+# 2.1.0
+
+ - ENHANCEMENT: Add code line numbers with code-highlight-linenums
diff --git a/README.md b/README.md
index e137704..26d4995 100644
--- a/README.md
+++ b/README.md
@@ -151,6 +151,23 @@ app.import('vendor/highlight.pack.js', {
});
```
+# Line Numbering Support
+
+Line numbering support is provided by a highlight addon [code-highlight-linenums](https://github.com/OverZealous/code-highlight-linenums). To enable call with `lineNumbers=true`.
+
+```hbs
+{{code-snippet name="my-nice-example.js" lineNumbers=true}}
+```
+
+For not including the code-highlight-linenums library, specify on the config like this:
+
+```js
+ // in ember-cli-build.js
+ var app = new EmberApp(defaults, {
+ includeCodeHighlightLinenums: false
+ });
+```
+
# Theming Support
We include a basic syntax-highlighting theme by default, but highlight.js has 79 different themes to choose from and it's possible to make your own just by writing a stylesheet.
diff --git a/app/components/code-snippet.js b/app/components/code-snippet.js
index 61b0775..78b6e80 100644
--- a/app/components/code-snippet.js
+++ b/app/components/code-snippet.js
@@ -1,5 +1,6 @@
import Ember from "ember";
import Snippets from "../snippets";
+import codeHighlightLinenums from "code-highlight-linenums";
/* global require */
var Highlight = self.require('highlight.js');
@@ -7,7 +8,9 @@ var Highlight = self.require('highlight.js');
export default Ember.Component.extend({
tagName: 'pre',
classNameBindings: ['language'],
+ classNames: ['code-snippet'],
unindent: true,
+ lineNumbers: false,
_unindent: function(src) {
if (!this.get('unindent')) {
@@ -27,15 +30,24 @@ export default Ember.Component.extend({
},
source: Ember.computed('name', function(){
- return this._unindent(
+ const source = this._unindent(
(Snippets[this.get('name')] || "")
.replace(/^(\s*\n)*/, '')
.replace(/\s*$/, '')
);
+ if (this.get('lineNumbers')) {
+ const lang = this.get('language');
+ return codeHighlightLinenums(source, {hljs:Highlight, lang, start:1 })
+ }
+
+ return source;
+
}),
didInsertElement: function(){
- Highlight.highlightBlock(this.get('element'));
+ if(!this.get('lineNumbers')) {
+ Highlight.highlightBlock(this.get('element'));
+ }
},
language: Ember.computed('name', function(){
diff --git a/app/templates/components/code-snippet.hbs b/app/templates/components/code-snippet.hbs
index 3a73287..ca5b2f3 100644
--- a/app/templates/components/code-snippet.hbs
+++ b/app/templates/components/code-snippet.hbs
@@ -1 +1,5 @@
-{{source}}
+
+{{#if lineNumbers}}
+{{{source}}}
+{{else}}{{source}}{{/if}}
+
diff --git a/index.js b/index.js
index 987d2d8..8cbb541 100644
--- a/index.js
+++ b/index.js
@@ -37,6 +37,15 @@ module.exports = {
}
},
+ includeCodeHighlightLinenums: function() {
+ var app = findHost(this);
+ if (typeof app.options.includeCodeHighlightLinenums === 'boolean') {
+ return app.options.includeCodeHighlightLinenums;
+ } else {
+ return true;
+ }
+ },
+
includeHighlightStyle: function() {
var app = findHost(this);
if (typeof app.options.includeHighlightStyle === 'boolean') {
@@ -72,5 +81,9 @@ module.exports = {
if (this.includeHighlightStyle()) {
app.import('vendor/highlight-style.css');
}
+ if (this.includeCodeHighlightLinenums()) {
+ app.import('vendor/code-highlight-linenums.js');
+ }
+ app.import('vendor/shims/code-highlight-linenums.js');
}
};
diff --git a/vendor/code-highlight-linenums.js b/vendor/code-highlight-linenums.js
new file mode 100644
index 0000000..13bfb79
--- /dev/null
+++ b/vendor/code-highlight-linenums.js
@@ -0,0 +1,96 @@
+(function(root) {
+ "use strict";
+
+ function codeHighlightLinenums(code, opts) {
+ opts = opts || {};
+ var hljs = opts.hljs,
+ lang = opts.lang,
+ start = opts.start || 0;
+ // prevent errors by hljs
+ code = code || '';
+ if(lang && /:/.test(lang)) {
+ start = +lang.split(/:/)[1];
+ lang = lang.split(/:/)[0];
+ } else {
+ start = +start;
+ }
+
+ if(hljs) {
+ if(lang) {
+ code = hljs.highlight(lang, code).value;
+ } else {
+ code = hljs.highlightAuto(code).value;
+ }
+ }
+
+ if(start) {
+ // move all closing spans to the previous line
+ code = code.replace(/([\r\n]\s*)(<\/span>)/ig, '$2$1');
+
+ // replace spans with line-wraps inside them
+ code = cleanLineBreaks(code);
+
+ code = code.split(/\r\n|\r|\n/);
+ var max = (start + code.length).toString().length;
+
+ code = code
+ .map(function(line, i) {
+ return '' + line + '';
+ })
+ .join('\n');
+ }
+
+ return code;
+ }
+
+ // Simplified parser that looks for opening & closing spans, and walks the tree.
+ // If there are any unclosed spans when a newline is encountered, we close them on the previous line,
+ // and copy them forward to the next line.
+ function cleanLineBreaks(code) {
+ var openSpans = [],
+ matcher = /<\/?span[^>]*>|\r\n|\r|\n/ig,
+ newline = /\r\n|\r|\n/,
+ closingTag = /^<\//;
+
+ return code.replace(matcher, function(match) {
+ if(newline.test(match)) {
+ if(openSpans.length) {
+ return openSpans.map(function() { return '' }).join('') + match + openSpans.join('');
+ } else {
+ return match;
+ }
+ } else if(closingTag.test(match)) {
+ openSpans.pop();
+ return match;
+ } else {
+ openSpans.push(match);
+ return match;
+ }
+ });
+ }
+
+ (function(factory) {
+ if(typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(factory)
+ } else if(typeof exports === 'object') {
+ /**
+ * Node. Does not work with strict CommonJS, but
+ * only CommonJS-like environments that support module.exports,
+ * like Node.
+ */
+ module.exports = factory();
+ } else {
+ // Browser globals (root is window)
+ root.codeHighlightLinenums = factory();
+ }
+ }(function() {
+ /**
+ * Just return a value to define the module export.
+ * This example returns an object, but the module
+ * can return a function as the exported value.
+ */
+ return codeHighlightLinenums;
+ }))
+
+})(this);
diff --git a/vendor/highlight-style.css b/vendor/highlight-style.css
index b50e3b8..56bba76 100644
--- a/vendor/highlight-style.css
+++ b/vendor/highlight-style.css
@@ -118,4 +118,82 @@
.hljs-chunk {
color: #aaa;
-}
\ No newline at end of file
+}
+
+/* this uses the CSS suggested by
+ https://github.com/OverZealous/code-highlight-linenums#example-lesscss
+*/
+
+pre.code-snippet > code .line {
+ display: inline-block;
+ position: relative;
+ padding-left: calc(2ch + 18px);
+}
+
+pre.code-snippet > code .line:before {
+ box-sizing: content-box;
+ display: inline-block;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ text-align: right;
+ width: 2ch;
+ content: attr(start);
+ padding-right: 9px;
+ padding-left: 9px;
+ margin-left: calc(-2ch - 27px);
+ margin-right: 9px;
+}
+
+pre.code-snippet > code .line:after {
+ content: ' ';
+}
+
+
+pre.code-snippet > code .line:first-child:before {
+ padding-top: 9px;
+ margin-top: -9px;
+ border-bottom-left-radius: 1em;
+}
+
+pre.code-snippet > code .line:last-child:before {
+ padding-bottom: 9px;
+ margin-bottom: -9px;
+ border-bottom-left-radius: 1em;
+}
+
+pre.code-snippet > code .line.width-3 {
+ padding-left: calc(3ch + 18px);
+}
+
+pre.code-snippet > code .line.width-3:before {
+ width: 3ch;
+ margin-left: calc(-3ch - 27px);
+}
+
+pre.code-snippet > code .line.width-4 {
+ padding-left: calc(4ch + 18px);
+}
+
+pre.code-snippet > code .line.width-4:before {
+ width: 4ch;
+ margin-left: calc(-4ch - 27px);
+}
+
+pre.code-snippet > code .line.width-5 {
+ padding-left: calc(5ch + 18px);
+}
+
+pre.code-snippet > code .line.width-5:before {
+ width: 5ch;
+ margin-left: calc(-5ch - 27px);
+}
+
+pre.code-snippet > code .line.width-6 {
+ padding-left: calc(6ch + 18px);
+}
+
+pre.code-snippet > code .line.width-6:before {
+ width: 6ch;
+ margin-left: calc(-6ch - 27px);
+}
diff --git a/vendor/shims/code-highlight-linenums.js b/vendor/shims/code-highlight-linenums.js
new file mode 100644
index 0000000..0e5d58d
--- /dev/null
+++ b/vendor/shims/code-highlight-linenums.js
@@ -0,0 +1,12 @@
+(function() {
+ function vendorModule() {
+ 'use strict';
+
+ return {
+ 'default': self['codeHighlightLinenums'],
+ __esModule: true,
+ };
+ }
+
+ define('code-highlight-linenums', [], vendorModule);
+})();
diff --git a/yarn.lock b/yarn.lock
index c932cc6..247e5c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -76,6 +76,10 @@ can-symlink@^1.0.0:
dependencies:
tmp "0.0.28"
+code-highlight-linenums@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/code-highlight-linenums/-/code-highlight-linenums-0.2.1.tgz#9519151404fdaa34df0b8fb809c0e2ee2913ce0e"
+
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"