diff --git a/plugins/sh/resources/META-INF/plugin.xml b/plugins/sh/resources/META-INF/plugin.xml index 8e7255dab6c3..ece71543e850 100644 --- a/plugins/sh/resources/META-INF/plugin.xml +++ b/plugins/sh/resources/META-INF/plugin.xml @@ -69,6 +69,7 @@ Adds support for working with shell script files + diff --git a/plugins/sh/resources/messages/ShBundle.properties b/plugins/sh/resources/messages/ShBundle.properties index 5a106bc83ff1..77641bd6e3ec 100644 --- a/plugins/sh/resources/messages/ShBundle.properties +++ b/plugins/sh/resources/messages/ShBundle.properties @@ -3,6 +3,7 @@ sh.code.style.choose.path=Choose Path to the Shfmt Formatter: sh.code.style.download.link=Download shfmt formatter sh.code.style.keep.column.alignment.padding=Keep column alignment padding sh.code.style.minify.program.to.reduce.its.size=Minify program to reduce its size +sh.code.style.unix.line.separator=Use Unix line separator (\\n) sh.code.style.redirect.operators.will.be.followed.by.a.space=Redirect operators will be followed by a space sh.code.style.switch.cases.will.be.indented=Switch cases will be indented diff --git a/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStylePanel.form b/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStylePanel.form index 5cc0a6531e72..a9a64784aedf 100644 --- a/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStylePanel.form +++ b/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStylePanel.form @@ -46,7 +46,7 @@ - + @@ -84,7 +84,7 @@ - + @@ -92,7 +92,7 @@ - + @@ -143,6 +143,12 @@ + + + + + + diff --git a/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStylePanel.java b/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStylePanel.java index 1557ee5357eb..d6b2c5fab14d 100644 --- a/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStylePanel.java +++ b/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStylePanel.java @@ -50,6 +50,7 @@ public class ShCodeStylePanel extends CodeStyleAbstractPanel { private JCheckBox myRedirectFollowedBySpace; private JCheckBox myKeepColumnAlignmentPadding; private JCheckBox myMinifyProgram; + private JCheckBox myUnixLineSeparator; @SuppressWarnings("unused") private ActionLink myShfmtDownloadLink; @@ -76,6 +77,7 @@ public class ShCodeStylePanel extends CodeStyleAbstractPanel { myRedirectFollowedBySpace.setText(ShBundle.message("sh.code.style.redirect.operators.will.be.followed.by.a.space")); myKeepColumnAlignmentPadding.setText(ShBundle.message("sh.code.style.keep.column.alignment.padding")); myMinifyProgram.setText(ShBundle.message("sh.code.style.minify.program.to.reduce.its.size")); + myUnixLineSeparator.setText(ShBundle.message("sh.code.style.unix.line.separator")); addPanelToWatch(myPanel); } @@ -130,6 +132,7 @@ public class ShCodeStylePanel extends CodeStyleAbstractPanel { shSettings.REDIRECT_FOLLOWED_BY_SPACE = myRedirectFollowedBySpace.isSelected(); shSettings.KEEP_COLUMN_ALIGNMENT_PADDING = myKeepColumnAlignmentPadding.isSelected(); shSettings.MINIFY_PROGRAM = myMinifyProgram.isSelected(); + shSettings.USE_UNIX_LINE_SEPARATOR = myUnixLineSeparator.isSelected(); ShSettings.setShfmtPath(myShfmtPathSelector.getText()); myWarningPanel.setVisible(!ShShfmtFormatterUtil.isValidPath(myShfmtPathSelector.getText())); myErrorLabel.setVisible(false); @@ -145,6 +148,7 @@ public class ShCodeStylePanel extends CodeStyleAbstractPanel { || isFieldModified(myRedirectFollowedBySpace, shSettings.REDIRECT_FOLLOWED_BY_SPACE) || isFieldModified(myKeepColumnAlignmentPadding, shSettings.KEEP_COLUMN_ALIGNMENT_PADDING) || isFieldModified(myMinifyProgram, shSettings.MINIFY_PROGRAM) + || isFieldModified(myUnixLineSeparator, shSettings.USE_UNIX_LINE_SEPARATOR) || isFieldModified(myTabCharacter, indentOptions.USE_TAB_CHARACTER) || isFieldModified(myIndentField, indentOptions.INDENT_SIZE) || isFieldModified(myTabField, indentOptions.TAB_SIZE) @@ -170,6 +174,7 @@ public class ShCodeStylePanel extends CodeStyleAbstractPanel { myRedirectFollowedBySpace.setSelected(shSettings.REDIRECT_FOLLOWED_BY_SPACE); myKeepColumnAlignmentPadding.setSelected(shSettings.KEEP_COLUMN_ALIGNMENT_PADDING); myMinifyProgram.setSelected(shSettings.MINIFY_PROGRAM); + myUnixLineSeparator.setSelected(shSettings.USE_UNIX_LINE_SEPARATOR); myShfmtPathSelector.setText(ShSettings.getShfmtPath()); myWarningPanel.setVisible(!ShShfmtFormatterUtil.isValidPath(ShSettings.getShfmtPath())); myErrorLabel.setVisible(false); diff --git a/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStyleSettings.java b/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStyleSettings.java index 9e3c1e580c74..5ea76ae8a580 100644 --- a/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStyleSettings.java +++ b/plugins/sh/src/com/intellij/sh/codeStyle/ShCodeStyleSettings.java @@ -10,6 +10,7 @@ public class ShCodeStyleSettings extends CustomCodeStyleSettings { public boolean REDIRECT_FOLLOWED_BY_SPACE = false; public boolean KEEP_COLUMN_ALIGNMENT_PADDING = false; public boolean MINIFY_PROGRAM = false; + public boolean USE_UNIX_LINE_SEPARATOR = true; public ShCodeStyleSettings(CodeStyleSettings container) { super("Shell_Script", container); diff --git a/plugins/sh/src/com/intellij/sh/formatter/ShLineEndingsAdapter.java b/plugins/sh/src/com/intellij/sh/formatter/ShLineEndingsAdapter.java new file mode 100644 index 000000000000..6dfd907b08e6 --- /dev/null +++ b/plugins/sh/src/com/intellij/sh/formatter/ShLineEndingsAdapter.java @@ -0,0 +1,74 @@ +// 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. +package com.intellij.sh.formatter; + +import com.intellij.application.options.CodeStyle; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.FileDocumentManagerListener; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.FileEditorManagerEvent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.StatusBar; +import com.intellij.openapi.wm.StatusBarWidget; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.wm.impl.status.LineSeparatorPanel; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.sh.codeStyle.ShCodeStyleSettings; +import com.intellij.sh.psi.ShFile; +import com.intellij.util.LineSeparator; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.openapi.wm.StatusBar.StandardWidgets.LINE_SEPARATOR_PANEL; + +public class ShLineEndingsAdapter implements FileDocumentManagerListener { + private boolean statusBarUpdated; + + @Override + public void beforeAllDocumentsSaving() { + statusBarUpdated = false; + } + + @Override + public void beforeDocumentSaving(@NotNull Document document) { + VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); + if (virtualFile == null) return; + + Project project = ProjectUtil.guessProjectForFile(virtualFile); + if (project == null) return; + PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); + if (!(psiFile instanceof ShFile)) return; + + CodeStyleSettings settings = CodeStyle.getSettings(psiFile); + ShCodeStyleSettings shSettings = settings.getCustomSettings(ShCodeStyleSettings.class); + String expectedLineSeparator = shSettings.USE_UNIX_LINE_SEPARATOR ? LineSeparator.LF.getSeparatorString() : + LineSeparator.getSystemLineSeparator().getSeparatorString(); + String fileDetectedLineSeparator = virtualFile.getDetectedLineSeparator(); + if (fileDetectedLineSeparator == null || !fileDetectedLineSeparator.equals(expectedLineSeparator)) { + virtualFile.setDetectedLineSeparator(expectedLineSeparator); + if (!statusBarUpdated) { + statusBarUpdated = true; + updateStatusBar(project); + } + } + } + + private static void updateStatusBar(@NotNull Project project) { + ApplicationManager.getApplication().invokeLater(() -> { + IdeFrame frame = WindowManager.getInstance().getIdeFrame(project); + StatusBar statusBar = frame != null ? frame.getStatusBar() : null; + StatusBarWidget widget = statusBar != null ? statusBar.getWidget(LINE_SEPARATOR_PANEL) : null; + + if (widget instanceof LineSeparatorPanel) { + FileEditorManagerEvent event = new FileEditorManagerEvent(FileEditorManager.getInstance(project), + null, null, null, null); + ((LineSeparatorPanel)widget).selectionChanged(event); + } + }); + } +}