From 6ff61d3f4756a38703c465812fc9622e92e97be3 Mon Sep 17 00:00:00 2001 From: Mingun Date: Wed, 5 May 2021 20:45:24 +0500 Subject: [PATCH 1/2] Website: add visual indicators of errors in grammar --- docs/js/online.js | 132 +++++++++------ docs/online.html | 2 + docs/vendor/codemirror/lint.css | 79 +++++++++ docs/vendor/codemirror/lint.js | 291 ++++++++++++++++++++++++++++++++ 4 files changed, 455 insertions(+), 49 deletions(-) create mode 100644 docs/vendor/codemirror/lint.css create mode 100644 docs/vendor/codemirror/lint.js diff --git a/docs/js/online.js b/docs/js/online.js index fb33d8bb..ffa3ad10 100644 --- a/docs/js/online.js +++ b/docs/js/online.js @@ -5,22 +5,57 @@ $(document).ready(function() { var parser; var parserSource = null; - var buildAndParseTimer = null; var parseTimer = null; - var oldGrammar = null; - var oldParserVar = null; - var oldOptionCache = null; var oldInput = null; var editor = CodeMirror.fromTextArea($("#grammar").get(0), { lineNumbers: true, - mode: "pegjs" + mode: "pegjs", + gutters: ["CodeMirror-lint-markers"], + lint: true, }); var input = CodeMirror.fromTextArea($("#input").get(0), { lineNumbers: true, }); + CodeMirror.registerHelper("lint", "pegjs", function(grammar) { + var problems = []; + buildAndParse(grammar, problems); + return problems; + }); + + function convertLocation(location) { + return CodeMirror.Pos(location.line - 1, location.column - 1); + } + + function convertError(e, problems) { + if (e.location !== undefined) { + problems.push({ + severity: "error", + message: e.message, + from: convertLocation(e.location.start), + to: convertLocation(e.location.end), + }); + } else { + problems.push({ + severity: "error", + message: e.message, + }); + } + if (e.diagnostics !== undefined) { + for (var i = 0; i < e.diagnostics.length; ++i) { + var d = e.diagnostics[i]; + problems.push({ + severity: "warning", + message: d.message, + from: convertLocation(d.location.start), + to: convertLocation(d.location.end), + }); + } + } + } + function buildSizeAndTimeInfoHtml(title, size, time) { return $("", { "class": "size-and-time", @@ -37,11 +72,16 @@ $(document).ready(function() { : e.message; } - function build() { - oldGrammar = getGrammar(); - oldParserVar = $("#parser-var").val(); - oldOptionCache = $("#option-cache").is(":checked"); - + /** + * Generates code from the parser, collects problems in `problems` in CodeMirror + * lint format. + * + * @param {string} grammar Grammar text + * @param {CodeMirror.lint.Annotation[]} problems List of problems of current + * grammar that editor should show + * @returns {string} Source code of the parser + */ + function build(grammar, problems) { $('#build-message').attr("class", "message progress").text("Building the parser..."); $("#input").attr("disabled", "disabled"); $("#parse-message").attr("class", "message disabled").text("Parser not available."); @@ -52,9 +92,26 @@ $(document).ready(function() { try { var timeBefore = (new Date).getTime(); - parserSource = peggy.generate(getGrammar(), { + parserSource = peggy.generate(grammar, { cache: $("#option-cache").is(":checked"), - output: "source" + output: "source", + + error: function(_stage, message, location) { + problems.push({ + severity: "error", + message: message, + from: convertLocation(location.start), + to: convertLocation(location.end), + }); + }, + warn: function(_stage, message, location) { + problems.push({ + severity: "warning", + message: message, + from: convertLocation(location.start), + to: convertLocation(location.end), + }); + }, }); var timeAfter = (new Date).getTime(); @@ -65,7 +122,7 @@ $(document).ready(function() { .html("Parser built successfully.") .append(buildSizeAndTimeInfoHtml( "Parser build time and speed", - getGrammar().length, + grammar.length, timeAfter - timeBefore )); $("#input").removeAttr("disabled"); @@ -75,6 +132,7 @@ $(document).ready(function() { var result = true; } catch (e) { + convertError(e, problems); $("#build-message").attr("class", "message error").text(buildErrorMessage(e)); var result = false; @@ -118,34 +176,16 @@ $(document).ready(function() { return result; } - function buildAndParse() { - build() && parse(); + function buildAndParse(grammar, problems) { + build(grammar, problems) && parse(); } - function scheduleBuildAndParse() { - var nothingChanged = getGrammar() === oldGrammar - && $("#parser-var").val() === oldParserVar - && $("#option-cache").is(":checked") === oldOptionCache; - if (nothingChanged) { return; } - - if (buildAndParseTimer !== null) { - clearTimeout(buildAndParseTimer); - buildAndParseTimer = null; - } - if (parseTimer !== null) { - clearTimeout(parseTimer); - parseTimer = null; - } - - buildAndParseTimer = setTimeout(function() { - buildAndParse(); - buildAndParseTimer = null; - }, 500); + function rebuildGrammar() { + buildAndParse(editor.getValue(), []); } function scheduleParse() { if (input.getValue() === oldInput) { return; } - if (buildAndParseTimer !== null) { return; } if (parseTimer !== null) { clearTimeout(parseTimer); @@ -177,20 +217,14 @@ $(document).ready(function() { } } - function getGrammar() { - return editor.getValue(); - } - - editor.on("change", scheduleBuildAndParse); - $("#parser-var, #option-cache") - .change(scheduleBuildAndParse) - .mousedown(scheduleBuildAndParse) - .mouseup(scheduleBuildAndParse) - .click(scheduleBuildAndParse) - .keydown(scheduleBuildAndParse) - .keyup(scheduleBuildAndParse) - .keypress(scheduleBuildAndParse); + .change(rebuildGrammar) + .mousedown(rebuildGrammar) + .mouseup(rebuildGrammar) + .click(rebuildGrammar) + .keydown(rebuildGrammar) + .keyup(rebuildGrammar) + .keypress(rebuildGrammar); input.on("change", scheduleParse); @@ -210,7 +244,7 @@ $(document).ready(function() { $("#grammar, #parser-var, #option-cache").removeAttr("disabled"); - buildAndParse(); + rebuildGrammar(); editor.refresh(); editor.focus(); diff --git a/docs/online.html b/docs/online.html index 2f1477bd..32dc3099 100644 --- a/docs/online.html +++ b/docs/online.html @@ -20,6 +20,7 @@ + @@ -159,6 +160,7 @@

+ diff --git a/docs/vendor/codemirror/lint.css b/docs/vendor/codemirror/lint.css new file mode 100644 index 00000000..e1560db9 --- /dev/null +++ b/docs/vendor/codemirror/lint.css @@ -0,0 +1,79 @@ +/* The lint marker gutter */ +.CodeMirror-lint-markers { + width: 16px; +} + +.CodeMirror-lint-tooltip { + background-color: #ffd; + border: 1px solid black; + border-radius: 4px 4px 4px 4px; + color: black; + font-family: monospace; + font-size: 10pt; + overflow: hidden; + padding: 2px 5px; + position: fixed; + white-space: pre; + white-space: pre-wrap; + z-index: 100; + max-width: 600px; + opacity: 0; + transition: opacity .4s; + -moz-transition: opacity .4s; + -webkit-transition: opacity .4s; + -o-transition: opacity .4s; + -ms-transition: opacity .4s; +} + +.CodeMirror-lint-mark { + background-position: left bottom; + background-repeat: repeat-x; +} + +.CodeMirror-lint-mark-warning { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); +} + +.CodeMirror-lint-mark-error { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg=="); +} + +.CodeMirror-lint-marker { + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; + display: inline-block; + height: 16px; + width: 16px; + vertical-align: middle; + position: relative; +} + +.CodeMirror-lint-message { + padding-left: 18px; + background-position: top left; + background-repeat: no-repeat; +} + +.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); +} + +.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); +} + +.CodeMirror-lint-marker-multiple { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); + background-repeat: no-repeat; + background-position: right bottom; + width: 100%; height: 100%; +} + +.CodeMirror-lint-line-error { + background-color: rgba(183, 76, 81, 0.08); +} + +.CodeMirror-lint-line-warning { + background-color: rgba(255, 211, 0, 0.1); +} diff --git a/docs/vendor/codemirror/lint.js b/docs/vendor/codemirror/lint.js new file mode 100644 index 00000000..7b40e10e --- /dev/null +++ b/docs/vendor/codemirror/lint.js @@ -0,0 +1,291 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/5/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var GUTTER_ID = "CodeMirror-lint-markers"; + var LINT_LINE_ID = "CodeMirror-lint-line-"; + + function showTooltip(cm, e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme; + tt.appendChild(content.cloneNode(true)); + if (cm.state.lint.options.selfContain) + cm.getWrapperElement().appendChild(tt); + else + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + if (tt.style.opacity != null) tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function showTooltipFor(cm, e, content, node) { + var tooltip = showTooltip(cm, e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n && n.nodeType == 11) n = n.host; + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + + function LintState(cm, conf, hasGutter) { + this.marked = []; + if (conf instanceof Function) conf = {getAnnotations: conf}; + if (!conf || conf === true) conf = {}; + this.options = {}; + this.linterOptions = conf.options || {}; + for (var prop in defaults) this.options[prop] = defaults[prop]; + for (var prop in conf) { + if (defaults.hasOwnProperty(prop)) { + if (conf[prop] != null) this.options[prop] = conf[prop]; + } else if (!conf.options) { + this.linterOptions[prop] = conf[prop]; + } + } + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; + this.waitingFor = 0 + } + + var defaults = { + highlightLines: false, + tooltips: true, + delay: 500, + lintOnChange: true, + getAnnotations: null, + async: false, + selfContain: null, + formatAnnotation: null, + onUpdateLinting: null + } + + function clearMarks(cm) { + var state = cm.state.lint; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); + if (state.options.highlightLines) clearErrorLines(cm); + for (var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked.length = 0; + } + + function clearErrorLines(cm) { + cm.eachLine(function(line) { + var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass); + if (has) cm.removeLineClass(line, "wrap", has[0]); + }) + } + + function makeMarker(cm, labels, severity, multiple, tooltips) { + var marker = document.createElement("div"), inner = marker; + marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple"; + } + + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(cm, e, labels, inner); + }); + + return marker; + } + + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; + } + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); + } + return lines; + } + + function annotationTooltip(ann) { + var severity = ann.severity; + if (!severity) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity; + if (typeof ann.messageHTML != 'undefined') { + tip.innerHTML = ann.messageHTML; + } else { + tip.appendChild(document.createTextNode(ann.message)); + } + return tip; + } + + function lintAsync(cm, getAnnotations) { + var state = cm.state.lint + var id = ++state.waitingFor + function abort() { + id = -1 + cm.off("change", abort) + } + cm.on("change", abort) + getAnnotations(cm.getValue(), function(annotations, arg2) { + cm.off("change", abort) + if (state.waitingFor != id) return + if (arg2 && annotations instanceof CodeMirror) annotations = arg2 + cm.operation(function() {updateLinting(cm, annotations)}) + }, state.linterOptions, cm); + } + + function startLinting(cm) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + /* + * Passing rules in `options` property prevents JSHint (and other linters) from complaining + * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc. + */ + var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!getAnnotations) return; + if (options.async || getAnnotations.async) { + lintAsync(cm, getAnnotations) + } else { + var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm); + if (!annotations) return; + if (annotations.then) annotations.then(function(issues) { + cm.operation(function() {updateLinting(cm, issues)}) + }); + else cm.operation(function() {updateLinting(cm, annotations)}) + } + } + + function updateLinting(cm, annotationsNotSorted) { + var state = cm.state.lint; + if (!state) return; + var options = state.options; + clearMarks(cm); + + var annotations = groupByLine(annotationsNotSorted); + + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; + + // filter out duplicate messages + var message = []; + anns = anns.filter(function(item) { return message.indexOf(item.message) > -1 ? false : message.push(item.message) }); + + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); + + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!severity) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); + + if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); + + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity, + __annotation: ann + })); + } + // use original annotations[line] to show multiple messages + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, annotations[line].length > 1, + options.tooltips)); + + if (options.highlightLines) + cm.addLineClass(line, "wrap", LINT_LINE_ID + maxSeverity); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm.state.lint; + if (!state) return; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay); + } + + function popupTooltips(cm, annotations, e) { + var target = e.target || e.srcElement; + var tooltip = document.createDocumentFragment(); + for (var i = 0; i < annotations.length; i++) { + var ann = annotations[i]; + tooltip.appendChild(annotationTooltip(ann)); + } + showTooltipFor(cm, e, tooltip, target); + } + + function onMouseOver(cm, e) { + var target = e.target || e.srcElement; + if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; + var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; + var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); + + var annotations = []; + for (var i = 0; i < spans.length; ++i) { + var ann = spans[i].__annotation; + if (ann) annotations.push(ann); + } + if (annotations.length) popupTooltips(cm, annotations, e); + } + + CodeMirror.defineOption("lint", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + if (cm.state.lint.options.lintOnChange !== false) + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + clearTimeout(cm.state.lint.timeout); + delete cm.state.lint; + } + + if (val) { + var gutters = cm.getOption("gutters"), hasLintGutter = false; + for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; + var state = cm.state.lint = new LintState(cm, val, hasLintGutter); + if (state.options.lintOnChange) + cm.on("change", onChange); + if (state.options.tooltips != false && state.options.tooltips != "gutter") + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + + startLinting(cm); + } + }); + + CodeMirror.defineExtension("performLint", function() { + startLinting(this); + }); +}); From be9425da0c2ef6c9ed2af868bad94623194d1b54 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 12 Jun 2022 20:46:48 +0500 Subject: [PATCH 2/2] Website: add visual indicators of errors in input area --- CHANGELOG.md | 2 ++ docs/js/online.js | 37 ++++++++++++------------------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac4cfa10..116b732e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ Released: TBD - [#206](https://github.com/peggyjs/peggy/pull/206): New output type `ast` and an `--ast` flag for the CLI to get an internal grammar AST for investigation (can be useful for plugin writers), from @Mingun +- [#294](https://github.com/peggyjs/peggy/pull/294) Website: show errors in the + editors, from @Mingun ### Bug Fixes diff --git a/docs/js/online.js b/docs/js/online.js index ffa3ad10..add2ea89 100644 --- a/docs/js/online.js +++ b/docs/js/online.js @@ -5,10 +5,6 @@ $(document).ready(function() { var parser; var parserSource = null; - var parseTimer = null; - - var oldInput = null; - var editor = CodeMirror.fromTextArea($("#grammar").get(0), { lineNumbers: true, mode: "pegjs", @@ -17,6 +13,9 @@ $(document).ready(function() { }); var input = CodeMirror.fromTextArea($("#input").get(0), { lineNumbers: true, + mode: null, + gutters: ["CodeMirror-lint-markers"], + lint: true, }); CodeMirror.registerHelper("lint", "pegjs", function(grammar) { @@ -25,6 +24,12 @@ $(document).ready(function() { return problems; }); + CodeMirror.registerHelper("lint", null, function(content) { + var problems = []; + parse(content, problems); + return problems; + }); + function convertLocation(location) { return CodeMirror.Pos(location.line - 1, location.column - 1); } @@ -142,15 +147,12 @@ $(document).ready(function() { return result; } - function parse() { - oldInput = input.getValue(); - + function parse(newInput, problems) { $("#input").removeAttr("disabled"); $("#parse-message").attr("class", "message progress").text("Parsing the input..."); $("#output").addClass("disabled").text("Output not available."); try { - var newInput = input.getValue(); var timeBefore = (new Date).getTime(); var output = parser.parse(newInput); var timeAfter = (new Date).getTime(); @@ -167,6 +169,7 @@ $(document).ready(function() { var result = true; } catch (e) { + convertError(e, problems); $("#parse-message").attr("class", "message error").text(buildErrorMessage(e)); var result = false; @@ -177,27 +180,13 @@ $(document).ready(function() { } function buildAndParse(grammar, problems) { - build(grammar, problems) && parse(); + build(grammar, problems) && parse(input.getValue(), []); } function rebuildGrammar() { buildAndParse(editor.getValue(), []); } - function scheduleParse() { - if (input.getValue() === oldInput) { return; } - - if (parseTimer !== null) { - clearTimeout(parseTimer); - parseTimer = null; - } - - parseTimer = setTimeout(function() { - parse(); - parseTimer = null; - }, 500); - } - function doLayout() { var editors = $(".CodeMirror"); /* @@ -226,8 +215,6 @@ $(document).ready(function() { .keyup(rebuildGrammar) .keypress(rebuildGrammar); - input.on("change", scheduleParse); - $( "#parser-download" ) .click(function(){