1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-06 17:26:01 +02:00

Bug 520340: enable CLI to CDT's code formatter

example usage:
eclipse -nosplash -application org.eclipse.cdt.core.CodeFormatter -consoleLog \
        -verbose -config .settings/org.eclipse.cdt.core.prefs src/

imported CodeFormatterApplication from eclipse.jdt.core:
org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/CodeFormatterApplication.java
revision 823698366b88fe7e6c1438813d7dd191ff9aa71f
org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/messages.properties
revision c2646812d22906feca88c2f52911ca7c4eae2299

Change-Id: Id934c8958c66f562bd2402dcbd8b7c60e39cc7a5
Signed-off-by: Mustafa Yücel <mustafa.yuecel@siemens.com>
This commit is contained in:
Mustafa Yücel 2017-06-29 22:54:53 +02:00 committed by Jonah Graham
parent ebbe75d37f
commit 2e0f85e514
9 changed files with 607 additions and 0 deletions

View file

@ -876,6 +876,13 @@
</adapter> </adapter>
</factory> </factory>
</extension> </extension>
<extension
id="CodeFormatter"
point="org.eclipse.core.runtime.applications">
<application>
<run class="org.eclipse.cdt.core.formatter.CodeFormatterApplication"/>
</application>
</extension>
<extension <extension
id="cBuilder" id="cBuilder"
point="org.eclipse.core.resources.builders"> point="org.eclipse.core.resources.builders">

View file

