Skip to content

Commit

Permalink
Visual indent guides (soft margins) initial implementation (IDEA-99875)
Browse files Browse the repository at this point in the history
  • Loading branch information
rvishnyakov committed Sep 7, 2017
1 parent 723f8ad commit 2a17866
Show file tree
Hide file tree
Showing 29 changed files with 965 additions and 184 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,10 @@
package com.intellij.openapi.editor;

import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

public interface EditorSettings {
boolean isRightMarginShown();
Expand All @@ -36,6 +40,22 @@ public interface EditorSettings {
int getRightMargin(Project project);
void setRightMargin(int myRightMargin);

/**
* Retrieves a list of soft margins (visual indent guides) to be used in the editor. If soft margins haven't been explicitly set
* with {@link #setSoftMargins(List)} method, they are obtained from code style settings: {@code CodeStyleSettings.getSoftMargins()}.
* @return A list of current editor soft margins. The list may be empty if no soft margins are defined.
*/
@NotNull
List<Integer> getSoftMargins();

/**
* Explicitly sets soft margins (visual indent guides) to be used in the editor instead of obtaining them from code style settings via
* {@code CodeStyleSettings.getSoftMargins()} method. It is important to distinguish and empty list from {@code null} value: the first
* will define no soft margins for the eidtor while the latter will restore the default behavior of using them from code style settings.
* @param softMargins A list of soft margins or {@code null} to use margins from code style settings.
*/
void setSoftMargins(@Nullable List<Integer> softMargins);

boolean isWrapWhenTypingReachesRightMargin(Project project);
void setWrapWhenTypingReachesRightMargin(boolean val);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,6 @@ public interface EditorColors {
TextAttributesKey BREADCRUMBS_HOVERED = TextAttributesKey.createTextAttributesKey("BREADCRUMBS_HOVERED");
TextAttributesKey BREADCRUMBS_CURRENT = TextAttributesKey.createTextAttributesKey("BREADCRUMBS_CURRENT");
TextAttributesKey BREADCRUMBS_INACTIVE = TextAttributesKey.createTextAttributesKey("BREADCRUMBS_INACTIVE");

ColorKey VISUAL_INDENT_GUIDE_COLOR = ColorKey.createColorKey("VISUAL_INDENT_GUIDE");
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ private void copyCustomSettingsFrom(@NotNull CodeStyleSettings from) {
public void copyFrom(CodeStyleSettings from) {
copyPublicFields(from, this);
copyPublicFields(from.OTHER_INDENT_OPTIONS, OTHER_INDENT_OPTIONS);
mySoftMargins.setValues(from.getSoftMargins());
copyCustomSettingsFrom(from);
}

Expand Down Expand Up @@ -760,6 +761,8 @@ public void readExternal(Element element) throws InvalidDataException {
IGNORE_SAME_INDENTS_FOR_LANGUAGES = true;
}

mySoftMargins.deserializeFrom(element);

migrateLegacySettings();
}

Expand All @@ -768,6 +771,7 @@ public void writeExternal(Element element) throws WriteExternalException {
setVersion(element, myVersion);
CodeStyleSettings parentSettings = new CodeStyleSettings();
DefaultJDOMExternalizer.writeExternal(this, element, new DifferenceFilter<>(this, parentSettings));
mySoftMargins.serializeInto(element);

myUnknownElementWriter.write(element, getCustomSettingsValues(), CustomCodeStyleSettings::getTagName, settings -> {
CustomCodeStyleSettings parentCustomSettings = parentSettings.getCustomSettings(settings.getClass());
Expand Down Expand Up @@ -1302,4 +1306,49 @@ public boolean isAccept(@NotNull Field field) {
public int getVersion() {
return myVersion;
}

/**
* Returns soft margins (visual indent guides positions) for the language. If language settings do not exists or language soft margins are
* empty, default (root) soft margins are returned.
* @param language The language to retrieve soft margins for or {@code null} for default soft margins.
* @return Language or default soft margins.
* @see #getDefaultSoftMargins()
*/
@NotNull
public List<Integer> getSoftMargins(@Nullable Language language) {
if (language != null) {
CommonCodeStyleSettings languageSettings = getCommonSettings(language);
if (languageSettings != null && !languageSettings.getSoftMargins().isEmpty()) {
return languageSettings.getSoftMargins();
}
}
return getDefaultSoftMargins();
}

/**
* Set soft margins (visual indent guides) for the language. Note: language code style settings must exist.
* @param language The language to set soft margins for.
* @param softMargins The soft margins to set.
*/
public void setSoftMargins(@NotNull Language language, List<Integer> softMargins) {
CommonCodeStyleSettings languageSettings = getCommonSettings(language);
assert languageSettings != null : "Settings for language " + language.getDisplayName() + " do not exist";
languageSettings.setSoftMargins(softMargins);
}

/**
* @return Default (root) soft margins used for languages not defining them explicitly.
*/
@NotNull
public List<Integer> getDefaultSoftMargins() {
return getSoftMargins();
}

/**
* Sets the default soft margins used for languages not defining them explicitly.
* @param softMargins The default soft margins.
*/
public void setDefaultSoftMargins(List<Integer> softMargins) {
setSoftMargins(softMargins);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;

/**
Expand All @@ -59,6 +60,8 @@ public class CommonCodeStyleSettings {
private final FileType myFileType;
private boolean myForceArrangeMenuAvailable;

protected SoftMargins mySoftMargins = new SoftMargins();

@NonNls private static final String INDENT_OPTIONS_TAG = "indentOptions";

public CommonCodeStyleSettings(Language language, FileType fileType) {
Expand Down Expand Up @@ -129,6 +132,7 @@ public CommonCodeStyleSettings clone(@NotNull CodeStyleSettings rootSettings) {
if (myArrangementSettings != null) {
commonSettings.setArrangementSettings(myArrangementSettings.clone());
}
commonSettings.setSoftMargins(getSoftMargins());
return commonSettings;
}

Expand Down Expand Up @@ -167,6 +171,7 @@ public void readExternal(Element element) throws InvalidDataException {
if (arrangementRulesContainer != null) {
myArrangementSettings = ArrangementUtil.readExternal(arrangementRulesContainer, myLanguage);
}
mySoftMargins.deserializeFrom(element);
}

public void writeExternal(Element element) throws WriteExternalException {
Expand All @@ -177,6 +182,7 @@ public void writeExternal(Element element) throws WriteExternalException {
supportedFields.add("FORCE_REARRANGE_MODE");
}
DefaultJDOMExternalizer.writeExternal(this, element, new SupportedFieldsDiffFilter(this, supportedFields, defaultSettings));
mySoftMargins.serializeInto(element);
if (myIndentOptions != null) {
IndentOptions defaultIndentOptions = defaultSettings != null ? defaultSettings.getIndentOptions() : null;
Element indentOptionsElement = new Element(INDENT_OPTIONS_TAG);
Expand Down Expand Up @@ -1074,4 +1080,13 @@ protected boolean arrangementSettingsEqual(CommonCodeStyleSettings obj) {
}
return Comparing.equal(theseSettings, obj.getArrangementSettings());
}

@NotNull
public List<Integer> getSoftMargins() {
return mySoftMargins.getValues();
}

void setSoftMargins(List<Integer> values) {
mySoftMargins.setValues(values);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ public static void copy(@NotNull CommonCodeStyleSettings source, @NotNull Common
CommonCodeStyleSettings.copyPublicFields(sourceIndentOptions, targetIndentOptions);
}
}
target.setSoftMargins(source.getSoftMargins());
}

@Override
Expand Down
109 changes: 109 additions & 0 deletions platform/lang-api/src/com/intellij/psi/codeStyle/SoftMargins.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle;

import com.intellij.util.xmlb.XmlSerializer;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class SoftMargins implements Cloneable {

private List<Integer> myValues;

@SuppressWarnings("unused") // Serialization getter
@Nullable
public String getSOFT_MARGINS() {
return myValues != null ? toString() : null;
}

@SuppressWarnings("unused") // Serialization setter
public void setSOFT_MARGINS(@Nullable String valueList) {
if (valueList != null) {
String[] values = valueList.split(",\\s*");
myValues = new ArrayList<>(values.length);
int i = 0;
for (String value : values) {
try {
myValues.add(Integer.parseInt(value));
}
catch (NumberFormatException nfe) {
myValues = null;
return;
}
}
Collections.sort(myValues);
}
}

@NotNull
List<Integer> getValues() {
return myValues != null ? myValues : Collections.emptyList();
}

void setValues(List<Integer> values) {
if (values != null) {
myValues = new ArrayList<>(values);
Collections.sort(myValues);
}
else {
myValues = null;
}
}

@SuppressWarnings("MethodDoesntCallSuperMethod")
@Override
public Object clone() {
SoftMargins copy = new SoftMargins();
copy.setValues(myValues);
return copy;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof SoftMargins) {
List<Integer> otherMargins = ((SoftMargins)obj).getValues();
return otherMargins.equals(getValues());
}
return false;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (myValues != null) {
for (int margin: myValues) {
if (sb.length() > 0) sb.append(",");
sb.append(margin);
}
}
return sb.toString();
}

public void serializeInto(@NotNull Element element) {
if (myValues != null && myValues.size() > 0) {
XmlSerializer.serializeInto(this, element);
}
}

public void deserializeFrom(@NotNull Element element) {
XmlSerializer.deserializeInto(this, element);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,12 @@ protected static void putGroupTop(@NotNull Map<CodeStyleSettingPresentation.Sett
result.put(new SettingsGroup(null), ContainerUtil.immutableList(
new CodeStyleBoundedIntegerSettingPresentation("RIGHT_MARGIN", ApplicationBundle.message("editbox.right.margin.columns"), 0, 999,
-1,
ApplicationBundle.message("settings.code.style.default.general"))
ApplicationBundle.message("settings.code.style.default.general")),
new CodeStyleSelectSettingPresentation("WRAP_ON_TYPING", ApplicationBundle.message("wrapping.wrap.on.typing"), WRAP_ON_TYPING_VALUES,
WRAP_ON_TYPING_OPTIONS),
new CodeStyleSoftMarginsPresentation()
));

putGroupTop(result, "WRAP_ON_TYPING", ApplicationBundle.message("wrapping.wrap.on.typing"), WRAP_ON_TYPING_VALUES,
WRAP_ON_TYPING_OPTIONS);

result.put(new SettingsGroup(WRAPPING_KEEP), ContainerUtil.immutableList(
new CodeStyleSettingPresentation("KEEP_LINE_BREAKS", ApplicationBundle.message("wrapping.keep.line.breaks")),
new CodeStyleSettingPresentation("KEEP_FIRST_COLUMN_COMMENT",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle.presentation;

import com.intellij.openapi.application.ApplicationBundle;

public class CodeStyleSoftMarginsPresentation extends CodeStyleSettingPresentation {

public static final String OPTION_NAME = "SoftMargins";

public CodeStyleSoftMarginsPresentation() {
super(OPTION_NAME, ApplicationBundle.message("settings.code.style.visual.guides"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ private static void fillEditorSettings(final EditorSettings editorSettings) {
editorSettings.setAdditionalColumnsCount(0);
editorSettings.setAdditionalLinesCount(1);
editorSettings.setUseSoftWraps(false);
editorSettings.setSoftMargins(Collections.emptyList());
}

protected void updatePreview(boolean useDefaultSample) {
Expand All @@ -190,10 +191,15 @@ else if (useDefaultSample || myTextToReformat == null) {
CommandProcessor.getInstance().executeCommand(finalProject, () -> replaceText(finalProject), null, null);

myEditor.getSettings().setRightMargin(getAdjustedRightMargin());
myEditor.getSettings().setSoftMargins(getCurrentSoftMargins());
myLastDocumentModificationStamp = myEditor.getDocument().getModificationStamp();
myEditor.getScrollingModel().scrollVertically(currOffs);
}

private List<Integer> getCurrentSoftMargins() {
return getSettings().getSoftMargins(getDefaultLanguage());
}

private int getAdjustedRightMargin() {
int result = getRightMargin();
return result > 0 ? result : CodeStyleFacade.getInstance(ProjectUtil.guessCurrentProject(getPanel())).getRightMargin(getDefaultLanguage());
Expand Down
Loading

0 comments on commit 2a17866

Please sign in to comment.