mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
Temporary revert IDEA-181641 and IDEA-58488 to fix tests
GitOrigin-RevId: 22963f4a05714895de8cae5dbda3529f21c5f3bc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cae9e70461
commit
6206bd18f2
@@ -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",
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.company;
|
||||
|
||||
class Löschen
|
||||
{
|
||||
// This class name will be broken unless encoding is set
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.company;
|
||||
class Löschen {
|
||||
// This class name will be broken unless encoding is set
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user