@ -0,0 +1,327 @@
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Mustafa Yuecel - initial implementation - adapted from org.eclipse.jdt.core
*******************************************************************************/
package org.eclipse.cdt.core.formatter;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.ToolFactory;
import org.eclipse.cdt.core.formatter.CodeFormatter;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.TextEdit;
/**
* Implements an Eclipse Application for org.eclipse.cdt.core.CodeFormatter.
*
* <p>On MacOS, when invoked using the Eclipse executable, the "user.dir" property is set to the folder
* in which the eclipse.ini file is located. This makes it harder to use relative paths to point to the
* files to be formatted or the configuration file to use to set the code formatter's options.</p>
*
* <p>There are a couple improvements that could be made: 1. Make a list of all the
* files first so that a file does not get formatted twice. 2. Use a text based
* progress monitor for output.</p>
*
* @author Ben Konrath <bkonrath@redhat.com>
* @since 6.3
* @noinstantiate This class is not intended to be instantiated by clients.
* @noextend This class is not intended to be subclassed by clients.
*/
public class CodeFormatterApplication implements IApplication {
private static final String ARG_CONFIG = "-config"; //$NON-NLS-1$
private static final String ARG_HELP = "-help"; //$NON-NLS-1$
private static final String ARG_QUIET = "-quiet"; //$NON-NLS-1$
private static final String ARG_VERBOSE = "-verbose"; //$NON-NLS-1$
private String configName;
@SuppressWarnings("rawtypes")
private Map options = null;
private boolean quiet = false;
private boolean verbose = false;
/**
* Display the command line usage message.
*/
private void displayHelp() {
System.out.println(Messages.CommandLineUsage);
}
private void displayHelp(String message) {
System.err.println(message);
System.out.println();
displayHelp();
}
/**
* Recursively format the C/C++ source code that is contained in the
* directory rooted at dir.
*/
private void formatDirTree(File dir, CodeFormatter codeFormatter) {
File[] files = dir.listFiles();
if (files == null)
return;
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isDirectory()) {
formatDirTree(file, codeFormatter);
} else if (hasSuitableFileExtension(file)) {
formatFile(file, codeFormatter);
}
}
}
/**
* Format the given C/C++ source file.
*/
private void formatFile(File file, CodeFormatter codeFormatter) {
IDocument doc = new Document();
try {
// read the file
if (this.verbose) {
System.out.println(Messages.bind(Messages.CommandLineFormatting, file.getAbsolutePath()));
}
String contents = new String(Files.readAllBytes(file.toPath())); // compatible only with Java 7+
// format the file (the meat and potatoes)
doc.set(contents);
TextEdit edit = codeFormatter.format(CodeFormatter.K_TRANSLATION_UNIT, contents, 0, contents.length(), 0, null);
if (edit != null) {
edit.apply(doc);
} else {
System.err.println(Messages.bind(Messages.FormatProblem, file.getAbsolutePath()));
return;
}
// write the file
final BufferedWriter out = new BufferedWriter(new FileWriter(file));
try {
out.write(doc.get());
out.flush();
} finally {
try {
out.close();
} catch (IOException e) {
/* ignore */
}
}
} catch (IOException e) {
String errorMessage = Messages.bind(Messages.CaughtException, "IOException", e.getLocalizedMessage()); //$NON-NLS-1$
System.err.println(Messages.bind(Messages.ExceptionSkip ,errorMessage));
} catch (BadLocationException e) {
String errorMessage = Messages.bind(Messages.CaughtException, "BadLocationException", e.getLocalizedMessage()); //$NON-NLS-1$
System.err.println(Messages.bind(Messages.ExceptionSkip ,errorMessage));
}
}
private File[] processCommandLine(String[] argsArray) {
ArrayList<String> args = new ArrayList<String>();
for (int i = 0, max = argsArray.length; i < max; i++) {
args.add(argsArray[i]);
}
int index = 0;
final int argCount = argsArray.length;
final int DEFAULT_MODE = 0;
final int CONFIG_MODE = 1;
int mode = DEFAULT_MODE;
ArrayList<File> filesToFormat = new ArrayList<File>();
loop: while (index < argCount) {
String currentArg = argsArray[index++];
switch(mode) {
case DEFAULT_MODE :
if (ARG_HELP.equals(currentArg)) {
displayHelp();
return null;
}
if (ARG_VERBOSE.equals(currentArg)) {
this.verbose = true;
continue loop;
}
if (ARG_QUIET.equals(currentArg)) {
this.quiet = true;
continue loop;
}
if (ARG_CONFIG.equals(currentArg)) {
mode = CONFIG_MODE;
continue loop;
}
// the current arg should be a file or a directory name
File file = new File(currentArg);
if (file.exists()) {
filesToFormat.add(file);
} else {
String canonicalPath;
try {
canonicalPath = file.getCanonicalPath();
} catch(IOException e2) {
canonicalPath = file.getAbsolutePath();
}
String errorMsg = file.isAbsolute() ?
Messages.bind(Messages.CommandLineErrorFile, canonicalPath):
Messages.bind(Messages.CommandLineErrorFileTryFullPath, canonicalPath);
displayHelp(errorMsg);
return null;
}
break;
case CONFIG_MODE :
this.configName = currentArg;
this.options = readConfig(currentArg);
boolean validConfig = false;
if (this.options != null && !this.options.isEmpty()) {
@SuppressWarnings("unchecked")
Iterator<String> it = this.options.keySet().iterator();
// at least 1 property key starts with org.eclipse.cdt.core.formatter
while (it.hasNext()) {
if (it.next().startsWith(this.getClass().getPackage().getName())) {
validConfig = true;
break;
}
}
}
if (!validConfig) {
displayHelp(Messages.bind(Messages.CommandLineErrorConfig, currentArg));
return null;
}
mode = DEFAULT_MODE;
continue loop;
}
}
if (this.quiet && this.verbose) {
displayHelp(
Messages.bind(
Messages.CommandLineErrorQuietVerbose,
new String[] { ARG_QUIET, ARG_VERBOSE }
));
return null;
}
if (filesToFormat.isEmpty()) {
displayHelp(Messages.CommandLineErrorFileDir);
return null;
}
return filesToFormat.toArray(new File[0]);
}
/**
* Return a Properties file representing the options that are in the
* specified configuration file.
*/
private Properties readConfig(String filename) {
File configFile = new File(filename);
try (BufferedInputStream stream = new BufferedInputStream(new FileInputStream(configFile))) {
final Properties formatterOptions = new Properties();
formatterOptions.load(stream);
return formatterOptions;
} catch (IOException e) {
String canonicalPath = null;
try {
canonicalPath = configFile.getCanonicalPath();
} catch(IOException e2) {
canonicalPath = configFile.getAbsolutePath();
}
String errorMessage;
if (!configFile.exists() && !configFile.isAbsolute()) {
errorMessage = Messages.bind(Messages.ConfigFileNotFoundErrorTryFullPath, new Object[] {
canonicalPath,
System.getProperty("user.dir") //$NON-NLS-1$
});
} else {
errorMessage = Messages.bind(Messages.ConfigFileReadingError, canonicalPath);
}
System.err.println(errorMessage);
}
return null;
}
/**
* Checks if given file name ends with e.g. .c .cpp or .h
*/
private boolean hasSuitableFileExtension(File file) {
IContentType ct= CCorePlugin.getContentType(null, file.getName());
if (ct != null) {
String id = ct.getId();
if (CCorePlugin.CONTENT_TYPE_CSOURCE.equals(id) || CCorePlugin.CONTENT_TYPE_CHEADER.equals(id) ||
CCorePlugin.CONTENT_TYPE_CXXSOURCE.equals(id) || CCorePlugin.CONTENT_TYPE_CXXHEADER.equals(id)) {
return true;
}
}
return false;
}
/**
* Runs the code formatter application
*/
@Override
public Object start(IApplicationContext context) throws Exception {
File[] filesToFormat = processCommandLine((String[]) context.getArguments().get(IApplicationContext.APPLICATION_ARGS));
if (filesToFormat == null) {
return IApplication.EXIT_OK;
}
if (!this.quiet) {
if (this.configName != null) {
System.out.println(Messages.bind(Messages.CommandLineConfigFile, this.configName));
}
System.out.println(Messages.CommandLineStart);
}
@SuppressWarnings("unchecked")
final CodeFormatter codeFormatter = ToolFactory.createCodeFormatter(this.options);
// format the list of files and/or directories
for (int i = 0, max = filesToFormat.length; i < max; i++) {
final File file = filesToFormat[i];
if (file.isDirectory()) {
formatDirTree(file, codeFormatter);
} else {
formatFile(file, codeFormatter);
}
}
if (!this.quiet) {
System.out.println(Messages.CommandLineDone);
}
return IApplication.EXIT_OK;
}
@Override
public void stop() {
// do nothing
}
}

