1+ /**
2+ * Adds a bar above code blocks that shows the language and a copy button
3+ */
4+
5+ import yaml from 'js-yaml'
6+ import fs from 'fs'
7+ import { visit } from 'unist-util-visit'
18import { h } from 'hastscript'
29import octicons from '@primer/octicons'
310import { parse } from 'parse5'
411import { fromParse5 } from 'hast-util-from-parse5'
512
6- const LANGUAGE_MAP = {
7- asp : 'ASP' ,
8- aspx : 'ASP' ,
9- 'aspx-vb' : 'ASP' ,
10- as3 : 'ActionScript' ,
11- apache : 'ApacheConf' ,
12- nasm : 'Assembly' ,
13- bat : 'Batchfile' ,
14- 'c#' : 'C#' ,
15- csharp : 'C#' ,
16- c : 'C' ,
17- 'c++' : 'C++' ,
18- cpp : 'C++' ,
19- chpl : 'Chapel' ,
20- coffee : 'CoffeeScript' ,
21- 'coffee-script' : 'CoffeeScript' ,
22- cfm : 'ColdFusion' ,
23- 'common-lisp' : 'Common Lisp' ,
24- lisp : 'Common Lisp' ,
25- dpatch : 'Darcs Patch' ,
26- dart : 'Dart' ,
27- elisp : 'Emacs Lisp' ,
28- emacs : 'Emacs Lisp' ,
29- 'emacs-lisp' : 'Emacs Lisp' ,
30- pot : 'Gettext Catalog' ,
31- html : 'HTML' ,
32- xhtml : 'HTML' ,
33- 'html+erb' : 'HTML+ERB' ,
34- erb : 'HTML+ERB' ,
35- irc : 'IRC log' ,
36- json : 'JSON' ,
37- jsp : 'Java Server Pages' ,
38- java : 'Java' ,
39- javascript : 'JavaScript' ,
40- js : 'JavaScript' ,
41- lhs : 'Literate Haskell' ,
42- 'literate-haskell' : 'Literate Haskell' ,
43- objc : 'Objective-C' ,
44- openedge : 'OpenEdge ABL' ,
45- progress : 'OpenEdge ABL' ,
46- abl : 'OpenEdge ABL' ,
47- pir : 'Parrot Internal Representation' ,
48- posh : 'PowerShell' ,
49- puppet : 'Puppet' ,
50- 'pure-data' : 'Pure Data' ,
51- raw : 'Raw token data' ,
52- rb : 'Ruby' ,
53- ruby : 'Ruby' ,
54- r : 'R' ,
55- scheme : 'Scheme' ,
56- bash : 'Shell' ,
57- sh : 'Shell' ,
58- shell : 'Shell' ,
59- zsh : 'Shell' ,
60- supercollider : 'SuperCollider' ,
61- tex : 'TeX' ,
62- ts : 'TypeScript' ,
63- vim : 'Vim script' ,
64- viml : 'Vim script' ,
65- rst : 'reStructuredText' ,
66- xbm : 'X BitMap' ,
67- xpm : 'X PixMap' ,
68- yaml : 'YAML' ,
69- yml : 'YAML' ,
70-
71- // Unofficial languages
72- shellsession : 'Shell' ,
73- jsx : 'JSX' ,
74- }
75-
76- const COPY_REGEX = / \{ : c o p y \} $ /
13+ const languages = yaml . load ( fs . readFileSync ( './data/variables/code-languages.yml' , 'utf8' ) )
7714
78- /**
79- * Adds a bar above code blocks that shows the language and a copy button
80- */
81- export default function addCodeHeader ( node ) {
82- // Check if the language matches `lang{:copy}`
83- const hasCopy = node . lang && COPY_REGEX . test ( node . lang )
15+ const matcher = ( node ) =>
16+ node . type === 'element' &&
17+ node . tagName === 'pre' &&
18+ // For now, limit to ones with the copy meta,
19+ // but we may enable for all examples later.
20+ getPreMeta ( node ) . copy &&
21+ // Don't add this header for annotated examples.
22+ ! getPreMeta ( node ) . annotate
8423
85- if ( hasCopy ) {
86- // js{:copy} => js
87- node . lang = node . lang . replace ( COPY_REGEX , '' )
88- } else {
89- // It doesn't have the copy annotation, so don't add the header
90- return
24+ export default function codeHeader ( ) {
25+ return ( tree ) => {
26+ visit ( tree , matcher , ( node , index , parent ) => {
27+ parent . children [ index ] = wrapCodeExample ( node )
28+ } )
9129 }
30+ }
9231
93- // Display the language using the above map of `{ [shortCode]: language }`
94- const language = LANGUAGE_MAP [ node . lang ] || node . lang || 'Code'
95-
96- const btnIconHtml = octicons . copy . toSVG ( )
97- const btnIconAst = parse ( String ( btnIconHtml ) , { sourceCodeLocationInfo : true } )
98- const btnIcon = fromParse5 ( btnIconAst , { file : btnIconHtml } )
32+ function wrapCodeExample ( node ) {
33+ const lang = node . children [ 0 ] . properties . className ?. [ 0 ] . replace ( 'language-' , '' )
34+ const code = node . children [ 0 ] . children [ 0 ] . value
35+ return h ( 'div' , { className : 'code-example' } , [ header ( lang , code ) , node ] )
36+ }
9937
100- // Need to create the header using Markdown AST utilities, to fit
101- // into the Unified processor ecosystem.
102- const header = h (
38+ export function header ( lang , code ) {
39+ return h (
10340 'header' ,
10441 {
10542 class : [
@@ -109,24 +46,35 @@ export default function addCodeHeader(node) {
10946 'p-2' ,
11047 'text-small' ,
11148 'rounded-top-1' ,
112- 'border' ,
49+ 'border-top' ,
50+ 'border-left' ,
51+ 'border-right' ,
11352 ] ,
11453 } ,
11554 [
116- h ( 'span' , language ) ,
55+ h ( 'span' , languages [ lang ] ?. name ) ,
11756 h (
11857 'button' ,
11958 {
12059 class : [ 'js-btn-copy' , 'btn' , 'btn-sm' , 'tooltipped' , 'tooltipped-nw' ] ,
121- 'data-clipboard-text' : node . value ,
60+ 'data-clipboard-text' : code ,
12261 'aria-label' : 'Copy code to clipboard' ,
12362 } ,
124- btnIcon
63+ btnIcon ( )
12564 ) ,
12665 ]
12766 )
67+ }
12868
129- return {
130- before : [ header ] ,
131- }
69+ function btnIcon ( ) {
70+ const btnIconHtml = octicons . copy . toSVG ( )
71+ const btnIconAst = parse ( String ( btnIconHtml ) , { sourceCodeLocationInfo : true } )
72+ const btnIcon = fromParse5 ( btnIconAst , { file : btnIconHtml } )
73+ return btnIcon
74+ }
75+
76+ function getPreMeta ( node ) {
77+ // Here's why this monstrosity works:
78+ // https://github.com/syntax-tree/mdast-util-to-hast/blob/c87cd606731c88a27dbce4bfeaab913a9589bf83/lib/handlers/code.js#L40-L42
79+ return node . children [ 0 ] ?. data ?. meta || { }
13280}
0 commit comments