<|start_filename|>plugin/src/main/kotlin/com/github/droibit/plugin/truth/postfix/TruthPostfixTemplateProvider.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix

import com.github.droibit.plugin.truth.postfix.template.AssertAboutTemplate
import com.github.droibit.plugin.truth.postfix.template.AssertThatTemplate
import com.github.droibit.plugin.truth.postfix.template.AssertWithMessageTemplate
import com.intellij.codeInsight.template.postfix.templates.JavaPostfixTemplateProvider
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate

/**
 * @author kumagai
 */
class TruthPostfixTemplateProvider: JavaPostfixTemplateProvider {

    private val templates: Set<PostfixTemplate>

    constructor() {
        templates = hashSetOf(
                AssertThatTemplate(),
                AssertWithMessageTemplate(),
                AssertAboutTemplate()
        )
    }

    override fun getTemplates() = templates
}

<|start_filename|>example/src/test/java/com/github/droibit/plugin/truth/postfix/example/TruthPostfixTest.java<|end_filename|>
package com.github.droibit.plugin.truth.postfix.example;

import com.github.droibit.plugin.truth.postfix.example.delegation.Foo;

import org.junit.Test;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.util.Arrays.asList;
import static org.junit.Assert.fail;

/**
 * @author kumagai
 */
public class TruthPostfixTest {

    private int field = 1;

    @Test
    public void assertThatWithInt() {
        int actual = 100;
        assertThat(actual).isEqualTo(100);
    }

    @Test
    public void assertThatWithIntField() {
        assertThat(field).isEqualTo(1);
    }

    @Test
    public void assertThatWithIntLiteral() {
        assertThat(100).isEqualTo(100);
    }

    @Test
    public void assertThatWithFloat() {
        float actual = 100.0f;
        assertThat(actual).isWithin(100.0f);
    }

    @Test
    public void assertThatWithDouble() {
        double actual = 100.0;
        assertThat(actual).isWithin(100);
    }

    @Test
    public void assertThatWithStringMethod() {
        String actual = "hoge";
        assertThat(actual.isEmpty()).isFalse();
    }

    @Test
    public void truthAssertWithMessage() {
        try {
            assertWithMessage("custom msg").that(asList(1, 2, 3)).contains(5);
            fail("Should have thrown.");
        } catch (AssertionError e) {
            assertThat(e).hasMessage("custom msg: <[1, 2, 3]> should have contained <5>");
        }
    }

    @Test
    public void assertThatWithDefinedClass() {
        Foo foo = new Foo(100);
        assertThat(foo).isNotNull();
    }
}


<|start_filename|>example/src/test/java/com/github/droibit/plugin/truth/postfix/example/delegation/DelegationTest.java<|end_filename|>
package com.github.droibit.plugin.truth.postfix.example.delegation;

import org.junit.Test;

import static com.github.droibit.plugin.truth.postfix.example.delegation.FooSubject.foo;
import static com.google.common.truth.Truth.assertAbout;

/**
 * @author kumagai
 */
public class DelegationTest {

    @Test
    public void assertAboutWithFoo() {
        assertAbout(foo()).that(new Foo(5)).matches(new Foo(5));
    }
}


<|start_filename|>plugin/src/main/kotlin/com/github/droibit/plugin/truth/postfix/template/selector/JavaSelectorConditions.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix.template.selector

import com.intellij.openapi.util.Condition
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiType
import com.intellij.psi.util.InheritanceUtil

object JavaSelectorConditions {

    private val JAVA_LANG_CHAR_SEQUENCE = "java.lang.CharSequence"
    private val TRUTH_SUBJECT_FACTORY = "com.google.common.truth.SubjectFactory"

    @JvmField
    internal val IS_CHAR_SEQUENCE = Condition<PsiElement> { e ->
        InheritanceUtil.isInheritor(e.type, JAVA_LANG_CHAR_SEQUENCE)
    }

    @JvmField
    internal val IS_SUBJECT_FACTORY = Condition<PsiElement> { e ->
        InheritanceUtil.isInheritor(e.type, TRUTH_SUBJECT_FACTORY)
    }
}

private val PsiElement.type: PsiType?
    get() = if (this is PsiExpression) type else null

<|start_filename|>plugin/src/main/kotlin/com/github/droibit/plugin/truth/postfix/template/selector/KotlinAncestorSelectors.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix.template.selector

import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateExpressionSelectorBase
import com.intellij.openapi.editor.Document
import com.intellij.openapi.util.Condition
import com.intellij.openapi.util.Conditions
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtExpression

/**
 *  * [AncestorSelector.scala](https://goo.gl/2c7ZJl)
 *  * [ScalaPsiUtil.scala](https://goo.gl/bVBMGk)
 */
internal object KotlinAncestorSelectors {

