From e83e5287d50b4f50909ff8c6e226f2407b2c3ab3 Mon Sep 17 00:00:00 2001 From: morhook Date: Tue, 5 Dec 2017 10:37:14 -0300 Subject: [PATCH] Add line numbers library - Downloaded code-highlight-linenums dependency to count lines on vendor directory. - Added code highlight call and also loading of library - CodeHighlightLinenums is an optional library, so it can be avoided loading - Added css for line numbering. Added shim for code-highlight-lineums too. Indented better the coding with line numbers - Scoped new line-numbers CSS code - Added documentation on README.md and CHANGELOG.md --- CHANGELOG.md | 4 + README.md | 17 ++++ app/components/code-snippet.js | 16 +++- app/templates/components/code-snippet.hbs | 6 +- index.js | 13 +++ vendor/code-highlight-linenums.js | 96 +++++++++++++++++++++++ vendor/highlight-style.css | 80 ++++++++++++++++++- vendor/shims/code-highlight-linenums.js | 12 +++ yarn.lock | 4 + 9 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 vendor/code-highlight-linenums.js create mode 100644 vendor/shims/code-highlight-linenums.js 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"