View file

@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Mustafa Yuecel - initial implementation - adapted from org.eclipse.jdt.core
*******************************************************************************/
package org.eclipse.cdt.core.formatter;
import org.eclipse.osgi.util.NLS;
/**
* @since 6.3
*/
public class Messages extends NLS {
public static String CommandLineConfigFile;
public static String CommandLineDone;
public static String CommandLineErrorConfig;
public static String CommandLineErrorFileTryFullPath;
public static String CommandLineErrorFile;
public static String CommandLineErrorFileDir;
public static String CommandLineErrorQuietVerbose;
public static String CommandLineFormatting;
public static String CommandLineStart;
public static String CommandLineUsage;
public static String ConfigFileNotFoundErrorTryFullPath;
public static String ConfigFileReadingError;
public static String FormatProblem;
public static String CaughtException;
public static String ExceptionSkip;
static {
NLS.initializeMessages(Messages.class.getName(), Messages.class);
}
private Messages() {
}
}

View file

@ -0,0 +1,43 @@
###############################################################################
# Copyright (c) 2006, 2017 Ben Konrath <ben@bagu.org> and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Mustafa Yuecel - initial implementation - adapted from org.eclipse.jdt.core
###############################################################################
CommandLineStart=Starting format job ...
CommandLineDone=Done.
CommandLineConfigFile=Configuration Name: {0}
CommandLineFormatting=Formatting: {0}
CommandLineUsage=Usage: eclipse -application org.eclipse.cdt.core.CodeFormatter [ OPTIONS ] [ -config <configFile> ] <files>\n\
\n\
\ <files> C/C++ source/header files and/or directories to format.\n\
\ Only source/header file endings (like .c .cpp or .h) will be formatted in the given directory.\n\
\ -config <configFile> Use the formatting style from the specified properties file.\n\
\ Refer to the help documentation to find out how to generate this file.\n\
\n\
\ The usage of -data for setting the workspace is not supported yet and should be avoided!\n\
\n\
\ OPTIONS:\n\
\n\
\ -help Display this message.\n\
\ -quiet Only print error messages.\n\
\ -verbose Be verbose about the formatting job.
CommandLineErrorFileTryFullPath={0} does not exist. Please try specifying valid absolute path.
CommandLineErrorFile={0} does not exist. Please specify only valid C/C++ source/header files.
CommandLineErrorConfig=A problem occurred while reading the config file {0}. Expected at least one property name to start with org.eclipse.cdt.core.formatter.
CommandLineErrorFileDir=You must specify at least one file or directory to format.
CommandLineErrorQuietVerbose=You cannot use the options {0} and {1} together.
CaughtException=Caught {0}: {1}
ExceptionSkip= {0}\nSkipping File.
ConfigFileNotFoundErrorTryFullPath=Error reading configuration file (file path: {0}, current user directory used to read the file: {1}). Try specifying absolute path.
ConfigFileReadingError=Error reading configuration file {0}.
FormatProblem=The Eclipse formatter failed to format {0}. Skipping the file.