    @JvmStatic
    fun selectorTopmost(condition: Condition<PsiElement>) = object: PostfixTemplateExpressionSelectorBase(condition) {

        override fun getFilters(offset: Int) = Conditions.and(super.getFilters(offset), psiErrorFilter)

        override fun getNonFilteredExpressions(context: PsiElement, document: Document, offset: Int): List<PsiElement> {
            val element = context.parentOfType(KtExpression::class.java)
            return when (element) {
                is KtExpression -> {
                    val result = arrayListOf(element)
                    var current = element.parent
                    while (current != null &&
                           current.textRange != null &&
                           current.textRange.endOffset <= offset &&
                           current is KtExpression) {
                        result.add(current)
                        current = current.parent
                    }
                    result
                }
                else -> emptyList()
            }
        }
    }
}

/**
 * [SelectorConditions.scala](https://goo.gl/Rf8Qp9)
 * [ScExpression.scala](https://goo.gl/a8EUD9)
 */
internal object KotlinSelectorConditions {

    @JvmField
    val ANY_EXPR = Condition<PsiElement> {
        it is KtExpression
    }
}

private fun PsiElement.parentOfType(vararg classes: Class<out PsiElement>): PsiElement {
    var current = this
    while (current != null && classes.find { it.isInstance(current) } == null) {
        current = current.parent
    }
    return current
}

<|start_filename|>plugin/src/main/kotlin/com/github/droibit/plugin/truth/postfix/utils/TemplateUtils.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix.utils

import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.caches.resolve.ModuleTestSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.getNullableModuleInfo

internal val TRUTH_CLASS_NAME = "com.google.common.truth.Truth"

internal fun getTemplateStringIfWithinTestModule(element: PsiElement, template: String): String? {
    return if (element.getNullableModuleInfo() is ModuleTestSourceInfo) template else null
}

<|start_filename|>plugin/src/main/kotlin/com/github/droibit/plugin/truth/postfix/template/KotlinAssertThatTemplate.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix.template

import com.github.droibit.plugin.truth.postfix.template.selector.KotlinAncestorSelectors.selectorTopmost
import com.github.droibit.plugin.truth.postfix.template.selector.KotlinSelectorConditions.ANY_EXPR
import com.github.droibit.plugin.truth.postfix.utils.TRUTH_CLASS_NAME
import com.github.droibit.plugin.truth.postfix.utils.getTemplateStringIfWithinTestModule
import com.intellij.codeInsight.template.Template
import com.intellij.codeInsight.template.TemplateManager
import com.intellij.codeInsight.template.postfix.templates.StringBasedPostfixTemplate
import com.intellij.psi.PsiElement

/**
 * @author kumagai
 */
class KotlinAssertThatTemplate : StringBasedPostfixTemplate(
        "assertThat",
        "assertThat(expr)",
        selectorTopmost(ANY_EXPR)) {

    // Ref: https://goo.gl/jpjA7w
    override fun getElementToRemove(expr: PsiElement) = expr

    override fun createTemplate(manager: TemplateManager, templateString: String): Template {
        return super.createTemplate(manager, templateString).apply {
            isToReformat = shouldReformat()
        }
    }

    override fun getTemplateString(psiElement: PsiElement): String? {
        return getTemplateStringIfWithinTestModule(element = psiElement,
                                                   template = "$TRUTH_CLASS_NAME.assertThat(\$expr\$)\$END\$")
    }
}

<|start_filename|>example/src/test/java/com/github/droibit/plugin/truth/postfix/example/delegation/FooSubject.java<|end_filename|>
/*
 * Copyright (c) 2011 Google, Inc.
 *
 * 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.github.droibit.plugin.truth.postfix.example.delegation;

import com.google.common.truth.FailureStrategy;
import com.google.common.truth.Subject;
import com.google.common.truth.SubjectFactory;

import java.util.Arrays;

/**
 * A simple example Subject to demonstrate extension.
 *
 * <p>Callers would call {@code assertAbout(foo()).that(foo).matches(bar);}
 * or {@code assertAbout(foo()).that(foo).matchesEither(bar, baz);}
 *
 * @author <NAME> (<EMAIL>)
 */
public class FooSubject extends Subject<FooSubject, Foo> {
    private static final SubjectFactory<FooSubject, Foo> FOO =
            new SubjectFactory<FooSubject, Foo>() {
                @Override
                public FooSubject getSubject(FailureStrategy fs, Foo target) {
                    return new FooSubject(fs, target);
                }
            };

    public static SubjectFactory<FooSubject, Foo> foo() {
        return FOO;
    }

    // Must be public and non-final for generated subclasses (wrappers)
    public FooSubject(FailureStrategy failureStrategy, Foo subject) {
        super(failureStrategy, subject);
    }

    public void matches(Foo expected) {
        if (getSubject().value != expected.value) {
            fail("matches", expected);
        }
    }

    public void matchesAny(Foo... expecteds) {
        for (Foo expected : expecteds) {
            if (getSubject().value == expected.value) {
                return;
            }
        }
        fail("matches", Arrays.asList(expecteds));
    }
}

