4a4185570db4afe6afab68f06a602f915522819d
[idea/community.git] / platform / diff-impl / src / com / intellij / diff / tools / util / base / TextDiffSettingsHolder.kt
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.diff.tools.util.base
3
4 import com.intellij.diff.tools.util.breadcrumbs.BreadcrumbsPlacement
5 import com.intellij.diff.util.DiffPlaces
6 import com.intellij.diff.util.DiffUtil
7 import com.intellij.openapi.Disposable
8 import com.intellij.openapi.components.*
9 import com.intellij.openapi.diff.DiffBundle
10 import com.intellij.openapi.util.Key
11 import com.intellij.util.EventDispatcher
12 import com.intellij.util.xmlb.annotations.OptionTag
13 import com.intellij.util.xmlb.annotations.Transient
14 import com.intellij.util.xmlb.annotations.XMap
15 import org.jetbrains.annotations.NonNls
16 import java.util.*
17
18 @State(name = "TextDiffSettings", storages = [(Storage(value = DiffUtil.DIFF_CONFIG))], category = SettingsCategory.CODE)
19 class TextDiffSettingsHolder : PersistentStateComponent<TextDiffSettingsHolder.State> {
20   companion object {
21     @JvmField val CONTEXT_RANGE_MODES: IntArray = intArrayOf(1, 2, 4, 8, -1)
22     @JvmField val CONTEXT_RANGE_MODE_LABELS: Array<String> = arrayOf("1", "2", "4", "8", DiffBundle.message("configurable.diff.collapse.unchanged.ranges.disable"))
23   }
24
25   data class SharedSettings(
26     // Fragments settings
27     var CONTEXT_RANGE: Int = 4,
28
29     var MERGE_AUTO_APPLY_NON_CONFLICTED_CHANGES: Boolean = false,
30     var MERGE_LST_GUTTER_MARKERS: Boolean = true
31   )
32
33   data class PlaceSettings(
34     // Diff settings
35     var HIGHLIGHT_POLICY: HighlightPolicy = HighlightPolicy.BY_WORD,
36     var IGNORE_POLICY: IgnorePolicy = IgnorePolicy.DEFAULT,
37
38     // Editor settings
39     var SHOW_WHITESPACES: Boolean = false,
40     var SHOW_LINE_NUMBERS: Boolean = true,
41     var SHOW_INDENT_LINES: Boolean = false,
42     var USE_SOFT_WRAPS: Boolean = false,
43     var HIGHLIGHTING_LEVEL: HighlightingLevel = HighlightingLevel.INSPECTIONS,
44     var READ_ONLY_LOCK: Boolean = true,
45     var BREADCRUMBS_PLACEMENT: BreadcrumbsPlacement = BreadcrumbsPlacement.HIDDEN,
46
47     // Fragments settings
48     var EXPAND_BY_DEFAULT: Boolean = true
49   ) {
50     @Transient
51     val eventDispatcher: EventDispatcher<TextDiffSettings.Listener> = EventDispatcher.create(TextDiffSettings.Listener::class.java)
52   }
53
54   class TextDiffSettings internal constructor(private val SHARED_SETTINGS: SharedSettings,
55                                               private val PLACE_SETTINGS: PlaceSettings,
56                                               val place: String?) {
57     constructor() : this(SharedSettings(), PlaceSettings(), null)
58
59     fun addListener(listener: Listener, disposable: Disposable) {
60       PLACE_SETTINGS.eventDispatcher.addListener(listener, disposable)
61     }
62
63     // Presentation settings
64
65     var isEnableSyncScroll: Boolean = true
66
67     // Diff settings
68
69     var highlightPolicy: HighlightPolicy = PLACE_SETTINGS.HIGHLIGHT_POLICY
70       set(value) {
71         field = value
72         if (value != HighlightPolicy.DO_NOT_HIGHLIGHT) { // do not persist confusing value as new default
73           PLACE_SETTINGS.HIGHLIGHT_POLICY = value
74         }
75         PLACE_SETTINGS.eventDispatcher.multicaster.highlightPolicyChanged()
76       }
77
78     var ignorePolicy: IgnorePolicy
79       get()      = PLACE_SETTINGS.IGNORE_POLICY
80       set(value) { PLACE_SETTINGS.IGNORE_POLICY = value
81                    PLACE_SETTINGS.eventDispatcher.multicaster.ignorePolicyChanged() }
82
83     //
84     // Merge
85     //
86
87     var isAutoApplyNonConflictedChanges: Boolean
88       get()      = SHARED_SETTINGS.MERGE_AUTO_APPLY_NON_CONFLICTED_CHANGES
89       set(value) { SHARED_SETTINGS.MERGE_AUTO_APPLY_NON_CONFLICTED_CHANGES = value }
90
91     var isEnableLstGutterMarkersInMerge: Boolean
92       get()      = SHARED_SETTINGS.MERGE_LST_GUTTER_MARKERS
93       set(value) { SHARED_SETTINGS.MERGE_LST_GUTTER_MARKERS = value }
94
95     // Editor settings
96
97     var isShowLineNumbers: Boolean
98       get()      = PLACE_SETTINGS.SHOW_LINE_NUMBERS
99       set(value) { PLACE_SETTINGS.SHOW_LINE_NUMBERS = value }
100
101     var isShowWhitespaces: Boolean
102       get()      = PLACE_SETTINGS.SHOW_WHITESPACES
103       set(value) { PLACE_SETTINGS.SHOW_WHITESPACES = value }
104
105     var isShowIndentLines: Boolean
106       get()      = PLACE_SETTINGS.SHOW_INDENT_LINES
107       set(value) { PLACE_SETTINGS.SHOW_INDENT_LINES = value }
108
109     var isUseSoftWraps: Boolean
110       get()      = PLACE_SETTINGS.USE_SOFT_WRAPS
111       set(value) { PLACE_SETTINGS.USE_SOFT_WRAPS = value }
112
113     var highlightingLevel: HighlightingLevel
114       get()      = PLACE_SETTINGS.HIGHLIGHTING_LEVEL
115       set(value) { PLACE_SETTINGS.HIGHLIGHTING_LEVEL = value }
116
117     var contextRange: Int
118       get()      = SHARED_SETTINGS.CONTEXT_RANGE
119       set(value) { SHARED_SETTINGS.CONTEXT_RANGE = value }
120
121     var isExpandByDefault: Boolean
122       get()      = PLACE_SETTINGS.EXPAND_BY_DEFAULT
123       set(value) { PLACE_SETTINGS.EXPAND_BY_DEFAULT = value }
124
125     var isReadOnlyLock: Boolean
126       get()      = PLACE_SETTINGS.READ_ONLY_LOCK
127       set(value) { PLACE_SETTINGS.READ_ONLY_LOCK = value }
128
129     var breadcrumbsPlacement: BreadcrumbsPlacement
130       get()      = PLACE_SETTINGS.BREADCRUMBS_PLACEMENT
131       set(value) { PLACE_SETTINGS.BREADCRUMBS_PLACEMENT = value
132                    PLACE_SETTINGS.eventDispatcher.multicaster.breadcrumbsPlacementChanged() }
133
134     //
135     // Impl
136     //
137
138     companion object {
139       @JvmField val KEY: Key<TextDiffSettings> = Key.create("TextDiffSettings")
140
141       @JvmStatic fun getSettings(): TextDiffSettings = getSettings(null)
142       @JvmStatic fun getSettings(place: String?): TextDiffSettings = service<TextDiffSettingsHolder>().getSettings(place)
143       internal fun getDefaultSettings(place: String): TextDiffSettings =
144         TextDiffSettings(SharedSettings(), service<TextDiffSettingsHolder>().defaultPlaceSettings(place), place)
145     }
146
147     interface Listener : EventListener {
148       fun highlightPolicyChanged() {}
149       fun ignorePolicyChanged() {}
150       fun breadcrumbsPlacementChanged() {}
151
152       abstract class Adapter : Listener
153     }
154   }
155
156   fun getSettings(@NonNls place: String?): TextDiffSettings {
157     val placeKey = place ?: DiffPlaces.DEFAULT
158     val placeSettings = myState.PLACES_MAP.getOrPut(placeKey) { defaultPlaceSettings(placeKey) }
159     return TextDiffSettings(myState.SHARED_SETTINGS, placeSettings, placeKey)
160   }
161
162   private fun copyStateWithoutDefaults(): State {
163     val result = State()
164     result.SHARED_SETTINGS = myState.SHARED_SETTINGS
165     result.PLACES_MAP = DiffUtil.trimDefaultValues(myState.PLACES_MAP) { defaultPlaceSettings(it) }
166     return result
167   }
168
169   private fun defaultPlaceSettings(place: String): PlaceSettings {
170     val settings = PlaceSettings()
171     if (place == DiffPlaces.CHANGES_VIEW) {
172       settings.EXPAND_BY_DEFAULT = false
173       settings.SHOW_LINE_NUMBERS = false
174     }
175     if (place == DiffPlaces.COMMIT_DIALOG) {
176       settings.EXPAND_BY_DEFAULT = false
177     }
178     if (place == DiffPlaces.VCS_LOG_VIEW) {
179       settings.EXPAND_BY_DEFAULT = false
180     }
181     if (place == DiffPlaces.VCS_FILE_HISTORY_VIEW) {
182       settings.EXPAND_BY_DEFAULT = false
183     }
184     return settings
185   }
186
187
188   class State {
189     @OptionTag
190     @XMap
191     @JvmField var PLACES_MAP: TreeMap<String, PlaceSettings> = TreeMap()
192     @JvmField var SHARED_SETTINGS: SharedSettings = SharedSettings()
193   }
194
195   private var myState: State = State()
196
197   override fun getState(): State {
198     return copyStateWithoutDefaults()
199   }
200
201   override fun loadState(state: State) {
202     myState = state
203   }
204 }