Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composable helpers #62

Merged
merged 11 commits into from Jun 24, 2019
2 changes: 1 addition & 1 deletion .eslintrc.js
@@ -1,7 +1,7 @@
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2017,
ecmaVersion: 2018,
sourceType: 'module'
},
plugins: [
Expand Down
6 changes: 5 additions & 1 deletion .template-lintrc.js
@@ -1,5 +1,9 @@
'use strict';

module.exports = {
extends: 'recommended'
extends: 'recommended',
rules: {
'block-indentation': false,
'no-html-comments': false
}
};
109 changes: 39 additions & 70 deletions README.md
@@ -1,16 +1,11 @@
Code Snippet Ember Component
Code Snippet Ember Helper
============================

This is an Ember component (and ember-cli addon) that lets you render
This is an Ember helper (and ember-cli addon) that lets you render
code snippets within your app. The code snippets can live in their own
dedicated files or you can extract blocks of code from your
application itself.

- Syntax highlighting thanks to [highlight.js](http://highlightjs.org/). To see how it looks, [view the highlightjs previews](https://highlightjs.org/).
- ember-cli's auto-reload will pick up changes to any of the snippet files.
- the component uses file extensions to help highlight.js guess the
right language. See below for details on choosing the supported languages.

Compatibility
------------------------------------------------------------------------------

Expand All @@ -28,10 +23,12 @@ ember install ember-code-snippet
Usage
------------------------------------------------------------------------------

### Defining snippets

There are two ways to store your code snippets. You can use either or
both together.

### With separate snippet files
#### With separate snippet files

Create a new "snippets" directory at the top level of your ember-cli
application or addon, and place the code snippets you'd like to render in their
Expand Down Expand Up @@ -61,7 +58,7 @@ var app = new EmberAddon({
});
```

### From within your application source
#### From within your application source

In any file under your `app` tree, annotate the start and end of a
code snippet block by placing comments like this:
Expand All @@ -78,15 +75,9 @@ The above is a Javascript example, but you can use any language's
comment format. We're just looking for lines that match
`/\bBEGIN-SNIPPET\s+(\S+)\b/` and `/\bEND-SNIPPET\b/`.

The opening comment must include a name. The component will identify
The opening comment must include a name. The helper will identify
these snippets using the names you specified plus the file extension
of the file in which they appeared (which helps us detect languages
for better highlighting). So the above example could be included in a
template like this:

```hbs
{{code-snippet name="my-nice-example.js"}}
```
of the file in which they appeared.

You can also define your own regex to find snippets. Just use the `snippetRegexes` option:

Expand All @@ -101,15 +92,6 @@ var app = new EmberAddon({

In the regex above everything in the `element-example` component block will be a snippet! Just make sure the first regex capture group is the name of the snippet.

By default, the component will try to unindent the code block by
removing whitespace characters from the start of each line until the
code bumps up against the edge. You can disable this with:

```hbs
{{code-snippet name="my-nice-example.js" unindent=false}}
```


You can choose which paths will be searched for inline snippets by
settings the snippetSearchPaths option when creating your application
in ember-cli-build.js:
Expand All @@ -128,68 +110,55 @@ var app = new EmberApp({
});
```

# Syntax Highlighting Language Support

We depend on [highlight.js](http://highlightjs.org/) for syntax highlighting. It supports 176 languages. But you probably don't want to build all of those into your app.

Out of the box, we only enable:
### Helper usage

- css
- coffeescript
- html/xml
- json
- javascript
- markdown
- handlebars
- htmlbars
After you have defined your snippets, you can use the `get-code-snippet` helper to get the snippet data
for rendering: `{{get-code-snippet "my-nice-example.js"}}`. The returned value will be a JavaScript object with the
following properties:

If you want a different set, you can:
* `source`: the source code extracted from the given snippet
* `language`: the snippets language, following the naming conventions of the popular `highlight.js` library, e.g. `htmlbars` for Ember templates
* `extension`: the file extension of the file containing the given snippet

1. Tell ember-code-snippet not to include highlight.js automatically for you. Also, include an array of file extensions corresponding to the languages you want to use. If ```snippetExtensions``` is not defined, the file extensions corresponding to the default list of supported languages will be used.
By default, the helper will try to unindent the code block by
removing whitespace characters from the start of each line until the
code bumps up against the edge. You can disable this with:

```js
// in ember-cli-build.js
var app = new EmberApp(defaults, {
includeHighlightJS: false,
snippetExtensions: ['js','java','php']
});
```hbs
{{get-code-snippet "my-nice-example.js" unindent=false}}
```

2. Go to https://highlightjs.org/download/ and generate your own build of highlight.js using the languages you want.
The following example will render a `<pre>` tag with a given code snippet:

3. Place the resulting highlight.pack.js in your `vendor` directory.

4. Import it directly from your ember-cli-build.js file:

```js
app.import('vendor/highlight.pack.js', {
using: [ { transformation: 'amd', as: 'highlight.js' } ]
});
```hbs
{{#let (get-code-snippet "static.hbs") as |snippet|}}
<pre class={{snippet.language}}>{{snippet.source}}</pre>
{{/let}}
```

# 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.

To use a different theme:
### Syntax Highlighting

1. Tell ember-code-snippet not to include its own theme:
This addon does not provide any syntax highlighting capabilities itself, but instead it is designed with composability
in mind, so you can add highlighting capabilities with any highlighting library on top of the snippet extraction
primitives of this addon. The following is an example of rendering a code snippet using code highlighting provided by the
[ember-prism](https://github.com/shipshapecode/ember-prism) addon:

```js
// in ember-cli-build.js
var app = new EmberApp(defaults, {
includeHighlightStyle: false
});
```hbs
{{#code-block language="handlebars"}}{{get (get-code-snippet "demo.hbs") "source"}}{{/code-block}}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use an example like this?

{{#let (get-code-snippet "demo.hbs") as |snippet|}}
  {{#code-block language=snippet.language}}
    {{snippet.source]}
  {{/code-block}}
{{/let}}

It would show why we decided to return a POJO with the language too. This snippet can more easily generalize to a reusable component that accepts the snippet name as an argument:

{{!-- templates/components/code-snippet.hbs --}}
{{#let (get-code-snippet @name) as |snippet|}}
  {{#code-block language=snippet.language}}
    {{snippet.source]}
  {{/code-block}}
{{/let}}

Which you would use like:

<CodeSnippet @name="demo.hbs" />

Maybe we should even suggest that pattern in the README. I think most apps that will have many snippets should make a component like this that puts together their chosen syntax highlighter and get-code-snippet.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and also we should explain this is the upgrade path for people who were using the current version of the addon. If they just want to keep the behavior they had, they should make their own <CodeSnippet /> component to replace the one we removed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, updated according to your suggestion!

It would show why we decided to return a POJO with the language too

There is one potential problem though: that example would actually not work properly with prism.js and hbs templates, as our helper returns 'htmlbars' as the language, while prism expects 'handlebars'. But as that the language value is not standardized across highlighters, there is not so much we can do about it!?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If prism is going to be our recommended syntax highlighter, I'm fine with changing our helper to return handlebars.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized I committed my recent Readme updates to the wrong branch. Fixed! 🤦‍♂️

If prism is going to be our recommended syntax highlighter, I'm fine with changing our helper to return handlebars.

Ok, agree. Updated that, including a test + docs.

```

2. Place your chosen style in `vendor`.
### JavaScript usage

3. Import it directly from your ember-cli-build.js:
When you want to use the code snippet functionality from JavaScript, you can import the `getCodeSnippet` function like
this:

```js
app.import('vendor/my-highlight-style.css');
import { getCodeSnippet } from 'ember-code-snippet';
```

Its call signature is similar to the helper invocation: `getCodeSnippet(name, unindent = true)`, and it returns the same
POJO as described above.


Contributing
------------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions addon/-private/extension.js
@@ -0,0 +1,4 @@
export default function getExtension(name) {
let m = /\.(\w+)$/i.exec(name);
return m ? m[1].toLowerCase() : undefined;
}
29 changes: 29 additions & 0 deletions addon/-private/get-snippet.js
@@ -0,0 +1,29 @@
import snippets from 'ember-code-snippet/snippets';
import getLanguage from './language';
import getExtension from './extension';
import unindentSource from '../-private/unindent';
import { assert } from '@ember/debug';

export default function getSnippet(name, unindent = true) {
let source = name
.split('/')
.reduce((dir, name) => dir && dir[name], snippets);
assert(`Code snippet with name "${name}" not found.`, source);

source = source
.replace(/^(\s*\n)*/, '')
.replace(/\s*$/, '');

if (unindent) {
source = unindentSource(source);
}

let language = getLanguage(name);
let extension = getExtension(name);

return {
source,
language,
extension
};
}
27 changes: 27 additions & 0 deletions addon/-private/language.js
@@ -0,0 +1,27 @@
import getExtension from './extension';

export default function getLanguage(name) {
let ext = getExtension(name);
if (ext) {
switch (ext) {
case 'js':
return 'javascript';
case 'coffee':
return 'coffeescript';
case 'hbs':
return 'htmlbars';
case 'css':
return 'css';
case 'scss':
return 'scss';
case 'less':
return 'less';
case 'emblem':
return 'emblem';
case 'ts':
return 'typescript';
default:
return ext;
}
}
}
13 changes: 13 additions & 0 deletions addon/-private/unindent.js
@@ -0,0 +1,13 @@
export default function unindent(src) {
let match, min, lines = src.split("\n").filter(l => l !== '');
for (let i = 0; i < lines.length; i++) {
match = /^[ \t]*/.exec(lines[i]);
if (match && (typeof min === 'undefined' || min > match[0].length)) {
min = match[0].length;
}
}
if (typeof min !== 'undefined' && min > 0) {
src = src.replace(new RegExp("^[ \t]{" + min + "}", 'gm'), "");
}
return src;
}
69 changes: 0 additions & 69 deletions addon/components/code-snippet.js

This file was deleted.

6 changes: 6 additions & 0 deletions addon/helpers/get-code-snippet.js
@@ -0,0 +1,6 @@
import { helper } from '@ember/component/helper';
import { getCodeSnippet } from 'ember-code-snippet';

export default helper(function([name], { unindent = true }) {
return getCodeSnippet(name, unindent);
});
5 changes: 5 additions & 0 deletions addon/index.js
@@ -0,0 +1,5 @@
import getCodeSnippet from './-private/get-snippet';

export {
getCodeSnippet
};
1 change: 0 additions & 1 deletion addon/templates/components/code-snippet.hbs

This file was deleted.

6 changes: 0 additions & 6 deletions app/components/code-snippet.js

This file was deleted.

1 change: 1 addition & 0 deletions app/helpers/get-code-snippet.js
@@ -0,0 +1 @@
export { default } from 'ember-code-snippet/helpers/get-code-snippet';
6 changes: 5 additions & 1 deletion ember-cli-build.js
Expand Up @@ -4,7 +4,11 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
let app = new EmberAddon(defaults, {
snippetSearchPaths: ['tests/integration']
snippetSearchPaths: ['tests/integration', 'tests/dummy'],
'ember-prism': {
'components': ['markup-templating', 'handlebars', 'javascript'],
'plugins': ['line-highlight']
}
});

/*
Expand Down