View file

@ -80,6 +80,9 @@
<img src="../images/trans.gif" width="50" height="1" alt=""><a style="text-decoration:none" href="cdt_t_sel_search.htm">Selection Searching for C/C++ elements</a><br> <img src="../images/trans.gif" width="50" height="1" alt=""><a style="text-decoration:none" href="cdt_t_sel_search.htm">Selection Searching for C/C++ elements</a><br>
<img src="../images/trans.gif" width="50" height="1" alt=""><a style="text-decoration:none" href="cdt_t_set_src_fold.htm">Setting Source Folders</a><br> <img src="../images/trans.gif" width="50" height="1" alt=""><a style="text-decoration:none" href="cdt_t_set_src_fold.htm">Setting Source Folders</a><br>
<img src="../images/trans.gif" width="25" height="1" alt=""><a style="text-decoration:none" href="cdt_t_sd.htm">Include paths and macros for C/C++ indexer</a><br> <img src="../images/trans.gif" width="25" height="1" alt=""><a style="text-decoration:none" href="cdt_t_sd.htm">Include paths and macros for C/C++ indexer</a><br>
<img src="../images/trans.gif" width="25" height="1" alt=""><a style="text-decoration:none" href="cdt_t_format_app.htm">Using the Formatter Application</a><br>
<img src="../images/trans.gif" width="50" height="1" alt=""><a style="text-decoration:none" href="cdt_t_format_app_run.htm">Running the Formatter Application</a><br>
<img src="../images/trans.gif" width="50" height="1" alt=""><a style="text-decoration:none" href="cdt_t_format_app_gen_config.htm">Generating a Config File for the Formatter Application</a><br>
<p><img src="../images/ng00_04a.gif" ALT="QNX Copyright Statement" ></p> <p><img src="../images/ng00_04a.gif" ALT="QNX Copyright Statement" ></p>
</div></body> </div></body>

View file

@ -0,0 +1,30 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<meta name="copyright" content="Copyright (c) Red Hat Incorporated 2006, 2017. This page is made available under license. For full details see the LEGAL in the documentation book that contains this page." >
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<title>
Using the Formatter Application
</title>
<link rel="stylesheet" href="../book.css" charset="ISO-8859-1" type="text/css">
</head>
<body>
<h1>
Using the Formatter Application
</h1>
<p>
The CDT has a command line Eclipse application that allows users to format C/C++ source code either with
the Eclipse default code formatter options or with custom options.
</p>
<p>
<img src="../images/ngtasks.gif" alt="Related tasks" width="143" height="21">
</p>
<p>
<a href="cdt_t_format_app_run.htm">Running the Formatter Application</a>
</p>
<p>
<a href="cdt_t_format_app_gen_config.htm">Generating a Config File for the Formatter Application</a>
</p>
</body>
</html>

View file

