Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'wrap' and other IDE-backed options #795

Merged
merged 26 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
df54168
Introduce 'wrap' option based on IntelliJ setting
citizenmatt Jan 4, 2024
a841bfd
Track how option value is set
citizenmatt Jan 4, 2024
f4c4485
Treat IDE value as default for 'wrap' option
citizenmatt Jan 4, 2024
1d8d1f0
Extract base implementation for IDE backed options
citizenmatt Jan 8, 2024
6dd4a06
Sort ideavim.dic to make it easier to modify
citizenmatt Feb 6, 2024
1a7774d
Add 'breakindent' option
citizenmatt Jan 8, 2024
bea9f42
Add 'list' option to show whitespace
citizenmatt Jan 8, 2024
4ec6d27
Add 'cursorline' option
citizenmatt Jan 9, 2024
5b04363
Add 'textwidth' option
citizenmatt Jan 9, 2024
6ca9160
Add 'colorcolumn' option to show visual guides
citizenmatt Jan 9, 2024
1856e9a
Move number/relativenumber options out of engine
citizenmatt Feb 5, 2024
8a86082
Improve relative line converter for soft wraps
citizenmatt Feb 6, 2024
9ad4a5d
Map 'number' and 'relativenumber' options
citizenmatt Feb 6, 2024
3dfc084
Add 'fileformat' option
citizenmatt Feb 12, 2024
89abc43
Add 'bomb' option
citizenmatt Feb 12, 2024
ba81382
Add 'fileencoding' option
citizenmatt Feb 13, 2024
8c361be
Map 'scrolljump' and 'sidescroll' options
citizenmatt Feb 14, 2024
02344b4
Match Vim's behaviour for :set[local] {option}<
citizenmatt Feb 20, 2024
1769690
Map 'scrolloff' and 'sidescrolloff' options
citizenmatt Feb 21, 2024
a7a0e2a
Prevent resetting options when plugin re-enabled
citizenmatt Mar 4, 2024
64dbb17
Updated descriptions as per review comments
citizenmatt Apr 4, 2024
0f3f604
Codify assumption re global-local external setting
citizenmatt Apr 8, 2024
d245229
Fix nullability warning
citizenmatt Apr 8, 2024
57bd01c
Reset Vim options when IDE setting changes
citizenmatt Apr 12, 2024
f5a6500
Update Vim option even when IdeaVim is disabled
citizenmatt Apr 12, 2024
aa512ad
Update options documentation
citizenmatt Apr 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ tasks {

patchPluginXml {
// Don't forget to update plugin.xml
sinceBuild.set("233.11799.67")
sinceBuild.set("233.11799.241")

changeNotes.set(
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
Expand Down
402 changes: 265 additions & 137 deletions doc/set-commands.md

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion src/main/java/com/maddyhome/idea/vim/VimPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,13 @@ private void registerIdeavimrc(VimEditor editor) {
ideavimrcRegistered = true;

if (!ApplicationManager.getApplication().isUnitTestMode()) {
executeIdeaVimRc(editor);
try {
VimInjectorKt.injector.getOptionGroup().startInitVimRc();
executeIdeaVimRc(editor);
}
finally {
VimInjectorKt.injector.getOptionGroup().endInitVimRc();
}
}
}

Expand Down
86 changes: 39 additions & 47 deletions src/main/java/com/maddyhome/idea/vim/group/EditorGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.intellij.openapi.editor.EditorSettings.*;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;

/**
Expand All @@ -55,12 +55,31 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor

private Boolean isKeyRepeat = null;

// TODO: Get rid of this custom line converter once we support soft wraps properly
// The builtin relative line converter looks like it's using Vim's logical lines for counting, where a Vim logical
// line is a buffer line, or a single line representing a fold of several buffer lines. This converter is counting
// screen lines (but badly - if you're on the second line of a wrapped line, it still counts like you're on the first.
// We really want to use Vim logical lines, but we don't currently support them for movement - we move by screen line.

private final CaretListener myLineNumbersCaretListener = new CaretListener() {
@Override
public void caretPositionChanged(@NotNull CaretEvent e) {
final boolean requiresRepaint = e.getNewPosition().line != e.getOldPosition().line;
if (requiresRepaint && options(injector, new IjVimEditor(e.getEditor())).getRelativenumber()) {
repaintRelativeLineNumbers(e.getEditor());
// We don't get notified when the IDE's settings change, so make sure we're up-to-date when the caret moves
final Editor editor = e.getEditor();
boolean relativenumber = ijOptions(injector, new IjVimEditor(editor)).getRelativenumber();
if (relativenumber) {
if (!hasRelativeLineNumbersInstalled(editor)) {
installRelativeLineNumbers(editor);
}
else {
// We must repaint on each caret move, so we update when caret's visual line doesn't match logical line
repaintRelativeLineNumbers(editor);
}
}
else {
if (hasRelativeLineNumbersInstalled(editor)) {
removeRelativeLineNumbers(editor);
}
}
}
};
Expand All @@ -73,11 +92,10 @@ private void initLineNumbers(final @NotNull Editor editor) {
editor.getCaretModel().addCaretListener(myLineNumbersCaretListener);
UserDataManager.setVimEditorGroup(editor, true);

UserDataManager.setVimLineNumbersInitialState(editor, editor.getSettings().isLineNumbersShown());
updateLineNumbers(editor);
}

private void deinitLineNumbers(@NotNull Editor editor, boolean isReleasing) {
private void deinitLineNumbers(@NotNull Editor editor) {
if (isProjectDisposed(editor) || !supportsVimLineNumbers(editor) || !UserDataManager.getVimEditorGroup(editor)) {
return;
}
Expand All @@ -86,14 +104,6 @@ private void deinitLineNumbers(@NotNull Editor editor, boolean isReleasing) {
UserDataManager.setVimEditorGroup(editor, false);

removeRelativeLineNumbers(editor);

// Don't reset the built in line numbers if we're releasing the editor. If we do, EditorSettings.setLineNumbersShown
// can cause the editor to refresh settings and can call into FileManagerImpl.getCachedPsiFile AFTER FileManagerImpl
// has been disposed (Closing the project with a Find Usages result showing a preview panel is a good repro case).
// See IDEA-184351 and VIM-1671
if (!isReleasing) {
setBuiltinLineNumbers(editor, UserDataManager.getVimLineNumbersInitialState(editor));
}
}

private static boolean supportsVimLineNumbers(final @NotNull Editor editor) {
Expand All @@ -107,41 +117,22 @@ private static boolean isProjectDisposed(final @NotNull Editor editor) {
}

private static void updateLineNumbers(final @NotNull Editor editor) {
final EffectiveOptions options = options(injector, new IjVimEditor(editor));
final boolean relativeNumber = options.getRelativenumber();
final boolean number = options.getNumber();

final boolean showBuiltinEditorLineNumbers = shouldShowBuiltinLineNumbers(editor, number, relativeNumber);

final EditorSettings settings = editor.getSettings();
if (settings.isLineNumbersShown() ^ showBuiltinEditorLineNumbers) {
// Update line numbers later since it may be called from a caret listener
// on the caret move and it may move the caret internally
ApplicationManager.getApplication().invokeLater(() -> {
if (editor.isDisposed()) return;
setBuiltinLineNumbers(editor, showBuiltinEditorLineNumbers);
});
final boolean isLineNumbersShown = editor.getSettings().isLineNumbersShown();
if (!isLineNumbersShown) {
return;
}

if (relativeNumber) {
final LineNumerationType lineNumerationType = editor.getSettings().getLineNumerationType();
if (lineNumerationType == LineNumerationType.RELATIVE || lineNumerationType == LineNumerationType.HYBRID) {
if (!hasRelativeLineNumbersInstalled(editor)) {
installRelativeLineNumbers(editor);
}
}
else if (hasRelativeLineNumbersInstalled(editor)) {
else {
removeRelativeLineNumbers(editor);
}
}

private static boolean shouldShowBuiltinLineNumbers(final @NotNull Editor editor, boolean number, boolean relativeNumber) {
final boolean initialState = UserDataManager.getVimLineNumbersInitialState(editor);
return initialState || number || relativeNumber;
}

private static void setBuiltinLineNumbers(final @NotNull Editor editor, boolean show) {
editor.getSettings().setLineNumbersShown(show);
}

private static boolean hasRelativeLineNumbersInstalled(final @NotNull Editor editor) {
return UserDataManager.getVimHasRelativeLineNumbersInstalled(editor);
}
Expand Down Expand Up @@ -256,8 +247,8 @@ public void editorCreated(@NotNull Editor editor) {
updateCaretsVisualAttributes(new IjVimEditor(editor));
}

public void editorDeinit(@NotNull Editor editor, boolean isReleased) {
deinitLineNumbers(editor, isReleased);
public void editorDeinit(@NotNull Editor editor) {
deinitLineNumbers(editor);
UserDataManager.unInitializeEditor(editor);
VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
Expand Down Expand Up @@ -322,17 +313,18 @@ public void onEffectiveValueChanged(@NotNull VimEditor editor) {
private static class RelativeLineNumberConverter implements LineNumberConverter {
@Override
public Integer convert(@NotNull Editor editor, int lineNumber) {
final boolean number = options(injector, new IjVimEditor(editor)).getNumber();
final IjVimEditor ijVimEditor = new IjVimEditor(editor);
final boolean number = ijOptions(injector, ijVimEditor).getNumber();
final int caretLine = editor.getCaretModel().getLogicalPosition().line;

// lineNumber is 1 based
if (number && (lineNumber - 1) == caretLine) {
return lineNumber;
if ((lineNumber - 1) == caretLine) {
return number ? lineNumber : 0;
}
else {
final int visualLine = new IjVimEditor(editor).bufferLineToVisualLine(lineNumber - 1);
final int currentVisualLine = new IjVimEditor(editor).bufferLineToVisualLine(caretLine);
return Math.abs(currentVisualLine - visualLine);
final int visualLine = ijVimEditor.bufferLineToVisualLine(lineNumber - 1);
final int caretVisualLine = editor.getCaretModel().getVisualPosition().line;
return Math.abs(caretVisualLine - visualLine);
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/maddyhome/idea/vim/group/IjOptionProperties.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
* As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
*/
public class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
public var breakindent: Boolean by optionProperty(IjOptions.breakindent)
public val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
public var cursorline: Boolean by optionProperty(IjOptions.cursorline)
public var fileformat: String by optionProperty(IjOptions.fileformat)
public var list: Boolean by optionProperty(IjOptions.list)
public var number: Boolean by optionProperty(IjOptions.number)
public var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
public var textwidth: Int by optionProperty(IjOptions.textwidth)
public var wrap: Boolean by optionProperty(IjOptions.wrap)

// IntelliJ specific options
public var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
public var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
public var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/com/maddyhome/idea/vim/group/IjOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ package com.maddyhome.idea.vim.group

import com.intellij.openapi.application.ApplicationNamesInfo
import com.maddyhome.idea.vim.api.Options
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.ex.exExceptionMessage
import com.maddyhome.idea.vim.options.NumberOption
import com.maddyhome.idea.vim.options.Option
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.LOCAL_TO_BUFFER
import com.maddyhome.idea.vim.options.OptionDeclaredScope.LOCAL_TO_WINDOW
import com.maddyhome.idea.vim.options.StringListOption
import com.maddyhome.idea.vim.options.StringOption
import com.maddyhome.idea.vim.options.ToggleOption
Expand All @@ -33,6 +38,56 @@ public object IjOptions {
Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
}

// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
public val breakindent: ToggleOption = addOption(ToggleOption("breakindent", LOCAL_TO_WINDOW, "bri", false))
public val colorcolumn: StringListOption = addOption(object : StringListOption("colorcolumn", LOCAL_TO_WINDOW, "cc", "") {
override fun checkIfValueValid(value: VimDataType, token: String) {
super.checkIfValueValid(value, token)
if (value != VimString.EMPTY) {
// Each element in the comma-separated string list needs to be a number. No spaces. Vim supports numbers
// beginning "+" or "-" to draw a highlight column relative to the 'textwidth' value. We don't fully support
// that, but we do automatically add "+0" because IntelliJ always displays the right margin
split((value as VimString).asString()).forEach {
if (!it.matches(Regex("[+-]?[0-9]+"))) {
throw exExceptionMessage("E474", token)
}
}
}
}
})
public val cursorline: ToggleOption = addOption(ToggleOption("cursorline", LOCAL_TO_WINDOW, "cul", false))
public val list: ToggleOption = addOption(ToggleOption("list", LOCAL_TO_WINDOW, "list", false))
public val number: ToggleOption = addOption(ToggleOption("number", LOCAL_TO_WINDOW, "nu", false))
public val relativenumber: ToggleOption = addOption(ToggleOption("relativenumber", LOCAL_TO_WINDOW, "rnu", false))
public val textwidth: NumberOption = addOption(UnsignedNumberOption("textwidth", LOCAL_TO_BUFFER, "tw", 0))
public val wrap: ToggleOption = addOption(ToggleOption("wrap", LOCAL_TO_WINDOW, "wrap", true))

// These options are not explicitly listed as local-noglobal in Vim's help, but are set when a new buffer is edited,
// based on the value of 'fileformats' or 'fileencodings'. To prevent unexpected file cnversion, we treat them as
// local-noglobal. See `:help local-noglobal`, `:help 'fileformats'` and `:help 'fileencodings'`
public val bomb: ToggleOption =
addOption(ToggleOption("bomb", LOCAL_TO_BUFFER, "bomb", false, isLocalNoGlobal = true))
public val fileencoding: StringOption = addOption(
StringOption(
"fileencoding",
LOCAL_TO_BUFFER,
"fenc",
VimString.EMPTY,
isLocalNoGlobal = true
)
)
public val fileformat: StringOption = addOption(
StringOption(
"fileformat",
LOCAL_TO_BUFFER,
"ff",
if (injector.systemInfoService.isWindows) "dos" else "unix",
boundedValues = setOf("dos", "unix", "mac"),
isLocalNoGlobal = true
)
)

// IntelliJ specific functionality - custom options
public val ide: StringOption = addOption(
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
)
Expand Down
Loading
Loading