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);
+ }
+ });
+ }
+}