Temporary revert IDEA-181641 and IDEA-58488 to fix tests

GitOrigin-RevId: 22963f4a05714895de8cae5dbda3529f21c5f3bc
This commit is contained in:
Aleksei Pomelov
2021-10-27 20:50:16 +03:00
committed by intellij-monorepo-bot
parent cae9e70461
commit 6206bd18f2
29 changed files with 508 additions and 966 deletions

View File

@@ -130,7 +130,6 @@
"lambda_brace_style": "end_of_line",
"layout_static_imports_separately": true,
"line_comment_add_space": false,
"line_comment_add_space_on_reformat": false,
"line_comment_at_first_column": true,
"method_annotation_wrap": "split_into_lines",
"method_brace_style": "next_line_if_wrapped",

View File

@@ -1,6 +0,0 @@
package com.company;
class Löschen
{
// This class name will be broken unless encoding is set
}

View File

@@ -1,4 +0,0 @@
package com.company;
class Löschen {
// This class name will be broken unless encoding is set
}

View File

@@ -1,77 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.formatting.commandLine
import com.intellij.JavaTestUtil
import com.intellij.application.options.CodeStyle
import com.intellij.formatting.commandLine.FileSetCodeStyleProcessor
import com.intellij.formatting.commandLine.MessageOutput
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VfsUtilCore.loadText
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import com.intellij.testFramework.LightPlatformTestCase
import junit.framework.TestCase.*
import java.io.File
import java.io.PrintWriter
val BASE_PATH = JavaTestUtil.getJavaTestDataPath() + "/psi/formatter/commandLine"
abstract class FileSetCodeStyleProcessorTestBase : LightPlatformTestCase() {
var codeStyleSettings: CodeStyleSettings? = null
var messageOutput: MessageOutput? = null
var processor: FileSetCodeStyleProcessor? = null
override fun setUp() {
super.setUp()
codeStyleSettings = CodeStyle.createTestSettings().apply {
getCommonSettings(JavaLanguage.INSTANCE).apply {
indentOptions!!.INDENT_SIZE = 2
CLASS_BRACE_STYLE = CommonCodeStyleSettings.NEXT_LINE
IF_BRACE_FORCE = CommonCodeStyleSettings.FORCE_BRACES_ALWAYS
}
}
messageOutput = MessageOutput(PrintWriter(System.out), PrintWriter(System.err))
}
override fun tearDown() {
processor?.close()
super.tearDown()
}
}
fun compareDirs(expectedDir: File, resultDir: File) {
assertTrue(expectedDir.isDirectory && resultDir.isDirectory)
expectedDir.listFiles()?.forEach { expected ->
val actual = File(resultDir.canonicalPath + File.separator + expected.name)
assertTrue("Cannot find expected " + actual.path, actual.exists())
if (expected.isDirectory) {
compareDirs(expected, actual)
}
else {
assertContentEquals(expected, actual)
}
}
}
fun assertContentEquals(expectedFile: File, actualFile: File) {
val expectedVFile = VfsUtil.findFileByIoFile(expectedFile, true)
assertNotNull(expectedVFile)
val actualVFile = VfsUtil.findFileByIoFile(actualFile, true)
assertNotNull(actualVFile)
assertEquals(loadText(expectedVFile!!), loadText(actualVFile!!))
}
fun createSourceDir(subDir: String) =
FileUtil
.createTempDirectory("unitTest", "_format")
.also { sourceDir ->
FileUtil.copyDir(File(BASE_PATH).resolve(subDir), sourceDir)
}

View File

@@ -1,38 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.formatting.commandLine
import com.intellij.formatting.commandLine.FileSetFormatValidator
import junit.framework.TestCase
import java.io.File
class FileSetFormatValidatorTest : FileSetCodeStyleProcessorTestBase() {
override fun setUp() {
super.setUp()
processor = FileSetFormatValidator(codeStyleSettings!!, messageOutput!!, true).also {
it.addFileMask(Regex(".*\\.java"))
}
}
fun testFormatDryRun_needsFormatting() {
processor?.apply {
val sourceDir = createSourceDir("baseTest/original")
addEntry(sourceDir.canonicalPath)
processFiles()
compareDirs(File(BASE_PATH).resolve("baseTest/original"), sourceDir) // No modifications expected
assertNotSame(processed, succeeded)
}
}
fun testFormatDryRun_wellFormatted() {
processor?.apply {
val sourceDir = createSourceDir("baseTest/expected")
addEntry(sourceDir.canonicalPath)
processFiles()
compareDirs(File(BASE_PATH).resolve("baseTest/expected"), sourceDir) // No modifications expected
TestCase.assertEquals(processed, succeeded)
}
}
}

View File

