const {assert} = require('chai'); let Parser, JavaScript, languageURL; describe("Parser", () => { let parser; before(async () => ({Parser, JavaScript, languageURL} = await require('./helper')) ); beforeEach(() => { parser = new Parser(); }); afterEach(() => { parser.delete() }); describe(".setLanguage", () => { it("allows setting the language to null", () => { assert.equal(parser.getLanguage(), null); parser.setLanguage(JavaScript); assert.equal(parser.getLanguage(), JavaScript); parser.setLanguage(null); assert.equal(parser.getLanguage(), null); }); it("throws an exception when the given object is not a tree-sitter language", () => { assert.throws(() => parser.setLanguage({}), /Argument must be a Language/); assert.throws(() => parser.setLanguage(1), /Argument must be a Language/); }); }); describe(".setLogger", () => { beforeEach(() => { parser.setLanguage(JavaScript) }); it("calls the given callback for each parse event", () => { const debugMessages = []; parser.setLogger((message) => debugMessages.push(message)); parser.parse("a + b + c"); assert.includeMembers(debugMessages, [ "skip character:' '", "consume character:'b'", "reduce sym:program, child_count:1", "accept" ]); }); it("allows the callback to be retrieved later", () => { const callback = () => {} parser.setLogger(callback); assert.equal(parser.getLogger(), callback); parser.setLogger(false); assert.equal(parser.getLogger(), null); }); it("disables debugging when given a falsy value", () => { const debugMessages = []; parser.setLogger((message) => debugMessages.push(message)); parser.setLogger(false); parser.parse("a + b * c"); assert.equal(debugMessages.length, 0); }); it("throws an error when given a truthy value that isn't a function ", () => { assert.throws( () => parser.setLogger("5"), "Logger callback must be a function" ); }); it("rethrows errors thrown by the logging callback", () => { const error = new Error("The error message"); parser.setLogger((msg, params) => { throw error; }); assert.throws( () => parser.parse("ok;"), "The error message" ); }); }); describe(".parse", () => { let tree; beforeEach(() => { tree = null; parser.setLanguage(JavaScript) }); afterEach(() => { if (tree) tree.delete(); }); it("reads from the given input", () => { const parts = ["first", "_", "second", "_", "third"]; tree = parser.parse(() => parts.shift()); assert.equal(tree.rootNode.toString(), "(program (expression_statement (identifier)))"); }); it("stops reading when the input callback return something that's not a string", () => { const parts = ["abc", "def", "ghi", {}, {}, {}, "second-word", " "]; tree = parser.parse(() => parts.shift()); assert.equal( tree.rootNode.toString(), "(program (expression_statement (identifier)))" ); assert.equal(tree.rootNode.endIndex, 9); assert.equal(parts.length, 2); }); it("throws an exception when the given input is not a function", () => { assert.throws(() => parser.parse(null), "Argument must be a string or a function"); assert.throws(() => parser.parse(5), "Argument must be a string or a function"); assert.throws(() => parser.parse({}), "Argument must be a string or a function"); }); it("handles long input strings", () => { const repeatCount = 10000; const inputString = "[" + "0,".repeat(repeatCount) + "]"; tree = parser.parse(inputString); assert.equal(tree.rootNode.type, "program"); assert.equal(tree.rootNode.firstChild.firstChild.namedChildCount, repeatCount); }).timeout(5000); it("can use the bash parser", async () => { parser.setLanguage(await Parser.Language.load(languageURL('bash'))); tree = parser.parse("FOO=bar echo < err.txt > hello.txt \nhello\nEOF"); assert.equal( tree.rootNode.toString(), '(program (redirected_statement (command ' + '(variable_assignment (variable_name) (word)) ' + '(command_name (word))) ' + '(heredoc_redirect (heredoc_start)) ' + '(file_redirect (file_descriptor) (word)) ' + '(file_redirect (word))) ' + '(heredoc_body))' ); }).timeout(5000); it("can use the c++ parser", async () => { parser.setLanguage(await Parser.Language.load(languageURL('cpp'))); tree = parser.parse("const char *s = R\"EOF(HELLO WORLD)EOF\";"); assert.equal( tree.rootNode.toString(), '(translation_unit (declaration (type_qualifier) (primitive_type) (init_declarator (pointer_declarator (identifier)) (raw_string_literal))))' ); }).timeout(5000); it("can use the HTML parser", async () => { parser.setLanguage(await Parser.Language.load(languageURL('html'))); tree = parser.parse("
"); assert.equal( tree.rootNode.toString(), '(fragment (element (start_tag (tag_name)) (element (start_tag (tag_name)) (element (start_tag (tag_name)) (end_tag (tag_name))) (end_tag (tag_name))) (end_tag (tag_name))))' ); }).timeout(5000); it("can use the python parser", async () => { parser.setLanguage(await Parser.Language.load(languageURL('python'))); tree = parser.parse("class A:\n def b():\n c()"); assert.equal( tree.rootNode.toString(), '(module (class_definition (identifier) (function_definition (identifier) (parameters) (expression_statement (call (identifier) (argument_list))))))' ); }).timeout(5000); it("can use the rust parser", async () => { parser.setLanguage(await Parser.Language.load(languageURL('rust'))); tree = parser.parse("const x: &'static str = r###\"hello\"###;"); assert.equal( tree.rootNode.toString(), '(source_file (const_item (identifier) (reference_type (lifetime (identifier)) (primitive_type)) (raw_string_literal)))' ); }).timeout(5000); it('parses only the text within the `includedRanges` if they are specified', () => { const sourceCode = "<% foo() %> <% bar %>"; const start1 = sourceCode.indexOf('foo'); const end1 = start1 + 5 const start2 = sourceCode.indexOf('bar'); const end2 = start2 + 3 const tree = parser.parse(sourceCode, null, { includedRanges: [ { startIndex: start1, endIndex: end1, startPosition: {row: 0, column: start1}, endPosition: {row: 0, column: end1} }, { startIndex: start2, endIndex: end2, startPosition: {row: 0, column: start2}, endPosition: {row: 0, column: end2} }, ] }); assert.equal( tree.rootNode.toString(), '(program (expression_statement (call_expression (identifier) (arguments))) (expression_statement (identifier)))' ); }) });});