Skip to content

Commit 0fe5ff7

Browse files
author
Patrice de Saint Steban
committed
🔧 Add project-specific settings option and migration support
1 parent 6dd8872 commit 0fe5ff7

5 files changed

Lines changed: 221 additions & 29 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.github.patou.gitmoji
2+
3+
import com.intellij.ide.util.PropertiesComponent
4+
import com.intellij.openapi.project.Project
5+
6+
object ConfigUtil {
7+
/**
8+
* Return PropertiesComponent to use for a given project.
9+
* Reads from project if CONFIG_USE_PROJECT_SETTINGS is true, otherwise from application.
10+
*/
11+
fun propsFor(project: Project?): PropertiesComponent {
12+
val appProps = PropertiesComponent.getInstance()
13+
if (project == null) return appProps
14+
15+
return try {
16+
val projProps = PropertiesComponent.getInstance(project)
17+
val useProject = projProps.getBoolean(CONFIG_USE_PROJECT_SETTINGS, false)
18+
if (useProject) projProps else appProps
19+
} catch (_: Exception) {
20+
appProps
21+
}
22+
}
23+
}

src/main/kotlin/com/github/patou/gitmoji/GitCommitAction.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.github.patou.gitmoji
22

3-
import com.intellij.ide.util.PropertiesComponent
43
import com.intellij.openapi.actionSystem.ActionUpdateThread
54
import com.intellij.openapi.actionSystem.AnAction
65
import com.intellij.openapi.actionSystem.AnActionEvent
@@ -61,7 +60,7 @@ class GitCommitAction : AnAction() {
6160
var wasChosen = false
6261
val rightMargin = getSubjectRightMargin(project)
6362
val previewCommandGroup = sentinel("Preview Commit Message")
64-
val projectInstance = PropertiesComponent.getInstance(project)
63+
val projectInstance = ConfigUtil.propsFor(project)
6564
val displayEmoji =
6665
projectInstance.getValue(CONFIG_DISPLAY_ICON, defaultDisplayType()) == "emoji"
6766
val currentCommitMessage = commitMessage.editorField.text
@@ -134,7 +133,7 @@ class GitCommitAction : AnAction() {
134133
groupId: Any
135134
) =
136135
CommandProcessor.getInstance().executeCommand(project, {
137-
val projectInstance = PropertiesComponent.getInstance(project)
136+
val projectInstance = ConfigUtil.propsFor(project)
138137
val useUnicode = projectInstance.getBoolean(CONFIG_USE_UNICODE, false)
139138
val insertInCarretPosition = projectInstance.getBoolean(CONFIG_INSERT_IN_CURSOR_POSITION, false)
140139
val includeGitMojiDescription = projectInstance.getBoolean(CONFIG_INCLUDE_GITMOJI_DESCRIPTION, false)

src/main/kotlin/com/github/patou/gitmoji/GitMojiConfig.kt

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import javax.swing.JPanel
1515

1616
class GitMojiConfig(private val project: Project) : SearchableConfigurable {
1717
private val mainPanel: JPanel
18+
private val useProjectSettings = JCheckBox("Use project-specific settings (instead of global)")
1819
private val useUnicode = JCheckBox(GitmojiBundle.message("config.useUnicode"))
1920
private val displayEmoji =
2021
JCheckBox(GitmojiBundle.message("config.displayEmoji"))
2122
private val insertInCursorPosition = JCheckBox(GitmojiBundle.message("config.insertInCursorPosition"))
2223
private val includeGitMojiDescription = JCheckBox(GitmojiBundle.message("config.includeGitMojiDescription"))
24+
private var useProjectSettingsConfig: Boolean = false
2325
private var useUnicodeConfig: Boolean = false
2426
private var displayEmojiConfig: String = "emoji"
2527
private var insertInCursorPositionConfig: Boolean = false
@@ -32,13 +34,12 @@ class GitMojiConfig(private val project: Project) : SearchableConfigurable {
3234
private var languagesConfig:String = "auto"
3335

3436
override fun isModified(): Boolean =
35-
Configurable.isCheckboxModified(displayEmoji, displayEmojiConfig == "emoji") || Configurable.isCheckboxModified(
36-
useUnicode,
37-
useUnicodeConfig
38-
) || isModified(textAfterUnicode, textAfterUnicodeConfig) || Configurable.isCheckboxModified(
39-
insertInCursorPosition,
40-
insertInCursorPositionConfig
41-
) || Configurable.isCheckboxModified(includeGitMojiDescription, includeGitMojiDescriptionConfig)
37+
Configurable.isCheckboxModified(useProjectSettings, useProjectSettingsConfig) ||
38+
Configurable.isCheckboxModified(displayEmoji, displayEmojiConfig == "emoji") ||
39+
Configurable.isCheckboxModified(useUnicode, useUnicodeConfig) ||
40+
isModified(textAfterUnicode, textAfterUnicodeConfig) ||
41+
Configurable.isCheckboxModified(insertInCursorPosition, insertInCursorPositionConfig) ||
42+
Configurable.isCheckboxModified(includeGitMojiDescription, includeGitMojiDescriptionConfig)
4243

4344
private fun isModified(comboBox: ComboBox<String>, value: String): Boolean {
4445
return !Comparing.equal(comboBox.selectedItem, value)
@@ -50,6 +51,8 @@ class GitMojiConfig(private val project: Project) : SearchableConfigurable {
5051
init {
5152
val flow = GridLayout(20, 2)
5253
mainPanel = JPanel(flow)
54+
mainPanel.add(useProjectSettings, null)
55+
mainPanel.add(JPanel(), null) // empty cell
5356
mainPanel.add(displayEmoji, null)
5457
mainPanel.add(useUnicode, null)
5558
mainPanel.add(insertInCursorPosition, null)
@@ -65,6 +68,9 @@ class GitMojiConfig(private val project: Project) : SearchableConfigurable {
6568
}
6669

6770
override fun apply() {
71+
val wasProjectSettings = useProjectSettingsConfig
72+
useProjectSettingsConfig = useProjectSettings.isSelected
73+
6874
displayEmojiConfig = if (displayEmoji.isSelected) "emoji" else "icon"
6975
useUnicodeConfig = useUnicode.isSelected
7076
insertInCursorPositionConfig = insertInCursorPosition.isSelected
@@ -76,27 +82,68 @@ class GitMojiConfig(private val project: Project) : SearchableConfigurable {
7682
}
7783
languagesConfig = languageOptions[languages.selectedIndex]
7884

79-
val projectInstance = PropertiesComponent.getInstance(project)
80-
val instance = PropertiesComponent.getInstance()
81-
projectInstance.setValue(CONFIG_DISPLAY_ICON, displayEmojiConfig)
82-
projectInstance.setValue(CONFIG_INSERT_IN_CURSOR_POSITION, insertInCursorPositionConfig)
83-
projectInstance.setValue(CONFIG_USE_UNICODE, useUnicodeConfig)
84-
projectInstance.setValue(CONFIG_INCLUDE_GITMOJI_DESCRIPTION, includeGitMojiDescriptionConfig)
85-
projectInstance.setValue(CONFIG_AFTER_UNICODE, textAfterUnicodeConfig)
86-
instance.setValue(CONFIG_LANGUAGE, languagesConfig)
85+
val projectProps = PropertiesComponent.getInstance(project)
86+
val appProps = PropertiesComponent.getInstance()
87+
88+
// Save the toggle itself in project
89+
projectProps.setValue(CONFIG_USE_PROJECT_SETTINGS, useProjectSettingsConfig)
90+
91+
if (useProjectSettingsConfig) {
92+
// Save in project
93+
projectProps.setValue(CONFIG_DISPLAY_ICON, displayEmojiConfig)
94+
projectProps.setValue(CONFIG_INSERT_IN_CURSOR_POSITION, insertInCursorPositionConfig)
95+
projectProps.setValue(CONFIG_USE_UNICODE, useUnicodeConfig)
96+
projectProps.setValue(CONFIG_INCLUDE_GITMOJI_DESCRIPTION, includeGitMojiDescriptionConfig)
97+
projectProps.setValue(CONFIG_AFTER_UNICODE, textAfterUnicodeConfig)
98+
projectProps.setValue(CONFIG_LANGUAGE, languagesConfig)
99+
} else {
100+
// Save in global
101+
appProps.setValue(CONFIG_DISPLAY_ICON, displayEmojiConfig)
102+
appProps.setValue(CONFIG_INSERT_IN_CURSOR_POSITION, insertInCursorPositionConfig)
103+
appProps.setValue(CONFIG_USE_UNICODE, useUnicodeConfig)
104+
appProps.setValue(CONFIG_INCLUDE_GITMOJI_DESCRIPTION, includeGitMojiDescriptionConfig)
105+
appProps.setValue(CONFIG_AFTER_UNICODE, textAfterUnicodeConfig)
106+
appProps.setValue(CONFIG_LANGUAGE, languagesConfig)
107+
108+
// If we just unchecked, remove project settings
109+
if (wasProjectSettings && !useProjectSettingsConfig) {
110+
clearProjectSettings(projectProps)
111+
}
112+
}
113+
87114
GitmojiLocale.loadTranslations()
88115
}
89116

117+
private fun clearProjectSettings(props: PropertiesComponent) {
118+
try {
119+
props.unsetValue(CONFIG_DISPLAY_ICON)
120+
props.unsetValue(CONFIG_INSERT_IN_CURSOR_POSITION)
121+
props.unsetValue(CONFIG_USE_UNICODE)
122+
props.unsetValue(CONFIG_INCLUDE_GITMOJI_DESCRIPTION)
123+
props.unsetValue(CONFIG_AFTER_UNICODE)
124+
props.unsetValue(CONFIG_LANGUAGE)
125+
} catch (_: Exception) {
126+
// Ignore errors during cleanup
127+
}
128+
}
129+
90130
override fun reset() {
91-
val propertiesComponent = PropertiesComponent.getInstance(project)
92-
val instance = PropertiesComponent.getInstance()
93-
94-
displayEmojiConfig = propertiesComponent.getValue(CONFIG_DISPLAY_ICON, defaultDisplayType())
95-
useUnicodeConfig = propertiesComponent.getBoolean(CONFIG_USE_UNICODE, false)
96-
insertInCursorPositionConfig = propertiesComponent.getBoolean(CONFIG_INSERT_IN_CURSOR_POSITION, false)
97-
includeGitMojiDescriptionConfig = propertiesComponent.getBoolean(CONFIG_INCLUDE_GITMOJI_DESCRIPTION, false)
98-
textAfterUnicodeConfig = propertiesComponent.getValue(CONFIG_AFTER_UNICODE, " ")
99-
languagesConfig = instance.getValue(CONFIG_LANGUAGE, "auto")
131+
val projectProps = PropertiesComponent.getInstance(project)
132+
val appProps = PropertiesComponent.getInstance()
133+
134+
// Check if using project settings
135+
useProjectSettingsConfig = projectProps.getBoolean(CONFIG_USE_PROJECT_SETTINGS, false)
136+
useProjectSettings.isSelected = useProjectSettingsConfig
137+
138+
// Load from appropriate source
139+
val props = if (useProjectSettingsConfig) projectProps else appProps
140+
141+
displayEmojiConfig = props.getValue(CONFIG_DISPLAY_ICON, defaultDisplayType())
142+
useUnicodeConfig = props.getBoolean(CONFIG_USE_UNICODE, false)
143+
insertInCursorPositionConfig = props.getBoolean(CONFIG_INSERT_IN_CURSOR_POSITION, false)
144+
includeGitMojiDescriptionConfig = props.getBoolean(CONFIG_INCLUDE_GITMOJI_DESCRIPTION, false)
145+
textAfterUnicodeConfig = props.getValue(CONFIG_AFTER_UNICODE, " ")
146+
languagesConfig = props.getValue(CONFIG_LANGUAGE, "auto")
100147

101148
displayEmoji.isSelected = displayEmojiConfig == "emoji"
102149
useUnicode.isSelected = useUnicodeConfig

src/main/kotlin/com/github/patou/gitmoji/GitmojiData.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const val CONFIG_AFTER_UNICODE: String = "com.github.patou.gitmoji.text-after-un
99
const val CONFIG_LANGUAGE: String = "com.github.patou.gitmoji.language"
1010
const val CONFIG_INSERT_IN_CURSOR_POSITION: String = "com.github.patou.gitmoji.insert-in-cursor-position"
1111
const val CONFIG_INCLUDE_GITMOJI_DESCRIPTION: String = "com.github.patou.gitmoji.include-gitmoji-description"
12+
const val CONFIG_USE_PROJECT_SETTINGS: String = "com.github.patou.gitmoji.use-project-settings"
1213

1314
data class GitmojiData(val code: String, val emoji: String, val description: String, val name: String) {
1415
private lateinit var _icon: Icon
@@ -22,7 +23,7 @@ data class GitmojiData(val code: String, val emoji: String, val description: Str
2223
false,
2324
true
2425
)!!
25-
} catch (e: Exception) {
26+
} catch (_: Exception) {
2627
IconLoader.getIcon("/icons/gitmoji/anguished.png", GitmojiData::class.java)
2728
}
2829
}
Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,131 @@
11
package com.github.patou.gitmoji
2+
3+
import com.intellij.ide.util.PropertiesComponent
4+
import com.intellij.openapi.application.ApplicationManager
25
import com.intellij.openapi.project.Project
36
import com.intellij.openapi.startup.ProjectActivity
7+
import com.intellij.openapi.ui.Messages
48

59
class GitmojiStartupActivity : ProjectActivity {
610
override suspend fun execute(project: Project) {
7-
Gitmojis.ensureGitmojisLoaded();
11+
offerMigrationIfNeeded(project)
12+
Gitmojis.ensureGitmojisLoaded()
13+
}
14+
15+
private fun offerMigrationIfNeeded(project: Project) {
16+
val projectProps = PropertiesComponent.getInstance(project)
17+
val appProps = PropertiesComponent.getInstance()
18+
19+
println("[Gitmoji Migration] Starting migration check for project: ${project.name}")
20+
21+
// Check if already migrated or already has project settings toggle
22+
val migrationDoneKey = "com.github.patou.gitmoji.migration-offered"
23+
val alreadyMigrated = projectProps.getBoolean(migrationDoneKey, false)
24+
25+
println("[Gitmoji Migration] Already migrated flag: $alreadyMigrated")
26+
27+
if (alreadyMigrated) {
28+
println("[Gitmoji Migration] Migration already offered for project: ${project.name}")
29+
return
30+
}
31+
32+
// Check if CONFIG_USE_PROJECT_SETTINGS is already set (new version)
33+
val hasNewConfig = projectProps.getValue(CONFIG_USE_PROJECT_SETTINGS) != null
34+
println("[Gitmoji Migration] Has new config system: $hasNewConfig")
35+
36+
if (hasNewConfig) {
37+
println("[Gitmoji Migration] Project already using new config system: ${project.name}")
38+
projectProps.setValue(migrationDoneKey, true)
39+
return
40+
}
41+
42+
// Check if project has old settings (stored per-project before migration)
43+
// Check for both string values and the keys existence
44+
val displayIcon = projectProps.getValue(CONFIG_DISPLAY_ICON)
45+
val useUnicode = projectProps.getValue(CONFIG_USE_UNICODE)
46+
val afterUnicode = projectProps.getValue(CONFIG_AFTER_UNICODE)
47+
val insertPos = projectProps.getValue(CONFIG_INSERT_IN_CURSOR_POSITION)
48+
val includeDesc = projectProps.getValue(CONFIG_INCLUDE_GITMOJI_DESCRIPTION)
49+
val language = projectProps.getValue(CONFIG_LANGUAGE)
50+
51+
println("[Gitmoji Migration] Old settings check:")
52+
println("[Gitmoji Migration] CONFIG_DISPLAY_ICON: $displayIcon")
53+
println("[Gitmoji Migration] CONFIG_USE_UNICODE: $useUnicode")
54+
println("[Gitmoji Migration] CONFIG_AFTER_UNICODE: $afterUnicode")
55+
println("[Gitmoji Migration] CONFIG_INSERT_IN_CURSOR_POSITION: $insertPos")
56+
println("[Gitmoji Migration] CONFIG_INCLUDE_GITMOJI_DESCRIPTION: $includeDesc")
57+
println("[Gitmoji Migration] CONFIG_LANGUAGE: $language")
58+
59+
val hasOldProjectSettings =
60+
displayIcon != null ||
61+
useUnicode != null ||
62+
afterUnicode != null ||
63+
insertPos != null ||
64+
includeDesc != null ||
65+
language != null
66+
67+
println("[Gitmoji Migration] Has old project settings: $hasOldProjectSettings")
68+
69+
if (!hasOldProjectSettings) {
70+
// No old settings, mark as migrated
71+
println("[Gitmoji Migration] No old settings found, marking as migrated")
72+
projectProps.setValue(migrationDoneKey, true)
73+
return
74+
}
75+
76+
println("[Gitmoji Migration] Showing migration dialog for project: ${project.name}")
77+
78+
// Ask user what to do with old project settings
79+
ApplicationManager.getApplication().invokeLater {
80+
val result = Messages.showYesNoDialog(
81+
project,
82+
"This project has Gitmoji settings stored per-project from a previous version.\n\n" +
83+
"Would you like to migrate them to global settings?\n" +
84+
"(You can still use project-specific settings later via the checkbox in Settings)\n\n" +
85+
"• Yes: Move settings to global and remove from project\n" +
86+
"• No: Keep settings in this project (enable project-specific mode)",
87+
"Gitmoji Settings Migration",
88+
"Migrate to Global",
89+
"Keep in Project",
90+
Messages.getQuestionIcon()
91+
)
92+
93+
if (result == Messages.YES) {
94+
// Migrate to global
95+
migrateToGlobal(projectProps, appProps)
96+
projectProps.setValue(CONFIG_USE_PROJECT_SETTINGS, false)
97+
} else {
98+
// Keep in project - enable project-specific mode
99+
projectProps.setValue(CONFIG_USE_PROJECT_SETTINGS, true)
100+
}
101+
102+
projectProps.setValue(migrationDoneKey, true)
103+
}
104+
}
105+
106+
private fun migrateToGlobal(projectProps: PropertiesComponent, appProps: PropertiesComponent) {
107+
val keys = listOf(
108+
CONFIG_DISPLAY_ICON,
109+
CONFIG_INSERT_IN_CURSOR_POSITION,
110+
CONFIG_USE_UNICODE,
111+
CONFIG_INCLUDE_GITMOJI_DESCRIPTION,
112+
CONFIG_AFTER_UNICODE,
113+
CONFIG_LANGUAGE
114+
)
115+
116+
for (key in keys) {
117+
projectProps.getValue(key)?.let { value ->
118+
// Only migrate if global doesn't have a value yet
119+
if (appProps.getValue(key) == null) {
120+
appProps.setValue(key, value)
121+
}
122+
// Remove from project
123+
try {
124+
projectProps.unsetValue(key)
125+
} catch (_: Exception) {
126+
// Ignore errors during cleanup
127+
}
128+
}
129+
}
8130
}
9131
}

0 commit comments

Comments
 (0)