@@ -1,62 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.formatting.commandLine
import com.intellij.formatting.commandLine.CodeStyleProcessorBuildException.ArgumentsException
import com.intellij.formatting.commandLine.CodeStyleProcessorBuildException.ShowUsageException
import com.intellij.formatting.commandLine.FileSetCodeStyleProcessor
import com.intellij.formatting.commandLine.FileSetFormatValidator
import com.intellij.formatting.commandLine.FileSetFormatter
import com.intellij.formatting.commandLine.createFormatter
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import com.intellij.testFramework.LightPlatformTestCase
import java.io.File
class FileSetFormatterStarterTest : LightPlatformTestCase() {
private inline fun <reified T : Exception> expectExceptionsOnArgs(vararg args: String) {
try {
createFormatter(args.toList().toTypedArray())
fail("Missing expected exception ${T::class}")
}
catch (e: Exception) {
assertInstanceOf(e, T::class.java)
}
}
fun testHelp_noArgs0() = expectExceptionsOnArgs<ShowUsageException>()
fun testHelp_noArgs1() = expectExceptionsOnArgs<ShowUsageException>("format")
fun testHelp_explicit_only() = expectExceptionsOnArgs<ShowUsageException>("format", "-help")
fun testHelp_explicit_withOthers() = expectExceptionsOnArgs<ShowUsageException>("format", "-r", "src", "-h")
fun testUnknownArgumentFails() = expectExceptionsOnArgs<ArgumentsException>("format", "-r", "src", "-unknown_arg")
fun testMissingParamForMasks() = expectExceptionsOnArgs<ArgumentsException>("format", "-r", "src", "-m")
fun testMissingParamForSettings() = expectExceptionsOnArgs<ArgumentsException>("format", "-r", "src", "-s")
fun testMissingSettingsFile() = expectExceptionsOnArgs<ArgumentsException>("format", "-s",
"really_hope_noone_adds_file_with_this_name_in_future", "src")
fun testDefaultArgs() {
val processor: FileSetCodeStyleProcessor = createFormatter(arrayOf("format", "."))
assertInstanceOf(processor, FileSetFormatter::class.java)
assertFalse(processor.isRecursive)
assertEmpty(processor.getFileMasks())
assertEquals(CodeStyleSettingsManager.getInstance().createSettings(), processor.codeStyleSettings)
assertEquals(1, processor.getEntries().size)
assertEquals(File(".").absolutePath, processor.getEntries()[0].absolutePath)
}
fun testNonDefaultArgs() {
val processor: FileSetCodeStyleProcessor = createFormatter(arrayOf("format", "-r", "-d", "-m", "*.java, ,*.kt,", ".", ".."))
assertInstanceOf(processor, FileSetFormatValidator::class.java)
assertTrue(processor.isRecursive)
assertEquals(2, processor.getFileMasks().size)
assertEquals(".*\\.java", processor.getFileMasks()[0].pattern)
assertEquals(".*\\.kt", processor.getFileMasks()[1].pattern)
assertEquals(2, processor.getEntries().size)
assertEquals(File(".").absolutePath, processor.getEntries()[0].absolutePath)
assertEquals(File("..").absolutePath, processor.getEntries()[1].absolutePath)
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.java.formatting.commandLine;
import com.intellij.JavaTestUtil;
import com.intellij.application.options.CodeStyle;
import com.intellij.formatting.commandLine.FileSetFormatter;
import com.intellij.formatting.commandLine.MessageOutput;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.testFramework.LightPlatformTestCase;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
public class FileSetFormatterTest extends LightPlatformTestCase {
private static final String BASE_PATH = JavaTestUtil.getJavaTestDataPath() + "/psi/formatter/commandLine";
public void testFormat() throws IOException {
CodeStyleSettings settings = CodeStyle.createTestSettings();
CommonCodeStyleSettings javaSettings = settings.getCommonSettings(JavaLanguage.INSTANCE);
javaSettings.getIndentOptions().INDENT_SIZE = 2;
javaSettings.CLASS_BRACE_STYLE = CommonCodeStyleSettings.NEXT_LINE;
javaSettings.IF_BRACE_FORCE = CommonCodeStyleSettings.FORCE_BRACES_ALWAYS;
File sourceDir = createSourceDir("original");
String fileSpec = sourceDir.getCanonicalPath();
MessageOutput messageOutput = new MessageOutput(new PrintWriter(System.out), new PrintWriter(System.err));
FileSetFormatter formatter = new FileSetFormatter(messageOutput);
formatter.addEntry(fileSpec);
formatter.addFileMask("*.java");
formatter.setRecursive();
formatter.setCodeStyleSettings(settings);
formatter.processFiles();
compareDirs(new File(BASE_PATH + File.separator + "expected"), sourceDir);
}
private static File createSourceDir(@NotNull String subDir) throws IOException {
File sourceDir = FileUtil.createTempDirectory("unitTest", "_format");
FileUtil.copyDir(new File(BASE_PATH + File.separator + subDir), sourceDir);
return sourceDir;
}
private static void compareDirs(@NotNull File expectedDir, @NotNull File resultDir) throws IOException {
assertTrue(expectedDir.isDirectory() && resultDir.isDirectory());
File[] childFiles = expectedDir.listFiles();
if (childFiles != null) {
for (File file : childFiles) {
File resultEntry = new File(resultDir.getCanonicalPath() + File.separator + file.getName());
assertTrue("Cannot find expected" + resultEntry.getPath(), resultEntry.exists());
if (!file.isDirectory()) {
assertContentEquals(file, resultEntry);
}
else {
compareDirs(file, resultEntry);
}
}
}
}
private static void assertContentEquals(@NotNull File expectedFile, @NotNull File actualFile) throws IOException {
VirtualFile expectedVFile = VfsUtil.findFileByIoFile(expectedFile, true);
assertNotNull(expectedVFile);
VirtualFile actualVFile = VfsUtil.findFileByIoFile(actualFile, true);
assertNotNull(actualVFile);
String expected = VfsUtilCore.loadText(expectedVFile);
String actual = VfsUtilCore.loadText(actualVFile);
assertEquals(expected, actual);
}
}

View File

@@ -1,56 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.formatting.commandLine
import com.intellij.formatting.commandLine.FileSetFormatter
import junit.framework.ComparisonFailure
import java.io.File
import java.nio.charset.Charset
import kotlin.text.Charsets.UTF_8
class FileSetFormatterTest : FileSetCodeStyleProcessorTestBase() {
override fun setUp() {
super.setUp()
processor = FileSetFormatter(codeStyleSettings!!, messageOutput!!, true).also {
it.addFileMask(Regex(".*\\.java"))
}
}
fun testFormat() {
processor?.apply {
val sourceDir = createSourceDir("baseTest/original")
addEntry(sourceDir.canonicalPath)
processFiles()
compareDirs(File(BASE_PATH).resolve("baseTest/expected"), sourceDir)
}
}
private fun testCustomEncoding(charset: Charset) {
FileSetFormatter(codeStyleSettings!!, messageOutput!!, true, charset).use {
val sourceDir = createSourceDir("encoding/original")
it.addEntry(sourceDir.canonicalPath)
it.processFiles()
val expectedBytes = File(BASE_PATH).resolve("encoding/expected").resolve("Test_ISO_8859_15.java").readBytes()
val processedBytes = sourceDir.resolve("Test_ISO_8859_15.java").readBytes()
assertEquals(expectedBytes.contentToString(), processedBytes.contentToString())
}
}
fun testCustomEncodingFail() {
try {
testCustomEncoding(UTF_8)
fail("Missing expected exception")
}
catch (e: ComparisonFailure) {
// OK, expected exception
}
}
fun testCustomEncodingOk() {
testCustomEncoding(Charset.forName("ISO-8859-15"))
}
}

View File

@@ -208,7 +208,6 @@ public interface CodeStyleSettingsCustomizable {
enum CommenterOption {
LINE_COMMENT_ADD_SPACE,
LINE_COMMENT_ADD_SPACE_ON_REFORMAT,
LINE_COMMENT_AT_FIRST_COLUMN,
BLOCK_COMMENT_AT_FIRST_COLUMN,
BLOCK_COMMENT_ADD_SPACE

View File

@@ -242,8 +242,6 @@ public class CommonCodeStyleSettings {
public boolean LINE_COMMENT_ADD_SPACE = false;
public boolean BLOCK_COMMENT_ADD_SPACE = false;
public boolean LINE_COMMENT_ADD_SPACE_ON_REFORMAT = false;
public boolean KEEP_LINE_BREAKS = true;
/**

View File

@@ -14,7 +14,6 @@ import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable.CommenterOption;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.*;
@@ -24,8 +23,6 @@ import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import static java.lang.Character.isLetterOrDigit;
/**
* Base class and extension point for common code style settings for a specific language.
*/
@@ -456,31 +453,4 @@ public abstract class LanguageCodeStyleSettingsProvider extends CodeStyleSetting
public boolean usesCommonKeepLineBreaks() {
return false;
}
/**
* Checks if formatter is allowed to enforce a leading space in the
* line comment. Formatter will make a transformation like:
* <pre>//comment</pre> => <pre>// comment</pre>
* in case of {@link CommenterOption#LINE_COMMENT_ADD_SPACE_ON_REFORMAT}
* is enabled.
* <br/>
* <br/>
* This method will be called before transformation to ensure if transformation is possible
* to avoid breaking of some compiler directives. For example, Go compiler accepts directives
* in the code starts from {@code //go:...} and the space between comment prefix and {@code go}
* keyword is not allowed.
* <br/>
* <br/>
* The default implementation checks whether comment is not empty and starts from
* alphanumeric character. The typical implementation should add its own guard conditions
* first and then return the super-call.
*
* @param commentContents Text of the comment <b>without</b> a comment prefix
* @return {@code true} if and only if the transformation is allowed
*/
public boolean canInsertSpaceInLineComment(@NotNull String commentContents) {
if (commentContents.isBlank()) return false;
if (!isLetterOrDigit(commentContents.charAt(0))) return false;
return true;
}
}

View File

@@ -1,81 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.formatting
import com.intellij.lang.Commenter
import com.intellij.lang.LanguageCommenters
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider
import com.intellij.psi.impl.source.codeStyle.PostFormatProcessor
class LineCommentAddSpacePostFormatProcessor : PostFormatProcessor {
override fun processElement(source: PsiElement, settings: CodeStyleSettings) = source
.also { processText(it.containingFile, it.textRange, settings) }
override fun processText(source: PsiFile, rangeToReformat: TextRange, settings: CodeStyleSettings): TextRange {
val language = source.language
if (!settings.getCommonSettings(language).LINE_COMMENT_ADD_SPACE_ON_REFORMAT) {
return rangeToReformat // Option is disabled
}
val commenter = LanguageCommenters.INSTANCE.forLanguage(language) ?: return rangeToReformat
val languageCodeStyleSettingsProvider = LanguageCodeStyleSettingsProvider.forLanguage(language) ?: return rangeToReformat
val commentFinder = SingleLineCommentFinder(rangeToReformat, languageCodeStyleSettingsProvider, commenter)
source.accept(commentFinder)
val commentOffsets = commentFinder.commentOffsets
.filter { rangeToReformat.contains(it) }
.sorted()
.takeIf { it.isNotEmpty() }
?: return rangeToReformat // Nothing useful found
val documentManager = PsiDocumentManager.getInstance(source.project)
val document = documentManager.getDocument(source) ?: return rangeToReformat // Failed to get document
// Going backwards to protect earlier offsets from modifications in latter ones
commentOffsets.asReversed().forEach { document.insertString(it, " ") }
documentManager.commitDocument(document)
return rangeToReformat.grown(commentOffsets.size)
}
}
internal class SingleLineCommentFinder(val rangeToReformat: TextRange,
val languageCodeStyleSettingsProvider: LanguageCodeStyleSettingsProvider,
commenter: Commenter) : PsiRecursiveElementVisitor() {
val lineCommentPrefixes = commenter.lineCommentPrefixes.map { it.trim() }
val commentOffsets = arrayListOf<Int>()
override fun visitElement(element: PsiElement) {
if (element.textRange.intersects(rangeToReformat)) {
super.visitElement(element)
}
}
override fun visitComment(comment: PsiComment) {
val commentText = comment.text
val commentPrefixLength = lineCommentPrefixes
.find { commentText.startsWith(it) } // Find the line comment prefix
?.length // Not found -> not a line comment
?.takeUnless { commentText.length == it } // Empty comment, no need to add a trailing space
?: return
val commentContents = commentText.substring(commentPrefixLength)
if (!languageCodeStyleSettingsProvider.canInsertSpaceInLineComment(commentContents)) {
return
}
commentOffsets.add(comment.textRange.startOffset + commentPrefixLength)
}
}

View File

@@ -601,7 +601,6 @@ code.style.other.file.types=Other File Types
code.style.other.label=Text files and unsupported file types\:
checkbox.line.comment.add.space=Add a space at line comment start
checkbox.line.comment.add.space.on.reformat=Enforce on reformat
checkbox.block.comment.add.space=Add a space at block comment start
rainbow.option.panel.display.name=Semantic highlighting

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.application.options.codeStyle.CommenterForm">
<grid id="27dc6" binding="myCommenterPanel" layout-manager="GridLayoutManager" row-count="6" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myCommenterPanel" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
@@ -18,7 +18,7 @@
</component>
<vspacer id="c2a8d">
<constraints>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="98a1c" class="com.intellij.ui.components.JBCheckBox" binding="myLineCommentAddSpaceCb">
@@ -29,17 +29,9 @@
<text resource-bundle="messages/ApplicationBundle" key="checkbox.line.comment.add.space"/>
</properties>
</component>
<component id="dedd8" class="com.intellij.ui.components.JBCheckBox" binding="myLineCommentAddSpaceOnReformatCb">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="4" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ApplicationBundle" key="checkbox.line.comment.add.space.on.reformat"/>
</properties>
</component>
<component id="35ede" class="com.intellij.ui.components.JBCheckBox" binding="myBlockCommentAtFirstJBCheckBox" default-binding="true">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ApplicationBundle" key="checkbox.block.comment.at.first.column"/>
@@ -47,7 +39,7 @@
</component>
<component id="9d907" class="com.intellij.ui.components.JBCheckBox" binding="myBlockCommentAddSpaceCb">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ApplicationBundle" key="checkbox.block.comment.add.space"/>

View File

@@ -34,10 +34,8 @@ import javax.swing.*;
*/
public class CommenterForm implements CodeStyleSettingsCustomizable {
private JPanel myCommenterPanel;
private JBCheckBox myLineCommentAtFirstColumnCb;
private JBCheckBox myLineCommentAddSpaceCb;
private JBCheckBox myLineCommentAddSpaceOnReformatCb;
private JBCheckBox myBlockCommentAtFirstJBCheckBox;
private JBCheckBox myBlockCommentAddSpaceCb;
@@ -55,32 +53,18 @@ public class CommenterForm implements CodeStyleSettingsCustomizable {
myLineCommentAtFirstColumnCb.addActionListener(e -> {
if (myLineCommentAtFirstColumnCb.isSelected()) {
myLineCommentAddSpaceCb.setSelected(false);
myLineCommentAddSpaceOnReformatCb.setSelected(false);
}
myLineCommentAddSpaceCb.setEnabled(!myLineCommentAtFirstColumnCb.isSelected());
myLineCommentAddSpaceOnReformatCb.setEnabled(myLineCommentAddSpaceCb.isSelected());
});
myLineCommentAddSpaceCb.addActionListener(e -> {
boolean addSpace = myLineCommentAddSpaceCb.isSelected();
myLineCommentAddSpaceOnReformatCb.setEnabled(addSpace);
myLineCommentAddSpaceOnReformatCb.setSelected(addSpace);
});
myLineCommentAddSpaceOnReformatCb.setVisible(false);
customizeSettings();
}
public void reset(@NotNull CodeStyleSettings settings) {
CommonCodeStyleSettings langSettings = settings.getCommonSettings(myLanguage);
myLineCommentAtFirstColumnCb.setSelected(langSettings.LINE_COMMENT_AT_FIRST_COLUMN);
myBlockCommentAtFirstJBCheckBox.setSelected(langSettings.BLOCK_COMMENT_AT_FIRST_COLUMN);
myLineCommentAddSpaceCb.setSelected(langSettings.LINE_COMMENT_ADD_SPACE && !langSettings.LINE_COMMENT_AT_FIRST_COLUMN);
myLineCommentAddSpaceCb.setEnabled(!langSettings.LINE_COMMENT_AT_FIRST_COLUMN);
myLineCommentAddSpaceOnReformatCb.setEnabled(langSettings.LINE_COMMENT_ADD_SPACE);
myLineCommentAddSpaceOnReformatCb.setSelected(langSettings.LINE_COMMENT_ADD_SPACE_ON_REFORMAT);
myLineCommentAddSpaceCb.setEnabled(!langSettings .LINE_COMMENT_AT_FIRST_COLUMN);
myBlockCommentAddSpaceCb.setSelected(langSettings.BLOCK_COMMENT_ADD_SPACE);
}
@@ -90,7 +74,6 @@ public class CommenterForm implements CodeStyleSettingsCustomizable {
langSettings.LINE_COMMENT_AT_FIRST_COLUMN = myLineCommentAtFirstColumnCb.isSelected();
langSettings.BLOCK_COMMENT_AT_FIRST_COLUMN = myBlockCommentAtFirstJBCheckBox.isSelected();
langSettings.LINE_COMMENT_ADD_SPACE = myLineCommentAddSpaceCb.isSelected();
langSettings.LINE_COMMENT_ADD_SPACE_ON_REFORMAT = myLineCommentAddSpaceOnReformatCb.isSelected();
langSettings.BLOCK_COMMENT_ADD_SPACE = myBlockCommentAddSpaceCb.isSelected();
}
@@ -99,9 +82,7 @@ public class CommenterForm implements CodeStyleSettingsCustomizable {
return myLineCommentAtFirstColumnCb.isSelected() != langSettings.LINE_COMMENT_AT_FIRST_COLUMN
|| myBlockCommentAtFirstJBCheckBox.isSelected() != langSettings.BLOCK_COMMENT_AT_FIRST_COLUMN
|| myLineCommentAddSpaceCb.isSelected() != langSettings.LINE_COMMENT_ADD_SPACE
|| myBlockCommentAddSpaceCb.isSelected() != langSettings.BLOCK_COMMENT_ADD_SPACE
|| myLineCommentAddSpaceOnReformatCb.isSelected() != langSettings.LINE_COMMENT_ADD_SPACE_ON_REFORMAT
;
|| myBlockCommentAddSpaceCb.isSelected() != langSettings.BLOCK_COMMENT_ADD_SPACE;
}
public JPanel getCommenterPanel() {
@@ -119,9 +100,6 @@ public class CommenterForm implements CodeStyleSettingsCustomizable {
if (CommenterOption.LINE_COMMENT_ADD_SPACE.name().equals(optionName)) {
myLineCommentAddSpaceCb.setVisible(true);
}
if (CommenterOption.LINE_COMMENT_ADD_SPACE_ON_REFORMAT.name().equals(optionName)) {
myLineCommentAddSpaceOnReformatCb.setVisible(true);
}
else if (CommenterOption.LINE_COMMENT_AT_FIRST_COLUMN.name().equals(optionName)) {
myLineCommentAtFirstColumnCb.setVisible(true);
}
@@ -139,7 +117,6 @@ public class CommenterForm implements CodeStyleSettingsCustomizable {
myLineCommentAddSpaceCb.setVisible(isVisible);
myBlockCommentAtFirstJBCheckBox.setVisible(isVisible);
myBlockCommentAddSpaceCb.setVisible(isVisible);
myLineCommentAddSpaceOnReformatCb.setVisible(isVisible);
}
private void customizeSettings() {
@@ -151,7 +128,6 @@ public class CommenterForm implements CodeStyleSettingsCustomizable {
myCommenterPanel.setVisible(
myLineCommentAtFirstColumnCb.isVisible()
|| myLineCommentAddSpaceCb.isVisible()
|| myLineCommentAddSpaceOnReformatCb.isVisible()
|| myBlockCommentAtFirstJBCheckBox.isVisible()
|| myBlockCommentAddSpaceCb.isVisible()
);

View File

@@ -1,67 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.formatting.commandLine
import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import java.io.File
import java.nio.charset.Charset
private val LOG = Logger.getInstance(CodeStyleProcessorBuilder::class.java)
class CodeStyleProcessorBuilder(val messageOutput: MessageOutput) {
var isDryRun = false
var isRecursive = false
var codeStyleSettings = CodeStyleSettingsManager.getInstance().createSettings()
var fileMasks = emptyList<Regex>()
val entries = arrayListOf<File>()
var charset: Charset? = null
fun dryRun() = this.also { isDryRun = true }
fun recursive() = this.also { isRecursive = true }
fun withCodeStyleSettings(settings: CodeStyleSettings) = this.also { codeStyleSettings = settings }
fun withFileMasks(masks: String) = this.also {
fileMasks = masks
.split(",")
.asSequence()
.map { it.trim() }
.filter { it.isNotBlank() }
.map { mask ->
mask
.replace(".", "\\.")
.replace("*", ".*")
.replace("?", ".")
.replace("+", "\\+")
}
.map { pattern -> Regex(pattern) }
.toList()
}
fun withEntry(entryPath: String) = this.also { entries.add(File(entryPath)) }
fun withCharset(charset: Charset) = this.also { this.charset = charset }
private fun FileSetCodeStyleProcessor.configure() = apply {
fileMasks.forEach { mask ->
LOG.info("File mask regexp: ${mask.pattern}")
addFileMask(mask)
}
entries.forEach { file ->
addEntry(file)
}
}
private fun buildFormatter() =
FileSetFormatter(codeStyleSettings, messageOutput, isRecursive, charset).configure()
private fun buildFormatValidator() =
FileSetFormatValidator(codeStyleSettings, messageOutput, isRecursive, charset).configure()
fun build(): FileSetCodeStyleProcessor =
if (isDryRun) buildFormatValidator() else buildFormatter()
}

View File

@@ -1,230 +0,0 @@
// 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.formatting.commandLine
import com.intellij.application.options.CodeStyle
import com.intellij.formatting.service.CoreFormattingService
import com.intellij.formatting.service.FormattingServiceUtil
import com.intellij.ide.impl.OpenProjectTask.Companion.newProject
import com.intellij.lang.LanguageFormatting
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Document
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.impl.NonProjectFileWritingAccessProvider
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.util.LocalTimeCounter
import com.intellij.util.PlatformUtils
import org.jetbrains.jps.model.serialization.PathMacroUtil
import java.io.Closeable
import java.nio.charset.Charset
import java.nio.file.Files
import java.util.*
private val LOG = Logger.getInstance(FileSetCodeStyleProcessor::class.java)
private const val RESULT_MESSAGE_OK = "OK"
private const val RESULT_MESSAGE_FAILED = "Failed"
private const val RESULT_MESSAGE_NOT_SUPPORTED = "Skipped, not supported."
private const val RESULT_MESSAGE_REJECTED_BY_FORMATTER = "Skipped, rejected by formatter."
private const val RESULT_MESSAGE_BINARY_FILE = "Skipped, binary file."
private const val RESULT_MESSAGE_DRY_OK = "Formatted well"
private const val RESULT_MESSAGE_DRY_FAIL = "Needs reformatting"
class FileSetFormatter(
codeStyleSettings: CodeStyleSettings,
messageOutput: MessageOutput,
isRecursive: Boolean,
charset: Charset? = null
) : FileSetCodeStyleProcessor(codeStyleSettings, messageOutput, isRecursive, charset) {
override val operationContinuous = "Formatting"
override val operationPerfect = "formatted"
override fun processFileInternal(virtualFile: VirtualFile): String {
val document = FileDocumentManager.getInstance().getDocument(virtualFile)
if (document == null) {
LOG.warn("No document available for " + virtualFile.path)
return RESULT_MESSAGE_FAILED
}
try {
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document)
NonProjectFileWritingAccessProvider.allowWriting(listOf(virtualFile))
if (psiFile == null) {
LOG.warn("Unable to get a PSI file for " + virtualFile.path)
return RESULT_MESSAGE_FAILED
}
if (!psiFile.isFormattingSupported()) {
return RESULT_MESSAGE_NOT_SUPPORTED
}
try {
reformatFile(psiFile, document)
}
catch (pce: ProcessCanceledException) {
val cause = pce.cause?.message ?: pce.message ?: ""
LOG.warn("${virtualFile.canonicalPath}: $RESULT_MESSAGE_REJECTED_BY_FORMATTER $cause")
return RESULT_MESSAGE_REJECTED_BY_FORMATTER
}
FileDocumentManager.getInstance().saveDocument(document)
}
finally {
closeOpenFiles()
}
statistics.fileProcessed(true)
return RESULT_MESSAGE_OK
}
private fun reformatFile(file: PsiFile, document: Document) {
WriteCommandAction.runWriteCommandAction(project) {
val codeStyleManager = CodeStyleManager.getInstance(project)
codeStyleManager.reformatText(file, 0, file.textLength)
PsiDocumentManager.getInstance(project).commitDocument(document)
}
}
private fun closeOpenFiles() {
val editorManager = FileEditorManager.getInstance(project)
val openFiles = editorManager.openFiles
for (openFile in openFiles) {
editorManager.closeFile(openFile)
}
}
}
class FileSetFormatValidator(
codeStyleSettings: CodeStyleSettings,
messageOutput: MessageOutput,
isRecursive: Boolean,
charset: Charset? = null
) : FileSetCodeStyleProcessor(codeStyleSettings, messageOutput, isRecursive, charset) {
override val operationContinuous = "Checking"
override val operationPerfect = "checked"
override fun printReport() {
super.printReport()
messageOutput.info("${succeeded} file(s) are well formed.\n")
}
override fun isResultSuccessful() = succeeded == processed
override fun processFileInternal(virtualFile: VirtualFile): String {
val document = FileDocumentManager.getInstance().getDocument(virtualFile)
if (document == null) {
LOG.warn("No document available for " + virtualFile.path)
return RESULT_MESSAGE_FAILED
}
val originalContent = document.text
val psiCopy = createPsiCopy(virtualFile, originalContent)
CodeStyleManager
.getInstance(project)
.reformatText(psiCopy, 0, psiCopy.textLength)
val reformattedContent = psiCopy.text
return if (originalContent == reformattedContent) {
statistics.fileProcessed(true)
RESULT_MESSAGE_DRY_OK
}
else {
statistics.fileProcessed(false)
RESULT_MESSAGE_DRY_FAIL
}
}
private fun createPsiCopy(originalFile: VirtualFile, originalContent: String) = PsiFileFactory.getInstance(project).createFileFromText(
"a." + originalFile.fileType.defaultExtension,
originalFile.fileType,
originalContent,
LocalTimeCounter.currentTime(),
false
)
}
abstract class FileSetCodeStyleProcessor(
val codeStyleSettings: CodeStyleSettings,
messageOutput: MessageOutput,
isRecursive: Boolean,
charset: Charset? = null
) : FileSetProcessor(messageOutput, isRecursive, charset), Closeable {
private val projectUID = UUID.randomUUID().toString()
protected val project = createProject(projectUID, codeStyleSettings)
abstract val operationContinuous: String
abstract val operationPerfect: String
abstract fun processFileInternal(virtualFile: VirtualFile): String
open fun printReport() {
messageOutput.info("\n")
messageOutput.info("${total} file(s) scanned.\n")
messageOutput.info("${processed} file(s) $operationPerfect.\n")
}
open fun isResultSuccessful() = true
override fun close() {
ProjectManagerEx.getInstanceEx().closeAndDispose(project)
}
override fun processVirtualFile(virtualFile: VirtualFile) {
messageOutput.info("$operationContinuous ${virtualFile.canonicalPath}...")
VfsUtil.markDirtyAndRefresh(false, false, false, virtualFile)
val resultMessage =
if (virtualFile.fileType.isBinary) {
RESULT_MESSAGE_BINARY_FILE
}
else {
processFileInternal(virtualFile)
}
messageOutput.info("$resultMessage\n")
}
}
private val PROJECT_DIR_PREFIX = PlatformUtils.getPlatformPrefix() + ".format."
private const val PROJECT_DIR_SUFFIX = ".tmp"
private fun createProjectDir(projectUID: String) = FileUtil
.createTempDirectory(PROJECT_DIR_PREFIX, projectUID + PROJECT_DIR_SUFFIX)
.toPath()
.resolve(PathMacroUtil.DIRECTORY_STORE_NAME)
.also { Files.createDirectories(it) }
private fun createProject(projectUID: String, codeStyleSettings: CodeStyleSettings) =
ProjectManagerEx.getInstanceEx()
.openProject(createProjectDir(projectUID), newProject())
?.also { CodeStyle.setMainProjectSettings(it, codeStyleSettings) }
?: throw RuntimeException("Failed to create temporary project $projectUID")
private fun PsiFile.isFormattingSupported(): Boolean {
val formattingService = FormattingServiceUtil.findService(this, true, true)
return (formattingService !is CoreFormattingService)
|| (LanguageFormatting.INSTANCE.forContext(this) != null)
}

View File

@@ -0,0 +1,158 @@
// 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.formatting.commandLine;
import com.intellij.application.options.CodeStyle;
import com.intellij.formatting.service.CoreFormattingService;
import com.intellij.formatting.service.FormattingService;
import com.intellij.formatting.service.FormattingServiceUtil;
import com.intellij.ide.impl.OpenProjectTask;
import com.intellij.lang.LanguageFormatting;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.impl.NonProjectFileWritingAccessProvider;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.util.PlatformUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.serialization.PathMacroUtil;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.UUID;
public final class FileSetFormatter extends FileSetProcessor {
private static final Logger LOG = Logger.getInstance(FileSetFormatter.class);
private final static String PROJECT_DIR_PREFIX = PlatformUtils.getPlatformPrefix() + ".format.";
private final static String PROJECT_DIR_SUFFIX = ".tmp";
private final static String RESULT_MESSAGE_OK = "OK";
private final static String RESULT_MESSAGE_FAILED = "Failed";
private final static String RESULT_MESSAGE_NOT_SUPPORTED = "Skipped, not supported.";
private final static String RESULT_MESSAGE_REJECTED_BY_FORMATTER = "Skipped, rejected by formatter.";
private final static String RESULT_MESSAGE_BINARY_FILE = "Skipped, binary file.";
private final @NotNull String myProjectUID;
private @Nullable Project myProject;
private final MessageOutput myMessageOutput;
private @NotNull CodeStyleSettings mySettings;
public FileSetFormatter(@NotNull MessageOutput messageOutput) {
myMessageOutput = messageOutput;
mySettings = CodeStyleSettingsManager.getInstance().createSettings();
myProjectUID = UUID.randomUUID().toString();
}
public void setCodeStyleSettings(@NotNull CodeStyleSettings settings) {
mySettings = settings;
}
private void createProject() throws IOException {
myProject = ProjectManagerEx.getInstanceEx().openProject(createProjectDir(), OpenProjectTask.newProject());
if (myProject != null) {
CodeStyle.setMainProjectSettings(myProject, mySettings);
}
}
private @NotNull Path createProjectDir() throws IOException {
Path projectDir = FileUtil.createTempDirectory(PROJECT_DIR_PREFIX, myProjectUID + PROJECT_DIR_SUFFIX).toPath().resolve(PathMacroUtil.DIRECTORY_STORE_NAME);
Files.createDirectories(projectDir);
return projectDir;
}
private void closeProject() {
if (myProject != null) {
ProjectManagerEx.getInstanceEx().closeAndDispose(myProject);
}
}
@Override
public void processFiles() throws IOException {
createProject();
if (myProject != null) {
super.processFiles();
closeProject();
}
}
@Override
protected boolean processFile(@NotNull VirtualFile virtualFile) {
String resultMessage = RESULT_MESSAGE_OK;
assert myProject != null;
VfsUtil.markDirtyAndRefresh(false, false, false, virtualFile);
myMessageOutput.info("Formatting " + virtualFile.getCanonicalPath() + "...");
if (!virtualFile.getFileType().isBinary()) {
Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
if (document != null) {
PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
NonProjectFileWritingAccessProvider.allowWriting(Collections.singletonList(virtualFile));
if (psiFile != null) {
if (isFormattingSupported(psiFile)) {
try {
reformatFile(myProject, psiFile, document);
}
catch (ProcessCanceledException pce) {
final String cause = StringUtil.notNullize(pce.getCause() != null ? pce.getCause().getMessage() : pce.getMessage());
LOG.warn(virtualFile.getCanonicalPath() + ": " + RESULT_MESSAGE_REJECTED_BY_FORMATTER + " " + cause);
resultMessage = RESULT_MESSAGE_REJECTED_BY_FORMATTER;
}
FileDocumentManager.getInstance().saveDocument(document);
}
else {
resultMessage = RESULT_MESSAGE_NOT_SUPPORTED;
}
}
else {
LOG.warn("Unable to get a PSI file for " + virtualFile.getPath());
resultMessage = RESULT_MESSAGE_FAILED;
}
}
else {
LOG.warn("No document available for " + virtualFile.getPath());
resultMessage = RESULT_MESSAGE_FAILED;
}
FileEditorManager editorManager = FileEditorManager.getInstance(myProject);
VirtualFile[] openFiles = editorManager.getOpenFiles();
for (VirtualFile openFile : openFiles) {
editorManager.closeFile(openFile);
}
}
else {
resultMessage = RESULT_MESSAGE_BINARY_FILE;
}
myMessageOutput.info(resultMessage + "\n");
return RESULT_MESSAGE_OK.equals(resultMessage);
}
private static void reformatFile(@NotNull Project project, @NotNull final PsiFile file, @NotNull Document document) {
WriteCommandAction.runWriteCommandAction(project, () -> {
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
codeStyleManager.reformatText(file, 0, file.getTextLength());
PsiDocumentManager.getInstance(project).commitDocument(document);
});
}
private static boolean isFormattingSupported(@NotNull PsiFile file) {
FormattingService formattingService = FormattingServiceUtil.findService(file, true, true);
if (formattingService instanceof CoreFormattingService) {
return LanguageFormatting.INSTANCE.forContext(file) != null;
}
return true;
}
}

View File

@@ -1,30 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.formatting.commandLine
import java.util.concurrent.atomic.AtomicInteger
class FileSetProcessingStatistics {
private val total = AtomicInteger()
private val processed = AtomicInteger()
private val valid = AtomicInteger()
fun fileTraversed() {
total.incrementAndGet()
}
fun fileProcessed(valid: Boolean) {
processed.incrementAndGet()
if (valid) {
this.valid.incrementAndGet()
}
}
fun getTotal(): Int = total.get()
fun getProcessed(): Int = processed.get()
fun getValid(): Int = valid.get()
}

View File

@@ -0,0 +1,96 @@
// Copyright 2000-2019 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.formatting.commandLine;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
public abstract class FileSetProcessor {
private static final Logger LOG = Logger.getInstance(FileSetProcessor.class);
private final Set<File> myTopEntries = new HashSet<>();
private final Set<String> myFileMasks = new HashSet<>();
private int myProcessedFiles;
private boolean isRecursive;
public void processFiles() throws IOException {
for (File topEntry : myTopEntries) {
processEntry(topEntry);
}
}
public void setRecursive() {
isRecursive = true;
}
public void addFileMask(@NotNull String fileMask) {
String fileMaskRegexp = fileMaskToRegexp(fileMask);
LOG.info("File mask regexp: " + fileMaskRegexp);
myFileMasks.add(fileMaskRegexp);
}
private static String fileMaskToRegexp(@NotNull String fileMask) {
return
fileMask
.replace(".", "\\.")
.replace("*", ".*")
.replace("?", ".")
.replace("+", "\\+");
}
public void addEntry(@NotNull String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
throw new IOException("File " + filePath + " not found.");
}
myTopEntries.add(file);
}
private void processEntry(@NotNull File entry) throws IOException {
if (entry.exists()) {
if (entry.isDirectory()) {
LOG.info("Scanning directory " + entry.getPath());
File[] subEntries = entry.listFiles();
if (subEntries != null) {
for (File subEntry : subEntries) {
if (!subEntry.isDirectory() || isRecursive) {
processEntry(subEntry);
}
}
}
}
else {
if (matchesFileMask(entry.getName())) {
VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(entry);
if (virtualFile == null) {
throw new IOException("Can not find " + entry.getPath());
}
LOG.info("Processing " + virtualFile.getPath());
if (processFile(virtualFile)) myProcessedFiles++;
}
}
}
}
private boolean matchesFileMask(@NotNull String name) {
if (myFileMasks.isEmpty()) return true;
for (String fileMask : myFileMasks) {
if (name.matches(fileMask)) {
return true;
}
}
return false;
}
protected abstract boolean processFile(@NotNull VirtualFile virtualFile);
public int getProcessedFiles() {
return myProcessedFiles;
}
}

View File

@@ -1,75 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.formatting.commandLine
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import java.io.File
import java.io.IOException
import java.nio.charset.Charset
private var LOG = Logger.getInstance(FileSetProcessor::class.java)
abstract class FileSetProcessor(
val messageOutput: MessageOutput,
val isRecursive: Boolean,
val charset: Charset? = null
) {
private val topEntries = arrayListOf<File>()
private val fileMasks = arrayListOf<Regex>()
protected val statistics = FileSetProcessingStatistics()
val total: Int
get() = statistics.getTotal()
val processed: Int
get() = statistics.getProcessed()
val succeeded: Int
get() = statistics.getValid()
fun addEntry(filePath: String) = addEntry(File(filePath))
fun addEntry(file: File) =
file
.takeIf { it.exists() }
?.let { topEntries.add(it) }
?: throw IOException("File $file not found.")
fun addFileMask(mask: Regex) = fileMasks.add(mask)
private fun File.matchesFileMask() =
fileMasks.isEmpty() || fileMasks.any { mask -> mask.matches(name) }
private fun File.toVirtualFile() =
LocalFileSystem.getInstance().refreshAndFindFileByIoFile(this) ?: throw IOException("Can not find $path")
fun processFiles() = topEntries.forEach { entry ->
entry
.walkTopDown()
.maxDepth(if (isRecursive) Int.MAX_VALUE else 1)
.onEnter {
LOG.info("Scanning directory ${it.path}")
true
}
.filter { it.isFile }
.filter { it.matchesFileMask() }
.map { ioFile -> ioFile.toVirtualFile() }
.onEach { vFile -> charset?.let { vFile.charset = it } }
.forEach { vFile ->
LOG.info("Processing ${vFile.path}")
statistics.fileTraversed()
processVirtualFile(vFile)
}
}
abstract fun processVirtualFile(virtualFile: VirtualFile)
fun getFileMasks() = fileMasks.toList()
fun getEntries() = topEntries.toList()
}

View File

@@ -0,0 +1,141 @@
// Copyright 2000-2019 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.formatting.commandLine;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationStarter;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.SchemeImportException;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.impl.source.codeStyle.CodeStyleSettingsLoader;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
/**
* A launcher class for command-line formatter.
*/
public class FormatterStarter implements ApplicationStarter {
public static final String FORMAT_COMMAND_NAME = "format";
private static final Logger LOG = Logger.getInstance(FormatterStarter.class);
@Override
public String getCommandName() {
return FORMAT_COMMAND_NAME;
}
@Override
public void main(String @NotNull [] args) {
@SuppressWarnings("UseOfSystemOutOrSystemErr")
MessageOutput messageOutput = new MessageOutput(
new PrintWriter(System.out),
new PrintWriter(System.err));
messageOutput.info(getAppInfo() + " Formatter\n");
FileSetFormatter fileSetFormatter = new FileSetFormatter(messageOutput);
logArgs(args);
if (args.length < 2) {
showUsageInfo(messageOutput);
}
for (int i = 1; i < args.length; i++) {
if (args[i].startsWith("-")) {
if (checkOption(args[i], "-h", "-help")) {
showUsageInfo(messageOutput);
}
if (checkOption(args[i], "-s", "-settings")) {
//noinspection AssignmentToForLoopParameter
i++;
if (i >= args.length) {
fatalError(messageOutput, "Missing settings file path.");
}
try {
CodeStyleSettings settings = readSettings(args[i]);
fileSetFormatter.setCodeStyleSettings(settings);
}
catch (SchemeImportException e) {
fatalError(messageOutput, e.getLocalizedMessage() + "\n");
}
}
else if (checkOption(args[i], "-r", "-R")) {
fileSetFormatter.setRecursive();
}
else if (checkOption(args[i], "-m", "-mask")) {
//noinspection AssignmentToForLoopParameter
i++;
if (i >= args.length) {
fatalError(messageOutput, "Missing file mask(s).");
}
for (String mask : args[i].split(",")) {
if (!mask.isEmpty()) {
fileSetFormatter.addFileMask(mask);
}
}
}
else {
fatalError(messageOutput, "Unknown option " + args[i]);
}
}
else {
try {
fileSetFormatter.addEntry(args[i]);
}
catch (IOException e) {
fatalError(messageOutput, e.getLocalizedMessage());
}
}
}
try {
fileSetFormatter.processFiles();
messageOutput.info("\n" + fileSetFormatter.getProcessedFiles() + " file(s) formatted.\n");
}
catch (IOException e) {
fatalError(messageOutput, e.getLocalizedMessage());
}
((ApplicationEx)ApplicationManager.getApplication()).exit(true, true);
}
private static void fatalError(@NotNull MessageOutput messageOutput, @NotNull String message) {
messageOutput.error("ERROR: " + message + "\n");
System.exit(1);
}
private static CodeStyleSettings readSettings(@NotNull String settingsPath) throws SchemeImportException {
VirtualFile vFile = VfsUtil.findFileByIoFile(new File(settingsPath), true);
CodeStyleSettingsLoader loader = new CodeStyleSettingsLoader();
if (vFile == null) {
throw new SchemeImportException("Cannot find file " + settingsPath);
}
return loader.loadSettings(vFile);
}
private static boolean checkOption(@NotNull String arg, String... variants) {
return ArrayUtil.contains(arg, variants);
}
private static String getAppInfo() {
ApplicationInfoImpl appInfo = (ApplicationInfoImpl)ApplicationInfoEx.getInstanceEx();
return String.format("%s, build %s", appInfo.getFullApplicationName(), appInfo.getBuild().asString());
}
private static void showUsageInfo(@NotNull MessageOutput messageOutput) {
messageOutput.info("Usage: format [-h] [-r|-R] [-s|-settings settingsPath] path1 path2...\n");
messageOutput.info(" -h|-help Show a help message and exit.\n");
messageOutput.info(" -s|-settings A path to Intellij IDEA code style settings .xml file.\n");
messageOutput.info(" -r|-R Scan directories recursively.\n");
messageOutput.info(" -m|-mask A comma-separated list of file masks.\n");
messageOutput.info(" path<n> A path to a file or a directory.\n");
System.exit(0);
}
private static void logArgs(String @NotNull [] args) {
LOG.info("Arguments: " + String.join(",", args));
}
}

View File

@@ -1,155 +0,0 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.formatting.commandLine
import com.intellij.formatting.commandLine.CodeStyleProcessorBuildException.ArgumentsException
import com.intellij.formatting.commandLine.CodeStyleProcessorBuildException.ShowUsageException
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ApplicationStarter
import com.intellij.openapi.application.ex.ApplicationEx
import com.intellij.openapi.application.ex.ApplicationInfoEx
import com.intellij.openapi.application.impl.ApplicationInfoImpl
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.impl.source.codeStyle.CodeStyleSettingsLoader
import java.io.File
import java.io.IOException
import java.nio.charset.Charset
import kotlin.system.exitProcess
const val FORMAT_COMMAND_NAME = "format"
private val LOG = Logger.getInstance(FormatterStarter::class.java)
/**
* A launcher class for command-line formatter.
*/
class FormatterStarter : ApplicationStarter {
val messageOutput = StdIoMessageOutput
override fun getCommandName() = FORMAT_COMMAND_NAME
override fun main(args: Array<String>) {
messageOutput.info("$appInfo Formatter\n")
LOG.info(args.joinToString(",", prefix = "Attributes: "))
val processor = try {
createFormatter(args, messageOutput)
}
catch (e: ShowUsageException) {
messageOutput.info(usageInfo)
exitProcess(0)
}
catch (e: ArgumentsException) {
messageOutput.error("ERROR: ${e.message}\n")
exitProcess(1)
}
try {
processor.use {
it.processFiles()
it.printReport()
if (!it.isResultSuccessful()) {
exitProcess(1)
}
}
}
catch (e: IOException) {
messageOutput.error("ERROR: ${e.localizedMessage}\n")
exitProcess(1)
}
(ApplicationManager.getApplication() as ApplicationEx).exit(true, true)
}
}
fun createFormatter(args: Array<String>, messageOutput: MessageOutput = StdIoMessageOutput) =
CodeStyleProcessorBuilder(messageOutput)
.apply {
if (args.size < 2) throw ShowUsageException()
val skipFlag = Skipper()
args
.asSequence().drop(1) // Skip first argument "format" -- routing from the top level
// try to treat every two arguments as an argument and its param.
// If param is not needed we'll take it as an arg on the next iteration,
// otherwise we'll skip using a boolean flag `skipNext`
.windowed(size = 2, step = 1, partialWindows = true) { Pair(it[0], it.getOrNull(1)) }
.forEach { (arg, param) ->
skipFlag.check { return@forEach }
when (arg) {
"-h", "-help" -> throw ShowUsageException()
"-r", "-R" -> recursive()
"-d", "-dry" -> dryRun()
"-s", "-settings" -> {
param ?: throw ArgumentsException("Missing settings file path.")
withCodeStyleSettings(readSettings(param) ?: throw ArgumentsException("Cannot find file $param"))
skipFlag.skip()
}
"-m", "-mask" -> {
withFileMasks(param ?: throw ArgumentsException("Missing file mask(s)."))
skipFlag.skip()
}
"-charset" -> {
param ?: throw ArgumentsException("Missing file mask(s).")
runCatching { Charset.forName(param) }
.onSuccess { withCharset(it) }
.onFailure { messageOutput.error("Ignoring charset setting: ${it.message}") }
skipFlag.skip()
}
else -> {
if (arg.startsWith("-")) throw ArgumentsException("Unknown option $arg")
withEntry(arg)
}
}
}
}
.build()
private const val usageInfo = """
Usage: format [-h] [-r|-R] [-d|-dry] [-s|-settings settingsPath] [-charset charsetName] path1 path2...
-h|-help Show a help message and exit.
-s|-settings A path to Intellij IDEA code style settings .xml file.
-r|-R Scan directories recursively.
-d|-dry Perform a dry run: no file modifications, only exit status.
-m|-mask A comma-separated list of file masks.
-charset Force charset to use when reading and writing files.
path<n> A path to a file or a directory.
"""
private fun readSettings(settingsPath: String): CodeStyleSettings? =
VfsUtil.findFileByIoFile(File(settingsPath), true)
?.let { CodeStyleSettingsLoader().loadSettings(it) }
private val appInfo: String =
(ApplicationInfoEx.getInstanceEx() as ApplicationInfoImpl)
.let { "${it.fullApplicationName}, build ${it.build.asString()}" }
sealed class CodeStyleProcessorBuildException : RuntimeException {
constructor() : super()
constructor(message: String) : super(message)
class ShowUsageException : CodeStyleProcessorBuildException()
class ArgumentsException(message: String) : CodeStyleProcessorBuildException(message)
}
private class Skipper(private var skip: Boolean = false) {
fun skip() {
skip = true
}
inline fun check(action: () -> Unit) {
if (skip) {
skip = false
action()
}
}
}

View File

@@ -13,22 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.formatting.commandLine
package com.intellij.formatting.commandLine;
import java.io.PrintWriter
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter;
open class MessageOutput(val infoOut: PrintWriter, val errorOut: PrintWriter) {
public class MessageOutput {
fun info(message: String) = infoOut.printAndFlush(message)
private final PrintWriter myErrorOutput;
private final PrintWriter myInfoOutput;
fun error(message: String) = errorOut.printAndFlush(message)
private fun PrintWriter.printAndFlush(message: String) {
print(message)
flush()
public MessageOutput(PrintWriter infoOutput, PrintWriter errorOuput) {
myInfoOutput = infoOutput;
myErrorOutput = errorOuput;
}
}
public void info(@NotNull String message) {
myInfoOutput.print(message);
myInfoOutput.flush();
}
object StdIoMessageOutput : MessageOutput(PrintWriter(System.out), PrintWriter(System.err))
public void error(@NotNull String message) {
myErrorOutput.print(message);
myErrorOutput.flush();
}
}

View File

@@ -246,7 +246,6 @@
<formattingService implementation="com.intellij.formatting.service.ExternalFormatProcessorAdapter" order="first"/>
<formattingService implementation="com.intellij.formatting.service.CoreFormattingService" order="last"/>
<postFormatProcessor implementation="com.intellij.formatting.LineCommentAddSpacePostFormatProcessor"/>
<applicationService serviceInterface="com.intellij.codeInsight.editorActions.TabOutScopesTracker"
serviceImplementation="com.intellij.codeInsight.editorActions.TabOutScopesTrackerImpl"/>