mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
[vcs-log] highlight text matches in the table
GitOrigin-RevId: 87239c349bfbad159803eef9296691adadc4022a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
3935eedbdd
commit
9a6a8736af
@@ -758,6 +758,8 @@ vcs.log.copy.filters.to.new.tab=true
|
||||
vcs.log.copy.filters.to.new.tab.description=If enabled, "Open New Vcs Log Tab" action will copy active filters to the new tab.
|
||||
vcs.log.filter.text.on.the.fly=false
|
||||
vcs.log.filter.text.on.the.fly.description=If enabled, applies text filter to the Log while typing
|
||||
vcs.log.filter.text.highlight.matches=false
|
||||
vcs.log.filter.text.highlight.matches.description=Highlight text filter matches in the Log table
|
||||
vcs.log.max.changes.shown=50000
|
||||
vcs.log.max.changes.shown.description=Limit for showing commits in the changes view.
|
||||
vcs.log.max.branches.shown=100
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.vcs.log.ui.render;
|
||||
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
|
||||
import com.intellij.openapi.vcs.changes.ui.CurrentBranchComponent;
|
||||
@@ -10,6 +11,8 @@ import com.intellij.ui.scale.JBUIScale;
|
||||
import com.intellij.ui.speedSearch.SpeedSearchUtil;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.StartupUiUtil;
|
||||
import com.intellij.vcs.log.VcsLogFilterCollection;
|
||||
import com.intellij.vcs.log.VcsLogTextFilter;
|
||||
import com.intellij.vcs.log.VcsRef;
|
||||
import com.intellij.vcs.log.data.VcsLogData;
|
||||
import com.intellij.vcs.log.graph.EdgePrintElement;
|
||||
@@ -21,6 +24,7 @@ import com.intellij.vcs.log.ui.table.VcsLogCellRenderer;
|
||||
import com.intellij.vcs.log.ui.table.VcsLogGraphTable;
|
||||
import com.intellij.vcs.log.ui.table.column.Commit;
|
||||
import com.intellij.vcs.log.ui.table.column.VcsLogColumnManager;
|
||||
import com.intellij.vcs.log.visible.filters.VcsLogTextFilterWithMatches;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -239,6 +243,13 @@ public class GraphCommitCellRenderer extends TypeSafeTableCellRenderer<GraphComm
|
||||
private void appendText(@NotNull GraphCommitCell cell, @NotNull SimpleTextAttributes style, boolean isSelected) {
|
||||
myIssueLinkRenderer.appendTextWithLinks(StringUtil.replace(cell.getText(), "\t", " ").trim(), style);
|
||||
SpeedSearchUtil.applySpeedSearchHighlighting(myGraphTable, this, false, isSelected);
|
||||
if (Registry.is("vcs.log.filter.text.highlight.matches")) {
|
||||
VcsLogTextFilter textFilter = myGraphTable.getModel().getVisiblePack().getFilters().get(VcsLogFilterCollection.TEXT_FILTER);
|
||||
if (textFilter instanceof VcsLogTextFilterWithMatches textFilterWithMatches) {
|
||||
String text = getCharSequence(false).toString();
|
||||
SpeedSearchUtil.applySpeedSearchHighlighting(this, textFilterWithMatches.matchingRanges(text), isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getAvailableWidth(int column, int graphWidth) {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.vcs.log.visible.filters
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.vcs.log.VcsLogDetailsFilter
|
||||
import com.intellij.vcs.log.VcsLogTextFilter
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
/**
|
||||
* @see VcsLogFilterObject.fromPattern
|
||||
*/
|
||||
internal data class VcsLogTextFilterImpl(private val text: String,
|
||||
private val isMatchCase: Boolean) : VcsLogDetailsFilter, VcsLogTextFilter {
|
||||
private val isMatchCase: Boolean) : VcsLogDetailsFilter, VcsLogTextFilterWithMatches {
|
||||
|
||||
override fun matches(message: String): Boolean = message.contains(text, !isMatchCase)
|
||||
|
||||
@@ -19,6 +19,18 @@ internal data class VcsLogTextFilterImpl(private val text: String,
|
||||
|
||||
override fun matchesCase(): Boolean = isMatchCase
|
||||
|
||||
override fun matchingRanges(message: String): Iterable<TextRange> {
|
||||
return generateSequence({ findMatchingRange(message, null) }) {
|
||||
lastRange -> findMatchingRange(message, lastRange)
|
||||
}.asIterable()
|
||||
}
|
||||
|
||||
private fun findMatchingRange(message: String, previousRange: TextRange?): TextRange? {
|
||||
val startIndex = previousRange?.endOffset ?: 0
|
||||
val startOffset = message.indexOf(text, startIndex, !isMatchCase).takeIf { it >= 0 } ?: return null
|
||||
return TextRange(startOffset, startOffset + text.length)
|
||||
}
|
||||
|
||||
@NonNls
|
||||
override fun toString(): String {
|
||||
return "containing '$text' ${caseSensitiveText()}"
|
||||
|
||||
@@ -2,16 +2,33 @@
|
||||
package com.intellij.vcs.log.visible.filters
|
||||
|
||||
import com.intellij.openapi.util.Comparing
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.vcs.log.VcsLogDetailsFilter
|
||||
import com.intellij.vcs.log.VcsLogTextFilter
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@ApiStatus.Experimental
|
||||
interface VcsLogTextFilterWithMatches : VcsLogTextFilter {
|
||||
override fun matches(message: String): Boolean {
|
||||
return matchingRanges(message).iterator().hasNext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text ranges for matches in the specified commit message.
|
||||
*
|
||||
* @param message a commit message to match
|
||||
* @return an Iterable containing text ranges for matches
|
||||
*/
|
||||
fun matchingRanges(message: String): Iterable<TextRange>
|
||||
}
|
||||
|
||||
/**
|
||||
* @see VcsLogFilterObject.fromPattern
|
||||
*/
|
||||
internal data class VcsLogRegexTextFilter(private val pattern: Pattern) : VcsLogDetailsFilter, VcsLogTextFilter {
|
||||
internal data class VcsLogRegexTextFilter(private val pattern: Pattern) : VcsLogDetailsFilter, VcsLogTextFilterWithMatches {
|
||||
override fun matches(message: String): Boolean = pattern.matcher(message).find()
|
||||
|
||||
override fun getText(): String = pattern.pattern()
|
||||
@@ -20,6 +37,10 @@ internal data class VcsLogRegexTextFilter(private val pattern: Pattern) : VcsLog
|
||||
|
||||
override fun matchesCase(): Boolean = (pattern.flags() and Pattern.CASE_INSENSITIVE) == 0
|
||||
|
||||
override fun matchingRanges(message: String): Iterable<TextRange> {
|
||||
return Iterable { pattern.matcher(message).results().map { TextRange(it.start(), it.end()) }.iterator() }
|
||||
}
|
||||
|
||||
@NonNls
|
||||
override fun toString(): String {
|
||||
return "matching '$text' ${caseSensitiveText()}"
|
||||
@@ -30,7 +51,7 @@ internal data class VcsLogRegexTextFilter(private val pattern: Pattern) : VcsLog
|
||||
* @see VcsLogFilterObject.fromPatternsList
|
||||
*/
|
||||
internal class VcsLogMultiplePatternsTextFilter(val patterns: List<String>,
|
||||
private val isMatchCase: Boolean) : VcsLogDetailsFilter, VcsLogTextFilter {
|
||||
private val isMatchCase: Boolean) : VcsLogDetailsFilter, VcsLogTextFilterWithMatches {
|
||||
override fun getText(): String = if (patterns.size == 1) patterns.single() else patterns.joinToString("|") { Pattern.quote(it) }
|
||||
|
||||
override fun isRegex(): Boolean = patterns.size > 1
|
||||
@@ -39,6 +60,27 @@ internal class VcsLogMultiplePatternsTextFilter(val patterns: List<String>,
|
||||
|
||||
override fun matches(message: String): Boolean = patterns.any { message.contains(it, !isMatchCase) }
|
||||
|
||||
override fun matchingRanges(message: String): Iterable<TextRange> {
|
||||
return generateSequence({ findNextMatch(message, null) }) { previousRange ->
|
||||
findNextMatch(message, previousRange)
|
||||
}.asIterable()
|
||||
}
|
||||
|
||||
private fun findNextMatch(message: String, previousRange: TextRange?): TextRange? {
|
||||
val startIndex = previousRange?.endOffset ?: 0
|
||||
|
||||
var match: TextRange? = null
|
||||
for (pattern in patterns) {
|
||||
val patternIndex = message.indexOf(pattern, startIndex, !isMatchCase)
|
||||
if (patternIndex < 0) continue
|
||||
|
||||
if (match == null || patternIndex <= match.startOffset) {
|
||||
match = TextRange(patternIndex, patternIndex + pattern.length)
|
||||
}
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
@NonNls
|
||||
override fun toString(): String {
|
||||
return "containing at least one of the ${patterns.joinToString(", ") { s -> "'$s'" }} ${caseSensitiveText()}"
|
||||
|
||||
Reference in New Issue
Block a user