blob: b7cc6c751a6aead2c00b836ff18404f5c9b679fd [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle.arrangement
import com.intellij.lang.Language
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.util.TextRange
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import com.intellij.psi.codeStyle.arrangement.engine.ArrangementEngine
import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingRule
import com.intellij.psi.codeStyle.arrangement.match.ArrangementSectionRule
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition
import com.intellij.psi.codeStyle.arrangement.std.ArrangementSettingsToken
import com.intellij.psi.codeStyle.arrangement.std.StdArrangementSettings
import com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.junit.Assert
import static com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens.Order.KEEP
/**
* @author Denis Zhdanov
* @since 7/20/12 2:54 PM
*/
abstract class AbstractRearrangerTest extends LightPlatformCodeInsightFixtureTestCase {
static final def RICH_TEXT_HANDLERS = [ new RangeHandler(), new FoldingHandler() ]
FileType fileType
Language language;
@Override
protected void setUp() {
super.setUp()
CodeStyleSettingsManager.getInstance(myFixture.project).temporarySettings = new CodeStyleSettings()
}
@Override
protected void tearDown() {
CodeStyleSettingsManager.getInstance(myFixture.project).dropTemporarySettings()
super.tearDown()
}
@NotNull
protected CommonCodeStyleSettings getCommonSettings() {
CodeStyleSettingsManager.getInstance(myFixture.project).currentSettings.getCommonSettings(language)
}
protected static ArrangementSectionRule section(@NotNull StdArrangementMatchRule... rules) {
section(null, null, rules);
}
protected static ArrangementSectionRule section(@Nullable String start, @Nullable String end, @NotNull StdArrangementMatchRule... rules) {
ArrangementSectionRule.create(start, end, rules);
}
@NotNull
protected static ArrangementGroupingRule group(@NotNull ArrangementSettingsToken type) {
group(type, KEEP)
}
@NotNull
protected static ArrangementGroupingRule group(@NotNull ArrangementSettingsToken type, @NotNull ArrangementSettingsToken order) {
new ArrangementGroupingRule(type, order)
}
@NotNull
protected static StdArrangementMatchRule rule(@NotNull ArrangementSettingsToken token) {
new StdArrangementMatchRule(new StdArrangementEntryMatcher(atom(token)))
}
@NotNull
protected static StdArrangementMatchRule nameRule(@NotNull String nameFilter, @NotNull ArrangementSettingsToken ... tokens) {
if (tokens.length == 0) {
new StdArrangementMatchRule(new StdArrangementEntryMatcher(atom(nameFilter)))
}
else {
def compositeCondition = ArrangementUtil.combine([atom(nameFilter)] + tokens.collect { atom(it) } as ArrangementAtomMatchCondition[])
new StdArrangementMatchRule(new StdArrangementEntryMatcher(compositeCondition))
}
}
@NotNull
protected static StdArrangementMatchRule rule(@NotNull ArrangementSettingsToken ... conditions) {
rule(conditions.collect { atom(it) })
}
@NotNull
protected static StdArrangementMatchRule rule(@NotNull ArrangementAtomMatchCondition ... conditions) {
def compositeCondition = ArrangementUtil.combine(conditions)
new StdArrangementMatchRule(new StdArrangementEntryMatcher(compositeCondition))
}
@NotNull
protected static StdArrangementMatchRule rule(@NotNull List<ArrangementAtomMatchCondition> conditions) {
rule(conditions as ArrangementAtomMatchCondition[])
}
@NotNull
protected static StdArrangementMatchRule ruleWithOrder(@NotNull ArrangementSettingsToken orderType,
@NotNull StdArrangementMatchRule rule)
{
new StdArrangementMatchRule(rule.matcher, orderType)
}
@NotNull
protected static ArrangementAtomMatchCondition atom(@NotNull ArrangementSettingsToken token) {
new ArrangementAtomMatchCondition(token)
}
@NotNull
protected static ArrangementAtomMatchCondition atom(@NotNull String nameFilter) {
new ArrangementAtomMatchCondition(StdArrangementTokens.Regexp.NAME, nameFilter)
}
protected void doTest(@NotNull args) {
Info info = parse(args.initial)
if (info.ranges && args.ranges) {
junit.framework.Assert.fail(
"Duplicate ranges info detected: explicitly given: $args.ranges, derived from markup: ${info.ranges}. Text:\n$args.initial"
)
}
if (!info.ranges) {
info.ranges = args.ranges ?: [TextRange.from(0, args.initial.length())]
}
myFixture.configureByText(fileType, info.text)
def foldingModel = myFixture.editor.foldingModel
info.foldings.each { FoldingInfo foldingInfo ->
foldingModel.runBatchFoldingOperation {
def region = foldingModel.addFoldRegion(foldingInfo.start, foldingInfo.end, foldingInfo.placeholder)
region.expanded = false
}
}
def settings = CodeStyleSettingsManager.getInstance(myFixture.project).currentSettings.getCommonSettings(language)
def sectionRules = args.rules ? args.rules.collect { def section ->
section instanceof ArrangementSectionRule ? section : ArrangementSectionRule.create(section)} : [];
settings.arrangementSettings = new StdArrangementSettings(args.groups ?: [], sectionRules)
ArrangementEngine engine = ServiceManager.getService(myFixture.project, ArrangementEngine)
engine.arrange(myFixture.editor, myFixture.file, info.ranges);
// Check expectation.
info = parse(args.expected)
Assert.assertEquals(info.text, myFixture.editor.document.text)
info.foldings.each {
def foldRegion = foldingModel.getCollapsedRegionAtOffset(it.start)
assertNotNull("Expected to find fold region at offset ${it.start}", foldRegion)
assertEquals(it.end, foldRegion.endOffset)
}
}
@NotNull
private static def parse(@NotNull String text) {
def handlers = [:]
RICH_TEXT_HANDLERS.each { handlers["<${it.marker}"] = it }
def result = new Info()
def buffer = new StringBuilder(text)
int offset = 0
int richTextMarkStart = -1
RichTextHandler handler = null
while (offset < buffer.length()) {
handlers.each { String key, RichTextHandler value ->
int i = buffer.indexOf(key, offset)
if (i >= 0 && (handler == null || i < richTextMarkStart)) {
richTextMarkStart = i
handler = value
}
}
if (handler) {
int openingTagEndOffset = buffer.indexOf('>', richTextMarkStart)
int openTagLength = openingTagEndOffset - richTextMarkStart + 1
def attributes = parseAttributes(buffer.substring(1 + richTextMarkStart + handler.marker.length(), openingTagEndOffset))
def closingTag = "</${handler.marker}>"
int closingTagStart = buffer.indexOf(closingTag)
assert closingTagStart > 0
int closingTagLength = 3 + handler.marker.length() // </marker>
handler.handle(result, attributes, richTextMarkStart, closingTagStart - openTagLength)
buffer.delete(closingTagStart, closingTagStart + closingTagLength)
buffer.delete(richTextMarkStart, openingTagEndOffset + 1)
offset = closingTagStart - openTagLength
richTextMarkStart = -1
handler = null
}
else {
break
}
}
result.text = buffer.toString()
result
}
@NotNull
private static Map<String, String> parseAttributes(@NotNull String text) {
def result = [:]
(text =~ /([^\s]+)=([^\s]+)/).each {
result[it[1]] = it[2]
}
result
}
}