diff --git a/changelog.xml b/changelog.xml
index f8a819db..c4b8670c 100644
--- a/changelog.xml
+++ b/changelog.xml
@@ -19,6 +19,15 @@
Add support for the `main` option of the babel package (LaTeX)
+
+ Add support for [`ltex.dictionary`](https://valentjn.github.io/vscode-ltex/docs/settings.html#ltexdictionary) when using a LanguageTool HTTP server
+
+
+ Handle disabled rules ourselves to prevent reinitialization of LanguageTool when running the `Disable rule` quick fix
+
+
+ Fix LanguageTool reinitialized when running the `Add '...' to dictionary` quick fix
+
Fix used i18n keys removed
diff --git a/pom.xml b/pom.xml
index f2fc2b2c..24ab8bbe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
4.0.0
org.bsplines
ltexls
- 13.0.1-alpha.1.develop
+ 13.1.0-alpha.1.develop
${project.groupId}:${project.artifactId}
LTeX Language Server (LTeX LS): LSP language server for LanguageTool with support for LaTeX, Markdown, and others
https://github.com/valentjn/ltex-ls
diff --git a/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolHttpInterface.kt b/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolHttpInterface.kt
index 5ac1d6a1..34ce6b43 100644
--- a/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolHttpInterface.kt
+++ b/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolHttpInterface.kt
@@ -16,7 +16,6 @@ import org.bsplines.ltexls.tools.I18n
import org.bsplines.ltexls.tools.Logging
import org.languagetool.markup.AnnotatedText
import org.languagetool.markup.TextPart
-import org.languagetool.rules.RuleMatch
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.net.MalformedURLException
@@ -34,9 +33,8 @@ class LanguageToolHttpInterface(
uriString: String,
private val languageShortCode: String,
private val motherTongueShortCode: String,
-) : LanguageToolInterface {
- private val enabledRuleIds: MutableList = ArrayList()
- private val disabledRuleIds: MutableList = ArrayList()
+) : LanguageToolInterface() {
+ private val enabledRules: MutableList = ArrayList()
private val httpClient: HttpClient = HttpClient.newHttpClient()
private val uri: URI?
@@ -61,7 +59,9 @@ class LanguageToolHttpInterface(
return (this.uri != null)
}
- override fun check(annotatedTextFragment: AnnotatedTextFragment): List {
+ override fun checkInternal(
+ annotatedTextFragment: AnnotatedTextFragment,
+ ): List {
if (!isInitialized()) return emptyList()
val requestBody: String = createRequestBody(annotatedTextFragment) ?: return emptyList()
@@ -92,20 +92,9 @@ class LanguageToolHttpInterface(
val result = ArrayList()
for (jsonElement: JsonElement in jsonMatches) {
- val jsonMatch: JsonObject = jsonElement.asJsonObject
- val ruleId: String = jsonMatch.get("rule").asJsonObject.get("id").asString
- val sentence: String = jsonMatch.get("sentence").asString
- val fromPos: Int = jsonMatch.get("offset").asInt
- val toPos: Int = fromPos + jsonMatch.get("length").asInt
- val message: String = jsonMatch.get("message").asString
- val suggestedReplacements = ArrayList()
-
- for (replacement: JsonElement in jsonMatch.get("replacements").asJsonArray) {
- suggestedReplacements.add(replacement.asJsonObject.get("value").asString)
- }
-
- result.add(LanguageToolRuleMatch.fromLanguageTool(ruleId, sentence, fromPos, toPos, message,
- suggestedReplacements, RuleMatch.Type.Hint, annotatedTextFragment))
+ result.add(
+ LanguageToolRuleMatch.fromLanguageTool(jsonElement.asJsonObject, annotatedTextFragment)
+ )
}
return result
@@ -127,12 +116,8 @@ class LanguageToolHttpInterface(
requestEntries["motherTongue"] = this.motherTongueShortCode
}
- if (this.enabledRuleIds.isNotEmpty()) {
- requestEntries["enabledRules"] = this.enabledRuleIds.joinToString(",")
- }
-
- if (this.disabledRuleIds.isNotEmpty()) {
- requestEntries["disabledRules"] = this.disabledRuleIds.joinToString(",")
+ if (this.enabledRules.isNotEmpty()) {
+ requestEntries["enabledRules"] = this.enabledRules.joinToString(",")
}
val builder = StringBuilder()
@@ -169,13 +154,7 @@ class LanguageToolHttpInterface(
}
override fun enableRules(ruleIds: Set) {
- this.enabledRuleIds.addAll(ruleIds)
- this.disabledRuleIds.removeAll(ruleIds)
- }
-
- override fun disableRules(ruleIds: Set) {
- this.enabledRuleIds.removeAll(ruleIds)
- this.disabledRuleIds.addAll(ruleIds)
+ this.enabledRules.addAll(ruleIds)
}
override fun enableEasterEgg() {
diff --git a/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolInterface.kt b/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolInterface.kt
index fe792015..c1a1f6b2 100644
--- a/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolInterface.kt
+++ b/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolInterface.kt
@@ -9,14 +9,45 @@ package org.bsplines.ltexls.languagetool
import org.bsplines.ltexls.parsing.AnnotatedTextFragment
-interface LanguageToolInterface {
- fun isInitialized(): Boolean
- fun check(annotatedTextFragment: AnnotatedTextFragment): List
- fun activateDefaultFalseFriendRules()
- fun activateLanguageModelRules(languageModelRulesDirectory: String)
- fun activateNeuralNetworkRules(neuralNetworkRulesDirectory: String)
- fun activateWord2VecModelRules(word2vecRulesDirectory: String)
- fun enableRules(ruleIds: Set)
- fun disableRules(ruleIds: Set)
- fun enableEasterEgg()
+abstract class LanguageToolInterface {
+ var dictionary: Set = emptySet()
+ var disabledRules: Set = emptySet()
+
+ fun check(annotatedTextFragment: AnnotatedTextFragment): List {
+ val matches = ArrayList()
+
+ for (match: LanguageToolRuleMatch in checkInternal(annotatedTextFragment)) {
+ if (checkMatchValidity(annotatedTextFragment, match)) matches.add(match)
+ }
+
+ return matches
+ }
+
+ protected fun checkMatchValidity(
+ annotatedTextFragment: AnnotatedTextFragment,
+ match: LanguageToolRuleMatch,
+ ): Boolean {
+ return (
+ (
+ !match.isUnknownWordRule()
+ || !this.dictionary.contains(
+ annotatedTextFragment.getSubstringOfPlainText(match.fromPos, match.toPos)
+ )
+ )
+ && !this.disabledRules.contains(match.ruleId)
+ )
+ }
+
+ abstract fun isInitialized(): Boolean
+
+ protected abstract fun checkInternal(
+ annotatedTextFragment: AnnotatedTextFragment,
+ ): List
+
+ abstract fun activateDefaultFalseFriendRules()
+ abstract fun activateLanguageModelRules(languageModelRulesDirectory: String)
+ abstract fun activateNeuralNetworkRules(neuralNetworkRulesDirectory: String)
+ abstract fun activateWord2VecModelRules(word2vecRulesDirectory: String)
+ abstract fun enableRules(ruleIds: Set)
+ abstract fun enableEasterEgg()
}
diff --git a/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolJavaInterface.kt b/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolJavaInterface.kt
index 81183e16..765c5c5f 100644
--- a/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolJavaInterface.kt
+++ b/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolJavaInterface.kt
@@ -37,8 +37,7 @@ class LanguageToolJavaInterface(
motherTongueShortCode: String,
sentenceCacheSize: Long,
dictionary: Set,
-) : LanguageToolInterface {
- private val dictionary: Set = dictionary.toSet()
+) : LanguageToolInterface() {
private val resultCache =
ResultCache(sentenceCacheSize, RESULT_CACHE_EXPIRE_AFTER_MINUTES, TimeUnit.MINUTES)
private val languageTool: JLanguageTool?
@@ -61,7 +60,9 @@ class LanguageToolJavaInterface(
}
@Suppress("INACCESSIBLE_TYPE")
- override fun check(annotatedTextFragment: AnnotatedTextFragment): List {
+ override fun checkInternal(
+ annotatedTextFragment: AnnotatedTextFragment,
+ ): List {
val languageTool: JLanguageTool? = this.languageTool
if (languageTool == null) {
@@ -123,16 +124,7 @@ class LanguageToolJavaInterface(
val result = ArrayList()
for (match: RuleMatch in matches) {
- val languageToolRuleMatch = LanguageToolRuleMatch.fromLanguageTool(
- match, annotatedTextFragment)
-
- if (languageToolRuleMatch.isUnknownWordRule()
- && this.dictionary.contains(annotatedTextFragment.getSubstringOfPlainText(
- languageToolRuleMatch.fromPos, languageToolRuleMatch.toPos))) {
- continue
- }
-
- result.add(languageToolRuleMatch)
+ result.add(LanguageToolRuleMatch.fromLanguageTool(match, annotatedTextFragment))
}
return result
@@ -206,11 +198,6 @@ class LanguageToolJavaInterface(
}
}
- override fun disableRules(ruleIds: Set) {
- val languageTool: JLanguageTool = (this.languageTool ?: return)
- languageTool.disableRules(ruleIds.toList())
- }
-
override fun enableEasterEgg() {
val languageTool: JLanguageTool = (this.languageTool ?: return)
diff --git a/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolRuleMatch.kt b/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolRuleMatch.kt
index b95f38da..6047ed5b 100644
--- a/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolRuleMatch.kt
+++ b/src/main/kotlin/org/bsplines/ltexls/languagetool/LanguageToolRuleMatch.kt
@@ -7,6 +7,8 @@
package org.bsplines.ltexls.languagetool
+import com.google.gson.JsonElement
+import com.google.gson.JsonObject
import org.bsplines.ltexls.parsing.AnnotatedTextFragment
import org.bsplines.ltexls.server.LtexTextDocumentItem
import org.bsplines.ltexls.tools.Tools
@@ -34,8 +36,10 @@ data class LanguageToolRuleMatch(
companion object {
private val TWO_OR_MORE_SPACES_REGEX = Regex("[ \n]{2,}")
- fun fromLanguageTool(match: RuleMatch, annotatedTextFragment: AnnotatedTextFragment):
- LanguageToolRuleMatch {
+ fun fromLanguageTool(
+ match: RuleMatch,
+ annotatedTextFragment: AnnotatedTextFragment,
+ ): LanguageToolRuleMatch {
return fromLanguageTool(
match.rule?.id,
match.sentence?.text,
@@ -48,6 +52,29 @@ data class LanguageToolRuleMatch(
)
}
+ fun fromLanguageTool(
+ jsonMatch: JsonObject,
+ annotatedTextFragment: AnnotatedTextFragment,
+ ): LanguageToolRuleMatch {
+ val fromPos: Int = jsonMatch.get("offset").asInt
+ val suggestedReplacements = ArrayList()
+
+ for (replacement: JsonElement in jsonMatch.get("replacements").asJsonArray) {
+ suggestedReplacements.add(replacement.asJsonObject.get("value").asString)
+ }
+
+ return fromLanguageTool(
+ jsonMatch.get("rule").asJsonObject.get("id").asString,
+ jsonMatch.get("sentence").asString,
+ fromPos,
+ fromPos + jsonMatch.get("length").asInt,
+ jsonMatch.get("message").asString,
+ suggestedReplacements,
+ RuleMatch.Type.Hint,
+ annotatedTextFragment,
+ )
+ }
+
@Suppress("LongParameterList")
fun fromLanguageTool(
ruleId: String?,
diff --git a/src/main/kotlin/org/bsplines/ltexls/server/CodeActionGenerator.kt b/src/main/kotlin/org/bsplines/ltexls/server/CodeActionGenerator.kt
index a02fd33c..44a187a0 100644
--- a/src/main/kotlin/org/bsplines/ltexls/server/CodeActionGenerator.kt
+++ b/src/main/kotlin/org/bsplines/ltexls/server/CodeActionGenerator.kt
@@ -99,8 +99,7 @@ class CodeActionGenerator(
document, newWord, acceptSuggestionsMatches)))
}
- if (addToDictionaryMatches.isNotEmpty()
- && this.settingsManager.settings.languageToolHttpServerUri.isEmpty()) {
+ if (addToDictionaryMatches.isNotEmpty()) {
result.add(Either.forRight(getAddWordToDictionaryCodeAction(document,
addToDictionaryMatches, annotatedTextFragments)))
}
diff --git a/src/main/kotlin/org/bsplines/ltexls/settings/Settings.kt b/src/main/kotlin/org/bsplines/ltexls/settings/Settings.kt
index 20fa0088..546253b6 100644
--- a/src/main/kotlin/org/bsplines/ltexls/settings/Settings.kt
+++ b/src/main/kotlin/org/bsplines/ltexls/settings/Settings.kt
@@ -89,14 +89,6 @@ data class Settings(
return differences
}
- if (dictionary != other.dictionary) {
- differences.add(SettingsDifference("dictionary", this.dictionary, other.dictionary))
- }
-
- if (disabledRules != other.disabledRules) {
- differences.add(SettingsDifference("disabledRules", this.disabledRules, other.disabledRules))
- }
-
if (enabledRules != other.enabledRules) {
differences.add(SettingsDifference("enabledRules", this.enabledRules, other.enabledRules))
}
diff --git a/src/main/kotlin/org/bsplines/ltexls/settings/SettingsManager.kt b/src/main/kotlin/org/bsplines/ltexls/settings/SettingsManager.kt
index be5d8f7c..db623af7 100644
--- a/src/main/kotlin/org/bsplines/ltexls/settings/SettingsManager.kt
+++ b/src/main/kotlin/org/bsplines/ltexls/settings/SettingsManager.kt
@@ -33,7 +33,11 @@ class SettingsManager {
value.getDifferencesRelevantForLanguageTool(oldSettings)
if (settingsDifferencesRelevantForLanguageTool.isEmpty()) {
- this.languageToolInterface = this.languageToolInterfaceMap[newLanguage]
+ val languageToolInterface: LanguageToolInterface? =
+ this.languageToolInterfaceMap[newLanguage]
+ this.languageToolInterface?.dictionary = value.dictionary
+ this.languageToolInterface?.disabledRules = value.disabledRules
+ this.languageToolInterface = languageToolInterface
} else {
if (Logging.logger.isLoggable(Level.FINE)) {
logDifferentSettings(newLanguage, settingsDifferencesRelevantForLanguageTool)
@@ -100,7 +104,6 @@ class SettingsManager {
}
languageToolInterface.enableRules(this.settings.enabledRules)
- languageToolInterface.disableRules(this.settings.disabledRules)
this.languageToolInterface = languageToolInterface
}
diff --git a/src/test/kotlin/org/bsplines/ltexls/settings/SettingsTest.kt b/src/test/kotlin/org/bsplines/ltexls/settings/SettingsTest.kt
index 23515749..4dd54598 100644
--- a/src/test/kotlin/org/bsplines/ltexls/settings/SettingsTest.kt
+++ b/src/test/kotlin/org/bsplines/ltexls/settings/SettingsTest.kt
@@ -35,13 +35,13 @@ class SettingsTest {
settings = settings.copy(_allDictionaries = settings.getModifiedDictionary(setOf("dictionary")))
assertEquals(setOf("dictionary"), settings.dictionary)
- settings2 = compareSettings(settings, settings2, true)
+ settings2 = compareSettings(settings, settings2, false)
settings = settings.copy(
_allDisabledRules = settings.getModifiedDisabledRules(setOf("disabledRules"))
)
assertEquals(setOf("disabledRules"), settings.disabledRules)
- settings2 = compareSettings(settings, settings2, true)
+ settings2 = compareSettings(settings, settings2, false)
settings = settings.copy(
_allEnabledRules = settings.getModifiedEnabledRules(setOf("enabledRules"))