<|start_filename|>plugin/src/main/kotlin/com/github/droibit/plugin/truth/postfix/template/AssertAboutTemplate.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix.template

import com.github.droibit.plugin.truth.postfix.template.selector.JavaSelectorConditions.IS_SUBJECT_FACTORY
import com.github.droibit.plugin.truth.postfix.utils.TRUTH_CLASS_NAME
import com.github.droibit.plugin.truth.postfix.utils.getTemplateStringIfWithinTestModule
import com.intellij.codeInsight.template.Template
import com.intellij.codeInsight.template.Template.Property.USE_STATIC_IMPORT_IF_POSSIBLE
import com.intellij.codeInsight.template.TemplateManager
import com.intellij.codeInsight.template.impl.MacroCallNode
import com.intellij.codeInsight.template.macro.SuggestVariableNameMacro
import com.intellij.codeInsight.template.postfix.templates.StringBasedPostfixTemplate
import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils.selectorTopmost
import com.intellij.psi.PsiElement

/**
 * @author kumagai
 */
class AssertAboutTemplate : StringBasedPostfixTemplate(
        "assertAbout",
        "assertAbout(expr).that(target)",
        selectorTopmost(IS_SUBJECT_FACTORY)) {

    override fun createTemplate(manager: TemplateManager, templateString: String): Template {
        return super.createTemplate(manager, templateString).apply {
            setValue(USE_STATIC_IMPORT_IF_POSSIBLE, true)
        }
    }

    override fun getTemplateString(psiElement: PsiElement): String? {
        return getTemplateStringIfWithinTestModule(element = psiElement,
                                                   template = "$TRUTH_CLASS_NAME.assertAbout(\$expr\$).that(\$target\$)\$END\$")
    }

    override fun setVariables(template: Template, element: PsiElement) {
        template.addVariable("target", MacroCallNode(SuggestVariableNameMacro()), false)
    }
}

<|start_filename|>example-kotlin/src/test/kotlin/com/github/droibit/plugin/truth/postfix/example/TruthPostfixTest.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix.example

import com.google.common.truth.Truth.assertThat
import org.junit.Test

/**
 * @author kumagai
 */
class TruthPostfixTest {

    private val field = 100

    @Test
    fun assertThatWithInt() {
        val actual = 100
        assertThat(actual).isEqualTo(100)
    }

    @Test
    fun assertThatWithIntField() {
        assertThat(field).isEqualTo(100)
    }

    @Test
    fun assertThatWithIntLiteral() {
        assertThat(100).isEqualTo(100)
    }

    @Test
    fun assertThatWithFloat() {
        val actual = 100f
        assertThat(actual).isWithin(100f)
    }

    @Test
    fun assertThatWithDouble() {
        val actual = 100.0
        assertThat(actual).isWithin(100.0)
    }

    @Test
    fun assertThatWithStringMethod() {
        val actual = "hoge"
        assertThat(actual.isNotEmpty()).isTrue()
    }

    @Test
    fun assertThatWithDefinedClass() {
        class Foo(val value: Int)
        val foo = Foo(value = 100)
        assertThat(foo).isNotNull()
    }
}

<|start_filename|>example/src/main/kotlin/com/github/droibit/plugin/truth/postfix/example/Main.java<|end_filename|>
package com.github.droibit.plugin.truth.postfix.example;

/**
 * @author kumagai
 */
public class Main {

    public static void main(String[] args) {
    }
}


<|start_filename|>example-kotlin/src/test/kotlin/com/github/droibit/plugin/truth/postfix/example/ImportTest.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix.example

import com.google.common.truth.Truth
import org.junit.Test

/**
 * @author kumagai
 */
class ImportTest {

    @Test
    fun test() {
        val actual = 1
        Truth.assertThat(actual).isEqualTo(10)
    }
}



<|start_filename|>example-kotlin/src/main/kotlin/com/github/droibit/plugin/truth/postfix/example/Maiin.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix.example

/**
 * @author kumagai
 */

fun main(vararg args: String) {
}


<|start_filename|>plugin/src/main/kotlin/com/github/droibit/plugin/truth/postfix/KotlinTruthPostfixTemplateProvider.kt<|end_filename|>
package com.github.droibit.plugin.truth.postfix

import com.github.droibit.plugin.truth.postfix.template.KotlinAssertThatTemplate
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiFile

/**
 * @author kumagai
 */
class KotlinTruthPostfixTemplateProvider : PostfixTemplateProvider {

    private val templates: Set<PostfixTemplate>

    constructor() {
        templates = hashSetOf(
                KotlinAssertThatTemplate()
        )
    }

    override fun getTemplates() = templates

    override fun isTerminalSymbol(currentChar: Char) = currentChar == '.' || currentChar == '!'

    override fun afterExpand(file: PsiFile, editor: Editor) { }

    override fun preCheck(copyFile: PsiFile, realEditor: Editor, currentOffset: Int) = copyFile

    override fun preExpand(file: PsiFile, editor: Editor) { }
}