├── .gitattributes ├── .gitignore ├── .mvn ├── extensions.xml └── maven.config ├── .travis.yml ├── LICENSE.txt ├── README.md ├── maven-repo └── .gitkeep ├── nb-configuration.xml ├── pom.xml └── src ├── main ├── java │ ├── com │ │ └── welovecoding │ │ │ └── nbeditorconfig │ │ │ ├── config │ │ │ ├── LoggerSettings.java │ │ │ └── Settings.java │ │ │ ├── editor │ │ │ ├── api │ │ │ │ └── lexer │ │ │ │ │ └── ECTokenId.java │ │ │ ├── csl │ │ │ │ ├── EditorConfigElementHandle.java │ │ │ │ ├── EditorConfigLanguage.java │ │ │ │ ├── EditorConfigPropertyStructureItem.java │ │ │ │ ├── EditorConfigSectionStructureItem.java │ │ │ │ └── EditorConfigStructureScanner.java │ │ │ ├── lexer │ │ │ │ ├── AntlrCharStream.java │ │ │ │ └── ECLexer.java │ │ │ └── parser │ │ │ │ ├── ECParser.java │ │ │ │ ├── ECParserResult.java │ │ │ │ ├── SyntaxErrorsTask.java │ │ │ │ └── SyntaxErrorsTaskFactory.java │ │ │ ├── filetype │ │ │ ├── EditorConfigDataObject.java │ │ │ └── FilenameResolver.java │ │ │ ├── io │ │ │ ├── exception │ │ │ │ ├── FileAccessException.java │ │ │ │ └── FileObjectLockException.java │ │ │ ├── model │ │ │ │ ├── FirstLineInfo.java │ │ │ │ ├── MappedCharset.java │ │ │ │ └── SupportedCharsets.java │ │ │ ├── reader │ │ │ │ ├── FileInfoReader.java │ │ │ │ ├── FileObjectReader.java │ │ │ │ └── StyledDocumentReader.java │ │ │ └── writer │ │ │ │ ├── ConfigWriter.java │ │ │ │ ├── FileObjectWriter.java │ │ │ │ └── StyledDocumentWriter.java │ │ │ ├── listener │ │ │ ├── EditorConfigChangeListener.java │ │ │ ├── FileChangeListener.java │ │ │ ├── Installer.java │ │ │ ├── ListenerAttacher.java │ │ │ ├── ProjectChangeListener.java │ │ │ ├── ProjectHookLookup.java │ │ │ └── ProjectOpenCloseListener.java │ │ │ ├── mapper │ │ │ ├── EditorConfigPropertyMapper.java │ │ │ └── EditorConfigPropertyMappingException.java │ │ │ ├── model │ │ │ ├── EditorConfigConstant.java │ │ │ └── MappedEditorConfig.java │ │ │ ├── options │ │ │ ├── EditorConfigOptions.java │ │ │ ├── EditorConfigOptionsPanel.form │ │ │ ├── EditorConfigOptionsPanel.java │ │ │ └── EditorConfigOptionsPanelController.java │ │ │ └── processor │ │ │ ├── EditorConfigProcessor.java │ │ │ ├── FileInfo.java │ │ │ ├── SmartSkip.java │ │ │ ├── WriteEditorAction.java │ │ │ ├── WriteFileAction.java │ │ │ ├── WriteStringToFileAction.java │ │ │ └── operation │ │ │ ├── CodeStyleOperation.java │ │ │ ├── FinalNewLineOperation.java │ │ │ ├── IndentSizeOperation.java │ │ │ ├── IndentStyleOperation.java │ │ │ ├── LineEndingOperation.java │ │ │ ├── TabWidthOperation.java │ │ │ ├── TrimTrailingWhiteSpaceOperation.java │ │ │ └── tobedone │ │ │ └── CharsetOperation.java │ └── org │ │ ├── antlr4 │ │ ├── EditorConfigErrorListener.java │ │ ├── EditorConfigLexer.java │ │ ├── EditorConfigParser.java │ │ ├── EditorConfigParserBaseListener.java │ │ ├── EditorConfigParserBaseVisitor.java │ │ ├── EditorConfigParserListener.java │ │ ├── EditorConfigParserVisitor.java │ │ └── SyntaxError.java │ │ └── editorconfig │ │ └── core │ │ ├── EditorConfig.java │ │ ├── EditorConfigException.java │ │ ├── ParsingException.java │ │ ├── VersionException.java │ │ └── package-info.java ├── nbm │ └── manifest.mf └── resources │ └── com │ └── welovecoding │ └── nbeditorconfig │ ├── Bundle.properties │ ├── EditorConfigExample.editorconfig │ ├── FontAndColors.xml │ ├── editorconfig-code-templates.xml │ ├── filetype │ ├── description.html │ └── ec.png │ ├── layer.xml │ ├── listener │ └── Bundle.properties │ ├── options │ └── Bundle.properties │ └── section16.png └── test ├── java └── com │ └── welovecoding │ └── nbeditorconfig │ ├── io │ └── reader │ │ └── FileInfoReaderTest.java │ ├── mapper │ └── EditorConfigPropertyMapperTest.java │ └── processor │ ├── EditorConfigProcessorTest.java │ └── operation │ ├── FinalNewLineOperationTest.java │ ├── IndentSizeOperationTest.java │ ├── LineEndingOperationTest.java │ └── TrimTrailingWhiteSpaceOperationTest.java └── resources └── files ├── IndentSize.html ├── charsets ├── latin1.txt ├── utf-16-be.txt ├── utf-16-le.txt ├── utf-8-bom.txt └── utf-8.txt ├── configs └── editorconfig-test.ini └── line-endings ├── utf8_final_newline_1_character.txt ├── utf8_final_newline_n.txt ├── utf8_final_newline_rn.txt ├── utf8_no_final_newline.txt └── utf8_no_final_newline_1_character.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Reference: https://help.github.com/articles/dealing-with-line-endings/ 2 | 3 | # enforce LF line endings 4 | * text eol=lf 5 | 6 | # Denote all files that are truly binary and should not be modified. 7 | *.eot binary 8 | *.jar binary 9 | *.jpg binary 10 | *.otf binary 11 | *.png binary 12 | *.svg binary 13 | *.ttf binary 14 | *.woff binary 15 | *.woff2 binary 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | .DS_Store 3 | nb-configuration.xml 4 | release.properties 5 | nbactions.xml -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | io.takari.maven 5 | takari-smart-builder 6 | 0.6.1 7 | 8 | 9 | io.takari.aether 10 | takari-local-repository 11 | 0.11.2 12 | 13 | 14 | io.takari.aether 15 | aether-connector-okhttp 16 | 0.17.4 17 | 18 | 19 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | --fail-fast --strict-checksums 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # http://docs.travis-ci.com/user/workers/container-based-infrastructure/ 2 | sudo: false 3 | 4 | # http://docs.travis-ci.com/user/languages/java/ 5 | language: java 6 | 7 | jdk: 8 | - openjdk7 9 | - openjdk8 10 | - openjdk9 11 | - openjdk10 12 | - openjdk11 13 | - oraclejdk8 14 | - oraclejdk9 15 | - oraclejdk11 16 | 17 | #Cache downloaded Maven artifacts between builds 18 | cache: 19 | directories: 20 | - "${HOME}/.m2" 21 | 22 | notifications: 23 | email: false 24 | slack: wlc:Kinf3gdySndcFTBtjcci3rJN 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Benny Neugebauer, Michael Koppen, Junichi Yamamoto 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ⚠️ **This repository is deprecated and unmaintained.** Please follow the "[First class EditorConfig support](https://issues.apache.org/jira/browse/NETBEANS-1287)" thread on the Apache's JIRA issue tracker to be informed about updates. 2 | 3 | # EditorConfig NetBeans Plugin 4 | 5 | A NetBeans IDE plugin supporting the [EditorConfig][editorconfig] standard. 6 | 7 | - Supports NetBeans 8 and above 8 | - Requires Java 7+ 9 | 10 | ## Features 11 | 12 | ![Plugin Screenshot](https://user-images.githubusercontent.com/469989/49339217-fee2b900-f62e-11e8-9677-ed25191bb7fd.png) 13 | 14 | - Reads EditorConfig files 15 | - [Syntax highlighting](https://user-images.githubusercontent.com/469989/49339218-fee2b900-f62e-11e8-8d57-070313add68b.png) 16 | - [Navigation in EditorConfig files](https://user-images.githubusercontent.com/469989/49339216-fee2b900-f62e-11e8-9a8b-7bd5bd08ff20.png) 17 | 18 | ## EditorConfig Project 19 | 20 | EditorConfig makes it easy to maintain the correct coding style when switching between different text editors and between different projects. The EditorConfig project maintains a file format and plugins for various text editors which allow this file format to be read and used by those editors. For information on the file format and supported text editors, see the [EditorConfig website][editorconfig]. 21 | 22 | ## Example file 23 | 24 | **.editorconfig** 25 | 26 | ```ini 27 | # top-most EditorConfig file 28 | root = true 29 | 30 | # Unix-style newlines with a newline ending every file 31 | [*] 32 | end_of_line = lf 33 | insert_final_newline = true 34 | 35 | # 4 space indentation 36 | [*.py] 37 | indent_style = space 38 | indent_size = 4 39 | 40 | # Tab indentation (no size specified) 41 | [*.js] 42 | indent_style = tab 43 | 44 | # Indentation override for all JS under lib directory 45 | [lib/**.js] 46 | indent_style = space 47 | indent_size = 2 48 | 49 | # Matches the exact files either package.json or .travis.yml 50 | [{package.json,.travis.yml}] 51 | indent_style = space 52 | indent_size = 2 53 | ``` 54 | 55 | ## Supported properties 56 | 57 | ### :construction: 1. charset 58 | 59 | *Values:* `latin1`, `utf-8`, `utf-8-bom`, `utf-16be`, `utf-16le` 60 | 61 | ### :white_check_mark: 2. end_of_line 62 | 63 | *Values:* `lf`, `cr`, `crlf` 64 | 65 | ### :white_check_mark: 3. indent_size 66 | 67 | *Values:* `[number]`, `tab` 68 | 69 | *Special case:* 70 | Indent_size can be set to `tab` if `indent_size` is unspecified and `indent_style` is set to `tab`. 71 | When set to `tab`, the value of `tab_width` (if specified) will be used. 72 | 73 | Read our notes on [Indentation](https://github.com/welovecoding/editorconfig-netbeans/wiki/EditorConfig---Rule-Evaluation#indentation). 74 | 75 | ### :white_check_mark: 4. indent_style 76 | 77 | *Values:* `space`, `tab` 78 | 79 | Read our notes on [Indentation](https://github.com/welovecoding/editorconfig-netbeans/wiki/EditorConfig---Rule-Evaluation#indentation). 80 | 81 | ### :white_check_mark: 5. insert_final_newline 82 | 83 | *Values:* `false`, `true` 84 | 85 | ### :white_check_mark: 6. tab_width 86 | 87 | *Values:* `[number]` 88 | 89 | *Special case:* 90 | Defaults to the value of `indent_size` and doesn't usually need to be specified. 91 | 92 | Read our notes on [Indentation](https://github.com/welovecoding/editorconfig-netbeans/wiki/EditorConfig---Rule-Evaluation#indentation). 93 | 94 | ### :white_check_mark: 7. trim_trailing_whitespace 95 | 96 | *Values:* `false`, `true` 97 | 98 | ## How to use 99 | 100 | - A project must be closed and opened after the plugin is installed, to setup hooks for `.editorconfig` files 101 | - Rules are applied when a file (which is matched by a rule) is saved 102 | 103 | ## Build 104 | 105 | ### Instructions 106 | 107 | ```bash 108 | mvn clean install 109 | ``` 110 | 111 | ### Releases 112 | 113 | - [Release Versions](https://github.com/welovecoding/editorconfig-netbeans/releases) 114 | 115 | ### Status 116 | 117 | [![](https://travis-ci.org/welovecoding/editorconfig-netbeans.svg?branch=master)](https://travis-ci.org/welovecoding/editorconfig-netbeans) 118 | 119 | ## Disclaimer 120 | 121 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 122 | 123 | ## Contributors 124 | 125 | [![](https://avatars1.githubusercontent.com/u/469989?v=3&s=100)][benny_neugebauer] | [![](https://avatars1.githubusercontent.com/u/1138344?v=3&s=100)][michael_koppen] | [![](https://avatars1.githubusercontent.com/u/738383?v=3&s=100)][junichi_yamamoto] | [![](https://avatars1.githubusercontent.com/u/3370875?v=3&s=100)][emily_mabrey] 126 | :---:|:---:|:---:|:---: 127 | [**Benny Neugebauer**][benny_neugebauer] | [**Michael Koppen**][michael_koppen] | [**Junichi Yamamoto**][junichi_yamamoto] | [**Emily Mabrey**][emily_mabrey] 128 | 129 | ### Special Credits 130 | - [Geertjan Wielenga](https://blogs.oracle.com/geertjan) for his posts on [EditorConfig and NetBeans IDE](https://blogs.oracle.com/geertjan/entry/editorconfig_and_netbeans_ide) 131 | 132 | [benny_neugebauer]: http://www.bennyn.de/ "Benny Neugebauer" 133 | [editorconfig]: http://editorconfig.org/ "EditorConfig" 134 | [emily_mabrey]: https://www.openhub.net/accounts/emabrey "Emily Mabrey" 135 | [junichi_yamamoto]: http://junichi11.com/ "Junichi Yamamoto" 136 | [michael_koppen]: https://beanbelt.blogspot.de/ "Michael Koppen" 137 | -------------------------------------------------------------------------------- /maven-repo/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welovecoding/editorconfig-netbeans/2796df7e1756b7212c89b20f39b3b0f489d6c38f/maven-repo/.gitkeep -------------------------------------------------------------------------------- /nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | project 5 | 2 6 | 2 7 | 2 8 | true 9 | 80 10 | none 11 | 2 12 | 2 13 | 2 14 | true 15 | 80 16 | none 17 | WRAP_IF_LONG 18 | true 19 | true 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/config/LoggerSettings.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.config; 2 | 3 | import java.util.logging.Level; 4 | 5 | public class LoggerSettings { 6 | 7 | public static final Level OPERATION_LOG_LEVEL = Level.INFO; 8 | public static final Level PROCESSOR_LOG_LEVEL = Level.INFO; 9 | public static final Level LISTENER_LOG_LEVEL = Level.INFO; 10 | public static final Level MAPPER_LOG_LEVEL = Level.INFO; 11 | 12 | private LoggerSettings() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/config/Settings.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.config; 2 | 3 | import org.netbeans.api.annotations.common.StaticResource; 4 | 5 | public final class Settings { 6 | 7 | public static final String MIME_TYPE = "text/x-editorconfig"; // NOI18N 8 | public static final String EXTENSION = "editorconfig"; // NOI18N 9 | public static final String DEFAULT_FILE_NAME = ".editorconfig"; // NOI18N 10 | public static final String RESOURCE_PATH = "com/welovecoding/nbeditorconfig"; // NOI18N 11 | 12 | @StaticResource 13 | public static final String LOGO_PATH = RESOURCE_PATH + "/filetype/ec.png"; // NOI18N 14 | @StaticResource 15 | public static final String SECTION_ICON_PATH = RESOURCE_PATH + "/section16.png"; // NOI18N 16 | public static final String ENCODING_SETTING = "wlc.editorconfig.charset"; // NOI18N 17 | public static final String EDITORCONFIG = "editorconfig"; // NOI18N 18 | 19 | private Settings() { 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/api/lexer/ECTokenId.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.api.lexer; 2 | 3 | import com.welovecoding.nbeditorconfig.config.Settings; 4 | import com.welovecoding.nbeditorconfig.editor.lexer.ECLexer; 5 | import com.welovecoding.nbeditorconfig.model.EditorConfigConstant; 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | import java.util.EnumSet; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import org.netbeans.api.lexer.Language; 12 | import org.netbeans.api.lexer.TokenId; 13 | import org.netbeans.spi.lexer.LanguageHierarchy; 14 | import org.netbeans.spi.lexer.Lexer; 15 | import org.netbeans.spi.lexer.LexerRestartInfo; 16 | 17 | /** 18 | * 19 | * @author junichi11 20 | */ 21 | public enum ECTokenId implements TokenId { 22 | 23 | WS(null, Category.WHITESPACE, ECTokenId.TOKEN_WS), 24 | PROPERTY_ROOT(EditorConfigConstant.ROOT, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_ROOT), // NOI18N 25 | PROPERTY_CHARSET(EditorConfigConstant.CHARSET, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_CHARSET), // NOI18N 26 | PROPERTY_END_OF_LINE(EditorConfigConstant.END_OF_LINE, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_END_OF_LINE), // NOI18N 27 | PROPERTY_INDENT_SIZE(EditorConfigConstant.INDENT_SIZE, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_INDENT_SIZE), // NOI18N 28 | PROPERTY_INDENT_STYLE(EditorConfigConstant.INDENT_STYLE, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_INDENT_STYLE), // NOI18N 29 | PROPERTY_INSERT_FINAL_NEWLINE(EditorConfigConstant.INSERT_FINAL_NEWLINE, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_INSERT_FINAL_NEWLINE), // NOI18N 30 | PROPERTY_MAX_LINE_LENGTH(EditorConfigConstant.MAX_LINE_LENGTH, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_MAX_LINE_LENGTH), // NOI18N 31 | PROPERTY_TAB_WIDTH(EditorConfigConstant.TAB_WIDTH, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_TAB_WIDTH), // NOI18N 32 | PROPERTY_TRIM_TRAILING_WHITESPACE(EditorConfigConstant.TRIM_TRAILING_WHITESPACE, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_TRIM_TRAILING_WHITESPACE), // NOI18N 33 | PROPERTY_UNKNOWN(null, Category.KEYWORD, ECTokenId.TOKEN_PROPERTY_KEY_UNKNOWN), 34 | SECTION_LDELIMITER("[", Category.SECTION_DELIMITER, ECTokenId.TOKEN_SECTION_LDELIMITER), // NOI18N 35 | SECTION_RDELIMITER("]", Category.SECTION_DELIMITER, ECTokenId.TOKEN_SECTION_RDELIMITER), // NOI18N 36 | OPERATOR_ASSIGNMENT("=", Category.EQUALS, ECTokenId.TOKEN_ASSIGNMENT), // NOI18N 37 | COMMENT(null, Category.COMMENT, ECTokenId.TOKEN_COMMENT), 38 | TEXT(null, Category.STRING, ECTokenId.TOKEN_TEXT), 39 | SECTION_NAME(null, Category.SECTION, ECTokenId.TOKEN_SECTION_NAME), 40 | PROPERTY_VALUE(null, Category.STRING, ECTokenId.TOKEN_PROPERTY_VALUE), 41 | EOF(null, Category.WHITESPACE, ECTokenId.TOKEN_EOF); 42 | 43 | private enum Category { 44 | 45 | WHITESPACE("wlc-nbeditorconfig-whitespace"), // NOI18N 46 | KEYWORD("wlc-nbeditorconfig-keyword"), // NOI18N 47 | SECTION("wlc-nbeditorconfig-section"), // NOI18N 48 | SECTION_DELIMITER("wlc-nbeditorconfig-section-delimiter"), // NOI18N 49 | COMMENT("wlc-nbeditorconfig-comment"), // NOI18N 50 | STRING("wlc-nbeditorconfig-string"), // NOI18N 51 | EQUALS("wlc-nbeditorconfig-equals"); // NOI18N 52 | private final String name; 53 | 54 | Category(String name) { 55 | this.name = name; 56 | } 57 | 58 | public String getName() { 59 | return name; 60 | } 61 | } 62 | 63 | public static final int TOKEN_WS = 1; 64 | public static final int TOKEN_PROPERTY_KEY_ROOT = 2; 65 | public static final int TOKEN_PROPERTY_KEY_CHARSET = 3; 66 | public static final int TOKEN_PROPERTY_KEY_END_OF_LINE = 4; 67 | public static final int TOKEN_PROPERTY_KEY_INDENT_SIZE = 5; 68 | public static final int TOKEN_PROPERTY_KEY_INDENT_STYLE = 6; 69 | public static final int TOKEN_PROPERTY_KEY_INSERT_FINAL_NEWLINE = 7; 70 | public static final int TOKEN_PROPERTY_KEY_MAX_LINE_LENGTH = 8; 71 | public static final int TOKEN_PROPERTY_KEY_TAB_WIDTH = 9; 72 | public static final int TOKEN_PROPERTY_KEY_TRIM_TRAILING_WHITESPACE = 10; 73 | public static final int TOKEN_PROPERTY_KEY_UNKNOWN = 11; 74 | public static final int TOKEN_SECTION_LDELIMITER = 12; 75 | public static final int TOKEN_SECTION_RDELIMITER = 13; 76 | public static final int TOKEN_ASSIGNMENT = 14; 77 | public static final int TOKEN_COMMENT = 15; 78 | public static final int TOKEN_TEXT = 16; 79 | public static final int TOKEN_SECTION_NAME = 17; 80 | public static final int TOKEN_PROPERTY_VALUE = 18; 81 | public static final int TOKEN_EOF = -1; 82 | 83 | private final String fixedText; 84 | private final Category primaryCategory; 85 | private final int id; 86 | private static final Map TOKENS = Collections.synchronizedMap(new HashMap()); 87 | 88 | static { 89 | for (ECTokenId value : values()) { 90 | TOKENS.put(value.getId(), value); 91 | } 92 | } 93 | 94 | ECTokenId(String fixedText, Category primaryCategory, int id) { 95 | this.fixedText = fixedText; 96 | this.primaryCategory = primaryCategory; 97 | this.id = id; 98 | } 99 | 100 | @Override 101 | public String primaryCategory() { 102 | return primaryCategory.getName(); 103 | } 104 | 105 | public String getFixedText() { 106 | return fixedText; 107 | } 108 | 109 | public int getId() { 110 | return id; 111 | } 112 | 113 | public static ECTokenId toEnum(int tokenType) { 114 | return TOKENS.get(tokenType); 115 | } 116 | 117 | private static final Language EDITORCONFIG_LANGUAGE = new LanguageHierarchy() { 118 | 119 | @Override 120 | protected Collection createTokenIds() { 121 | return EnumSet.allOf(ECTokenId.class); 122 | } 123 | 124 | @Override 125 | protected Lexer createLexer(LexerRestartInfo info) { 126 | return ECLexer.create(info); 127 | } 128 | 129 | @Override 130 | protected String mimeType() { 131 | return Settings.MIME_TYPE; 132 | } 133 | 134 | }.language(); 135 | 136 | public static Language language() { 137 | return EDITORCONFIG_LANGUAGE; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/csl/EditorConfigElementHandle.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.csl; 2 | 3 | import com.welovecoding.nbeditorconfig.config.Settings; 4 | import java.util.Collections; 5 | import java.util.Set; 6 | import org.netbeans.modules.csl.api.ElementHandle; 7 | import org.netbeans.modules.csl.api.ElementKind; 8 | import org.netbeans.modules.csl.api.Modifier; 9 | import org.netbeans.modules.csl.api.OffsetRange; 10 | import org.netbeans.modules.csl.spi.ParserResult; 11 | import org.openide.filesystems.FileObject; 12 | 13 | /** 14 | * 15 | * @author junichi11 16 | */ 17 | public class EditorConfigElementHandle implements ElementHandle { 18 | 19 | private final String name; 20 | private final int startPosition; 21 | private final int endPosition; 22 | private final ElementKind elementKind; 23 | 24 | public EditorConfigElementHandle(String name, int startPosition, int endPosition, ElementKind elementKind) { 25 | this.name = name; 26 | this.startPosition = startPosition; 27 | this.endPosition = endPosition; 28 | this.elementKind = elementKind; 29 | } 30 | 31 | @Override 32 | public FileObject getFileObject() { 33 | return null; 34 | } 35 | 36 | @Override 37 | public String getMimeType() { 38 | return Settings.MIME_TYPE; 39 | } 40 | 41 | @Override 42 | public String getName() { 43 | return name; 44 | } 45 | 46 | @Override 47 | public String getIn() { 48 | return null; 49 | } 50 | 51 | @Override 52 | public ElementKind getKind() { 53 | return elementKind; 54 | } 55 | 56 | @Override 57 | public Set getModifiers() { 58 | return Collections.emptySet(); 59 | } 60 | 61 | @Override 62 | public boolean signatureEquals(ElementHandle elementHandle) { 63 | if (elementHandle instanceof EditorConfigElementHandle) { 64 | return name.equals(((EditorConfigElementHandle) elementHandle).name); 65 | } 66 | return false; 67 | } 68 | 69 | @Override 70 | public OffsetRange getOffsetRange(ParserResult pr) { 71 | return new OffsetRange(startPosition, endPosition); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/csl/EditorConfigLanguage.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.csl; 2 | 3 | import com.welovecoding.nbeditorconfig.config.Settings; 4 | import com.welovecoding.nbeditorconfig.editor.api.lexer.ECTokenId; 5 | import com.welovecoding.nbeditorconfig.editor.parser.ECParser; 6 | import com.welovecoding.nbeditorconfig.options.EditorConfigOptions; 7 | import org.netbeans.api.lexer.Language; 8 | import org.netbeans.modules.csl.api.StructureScanner; 9 | import org.netbeans.modules.csl.spi.DefaultLanguageConfig; 10 | import org.netbeans.modules.csl.spi.LanguageRegistration; 11 | import org.netbeans.modules.parsing.spi.Parser; 12 | 13 | /** 14 | * 15 | * @author junichi11 16 | */ 17 | @LanguageRegistration(mimeType = Settings.MIME_TYPE, useMultiview = true) 18 | public class EditorConfigLanguage extends DefaultLanguageConfig { 19 | 20 | @Override 21 | public Language getLexerLanguage() { 22 | return ECTokenId.language(); 23 | } 24 | 25 | @Override 26 | public String getDisplayName() { 27 | return "EditorConfig"; // NOI18N 28 | } 29 | 30 | @Override 31 | public Parser getParser() { 32 | return new ECParser(); 33 | } 34 | 35 | @Override 36 | public String getLineCommentPrefix() { 37 | // # or ; 38 | return EditorConfigOptions.getInstance().getLineCommentPrefix(); 39 | } 40 | 41 | @Override 42 | public StructureScanner getStructureScanner() { 43 | return new EditorConfigStructureScanner(); 44 | } 45 | 46 | @Override 47 | public boolean hasStructureScanner() { 48 | return true; 49 | } 50 | 51 | @Override 52 | public String getPreferredExtension() { 53 | return Settings.EDITORCONFIG; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/csl/EditorConfigPropertyStructureItem.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.csl; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.Set; 6 | import javax.swing.ImageIcon; 7 | import org.netbeans.modules.csl.api.ElementHandle; 8 | import org.netbeans.modules.csl.api.ElementKind; 9 | import org.netbeans.modules.csl.api.HtmlFormatter; 10 | import org.netbeans.modules.csl.api.Modifier; 11 | import org.netbeans.modules.csl.api.StructureItem; 12 | 13 | /** 14 | * 15 | * @author junichi11 16 | */ 17 | public class EditorConfigPropertyStructureItem implements StructureItem { 18 | 19 | private final String key; 20 | private final String value; 21 | private final int startPosition; 22 | private final int endPosition; 23 | 24 | /** 25 | * StructureItem for a property. 26 | * 27 | * @param key property key 28 | * @param value property value 29 | * @param startPosition start position of a property 30 | * @param endPosition end position of a property 31 | */ 32 | public EditorConfigPropertyStructureItem(String key, String value, int startPosition, int endPosition) { 33 | this.key = key; 34 | this.value = value; 35 | this.startPosition = startPosition; 36 | this.endPosition = endPosition; 37 | } 38 | 39 | @Override 40 | public String getName() { 41 | return key; 42 | } 43 | 44 | @Override 45 | public String getSortText() { 46 | return key; 47 | } 48 | 49 | @Override 50 | public String getHtml(HtmlFormatter formatter) { 51 | return String.format("%s : %s", key, value); // NOI18N 52 | } 53 | 54 | @Override 55 | public ElementHandle getElementHandle() { 56 | return new EditorConfigElementHandle(key, startPosition, endPosition, ElementKind.PROPERTY); 57 | } 58 | 59 | @Override 60 | public ElementKind getKind() { 61 | return ElementKind.PROPERTY; 62 | } 63 | 64 | @Override 65 | public Set getModifiers() { 66 | return Collections.emptySet(); 67 | } 68 | 69 | @Override 70 | public boolean isLeaf() { 71 | return true; 72 | } 73 | 74 | @Override 75 | public List getNestedItems() { 76 | return Collections.emptyList(); 77 | } 78 | 79 | @Override 80 | public long getPosition() { 81 | return startPosition; 82 | } 83 | 84 | @Override 85 | public long getEndPosition() { 86 | return endPosition; 87 | } 88 | 89 | @Override 90 | public ImageIcon getCustomIcon() { 91 | return null; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/csl/EditorConfigSectionStructureItem.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.csl; 2 | 3 | import com.welovecoding.nbeditorconfig.config.Settings; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Set; 7 | import javax.swing.ImageIcon; 8 | import org.netbeans.modules.csl.api.ElementHandle; 9 | import org.netbeans.modules.csl.api.ElementKind; 10 | import org.netbeans.modules.csl.api.HtmlFormatter; 11 | import org.netbeans.modules.csl.api.Modifier; 12 | import org.netbeans.modules.csl.api.StructureItem; 13 | import org.openide.util.ImageUtilities; 14 | 15 | /** 16 | * 17 | * @author junichi11 18 | */ 19 | public class EditorConfigSectionStructureItem implements StructureItem { 20 | 21 | private final String section; 22 | private final int startPosition; 23 | private final int endPosition; 24 | private final List properties; 25 | 26 | /** 27 | * StructureItem for a section. It can have properties as children. 28 | * 29 | * @param section section name 30 | * @param startPosition start position of section 31 | * @param endPosition end position of section 32 | * @param properties properties (children) of section 33 | */ 34 | public EditorConfigSectionStructureItem(String section, int startPosition, int endPosition, List properties) { 35 | this.section = section; 36 | this.startPosition = startPosition; 37 | this.endPosition = endPosition; 38 | this.properties = properties; 39 | } 40 | 41 | @Override 42 | public String getName() { 43 | return section; 44 | } 45 | 46 | @Override 47 | public String getSortText() { 48 | return section; 49 | } 50 | 51 | @Override 52 | public String getHtml(HtmlFormatter formatter) { 53 | return section; 54 | } 55 | 56 | @Override 57 | public ElementHandle getElementHandle() { 58 | return new EditorConfigElementHandle(section, startPosition, endPosition, ElementKind.RULE); 59 | } 60 | 61 | @Override 62 | public ElementKind getKind() { 63 | return ElementKind.RULE; 64 | } 65 | 66 | @Override 67 | public Set getModifiers() { 68 | return Collections.emptySet(); 69 | } 70 | 71 | @Override 72 | public boolean isLeaf() { 73 | return properties.isEmpty(); 74 | } 75 | 76 | @Override 77 | public List getNestedItems() { 78 | return properties; 79 | } 80 | 81 | @Override 82 | public long getPosition() { 83 | return startPosition; 84 | } 85 | 86 | @Override 87 | public long getEndPosition() { 88 | return endPosition; 89 | } 90 | 91 | @Override 92 | public ImageIcon getCustomIcon() { 93 | return ImageUtilities.loadImageIcon(Settings.SECTION_ICON_PATH, true); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/csl/EditorConfigStructureScanner.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.csl; 2 | 3 | import com.welovecoding.nbeditorconfig.editor.parser.ECParserResult; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import java.util.Map; 8 | import org.antlr.v4.runtime.tree.TerminalNode; 9 | import org.antlr4.EditorConfigParser; 10 | import org.antlr4.EditorConfigParserBaseVisitor; 11 | import org.netbeans.modules.csl.api.OffsetRange; 12 | import org.netbeans.modules.csl.api.StructureItem; 13 | import org.netbeans.modules.csl.api.StructureScanner; 14 | import org.netbeans.modules.csl.spi.ParserResult; 15 | 16 | /** 17 | * 18 | * @author junichi11 19 | */ 20 | public class EditorConfigStructureScanner implements StructureScanner { 21 | 22 | @Override 23 | public List scan(ParserResult parserResult) { 24 | ECParserResult result = (ECParserResult) parserResult; 25 | StructureVisitor visitor = new StructureVisitor(); 26 | visitor.visit(result.getRoot()); 27 | return visitor.getElements(); 28 | } 29 | 30 | @Override 31 | public Map> folds(ParserResult parserResult) { 32 | return Collections.emptyMap(); 33 | } 34 | 35 | @Override 36 | public Configuration getConfiguration() { 37 | return null; 38 | } 39 | 40 | private static class StructureVisitor extends EditorConfigParserBaseVisitor { 41 | 42 | private final ArrayList sections = new ArrayList<>(); 43 | private final ArrayList properties = new ArrayList<>(); 44 | private String sectionName; 45 | private int sectionStart; 46 | private int sectionEnd; 47 | private int propertyStart; 48 | private int propertyEnd; 49 | private String propertyKey; 50 | private String propertyValue; 51 | private int childCount; 52 | private int currentChildCount; 53 | 54 | @Override 55 | public Void visitSection(EditorConfigParser.SectionContext section) { 56 | childCount = section.getChildCount(); 57 | return super.visitSection(section); 58 | } 59 | 60 | @Override 61 | public Void visitPropertyStatement(EditorConfigParser.PropertyStatementContext property) { 62 | propertyStart = property.getStart().getStartIndex(); 63 | propertyEnd = property.getStop().getStopIndex(); 64 | currentChildCount++; 65 | return super.visitPropertyStatement(property); 66 | } 67 | 68 | @Override 69 | public Void visitPropertyValue(EditorConfigParser.PropertyValueContext ctx) { 70 | propertyValue = ctx.getText(); 71 | if (propertyKey != null && !propertyKey.isEmpty() && propertyValue != null) { 72 | properties.add(new EditorConfigPropertyStructureItem(propertyKey, propertyValue, propertyStart, propertyEnd)); 73 | } 74 | propertyKey = null; 75 | propertyValue = null; 76 | propertyStart = 0; 77 | propertyEnd = 0; 78 | 79 | if (childCount == currentChildCount) { 80 | if (sectionName != null) { 81 | ArrayList children = new ArrayList<>(properties); 82 | sections.add(new EditorConfigSectionStructureItem(sectionName, sectionStart, sectionEnd, children)); 83 | } 84 | sectionName = null; 85 | sectionStart = 0; 86 | sectionEnd = 0; 87 | properties.clear(); 88 | currentChildCount = 0; 89 | } 90 | return super.visitPropertyValue(ctx); 91 | } 92 | 93 | @Override 94 | public Void visitPropertyKey(EditorConfigParser.PropertyKeyContext ctx) { 95 | propertyKey = ctx.getText(); 96 | return super.visitPropertyKey(ctx); 97 | } 98 | 99 | @Override 100 | public Void visitSectionHeader(EditorConfigParser.SectionHeaderContext sectionHeader) { 101 | sectionStart = sectionHeader.getStart().getStartIndex(); 102 | sectionEnd = sectionHeader.getStop().getStopIndex(); 103 | TerminalNode sectionNode = sectionHeader.SECTION_NAME(); 104 | sectionName = sectionNode != null ? sectionNode.getText() : ""; // NOI18N 105 | currentChildCount++; 106 | return super.visitSectionHeader(sectionHeader); 107 | } 108 | 109 | public List getElements() { 110 | return sections; 111 | } 112 | 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/lexer/AntlrCharStream.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.lexer; 2 | 3 | import org.antlr.v4.runtime.CharStream; 4 | import org.antlr.v4.runtime.misc.Interval; 5 | import org.netbeans.spi.lexer.LexerInput; 6 | 7 | public class AntlrCharStream implements CharStream { 8 | 9 | private final LexerInput input; 10 | private final String name; 11 | private int index = 0; 12 | private int markDepth = 0; 13 | 14 | public AntlrCharStream(LexerInput input, String name) { 15 | this.input = input; 16 | this.name = name; 17 | } 18 | 19 | @Override 20 | public String getText(Interval interval) { 21 | return input.readText().toString(); 22 | } 23 | 24 | @Override 25 | public void consume() { 26 | int character = read(); 27 | if (character == EOF) { 28 | backup(1); 29 | throw new IllegalStateException("Attempting to consume EOF"); 30 | } 31 | index++; 32 | } 33 | 34 | @Override 35 | public int LA(int lookaheadAmount) { 36 | if (lookaheadAmount < 0) { 37 | return lookBack(-lookaheadAmount); 38 | } else if (lookaheadAmount > 0) { 39 | return lookAhead(lookaheadAmount); 40 | } else { 41 | return 0; //Behaviour is undefined when lookaheadAmount == 0 42 | } 43 | } 44 | 45 | private int lookBack(int amount) { 46 | backup(amount); 47 | int character = read(); 48 | for (int i = 1; i < amount; i++) { 49 | read(); 50 | } 51 | return character; 52 | } 53 | 54 | private int lookAhead(int amount) { 55 | int character = 0; 56 | for (int i = 0; i < amount; i++) { 57 | character = read(); 58 | } 59 | backup(amount); 60 | return character; 61 | } 62 | 63 | @Override 64 | public int mark() { 65 | return ++markDepth; 66 | } 67 | 68 | @Override 69 | public void release(int marker) { 70 | // unwind any other markers made after m and release m 71 | markDepth = marker; 72 | // release this marker 73 | markDepth--; 74 | } 75 | 76 | @Override 77 | public void seek(int index) { 78 | if (index < 0) { 79 | throw new IllegalArgumentException(String.format("Invalid index (%s < 0)", index)); 80 | } 81 | 82 | if (index < this.index) { 83 | backup(this.index - index); 84 | this.index = index; 85 | return; 86 | } 87 | 88 | // seek forward, consume until p hits index 89 | while (this.index < index) { 90 | consume(); 91 | } 92 | } 93 | 94 | @Override 95 | public int index() { 96 | return index; 97 | } 98 | 99 | @Override 100 | public int size() { 101 | throw new UnsupportedOperationException("Stream size unknown"); 102 | } 103 | 104 | @Override 105 | public String getSourceName() { 106 | return name; 107 | } 108 | 109 | private int read() { 110 | int result = input.read(); 111 | if (result == LexerInput.EOF) { 112 | return EOF; 113 | } else { 114 | return result; 115 | } 116 | } 117 | 118 | private void backup(int count) { 119 | input.backup(count); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/lexer/ECLexer.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.lexer; 2 | 3 | import com.welovecoding.nbeditorconfig.editor.api.lexer.ECTokenId; 4 | import org.antlr.v4.runtime.misc.IntegerStack; 5 | import org.antlr4.EditorConfigLexer; 6 | import org.netbeans.api.lexer.Token; 7 | import org.netbeans.spi.lexer.Lexer; 8 | import org.netbeans.spi.lexer.LexerRestartInfo; 9 | 10 | /** 11 | * 12 | * @author junichi11 13 | */ 14 | public class ECLexer implements Lexer { 15 | 16 | private final LexerRestartInfo info; 17 | private final EditorConfigLexer lexer; 18 | 19 | private ECLexer(LexerRestartInfo info) { 20 | this.info = info; 21 | AntlrCharStream charStream = new AntlrCharStream(info.input(), "EditorConfig"); // NOI18N 22 | lexer = new EditorConfigLexer(charStream); 23 | lexer.removeErrorListeners(); 24 | // TODO add listener 25 | LexerState lexerMode = (LexerState) info.state(); 26 | if (lexerMode != null) { 27 | lexer._mode = lexerMode.Mode; 28 | lexer._modeStack.addAll(lexerMode.Stack); 29 | } 30 | } 31 | 32 | public static synchronized ECLexer create(LexerRestartInfo info) { 33 | return new ECLexer(info); 34 | } 35 | 36 | @Override 37 | public Token nextToken() { 38 | org.antlr.v4.runtime.Token token = lexer.nextToken(); 39 | int type = token.getType(); 40 | ECTokenId tokenId = ECTokenId.toEnum(type); 41 | assert tokenId != null; 42 | if (tokenId != ECTokenId.EOF) { 43 | return info.tokenFactory().createToken(tokenId); 44 | } 45 | return null; 46 | } 47 | 48 | @Override 49 | public Object state() { 50 | return new LexerState(lexer._mode, lexer._modeStack); 51 | } 52 | 53 | @Override 54 | public void release() { 55 | } 56 | 57 | // If mode is used, keep state of it 58 | // http://stackoverflow.com/questions/23887888/antlr4-based-lexer-loses-syntax-hightlighting-during-typing-on-netbeans 59 | private static class LexerState { 60 | 61 | public int Mode = -1; 62 | public IntegerStack Stack = null; 63 | 64 | public LexerState(int mode, IntegerStack stack) { 65 | Mode = mode; 66 | Stack = new IntegerStack(stack); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/parser/ECParser.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.parser; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import javax.swing.event.ChangeListener; 6 | import org.antlr.v4.runtime.ANTLRInputStream; 7 | import org.antlr.v4.runtime.CommonTokenStream; 8 | import org.antlr.v4.runtime.Lexer; 9 | import org.antlr4.EditorConfigErrorListener; 10 | import org.antlr4.EditorConfigLexer; 11 | import org.antlr4.EditorConfigParser; 12 | import org.antlr4.SyntaxError; 13 | import org.netbeans.modules.parsing.api.Snapshot; 14 | import org.netbeans.modules.parsing.api.Task; 15 | import org.netbeans.modules.parsing.spi.ParseException; 16 | import org.netbeans.modules.parsing.spi.Parser; 17 | import org.netbeans.modules.parsing.spi.SourceModificationEvent; 18 | 19 | /** 20 | * 21 | * @author junichi11 22 | */ 23 | public class ECParser extends Parser { 24 | 25 | private Snapshot snapshot; 26 | private EditorConfigParser parser; 27 | private List syntaxErrors; 28 | private ECParserResult result; 29 | 30 | @Override 31 | public void parse(Snapshot snapshot, Task task, SourceModificationEvent event) throws ParseException { 32 | this.snapshot = snapshot; 33 | String text = snapshot.getText().toString(); 34 | ANTLRInputStream input = new ANTLRInputStream(text); 35 | Lexer lexer = new EditorConfigLexer(input); 36 | lexer.removeErrorListeners(); 37 | 38 | CommonTokenStream tokens = new CommonTokenStream(lexer); 39 | parser = new EditorConfigParser(tokens); 40 | parser.removeErrorListeners(); 41 | syntaxErrors = new ArrayList<>(); 42 | EditorConfigErrorListener errorListener = new EditorConfigErrorListener(syntaxErrors); 43 | parser.addErrorListener(errorListener); 44 | EditorConfigParser.FileContext root = parser.file(); 45 | result = new ECParserResult(snapshot, parser, root); 46 | } 47 | 48 | @Override 49 | public Result getResult(Task task) throws ParseException { 50 | return result; 51 | } 52 | 53 | @Override 54 | public void addChangeListener(ChangeListener listener) { 55 | } 56 | 57 | @Override 58 | public void removeChangeListener(ChangeListener listener) { 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/parser/ECParserResult.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.parser; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import org.antlr.v4.runtime.ANTLRErrorListener; 6 | import org.antlr.v4.runtime.tree.ParseTree; 7 | import org.antlr4.EditorConfigErrorListener; 8 | import org.antlr4.EditorConfigParser; 9 | import org.antlr4.SyntaxError; 10 | import org.netbeans.modules.csl.api.Error; 11 | import org.netbeans.modules.csl.spi.ParserResult; 12 | import org.netbeans.modules.parsing.api.Snapshot; 13 | import org.netbeans.modules.parsing.spi.ParseException; 14 | 15 | /** 16 | * 17 | * @author junichi11 18 | */ 19 | public class ECParserResult extends ParserResult { 20 | 21 | private final EditorConfigParser parser; 22 | private boolean valid = true; 23 | private final ParseTree root; 24 | 25 | public ECParserResult(Snapshot snapshot, EditorConfigParser parser, ParseTree root) { 26 | super(snapshot); 27 | this.parser = parser; 28 | this.root = root; 29 | } 30 | 31 | public EditorConfigParser getEditorConfigParser() throws ParseException { 32 | if (!valid) { 33 | throw new ParseException(); 34 | } 35 | return parser; 36 | } 37 | 38 | public ParseTree getRoot() { 39 | return root; 40 | } 41 | 42 | @Override 43 | protected void invalidate() { 44 | valid = false; 45 | } 46 | 47 | @Override 48 | public List getDiagnostics() { 49 | return Collections.emptyList(); 50 | } 51 | 52 | public List getErrors() { 53 | List errorListeners = parser.getErrorListeners(); 54 | for (ANTLRErrorListener errorListener : errorListeners) { 55 | if (errorListener instanceof EditorConfigErrorListener) { 56 | EditorConfigErrorListener ecErrorListener = (EditorConfigErrorListener) errorListener; 57 | return ecErrorListener.getErrorMessages(); 58 | } 59 | } 60 | return Collections.emptyList(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/parser/SyntaxErrorsTask.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.parser; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.Settings.EDITORCONFIG; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import javax.swing.text.Document; 7 | import org.antlr4.SyntaxError; 8 | import org.netbeans.modules.parsing.spi.ParserResultTask; 9 | import org.netbeans.modules.parsing.spi.Scheduler; 10 | import org.netbeans.modules.parsing.spi.SchedulerEvent; 11 | import org.netbeans.spi.editor.hints.ErrorDescription; 12 | import org.netbeans.spi.editor.hints.ErrorDescriptionFactory; 13 | import org.netbeans.spi.editor.hints.HintsController; 14 | import org.netbeans.spi.editor.hints.Severity; 15 | 16 | /** 17 | * 18 | * @author junichi11 19 | */ 20 | public class SyntaxErrorsTask extends ParserResultTask { 21 | 22 | @Override 23 | public void run(ECParserResult result, SchedulerEvent event) { 24 | List syntaxErrors = result.getErrors(); 25 | Document document = result.getSnapshot().getSource().getDocument(false); 26 | List errors = new ArrayList<>(); 27 | for (SyntaxError syntaxError : syntaxErrors) { 28 | String message = syntaxError.getMessage(); 29 | int line = syntaxError.getLine(); 30 | if (line <= 0) { 31 | continue; 32 | } 33 | ErrorDescription errorDescription = ErrorDescriptionFactory.createErrorDescription( 34 | Severity.ERROR, 35 | message, 36 | document, 37 | line); 38 | errors.add(errorDescription); 39 | } 40 | HintsController.setErrors(document, EDITORCONFIG, errors); // NOI18N 41 | } 42 | 43 | @Override 44 | public int getPriority() { 45 | return 100; 46 | } 47 | 48 | @Override 49 | public Class getSchedulerClass() { 50 | return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER; 51 | } 52 | 53 | @Override 54 | public void cancel() { 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/editor/parser/SyntaxErrorsTaskFactory.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.editor.parser; 2 | 3 | import com.welovecoding.nbeditorconfig.config.Settings; 4 | import java.util.Collection; 5 | import java.util.Collections; 6 | import org.netbeans.api.editor.mimelookup.MimeRegistration; 7 | import org.netbeans.modules.parsing.api.Snapshot; 8 | import org.netbeans.modules.parsing.spi.SchedulerTask; 9 | import org.netbeans.modules.parsing.spi.TaskFactory; 10 | 11 | /** 12 | * 13 | * @author junichi11 14 | */ 15 | @MimeRegistration(mimeType = Settings.MIME_TYPE, service = TaskFactory.class) 16 | public class SyntaxErrorsTaskFactory extends TaskFactory { 17 | 18 | @Override 19 | public Collection create(Snapshot snapshot) { 20 | return Collections.singleton(new SyntaxErrorsTask()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/filetype/EditorConfigDataObject.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.filetype; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.Settings.LOGO_PATH; 4 | import static com.welovecoding.nbeditorconfig.config.Settings.MIME_TYPE; 5 | import java.io.IOException; 6 | import org.netbeans.api.templates.TemplateRegistration; 7 | import org.openide.WizardDescriptor; 8 | import org.openide.awt.ActionID; 9 | import org.openide.awt.ActionReference; 10 | import org.openide.awt.ActionReferences; 11 | import org.openide.filesystems.FileObject; 12 | import org.openide.filesystems.MIMEResolver; 13 | import org.openide.loaders.DataObject; 14 | import org.openide.loaders.DataObjectExistsException; 15 | import org.openide.loaders.MultiDataObject; 16 | import org.openide.loaders.MultiFileLoader; 17 | import org.openide.util.NbBundle.Messages; 18 | 19 | @MIMEResolver.ExtensionRegistration( 20 | displayName = "#LBL_EditorConfig_LOADER", 21 | mimeType = MIME_TYPE, 22 | extension = {"editorconfig"}, 23 | position = 3214328 24 | ) 25 | @DataObject.Registration( 26 | mimeType = MIME_TYPE, 27 | iconBase = LOGO_PATH, 28 | displayName = "#LBL_EditorConfig_LOADER", 29 | position = 300 30 | ) 31 | @ActionReferences({ 32 | @ActionReference( 33 | path = "Loaders/text/plain/Actions", 34 | id = @ActionID(category = "System", id = "org.openide.actions.OpenAction"), 35 | position = 100, 36 | separatorAfter = 200 37 | ), 38 | @ActionReference( 39 | path = "Loaders/text/plain/Actions", 40 | id = @ActionID(category = "Edit", id = "org.openide.actions.CutAction"), 41 | position = 300 42 | ), 43 | @ActionReference( 44 | path = "Loaders/text/plain/Actions", 45 | id = @ActionID(category = "Edit", id = "org.openide.actions.CopyAction"), 46 | position = 400, 47 | separatorAfter = 500 48 | ), 49 | @ActionReference( 50 | path = "Loaders/text/plain/Actions", 51 | id = @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"), 52 | position = 600 53 | ), 54 | @ActionReference( 55 | path = "Loaders/text/plain/Actions", 56 | id = @ActionID(category = "System", id = "org.openide.actions.RenameAction"), 57 | position = 700, 58 | separatorAfter = 800 59 | ), 60 | @ActionReference( 61 | path = "Loaders/text/plain/Actions", 62 | id = @ActionID(category = "System", id = "org.openide.actions.SaveAsTemplateAction"), 63 | position = 900, 64 | separatorAfter = 1000 65 | ), 66 | @ActionReference( 67 | path = "Loaders/text/plain/Actions", 68 | id = @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"), 69 | position = 1100, 70 | separatorAfter = 1200 71 | ), 72 | @ActionReference( 73 | path = "Loaders/text/plain/Actions", 74 | id = @ActionID(category = "System", id = "org.openide.actions.ToolsAction"), 75 | position = 1300 76 | ), 77 | @ActionReference( 78 | path = "Loaders/text/plain/Actions", 79 | id = @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"), 80 | position = 1400 81 | ) 82 | }) 83 | @Messages({ 84 | "LBL_EditorConfig_LOADER=EditorConfig File", 85 | "LBL_EditorConfig_template_displayName=EditorConfig File" 86 | 87 | }) 88 | public class EditorConfigDataObject extends MultiDataObject { 89 | 90 | public EditorConfigDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException { 91 | super(pf, loader); 92 | registerEditor("text/plain", true); 93 | } 94 | 95 | @Override 96 | protected int associateLookup() { 97 | return 1; 98 | } 99 | 100 | @TemplateRegistration( 101 | displayName = "#LBL_EditorConfig_template_displayName", 102 | description = "description.html", 103 | folder = "Other", 104 | targetName = "", 105 | content = "../EditorConfigExample.editorconfig", 106 | iconBase = LOGO_PATH, 107 | position = 112) 108 | public static WizardDescriptor.InstantiatingIterator templateIterator() { 109 | return null; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/filetype/FilenameResolver.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.filetype; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.Settings.MIME_TYPE; 4 | import java.util.logging.Logger; 5 | import org.openide.filesystems.FileObject; 6 | import org.openide.filesystems.MIMEResolver; 7 | import org.openide.util.lookup.ServiceProvider; 8 | 9 | @ServiceProvider(service = MIMEResolver.class, position = 3214328) 10 | public class FilenameResolver extends MIMEResolver { 11 | 12 | private static final Logger LOG = Logger.getLogger(FilenameResolver.class.getName()); 13 | 14 | public FilenameResolver() { 15 | super(MIME_TYPE); 16 | } 17 | 18 | @Override 19 | public String findMIMEType(FileObject fo) { 20 | String nameExt = fo.getNameExt(); 21 | 22 | if (".editorconfig".equalsIgnoreCase(nameExt)) { 23 | return MIME_TYPE; 24 | } 25 | 26 | return null; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/exception/FileAccessException.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.exception; 2 | 3 | public class FileAccessException extends Exception { 4 | 5 | public FileAccessException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/exception/FileObjectLockException.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.exception; 2 | 3 | public class FileObjectLockException extends Exception { 4 | 5 | public FileObjectLockException(String message) { 6 | super("\u00ac " + message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/model/FirstLineInfo.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.model; 2 | 3 | /** 4 | * File information which can be parsed from the first line of a file. 5 | */ 6 | public class FirstLineInfo { 7 | 8 | private final MappedCharset charset; 9 | private final String lineEnding; 10 | private final boolean marked; 11 | 12 | public FirstLineInfo(MappedCharset charset, String lineEnding, boolean marked) { 13 | this.charset = charset; 14 | this.lineEnding = lineEnding; 15 | this.marked = marked; 16 | } 17 | 18 | public MappedCharset getCharset() { 19 | return charset; 20 | } 21 | 22 | public String getLineEnding() { 23 | return lineEnding; 24 | } 25 | 26 | public boolean isMarked() { 27 | return marked; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/model/MappedCharset.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.model; 2 | 3 | import java.nio.charset.Charset; 4 | import java.nio.charset.StandardCharsets; 5 | import java.util.Objects; 6 | 7 | public class MappedCharset { 8 | 9 | public static final String FILE_MARK = "\uFEFF"; 10 | 11 | private Charset charset = null; 12 | private String mark = null; 13 | private String name = null; 14 | 15 | public MappedCharset(String name) { 16 | this.charset = null; 17 | this.mark = null; 18 | this.name = name; 19 | init(name); 20 | } 21 | 22 | private void init(String name) { 23 | switch (name) { 24 | case "ISO-8859-1": 25 | charset = StandardCharsets.ISO_8859_1; 26 | break; 27 | case "UTF-8": 28 | charset = StandardCharsets.UTF_8; 29 | break; 30 | case "UTF-8-BOM": 31 | charset = StandardCharsets.UTF_8; 32 | mark = FILE_MARK; 33 | break; 34 | case "UTF-16BE": 35 | charset = StandardCharsets.UTF_16BE; 36 | mark = FILE_MARK; 37 | break; 38 | case "UTF-16LE": 39 | charset = StandardCharsets.UTF_16LE; 40 | mark = FILE_MARK; 41 | break; 42 | default: 43 | charset = StandardCharsets.UTF_8; 44 | break; 45 | } 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | int hash = 7; 51 | hash = 79 * hash + Objects.hashCode(this.name); 52 | return hash; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object obj) { 57 | if (obj == null) { 58 | return false; 59 | } 60 | if (getClass() != obj.getClass()) { 61 | return false; 62 | } 63 | final MappedCharset other = (MappedCharset) obj; 64 | return Objects.equals(this.name, other.name); 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "MappedCharset{" + "charset=" + charset + ", mark=" + mark + ", name=" + name + '}'; 70 | } 71 | 72 | public Charset getCharset() { 73 | return charset; 74 | } 75 | 76 | public void setCharset(Charset charset) { 77 | this.charset = charset; 78 | } 79 | 80 | public String getMark() { 81 | return mark; 82 | } 83 | 84 | public void setMark(String mark) { 85 | this.mark = mark; 86 | } 87 | 88 | public String getName() { 89 | return name; 90 | } 91 | 92 | public void setName(String name) { 93 | this.name = name; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/model/SupportedCharsets.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.model; 2 | 3 | public class SupportedCharsets { 4 | 5 | public static final MappedCharset LATIN_1 = new MappedCharset("ISO-8859-1"); 6 | public static final MappedCharset UTF_8 = new MappedCharset("UTF-8"); 7 | public static final MappedCharset UTF_8_BOM = new MappedCharset("UTF-8-BOM"); 8 | public static final MappedCharset UTF_16_BE = new MappedCharset("UTF-16BE"); 9 | public static final MappedCharset UTF_16_LE = new MappedCharset("UTF-16LE"); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/reader/FileInfoReader.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.reader; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.Settings.ENCODING_SETTING; 4 | import com.welovecoding.nbeditorconfig.io.model.FirstLineInfo; 5 | import com.welovecoding.nbeditorconfig.io.model.MappedCharset; 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.StringReader; 11 | import java.nio.charset.Charset; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.ArrayList; 14 | import java.util.Collection; 15 | import java.util.List; 16 | import java.util.logging.Logger; 17 | import org.mozilla.universalchardet.UniversalDetector; 18 | import org.openide.filesystems.FileObject; 19 | import org.openide.util.Exceptions; 20 | import org.apache.commons.io.IOUtils; 21 | 22 | /** 23 | * @see 24 | * Detection 25 | * Without External Encoding Information 26 | */ 27 | public class FileInfoReader { 28 | 29 | private static final Logger LOG = Logger.getLogger(FileInfoReader.class.getName()); 30 | 31 | private static final String[] UNICODE_CHARSETS = new String[]{ 32 | "UTF-16BE", 33 | "UTF-16LE", 34 | "UTF-8" 35 | }; 36 | 37 | private static String detectLineEnding(String line) { 38 | String lineEnding = System.lineSeparator(); 39 | 40 | if (line.endsWith("\r\n")) { 41 | lineEnding = "\r\n"; 42 | } else if (line.endsWith("\n")) { 43 | lineEnding = "\n"; 44 | } else if (line.endsWith("\r")) { 45 | lineEnding = "\r"; 46 | } 47 | 48 | return lineEnding; 49 | } 50 | 51 | public static MappedCharset readCharset(FileObject fo) { 52 | MappedCharset mappedCharset; 53 | 54 | Object charsetName = fo.getAttribute(ENCODING_SETTING); 55 | 56 | if (charsetName != null) { 57 | mappedCharset = new MappedCharset(charsetName.toString()); 58 | } else { 59 | Charset charset = guessCharset(fo); 60 | mappedCharset = new MappedCharset(charset.name()); 61 | } 62 | 63 | return mappedCharset; 64 | } 65 | 66 | protected static Charset guessCharset(FileObject fo) { 67 | Charset charset = StandardCharsets.UTF_8; 68 | byte[] buf = new byte[4096]; 69 | 70 | try (InputStream is = fo.getInputStream()) { 71 | UniversalDetector detector = new UniversalDetector(null); 72 | 73 | int nread; 74 | while ((nread = is.read(buf)) > 0 && !detector.isDone()) { 75 | detector.handleData(buf, 0, nread); 76 | } 77 | 78 | detector.dataEnd(); 79 | 80 | String encoding = detector.getDetectedCharset(); 81 | 82 | if (encoding == null) { 83 | encoding = "ISO-8859-1"; 84 | } 85 | 86 | detector.reset(); 87 | 88 | charset = Charset.forName(encoding); 89 | } catch (IllegalArgumentException | IOException ex) { 90 | Exceptions.printStackTrace(ex); 91 | } 92 | 93 | return charset; 94 | } 95 | 96 | public static FirstLineInfo parseFirstLineInfo(FileObject fo) { 97 | Charset charset = FileInfoReader.guessCharset(fo); 98 | MappedCharset supportedCharset; 99 | String charsetName = charset.name(); 100 | String firstLine = readFirstLineWithSeparator(fo, charset); 101 | String lineEnding = detectLineEnding(firstLine); 102 | boolean marked = false; 103 | 104 | if (charset.equals(StandardCharsets.UTF_8) 105 | && firstLine.startsWith(MappedCharset.FILE_MARK)) { 106 | charsetName = "UTF-8-BOM"; 107 | marked = true; 108 | } else if (charset.equals(StandardCharsets.UTF_16BE) 109 | && firstLine.startsWith(MappedCharset.FILE_MARK)) { 110 | marked = true; 111 | } else if (charset.equals(StandardCharsets.UTF_16LE) 112 | && firstLine.startsWith(MappedCharset.FILE_MARK)) { 113 | marked = true; 114 | } 115 | 116 | supportedCharset = new MappedCharset(charsetName); 117 | 118 | return new FirstLineInfo(supportedCharset, lineEnding, marked); 119 | } 120 | 121 | /** 122 | * Reads the first line of a file with it's termination sequence. A 123 | * termination sequence can be a line feed ('\n'), a carriage return ('\r'), 124 | * or a carriage return followed immediately by a linefeed. 125 | * 126 | * @param fo 127 | * @param charset 128 | * 129 | * @return First line of a file. 130 | */ 131 | private static String readFirstLineWithSeparator(FileObject fo, Charset charset) { 132 | StringBuilder sb = new StringBuilder(); 133 | String firstLine; 134 | int c; 135 | 136 | try ( 137 | InputStream is = fo.getInputStream(); 138 | InputStreamReader isr = new InputStreamReader(is, charset); 139 | BufferedReader br = new BufferedReader(isr)) { 140 | // Read first line 141 | while ((c = br.read()) != -1) { 142 | if (c == '\r') { 143 | // Mac OS 144 | sb.append('\r'); 145 | // Windows 146 | if (br.read() == '\n') { 147 | sb.append('\n'); 148 | } 149 | break; 150 | } else if (c == '\n') { 151 | // Mac OS X 152 | sb.append('\n'); 153 | break; 154 | } else { 155 | sb.append((char) c); 156 | } 157 | } 158 | 159 | firstLine = sb.toString(); 160 | 161 | } catch (IOException ex) { 162 | firstLine = ""; 163 | } 164 | 165 | return firstLine; 166 | } 167 | 168 | public static String trimTrailingWhitespace(Collection lines, String lineEnding) { 169 | StringBuilder sb = new StringBuilder(); 170 | for (String content : lines) { 171 | sb.append(content.replaceAll("\\s+$", "")); 172 | sb.append(lineEnding); 173 | } 174 | return sb.toString().trim(); 175 | } 176 | 177 | public static String trimTrailingWhitespace(String text, String lineEnding) { 178 | List lines = readLines(text); 179 | return trimTrailingWhitespace(lines, lineEnding); 180 | } 181 | 182 | public static String replaceLineEndings(Collection lines, String lineEnding) { 183 | StringBuilder sb = new StringBuilder(); 184 | for (String content : lines) { 185 | sb.append(content); 186 | sb.append(lineEnding); 187 | } 188 | return sb.toString().trim(); 189 | } 190 | 191 | public static String replaceLineEndings(String text, String lineEnding) { 192 | List lines = readLines(text); 193 | return replaceLineEndings(lines, lineEnding); 194 | } 195 | 196 | public static List readLines(String text) { 197 | List lines = new ArrayList<>(); 198 | 199 | try { 200 | lines = IOUtils.readLines(new StringReader(text)); 201 | } catch (IOException ex) { 202 | Exceptions.printStackTrace(ex); 203 | } 204 | 205 | return lines; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/reader/FileObjectReader.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.reader; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStreamReader; 5 | import java.io.Reader; 6 | import java.nio.charset.Charset; 7 | import org.netbeans.api.queries.FileEncodingQuery; 8 | import org.openide.filesystems.FileObject; 9 | import org.openide.util.Exceptions; 10 | 11 | public class FileObjectReader { 12 | 13 | public static synchronized String read(FileObject fo) { 14 | return FileObjectReader.read(fo, FileEncodingQuery.getEncoding(fo)); 15 | } 16 | 17 | public static synchronized String read(FileObject fo, Charset cs) { 18 | final StringBuilder sb = new StringBuilder(); 19 | final char[] buffer = new char[512]; 20 | 21 | try (Reader in = new InputStreamReader(fo.getInputStream(), cs)) { 22 | int len; 23 | while ((len = in.read(buffer)) > 0) { 24 | sb.append(buffer, 0, len); 25 | } 26 | } catch (IOException ex) { 27 | Exceptions.printStackTrace(ex); 28 | } 29 | 30 | return sb.toString(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/reader/StyledDocumentReader.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.reader; 2 | 3 | import com.welovecoding.nbeditorconfig.io.exception.FileAccessException; 4 | import java.io.BufferedReader; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.nio.charset.Charset; 8 | import java.util.ArrayList; 9 | import org.openide.filesystems.FileObject; 10 | 11 | public class StyledDocumentReader { 12 | 13 | public static ArrayList readFileObjectIntoLines(FileObject fo, Charset charset, String lineEnding) 14 | throws FileAccessException { 15 | ArrayList lines = new ArrayList<>(); 16 | String line; 17 | 18 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(fo.getInputStream(), charset))) { 19 | while ((line = reader.readLine()) != null) { 20 | lines.add(line); 21 | lines.add(lineEnding); 22 | } 23 | 24 | // Remove last line-break 25 | lines.remove(lines.size() - 1); 26 | } catch (IOException ex) { 27 | throw new FileAccessException("Document could not be read: " + ex.getMessage()); 28 | } 29 | 30 | return lines; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/writer/ConfigWriter.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.writer; 2 | 3 | import com.welovecoding.nbeditorconfig.io.model.MappedCharset; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.OutputStreamWriter; 7 | import java.io.Reader; 8 | import java.io.Writer; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | import org.openide.cookies.EditorCookie; 12 | import org.openide.cookies.OpenCookie; 13 | import org.openide.filesystems.FileAlreadyLockedException; 14 | import org.openide.filesystems.FileLock; 15 | import org.openide.filesystems.FileObject; 16 | import org.openide.loaders.DataObject; 17 | 18 | public class ConfigWriter { 19 | 20 | private static final Logger LOG = Logger.getLogger(ConfigWriter.class.getName()); 21 | 22 | public static void rewrite(DataObject dataObject, MappedCharset currentCharset, MappedCharset requestedCharset) throws IOException { 23 | FileObject fo = dataObject.getPrimaryFile(); 24 | 25 | // Read file 26 | final StringBuilder sb = new StringBuilder(); 27 | final char[] buffer = new char[512]; 28 | 29 | try (Reader in = new InputStreamReader(fo.getInputStream(), currentCharset.getCharset())) { 30 | int len; 31 | while ((len = in.read(buffer)) > 0) { 32 | sb.append(buffer, 0, len); 33 | } 34 | } catch (IOException ex) { 35 | LOG.log(Level.WARNING, "Cannot write file: {0}", ex.getMessage()); 36 | } 37 | 38 | // Write file 39 | FileLock lock = null; 40 | 41 | try { 42 | lock = fo.lock(); 43 | } catch (FileAlreadyLockedException ex) { 44 | // Try again later; perhaps display a warning dialog. 45 | LOG.log(Level.WARNING, "File is alreay locked: {0}", ex.getMessage()); 46 | } catch (IOException ex) { 47 | LOG.log(Level.WARNING, "Cannot lock file: {0}", ex.getMessage()); 48 | } 49 | 50 | try (Writer out = new OutputStreamWriter(fo.getOutputStream(lock), requestedCharset.getCharset())) { 51 | out.write(sb.toString()); 52 | } finally { 53 | if (lock != null) { 54 | lock.releaseLock(); 55 | } 56 | } 57 | 58 | final DataObject newDobj = DataObject.find(fo); 59 | final OpenCookie oc = newDobj.getLookup().lookup(OpenCookie.class); 60 | 61 | if (oc != null) { 62 | EditorCookie ec = dataObject.getLookup().lookup(EditorCookie.class); 63 | if (ec != null) { 64 | ec.close(); 65 | } 66 | oc.open(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/writer/FileObjectWriter.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.writer; 2 | 3 | import com.welovecoding.nbeditorconfig.io.exception.FileObjectLockException; 4 | import java.io.IOException; 5 | import java.io.OutputStreamWriter; 6 | import java.io.Writer; 7 | import java.nio.charset.Charset; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | import javax.swing.text.BadLocationException; 11 | import org.openide.cookies.EditorCookie; 12 | import org.openide.cookies.OpenCookie; 13 | import org.openide.filesystems.FileAlreadyLockedException; 14 | import org.openide.filesystems.FileLock; 15 | import org.openide.filesystems.FileObject; 16 | import org.openide.loaders.DataObject; 17 | import org.openide.text.NbDocument; 18 | import org.openide.util.Exceptions; 19 | import org.openide.util.UserQuestionException; 20 | 21 | public class FileObjectWriter { 22 | 23 | private static final Logger LOG = Logger.getLogger(FileObjectWriter.class.getName()); 24 | 25 | public static synchronized void writeWithAtomicAction(final DataObject dataObject, final Charset cs, final String content) { 26 | try { 27 | final FileObject fo = dataObject.getPrimaryFile(); 28 | final EditorCookie cookie = dataObject.getLookup().lookup(EditorCookie.class); 29 | NbDocument.runAtomicAsUser(cookie.openDocument(), new Runnable() { 30 | @Override 31 | public void run() { 32 | try (Writer out = new OutputStreamWriter(fo.getOutputStream(), cs)) { 33 | LOG.log(Level.INFO, "\u00ac Writing file"); 34 | out.write(content); 35 | } catch (IOException ex) { 36 | Exceptions.printStackTrace(ex); 37 | } 38 | } 39 | }); 40 | } catch (BadLocationException ex) { 41 | Exceptions.printStackTrace(ex); 42 | } catch (IOException ex) { 43 | System.out.println("Document cannot be opened."); 44 | Exceptions.printStackTrace(ex); 45 | } 46 | } 47 | 48 | public static synchronized void write(FileObject fo, Charset cs, String content) throws FileObjectLockException { 49 | FileLock lock = FileLock.NONE; 50 | 51 | try { 52 | LOG.log(Level.INFO, "\u00ac Trying to lock file: \"{0}\"", fo.getPath()); 53 | lock = fo.lock(); 54 | } catch (FileAlreadyLockedException ex) { 55 | throw new FileObjectLockException("File is already locked: " + ex.getMessage()); 56 | } catch (UserQuestionException ex2) { 57 | throw new FileObjectLockException("Lock cannot be obtained now: " + ex2.getMessage()); 58 | } catch (IOException ex3) { 59 | throw new FileObjectLockException("File cannot be locked: " + ex3.getMessage()); 60 | } 61 | 62 | try (Writer out = new OutputStreamWriter(fo.getOutputStream(lock), cs)) { 63 | LOG.log(Level.INFO, "\u00ac Writing file"); 64 | out.write(content); 65 | } catch (IOException ex) { 66 | Exceptions.printStackTrace(ex); 67 | } finally { 68 | LOG.log(Level.INFO, "\u00ac Released file lock"); 69 | lock.releaseLock(); 70 | } 71 | 72 | } 73 | 74 | public static synchronized void closeDocumentInEditor(DataObject dataObject) { 75 | final OpenCookie oc = dataObject.getLookup().lookup(OpenCookie.class); 76 | if (oc != null) { 77 | EditorCookie ec = dataObject.getLookup().lookup(EditorCookie.class); 78 | if (ec != null) { 79 | ec.close(); 80 | } 81 | } 82 | } 83 | 84 | public static synchronized void openDocumentInEditor(DataObject dataObject) { 85 | final OpenCookie oc = dataObject.getLookup().lookup(OpenCookie.class); 86 | if (oc != null) { 87 | oc.open(); 88 | } 89 | } 90 | 91 | public static synchronized void reopenDocumentInEditor(DataObject dataObject) { 92 | final OpenCookie oc = dataObject.getLookup().lookup(OpenCookie.class); 93 | if (oc != null) { 94 | EditorCookie ec = dataObject.getLookup().lookup(EditorCookie.class); 95 | if (ec != null) { 96 | ec.close(); 97 | } 98 | oc.open(); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/io/writer/StyledDocumentWriter.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.io.writer; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.Settings.ENCODING_SETTING; 4 | import com.welovecoding.nbeditorconfig.processor.FileInfo; 5 | import com.welovecoding.nbeditorconfig.io.exception.FileAccessException; 6 | import java.io.BufferedReader; 7 | import java.io.BufferedWriter; 8 | import java.io.ByteArrayInputStream; 9 | import java.io.File; 10 | import java.io.FileWriter; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.InputStreamReader; 14 | import java.io.Reader; 15 | import java.nio.charset.Charset; 16 | import java.nio.file.Files; 17 | import java.nio.file.Paths; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.logging.Level; 21 | import java.util.logging.Logger; 22 | import java.util.prefs.Preferences; 23 | import javax.swing.SwingUtilities; 24 | import javax.swing.text.BadLocationException; 25 | import javax.swing.text.Caret; 26 | import javax.swing.text.EditorKit; 27 | import javax.swing.text.StyledDocument; 28 | import org.netbeans.api.editor.mimelookup.MimeLookup; 29 | import org.netbeans.api.editor.mimelookup.MimePath; 30 | import org.netbeans.api.editor.settings.SimpleValueNames; 31 | import org.netbeans.lib.editor.util.swing.DocumentUtilities; 32 | import org.netbeans.modules.editor.indent.api.Reformat; 33 | import org.openide.cookies.EditorCookie; 34 | import org.openide.filesystems.FileLock; 35 | import org.openide.loaders.DataObject; 36 | import org.openide.util.Lookup; 37 | import org.openide.filesystems.FileObject; 38 | import org.openide.text.NbDocument; 39 | import org.openide.util.Utilities; 40 | 41 | public class StyledDocumentWriter { 42 | 43 | private static final Logger LOG = Logger.getLogger(StyledDocumentWriter.class.getName()); 44 | 45 | private static EditorCookie getEditorCookie(DataObject dataObject) { 46 | return dataObject.getLookup().lookup(EditorCookie.class); 47 | } 48 | 49 | public static EditorKit getEditorKit(DataObject dataObject) { 50 | FileObject fileObject = dataObject.getPrimaryFile(); 51 | String mimePath = fileObject.getMIMEType(); 52 | Lookup lookup = MimeLookup.getLookup(MimePath.parse(mimePath)); 53 | EditorKit kit = lookup.lookup(EditorKit.class); 54 | 55 | return kit; 56 | } 57 | 58 | public static ArrayList readFileObjectIntoLines(FileObject fo, Charset charset, String lineEnding) 59 | throws FileAccessException { 60 | ArrayList lines = new ArrayList<>(); 61 | String line; 62 | 63 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(fo.getInputStream(), charset))) { 64 | while ((line = reader.readLine()) != null) { 65 | lines.add(line); 66 | lines.add(lineEnding); 67 | } 68 | 69 | // Remove last line-break 70 | lines.remove(lines.size() - 1); 71 | } catch (IOException ex) { 72 | throw new FileAccessException("Document could not be read: " + ex.getMessage()); 73 | } 74 | 75 | return lines; 76 | } 77 | 78 | public static void writeFile(FileObject fo, Charset charset, String content) 79 | throws FileAccessException { 80 | File file = Utilities.toFile(fo.toURI()); 81 | 82 | try { 83 | Files.write(file.toPath(), content.getBytes(charset)); 84 | } catch (IOException ex) { 85 | throw new FileAccessException("Document could not be written: " + ex.getMessage()); 86 | } 87 | } 88 | 89 | public static void writeOnFile(FileObject fo, String content) 90 | throws FileAccessException { 91 | File file = Utilities.toFile(fo.toURI()); 92 | 93 | // write file 94 | try (FileWriter fileWriter = new FileWriter(file, true); 95 | BufferedWriter bufferWritter = new BufferedWriter(fileWriter)) { 96 | bufferWritter.write(content); 97 | } catch (IOException ex) { 98 | throw new FileAccessException("Document could not be written: " + ex.getMessage()); 99 | } 100 | } 101 | 102 | public static void writeOnFileWithLines(FileObject fo, Charset charset, List lines) 103 | throws FileAccessException { 104 | File file = Utilities.toFile(fo.toURI()); 105 | writeOnFileWithLines(file, charset, lines); 106 | } 107 | 108 | public static void writeOnFileWithLines(File file, Charset charset, List lines) 109 | throws FileAccessException { 110 | try { 111 | Files.write(file.toPath(), lines, charset); 112 | } catch (IOException ex) { 113 | throw new FileAccessException("Document could not be written: " + ex.getMessage()); 114 | } 115 | } 116 | 117 | public static void writeWithEditorKit(final FileInfo info) 118 | throws FileAccessException, IOException { 119 | 120 | final EditorCookie cookie = info.getCookie(); 121 | final StyledDocument openedDocument = cookie.openDocument(); 122 | final EditorKit kit = getEditorKit(info.getDataObject()); 123 | 124 | try (InputStream is = new ByteArrayInputStream(info.getContentAsBytes())) { 125 | // Backup caret position 126 | final Caret caret = info.getCaret(); 127 | if (caret == null) { 128 | LOG.log(Level.WARNING, "Could not get Caret"); 129 | return; 130 | } 131 | final int caretPosition = info.getCurrentCaretPosition(); 132 | Runnable runner = new Runnable() { 133 | @Override 134 | public void run() { 135 | NbDocument.runAtomic(openedDocument, new Runnable() { 136 | @Override 137 | public void run() { 138 | try { 139 | // Wipe document 140 | cookie.getDocument().remove(0, cookie.getDocument().getLength()); 141 | 142 | LOG.log(Level.INFO, "Write to \"is\": {0}", is); 143 | LOG.log(Level.INFO, "Write to \"document\": {0}", cookie.getDocument()); 144 | 145 | // Read input stream into the document (which is a "write" operation) 146 | try (Reader reader = new InputStreamReader(is, info.getCharset())) { 147 | kit.read(reader, cookie.getDocument(), 0); 148 | } 149 | cookie.saveDocument(); 150 | 151 | info.getFileObject().setAttribute(ENCODING_SETTING, info.getCharset().name()); 152 | 153 | // Reset caret positon 154 | // caretPosition -= info.getCaretOffset(); 155 | if (caretPosition < cookie.getDocument().getLength()) { 156 | LOG.log(Level.INFO, "Moving caret position from {0} to: {1} / {2}", 157 | new Object[]{info.getCaretOffset(), caretPosition, cookie.getDocument().getLength()}); 158 | caret.setDot(caretPosition); 159 | } 160 | 161 | // Reformat code (to apply ident size & styles) 162 | // TODO: Do this only if CodeStylePreferences have been changed 163 | 164 | Preferences prefs = MimeLookup.getLookup(DocumentUtilities.getMimeType(cookie.getDocument())).lookup(Preferences.class); 165 | 166 | if (prefs.getBoolean(SimpleValueNames.ON_SAVE_USE_GLOBAL_SETTINGS, true)) { 167 | prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class); 168 | } 169 | 170 | String policy = prefs.get(SimpleValueNames.ON_SAVE_REFORMAT, null); 171 | 172 | if (!"never".equals(policy)) { 173 | Reformat reformat = Reformat.get(cookie.getDocument()); 174 | reformat.lock(); 175 | 176 | try { 177 | reformat.reformat(0, cookie.getDocument().getLength()); 178 | } catch (BadLocationException ex) { 179 | LOG.log(Level.SEVERE, "AutoFormat on document not possible: {0}", ex.getMessage()); 180 | } finally { 181 | reformat.unlock(); 182 | // Save document after reformat 183 | cookie.saveDocument(); 184 | } 185 | } 186 | } catch (BadLocationException | IOException ex) { 187 | LOG.log(Level.SEVERE, "Document could not be saved: {0}", ex.getMessage()); 188 | } 189 | } 190 | }); 191 | } 192 | }; 193 | 194 | if (SwingUtilities.isEventDispatchThread()) { 195 | runner.run(); 196 | } else { 197 | SwingUtilities.invokeLater(runner); 198 | } 199 | } catch (IOException ex) { 200 | LOG.log(Level.SEVERE, "Could not load content of document: {0}", ex.getMessage()); 201 | } 202 | } 203 | 204 | public static void writeWithFilesystemAPI(FileInfo info, List lines) 205 | throws FileAccessException { 206 | FileLock lock = FileLock.NONE; 207 | FileObject fo = info.getFileObject(); 208 | 209 | // write file 210 | try { 211 | lock = fo.lock(); 212 | if (fo.isLocked()) { 213 | Files.write(Paths.get(fo.toURI()), lines, info.getCharset()); 214 | fo.setAttribute(ENCODING_SETTING, info.getCharset().name()); 215 | } 216 | } catch (IOException ex) { 217 | throw new FileAccessException("Document could not be written: " + ex.getMessage()); 218 | } finally { 219 | lock.releaseLock(); 220 | } 221 | } 222 | 223 | public static void writeWithString(FileInfo info) 224 | throws FileAccessException { 225 | EditorCookie cookie = getEditorCookie(info.getDataObject()); 226 | StyledDocument document = null; 227 | int caretPosition; 228 | 229 | try { 230 | document = cookie.openDocument(); 231 | } catch (IOException ex) { 232 | throw new FileAccessException("Document could not be loaded: " + ex.getMessage()); 233 | } 234 | 235 | try { 236 | // save caret position 237 | Caret caret = cookie.getOpenedPanes()[0].getCaret(); 238 | caretPosition = caret.getDot(); 239 | 240 | // write file 241 | document.remove(0, document.getLength()); 242 | document.insertString(0, info.getContentAsString(), null); 243 | cookie.saveDocument(); 244 | info.getFileObject().setAttribute(ENCODING_SETTING, info.getCharset().name()); 245 | 246 | // reset the caret positon 247 | if (caretPosition < document.getLength()) { 248 | caret.setDot(caretPosition); 249 | } 250 | } catch (BadLocationException | IOException ex) { 251 | throw new FileAccessException("Document could not be written: " + ex.getMessage()); 252 | } 253 | } 254 | 255 | } 256 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/listener/EditorConfigChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.listener; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.LISTENER_LOG_LEVEL; 4 | import java.util.Collections; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | import org.netbeans.api.project.Project; 8 | import org.openide.filesystems.FileAttributeEvent; 9 | import org.openide.filesystems.FileChangeAdapter; 10 | import org.openide.filesystems.FileEvent; 11 | import org.openide.filesystems.FileObject; 12 | import org.openide.filesystems.FileRenameEvent; 13 | 14 | /** 15 | * This kind of listener is attached to editorconfig files within a project. 16 | * When this listener is attached to an editorconfig a FileChangeListener will 17 | * be attached to all files in the folder with the editorconfig and subsequent 18 | * ones. http://bits.netbeans.org/dev/javadoc/ 19 | */ 20 | public class EditorConfigChangeListener extends FileChangeAdapter { 21 | 22 | private static final Logger LOG = Logger.getLogger(EditorConfigChangeListener.class.getName()); 23 | private final Project project; 24 | private final FileObject editorConfigFileObject; 25 | private final FileChangeListener subsequentFilesListener; 26 | 27 | static { 28 | LOG.setLevel(LISTENER_LOG_LEVEL); 29 | } 30 | 31 | public EditorConfigChangeListener(Project project, FileObject editorConfigFileObject) { 32 | this.project = project; 33 | this.editorConfigFileObject = editorConfigFileObject; 34 | 35 | LOG.log(Level.INFO, "Attached EditorConfigChangeListener to: {0}", editorConfigFileObject.getPath()); 36 | this.subsequentFilesListener = new FileChangeListener(project, editorConfigFileObject); 37 | editorConfigFileObject.getParent().addRecursiveListener(subsequentFilesListener); 38 | } 39 | 40 | @Override 41 | public void fileDeleted(FileEvent event) { 42 | super.fileDeleted(event); 43 | LOG.log(Level.INFO, "Deleted file: {0}", event.getFile().getPath()); 44 | event.getFile().getParent().removeRecursiveListener(subsequentFilesListener); 45 | event.getFile().removeFileChangeListener(this); 46 | } 47 | 48 | @Override 49 | public void fileChanged(FileEvent event) { 50 | super.fileChanged(event); 51 | LOG.log(Level.INFO, "EditorConfigs content changed: {0}", event.getFile().getPath()); 52 | } 53 | 54 | /** 55 | * Propagates change on the editorconfig file to every observed file in this 56 | * project 57 | */ 58 | private void propagateChanges() { 59 | for (FileObject fo : Collections.list(editorConfigFileObject.getParent().getChildren(true))) { 60 | LOG.log(Level.INFO, "Updating subsequent file: {0}", fo.getPath()); 61 | subsequentFilesListener.fileChanged(new FileEvent(fo)); 62 | } 63 | } 64 | 65 | @Override 66 | public void fileFolderCreated(FileEvent event) { 67 | super.fileFolderCreated(event); 68 | LOG.log(Level.FINE, "Created folder: {0}", event.getFile().getPath()); 69 | //TODO search for editor-configs and attach listeners 70 | } 71 | 72 | /** 73 | * Method is triggered when content has changed and it's possible to display 74 | * content in NetBeans. Method is also triggered when project will be opened. 75 | * 76 | * @param event Event for listening on filesystem changes 77 | */ 78 | @Override 79 | public void fileDataCreated(FileEvent event) { 80 | super.fileDataCreated(event); 81 | LOG.log(Level.FINE, "fileDataCreated: {0}", event.getFile().getPath()); 82 | } 83 | 84 | @Override 85 | public void fileAttributeChanged(FileAttributeEvent event) { 86 | super.fileAttributeChanged(event); 87 | LOG.log(Level.FINE, "Attribute changed: {0}", event.getFile().getPath()); 88 | } 89 | 90 | @Override 91 | public void fileRenamed(FileRenameEvent event) { 92 | super.fileRenamed(event); 93 | LOG.log(Level.FINE, "Renamed file: {0}", event.getFile().getPath()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/listener/FileChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.listener; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.LISTENER_LOG_LEVEL; 4 | import com.welovecoding.nbeditorconfig.processor.EditorConfigProcessor; 5 | import com.welovecoding.nbeditorconfig.processor.SmartSkip; 6 | import com.welovecoding.nbeditorconfig.processor.WriteEditorAction; 7 | import com.welovecoding.nbeditorconfig.processor.WriteStringToFileAction; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | import org.netbeans.api.project.Project; 11 | import org.openide.filesystems.FileAttributeEvent; 12 | import org.openide.filesystems.FileChangeAdapter; 13 | import org.openide.filesystems.FileEvent; 14 | import org.openide.filesystems.FileObject; 15 | import org.openide.filesystems.FileRenameEvent; 16 | import org.openide.loaders.DataObject; 17 | import org.openide.loaders.DataObjectNotFoundException; 18 | import org.openide.util.Exceptions; 19 | 20 | /** 21 | * http://bits.netbeans.org/dev/javadoc/ 22 | */ 23 | public class FileChangeListener extends FileChangeAdapter { 24 | 25 | private static final Logger LOG = Logger.getLogger(FileChangeListener.class.getName()); 26 | private final Project project; 27 | private final FileObject editorConfigFileObject; 28 | private final EditorConfigProcessor editorConfigProcessor = new EditorConfigProcessor(); 29 | 30 | static { 31 | LOG.setLevel(LISTENER_LOG_LEVEL); 32 | } 33 | 34 | public FileChangeListener(Project project, FileObject editorConfigFileObject) { 35 | this.project = project; 36 | this.editorConfigFileObject = editorConfigFileObject; 37 | LOG.log(Level.INFO, "[EC for {0}] Attached FileChangeListener to: {1}", new Object[]{editorConfigFileObject.getPath(), editorConfigFileObject.getParent().getPath()}); 38 | } 39 | 40 | @Override 41 | public void fileDeleted(FileEvent event) { 42 | super.fileDeleted(event); 43 | LOG.log(Level.INFO, "[EC for {0}] Deleted file: {1}", new Object[]{editorConfigFileObject.getPath(), event.getFile().getPath()}); 44 | event.getFile().removeRecursiveListener(this); 45 | } 46 | 47 | @Override 48 | public void fileChanged(FileEvent event) { 49 | super.fileChanged(event); 50 | String path = event.getFile().getPath(); 51 | 52 | LOG.log(Level.INFO, "[EC for {0}] File content changed: {1}", new Object[]{editorConfigFileObject.getPath(), path}); 53 | 54 | if (!event.firedFrom(new WriteEditorAction()) && !event.firedFrom(new WriteStringToFileAction())) { 55 | if (isCandidateForProcessing(event)) { 56 | try { 57 | LOG.log(Level.INFO, "[EC for {0}] Applying rules"); 58 | editorConfigProcessor.applyRulesToFile(DataObject.find(event.getFile())); 59 | } catch (DataObjectNotFoundException ex) { 60 | Exceptions.printStackTrace(ex); 61 | } catch (Exception ex) { 62 | Exceptions.printStackTrace(ex); 63 | } 64 | } else { 65 | LOG.log(Level.INFO, "[EC for {0}] Rules will not be applied to: {1}", new Object[]{editorConfigFileObject.getPath(), path}); 66 | } 67 | } else { 68 | LOG.log(Level.INFO, "[EC for {0}] Rules will not be applied to: {1} - Change triggered by EditorConfig plugin", new Object[]{editorConfigFileObject.getPath(), path}); 69 | } 70 | 71 | } 72 | 73 | private boolean isCandidateForProcessing(FileEvent event) { 74 | FileObject file = event.getFile(); 75 | 76 | boolean applyRules = false; 77 | boolean isntFolder = !file.isFolder(); 78 | boolean isUnexpected = !event.isExpected(); 79 | boolean isntSkipped = !SmartSkip.skipDirectory(file); 80 | 81 | if (isUnexpected && isntFolder && isntSkipped) { 82 | applyRules = true; 83 | } 84 | 85 | return applyRules; 86 | } 87 | 88 | @Override 89 | public void fileFolderCreated(FileEvent event) { 90 | super.fileFolderCreated(event); 91 | LOG.log(Level.FINE, "[EC for {0}] Created folder: {1}", new Object[]{editorConfigFileObject.getPath(), event.getFile().getPath()}); 92 | //TODO search for editor-configs and attach listeners 93 | } 94 | 95 | /** 96 | * Method is triggered when content has changed and it's possible to display 97 | * content in NetBeans. Method is also triggered when project will be opened. 98 | * 99 | * @param event Event for listening on filesystem changes 100 | */ 101 | @Override 102 | public void fileDataCreated(FileEvent event) { 103 | super.fileDataCreated(event); 104 | FileObject primaryFile = event.getFile(); 105 | LOG.log(Level.FINE, "[EC for {0}] Added new file to project: {1} (MIME type: {2})", 106 | new Object[]{editorConfigFileObject.getPath(), primaryFile.getPath(), primaryFile.getMIMEType()}); 107 | } 108 | 109 | @Override 110 | public void fileAttributeChanged(FileAttributeEvent event) { 111 | super.fileAttributeChanged(event); 112 | LOG.log(Level.FINE, "[EC for {0}] Attribute changed: {1}", new Object[]{editorConfigFileObject.getPath(), event.getFile().getPath()}); 113 | } 114 | 115 | @Override 116 | public void fileRenamed(FileRenameEvent event) { 117 | super.fileRenamed(event); 118 | LOG.log(Level.FINE, "[EC for {0}] Renamed file: {1}", new Object[]{editorConfigFileObject.getPath(), event.getFile().getPath()}); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/listener/Installer.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.listener; 2 | 3 | import java.awt.event.ActionEvent; 4 | import java.awt.event.ActionListener; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | import javax.swing.Icon; 8 | import javax.swing.plaf.metal.MetalIconFactory; 9 | import org.netbeans.api.java.platform.JavaPlatform; 10 | import org.netbeans.api.java.platform.JavaPlatformManager; 11 | import org.netbeans.api.java.platform.Specification; 12 | import org.openide.DialogDisplayer; 13 | import org.openide.NotifyDescriptor; 14 | import org.openide.awt.NotificationDisplayer; 15 | import org.openide.modules.ModuleInstall; 16 | import org.openide.modules.SpecificationVersion; 17 | import org.openide.util.NbBundle; 18 | 19 | public class Installer extends ModuleInstall { 20 | 21 | /** 22 | * Class Logger 23 | */ 24 | private static final Logger INSTALLER_LOGGER = Logger.getLogger(Installer.class.getName()); 25 | 26 | /** 27 | * A representation of the minimum required version of Java as a 28 | * {@link SpecificationVersion} compatible "Dewey-decimal version". 29 | */ 30 | private static final String MIN_JAVA_VERSION = "1.7.0"; 31 | 32 | @Override 33 | public void restored() { 34 | if (isJavaVersionIncompatiable()) { 35 | registerIncompatabilityNotification(); 36 | } 37 | } 38 | 39 | /** 40 | * Displays a notification in the default notification UI containing 41 | * information on the plugin's incompatibility with the current IDE's version 42 | * of the JVM 43 | */ 44 | private static void registerIncompatabilityNotification() { 45 | 46 | //These are the title, message and icon displayed in the notification area and within popup if user interacts with notification 47 | final String incompatTitle = NbBundle.getMessage(Installer.class, "wlc-nbeditorconfig-version-error-title"); 48 | final String incompatMessage = NbBundle.getMessage(Installer.class, "wlc-nbeditorconfig-version-error-message"); 49 | final Icon incompatIcon = new MetalIconFactory.FileIcon16(); 50 | 51 | INSTALLER_LOGGER.log(Level.SEVERE, incompatMessage); 52 | 53 | final ActionListener notificationInteractionListener = new ActionListener() { 54 | @Override 55 | public void actionPerformed(ActionEvent e) { 56 | NotifyDescriptor.Message pluginIncompatiableMessage = new NotifyDescriptor.Message(incompatMessage, NotifyDescriptor.ERROR_MESSAGE); 57 | DialogDisplayer.getDefault().notify(pluginIncompatiableMessage); 58 | } 59 | }; 60 | 61 | NotificationDisplayer.getDefault().notify(incompatTitle, incompatIcon, incompatMessage, notificationInteractionListener); 62 | 63 | } 64 | 65 | /** 66 | * Evaluates the compatibility of the current JVM running the IDE against 67 | * {@link #MIN_JAVA_VERSION}. 68 | * 69 | * @return True if the JVM running the IDE is not compatible with this 70 | * plugin. 71 | */ 72 | private boolean isJavaVersionIncompatiable() { 73 | SpecificationVersion javaVersion = getJavaPlatformVersion(); 74 | 75 | INSTALLER_LOGGER.log(Level.FINE, "Found Java version: " + javaVersion); 76 | INSTALLER_LOGGER.log(Level.FINE, "Expected Java version: " + MIN_JAVA_VERSION); 77 | 78 | return javaVersion.compareTo(getMinimumRequiredJavaVersion()) < 0; 79 | } 80 | 81 | /** 82 | * Convenience/readability method for generating a 83 | * {@link SpecificationVersion} containing the version information equivalent 84 | * to {@link #MIN_JAVA_VERSION}. 85 | * 86 | * @return 87 | */ 88 | private static SpecificationVersion getMinimumRequiredJavaVersion() { 89 | return new SpecificationVersion(MIN_JAVA_VERSION); 90 | } 91 | 92 | /** 93 | * Determines the version of the Java environment the IDE is running within 94 | * utilizing {@link JavaPlatform}. 95 | * 96 | * @return A {@link SpecificationVersion} instance containing the version 97 | * information of the IDE JVM using a "Dewey-decimal format". 98 | */ 99 | private static SpecificationVersion getJavaPlatformVersion() { 100 | JavaPlatformManager idePlatformManager = JavaPlatformManager.getDefault(); 101 | JavaPlatform idePlatform = idePlatformManager.getDefaultPlatform(); 102 | Specification idePlatformSpec = idePlatform.getSpecification(); 103 | 104 | return idePlatformSpec.getVersion(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/listener/ListenerAttacher.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.listener; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.LISTENER_LOG_LEVEL; 4 | import static com.welovecoding.nbeditorconfig.config.Settings.DEFAULT_FILE_NAME; 5 | import static com.welovecoding.nbeditorconfig.config.Settings.EXTENSION; 6 | import static com.welovecoding.nbeditorconfig.listener.ProjectOpenCloseListener.LISTENER_REGISTRY; 7 | import com.welovecoding.nbeditorconfig.processor.SmartSkip; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | import org.netbeans.api.project.Project; 11 | import org.openide.filesystems.FileObject; 12 | 13 | public class ListenerAttacher { 14 | 15 | private static final Logger LOG = Logger.getLogger(ListenerAttacher.class.getName()); 16 | 17 | static { 18 | LOG.setLevel(LISTENER_LOG_LEVEL); 19 | } 20 | 21 | /** 22 | * Recursively attach listeners to folders containing a .editorconfig file. 23 | * 24 | * @param file file or folder to attach listener 25 | * @param project the project the file is related to 26 | */ 27 | public static void attachListeners(FileObject file, Project project) { 28 | if (project.getProjectDirectory().equals(file)) { 29 | ProjectChangeListener projectChangeListener = new ProjectChangeListener(project); 30 | LISTENER_REGISTRY.put(file, projectChangeListener); 31 | file.addRecursiveListener(projectChangeListener); 32 | } 33 | 34 | if (file.isFolder()) { 35 | if (SmartSkip.skipDirectory(file)) { 36 | LOG.log(Level.INFO, "\u00ac Skipped directory: {0}", file.getPath()); 37 | } else { 38 | for (FileObject child : file.getChildren()) { 39 | attachListeners(child, project); 40 | } 41 | } 42 | } else { 43 | if (file.getExt().equals(EXTENSION) || file.getName().equals(DEFAULT_FILE_NAME)) { 44 | EditorConfigChangeListener editorConfigChangeListener = new EditorConfigChangeListener(project, file); 45 | LISTENER_REGISTRY.put(file, editorConfigChangeListener); 46 | file.addFileChangeListener(editorConfigChangeListener); 47 | LOG.log(Level.INFO, "\u00ac Found EditorConfig: {0}", file.getPath()); 48 | } else { 49 | LOG.log(Level.FINE, "\u00ac No EditorConfig Found: {0}", file.getPath()); 50 | } 51 | } 52 | } 53 | 54 | public static void removeListeners(FileObject file, Project project) { 55 | if (project.getProjectDirectory().equals(file)) { 56 | file.removeRecursiveListener(LISTENER_REGISTRY.get(file)); 57 | } 58 | 59 | if (file.isFolder()) { 60 | if (SmartSkip.skipDirectory(file)) { 61 | LOG.log(Level.INFO, "\u00ac Skipped directory: {0}", file.getPath()); 62 | } else { 63 | for (FileObject child : file.getChildren()) { 64 | removeListeners(child, project); 65 | } 66 | } 67 | } else { 68 | if (file.getExt().equals(EXTENSION) || file.getName().equals(DEFAULT_FILE_NAME)) { 69 | file.removeFileChangeListener(LISTENER_REGISTRY.get(file)); 70 | LOG.log(Level.INFO, "\u00ac Found EditorConfig: {0}", file.getPath()); 71 | } else { 72 | LOG.log(Level.FINE, "\u00ac No EditorConfig Found: {0}", file.getPath()); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/listener/ProjectChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.listener; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.LISTENER_LOG_LEVEL; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | import org.netbeans.api.project.Project; 7 | import org.openide.filesystems.FileAttributeEvent; 8 | import org.openide.filesystems.FileChangeAdapter; 9 | import org.openide.filesystems.FileEvent; 10 | import org.openide.filesystems.FileRenameEvent; 11 | 12 | /** 13 | * http://bits.netbeans.org/dev/javadoc/ 14 | */ 15 | public class ProjectChangeListener extends FileChangeAdapter { 16 | 17 | private static final Logger LOG = Logger.getLogger(ProjectChangeListener.class.getName()); 18 | private final Project project; 19 | 20 | static { 21 | LOG.setLevel(LISTENER_LOG_LEVEL); 22 | } 23 | 24 | public ProjectChangeListener(Project project) { 25 | this.project = project; 26 | } 27 | 28 | @Override 29 | public void fileFolderCreated(FileEvent event) { 30 | super.fileFolderCreated(event); 31 | LOG.log(Level.INFO, "Created folder: {0}", event.getFile().getPath()); 32 | ListenerAttacher.attachListeners(event.getFile(), project); 33 | } 34 | 35 | /** 36 | * Method is triggered when content has changed and it's possible to display 37 | * content in NetBeans. Method is also triggered when project will be opened. 38 | * 39 | * @param event Event for listening on filesystem changes 40 | */ 41 | @Override 42 | public void fileDataCreated(FileEvent event) { 43 | super.fileDataCreated(event); 44 | LOG.log(Level.INFO, "fileDataCreated: {0}", event.getFile().getPath()); 45 | ListenerAttacher.attachListeners(event.getFile(), project); 46 | } 47 | 48 | @Override 49 | public void fileAttributeChanged(FileAttributeEvent event) { 50 | super.fileAttributeChanged(event); 51 | LOG.log(Level.FINE, "Attribute changed: {0}", event.getFile().getPath()); 52 | } 53 | 54 | @Override 55 | public void fileRenamed(FileRenameEvent event) { 56 | super.fileRenamed(event); 57 | LOG.log(Level.FINE, "Renamed file: {0}", event.getFile().getPath()); 58 | } 59 | 60 | @Override 61 | public void fileDeleted(FileEvent event) { 62 | super.fileDeleted(event); 63 | LOG.log(Level.FINE, "Deleted file: {0}", event.getFile().getPath()); 64 | } 65 | 66 | @Override 67 | public void fileChanged(FileEvent event) { 68 | super.fileChanged(event); 69 | LOG.log(Level.FINE, "File content changed: {0}", event.getFile().getPath()); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/listener/ProjectHookLookup.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.listener; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.LISTENER_LOG_LEVEL; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | import org.netbeans.api.project.Project; 7 | import org.netbeans.spi.project.LookupProvider; 8 | import org.openide.util.Lookup; 9 | import org.openide.util.lookup.Lookups; 10 | 11 | /** 12 | * Project types supported by NetBeans IDE: 13 | * https://platform.netbeans.org/tutorials/nbm-projectextension.html 14 | * 15 | * Module overview: 16 | * http://bits.netbeans.org/nexus/content/groups/netbeans/org/netbeans/modules/ 17 | * 18 | * The module type can be found in the "project.xml" file of a NetBeans project. 19 | * Dots in the project type must be replaced with dashes. 20 | */ 21 | @LookupProvider.Registration(projectType = { 22 | "org-netbeans-modules-ant-freeform", 23 | "org-netbeans-modules-apisupport-project", 24 | "org-netbeans-modules-apisupport-project-suite", 25 | "org-netbeans-modules-j2ee-archiveproject", 26 | "org-netbeans-modules-j2ee-clientproject", 27 | "org-netbeans-modules-j2ee-earproject", 28 | "org-netbeans-modules-j2ee-ejbjarproject", 29 | "org-netbeans-modules-java-j2seproject", 30 | "org-netbeans-modules-maven", 31 | "org-netbeans-modules-php-project", 32 | "org-netbeans-modules-web-clientproject", 33 | "org-netbeans-modules-web-project" 34 | }) 35 | /** 36 | * Listener for newly opened Projects. 37 | */ 38 | public class ProjectHookLookup implements LookupProvider { 39 | 40 | private static final Logger LOG = Logger.getLogger(ProjectHookLookup.class.getName()); 41 | 42 | static { 43 | LOG.setLevel(LISTENER_LOG_LEVEL); 44 | } 45 | 46 | @Override 47 | public Lookup createAdditionalLookup(Lookup lookup) { 48 | final ClassLoader cl = Thread.currentThread().getContextClassLoader(); 49 | try { 50 | Thread.currentThread().setContextClassLoader(ProjectHookLookup.class.getClassLoader()); 51 | final Project project = lookup.lookup(Project.class); 52 | String projectName = project.getProjectDirectory().getName(); 53 | 54 | LOG.log(Level.INFO, "Setup hooks for: {0}", projectName); 55 | final ProjectOpenCloseListener listener = new ProjectOpenCloseListener(project); 56 | return Lookups.fixed(listener); 57 | } finally { 58 | Thread.currentThread().setContextClassLoader(cl); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/listener/ProjectOpenCloseListener.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.listener; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.LISTENER_LOG_LEVEL; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | import org.netbeans.api.project.Project; 9 | import org.netbeans.spi.project.ui.ProjectOpenedHook; 10 | import org.openide.filesystems.FileChangeAdapter; 11 | import org.openide.filesystems.FileObject; 12 | 13 | public class ProjectOpenCloseListener extends ProjectOpenedHook { 14 | 15 | public static final Map LISTENER_REGISTRY = new ConcurrentHashMap<>(); 16 | 17 | private static final Logger LOG = Logger.getLogger(ProjectOpenCloseListener.class.getName()); 18 | private Project project; 19 | 20 | static { 21 | LOG.setLevel(LISTENER_LOG_LEVEL); 22 | } 23 | 24 | public ProjectOpenCloseListener() { 25 | super(); 26 | } 27 | 28 | public ProjectOpenCloseListener(Project project) { 29 | this(); 30 | this.project = project; 31 | } 32 | 33 | @Override 34 | protected void projectOpened() { 35 | FileObject projectFileObject = project.getProjectDirectory(); 36 | LOG.log(Level.INFO, "Opened project: {0}", projectFileObject.getName()); 37 | ListenerAttacher.attachListeners(projectFileObject, project); 38 | } 39 | 40 | @Override 41 | protected void projectClosed() { 42 | FileObject projectFileObject = project.getProjectDirectory(); 43 | LOG.log(Level.FINE, "Closed project: {0}", project.getProjectDirectory().getName()); 44 | ListenerAttacher.removeListeners(projectFileObject, project); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/mapper/EditorConfigPropertyMapper.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.mapper; 2 | 3 | import com.welovecoding.nbeditorconfig.config.LoggerSettings; 4 | import com.welovecoding.nbeditorconfig.io.model.MappedCharset; 5 | import com.welovecoding.nbeditorconfig.io.model.SupportedCharsets; 6 | import com.welovecoding.nbeditorconfig.model.EditorConfigConstant; 7 | import com.welovecoding.nbeditorconfig.model.MappedEditorConfig; 8 | import java.io.File; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.logging.Level; 13 | import java.util.logging.Logger; 14 | import org.editorconfig.core.EditorConfig; 15 | import org.editorconfig.core.EditorConfigException; 16 | import org.netbeans.editor.BaseDocument; 17 | 18 | public class EditorConfigPropertyMapper { 19 | 20 | private static final Logger LOG = Logger.getLogger(EditorConfigPropertyMapper.class.getName()); 21 | 22 | static { 23 | LOG.setLevel(LoggerSettings.MAPPER_LOG_LEVEL); 24 | } 25 | 26 | public static synchronized MappedEditorConfig createEditorConfig(String filePath) { 27 | return createEditorConfig(filePath, null); 28 | } 29 | 30 | public static synchronized MappedEditorConfig createEditorConfig(File file, String configName) { 31 | return createEditorConfig(file.getAbsolutePath(), configName); 32 | } 33 | 34 | /** 35 | * Keyed Rules
36 | * "charset": "utf-8"
37 | * "end_of_line": "lf"
38 | * "indent_size": "2"
39 | * "indent_style": "space"
40 | * "insert_final_newline": "true"
41 | * "tab_width": "2"
42 | * "trim_trailing_whitespace": "true"
43 | */ 44 | private static synchronized MappedEditorConfig createEditorConfig(String filePath, String configName) { 45 | EditorConfig ec; 46 | 47 | if (configName == null) { 48 | ec = new EditorConfig(); 49 | } else { 50 | ec = new EditorConfig(configName, EditorConfig.VERSION); 51 | } 52 | 53 | MappedEditorConfig mappedConfig = new MappedEditorConfig(); 54 | 55 | List rules = new ArrayList<>(); 56 | HashMap keyedRules = new HashMap<>(); 57 | 58 | try { 59 | // The "EditorConfig.java" method "filenameMatches" needs forward slashes 60 | rules = ec.getProperties(filePath.replace("\\", "/")); 61 | } catch (EditorConfigException ex) { 62 | LOG.log(Level.SEVERE, ex.getMessage()); 63 | } 64 | 65 | for (EditorConfig.OutPair rule : rules) { 66 | LOG.log(Level.INFO, rule.getKey().toLowerCase()); 67 | keyedRules.put(rule.getKey().toLowerCase(), rule.getVal().toLowerCase()); 68 | } 69 | 70 | for (String key : keyedRules.keySet()) { 71 | String value = keyedRules.get(key); 72 | 73 | switch (key) { 74 | case "charset": 75 | MappedCharset charset = mapCharset(value); 76 | mappedConfig.setCharset(charset); 77 | break; 78 | case "end_of_line": 79 | String lineEnding = mapLineEnding(value); 80 | mappedConfig.setEndOfLine(lineEnding); 81 | break; 82 | case "indent_size": 83 | Integer indentSize; 84 | try { 85 | indentSize = Integer.valueOf(value); 86 | } catch (NumberFormatException ex) { 87 | // "indent_size" is "tab" 88 | indentSize = -2; 89 | } 90 | mappedConfig.setIndentSize(indentSize); 91 | break; 92 | case "indent_style": 93 | mappedConfig.setIndentStyle(value); 94 | break; 95 | case "insert_final_newline": 96 | boolean insertFinalNewLine = Boolean.valueOf(value); 97 | mappedConfig.setInsertFinalNewLine(insertFinalNewLine); 98 | break; 99 | case "tab_width": 100 | int tabWidth = Integer.valueOf(value); 101 | mappedConfig.setTabWidth(tabWidth); 102 | break; 103 | case "trim_trailing_whitespace": 104 | boolean trimTrailingWhiteSpace = Boolean.valueOf(value); 105 | mappedConfig.setTrimTrailingWhiteSpace(trimTrailingWhiteSpace); 106 | break; 107 | default: 108 | LOG.log(Level.INFO, "Unknown EditorConfig property: {0} ({1})", 109 | new Object[]{key, value}); 110 | } 111 | } 112 | 113 | return mappedConfig; 114 | } 115 | 116 | protected static synchronized String getFileMark(String editorConfigCharset) { 117 | String fileMark = null; 118 | 119 | if (editorConfigCharset != null) { 120 | switch (editorConfigCharset) { 121 | case EditorConfigConstant.CHARSET_UTF_8_BOM: 122 | fileMark = "\uFEFF"; // "EF BB BF" 123 | break; 124 | case EditorConfigConstant.CHARSET_UTF_16_BE: 125 | fileMark = "\uFEFF"; // "FE FF" 126 | break; 127 | case EditorConfigConstant.CHARSET_UTF_16_LE: 128 | fileMark = "\uFEFF"; // "FF FE" (reverse) 129 | break; 130 | } 131 | } 132 | 133 | return fileMark; 134 | } 135 | 136 | /** 137 | * Maps a charset string into a charset object. 138 | * 139 | * @param ecCharset String value for these charsets: "latin1", "utf-8", 140 | * "utf-8-bom", "utf-16be" or "utf-16le" 141 | * 142 | * @return the mapped charset 143 | */ 144 | protected static synchronized MappedCharset mapCharset(String ecCharset) { 145 | MappedCharset charset; 146 | 147 | if (ecCharset == null) { 148 | return SupportedCharsets.UTF_8; 149 | } 150 | 151 | switch (ecCharset) { 152 | case "latin1": 153 | charset = SupportedCharsets.LATIN_1; 154 | break; 155 | case "utf-8": 156 | charset = SupportedCharsets.UTF_8; 157 | break; 158 | case "utf-8-bom": 159 | charset = SupportedCharsets.UTF_8_BOM; 160 | break; 161 | case "utf-16be": 162 | charset = SupportedCharsets.UTF_16_BE; 163 | break; 164 | case "utf-16le": 165 | charset = SupportedCharsets.UTF_16_LE; 166 | break; 167 | default: 168 | charset = SupportedCharsets.UTF_8; 169 | LOG.log(Level.INFO, "Unsported charset: {0}. Using: {1}.", 170 | new Object[]{ecCharset, charset.getName()}); 171 | break; 172 | } 173 | 174 | return charset; 175 | } 176 | 177 | protected static synchronized String mapLineEnding(String ecLineEnding) { 178 | String normalizedLineEnding; 179 | 180 | if (ecLineEnding == null) { 181 | LOG.log(Level.INFO, "Using line ending from System properties."); 182 | return System.lineSeparator(); 183 | } 184 | 185 | switch (ecLineEnding) { 186 | case EditorConfigConstant.END_OF_LINE_LF: 187 | normalizedLineEnding = BaseDocument.LS_LF; 188 | LOG.log(Level.INFO, "Using line ending: LF"); 189 | break; 190 | case EditorConfigConstant.END_OF_LINE_CR: 191 | normalizedLineEnding = BaseDocument.LS_CR; 192 | LOG.log(Level.INFO, "Using line ending: CR"); 193 | break; 194 | case EditorConfigConstant.END_OF_LINE_CRLF: 195 | normalizedLineEnding = BaseDocument.LS_CRLF; 196 | LOG.log(Level.INFO, "Using line ending: CRLF"); 197 | break; 198 | default: 199 | normalizedLineEnding = System.lineSeparator(); 200 | LOG.log(Level.INFO, "Using line ending from System properties."); 201 | break; 202 | } 203 | 204 | return normalizedLineEnding; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/mapper/EditorConfigPropertyMappingException.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.mapper; 2 | 3 | public class EditorConfigPropertyMappingException extends Exception { 4 | 5 | public EditorConfigPropertyMappingException() { 6 | } 7 | 8 | public EditorConfigPropertyMappingException(String message) { 9 | super(message); 10 | } 11 | 12 | public EditorConfigPropertyMappingException(Exception ex) { 13 | super(ex.getMessage()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/model/EditorConfigConstant.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.model; 2 | 3 | public class EditorConfigConstant { 4 | 5 | public static final String ROOT = "root"; 6 | public static final String MAX_LINE_LENGTH = "max_line_length"; 7 | 8 | /** 9 | * Set to latin1, utf-8, utf-8-bom, utf-16be or utf-16le to control the 10 | * character set. Use of utf-8-bom is discouraged. 11 | */ 12 | public static final String CHARSET = "charset"; 13 | public static final String CHARSET_LATIN_1 = "latin1"; // ISO-LATIN-1 14 | public static final String CHARSET_UTF_8 = "utf-8"; // UTF-8 15 | public static final String CHARSET_UTF_8_BOM = "utf-8-bom"; // UTF-8 with signature 16 | public static final String CHARSET_UTF_16_BE = "utf-16be"; // UTF-16BE 17 | public static final String CHARSET_UTF_16_LE = "utf-16le"; // UTF-16LE (UCS-2-LE) 18 | /** 19 | * Set to lf, cr, or crlf to control how line breaks are represented. 20 | */ 21 | public static final String END_OF_LINE = "end_of_line"; 22 | public static final String END_OF_LINE_LF = "lf"; // Linux, Mac OS X 23 | public static final String END_OF_LINE_CR = "cr"; // Mac OS 9 24 | public static final String END_OF_LINE_CRLF = "crlf"; 25 | 26 | /** 27 | * A whole number defining the number of columns used for each indentation 28 | * level and the width of soft tabs (when supported). When set to tab, the 29 | * value of tab_width (if specified) will be used. 30 | */ 31 | public static final String INDENT_SIZE = "indent_size"; 32 | /** 33 | * Set to tab or space to use hard tabs or soft tabs respectively. 34 | */ 35 | public static final String INDENT_STYLE = "indent_style"; 36 | public static final String INDENT_STYLE_SPACE = "space"; 37 | public static final String INDENT_STYLE_TAB = "tab"; 38 | /** 39 | * Set to true ensure file ends with a newline when saving and false to ensure 40 | * it doesn't. 41 | */ 42 | public static final String INSERT_FINAL_NEWLINE = "insert_final_newline"; 43 | /** 44 | * A whole number defining the number of columns used to represent a tab 45 | * character. This defaults to the value of indent_size and doesn't usually 46 | * need to be specified. 47 | */ 48 | public static final String TAB_WIDTH = "tab_width"; 49 | /** 50 | * Set to true to remove any whitespace characters preceding newline 51 | * characters and false to ensure it doesn't. 52 | */ 53 | public static final String TRIM_TRAILING_WHITESPACE = "trim_trailing_whitespace"; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/model/MappedEditorConfig.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.model; 2 | 3 | import com.welovecoding.nbeditorconfig.io.model.MappedCharset; 4 | import java.text.MessageFormat; 5 | 6 | public class MappedEditorConfig { 7 | 8 | // 1. charset 9 | private MappedCharset charset; 10 | // 2. end_of_line 11 | private String endOfLine; 12 | // 3. indent_size 13 | private int indentSize = -1; 14 | // 4. indent_style 15 | private String indentStyle; 16 | // 5. insert_final_newline 17 | private boolean insertFinalNewLine = false; 18 | // 6. tab_width 19 | private int tabWidth = -1; 20 | // 7. trim_trailing_whitespace 21 | private boolean trimTrailingWhiteSpace = false; 22 | 23 | public MappedEditorConfig() { 24 | } 25 | 26 | public String getReadableEndOfLine() { 27 | String lineEnding = "CRLF"; 28 | 29 | if (endOfLine != null) { 30 | switch (endOfLine) { 31 | case "\r": 32 | lineEnding = "CR"; 33 | break; 34 | case "\n": 35 | lineEnding = "LF"; 36 | break; 37 | } 38 | } else { 39 | lineEnding = null; 40 | } 41 | 42 | return lineEnding; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String template 48 | = "1. charset: {0}" 49 | + System.lineSeparator() 50 | + "2. end_of_line: {1}" 51 | + System.lineSeparator() 52 | + "3. indent_size: {2}" 53 | + System.lineSeparator() 54 | + "4. indent_style: {3}" 55 | + System.lineSeparator() 56 | + "5. insert_final_newline: {4}" 57 | + System.lineSeparator() 58 | + "6. tab_width: {5}" 59 | + System.lineSeparator() 60 | + "7. trim_trailing_whitespace: {6}" 61 | + System.lineSeparator(); 62 | 63 | String charsetName = null; 64 | if (charset != null) { 65 | charsetName = charset.getName(); 66 | } 67 | 68 | Object[] values = new Object[]{ 69 | charsetName, 70 | getReadableEndOfLine(), 71 | indentSize, 72 | indentStyle, 73 | insertFinalNewLine, 74 | tabWidth, 75 | trimTrailingWhiteSpace 76 | }; 77 | 78 | return MessageFormat.format(template, values); 79 | } 80 | 81 | // 82 | public MappedCharset getCharset() { 83 | return charset; 84 | } 85 | 86 | public void setCharset(MappedCharset charset) { 87 | this.charset = charset; 88 | } 89 | 90 | public String getEndOfLine() { 91 | return endOfLine; 92 | } 93 | 94 | public void setEndOfLine(String endOfLine) { 95 | this.endOfLine = endOfLine; 96 | } 97 | 98 | public int getIndentSize() { 99 | return indentSize; 100 | } 101 | 102 | public void setIndentSize(int indentSize) { 103 | this.indentSize = indentSize; 104 | } 105 | 106 | public String getIndentStyle() { 107 | return indentStyle; 108 | } 109 | 110 | public void setIndentStyle(String indentStyle) { 111 | this.indentStyle = indentStyle; 112 | } 113 | 114 | public boolean isInsertFinalNewLine() { 115 | return insertFinalNewLine; 116 | } 117 | 118 | public void setInsertFinalNewLine(boolean insertFinalNewLine) { 119 | this.insertFinalNewLine = insertFinalNewLine; 120 | } 121 | 122 | public int getTabWidth() { 123 | return tabWidth; 124 | } 125 | 126 | public void setTabWidth(int tabWidth) { 127 | this.tabWidth = tabWidth; 128 | } 129 | 130 | public boolean isTrimTrailingWhiteSpace() { 131 | return trimTrailingWhiteSpace; 132 | } 133 | 134 | public void setTrimTrailingWhiteSpace(boolean trimTrailingWhiteSpace) { 135 | this.trimTrailingWhiteSpace = trimTrailingWhiteSpace; 136 | } 137 | // 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/options/EditorConfigOptions.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.options; 2 | 3 | import com.welovecoding.nbeditorconfig.config.Settings; 4 | import java.util.prefs.Preferences; 5 | import org.openide.util.NbPreferences; 6 | 7 | /** 8 | * 9 | * @author junichi11 10 | */ 11 | public class EditorConfigOptions { 12 | 13 | private static final EditorConfigOptions INSTANCE = new EditorConfigOptions(); 14 | private static final String LINE_COMMENT_PREFIX = "comment.prefix"; // NOI18N 15 | 16 | private EditorConfigOptions() { 17 | } 18 | 19 | public static EditorConfigOptions getInstance() { 20 | return INSTANCE; 21 | } 22 | 23 | /** 24 | * Get a comment prefix. Can use "#" or ";". 25 | * 26 | * @return a comment prefix 27 | */ 28 | public String getLineCommentPrefix() { 29 | return getPreferences().get(LINE_COMMENT_PREFIX, "#"); // NOI18N 30 | } 31 | 32 | public void setLineCommentPrefix(String lineCommentPrefix) { 33 | getPreferences().put(LINE_COMMENT_PREFIX, lineCommentPrefix); 34 | } 35 | 36 | private Preferences getPreferences() { 37 | return NbPreferences.forModule(EditorConfigOptions.class).node(Settings.EDITORCONFIG); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/options/EditorConfigOptionsPanel.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
64 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/options/EditorConfigOptionsPanel.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.options; 2 | 3 | final class EditorConfigOptionsPanel extends javax.swing.JPanel { 4 | 5 | private static final long serialVersionUID = 6812836508405537948L; 6 | 7 | private final EditorConfigOptionsPanelController controller; 8 | 9 | EditorConfigOptionsPanel(EditorConfigOptionsPanelController controller) { 10 | this.controller = controller; 11 | initComponents(); 12 | // TODO listen to changes in form fields and call controller.changed() 13 | } 14 | 15 | /** 16 | * This method is called from within the constructor to initialize the form. 17 | * WARNING: Do NOT modify this code. The content of this method is always 18 | * regenerated by the Form Editor. 19 | */ 20 | // //GEN-BEGIN:initComponents 21 | private void initComponents() { 22 | 23 | lineCommentPrefixComboBox = new javax.swing.JComboBox(); 24 | lineCommnetPrefixLabel = new javax.swing.JLabel(); 25 | 26 | lineCommentPrefixComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "#", ";" })); 27 | 28 | org.openide.awt.Mnemonics.setLocalizedText(lineCommnetPrefixLabel, org.openide.util.NbBundle.getMessage(EditorConfigOptionsPanel.class, "EditorConfigOptionsPanel.lineCommnetPrefixLabel.text")); // NOI18N 29 | 30 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); 31 | this.setLayout(layout); 32 | layout.setHorizontalGroup( 33 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 34 | .addGroup(layout.createSequentialGroup() 35 | .addContainerGap() 36 | .addComponent(lineCommnetPrefixLabel) 37 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 38 | .addComponent(lineCommentPrefixComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) 39 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 40 | ); 41 | layout.setVerticalGroup( 42 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 43 | .addGroup(layout.createSequentialGroup() 44 | .addContainerGap() 45 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 46 | .addComponent(lineCommentPrefixComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 47 | .addComponent(lineCommnetPrefixLabel)) 48 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 49 | ); 50 | }// //GEN-END:initComponents 51 | 52 | void load() { 53 | EditorConfigOptions options = EditorConfigOptions.getInstance(); 54 | lineCommentPrefixComboBox.setSelectedItem(options.getLineCommentPrefix()); 55 | } 56 | 57 | void store() { 58 | EditorConfigOptions options = EditorConfigOptions.getInstance(); 59 | options.setLineCommentPrefix(getLineCommentPrefix()); 60 | } 61 | 62 | private String getLineCommentPrefix() { 63 | return (String) lineCommentPrefixComboBox.getSelectedItem(); 64 | } 65 | 66 | boolean valid() { 67 | // TODO check whether form is consistent and complete 68 | return true; 69 | } 70 | 71 | // Variables declaration - do not modify//GEN-BEGIN:variables 72 | private javax.swing.JComboBox lineCommentPrefixComboBox; 73 | private javax.swing.JLabel lineCommnetPrefixLabel; 74 | // End of variables declaration//GEN-END:variables 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/options/EditorConfigOptionsPanelController.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.options; 2 | 3 | import java.beans.PropertyChangeListener; 4 | import java.beans.PropertyChangeSupport; 5 | import javax.swing.JComponent; 6 | import javax.swing.SwingUtilities; 7 | import org.netbeans.spi.options.OptionsPanelController; 8 | import org.openide.util.HelpCtx; 9 | import org.openide.util.Lookup; 10 | 11 | @OptionsPanelController.SubRegistration( 12 | displayName = "#AdvancedOption_DisplayName_EditorConfig", 13 | keywords = "#AdvancedOption_Keywords_EditorConfig", 14 | keywordsCategory = "Advanced/EditorConfig" 15 | ) 16 | 17 | @org.openide.util.NbBundle.Messages({"AdvancedOption_DisplayName_EditorConfig=EditorConfig", "AdvancedOption_Keywords_EditorConfig=EditorConfig, editorconfig"}) 18 | public final class EditorConfigOptionsPanelController extends OptionsPanelController { 19 | 20 | private EditorConfigOptionsPanel panel; 21 | private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 22 | private boolean changed; 23 | 24 | @Override 25 | public void update() { 26 | getPanel().load(); 27 | changed = false; 28 | } 29 | 30 | @Override 31 | public void applyChanges() { 32 | SwingUtilities.invokeLater(new Runnable() { 33 | @Override 34 | public void run() { 35 | getPanel().store(); 36 | changed = false; 37 | } 38 | }); 39 | } 40 | 41 | @Override 42 | public void cancel() { 43 | // need not do anything special, if no changes have been persisted yet 44 | } 45 | 46 | @Override 47 | public boolean isValid() { 48 | return getPanel().valid(); 49 | } 50 | 51 | @Override 52 | public boolean isChanged() { 53 | return changed; 54 | } 55 | 56 | @Override 57 | public HelpCtx getHelpCtx() { 58 | return null; // new HelpCtx("...ID") if you have a help set 59 | } 60 | 61 | @Override 62 | public JComponent getComponent(Lookup masterLookup) { 63 | return getPanel(); 64 | } 65 | 66 | @Override 67 | public void addPropertyChangeListener(PropertyChangeListener l) { 68 | pcs.addPropertyChangeListener(l); 69 | } 70 | 71 | @Override 72 | public void removePropertyChangeListener(PropertyChangeListener l) { 73 | pcs.removePropertyChangeListener(l); 74 | } 75 | 76 | private EditorConfigOptionsPanel getPanel() { 77 | if (panel == null) { 78 | panel = new EditorConfigOptionsPanel(this); 79 | } 80 | return panel; 81 | } 82 | 83 | void changed() { 84 | if (!changed) { 85 | changed = true; 86 | pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true); 87 | } 88 | pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/FileInfo.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.nio.charset.Charset; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | import javax.swing.JEditorPane; 8 | import javax.swing.SwingUtilities; 9 | import javax.swing.text.Caret; 10 | import org.openide.cookies.EditorCookie; 11 | import org.openide.filesystems.FileObject; 12 | import org.openide.loaders.DataObject; 13 | import org.openide.text.NbDocument; 14 | 15 | public class FileInfo { 16 | 17 | private static final Logger LOG = Logger.getLogger(FileInfo.class.getName()); 18 | 19 | private Caret currentCaret; 20 | 21 | // File references 22 | private DataObject dataObject; 23 | private EditorCookie cookie; 24 | private StringBuilder content; 25 | 26 | // Configurations 27 | private Charset charset; 28 | private String endOfLine = System.lineSeparator(); 29 | private String fileMark; 30 | private int caretOffset; 31 | 32 | // Switches 33 | private boolean fileChangeNeeded = false; 34 | private boolean openedInEditor = false; 35 | private boolean styleFlushNeeded = false; 36 | 37 | public FileInfo() { 38 | } 39 | 40 | public FileInfo(DataObject dataObject) { 41 | this.dataObject = dataObject; 42 | } 43 | 44 | public Caret getCaret() { 45 | Runnable runner = new Runnable() { 46 | @Override 47 | public void run() { 48 | NbDocument.runAtomic(cookie.getDocument(), new Runnable() { 49 | @Override 50 | public void run() { 51 | JEditorPane pane = cookie.getOpenedPanes()[0]; 52 | if (pane != null) { 53 | currentCaret = pane.getCaret(); 54 | } else { 55 | LOG.log(Level.SEVERE, "Could not get JEditorPane for Document"); 56 | } 57 | } 58 | }); 59 | } 60 | }; 61 | if (SwingUtilities.isEventDispatchThread()) { 62 | runner.run(); 63 | } else { 64 | try { 65 | SwingUtilities.invokeAndWait(runner); 66 | } catch (InterruptedException | InvocationTargetException ex) { 67 | LOG.log(Level.SEVERE, "Could not determine actual caret in editor.", ex); 68 | } 69 | } 70 | return currentCaret; 71 | } 72 | 73 | public int getCurrentCaretPosition() { 74 | int position = -1; 75 | 76 | if (openedInEditor && getCaret() != null) { 77 | position = getCaret().getDot(); 78 | } 79 | 80 | return position; 81 | } 82 | 83 | public String getPath() { 84 | return this.getFileObject().getPath(); 85 | } 86 | 87 | public FileObject getFileObject() { 88 | return this.dataObject.getPrimaryFile(); 89 | } 90 | 91 | public String getContentAsString() { 92 | String contentAsString = content.toString(); 93 | 94 | if (fileMark != null && !contentAsString.startsWith(fileMark)) { 95 | contentAsString = fileMark + contentAsString; 96 | } 97 | 98 | return contentAsString; 99 | } 100 | 101 | public byte[] getContentAsBytes() { 102 | return getContentAsString().getBytes(charset); 103 | } 104 | 105 | // 106 | public boolean isStyleFlushNeeded() { 107 | return styleFlushNeeded; 108 | } 109 | 110 | public void setStyleFlushNeeded(boolean styleFlushNeeded) { 111 | this.styleFlushNeeded = styleFlushNeeded; 112 | } 113 | 114 | public int getCaretOffset() { 115 | return caretOffset; 116 | } 117 | 118 | public void setCaretOffset(int caretOffset) { 119 | this.caretOffset = caretOffset; 120 | } 121 | 122 | public String getEndOfLine() { 123 | return endOfLine; 124 | } 125 | 126 | public void setEndOfLine(String endOfLine) { 127 | this.endOfLine = endOfLine; 128 | } 129 | 130 | public boolean isFileChangeNeeded() { 131 | return fileChangeNeeded; 132 | } 133 | 134 | public void setFileChangeNeeded(boolean fileChangeNeeded) { 135 | this.fileChangeNeeded = fileChangeNeeded; 136 | } 137 | 138 | public DataObject getDataObject() { 139 | return dataObject; 140 | } 141 | 142 | public void setDataObject(DataObject dataObject) { 143 | this.dataObject = dataObject; 144 | } 145 | 146 | public String getFileMark() { 147 | return fileMark; 148 | } 149 | 150 | public void setFileMark(String fileMark) { 151 | this.fileMark = fileMark; 152 | } 153 | 154 | public StringBuilder getContent() { 155 | return content; 156 | } 157 | 158 | public void setContent(StringBuilder content) { 159 | this.content = content; 160 | } 161 | 162 | public Charset getCharset() { 163 | return charset; 164 | } 165 | 166 | public void setCharset(Charset charset) { 167 | this.charset = charset; 168 | } 169 | 170 | public boolean isOpenedInEditor() { 171 | return openedInEditor; 172 | } 173 | 174 | public void setOpenedInEditor(boolean openedInEditor) { 175 | this.openedInEditor = openedInEditor; 176 | } 177 | 178 | public EditorCookie getCookie() { 179 | return cookie; 180 | } 181 | 182 | public void setCookie(EditorCookie cookie) { 183 | this.cookie = cookie; 184 | } 185 | // 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/SmartSkip.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor; 2 | 3 | import org.openide.filesystems.FileObject; 4 | 5 | public class SmartSkip { 6 | 7 | public static final boolean IS_ON = true; 8 | static final String[] IGNORED_FILES = { 9 | ".git", 10 | ".grunt", 11 | ".idea", 12 | ".sass-cache", 13 | ".svn", 14 | ".tscache", 15 | "bower_components", 16 | "nbproject", 17 | "node_modules", 18 | "vendor" 19 | }; 20 | 21 | public static boolean skipDirectory(FileObject directory) { 22 | String fileName = directory.getName(); 23 | boolean skip = false; 24 | 25 | for (String pattern : IGNORED_FILES) { 26 | if (fileName.startsWith(pattern)) { 27 | skip = true; 28 | } 29 | } 30 | 31 | if (directory.isFolder() && fileName.startsWith(".")) { 32 | skip = true; 33 | } 34 | 35 | return skip; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/WriteEditorAction.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor; 2 | 3 | import com.welovecoding.nbeditorconfig.io.writer.StyledDocumentWriter; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | import org.openide.filesystems.FileSystem; 7 | 8 | public class WriteEditorAction implements FileSystem.AtomicAction, Runnable { 9 | 10 | private static final Logger LOG = Logger.getLogger(WriteEditorAction.class.getName()); 11 | 12 | private final FileInfo info; 13 | 14 | public WriteEditorAction() { 15 | this.info = null; 16 | } 17 | 18 | public WriteEditorAction(FileInfo info) { 19 | this.info = info; 20 | } 21 | 22 | @Override 23 | public void run() { 24 | try { 25 | StyledDocumentWriter.writeWithEditorKit(info); 26 | } catch (Exception ex) { 27 | LOG.log(Level.SEVERE, ex.getMessage()); 28 | } 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | return obj != null && obj instanceof WriteEditorAction; 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return getClass().getName().hashCode(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/WriteFileAction.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.Settings.ENCODING_SETTING; 4 | import com.welovecoding.nbeditorconfig.io.model.MappedCharset; 5 | import com.welovecoding.nbeditorconfig.io.reader.FileInfoReader; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.io.OutputStreamWriter; 9 | import java.nio.charset.Charset; 10 | import java.util.logging.Level; 11 | import java.util.logging.Logger; 12 | import org.openide.filesystems.FileLock; 13 | import org.openide.filesystems.FileObject; 14 | import org.openide.filesystems.FileSystem; 15 | import org.openide.util.Exceptions; 16 | 17 | public abstract class WriteFileAction implements FileSystem.AtomicAction, Runnable { 18 | 19 | private static final Logger LOG = Logger.getLogger(WriteFileAction.class.getName()); 20 | 21 | private final FileObject fileObject; 22 | private final Charset charset; 23 | 24 | public WriteFileAction() { 25 | fileObject = null; 26 | charset = null; 27 | } 28 | 29 | public WriteFileAction(FileObject fileObject, Charset charset) { 30 | this.fileObject = fileObject; 31 | this.charset = charset; 32 | } 33 | 34 | public WriteFileAction(FileObject fileObject) { 35 | this.fileObject = fileObject; 36 | MappedCharset mappedCharset = FileInfoReader.readCharset(fileObject); 37 | this.charset = mappedCharset.getCharset(); 38 | } 39 | 40 | @Override 41 | public void run() { 42 | FileLock lock = FileLock.NONE; 43 | try { 44 | try (OutputStream outputStream = fileObject.getOutputStream(lock); OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset)) { 45 | // ##################### 46 | apply(writer); 47 | // ##################### 48 | setFileAttribute(fileObject, ENCODING_SETTING, charset.name()); 49 | writer.flush(); 50 | outputStream.flush(); 51 | } 52 | } catch (IOException ex) { 53 | Exceptions.printStackTrace(ex); 54 | } finally { 55 | lock.releaseLock(); 56 | } 57 | } 58 | 59 | private void setFileAttribute(FileObject fo, String key, String value) { 60 | try { 61 | fo.setAttribute(key, value); 62 | } catch (IOException ex) { 63 | LOG.log(Level.SEVERE, "Error setting file attribute \"{0}\" with value \"{1}\" for {2}. {3}", 64 | new Object[]{ 65 | key, 66 | value, 67 | fo.getPath(), 68 | ex.getMessage() 69 | }); 70 | } 71 | } 72 | 73 | public abstract void apply(OutputStreamWriter writer); 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/WriteStringToFileAction.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStreamWriter; 5 | import java.nio.charset.Charset; 6 | import java.util.logging.Logger; 7 | import org.openide.filesystems.FileObject; 8 | import org.openide.util.Exceptions; 9 | 10 | public class WriteStringToFileAction extends WriteFileAction { 11 | 12 | private static final Logger LOG = Logger.getLogger(WriteStringToFileAction.class.getName()); 13 | private final String content; 14 | 15 | public WriteStringToFileAction() { 16 | super(); 17 | content = ""; 18 | } 19 | 20 | public WriteStringToFileAction(FileInfo info) { 21 | super(info.getFileObject(), info.getCharset()); 22 | this.content = info.getContent().toString(); 23 | } 24 | 25 | public WriteStringToFileAction(FileObject fileObject, Charset charset, String content) { 26 | super(fileObject, charset); 27 | this.content = content; 28 | } 29 | 30 | @Override 31 | public void apply(OutputStreamWriter writer) { 32 | try { 33 | writer.write(content); 34 | } catch (IOException ex) { 35 | Exceptions.printStackTrace(ex); 36 | } 37 | } 38 | 39 | @Override 40 | public boolean equals(Object obj) { 41 | return obj != null && obj instanceof WriteStringToFileAction; 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return getClass().getName().hashCode(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/operation/CodeStyleOperation.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.OPERATION_LOG_LEVEL; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | import java.util.prefs.Preferences; 7 | import org.netbeans.modules.editor.indent.spi.CodeStylePreferences; 8 | import org.openide.filesystems.FileObject; 9 | 10 | public abstract class CodeStyleOperation { 11 | 12 | private static final Logger LOG = Logger.getLogger(CodeStyleOperation.class.getName()); 13 | protected FileObject file; 14 | 15 | static { 16 | LOG.setLevel(OPERATION_LOG_LEVEL); 17 | } 18 | 19 | public CodeStyleOperation(FileObject file) { 20 | this.file = file; 21 | } 22 | 23 | protected boolean operate(String simpleValueName, String value) { 24 | boolean codeStyleChangeNeeded = false; 25 | 26 | Preferences codeStyle = CodeStylePreferences.get(file, file.getMIMEType()).getPreferences(); 27 | String currentValue = codeStyle.get(simpleValueName, ""); 28 | 29 | LOG.log(Level.INFO, "\u00ac Current value: {0}", currentValue); 30 | LOG.log(Level.INFO, "\u00ac New value: {0}", value); 31 | 32 | if (currentValue.equals(value)) { 33 | LOG.log(Level.INFO, "\u00ac No change needed"); 34 | } else { 35 | codeStyle.put(simpleValueName, value); 36 | codeStyleChangeNeeded = true; 37 | LOG.log(Level.INFO, "\u00ac Changing value from \"{0}\" to \"{1}\"", 38 | new Object[]{currentValue, value}); 39 | } 40 | 41 | return codeStyleChangeNeeded; 42 | } 43 | 44 | protected boolean operate(String simpleValueName, boolean value) { 45 | boolean codeStyleChangeNeeded = false; 46 | 47 | Preferences codeStyle = CodeStylePreferences.get(file, file.getMIMEType()).getPreferences(); 48 | boolean currentValue = codeStyle.getBoolean(simpleValueName, false); 49 | 50 | LOG.log(Level.INFO, "\u00ac Current value: {0}", currentValue); 51 | LOG.log(Level.INFO, "\u00ac New value: {0}", value); 52 | 53 | if (currentValue == value) { 54 | LOG.log(Level.INFO, "\u00ac No change needed"); 55 | } else { 56 | codeStyle.putBoolean(simpleValueName, value); 57 | codeStyleChangeNeeded = true; 58 | LOG.log(Level.INFO, "\u00ac Changing value from \"{0}\" to \"{1}\"", 59 | new Object[]{currentValue, value}); 60 | } 61 | 62 | return codeStyleChangeNeeded; 63 | } 64 | 65 | protected boolean operate(String simpleValueName, int value) { 66 | boolean codeStyleChangeNeeded = false; 67 | 68 | if (value < 0) { 69 | return false; 70 | } 71 | 72 | Preferences codeStyle = CodeStylePreferences.get(file, file.getMIMEType()).getPreferences(); 73 | int currentValue = codeStyle.getInt(simpleValueName, -1); 74 | 75 | LOG.log(Level.INFO, "\u00ac Current value: {0}", currentValue); 76 | LOG.log(Level.INFO, "\u00ac New value: {0}", value); 77 | 78 | if (currentValue == value) { 79 | LOG.log(Level.INFO, "\u00ac No change needed"); 80 | } else { 81 | codeStyle.putInt(simpleValueName, value); 82 | codeStyleChangeNeeded = true; 83 | LOG.log(Level.INFO, "\u00ac Changing value from \"{0}\" to \"{1}\"", 84 | new Object[]{currentValue, value}); 85 | } 86 | 87 | return codeStyleChangeNeeded; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/operation/FinalNewLineOperation.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.OPERATION_LOG_LEVEL; 4 | import com.welovecoding.nbeditorconfig.processor.FileInfo; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | public class FinalNewLineOperation { 9 | 10 | private static final Logger LOG = Logger.getLogger(FinalNewLineOperation.class.getName()); 11 | 12 | static { 13 | LOG.setLevel(OPERATION_LOG_LEVEL); 14 | } 15 | 16 | /** 17 | * Inserts the given content with a final new line. We have to work with a 18 | * StringBuilder to work with a reference of the content. 19 | * 20 | * @param info Object that represents the configuration options (mapped by an 21 | * EditorConfig rule) for a specific file 22 | * 23 | * @return true if the operation was performed 24 | */ 25 | public boolean operate(FileInfo info) { 26 | return operate(info.getContent(), true, info.getEndOfLine()); 27 | } 28 | 29 | private boolean operate(StringBuilder content, final boolean insertFinalNewLine, final String lineEnding) { 30 | boolean changedLineEndings = false; 31 | 32 | if (insertFinalNewLine) { 33 | String contentBeforeOperation = content.toString(); 34 | content = addFinalNewLine(content, lineEnding); 35 | changedLineEndings = !contentBeforeOperation.equals(content.toString()); 36 | } 37 | 38 | return changedLineEndings; 39 | } 40 | 41 | private StringBuilder addFinalNewLine(StringBuilder content, String lineEnding) { 42 | if (!content.toString().endsWith("\n") && !content.toString().endsWith("\r")) { 43 | LOG.log(Level.INFO, "\u00ac Added final new line"); 44 | return content.append(lineEnding); 45 | } else { 46 | LOG.log(Level.INFO, "\u00ac No change needed"); 47 | return content; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/operation/IndentSizeOperation.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import org.netbeans.api.editor.settings.SimpleValueNames; 4 | import org.openide.filesystems.FileObject; 5 | 6 | public class IndentSizeOperation extends CodeStyleOperation { 7 | 8 | public IndentSizeOperation(FileObject file) { 9 | super(file); 10 | } 11 | 12 | /** 13 | * Changes the indent size of the NetBeans editor. This change affects only 14 | * the editor's view. Indent sizes of the actual file will not be changed. To 15 | * change the indent size for the file, a reformat of the code is needed 16 | * combined with saving the file. Reformatting and saving the file is part of 17 | * {@link com.welovecoding.netbeans.plugin.editorconfig.io.writer.StyledDocumentWriter#writeWithEditorKit}. 18 | * 19 | * @param value Indent size which should be set 20 | * 21 | * @return true if the operation was performed 22 | */ 23 | public boolean changeIndentSize(int value) { 24 | if (value == -2) { 25 | return operate(SimpleValueNames.EXPAND_TABS, false); 26 | } else { 27 | return operate(SimpleValueNames.INDENT_SHIFT_WIDTH, value); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/operation/IndentStyleOperation.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import com.welovecoding.nbeditorconfig.model.EditorConfigConstant; 4 | import org.netbeans.api.editor.settings.SimpleValueNames; 5 | import org.openide.filesystems.FileObject; 6 | 7 | public class IndentStyleOperation extends CodeStyleOperation { 8 | 9 | public IndentStyleOperation(FileObject file) { 10 | super(file); 11 | } 12 | 13 | public boolean changeIndentStyle(String value) { 14 | if (value.equals(EditorConfigConstant.INDENT_STYLE_SPACE)) { 15 | return operate(SimpleValueNames.EXPAND_TABS, true); 16 | } else { 17 | return operate(SimpleValueNames.EXPAND_TABS, false); 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/operation/LineEndingOperation.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import com.welovecoding.nbeditorconfig.config.LoggerSettings; 4 | import com.welovecoding.nbeditorconfig.io.reader.FileInfoReader; 5 | import com.welovecoding.nbeditorconfig.processor.FileInfo; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | public class LineEndingOperation { 10 | 11 | private static final Logger LOG = Logger.getLogger(LineEndingOperation.class.getName()); 12 | 13 | static { 14 | LOG.setLevel(LoggerSettings.OPERATION_LOG_LEVEL); 15 | } 16 | 17 | public boolean operate(FileInfo info) { 18 | boolean changed; 19 | 20 | String source = info.getContentAsString(); 21 | StringBuilder modified = replaceLineEndings(info.getContent(), info.getEndOfLine()); 22 | 23 | if (source.equals(modified.toString())) { 24 | LOG.log(Level.INFO, "\u00ac No change needed"); 25 | changed = false; 26 | } else { 27 | LOG.log(Level.INFO, "\u00ac Changed line endings"); 28 | changed = true; 29 | } 30 | 31 | return changed; 32 | } 33 | 34 | private StringBuilder replaceLineEndings(StringBuilder content, String lineEnding) { 35 | // Note: As a side effect this will strip a final newline 36 | String tempContent = FileInfoReader.replaceLineEndings(content.toString(), lineEnding); 37 | 38 | // Append line ending only if that was the case in the old content 39 | if (content.toString().endsWith("\n") || content.toString().endsWith("\r")) { 40 | content.delete(0, content.length()); 41 | content.append(tempContent).append(lineEnding); 42 | } else { 43 | content.delete(0, content.length()); 44 | content.append(tempContent); 45 | } 46 | 47 | return content; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/operation/TabWidthOperation.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import org.netbeans.api.editor.settings.SimpleValueNames; 4 | import org.openide.filesystems.FileObject; 5 | 6 | public class TabWidthOperation extends CodeStyleOperation { 7 | 8 | public TabWidthOperation(FileObject file) { 9 | super(file); 10 | } 11 | 12 | public boolean changeTabWidth(int value) { 13 | return operate(SimpleValueNames.TAB_SIZE, value); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/operation/TrimTrailingWhiteSpaceOperation.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import static com.welovecoding.nbeditorconfig.config.LoggerSettings.OPERATION_LOG_LEVEL; 4 | import com.welovecoding.nbeditorconfig.io.reader.FileInfoReader; 5 | import com.welovecoding.nbeditorconfig.processor.FileInfo; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | public class TrimTrailingWhiteSpaceOperation { 10 | 11 | private static final Logger LOG = Logger.getLogger(TrimTrailingWhiteSpaceOperation.class.getName()); 12 | 13 | static { 14 | LOG.setLevel(OPERATION_LOG_LEVEL); 15 | } 16 | 17 | public boolean operate(FileInfo info) { 18 | return operate(info, true); 19 | } 20 | 21 | private boolean operate(FileInfo info, final boolean trimWhiteSpace) { 22 | StringBuilder content = info.getContent(); 23 | boolean trimmedWhiteSpaces = false; 24 | 25 | if (trimWhiteSpace) { 26 | String contentBeforeOperation = content.toString(); 27 | 28 | detectCaretOffset(info); 29 | content = trim(info); 30 | 31 | if (contentBeforeOperation.equals(content.toString())) { 32 | LOG.log(Level.INFO, "\u00ac No whitespace trimmed"); 33 | trimmedWhiteSpaces = false; 34 | } else { 35 | LOG.log(Level.INFO, "\u00ac Trimmed whitespaces"); 36 | trimmedWhiteSpaces = true; 37 | } 38 | } 39 | 40 | return trimmedWhiteSpaces; 41 | } 42 | 43 | private void detectCaretOffset(FileInfo info) { 44 | StringBuilder content = info.getContent(); 45 | String contentCopy = content.toString(); 46 | 47 | // Trim until caret 48 | String contentUntilCaret = ""; 49 | int caretPosition = info.getCurrentCaretPosition(); 50 | if (caretPosition > 0) { 51 | contentUntilCaret = contentCopy.substring(0, caretPosition); 52 | } 53 | 54 | String trimmedContent = FileInfoReader.trimTrailingWhitespace(contentUntilCaret, info.getEndOfLine()); 55 | 56 | // Count the characters which have been trimmed until the caret positon 57 | // (this will be our caret offset) 58 | int offset = contentUntilCaret.length() - trimmedContent.length(); 59 | info.setCaretOffset(offset); 60 | 61 | LOG.log(Level.INFO, "\u00ac Content length until caret: {0}", contentUntilCaret.length()); 62 | LOG.log(Level.INFO, "\u00ac Trimmed content length: {0}", trimmedContent.length()); 63 | LOG.log(Level.INFO, "\u00ac Caret offset: {0}", offset); 64 | } 65 | 66 | // TODO: Caret position is not set properly when text is trimmed 67 | // If the caret is in a line where we trim text, then we have to move it 68 | // minus the amount of characters which have been removed. 69 | // We need to find AND save the caret offset. 70 | private StringBuilder trim(FileInfo info) { 71 | StringBuilder content = info.getContent(); 72 | String lineEnding = info.getEndOfLine(); 73 | String contentCopy = content.toString(); 74 | 75 | // Note: As a side effect this will strip a final newline! 76 | String trimmedContent = FileInfoReader.trimTrailingWhitespace(contentCopy, lineEnding); 77 | 78 | // Exchange original content with trimmed content 79 | content.delete(0, content.length()); 80 | content.append(trimmedContent); 81 | 82 | // Append line ending if old content had a line ending 83 | if (contentCopy.endsWith("\n") || contentCopy.endsWith("\r")) { 84 | content.append(lineEnding); 85 | } 86 | 87 | return content; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/welovecoding/nbeditorconfig/processor/operation/tobedone/CharsetOperation.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation.tobedone; 2 | 3 | import com.welovecoding.nbeditorconfig.config.LoggerSettings; 4 | import com.welovecoding.nbeditorconfig.io.model.MappedCharset; 5 | import com.welovecoding.nbeditorconfig.io.reader.FileInfoReader; 6 | import com.welovecoding.nbeditorconfig.io.reader.FileObjectReader; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | import org.openide.filesystems.FileObject; 10 | import org.openide.loaders.DataObject; 11 | 12 | public class CharsetOperation { 13 | 14 | private static final Logger LOG = Logger.getLogger(CharsetOperation.class.getName()); 15 | 16 | static { 17 | LOG.setLevel(LoggerSettings.OPERATION_LOG_LEVEL); 18 | } 19 | 20 | /** 21 | * TODO:
22 | * 1. Check charset attribute
23 | * 2. If no attribute exists, then take the default encoding from project 24 | * settings
25 | * 3. Change charset (if needed) and save file & charset attribute 26 | * 27 | * @param dataObject Object that represents the file 28 | * @param requestedCharset Charset which should be used to save the file 29 | * 30 | * @return true if the operation was successful 31 | */ 32 | public boolean run(DataObject dataObject, MappedCharset requestedCharset) { 33 | boolean changedCharset = false; 34 | FileObject fo = dataObject.getPrimaryFile(); 35 | MappedCharset currentCharset = FileInfoReader.readCharset(fo); 36 | 37 | LOG.log(Level.INFO, "\u00ac Current charset: {0}", currentCharset.getName()); 38 | 39 | if (!currentCharset.getCharset().name().equals(requestedCharset.getCharset().name())) { 40 | LOG.log(Level.INFO, "\u00ac Changing charset from \"{0}\" to \"{1}\"", 41 | new Object[]{currentCharset.getName(), requestedCharset.getName()}); 42 | 43 | String content = FileObjectReader.read(fo, currentCharset.getCharset()); 44 | // FileObjectWriter.writeWithAtomicAction(dataObject, requestedCharset.getCharset(), content); 45 | 46 | } else { 47 | /* 48 | try { 49 | // TODO: A bit dangerous atm! 50 | // ConfigWriter.rewrite(dataObject, currentCharset, requestedCharset); 51 | } catch (IOException ex) { 52 | Exceptions.printStackTrace(ex); 53 | } 54 | */ 55 | LOG.log(Level.INFO, "\u00ac No change needed"); 56 | } 57 | 58 | return changedCharset; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/antlr4/EditorConfigErrorListener.java: -------------------------------------------------------------------------------- 1 | package org.antlr4; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import org.antlr.v4.runtime.BaseErrorListener; 7 | import org.antlr.v4.runtime.RecognitionException; 8 | import org.antlr.v4.runtime.Recognizer; 9 | 10 | /** 11 | * 12 | * @author junichi11 13 | */ 14 | public class EditorConfigErrorListener extends BaseErrorListener { 15 | 16 | private final List errorMessages; 17 | 18 | public EditorConfigErrorListener(List errorMessages) { 19 | if (errorMessages == null) { 20 | errorMessages = Collections.synchronizedList(new ArrayList()); 21 | } 22 | this.errorMessages = errorMessages; 23 | } 24 | 25 | @Override 26 | public synchronized void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { 27 | errorMessages.add(new SyntaxError(msg, line, charPositionInLine, e)); 28 | } 29 | 30 | public synchronized List getErrorMessages() { 31 | return errorMessages; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/antlr4/EditorConfigLexer.java: -------------------------------------------------------------------------------- 1 | // Generated from editorconfig/EditorConfigLexer.g4 by ANTLR 4.5 2 | package org.antlr4; 3 | 4 | import org.antlr.v4.runtime.CharStream; 5 | import org.antlr.v4.runtime.Lexer; 6 | import org.antlr.v4.runtime.RuntimeMetaData; 7 | import org.antlr.v4.runtime.Vocabulary; 8 | import org.antlr.v4.runtime.VocabularyImpl; 9 | import org.antlr.v4.runtime.atn.ATN; 10 | import org.antlr.v4.runtime.atn.ATNDeserializer; 11 | import org.antlr.v4.runtime.atn.LexerATNSimulator; 12 | import org.antlr.v4.runtime.atn.PredictionContextCache; 13 | import org.antlr.v4.runtime.dfa.DFA; 14 | 15 | @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) 16 | public class EditorConfigLexer extends Lexer { 17 | 18 | static { 19 | RuntimeMetaData.checkVersion("4.5", RuntimeMetaData.VERSION); 20 | } 21 | 22 | protected static final DFA[] _decisionToDFA; 23 | protected static final PredictionContextCache _sharedContextCache 24 | = new PredictionContextCache(); 25 | public static final int WS = 1, PROPERTY_KEY_ROOT = 2, PROPERTY_KEY_CHARSET = 3, PROPERTY_KEY_END_OF_LINE = 4, 26 | PROPERTY_KEY_INDENT_SIZE = 5, PROPERTY_KEY_INDENT_STYLE = 6, PROPERTY_KEY_INSERT_FINAL_NEWLINE = 7, 27 | PROPERTY_KEY_MAX_LINE_LENGTH = 8, PROPERTY_KEY_TAB_WIDTH = 9, PROPERTY_KEY_TRIM_TRAILING_WHITESPACE = 10, 28 | PROPERTY_KEY_UNKNOWN = 11, SECTION_LDELIMITER = 12, SECTION_RDELIMITER = 13, 29 | ASSIGNMENT = 14, COMMENT = 15, TEXT = 16, SECTION_NAME = 17, PROPERTY_VALUE = 18; 30 | public static final int SECTION_INNER_MODE = 1; 31 | public static final int PROPERTY_VALUE_MODE = 2; 32 | public static String[] modeNames = { 33 | "DEFAULT_MODE", "SECTION_INNER_MODE", "PROPERTY_VALUE_MODE" 34 | }; 35 | 36 | public static final String[] ruleNames = { 37 | "PROPERTY_KEY_ROOT", "PROPERTY_KEY_CHARSET", "PROPERTY_KEY_END_OF_LINE", 38 | "PROPERTY_KEY_INDENT_SIZE", "PROPERTY_KEY_INDENT_STYLE", "PROPERTY_KEY_INSERT_FINAL_NEWLINE", 39 | "PROPERTY_KEY_MAX_LINE_LENGTH", "PROPERTY_KEY_TAB_WIDTH", "PROPERTY_KEY_TRIM_TRAILING_WHITESPACE", 40 | "PROPERTY_KEY_UNKNOWN", "SECTION_LDELIMITER", "SECTION_RDELIMITER", "ASSIGNMENT", 41 | "WS", "COMMENT", "TEXT", "SECTION_NAME", "SECTION_EXIT", "PROPERTY_VALUE_WS", 42 | "PROPERTY_VALUE" 43 | }; 44 | 45 | private static final String[] _LITERAL_NAMES = { 46 | null, null, "'root'", "'charset'", "'end_of_line'", "'indent_size'", "'indent_style'", 47 | "'insert_final_newline'", "'max_line_length'", "'tab_width'", "'trim_trailing_whitespace'", 48 | null, "'['", "']'", "'='" 49 | }; 50 | private static final String[] _SYMBOLIC_NAMES = { 51 | null, "WS", "PROPERTY_KEY_ROOT", "PROPERTY_KEY_CHARSET", "PROPERTY_KEY_END_OF_LINE", 52 | "PROPERTY_KEY_INDENT_SIZE", "PROPERTY_KEY_INDENT_STYLE", "PROPERTY_KEY_INSERT_FINAL_NEWLINE", 53 | "PROPERTY_KEY_MAX_LINE_LENGTH", "PROPERTY_KEY_TAB_WIDTH", "PROPERTY_KEY_TRIM_TRAILING_WHITESPACE", 54 | "PROPERTY_KEY_UNKNOWN", "SECTION_LDELIMITER", "SECTION_RDELIMITER", "ASSIGNMENT", 55 | "COMMENT", "TEXT", "SECTION_NAME", "PROPERTY_VALUE" 56 | }; 57 | public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); 58 | 59 | /** 60 | * @deprecated Use {@link #VOCABULARY} instead. 61 | */ 62 | @Deprecated 63 | public static final String[] tokenNames; 64 | 65 | static { 66 | tokenNames = new String[_SYMBOLIC_NAMES.length]; 67 | for (int i = 0; i < tokenNames.length; i++) { 68 | tokenNames[i] = VOCABULARY.getLiteralName(i); 69 | if (tokenNames[i] == null) { 70 | tokenNames[i] = VOCABULARY.getSymbolicName(i); 71 | } 72 | 73 | if (tokenNames[i] == null) { 74 | tokenNames[i] = ""; 75 | } 76 | } 77 | } 78 | 79 | @Override 80 | @Deprecated 81 | public String[] getTokenNames() { 82 | return tokenNames; 83 | } 84 | 85 | @Override 86 | public Vocabulary getVocabulary() { 87 | return VOCABULARY; 88 | } 89 | 90 | public EditorConfigLexer(CharStream input) { 91 | super(input); 92 | _interp = new LexerATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache); 93 | } 94 | 95 | @Override 96 | public String getGrammarFileName() { 97 | return "EditorConfigLexer.g4"; 98 | } 99 | 100 | @Override 101 | public String[] getRuleNames() { 102 | return ruleNames; 103 | } 104 | 105 | @Override 106 | public String getSerializedATN() { 107 | return _serializedATN; 108 | } 109 | 110 | @Override 111 | public String[] getModeNames() { 112 | return modeNames; 113 | } 114 | 115 | @Override 116 | public ATN getATN() { 117 | return _ATN; 118 | } 119 | 120 | public static final String _serializedATN 121 | = "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\24\u00e6\b\1\b\1" 122 | + "\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4" 123 | + "\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t" 124 | + "\21\4\22\t\22\4\23\t\23\4\24\t\24\4\25\t\25\3\2\3\2\3\2\3\2\3\2\3\3\3" 125 | + "\3\3\3\3\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4" 126 | + "\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\3" 127 | + "\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7" 128 | + "\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b\3\b\3" 129 | + "\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3\t\3\t" 130 | + "\3\t\3\t\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3" 131 | + "\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\f\3\f\3\f\3\f\3" 132 | + "\r\3\r\3\16\3\16\3\16\3\16\3\17\6\17\u00b5\n\17\r\17\16\17\u00b6\3\17" 133 | + "\3\17\3\20\3\20\7\20\u00bd\n\20\f\20\16\20\u00c0\13\20\3\20\3\20\3\21" 134 | + "\6\21\u00c5\n\21\r\21\16\21\u00c6\3\22\3\22\3\22\7\22\u00cc\n\22\f\22" 135 | + "\16\22\u00cf\13\22\3\22\6\22\u00d2\n\22\r\22\16\22\u00d3\3\22\3\22\3\23" 136 | + "\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25" 137 | + "\3\u00cd\2\26\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33" 138 | + "\17\35\20\37\3!\21#\22%\23\'\2)\2+\24\5\2\3\4\7\5\2\13\f\17\17\"\"\4\2" 139 | + "%%==\4\2\f\f\17\17\n\2\13\f\17\17\"\"%%==??]]__\6\2\f\f\17\17%%]_\u00e9" 140 | + "\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2" 141 | + "\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2" 142 | + "\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\3%\3\2\2" 143 | + "\2\3\'\3\2\2\2\4)\3\2\2\2\4+\3\2\2\2\5-\3\2\2\2\7\62\3\2\2\2\t:\3\2\2" 144 | + "\2\13F\3\2\2\2\rR\3\2\2\2\17_\3\2\2\2\21t\3\2\2\2\23\u0084\3\2\2\2\25" 145 | + "\u008e\3\2\2\2\27\u00a7\3\2\2\2\31\u00a9\3\2\2\2\33\u00ad\3\2\2\2\35\u00af" 146 | + "\3\2\2\2\37\u00b4\3\2\2\2!\u00ba\3\2\2\2#\u00c4\3\2\2\2%\u00d1\3\2\2\2" 147 | + "\'\u00d7\3\2\2\2)\u00dd\3\2\2\2+\u00e2\3\2\2\2-.\7t\2\2./\7q\2\2/\60\7" 148 | + "q\2\2\60\61\7v\2\2\61\6\3\2\2\2\62\63\7e\2\2\63\64\7j\2\2\64\65\7c\2\2" 149 | + "\65\66\7t\2\2\66\67\7u\2\2\678\7g\2\289\7v\2\29\b\3\2\2\2:;\7g\2\2;<\7" 150 | + "p\2\2<=\7f\2\2=>\7a\2\2>?\7q\2\2?@\7h\2\2@A\7a\2\2AB\7n\2\2BC\7k\2\2C" 151 | + "D\7p\2\2DE\7g\2\2E\n\3\2\2\2FG\7k\2\2GH\7p\2\2HI\7f\2\2IJ\7g\2\2JK\7p" 152 | + "\2\2KL\7v\2\2LM\7a\2\2MN\7u\2\2NO\7k\2\2OP\7|\2\2PQ\7g\2\2Q\f\3\2\2\2" 153 | + "RS\7k\2\2ST\7p\2\2TU\7f\2\2UV\7g\2\2VW\7p\2\2WX\7v\2\2XY\7a\2\2YZ\7u\2" 154 | + "\2Z[\7v\2\2[\\\7{\2\2\\]\7n\2\2]^\7g\2\2^\16\3\2\2\2_`\7k\2\2`a\7p\2\2" 155 | + "ab\7u\2\2bc\7g\2\2cd\7t\2\2de\7v\2\2ef\7a\2\2fg\7h\2\2gh\7k\2\2hi\7p\2" 156 | + "\2ij\7c\2\2jk\7n\2\2kl\7a\2\2lm\7p\2\2mn\7g\2\2no\7y\2\2op\7n\2\2pq\7" 157 | + "k\2\2qr\7p\2\2rs\7g\2\2s\20\3\2\2\2tu\7o\2\2uv\7c\2\2vw\7z\2\2wx\7a\2" 158 | + "\2xy\7n\2\2yz\7k\2\2z{\7p\2\2{|\7g\2\2|}\7a\2\2}~\7n\2\2~\177\7g\2\2\177" 159 | + "\u0080\7p\2\2\u0080\u0081\7i\2\2\u0081\u0082\7v\2\2\u0082\u0083\7j\2\2" 160 | + "\u0083\22\3\2\2\2\u0084\u0085\7v\2\2\u0085\u0086\7c\2\2\u0086\u0087\7" 161 | + "d\2\2\u0087\u0088\7a\2\2\u0088\u0089\7y\2\2\u0089\u008a\7k\2\2\u008a\u008b" 162 | + "\7f\2\2\u008b\u008c\7v\2\2\u008c\u008d\7j\2\2\u008d\24\3\2\2\2\u008e\u008f" 163 | + "\7v\2\2\u008f\u0090\7t\2\2\u0090\u0091\7k\2\2\u0091\u0092\7o\2\2\u0092" 164 | + "\u0093\7a\2\2\u0093\u0094\7v\2\2\u0094\u0095\7t\2\2\u0095\u0096\7c\2\2" 165 | + "\u0096\u0097\7k\2\2\u0097\u0098\7n\2\2\u0098\u0099\7k\2\2\u0099\u009a" 166 | + "\7p\2\2\u009a\u009b\7i\2\2\u009b\u009c\7a\2\2\u009c\u009d\7y\2\2\u009d" 167 | + "\u009e\7j\2\2\u009e\u009f\7k\2\2\u009f\u00a0\7v\2\2\u00a0\u00a1\7g\2\2" 168 | + "\u00a1\u00a2\7u\2\2\u00a2\u00a3\7r\2\2\u00a3\u00a4\7c\2\2\u00a4\u00a5" 169 | + "\7e\2\2\u00a5\u00a6\7g\2\2\u00a6\26\3\2\2\2\u00a7\u00a8\5#\21\2\u00a8" 170 | + "\30\3\2\2\2\u00a9\u00aa\7]\2\2\u00aa\u00ab\3\2\2\2\u00ab\u00ac\b\f\2\2" 171 | + "\u00ac\32\3\2\2\2\u00ad\u00ae\7_\2\2\u00ae\34\3\2\2\2\u00af\u00b0\7?\2" 172 | + "\2\u00b0\u00b1\3\2\2\2\u00b1\u00b2\b\16\3\2\u00b2\36\3\2\2\2\u00b3\u00b5" 173 | + "\t\2\2\2\u00b4\u00b3\3\2\2\2\u00b5\u00b6\3\2\2\2\u00b6\u00b4\3\2\2\2\u00b6" 174 | + "\u00b7\3\2\2\2\u00b7\u00b8\3\2\2\2\u00b8\u00b9\b\17\4\2\u00b9 \3\2\2\2" 175 | + "\u00ba\u00be\t\3\2\2\u00bb\u00bd\n\4\2\2\u00bc\u00bb\3\2\2\2\u00bd\u00c0" 176 | + "\3\2\2\2\u00be\u00bc\3\2\2\2\u00be\u00bf\3\2\2\2\u00bf\u00c1\3\2\2\2\u00c0" 177 | + "\u00be\3\2\2\2\u00c1\u00c2\b\20\4\2\u00c2\"\3\2\2\2\u00c3\u00c5\n\5\2" 178 | + "\2\u00c4\u00c3\3\2\2\2\u00c5\u00c6\3\2\2\2\u00c6\u00c4\3\2\2\2\u00c6\u00c7" 179 | + "\3\2\2\2\u00c7$\3\2\2\2\u00c8\u00d2\n\6\2\2\u00c9\u00cd\7]\2\2\u00ca\u00cc" 180 | + "\13\2\2\2\u00cb\u00ca\3\2\2\2\u00cc\u00cf\3\2\2\2\u00cd\u00ce\3\2\2\2" 181 | + "\u00cd\u00cb\3\2\2\2\u00ce\u00d0\3\2\2\2\u00cf\u00cd\3\2\2\2\u00d0\u00d2" 182 | + "\7_\2\2\u00d1\u00c8\3\2\2\2\u00d1\u00c9\3\2\2\2\u00d2\u00d3\3\2\2\2\u00d3" 183 | + "\u00d1\3\2\2\2\u00d3\u00d4\3\2\2\2\u00d4\u00d5\3\2\2\2\u00d5\u00d6\b\22" 184 | + "\5\2\u00d6&\3\2\2\2\u00d7\u00d8\t\4\2\2\u00d8\u00d9\3\2\2\2\u00d9\u00da" 185 | + "\b\23\6\2\u00da\u00db\b\23\4\2\u00db\u00dc\b\23\5\2\u00dc(\3\2\2\2\u00dd" 186 | + "\u00de\5\37\17\2\u00de\u00df\3\2\2\2\u00df\u00e0\b\24\6\2\u00e0\u00e1" 187 | + "\b\24\4\2\u00e1*\3\2\2\2\u00e2\u00e3\5#\21\2\u00e3\u00e4\3\2\2\2\u00e4" 188 | + "\u00e5\b\25\5\2\u00e5,\3\2\2\2\13\2\3\4\u00b6\u00be\u00c6\u00cd\u00d1" 189 | + "\u00d3\7\7\3\2\7\4\2\2\3\2\6\2\2\t\3\2"; 190 | public static final ATN _ATN 191 | = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); 192 | 193 | static { 194 | _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; 195 | for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { 196 | _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/main/java/org/antlr4/EditorConfigParserBaseListener.java: -------------------------------------------------------------------------------- 1 | // Generated from editorconfig/EditorConfigParser.g4 by ANTLR 4.5 2 | package org.antlr4; 3 | 4 | import org.antlr.v4.runtime.ParserRuleContext; 5 | import org.antlr.v4.runtime.tree.ErrorNode; 6 | import org.antlr.v4.runtime.tree.TerminalNode; 7 | 8 | /** 9 | * This class provides an empty implementation of 10 | * {@link EditorConfigParserListener}, which can be extended to create a 11 | * listener which only needs to handle a subset of the available methods. 12 | */ 13 | public class EditorConfigParserBaseListener implements EditorConfigParserListener { 14 | 15 | /** 16 | * {@inheritDoc} 17 | * 18 | *

19 | * The default implementation does nothing.

20 | */ 21 | @Override 22 | public void enterFile(EditorConfigParser.FileContext ctx) { 23 | } 24 | 25 | /** 26 | * {@inheritDoc} 27 | * 28 | *

29 | * The default implementation does nothing.

30 | */ 31 | @Override 32 | public void exitFile(EditorConfigParser.FileContext ctx) { 33 | } 34 | 35 | /** 36 | * {@inheritDoc} 37 | * 38 | *

39 | * The default implementation does nothing.

40 | */ 41 | @Override 42 | public void enterSection(EditorConfigParser.SectionContext ctx) { 43 | } 44 | 45 | /** 46 | * {@inheritDoc} 47 | * 48 | *

49 | * The default implementation does nothing.

50 | */ 51 | @Override 52 | public void exitSection(EditorConfigParser.SectionContext ctx) { 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | * 58 | *

59 | * The default implementation does nothing.

60 | */ 61 | @Override 62 | public void enterSectionHeader(EditorConfigParser.SectionHeaderContext ctx) { 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | * 68 | *

69 | * The default implementation does nothing.

70 | */ 71 | @Override 72 | public void exitSectionHeader(EditorConfigParser.SectionHeaderContext ctx) { 73 | } 74 | 75 | /** 76 | * {@inheritDoc} 77 | * 78 | *

79 | * The default implementation does nothing.

80 | */ 81 | @Override 82 | public void enterRootPropertyStatement(EditorConfigParser.RootPropertyStatementContext ctx) { 83 | } 84 | 85 | /** 86 | * {@inheritDoc} 87 | * 88 | *

89 | * The default implementation does nothing.

90 | */ 91 | @Override 92 | public void exitRootPropertyStatement(EditorConfigParser.RootPropertyStatementContext ctx) { 93 | } 94 | 95 | /** 96 | * {@inheritDoc} 97 | * 98 | *

99 | * The default implementation does nothing.

100 | */ 101 | @Override 102 | public void enterPropertyStatement(EditorConfigParser.PropertyStatementContext ctx) { 103 | } 104 | 105 | /** 106 | * {@inheritDoc} 107 | * 108 | *

109 | * The default implementation does nothing.

110 | */ 111 | @Override 112 | public void exitPropertyStatement(EditorConfigParser.PropertyStatementContext ctx) { 113 | } 114 | 115 | /** 116 | * {@inheritDoc} 117 | * 118 | *

119 | * The default implementation does nothing.

120 | */ 121 | @Override 122 | public void enterPropertyKey(EditorConfigParser.PropertyKeyContext ctx) { 123 | } 124 | 125 | /** 126 | * {@inheritDoc} 127 | * 128 | *

129 | * The default implementation does nothing.

130 | */ 131 | @Override 132 | public void exitPropertyKey(EditorConfigParser.PropertyKeyContext ctx) { 133 | } 134 | 135 | /** 136 | * {@inheritDoc} 137 | * 138 | *

139 | * The default implementation does nothing.

140 | */ 141 | @Override 142 | public void enterPropertyValue(EditorConfigParser.PropertyValueContext ctx) { 143 | } 144 | 145 | /** 146 | * {@inheritDoc} 147 | * 148 | *

149 | * The default implementation does nothing.

150 | */ 151 | @Override 152 | public void exitPropertyValue(EditorConfigParser.PropertyValueContext ctx) { 153 | } 154 | 155 | /** 156 | * {@inheritDoc} 157 | * 158 | *

159 | * The default implementation does nothing.

160 | */ 161 | @Override 162 | public void enterEveryRule(ParserRuleContext ctx) { 163 | } 164 | 165 | /** 166 | * {@inheritDoc} 167 | * 168 | *

169 | * The default implementation does nothing.

170 | */ 171 | @Override 172 | public void exitEveryRule(ParserRuleContext ctx) { 173 | } 174 | 175 | /** 176 | * {@inheritDoc} 177 | * 178 | *

179 | * The default implementation does nothing.

180 | */ 181 | @Override 182 | public void visitTerminal(TerminalNode node) { 183 | } 184 | 185 | /** 186 | * {@inheritDoc} 187 | * 188 | *

189 | * The default implementation does nothing.

190 | */ 191 | @Override 192 | public void visitErrorNode(ErrorNode node) { 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/org/antlr4/EditorConfigParserBaseVisitor.java: -------------------------------------------------------------------------------- 1 | // Generated from editorconfig/EditorConfigParser.g4 by ANTLR 4.5 2 | package org.antlr4; 3 | 4 | import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; 5 | 6 | /** 7 | * This class provides an empty implementation of 8 | * {@link EditorConfigParserVisitor}, which can be extended to create a visitor 9 | * which only needs to handle a subset of the available methods. 10 | * 11 | * @param The return type of the visit operation. Use {@link Void} for 12 | * operations with no return type. 13 | */ 14 | public class EditorConfigParserBaseVisitor extends AbstractParseTreeVisitor implements EditorConfigParserVisitor { 15 | 16 | /** 17 | * {@inheritDoc} 18 | * 19 | *

20 | * The default implementation returns the result of calling 21 | * {@link #visitChildren} on {@code ctx}.

22 | */ 23 | @Override 24 | public T visitFile(EditorConfigParser.FileContext ctx) { 25 | return visitChildren(ctx); 26 | } 27 | 28 | /** 29 | * {@inheritDoc} 30 | * 31 | *

32 | * The default implementation returns the result of calling 33 | * {@link #visitChildren} on {@code ctx}.

34 | */ 35 | @Override 36 | public T visitSection(EditorConfigParser.SectionContext ctx) { 37 | return visitChildren(ctx); 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | * 43 | *

44 | * The default implementation returns the result of calling 45 | * {@link #visitChildren} on {@code ctx}.

46 | */ 47 | @Override 48 | public T visitSectionHeader(EditorConfigParser.SectionHeaderContext ctx) { 49 | return visitChildren(ctx); 50 | } 51 | 52 | /** 53 | * {@inheritDoc} 54 | * 55 | *

56 | * The default implementation returns the result of calling 57 | * {@link #visitChildren} on {@code ctx}.

58 | */ 59 | @Override 60 | public T visitRootPropertyStatement(EditorConfigParser.RootPropertyStatementContext ctx) { 61 | return visitChildren(ctx); 62 | } 63 | 64 | /** 65 | * {@inheritDoc} 66 | * 67 | *

68 | * The default implementation returns the result of calling 69 | * {@link #visitChildren} on {@code ctx}.

70 | */ 71 | @Override 72 | public T visitPropertyStatement(EditorConfigParser.PropertyStatementContext ctx) { 73 | return visitChildren(ctx); 74 | } 75 | 76 | /** 77 | * {@inheritDoc} 78 | * 79 | *

80 | * The default implementation returns the result of calling 81 | * {@link #visitChildren} on {@code ctx}.

82 | */ 83 | @Override 84 | public T visitPropertyKey(EditorConfigParser.PropertyKeyContext ctx) { 85 | return visitChildren(ctx); 86 | } 87 | 88 | /** 89 | * {@inheritDoc} 90 | * 91 | *

92 | * The default implementation returns the result of calling 93 | * {@link #visitChildren} on {@code ctx}.

94 | */ 95 | @Override 96 | public T visitPropertyValue(EditorConfigParser.PropertyValueContext ctx) { 97 | return visitChildren(ctx); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/org/antlr4/EditorConfigParserListener.java: -------------------------------------------------------------------------------- 1 | // Generated from editorconfig/EditorConfigParser.g4 by ANTLR 4.5 2 | package org.antlr4; 3 | 4 | import org.antlr.v4.runtime.tree.ParseTreeListener; 5 | 6 | /** 7 | * This interface defines a complete listener for a parse tree produced by 8 | * {@link EditorConfigParser}. 9 | */ 10 | public interface EditorConfigParserListener extends ParseTreeListener { 11 | 12 | /** 13 | * Enter a parse tree produced by {@link EditorConfigParser#file}. 14 | * 15 | * @param ctx the parse tree 16 | */ 17 | void enterFile(EditorConfigParser.FileContext ctx); 18 | 19 | /** 20 | * Exit a parse tree produced by {@link EditorConfigParser#file}. 21 | * 22 | * @param ctx the parse tree 23 | */ 24 | void exitFile(EditorConfigParser.FileContext ctx); 25 | 26 | /** 27 | * Enter a parse tree produced by {@link EditorConfigParser#section}. 28 | * 29 | * @param ctx the parse tree 30 | */ 31 | void enterSection(EditorConfigParser.SectionContext ctx); 32 | 33 | /** 34 | * Exit a parse tree produced by {@link EditorConfigParser#section}. 35 | * 36 | * @param ctx the parse tree 37 | */ 38 | void exitSection(EditorConfigParser.SectionContext ctx); 39 | 40 | /** 41 | * Enter a parse tree produced by {@link EditorConfigParser#sectionHeader}. 42 | * 43 | * @param ctx the parse tree 44 | */ 45 | void enterSectionHeader(EditorConfigParser.SectionHeaderContext ctx); 46 | 47 | /** 48 | * Exit a parse tree produced by {@link EditorConfigParser#sectionHeader}. 49 | * 50 | * @param ctx the parse tree 51 | */ 52 | void exitSectionHeader(EditorConfigParser.SectionHeaderContext ctx); 53 | 54 | /** 55 | * Enter a parse tree produced by 56 | * {@link EditorConfigParser#rootPropertyStatement}. 57 | * 58 | * @param ctx the parse tree 59 | */ 60 | void enterRootPropertyStatement(EditorConfigParser.RootPropertyStatementContext ctx); 61 | 62 | /** 63 | * Exit a parse tree produced by 64 | * {@link EditorConfigParser#rootPropertyStatement}. 65 | * 66 | * @param ctx the parse tree 67 | */ 68 | void exitRootPropertyStatement(EditorConfigParser.RootPropertyStatementContext ctx); 69 | 70 | /** 71 | * Enter a parse tree produced by 72 | * {@link EditorConfigParser#propertyStatement}. 73 | * 74 | * @param ctx the parse tree 75 | */ 76 | void enterPropertyStatement(EditorConfigParser.PropertyStatementContext ctx); 77 | 78 | /** 79 | * Exit a parse tree produced by {@link EditorConfigParser#propertyStatement}. 80 | * 81 | * @param ctx the parse tree 82 | */ 83 | void exitPropertyStatement(EditorConfigParser.PropertyStatementContext ctx); 84 | 85 | /** 86 | * Enter a parse tree produced by {@link EditorConfigParser#propertyKey}. 87 | * 88 | * @param ctx the parse tree 89 | */ 90 | void enterPropertyKey(EditorConfigParser.PropertyKeyContext ctx); 91 | 92 | /** 93 | * Exit a parse tree produced by {@link EditorConfigParser#propertyKey}. 94 | * 95 | * @param ctx the parse tree 96 | */ 97 | void exitPropertyKey(EditorConfigParser.PropertyKeyContext ctx); 98 | 99 | /** 100 | * Enter a parse tree produced by {@link EditorConfigParser#propertyValue}. 101 | * 102 | * @param ctx the parse tree 103 | */ 104 | void enterPropertyValue(EditorConfigParser.PropertyValueContext ctx); 105 | 106 | /** 107 | * Exit a parse tree produced by {@link EditorConfigParser#propertyValue}. 108 | * 109 | * @param ctx the parse tree 110 | */ 111 | void exitPropertyValue(EditorConfigParser.PropertyValueContext ctx); 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/antlr4/EditorConfigParserVisitor.java: -------------------------------------------------------------------------------- 1 | // Generated from editorconfig/EditorConfigParser.g4 by ANTLR 4.5 2 | package org.antlr4; 3 | 4 | import org.antlr.v4.runtime.tree.ParseTreeVisitor; 5 | 6 | /** 7 | * This interface defines a complete generic visitor for a parse tree produced 8 | * by {@link EditorConfigParser}. 9 | * 10 | * @param The return type of the visit operation. Use {@link Void} for 11 | * operations with no return type. 12 | */ 13 | public interface EditorConfigParserVisitor extends ParseTreeVisitor { 14 | 15 | /** 16 | * Visit a parse tree produced by {@link EditorConfigParser#file}. 17 | * 18 | * @param ctx the parse tree 19 | * @return the visitor result 20 | */ 21 | T visitFile(EditorConfigParser.FileContext ctx); 22 | 23 | /** 24 | * Visit a parse tree produced by {@link EditorConfigParser#section}. 25 | * 26 | * @param ctx the parse tree 27 | * @return the visitor result 28 | */ 29 | T visitSection(EditorConfigParser.SectionContext ctx); 30 | 31 | /** 32 | * Visit a parse tree produced by {@link EditorConfigParser#sectionHeader}. 33 | * 34 | * @param ctx the parse tree 35 | * @return the visitor result 36 | */ 37 | T visitSectionHeader(EditorConfigParser.SectionHeaderContext ctx); 38 | 39 | /** 40 | * Visit a parse tree produced by 41 | * {@link EditorConfigParser#rootPropertyStatement}. 42 | * 43 | * @param ctx the parse tree 44 | * @return the visitor result 45 | */ 46 | T visitRootPropertyStatement(EditorConfigParser.RootPropertyStatementContext ctx); 47 | 48 | /** 49 | * Visit a parse tree produced by 50 | * {@link EditorConfigParser#propertyStatement}. 51 | * 52 | * @param ctx the parse tree 53 | * @return the visitor result 54 | */ 55 | T visitPropertyStatement(EditorConfigParser.PropertyStatementContext ctx); 56 | 57 | /** 58 | * Visit a parse tree produced by {@link EditorConfigParser#propertyKey}. 59 | * 60 | * @param ctx the parse tree 61 | * @return the visitor result 62 | */ 63 | T visitPropertyKey(EditorConfigParser.PropertyKeyContext ctx); 64 | 65 | /** 66 | * Visit a parse tree produced by {@link EditorConfigParser#propertyValue}. 67 | * 68 | * @param ctx the parse tree 69 | * @return the visitor result 70 | */ 71 | T visitPropertyValue(EditorConfigParser.PropertyValueContext ctx); 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/antlr4/SyntaxError.java: -------------------------------------------------------------------------------- 1 | package org.antlr4; 2 | 3 | import org.antlr.v4.runtime.RecognitionException; 4 | 5 | /** 6 | * 7 | * @author junichi11 8 | */ 9 | public class SyntaxError { 10 | 11 | private final String message; 12 | private final int line; 13 | private final int charPositionInLine; 14 | private final RecognitionException recognitionException; 15 | 16 | public SyntaxError(String message, int line, int charPositionInLine, RecognitionException recognitionException) { 17 | this.message = message; 18 | this.line = line; 19 | this.charPositionInLine = charPositionInLine; 20 | this.recognitionException = recognitionException; 21 | } 22 | 23 | public String getMessage() { 24 | return message; 25 | } 26 | 27 | public int getLine() { 28 | return line; 29 | } 30 | 31 | public int getCharPositionInLine() { 32 | return charPositionInLine; 33 | } 34 | 35 | public RecognitionException getRecognitionException() { 36 | return recognitionException; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/editorconfig/core/EditorConfigException.java: -------------------------------------------------------------------------------- 1 | package org.editorconfig.core; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * The base class of all EditorConfig exceptions 7 | * 8 | * @author Dennis.Ushakov 9 | */ 10 | public class EditorConfigException extends Exception { 11 | public EditorConfigException(String s, IOException e) { 12 | super(s, e); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/editorconfig/core/ParsingException.java: -------------------------------------------------------------------------------- 1 | package org.editorconfig.core; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Exception which is thrown by {@link EditorConfig#getProperties(String)} if an EditorConfig file could not be parsed 7 | * 8 | * @author Dennis.Ushakov 9 | */ 10 | public class ParsingException extends EditorConfigException { 11 | public ParsingException(String s, IOException e) { 12 | super(s, e); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/editorconfig/core/VersionException.java: -------------------------------------------------------------------------------- 1 | package org.editorconfig.core; 2 | 3 | /** 4 | * Exception which is thrown by {@link EditorConfig#getProperties(String)} if an invalid version number is specified 5 | * 6 | * @author Dennis.Ushakov 7 | */ 8 | public class VersionException extends EditorConfigException { 9 | public VersionException(String s) { 10 | super(s, null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/editorconfig/core/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides the Java API for accessing 3 | * EditorConfig Core 4 | * (For the purpose and usage of EditorConfig, see 5 | * EditorConfig homepage for details). 6 | */ 7 | 8 | package org.editorconfig.core; -------------------------------------------------------------------------------- /src/main/nbm/manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | OpenIDE-Module-Install: com/welovecoding/nbeditorconfig/listener/Installer.class 3 | OpenIDE-Module-Layer: com/welovecoding/nbeditorconfig/layer.xml 4 | OpenIDE-Module-Localizing-Bundle: com/welovecoding/nbeditorconfig/Bundle.properties 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/Bundle.properties: -------------------------------------------------------------------------------- 1 | OpenIDE-Module-Name=EditorConfig Plugin 2 | OpenIDE-Module-Short-Description=EditorConfig Plugin for NetBeans IDE. 3 | OpenIDE-Module-Long-Description=\ 4 | A NetBeans IDE plugin supporting the EditorConfig standard. For more Information check: http://editorconfig.org/ \u2014 After installation, simply restart your IDE to activate the plugin.\ 5 |

Plugin info: https://github.com/welovecoding/editorconfig-netbeans

\ 6 |

BETA DISCLAIMER
THIS SOFTWARE IS IN BETA AND IS BELIEVED TO CONTAIN DEFECTS. A PRIMARY PURPOSE OF THIS BETA VERSION IS TO OBTAIN FEEDBACK ON SOFTWARE PERFORMANCE AND THE IDENTIFICATION OF DEFECTS. WE DO NOT RECOMMEND TO USE THIS SOFTWARE WITH PRODUCTION CODE. PLEASE MAKE BACKUPS OF IMPORTANT FILES BEFORE USING THIS SOFTWARE; USE CAUTION AND DO NOT TO RELY IN ANY WAY ON THE CORRECT FUNCTIONING OR PERFORMANCE OF THE SOFTWARE AND/OR ACCOMPANYING PARTS.

7 | OpenIDE-Module-Display-Category=Editing 8 | 9 | text/x-editorconfig=EditorConfig 10 | 11 | wlc-nbeditorconfig-comment=Comment 12 | wlc-nbeditorconfig-equals=Equals 13 | wlc-nbeditorconfig-keyword=Keyword 14 | wlc-nbeditorconfig-section=Section 15 | wlc-nbeditorconfig-section-delimiter=Section Delimiter 16 | wlc-nbeditorconfig-string=String 17 | wlc-nbeditorconfig-whitespace=Whitespace 18 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/EditorConfigExample.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line=lf 9 | insert_final_newline = true 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{js,py}] 14 | charset = utf-8 15 | 16 | # 4 space indentation 17 | [*.py] 18 | indent_style = space 19 | indent_size = 4 20 | 21 | # Tab indentation (no size specified) 22 | [*.js] 23 | indent_style = tab 24 | 25 | ; Indentation override for all JS under lib directory 26 | [lib/**.js] 27 | indent_style = space 28 | indent_size = 2 29 | 30 | # Matches the exact files either package.json or .travis.yml 31 | [{package.json,.travis.yml}] 32 | indent_style = space 33 | indent_size = 2 34 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/FontAndColors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/editorconfig-code-templates.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | root 7 | 8 | 9 | 10 | indent_style 11 | 12 | 13 | 14 | indent_size 15 | 16 | 17 | 18 | tab_width 19 | 20 | 21 | 22 | end_of_line 23 | 24 | 25 | 26 | charset 27 | 28 | 29 | 30 | trim_trailing_whitespace 31 | 32 | 33 | 34 | insert_final_newline 35 | 36 | 37 | 38 | max_line_length 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/filetype/description.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | EditorConfig 4 | 5 | 6 | EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. 7 | The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors 8 | to read the file format and adhere to defined styles. 9 | EditorConfig files are easily readable and they work nicely with version control systems. 10 |

11 | Visit the EditorConfig website 12 |
13 | Visit the website of the EditorConfig Plugin creators 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/filetype/ec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welovecoding/editorconfig-netbeans/2796df7e1756b7212c89b20f39b3b0f489d6c38f/src/main/resources/com/welovecoding/nbeditorconfig/filetype/ec.png -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/layer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/listener/Bundle.properties: -------------------------------------------------------------------------------- 1 | wlc-nbeditorconfig-version-error-title=Invalid Java Version - EditorConfig Plugin 2 | wlc-nbeditorconfig-version-error-message=The EditorConfig Plugin needs Java 1.7 or greater to work. Please consider updating to a compatible Java version. 3 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/options/Bundle.properties: -------------------------------------------------------------------------------- 1 | EditorConfigOptionsPanel.lineCommnetPrefixLabel.text=Line comment prefix: 2 | -------------------------------------------------------------------------------- /src/main/resources/com/welovecoding/nbeditorconfig/section16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welovecoding/editorconfig-netbeans/2796df7e1756b7212c89b20f39b3b0f489d6c38f/src/main/resources/com/welovecoding/nbeditorconfig/section16.png -------------------------------------------------------------------------------- /src/test/java/com/welovecoding/nbeditorconfig/processor/EditorConfigProcessorTest.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor; 2 | 3 | import com.welovecoding.nbeditorconfig.processor.FileInfo; 4 | import com.welovecoding.nbeditorconfig.processor.EditorConfigProcessor; 5 | import com.welovecoding.nbeditorconfig.io.model.MappedCharset; 6 | import com.welovecoding.nbeditorconfig.model.MappedEditorConfig; 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.nio.charset.StandardCharsets; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.nio.file.Paths; 13 | import java.nio.file.StandardOpenOption; 14 | import org.junit.Test; 15 | import static org.junit.Assert.*; 16 | import org.openide.filesystems.FileUtil; 17 | import org.openide.loaders.DataObject; 18 | import org.openide.util.Exceptions; 19 | import org.openide.util.Utilities; 20 | 21 | public class EditorConfigProcessorTest { 22 | 23 | public EditorConfigProcessorTest() { 24 | } 25 | 26 | @Test 27 | public void itDoesNotLoopOnFinalNewLineOperation() throws Exception { 28 | // Setup test file 29 | DataObject dataObject = null; 30 | File file = null; 31 | 32 | String content = "alert('Hello World! or Καλημέρα κόσμε! or こんにちは 世界!');"; 33 | 34 | try { 35 | file = File.createTempFile(this.getClass().getSimpleName(), ".js"); 36 | Path path = Paths.get(Utilities.toURI(file)); 37 | Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); 38 | dataObject = DataObject.find(FileUtil.toFileObject(file)); 39 | } catch (IOException ex) { 40 | Exceptions.printStackTrace(ex); 41 | } 42 | 43 | // Setup EditorConfig 44 | MappedCharset charset = new MappedCharset(StandardCharsets.UTF_8.name()); 45 | MappedEditorConfig config = new MappedEditorConfig(); 46 | config.setCharset(charset); 47 | config.setEndOfLine(System.lineSeparator()); 48 | config.setInsertFinalNewLine(true); 49 | 50 | // Run processor 51 | EditorConfigProcessor proc = new EditorConfigProcessor(); 52 | FileInfo info = proc.excuteOperations(dataObject, config); 53 | assertEquals(true, info.isFileChangeNeeded()); 54 | 55 | /* 56 | Run the processor a second time and test that a file change is NOT 57 | needed (because it has been already performed during the first run). 58 | */ 59 | proc.flushFile(info); 60 | info = proc.excuteOperations(dataObject, config); 61 | assertEquals(false, info.isFileChangeNeeded()); 62 | 63 | // Delete test file 64 | assertEquals(true, file.delete()); 65 | } 66 | 67 | @Test 68 | public void itDoesNotLoopOnTrimTrailingWhiteSpaceOperation() throws Exception { 69 | // Setup test file 70 | DataObject dataObject = null; 71 | File file = null; 72 | 73 | String content = "alert('Hello World!'); "; 74 | 75 | try { 76 | file = File.createTempFile(this.getClass().getSimpleName(), ".js"); 77 | Path path = Paths.get(Utilities.toURI(file)); 78 | Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); 79 | dataObject = DataObject.find(FileUtil.toFileObject(file)); 80 | } catch (IOException ex) { 81 | Exceptions.printStackTrace(ex); 82 | } 83 | 84 | // Setup EditorConfig 85 | MappedEditorConfig config = new MappedEditorConfig(); 86 | config.setEndOfLine(System.lineSeparator()); 87 | config.setTrimTrailingWhiteSpace(true); 88 | 89 | // Run processor 90 | EditorConfigProcessor proc = new EditorConfigProcessor(); 91 | FileInfo info = proc.excuteOperations(dataObject, config); 92 | assertEquals(true, info.isFileChangeNeeded()); 93 | 94 | /* 95 | Run the processor a second time and test that a file change is NOT 96 | needed (because it has been already performed during the first run). 97 | */ 98 | proc.flushFile(info); 99 | info = proc.excuteOperations(dataObject, config); 100 | assertEquals(false, info.isFileChangeNeeded()); 101 | 102 | // Delete test file 103 | assertEquals(true, file.delete()); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/test/java/com/welovecoding/nbeditorconfig/processor/operation/FinalNewLineOperationTest.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import com.welovecoding.nbeditorconfig.processor.operation.FinalNewLineOperation; 4 | import com.welovecoding.nbeditorconfig.processor.FileInfo; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.net.URISyntaxException; 8 | import java.nio.charset.StandardCharsets; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | import java.nio.file.StandardOpenOption; 13 | import org.junit.After; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import static org.junit.Assert.*; 17 | import org.openide.filesystems.FileUtil; 18 | import org.openide.loaders.DataObject; 19 | import org.openide.loaders.DataObjectNotFoundException; 20 | import org.openide.util.Exceptions; 21 | import org.openide.util.Utilities; 22 | 23 | public class FinalNewLineOperationTest { 24 | 25 | private DataObject dataObject = null; 26 | private File file; 27 | 28 | @Before 29 | public void setUp() throws DataObjectNotFoundException, URISyntaxException { 30 | String content = "alert('no final new line.');"; 31 | 32 | try { 33 | file = File.createTempFile(this.getClass().getSimpleName(), ".js"); 34 | Path path = Paths.get(Utilities.toURI(file)); 35 | Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); 36 | dataObject = DataObject.find(FileUtil.toFileObject(file)); 37 | } catch (IOException ex) { 38 | Exceptions.printStackTrace(ex); 39 | } 40 | } 41 | 42 | @After 43 | public void tearDown() { 44 | file.delete(); 45 | } 46 | 47 | public FinalNewLineOperationTest() { 48 | } 49 | 50 | @Test 51 | public void itAddsAFinalNewLine() throws IOException { 52 | StringBuilder content = new StringBuilder(dataObject.getPrimaryFile().asText()); 53 | 54 | String expectedContent = content.toString(); 55 | expectedContent += System.lineSeparator(); 56 | 57 | FileInfo info = new FileInfo(); 58 | info.setContent(content); 59 | info.setEndOfLine(System.lineSeparator()); 60 | 61 | boolean wasPerformed = new FinalNewLineOperation().operate(info); 62 | 63 | assertEquals(true, wasPerformed); 64 | assertEquals(expectedContent, content.toString()); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/com/welovecoding/nbeditorconfig/processor/operation/IndentSizeOperationTest.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import com.welovecoding.nbeditorconfig.processor.operation.IndentSizeOperation; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.nio.charset.StandardCharsets; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | import java.nio.file.StandardOpenOption; 11 | import java.util.prefs.BackingStoreException; 12 | import java.util.prefs.Preferences; 13 | import javax.swing.text.BadLocationException; 14 | import javax.swing.text.StyledDocument; 15 | import org.junit.After; 16 | import static org.junit.Assert.assertEquals; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | import org.netbeans.api.editor.settings.SimpleValueNames; 20 | import org.netbeans.modules.editor.indent.api.Reformat; 21 | import org.netbeans.modules.editor.indent.spi.CodeStylePreferences; 22 | import org.openide.cookies.EditorCookie; 23 | import org.openide.filesystems.FileUtil; 24 | import org.openide.loaders.DataObject; 25 | import org.openide.text.NbDocument; 26 | import org.openide.util.Exceptions; 27 | import org.openide.util.Utilities; 28 | 29 | public class IndentSizeOperationTest { 30 | 31 | private DataObject dataObject; 32 | private File file; 33 | 34 | @Before 35 | public void setUp() { 36 | String codeWith4SpacesIndent = "(function(){" + System.lineSeparator(); 37 | codeWith4SpacesIndent += " alert('Hello World!');" + System.lineSeparator(); 38 | codeWith4SpacesIndent += "})();"; 39 | 40 | try { 41 | file = File.createTempFile(this.getClass().getSimpleName(), ".js"); 42 | Path path = Paths.get(Utilities.toURI(file)); 43 | Files.write(path, codeWith4SpacesIndent.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); 44 | dataObject = DataObject.find(FileUtil.toFileObject(file)); 45 | } catch (IOException ex) { 46 | Exceptions.printStackTrace(ex); 47 | } 48 | } 49 | 50 | @After 51 | public void tearDown() { 52 | file.delete(); 53 | } 54 | 55 | public IndentSizeOperationTest() { 56 | } 57 | 58 | @Test 59 | public void itDetectsIfChangesAreNeeded() throws 60 | IOException, BadLocationException, BackingStoreException { 61 | int indentWidth = 2; 62 | 63 | String codeWith2SpacesIndent = "(function(){" + System.lineSeparator(); 64 | codeWith2SpacesIndent += " alert('Hello World!');" + System.lineSeparator(); 65 | codeWith2SpacesIndent += "})();"; 66 | 67 | Preferences codeStyle = CodeStylePreferences.get( 68 | dataObject.getPrimaryFile(), 69 | dataObject.getPrimaryFile().getMIMEType() 70 | ).getPreferences(); 71 | 72 | // Check indent size before change 73 | // Note: "org-netbeans-modules-csl-api" sets default value to 4 74 | int indentSizeBefore = codeStyle.getInt(SimpleValueNames.INDENT_SHIFT_WIDTH, 4); 75 | assertEquals(4, indentSizeBefore); 76 | 77 | // Change indent size within an operation 78 | boolean changeNeeded = new IndentSizeOperation(dataObject.getPrimaryFile()).changeIndentSize(indentWidth); 79 | assertEquals(true, changeNeeded); 80 | 81 | // Update code style reference 82 | codeStyle = CodeStylePreferences.get( 83 | dataObject.getPrimaryFile(), 84 | dataObject.getPrimaryFile().getMIMEType() 85 | ).getPreferences(); 86 | 87 | // Save the new style 88 | codeStyle.flush(); 89 | 90 | // Check that new style has been applied 91 | int indentSizeAfter = codeStyle.getInt(SimpleValueNames.INDENT_SHIFT_WIDTH, -1); 92 | assertEquals(indentWidth, indentSizeAfter); 93 | 94 | // Save indent size 95 | final EditorCookie cookie = dataObject.getLookup().lookup(EditorCookie.class); 96 | cookie.open(); 97 | 98 | final StyledDocument document = cookie.openDocument(); 99 | 100 | NbDocument.runAtomicAsUser(document, new Runnable() { 101 | @Override 102 | public void run() { 103 | try { 104 | // Save test file 105 | cookie.saveDocument(); 106 | 107 | // Reformat test file 108 | Reformat reformat = Reformat.get(document); 109 | reformat.lock(); 110 | 111 | try { 112 | reformat.reformat(0, document.getLength()); 113 | } catch (BadLocationException ex) { 114 | Exceptions.printStackTrace(ex); 115 | } finally { 116 | reformat.unlock(); 117 | try { 118 | // Save formatted document 119 | cookie.saveDocument(); 120 | System.out.println("Content saved:"); 121 | System.out.println(document.getText(0, document.getLength())); 122 | } catch (IOException | BadLocationException ex) { 123 | Exceptions.printStackTrace(ex); 124 | } 125 | } 126 | } catch (IOException ex) { 127 | Exceptions.printStackTrace(ex); 128 | } 129 | } 130 | }); 131 | 132 | // TODO: This check fails 133 | // assertEquals(codeWith2SpacesIndent, dataObject.getPrimaryFile().asText()); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/test/java/com/welovecoding/nbeditorconfig/processor/operation/LineEndingOperationTest.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import com.welovecoding.nbeditorconfig.processor.operation.LineEndingOperation; 4 | import com.welovecoding.nbeditorconfig.processor.FileInfo; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.net.URISyntaxException; 8 | import java.nio.charset.StandardCharsets; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | import java.nio.file.StandardOpenOption; 13 | import org.junit.After; 14 | import static org.junit.Assert.assertEquals; 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.openide.filesystems.FileUtil; 18 | import org.openide.loaders.DataObject; 19 | import org.openide.loaders.DataObjectNotFoundException; 20 | import org.openide.util.Exceptions; 21 | import org.openide.util.Utilities; 22 | 23 | public class LineEndingOperationTest { 24 | 25 | private DataObject dataObject = null; 26 | private File file; 27 | 28 | @Before 29 | public void setUp() throws DataObjectNotFoundException, URISyntaxException { 30 | String content = "alert('no final new line.');\n"; 31 | 32 | try { 33 | file = File.createTempFile(this.getClass().getSimpleName(), ".js"); 34 | Path path = Paths.get(Utilities.toURI(file)); 35 | Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); 36 | dataObject = DataObject.find(FileUtil.toFileObject(file)); 37 | } catch (IOException ex) { 38 | Exceptions.printStackTrace(ex); 39 | } 40 | } 41 | 42 | @After 43 | public void tearDown() { 44 | file.delete(); 45 | } 46 | 47 | public LineEndingOperationTest() { 48 | } 49 | 50 | /** 51 | * Test of operate method, of class LineEndingOperation. 52 | */ 53 | @Test 54 | public void itChangesEndOfLine() throws IOException { 55 | StringBuilder content = new StringBuilder(dataObject.getPrimaryFile().asText()); 56 | 57 | String expectedContent = content.toString() + "\r"; 58 | 59 | FileInfo info = new FileInfo(); 60 | info.setContent(content); 61 | info.setEndOfLine("\n\r"); 62 | 63 | boolean wasPerformed = new LineEndingOperation().operate(info); 64 | 65 | assertEquals(true, wasPerformed); 66 | assertEquals(expectedContent, content.toString()); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/welovecoding/nbeditorconfig/processor/operation/TrimTrailingWhiteSpaceOperationTest.java: -------------------------------------------------------------------------------- 1 | package com.welovecoding.nbeditorconfig.processor.operation; 2 | 3 | import com.welovecoding.nbeditorconfig.processor.operation.TrimTrailingWhiteSpaceOperation; 4 | import com.welovecoding.nbeditorconfig.processor.FileInfo; 5 | import org.junit.Test; 6 | import static org.junit.Assert.*; 7 | 8 | public class TrimTrailingWhiteSpaceOperationTest { 9 | 10 | public TrimTrailingWhiteSpaceOperationTest() { 11 | } 12 | 13 | @Test 14 | public void itRemovesSpacesFromEndOfLine() { 15 | StringBuilder content = new StringBuilder(); 16 | content.append("(function(){ ").append(System.lineSeparator()); 17 | content.append(" alert('Hello World!'); ").append(System.lineSeparator()); 18 | content.append("})(); ").append(System.lineSeparator()); 19 | 20 | StringBuilder expectedContent = new StringBuilder(); 21 | expectedContent.append("(function(){").append(System.lineSeparator()); 22 | expectedContent.append(" alert('Hello World!');").append(System.lineSeparator()); 23 | expectedContent.append("})();").append(System.lineSeparator()); 24 | 25 | FileInfo info = new FileInfo(); 26 | info.setContent(content); 27 | info.setEndOfLine(System.lineSeparator()); 28 | 29 | boolean removedWhiteSpaces = new TrimTrailingWhiteSpaceOperation().operate(info); 30 | 31 | assertEquals(true, removedWhiteSpaces); 32 | assertEquals(expectedContent.toString(), content.toString()); 33 | } 34 | 35 | @Test 36 | public void itRemovesMultipleSpacesFromEndOfLine() { 37 | StringBuilder content = new StringBuilder(); 38 | content.append("(function(){ ").append(System.lineSeparator()); 39 | content.append(" alert('Hello World!'); ").append(System.lineSeparator()); 40 | content.append("})(); ").append(System.lineSeparator()); 41 | 42 | StringBuilder expectedContent = new StringBuilder(); 43 | expectedContent.append("(function(){").append(System.lineSeparator()); 44 | expectedContent.append(" alert('Hello World!');").append(System.lineSeparator()); 45 | expectedContent.append("})();").append(System.lineSeparator()); 46 | 47 | FileInfo info = new FileInfo(); 48 | info.setContent(content); 49 | info.setEndOfLine(System.lineSeparator()); 50 | 51 | boolean removedWhiteSpaces = new TrimTrailingWhiteSpaceOperation().operate(info); 52 | 53 | assertEquals(true, removedWhiteSpaces); 54 | assertEquals(expectedContent.toString(), content.toString()); 55 | } 56 | 57 | @Test 58 | public void itRemovesTabsFromEndOfLine() { 59 | StringBuilder content = new StringBuilder(); 60 | content.append("(function(){\t").append(System.lineSeparator()); 61 | content.append(" alert('Hello World!');\t").append(System.lineSeparator()); 62 | content.append("})();\t").append(System.lineSeparator()); 63 | 64 | StringBuilder expectedContent = new StringBuilder(); 65 | expectedContent.append("(function(){").append(System.lineSeparator()); 66 | expectedContent.append(" alert('Hello World!');").append(System.lineSeparator()); 67 | expectedContent.append("})();").append(System.lineSeparator()); 68 | 69 | FileInfo info = new FileInfo(); 70 | info.setContent(content); 71 | info.setEndOfLine(System.lineSeparator()); 72 | 73 | boolean removedWhiteSpaces = new TrimTrailingWhiteSpaceOperation().operate(info); 74 | 75 | assertEquals(true, removedWhiteSpaces); 76 | assertEquals(expectedContent.toString(), content.toString()); 77 | } 78 | 79 | @Test 80 | public void itRemovesMultipleTabsFromEndOfLine() { 81 | StringBuilder content = new StringBuilder(); 82 | content.append("(function(){\t\t").append(System.lineSeparator()); 83 | content.append(" alert('Hello World!');\t\t").append(System.lineSeparator()); 84 | content.append("})();\t\t").append(System.lineSeparator()); 85 | 86 | StringBuilder expectedContent = new StringBuilder(); 87 | expectedContent.append("(function(){").append(System.lineSeparator()); 88 | expectedContent.append(" alert('Hello World!');").append(System.lineSeparator()); 89 | expectedContent.append("})();").append(System.lineSeparator()); 90 | 91 | FileInfo info = new FileInfo(); 92 | info.setContent(content); 93 | info.setEndOfLine(System.lineSeparator()); 94 | 95 | boolean removedWhiteSpaces = new TrimTrailingWhiteSpaceOperation().operate(info); 96 | 97 | assertEquals(true, removedWhiteSpaces); 98 | assertEquals(expectedContent.toString(), content.toString()); 99 | } 100 | 101 | @Test 102 | public void itRemovesMixedTabsAndSpacesFromEndOfLine() { 103 | StringBuilder content = new StringBuilder(); 104 | content.append("(function(){ ").append(System.lineSeparator()); 105 | content.append(" alert('Hello World!');\t \t").append(System.lineSeparator()); 106 | content.append("})(); \t\t").append(System.lineSeparator()); 107 | 108 | StringBuilder expectedContent = new StringBuilder(); 109 | expectedContent.append("(function(){").append(System.lineSeparator()); 110 | expectedContent.append(" alert('Hello World!');").append(System.lineSeparator()); 111 | expectedContent.append("})();").append(System.lineSeparator()); 112 | 113 | FileInfo info = new FileInfo(); 114 | info.setContent(content); 115 | info.setEndOfLine(System.lineSeparator()); 116 | 117 | boolean removedWhiteSpaces = new TrimTrailingWhiteSpaceOperation().operate(info); 118 | 119 | assertEquals(true, removedWhiteSpaces); 120 | assertEquals(expectedContent.toString(), content.toString()); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/test/resources/files/IndentSize.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/test/resources/files/charsets/latin1.txt: -------------------------------------------------------------------------------- 1 | EditorConfig: latin1 2 | Java: ISO-8859-1 3 | Notepad++: ANSI / US-ASCII -------------------------------------------------------------------------------- /src/test/resources/files/charsets/utf-16-be.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welovecoding/editorconfig-netbeans/2796df7e1756b7212c89b20f39b3b0f489d6c38f/src/test/resources/files/charsets/utf-16-be.txt -------------------------------------------------------------------------------- /src/test/resources/files/charsets/utf-16-le.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welovecoding/editorconfig-netbeans/2796df7e1756b7212c89b20f39b3b0f489d6c38f/src/test/resources/files/charsets/utf-16-le.txt -------------------------------------------------------------------------------- /src/test/resources/files/charsets/utf-8-bom.txt: -------------------------------------------------------------------------------- 1 | EditorConfig: utf-8 2 | Java: UTF-8 3 | Notepad++: UTF-8 4 | 5 | Buchstabe y 6 | Buchstabe ä 7 | Eurozeichen € 8 | Violinschlüssel 𝄞 9 | CJK-Ideogramm 𤽜 10 | -------------------------------------------------------------------------------- /src/test/resources/files/charsets/utf-8.txt: -------------------------------------------------------------------------------- 1 | EditorConfig: utf-8 2 | Java: UTF-8 3 | Notepad++: UTF-8 without BOM 4 | 5 | Buchstabe y 6 | Buchstabe ä 7 | Eurozeichen € 8 | Violinschlüssel 𝄞 9 | CJK-Ideogramm 𤽜 10 | -------------------------------------------------------------------------------- /src/test/resources/files/configs/editorconfig-test.ini: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | # 4 space indentation 14 | [*.py] 15 | indent_size = 4 16 | indent_style = space 17 | 18 | # Tab indentation (no size specified) 19 | [*.js] 20 | indent_style = tab 21 | tab_width = 4 22 | 23 | # Indentation override for all JS under lib directory 24 | [lib/**.js] 25 | indent_size = 2 26 | indent_style = space 27 | 28 | # Matches the exact files either package.json or .travis.yml 29 | [{package.json,.travis.yml}] 30 | indent_size = 4 31 | indent_style = space 32 | -------------------------------------------------------------------------------- /src/test/resources/files/line-endings/utf8_final_newline_1_character.txt: -------------------------------------------------------------------------------- 1 | A 2 | -------------------------------------------------------------------------------- /src/test/resources/files/line-endings/utf8_final_newline_n.txt: -------------------------------------------------------------------------------- 1 | # Final new line in UNIX style (\n) 2 | -------------------------------------------------------------------------------- /src/test/resources/files/line-endings/utf8_final_newline_rn.txt: -------------------------------------------------------------------------------- 1 | # Last line in Windows format 2 | -------------------------------------------------------------------------------- /src/test/resources/files/line-endings/utf8_no_final_newline.txt: -------------------------------------------------------------------------------- 1 | # No final new line -------------------------------------------------------------------------------- /src/test/resources/files/line-endings/utf8_no_final_newline_1_character.txt: -------------------------------------------------------------------------------- 1 | A --------------------------------------------------------------------------------