Skip to content

Commit

Permalink
Add ability to set the highlightedyank foreground color
Browse files Browse the repository at this point in the history
  • Loading branch information
jphalip authored and lippfi committed Nov 22, 2024
1 parent 365b58e commit 5db2984
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 17 deletions.
5 changes: 4 additions & 1 deletion doc/IdeaVim Plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,10 @@ If you want to optimize highlight duration, assign a time in milliseconds:

If you want to change background color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`


If you want to change text color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"`

https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt

</details>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ private val HIGHLIGHT_DURATION_VARIABLE_NAME = "highlightedyank_highlight_durati

@NonNls
private val HIGHLIGHT_COLOR_VARIABLE_NAME = "highlightedyank_highlight_color"

@NonNls
private val HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME = "highlightedyank_highlight_foreground_color"

private var defaultHighlightTextColor: Color? = null

private fun getDefaultHighlightTextColor(): Color {
Expand Down Expand Up @@ -77,6 +81,9 @@ internal class HighlightColorResetter : LafManagerListener {
* if you want to change background color of highlight you can provide the rgba of the color you want e.g.
* let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"
*
* if you want to change text color of highlight you can provide the rgba of the color you want e.g.
* let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"
*
* When a new text is yanked or user starts editing, the old highlighting would be deleted.
*/
internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeListener {
Expand Down Expand Up @@ -186,13 +193,15 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
highlighters.clear()
}

private fun getHighlightTextAttributes(editor: Editor) = TextAttributes(
null,
extractUsersHighlightColor(),
editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
EffectType.SEARCH_MATCH,
Font.PLAIN,
)
private fun getHighlightTextAttributes(editor: Editor): TextAttributes {
return TextAttributes(
extractUserHighlightForegroundColor(),
extractUsersHighlightColor(),
editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
EffectType.SEARCH_MATCH,
Font.PLAIN,
)
}

private fun extractUsersHighlightDuration(): Int {
return extractVariable(HIGHLIGHT_DURATION_VARIABLE_NAME, DEFAULT_HIGHLIGHT_DURATION) {
Expand All @@ -205,17 +214,54 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
}

private fun extractUsersHighlightColor(): Color {
return extractVariable(HIGHLIGHT_COLOR_VARIABLE_NAME, getDefaultHighlightTextColor()) { value ->
val rgba = value.asString()
.substring(4)
.filter { it != '(' && it != ')' && !it.isWhitespace() }
.split(',')
.map { it.toInt() }

Color(rgba[0], rgba[1], rgba[2], rgba[3])
val value = VimPlugin.getVariableService().getGlobalVariableValue(HIGHLIGHT_COLOR_VARIABLE_NAME)
if (value != null) {
return try {
parseRgbaColor(value.asString())
} catch (e: Exception) {
@VimNlsSafe val message = MessageHelper.message(
"highlightedyank.invalid.value.of.0.1",
"g:$HIGHLIGHT_COLOR_VARIABLE_NAME",
e.message ?: "",
)
VimPlugin.showMessage(message)
getDefaultHighlightTextColor()
}
}
return getDefaultHighlightTextColor()
}

private fun extractUserHighlightForegroundColor(): Color? {
val value = VimPlugin.getVariableService().getGlobalVariableValue(HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME)
?: return null

return try {
parseRgbaColor(value.asString())
} catch (e: Exception) {
@VimNlsSafe val message = MessageHelper.message(
"highlightedyank.invalid.value.of.0.1",
"g:$HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME",
e.message ?: "",
)
VimPlugin.showMessage(message)
null
}
}

private fun parseRgbaColor(colorString: String): Color {
val rgba = colorString
.substring(4)
.filter { it != '(' && it != ')' && !it.isWhitespace() }
.split(',')
.map { it.toInt() }

if (rgba.size != 4 || rgba.any { it < 0 || it > 255 }) {
throw IllegalArgumentException("Invalid RGBA values. Each component must be between 0 and 255")
}

return Color(rgba[0], rgba[1], rgba[2], rgba[3])
}

private fun <T> extractVariable(variable: String, default: T, extractFun: (value: VimDataType) -> T): T {
val value = VimPlugin.getVariableService().getGlobalVariableValue(variable)
if (value != null) {
Expand All @@ -236,4 +282,4 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
return default
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package org.jetbrains.plugins.ideavim.extension.highlightedyank

import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.util.TextRange
import com.maddyhome.idea.vim.VimPlugin
Expand Down Expand Up @@ -182,6 +183,84 @@ class VimHighlightedYankTest : VimTestCase() {
}
}

@Test
fun `test no foreground color used when not explicitly provided`() {
configureByText(code)
typeText("yy")

val attributes = getFirstHighlighter().getTextAttributes(null)
assertEquals(null, attributes?.foregroundColor)
}

@Test
fun `test custom foreground colour used when provided by user`() {
configureByText(code)
enterCommand("let g:highlightedyank_highlight_foreground_color=\"rgba(100,10,20,30)\"")
typeText("yy")

val highlighter = getFirstHighlighter()
assertEquals(Color(100, 10, 20, 30), highlighter.getTextAttributes(null)?.foregroundColor)
}

@Test
fun `test indicating error when incorrect background highlight color was provided by user`() {
configureByText(code)

listOf(
"rgba(1,2,3)",
"rgba(1, 2, 3, 0.1)",
"rgb(1,2,3)",
"rgba(260, 2, 5, 6)",
"rgba(0, 0, 0, 300)"
).forEach { color ->
enterCommand("let g:highlightedyank_highlight_color = \"$color\"")
typeText("yy")

assertTrue(
VimPlugin.getMessage().contains("highlightedyank: Invalid value of g:highlightedyank_highlight_color"),
color,
)
// Should fall back to default background color when there's an error
val defaultColor = EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES.defaultAttributes.backgroundColor
assertEquals(defaultColor, getFirstHighlighter().getTextAttributes(null)?.backgroundColor)
}
}

@Test
fun `test indicating error when incorrect foreground highlight color was provided by user`() {
configureByText(code)

listOf(
"rgba(1,2,3)",
"rgba(1, 2, 3, 0.1)",
"rgb(1,2,3)",
"rgba(260, 2, 5, 6)",
"rgba(0, 0, 0, 300)"
).forEach { color ->
enterCommand("let g:highlightedyank_highlight_foreground_color = \"$color\"")
typeText("yy")

assertTrue(
VimPlugin.getMessage().contains("highlightedyank: Invalid value of g:highlightedyank_highlight_foreground_color"),
color,
)
// Should not set a foreground color when there's an error
assertEquals(null, getFirstHighlighter().getTextAttributes(null)?.foregroundColor)
}
}

@Test
fun `test both foreground and background colors can be set simultaneously`() {
configureByText(code)
enterCommand("let g:highlightedyank_highlight_foreground_color=\"rgba(255,0,0,255)\"")
enterCommand("let g:highlightedyank_highlight_color=\"rgba(0,255,0,128)\"")
typeText("yy")

val attributes = getFirstHighlighter().getTextAttributes(null)
assertEquals(Color(255, 0, 0, 255), attributes?.foregroundColor)
assertEquals(Color(0, 255, 0, 128), attributes?.backgroundColor)
}

private val code = """
fun ${c}sum(x: Int, y: Int, z: Int): Int {
return x + y + z
Expand Down

0 comments on commit 5db2984

Please sign in to comment.