mirror of https://github.com/Wilfred/difftastic/
Merge commit '45ce22c16ec924e34517cf785e23c07952e45893'
commit
a6eb1fb41d
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "tree-sitter-hcl"
|
||||
description = "hcl grammar for the tree-sitter parsing library"
|
||||
version = "0.0.1"
|
||||
keywords = ["incremental", "parsing", "hcl"]
|
||||
categories = ["parsing", "text-editors"]
|
||||
repository = "https://github.com/MichaHoffmann/tree-sitter-hcl"
|
||||
edition = "2018"
|
||||
license = "Apache"
|
||||
|
||||
build = "bindings/rust/build.rs"
|
||||
include = [
|
||||
"bindings/rust/*",
|
||||
"grammar.js",
|
||||
"queries/*",
|
||||
"src/*",
|
||||
]
|
||||
|
||||
[lib]
|
||||
path = "bindings/rust/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
tree-sitter = "~0.20"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
@ -0,0 +1,19 @@
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "tree_sitter_hcl_binding",
|
||||
"include_dirs": [
|
||||
"<!(node -e \"require('nan')\")",
|
||||
"src"
|
||||
],
|
||||
"sources": [
|
||||
"bindings/node/binding.cc",
|
||||
"src/parser.c",
|
||||
# If your language uses an external scanner, add it here.
|
||||
],
|
||||
"cflags_c": [
|
||||
"-std=c99",
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
#include "tree_sitter/parser.h"
|
||||
#include <node.h>
|
||||
#include "nan.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
extern "C" TSLanguage * tree_sitter_hcl();
|
||||
|
||||
namespace {
|
||||
|
||||
NAN_METHOD(New) {}
|
||||
|
||||
void Init(Local<Object> exports, Local<Object> module) {
|
||||
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
|
||||
tpl->SetClassName(Nan::New("Language").ToLocalChecked());
|
||||
tpl->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
|
||||
Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
|
||||
Local<Object> instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
|
||||
Nan::SetInternalFieldPointer(instance, 0, tree_sitter_hcl());
|
||||
|
||||
Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("hcl").ToLocalChecked());
|
||||
Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
|
||||
}
|
||||
|
||||
NODE_MODULE(tree_sitter_hcl_binding, Init)
|
||||
|
||||
} // namespace
|
||||
@ -0,0 +1,19 @@
|
||||
try {
|
||||
module.exports = require("../../build/Release/tree_sitter_hcl_binding");
|
||||
} catch (error1) {
|
||||
if (error1.code !== 'MODULE_NOT_FOUND') {
|
||||
throw error1;
|
||||
}
|
||||
try {
|
||||
module.exports = require("../../build/Debug/tree_sitter_hcl_binding");
|
||||
} catch (error2) {
|
||||
if (error2.code !== 'MODULE_NOT_FOUND') {
|
||||
throw error2;
|
||||
}
|
||||
throw error1
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
module.exports.nodeTypeInfo = require("../../src/node-types.json");
|
||||
} catch (_) {}
|
||||
@ -0,0 +1,35 @@
|
||||
fn main() {
|
||||
let src_dir = std::path::Path::new("src");
|
||||
|
||||
let mut c_config = cc::Build::new();
|
||||
c_config.include(&src_dir);
|
||||
c_config
|
||||
.flag_if_supported("-Wno-unused-parameter")
|
||||
.flag_if_supported("-Wno-unused-but-set-variable")
|
||||
.flag_if_supported("-Wno-trigraphs");
|
||||
let parser_path = src_dir.join("parser.c");
|
||||
c_config.file(&parser_path);
|
||||
|
||||
// If your language uses an external scanner written in C,
|
||||
// then include this block of code:
|
||||
|
||||
/*
|
||||
let scanner_path = src_dir.join("scanner.c");
|
||||
c_config.file(&scanner_path);
|
||||
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
|
||||
*/
|
||||
|
||||
c_config.compile("parser");
|
||||
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
|
||||
|
||||
let mut cpp_config = cc::Build::new();
|
||||
cpp_config.cpp(true);
|
||||
cpp_config.include(&src_dir);
|
||||
cpp_config
|
||||
.flag_if_supported("-Wno-unused-parameter")
|
||||
.flag_if_supported("-Wno-unused-but-set-variable");
|
||||
let scanner_path = src_dir.join("scanner.cc");
|
||||
cpp_config.file(&scanner_path);
|
||||
cpp_config.compile("scanner");
|
||||
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
//! This crate provides hcl language support for the [tree-sitter][] parsing library.
|
||||
//!
|
||||
//! Typically, you will use the [language][language func] function to add this language to a
|
||||
//! tree-sitter [Parser][], and then use the parser to parse some code:
|
||||
//!
|
||||
//! ```
|
||||
//! let code = "";
|
||||
//! let mut parser = tree_sitter::Parser::new();
|
||||
//! parser.set_language(tree_sitter_hcl::language()).expect("Error loading hcl grammar");
|
||||
//! let tree = parser.parse(code, None).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
|
||||
//! [language func]: fn.language.html
|
||||
//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
|
||||
//! [tree-sitter]: https://tree-sitter.github.io/
|
||||
|
||||
use tree_sitter::Language;
|
||||
|
||||
extern "C" {
|
||||
fn tree_sitter_hcl() -> Language;
|
||||
}
|
||||
|
||||
/// Get the tree-sitter [Language][] for this grammar.
|
||||
///
|
||||
/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
|
||||
pub fn language() -> Language {
|
||||
unsafe { tree_sitter_hcl() }
|
||||
}
|
||||
|
||||
/// The content of the [`node-types.json`][] file for this grammar.
|
||||
///
|
||||
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
|
||||
pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
|
||||
|
||||
// Uncomment these to include any queries that this grammar contains
|
||||
|
||||
// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
|
||||
// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
|
||||
// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
|
||||
// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_can_load_grammar() {
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
parser
|
||||
.set_language(super::language())
|
||||
.expect("Error loading hcl language");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Tree Sitter HCL Playground</title>
|
||||
<style>
|
||||
#playground-container {
|
||||
max-width: 640px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
#playground-container .CodeMirror {
|
||||
border: 1px solid;
|
||||
}
|
||||
#create-issue-btn {
|
||||
padding: 0.2em;
|
||||
float: right;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
#checkboxes {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
#output-container {
|
||||
border: 1px solid;
|
||||
}
|
||||
.highlight {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
This file is licensed under MIT license
|
||||
Copyright (c) 2018 Max Brunsfeld
|
||||
Taken from https://github.com/tree-sitter/tree-sitter/docs/section-7-playground.html
|
||||
-->
|
||||
|
||||
<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"
|
||||
/>
|
||||
|
||||
<div id="playground-container">
|
||||
<h1>Tree Sitter HCL Playground</h1>
|
||||
<h4>Code</h4>
|
||||
<div id="checkboxes">
|
||||
<input id="logging-checkbox" type="checkbox" />
|
||||
<label for="logging-checkbox">Log</label>
|
||||
|
||||
<input id="query-checkbox" type="checkbox" />
|
||||
<label for="query-checkbox">Query</label>
|
||||
</div>
|
||||
|
||||
<textarea id="code-input">
|
||||
example "test" {
|
||||
foo = "bar"
|
||||
}
|
||||
</textarea>
|
||||
|
||||
<div id="query-container" style="visibility: hidden; position: absolute">
|
||||
<h4>Query</h4>
|
||||
<textarea id="query-input"></textarea>
|
||||
</div>
|
||||
|
||||
<h4>Tree</h4>
|
||||
<span id="update-time"></span>
|
||||
<div id="output-container-scroll">
|
||||
<pre id="output-container" class="highlight"></pre>
|
||||
</div>
|
||||
<button id="create-issue-btn" type="button">Create Issue</button>
|
||||
</div>
|
||||
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.45.0/codemirror.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/clusterize.js/0.18.0/clusterize.min.js"></script>
|
||||
<script src="./vendor/tree-sitter.js"></script>
|
||||
<script id="playground-script" src="./playground.js?v=3"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,498 @@
|
||||
// This file is licensed under MIT license
|
||||
// Copyright (c) 2018 Max Brunsfeld
|
||||
// Taken from https://github.com/tree-sitter/tree-sitter/docs/assets/playground.js
|
||||
let tree;
|
||||
|
||||
(async () => {
|
||||
const CAPTURE_REGEX = /@\s*([\w\._-]+)/g;
|
||||
const COLORS_BY_INDEX = [
|
||||
"blue",
|
||||
"chocolate",
|
||||
"darkblue",
|
||||
"darkcyan",
|
||||
"darkgreen",
|
||||
"darkred",
|
||||
"darkslategray",
|
||||
"dimgray",
|
||||
"green",
|
||||
"indigo",
|
||||
"navy",
|
||||
"red",
|
||||
"sienna",
|
||||
];
|
||||
|
||||
const scriptURL = document.getElementById("playground-script").src;
|
||||
|
||||
const codeInput = document.getElementById("code-input");
|
||||
const loggingCheckbox = document.getElementById("logging-checkbox");
|
||||
const outputContainer = document.getElementById("output-container");
|
||||
const outputContainerScroll = document.getElementById(
|
||||
"output-container-scroll",
|
||||
);
|
||||
const playgroundContainer = document.getElementById("playground-container");
|
||||
const queryCheckbox = document.getElementById("query-checkbox");
|
||||
const createIssueBtn = document.getElementById("create-issue-btn");
|
||||
const queryContainer = document.getElementById("query-container");
|
||||
const queryInput = document.getElementById("query-input");
|
||||
const updateTimeSpan = document.getElementById("update-time");
|
||||
|
||||
loadState();
|
||||
|
||||
await TreeSitter.init();
|
||||
|
||||
const parser = new TreeSitter();
|
||||
const codeEditor = CodeMirror.fromTextArea(codeInput, {
|
||||
lineNumbers: true,
|
||||
showCursorWhenSelecting: true,
|
||||
});
|
||||
|
||||
const queryEditor = CodeMirror.fromTextArea(queryInput, {
|
||||
lineNumbers: true,
|
||||
showCursorWhenSelecting: true,
|
||||
});
|
||||
|
||||
const cluster = new Clusterize({
|
||||
rows: [],
|
||||
noDataText: null,
|
||||
contentElem: outputContainer,
|
||||
scrollElem: outputContainerScroll,
|
||||
});
|
||||
const renderTreeOnCodeChange = debounce(renderTree, 50);
|
||||
const saveStateOnChange = debounce(saveState, 2000);
|
||||
const runTreeQueryOnChange = debounce(runTreeQuery, 50);
|
||||
|
||||
let languageName = "hcl";
|
||||
let treeRows = null;
|
||||
let treeRowHighlightedIndex = -1;
|
||||
let parseCount = 0;
|
||||
let isRendering = 0;
|
||||
let query;
|
||||
|
||||
codeEditor.on("changes", handleCodeChange);
|
||||
codeEditor.on("viewportChange", runTreeQueryOnChange);
|
||||
codeEditor.on("cursorActivity", debounce(handleCursorMovement, 150));
|
||||
queryEditor.on("changes", debounce(handleQueryChange, 150));
|
||||
|
||||
loggingCheckbox.addEventListener("change", handleLoggingChange);
|
||||
queryCheckbox.addEventListener("change", handleQueryEnableChange);
|
||||
outputContainer.addEventListener("click", handleTreeClick);
|
||||
createIssueBtn.addEventListener("click", handleCreateIssue);
|
||||
handleQueryEnableChange();
|
||||
await loadLanguage();
|
||||
|
||||
playgroundContainer.style.visibility = "visible";
|
||||
|
||||
async function loadLanguage() {
|
||||
const query = new URL(scriptURL).search;
|
||||
const url = `tree-sitter-hcl.wasm${query}`;
|
||||
const language = await TreeSitter.Language.load(url);
|
||||
tree = null;
|
||||
parser.setLanguage(language);
|
||||
handleCodeChange();
|
||||
handleQueryChange();
|
||||
}
|
||||
|
||||
async function handleCodeChange(editor, changes) {
|
||||
const newText = codeEditor.getValue() + "\n";
|
||||
const edits = tree && changes && changes.map(treeEditForEditorChange);
|
||||
|
||||
const start = performance.now();
|
||||
if (edits) {
|
||||
for (const edit of edits) {
|
||||
tree.edit(edit);
|
||||
}
|
||||
}
|
||||
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();
|
||||
runTreeQueryOnChange();
|
||||
saveStateOnChange();
|
||||
}
|
||||
|
||||
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 runTreeQuery(_, startRow, endRow) {
|
||||
if (endRow == null) {
|
||||
const viewport = codeEditor.getViewport();
|
||||
startRow = viewport.from;
|
||||
endRow = viewport.to;
|
||||
}
|
||||
|
||||
codeEditor.operation(() => {
|
||||
const marks = codeEditor.getAllMarks();
|
||||
marks.forEach(m => m.clear());
|
||||
|
||||
if (tree && query) {
|
||||
const captures = query.captures(
|
||||
tree.rootNode,
|
||||
{ row: startRow, column: 0 },
|
||||
{ row: endRow, column: 0 },
|
||||
);
|
||||
let lastNodeId;
|
||||
for (const { name, node } of captures) {
|
||||
if (node.id === lastNodeId) continue;
|
||||
lastNodeId = node.id;
|
||||
const { startPosition, endPosition } = node;
|
||||
codeEditor.markText(
|
||||
{ line: startPosition.row, ch: startPosition.column },
|
||||
{ line: endPosition.row, ch: endPosition.column },
|
||||
{
|
||||
inclusiveLeft: true,
|
||||
inclusiveRight: true,
|
||||
css: `color: ${colorForCaptureName(name)}`,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleQueryChange() {
|
||||
if (query) {
|
||||
query.delete();
|
||||
query.deleted = true;
|
||||
query = null;
|
||||
}
|
||||
|
||||
queryEditor.operation(() => {
|
||||
queryEditor.getAllMarks().forEach(m => m.clear());
|
||||
if (!queryCheckbox.checked) return;
|
||||
|
||||
const queryText = queryEditor.getValue();
|
||||
|
||||
try {
|
||||
query = parser.getLanguage().query(queryText);
|
||||
let match;
|
||||
|
||||
let row = 0;
|
||||
queryEditor.eachLine(line => {
|
||||
while ((match = CAPTURE_REGEX.exec(line.text))) {
|
||||
queryEditor.markText(
|
||||
{ line: row, ch: match.index },
|
||||
{ line: row, ch: match.index + match[0].length },
|
||||
{
|
||||
inclusiveLeft: true,
|
||||
inclusiveRight: true,
|
||||
css: `color: ${colorForCaptureName(match[1])}`,
|
||||
},
|
||||
);
|
||||
}
|
||||
row++;
|
||||
});
|
||||
} catch (error) {
|
||||
const startPosition = queryEditor.posFromIndex(error.index);
|
||||
const endPosition = {
|
||||
line: startPosition.line,
|
||||
ch: startPosition.ch + (error.length || Infinity),
|
||||
};
|
||||
|
||||
if (error.index === queryText.length) {
|
||||
if (startPosition.ch > 0) {
|
||||
startPosition.ch--;
|
||||
} else if (startPosition.row > 0) {
|
||||
startPosition.row--;
|
||||
startPosition.column = Infinity;
|
||||
}
|
||||
}
|
||||
|
||||
queryEditor.markText(startPosition, endPosition, {
|
||||
className: "query-error",
|
||||
inclusiveLeft: true,
|
||||
inclusiveRight: true,
|
||||
attributes: { title: error.message },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
runTreeQuery();
|
||||
saveQueryState();
|
||||
}
|
||||
|
||||
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 handleCreateIssue() {
|
||||
const queryText = codeEditor.getValue();
|
||||
const outputText = outputContainer.innerText;
|
||||
const title = `Error parsing SQL`;
|
||||
const body = `Error when parsing the following SQL:
|
||||
\`\`\`
|
||||
${queryText}
|
||||
\`\`\`
|
||||
Error:
|
||||
\`\`\`
|
||||
${outputText}
|
||||
\`\`\``;
|
||||
const queryParams = `title=${encodeURIComponent(
|
||||
title,
|
||||
)}&body=${encodeURIComponent(body)}`;
|
||||
const url = `https://github.com/MichaHoffmann/tree-sitter-hcl/issues/new?${queryParams}`;
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
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 handleQueryEnableChange() {
|
||||
if (queryCheckbox.checked) {
|
||||
queryContainer.style.visibility = "";
|
||||
queryContainer.style.position = "";
|
||||
} else {
|
||||
queryContainer.style.visibility = "hidden";
|
||||
queryContainer.style.position = "absolute";
|
||||
}
|
||||
handleQueryChange();
|
||||
}
|
||||
|
||||
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 colorForCaptureName(capture) {
|
||||
const id = query.captureNames.indexOf(capture);
|
||||
return COLORS_BY_INDEX[id % COLORS_BY_INDEX.length];
|
||||
}
|
||||
|
||||
function storageGetItem(lookupKey) {
|
||||
try {
|
||||
return localStorage.getItem(lookupKey);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function storageSetItem(lookupKey, value) {
|
||||
try {
|
||||
return localStorage.setIem(lookupKey, value);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function loadState() {
|
||||
const language = storageGetItem("language");
|
||||
const sourceCode = storageGetItem("sourceCode");
|
||||
const query = storageGetItem("query");
|
||||
const queryEnabled = storageGetItem("queryEnabled");
|
||||
if (language != null && sourceCode != null && query != null) {
|
||||
queryInput.value = query;
|
||||
codeInput.value = sourceCode;
|
||||
queryCheckbox.checked = queryEnabled === "true";
|
||||
}
|
||||
}
|
||||
|
||||
function saveState() {
|
||||
storageSetItem("sourceCode", codeEditor.getValue());
|
||||
saveQueryState();
|
||||
}
|
||||
|
||||
function saveQueryState() {
|
||||
storageSetItem("queryEnabled", queryCheckbox.checked);
|
||||
storageSetItem("query", queryEditor.getValue());
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue