mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Command line formatter (IDEA-99267)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
package com.company;
|
||||
|
||||
public class Test {
|
||||
class Foo {
|
||||
void foo() {
|
||||
//other comment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.company.subdir;
|
||||
|
||||
public class Test2 {
|
||||
class Foo {
|
||||
void foo() {
|
||||
//other comment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.company;
|
||||
public class Test {class Foo {
|
||||
void foo() {
|
||||
//other comment
|
||||
}}}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.company.subdir;
|
||||
public class Test2 {class Foo {
|
||||
void foo() {
|
||||
//other comment
|
||||
}}}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.formatting.commandLine;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
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.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 {
|
||||
File sourceDir = createSourceDir("original");
|
||||
String fileSpec = sourceDir.getCanonicalPath() + File.separator + "**.java";
|
||||
MessageOutput messageOutput = new MessageOutput(new PrintWriter(System.out), new PrintWriter(System.err));
|
||||
FileSetFormatter formatter = new FileSetFormatter(fileSpec, messageOutput);
|
||||
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(resultEntry, file);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -133,6 +133,7 @@ public class Main {
|
||||
Comparing.strEqual(firstArg, "duplocate") ||
|
||||
Comparing.strEqual(firstArg, "traverseUI") ||
|
||||
Comparing.strEqual(firstArg, "buildAppcodeCache") ||
|
||||
Comparing.strEqual(firstArg, "format") ||
|
||||
(firstArg.length() < 20 && firstArg.endsWith("inspect"));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.formatting.commandLine;
|
||||
|
||||
import com.intellij.openapi.application.AccessToken;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
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.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.project.ex.ProjectManagerEx;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
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.codeStyle.CodeStyleManager;
|
||||
import com.intellij.util.PlatformUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
class FileSetFormatter extends FileSetProcessor {
|
||||
private final static String PROJECT_DIR_PREFIX = PlatformUtils.getPlatformPrefix() + ".format.";
|
||||
private final static String PROJECT_DIR_SUFFIX = ".tmp";
|
||||
|
||||
private final @NotNull String myProjectUID;
|
||||
private @Nullable Project myProject;
|
||||
private MessageOutput myMessageOutput;
|
||||
|
||||
FileSetFormatter(@NotNull String fileSpec, @NotNull MessageOutput messageOutput) {
|
||||
super(fileSpec);
|
||||
myMessageOutput = messageOutput;
|
||||
myProjectUID = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private File createProject() throws IOException {
|
||||
ProjectManagerEx projectManager = (ProjectManagerEx)ProjectManager.getInstance();
|
||||
File projectDir = createProjectDir();
|
||||
myProject = projectManager.createProject(myProjectUID, projectDir.getPath());
|
||||
if (myProject != null) {
|
||||
projectManager.openProject(myProject);
|
||||
return projectDir;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File createProjectDir() throws IOException {
|
||||
File tempDir = FileUtil.createTempDirectory(PROJECT_DIR_PREFIX, myProjectUID + PROJECT_DIR_SUFFIX);
|
||||
File projectDir = new File(tempDir.getPath() + File.separator + ".idea");
|
||||
if (projectDir.mkdirs()) {
|
||||
return projectDir;
|
||||
}
|
||||
throw new IOException("Cannot create a temporary project at " + projectDir);
|
||||
}
|
||||
|
||||
private void closeProject() {
|
||||
if (myProject != null) {
|
||||
ProjectManager.getInstance().closeProject(myProject);
|
||||
AccessToken writeToken = ApplicationManager.getApplication().acquireWriteActionLock(this.getClass());
|
||||
Disposer.dispose(myProject);
|
||||
writeToken.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processFiles() throws IOException {
|
||||
File projectDir = createProject();
|
||||
if (projectDir != null) {
|
||||
super.processFiles();
|
||||
closeProject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processFile(@NotNull VirtualFile virtualFile) {
|
||||
if (myProject != null) {
|
||||
VfsUtil.markDirtyAndRefresh(false, false, false, virtualFile);
|
||||
myMessageOutput.info("Formatting " + virtualFile.getCanonicalPath() + "...");
|
||||
Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
|
||||
if (document != null) {
|
||||
PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
|
||||
NonProjectFileWritingAccessProvider.allowWriting(virtualFile);
|
||||
if (psiFile != null) {
|
||||
reformatFile(myProject, psiFile, document);
|
||||
}
|
||||
FileDocumentManager.getInstance().saveDocument(document);
|
||||
}
|
||||
FileEditorManager editorManager = FileEditorManager.getInstance(myProject);
|
||||
VirtualFile[] openFiles = editorManager.getOpenFiles();
|
||||
for (VirtualFile openFile : openFiles) {
|
||||
editorManager.closeFile(openFile);
|
||||
}
|
||||
myMessageOutput.info("OK\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void reformatFile(@NotNull Project project, @NotNull PsiFile file, @NotNull Document document) {
|
||||
AccessToken writeToken = ApplicationManager.getApplication().acquireWriteActionLock(this.getClass());
|
||||
try {
|
||||
CommandProcessor.getInstance().executeCommand(
|
||||
myProject,
|
||||
() -> {
|
||||
CodeStyleManager.getInstance(project).reformatText(file, 0, file.getTextLength());
|
||||
PsiDocumentManager.getInstance(project).commitDocument(document);
|
||||
}, null, null);
|
||||
}
|
||||
finally {
|
||||
writeToken.finish();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.formatting.commandLine;
|
||||
|
||||
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;
|
||||
|
||||
public abstract class FileSetProcessor {
|
||||
private File myRoot;
|
||||
private String myPattern;
|
||||
|
||||
FileSetProcessor(@NotNull String fileSpec) {
|
||||
setRootAndPattern(fileSpec);
|
||||
}
|
||||
|
||||
public void processFiles() throws IOException {
|
||||
processEntry(myRoot);
|
||||
}
|
||||
|
||||
private void processEntry(@NotNull File entry) throws IOException {
|
||||
if (entry.exists()) {
|
||||
if (entry.isDirectory()) {
|
||||
File[] subEntries = entry.listFiles();
|
||||
if (subEntries != null) {
|
||||
for (File subEntry : subEntries) {
|
||||
processEntry(subEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (myPattern == null || entry.getCanonicalPath().matches(myPattern)) {
|
||||
VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(entry);
|
||||
if (virtualFile == null) {
|
||||
throw new IOException("Can not find " + entry.getPath());
|
||||
}
|
||||
processFile(virtualFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void processFile(@NotNull VirtualFile virtualFile);
|
||||
|
||||
private void setRootAndPattern(@NotNull String fileSpec) {
|
||||
String rootPath = fileSpec;
|
||||
int starPos = fileSpec.indexOf("*");
|
||||
int questionPos = fileSpec.indexOf("?");
|
||||
int wildcardPos = starPos >= 0 ? questionPos >= 0 ? Math.min(starPos, questionPos) : starPos : questionPos;
|
||||
if (wildcardPos >= 0) rootPath = rootPath.substring(0, wildcardPos);
|
||||
int lastSlash = Math.max(rootPath.lastIndexOf("/"), rootPath.lastIndexOf("\\"));
|
||||
rootPath = lastSlash >= 0 ? rootPath.substring(0, lastSlash) : "";
|
||||
myPattern = fileSpecToRegExp(fileSpec);
|
||||
myRoot = new File(rootPath);
|
||||
}
|
||||
|
||||
private static String fileSpecToRegExp(@NotNull String fileSpec) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
char[] fileSpecChars = fileSpec.toCharArray();
|
||||
for (int i = 0; i < fileSpecChars.length; i ++) {
|
||||
char c = fileSpecChars[i];
|
||||
switch (c) {
|
||||
case '.':
|
||||
result.append("\\.");
|
||||
break;
|
||||
case '*':
|
||||
char next = i + 1 < fileSpecChars.length ? fileSpecChars[i + 1] : 0;
|
||||
result.append(next == '*' ? ".*" : "[^\\\\]*");
|
||||
break;
|
||||
case '?':
|
||||
result.append(".");
|
||||
break;
|
||||
case '/':
|
||||
result.append("[\\\\/]");
|
||||
break;
|
||||
case '\\':
|
||||
result.append("[\\\\/]");
|
||||
break;
|
||||
default:
|
||||
result.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.formatting.commandLine;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ApplicationStarterEx;
|
||||
import com.intellij.openapi.application.ex.ApplicationEx;
|
||||
import com.intellij.openapi.application.ex.ApplicationInfoEx;
|
||||
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* A launcher class for command-line formatter.
|
||||
*/
|
||||
public class FormatterStarter extends ApplicationStarterEx {
|
||||
|
||||
public static final String FORMAT_COMMAND_NAME = "format";
|
||||
|
||||
@Override
|
||||
public boolean isHeadless() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return FORMAT_COMMAND_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void premain(String[] args) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void main(String[] args) {
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
MessageOutput messageOutput = new MessageOutput(
|
||||
new PrintWriter(System.out),
|
||||
new PrintWriter(System.err));
|
||||
messageOutput.info(getAppInfo() + " Formatter\n");
|
||||
for (int i = 1; i < args.length; i ++) {
|
||||
if (args[i].startsWith("-")) {
|
||||
if (checkOption(args[i], "-h", "--help")) {
|
||||
showUsageInfo(messageOutput);
|
||||
}
|
||||
else {
|
||||
messageOutput.error("Unknown option " + args[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
FileSetFormatter fileSetFormatter = new FileSetFormatter(args[i], messageOutput);
|
||||
try {
|
||||
fileSetFormatter.processFiles();
|
||||
}
|
||||
catch (IOException e) {
|
||||
messageOutput.error("FAILED: " + e.getLocalizedMessage() + "\n");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
((ApplicationEx)ApplicationManager.getApplication()).exit(true, true);
|
||||
}
|
||||
|
||||
private static boolean checkOption(@NotNull String arg, String... variants) {
|
||||
for (String variant: variants) {
|
||||
if (variant.equals(arg)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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 fileSpec...");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.formatting.commandLine;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
class MessageOutput {
|
||||
|
||||
private final PrintWriter myErrorOutput;
|
||||
private final PrintWriter myInfoOutput;
|
||||
|
||||
public MessageOutput(PrintWriter infoOutput, PrintWriter errorOuput) {
|
||||
myInfoOutput = infoOutput;
|
||||
myErrorOutput = errorOuput;
|
||||
}
|
||||
|
||||
public void info(@NotNull String message) {
|
||||
myInfoOutput.print(message);
|
||||
myInfoOutput.flush();
|
||||
}
|
||||
|
||||
public void error(@NotNull String message) {
|
||||
myErrorOutput.print(message);
|
||||
myErrorOutput.flush();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<appStarter implementation="com.intellij.codeInspection.InspectionMain"/>
|
||||
<appStarter implementation="com.intellij.ide.ui.search.TraverseUIStarter"/>
|
||||
<appStarter implementation="com.intellij.formatting.commandLine.FormatterStarter"/>
|
||||
|
||||
<referencesSearch implementation="com.intellij.psi.impl.search.CachesBasedRefSearcher"/>
|
||||
<referencesSearch implementation="com.intellij.psi.impl.search.NonPhysicalReferenceSearcher"/>
|
||||
|
||||
Reference in New Issue
Block a user