mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[grazie] IJPL-201886 Support language and text domain handling in Configure Rule action
Co-authored-by: Peter Gromov <peter.gromov@jetbrains.com> Merge-request: IJ-MR-176906 Merged-by: Ilia Permiashkin <ilia.permiashkin@jetbrains.com> GitOrigin-RevId: cc4ee0281c9ef6b9526c1aea1e46a9161a1ba699
This commit is contained in:
committed by
intellij-monorepo-bot
co-authored by
Peter Gromov
parent
d633ca2c95
commit
418f329ce1
@@ -1,6 +1,7 @@
|
||||
package com.intellij.grazie.grammar
|
||||
|
||||
import com.intellij.grazie.GrazieBundle
|
||||
import com.intellij.grazie.detection.toLanguage
|
||||
import com.intellij.grazie.ide.ui.components.utils.html
|
||||
import com.intellij.grazie.jlanguage.Lang
|
||||
import com.intellij.grazie.jlanguage.LangTool
|
||||
@@ -17,7 +18,7 @@ import java.util.*
|
||||
// ltRule used in ReSharper
|
||||
class LanguageToolRule(
|
||||
private val lang: Lang, val ltRule: org.languagetool.rules.Rule, private val similarLtRules: List<org.languagetool.rules.Rule> = emptyList(),
|
||||
) : Rule(LangTool.globalIdPrefix(lang) + ltRule.id, ltRule.description, categories(ltRule, lang)) {
|
||||
) : Rule(LangTool.globalIdPrefix(lang) + ltRule.id, lang.toLanguage(), ltRule.description, categories(ltRule, lang)) {
|
||||
|
||||
override fun isEnabledByDefault(domain: TextStyleDomain): Boolean = LangTool.isRuleEnabledByDefault(lang, ltRule.id, domain)
|
||||
|
||||
|
||||
+15
-17
@@ -6,46 +6,44 @@ import com.intellij.codeInspection.IntentionAndQuickFixAction
|
||||
import com.intellij.grazie.GrazieConfig
|
||||
import com.intellij.grazie.ide.fus.GrazieFUSCounter
|
||||
import com.intellij.grazie.ide.ui.components.dsl.msg
|
||||
import com.intellij.grazie.ide.ui.grammar.GrazieConfigurable
|
||||
import com.intellij.grazie.ide.ui.configurable.StyleConfigurable
|
||||
import com.intellij.grazie.text.Rule
|
||||
import com.intellij.grazie.utils.TextStyleDomain
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.options.ShowSettingsUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Iconable
|
||||
import com.intellij.psi.PsiFile
|
||||
import javax.swing.Icon
|
||||
|
||||
class GrazieRuleSettingsAction(private val ruleName: String, private val rule: Rule)
|
||||
class GrazieRuleSettingsAction(private val rule: Rule, private val domain: TextStyleDomain)
|
||||
: IntentionAndQuickFixAction(), Iconable, CustomizableIntentionAction, Comparable<IntentionAction>
|
||||
{
|
||||
|
||||
override fun isShowSubmenu(): Boolean = false
|
||||
|
||||
override fun getIcon(flags: Int): Icon = AllIcons.Actions.Edit
|
||||
|
||||
override fun getName() = msg("grazie.grammar.quickfix.open.rule.text", ruleName)
|
||||
override fun getName(): String = msg(when (domain) {
|
||||
TextStyleDomain.Other -> "grazie.grammar.quickfix.open.rule.text"
|
||||
TextStyleDomain.Commit -> "grazie.grammar.quickfix.open.rule.text.commit"
|
||||
TextStyleDomain.CodeComment -> "grazie.grammar.quickfix.open.rule.text.comments"
|
||||
TextStyleDomain.CodeDocumentation -> "grazie.grammar.quickfix.open.rule.text.documentation"
|
||||
TextStyleDomain.AIPrompt -> "grazie.grammar.quickfix.open.rule.text.ai"
|
||||
}, rule.presentableName)
|
||||
|
||||
override fun getFamilyName(): String = msg("grazie.grammar.quickfix.open.rule.family")
|
||||
|
||||
override fun startInWriteAction() = false
|
||||
override fun startInWriteAction(): Boolean = false
|
||||
|
||||
override fun compareTo(other: IntentionAction): Int {
|
||||
return if (other is GrazieAddExceptionQuickFix) 1 else 0
|
||||
}
|
||||
|
||||
override fun applyFix(project: Project, psiFile: PsiFile?, editor: Editor?) {
|
||||
val state1 = GrazieConfig.get()
|
||||
|
||||
val ok: Boolean
|
||||
val navigatable = rule.editSettings()
|
||||
if (navigatable != null && navigatable.canNavigate()) {
|
||||
navigatable.navigate(true)
|
||||
ok = true
|
||||
} else {
|
||||
ok = ShowSettingsUtil.getInstance().editConfigurable(project, GrazieConfigurable())
|
||||
}
|
||||
|
||||
val result = if (!ok) "canceled" else analyzeStateChange(state1, GrazieConfig.get())
|
||||
val state = GrazieConfig.get()
|
||||
val ok = StyleConfigurable.focusSetting(rule.featuredSetting, rule, domain, rule.language, project)
|
||||
val result = if (!ok) "canceled" else analyzeStateChange(state, GrazieConfig.get())
|
||||
GrazieFUSCounter.settingsUpdated("rule.settings:$result", rule, project)
|
||||
}
|
||||
|
||||
|
||||
+117
-83
@@ -11,14 +11,12 @@ import ai.grazie.rules.toolkit.LanguageToolkit
|
||||
import com.intellij.grazie.GrazieBundle
|
||||
import com.intellij.grazie.GrazieConfig
|
||||
import com.intellij.grazie.detection.toLanguage
|
||||
import com.intellij.grazie.ide.ui.components.dsl.msg
|
||||
import com.intellij.grazie.ide.ui.configurable.StyleConfigurable.Companion.ruleEngineLanguages
|
||||
import com.intellij.grazie.ide.ui.grammar.tabs.rules.component.GrazieDescriptionComponent
|
||||
import com.intellij.grazie.ide.ui.grammar.tabs.rules.component.GrazieTreeComponent
|
||||
import com.intellij.grazie.rule.RuleIdeClient
|
||||
import com.intellij.grazie.utils.TextStyleDomain
|
||||
import com.intellij.grazie.utils.getAffectedGlobalRules
|
||||
import com.intellij.grazie.utils.getOtherDomainStyles
|
||||
import com.intellij.grazie.utils.getTextDomain
|
||||
import com.intellij.grazie.utils.*
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.openapi.Disposable
|
||||
@@ -31,7 +29,6 @@ import com.intellij.openapi.ui.DialogPanel
|
||||
import com.intellij.openapi.ui.getParentOfType
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.intellij.openapi.wm.IdeFocusManager
|
||||
import com.intellij.pom.Navigatable
|
||||
import com.intellij.psi.codeStyle.NameUtil
|
||||
import com.intellij.ui.*
|
||||
import com.intellij.ui.components.JBLabel
|
||||
@@ -55,7 +52,6 @@ import javax.swing.event.DocumentEvent
|
||||
import javax.swing.event.HyperlinkEvent
|
||||
|
||||
class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.settings.grammar.tabs.rules"), null), Disposable, Configurable.NoScroll {
|
||||
private var focusedControl: JComponent? = null
|
||||
private val settings: Settings = Settings()
|
||||
private val langComboModel = CollectionComboBoxModel(ArrayList<Language>())
|
||||
private lateinit var langCombo: ComboBox<Language>
|
||||
@@ -72,7 +68,7 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
|
||||
private val treeWrapper by lazy {
|
||||
JBSplitter(false, 0.45f).apply {
|
||||
firstComponent = createScrollTreeComponent()
|
||||
firstComponent = createScrollTreeComponent(textStyle, Language.ENGLISH)
|
||||
secondComponent = settings.getTreeSettings(textStyle, Language.ENGLISH).description.component
|
||||
}
|
||||
}
|
||||
@@ -118,7 +114,6 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
label(GrazieBundle.message("grazie.settings.writing.style.domain"))
|
||||
domainComboBox = domainComboBox()
|
||||
.whenItemSelectedFromUi { domainId ->
|
||||
styleRowVisibleUpdater.invoke(domainId == TextStyleDomain.Other)
|
||||
val textStyle = if (domainId == TextStyleDomain.Other) GrazieConfig.get().getTextStyle() else domainId.textStyle
|
||||
selectTextStyle(textStyle, langComboModel.selected!!)
|
||||
}
|
||||
@@ -151,8 +146,7 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
langCombo = comboBox(langComboModel, SimpleListCellRenderer.create { label, lang, _ -> label.text = lang.nativeName })
|
||||
.widthGroup("TopCombo")
|
||||
.whenItemSelectedFromUi { language ->
|
||||
settings.addTextStyle(textStyle, language, filterComponent)
|
||||
selectLanguage(textStyle, language)
|
||||
selectTextStyle(textStyle, language)
|
||||
separator.text = GrazieBundle.message(if (language in ruleEngineLanguages) "grazie.settings.style.rules.other" else "grazie.settings.style.rules.all")
|
||||
if (filterComponent.text.isNotBlank()) settings.updateFilter(textStyle, language, filterComponent.text)
|
||||
settings.getTreeSettings(textStyle, language).description.listener(language)
|
||||
@@ -190,11 +184,11 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
|
||||
override fun createPanel(): DialogPanel = component
|
||||
|
||||
override fun isModified(): Boolean = super<BoundConfigurable>.isModified || settings.isModified()
|
||||
override fun isModified(): Boolean = super<BoundConfigurable>.isModified || settings.isModified(GrazieConfig.get())
|
||||
|
||||
override fun apply() {
|
||||
super.apply()
|
||||
settings.apply()
|
||||
settings.apply(GrazieConfig.get())
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
@@ -207,7 +201,12 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
}
|
||||
|
||||
private fun selectTextStyle(textStyle: TextStyle, language: Language) {
|
||||
val domain = textStyle.getTextDomain()
|
||||
if (domain != TextStyleDomain.Other) domainComboBox.selectedItem = domain else styleProfileCombo.selectedItem = textStyle
|
||||
styleRowVisibleUpdater.invoke(domain == TextStyleDomain.Other)
|
||||
|
||||
settings.addTextStyle(textStyle, language, filterComponent)
|
||||
langCombo.selectedItem = language
|
||||
repaintSettings(textStyle, language)
|
||||
}
|
||||
|
||||
@@ -226,7 +225,8 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
settingWrapper.maximumSize = Dimension(Int.MAX_VALUE, settingWrapper.preferredSize.height)
|
||||
settingWrapper.repaint()
|
||||
}
|
||||
treeWrapper.firstComponent = createScrollTreeComponent()
|
||||
treeWrapper.removeAll()
|
||||
treeWrapper.firstComponent = createScrollTreeComponent(textStyle, language)
|
||||
treeWrapper.secondComponent = settings.getTreeSettings(textStyle, language).description.component
|
||||
treeWrapper.repaint()
|
||||
}
|
||||
@@ -248,15 +248,11 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getDisplayName(): @NlsContexts.ConfigurableName String = ""
|
||||
override fun getDisplayName(): @NlsContexts.ConfigurableName String = msg("grazie.settings.page.name")
|
||||
|
||||
override fun getPreferredFocusedComponent(): JComponent? {
|
||||
return focusedControl ?: super.getPreferredFocusedComponent()
|
||||
}
|
||||
|
||||
private fun createScrollTreeComponent(): JScrollPane {
|
||||
private fun createScrollTreeComponent(textStyle: TextStyle, language: Language): JScrollPane {
|
||||
return ScrollPaneFactory.createScrollPane(
|
||||
settings.getTreeSettings(textStyle, Language.ENGLISH).tree,
|
||||
settings.getTreeSettings(textStyle, language).tree,
|
||||
VERTICAL_SCROLLBAR_AS_NEEDED,
|
||||
HORIZONTAL_SCROLLBAR_AS_NEEDED
|
||||
)
|
||||
@@ -268,37 +264,61 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
val ruleEngineLanguages: List<Language> = listOf(Language.ENGLISH, Language.GERMAN, Language.RUSSIAN, Language.UKRAINIAN)
|
||||
|
||||
@JvmStatic
|
||||
fun featuredSettings(toolkit: LanguageToolkit): List<Setting> = toolkit.getSettings(RuleIdeClient.INSTANCE).flatMap { it.settings }
|
||||
fun focusSetting(setting: Setting, domain: TextStyleDomain, language: Language, project: Project): Boolean {
|
||||
return focusSetting(setting, null, domain, language, project)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun focusSetting(setting: Setting, contextProject: Project): Navigatable {
|
||||
return object : Navigatable {
|
||||
override fun navigate(requestFocus: Boolean) {
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(contextProject, StyleConfigurable::class.java) { conf ->
|
||||
conf.createComponent()
|
||||
val style = getTextStyle(GrazieConfig.get().styleProfile ?: TextStyle.Unspecified.id)
|
||||
val featuredSettings = conf.settings.featuredSettings[style.id]!!
|
||||
for ((lang, data) in featuredSettings) {
|
||||
val settingComponent = data.component.findParentSettingComponent(setting)
|
||||
val paramComponent = data.component.findOwnSettingComponent(setting)
|
||||
if (paramComponent != null && settingComponent != null) {
|
||||
conf.langComboModel.add(lang)
|
||||
conf.selectLanguage(style, lang)
|
||||
UiNotifyConnector.doWhenFirstShown(data.component) {
|
||||
SwingUtilities.invokeLater {
|
||||
val scrollPane = settingComponent.getParentOfType<JBScrollPane>()!!
|
||||
settingComponent.scrollRectToVisible(Rectangle(settingComponent.width, scrollPane.height))
|
||||
IdeFocusManager.getInstance(contextProject).requestFocus(paramComponent, true)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
fun focusSetting(setting: Setting?, rule: com.intellij.grazie.text.Rule?, domain: TextStyleDomain, language: Language, project: Project): Boolean {
|
||||
require(setting != null || rule != null) { "Setting and Rule can't be null" }
|
||||
val configurable = StyleConfigurable().apply {
|
||||
createComponent()
|
||||
val style = if (domain == TextStyleDomain.Other) GrazieConfig.get().getTextStyle() else domain.textStyle
|
||||
selectTextStyle(style, language)
|
||||
|
||||
if (setting != null && featuredSettings(language).contains(setting)) focusFeaturedSetting(this, setting, style, language, project)
|
||||
else if (rule != null) focusTreeSetting(this, rule, style, language, project)
|
||||
}
|
||||
return ShowSettingsUtil.getInstance().editConfigurable(project, configurable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun focusFeaturedSetting(styleConfigurable: StyleConfigurable, setting: Setting, style: TextStyle, language: Language, project: Project) {
|
||||
val data = settings.getFeaturedSettings(style, language)
|
||||
if (data == null) return
|
||||
styleConfigurable.apply {
|
||||
val settingComponent = data.component.findParentSettingComponent(setting)
|
||||
val paramComponent = data.component.findOwnSettingComponent(setting)
|
||||
if (paramComponent != null && settingComponent != null) {
|
||||
UiNotifyConnector.doWhenFirstShown(data.component) {
|
||||
SwingUtilities.invokeLater {
|
||||
val scrollPane = settingComponent.getParentOfType<JBScrollPane>()!!
|
||||
settingComponent.scrollRectToVisible(Rectangle(settingComponent.width, scrollPane.height))
|
||||
IdeFocusManager.getInstance(project).requestFocus(paramComponent, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun canNavigate() = true
|
||||
override fun canNavigateToSource() = false
|
||||
private fun focusTreeSetting(styleConfigurable: StyleConfigurable, rule: com.intellij.grazie.text.Rule, style: TextStyle, language: Language, project: Project) {
|
||||
val data = settings.getTreeSettings(style, language)
|
||||
styleConfigurable.apply {
|
||||
data.tree.focusRule(rule)
|
||||
UiNotifyConnector.doWhenFirstShown(data.tree) {
|
||||
SwingUtilities.invokeLater {
|
||||
val scroll = data.tree.getParentOfType<JBScrollPane>()
|
||||
val dataTreeScroll = scroll?.getParentOfType<JBScrollPane>()
|
||||
val componentScroll = dataTreeScroll?.getParentOfType<JBScrollPane>()
|
||||
if (dataTreeScroll == null || componentScroll == null) return@invokeLater
|
||||
val destination = SwingUtilities.convertRectangle(
|
||||
scroll,
|
||||
Rectangle(0, 0, scroll.width, scroll.height),
|
||||
dataTreeScroll.viewport.view
|
||||
)
|
||||
componentScroll.scrollRectToVisible(destination)
|
||||
IdeFocusManager.getInstance(project).requestFocus(data.tree, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,19 +326,19 @@ class StyleConfigurable : BoundConfigurable(GrazieBundle.message("grazie.setting
|
||||
|
||||
data class Settings(
|
||||
val featuredSettings: MutableMap<String, MutableMap<Language, FeaturedSettings>> = HashMap(),
|
||||
private val treeSettings: MutableMap<String, MutableMap<Language, TreeSettings>> = HashMap(),
|
||||
val treeSettings: MutableMap<String, MutableMap<Language, TreeSettings>> = HashMap(),
|
||||
) {
|
||||
fun getFeaturedSettings(textStyle: TextStyle, language: Language): FeaturedSettings? = featuredSettings[textStyle.id]?.get(language)
|
||||
fun getTreeSettings(textStyle: TextStyle, language: Language): TreeSettings = treeSettings[textStyle.id]!![language]!!
|
||||
|
||||
fun isModified(): Boolean =
|
||||
featuredSettings.values.any { isModifiedFeaturedSettings(it) } ||
|
||||
treeSettings.values.any { isModifiedTreeSettings(it) }
|
||||
fun isModified(state: GrazieConfig.State): Boolean =
|
||||
featuredSettings.values.any { areModifiedFeaturedSettings(it) } ||
|
||||
treeSettings.values.any { areModifiedTreeSettings(it, state) }
|
||||
|
||||
fun reset(state: GrazieConfig.State) {
|
||||
featuredSettings.forEach { (domain, featuredSettings) ->
|
||||
if (isModifiedFeaturedSettings(featuredSettings)) {
|
||||
featuredSettings.forEach { (language, settings) ->
|
||||
if (areModifiedFeaturedSettings(featuredSettings)) {
|
||||
featuredSettings.filter { areModifiedFeaturedSettings(it.value) }.forEach { (language, settings) ->
|
||||
val textStyle = getTextStyle(domain)
|
||||
settings.component.loadState(getSettingsState(language, textStyle), textStyle)
|
||||
settings.resetState = settings.component.state
|
||||
@@ -326,57 +346,63 @@ data class Settings(
|
||||
}
|
||||
}
|
||||
treeSettings.forEach { (_, treesSettings) ->
|
||||
if (isModifiedTreeSettings(treesSettings)) {
|
||||
treesSettings.forEach { (_, settings) -> settings.tree.reset(state) }
|
||||
if (areModifiedTreeSettings(treesSettings, state)) {
|
||||
treesSettings.filter { areModifiedTreeSettings(it.value, state) }.forEach { (_, settings) -> settings.tree.reset(state) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun apply() {
|
||||
fun apply(originalState: GrazieConfig.State) {
|
||||
treeSettings
|
||||
.filter { (domainId, treeSettingsMap) -> isModifiedFeaturedSettings(featuredSettings[domainId]!!) || isModifiedTreeSettings(treeSettingsMap) }
|
||||
.filter { (domainId, treeSettingsMap) -> areModifiedFeaturedSettings(featuredSettings[domainId]!!) || areModifiedTreeSettings(treeSettingsMap, originalState) }
|
||||
.forEach { (domainId, treeSettingsMap) ->
|
||||
val domain = getTextStyle(domainId).getTextDomain()
|
||||
val userEnabledRules = HashSet<String>()
|
||||
val userDisabledRules = HashSet<String>()
|
||||
val parameters = HashMap<Language, Map<String, String>>()
|
||||
|
||||
featuredSettings[domainId]?.forEach { (language, settings) ->
|
||||
val prefix = Rule.globalIdPrefix(language)
|
||||
val settingsState = settings.component.state
|
||||
settings.resetState = settingsState
|
||||
treeSettingsMap.filter { areModifiedTreeSettings(it.value, originalState) }.forEach { (language, treeSettings) ->
|
||||
val userEnabledRulesPerLanguage = HashSet<String>()
|
||||
val userDisabledRulesPerLanguage = HashSet<String>()
|
||||
|
||||
for (id in settingsState.enabledRules) {
|
||||
userEnabledRules.add(prefix + id)
|
||||
userDisabledRules.remove(prefix + id)
|
||||
featuredSettings[domainId]?.filter { areModifiedFeaturedSettings(it.value) }?.get(language)?.let { featuredSettings ->
|
||||
val prefix = Rule.globalIdPrefix(language)
|
||||
val settingsState = featuredSettings.component.state
|
||||
featuredSettings.resetState = settingsState
|
||||
|
||||
for (id in settingsState.enabledRules) {
|
||||
userEnabledRulesPerLanguage.add(prefix + id)
|
||||
userDisabledRulesPerLanguage.remove(prefix + id)
|
||||
}
|
||||
for (id in settingsState.disabledRules) {
|
||||
userEnabledRulesPerLanguage.remove(prefix + id)
|
||||
userDisabledRulesPerLanguage.add(prefix + id)
|
||||
}
|
||||
parameters[language] = settingsState.paramValues
|
||||
}
|
||||
for (id in settingsState.disabledRules) {
|
||||
userEnabledRules.remove(prefix + id)
|
||||
userDisabledRules.add(prefix + id)
|
||||
|
||||
val updatedState = treeSettings.tree.apply(originalState)
|
||||
val affectedGlobalRules = getAffectedGlobalRules(language)
|
||||
updatedState.getUserChangedRules(domain).let { (enabledRules, disabledRules) ->
|
||||
userEnabledRulesPerLanguage.addAll(enabledRules - affectedGlobalRules)
|
||||
userDisabledRulesPerLanguage.addAll(disabledRules - affectedGlobalRules)
|
||||
}
|
||||
parameters[language] = settingsState.paramValues
|
||||
userEnabledRules.addAll(userEnabledRulesPerLanguage)
|
||||
userDisabledRules.addAll(userDisabledRulesPerLanguage)
|
||||
}
|
||||
|
||||
if (domain == TextStyleDomain.Other) GrazieConfig.update { it.copy(parameters = parameters) }
|
||||
else GrazieConfig.update { it.copy(parametersPerDomain = mapOf(domain to parameters)) }
|
||||
|
||||
treeSettingsMap.forEach {
|
||||
val updatedState = it.value.tree.apply(GrazieConfig.get())
|
||||
val affectedGlobalRules = getAffectedGlobalRules(it.key)
|
||||
updatedState.getUserChangedRules(domain).let { (enabledRules, disabledRules) ->
|
||||
userEnabledRules.addAll(enabledRules - affectedGlobalRules)
|
||||
userDisabledRules.addAll(disabledRules - affectedGlobalRules)
|
||||
}
|
||||
}
|
||||
GrazieConfig.update { it.updateUserRules(domain, userEnabledRules, userDisabledRules) }
|
||||
treeSettingsMap.forEach { it.value.tree.reset(GrazieConfig.get()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun addTextStyle(textStyle: TextStyle, language: Language, filterComponent: SearchTextField) {
|
||||
if (textStyle.id in featuredSettings && textStyle.id in treeSettings) return
|
||||
featuredSettings[textStyle.id] = HashMap()
|
||||
treeSettings[textStyle.id] = HashMap()
|
||||
val settings = treeSettings[textStyle.id]
|
||||
if (settings != null && language in settings) return
|
||||
if (featuredSettings[textStyle.id] == null) featuredSettings[textStyle.id] = HashMap()
|
||||
if (treeSettings[textStyle.id] == null) treeSettings[textStyle.id] = HashMap()
|
||||
addLanguage(textStyle, language, filterComponent)
|
||||
}
|
||||
|
||||
@@ -451,12 +477,20 @@ data class Settings(
|
||||
treeSettings.clear()
|
||||
}
|
||||
|
||||
private fun isModifiedFeaturedSettings(featuredSettings: MutableMap<Language, FeaturedSettings>): Boolean {
|
||||
return featuredSettings.any { it.value.component.state != it.value.resetState }
|
||||
private fun areModifiedFeaturedSettings(featuredSettings: MutableMap<Language, FeaturedSettings>): Boolean {
|
||||
return featuredSettings.any { areModifiedFeaturedSettings(it.value) }
|
||||
}
|
||||
|
||||
private fun isModifiedTreeSettings(treeSettings: MutableMap<Language, TreeSettings>): Boolean {
|
||||
return treeSettings.any { it.value.tree.isModified(GrazieConfig.get()) }
|
||||
private fun areModifiedTreeSettings(treeSettings: MutableMap<Language, TreeSettings>, state: GrazieConfig.State): Boolean {
|
||||
return treeSettings.any { areModifiedTreeSettings(it.value, state) }
|
||||
}
|
||||
|
||||
private fun areModifiedFeaturedSettings(featuredSettings: FeaturedSettings): Boolean {
|
||||
return featuredSettings.component.state != featuredSettings.resetState
|
||||
}
|
||||
|
||||
private fun areModifiedTreeSettings(treeSettings: TreeSettings, state: GrazieConfig.State): Boolean {
|
||||
return treeSettings.tree.isModified(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -105,6 +105,13 @@ class GrazieTreeComponent(
|
||||
|
||||
fun getCurrentFilterString(): String? = filter.text
|
||||
|
||||
fun focusRule(rule: Rule) {
|
||||
val ruleNode = (model.root as GrazieRulesTreeNode).findRuleNode(rule.globalId)
|
||||
if (ruleNode != null) {
|
||||
TreeUtil.selectNode(this, ruleNode)
|
||||
}
|
||||
}
|
||||
|
||||
fun resetTreeModel(rules: List<Rule>) {
|
||||
val root = GrazieRulesTreeNode(domain)
|
||||
val model = model as DefaultTreeModel
|
||||
|
||||
+5
-2
@@ -1,14 +1,17 @@
|
||||
package com.intellij.grazie.style;
|
||||
|
||||
import ai.grazie.nlp.langs.Language;
|
||||
import ai.grazie.rules.tree.Parameter;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName;
|
||||
import com.intellij.grazie.ide.ui.configurable.StyleConfigurable;
|
||||
import com.intellij.grazie.utils.TextStyleDomain;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record ConfigureSuggestedParameter(Parameter parameter, String text) implements LocalQuickFix {
|
||||
public record ConfigureSuggestedParameter(Parameter parameter, TextStyleDomain domain, Language language, String text)
|
||||
implements LocalQuickFix {
|
||||
|
||||
public ConfigureSuggestedParameter {
|
||||
assert !parameter.id().equals(Parameter.LANGUAGE_VARIANT);
|
||||
@@ -26,7 +29,7 @@ public record ConfigureSuggestedParameter(Parameter parameter, String text) impl
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
StyleConfigurable.focusSetting(parameter, project).navigate(true);
|
||||
StyleConfigurable.focusSetting(parameter, domain, language, project);
|
||||
//todo FUS?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.grazie.ide.inspection.grammar.quickfix.GrazieCustomFixWrappe
|
||||
import com.intellij.grazie.ide.inspection.grammar.quickfix.GrazieReplaceTypoQuickFix
|
||||
import com.intellij.grazie.ide.inspection.grammar.quickfix.GrazieRuleSettingsAction
|
||||
import com.intellij.grazie.ide.language.LanguageGrammarChecking
|
||||
import com.intellij.grazie.utils.getTextDomain
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
@@ -233,7 +234,7 @@ class CheckerRunner(val text: TextContent) {
|
||||
super.applyFix(project, psiFile, editor)
|
||||
}
|
||||
})
|
||||
result.add(GrazieRuleSettingsAction(problem.rule.presentableName, problem.rule))
|
||||
result.add(GrazieRuleSettingsAction(problem.rule, problem.text.getTextDomain()))
|
||||
return result.toTypedArray()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.intellij.grazie.text;
|
||||
|
||||
import ai.grazie.nlp.langs.Language;
|
||||
import ai.grazie.rules.settings.Setting;
|
||||
import com.intellij.grazie.GrazieConfig;
|
||||
import com.intellij.grazie.utils.TextStyleDomain;
|
||||
import com.intellij.grazie.utils.TextUtilsKt;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -14,15 +15,16 @@ import java.util.Objects;
|
||||
/** A representation of rules from various {@link TextChecker}s. */
|
||||
public abstract class Rule {
|
||||
private final String globalId;
|
||||
private final Language language;
|
||||
private final String presentableName;
|
||||
private final List<String> categories;
|
||||
|
||||
/**
|
||||
* Create a rule in a single category
|
||||
* @see #Rule(String, String, List)
|
||||
* @see #Rule(String, Language, String, List)
|
||||
*/
|
||||
public Rule(String globalId, String presentableName, String category) {
|
||||
this(globalId, presentableName, List.of(category));
|
||||
public Rule(String globalId, Language language, String presentableName, String category) {
|
||||
this(globalId, language, presentableName, List.of(category));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,8 +36,9 @@ public abstract class Rule {
|
||||
* @param categories a non-empty list of the presentable names of the rule's categories.
|
||||
* It's used to group the rules in the settings, starting from the language node.
|
||||
*/
|
||||
public Rule(String globalId, String presentableName, List<String> categories) {
|
||||
public Rule(String globalId, Language language, String presentableName, List<String> categories) {
|
||||
this.globalId = globalId;
|
||||
this.language = language;
|
||||
this.presentableName = presentableName;
|
||||
this.categories = List.copyOf(categories);
|
||||
if (categories.isEmpty()) {
|
||||
@@ -54,6 +57,13 @@ public abstract class Rule {
|
||||
return presentableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the language that the rule is applicable to
|
||||
*/
|
||||
public Language getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a text/HTML description of what the rule does, to be displayed in settings
|
||||
*/
|
||||
@@ -100,9 +110,9 @@ public abstract class Rule {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an optional navigatable to open from "Rule X settings" quick fix.
|
||||
* @return an optional featured rule setting to open from "Rule X settings" quick fix.
|
||||
*/
|
||||
public @Nullable Navigatable editSettings() {
|
||||
public @Nullable Setting getFeaturedSetting() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import ai.grazie.rules.document.Delimiter;
|
||||
import ai.grazie.rules.document.DocumentRule;
|
||||
import ai.grazie.rules.document.DocumentSentence;
|
||||
import ai.grazie.rules.settings.RuleSetting;
|
||||
import ai.grazie.rules.settings.Setting;
|
||||
import ai.grazie.rules.settings.TextStyle;
|
||||
import ai.grazie.rules.toolkit.LanguageToolkit;
|
||||
import ai.grazie.rules.tree.ActionSuggestion;
|
||||
@@ -35,15 +36,12 @@ import com.intellij.grazie.text.TextContent.TextDomain;
|
||||
import com.intellij.grazie.utils.HighlightingUtil;
|
||||
import com.intellij.grazie.utils.Text;
|
||||
import com.intellij.grazie.utils.TextStyleDomain;
|
||||
import com.intellij.grazie.utils.TextUtilsKt;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Attachment;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vcs.ui.CommitMessage;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiPlainTextFile;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
@@ -134,7 +132,7 @@ public final class TreeRuleChecker {
|
||||
List<String> categories = rule.isStyleLike()
|
||||
? List.of(styleCategories.get(langCode))
|
||||
: List.of(category);
|
||||
return new Rule(id, rule.displayName, categories.getFirst()) {
|
||||
return new Rule(id, rule.language(), rule.displayName, categories.getFirst()) {
|
||||
|
||||
@Override
|
||||
public List<String> getCategories() {
|
||||
@@ -170,14 +168,9 @@ public final class TreeRuleChecker {
|
||||
return rule.isRuleEnabledByDefault(GrazieConfig.Companion.get().getTextStyle(domain), RuleIdeClient.INSTANCE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SuspiciousMethodCalls")//false negative in Qodana
|
||||
@Override
|
||||
public Navigatable editSettings() {
|
||||
RuleSetting setting = new RuleSetting(rule);
|
||||
LanguageToolkit toolkit = LanguageToolkit.forLanguage(rule.language());
|
||||
return StyleConfigurable.featuredSettings(toolkit).contains(setting)
|
||||
? StyleConfigurable.focusSetting(setting, null)
|
||||
: null;
|
||||
public Setting getFeaturedSetting() {
|
||||
return new RuleSetting(rule);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -540,6 +533,7 @@ public final class TreeRuleChecker {
|
||||
public static class TreeProblem extends GrazieProblem {
|
||||
public final RuleMatch match;
|
||||
private final List<LocalQuickFix> customFixes;
|
||||
private final TextStyleDomain domain;
|
||||
|
||||
TreeProblem(Problem problem, RuleMatch match, TextContent text) {
|
||||
this(problem, toGrazieRule(match.rule()), text, match, List.of());
|
||||
@@ -551,6 +545,7 @@ public final class TreeRuleChecker {
|
||||
super(problem, ideaRule, text);
|
||||
this.match = match;
|
||||
this.customFixes = customFixes;
|
||||
this.domain = getTextDomain(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -567,7 +562,7 @@ public final class TreeRuleChecker {
|
||||
if (parameter.id().equals(Parameter.LANGUAGE_VARIANT)) {
|
||||
return ChangeLanguageVariant.create(match.rule().language(), Objects.requireNonNull(suggestedValue), quickFixText);
|
||||
}
|
||||
return new ConfigureSuggestedParameter(parameter, quickFixText);
|
||||
return new ConfigureSuggestedParameter(parameter, domain, match.rule().language(), quickFixText);
|
||||
}
|
||||
if (sug == ActionSuggestion.REPHRASE) {
|
||||
return new RephraseAction();
|
||||
@@ -612,24 +607,6 @@ public final class TreeRuleChecker {
|
||||
public boolean shouldSuppressInCodeLikeFragments() {
|
||||
return match.rule().shouldSuppressInCodeLikeFragments();
|
||||
}
|
||||
|
||||
private record MySuggestion(ProblemFix fix, TextContent text) implements TextProblem.Suggestion {
|
||||
|
||||
@Override
|
||||
public List<StringOperation> getChanges() {
|
||||
return ContainerUtil.map(fix.getChanges(), r -> StringOperation.replace(ijRange(r), r.getText()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPresentableText() {
|
||||
return getQuickFixText(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getBatchId() {
|
||||
return fix.getBatchId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DocProblemFilter extends ProblemFilter {
|
||||
|
||||
@@ -4,10 +4,11 @@ import ai.grazie.gec.model.problem.ProblemHighlighting
|
||||
import ai.grazie.nlp.langs.Language
|
||||
import ai.grazie.rules.Rule
|
||||
import ai.grazie.rules.settings.RuleSetting
|
||||
import ai.grazie.rules.settings.Setting
|
||||
import ai.grazie.rules.toolkit.LanguageToolkit
|
||||
import com.intellij.grazie.detection.LangDetector
|
||||
import com.intellij.grazie.ide.ui.configurable.StyleConfigurable.Companion.featuredSettings
|
||||
import com.intellij.grazie.ide.ui.configurable.StyleConfigurable.Companion.ruleEngineLanguages
|
||||
import com.intellij.grazie.rule.RuleIdeClient
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.util.containers.ContainerUtil.createConcurrentSoftValueMap
|
||||
import ai.grazie.text.TextRange as GrazieTextRange
|
||||
@@ -17,15 +18,24 @@ private val affectedGlobalRules = createConcurrentSoftValueMap<Language, Set<Str
|
||||
fun getAffectedGlobalRules(language: Language): Set<String> {
|
||||
if (language !in ruleEngineLanguages) return emptySet()
|
||||
return affectedGlobalRules.computeIfAbsent(language) {
|
||||
val toolkit = LanguageToolkit.forLanguage(language)
|
||||
val prefix = Rule.globalIdPrefix(language)
|
||||
featuredSettings(toolkit)
|
||||
featuredSettings(language)
|
||||
.filterIsInstance<RuleSetting>()
|
||||
.map { prefix + it.rule.id }
|
||||
.toHashSet()
|
||||
}
|
||||
}
|
||||
|
||||
fun featuredSettings(language: Language): List<Setting> =
|
||||
LanguageToolkit.forLanguage(language).getSettings(RuleIdeClient.INSTANCE)
|
||||
.flatMap { it.settings() }
|
||||
.flatMap { setting ->
|
||||
when (setting) {
|
||||
is RuleSetting -> listOf(setting) + setting.subSettings
|
||||
else -> listOf(setting)
|
||||
}
|
||||
}
|
||||
|
||||
fun getLanguageIfAvailable(text: String): Language? {
|
||||
return LangDetector.getLanguage(text)?.takeIf { HighlightingUtil.findInstalledLang(it) != null }
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ grazie.grammar.inspection.grammar.text=Grammar
|
||||
|
||||
grazie.grammar.quickfix.open.rule.family=Configure rule
|
||||
grazie.grammar.quickfix.open.rule.text=Configure rule ''{0}''\u2026
|
||||
grazie.grammar.quickfix.open.rule.text.commit=Configure rule ''{0}'' in commit messages\u2026
|
||||
grazie.grammar.quickfix.open.rule.text.documentation=Configure rule ''{0}'' in documentation\u2026
|
||||
grazie.grammar.quickfix.open.rule.text.comments=Configure rule ''{0}'' in comments\u2026
|
||||
grazie.grammar.quickfix.open.rule.text.ai=Configure rule ''{0}'' in AI prompts\u2026
|
||||
|
||||
grazie.grammar.quickfix.ignore.family=Add exception
|
||||
grazie.grammar.quickfix.ignore.text.no.context=Ignore ''{0}''
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package com.intellij.grazie.ide.language
|
||||
|
||||
import ai.grazie.nlp.langs.Language
|
||||
import com.intellij.grazie.text.*
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
@@ -110,7 +111,7 @@ class CheckerRunnerTest: BasePlatformTestCase() {
|
||||
}
|
||||
|
||||
private fun createFakeProblem(text: TextContent, range: TextRange): TextProblem {
|
||||
val rule = object: Rule("fake.global.id", "Fake rule", "Fake category") {
|
||||
val rule = object : Rule("fake.global.id", Language.UNKNOWN, "Fake rule", "Fake category") {
|
||||
override fun getDescription(): String {
|
||||
return "Fake rule description"
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ class CommitSupportTest : GrazieTestBase() {
|
||||
myFixture.findSingleIntention("Wrong article")
|
||||
myFixture.findSingleIntention("an")
|
||||
myFixture.findSingleIntention("Ignore 'a error'")
|
||||
myFixture.findSingleIntention("Configure rule 'Use of 'a' vs. 'an''…")
|
||||
myFixture.findSingleIntention("Configure rule 'Use of 'a' vs. 'an'' in commit messages…")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.intellij.grazie.ide.language
|
||||
|
||||
import ai.grazie.nlp.langs.Language
|
||||
import com.intellij.codeInsight.CodeInsightBundle
|
||||
import com.intellij.codeInsight.daemon.impl.actions.IntentionActionWithFixAllOption
|
||||
import com.intellij.codeInsight.intention.CustomizableIntentionAction
|
||||
@@ -180,7 +181,7 @@ class ReportingTest : BasePlatformTestCase() {
|
||||
range: TextRange,
|
||||
suggestions: List<TextProblem.Suggestion>,
|
||||
customFixes: List<LocalQuickFix>): TextProblem {
|
||||
val rule = object : Rule("something.something", "something", "something") {
|
||||
val rule = object : Rule("something.something", Language.UNKNOWN, "something", "something") {
|
||||
override fun getDescription() = "something"
|
||||
}
|
||||
return object : TextProblem(rule, text, range) {
|
||||
|
||||
Reference in New Issue
Block a user