diff options
9 files changed, 607 insertions, 0 deletions
diff --git a/core/org.eclipse.cdt.core/plugin.xml b/core/org.eclipse.cdt.core/plugin.xml index 086b08d5efe..dfdef215f16 100644 --- a/core/org.eclipse.cdt.core/plugin.xml +++ b/core/org.eclipse.cdt.core/plugin.xml @@ -877,6 +877,13 @@ </factory> </extension> <extension + id="CodeFormatter" + point="org.eclipse.core.runtime.applications"> + <application> + <run class="org.eclipse.cdt.core.formatter.CodeFormatterApplication"/> + </application> + </extension> + <extension id="cBuilder" point="org.eclipse.core.resources.builders"> <builder diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/CodeFormatterApplication.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/CodeFormatterApplication.java new file mode 100644 index 00000000000..4c849fe0e68 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/CodeFormatterApplication.java @@ -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 + } +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/Messages.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/Messages.java new file mode 100644 index 00000000000..994215c1687 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/Messages.java @@ -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() { + } +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/Messages.properties b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/Messages.properties new file mode 100644 index 00000000000..35c85d167f7 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/Messages.properties @@ -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.
\ No newline at end of file diff --git a/doc/org.eclipse.cdt.doc.user/tasks/cdt_o_tasks.htm b/doc/org.eclipse.cdt.doc.user/tasks/cdt_o_tasks.htm index 336973b40d7..2d916bc75f4 100644 --- a/doc/org.eclipse.cdt.doc.user/tasks/cdt_o_tasks.htm +++ b/doc/org.eclipse.cdt.doc.user/tasks/cdt_o_tasks.htm @@ -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_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_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> </div></body> diff --git a/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app.htm b/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app.htm new file mode 100644 index 00000000000..c7caa694968 --- /dev/null +++ b/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app.htm @@ -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> diff --git a/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app_gen_config.htm b/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app_gen_config.htm new file mode 100644 index 00000000000..1803088382b --- /dev/null +++ b/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app_gen_config.htm @@ -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 > 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> diff --git a/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app_run.htm b/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app_run.htm new file mode 100644 index 00000000000..98a2982d8a6 --- /dev/null +++ b/doc/org.eclipse.cdt.doc.user/tasks/cdt_t_format_app_run.htm @@ -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 <configFile> ] <files> +</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%"> + <files> + </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 <configFile> + </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> diff --git a/doc/org.eclipse.cdt.doc.user/topics_Tasks.xml b/doc/org.eclipse.cdt.doc.user/topics_Tasks.xml index afb719e5676..74ceea40428 100644 --- a/doc/org.eclipse.cdt.doc.user/topics_Tasks.xml +++ b/doc/org.eclipse.cdt.doc.user/topics_Tasks.xml @@ -93,4 +93,9 @@ </topic> <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> |