<NME> ElmHighlightingPerformanceTest.kt
<BEF> /*
The MIT License (MIT)

Derived from intellij-rust
Copyright (c) 2015 Aleksey Kladov, Evgeny Kurbatsky, Alexey Kudinkin and contributors
Copyright (c) 2016 JetBrains

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */

package org.elmPerformanceTests

import com.intellij.psi.util.PsiModificationTracker
import org.elm.lang.core.psi.descendantsOfType
import org.elm.lang.core.resolve.ElmReferenceElement
import org.elm.utils.Timings

class ElmHighlightingPerformanceTest : ElmRealProjectTestBase() {

    // It is a performance test, but we will measure performance ourselves
    override fun isPerformanceTest(): Boolean = false

    fun `test highlighting elm-json-tree-view`() =
            repeatTest { highlightProjectFile(JSON_TREE_VIEW, "src/JsonTree.elm") }

    fun `test highlighting elm-spa-example`() =
            repeatTest { highlightProjectFile(SPA, "src/Page/Article/Editor.elm") }

    fun `test highlighting elm-css`() =
            repeatTest { highlightProjectFile(ELM_CSS, "src/Css/Transitions.elm") }

    fun `test highlighting elm-list-extra`() =
            repeatTest { highlightProjectFile(LIST_EXTRA, "src/List/Extra.elm") }

    fun `test highlighting elm-dev-tools`() =
            repeatTest { highlightProjectFile(DEV_TOOLS, "src/Browser/DevTools/Main.elm") }

    private fun repeatTest(f: () -> Timings) {
        var result = Timings()
        println("${name.substring("test ".length)}:")
        repeat(10) {
            result = result.merge(f())
            tearDown()
            setUp()
        }
        result.report()
    }

    private fun highlightProjectFile(info: RealProjectInfo, filePath: String): Timings {
        val timings = Timings()
        val base = openRealProject(info) ?: return timings

        myFixture.configureFromTempProjectFile(filePath)

        val modificationCount = currentPsiModificationCount()

        val refs = timings.measure("collecting") {
            myFixture.file.descendantsOfType<ElmReferenceElement>()
        }

        timings.measure("resolve") {
            refs.forEach { it.reference.resolve() }
        }
        timings.measure("highlighting") {
            myFixture.doHighlighting()
        }

        check(modificationCount == currentPsiModificationCount()) {
            "PSI changed during resolve and highlighting, resolve might be double counted"
        }

        timings.measure("resolve_cached") {
            refs.forEach { it.reference.resolve() }
        }

        return timings
    }

    private fun currentPsiModificationCount() =
            PsiModificationTracker.SERVICE.getInstance(project).modificationCount
}

<MSG> Add color swatch line markers (#569)

This PR adds a marker to lines containing expressions with colors. Clicking the marker will open a color picker, and chossing a color from that picker will update the expression, keeping whatever format and units currently exists.

I added support for colors in two formats:

## String literals

And string that contains a CSS hex, hsl(a), or rgb(a) color will work. The string can contain other characters. I chose not to limit this to arguments to know functions, since these formats are fairly unique, so it seems that the risk of false positives are low. On the other hand, because I didn't limit which strings can contain a color, I chose not to support CSS color keywords such as `"red"` or `"rebeccapurple"`, since I think it would lead to noticeable false-positives. Like all decisions, this is open for discussion.

## Function calls

I support a specific set of functions when called with number literals. I do not attempt to resolve references if the arguments to these functions are in a variable or other expression. I support most of the color functions from [elm-css](https://package.elm-lang.org/packages/rtfeldman/elm-css/latest/Css#rgb) and [elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/Element#rgb).

The following functions are supported:

- `rgb`, with either `Int` arguments (like in elm-css) or `Float` arguments (like elm-ui)
- `rgba`
- `rgb255`
- `rgba255`
- `hsl`
- `hsla`

The marker provider only looks at the function name and arguments. This mean we don't need to resolve any references in the marker provider, and allows us to support identical functions in other libraries (there are a number of Elm packages that copy these functions).

## Limitations

### Multiple colors in a string

Even though IntelliJ supports multiple color markers per line, due to the way `ElementColorProvider` is designed, each PsiElement can only have one color. This prevents us from showing more than one marker for each string literal, even if the colors are on separate lines.

A possible workaround would be to add a color to the `OPEN_QUOTE` and `CLOSE_QUOTE` elements in addition to the existing `REGULAR_STRING_PART` element. This would allow us to have at least three colors per string literal.

### Rounding

When a user chooses a color from the picker, floating point number might not match the chosen color exactly. `ElementColorProvider` gives us the user's choice as a `java.awt.Color`, which only has 8-bit color channels (including alpha) rather than floating point numbers.

For example, if the user chooses an alpha of `0.5`, `java.awt.Color` rounds this to 127, which is `0.498 * 255`, so we have no way of knowing that the user actually selected 0.5 instead. I'm open to suggestions on how to improve this situation.
<DFF> @@ -64,7 +64,7 @@ class ElmHighlightingPerformanceTest : ElmRealProjectTestBase() {
 
     private fun highlightProjectFile(info: RealProjectInfo, filePath: String): Timings {
         val timings = Timings()
-        val base = openRealProject(info) ?: return timings
+        openRealProject(info) ?: return timings
 
         myFixture.configureFromTempProjectFile(filePath)
 

Add color swatch line markers (#569)