├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── change_version.sh ├── deploy.sh ├── feature ├── .gitignore ├── build.properties ├── feature.xml └── pom.xml ├── plugin ├── .gitignore ├── META-INF │ └── MANIFEST.MF ├── build.properties ├── plugin.xml ├── pom.xml └── src │ └── org │ └── wangzw │ └── plugin │ └── cppstyle │ ├── ClangFormatFormatter.java │ ├── Command.java │ ├── CommandBuilder.java │ ├── ConsoleOutputSniffer.java │ ├── CppStyle.java │ ├── CpplintCheckSettings.java │ ├── CpplintChecker.java │ ├── CpplintErrorParser.java │ ├── CpplintInvoker.java │ ├── InvocationParameters.java │ ├── diff_match_patch.java │ └── ui │ ├── CppStyleConsolePage.java │ ├── CppStyleConsolePatternMatchListener.java │ ├── CppStyleConsoleViewer.java │ ├── CppStyleConstants.java │ ├── CppStyleHandler.java │ ├── CppStyleMessageConsole.java │ ├── CppStylePerfPage.java │ ├── CppStylePropertyPage.java │ └── PreferenceInitializer.java ├── pom.xml └── update ├── .gitignore ├── category.xml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | .DS_Store 4 | *.swp 5 | META-INF/ 6 | target/ 7 | .settings/ 8 | .metadata/ 9 | .recommenders/ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: false 3 | jdk: 4 | - oraclejdk8 5 | after_success: 6 | - bash deploy.sh 7 | env: 8 | global: 9 | secure: aXgMnZs91TN1+nkoMlWLlCeppVk6a2XSJyduiN0nooFyLtrKlodRy0epS7O3uSumjnYWGRDeB9QiV8Hwb+vb+xABTFMnJ132vbQhp3F7StShTtkoFDknPEhxyoecqLsHCsRN0eO0yttaDoQF4zuxf13/2dvPSQAzOJzailD7Rjs= 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Zhanwei Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CppStyle 2 | ======================== 3 | 4 | NOTE: I stop using Eclipse for many years. So this plugin is almost abandoned. 5 | 6 | [![Build Status](https://travis-ci.org/wangzw/CppStyle.svg?branch=master)](https://travis-ci.org/wangzw/CppStyle) 7 | 8 | 9 | Drag to your running Eclipse* workspace. *Requires Eclipse Marketplace Client 10 | 11 | 12 | **An Eclipse plugin that integrates the clang-format tool as an alternative C/C++ code formatter and checks C++ coding style with the cpplint.py tool.** 13 | 14 | ## Description 15 | A consistent coding style is important for a project. And many projects use tools to format the code and check coding style. Many developers use Eclipse as a C/C++ IDE, but it is a little difficult to integrate an external tool to Eclipse. People have to switch to a command line and run the tools to format the code and check the coding style. And then they need to switch back to Eclipse to find the line and fix the coding style issue based on the tool's output. For the "lazy" people like me, this is irritating. 16 | 17 | The expected behaviors is that people just format the code fragment by first selecting it and then pressing `Command + Shift + f` on MacOS or `Ctrl + Shift + f` on Linux and other systems. Further more, the coding style checker is run whenever a file is saved and all the issues are marked on the editor. That is exactly what CppStyle does. 18 | 19 | There are many C/C++ code format tools such as "[astyle](http://astyle.sourceforge.net/)" but currently **"[clang-format](http://clang.llvm.org/docs/ClangFormat.html)"** is my favorite. It has several pre-defined styles and is highly configurable. 20 | 21 | **[cpplint.py](https://google.github.io/styleguide/cppguide.html#cpplint)** is a C++ coding style checker provided by google. It can be used to check the C++ code against the [Google C++ coding style](http://google-styleguide.googlecode.com/svn/trunk/cppguide.html). It can detect many style errors and maintain the consistency of coding style. 22 | 23 | ## Requirement 24 | cpplint.py https://google.github.io/styleguide/cppguide.html#cpplint 25 | clang-format http://clang.llvm.org/docs/ClangFormat.html 26 | 27 | ### Install cpplint.py on Linux/MacOS 28 | 29 | sudo curl -L "https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py" -o /usr/bin/cpplint.py 30 | sudo chmod a+x /usr/bin/cpplint.py 31 | 32 | ### Install clang-format on Linux/MacOS 33 | clang-format can be built from llvm/clang source. But installing from binary is much easier. 34 | 35 | For Ubuntu 36 | 37 | sudo apt-get install clang-format-3.4 38 | sudo ln -s /usr/bin/clang-format-3.4 /usr/bin/clang-format 39 | 40 | On 64 bit platform, clang-format can also be downloaded from this [page](https://sublime.wbond.net/packages/Clang%20Format). 41 | 42 | If you prefer, you can download the [entire LLVM toolchain](http://llvm.org/releases/download.html) and extract the clang-format binary yourself. Just extract the .tar.xz file and copy bin/clang-format into your PATH (e.g. /usr/local/bin). - Set the path to the clang-format binaries. 43 | 44 | ## Installation 45 | 46 | ### Install from Eclipse Marketplace (Recommend) 47 | 48 | 49 | Drag to your running Eclipse* workspace. *Requires Eclipse Marketplace Client 50 | 51 | 52 | **Drag the above button to your running Eclipse workspace** 53 | 54 | [Go to Eclipse Marketplace page] (https://marketplace.eclipse.org/content/cppstyle) 55 | 56 | ### Install from update site 57 | 58 | CppStyle can be installed like other eclipse plugins from this site. 59 | 60 | http://www.zhanwei.wang/CppStyle/update (Latest) 61 | http://www.zhanwei.wang/CppStyle/oxygen (Eclipse Oxygen) 62 | http://www.zhanwei.wang/CppStyle/mars (Eclipse Mars) 63 | http://www.zhanwei.wang/CppStyle/luna (Eclipse Luna) 64 | http://www.zhanwei.wang/CppStyle/kepler (Eclipse Kepler) 65 | 66 | Go to **Help -> Install New Software** page, click **Add** button and then enter a name (`CppStyle`) and the above URL, and then click **OK**. 67 | 68 | Select **CppStyle** from drop-down list and then check the name **CppStyle** listed in the page. And then click **Next** and **OK** until restart. 69 | 70 | ### Manual 71 | 72 | * Build CppStye with maven first. ```mvn clean package``` 73 | * Install CppStyle with local update site ```file:////update/target/site``` 74 | 75 | Restart Eclipse. 76 | 77 | ## Configure CppStyle 78 | 79 | To configure CppStyle globally, go to **Preferences -> C/C++ -> CppStyle** dialog. 80 | 81 | To configure CppStyle for a C/C++ project, go to **Project properties -> CppStyle** dialog. 82 | 83 | To enable CppStyle(clang-format) as default C/C++ code formatter, go to **Preferences -> C/C++ -> Code Style -> Formatter** page and switch **"Code Formatter"** from **[built-in]** to **"CppStyle (clang-format)"** 84 | 85 | To enable CppStyle(clang-format) as C/C++ code formatter for a project, go to **Project properties -> C/C++ General -> Formatter** page and switch **"Code Formatter"** from **[built-in]** to **"CppStyle (clang-format)"** 86 | 87 | ## To configure clang-format 88 | 89 | CppStyle does not support appending command line parameters to clang-format and cpplint.py. So, use their respective configuration files to do this. 90 | 91 | CppStyle will pass the full absolute path of the source file to clang-format in command line. And clang-format will try to find the configuration file named **.clang-format** in the source file's path, and its parent's path if not found previously, and parent's path of the parent and so on. 92 | 93 | So put the configuration file **.clang-format** into the project's root directory can make it work for all source files in the project. 94 | 95 | Further more, you can also add the configuration file **.clang-format** into Eclipse workspace root directory to make it work for all projects in the workspace. 96 | 97 | To generate the clang-format configuration file **.clang-format**: 98 | 99 | clang-format -dump-config -style=Google > .clang-format 100 | 101 | **If no configure file named .clang-format is found, "-style=Google" will be passed to clang-format and Google style will be used by default.** 102 | 103 | ## To configure cpplint.py 104 | 105 | By default, if you enable `cpplint.py` in **CppStyle** page, `cpplint.py` will be triggered every time when you save a file. You can also trigger `cpplint.py` by click the button **Run C/C++ Code Analysis** in the popup menu when you right click the file, directory or the entire project in **Project Explorer**. 106 | 107 | CppStyle will pass source file's full absolute path to `cpplint.py` in the command line. And `cpplint.py` also supports per-directory configuration by the configuration file named `CPPLINT.cfg`. 108 | 109 | If **Root** is set in project property page, **--root=Root** will pass to `cpplint.py`. 110 | 111 | `CPPLINT.cfg` file can contain a number of key=value pairs. 112 | Currently the following options are supported: 113 | 114 | set noparent 115 | filter=+filter1,-filter2,... 116 | exclude_files=regex 117 | linelength=80 118 | 119 | To get the details of these options you can run the command: 120 | 121 | cpplint.py --help 122 | 123 | ### To use cpplint.py on Windows 124 | 125 | On Windows, the path in the CppStyle settings should point to an executable of cpplint. One option is to create an executable from cpplint.py using PyInstaller. 126 | 127 | ## To enable or disable cpplint.py on specific issues or files 128 | 129 | There are two ways to enable or disable cpplint.py on specific issues or files. The first and recommended one is to use configure file named `CPPLINT.cfg`. The benefit of using configure file is that it can be version controlled and shared with others in a team. It also can produce the consistent result if you use `cpplint.py` in command line instead of CppStyle in Eclipse. 130 | 131 | The "filter" option is similar in function to --filter flag. It specifies 132 | message filters in addition to the |_DEFAULT_FILTERS| and those specified 133 | through --filter command-line flag. 134 | 135 | "exclude_files" allows to specify a regular expression to be matched against 136 | a file name. If the expression matches, the file is skipped and not run 137 | through liner. 138 | 139 | The other way is to configure **Code Analysis** in **Preferences -> C/C++ -> Code Analysis -> Cpplint Issues** globally, or in **Project property -> C/C++ General -> Code Analysis -> Cpplint Issues** for a C/C++ project. Eclipse Mars has [bug](https://bugs.eclipse.org/bugs/show_bug.cgi?id=471967) to prevent opening **Code Analysis** page in project's property. 140 | -------------------------------------------------------------------------------- /change_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT=`cd "\`dirname \"$0\"\`";pwd` 4 | 5 | OLD=$1 6 | NEW=$2 7 | 8 | [ -z "$OLD" ] && echo "current version is not specified" && exit 1 9 | [ -z "$NEW" ] && echo "target version is not specified" && exit 1 10 | 11 | echo "change version from $OLD to $NEW" 12 | 13 | case "`uname -s`" in 14 | Darwin) 15 | find $ROOT -name "*.xml" -or -name "*.MF" | grep -v metadata | xargs sed -i "" -e "s|$OLD|$NEW|g" 16 | ;; 17 | 18 | *) 19 | find $ROOT -name "*.xml" -or -name "*.MF" | grep -v metadata | xargs sed -i -e "s|$OLD|$NEW|g" 20 | ;; 21 | esac 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | BRANCH=gh-pages 3 | TARGET_REPO=wangzw/cppstyle 4 | OUTPUT_FOLDER=update/target/site 5 | SITE_DIR=$TRAVIS_BRANCH 6 | 7 | echo -e "Testing travis-encrypt" 8 | echo -e "$VARNAME" 9 | 10 | if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then 11 | echo -e "Starting to deploy to Github Pages\n" 12 | if [ "$TRAVIS" == "true" ]; then 13 | git config --global user.email "travis@travis-ci.org" 14 | git config --global user.name "Travis" 15 | fi 16 | #using token clone gh-pages branch 17 | git clone --quiet --branch=$BRANCH https://${GH_TOKEN}@github.com/$TARGET_REPO built_website > /dev/null 18 | #go into directory and copy data we're interested in to that directory 19 | cd built_website 20 | 21 | if [ "$TRAVIS_BRANCH" == "master" ]; then 22 | SITE_DIR=update 23 | fi 24 | 25 | rm -rf $SITE_DIR 26 | mkdir -p $SITE_DIR 27 | cd $SITE_DIR 28 | cp ../site-index.html index.html 29 | rsync -rv --exclude=.git ../../$OUTPUT_FOLDER/* . 30 | 31 | #add, commit and push files 32 | git add --all -f . 33 | git commit -m "Travis build $TRAVIS_BUILD_NUMBER pushed to Github Pages" 34 | git push -fq origin $BRANCH > /dev/null 35 | echo -e "Deploy completed\n" 36 | fi 37 | -------------------------------------------------------------------------------- /feature/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | -------------------------------------------------------------------------------- /feature/build.properties: -------------------------------------------------------------------------------- 1 | bin.includes = feature.xml 2 | -------------------------------------------------------------------------------- /feature/feature.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | Integrate clang-format as an alternative c/c++ code formater and check c++ code style with cpplint.py 10 | 11 | 12 | 13 | © Zhanwei Wang, wangzw@wangz.org 14 | 15 | 16 | 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2015 Zhanwei Wang 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /feature/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cppstyle-group 7 | cppstyle 8 | 1.5.0.0 9 | 10 | cppstyle-group 11 | org.wangzw.cppstyle.feature 12 | 1.5.0.0 13 | eclipse-feature 14 | 15 | -------------------------------------------------------------------------------- /plugin/.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | -------------------------------------------------------------------------------- /plugin/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Bundle-ManifestVersion: 2 3 | Bundle-Name: org.wangzw.cppstyle 4 | Bundle-SymbolicName: org.wangzw.cppstyle;singleton:=true 5 | Bundle-Version: 1.5.0.0 6 | Bundle-Activator: org.wangzw.plugin.cppstyle.CppStyle 7 | Bundle-Vendor: Zhanwei Wang 8 | Require-Bundle: org.eclipse.ui, 9 | org.eclipse.core.runtime, 10 | org.eclipse.core.resources, 11 | org.eclipse.cdt.core, 12 | org.eclipse.text, 13 | org.eclipse.cdt.ui, 14 | org.eclipse.ui.workbench.texteditor, 15 | org.eclipse.ui.editors, 16 | org.eclipse.ui.workbench, 17 | org.eclipse.ui.ide, 18 | org.eclipse.ui.console, 19 | org.eclipse.debug.ui, 20 | org.eclipse.jface.text, 21 | org.eclipse.core.expressions, 22 | org.eclipse.cdt.codan.core, 23 | org.eclipse.cdt.codan.core.cxx, 24 | org.eclipse.core.filesystem 25 | Bundle-RequiredExecutionEnvironment: JavaSE-1.7 26 | Bundle-ActivationPolicy: lazy 27 | Bundle-ClassPath: . 28 | -------------------------------------------------------------------------------- /plugin/build.properties: -------------------------------------------------------------------------------- 1 | source.. = src/ 2 | output.. = bin/ 3 | bin.includes = plugin.xml,\ 4 | META-INF/,\ 5 | ., 6 | -------------------------------------------------------------------------------- /plugin/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 10 | 11 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 27 | 28 | 29 | 31 | 33 | 34 | 35 | 37 | 41 | 44 | 45 | 46 | 48 | 49 | 50 | 51 | 52 | 54 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | 69 | 70 | 74 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 85 | 87 | 89 | 91 | 93 | 94 | 98 | 102 | 103 | 107 | 111 | 115 | 119 | 123 | 127 | 131 | 135 | 139 | 143 | 147 | 151 | 155 | 156 | 160 | 161 | 165 | 169 | 173 | 177 | 181 | 185 | 189 | 193 | 197 | 201 | 205 | 209 | 213 | 217 | 221 | 225 | 226 | 230 | 234 | 238 | 242 | 246 | 250 | 254 | 258 | 262 | 266 | 270 | 274 | 278 | 282 | 286 | 290 | 291 | 295 | 299 | 303 | 307 | 311 | 315 | 319 | 323 | 327 | 331 | 335 | 339 | 343 | 347 | 351 | 355 | 359 | 360 | 361 | 362 | 363 | -------------------------------------------------------------------------------- /plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cppstyle-group 7 | cppstyle 8 | 1.5.0.0 9 | 10 | cppstyle-group 11 | org.wangzw.cppstyle 12 | 1.5.0.0 13 | eclipse-plugin 14 | 15 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ClangFormatFormatter.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.OutputStreamWriter; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.LinkedList; 10 | import java.util.Map; 11 | import java.net.URI; 12 | 13 | import org.eclipse.cdt.core.CCorePlugin; 14 | import org.eclipse.cdt.core.formatter.CodeFormatter; 15 | import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants; 16 | import org.eclipse.cdt.core.model.CoreModel; 17 | import org.eclipse.cdt.core.model.ITranslationUnit; 18 | import org.eclipse.cdt.ui.ICEditor; 19 | import org.eclipse.core.filesystem.URIUtil; 20 | import org.eclipse.core.resources.IFile; 21 | import org.eclipse.core.resources.IProject; 22 | import org.eclipse.core.resources.IResource; 23 | import org.eclipse.core.resources.ResourcesPlugin; 24 | import org.eclipse.core.runtime.CoreException; 25 | import org.eclipse.core.runtime.IPath; 26 | import org.eclipse.core.runtime.QualifiedName; 27 | import org.eclipse.jface.text.BadLocationException; 28 | import org.eclipse.jface.text.IDocument; 29 | import org.eclipse.jface.text.IRegion; 30 | import org.eclipse.jface.text.Region; 31 | import org.eclipse.text.edits.DeleteEdit; 32 | import org.eclipse.text.edits.InsertEdit; 33 | import org.eclipse.text.edits.MalformedTreeException; 34 | import org.eclipse.text.edits.MultiTextEdit; 35 | import org.eclipse.text.edits.TextEdit; 36 | import org.eclipse.text.undo.DocumentUndoManagerRegistry; 37 | import org.eclipse.text.undo.IDocumentUndoManager; 38 | import org.eclipse.ui.IFileEditorInput; 39 | import org.eclipse.ui.console.MessageConsoleStream; 40 | import org.eclipse.ui.IWorkbenchPage; 41 | import org.eclipse.ui.IWorkbenchWindow; 42 | import org.eclipse.ui.IWorkbench; 43 | import org.eclipse.ui.PlatformUI; 44 | import org.eclipse.ui.IEditorInput; 45 | import org.eclipse.ui.IEditorPart; 46 | import org.eclipse.ui.IURIEditorInput; 47 | import org.eclipse.ui.editors.text.ILocationProvider; 48 | 49 | import org.wangzw.plugin.cppstyle.diff_match_patch.Diff; 50 | import org.wangzw.plugin.cppstyle.ui.CppStyleConstants; 51 | import org.wangzw.plugin.cppstyle.ui.CppStyleMessageConsole; 52 | 53 | 54 | public class ClangFormatFormatter extends CodeFormatter { 55 | private MessageConsoleStream err = null; 56 | private Map options; 57 | 58 | public ClangFormatFormatter() { 59 | super(); 60 | CppStyleMessageConsole console = CppStyle.buildConsole(); 61 | err = console.getErrorStream(); 62 | } 63 | 64 | @Override 65 | public String createIndentationString(int indentationLevel) { 66 | return super.createIndentationString(indentationLevel); 67 | } 68 | 69 | @Override 70 | public void setOptions(Map options) { 71 | if (options != null) { 72 | this.options = options; 73 | } else { 74 | this.options = CCorePlugin.getOptions(); 75 | } 76 | } 77 | 78 | private String getSourceFilePath() { 79 | IWorkbench wb = PlatformUI.getWorkbench(); 80 | if (wb != null) 81 | { 82 | IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); 83 | if (window != null) 84 | { 85 | IWorkbenchPage page = window.getActivePage(); 86 | if (page != null) { 87 | IEditorPart activeEditor = page.getActiveEditor(); 88 | if (activeEditor != null) { 89 | IEditorInput editorInput = activeEditor.getEditorInput(); 90 | if (editorInput != null) { 91 | IPath filePath = getSourceFilePathFromEditorInput(editorInput); 92 | if (filePath != null) { 93 | return filePath.toOSString(); 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | 101 | ITranslationUnit tu = (ITranslationUnit) options.get(DefaultCodeFormatterConstants.FORMATTER_TRANSLATION_UNIT); 102 | 103 | if (tu == null) { 104 | IFile file = (IFile) options.get(DefaultCodeFormatterConstants.FORMATTER_CURRENT_FILE); 105 | if (file != null) { 106 | tu = (ITranslationUnit) CoreModel.getDefault().create(file); 107 | } 108 | } 109 | 110 | if (tu != null) { 111 | return tu.getResource().getRawLocation().toOSString(); 112 | } else { 113 | String root = ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString(); 114 | return new File(root, "a.cc").getAbsolutePath(); 115 | } 116 | } 117 | 118 | private static IPath getSourceFilePathFromEditorInput(IEditorInput editorInput) { 119 | if (editorInput instanceof IURIEditorInput) { 120 | URI uri = ((IURIEditorInput) editorInput).getURI(); 121 | if (uri != null) { 122 | IPath path = URIUtil.toPath(uri); 123 | if (path != null) { 124 | return path; 125 | } 126 | } 127 | } 128 | 129 | if (editorInput instanceof IFileEditorInput) { 130 | IFile file = ((IFileEditorInput) editorInput).getFile(); 131 | if (file != null) { 132 | return file.getLocation(); 133 | } 134 | } 135 | 136 | if (editorInput instanceof ILocationProvider) { 137 | return ((ILocationProvider) editorInput).getPath(editorInput); 138 | } 139 | 140 | return null; 141 | } 142 | 143 | @Override 144 | public TextEdit format(int kind, String source, int offset, int length, int arg4, String lineSeparator) { 145 | TextEdit retval = format(source, getSourceFilePath(), new Region(offset, length)); 146 | return retval != null ? retval : new MultiTextEdit(); 147 | } 148 | 149 | public void formatAndApply(ICEditor editor) { 150 | IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput()); 151 | 152 | String path = ((IFileEditorInput) editor.getEditorInput()).getFile().getLocation().toOSString(); 153 | TextEdit res = format(doc.get(), path, null); 154 | 155 | if (res == null) { 156 | return; 157 | } 158 | 159 | IDocumentUndoManager manager = DocumentUndoManagerRegistry.getDocumentUndoManager(doc); 160 | manager.beginCompoundChange(); 161 | 162 | try { 163 | res.apply(doc); 164 | } catch (MalformedTreeException e) { 165 | CppStyle.log("Failed to apply change", e); 166 | } catch (BadLocationException e) { 167 | CppStyle.log("Failed to apply change", e); 168 | } 169 | 170 | manager.endCompoundChange(); 171 | 172 | } 173 | 174 | private TextEdit format(String source, String path, IRegion region) { 175 | String clangFormatPath = getClangFormatPath(); 176 | if (checkClangFormat(clangFormatPath) == false) { 177 | return null; 178 | } 179 | 180 | String confPath = getClangFormatConfigureFile(path); 181 | if (confPath == null) { 182 | err.println( 183 | "Cannot find .clang-format or _clang-format configuration file under any level " 184 | + "parent directories of path (" + path + ")."); 185 | err.println("Clang-format will default to Google style."); 186 | } 187 | 188 | // make clang-format do its own search for the configuration, but fall back to Google. 189 | String stdArg = "-style=file"; 190 | String fallbackArg = "-fallback-style=Google"; 191 | 192 | ArrayList commands = new ArrayList( 193 | Arrays.asList(clangFormatPath, "-assume-filename=" + path, stdArg, fallbackArg)); 194 | 195 | StringBuffer sb = new StringBuffer(); 196 | sb.append(stdArg + " " + fallbackArg + " "); 197 | 198 | if (region != null) { 199 | commands.add("-offset=" + region.getOffset()); 200 | commands.add("-length=" + region.getLength()); 201 | 202 | sb.append("-offset="); 203 | sb.append(region.getOffset()); 204 | sb.append(" -length="); 205 | sb.append(region.getLength()); 206 | sb.append(' '); 207 | } 208 | 209 | ProcessBuilder builder = new ProcessBuilder(commands); 210 | 211 | String root = ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString(); 212 | builder.directory(new File(root)); 213 | 214 | try { 215 | Process process = builder.start(); 216 | OutputStreamWriter output = new OutputStreamWriter(process.getOutputStream()); 217 | 218 | output.write(source); 219 | output.flush(); 220 | output.close(); 221 | 222 | InputStreamReader reader = new InputStreamReader(process.getInputStream()); 223 | InputStreamReader error = new InputStreamReader(process.getErrorStream()); 224 | 225 | final char[] buffer = new char[1024]; 226 | final StringBuilder stdout = new StringBuilder(); 227 | final StringBuilder errout = new StringBuilder(); 228 | 229 | for (;;) { 230 | int rsz = reader.read(buffer, 0, buffer.length); 231 | 232 | if (rsz < 0) { 233 | break; 234 | } 235 | 236 | stdout.append(buffer, 0, rsz); 237 | } 238 | 239 | for (;;) { 240 | int rsz = error.read(buffer, 0, buffer.length); 241 | 242 | if (rsz < 0) { 243 | break; 244 | } 245 | 246 | errout.append(buffer, 0, rsz); 247 | } 248 | 249 | String newSource = stdout.toString(); 250 | 251 | int code = process.waitFor(); 252 | if (code != 0) { 253 | err.println("clang-format return error (" + code + ")."); 254 | err.println(errout.toString()); 255 | return null; 256 | } 257 | 258 | if (errout.length() > 0) { 259 | err.println(errout.toString()); 260 | return null; 261 | } 262 | 263 | if (0 == source.compareTo(newSource)) { 264 | return null; 265 | } 266 | 267 | diff_match_patch diff = new diff_match_patch(); 268 | 269 | LinkedList diffs = diff.diff_main(source, newSource); 270 | diff.diff_cleanupEfficiency(diffs); 271 | 272 | int offset = 0; 273 | MultiTextEdit edit = new MultiTextEdit(); 274 | 275 | for (Diff d : diffs) { 276 | switch (d.operation) { 277 | case INSERT: 278 | InsertEdit e = new InsertEdit(offset, d.text); 279 | edit.addChild(e); 280 | break; 281 | case DELETE: 282 | DeleteEdit e1 = new DeleteEdit(offset, d.text.length()); 283 | offset += d.text.length(); 284 | edit.addChild(e1); 285 | break; 286 | case EQUAL: 287 | offset += d.text.length(); 288 | break; 289 | } 290 | } 291 | 292 | return edit; 293 | 294 | } catch (IOException e) { 295 | CppStyle.log("Failed to format code", e); 296 | } catch (InterruptedException e) { 297 | CppStyle.log("Failed to format code", e); 298 | } 299 | 300 | return null; 301 | } 302 | 303 | private String getClangFormatConfigureFile(String path) { 304 | File file = new File(path); 305 | 306 | while (file != null) { 307 | File dir = file.getParentFile(); 308 | if (dir != null) { 309 | File conf = new File(dir, ".clang-format"); 310 | if (conf.exists()) { 311 | return conf.getAbsolutePath(); 312 | } 313 | 314 | conf = new File(dir, "_clang-format"); 315 | if (conf.exists()) { 316 | return conf.getAbsolutePath(); 317 | } 318 | } 319 | 320 | file = dir; 321 | } 322 | 323 | return null; 324 | } 325 | 326 | public boolean checkClangFormat(String clangformat) { 327 | if (clangformat == null) { 328 | err.println("clang-format is not specified."); 329 | return false; 330 | } 331 | 332 | File file = new File(clangformat); 333 | 334 | if (!file.exists()) { 335 | err.println("clang-format (" + clangformat + ") does not exist."); 336 | return false; 337 | } 338 | 339 | if (!file.canExecute()) { 340 | err.println("clang-format (" + clangformat + ") is not executable."); 341 | return false; 342 | } 343 | 344 | return true; 345 | } 346 | 347 | private boolean enableClangFormatOnSave(IResource resource) { 348 | boolean enable = CppStyle.getDefault().getPreferenceStore() 349 | .getBoolean(CppStyleConstants.ENABLE_CLANGFORMAT_ON_SAVE); 350 | 351 | try { 352 | IProject project = resource.getProject(); 353 | String enableProjectSpecific = project 354 | .getPersistentProperty(new QualifiedName("", CppStyleConstants.PROJECTS_PECIFIC_PROPERTY)); 355 | 356 | if (enableProjectSpecific != null && Boolean.parseBoolean(enableProjectSpecific)) { 357 | String value = project 358 | .getPersistentProperty(new QualifiedName("", CppStyleConstants.ENABLE_CLANGFORMAT_PROPERTY)); 359 | if (value != null) { 360 | return Boolean.parseBoolean(value); 361 | } 362 | 363 | return false; 364 | } 365 | } catch (CoreException e) { 366 | CppStyle.log(e); 367 | } 368 | 369 | return enable; 370 | } 371 | 372 | public boolean runClangFormatOnSave(IResource resource) { 373 | if (!enableClangFormatOnSave(resource)) { 374 | return false; 375 | } 376 | 377 | String clangFormat = getClangFormatPath(); 378 | 379 | if (clangFormat == null) { 380 | err.println("clang-format command must be specified in preferences."); 381 | return false; 382 | } 383 | 384 | File file = new File(clangFormat); 385 | 386 | if (!file.exists()) { 387 | err.println("clang-format (" + clangFormat + ") does not exist."); 388 | return false; 389 | } 390 | 391 | if (!file.canExecute()) { 392 | err.println("clang-format (" + clangFormat + ") is not executable."); 393 | return false; 394 | } 395 | 396 | return true; 397 | } 398 | 399 | public static String getClangFormatPath() { 400 | return CppStyle.getDefault().getPreferenceStore().getString(CppStyleConstants.CLANG_FORMAT_PATH); 401 | } 402 | 403 | } 404 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/Command.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import org.eclipse.core.runtime.IPath; 4 | 5 | /** 6 | * The command to execute to invoke an external tool. 7 | */ 8 | class Command { 9 | private final IPath path; 10 | private final String[] args; 11 | private final String[] env; 12 | 13 | Command(IPath path, String[] args) { 14 | this(path, args, new String[] {}); 15 | } 16 | 17 | Command(IPath path, String[] args, String[] env) { 18 | this.path = path; 19 | this.args = args; 20 | this.env = env; 21 | } 22 | 23 | IPath getPath() { 24 | return path; 25 | } 26 | 27 | String[] getArgs() { 28 | return args; 29 | } 30 | 31 | String[] getEnv() { 32 | return env; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/CommandBuilder.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import java.io.File; 4 | 5 | import org.eclipse.cdt.codan.core.cxx.externaltool.ArgsSeparator; 6 | import org.eclipse.core.runtime.IPath; 7 | import org.eclipse.core.runtime.Path; 8 | 9 | /** 10 | * Creates the command to use to invoke an external tool. 11 | */ 12 | public class CommandBuilder { 13 | Command buildCommand(InvocationParameters parameters, CpplintCheckSettings settings, ArgsSeparator argsSeparator) { 14 | IPath executablePath = executablePath(settings); 15 | String[] args = argsToPass(parameters, settings, argsSeparator); 16 | return new Command(executablePath, args); 17 | } 18 | 19 | private IPath executablePath(CpplintCheckSettings configurationSettings) { 20 | File executablePath = configurationSettings.getPath(); 21 | return new Path(executablePath.toString()); 22 | } 23 | 24 | private String[] argsToPass(InvocationParameters parameters, CpplintCheckSettings configurationSettings, 25 | ArgsSeparator argsSeparator) { 26 | String actualFilePath = parameters.getActualFilePath(); 27 | String[] args = configuredArgs(configurationSettings, argsSeparator); 28 | return addFilePathToArgs(actualFilePath, args); 29 | } 30 | 31 | private String[] configuredArgs(CpplintCheckSettings settings, ArgsSeparator argsSeparator) { 32 | String args = settings.getArgs(); 33 | return argsSeparator.splitArguments(args); 34 | } 35 | 36 | private String[] addFilePathToArgs(String actualFilePath, String[] configuredArgs) { 37 | int argCount = configuredArgs.length; 38 | String[] allArgs = new String[argCount + 1]; 39 | allArgs[0] = actualFilePath; 40 | // Copy arguments 41 | System.arraycopy(configuredArgs, 0, allArgs, 1, argCount); 42 | return allArgs; 43 | } 44 | } -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ConsoleOutputSniffer.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | import org.eclipse.cdt.core.IConsoleParser; 7 | 8 | /** 9 | * Intercepts an output to console and forwards it to console parsers for 10 | * processing 11 | */ 12 | public class ConsoleOutputSniffer { 13 | 14 | /** 15 | * Private class to sniff the output stream for this sniffer. 16 | */ 17 | private class ConsoleOutputStream extends OutputStream { 18 | // Stream's private buffer for the stream's read contents. 19 | private StringBuffer currentLine = new StringBuffer(); 20 | private OutputStream outputStream = null; 21 | 22 | public ConsoleOutputStream(OutputStream outputStream) { 23 | this.outputStream = outputStream; 24 | } 25 | 26 | @Override 27 | public void write(int b) throws IOException { 28 | currentLine.append((char) b); 29 | checkLine(false); 30 | 31 | // Continue writing the bytes to the console's output. 32 | if (outputStream != null) { 33 | outputStream.write(b); 34 | } 35 | } 36 | 37 | @Override 38 | public void write(byte[] b, int off, int len) throws IOException { 39 | if (b == null) { 40 | throw new NullPointerException(); 41 | } else if (off != 0 || (len < 0) || (len > b.length)) { 42 | throw new IndexOutOfBoundsException(); 43 | } else if (len == 0) { 44 | return; 45 | } 46 | currentLine.append(new String(b, 0, len)); 47 | checkLine(false); 48 | 49 | // Continue writing the bytes to the console's output. 50 | if (outputStream != null) 51 | outputStream.write(b, off, len); 52 | } 53 | 54 | @Override 55 | public void close() throws IOException { 56 | checkLine(true); 57 | closeConsoleOutputStream(); 58 | } 59 | 60 | @Override 61 | public void flush() throws IOException { 62 | if (outputStream != null) { 63 | outputStream.flush(); 64 | } 65 | } 66 | 67 | /** 68 | * Checks to see if the already read input constitutes a complete line 69 | * (e.g. does the sniffing). If so, then send it to processLine. 70 | * 71 | * @param flush 72 | */ 73 | private void checkLine(boolean flush) { 74 | if (currentLine.length() == 0) { 75 | return; 76 | } 77 | 78 | String buffer = currentLine.toString(); 79 | int i = 0; 80 | while ((i = buffer.indexOf('\n')) != -1) { 81 | int eol = i; 82 | if (i > 0 && buffer.charAt(i - 1) == '\r') { 83 | // also get rid of trailing \r in case of Windows line 84 | // delimiter "\r\n" 85 | eol = i - 1; 86 | } 87 | String line = buffer.substring(0, eol); 88 | processLine(line); 89 | 90 | buffer = buffer.substring(i + 1); // skip the \n and advance 91 | } 92 | currentLine.setLength(0); 93 | if (flush) { 94 | if (buffer.length() > 0) { 95 | processLine(buffer); 96 | } 97 | } else { 98 | currentLine.append(buffer); 99 | } 100 | } 101 | 102 | } // end ConsoleOutputStream class 103 | 104 | private int nOpens = 0; 105 | private OutputStream consoleOutputStream; 106 | private OutputStream consoleErrorStream; 107 | private IConsoleParser[] parsers; 108 | 109 | public ConsoleOutputSniffer(IConsoleParser[] parsers) { 110 | this.parsers = parsers; 111 | } 112 | 113 | public ConsoleOutputSniffer(OutputStream outputStream, OutputStream errorStream, IConsoleParser[] parsers) { 114 | this(parsers); 115 | this.consoleOutputStream = outputStream; 116 | this.consoleErrorStream = errorStream; 117 | } 118 | 119 | /** 120 | * Returns an output stream that will be sniffed. This stream should be 121 | * hooked up so the command output stream goes into here. 122 | */ 123 | public OutputStream getOutputStream() { 124 | incNOpens(); 125 | return new ConsoleOutputStream(consoleOutputStream); 126 | } 127 | 128 | /** 129 | * Returns an error stream that will be sniffed. This stream should be 130 | * hooked up so the command error stream goes into here. 131 | */ 132 | public OutputStream getErrorStream() { 133 | incNOpens(); 134 | return new ConsoleOutputStream(consoleErrorStream); 135 | } 136 | 137 | private synchronized void incNOpens() { 138 | nOpens++; 139 | } 140 | 141 | /* 142 | */ 143 | public synchronized void closeConsoleOutputStream() throws IOException { 144 | if (nOpens > 0 && --nOpens == 0) { 145 | for (int i = 0; i < parsers.length; ++i) { 146 | try { 147 | parsers[i].shutdown(); 148 | } catch (Throwable e) { 149 | // Report exception if any but let all the parsers a chance 150 | // to shutdown. 151 | CppStyle.log(e); 152 | } 153 | } 154 | } 155 | } 156 | 157 | /* 158 | * Processes the line by passing the line to the parsers. 159 | * 160 | * @param line 161 | */ 162 | private synchronized void processLine(String line) { 163 | for (IConsoleParser parser : parsers) { 164 | try { 165 | // Report exception if any but let all the parsers a chance to 166 | // process the line. 167 | parser.processLine(line); 168 | } catch (Throwable e) { 169 | CppStyle.log(e); 170 | } 171 | } 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/CppStyle.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import org.eclipse.core.runtime.IStatus; 4 | import org.eclipse.core.runtime.Status; 5 | import org.eclipse.jface.resource.ImageDescriptor; 6 | import org.eclipse.ui.console.ConsolePlugin; 7 | import org.eclipse.ui.console.IConsole; 8 | import org.eclipse.ui.console.IConsoleManager; 9 | import org.eclipse.ui.plugin.AbstractUIPlugin; 10 | import org.osgi.framework.BundleContext; 11 | import org.wangzw.plugin.cppstyle.ui.CppStyleConsolePatternMatchListener; 12 | import org.wangzw.plugin.cppstyle.ui.CppStyleConstants; 13 | import org.wangzw.plugin.cppstyle.ui.CppStyleMessageConsole; 14 | 15 | /** 16 | * The activator class controls the plug-in life cycle 17 | */ 18 | public class CppStyle extends AbstractUIPlugin { 19 | // The plug-in ID 20 | public static final String PLUGIN_ID = "cppstyle"; //$NON-NLS-1$ 21 | 22 | // The shared instance 23 | private static CppStyle plugin; 24 | 25 | /** 26 | * The constructor 27 | */ 28 | public CppStyle() { 29 | } 30 | 31 | public void start(BundleContext context) throws Exception { 32 | super.start(context); 33 | plugin = this; 34 | } 35 | 36 | public void stop(BundleContext context) throws Exception { 37 | plugin = null; 38 | super.stop(context); 39 | } 40 | 41 | public static String getCpplintPath() { 42 | return plugin.getPreferenceStore().getString(CppStyleConstants.CPPLINT_PATH); 43 | } 44 | 45 | /** 46 | * Returns the shared instance 47 | * 48 | * @return the shared instance 49 | */ 50 | public static CppStyle getDefault() { 51 | return plugin; 52 | } 53 | 54 | /** 55 | * Returns an image descriptor for the image file at the given plug-in 56 | * relative path 57 | * 58 | * @param path 59 | * the path 60 | * @return the image descriptor 61 | */ 62 | public static ImageDescriptor getImageDescriptor(String path) { 63 | return imageDescriptorFromPlugin(PLUGIN_ID, path); 64 | } 65 | 66 | /** 67 | * Logs the specified status with this plug-in's log. 68 | * 69 | * @param status 70 | * status to log 71 | */ 72 | public static void log(IStatus status) { 73 | getDefault().getLog().log(status); 74 | } 75 | 76 | /** 77 | * Logs an internal error with the specified {@code Throwable}. 78 | * 79 | * @param t 80 | * the {@code Throwable} to be logged 81 | */ 82 | public static void log(Throwable t) { 83 | log(new Status(IStatus.ERROR, PLUGIN_ID, 1, "Internal Error", t)); //$NON-NLS-1$ 84 | } 85 | 86 | /** 87 | * Logs an internal error with the specified message. 88 | * 89 | * @param message 90 | * the error message to log 91 | */ 92 | public static void log(String message) { 93 | log(new Status(IStatus.ERROR, PLUGIN_ID, 1, message, null)); 94 | } 95 | 96 | /** 97 | * Logs an internal error with the specified message and {@code Throwable}. 98 | * 99 | * @param message 100 | * the error message to log 101 | * @param t 102 | * the {@code Throwable} to be logged 103 | * 104 | */ 105 | public static void log(String message, Throwable t) { 106 | log(new Status(IStatus.ERROR, PLUGIN_ID, 1, message, t)); 107 | } 108 | 109 | public static CppStyleMessageConsole buildConsole() { 110 | CppStyleMessageConsole console = null; 111 | ConsolePlugin plugin = ConsolePlugin.getDefault(); 112 | IConsoleManager conMan = plugin.getConsoleManager(); 113 | IConsole[] existing = conMan.getConsoles(); 114 | 115 | for (int i = 0; i < existing.length; i++) { 116 | if (CppStyleConstants.CONSOLE_NAME.equals(existing[i].getName())) { 117 | console = (CppStyleMessageConsole) existing[i]; 118 | } 119 | } 120 | 121 | if (console == null) { 122 | // no console found, so create a new one 123 | CppStyleConsolePatternMatchListener listener = new CppStyleConsolePatternMatchListener(); 124 | console = new CppStyleMessageConsole(listener); 125 | conMan.addConsoles(new IConsole[] { console }); 126 | } 127 | 128 | console.clear(); 129 | return console; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/CpplintCheckSettings.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.eclipse.core.resources.IFile; 8 | import org.eclipse.core.resources.IProject; 9 | import org.eclipse.core.resources.IResource; 10 | import org.eclipse.core.runtime.CoreException; 11 | import org.eclipse.core.runtime.Path; 12 | import org.eclipse.core.runtime.QualifiedName; 13 | import org.wangzw.plugin.cppstyle.ui.CppStyleConstants; 14 | 15 | /** 16 | * User-configurable external tool settings. 17 | */ 18 | public final class CpplintCheckSettings { 19 | private File path; 20 | private String args; 21 | private final String externalToolName; 22 | 23 | /** 24 | * Constructor. 25 | * 26 | * @param externalToolName 27 | */ 28 | public CpplintCheckSettings() { 29 | this.externalToolName = "cpplint"; 30 | this.path = null; 31 | this.args = null; 32 | } 33 | 34 | /** 35 | * Returns the name of the external tool, to be displayed to the user. 36 | * 37 | * @return the name of the external tool, to be displayed to the user. 38 | */ 39 | public String getExternalToolName() { 40 | return externalToolName; 41 | } 42 | 43 | /** 44 | * Returns the setting that specifies the path and name of the external tool 45 | * to invoke. 46 | * 47 | * @return the setting that specifies the path and name of the external tool 48 | * to invoke. 49 | */ 50 | public File getPath() { 51 | return path; 52 | } 53 | 54 | /** 55 | * Returns the setting that specifies the arguments to pass when invoking 56 | * the external tool. 57 | * 58 | * @return the setting that specifies the arguments to pass when invoking 59 | * the external tool. 60 | */ 61 | public String getArgs() { 62 | return args; 63 | } 64 | 65 | /** 66 | * Updates the values of the configuration settings value with the ones 67 | * stored in the given preference map. 68 | * 69 | * @param preferences 70 | * the given preference map that may contain the values to set. 71 | * @throws ClassCastException 72 | * if any of the values to set is not of the same type as the 73 | * one supported by a setting. 74 | */ 75 | public void updateValuesFrom(IFile file) { 76 | path = new File(CppStyle.getCpplintPath()); 77 | args = prepareParameters(file); 78 | } 79 | 80 | private String prepareParameters(IFile file) { 81 | String root = getCpplintRoot(file); 82 | 83 | List commands = new ArrayList(); 84 | 85 | if (root != null && !root.isEmpty()) { 86 | commands.add("--root=" + root); 87 | } 88 | 89 | StringBuffer sb = new StringBuffer(); 90 | 91 | for (String arg : commands) { 92 | sb.append(arg); 93 | sb.append(' '); 94 | } 95 | 96 | return sb.toString(); 97 | } 98 | 99 | static String getVersionControlRoot(IFile file) { 100 | File current = file.getLocation().toFile(); 101 | 102 | File dir = current.getParentFile(); 103 | 104 | return getVersionControlRoot(dir); 105 | } 106 | 107 | public static String getVersionControlRoot(File dir) { 108 | 109 | while (dir != null) { 110 | for (final File entry : dir.listFiles()) { 111 | String name = entry.getName(); 112 | if (name.equals(".git") || name.equals(".hg") || name.equals(".svn")) { 113 | return dir.getPath(); 114 | } 115 | } 116 | 117 | dir = dir.getParentFile(); 118 | } 119 | 120 | return null; 121 | } 122 | 123 | public static String getCpplintRoot(IFile file) { 124 | IProject project = file.getProject(); 125 | String rootSpec; 126 | try { 127 | rootSpec = project.getPersistentProperty(new QualifiedName("", CppStyleConstants.CPPLINT_PROJECT_ROOT)); 128 | 129 | if (rootSpec == null || rootSpec.isEmpty()) { 130 | return null; 131 | } 132 | } catch (CoreException e) { 133 | return null; 134 | } 135 | 136 | String rootVc = getVersionControlRoot(file); 137 | 138 | if (rootVc == null) { 139 | return null; 140 | } 141 | 142 | String relative = new File(rootVc).toURI().relativize(new File(rootSpec).toURI()).getPath(); 143 | 144 | if (relative.endsWith("" + Path.SEPARATOR)) { 145 | return relative.substring(0, relative.length() - 1); 146 | } 147 | 148 | return relative; 149 | } 150 | 151 | private static boolean enableCpplint(IResource resource) { 152 | boolean enable = CppStyle.getDefault().getPreferenceStore() 153 | .getBoolean(CppStyleConstants.ENABLE_CPPLINT_ON_SAVE); 154 | 155 | try { 156 | IProject project = resource.getProject(); 157 | String enableProjectSpecific = project 158 | .getPersistentProperty(new QualifiedName("", CppStyleConstants.PROJECTS_PECIFIC_PROPERTY)); 159 | 160 | if (enableProjectSpecific != null && Boolean.parseBoolean(enableProjectSpecific)) { 161 | String value = project 162 | .getPersistentProperty(new QualifiedName("", CppStyleConstants.ENABLE_CPPLINT_PROPERTY)); 163 | if (value != null) { 164 | return Boolean.parseBoolean(value); 165 | } 166 | 167 | return false; 168 | } 169 | } catch (CoreException e) { 170 | e.printStackTrace(); 171 | } 172 | 173 | return enable; 174 | } 175 | 176 | public static boolean checkCpplint(IResource resource) { 177 | if (!enableCpplint(resource)) { 178 | return false; 179 | } 180 | 181 | String cpplint = CppStyle.getCpplintPath(); 182 | 183 | if (cpplint == null) { 184 | CppStyle.log("cpplint.py is not specified."); 185 | return false; 186 | } 187 | 188 | File file = new File(cpplint); 189 | 190 | if (!file.exists()) { 191 | CppStyle.log("cpplint.py (" + cpplint + ") does not exist."); 192 | return false; 193 | } 194 | 195 | if (!file.canExecute()) { 196 | CppStyle.log("cpplint.py (" + cpplint + ") is not executable."); 197 | return false; 198 | } 199 | 200 | return true; 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/CpplintChecker.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import java.net.URI; 4 | 5 | import org.eclipse.cdt.codan.core.CodanRuntime; 6 | import org.eclipse.cdt.codan.core.cxx.externaltool.ArgsSeparator; 7 | import org.eclipse.cdt.codan.core.cxx.externaltool.InvocationFailure; 8 | import org.eclipse.cdt.codan.core.model.AbstractCheckerWithProblemPreferences; 9 | import org.eclipse.cdt.codan.core.model.CheckerLaunchMode; 10 | import org.eclipse.cdt.codan.core.model.IProblem; 11 | import org.eclipse.cdt.codan.core.model.IProblemLocation; 12 | import org.eclipse.cdt.codan.core.model.IProblemLocationFactory; 13 | import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; 14 | import org.eclipse.cdt.codan.core.param.FileScopeProblemPreference; 15 | import org.eclipse.cdt.codan.core.param.MapProblemPreference; 16 | import org.eclipse.cdt.codan.core.param.RootProblemPreference; 17 | import org.eclipse.cdt.codan.core.param.SharedRootProblemPreference; 18 | import org.eclipse.cdt.core.ErrorParserManager; 19 | import org.eclipse.cdt.core.IConsoleParser; 20 | import org.eclipse.cdt.core.IMarkerGenerator; 21 | import org.eclipse.cdt.core.ProblemMarkerInfo; 22 | import org.eclipse.core.filesystem.URIUtil; 23 | import org.eclipse.core.resources.IFile; 24 | import org.eclipse.core.resources.IProject; 25 | import org.eclipse.core.resources.IResource; 26 | import org.eclipse.core.runtime.Path; 27 | import org.wangzw.plugin.cppstyle.ui.CppStyleConstants; 28 | import org.wangzw.plugin.cppstyle.ui.CppStyleMessageConsole; 29 | 30 | public class CpplintChecker extends AbstractCheckerWithProblemPreferences implements IMarkerGenerator { 31 | private final ArgsSeparator argsSeparator; 32 | private final CpplintCheckSettings settings; 33 | private final CpplintInvoker invoker; 34 | private final RootProblemPreference preferences; 35 | 36 | /** 37 | * Constructor. 38 | */ 39 | public CpplintChecker() { 40 | this.argsSeparator = new ArgsSeparator(); 41 | this.settings = new CpplintCheckSettings(); 42 | this.invoker = new CpplintInvoker(); 43 | this.preferences = new SharedRootProblemPreference(); 44 | } 45 | 46 | /** 47 | * Returns {@code false} because this checker cannot run "as you type" by 48 | * default. 49 | * 50 | * @return {@code false}. 51 | */ 52 | @Override 53 | public boolean runInEditor() { 54 | return false; 55 | } 56 | 57 | @Override 58 | public boolean processResource(IResource resource) { 59 | if (!shouldProduceProblems(resource)) { 60 | return false; 61 | } 62 | 63 | if (!CpplintCheckSettings.checkCpplint(resource)) { 64 | return false; 65 | } 66 | 67 | process(resource); 68 | return false; 69 | } 70 | 71 | private void process(IResource resource) { 72 | try { 73 | CppStyleMessageConsole console = CppStyle.buildConsole(); 74 | console.getListener().setFile((IFile) resource); 75 | String path = resource.getLocation().toOSString(); 76 | InvocationParameters parameters = new InvocationParameters(resource, resource, path, null, console); 77 | if (parameters != null) { 78 | invokeExternalTool(parameters); 79 | } 80 | } catch (Throwable error) { 81 | logResourceProcessingFailure(error, resource); 82 | } 83 | } 84 | 85 | private void invokeExternalTool(InvocationParameters parameters) throws Throwable { 86 | updateConfigurationSettingsFromPreferences(parameters.getActualFile()); 87 | IConsoleParser[] parsers = new IConsoleParser[] { createErrorParserManager(parameters) }; 88 | try { 89 | invoker.invoke(parameters, settings, argsSeparator, parsers); 90 | } catch (InvocationFailure error) { 91 | handleInvocationFailure(error, parameters); 92 | } 93 | } 94 | 95 | private void updateConfigurationSettingsFromPreferences(IResource fileToProcess) { 96 | settings.updateValuesFrom((IFile) fileToProcess); 97 | } 98 | 99 | private ErrorParserManager createErrorParserManager(InvocationParameters parameters) { 100 | IProject project = parameters.getActualFile().getProject(); 101 | URI workingDirectory = URIUtil.toURI(parameters.getWorkingDirectory()); 102 | return new ErrorParserManager(project, workingDirectory, this, getParserIDs()); 103 | } 104 | 105 | /** 106 | * @return the IDs of the parsers to use to parse the output of the external 107 | * tool. 108 | */ 109 | protected String[] getParserIDs() { 110 | return new String[] { CppStyleConstants.CPPLINT_OUTPUT_PARSER_ID }; 111 | } 112 | 113 | /** 114 | * Handles a failure reported when invoking the external tool. This 115 | * implementation simply logs the failure. 116 | * 117 | * @param error 118 | * the reported failure. 119 | * @param parameters 120 | * the parameters passed to the external tool executable. 121 | */ 122 | protected void handleInvocationFailure(InvocationFailure error, InvocationParameters parameters) { 123 | logResourceProcessingFailure(error, parameters.getActualFile()); 124 | } 125 | 126 | private void logResourceProcessingFailure(Throwable error, IResource resource) { 127 | String location = resource.getLocation().toOSString(); 128 | String msg = String.format("Unable to process resource %s", location); //$NON-NLS-1$ 129 | CppStyle.log(msg, error); 130 | } 131 | 132 | /** 133 | * Returns the id of the problem used as reference to obtain this checker's 134 | * preferences. All preferences in a external-tool-based checker are shared 135 | * among its defined problems. 136 | * 137 | * @return the id of the problem used as reference to obtain this checker's 138 | * preferences. 139 | */ 140 | protected String getReferenceProblemId() { 141 | return CppStyleConstants.CPPLINT_ERROR_PROBLEM_ID; 142 | } 143 | 144 | @Override 145 | public void initPreferences(IProblemWorkingCopy problem) { 146 | getTopLevelPreference(problem); // initialize 147 | 148 | FileScopeProblemPreference scope = getScopePreference(problem); 149 | Path[] value = new Path[6]; 150 | value[0] = new Path("*.cc"); 151 | value[1] = new Path("*.h"); 152 | value[2] = new Path("*.cpp"); 153 | value[3] = new Path("*.cu"); 154 | value[4] = new Path("*.cuh"); 155 | value[5] = new Path("*.hpp"); 156 | scope.setAttribute(FileScopeProblemPreference.INCLUSION, value); 157 | 158 | getLaunchModePreference(problem).enableInLaunchModes(CheckerLaunchMode.RUN_ON_FILE_SAVE, 159 | CheckerLaunchMode.RUN_ON_DEMAND); 160 | } 161 | 162 | @Override 163 | protected void setDefaultPreferenceValue(IProblemWorkingCopy problem, String key, Object defaultValue) { 164 | MapProblemPreference map = getTopLevelPreference(problem); 165 | map.setChildValue(key, defaultValue); 166 | } 167 | 168 | @Override 169 | public RootProblemPreference getTopLevelPreference(IProblem problem) { 170 | RootProblemPreference map = (RootProblemPreference) problem.getPreference(); 171 | if (map == null) { 172 | map = preferences; 173 | if (problem instanceof IProblemWorkingCopy) { 174 | ((IProblemWorkingCopy) problem).setPreference(map); 175 | } 176 | } 177 | return map; 178 | } 179 | 180 | @Deprecated 181 | @Override 182 | public void addMarker(IResource file, int lineNumber, String description, int severity, String variableName) { 183 | addMarker(new ProblemMarkerInfo(file, lineNumber, description, severity, variableName)); 184 | } 185 | 186 | @Override 187 | public void addMarker(ProblemMarkerInfo info) { 188 | String problemId = info.getAttribute(CppStyleConstants.CPPLINT_PROBLEM_ID_KEY); 189 | reportProblem(problemId, createProblemLocation(info), info.description); 190 | } 191 | 192 | protected IProblemLocation createProblemLocation(ProblemMarkerInfo info) { 193 | IProblemLocationFactory factory = CodanRuntime.getInstance().getProblemLocationFactory(); 194 | return factory.createProblemLocation((IFile) info.file, info.startChar, info.endChar, info.lineNumber); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/CpplintErrorParser.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | import org.eclipse.cdt.core.ErrorParserManager; 7 | import org.eclipse.cdt.core.IErrorParser; 8 | import org.eclipse.cdt.core.ProblemMarkerInfo; 9 | import org.eclipse.core.resources.IFile; 10 | import org.eclipse.core.resources.IMarker; 11 | import org.wangzw.plugin.cppstyle.ui.CppStyleConstants; 12 | 13 | public class CpplintErrorParser implements IErrorParser { 14 | private static String PROBLEM_PREFIX = "org.wangzw.plugin.cppstyle.cpplint."; 15 | 16 | private static Pattern pattern = Pattern.compile(CppStyleConstants.CPPLINT_OUTPUT_PATTERN); 17 | 18 | private int findSeverityCode(String text) { 19 | return IMarker.SEVERITY_ERROR; 20 | } 21 | 22 | private String findProblemId(String category, String subcategory) { 23 | String id = PROBLEM_PREFIX + category + "." + subcategory; 24 | return id; 25 | } 26 | 27 | @Override 28 | public boolean processLine(String line, ErrorParserManager parserManager) { 29 | Matcher matcher = pattern.matcher(line); 30 | 31 | if (!matcher.matches()) { 32 | return false; 33 | } 34 | 35 | IFile fileName = parserManager.findFileName(matcher.group(CppStyleConstants.CPPLINT_OUTPUT_PATTERN_PATH_GROUP)); 36 | 37 | if (fileName != null) { 38 | int lineNumber = Integer.parseInt(matcher.group(CppStyleConstants.CPPLINT_OUTPUT_PATTERN_LINE_NO_GROUP)); 39 | lineNumber = lineNumber > 0 ? lineNumber : 1; 40 | String description = matcher.group(CppStyleConstants.CPPLINT_OUTPUT_PATTERN_MSG_GROUP); 41 | int severity = findSeverityCode(matcher.group(CppStyleConstants.CPPLINT_OUTPUT_PATTERN_SEVERITY_GROUP)); 42 | ProblemMarkerInfo info = new ProblemMarkerInfo(fileName, lineNumber, description, severity, null); 43 | String category = matcher.group(CppStyleConstants.CPPLINT_OUTPUT_PATTERN_CATEGORY_GROUP); 44 | String subcate = matcher.group(CppStyleConstants.CPPLINT_OUTPUT_PATTERN_CATEGORY_SUBGROUP); 45 | String problem = findProblemId(category, subcate); 46 | int retry = 1; 47 | 48 | do { 49 | try { 50 | if (problem != null) { 51 | info.setAttribute(CppStyleConstants.CPPLINT_PROBLEM_ID_KEY, problem); 52 | parserManager.addProblemMarker(info); 53 | return true; 54 | } 55 | 56 | return false; 57 | } catch (IllegalArgumentException e) { 58 | CppStyle.log("Unexpected cpplint problem: " + category + "/" + subcate, e); 59 | problem = CppStyleConstants.CPPLINT_ERROR_PROBLEM_ID; 60 | } 61 | } while (retry-- > 0); 62 | } 63 | 64 | return false; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/CpplintInvoker.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | import org.eclipse.cdt.codan.core.cxx.externaltool.ArgsSeparator; 7 | import org.eclipse.cdt.codan.core.cxx.externaltool.InvocationFailure; 8 | import org.eclipse.cdt.core.CommandLauncher; 9 | import org.eclipse.cdt.core.ICommandLauncher; 10 | import org.eclipse.cdt.core.IConsoleParser; 11 | import org.eclipse.core.resources.IProject; 12 | import org.eclipse.core.runtime.CoreException; 13 | import org.eclipse.core.runtime.IPath; 14 | import org.eclipse.core.runtime.IProgressMonitor; 15 | import org.eclipse.core.runtime.NullProgressMonitor; 16 | import org.eclipse.core.runtime.SubProgressMonitor; 17 | import org.wangzw.plugin.cppstyle.ui.CppStyleMessageConsole; 18 | 19 | /** 20 | * Invokes an external tool to perform checks on a single file. 21 | */ 22 | public class CpplintInvoker { 23 | private static final NullProgressMonitor NULL_PROGRESS_MONITOR = new NullProgressMonitor(); 24 | private final CommandBuilder commandBuilder = new CommandBuilder(); 25 | 26 | /** 27 | * Invokes an external tool. 28 | * 29 | * @param parameters 30 | * the parameters to pass to the external tool executable. 31 | * @param settings 32 | * user-configurable settings. 33 | * @param argsSeparator 34 | * separates the arguments to pass to the external tool 35 | * executable. These arguments are stored in a single 36 | * {@code String}. 37 | * @param parsers 38 | * parse the output of the external tool. 39 | * @throws InvocationFailure 40 | * if the external tool could not be invoked or if the external 41 | * tool itself reports that it cannot be executed (e.g. due to a 42 | * configuration error). 43 | * @throws Throwable 44 | * if something else goes wrong. 45 | */ 46 | public void invoke(InvocationParameters parameters, CpplintCheckSettings settings, ArgsSeparator argsSeparator, 47 | IConsoleParser[] parsers) throws InvocationFailure, Throwable { 48 | Command command = commandBuilder.buildCommand(parameters, settings, argsSeparator); 49 | launchCommand(command, parsers, parameters, settings); 50 | } 51 | 52 | private void launchCommand(Command command, IConsoleParser[] parsers, InvocationParameters parameters, 53 | CpplintCheckSettings settings) throws InvocationFailure, CoreException { 54 | IProject project = parameters.getActualFile().getProject(); 55 | final String toolName = settings.getExternalToolName(); 56 | final IPath workingDirectory = parameters.getWorkingDirectory(); 57 | CppStyleMessageConsole console = parameters.getConsole(); 58 | final IPath commandPath = command.getPath(); 59 | final String[] commandArgs = command.getArgs(); 60 | final String[] commandEnv = command.getEnv(); 61 | launchOnBuildConsole(project, console, parsers, toolName, commandPath, commandArgs, commandEnv, 62 | workingDirectory, NULL_PROGRESS_MONITOR); 63 | } 64 | 65 | public void launchOnBuildConsole(IProject project, CppStyleMessageConsole console, IConsoleParser[] parsers, 66 | final String toolName, final IPath commandPath, final String[] commandArgs, final String[] commandEnv, 67 | final IPath workingDirectory, final IProgressMonitor monitor) throws CoreException, InvocationFailure { 68 | monitor.beginTask("Launching " + toolName, 100); 69 | ConsoleOutputSniffer sniffer = new ConsoleOutputSniffer(console.getOutputStream(), console.getErrorStream(), 70 | parsers); 71 | final OutputStream out = sniffer.getOutputStream(); 72 | final OutputStream err = sniffer.getErrorStream(); 73 | 74 | console.getOutputStream().println("Launching " + toolName); 75 | 76 | try { 77 | ICommandLauncher launcher = new CommandLauncher(); 78 | launcher.showCommand(true); 79 | launcher.setProject(project); 80 | Process p = launcher.execute(commandPath, commandArgs, commandEnv, workingDirectory, 81 | new SubProgressMonitor(monitor, 50)); 82 | if (p == null) { 83 | String format = "Unable to launch external tool '%s': %s"; //$NON-NLS-1$ 84 | throw new InvocationFailure(String.format(format, commandPath, launcher.getErrorMessage())); 85 | } 86 | try { 87 | // this is process input stream which we don't need 88 | p.getOutputStream().close(); 89 | } catch (Throwable ignored) { 90 | // ignore 91 | } 92 | try { 93 | launcher.waitAndRead(out, err, new SubProgressMonitor(monitor, 50)); 94 | } finally { 95 | p.destroy(); 96 | } 97 | 98 | } finally { 99 | // closing sniffer's streams will shut down the parsers as well 100 | try { 101 | out.close(); 102 | } catch (IOException e) { 103 | // ignore 104 | } 105 | try { 106 | err.close(); 107 | } catch (IOException e) { 108 | // ignore 109 | } 110 | monitor.done(); 111 | 112 | console.getOutputStream().println(""); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/InvocationParameters.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle; 2 | 3 | import org.eclipse.core.resources.IResource; 4 | import org.eclipse.core.runtime.IPath; 5 | import org.wangzw.plugin.cppstyle.ui.CppStyleMessageConsole; 6 | 7 | /** 8 | * Parameters to pass when invoking an external tool. 9 | * 10 | * @since 2.1 11 | */ 12 | public final class InvocationParameters { 13 | private final IResource originalFile; 14 | private final IResource actualFile; 15 | private final String actualFilePath; 16 | private final IPath workingDirectory; 17 | private final CppStyleMessageConsole console; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param originalFile 23 | * the original file to process. 24 | * @param actualFile 25 | * the actual file to process. 26 | * @param actualFilePath 27 | * the path of {@code actual}, in a format that the external tool 28 | * can understand. 29 | * @param workingDirectory 30 | * the directory where the external tool should be executed. 31 | * @see #getOriginalFile() 32 | * @see #getActualFile() 33 | */ 34 | public InvocationParameters(IResource originalFile, IResource actualFile, String actualFilePath, 35 | IPath workingDirectory, CppStyleMessageConsole console) { 36 | this.originalFile = originalFile; 37 | this.actualFile = actualFile; 38 | this.actualFilePath = actualFilePath; 39 | this.workingDirectory = workingDirectory; 40 | this.console = console; 41 | 42 | } 43 | 44 | /** 45 | * Returns the original file to process. This is the file that triggered 46 | * execution of a command-line tool when saved. 47 | * 48 | * @return the original file to process. 49 | */ 50 | public IResource getOriginalFile() { 51 | return originalFile; 52 | } 53 | 54 | /** 55 | * Returns the actual file to process. It may not be the same as 56 | * {@link #getOriginalFile()}, depending on how the external 57 | * tool works. 58 | *

59 | * A good example is an external tool that can only process C++ source files 60 | * but not header files. If the original file is a header file, the 61 | * checker could potentially find a C++ file that includes such header and 62 | * use it as the actual file to process. 63 | *

64 | *

65 | * We still need to keep a reference to the actual file, in order 66 | * to add markers to the editor in case of problems found. 67 | *

68 | * 69 | * @return the actual file to process. 70 | */ 71 | public IResource getActualFile() { 72 | return actualFile; 73 | } 74 | 75 | /** 76 | * Returns the path of {@link #getActualFile()}, in a format 77 | * the external tool can understand. 78 | * 79 | * @return the path of the actual file to process. 80 | */ 81 | public String getActualFilePath() { 82 | return actualFilePath; 83 | } 84 | 85 | /** 86 | * Returns the directory where the external tool should be executed. 87 | * 88 | * @return the directory where the external tool should be executed. 89 | */ 90 | public IPath getWorkingDirectory() { 91 | return workingDirectory; 92 | } 93 | 94 | public CppStyleMessageConsole getConsole() { 95 | return console; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/CppStyleConsolePage.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | import org.eclipse.debug.internal.ui.views.console.ShowStandardErrorAction; 4 | import org.eclipse.debug.internal.ui.views.console.ShowStandardOutAction; 5 | import org.eclipse.debug.internal.ui.views.console.ShowWhenContentChangesAction; 6 | import org.eclipse.jface.action.IMenuManager; 7 | import org.eclipse.jface.action.IToolBarManager; 8 | import org.eclipse.swt.widgets.Composite; 9 | import org.eclipse.ui.actions.ActionFactory; 10 | import org.eclipse.ui.console.IConsoleConstants; 11 | import org.eclipse.ui.console.IConsoleView; 12 | import org.eclipse.ui.console.TextConsolePage; 13 | import org.eclipse.ui.console.TextConsoleViewer; 14 | 15 | public class CppStyleConsolePage extends TextConsolePage { 16 | private CppStyleConsoleViewer viewer = null; 17 | private CppStyleMessageConsole console = null; 18 | private ShowWhenContentChangesAction fStdOut; 19 | private ShowWhenContentChangesAction fStdErr; 20 | 21 | public CppStyleConsolePage(CppStyleMessageConsole console, IConsoleView view) { 22 | super(console, view); 23 | this.console = console; 24 | } 25 | 26 | @Override 27 | public void dispose() { 28 | super.dispose(); 29 | 30 | if (fStdOut != null) { 31 | fStdOut.dispose(); 32 | fStdOut = null; 33 | } 34 | if (fStdErr != null) { 35 | fStdErr.dispose(); 36 | fStdErr = null; 37 | } 38 | } 39 | 40 | @Override 41 | protected TextConsoleViewer createViewer(Composite parent) { 42 | viewer = new CppStyleConsoleViewer(parent, console); 43 | return viewer; 44 | } 45 | 46 | @Override 47 | protected void contextMenuAboutToShow(IMenuManager menuManager) { 48 | super.contextMenuAboutToShow(menuManager); 49 | menuManager.remove(ActionFactory.CUT.getId()); 50 | menuManager.remove(ActionFactory.PASTE.getId()); 51 | } 52 | 53 | @Override 54 | protected void configureToolBar(IToolBarManager mgr) { 55 | super.configureToolBar(mgr); 56 | fStdOut = new ShowStandardOutAction(); 57 | fStdErr = new ShowStandardErrorAction(); 58 | mgr.appendToGroup(IConsoleConstants.OUTPUT_GROUP, fStdOut); 59 | mgr.appendToGroup(IConsoleConstants.OUTPUT_GROUP, fStdErr); 60 | } 61 | 62 | public boolean activeOnStdout() { 63 | if (fStdOut == null || !fStdOut.isChecked()) { 64 | return false; 65 | } 66 | 67 | return true; 68 | } 69 | 70 | public boolean activeOnStderr() { 71 | if (fStdErr == null || !fStdErr.isChecked()) { 72 | return false; 73 | } 74 | 75 | return true; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/CppStyleConsolePatternMatchListener.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | import org.eclipse.core.resources.IFile; 7 | import org.eclipse.debug.ui.console.FileLink; 8 | import org.eclipse.jface.text.BadLocationException; 9 | import org.eclipse.ui.console.IPatternMatchListener; 10 | import org.eclipse.ui.console.PatternMatchEvent; 11 | import org.eclipse.ui.console.TextConsole; 12 | import org.wangzw.plugin.cppstyle.CppStyle; 13 | 14 | public class CppStyleConsolePatternMatchListener implements IPatternMatchListener { 15 | 16 | private IFile file = null; 17 | private Pattern pattern = null; 18 | private int lineNumGroup = CppStyleConstants.CPPLINT_OUTPUT_PATTERN_LINE_NO_GROUP; 19 | private String patternMsg = CppStyleConstants.CPPLINT_OUTPUT_PATTERN; 20 | 21 | public IFile getFile() { 22 | return file; 23 | } 24 | 25 | public void setFile(IFile file) { 26 | this.file = file; 27 | } 28 | 29 | @Override 30 | public void connect(TextConsole console) { 31 | pattern = Pattern.compile(patternMsg); 32 | } 33 | 34 | @Override 35 | public void disconnect() { 36 | } 37 | 38 | @Override 39 | public String getPattern() { 40 | return patternMsg; 41 | } 42 | 43 | @Override 44 | public int getCompilerFlags() { 45 | return 0; 46 | } 47 | 48 | @Override 49 | public String getLineQualifier() { 50 | return "\\n|\\r"; 51 | } 52 | 53 | @Override 54 | public void matchFound(PatternMatchEvent event) { 55 | try { 56 | CppStyleMessageConsole console = (CppStyleMessageConsole) event.getSource(); 57 | 58 | String line = console.getDocument().get(event.getOffset(), event.getLength()); 59 | 60 | Matcher m = pattern.matcher(line); 61 | if (m.matches()) { 62 | String ln = m.group(lineNumGroup); 63 | 64 | int lineno = Integer.parseInt(ln); 65 | 66 | FileLink link = new FileLink(file, null, -1, -1, lineno == 0 ? 1 : lineno); 67 | console.addFileLink(link, event.getOffset(), event.getLength()); 68 | } 69 | } catch (BadLocationException e) { 70 | CppStyle.log("Failed to add link", e); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/CppStyleConsoleViewer.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | import org.eclipse.debug.ui.console.FileLink; 4 | import org.eclipse.jface.text.BadPositionCategoryException; 5 | import org.eclipse.jface.text.DocumentEvent; 6 | import org.eclipse.jface.text.IDocumentListener; 7 | import org.eclipse.jface.text.Position; 8 | import org.eclipse.swt.custom.StyledText; 9 | import org.eclipse.swt.events.MouseEvent; 10 | import org.eclipse.swt.graphics.Point; 11 | import org.eclipse.swt.widgets.Composite; 12 | import org.eclipse.swt.widgets.Control; 13 | import org.eclipse.ui.console.ConsolePlugin; 14 | import org.eclipse.ui.console.TextConsoleViewer; 15 | import org.wangzw.plugin.cppstyle.CppStyle; 16 | 17 | public class CppStyleConsoleViewer extends TextConsoleViewer { 18 | private CppStyleMessageConsole console = null; 19 | 20 | private IDocumentListener documentListener = new IDocumentListener() { 21 | @Override 22 | public void documentAboutToBeChanged(DocumentEvent event) { 23 | } 24 | 25 | @Override 26 | public void documentChanged(DocumentEvent event) { 27 | clearFileLink(); 28 | } 29 | }; 30 | 31 | public CppStyleConsoleViewer(Composite parent, CppStyleMessageConsole console) { 32 | super(parent, console); 33 | this.console = console; 34 | this.getDocument().addDocumentListener(documentListener); 35 | } 36 | 37 | @Override 38 | protected void createControl(Composite parent, int styles) { 39 | super.createControl(parent, styles); 40 | setReadOnly(); 41 | Control control = getTextWidget(); 42 | control.addMouseListener(this); 43 | } 44 | 45 | @Override 46 | public void mouseDoubleClick(MouseEvent e) { 47 | StyledText widget = getTextWidget(); 48 | if (widget != null) { 49 | int offset = -1; 50 | try { 51 | Point p = new Point(e.x, e.y); 52 | offset = widget.getOffsetAtLocation(p); 53 | FileLink link = getFileLink(offset); 54 | 55 | if (link != null) { 56 | link.linkActivated(); 57 | } 58 | 59 | } catch (IllegalArgumentException ex) { 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | protected void handleDispose() { 66 | Control control = getTextWidget(); 67 | 68 | if (control != null) { 69 | control.removeMouseListener(this); 70 | } 71 | 72 | this.getDocument().removeDocumentListener(documentListener); 73 | super.handleDispose(); 74 | } 75 | 76 | /** 77 | * makes the associated text widget uneditable. 78 | */ 79 | public void setReadOnly() { 80 | ConsolePlugin.getStandardDisplay().asyncExec(new Runnable() { 81 | @Override 82 | public void run() { 83 | StyledText text = getTextWidget(); 84 | if (text != null && !text.isDisposed()) { 85 | text.setEditable(false); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | public FileLink getFileLink(int offset) { 92 | if (offset >= 0 && console != null) { 93 | return console.getFileLink(offset); 94 | } 95 | return null; 96 | } 97 | 98 | public void clearFileLink() { 99 | try { 100 | Position[] positions = getDocument().getPositions(CppStyleMessageConsole.ERROR_MARKER_CATEGORY); 101 | 102 | for (Position position : positions) { 103 | getDocument().removePosition(CppStyleMessageConsole.ERROR_MARKER_CATEGORY, position); 104 | } 105 | } catch (BadPositionCategoryException e) { 106 | CppStyle.log(e); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/CppStyleConstants.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | /** 4 | * Constant definitions for plug-in 5 | */ 6 | public class CppStyleConstants { 7 | public static final String PerfPageId = "org.wangzw.plugin.cppstyle.ui.CppStylePerfPage"; 8 | 9 | public static final String CLANG_FORMAT_PATH = "cppstyle.clangformat.path"; 10 | public static final String CPPLINT_PATH = "cppstyle.cpplint.path"; 11 | public static final String ENABLE_CPPLINT_ON_SAVE = "cppstyle.enable.cpplint.on.save"; 12 | public static final String ENABLE_CLANGFORMAT_ON_SAVE = "cppstyle.enable.clangformat.on.save"; 13 | 14 | public static final String ENABLE_CPPLINT_TEXT = "Enable cpplint"; 15 | public static final String ENABLE_CLANGFORMAT_TEXT = "Run clang-format on file save"; 16 | public static final String PROJECT_ROOT_TEXT = "Select root path for cpplint (optional):"; 17 | 18 | public static final String PROJECTS_PECIFIC_PROPERTY = "cppstyle.ENABLE_PROJECTS_PECIFIC"; 19 | public static final String ENABLE_CPPLINT_PROPERTY = "cppstyle.ENABLE_CPPLINT"; 20 | public static final String ENABLE_CLANGFORMAT_PROPERTY = "cppstyle.ENABLE_CLANGFORMAT"; 21 | public static final String CPPLINT_PROJECT_ROOT = "cppstyle.PROJECT_ROOT"; 22 | 23 | public static final String CPPLINT_MARKER = "org.wangzw.plugin.cppstyle.CpplintMarker"; 24 | public static final String CONSOLE_NAME = "CppStyle Output"; 25 | public static final String CPPLINT_CONSOLE_PREFIX = "cpplint.py: "; 26 | public static final String CPPLINT_OUTPUT_PATTERN = "(.+)\\:(\\d+)\\:(.+)\\[(.+)/(.+)\\](.*)\\[(\\d)\\]"; 27 | public static final int CPPLINT_OUTPUT_PATTERN_PATH_GROUP = 1; 28 | public static final int CPPLINT_OUTPUT_PATTERN_LINE_NO_GROUP = 2; 29 | public static final int CPPLINT_OUTPUT_PATTERN_MSG_GROUP = 3; 30 | public static final int CPPLINT_OUTPUT_PATTERN_CATEGORY_GROUP = 4; 31 | public static final int CPPLINT_OUTPUT_PATTERN_CATEGORY_SUBGROUP = 5; 32 | public static final int CPPLINT_OUTPUT_PATTERN_SEVERITY_GROUP = 7; 33 | 34 | public static final String CPPLINT_OUTPUT_PARSER_ID = "org.wangzw.plugin.cppstyle.CpplintErrorParser"; 35 | public static final String CPPLINT_PROBLEM_ID_KEY = "id"; 36 | public static final String CPPLINT_ERROR_PROBLEM_ID = "org.wangzw.plugin.cppstyle.cpplint.build.categorized"; 37 | } 38 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/CppStyleHandler.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | import org.eclipse.cdt.ui.ICEditor; 4 | import org.eclipse.core.commands.ExecutionEvent; 5 | import org.eclipse.core.commands.ExecutionException; 6 | import org.eclipse.core.expressions.EvaluationResult; 7 | import org.eclipse.core.expressions.IEvaluationContext; 8 | import org.eclipse.core.resources.IFile; 9 | import org.eclipse.ui.IFileEditorInput; 10 | import org.eclipse.ui.ISaveablePart; 11 | import org.eclipse.ui.ISaveablesSource; 12 | import org.eclipse.ui.IWorkbenchPage; 13 | import org.eclipse.ui.IWorkbenchPart; 14 | import org.eclipse.ui.IWorkbenchWindow; 15 | import org.eclipse.ui.handlers.HandlerUtil; 16 | import org.eclipse.ui.internal.InternalHandlerUtil; 17 | import org.eclipse.ui.internal.SaveableHelper; 18 | import org.eclipse.ui.internal.WorkbenchPage; 19 | import org.eclipse.ui.internal.handlers.AbstractSaveHandler; 20 | import org.wangzw.plugin.cppstyle.ClangFormatFormatter; 21 | 22 | /** 23 | * Our sample handler extends AbstractHandler, an IHandler base class. 24 | * 25 | * @see org.eclipse.core.commands.IHandler 26 | * @see org.eclipse.core.commands.AbstractHandler 27 | */ 28 | public class CppStyleHandler extends AbstractSaveHandler { 29 | /** 30 | * The constructor. 31 | */ 32 | public CppStyleHandler() { 33 | registerEnablement(); 34 | } 35 | 36 | protected ICEditor getSaveableEditor(ExecutionEvent event) { 37 | 38 | IWorkbenchPart activePart = HandlerUtil.getActivePart(event); 39 | 40 | if (activePart instanceof ICEditor) { 41 | return (ICEditor) activePart; 42 | } 43 | 44 | return null; 45 | } 46 | 47 | /** 48 | * the command has been executed, so extract extract the needed information 49 | * from the application context. 50 | */ 51 | @Override 52 | public Object execute(ExecutionEvent event) throws ExecutionException { 53 | ICEditor editor = getSaveableEditor(event); 54 | 55 | if (editor == null) { 56 | return null; 57 | } 58 | 59 | if (!editor.isDirty()) { 60 | return null; 61 | } 62 | 63 | IFile file = ((IFileEditorInput) editor.getEditorInput()).getFile(); 64 | 65 | ClangFormatFormatter formater = new ClangFormatFormatter(); 66 | 67 | if (formater.runClangFormatOnSave(file)) { 68 | formater.formatAndApply(editor); 69 | } 70 | 71 | IWorkbenchPage page = editor.getSite().getPage(); 72 | page.saveEditor(editor, false); 73 | 74 | return null; 75 | } 76 | 77 | @Override 78 | protected EvaluationResult evaluate(IEvaluationContext context) { 79 | 80 | IWorkbenchWindow window = InternalHandlerUtil.getActiveWorkbenchWindow(context); 81 | // no window? not active 82 | if (window == null) 83 | return EvaluationResult.FALSE; 84 | WorkbenchPage page = (WorkbenchPage) window.getActivePage(); 85 | 86 | // no page? not active 87 | if (page == null) 88 | return EvaluationResult.FALSE; 89 | 90 | // get saveable part 91 | ISaveablePart saveablePart = getSaveablePart(context); 92 | if (saveablePart == null) 93 | return EvaluationResult.FALSE; 94 | 95 | if (saveablePart instanceof ISaveablesSource) { 96 | ISaveablesSource modelSource = (ISaveablesSource) saveablePart; 97 | if (SaveableHelper.needsSave(modelSource)) 98 | return EvaluationResult.TRUE; 99 | return EvaluationResult.FALSE; 100 | } 101 | 102 | if (saveablePart != null && saveablePart.isDirty()) 103 | return EvaluationResult.TRUE; 104 | 105 | return EvaluationResult.FALSE; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/CppStyleMessageConsole.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | import org.eclipse.debug.ui.console.FileLink; 4 | import org.eclipse.jface.text.BadLocationException; 5 | import org.eclipse.jface.text.BadPositionCategoryException; 6 | import org.eclipse.jface.text.IDocument; 7 | import org.eclipse.jface.text.Position; 8 | import org.eclipse.swt.graphics.Color; 9 | import org.eclipse.swt.graphics.RGB; 10 | import org.eclipse.swt.widgets.Display; 11 | import org.eclipse.ui.console.ConsolePlugin; 12 | import org.eclipse.ui.console.IConsoleManager; 13 | import org.eclipse.ui.console.IConsoleView; 14 | import org.eclipse.ui.console.MessageConsole; 15 | import org.eclipse.ui.console.MessageConsoleStream; 16 | import org.eclipse.ui.part.IPageBookViewPage; 17 | import org.wangzw.plugin.cppstyle.CppStyle; 18 | 19 | public class CppStyleMessageConsole extends MessageConsole { 20 | private CppStyleConsolePage page = null; 21 | private MessageConsoleStream out = new MessageConsoleStream(this); 22 | private MessageConsoleStream err = new MessageConsoleStream(this); 23 | 24 | private static class MarkerPosition extends Position { 25 | private FileLink link = null; 26 | 27 | public MarkerPosition(FileLink link, int offset, int length) { 28 | super(offset, length); 29 | this.link = link; 30 | } 31 | 32 | public FileLink getFileLink() { 33 | return link; 34 | } 35 | } 36 | 37 | public static final String ERROR_MARKER_CATEGORY = "org.wangzw.cppstyle.ERROR_MARKER_POSITION";; 38 | private CppStyleConsolePatternMatchListener listener = null; 39 | 40 | public CppStyleMessageConsole(CppStyleConsolePatternMatchListener listener) { 41 | super(CppStyleConstants.CONSOLE_NAME, null); 42 | this.getDocument().addPositionCategory(ERROR_MARKER_CATEGORY); 43 | this.listener = listener; 44 | addPatternMatchListener(listener); 45 | 46 | runUI(new Runnable() { 47 | 48 | @Override 49 | public void run() { 50 | err.setColor(new Color(getStandardDisplay(), new RGB(255, 0, 0))); 51 | }; 52 | }); 53 | } 54 | 55 | @Override 56 | public IPageBookViewPage createPage(IConsoleView view) { 57 | page = new CppStyleConsolePage(this, view); 58 | return page; 59 | } 60 | 61 | public CppStyleConsolePatternMatchListener getListener() { 62 | return listener; 63 | } 64 | 65 | public void setListener(CppStyleConsolePatternMatchListener listener) { 66 | removePatternMatchListener(this.listener); 67 | addPatternMatchListener(listener); 68 | this.listener = listener; 69 | } 70 | 71 | public CppStyleConsolePage getActivePage() { 72 | return page; 73 | } 74 | 75 | public FileLink getFileLink(int offset) { 76 | try { 77 | IDocument document = getDocument(); 78 | if (document != null) { 79 | Position[] positions = document.getPositions(ERROR_MARKER_CATEGORY); 80 | Position position = findPosition(offset, positions); 81 | if (position instanceof MarkerPosition) { 82 | return ((MarkerPosition) position).getFileLink(); 83 | } 84 | } 85 | } catch (BadPositionCategoryException e) { 86 | } 87 | return null; 88 | } 89 | 90 | public void addFileLink(FileLink link, int offset, int length) { 91 | IDocument document = getDocument(); 92 | MarkerPosition linkPosition = new MarkerPosition(link, offset, length); 93 | try { 94 | document.addPosition(ERROR_MARKER_CATEGORY, linkPosition); 95 | IConsoleManager fConsoleManager = ConsolePlugin.getDefault().getConsoleManager(); 96 | fConsoleManager.refresh(this); 97 | } catch (BadPositionCategoryException e) { 98 | CppStyle.log(e); 99 | } catch (BadLocationException e) { 100 | CppStyle.log(e); 101 | } 102 | } 103 | 104 | /** 105 | * Binary search for the position at a given offset. 106 | * 107 | * @param offset 108 | * the offset whose position should be found 109 | * @return the position containing the offset, or null 110 | */ 111 | private Position findPosition(int offset, Position[] positions) { 112 | 113 | if (positions.length == 0) { 114 | return null; 115 | } 116 | 117 | int left = 0; 118 | int right = positions.length - 1; 119 | int mid = 0; 120 | Position position = null; 121 | 122 | while (left < right) { 123 | 124 | mid = (left + right) / 2; 125 | 126 | position = positions[mid]; 127 | if (offset < position.getOffset()) { 128 | if (left == mid) { 129 | right = left; 130 | } else { 131 | right = mid - 1; 132 | } 133 | } else if (offset > (position.getOffset() + position.getLength() - 1)) { 134 | if (right == mid) { 135 | left = right; 136 | } else { 137 | left = mid + 1; 138 | } 139 | } else { 140 | left = right = mid; 141 | } 142 | } 143 | 144 | position = positions[left]; 145 | if (offset >= position.getOffset() && (offset < (position.getOffset() + position.getLength()))) { 146 | return position; 147 | } 148 | return null; 149 | } 150 | 151 | public MessageConsoleStream getOutputStream() { 152 | out.setActivateOnWrite(page != null ? page.activeOnStdout() : true); 153 | return out; 154 | } 155 | 156 | public MessageConsoleStream getErrorStream() { 157 | err.setActivateOnWrite(page != null ? page.activeOnStderr() : true); 158 | return err; 159 | } 160 | 161 | public static Display getStandardDisplay() { 162 | Display display = Display.getCurrent(); 163 | if (display == null) { 164 | display = Display.getDefault(); 165 | } 166 | return display; 167 | } 168 | 169 | private void runUI(Runnable run) { 170 | Display display; 171 | display = Display.getCurrent(); 172 | if (display == null) { 173 | display = Display.getDefault(); 174 | display.syncExec(run); 175 | } else { 176 | run.run(); 177 | } 178 | } 179 | 180 | public void clear() { 181 | runUI(new Runnable() { 182 | 183 | @Override 184 | public void run() { 185 | IDocument document = getDocument(); 186 | document.set(""); 187 | }; 188 | }); 189 | 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/CppStylePerfPage.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | import java.io.File; 4 | 5 | import org.eclipse.jface.preference.BooleanFieldEditor; 6 | import org.eclipse.jface.preference.FieldEditor; 7 | import org.eclipse.jface.preference.FieldEditorPreferencePage; 8 | import org.eclipse.jface.preference.FileFieldEditor; 9 | import org.eclipse.jface.util.PropertyChangeEvent; 10 | import org.eclipse.ui.IWorkbench; 11 | import org.eclipse.ui.IWorkbenchPreferencePage; 12 | import org.wangzw.plugin.cppstyle.ClangFormatFormatter; 13 | import org.wangzw.plugin.cppstyle.CppStyle; 14 | 15 | /** 16 | * This class represents a preference page that is contributed to the 17 | * Preferences dialog. By subclassing FieldEditorPreferencePage, we 18 | * can use the field support built into JFace that allows us to create a page 19 | * that is small and knows how to save, restore and apply itself. 20 | *

21 | * This page is used to modify preferences only. They are stored in the 22 | * preference store that belongs to the main plug-in class. That way, 23 | * preferences can be accessed directly via the preference store. 24 | */ 25 | 26 | public class CppStylePerfPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { 27 | private FileFieldEditor clangFormatPath = null; 28 | private FileFieldEditor cpplintPath = null; 29 | private BooleanFieldEditor enableCpplintOnSave = null; 30 | private BooleanFieldEditor enableClangFormatOnSave = null; 31 | 32 | public CppStylePerfPage() { 33 | super(GRID); 34 | setPreferenceStore(CppStyle.getDefault().getPreferenceStore()); 35 | } 36 | 37 | /** 38 | * Creates the field editors. Field editors are abstractions of the common 39 | * GUI blocks needed to manipulate various types of preferences. Each field 40 | * editor knows how to save and restore itself. 41 | */ 42 | public void createFieldEditors() { 43 | clangFormatPath = new FileFieldEditor(CppStyleConstants.CLANG_FORMAT_PATH, "Clang-format path:", 44 | getFieldEditorParent()); 45 | 46 | addField(clangFormatPath); 47 | 48 | cpplintPath = new FileFieldEditor(CppStyleConstants.CPPLINT_PATH, "Cpplint path:", getFieldEditorParent()); 49 | 50 | addField(cpplintPath); 51 | 52 | enableCpplintOnSave = new BooleanFieldEditor(CppStyleConstants.ENABLE_CPPLINT_ON_SAVE, 53 | CppStyleConstants.ENABLE_CPPLINT_TEXT, getFieldEditorParent()); 54 | 55 | if (!checkPathExist(CppStyle.getCpplintPath())) { 56 | enableCpplintOnSave.setEnabled(false, getFieldEditorParent()); 57 | } 58 | 59 | addField(enableCpplintOnSave); 60 | 61 | enableClangFormatOnSave = new BooleanFieldEditor(CppStyleConstants.ENABLE_CLANGFORMAT_ON_SAVE, 62 | CppStyleConstants.ENABLE_CLANGFORMAT_TEXT, getFieldEditorParent()); 63 | 64 | if (!checkPathExist(ClangFormatFormatter.getClangFormatPath())) { 65 | enableClangFormatOnSave.setEnabled(false, getFieldEditorParent()); 66 | } 67 | 68 | addField(enableClangFormatOnSave); 69 | } 70 | 71 | public void init(IWorkbench workbench) { 72 | } 73 | 74 | @Override 75 | public void propertyChange(PropertyChangeEvent event) { 76 | super.propertyChange(event); 77 | 78 | if (event.getProperty().equals(FieldEditor.VALUE)) { 79 | if (event.getSource() == clangFormatPath) { 80 | clangFormatPathChange(event.getNewValue().toString()); 81 | } else if (event.getSource() == cpplintPath) { 82 | cpplintPathChange(event.getNewValue().toString()); 83 | } 84 | 85 | checkState(); 86 | } 87 | } 88 | 89 | private boolean checkPathExist(String path) { 90 | File file = new File(path); 91 | return file.exists() && !file.isDirectory(); 92 | } 93 | 94 | private void clangFormatPathChange(String newPath) { 95 | if (!checkPathExist(newPath)) { 96 | enableClangFormatOnSave.setEnabled(false, getFieldEditorParent()); 97 | this.setValid(false); 98 | this.setErrorMessage("Clang-format path \"" + newPath + "\" does not exist"); 99 | } else { 100 | enableClangFormatOnSave.setEnabled(true, getFieldEditorParent()); 101 | this.setValid(true); 102 | this.setErrorMessage(null); 103 | } 104 | } 105 | 106 | private void cpplintPathChange(String newPath) { 107 | if (!checkPathExist(newPath)) { 108 | enableCpplintOnSave.setEnabled(false, getFieldEditorParent()); 109 | this.setValid(false); 110 | this.setErrorMessage("Cpplint path \"" + newPath + "\" does not exist"); 111 | } else { 112 | enableCpplintOnSave.setEnabled(true, getFieldEditorParent()); 113 | this.setValid(true); 114 | this.setErrorMessage(null); 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/CppStylePropertyPage.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | import java.io.File; 4 | 5 | import org.eclipse.core.resources.IProject; 6 | import org.eclipse.core.resources.IResource; 7 | import org.eclipse.core.runtime.CoreException; 8 | import org.eclipse.core.runtime.QualifiedName; 9 | import org.eclipse.jface.preference.DirectoryFieldEditor; 10 | import org.eclipse.jface.preference.FieldEditor; 11 | import org.eclipse.jface.preference.IPreferenceNode; 12 | import org.eclipse.jface.preference.IPreferencePage; 13 | import org.eclipse.jface.preference.PreferenceDialog; 14 | import org.eclipse.jface.preference.PreferenceManager; 15 | import org.eclipse.jface.preference.PreferenceNode; 16 | import org.eclipse.jface.preference.PreferencePage; 17 | import org.eclipse.jface.util.IPropertyChangeListener; 18 | import org.eclipse.jface.util.PropertyChangeEvent; 19 | import org.eclipse.jface.viewers.ISelection; 20 | import org.eclipse.jface.viewers.IStructuredSelection; 21 | import org.eclipse.swt.SWT; 22 | import org.eclipse.swt.custom.BusyIndicator; 23 | import org.eclipse.swt.events.ModifyEvent; 24 | import org.eclipse.swt.events.ModifyListener; 25 | import org.eclipse.swt.events.SelectionAdapter; 26 | import org.eclipse.swt.events.SelectionEvent; 27 | import org.eclipse.swt.events.SelectionListener; 28 | import org.eclipse.swt.layout.GridData; 29 | import org.eclipse.swt.layout.GridLayout; 30 | import org.eclipse.swt.widgets.Button; 31 | import org.eclipse.swt.widgets.Composite; 32 | import org.eclipse.swt.widgets.Control; 33 | import org.eclipse.swt.widgets.Label; 34 | import org.eclipse.swt.widgets.Text; 35 | import org.eclipse.ui.ISelectionService; 36 | import org.eclipse.ui.dialogs.PropertyPage; 37 | import org.eclipse.ui.internal.Workbench; 38 | import org.wangzw.plugin.cppstyle.CpplintCheckSettings; 39 | 40 | import org.eclipse.cdt.core.model.ICElement; 41 | 42 | public class CppStylePropertyPage extends PropertyPage implements 43 | SelectionListener, IPropertyChangeListener, ModifyListener { 44 | 45 | private static final String PROJECTS_PECIFIC_TEXT = "Enable project specific settings"; 46 | 47 | private Button projectSpecificButton; 48 | private Button enableCpplintOnSaveButton; 49 | private Button enableClangFormatOnSaveButton; 50 | private DirectoryFieldEditor projectRoot; 51 | private Text projectRootText = null; 52 | String projectPath = null; 53 | 54 | /** 55 | * Constructor for SamplePropertyPage. 56 | */ 57 | public CppStylePropertyPage() { 58 | super(); 59 | } 60 | 61 | private void constructPage(Composite parent) { 62 | Composite composite = createComposite(parent, 2); 63 | 64 | projectSpecificButton = new Button(composite, SWT.CHECK); 65 | projectSpecificButton.setText(PROJECTS_PECIFIC_TEXT); 66 | projectSpecificButton.addSelectionListener(this); 67 | 68 | Button perfSetting = new Button(composite, SWT.PUSH); 69 | perfSetting.setText("Configure Workspace Settings..."); 70 | perfSetting.addSelectionListener(new SelectionAdapter() { 71 | public void widgetSelected(SelectionEvent e) { 72 | configureWorkspaceSettings(); 73 | } 74 | }); 75 | 76 | createSepeerater(parent); 77 | 78 | composite = createComposite(parent, 1); 79 | 80 | enableCpplintOnSaveButton = new Button(composite, SWT.CHECK); 81 | enableCpplintOnSaveButton 82 | .setText(CppStyleConstants.ENABLE_CPPLINT_TEXT); 83 | enableCpplintOnSaveButton.addSelectionListener(this); 84 | 85 | enableClangFormatOnSaveButton = new Button(composite, SWT.CHECK); 86 | enableClangFormatOnSaveButton 87 | .setText(CppStyleConstants.ENABLE_CLANGFORMAT_TEXT); 88 | enableClangFormatOnSaveButton.addSelectionListener(this); 89 | 90 | createSepeerater(parent); 91 | 92 | composite = createComposite(parent, 1); 93 | 94 | Label laber = new Label(composite, SWT.NONE); 95 | laber.setText(CppStyleConstants.PROJECT_ROOT_TEXT); 96 | 97 | composite = createComposite(composite, 1); 98 | 99 | projectPath = getCurrentProject(); 100 | 101 | projectRoot = new DirectoryFieldEditor(CppStyleConstants.CPPLINT_PATH, 102 | "Root:", composite) { 103 | String errorMsg = super.getErrorMessage(); 104 | 105 | @Override 106 | protected boolean doCheckState() { 107 | this.setErrorMessage(errorMsg); 108 | 109 | String fileName = getTextControl().getText(); 110 | fileName = fileName.trim(); 111 | if (fileName.length() == 0 && isEmptyStringAllowed()) { 112 | return true; 113 | } 114 | 115 | File file = new File(fileName); 116 | if (false == file.isDirectory()) { 117 | return false; 118 | } 119 | 120 | this.setErrorMessage("Directory or its up level directories should contain .git, .hg, or .svn."); 121 | 122 | String path = CpplintCheckSettings.getVersionControlRoot(file); 123 | 124 | if (path == null) { 125 | return false; 126 | } 127 | 128 | if (!path.startsWith(projectPath)) { 129 | this.setErrorMessage("Should be a subdirectory of project's root."); 130 | return false; 131 | } 132 | 133 | return true; 134 | } 135 | 136 | }; 137 | 138 | projectRoot.setPage(this); 139 | projectRoot.setFilterPath(new File(projectPath)); 140 | projectRoot.setPropertyChangeListener(this); 141 | projectRootText = projectRoot.getTextControl(composite); 142 | projectRootText.addModifyListener(this); 143 | projectRoot.setEnabled(true, composite); 144 | 145 | if (!getPropertyValue(CppStyleConstants.PROJECTS_PECIFIC_PROPERTY)) { 146 | projectSpecificButton.setSelection(false); 147 | enableCpplintOnSaveButton.setEnabled(false); 148 | enableClangFormatOnSaveButton.setEnabled(false); 149 | } else { 150 | projectSpecificButton.setSelection(true); 151 | enableCpplintOnSaveButton.setEnabled(true); 152 | enableCpplintOnSaveButton 153 | .setSelection(getPropertyValue(CppStyleConstants.ENABLE_CPPLINT_PROPERTY)); 154 | enableClangFormatOnSaveButton.setEnabled(true); 155 | enableClangFormatOnSaveButton 156 | .setSelection(getPropertyValue(CppStyleConstants.ENABLE_CLANGFORMAT_PROPERTY)); 157 | } 158 | 159 | String root = getPropertyValueString(CppStyleConstants.CPPLINT_PROJECT_ROOT); 160 | projectRoot.setStringValue(root); 161 | } 162 | 163 | public static String getCurrentProject() { 164 | ISelectionService selectionService = Workbench.getInstance() 165 | .getActiveWorkbenchWindow().getSelectionService(); 166 | 167 | ISelection selection = selectionService.getSelection(); 168 | 169 | if (selection instanceof IStructuredSelection) { 170 | Object element = ((IStructuredSelection) selection).getFirstElement(); 171 | IProject project = null; 172 | if (element instanceof IResource) { 173 | project = ((IResource) element).getProject(); 174 | } 175 | else if (element instanceof ICElement) { 176 | project = ((ICElement) element).getResource().getProject(); 177 | } 178 | if (project != null) { 179 | return project.getLocation().toOSString(); 180 | } 181 | } 182 | return null; 183 | } 184 | 185 | /** 186 | * @see PreferencePage#createContents(Composite) 187 | */ 188 | protected Control createContents(Composite parent) { 189 | Composite composite = new Composite(parent, SWT.NONE); 190 | GridLayout layout = new GridLayout(); 191 | composite.setLayout(layout); 192 | GridData data = new GridData(SWT.FILL); 193 | data.grabExcessHorizontalSpace = true; 194 | composite.setLayoutData(data); 195 | constructPage(composite); 196 | return composite; 197 | } 198 | 199 | private Composite createComposite(Composite parent, int ncols) { 200 | Composite composite = new Composite(parent, SWT.NULL); 201 | GridLayout layout = new GridLayout(); 202 | layout.numColumns = ncols; 203 | composite.setLayout(layout); 204 | 205 | GridData data = new GridData(SWT.FILL); 206 | data.verticalAlignment = SWT.FILL; 207 | data.horizontalAlignment = SWT.FILL; 208 | data.grabExcessHorizontalSpace = true; 209 | composite.setLayoutData(data); 210 | 211 | return composite; 212 | } 213 | 214 | private void createSepeerater(Composite parent) { 215 | Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); 216 | GridData gridData = new GridData(); 217 | gridData.horizontalAlignment = SWT.FILL; 218 | gridData.grabExcessHorizontalSpace = true; 219 | separator.setLayoutData(gridData); 220 | } 221 | 222 | protected void performDefaults() { 223 | super.performDefaults(); 224 | projectSpecificButton.setSelection(false); 225 | enableCpplintOnSaveButton.setSelection(false); 226 | enableCpplintOnSaveButton.setEnabled(false); 227 | enableClangFormatOnSaveButton.setSelection(false); 228 | enableClangFormatOnSaveButton.setEnabled(false); 229 | projectRoot.setStringValue(""); 230 | } 231 | 232 | public boolean performOk() { 233 | try { 234 | ((IResource) getElement()).setPersistentProperty(new QualifiedName( 235 | "", CppStyleConstants.PROJECTS_PECIFIC_PROPERTY), 236 | new Boolean(projectSpecificButton.getSelection()) 237 | .toString()); 238 | 239 | ((IResource) getElement()).setPersistentProperty(new QualifiedName( 240 | "", CppStyleConstants.ENABLE_CPPLINT_PROPERTY), 241 | new Boolean(enableCpplintOnSaveButton.getSelection()) 242 | .toString()); 243 | 244 | ((IResource) getElement()).setPersistentProperty(new QualifiedName( 245 | "", CppStyleConstants.ENABLE_CLANGFORMAT_PROPERTY), 246 | new Boolean(enableClangFormatOnSaveButton.getSelection()) 247 | .toString()); 248 | 249 | ((IResource) getElement()).setPersistentProperty(new QualifiedName( 250 | "", CppStyleConstants.CPPLINT_PROJECT_ROOT), projectRoot 251 | .getStringValue()); 252 | } catch (CoreException e) { 253 | return false; 254 | } 255 | return true; 256 | } 257 | 258 | public String getPropertyValueString(String key) { 259 | String value; 260 | try { 261 | value = ((IResource) getElement()) 262 | .getPersistentProperty(new QualifiedName("", key)); 263 | if (value == null) { 264 | return ""; 265 | } 266 | 267 | } catch (CoreException e) { 268 | return ""; 269 | } 270 | 271 | return value; 272 | } 273 | 274 | public boolean getPropertyValue(String key) { 275 | return Boolean.parseBoolean(getPropertyValueString(key)); 276 | } 277 | 278 | /** 279 | * Creates a new preferences page and opens it 280 | */ 281 | public void configureWorkspaceSettings() { 282 | // create a new instance of the current class 283 | IPreferencePage page = new CppStylePerfPage(); 284 | page.setTitle(getTitle()); 285 | // and show it 286 | showPreferencePage(CppStyleConstants.PerfPageId, page); 287 | } 288 | 289 | /** 290 | * Show a single preference pages 291 | * 292 | * @param id 293 | * - the preference page identification 294 | * @param page 295 | * - the preference page 296 | */ 297 | protected void showPreferencePage(String id, IPreferencePage page) { 298 | final IPreferenceNode targetNode = new PreferenceNode(id, page); 299 | PreferenceManager manager = new PreferenceManager(); 300 | manager.addToRoot(targetNode); 301 | final PreferenceDialog dialog = new PreferenceDialog(getControl() 302 | .getShell(), manager); 303 | BusyIndicator.showWhile(getControl().getDisplay(), new Runnable() { 304 | public void run() { 305 | dialog.create(); 306 | dialog.setMessage(targetNode.getLabelText()); 307 | dialog.open(); 308 | } 309 | }); 310 | } 311 | 312 | @Override 313 | public void widgetSelected(SelectionEvent e) { 314 | if (e.getSource() == projectSpecificButton) { 315 | enableCpplintOnSaveButton.setEnabled(projectSpecificButton 316 | .getSelection()); 317 | enableClangFormatOnSaveButton.setEnabled(projectSpecificButton 318 | .getSelection()); 319 | } 320 | } 321 | 322 | @Override 323 | public void widgetDefaultSelected(SelectionEvent e) { 324 | widgetSelected(e); 325 | } 326 | 327 | @Override 328 | public void propertyChange(PropertyChangeEvent event) { 329 | if (event.getProperty().equals(FieldEditor.VALUE)) { 330 | if (event.getSource() == projectRoot) { 331 | setValid(projectRoot.isValid()); 332 | } 333 | } 334 | } 335 | 336 | @Override 337 | public void modifyText(ModifyEvent e) { 338 | if (e.getSource() == projectRootText) { 339 | if (projectRootText.getText().isEmpty()) { 340 | setValid(true); 341 | } 342 | } 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /plugin/src/org/wangzw/plugin/cppstyle/ui/PreferenceInitializer.java: -------------------------------------------------------------------------------- 1 | package org.wangzw.plugin.cppstyle.ui; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | 7 | import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; 8 | import org.eclipse.jface.preference.IPreferenceStore; 9 | import org.wangzw.plugin.cppstyle.CppStyle; 10 | 11 | /** 12 | * Class used to initialize default preference values. 13 | */ 14 | public class PreferenceInitializer extends AbstractPreferenceInitializer { 15 | 16 | public void initializeDefaultPreferences() { 17 | IPreferenceStore store = CppStyle.getDefault().getPreferenceStore(); 18 | store.setDefault(CppStyleConstants.CLANG_FORMAT_PATH, 19 | findBinaryPath("clang-format")); 20 | store.setDefault(CppStyleConstants.CPPLINT_PATH, 21 | findBinaryPath("cpplint.py")); 22 | store.setDefault(CppStyleConstants.ENABLE_CPPLINT_ON_SAVE, false); 23 | store.setDefault(CppStyleConstants.ENABLE_CLANGFORMAT_ON_SAVE, false); 24 | } 25 | 26 | private String findBinaryPath(String bin) { 27 | try { 28 | Process process = Runtime.getRuntime().exec("which " + bin); 29 | 30 | if (process.waitFor() == 0) { 31 | BufferedReader reader = new BufferedReader( 32 | new InputStreamReader(process.getInputStream())); 33 | return reader.readLine(); 34 | } 35 | 36 | return ""; 37 | } catch (IOException e) { 38 | } catch (InterruptedException e) { 39 | } 40 | 41 | return ""; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | cppstyle-group 7 | cppstyle 8 | 1.5.0.0 9 | pom 10 | 11 | plugin 12 | feature 13 | update 14 | 15 | 16 | 17 | 4.0.1 18 | 4.0.1 19 | 20 | 21 | 22 | 23 | eclipse 24 | p2 25 | http://download.eclipse.org/releases/2023-06 26 | 27 | 28 | cdt 29 | p2 30 | http://download.eclipse.org/tools/cdt/releases/11.2 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.eclipse.tycho 38 | tycho-maven-plugin 39 | ${tycho.version} 40 | true 41 | 42 | 43 | 44 | org.eclipse.tycho 45 | target-platform-configuration 46 | ${targetplatform.version} 47 | 48 | p2 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /update/.gitignore: -------------------------------------------------------------------------------- 1 | *.jar 2 | -------------------------------------------------------------------------------- /update/category.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /update/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cppstyle-group 7 | cppstyle 8 | 1.5.0.0 9 | 10 | cppstyle-group 11 | update 12 | eclipse-repository 13 | 14 | 15 | 16 | 17 | org.eclipse.tycho 18 | tycho-packaging-plugin 19 | ${tycho.version} 20 | 21 | true 22 | 23 | 24 | 25 | 26 | 27 | 28 | --------------------------------------------------------------------------------