@ -0,0 +1,45 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta name="copyright" content=
"Copyright (c) Red Hat Incorporated 2006, 2017. This page is made available under license. For full details see the LEGAL in the documentation book that contains this page." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>
Generating a Config File for the Formatter Application
</title>
<link rel="stylesheet" href="../book.css" charset="ISO-8859-1" type=
"text/css" />
</head>
<body>
<h1>
Generating a Config File for the Formatter Application
</h1>
<p>
Generating a config file for the formatter application involves modifying
the code formatter settings for a C/C++ project and copying
org.eclipse.cdt.core.prefs out of the .settings directory for that
project.
</p>
<ol>
<li>Select a C/C++ project, open the pop-up menu and choose
<strong>Properties</strong>.
</li>
<li>Select the <strong>C/C++ General &gt; Formatter</strong> page and
check <strong>Enable project specific settings</strong>.
</li>
<li>Select or edit a profile as explained above.
</li>
<li>Click <strong>OK</strong> when you are done.
</li>
<li>Use either a file manager or the command line to copy
<b><code>workspace/YourCProject/.settings/org.eclipse.cdt.core.prefs</code></b>
to a new location.
</li>
</ol>
</body>
</html>

View file

@ -0,0 +1,106 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta name="copyright" content=
"Copyright (c) Red Hat Incorporated 2006, 2017. This page is made available under license. For full details see the LEGAL in the documentation book that contains this page." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>
Running the Formatter Application
</title>
<link rel="stylesheet" href="../book.css" charset="ISO-8859-1" type=
"text/css" />
</head>
<body>
<h1>
Running the Formatter Application
</h1>
<p>
Running the formatter application is as simple as running the
org.eclipse.cdt.core.CodeFormatter application from the commandline:
</p>
<pre>
eclipse -application org.eclipse.cdt.core.CodeFormatter [ OPTIONS ] [ -config &lt;configFile&gt; ] &lt;files&gt;
</pre>
<p>When invoked on MacOS, the paths to point to the configuration file or the source files can be relative, but they will be computed
from the location of the eclipse.ini file. This is a limitation of the Eclipse launcher on MacOS. On all other platforms, the relative paths
are computed relative to the current user directory.</p>
<table border="1" cellspacing="0" cellpadding="5" width="600" summary="">
<tr>
<td width="25%">
&lt;files&gt;
</td>
<td>
C/C++ source/header files and/or directories to format. Only source/header file endings
(like .c .cpp or .h) will be formatted in the given directory.
</td>
</tr>
<tr>
<td width="25%">
-config &lt;configFile&gt;
</td>
<td>
Use the formatting style from the specified properties file.
Refer to <a href="cdt_t_format_app_gen_config.htm">Generating a Config File for the
Formatter Application</a> for details.
</td>
</tr>
</table>
<table border="1" cellspacing="0" cellpadding="5" width="600" summary="">
<thead>
<tr>
<th>
<p>
OPTIONS
</p>
</th>
<th>
<p>
Description
</p>
</th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" width="25%">
<p>
-help
</p>
</td>
<td valign="top">
<p>
Display the help message.
</p>
</td>
</tr>
<tr>
<td valign="top" width="25%">
<p>
-quiet
</p>
</td>
<td valign="top">
<p>
Only print error messages.
</p>
</td>
</tr>
<tr>
<td valign="top" width="25%">
<p>
-verbose
</p>
</td>
<td valign="top">
<p>
Be verbose about the formatting job.
</p>
</td>
</tr>
</tbody>
</table>
</body>
</html>

View file

@ -93,4 +93,9 @@
</topic> </topic>
<topic label="Include paths and macros for C/C++ indexer" href="tasks/cdt_t_sd.htm"/> <topic label="Include paths and macros for C/C++ indexer" href="tasks/cdt_t_sd.htm"/>
<topic label="Using the Formatter Application" href="tasks/cdt_t_format_app.htm">
<topic label="Running the Formatter Application" href="tasks/cdt_t_format_app_run.htm"/>
<topic label="Generating a Config File for the Formatter Application" href="tasks/cdt_t_format_app_gen_config.htm"/>
</topic>
</toc> </toc>