chore: add playground
This commit is contained in:
parent
d34a5b2fef
commit
2662c7d21a
96
docs/index.html
Normal file
96
docs/index.html
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<link rel="stylesheet" href="tree-sitter-0.15.7/style.css" media="screen" type="text/css">
|
||||||
|
<title>Tree-sitter TOML Playground</title>
|
||||||
|
<style>
|
||||||
|
#main-content {
|
||||||
|
left: 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
#version {
|
||||||
|
font-size: 0.5em;
|
||||||
|
}
|
||||||
|
#credit {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<main id="main-content">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.45.0/codemirror.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/clusterize.js/0.18.0/clusterize.min.css">
|
||||||
|
|
||||||
|
<h1><a href="https://github.com/ikatyang/tree-sitter-toml">Tree-sitter TOML</a> <span id="version">v0.2.0</span></h1>
|
||||||
|
|
||||||
|
<div id="playground-container" style="visibility: hidden;">
|
||||||
|
|
||||||
|
<h4>Code</h4>
|
||||||
|
<select id="language-select">
|
||||||
|
<option value="toml" selected="selected">TOML</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input id="logging-checkbox" type="checkbox"></input>
|
||||||
|
<label for="logging-checkbox">Log</label>
|
||||||
|
|
||||||
|
<textarea id="code-input">
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
<h4>Tree</h4>
|
||||||
|
<span id="update-time"></span>
|
||||||
|
<div id="output-container-scroll">
|
||||||
|
<pre id="output-container" class="highlight"></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="credit">
|
||||||
|
This playground was modified from <a href="http://tree-sitter.github.io/tree-sitter/playground">the official Tree-sitter Playground</a>.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.45.0/codemirror.min.js"></script>
|
||||||
|
|
||||||
|
<script>LANGUAGE_BASE_URL = "https://ikatyang.github.io/tree-sitter-toml/tree-sitter-toml-0.2.0";</script>
|
||||||
|
<script src="tree-sitter-0.15.7/tree-sitter.js"></script>
|
||||||
|
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/clusterize.js/0.18.0/clusterize.min.js"></script>
|
||||||
|
<script src="tree-sitter-0.15.7/playground.js"></script>
|
||||||
|
<script>
|
||||||
|
(codeExample => {
|
||||||
|
const handle = setInterval(() => {
|
||||||
|
const $codeEditor = document.querySelector('.CodeMirror');
|
||||||
|
if ($codeEditor) {
|
||||||
|
$codeEditor.CodeMirror.setValue(codeExample);
|
||||||
|
clearInterval(handle);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
})(`
|
||||||
|
# This is a TOML document.
|
||||||
|
|
||||||
|
title = "TOML Example"
|
||||||
|
|
||||||
|
[owner]
|
||||||
|
name = "Tom Preston-Werner"
|
||||||
|
dob = 1979-05-27T07:32:00-08:00 # First class dates
|
||||||
|
|
||||||
|
[database]
|
||||||
|
server = "192.168.1.1"
|
||||||
|
ports = [ 8001, 8001, 8002 ]
|
||||||
|
connection_max = 5000
|
||||||
|
`.trim());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<script
|
||||||
|
src="https://code.jquery.com/jquery-3.3.1.min.js"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
</script>
|
21
docs/tree-sitter-0.15.7/LICENSE
Normal file
21
docs/tree-sitter-0.15.7/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 Max Brunsfeld
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
279
docs/tree-sitter-0.15.7/playground.js
Normal file
279
docs/tree-sitter-0.15.7/playground.js
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
let tree;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const scriptURL = document.currentScript.getAttribute('src');
|
||||||
|
const codeInput = document.getElementById('code-input');
|
||||||
|
const languageSelect = document.getElementById('language-select');
|
||||||
|
const loggingCheckbox = document.getElementById('logging-checkbox');
|
||||||
|
const outputContainer = document.getElementById('output-container');
|
||||||
|
const outputContainerScroll = document.getElementById('output-container-scroll');
|
||||||
|
const updateTimeSpan = document.getElementById('update-time');
|
||||||
|
const demoContainer = document.getElementById('playground-container');
|
||||||
|
const languagesByName = {};
|
||||||
|
|
||||||
|
await TreeSitter.init();
|
||||||
|
|
||||||
|
const parser = new TreeSitter();
|
||||||
|
const codeEditor = CodeMirror.fromTextArea(codeInput, {
|
||||||
|
lineNumbers: true,
|
||||||
|
showCursorWhenSelecting: true
|
||||||
|
});
|
||||||
|
const cluster = new Clusterize({
|
||||||
|
rows: [],
|
||||||
|
noDataText: null,
|
||||||
|
contentElem: outputContainer,
|
||||||
|
scrollElem: outputContainerScroll
|
||||||
|
});
|
||||||
|
const renderTreeOnCodeChange = debounce(renderTree, 50);
|
||||||
|
|
||||||
|
let languageName = languageSelect.value;
|
||||||
|
let treeRows = null;
|
||||||
|
let treeRowHighlightedIndex = -1;
|
||||||
|
let parseCount = 0;
|
||||||
|
let isRendering = 0;
|
||||||
|
|
||||||
|
codeEditor.on('changes', handleCodeChange);
|
||||||
|
codeEditor.on('cursorActivity', debounce(handleCursorMovement, 150));
|
||||||
|
loggingCheckbox.addEventListener('change', handleLoggingChange);
|
||||||
|
languageSelect.addEventListener('change', handleLanguageChange);
|
||||||
|
outputContainer.addEventListener('click', handleTreeClick);
|
||||||
|
|
||||||
|
await handleLanguageChange()
|
||||||
|
|
||||||
|
demoContainer.style.visibility = 'visible';
|
||||||
|
|
||||||
|
async function handleLanguageChange() {
|
||||||
|
const newLanguageName = languageSelect.value;
|
||||||
|
if (!languagesByName[newLanguageName]) {
|
||||||
|
const url = `${LANGUAGE_BASE_URL}/tree-sitter-${newLanguageName}.wasm`
|
||||||
|
languageSelect.disabled = true;
|
||||||
|
try {
|
||||||
|
languagesByName[newLanguageName] = await TreeSitter.Language.load(url);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
languageSelect.value = languageName;
|
||||||
|
return
|
||||||
|
} finally {
|
||||||
|
languageSelect.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = null;
|
||||||
|
languageName = newLanguageName;
|
||||||
|
parser.setLanguage(languagesByName[newLanguageName]);
|
||||||
|
handleCodeChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCodeChange(editor, changes) {
|
||||||
|
const newText = codeEditor.getValue() + '\n';
|
||||||
|
|
||||||
|
const start = performance.now();
|
||||||
|
if (tree && changes) {
|
||||||
|
for (const change of changes) {
|
||||||
|
tree.edit(treeEditForEditorChange(change));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const newTree = parser.parse(newText, tree);
|
||||||
|
const duration = (performance.now() - start).toFixed(1);
|
||||||
|
|
||||||
|
updateTimeSpan.innerText = `${duration} ms`;
|
||||||
|
if (tree) tree.delete();
|
||||||
|
tree = newTree;
|
||||||
|
parseCount++;
|
||||||
|
renderTreeOnCodeChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderTree() {
|
||||||
|
isRendering++;
|
||||||
|
const cursor = tree.walk();
|
||||||
|
|
||||||
|
let currentRenderCount = parseCount;
|
||||||
|
let row = '';
|
||||||
|
let rows = [];
|
||||||
|
let finishedRow = false;
|
||||||
|
let visitedChildren = false;
|
||||||
|
let indentLevel = 0;
|
||||||
|
|
||||||
|
for (let i = 0;; i++) {
|
||||||
|
if (i > 0 && i % 10000 === 0) {
|
||||||
|
await new Promise(r => setTimeout(r, 0));
|
||||||
|
if (parseCount !== currentRenderCount) {
|
||||||
|
cursor.delete();
|
||||||
|
isRendering--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let displayName;
|
||||||
|
if (cursor.nodeIsMissing) {
|
||||||
|
displayName = `MISSING ${cursor.nodeType}`
|
||||||
|
} else if (cursor.nodeIsNamed) {
|
||||||
|
displayName = cursor.nodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visitedChildren) {
|
||||||
|
if (displayName) {
|
||||||
|
finishedRow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.gotoNextSibling()) {
|
||||||
|
visitedChildren = false;
|
||||||
|
} else if (cursor.gotoParent()) {
|
||||||
|
visitedChildren = true;
|
||||||
|
indentLevel--;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (displayName) {
|
||||||
|
if (finishedRow) {
|
||||||
|
row += '</div>';
|
||||||
|
rows.push(row);
|
||||||
|
finishedRow = false;
|
||||||
|
}
|
||||||
|
const start = cursor.startPosition;
|
||||||
|
const end = cursor.endPosition;
|
||||||
|
const id = cursor.nodeId;
|
||||||
|
let fieldName = cursor.currentFieldName();
|
||||||
|
if (fieldName) {
|
||||||
|
fieldName += ': ';
|
||||||
|
} else {
|
||||||
|
fieldName = '';
|
||||||
|
}
|
||||||
|
row = `<div>${' '.repeat(indentLevel)}${fieldName}<a class='plain' href="#" data-id=${id} data-range="${start.row},${start.column},${end.row},${end.column}">${displayName}</a> [${start.row}, ${start.column}] - [${end.row}, ${end.column}])`;
|
||||||
|
finishedRow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.gotoFirstChild()) {
|
||||||
|
visitedChildren = false;
|
||||||
|
indentLevel++;
|
||||||
|
} else {
|
||||||
|
visitedChildren = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (finishedRow) {
|
||||||
|
row += '</div>';
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.delete();
|
||||||
|
cluster.update(rows);
|
||||||
|
treeRows = rows;
|
||||||
|
isRendering--;
|
||||||
|
handleCursorMovement();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCursorMovement() {
|
||||||
|
if (isRendering) return;
|
||||||
|
|
||||||
|
const selection = codeEditor.getDoc().listSelections()[0];
|
||||||
|
let start = {row: selection.anchor.line, column: selection.anchor.ch};
|
||||||
|
let end = {row: selection.head.line, column: selection.head.ch};
|
||||||
|
if (
|
||||||
|
start.row > end.row ||
|
||||||
|
(
|
||||||
|
start.row === end.row &&
|
||||||
|
start.column > end.column
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
let swap = end;
|
||||||
|
end = start;
|
||||||
|
start = swap;
|
||||||
|
}
|
||||||
|
const node = tree.rootNode.namedDescendantForPosition(start, end);
|
||||||
|
if (treeRows) {
|
||||||
|
if (treeRowHighlightedIndex !== -1) {
|
||||||
|
const row = treeRows[treeRowHighlightedIndex];
|
||||||
|
if (row) treeRows[treeRowHighlightedIndex] = row.replace('highlighted', 'plain');
|
||||||
|
}
|
||||||
|
treeRowHighlightedIndex = treeRows.findIndex(row => row.includes(`data-id=${node.id}`));
|
||||||
|
if (treeRowHighlightedIndex !== -1) {
|
||||||
|
const row = treeRows[treeRowHighlightedIndex];
|
||||||
|
if (row) treeRows[treeRowHighlightedIndex] = row.replace('plain', 'highlighted');
|
||||||
|
}
|
||||||
|
cluster.update(treeRows);
|
||||||
|
const lineHeight = cluster.options.item_height;
|
||||||
|
const scrollTop = outputContainerScroll.scrollTop;
|
||||||
|
const containerHeight = outputContainerScroll.clientHeight;
|
||||||
|
const offset = treeRowHighlightedIndex * lineHeight;
|
||||||
|
if (scrollTop > offset - 20) {
|
||||||
|
$(outputContainerScroll).animate({scrollTop: offset - 20}, 150);
|
||||||
|
} else if (scrollTop < offset + lineHeight + 40 - containerHeight) {
|
||||||
|
$(outputContainerScroll).animate({scrollTop: offset - containerHeight + 40}, 150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTreeClick(event) {
|
||||||
|
if (event.target.tagName === 'A') {
|
||||||
|
event.preventDefault();
|
||||||
|
const [startRow, startColumn, endRow, endColumn] = event
|
||||||
|
.target
|
||||||
|
.dataset
|
||||||
|
.range
|
||||||
|
.split(',')
|
||||||
|
.map(n => parseInt(n));
|
||||||
|
codeEditor.focus();
|
||||||
|
codeEditor.setSelection(
|
||||||
|
{line: startRow, ch: startColumn},
|
||||||
|
{line: endRow, ch: endColumn}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLoggingChange() {
|
||||||
|
if (loggingCheckbox.checked) {
|
||||||
|
parser.setLogger((message, lexing) => {
|
||||||
|
if (lexing) {
|
||||||
|
console.log(" ", message)
|
||||||
|
} else {
|
||||||
|
console.log(message)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
parser.setLogger(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function treeEditForEditorChange(change) {
|
||||||
|
const oldLineCount = change.removed.length;
|
||||||
|
const newLineCount = change.text.length;
|
||||||
|
const lastLineLength = change.text[newLineCount - 1].length;
|
||||||
|
|
||||||
|
const startPosition = {row: change.from.line, column: change.from.ch};
|
||||||
|
const oldEndPosition = {row: change.to.line, column: change.to.ch};
|
||||||
|
const newEndPosition = {
|
||||||
|
row: startPosition.row + newLineCount - 1,
|
||||||
|
column: newLineCount === 1
|
||||||
|
? startPosition.column + lastLineLength
|
||||||
|
: lastLineLength
|
||||||
|
};
|
||||||
|
|
||||||
|
const startIndex = codeEditor.indexFromPos(change.from);
|
||||||
|
let newEndIndex = startIndex + newLineCount - 1;
|
||||||
|
let oldEndIndex = startIndex + oldLineCount - 1;
|
||||||
|
for (let i = 0; i < newLineCount; i++) newEndIndex += change.text[i].length;
|
||||||
|
for (let i = 0; i < oldLineCount; i++) oldEndIndex += change.removed[i].length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
startIndex, oldEndIndex, newEndIndex,
|
||||||
|
startPosition, oldEndPosition, newEndPosition
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function debounce(func, wait, immediate) {
|
||||||
|
var timeout;
|
||||||
|
return function() {
|
||||||
|
var context = this, args = arguments;
|
||||||
|
var later = function() {
|
||||||
|
timeout = null;
|
||||||
|
if (!immediate) func.apply(context, args);
|
||||||
|
};
|
||||||
|
var callNow = immediate && !timeout;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
if (callNow) func.apply(context, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
1
docs/tree-sitter-0.15.7/style.css
Normal file
1
docs/tree-sitter-0.15.7/style.css
Normal file
File diff suppressed because one or more lines are too long
1
docs/tree-sitter-0.15.7/tree-sitter.js
Normal file
1
docs/tree-sitter-0.15.7/tree-sitter.js
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/tree-sitter-0.15.7/tree-sitter.wasm
Normal file
BIN
docs/tree-sitter-0.15.7/tree-sitter.wasm
Normal file
Binary file not shown.
BIN
docs/tree-sitter-toml-0.2.0/tree-sitter-toml.wasm
Normal file
BIN
docs/tree-sitter-toml-0.2.0/tree-sitter-toml.wasm
Normal file
Binary file not shown.
Loading…
Reference in a new issue