mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
refactoring [text-matching]: convert tests to kotlin
GitOrigin-RevId: 906a9ab2cd4a2a65f7df4e02647d9e687534b5a8
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ecf232e443
commit
f2c2288fcf
-24
@@ -1,24 +0,0 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.util.text.matching;
|
||||
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.codeStyle.AllOccurrencesMatcher;
|
||||
import com.intellij.psi.codeStyle.MinusculeMatcher;
|
||||
import com.intellij.psi.codeStyle.NameUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
|
||||
|
||||
class AllOccurrencesMatcherTest {
|
||||
@Test
|
||||
void simpleCase() {
|
||||
MinusculeMatcher matcher = AllOccurrencesMatcher.create("*fooBar", NameUtil.MatchingCaseSensitivity.NONE, "");
|
||||
assertIterableEquals(matcher.matchingFragments("fooBarFooBar"), List.of(new TextRange(0, 6), new TextRange(6, 12)));
|
||||
assertIterableEquals(matcher.matchingFragments("fooBarFooBuzzBar"), List.of(new TextRange(0, 6), new TextRange(6, 9), new TextRange(13, 16)));
|
||||
assertIterableEquals(matcher.matchingFragments("fooBarBuzzFoo"), List.of(new TextRange(0, 6)));
|
||||
assertIterableEquals(matcher.matchingFragments("fooBarBuzzFooBuzzBar"), List.of(new TextRange(0, 6), new TextRange(10, 13), new TextRange(17, 20)));
|
||||
assertIterableEquals(matcher.matchingFragments("fooBuzzFooBuzzBar"), List.of(new TextRange(0, 3), new TextRange(14, 17)));
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.util.text.matching
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.codeStyle.AllOccurrencesMatcher
|
||||
import com.intellij.psi.codeStyle.NameUtil
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class AllOccurrencesMatcherTest {
|
||||
@Test
|
||||
fun simpleCase() {
|
||||
val matcher = AllOccurrencesMatcher.create("*fooBar", NameUtil.MatchingCaseSensitivity.NONE, "")
|
||||
assertEquals(matcher.matchingFragments("fooBarFooBar")?.toList(), listOf(TextRange(0, 6), TextRange(6, 12)));
|
||||
assertEquals(matcher.matchingFragments("fooBarFooBuzzBar")?.toList(), listOf(TextRange(0, 6), TextRange(6, 9), TextRange(13, 16)));
|
||||
assertEquals(matcher.matchingFragments("fooBarBuzzFoo")?.toList(), listOf(TextRange(0, 6)));
|
||||
assertEquals(matcher.matchingFragments("fooBarBuzzFooBuzzBar")?.toList(), listOf(TextRange(0, 6), TextRange(10, 13), TextRange(17, 20)));
|
||||
assertEquals(matcher.matchingFragments("fooBuzzFooBuzzBar")?.toList(), listOf(TextRange(0, 3), TextRange(14, 17)));
|
||||
}
|
||||
}
|
||||
+497
-500
File diff suppressed because it is too large
Load Diff
+44
-47
@@ -1,77 +1,74 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.util.text.matching;
|
||||
package com.intellij.platform.util.text.matching
|
||||
|
||||
import com.intellij.psi.codeStyle.NameUtil;
|
||||
import com.intellij.util.text.NameUtilCore;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.intellij.psi.codeStyle.NameUtil
|
||||
import com.intellij.util.text.NameUtilCore.isWordStart
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class NameUtilTest {
|
||||
class NameUtilTest {
|
||||
@Test
|
||||
public void testSplitIntoWords1() {
|
||||
assertSplitEquals(new String[]{"I", "Base"}, "IBase");
|
||||
fun testSplitIntoWords1() {
|
||||
assertSplitEquals(listOf("I", "Base"), "IBase")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitIntoWords2() {
|
||||
assertSplitEquals(new String[]{"Order", "Index"}, "OrderIndex");
|
||||
fun testSplitIntoWords2() {
|
||||
assertSplitEquals(listOf("Order", "Index"), "OrderIndex")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitIntoWords3() {
|
||||
assertSplitEquals(new String[]{"order", "Index"}, "orderIndex");
|
||||
fun testSplitIntoWords3() {
|
||||
assertSplitEquals(listOf("order", "Index"), "orderIndex")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitIntoWords4() {
|
||||
assertSplitEquals(new String[]{"Order", "Index"}, "Order_Index");
|
||||
fun testSplitIntoWords4() {
|
||||
assertSplitEquals(listOf("Order", "Index"), "Order_Index")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitIntoWords5() {
|
||||
assertSplitEquals(new String[]{"ORDER", "INDEX"}, "ORDER_INDEX");
|
||||
fun testSplitIntoWords5() {
|
||||
assertSplitEquals(listOf("ORDER", "INDEX"), "ORDER_INDEX")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitIntoWords6() {
|
||||
assertSplitEquals(new String[]{"gg", "J"}, "ggJ");
|
||||
fun testSplitIntoWords6() {
|
||||
assertSplitEquals(listOf("gg", "J"), "ggJ")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitIntoWordsCN() {
|
||||
assertSplitEquals(new String[]{"测", "试", "打", "补", "丁", "2"}, "测试打补丁2");
|
||||
fun testSplitIntoWordsCN() {
|
||||
assertSplitEquals(listOf("测", "试", "打", "补", "丁", "2"), "测试打补丁2")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitIntoWordsJP() {
|
||||
fun testSplitIntoWordsJP() {
|
||||
assertSplitEquals(
|
||||
new String[]{"ローマ", "由", "来", "の", "アルファベット", "(", "ラテン", "文", "字", ")", "を", "用", "いて", "日", "本", "語", "を", "表", "記", "することもでき", "、", "日", "本", "では", "ローマ", "字", "と", "呼", "ばれる"},
|
||||
"ローマ由来のアルファベット(ラテン文字)を用いて日本語を表記することもでき、日本ではローマ字と呼ばれる");
|
||||
//noinspection NonAsciiCharacters
|
||||
assertSplitEquals(new String[]{"近", "代", "では", "日", "本", "人", "が", "漢", "語", "を", "造", "語", "する", "例", "もあり", "、", "英", "語", "の", "philosophy", "、", "ドイツ", "語", "の", "Philosophie", "を", "指", "す", "用", "語"},
|
||||
"近代では日本人が漢語を造語する例もあり、英語のphilosophy、ドイツ語のPhilosophieを指す用語");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmoji() {
|
||||
assertSplitEquals(new String[]{"\uD83E\uDD2B", " ", "\uD83D\uDD2B", "\uD83E\uDDD2"}, "\uD83E\uDD2B \uD83D\uDD2B\uD83E\uDDD2");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsWordStart() {
|
||||
assertTrue(NameUtilCore.isWordStart("测试打补丁", 0));
|
||||
assertTrue(NameUtilCore.isWordStart("测试打补丁", 2));
|
||||
listOf("ローマ", "由", "来", "の", "アルファベット", "(", "ラテン", "文", "字", ")", "を", "用", "いて", "日", "本", "語",
|
||||
"を", "表", "記", "することもでき", "、", "日", "本", "では", "ローマ", "字", "と", "呼", "ばれる"),
|
||||
"ローマ由来のアルファベット(ラテン文字)を用いて日本語を表記することもでき、日本ではローマ字と呼ばれる")
|
||||
assertSplitEquals(
|
||||
listOf("近", "代", "では", "日", "本", "人", "が", "漢", "語", "を", "造", "語", "する", "例", "もあり", "、", "英", "語",
|
||||
"の", "philosophy", "、", "ドイツ", "語", "の", "Philosophie", "を", "指", "す", "用", "語"),
|
||||
"近代では日本人が漢語を造語する例もあり、英語のphilosophy、ドイツ語のPhilosophieを指す用語")
|
||||
}
|
||||
|
||||
private static void assertSplitEquals(String[] expected, String name) {
|
||||
final List<@NotNull String> result = NameUtil.splitNameIntoWordList(name);
|
||||
assertEquals(Arrays.asList(expected), result);
|
||||
@Test
|
||||
fun testEmoji() {
|
||||
assertSplitEquals(listOf("\uD83E\uDD2B", " ", "\uD83D\uDD2B", "\uD83E\uDDD2"),
|
||||
"\uD83E\uDD2B \uD83D\uDD2B\uD83E\uDDD2")
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testIsWordStart() {
|
||||
assertTrue(isWordStart("测试打补丁", 0))
|
||||
assertTrue(isWordStart("测试打补丁", 2))
|
||||
}
|
||||
|
||||
private fun assertSplitEquals(expected: List<String>, name: String) {
|
||||
assertEquals(expected, NameUtil.splitNameIntoWordList(name))
|
||||
}
|
||||
}
|
||||
|
||||
+132
-129
@@ -1,158 +1,161 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.util.text.matching;
|
||||
package com.intellij.platform.util.text.matching
|
||||
|
||||
import com.intellij.psi.codeStyle.PinyinMatcher;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.Normalizer;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import com.intellij.psi.codeStyle.PinyinMatcher
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.net.URL
|
||||
import java.text.Normalizer
|
||||
import java.util.zip.ZipInputStream
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
/**
|
||||
* Generates data arrays for {@link PinyinMatcher} using Unihan_Readings.txt file from unicode.org
|
||||
* Generates data arrays for [PinyinMatcher] using Unihan_Readings.txt file from unicode.org
|
||||
* Requires Internet connection.
|
||||
*/
|
||||
@Disabled("It's a generator, not a real test")
|
||||
public class PinyinMatcherDataTest {
|
||||
private static final int LINE_LENGTH = 100;
|
||||
private static final String DATA_SOURCE = "https://unicode.org/Public/UNIDATA/Unihan.zip";
|
||||
private static final String READINGS_FILE = "Unihan_Readings.txt";
|
||||
|
||||
class PinyinMatcherDataTest {
|
||||
@Test
|
||||
public void ensurePinyinDataIsUpToDate() throws IOException {
|
||||
List<Mapping> mappings = readMappings();
|
||||
List<String> initials = generateInitials(mappings);
|
||||
String encodingStr = String.join(",", initials);
|
||||
String data = getDataString(mappings, initials);
|
||||
fun ensurePinyinDataIsUpToDate() {
|
||||
val mappings = readMappings()
|
||||
val initials = generateInitials(mappings)
|
||||
val encodingStr = initials.joinToString(",")
|
||||
val data = getDataString(mappings, initials)
|
||||
|
||||
Supplier<String> message = () ->
|
||||
"Pinyin data mismatch. Please update constants in " + PinyinMatcher.class.getName() + " to the following:\n" +
|
||||
toJavaStringLiteral("ENCODING", encodingStr) + "\n" +
|
||||
toJavaStringLiteral("DATA", data) + "\n";
|
||||
val message = """Pinyin data mismatch. Please update constants in ${PinyinMatcher::class.qualifiedName} to the following:
|
||||
${toJavaStringLiteral("ENCODING", encodingStr)}
|
||||
${toJavaStringLiteral("DATA", data)}
|
||||
""".trimIndent()
|
||||
|
||||
Assertions.assertEquals(PinyinMatcher.ENCODING, encodingStr, message);
|
||||
Assertions.assertEquals(PinyinMatcher.DATA, data, message);
|
||||
assertEquals(PinyinMatcher.ENCODING, encodingStr, message)
|
||||
assertEquals(PinyinMatcher.DATA, data, message)
|
||||
}
|
||||
|
||||
private static String toJavaStringLiteral(String varName, String input) {
|
||||
StringBuilder result = new StringBuilder(varName + " =\n\"");
|
||||
int curLineLength = 0;
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
char ch = input.charAt(i);
|
||||
String charRepresentation;
|
||||
if (ch == '"' || ch == '\\') {
|
||||
charRepresentation = "\\" + ch;
|
||||
}
|
||||
else if (ch < 127) {
|
||||
charRepresentation = Character.toString(ch);
|
||||
}
|
||||
else {
|
||||
charRepresentation = String.format("\\u%04X", (int)ch);
|
||||
}
|
||||
result.append(charRepresentation);
|
||||
curLineLength += charRepresentation.length();
|
||||
if (curLineLength > LINE_LENGTH && i < input.length() - 1) {
|
||||
curLineLength = 0;
|
||||
result.append("\" +\n\"");
|
||||
companion object {
|
||||
private const val LINE_LENGTH = 100
|
||||
private const val DATA_SOURCE = "https://unicode.org/Public/UNIDATA/Unihan.zip"
|
||||
private const val READINGS_FILE = "Unihan_Readings.txt"
|
||||
|
||||
private val U4_UPPER = HexFormat {
|
||||
upperCase = true
|
||||
number {
|
||||
minLength = 4
|
||||
}
|
||||
}
|
||||
return result.append("\";").toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getDataString(List<Mapping> mappings, List<String> initials) {
|
||||
Map<String, Character> encoding = initials.stream()
|
||||
.collect(Collectors.toMap(s -> s, s -> (char)(PinyinMatcher.BASE_CHAR + initials.indexOf(s))));
|
||||
|
||||
Map<Integer, Character> map = mappings.stream()
|
||||
.collect(Collectors.toMap(mapping -> mapping.codePoint, m -> encoding.get(m.charString())));
|
||||
int lastCodePoint = mappings.stream().mapToInt(m -> m.codePoint).max().orElseThrow(NoSuchElementException::new);
|
||||
return IntStream.rangeClosed(PinyinMatcher.BASE_CODE_POINT, lastCodePoint)
|
||||
.mapToObj(i -> map.getOrDefault(i, ' ').toString()).collect(Collectors.joining());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<String> generateInitials(List<Mapping> mappings) {
|
||||
return mappings.stream().map(Mapping::charString).distinct()
|
||||
.sorted(Comparator.comparing(String::length).thenComparing(Comparator.naturalOrder()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Mapping> readMappings() throws IOException {
|
||||
try (ZipInputStream zis = new ZipInputStream(new URL(DATA_SOURCE).openStream())) {
|
||||
while (true) {
|
||||
ZipEntry entry = zis.getNextEntry();
|
||||
if (entry == null) {
|
||||
throw new IllegalStateException(String.format("No %s found inside %s", READINGS_FILE, DATA_SOURCE));
|
||||
private fun toJavaStringLiteral(varName: String?, input: String): String {
|
||||
val result = StringBuilder("$varName =\n\"")
|
||||
var curLineLength = 0
|
||||
input.forEachIndexed { i, ch ->
|
||||
val charRepresentation = when {
|
||||
ch == '"' || ch == '\\' -> {
|
||||
"\\" + ch
|
||||
}
|
||||
ch.code < 127 -> {
|
||||
ch.toString()
|
||||
}
|
||||
else -> {
|
||||
"\\u${unicodeEscapeCodePoint(ch.code)}"
|
||||
}
|
||||
}
|
||||
if (entry.getName().equals(READINGS_FILE)) {
|
||||
Collection<Mapping> mappings = new BufferedReader(new InputStreamReader(zis, StandardCharsets.UTF_8)).lines()
|
||||
.map(Mapping::parseUniHan)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(m -> m.codePoint, m -> m, Mapping::merge, LinkedHashMap::new))
|
||||
.values();
|
||||
return new ArrayList<>(mappings);
|
||||
result.append(charRepresentation)
|
||||
curLineLength += charRepresentation.length
|
||||
if (curLineLength > LINE_LENGTH && i < input.length - 1) {
|
||||
curLineLength = 0
|
||||
result.append("\" +\n\"")
|
||||
}
|
||||
}
|
||||
return result.append("\";").toString()
|
||||
}
|
||||
|
||||
// todo
|
||||
fun unicodeEscapeCodePoint(cp: Int): String {
|
||||
if (cp <= 0xFFFF) return "\\u" + cp.toHexString(U4_UPPER)
|
||||
val u = cp - 0x10000
|
||||
val hi = 0xD800 + (u ushr 10)
|
||||
val lo = 0xDC00 + (u and 0x3FF)
|
||||
return "\\u" + hi.toHexString(U4_UPPER) + "\\u" + lo.toHexString(U4_UPPER)
|
||||
}
|
||||
|
||||
private fun getDataString(mappings: List<Mapping>, initials: List<String>): String {
|
||||
val encoding = initials.associateWith { PinyinMatcher.BASE_CHAR + initials.indexOf(it) }
|
||||
val map = mappings.associate { mapping -> mapping.codePoint to encoding[mapping.charString()] }
|
||||
val lastCodePoint = mappings.maxOf(Mapping::codePoint)
|
||||
return (PinyinMatcher.BASE_CODE_POINT..lastCodePoint).map { map[it] ?: ' ' }.joinToString("")
|
||||
}
|
||||
|
||||
private fun generateInitials(mappings: List<Mapping>): List<String> {
|
||||
return mappings.map(Mapping::charString).distinct().sortedWith(compareBy(String::length).then(naturalOrder()))
|
||||
}
|
||||
|
||||
private fun readMappings(): List<Mapping> {
|
||||
ZipInputStream(URL(DATA_SOURCE).openStream()).use { zis ->
|
||||
while (true) {
|
||||
val entry = zis.nextEntry
|
||||
requireNotNull(entry) { "No $READINGS_FILE found inside $DATA_SOURCE" }
|
||||
if (entry.name == READINGS_FILE) {
|
||||
return zis.bufferedReader(Charsets.UTF_8).lineSequence()
|
||||
.mapNotNull(Mapping::parseUniHan)
|
||||
.groupBy(Mapping::codePoint)
|
||||
.map { (_, mappings) -> mappings.reduce { acc, mapping -> Mapping.merge(acc, mapping) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record Mapping(int codePoint, long chars) {
|
||||
String charString() {
|
||||
return BitSet.valueOf(new long[]{chars}).stream().mapToObj(bit -> Character.toString((char)(bit + 'a')))
|
||||
.collect(Collectors.joining());
|
||||
private data class Mapping(val codePoint: Int, val chars: Long) {
|
||||
fun charString(): String {
|
||||
return oneBitsSequence(chars).joinToString("") { bit -> (bit + 'a'.code).toChar().toString() }
|
||||
}
|
||||
|
||||
static Mapping merge(Mapping m1, Mapping m2) {
|
||||
if (m1.codePoint != m2.codePoint) throw new IllegalArgumentException();
|
||||
return new Mapping(m1.codePoint, m1.chars | m2.chars);
|
||||
}
|
||||
|
||||
static Mapping parseUniHan(String line) {
|
||||
if (line.startsWith("#")) return null;
|
||||
String[] parts = line.split("\\s+");
|
||||
if (parts.length != 3) return null;
|
||||
if (!parts[0].startsWith("U+")) return null;
|
||||
int codePoint = Integer.parseInt(parts[0].substring(2), 16);
|
||||
if (codePoint < PinyinMatcher.BASE_CODE_POINT) return null;
|
||||
// Codepoints outside BMP are not supported for now
|
||||
if (codePoint > 0xA000) return null;
|
||||
String[] readings;
|
||||
switch (parts[1]) {
|
||||
case "kMandarin" -> readings = new String[]{parts[2]};
|
||||
case "kHanyuPinyin" -> {
|
||||
int colonPos = parts[2].indexOf(':');
|
||||
if (colonPos == -1) return null;
|
||||
readings = parts[2].substring(colonPos + 1).split(",");
|
||||
}
|
||||
default -> {
|
||||
return null;
|
||||
}
|
||||
private fun oneBitsSequence(n: Long): Sequence<Int> = sequence {
|
||||
var bits = n
|
||||
while (bits != 0L) {
|
||||
val lsbIndex = bits.countTrailingZeroBits()
|
||||
yield(lsbIndex)
|
||||
bits = bits and (bits - 1)
|
||||
}
|
||||
long encoded = 0;
|
||||
for (String reading : readings) {
|
||||
char initial = Normalizer.normalize(reading, Normalizer.Form.NFKD).charAt(0);
|
||||
encoded |= 1L << (initial - 'a');
|
||||
}
|
||||
return new Mapping(codePoint, encoded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%04X: %s", codePoint, charString());
|
||||
override fun toString(): String {
|
||||
return "${codePoint.toHexString(U4_UPPER)}: ${charString()}"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val whitespaceRegex = "\\s+".toRegex()
|
||||
fun merge(m1: Mapping, m2: Mapping): Mapping {
|
||||
require(m1.codePoint == m2.codePoint)
|
||||
return Mapping(m1.codePoint, m1.chars or m2.chars)
|
||||
}
|
||||
|
||||
fun parseUniHan(line: String): Mapping? {
|
||||
if (line.startsWith('#')) return null
|
||||
val parts = line.split(whitespaceRegex)
|
||||
if (parts.size != 3) return null
|
||||
if (!parts.first().startsWith("U+")) return null
|
||||
val codePoint = parts.first().substring(2).toInt(16)
|
||||
if (codePoint < PinyinMatcher.BASE_CODE_POINT) return null
|
||||
// Codepoints outside BMP are not supported for now
|
||||
if (codePoint > 0xA000) return null
|
||||
val readings = when (parts[1]) {
|
||||
"kMandarin" -> listOf(parts[2])
|
||||
"kHanyuPinyin" -> {
|
||||
val colonPos = parts[2].indexOf(':')
|
||||
if (colonPos == -1) return null
|
||||
parts[2].substring(colonPos + 1).split(',')
|
||||
}
|
||||
else -> {
|
||||
return null
|
||||
}
|
||||
}
|
||||
var encoded: Long = 0
|
||||
for (reading in readings) {
|
||||
val initial = Normalizer.normalize(reading, Normalizer.Form.NFKD)[0]
|
||||
encoded = encoded or (1L shl (initial.code - 'a'.code))
|
||||
}
|
||||
return Mapping(codePoint, encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-42
@@ -1,55 +1,62 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.util.text.matching;
|
||||
package com.intellij.platform.util.text.matching
|
||||
|
||||
import com.intellij.psi.codeStyle.MinusculeMatcher;
|
||||
import com.intellij.psi.codeStyle.NameUtil;
|
||||
import com.intellij.psi.codeStyle.TypoTolerantMatcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.intellij.psi.codeStyle.NameUtil
|
||||
import com.intellij.psi.codeStyle.TypoTolerantMatcher
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.random.Random
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.SplittableRandom;
|
||||
import java.util.stream.Stream;
|
||||
class TypoTolerantMatcherTest {
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TypoTolerantMatcherTest {
|
||||
@Test
|
||||
public void testStability() {
|
||||
SplittableRandom random = new SplittableRandom(1);
|
||||
String[] data =
|
||||
Stream.generate(() -> {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
random.ints(random.nextInt(5, 25), 'a', 'z')
|
||||
.map(ch ->
|
||||
random.nextInt(100) == 0 ? "0123456789$_@%вгдежзиклмно".charAt(ch - 'a') :
|
||||
random.nextInt(6) == 0 ? Character.toUpperCase(ch) : ch)
|
||||
.forEach(sb::appendCodePoint);
|
||||
return sb.toString();
|
||||
}).limit(20000).toArray(String[]::new);
|
||||
TypoTolerantMatcher matcher = new TypoTolerantMatcher("asd", NameUtil.MatchingCaseSensitivity.FIRST_LETTER, "");
|
||||
List<String> matched = Arrays.stream(data).filter(matcher::matches).toList();
|
||||
List<String> expected =
|
||||
List.of("aqvQpSfmT", "arvIeSdS", "agofjgjwovuUiSmdalto", "jasridvantDr", "asvioqecqrkujxuLoiDo", "ssHDdcogvKq", "asQDqhkej",
|
||||
"gastDtHdqgG", "ahSDaks", "abKluwAdwxJoUyibvgeoh", "aDbsnmlGlJuBJsDi", "aamaoRrghlcD", "aadnjwforytcqwa", "adovoaqvSximVAdD",
|
||||
"adyqfdmDaryuakWicnjcj", "ahNbcEcpIsD", "aLompgtlMkDdypdxwmvvUG", "ajxoADelNmdbmvutbidrde", "nasbcDhbfXx", "aDhbEtoublcDryyh",
|
||||
"pAeloeorWqXslbSDbv", "asnnWshffqbujmcOd", "adXovaMuDYjyrlexj");
|
||||
assertEquals(expected, matched);
|
||||
fun testStability() {
|
||||
val data = randomStrings(Random(1)).take(20000)
|
||||
val matcher = TypoTolerantMatcher("asd", NameUtil.MatchingCaseSensitivity.FIRST_LETTER, "")
|
||||
val matched = data.filter(matcher::matches).toList()
|
||||
val expected = listOf("oaebOeSnflvteoDsqhFo", "avsdCzdi", "axFskogmnmdufynMoewjDzsU", "JazjkxFvzhdogdvSBDwq",
|
||||
"amEckovsuvscwqhgoSTdznk", "aAdmRJtQggyGcbzx", "asfZPktnVioCzeWx", "ajEhAd", "aaDuhgmkapirfpEufpNYbl",
|
||||
"ammimq3zszBNwCmDc", "adZOzpdakGDmhqc", "aAdbjfpbbksFvkoxzJncV", "asxQnpskleYdaUnydjy", "lAsdckUaxay",
|
||||
"aafjkqrMnkmwymDhl", "RaaFSqrfuDivxNksXibx", "aDPginwvboivkJjODzTuntQ", "jaazllSrlD", "zaEgqSZroDymriOjj",
|
||||
"aefqJsdfjacxfak_eoVe", "adDTvpUZAuxpX", "aRAdblxighsznpspe", "adCpMjbbtUDlkvfyakCje", "aSfygmyodrokNuwAvugkmc",
|
||||
"ashzDlibpOm", "aKSDdbtlphcbInJafkP", "ajmfuYsqPYFjD", "aFyjwqdwpWDygluewSmtD", "dazpjiSdf", "ademUttrQDu",
|
||||
"agupnvSxdgnukGlpwxhyLxvp", "avuvzzgdvtSdqEgFqz", "asfKryfdl", "hasfiwopqfaoghaDcGokaoq", "ssbzBsmkDv",
|
||||
"aqSSykiocfzWhajmtYHkshnm", "aiyADtwtlnkdYKqhdlj", "asfcqZgtHAqeOpcpFxp", "ssmw\$dsFzenxpyx1Ff")
|
||||
assertEquals(expected, matched, matched.joinToString(", ") { "\"$it\"" })
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyPattern() {
|
||||
TypoTolerantMatcher matcher = new TypoTolerantMatcher("*", NameUtil.MatchingCaseSensitivity.NONE, "");
|
||||
String[] data = new String[]{"foo", "bar", "buzz"};
|
||||
List<String> matched = Arrays.stream(data).filter(matcher::matches).toList();
|
||||
List<String> expected = List.of("foo", "bar", "buzz");
|
||||
assertEquals(expected, matched);
|
||||
fun testEmptyPattern() {
|
||||
val matcher = TypoTolerantMatcher("*", NameUtil.MatchingCaseSensitivity.NONE, "")
|
||||
val data = listOf("foo", "bar", "buzz")
|
||||
val matched = data.filter(matcher::matches)
|
||||
val expected = listOf("foo", "bar", "buzz")
|
||||
assertEquals(expected, matched)
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPattern() {
|
||||
MinusculeMatcher matcher = NameUtil.buildMatcher("MyLongTestClassName").typoTolerant().build();
|
||||
assertFalse(matcher instanceof TypoTolerantMatcher);
|
||||
assertTrue(matcher.matches("MyLongTestClassName"));
|
||||
fun testLongPattern() {
|
||||
val matcher = NameUtil.buildMatcher("MyLongTestClassName").typoTolerant().build()
|
||||
assertFalse(matcher is TypoTolerantMatcher)
|
||||
assertTrue(matcher.matches("MyLongTestClassName"))
|
||||
}
|
||||
|
||||
private fun randomStrings(random: Random): Sequence<String> = sequence {
|
||||
while (true) {
|
||||
val length = random.nextInt(5, 25)
|
||||
yield(buildString(capacity = length) {
|
||||
repeat(length) {
|
||||
val ch = random.nextInt('a'.code, 'z'.code + 1)
|
||||
val c = when {
|
||||
random.nextInt(100) == 0 -> "0123456789\$_@%вгдежзиклмно"[ch - 'a'.code].code
|
||||
random.nextInt(6) == 0 -> Character.toUpperCase(ch)
|
||||
else -> ch
|
||||
}
|
||||
appendCodePoint(c)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user