Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
es.iti.wakamiti.lsp.internal.GherkinDocumentMap Maven / Gradle / Ivy
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package es.iti.wakamiti.lsp.internal;
import static java.util.stream.Collectors.toList;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import es.iti.wakamiti.core.gherkin.parser.GherkinDialect;
import es.iti.wakamiti.core.gherkin.parser.GherkinDialectProvider;
import es.iti.wakamiti.core.gherkin.parser.GherkinLanguageConstants;
import es.iti.wakamiti.core.gherkin.parser.*;
import es.iti.wakamiti.api.WakamitiConfiguration;
/*
* This class associates each parsed section of a Gherkin documento
* to the actual position in the file
*/
public class GherkinDocumentMap {
private static final List propertiesRequiringParsing = List.of(
WakamitiConfiguration.LANGUAGE,
WakamitiConfiguration.DATA_FORMAT_LANGUAGE,
WakamitiConfiguration.MODULES
);
private static final Pattern propertyPattern = Pattern.compile("\\s*+#*+\\s*+(\\S++)\\s*+:\\s*+(\\S++)\\s*+");
private static final Pattern tagPattern = Pattern.compile("(\\s*+@\\w++\\s*+)*+");
private static final GherkinDialectProvider dialectProvider = new GherkinDialectProvider();
private Locale locale;
private GherkinDialect dialect;
private TextDocument document;
public GherkinDocumentMap(String document) {
this.document = new TextDocument(document);
this.locale = extractProperty("language",this.document).map(Locale::new).orElse(Locale.ENGLISH);
this.dialect = dialectProvider.getDialect(locale);
}
public String rawContent() {
return document.rawText();
}
public TextDocument document() {
return document;
}
public GherkinDialect dialect() {
return dialect;
}
public Locale locale() {
return locale;
}
public boolean replace(TextRange range, String text) {
boolean requireParsing = false;
if (range.isSingleLine()) {
requireParsing = checkReplaceSingleLineRequireParsing(range);
} else {
requireParsing = true;
}
document.replaceRange(range,text);
return requireParsing;
}
public boolean checkReplaceSingleLineRequireParsing(TextRange range) {
if (document.isEmpty()) {
return true;
}
boolean requireParsing = false;
int lineNumber = range.startLine();
String rawLine = document.extractLine(lineNumber);
String stripLineContent = rawLine.strip();
boolean isProperty = stripLineContent.startsWith("#");
if (isProperty) {
var matcher = propertyPattern.matcher(stripLineContent);
if (matcher.matches() && propertiesRequiringParsing.contains(matcher.group(1))) {
requireParsing = true;
}
} else {
boolean isTag = tagPattern.matcher(stripLineContent).matches();
if (isTag) {
requireParsing = true;
} else {
TextRange keywordRange = detectKeyword(
lineNumber,
stripLineContent,
GherkinDialect::getKeywords
);
if (!keywordRange.isEmpty() && range.intersect(keywordRange)) {
requireParsing = true;
}
}
}
return requireParsing;
}
public TextRange detectStepKeyword(int lineNumber, String stripLineContent) {
return detectKeyword(
lineNumber,
stripLineContent,
GherkinDialect::getStepKeywords
);
}
public TextRange detectScenarioKeyword(int lineNumber, String stripLineContent) {
return detectKeyword(
lineNumber,
stripLineContent,
GherkinDialect::getScenarioKeywords,
GherkinDialect::getScenarioOutlineKeywords
);
}
@SafeVarargs
public final TextRange detectKeyword(
int lineNumber,
String stripLineContent,
Function>... keywordSets
) {
TextRange keywordRange = TextRange.of(0,0,0,0);
for (var keywordSet : keywordSets) {
for (String keyword : keywordSet.apply(dialect)) {
if (stripLineContent.startsWith(keyword)) {
keywordRange = TextRange.of(lineNumber,0,lineNumber,keyword.length());
break;
}
}
}
return keywordRange;
}
@SafeVarargs
public final boolean hasKeyword(
int lineNumber,
String stripLineContent,
Function>... keywordSets
) {
return !detectKeyword(lineNumber, stripLineContent, keywordSets).isEmpty();
}
public boolean isStep(int lineNumber, String stripLineContent) {
TextRange keywordRange = detectKeyword(
lineNumber,
stripLineContent,
GherkinDialect::getStepKeywords
);
if (!keywordRange.isEmpty()) {
String lastKeyword = lastKeyword(lineNumber-1);
return dialect.getFeatureContentKeywords().contains(lastKeyword);
} else {
return false;
}
}
public String removeKeyword(int lineNumber, String stripLineContent) {
var keywordRange = detectKeyword(
lineNumber,
stripLineContent,
GherkinDialect::getKeywords
);
if (keywordRange.isEmpty()) {
return stripLineContent;
}
return stripLineContent.substring(keywordRange.endLinePosition());
}
private String lastKeyword(int lineNumber) {
for (int i = lineNumber; i>=0; i--) {
String line = document.extractLine(i).stripLeading();
if (line.startsWith("#")) {
continue;
}
int position = line.indexOf(':');
if (position > -1) {
String keyword = line.substring(0, position);
if (dialect.getKeywords().contains(keyword)) {
return keyword;
}
}
}
return null;
}
public List followingKeywords(int lineNumber) {
String line = lastLineWithContent(lineNumber);
if (line == null) {
line = "";
}
line = line.stripLeading();
if (line.startsWith(GherkinLanguageConstants.DOCSTRING_SEPARATOR) ||
line.startsWith(GherkinLanguageConstants.DOCSTRING_ALTERNATIVE_SEPARATOR) ||
line.startsWith(GherkinLanguageConstants.TABLE_CELL_SEPARATOR)) {
return List.of();
}
String lastKeyword = lastKeyword(lineNumber);
List result;
if (lastKeyword == null) {
result = suffix(dialect.getFeatureKeywords(),":");
} else if (dialect.getFeatureKeywords().contains(lastKeyword)) {
result = suffix(dialect.getFeatureContentKeywords(),":");
} else if (dialect.getFeatureContentKeywords().contains(lastKeyword)) {
result = dialect.getStepKeywords();
} else {
result = List.of();
}
return result;
}
private String lastLineWithContent(int lineNumber) {
for (int i = lineNumber; i>=0; i--) {
String line = document.extractLine(i);
if (line.stripLeading().isEmpty()) {
continue;
}
return line;
}
return null;
}
private static List suffix(List values, String suffix) {
return values.stream().map(s -> s+suffix).collect(toList());
}
/*
public String lineContent(int lineNumber, LineRange range) {
return range.extractString(lines.get(lineNumber));
}
public boolean updateLine(int lineNumber, LineRange range, String delta) {
boolean requireParsing = false;
String strippedLineContent = lines.get(lineNumber).stripLeading();
boolean isProperty = strippedLineContent.startsWith("#");
if (isProperty) {
var matcher = propertyPattern.matcher(strippedLineContent);
if (matcher.matches() && propertiesRequiringParsing.contains(matcher.group(1))) {
requireParsing = true;
}
} else {
LineRange keyword = detectKeyword(strippedLineContent, dialect.getKeywords());
if (!keyword.isEmpty() && range.intersect(keyword)) {
requireParsing = true;
}
}
lines.set(lineNumber, range.replaceString(lines.get(lineNumber),delta));
if (delta.contains("\n")) {
rearrangeLines();
}
return requireParsing;
}
private void rearrangeLines() {
String raw = lines.stream().collect(Collectors.joining("\n"));
lines.clear();
lines.addAll(Arrays.asList(raw.split("\n")));
if (raw.endsWith("\n")) {
lines.add("");
}
}
public LineRange detectKeyword(String strippedLineContent, List keywords) {
LineRange keywordRange = LineRange.empty();
for (String keyword : keywords) {
if (strippedLineContent.startsWith(keyword)) {
keywordRange = LineRange.of(0,keyword.length());
break;
}
}
return keywordRange;
}
public Locale locale() {
return locale;
}
public GherkinDialect dialect() {
return dialect;
}
public String replaceDocumentSegment(TextRange range, String delta) {
StringBuilder document = new StringBuilder();
// pre-range lines
for (int i=0; i lines() {
return lines;
}
*/
private static Optional extractProperty(String property,TextDocument document) {
Pattern pattern = Pattern.compile("\\s*#*\\s*"+property+"\\s*:\\s*([^\\s]+)\\s*");
for (int l=0; l segmentsInLines(int startLine, int endLine, Pattern pattern, int regexGroup) {
List segments = new ArrayList<>();
for (int lineNumber = startLine; lineNumber <= endLine; lineNumber ++) {
segments.addAll(document.extractSegments(lineNumber,pattern,regexGroup));
}
return segments;
}
public List tagsInLines(int startLine, int endLine) {
return segmentsInLines(startLine, endLine, Pattern.compile("@(\\w+)"),1);
}
public TextRange lineRangeWithoutKeyword(int lineNumber, String keyword) {
String line = document.extractLine(lineNumber);
return TextRange.of(lineNumber, line.indexOf(keyword)+1, lineNumber, line.length());
}
}