diff --git a/lib/ch_usi_si_seart_treesitter_Language.cc b/lib/ch_usi_si_seart_treesitter_Language.cc index 06770807..ae556426 100644 --- a/lib/ch_usi_si_seart_treesitter_Language.cc +++ b/lib/ch_usi_si_seart_treesitter_Language.cc @@ -565,3 +565,13 @@ JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Language_iterator( thisObject ); } + +JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Language_nextState( + JNIEnv* env, jclass self, jlong id, jint state, jint symbol) { + if (id == (jlong)ch_usi_si_seart_treesitter_Language_INVALID) return (jint)(-1); + return (jint)ts_language_next_state( + (const TSLanguage*)id, + (TSStateId)state, + (TSSymbol)symbol + ); +} diff --git a/lib/ch_usi_si_seart_treesitter_Language.h b/lib/ch_usi_si_seart_treesitter_Language.h index ac4c17e6..82d59748 100644 --- a/lib/ch_usi_si_seart_treesitter_Language.h +++ b/lib/ch_usi_si_seart_treesitter_Language.h @@ -505,6 +505,14 @@ JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Language_states JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_Language_iterator (JNIEnv *, jobject, jint); +/* + * Class: ch_usi_si_seart_treesitter_Language + * Method: nextState + * Signature: (JII)I + */ +JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Language_nextState + (JNIEnv *, jclass, jlong, jint, jint); + #ifdef __cplusplus } #endif diff --git a/src/main/java/ch/usi/si/seart/treesitter/Language.java b/src/main/java/ch/usi/si/seart/treesitter/Language.java index ba5c5fe9..a3466580 100644 --- a/src/main/java/ch/usi/si/seart/treesitter/Language.java +++ b/src/main/java/ch/usi/si/seart/treesitter/Language.java @@ -618,6 +618,32 @@ public static void validate(@NotNull Language language) { */ public native LookaheadIterator iterator(int state); + /** + * Obtain the next language parse state for a given {@link Node}. + *
+ * Combine this with lookahead iterators to generate completion
+ * suggestions or valid symbols in {@code ERROR} nodes.
+ *
+ * @param node the node
+ * @return the next parse state
+ * @throws NullPointerException if {@code node} is null
+ * @throws IllegalArgumentException if this language
+ * was not used to parse the node and its syntax tree
+ * @since 1.12.0
+ */
+ public int nextState(@NotNull Node node) {
+ Objects.requireNonNull(node, "Node must not be null!");
+ Language language = node.getLanguage();
+ if (!this.equals(language)) throw new IllegalArgumentException(
+ "Node language does not match the language of this instance!"
+ );
+ int state = node.getParseState();
+ Symbol symbol = node.getGrammarSymbol();
+ return nextState(id, state, symbol.getId());
+ }
+
+ private static native int nextState(long id, int state, int symbol);
+
@Generated
@SuppressWarnings("unused")
public int getTotalSymbols() {
diff --git a/src/test/java/ch/usi/si/seart/treesitter/LanguageTest.java b/src/test/java/ch/usi/si/seart/treesitter/LanguageTest.java
index a7c71ca0..8948f5d1 100644
--- a/src/test/java/ch/usi/si/seart/treesitter/LanguageTest.java
+++ b/src/test/java/ch/usi/si/seart/treesitter/LanguageTest.java
@@ -1,6 +1,8 @@
package ch.usi.si.seart.treesitter;
+import lombok.Cleanup;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
@@ -100,4 +102,21 @@ public Stream extends Arguments> provideArguments(ExtensionContext extensionCo
void testAssociatedWithThrows(Class