mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-02-27 17:45:59 +01:00
Add fallback window to capture local option state
This commit is contained in:
parent
0f19e50c69
commit
93037b6866
src/main/java/com/maddyhome/idea/vim
vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api
@ -40,6 +40,7 @@ import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimKeyListener
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.VimTypedActionHandler
|
||||
import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario
|
||||
import com.maddyhome.idea.vim.api.Options
|
||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
|
||||
@ -72,6 +73,7 @@ import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipEvents
|
||||
import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
@ -156,9 +158,16 @@ internal object VimListenerManager {
|
||||
Disposer.register(disposable) { editor.contentComponent.removeKeyListener(VimKeyListener) }
|
||||
|
||||
// Initialise the local options. We MUST do this before anything has the chance to query options
|
||||
val sourceEditor = getOpeningEditor(editor)
|
||||
val isSplit = editor.document == sourceEditor?.document
|
||||
VimPlugin.getOptionGroup().initialiseLocalOptions(editor.vim, sourceEditor?.vim, isSplit)
|
||||
val sourceEditor = getOpeningEditor(editor)?.vim
|
||||
|
||||
// Note that IdeaVim implements `:edit {file}` as `:new {file}` and doesn't implement `:new`, so the only scenario
|
||||
// we can handle here is NEW
|
||||
val scenario = when {
|
||||
sourceEditor == null -> LocalOptionInitialisationScenario.FALLBACK
|
||||
editor.document == sourceEditor.ij.document -> LocalOptionInitialisationScenario.SPLIT
|
||||
else -> LocalOptionInitialisationScenario.NEW
|
||||
}
|
||||
VimPlugin.getOptionGroup().initialiseLocalOptions(editor.vim, sourceEditor ?: injector.fallbackWindow, scenario)
|
||||
|
||||
val eventFacade = EventFacade.getInstance()
|
||||
eventFacade.addEditorMouseListener(editor, EditorMouseHandler, disposable)
|
||||
|
@ -12,9 +12,11 @@ import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.components.serviceIfCreated
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
|
||||
import com.maddyhome.idea.vim.api.EngineEditorHelper
|
||||
import com.maddyhome.idea.vim.api.ExEntryPanel
|
||||
import com.maddyhome.idea.vim.api.ExecutionContextManager
|
||||
import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario
|
||||
import com.maddyhome.idea.vim.api.NativeActionManager
|
||||
import com.maddyhome.idea.vim.api.SystemInfoService
|
||||
import com.maddyhome.idea.vim.api.VimActionExecutor
|
||||
@ -90,10 +92,17 @@ import com.maddyhome.idea.vim.vimscript.services.PatternService
|
||||
import com.maddyhome.idea.vim.vimscript.services.VariableService
|
||||
import com.maddyhome.idea.vim.yank.VimYankGroup
|
||||
import com.maddyhome.idea.vim.yank.YankGroupBase
|
||||
import javax.swing.JTextArea
|
||||
|
||||
internal class IjVimInjector : VimInjectorBase() {
|
||||
override fun <T : Any> getLogger(clazz: Class<T>): VimLogger = IjVimLogger(Logger.getInstance(clazz))
|
||||
|
||||
override val fallbackWindow: VimEditor by lazy {
|
||||
TextComponentEditorImpl(null, JTextArea()).vim.also {
|
||||
optionGroup.initialiseLocalOptions(it, null, LocalOptionInitialisationScenario.DEFAULTS)
|
||||
}
|
||||
}
|
||||
|
||||
override val actionExecutor: VimActionExecutor
|
||||
get() = service<IjActionExecutor>()
|
||||
override val exEntryPanel: ExEntryPanel
|
||||
|
@ -12,6 +12,7 @@ import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import java.awt.event.ActionEvent
|
||||
@ -231,12 +232,15 @@ internal class DeletePreviousWordAction : TextAction(DefaultEditorKit.deletePrev
|
||||
val caret = target.caret
|
||||
val project = target.editor.project
|
||||
|
||||
// Note that we need an editor when searching because we need per-editor options (i.e. 'iskeyword'). We initialise
|
||||
// it based on the owning editor, and treat it like a split, so we get a full copy of the local-to-window options
|
||||
// TODO: Over time, we should migrate ex actions to be based on VimEditor
|
||||
// This will give us an editor we can use for options, etc. and we can reuse the actions for other implementations
|
||||
// Create a VimEditor instance on the Swing text field which we can pass to the search helpers. We need an editor
|
||||
// rather than just working on a buffer because the search helpers need local options (specifically the local to
|
||||
// buffer 'iskeyword'). We use the CMD_LINE scenario to initialise local options from the main editor. The options
|
||||
// service will copy all local-to-buffer and local-to-window options, effectively cloning the options.
|
||||
// TODO: Over time, we should migrate all ex actions to be based on VimEditor
|
||||
// This will mean we always have an editor that has been initialised for options, etc. But also means that we can
|
||||
// share the command line entry actions between IdeaVim implementations
|
||||
val editor = TextComponentEditorImpl(project, target).vim
|
||||
injector.optionGroup.initialiseLocalOptions(editor, target.editor.vim, true)
|
||||
injector.optionGroup.initialiseLocalOptions(editor, target.editor.vim, LocalOptionInitialisationScenario.CMD_LINE)
|
||||
|
||||
val offset = injector.searchHelper.findNextWord(editor, caret.dot, -1, bigWord = false, spaceWords = false)
|
||||
if (logger.isDebugEnabled) logger.debug("offset=$offset")
|
||||
|
@ -23,6 +23,20 @@ import com.maddyhome.idea.vim.vimscript.services.VariableService
|
||||
import com.maddyhome.idea.vim.yank.VimYankGroup
|
||||
|
||||
public interface VimInjector {
|
||||
/**
|
||||
* The window used when we need a window but there are no editor windows available.
|
||||
*
|
||||
* This is primarily used to capture state for local options, either at startup, or when all editor windows are
|
||||
* closed.
|
||||
*
|
||||
* Vim always has at least one buffer and window. During startup, Vim will evaluate the appropriate `vimrc` files, and
|
||||
* any local or global-local options are set against this initial buffer and window. IdeaVim does not always have an
|
||||
* open buffer or window, so we create a hidden window, with a private buffer that can be used when evaluating the
|
||||
* `~/.ideavimrc` file, and updated with the last set local options of the current window. This window (and buffer) is
|
||||
* then used to initialise the local options of the first window that is subsequently opened or initialised.
|
||||
*/
|
||||
public val fallbackWindow: VimEditor
|
||||
|
||||
// [FINISHED] Fully moved to vim-engine. Should we remove it from injector?
|
||||
public val parser: VimStringParser
|
||||
|
||||
|
@ -11,8 +11,8 @@ package com.maddyhome.idea.vim.api
|
||||
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
|
||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
|
||||
import com.maddyhome.idea.vim.options.Option
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope
|
||||
import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope
|
||||
import com.maddyhome.idea.vim.options.StringListOption
|
||||
import com.maddyhome.idea.vim.options.ToggleOption
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
@ -30,22 +30,20 @@ public interface VimOptionGroup {
|
||||
/**
|
||||
* Initialise the local to buffer and local to window options for this editor
|
||||
*
|
||||
* Local to buffer options are copied from the current global values, while local to window options should be copied
|
||||
* from the per-window "global" values of the editor that caused this editor to open. Both of these global values are
|
||||
* updated by the `:set` or `:setglobal` commands.
|
||||
*
|
||||
* Note that global-local options are not copied from the source window. They are global values that are overridden
|
||||
* locally, and local values are never copied.
|
||||
* Depending on the initialisation scenario, the local-to-buffer, local-to-window and/or global-local options are
|
||||
* initialised. The scenario dictates where the local options get their values from. Typically, local-to-buffer
|
||||
* options are copied from the global values. Local-to-window options are either initialised from the per-window
|
||||
* "global" value or copied directly from the opening window. Global-local options are usually not initialised.
|
||||
*
|
||||
* TODO: IdeaVim currently does not support per-window "global" values
|
||||
*
|
||||
* @param editor The editor to initialise
|
||||
* @param sourceEditor The editor which is opening the new editor. This source editor is used to get the per-window
|
||||
* "global" values to initialise the new editor. If null, there is no source editor (e.g. all
|
||||
* editor windows are closed), and the options should be initialised to some other value.
|
||||
* @param isSplit True if the new editor is a split view of the source editor
|
||||
* editor windows are closed), and the options should be initialised to default values.
|
||||
* @param scenario The scenario for initialising the local options
|
||||
*/
|
||||
public fun initialiseLocalOptions(editor: VimEditor, sourceEditor: VimEditor?, isSplit: Boolean)
|
||||
public fun initialiseLocalOptions(editor: VimEditor, sourceEditor: VimEditor?, scenario: LocalOptionInitialisationScenario)
|
||||
|
||||
/**
|
||||
* Get the [Option] by its name or abbreviation
|
||||
@ -259,4 +257,78 @@ public fun VimOptionGroup.invertToggleOption(option: ToggleOption, scope: Option
|
||||
public fun VimOptionGroup.hasValue(option: StringListOption, scope: OptionAccessScope, value: String): Boolean {
|
||||
val optionValue = getOptionValue(option, scope)
|
||||
return option.split(optionValue.asString()).contains(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The scenario for initialising local options
|
||||
*/
|
||||
public enum class LocalOptionInitialisationScenario {
|
||||
/**
|
||||
* Set the local options to default (global) values.
|
||||
*/
|
||||
DEFAULTS,
|
||||
|
||||
/**
|
||||
* The new window is being initialised with the values of the fallback window
|
||||
*
|
||||
* Vim always has at least one buffer and window open, and the `vimrc` files are evaluated in this context. Any
|
||||
* options set during evaluation are applied to the first open window and buffer, as if the user had interactively
|
||||
* typed them in. IdeaVim does not always have an open window (and therefore buffer), so we evaluate `~/.ideavimrc` in
|
||||
* a special, hidden "fallback" window, that is always available even if there are no editor windows. This fallback
|
||||
* window is used to initialise the first editor window.
|
||||
*
|
||||
* Since Vim will evaluate `vimrc` in the context of the first window, any local-to-buffer options are set against the
|
||||
* first window's buffer. Therefore, this scenario will copy buffer and window local values, including global-local
|
||||
* values, and the per-window "global" values of local-to-window options.
|
||||
*/
|
||||
FALLBACK,
|
||||
|
||||
/**
|
||||
* The new window is a split of the opening/current window
|
||||
*
|
||||
* In this scenario, Vim is trying to make the new window behave exactly like the opening window, so will copy both
|
||||
* local and per-window "global" values of local-to-window and global-local (to window) options from the opening
|
||||
* window to the new window. Local-to-buffer windows are obviously already initialised and not modified.
|
||||
*/
|
||||
SPLIT,
|
||||
|
||||
/**
|
||||
* The user has opened a new buffer in the current window
|
||||
*
|
||||
* This scenario is not currently supported by IdeaVim.
|
||||
*
|
||||
* This is the `:edit {file}` command, where the current window is reused to edit a new or previously edited buffer.
|
||||
* Vim will reset any explicitly set local-to-window values. The local-to-buffer options are initialised for a new
|
||||
* buffer, by copying from the global values. Local-to-window values are reset to the existing per-window "global"
|
||||
* values.
|
||||
*/
|
||||
EDIT,
|
||||
|
||||
/**
|
||||
* The user has opened a new window
|
||||
*
|
||||
* This is Vim's `:new {file}` command, which will open a new or existing buffer in a new window. Vim treats this as
|
||||
* a split followed by `:edit`, which means copying local and per-window "global" local-to-window option values from
|
||||
* the opening window and then resetting any explicitly set local-to-window options to the per-window "global" values.
|
||||
*
|
||||
* Note that this scenario is used for IdeaVim's current implementation of the `:edit {file}` command.
|
||||
*/
|
||||
NEW,
|
||||
|
||||
/**
|
||||
* Initialise the [VimEditor] used for the `ex` command line text field
|
||||
*
|
||||
* Vim doesn't really have the concept of "editor". It has a window, which is a view on a buffer, and which can edit
|
||||
* the text of the buffer. The `ex` command line and search text entry are implemented as part of this window, and
|
||||
* therefore automatically uses the window's local options (e.g. search requires `'iskeyword'`)
|
||||
*
|
||||
* For IdeaVim, the `ex`/search text entry is a separate UI component to the main editor, implements [VimEditor] and
|
||||
* so needs its own copy of the local options. This scenario makes a full copy of the local to buffer and local to
|
||||
* window options, so has the same effect as [FALLBACK].
|
||||
*
|
||||
* We need to migrate more of the command line text handling to work with a [VimEditor]-based implementation (it's
|
||||
* currently very heavily based on Swing). As part of the implementation detail, we could look at sharing options
|
||||
* instead of copying them.
|
||||
*/
|
||||
CMD_LINE
|
||||
}
|
||||
|
@ -12,12 +12,12 @@ import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
|
||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
|
||||
import com.maddyhome.idea.vim.options.NumberOption
|
||||
import com.maddyhome.idea.vim.options.Option
|
||||
import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_BUFFER
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_WINDOW
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.LOCAL_TO_BUFFER
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.LOCAL_TO_WINDOW
|
||||
import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
import com.maddyhome.idea.vim.options.ToggleOption
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
|
||||
@ -34,12 +34,82 @@ public abstract class VimOptionGroupBase : VimOptionGroup {
|
||||
Options.initialise()
|
||||
}
|
||||
|
||||
override fun initialiseLocalOptions(editor: VimEditor, sourceEditor: VimEditor?, isSplit: Boolean) {
|
||||
// Initialise local-to-buffer options
|
||||
// They are stored per-buffer, so shared across all editors for the buffer. If the key exists, they've previously
|
||||
// been initialised, else initialise the options from the global values, which is always the most recently set value
|
||||
// (`:set` on a buffer-local option will set the local value, but it also sets the global value, for exactly this
|
||||
// reason)
|
||||
override fun initialiseLocalOptions(editor: VimEditor, sourceEditor: VimEditor?, scenario: LocalOptionInitialisationScenario) {
|
||||
when (scenario) {
|
||||
// We're initialising the first (hidden) editor. Vim always has at least one window (and buffer); IdeaVim doesn't.
|
||||
// We fake this with a hidden editor that is created when the plugin first starts. It is used to capture state
|
||||
// when initially evaluating `~/.ideavimrc`, and then used to initialise subsequent windows. But first, we must
|
||||
// initialise all local options to the default (global) values
|
||||
LocalOptionInitialisationScenario.DEFAULTS -> {
|
||||
check(sourceEditor == null) { "sourceEditor must be null for DEFAULTS scenario" }
|
||||
initialiseLocalToBufferOptions(editor)
|
||||
initialiseLocalToWindowOptions(editor)
|
||||
}
|
||||
|
||||
// The opening window is either:
|
||||
// a) the special initialisation window used to evaluate `~/.ideavimrc` during initialisation and potentially
|
||||
// before any windows are open or
|
||||
// b) the "ex" or search command line text field/editor associated with a main editor
|
||||
// Either way, the target window should be a clone of the source window, copying local to buffer and local to
|
||||
// window values
|
||||
LocalOptionInitialisationScenario.FALLBACK,
|
||||
LocalOptionInitialisationScenario.CMD_LINE -> {
|
||||
check(sourceEditor != null) { "sourceEditor must not be null for IDEAVIMRC or CMD_LINE scenarios" }
|
||||
copyLocalToBufferLocalValues(editor, sourceEditor)
|
||||
copyLocalToWindowLocalValues(editor, sourceEditor)
|
||||
copyLocalToWindowGlobalValues(editor, sourceEditor)
|
||||
}
|
||||
|
||||
// The opening/current window is being split. Clone the local-to-window options, both the local values and the
|
||||
// per-window "global" values. The buffer local options are obviously already initialised
|
||||
LocalOptionInitialisationScenario.SPLIT -> {
|
||||
check(sourceEditor != null) { "sourceEditor must not be null for SPLIT scenario" }
|
||||
initialiseLocalToBufferOptions(editor) // Should be a no-op
|
||||
copyLocalToWindowLocalValues(editor, sourceEditor)
|
||||
copyLocalToWindowGlobalValues(editor, sourceEditor)
|
||||
}
|
||||
|
||||
// Editing a new buffer in the current window (`:edit {file}`). Remove explicitly set local values, which means to
|
||||
// copy the per-window "global" value of local-to-window options to the local value, and to reset all window
|
||||
// global-local options. Since it's a new buffer, we initialise buffer local options.
|
||||
// Note that IdeaVim does not support this scenario because it implements `:edit {file}` as `:new {file}`
|
||||
LocalOptionInitialisationScenario.EDIT -> {
|
||||
check(sourceEditor != null) { "sourceEditor must not be null for EDIT scenario" }
|
||||
initialiseLocalToBufferOptions(editor)
|
||||
resetLocalToWindowOptions(editor)
|
||||
}
|
||||
|
||||
// Editing a new buffer in a new window (`:new {file}`). Vim treats this as a split followed by an edit. That
|
||||
// means, clone the window, then reset its local values to its global values
|
||||
LocalOptionInitialisationScenario.NEW -> {
|
||||
check(sourceEditor != null) { "sourceEditor must not be null for NEW scenario" }
|
||||
initialiseLocalToBufferOptions(editor)
|
||||
copyLocalToWindowLocalValues(editor, sourceEditor) // Technically redundant
|
||||
copyLocalToWindowGlobalValues(editor, sourceEditor)
|
||||
resetLocalToWindowOptions(editor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyLocalToBufferLocalValues(targetEditor: VimEditor, sourceEditor: VimEditor) {
|
||||
val localValues = getBufferLocalOptionStorage(targetEditor)
|
||||
getAllOptions().forEach { option ->
|
||||
if (option.declaredScope == LOCAL_TO_BUFFER || option.declaredScope == GLOBAL_OR_LOCAL_TO_BUFFER) {
|
||||
localValues[option.name] = getBufferLocalOptionValue(option, sourceEditor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise local-to-buffer options by copying the global value
|
||||
*
|
||||
* Note that the buffer might have been previously initialised. The global value is the most recently set value,
|
||||
* across any buffer and any window. This makes most sense for non-visible options - the user always gets what they
|
||||
* last set, regardless of where it was set.
|
||||
*
|
||||
* Remember that `:set` on a buffer-local option will set both the local value and the global value.
|
||||
*/
|
||||
private fun initialiseLocalToBufferOptions(editor: VimEditor) {
|
||||
injector.vimStorageService.getOrPutBufferData(editor, localOptionsKey) {
|
||||
mutableMapOf<String, VimDataType>().also { bufferOptions ->
|
||||
getAllOptions().forEach { option ->
|
||||
@ -51,67 +121,57 @@ public abstract class VimOptionGroupBase : VimOptionGroup {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We don't support per-window "global" values right now
|
||||
// These functions are here so we know what the semantics should be when it comes time to implement.
|
||||
// Default to getting the per-instance global value for now (per-instance meaning per VimOptionGroup service instance)
|
||||
// Set does nothing, because it's called with the current "global" value, which would be a no-op
|
||||
fun getPerWindowGlobalOptionValue(option: Option<VimDataType>, editor: VimEditor?) = getGlobalOptionValue(option)
|
||||
fun setPerWindowGlobalOptionValue(option: Option<VimDataType>, editor: VimEditor, value: VimDataType) {}
|
||||
|
||||
// Initialising local-to-window options is a little more involved (see [OptionDeclaredScope])
|
||||
// Assumptions:
|
||||
// * Vim always has at least one open window. A new window or buffer is initialised from this source window
|
||||
// IdeaVim does not always have an open window. The passed source window might be null.
|
||||
// TODO: How does this handle `:setlocal` in `~/.ideavimrc`?
|
||||
// We might need to create a dummy "root" window that evaluates `~/.ideavimrc` and is used to initialise the local
|
||||
// options of other windows.
|
||||
// * Vim's local-to-window options store "global" values as per-window global values.
|
||||
// TODO: IdeaVim does not currently support per-window global values
|
||||
// Scenarios:
|
||||
// 1. Split the current window
|
||||
// Vim tries to make the split an exact clone. Copy the source window's local and per-window global values to the
|
||||
// new window. This applies to local-to-window and "global or local to window" options
|
||||
// 2. Edit a new buffer in the current window (`:edit {file}`)
|
||||
// Reapply the current window's per-window global values as local values, to get rid of explicitly local values
|
||||
// IdeaVim does not currently support this scenario, because IdeaVim's implementation of `:edit` does not open
|
||||
// a file in the current window. It instead behaves like `:new {file}`.
|
||||
// We could implement it like the platform implements preview tabs, or reusing unmodified tabs - by opening a new
|
||||
// editor and immediately closing the old one. This would still behave like `:new` - copying the per-window
|
||||
// global values and applying them as local values, which would be the correct behaviour
|
||||
// 3. Open a new buffer in a new window (`:new {file}`)
|
||||
// Vim implements this as a split, then editing a new buffer in the new current window. This will copy the source
|
||||
// window's local and per-window global values to the new split window, then reapply the new window's per-window
|
||||
// global values to the new window's local options. In effect, copying the source window's per-window global
|
||||
// values to the new window
|
||||
// 3. Edit a previously edited buffer (in the current window)
|
||||
// Vim will reapply options saved from the last window used to edit this buffer. Details are a bit sketchy - when
|
||||
// are the options saved, when are the released, etc. so this scenario is not currently supported.
|
||||
// IdeaVim does not support this
|
||||
injector.vimStorageService.getOrPutWindowData(editor, localOptionsKey) {
|
||||
mutableMapOf<String, VimDataType>().also { windowOptions ->
|
||||
getAllOptions()
|
||||
.filter { it.declaredScope == LOCAL_TO_WINDOW || it.declaredScope == GLOBAL_OR_LOCAL_TO_WINDOW }
|
||||
.forEach { option ->
|
||||
if (isSplit && sourceEditor != null) {
|
||||
// Splitting the current window, make it look and behave the same as the source editor
|
||||
windowOptions[option.name] = getWindowLocalOptionValue(option, sourceEditor)
|
||||
setPerWindowGlobalOptionValue(option, editor, getPerWindowGlobalOptionValue(option, sourceEditor))
|
||||
}
|
||||
else {
|
||||
// All other scenarios (open new buffer in new or current window)
|
||||
windowOptions[option.name] = if (option.declaredScope == GLOBAL_OR_LOCAL_TO_WINDOW) {
|
||||
option.unsetValue
|
||||
}
|
||||
else {
|
||||
getPerWindowGlobalOptionValue(option, sourceEditor)
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun copyLocalToWindowLocalValues(targetEditor: VimEditor, sourceEditor: VimEditor) {
|
||||
val localValues = getWindowLocalOptionStorage(targetEditor)
|
||||
getAllOptions().forEach { option ->
|
||||
if (option.declaredScope == LOCAL_TO_WINDOW || option.declaredScope == GLOBAL_OR_LOCAL_TO_WINDOW) {
|
||||
localValues[option.name] = getWindowLocalOptionValue(option, sourceEditor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyLocalToWindowGlobalValues(targetEditor: VimEditor, sourceEditor: VimEditor) {
|
||||
getAllOptions().forEach { option ->
|
||||
if (option.declaredScope == LOCAL_TO_WINDOW || option.declaredScope == GLOBAL_OR_LOCAL_TO_WINDOW) {
|
||||
val localValue = getPerWindowGlobalOptionValue(option, sourceEditor)
|
||||
setPerWindowGlobalOptionValue(option, targetEditor, localValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetLocalToWindowOptions(editor: VimEditor) {
|
||||
val localValues = getWindowLocalOptionStorage(editor)
|
||||
getAllOptions().forEach { option ->
|
||||
if (option.declaredScope == LOCAL_TO_WINDOW) {
|
||||
localValues[option.name] = getPerWindowGlobalOptionValue(option, editor)
|
||||
}
|
||||
else if (option.declaredScope == GLOBAL_OR_LOCAL_TO_WINDOW) {
|
||||
localValues[option.name] = option.unsetValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initialiseLocalToWindowOptions(editor: VimEditor) {
|
||||
val localValues = getWindowLocalOptionStorage(editor)
|
||||
getAllOptions().forEach { option ->
|
||||
if (option.declaredScope == LOCAL_TO_WINDOW) {
|
||||
localValues[option.name] = getGlobalOptionValue(option)
|
||||
}
|
||||
else if (option.declaredScope == GLOBAL_OR_LOCAL_TO_WINDOW) {
|
||||
localValues[option.name] = option.unsetValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We don't support per-window "global" values right now
|
||||
// These functions are here so we know what the semantics should be when it comes time to implement.
|
||||
// Default to getting the per-instance global value for now (per-instance meaning per VimOptionGroup service instance)
|
||||
// Set does nothing, because it's called with the current "global" value, which would be a no-op
|
||||
private fun getPerWindowGlobalOptionValue(option: Option<VimDataType>, editor: VimEditor?) = getGlobalOptionValue(option)
|
||||
private fun setPerWindowGlobalOptionValue(option: Option<VimDataType>, editor: VimEditor, value: VimDataType) {}
|
||||
|
||||
override fun <T : VimDataType> getOptionValue(option: Option<T>, scope: OptionAccessScope): T = when (scope) {
|
||||
is OptionAccessScope.EFFECTIVE -> getEffectiveOptionValue(option, scope.editor)
|
||||
is OptionAccessScope.LOCAL -> getLocalOptionValue(option, scope.editor)
|
||||
|
Loading…
Reference in New Issue
Block a user