Skip to content

Instantly share code, notes, and snippets.

@nhz-io
Created February 4, 2017 20:14
Show Gist options
  • Save nhz-io/b34da04d83009d7217d4e401f7694ce8 to your computer and use it in GitHub Desktop.
Save nhz-io/b34da04d83009d7217d4e401f7694ce8 to your computer and use it in GitHub Desktop.
Code Histogram created by nhz-io - https://repl.it/F5Qs/98
.histogram {
margin: 0;
padding: 0;
box-sizing: border-box;
display: inline-block;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>repl.it</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/esprima/2.7.3/esprima.js"></script>
<link href="index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="histogram" data-url="index.js" style="font-size:12px;color:#222;width:800px;height:20px" data-fill="rgba(100,100,0,0.3)"></div>
<script src="index.js"></script>
</body>
</html>
function* map(list, fn) {
let i = 0
for (const item of list) {
yield fn.call(this, item, i, list)
i++
}
}
function* keys(target) {
if (typeof target === 'undefined') {
return yield* []
}
if (typeof target !== 'object') {
return yield* []
}
for (const key in target) {
if (target.hasOwnProperty(key)) {
yield key
}
}
}
function* values(target) {
if (typeof target === 'undefined') {
return yield* []
}
if (typeof target !== 'object') {
return yield* [target]
}
if (Array.isArray(target)) {
return yield* target
}
for (const key in target) {
if (target.hasOwnProperty(key)) {
yield target[key]
}
}
}
function* nodes(start) {
if (start && start.type && start.range && start.range.length) {
if (start.type !== 'Program') {
yield start
}
}
if (!start || typeof start !== 'object') {
return
}
for (const value of values(start)) {
yield* nodes(value)
}
}
d3.selectAll('.histogram').each(function () {
const url = this.getAttribute('data-url')
const width = parseInt(this.style.width || 500)
const height = parseInt(this.style.height || 30)
const fillStyle = this.getAttribute('data-fill') || 'rgba(0,0,0,0.08)'
const fontSize = parseInt(this.style.fontSize || 18)
const fontFamily = this.style.fontFamily || 'Monospace'
const textColor = this.style.color || 'black'
d3.text(url, (err, res) => {
if (err) {
throw err
}
const ast = esprima.parse(res, {range: true})
const list = Array.from(nodes(ast))
const types = []
const typedNodes = {}
const min = list.sort((a, b) => a.range[0] - b.range[0])[0].range[0]
const max = list.sort((a, b) => b.range[1] - a.range[1])[0].range[1]
console.log(min, max)
for (const item of list) {
const type = item.type
if (types.indexOf(type) === -1) {
types.push(type)
}
typedNodes[type] = typedNodes[type] || []
typedNodes[type].push(item)
}
const data = Array.from(
map(types, type => ({type, nodes: typedNodes[type]}))
)
const groups = d3.select(this).selectAll('canvas').data([types])
groups.enter()
.append('canvas')
.attr('width', width)
.attr('height', () => (height + fontSize + 10) * types.length + fontSize + 8)
.each(function () {
const ctx = this.getContext('2d')
ctx.font = `${fontSize}px ${fontFamily}`
const min = list.sort((a, b) => a.range[0] - b.range[0])[0].range[0]
const max = list.sort((a, b) => b.range[1] - a.range[1])[0].range[1]
const s = d3.scaleLinear().domain([min, max]).range([1, width])
ctx.fillStyle = 'white'
ctx.fillRect(0, 0, width, (height + fontSize + 10) * types.length + fontSize + 8)
ctx.fillStyle = textColor
ctx.fillText(url, 2, fontSize + 2)
types.forEach((type, i) => {
let y = (fontSize + 10 + height) * i + fontSize + 8
ctx.fillStyle = fillStyle
ctx.fillRect(0, y, width, 1)
y += fontSize + 4
ctx.fillStyle = textColor
ctx.fillText(`${type} (${typedNodes[type].length})`, 2, y)
y += 4
ctx.fillStyle = fillStyle
typedNodes[type].forEach(node => {
const width = s(node.range[1] - node.range[0]) - 1
ctx.fillRect(
s(node.range[0]),
y,
width < 1 ? 1 : width,
height
)
})
})
})
groups.exit().remove()
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment