summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGeorge Fraser <george@fivetran.com>2018-12-30 00:00:48 -0800
committerGeorge Fraser <george@fivetran.com>2018-12-30 00:00:48 -0800
commit9b3e6f044d3a37b4d27734ddee9e7bf1aa1f9c7b (patch)
tree230359f738fb9913c756c7466c637b9584a2089f /src
parenta21ed1db810a37cd3793116f9b46fce873fff5ca (diff)
downloadjava-language-server-9b3e6f044d3a37b4d27734ddee9e7bf1aa1f9c7b.zip
Use boyer-moore for word searches
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/javacs/JavaCompilerService.java37
-rw-r--r--src/main/java/org/javacs/Parser.java15
-rw-r--r--src/main/java/org/javacs/StringSearch.java43
-rw-r--r--src/test/java/org/javacs/StringSearchTest.java27
4 files changed, 103 insertions, 19 deletions
diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java
index ea53b93..56614a8 100644
--- a/src/main/java/org/javacs/JavaCompilerService.java
+++ b/src/main/java/org/javacs/JavaCompilerService.java
@@ -151,9 +151,7 @@ public class JavaCompilerService {
static boolean containsWord(String name, Path file) {
if (!name.matches("\\w*")) throw new RuntimeException(String.format("`%s` is not a word", name));
- // TODO consider using Parser.containsText, checking for \\b after finding matches
- var word = Pattern.compile("\\b" + name + "\\b");
- return Parser.containsPattern(file, word);
+ return Parser.containsWord(file, name);
}
static boolean importsAnyClass(String toPackage, List<String> toClasses, Path file) {
@@ -195,28 +193,33 @@ public class JavaCompilerService {
public List<URI> potentialReferences(Element to) {
LOG.info(String.format("Find potential references to `%s`...", to));
- // Find files that import toPackage.toClass and contain the word el
- var toPackage = packageName(to);
- var toClass = className(to);
- var name = to.getSimpleName().toString();
- if (name.equals("<init>")) name = to.getEnclosingElement().getSimpleName().toString();
- LOG.info(String.format("...look for the word `%s` in files that import %s.%s", name, toPackage, toClass));
-
// Check all files on source path
var allFiles = allJavaSources();
LOG.info(String.format("...check %d files on the source path", allFiles.size()));
- // Check files, one at a time
- var result = new ArrayList<URI>();
- int nScanned = 0;
+ // Figure out which files import `to`, explicitly or implicitly
+ var toPackage = packageName(to);
+ var toClass = className(to);
+ var hasImport = new ArrayList<Path>();
for (var file : allFiles) {
- if (containsImport(toPackage, toClass, file) && containsWord(name, file)) {
- result.add(file.toUri());
+ if (containsImport(toPackage, toClass, file)) {
+ hasImport.add(file);
}
}
- LOG.info(String.format("...%d files might have references to `%s`", result.size(), to));
+ LOG.info(String.format("...%d files import %s.%s", hasImport.size(), toPackage, toClass));
- return result;
+ // Figure out which of those files have the word `to`
+ var name = to.getSimpleName().toString();
+ if (name.equals("<init>")) name = to.getEnclosingElement().getSimpleName().toString();
+ var hasWord = new ArrayList<URI>();
+ for (var file : hasImport) {
+ if (containsWord(name, file)) {
+ hasWord.add(file.toUri());
+ }
+ }
+ LOG.info(String.format("...%d files contain the word `%s`", hasWord.size(), name));
+
+ return hasWord;
}
public static String packageName(Element e) {
diff --git a/src/main/java/org/javacs/Parser.java b/src/main/java/org/javacs/Parser.java
index 354ee54..d1d9bcb 100644
--- a/src/main/java/org/javacs/Parser.java
+++ b/src/main/java/org/javacs/Parser.java
@@ -152,6 +152,21 @@ class Parser {
}
}
+ static boolean containsWord(Path java, String query) {
+ var search = new StringSearch(query);
+ try (var channel = FileChannel.open(java)) {
+ // Read up to 1 MB of data from file
+ var limit = Math.min((int) channel.size(), SEARCH_BUFFER.capacity());
+ SEARCH_BUFFER.position(0);
+ SEARCH_BUFFER.limit(limit);
+ channel.read(SEARCH_BUFFER);
+ SEARCH_BUFFER.position(0);
+ return search.nextWord(SEARCH_BUFFER) != -1;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
static boolean containsPattern(Path java, Pattern pattern) {
try (var channel = FileChannel.open(java)) {
// Read up to 1 MB of data from file
diff --git a/src/main/java/org/javacs/StringSearch.java b/src/main/java/org/javacs/StringSearch.java
index 037e894..65a5022 100644
--- a/src/main/java/org/javacs/StringSearch.java
+++ b/src/main/java/org/javacs/StringSearch.java
@@ -96,7 +96,11 @@ class StringSearch {
}
int next(ByteBuffer text) {
- var i = pattern.length - 1;
+ return next(text, 0);
+ }
+
+ int next(ByteBuffer text, int startingAfter) {
+ var i = startingAfter + pattern.length - 1;
while (i < text.limit()) {
// Compare backwards from the end until the first unmatching character.
var j = pattern.length - 1;
@@ -112,6 +116,43 @@ class StringSearch {
return -1;
}
+ boolean isWordChar(byte b) {
+ char c = (char) (b + 128);
+ return Character.isAlphabetic(c) || Character.isDigit(c) || c == '$' || c == '_';
+ }
+
+ boolean startsWord(ByteBuffer text, int offset) {
+ if (offset == 0) return true;
+ return !isWordChar(text.get(offset - 1));
+ }
+
+ boolean endsWord(ByteBuffer text, int offset) {
+ if (offset + 1 >= text.limit()) return true;
+ return !isWordChar(text.get(offset + 1));
+ }
+
+ boolean isWord(ByteBuffer text, int offset) {
+ return startsWord(text, offset) && endsWord(text, offset + pattern.length - 1);
+ }
+
+ int nextWord(String text) {
+ return nextWord(text.getBytes());
+ }
+
+ int nextWord(byte[] text) {
+ return nextWord(ByteBuffer.wrap(text));
+ }
+
+ int nextWord(ByteBuffer text) {
+ var i = 0;
+ while (true) {
+ i = next(text, i);
+ if (i == -1) return -1;
+ if (isWord(text, i)) return i;
+ i++;
+ }
+ }
+
private boolean hasPrefix(byte[] s, Slice prefix) {
for (int i = 0; i < prefix.length(); i++) {
if (s[i] != prefix.get(i)) return false;
diff --git a/src/test/java/org/javacs/StringSearchTest.java b/src/test/java/org/javacs/StringSearchTest.java
index 7827fcd..b11efde 100644
--- a/src/test/java/org/javacs/StringSearchTest.java
+++ b/src/test/java/org/javacs/StringSearchTest.java
@@ -11,8 +11,13 @@ public class StringSearchTest {
assertThat(got, equalTo(index));
}
+ private void testNextWord(String pat, String text, int index) {
+ var got = new StringSearch(pat).nextWord(text);
+ assertThat(got, equalTo(index));
+ }
+
@Test
- public void testFinderNext() {
+ public void testNext() {
testNext("", "", 0);
testNext("", "abc", 0);
testNext("abc", "", -1);
@@ -29,4 +34,24 @@ public class StringSearchTest {
testNext("baa", "aaaaa", -1);
testNext("at that", "which finally halts. at that point", 22);
}
+
+ @Test
+ public void testNextWord() {
+ testNextWord("", "", 0);
+ testNextWord("", "abc", -1);
+ testNextWord("abc", "", -1);
+ testNextWord("abc", "abc", 0);
+ testNextWord("d", "abcdefg", -1);
+ testNextWord("d", "abc d efg", 4);
+ testNextWord("nan", "banana", -1);
+ testNextWord("nan", "ba nan a", 3);
+ testNextWord("abcd", "abc", -1);
+ testNextWord("abcd", "bcd", -1);
+ testNextWord("bcd", "abcd", -1);
+ testNextWord("bcd", "a bcd", 2);
+ testNextWord("abc", "abc d", 0);
+ testNextWord("aa", "aaa", -1);
+ testNextWord("aa", "a aa", 2);
+ testNextWord("aa", "aa a", 0);
+ }
}