Skip to content

Conversation

@fabrichter
Copy link
Collaborator

@fabrichter fabrichter commented Nov 20, 2025

Summary by CodeRabbit

  • New Features

    • Provide custom rule sets at initialization
    • Replace active rules at runtime via new API
    • Run text checks using an explicit custom rule set
  • Improvements

    • Remote check results now respect an offsets flag when reporting and caching matches
    • Text session identifiers are computed deterministically (hash-based) from provided inputs
  • Documentation

    • Public API docs updated to reflect custom-rule capabilities

✏️ Tip: You can customize this high-level summary in your review settings.

@fabrichter fabrichter requested a review from SteVio89 November 20, 2025 16:48
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

Walkthrough

Added runtime custom-rule support to JLanguageTool (new constructor, setRules, and checkInternalWithCustomRules), introduced RemoteRuleResult.adjustOffsets with related constructors and accessor, and centralized textSessionID computation in TextChecker via a new computeTextSessionID helper.

Changes

Cohort / File(s) Summary
JLanguageTool — custom-rule API and wiring
languagetool-core/src/main/java/org/languagetool/JLanguageTool.java
Added constructor overload accepting List<Rule> customRules; when provided, initializes builtin rules from that list and bypasses standard activation. Added public void setRules(List<Rule> rules) to replace active rules, clear caches, and assign provided rules to user rules. Added public CheckResults checkInternalWithCustomRules(...) and updated checkInternal(...) to delegate to the custom-rule path when appropriate. Adjusted constructor flow and Javadoc.
Remote rule result offsets flag
languagetool-core/src/main/java/org/languagetool/rules/RemoteRuleResult.java
Added private final boolean adjustOffsets field, updated constructors to accept or default this flag, and added public boolean adjustOffsets() accessor. Call sites (e.g., RemoteRule) updated to supply/handle the new parameter.
Remote rule handling adjustments
languagetool-core/src/main/java/org/languagetool/rules/RemoteRule.java
Updated construction of RemoteRuleResult to include the new adjusted-offsets argument and shifted subsequent parameters; preserved existing filtering and offset-adjustment logic while changing argument ordering.
Text session ID hashing (server)
languagetool-server/src/main/java/org/languagetool/server/TextChecker.java
Added protected static Long computeTextSessionID(String textSessionIdParam, String ip) that deterministically hashes chosen input with SHA-256 and returns the first 64 bits as Long. Replaced previous ad-hoc parsing/NumberFormatException logic in match(...) with calls to this helper; added necessary imports.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect constructor branching in JLanguageTool to ensure builtin activation is bypassed only when customRules is intentionally provided.
  • Verify setRules cache invalidation and any thread-safety concerns.
  • Check checkInternalWithCustomRules integration with listeners, remote-result handling, and caching.
  • Confirm all RemoteRuleResult construction sites align with the new parameter ordering and that adjustOffsets() is respected where offsets are applied.
  • Validate determinism and acceptable collision risk of truncating SHA-256 to 64 bits in computeTextSessionID.

Suggested reviewers

  • SteVio89

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the PR, which adds new constructors and APIs to JLanguageTool for custom rule management.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch custom-rules-jlanguagetool

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (3)

320-365: Custom-rules constructor behavior is reasonable but leaks the caller’s list and ignores withLanguageModel

The new 10‑arg constructor wires customRules straight into builtinRules and skips all default-rule initialization, which matches the high-level intent. Two caveats:

  • builtinRules = customRules; means the caller’s list is now the internal mutable list. Later calls like setRules() (which clears builtinRules) or any other in‑place operations will also mutate the original list reference the caller may still hold. Consider defensively copying: e.g. wrapping customRules in a new ArrayList before assigning to builtinRules.
  • When customRules != null, the withLanguageModel flag and the “optional LM rules” path are effectively ignored; only the caller-supplied rules are used. That’s probably fine, but it might be worth documenting explicitly in the new Javadoc so users don’t expect LM-capable rules to be auto-attached in this mode.

If you agree on the first point, a small refactor like this would avoid aliasing:

-    ResourceBundle messages = ResourceBundleTools.getMessageBundle(language);
-    if (customRules != null) {
-      builtinRules = customRules;
-    } else {
-      builtinRules = getAllBuiltinRules(language, messages, userConfig, globalConfig);
+    ResourceBundle messages = null;
+    if (customRules != null) {
+      builtinRules = new ArrayList<>(customRules);
+    } else {
+      messages = ResourceBundleTools.getMessageBundle(language);
+      builtinRules = getAllBuiltinRules(language, messages, userConfig, globalConfig);
     try {
       activateDefaultPatternRules();
       ...

805-817: setRules clears built‑ins but leaves enable/disable state and categories intact

The new setRules(List<Rule> rules) does a hard reset of the internal rule lists:

builtinRules.clear();
userRules.clear();
userRules.addAll(rules);
ruleSetCache.clear();

A few behavioral implications worth double‑checking:

  • You’re clearing builtinRules entirely and placing all new rules into userRules. Because other code usually treats both lists similarly this likely works, but semantically it means “everything is now user rules”. If the intent is “replace the complete active rule set”, you might instead want the new rules to live in builtinRules and keep userRules as the layer for incremental additions:

    public void setRules(List<Rule> rules) {
  • builtinRules.clear();

  • userRules.clear();

  • userRules.addAll(rules);

  • builtinRules.clear();
  • builtinRules.addAll(rules);
  • userRules.clear();
    ruleSetCache.clear();
    }

- The method doesn’t reset `disabledRules`, `enabledRules`, `disabledRuleCategories`, or `enabledRuleCategories`. Those historical flags will still affect the newly set rules if IDs happen to collide, which could be surprising when a caller expects a clean slate. Either clearing those sets here or clarifying in the Javadoc that enable/disable state is preserved would make the behavior less surprising.

Given this is new public API, aligning implementation and documentation now will avoid confusing interactions later.

---

`1081-1086`: **Refactor to `checkInternalWithCustomRules` looks correct; consider tightening the public API surface**

The split between:

- `checkInternal(...)` → derives `RuleSet` via `getActiveRulesForLevelAndToneTags` and
- `public CheckResults checkInternalWithCustomRules(RuleSet rules, ...)` → runs the full pipeline

is clean and appears to preserve the previous behavior: remote rules, caching, GRPC post‑processing, and final `filterMatches` all go through the same path, and tone/level filtering is applied consistently via `isRuleActiveForLevelAndToneTags` in `filterMatches`.

Two small API points to consider:

- `checkInternalWithCustomRules` is now public but accepts a raw `RuleSet` without any Javadoc. For external callers it’s not obvious that:
- They should generally pass a `RuleSet` built similarly to the internal one (e.g. `RuleSet.textLemmaHinted(...)`), and
- Level/tone filtering still applies at the match level, regardless of how `rules` was constructed.

A short Javadoc block spelling this out (and clarifying that disable/enable flags and tone tags are still honored in `filterMatches`) would help prevent misuse.
- The `rules` parameter is expected to be non‑null and compatible with the rest of the pipeline (e.g. `rules.rulesForSentence(...)` must be valid). You might want to annotate it as `@NotNull` to make that contract explicit.

Otherwise, the factoring and wiring into the existing check flow look solid.  




Also applies to: 1088-1176, 1178-1198

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 324ca0e32e89aa73d89b18761ebfbb157215222b and 5db7122d7a84a507f389c4e5faa53eee6a348811.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `languagetool-core/src/main/java/org/languagetool/JLanguageTool.java` (4 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)</summary>

* GitHub Check: Analyze (java-kotlin)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
languagetool-core/src/main/java/org/languagetool/rules/RemoteRuleResult.java (2)

60-62: Consider documenting the default adjustOffsets = true behavior

Since this constructor preserves the old signature, it will likely remain the most common entry point. A short Javadoc explaining that it defaults adjustOffsets to true would make the behavior explicit for callers.

-  public RemoteRuleResult(boolean remote, boolean success, List<RuleMatch> matches, List<AnalyzedSentence> processedSentences) {
+  /**
+   * Creates a {@code RemoteRuleResult} assuming that rule matches are sentence-relative
+   * and thus require offset adjustment (i.e. {@code adjustOffsets} is {@code true}).
+   */
+  public RemoteRuleResult(boolean remote, boolean success, List<RuleMatch> matches, List<AnalyzedSentence> processedSentences) {
     this(remote, success, true, matches, processedSentences);
   }

72-74: Optional: add Javadoc to clarify what “adjustOffsets” means for callers

Given the slightly imperative method name (adjustOffsets() vs. isXxx()), a brief Javadoc would help make the contract obvious at call sites (especially when reading if (result.adjustOffsets()) { ... }).

-  public boolean adjustOffsets() {
-    return adjustOffsets;
-  }
+  /**
+   * @return {@code true} if the {@link RuleMatch} offsets are relative to their
+   *         sentences and still need to be adjusted to the full text; {@code false}
+   *         if the matches already use full-text offsets and must not be adjusted.
+   */
+  public boolean adjustOffsets() {
+    return adjustOffsets;
+  }
languagetool-server/src/main/java/org/languagetool/server/TextChecker.java (1)

459-459: Changed textSessionId semantics – confirm downstream expectations

textSessionId is now always derived via computeTextSessionID(params.get("textSessionId"), remoteAddress), i.e., a SHA‑256‑based 64‑bit hash of either the client-supplied string or the IP (and possibly null if you adopt the defensive change above), instead of parsing a long and falling back on failure.

This is a behavioral change that affects any consumers that log, persist, or interpret textSessionId (e.g., database logs, A/B tests, remote rule infrastructure). Please double-check that:

  • No callers rely on being able to pass a specific numeric textSessionId value (e.g., for correlation across systems).
  • Schema/range assumptions for stored textSessionId values are still valid with potentially negative 64‑bit values and different distribution.

If needed, you might want a brief migration note or comment to make this intentional change clear.

languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (2)

805-817: setRules should also reset language-model rule bookkeeping; consider clarifying interaction with enable/disable sets

setRules currently clears builtinRules and userRules, then installs the new rules into userRules and clears the ruleSetCache. However:

  • optionalLanguageModelRules is left untouched. If updateOptionalLanguageModelRules(...) is called after setRules, it will remove rules from the new userRules whose IDs happen to match stale entries from the previous configuration.
  • disabledRules, enabledRules, disabledRuleCategories, and enabledRuleCategories are also preserved, which may or may not be desired when doing a full ruleset replacement (rules with reused IDs could be unexpectedly filtered).

At minimum, I’d strongly recommend clearing the LM bookkeeping to avoid subtle removal of unrelated rules:

  public void setRules(List<Rule> rules) {
    builtinRules.clear();
    userRules.clear();
    userRules.addAll(rules);
+   optionalLanguageModelRules.clear();
    ruleSetCache.clear();
  }

If the intent of setRules is to fully replace the ruleset, you may also want to document (or reconsider) how the enable/disable sets interact with this API.


1081-1176: New checkInternalWithCustomRules API looks good; consider tightening nullability and adding Javadoc

The refactor to have checkInternal(...) compute the active RuleSet and delegate to checkInternalWithCustomRules(...) is clean and appears behavior‑preserving. It also provides a useful hook for callers that want to supply their own RuleSet.

To make the new public API safer and more self‑documenting:

  • Mark rules as non‑null (consistent with other parameters already using @NotNull).
  • Add a short Javadoc comment that explains when checkInternalWithCustomRules should be used and how it differs from check(...) / check2(...).

Example tweak:

-  public CheckResults checkInternalWithCustomRules(RuleSet rules, AnnotatedText annotatedText, ParagraphHandling paraMode, RuleMatchListener listener,
+  public CheckResults checkInternalWithCustomRules(@NotNull RuleSet rules, AnnotatedText annotatedText, ParagraphHandling paraMode, RuleMatchListener listener,
                                      Mode mode, Level level, @NotNull Set<ToneTag> toneTags,
                                      @Nullable Long textSessionID, List<String> sentences, List<AnalyzedSentence> analyzedSentences) throws IOException {

This keeps the surface consistent with the rest of the class and helps avoid accidental null passing from external callers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5db7122 and e64e01f.

📒 Files selected for processing (3)
  • languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (6 hunks)
  • languagetool-core/src/main/java/org/languagetool/rules/RemoteRuleResult.java (3 hunks)
  • languagetool-server/src/main/java/org/languagetool/server/TextChecker.java (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (4)
languagetool-core/src/main/java/org/languagetool/rules/RemoteRuleResult.java (2)

32-33: adjustOffsets flag and comment are consistent with existing responsibilities

The new adjustOffsets field cleanly captures whether match offsets are sentence-relative vs. already aligned, and the inline comment is clear enough for future maintainers. No issues from my side here.


41-45: Constructor extension looks correct and preserves internal invariants

Wiring the new adjustOffsets parameter straight into the field while leaving the rest of the initialization untouched keeps behavior predictable. Combined with the delegating ctor below, this setup should preserve existing behavior while enabling the new mode.

languagetool-server/src/main/java/org/languagetool/server/TextChecker.java (1)

47-53: New imports are appropriate for hashing helper

The added imports (ByteBuffer, StandardCharsets, MessageDigest, NoSuchAlgorithmException) match the new computeTextSessionID implementation and are scoped reasonably; no issues here.

languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (1)

1354-1372: Offset adjustment and caching now correctly respect RemoteRuleResult.adjustOffsets()

The changes here (conditioning both cache writes and subsequent adjustOffset(...) calls on result.adjustOffsets()) look correct and avoid double‑adjusting matches for remote rules that already return offsets in the final coordinate space. They also prevent such matches from entering the remote‑match cache, which would otherwise be applied with adjustOffset(...) unconditionally on cache reads.

This is a good trade‑off: correctness is preserved for both legacy and new remote rules, and caching is still used where offset adjustment is safe.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (1)

297-300: Constructor ignores inputLogging argument and always enables logging

In the 7-arg constructor

public JLanguageTool(Language language, List<Language> altLanguages, Language motherTongue,
                     ResultCache cache, GlobalConfig globalConfig, UserConfig userConfig,
                     boolean inputLogging)

the inputLogging parameter is ignored:

this(language, altLanguages, motherTongue, cache, globalConfig, userConfig, true, false);

This hard-codes inputLogging = true, so callers cannot disable input logging anymore (and the signature/Javadoc become misleading). It also conflates the default for withLanguageModel with the logging flag.

Suggested fix:

 public JLanguageTool(Language language, List<Language> altLanguages, Language motherTongue, ResultCache cache,
                      GlobalConfig globalConfig, UserConfig userConfig, boolean inputLogging) {
-    this(language, altLanguages, motherTongue, cache, globalConfig, userConfig, true, false);
+    this(language, altLanguages, motherTongue, cache, globalConfig, userConfig, inputLogging, false);
 }

This restores the intended behavior: callers control logging via inputLogging, while withLanguageModel still defaults to false.

Also applies to: 320-322

♻️ Duplicate comments (2)
languagetool-server/src/main/java/org/languagetool/server/TextChecker.java (1)

47-53: computeTextSessionID: good centralization; consider treating empty param as “unset” and documenting nullability

The helper correctly centralizes textSessionId derivation, avoids the previous parsing/NumberFormatException mess, and now:

  • Handles the “both null” case via early return null.
  • Has removed the uncontrolled System.out logging of IDs/IPs.

One remaining behavioral edge case: an empty textSessionIdParam currently overrides ip and hashes "", giving all such requests the same session ID. Previously, an empty/invalid value would effectively fall back to IP. If you still want IP-based separation in that case, consider:

-  protected static Long computeTextSessionID(String textSessionIdParam, String ip) {
-      String input = textSessionIdParam != null ? textSessionIdParam : ip;
-      if (input == null) {
-        return null;
-      }
+  @Nullable
+  protected static Long computeTextSessionID(String textSessionIdParam, String ip) {
+      String param = (textSessionIdParam != null && !textSessionIdParam.isEmpty())
+        ? textSessionIdParam
+        : null;
+      String input = param != null ? param : ip;
+      if (input == null || input.isEmpty()) {
+        // No stable basis for a session id
+        return null;
+      }
       try {
         MessageDigest md = MessageDigest.getInstance("SHA-256");
         byte[] bytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
 
         ByteBuffer buffer = ByteBuffer.wrap(bytes);
-        Long textSessionId = buffer.getLong();
-        return textSessionId;
+        return buffer.getLong();
       } catch (NoSuchAlgorithmException e) {
         // Should not happen for SHA-256, wrap in a runtime exception
         throw new RuntimeException("SHA-256 not supported", e);
       }
   }

This preserves your new hashing approach, avoids NPE/PII issues, and restores more sensible behavior when the client sends an empty textSessionId.

Also applies to: 269-288, 461-461

languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (1)

342-365: Custom-rules constructor and defensive copy look correct

The new 9-arg constructor cleanly wires customRules into initialization and, importantly, wraps them in new ArrayList<>(customRules), so internal mutation of builtinRules won’t affect the caller’s list or blow up on unmodifiable lists. The fallback path for customRules == null preserves previous behavior (built-in rules, default activations, optional language model rules setup).

This API surface for custom rule initialization looks sound.

🧹 Nitpick comments (1)
languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (1)

805-817: Clear optionalLanguageModelRules when replacing rules via setRules

setRules correctly clears builtinRules, userRules, and ruleSetCache before installing the new rule list. However, optionalLanguageModelRules is left untouched:

public void setRules(List<Rule> rules) {
  builtinRules.clear();
  userRules.clear();
  userRules.addAll(rules);
  ruleSetCache.clear();
}

If the instance previously had language-model-capable rules loaded, optionalLanguageModelRules can still contain their IDs. A later call to updateOptionalLanguageModelRules(...) will then remove any newly set rules with those IDs and replace them with LM-specific variants, which is surprising when setRules was expected to take full control of the active set.

Consider explicitly resetting this internal LM bookkeeping:

 public void setRules(List<Rule> rules) {
   builtinRules.clear();
   userRules.clear();
+  optionalLanguageModelRules.clear();
   userRules.addAll(rules);
   ruleSetCache.clear();
 }

This keeps the LM-related state consistent with the new rule set and avoids hard-to-debug interactions for callers using custom rules plus language models.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e64e01f and bba4e3b.

📒 Files selected for processing (2)
  • languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (6 hunks)
  • languagetool-server/src/main/java/org/languagetool/server/TextChecker.java (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (1)
languagetool-core/src/main/java/org/languagetool/JLanguageTool.java (1)

1082-1090: checkInternalWithCustomRules + adjustOffsets gating for remote rules looks correct

The refactor to:

RuleSet rules = getActiveRulesForLevelAndToneTags(level, toneTags);
return checkInternalWithCustomRules(rules, ...);

plus the new public CheckResults checkInternalWithCustomRules(...) cleanly generalizes the internal check pipeline to arbitrary RuleSets without duplicating logic, and maintains existing behavior for the standard path.

In fetchResults, the new adjustOffsets usage:

if (cache != null && result.isSuccess() && result.adjustOffsets()) {
  // store in cache
  ...
  cacheEntry.put(ruleKey, matches);
}

...

List<RuleMatch> adjustedMatches = matches.stream().map(RuleMatch::new).collect(Collectors.toList());
if (result.adjustOffsets()) {
  for (RuleMatch match : adjustedMatches) {
    adjustOffset(annotatedText, offset, match);
  }
}

ensures that:

  • Only results whose offsets are sentence-relative are cached under InputSentence keys and later re-offset.
  • Results that already carry document-relative offsets are neither cached under the sentence key nor offset a second time.

This matches the new RemoteRuleResult.adjustOffsets contract and avoids double-adjusting or mis-caching remote matches.

Also applies to: 1354-1372

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
languagetool-core/src/main/java/org/languagetool/rules/RemoteRule.java (1)

193-225: Confirm adjustOffsets semantics when re‑wrapping RemoteRuleResult

You correctly preserve isRemote/isSuccess and the adjustOffsets() flag when creating new RemoteRuleResult instances after filterMatches and after suppressMisspelled, so any downstream handling based on that flag is not silently lost.

One thing to double‑check: above this block, fixMatchOffsets(...) may already have rewritten the offsets in place. If adjustOffsets is interpreted as “consumer still needs to adjust offsets”, you might now be propagating a flag whose precondition has been fulfilled locally, which could lead to double‑adjustment elsewhere. If, instead, the flag means “offsets have been adjusted already” (or is just metadata about the remote), the current propagation is fine. It’s worth verifying that the boolean’s semantics and the fixOffsets behavior are aligned.

As a minor, optional readability tweak, you could avoid double‑wrapping by performing both filtering steps into a single final List<RuleMatch> and constructing RemoteRuleResult only once at the end, and/or using more specific names than a second filteredMatches for the post‑suppression list.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bba4e3b and 96da4cf.

📒 Files selected for processing (1)
  • languagetool-core/src/main/java/org/languagetool/rules/RemoteRule.java (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java-kotlin)

@fabrichter fabrichter merged commit 1d0e61a into master Nov 26, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants