From 98daeb0f804a82e9a28ebe7f4c0859fa4a866f99 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Fri, 6 Nov 2026 01:15:01 -0800 Subject: [PATCH] Add max-line-length configuration support to the IntelliJ plugin - Add max line length setting to GoogleJavaFormatSettings and UI. - Preserve custom maxLineLength across Google and AOSP style selection. - Add tests for settings persistence and formatting service. Fixes https://github.com/google/google-java-format/pull/1471, https://github.com/google/google-java-format/issues/1516 COPYBARA_INTEGRATE_REVIEW=https://github.com/google/google-java-format/pull/1471 from fredyw:add-max-line-length-idea-plugin a90d346998f4b74e73c41069013f9be85d7bd16d PiperOrigin-RevId: 1015351515 --- .../GoogleJavaFormatConfigurable.form | 18 +++- .../GoogleJavaFormatConfigurable.java | 60 ++++++++++++- .../intellij/GoogleJavaFormatSettings.java | 17 +++- .../intellij/UiFormatterStyle.java | 10 +-- ...GoogleJavaFormatFormattingServiceTest.java | 25 ++++++ .../GoogleJavaFormatSettingsTest.java | 84 +++++++++++++++++++ 6 files changed, 198 insertions(+), 16 deletions(-) create mode 100644 idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettingsTest.java diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form index 1db1d7920..7f299f589 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.form @@ -1,6 +1,6 @@
- + @@ -18,7 +18,7 @@ - + @@ -35,6 +35,20 @@ + + + + + + + + + + + + + + diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java index 3a98c2eb9..72c0f49e2 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatConfigurable.java @@ -16,12 +16,15 @@ package com.google.googlejavaformat.intellij; +import com.google.googlejavaformat.Doc; import com.google.googlejavaformat.intellij.GoogleJavaFormatSettings.EnabledState; import com.intellij.openapi.options.BaseConfigurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; +import com.intellij.ui.SimpleListCellRenderer; +import com.intellij.ui.components.JBTextField; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; @@ -41,6 +44,7 @@ class GoogleJavaFormatConfigurable extends BaseConfigurable implements Searchabl private JPanel panel; private JCheckBox enable; private JComboBox styleComboBox; + private JBTextField maxLineLengthField; public GoogleJavaFormatConfigurable(Project project) { this.project = project; @@ -81,6 +85,17 @@ public void apply() throws ConfigurationException { GoogleJavaFormatSettings settings = GoogleJavaFormatSettings.getInstance(project); settings.setEnabled(enable.isSelected() ? EnabledState.ENABLED : getDisabledState()); settings.setStyle(((UiFormatterStyle) styleComboBox.getSelectedItem()).convert()); + int maxLineLength; + try { + maxLineLength = Integer.parseInt(maxLineLengthField.getText().trim()); + } catch (NumberFormatException e) { + throw new ConfigurationException("Max line length must be a valid integer"); + } + if (maxLineLength <= 0 || maxLineLength >= Doc.MAX_LINE_WIDTH) { + throw new ConfigurationException( + String.format("Max line length must be between 1 and %d", Doc.MAX_LINE_WIDTH - 1)); + } + settings.setMaxLineLength(maxLineLength); } private EnabledState getDisabledState() { @@ -95,13 +110,15 @@ public void reset() { GoogleJavaFormatSettings settings = GoogleJavaFormatSettings.getInstance(project); enable.setSelected(settings.isEnabled()); styleComboBox.setSelectedItem(UiFormatterStyle.convert(settings.getStyle())); + maxLineLengthField.setText(String.valueOf(settings.getMaxLineLength())); } @Override public boolean isModified() { GoogleJavaFormatSettings settings = GoogleJavaFormatSettings.getInstance(project); return enable.isSelected() != settings.isEnabled() - || !styleComboBox.getSelectedItem().equals(UiFormatterStyle.convert(settings.getStyle())); + || !styleComboBox.getSelectedItem().equals(UiFormatterStyle.convert(settings.getStyle())) + || !maxLineLengthField.getText().trim().equals(String.valueOf(settings.getMaxLineLength())); } @Override @@ -109,6 +126,9 @@ public void disposeUIResources() {} private void createUIComponents() { styleComboBox = new ComboBox<>(UiFormatterStyle.values()); + styleComboBox.setRenderer(SimpleListCellRenderer.create("", UiFormatterStyle::getDescription)); + maxLineLengthField = new JBTextField(); + maxLineLengthField.setColumns(4); } { @@ -127,7 +147,7 @@ private void createUIComponents() { private void $$$setupUI$$$() { createUIComponents(); panel = new JPanel(); - panel.setLayout(new GridLayoutManager(3, 2, new Insets(0, 0, 0, 0), -1, -1)); + panel.setLayout(new GridLayoutManager(4, 2, new Insets(0, 0, 0, 0), -1, -1)); enable = new JCheckBox(); enable.setText("Enable google-java-format"); panel.add( @@ -150,7 +170,7 @@ private void createUIComponents() { panel.add( spacer1, new GridConstraints( - 2, + 3, 0, 1, 2, @@ -197,6 +217,40 @@ private void createUIComponents() { null, 1, false)); + final JLabel label2 = new JLabel(); + label2.setText("Max line length"); + panel.add( + label2, + new GridConstraints( + 2, + 0, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 0, + false)); + panel.add( + maxLineLengthField, + new GridConstraints( + 2, + 1, + 1, + 1, + GridConstraints.ANCHOR_WEST, + GridConstraints.FILL_NONE, + GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_FIXED, + null, + null, + null, + 1, + false)); } /** diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java index 4546f68a3..1e443f7e6 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettings.java @@ -72,11 +72,21 @@ boolean isUninitialized() { } JavaFormatterOptions.Style getStyle() { - return state.style; + UiFormatterStyle style = state.style != null ? state.style : UiFormatterStyle.GOOGLE; + return style.convert().toBuilder().maxLineLength(state.maxLineLength).build(); } void setStyle(JavaFormatterOptions.Style style) { - state.style = style; + state.style = UiFormatterStyle.convert(style); + state.maxLineLength = style.maxLineLength(); + } + + int getMaxLineLength() { + return state.maxLineLength; + } + + void setMaxLineLength(int maxLineLength) { + state.maxLineLength = maxLineLength; } enum EnabledState { @@ -88,7 +98,8 @@ enum EnabledState { static class State { private EnabledState enabled = EnabledState.UNKNOWN; - public JavaFormatterOptions.Style style = JavaFormatterOptions.Style.GOOGLE; + public UiFormatterStyle style = UiFormatterStyle.GOOGLE; + public int maxLineLength = JavaFormatterOptions.Style.GOOGLE.maxLineLength(); // enabled used to be a boolean so we use bean property methods for backwards compatibility public void setEnabled(@Nullable String enabledStr) { diff --git a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/UiFormatterStyle.java b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/UiFormatterStyle.java index 24ed6f62c..eddd498ac 100644 --- a/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/UiFormatterStyle.java +++ b/idea_plugin/src/main/java/com/google/googlejavaformat/intellij/UiFormatterStyle.java @@ -18,8 +18,6 @@ import com.google.googlejavaformat.java.JavaFormatterOptions; import com.google.googlejavaformat.java.JavaFormatterOptions.Style; -import java.util.Arrays; -import java.util.Objects; /** Configuration options for the formatting style. */ enum UiFormatterStyle { @@ -34,8 +32,7 @@ enum UiFormatterStyle { this.style = style; } - @Override - public String toString() { + public String getDescription() { return description; } @@ -44,9 +41,6 @@ public JavaFormatterOptions.Style convert() { } static UiFormatterStyle convert(JavaFormatterOptions.Style style) { - return Arrays.stream(UiFormatterStyle.values()) - .filter(value -> Objects.equals(value.style, style)) - .findFirst() - .get(); + return style.isAosp() ? AOSP : GOOGLE; } } diff --git a/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java b/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java index bdf50a934..4a90079e7 100644 --- a/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java +++ b/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatFormattingServiceTest.java @@ -116,6 +116,31 @@ public void aospStyle() throws Exception { assertThat(delegatingFormatter.wasInvoked()).isTrue(); } + @Test + public void customMaxLineLength() throws Exception { + settings.setMaxLineLength(40); + PsiFile file = + createPsiFile( + "com/foo/FormatTest.java", + "package com.foo;", + "public class FormatTest {", + "void foo() {", + "System.out.println(\"This is a long string that wraps\");", + "}", + "}"); + String origText = file.getText(); + CodeStyleManager manager = CodeStyleManager.getInstance(file.getProject()); + WriteCommandAction.runWriteCommandAction( + file.getProject(), () -> manager.reformatText(file, 0, file.getTextLength())); + + Style styleWith40 = Style.GOOGLE.toBuilder().maxLineLength(40).build(); + assertThat(file.getText()) + .isEqualTo( + new Formatter(JavaFormatterOptions.builder().style(styleWith40).build()) + .formatSource(origText)); + assertThat(delegatingFormatter.wasInvoked()).isTrue(); + } + @Test public void canChangeWhitespaceOnlyDoesNotReorderModifiers() throws Exception { settings.setStyle(Style.GOOGLE); diff --git a/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettingsTest.java b/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettingsTest.java new file mode 100644 index 000000000..c003c3a75 --- /dev/null +++ b/idea_plugin/src/test/java/com/google/googlejavaformat/intellij/GoogleJavaFormatSettingsTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2026 Google Inc. All Rights Reserved. + * + * 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.google.googlejavaformat.intellij; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.googlejavaformat.intellij.GoogleJavaFormatSettings.State; +import com.google.googlejavaformat.java.JavaFormatterOptions.Style; +import com.intellij.configurationStore.XmlSerializer; +import org.jdom.Element; +import org.junit.Test; + +public class GoogleJavaFormatSettingsTest { + @Test + public void defaultMaxLineLengthMatchesGoogleStyle() { + State state = new State(); + assertThat(state.maxLineLength).isEqualTo(Style.GOOGLE.maxLineLength()); + } + + @Test + public void serializeAndDeserialize() { + State state = new State(); + state.setEnabled("true"); + state.style = UiFormatterStyle.AOSP; + state.maxLineLength = 120; + + Element element = XmlSerializer.serialize(state); + State loaded = XmlSerializer.deserialize(element, State.class); + + assertThat(loaded.getEnabled()).isEqualTo("true"); + assertThat(loaded.style).isEqualTo(UiFormatterStyle.AOSP); + assertThat(loaded.maxLineLength).isEqualTo(120); + } + + @Test + public void deserializeExistingProjectSettings() { + Element element = new Element("state"); + Element optAosp = new Element("option"); + optAosp.setAttribute("name", "style"); + optAosp.setAttribute("value", "AOSP"); + element.addContent(optAosp); + + State loaded = XmlSerializer.deserialize(element, State.class); + assertThat(loaded.style).isEqualTo(UiFormatterStyle.AOSP); + } + + @Test + public void nullStyleFallsBackToGoogle() { + State state = new State(); + state.style = null; + GoogleJavaFormatSettings settings = new GoogleJavaFormatSettings(null); + settings.loadState(state); + + assertThat(settings.getStyle()).isEqualTo(Style.GOOGLE); + } + + @Test + public void getAndSetStylePreservesMaxLineLength() { + GoogleJavaFormatSettings.State state = new GoogleJavaFormatSettings.State(); + state.maxLineLength = 140; + state.style = UiFormatterStyle.GOOGLE; + + assertThat( + state.style.convert().toBuilder() + .maxLineLength(state.maxLineLength) + .build() + .maxLineLength()) + .isEqualTo(140); + } +}