package com.dmarcotte.handlebars.editor.braces; import com.dmarcotte.handlebars.file.HbFileType; import com.intellij.codeInsight.highlighting.BraceMatchingUtil; import com.intellij.testFramework.PlatformTestCase; import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; public class HbBraceMatcherTest extends LightPlatformCodeInsightFixtureTestCase { private static final String ourBraceMatchIndicator = "<brace_match>"; /** * Expects "fileText" to have two "<brace_match>" tokens, placed in front of two braces which are * expected to be matched by the built-in brace matching (i.e. when the caret is at one of the <brace_match> * tokens, the brace match subsystem highlights the brace at the other <brace_match>) * <p/> * NOTE: the <brace_match> before you close brace should have a bit of whitespace before it to make this * test work correctly. For example, have "{{/ foo <brace_match>}}" rather than "{{/foo<brace_match>}}" */ private void doBraceTest(String fileText) { String textForTest = fileText; int firstBracePosition = textForTest.indexOf(ourBraceMatchIndicator); textForTest = textForTest.replaceFirst(ourBraceMatchIndicator, ""); // remove first brace from input int secondBracePosition = textForTest.indexOf(ourBraceMatchIndicator); textForTest = textForTest.replaceFirst(ourBraceMatchIndicator, ""); // remove second brace from input assertTrue("Should have two \"" + ourBraceMatchIndicator + "\" tokens in fileText. Given fileText:\n" + fileText, firstBracePosition > -1 && secondBracePosition > -1); String firstBraceResult = findMatchBraceForBraceAtCaret(textForTest, firstBracePosition, secondBracePosition); assertEquals("Result with caret at first <brace_match>", fileText, firstBraceResult); String secondBraceResult = findMatchBraceForBraceAtCaret(textForTest, secondBracePosition, firstBracePosition); assertEquals("Result with caret at second <brace_match>", fileText, secondBraceResult); } /** * Method to do the actual invocation of the brace match subsystem on a given file for a given caret position * * @param fileText the source to test brace matching on * @param caretPosition caret position to compute a matched brace for * @param expectedMatchedBracePosition the expected position of the brace which matches the brace at caretPosition * @return the given file text with the {link #ourBraceMatchIndicator} tokens place where * the the brace matching subsystem dictated */ private String findMatchBraceForBraceAtCaret(String fileText, int caretPosition, int expectedMatchedBracePosition) { String caretIndicator = "<caret>"; String textWithCaret = new StringBuilder(fileText).insert(caretPosition, caretIndicator).toString(); myFixture.configureByText(HbFileType.INSTANCE, textWithCaret); boolean caretFirst = expectedMatchedBracePosition > caretPosition; int actualBraceMatchPosition = BraceMatchingUtil.getMatchedBraceOffset(myFixture.getEditor(), caretFirst, myFixture.getFile()); // we want to have an easy to read result, so we insert a <brace_match> where // BraceMatchingUtil.getMatchedBraceOffset told us it should go. String result = new StringBuilder(textWithCaret) // note that we need to compensate for the length of the caretIndicator if it comes before the ourBraceMatchIndicator .insert(actualBraceMatchPosition + (caretFirst ? caretIndicator.length() : 0), ourBraceMatchIndicator) .toString(); // replace the caret indicator with a ourBraceMatchIndicator so that our result format matches our input format result = result.replace(caretIndicator, ourBraceMatchIndicator); return result; } /** * Convenience property for quickly setting up brace match tests. * <p/> * Things to note about this text: * - The braces we want to match have some whitespace around them (this lets them match when the caret is before them) * - All mustache ids (foo, foo2, bar, etc) are unique so that they can be easily targeted * by string replace functions. */ private static final String ourTestSource = "{{# foo1 }}\n" + " {{ bar }}\n" + " {{^ }}\n" + " {{# foo2 }}\n" + " <div>\n" + " {{^ foo3 }}\n" + " Content\n" + " {{/ foo3 }}\n" + " </div>\n" + " {{{ baz }}}\n" + " {{ bat }}\n" + " {{> partial }}\n" + " {{/ foo2 }}\n" + "{{/ foo1 }}\n" + "\n" + "{{^ foo4 }}\n" + " Content\n" + "{{/ foo4 }}\n"; public void testSimpleMustache() { doBraceTest( ourTestSource.replace("{{ bar }}", "<brace_match>{{ bar }}") .replace("{{ bar }}", "{{ bar <brace_match>}}") ); } public void testUnEscapedMustache() { doBraceTest( ourTestSource.replace("{{{ baz }}}", "<brace_match>{{{ baz }}}") .replace("{{{ baz }}}", "{{{ baz <brace_match>}}}") ); } public void testPartial() { doBraceTest( ourTestSource.replace("{{> partial }}", "<brace_match>{{> partial }}") .replace("{{> partial }}", "{{> partial <brace_match>}}") ); } public void testBlockMustache() { doBraceTest( ourTestSource.replace("{{# foo1 }}", "<brace_match>{{# foo1 }}") .replace("{{/ foo1 }}", "{{/ foo1 <brace_match>}}") ); } public void testInverseBlockMustache() { doBraceTest( ourTestSource.replace("{{^ foo4 }}", "<brace_match>{{^ foo4 }}") .replace("{{/ foo4 }}", "{{/ foo4 <brace_match>}}") ); } public void testSimpleInverseMustache() { doBraceTest( ourTestSource.replace("{{^ }}", "<brace_match>{{^ }}") .replace("{{^ }}", "{{^ <brace_match>}}") ); } public void testNestedBlockStache() { doBraceTest( ourTestSource.replace("{{# foo2 }}", "<brace_match>{{# foo2 }}") .replace("{{/ foo2 }}", "{{/ foo2 <brace_match>}}") ); } public void testInverseBlockStache() { doBraceTest( ourTestSource.replace("{{^ foo3 }}", "<brace_match>{{^ foo3 }}") .replace("{{/ foo3 }}", "{{/ foo3 <brace_match>}}") ); } }