Skip to content

Commit

Permalink
Merge pull request #95 from witspirit/1672
Browse files Browse the repository at this point in the history
v1.67.2
  • Loading branch information
picimako authored Dec 28, 2024
2 parents 7a3e28e + 6ad27e6 commit f3f4505
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 32 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## [Unreleased]

## [1.67.2]
### Changed
- Added a minor performance improvement to pattern variant building.

### Fixed
- [#94](https://github.com/witspirit/IntelliJBehave/issues/94): Improved matching steps with patterns with empty parameter values.

## [1.67.1]
### Fixed
- Fixed the K2 compiler compatibility configuration.
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = com.github.kumaraman21.intellijbehave
pluginName = JBehave Support
pluginRepositoryUrl = https://github.com/witspirit/IntelliJBehave
# SemVer format -> https://semver.org
pluginVersion = 1.67.1
pluginVersion = 1.67.2

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 242.21829.142
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public class PatternVariantBuilder {
* Regular expression that locates patterns to be evaluated in the input
* pattern.
*/
private static final Pattern REGEX = Pattern.compile("([^\\n{]*+)(\\{(([^|}]++)(\\|)?+)*+\\})([^\\n]*+)");
private static final Pattern REGEX = Pattern.compile("([^\\n{]*+)(\\{(([^|}]++)(\\|)?+)*+})([^\\n]*+)");

private final Set<String> variants;

Expand Down Expand Up @@ -143,7 +143,7 @@ private Set<String> variantsFor(String input) {
}

// isolate the pattern itself, removing its wrapping {}
String patternGroup = m.group(2).replaceAll("[\\{\\}]", "");
String patternGroup = m.group(2).replaceAll("[{}]", "");

// split the pattern into its options and add an empty
// string if it ends with a separator
Expand All @@ -152,10 +152,10 @@ private Set<String> variantsFor(String input) {
patternParts.add("");
}

// Store current invocation's results
Set<String> variants = new HashSet<>(8);

if (!patternParts.isEmpty()) {
// Store current invocation's results
Set<String> variants = new HashSet<>(8);

// isolate the part before the first pattern
String head = m.group(1);

Expand All @@ -164,23 +164,27 @@ private Set<String> variantsFor(String input) {

var variantsForTail = variantsFor(tail);

// Iterate over the current pattern's
// variants and construct the result.
for (String part : patternParts) {
var partString = head != null ? head + part : part;

// recurse on the tail of the input
// to handle the next pattern

// append all variants of the tail end
// and add each of them to the part we have
// built up so far.
for (String tailVariant : variantsForTail) {
variants.add(partString + tailVariant);
if (!variantsForTail.isEmpty()) {
// Iterate over the current pattern's
// variants and construct the result.
for (String part : patternParts) {
var partString = head != null ? head + part : part;

// recurse on the tail of the input
// to handle the next pattern

// append all variants of the tail end
// and add each of them to the part we have
// built up so far.
for (String tailVariant : variantsForTail) {
variants.add(partString + tailVariant);
}
}
}

return variants;
}
return variants;
return Collections.emptySet();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,15 @@
import static org.apache.commons.lang3.StringUtils.trim;

public class JBehaveStep extends ASTWrapperPsiElement {
private StepType stepType;
private final StepType stepType;

public JBehaveStep(@NotNull ASTNode node, StepType stepType) {
super(node);
this.stepType = stepType;
}

@Override
@NotNull
public PsiReference[] getReferences() {
public PsiReference @NotNull [] getReferences() {
return ReadAction.compute(() -> ReferenceProvidersRegistry.getReferencesFromProviders(this));
}

Expand All @@ -50,6 +49,10 @@ public ASTNode getKeyword() {
return getNode().findChildByType(StoryTokenType.STEP_TYPES);
}

/**
* Returns the step text without the step type. For example:
* for the step {@code Given I opened the homepage} it returns {@code I opened the homepage}.
*/
public String getStepText() {
int offset = getStepTextOffset();
String text = getText();
Expand All @@ -61,7 +64,11 @@ public String getStepText() {
}
}

@Nullable
/**
* Returns the step type keyword, for example {@code Given} for the step
* {@code Given I opened the homepage}.
*/
@Nullable("When the keyword is null.")
public String getActualStepPrefix() {
ASTNode keyword = getKeyword();
if (keyword == null) { // that's weird!
Expand All @@ -70,6 +77,12 @@ public String getActualStepPrefix() {
return keyword.getText();
}

/**
* Returns the off set the step text after the step type keyword. For example:
* for {@code Given I opened the homepage}, it would return 6.
* <p>
* Returns 0 when the step type prefix is null.
*/
public int getStepTextOffset() {
String stepPrefix = getActualStepPrefix();
return stepPrefix != null ? stepPrefix.length() + 1 : 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ internal class OptimizedStepMatcher(stepMatcher: StepMatcher) : StepMatcher by s
init {
val patterns = stepMatcher.pattern().resolved().split(StepPatterns.Plain.VARIABLE)
fun List<String>.toRegexes() = asSequence()
.map { it.replace(StepPatterns.Regex.SPACE, "\\s") }
.map(::Regex)
.toList()
//Replaces sequences of spaces with single \s symbols. This is in line with the trimming
// of space in the 'matches()' function below.
.map { it.replace(StepPatterns.Regex.SPACE, "\\s") }
.map(::Regex)
.toList()

matchesRegexes = patterns.toRegexes().mapIndexed { i, regex ->
when (i) {
Expand All @@ -39,12 +41,20 @@ internal class OptimizedStepMatcher(stepMatcher: StepMatcher) : StepMatcher by s

//TODO implement catching optimization strategy

fun matches(text: String): Boolean = text.trimSpaces().find(matchesRegexes)
fun matches(text: String): Boolean = text.trimSpaces().find()

private fun String.find(regexes: List<Regex>, textIndex: Int = 0, regexIndex: Int = 0)
: Boolean = regexIndex == regexes.size || textIndex < length && run {
val matchResult = regexes[regexIndex].find(this, textIndex)
matchResult != null && find(regexes, matchResult.range.endInclusive + 1, regexIndex + 1)
private fun String.find(textIndex: Int = 0, regexIndex: Int = 0): Boolean {
return if (regexIndex == matchesRegexes.size) true
else if (textIndex < length) doFind(textIndex, regexIndex)
// This helps to solve the case when the parameter is right at the end of the step pattern,
// e.g. '@When("a user$thing")' and the parameter is passed an empty value.
// In this case the remaining regex pattern would be a single $ sign which has to be matched against an empty string.
else textIndex == length
}

private fun String.doFind(textIndex: Int, regexIndex: Int): Boolean {
val matchResult = matchesRegexes[regexIndex].find(this, textIndex)
return matchResult != null && find(matchResult.range.last + 1, regexIndex + 1)
}
}

Expand Down
Loading

0 comments on commit f3f4505

Please sign in to comment.