├── .gitignore ├── LICENSE ├── README.markdown ├── build.xml ├── manifest.mf ├── nbproject ├── build-impl.xml ├── genfiles.properties ├── platform.properties ├── project.properties └── project.xml ├── release └── modules │ └── ext │ └── js.jar ├── screenshot.png ├── screenshot2.png └── src └── coffeescript └── nb ├── CoffeScriptLanguageRegistration.java ├── CoffeeScriptAction.java ├── CoffeeScriptAutocompileContext.java ├── CoffeeScriptAutocompileTask.java ├── CoffeeScriptCompiler.java ├── CoffeeScriptDataObject.java ├── CoffeeScriptEmbeddingProvidersFactory.java ├── CoffeeScriptFormatter.java ├── CoffeeScriptHTMLParserTask.java ├── CoffeeScriptLanguage.java ├── CoffeeScriptLexer.java ├── CoffeeScriptLexerBase.java ├── CoffeeScriptMimeResolver.java ├── CoffeeScriptNodeJSCompiler.java ├── CoffeeScriptParser.java ├── CoffeeScriptRegexpLanguage.java ├── CoffeeScriptRegexpLexer.java ├── CoffeeScriptRegexpTokenId.java ├── CoffeeScriptRhinoCompiler.java ├── CoffeeScriptSourceEncodingQuery.java ├── CoffeeScriptStringLanguage.java ├── CoffeeScriptStringLexer.java ├── CoffeeScriptStringTokenId.java ├── CoffeeScriptStructureScanner.java ├── CoffeeScriptTokenId.java ├── CoffeeScriptUtils.java ├── options ├── Bundle.properties ├── CoffeeScriptOptionsPanel.form ├── CoffeeScriptOptionsPanel.java ├── CoffeeScriptOptionsPanelController.java └── CoffeeScriptSettings.java ├── project ├── CoffeeScriptLogicalView.java ├── CoffeeScriptPanelProvider.java ├── CoffeeScriptProject.java ├── CoffeeScriptProjectCustomizer.java ├── CoffeeScriptProjectFactory.java ├── sample │ ├── Bundle.properties │ ├── CoffeeScriptApplicationPanelVisual.form │ ├── CoffeeScriptApplicationPanelVisual.java │ ├── CoffeeScriptApplicationProjectDescription.html │ ├── CoffeeScriptApplicationWizardIterator.java │ └── CoffeeScriptApplicationWizardPanel.java └── ui │ ├── CoffeeScriptActionSettingsPanel.form │ └── CoffeeScriptActionSettingsPanel.java └── resources ├── Bundle.properties ├── CoffeeScriptApplicationProject.zip ├── CoffeeScriptPreview.coffee ├── CoffeeScriptTemplate.coffee ├── FontAndColors.xml ├── coffee-script.js ├── coffeescript-icon.png ├── layer.xml ├── preferences.xml ├── refresh.png └── stop.png /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /nbproject/private/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | CoffeeScript support for NetBeans IDE 2 | --------------------- 3 | 4 | http://netbeans.org/ 5 | 6 | ### FEATURES: 7 | 8 | * Just works! 9 | * (No need for Node.js) 10 | 11 | * Advanced syntax highlighting 12 | 13 | * Error checking by background compilation 14 | * (Rhino JavaScript engine is used to compile *.coffee files) 15 | 16 | * Autocompiling to a JavaScript file in the same directory 17 | * (Test it in a web browser right away) 18 | 19 | * Syntax highlighting and error checking in scripts 20 | * (HTML and PHP files) 21 | 22 | * Indentation & formatting, code folding 23 | 24 | * Option to use Node.js with [coffee](http://jashkenas.github.com/coffee-script/#installation) command for compiling. Supported platforms: Mac, Linux and new experimental support for Windows. 25 | 26 | * Cakefile project support 27 | 28 | ### VERSION HISTORY: 29 | 30 | * 1.4 31 | 32 | * Internal improvements and bugfixes 33 | 34 | * 1.3 35 | 36 | * CoffeeScript updated to v1.6.3 37 | 38 | * Improved *nix command line support 39 | 40 | * 1.2.1 41 | 42 | * Bugfixes 43 | 44 | * 1.2 45 | 46 | * CoffeeScript updated to v1.4.0 47 | 48 | * New option to set autocompile target folder 49 | 50 | * New option to always use UTF8 encoding for CoffeeScript files 51 | 52 | * NetBeans 7.3 support 53 | 54 | * 1.1.1 55 | 56 | * CoffeeScript updated to v1.3.3 57 | 58 | * 1.1.0 59 | 60 | * CoffeeScript updated to v1.3.1 61 | 62 | * Syntax highlight improvements 63 | 64 | * 1.0.2 65 | 66 | * Bugfixes 67 | 68 | * 1.0.1 69 | 70 | * Bugfixes 71 | 72 | * 1.0.0 73 | 74 | * Node.js compiling experimental support for Windows 75 | 76 | * Cakefile project support 77 | 78 | * Default indentation level 2spaces 79 | 80 | * 0.9.6 81 | 82 | * CoffeeScript updated to v1.2.0 83 | 84 | * 0.9.5 85 | 86 | * Code folding 87 | 88 | * Bugfixes 89 | 90 | * 0.9.4 91 | 92 | * Support for Node.js and coffee command compiling (Mac, Linux only) 93 | 94 | * Bugfixes 95 | 96 | * 0.9.3 97 | 98 | * Bugfixes 99 | 100 | * 0.9.2 101 | 102 | * CoffeeScript updated to v1.1.2 103 | 104 | * Rhino updated to 1.7R3 105 | 106 | * Bugfixes 107 | 108 | * 0.9.1 109 | 110 | * Added the support for indentation & formatting 111 | 112 | * 0.9 113 | 114 | * CoffeeScript updated to v1.1.1 115 | 116 | * Added syntax highlighting and error checking in scripts (HTML, PHP) 117 | 118 | ### INSTRUCTIONS: 119 | 120 | Download [coffeescript-nb.nbm](http://plugins.netbeans.org/plugin/39007) and add it using NetBeans plugin manager 121 | 122 | Plugin requires NetBeans 7.0 123 | 124 | ### SCREENSHOT: 125 | 126 | ![screenshot2](https://github.com/dstepanov/coffeescript-netbeans/raw/master/screenshot2.png) 127 | * * * 128 | ![screenshot](https://github.com/dstepanov/coffeescript-netbeans/raw/master/screenshot.png) 129 | 130 | ### LICENSE: 131 | 132 | Apache License, Version 2.0 133 | 134 | ### COPYRIGHT: 135 | 136 | 2011-2013 Denis Stepanov 137 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Builds, tests, and runs the project coffeescript.nb. 7 | 8 | 9 | -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | OpenIDE-Module: coffeescript.nb/1 3 | OpenIDE-Module-Layer: coffeescript/nb/resources/layer.xml 4 | OpenIDE-Module-Localizing-Bundle: coffeescript/nb/resources/Bundle.properties 5 | OpenIDE-Module-Specification-Version: 1.4 6 | 7 | -------------------------------------------------------------------------------- /nbproject/build-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 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 | -------------------------------------------------------------------------------- /nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=b3b4798d 2 | build.xml.script.CRC32=97de7681 3 | build.xml.stylesheet.CRC32=a56c6a5b@2.55.1 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=b3b4798d 7 | nbproject/build-impl.xml.script.CRC32=f1c6dc8e 8 | nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.55.1 9 | -------------------------------------------------------------------------------- /nbproject/platform.properties: -------------------------------------------------------------------------------- 1 | cluster.path=\ 2 | ${nbplatform.active.dir}/apisupport:\ 3 | ${nbplatform.active.dir}/harness:\ 4 | ${nbplatform.active.dir}/ide:\ 5 | ${nbplatform.active.dir}/java:\ 6 | ${nbplatform.active.dir}/nb:\ 7 | ${nbplatform.active.dir}/php:\ 8 | ${nbplatform.active.dir}/platform:\ 9 | ${nbplatform.active.dir}/profiler:\ 10 | ${nbplatform.active.dir}/websvccommon 11 | nbplatform.active=default -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | file.reference.js.jar=release/modules/ext/js.jar 2 | javac.source=1.5 3 | javac.compilerargs=-Xlint -Xlint:-serial 4 | license.file=LICENSE 5 | nbm.homepage=https://github.com/dstepanov/coffeescript-netbeans 6 | nbm.module.author=Denis Stepanov 7 | nbm.needs.restart=true 8 | run.args.extra=-J-XX:PermSize=32m -J-XX:MaxPermSize=300m 9 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.apisupport.project 4 | 5 | 6 | coffeescript.nb 7 | 8 | 9 | 10 | org.netbeans.api.progress 11 | 12 | 13 | 14 | 1 15 | 1.23.1 16 | 17 | 18 | 19 | org.netbeans.modules.csl.api 20 | 21 | 22 | 23 | 2 24 | 2.14.1.2.1.1.6 25 | 26 | 27 | 28 | org.netbeans.modules.editor.indent 29 | 30 | 31 | 32 | 2 33 | 1.23.1 34 | 35 | 36 | 37 | org.netbeans.modules.editor.lib 38 | 39 | 40 | 41 | 3 42 | 3.9.1.13.10 43 | 44 | 45 | 46 | org.netbeans.modules.editor.mimelookup 47 | 48 | 49 | 50 | 1 51 | 1.21.1 52 | 53 | 54 | 55 | org.netbeans.modules.editor.settings 56 | 57 | 58 | 59 | 1 60 | 1.35.1 61 | 62 | 63 | 64 | org.netbeans.modules.html.lexer 65 | 66 | 67 | 68 | 1 69 | 1.12.1 70 | 71 | 72 | 73 | org.netbeans.modules.lexer 74 | 75 | 76 | 77 | 2 78 | 1.38.1.1 79 | 80 | 81 | 82 | org.netbeans.modules.options.api 83 | 84 | 85 | 86 | 1 87 | 1.21.1 88 | 89 | 90 | 91 | org.netbeans.modules.parsing.api 92 | 93 | 94 | 95 | 1 96 | 1.39.1.6 97 | 98 | 99 | 100 | org.netbeans.modules.projectapi 101 | 102 | 103 | 104 | 1 105 | 1.35.1 106 | 107 | 108 | 109 | org.netbeans.modules.projectuiapi 110 | 111 | 112 | 113 | 1 114 | 1.50.1.8 115 | 116 | 117 | 118 | org.netbeans.modules.queries 119 | 120 | 121 | 122 | 1 123 | 1.23.1 124 | 125 | 126 | 127 | org.netbeans.spi.editor.hints 128 | 129 | 130 | 131 | 0 132 | 1.17.1.7.8 133 | 134 | 135 | 136 | org.openide.awt 137 | 138 | 139 | 140 | 7.30.1 141 | 142 | 143 | 144 | org.openide.dialogs 145 | 146 | 147 | 148 | 7.20.1 149 | 150 | 151 | 152 | org.openide.execution 153 | 154 | 155 | 156 | 1.21.1 157 | 158 | 159 | 160 | org.openide.filesystems 161 | 162 | 163 | 164 | 7.46.1 165 | 166 | 167 | 168 | org.openide.io 169 | 170 | 171 | 172 | 1.26.1 173 | 174 | 175 | 176 | org.openide.loaders 177 | 178 | 179 | 180 | 7.20.1 181 | 182 | 183 | 184 | org.openide.nodes 185 | 186 | 187 | 188 | 7.20.1 189 | 190 | 191 | 192 | org.openide.text 193 | 194 | 195 | 196 | 6.36.1 197 | 198 | 199 | 200 | org.openide.util 201 | 202 | 203 | 204 | 8.14.1 205 | 206 | 207 | 208 | org.openide.util.lookup 209 | 210 | 211 | 212 | 8.6.1 213 | 214 | 215 | 216 | org.openide.windows 217 | 218 | 219 | 220 | 6.39.1 221 | 222 | 223 | 224 | 225 | 226 | ext/js.jar 227 | release/modules/ext/js.jar 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /release/modules/ext/js.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstepanov/coffeescript-netbeans/1af2eee5a4ad08b2db328274a001311732a46798/release/modules/ext/js.jar -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstepanov/coffeescript-netbeans/1af2eee5a4ad08b2db328274a001311732a46798/screenshot.png -------------------------------------------------------------------------------- /screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstepanov/coffeescript-netbeans/1af2eee5a4ad08b2db328274a001311732a46798/screenshot2.png -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeScriptLanguageRegistration.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import org.netbeans.api.lexer.Language; 17 | import org.netbeans.modules.csl.api.Formatter; 18 | import org.netbeans.modules.csl.api.StructureScanner; 19 | import org.netbeans.modules.csl.spi.DefaultLanguageConfig; 20 | import org.netbeans.modules.csl.spi.LanguageRegistration; 21 | import org.netbeans.modules.parsing.spi.Parser; 22 | 23 | /** 24 | * 25 | * @author Denis Stepanov 26 | */ 27 | @LanguageRegistration(mimeType = CoffeeScriptLanguage.MIME_TYPE) 28 | public class CoffeScriptLanguageRegistration extends DefaultLanguageConfig { 29 | 30 | @Override 31 | public String getLineCommentPrefix() { 32 | return "#"; 33 | } 34 | 35 | @Override 36 | public Language getLexerLanguage() { 37 | return CoffeeScriptLanguage.getLanguage(); 38 | } 39 | 40 | @Override 41 | public Parser getParser() { 42 | return new CoffeeScriptParser(); 43 | } 44 | 45 | @Override 46 | public boolean hasFormatter() { 47 | return true; 48 | } 49 | 50 | @Override 51 | public Formatter getFormatter() { 52 | return new CoffeeScriptFormatter(); 53 | } 54 | 55 | @Override 56 | public String getDisplayName() { 57 | return "CoffeeScript"; 58 | } 59 | 60 | @Override 61 | public StructureScanner getStructureScanner() { 62 | return new CoffeeScriptStructureScanner(); 63 | } 64 | 65 | @Override 66 | public boolean hasStructureScanner() { 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptAction.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import coffeescript.nb.CoffeeScriptCompiler.CompilerResult; 17 | import coffeescript.nb.options.CoffeeScriptSettings; 18 | import java.awt.event.ActionEvent; 19 | import java.util.Collection; 20 | import java.util.concurrent.Future; 21 | import javax.swing.AbstractAction; 22 | import javax.swing.Action; 23 | import javax.swing.JMenu; 24 | import javax.swing.JMenuItem; 25 | import org.netbeans.api.progress.ProgressHandle; 26 | import org.netbeans.api.progress.ProgressHandleFactory; 27 | import org.openide.LifecycleManager; 28 | import org.openide.awt.ActionID; 29 | import org.openide.awt.ActionReference; 30 | import org.openide.awt.ActionReferences; 31 | import org.openide.awt.ActionRegistration; 32 | import org.openide.execution.ExecutionEngine; 33 | import org.openide.execution.ExecutorTask; 34 | import org.openide.util.Cancellable; 35 | import org.openide.util.ContextAwareAction; 36 | import org.openide.util.Exceptions; 37 | import org.openide.util.ImageUtilities; 38 | import org.openide.util.Lookup; 39 | import org.openide.util.NbBundle; 40 | import org.openide.util.RequestProcessor; 41 | import org.openide.util.actions.Presenter.Popup; 42 | import org.openide.windows.IOProvider; 43 | import org.openide.windows.InputOutput; 44 | 45 | /** 46 | * 47 | * @author Denis Stepanov 48 | */ 49 | @ActionID(category = "Build", id = "CoffeeScriptAction") 50 | @ActionRegistration(displayName = "coffeescript.nb.resources.Bundle#CoffeeScriptAction") 51 | @ActionReferences({ 52 | @ActionReference(path = "Loaders/text/coffeescript/Actions", position = 200, separatorAfter = 201, separatorBefore = 199), 53 | @ActionReference(path = "Editors/text/coffeescript/Popup", position = 10001, separatorBefore = 10000) 54 | }) 55 | public class CoffeeScriptAction extends AbstractAction implements ContextAwareAction { 56 | 57 | public Action createContextAwareInstance(Lookup actionContext) { 58 | Collection data = actionContext.lookupAll(CoffeeScriptDataObject.class); 59 | if (data.isEmpty()) { 60 | return null; 61 | } 62 | return new PopupAction(data); 63 | } 64 | 65 | public void actionPerformed(ActionEvent ae) { 66 | } 67 | 68 | private static class PopupAction extends AbstractAction implements Popup { 69 | 70 | private Collection data; 71 | boolean switchOn = true, switchOff = true, switchMultipleValues; 72 | 73 | public PopupAction(Collection data) { 74 | super(NbBundle.getBundle("coffeescript.nb.resources.Bundle").getString("CoffeeScriptAction")); 75 | this.data = data; 76 | 77 | for (CoffeeScriptDataObject dataObject : data) { 78 | boolean status = CoffeeScriptAutocompileContext.get().isEnabled(dataObject.getPrimaryFile()); 79 | switchOn = switchOn && status; 80 | switchOff = switchOff && !status; 81 | } 82 | switchMultipleValues = !switchOn && !switchOff; 83 | } 84 | 85 | public JMenuItem getPopupPresenter() { 86 | String color = switchMultipleValues ? "gray" : (switchOn ? "green" : "black"); 87 | String label = switchMultipleValues ? "-" : (switchOn ? "on" : "off"); 88 | String switchActionName = String.format("Autocompile: %s", color, label); 89 | 90 | final JMenu menu = new JMenu(this); 91 | JMenu switchmenu = new JMenu(switchActionName); 92 | if (switchMultipleValues || switchOff) { 93 | switchmenu.add(new AbstractAction("Turn on") { 94 | 95 | public void actionPerformed(ActionEvent ae) { 96 | for (CoffeeScriptDataObject dataObject : data) { 97 | CoffeeScriptAutocompileContext.get().enableAutocompile(dataObject.getPrimaryFile()); 98 | } 99 | RequestProcessor processor = RequestProcessor.getDefault(); 100 | final Future[] futureHolder = new Future[1]; 101 | futureHolder[0] = processor.submit(new CompilerTask(data, CoffeeScriptSettings.get().isBare()) { 102 | 103 | public void run() { 104 | try { 105 | compile(); 106 | } catch (Exception e) { 107 | Exceptions.printStackTrace(e); 108 | } 109 | } 110 | 111 | public boolean cancel() { 112 | futureHolder[0].cancel(true); 113 | return true; 114 | } 115 | }); 116 | } 117 | }); 118 | } 119 | if (switchMultipleValues || switchOn) { 120 | switchmenu.add(new AbstractAction("Turn off") { 121 | 122 | public void actionPerformed(ActionEvent ae) { 123 | for (CoffeeScriptDataObject dataObject : data) { 124 | CoffeeScriptAutocompileContext.get().disableAutocompile(dataObject.getPrimaryFile()); 125 | } 126 | } 127 | }); 128 | } 129 | menu.add(switchmenu); 130 | menu.addSeparator(); 131 | menu.add(new CompileAction(data, false)); 132 | menu.add(new CompileAction(data, true)); 133 | return menu; 134 | } 135 | 136 | public void actionPerformed(ActionEvent ae) { 137 | } 138 | } 139 | 140 | private static class CompileAction extends AbstractAction { 141 | 142 | Collection data; 143 | boolean bare; 144 | 145 | public CompileAction(Collection data, boolean bare) { 146 | super(bare ? "Compile without the top-level function" : "Compile"); 147 | this.data = data; 148 | this.bare = bare; 149 | } 150 | 151 | public void actionPerformed(ActionEvent ae) { 152 | new ConsoleOutputCompileTask(data, bare).execute(); 153 | } 154 | } 155 | 156 | private static class CancelAction extends AbstractAction { 157 | 158 | private Cancellable cancellable; 159 | 160 | private CancelAction(Cancellable cancellable) { 161 | putValue(Action.SMALL_ICON, ImageUtilities.loadImageIcon("coffeescript/nb/resources/stop.png", false)); 162 | putValue(Action.NAME, "Stop"); 163 | setEnabled(false); 164 | this.cancellable = cancellable; 165 | } 166 | 167 | public void actionPerformed(ActionEvent e) { 168 | cancellable.cancel(); 169 | setEnabled(false); 170 | } 171 | } 172 | 173 | private static class ConsoleOutputCompileTask extends CompilerTask { 174 | 175 | private final CancelAction cancelAction; 176 | private ExecutorTask executorTask; 177 | private InputOutput io; 178 | 179 | public ConsoleOutputCompileTask(Collection data, boolean bare) { 180 | super(data, bare); 181 | this.cancelAction = new CancelAction(this); 182 | this.io = IOProvider.getDefault().getIO(getName(), new Action[]{cancelAction}); 183 | } 184 | 185 | public void execute() { 186 | executorTask = ExecutionEngine.getDefault().execute("CoffeeScriptExecutor", this, io); 187 | } 188 | 189 | @Override 190 | public void run() { 191 | try { 192 | cancelAction.setEnabled(true); 193 | super.compile(); 194 | } catch (Exception e) { 195 | e.printStackTrace(io.getErr()); 196 | } finally { 197 | cancelAction.setEnabled(false); 198 | } 199 | } 200 | 201 | @Override 202 | protected void handleResult(CompilerResult result) { 203 | if (result.getJs() != null) { 204 | io.getOut().append(result.getJs()); 205 | } else { 206 | io.getErr().append(result.getError().getMessage()); 207 | } 208 | } 209 | 210 | @Override 211 | public boolean cancel() { 212 | executorTask.stop(); 213 | cancelAction.setEnabled(false); 214 | return true; 215 | } 216 | } 217 | 218 | private static abstract class CompilerTask implements Cancellable, Runnable { 219 | 220 | private Collection data; 221 | private String taskName; 222 | private boolean bare; 223 | 224 | public CompilerTask(Collection data, boolean bare) { 225 | this.data = data; 226 | this.bare = bare; 227 | taskName = data.size() > 1 ? "Compiling CoffeeScript files" : "Compiling " + data.iterator().next().getPrimaryFile().getNameExt(); 228 | } 229 | 230 | public void compile() throws Exception { 231 | LifecycleManager.getDefault().saveAll(); 232 | for (CoffeeScriptDataObject coffeeFile : data) { 233 | boolean literate; 234 | ProgressHandle handle = ProgressHandleFactory.createHandle("Compiling " + coffeeFile.getPrimaryFile().getNameExt(), this); 235 | try { 236 | handle.start(); 237 | literate = coffeeFile.getPrimaryFile().getExt().equals(CoffeeScriptLanguage.LITERATE_EXTENSION); 238 | CoffeeScriptCompiler.CompilerResult result = CoffeeScriptSettings.getCompiler().compile(coffeeFile.getPrimaryFile().asText(), bare, literate); 239 | if (result == null) { 240 | return; // Canceled 241 | } 242 | if (result.getJs() != null) { 243 | CoffeeScriptUtils.writeJSForCoffeeScriptFile(result.getJs(), coffeeFile.getPrimaryFile()); 244 | } 245 | handleResult(result); 246 | } finally { 247 | handle.finish(); 248 | } 249 | } 250 | } 251 | 252 | protected void handleResult(CoffeeScriptRhinoCompiler.CompilerResult result) { 253 | } 254 | 255 | public String getName() { 256 | return taskName; 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptAutocompileContext.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import java.util.Collections; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import org.openide.filesystems.FileChangeAdapter; 21 | import org.openide.filesystems.FileObject; 22 | import org.openide.filesystems.FileRenameEvent; 23 | 24 | /** 25 | * 26 | * @author Denis Stepanov 27 | */ 28 | public class CoffeeScriptAutocompileContext { 29 | 30 | private Map autocompile = Collections.synchronizedMap(new HashMap()); 31 | private static CoffeeScriptAutocompileContext INSTANCE; 32 | 33 | private CoffeeScriptAutocompileContext() { 34 | } 35 | 36 | public static synchronized CoffeeScriptAutocompileContext get() { 37 | if (INSTANCE == null) { 38 | INSTANCE = new CoffeeScriptAutocompileContext(); 39 | } 40 | return INSTANCE; 41 | } 42 | 43 | public boolean isEnabled(FileObject file) { 44 | Boolean result = autocompile.get(file.getPath()); 45 | return result == null ? false : result; 46 | } 47 | 48 | public void enableAutocompile(final FileObject file) { 49 | final String path = file.getPath(); 50 | autocompile.put(path, Boolean.TRUE); 51 | file.addFileChangeListener(new FileChangeAdapter() { 52 | 53 | @Override 54 | public void fileRenamed(FileRenameEvent fe) { 55 | Boolean result = autocompile.remove(path); 56 | if (result != null) { 57 | autocompile.put(fe.getFile().getPath(), result); 58 | } 59 | } 60 | }); 61 | } 62 | 63 | public void disableAutocompile(FileObject file) { 64 | autocompile.remove(file.getPath()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptAutocompileTask.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import java.util.Collection; 17 | import java.util.Collections; 18 | import org.netbeans.modules.parsing.api.Snapshot; 19 | import org.netbeans.modules.parsing.spi.ParserResultTask; 20 | import org.netbeans.modules.parsing.spi.Scheduler; 21 | import org.netbeans.modules.parsing.spi.SchedulerEvent; 22 | import org.netbeans.modules.parsing.spi.SchedulerTask; 23 | import org.netbeans.modules.parsing.spi.TaskFactory; 24 | import org.openide.filesystems.FileObject; 25 | 26 | /** 27 | * @author Denis Stepanov 28 | */ 29 | public class CoffeeScriptAutocompileTask extends ParserResultTask { 30 | 31 | public void run(final CoffeeScriptParser.ParsingResult result, SchedulerEvent event) { 32 | if ((result != null) && !CoffeeScriptAutocompileContext.get().isEnabled(result.getSnapshot().getSource().getFileObject())) { 33 | return; 34 | } 35 | if ((result != null) && (result.getCompilerResult() != null) && (result.getCompilerResult().getJs() != null)) { 36 | FileObject coffeeFile = result.getSnapshot().getSource().getFileObject(); 37 | String js = result.getCompilerResult().getJs(); 38 | CoffeeScriptUtils.writeJSForCoffeeScriptFile(js, coffeeFile); 39 | } 40 | } 41 | 42 | public int getPriority() { 43 | return 10000; 44 | } 45 | 46 | public Class getSchedulerClass() { 47 | return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER; 48 | } 49 | 50 | public void cancel() { 51 | } 52 | 53 | public static class Factory extends TaskFactory { 54 | 55 | public Collection create(Snapshot snapshot) { 56 | return Collections.singleton(new CoffeeScriptAutocompileTask()); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptCompiler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | /** 17 | * 18 | * @author Denis Stepanov 19 | */ 20 | public interface CoffeeScriptCompiler { 21 | 22 | public CompilerResult compile(String code, boolean bare, boolean literate); 23 | 24 | public static class CompilerResult { 25 | 26 | private String js; 27 | private Error error; 28 | 29 | public CompilerResult(String js) { 30 | this.js = js; 31 | } 32 | 33 | public CompilerResult(Error error) { 34 | this.error = error; 35 | } 36 | 37 | public String getJs() { 38 | return js; 39 | } 40 | 41 | public Error getError() { 42 | return error; 43 | } 44 | } 45 | 46 | public static class Error { 47 | 48 | private final int line, column; 49 | private final String errorName, message; 50 | 51 | public Error(int line, String errorName, String message) { 52 | this(line, 0, errorName, message); 53 | } 54 | 55 | public Error(int line, int column, String errorName, String message) { 56 | this.line = line; 57 | this.column = column; 58 | this.errorName = errorName; 59 | this.message = message; 60 | } 61 | 62 | public int getLine() { 63 | return line; 64 | } 65 | 66 | public int getColumn() { 67 | return column; 68 | } 69 | 70 | public String getErrorName() { 71 | return errorName; 72 | } 73 | 74 | public String getMessage() { 75 | return message; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptDataObject.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import java.io.IOException; 18 | import org.openide.filesystems.FileObject; 19 | import org.openide.loaders.DataNode; 20 | import org.openide.loaders.DataObjectExistsException; 21 | import org.openide.loaders.MultiDataObject; 22 | import org.openide.loaders.MultiFileLoader; 23 | import org.openide.nodes.CookieSet; 24 | import org.openide.nodes.Node; 25 | import org.openide.nodes.Children; 26 | import org.openide.util.Lookup; 27 | import org.openide.text.DataEditorSupport; 28 | import org.openide.util.lookup.Lookups; 29 | import org.openide.util.lookup.ProxyLookup; 30 | 31 | /** 32 | * 33 | * @author Denis Stepanov 34 | */ 35 | public class CoffeeScriptDataObject extends MultiDataObject { 36 | 37 | public CoffeeScriptDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException { 38 | super(pf, loader); 39 | CookieSet cookies = getCookieSet(); 40 | cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies)); 41 | } 42 | 43 | @Override 44 | protected Node createNodeDelegate() { 45 | return new DataNode(this, Children.LEAF, getLookup()); 46 | } 47 | 48 | @Override 49 | public Lookup getLookup() { 50 | return new ProxyLookup(new Lookup[] { 51 | getCookieSet().getLookup(), 52 | Lookups.fixed(new CoffeeScriptSourceEncodingQuery()) 53 | }); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptEmbeddingProvidersFactory.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Collection; 18 | import java.util.Collections; 19 | import java.util.List; 20 | import org.netbeans.api.html.lexer.HTMLTokenId; 21 | import org.netbeans.api.lexer.Token; 22 | import org.netbeans.api.lexer.TokenHierarchy; 23 | import org.netbeans.api.lexer.TokenId; 24 | import org.netbeans.api.lexer.TokenSequence; 25 | import org.netbeans.modules.parsing.api.Embedding; 26 | import org.netbeans.modules.parsing.api.Snapshot; 27 | import org.netbeans.modules.parsing.spi.EmbeddingProvider; 28 | import org.netbeans.modules.parsing.spi.SchedulerTask; 29 | import org.netbeans.modules.parsing.spi.TaskFactory; 30 | 31 | /** 32 | * @author Denis Stepanov 33 | */ 34 | public final class CoffeeScriptEmbeddingProvidersFactory extends TaskFactory { 35 | 36 | public Collection create(Snapshot snapshot) { 37 | List ems = new ArrayList(); 38 | if (snapshot.getSource().getMimeType().equals("text/html")) { 39 | ems.add(new HTMLEmbeddingProvider()); 40 | } 41 | if (snapshot.getSource().getMimeType().equals("text/x-php5")) { 42 | ems.add(new PHPEmbeddingProvider()); 43 | } 44 | return ems; 45 | } 46 | 47 | public class HTMLEmbeddingProvider extends EmbeddingProvider { 48 | 49 | @Override 50 | public List getEmbeddings(Snapshot snapshot) { 51 | List embeddings = new ArrayList(); 52 | TokenHierarchy th = snapshot.getTokenHierarchy(); 53 | TokenSequence tokenSequence = th.tokenSequence(HTMLTokenId.language()); 54 | if (tokenSequence != null) { 55 | @SuppressWarnings("unchecked") 56 | TokenSequence htmlTokenSequence = (TokenSequence) tokenSequence; 57 | extractCoffeeScriptFromHTML(snapshot, htmlTokenSequence, embeddings); 58 | } 59 | if (embeddings.isEmpty()) { 60 | return Collections.emptyList(); 61 | } 62 | return Collections.singletonList(Embedding.create(embeddings)); 63 | } 64 | 65 | @Override 66 | public int getPriority() { 67 | return Integer.MAX_VALUE; 68 | } 69 | 70 | @Override 71 | public void cancel() { 72 | } 73 | } 74 | 75 | public class PHPEmbeddingProvider extends EmbeddingProvider { 76 | 77 | @Override 78 | public List getEmbeddings(Snapshot snapshot) { 79 | TokenHierarchy th = snapshot.getTokenHierarchy(); 80 | if (th == null) { 81 | return Collections.emptyList(); 82 | } 83 | 84 | TokenSequence tokenSequence = th.tokenSequence(); 85 | List embeddings = new ArrayList(); 86 | while (tokenSequence.moveNext()) { 87 | Token token = tokenSequence.token(); 88 | if (token.id().name().equals("T_INLINE_HTML")) { 89 | TokenSequence ts = tokenSequence.embeddedJoined(HTMLTokenId.language()); 90 | if (ts == null) { 91 | continue; 92 | } 93 | extractCoffeeScriptFromHTML(snapshot, ts, embeddings); 94 | break; 95 | } 96 | } 97 | return embeddings; 98 | } 99 | 100 | @Override 101 | public int getPriority() { 102 | return Integer.MAX_VALUE; 103 | } 104 | 105 | @Override 106 | public void cancel() { 107 | } 108 | } 109 | 110 | private void extractCoffeeScriptFromHTML(Snapshot snapshot, TokenSequence ts, List embeddings) { 111 | boolean inCoffeeScript = false; 112 | ts.moveStart(); 113 | while (ts.moveNext()) { 114 | Token htmlToken = ts.token(); 115 | HTMLTokenId htmlId = htmlToken.id(); 116 | if (htmlId == HTMLTokenId.TAG_OPEN) { 117 | String text = htmlToken.text().toString(); 118 | if ("script".equals(text)) { 119 | TokenSequence ets = ts.subSequence(ts.offset()); 120 | ets.moveStart(); 121 | boolean foundSrc = false; 122 | boolean foundType = false; 123 | String type = null; 124 | String src = null; 125 | while (ets.moveNext()) { 126 | Token t = ets.token(); 127 | HTMLTokenId id = t.id(); 128 | if (id == HTMLTokenId.TAG_CLOSE_SYMBOL) { 129 | break; 130 | } else if (foundSrc || foundType) { 131 | if (id == HTMLTokenId.ARGUMENT) { 132 | break; 133 | } else if (id == HTMLTokenId.VALUE) { 134 | if (foundSrc) { 135 | src = t.toString(); 136 | } else { 137 | assert foundType; 138 | type = t.toString(); 139 | } 140 | foundSrc = false; 141 | foundType = false; 142 | } 143 | } else if (id == HTMLTokenId.ARGUMENT) { 144 | String val = t.toString(); 145 | if ("src".equals(val)) { 146 | foundSrc = true; 147 | } else if ("type".equals(val)) { 148 | foundType = true; 149 | } 150 | } 151 | } 152 | if ((src == null) && (type != null) && type.contains(CoffeeScriptLanguage.MIME_TYPE)) { 153 | inCoffeeScript = true; 154 | } 155 | } 156 | } else if (inCoffeeScript && htmlId == HTMLTokenId.TEXT) { 157 | embeddings.add(snapshot.create(ts.offset(), htmlToken.length(), CoffeeScriptLanguage.MIME_TYPE)); 158 | } else if (htmlId == HTMLTokenId.TAG_CLOSE && "script".equals(htmlToken.toString())) { 159 | inCoffeeScript = false; 160 | } else { 161 | } 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptFormatter.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import java.util.Collection; 17 | import java.util.Deque; 18 | import java.util.LinkedList; 19 | import java.util.TreeSet; 20 | import javax.swing.text.BadLocationException; 21 | import org.netbeans.api.lexer.Token; 22 | import org.netbeans.api.lexer.TokenHierarchy; 23 | import org.netbeans.api.lexer.TokenSequence; 24 | import org.netbeans.modules.csl.api.Formatter; 25 | import org.netbeans.modules.csl.spi.ParserResult; 26 | import org.netbeans.modules.editor.indent.api.IndentUtils; 27 | import org.netbeans.modules.editor.indent.spi.Context; 28 | import org.openide.util.Exceptions; 29 | 30 | /** 31 | * @author Denis Stepanov 32 | */ 33 | public class CoffeeScriptFormatter implements Formatter { 34 | 35 | public void reformat(Context context, ParserResult compilationInfo) { 36 | TokenHierarchy th = createTokenHierarchy(context); 37 | TokenSequence ts = th.tokenSequence(CoffeeScriptLanguage.getLanguage()); 38 | Collection indentChanges = new TreeSet(); 39 | Deque indents = new LinkedList(); 40 | try { 41 | int currentLineStartOffset = 0; 42 | while (ts.moveNext()) { 43 | Token token = ts.offsetToken(); 44 | if (token.id() == CoffeeScriptTokenId.WHITESPACE || token.id() == CoffeeScriptTokenId.EOL) { 45 | continue; 46 | } 47 | int tokenOffset = token.offset(th); 48 | if (tokenOffset >= 0) { 49 | int tokenLineStartOffset = context.lineStartOffset(tokenOffset); 50 | if (currentLineStartOffset != tokenLineStartOffset) { 51 | currentLineStartOffset = tokenLineStartOffset; 52 | int tokenLineIndent = context.lineIndent(tokenLineStartOffset); 53 | while (!indents.isEmpty() && (indents.peek().getIndent() >= tokenLineIndent)) { 54 | indents.pop(); 55 | } 56 | int lineIndents = indents.isEmpty() ? 0 : indents.peek().getIndents(); 57 | int lineIndent = indents.isEmpty() ? 0 : indents.peek().getIndent(); 58 | if (lineIndent < tokenLineIndent) { 59 | lineIndents++; 60 | } else if (lineIndent > tokenLineIndent) { 61 | lineIndents--; 62 | } 63 | indents.push(new Indent(tokenLineIndent, lineIndents)); 64 | indentChanges.add(new IndentChange(tokenLineStartOffset, IndentUtils.indentLevelSize(context.document()) * (lineIndents < 0 ? 0 : lineIndents))); 65 | } 66 | } 67 | } 68 | int offsetChange = 0; 69 | for (IndentChange indentChange : indentChanges) { 70 | int indentOffsetChange = indentChange.getIndent() - context.lineIndent(offsetChange + indentChange.getOffset()); 71 | if (indentOffsetChange != 0) { 72 | context.modifyIndent(offsetChange + indentChange.getOffset(), indentChange.getIndent()); 73 | offsetChange += indentOffsetChange; 74 | } 75 | } 76 | } catch (BadLocationException ble) { 77 | Exceptions.printStackTrace(ble); 78 | } 79 | } 80 | 81 | @SuppressWarnings({"unchecked", "rawtypes"}) 82 | protected TokenHierarchy createTokenHierarchy(Context context) { 83 | return (TokenHierarchy) TokenHierarchy.get(context.document()); 84 | } 85 | 86 | public void reindent(Context context) { 87 | try { 88 | int currentLineStartOffset = context.lineStartOffset(context.startOffset()); 89 | if (currentLineStartOffset > 0) { 90 | int prevLineIndent = context.lineIndent(context.lineStartOffset(currentLineStartOffset - 1)); 91 | int currentLineIndent = context.lineIndent(currentLineStartOffset); 92 | if (currentLineIndent != prevLineIndent) { 93 | context.modifyIndent(currentLineStartOffset, prevLineIndent); 94 | } 95 | } 96 | } catch (BadLocationException ble) { 97 | Exceptions.printStackTrace(ble); 98 | } 99 | } 100 | 101 | public boolean needsParserResult() { 102 | return false; 103 | } 104 | 105 | public int indentSize() { 106 | return -1; 107 | } 108 | 109 | public int hangingIndentSize() { 110 | return -1; 111 | } 112 | 113 | private static class Indent { 114 | 115 | private final int indent, indents; 116 | 117 | public Indent(int indent, int indents) { 118 | this.indent = indent; 119 | this.indents = indents; 120 | } 121 | 122 | public int getIndent() { 123 | return indent; 124 | } 125 | 126 | public int getIndents() { 127 | return indents; 128 | } 129 | } 130 | 131 | private static class IndentChange implements Comparable { 132 | 133 | private final int offset; 134 | private int indent; 135 | 136 | public IndentChange(int offset, int indent) { 137 | this.offset = offset; 138 | this.indent = indent; 139 | } 140 | 141 | public int getIndent() { 142 | return indent < 0 ? 0 : indent; 143 | } 144 | 145 | public void setIndent(int indent) { 146 | this.indent = indent; 147 | } 148 | 149 | public int getOffset() { 150 | return offset; 151 | } 152 | 153 | public int compareTo(IndentChange t) { 154 | return Integer.valueOf(offset).compareTo(t.offset); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptHTMLParserTask.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import java.util.Collection; 17 | import java.util.Collections; 18 | import javax.swing.text.Document; 19 | import org.netbeans.api.html.lexer.HTMLTokenId; 20 | import org.netbeans.api.lexer.Token; 21 | import org.netbeans.api.lexer.TokenHierarchy; 22 | import org.netbeans.api.lexer.TokenSequence; 23 | import org.netbeans.modules.parsing.api.Snapshot; 24 | import org.netbeans.modules.parsing.spi.Parser.Result; 25 | import org.netbeans.modules.parsing.spi.ParserResultTask; 26 | import org.netbeans.modules.parsing.spi.Scheduler; 27 | import org.netbeans.modules.parsing.spi.SchedulerEvent; 28 | import org.netbeans.modules.parsing.spi.SchedulerTask; 29 | import org.netbeans.modules.parsing.spi.TaskFactory; 30 | 31 | /** 32 | * 33 | * @author Denis Stepanov 34 | */ 35 | public class CoffeeScriptHTMLParserTask extends ParserResultTask { 36 | 37 | @Override 38 | public void run(Result result, SchedulerEvent event) { 39 | Snapshot snapshot = result.getSnapshot(); 40 | TokenHierarchy th = TokenHierarchy.get(snapshot.getSource().getDocument(true)); 41 | if (snapshot.getMimeType().equals("text/html")) { 42 | TokenSequence ts = th.tokenSequence(HTMLTokenId.language()); 43 | if (ts == null) { 44 | TokenSequence ets = th.tokenSequence(); 45 | ets.moveStart(); 46 | ets.moveNext(); 47 | ts = ets.embeddedJoined(HTMLTokenId.language()); 48 | } 49 | if (ts != null) { 50 | extractCoffeeScriptFromHTML(ts); 51 | } 52 | } 53 | } 54 | 55 | private void extractCoffeeScriptFromHTML(TokenSequence ts) { 56 | boolean inCoffeeScript = false; 57 | ts.moveStart(); 58 | while (ts.moveNext()) { 59 | Token htmlToken = ts.token(); 60 | HTMLTokenId htmlId = htmlToken.id(); 61 | if (htmlId == HTMLTokenId.TAG_OPEN) { 62 | String text = htmlToken.text().toString(); 63 | if ("script".equals(text)) { 64 | TokenSequence ets = ts.subSequence(ts.offset()); 65 | ets.moveStart(); 66 | boolean foundSrc = false; 67 | boolean foundType = false; 68 | String type = null; 69 | String src = null; 70 | while (ets.moveNext()) { 71 | Token t = ets.token(); 72 | HTMLTokenId id = t.id(); 73 | if (id == HTMLTokenId.TAG_CLOSE_SYMBOL) { 74 | break; 75 | } else if (foundSrc || foundType) { 76 | if (id == HTMLTokenId.ARGUMENT) { 77 | break; 78 | } else if (id == HTMLTokenId.VALUE) { 79 | if (foundSrc) { 80 | src = t.toString(); 81 | } else { 82 | assert foundType; 83 | type = t.toString(); 84 | } 85 | foundSrc = false; 86 | foundType = false; 87 | } 88 | } else if (id == HTMLTokenId.ARGUMENT) { 89 | String val = t.toString(); 90 | if ("src".equals(val)) { 91 | foundSrc = true; 92 | } else if ("type".equals(val)) { 93 | foundType = true; 94 | } 95 | } 96 | } 97 | if ((src == null) && (type != null) && type.contains(CoffeeScriptLanguage.MIME_TYPE)) { 98 | inCoffeeScript = true; 99 | } 100 | } 101 | } else if (inCoffeeScript && htmlId == HTMLTokenId.TEXT) { 102 | ts.createEmbedding(CoffeeScriptLanguage.getLanguage(), 0, 0); 103 | inCoffeeScript = false; 104 | } 105 | } 106 | } 107 | 108 | @Override 109 | public int getPriority() { 110 | return 100; 111 | } 112 | 113 | @Override 114 | public Class getSchedulerClass() { 115 | return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER; 116 | } 117 | 118 | @Override 119 | public void cancel() { 120 | } 121 | 122 | public static class Factory extends TaskFactory { 123 | 124 | public Collection create(Snapshot snapshot) { 125 | return Collections.singleton(new CoffeeScriptHTMLParserTask()); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptLanguage.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import java.util.Collection; 18 | import java.util.EnumSet; 19 | import java.util.HashMap; 20 | import java.util.HashSet; 21 | import java.util.Map; 22 | import org.netbeans.api.lexer.InputAttributes; 23 | import org.netbeans.api.lexer.Language; 24 | import org.netbeans.api.lexer.LanguagePath; 25 | import org.netbeans.api.lexer.Token; 26 | import org.netbeans.spi.lexer.EmbeddingPresence; 27 | import org.netbeans.spi.lexer.LanguageEmbedding; 28 | import org.netbeans.spi.lexer.LanguageHierarchy; 29 | import org.netbeans.spi.lexer.Lexer; 30 | import org.netbeans.spi.lexer.LexerRestartInfo; 31 | 32 | /** 33 | * 34 | * @author Denis Stepanov 35 | */ 36 | public class CoffeeScriptLanguage extends LanguageHierarchy { 37 | 38 | public static final String LITERATE_EXTENSION = "litcoffee"; 39 | public static final String MIME_TYPE = "text/coffeescript"; //NOI18N 40 | private static final Language LANGUAGE = new CoffeeScriptLanguage().language(); 41 | 42 | public static final Language getLanguage() { 43 | return LANGUAGE; 44 | } 45 | 46 | private CoffeeScriptLanguage() { 47 | } 48 | 49 | protected Collection createTokenIds() { 50 | return EnumSet.allOf(CoffeeScriptTokenId.class); 51 | } 52 | 53 | @Override 54 | protected Lexer createLexer(LexerRestartInfo lri) { 55 | return new CoffeeScriptLexer(lri); 56 | } 57 | 58 | @Override 59 | protected Map> createTokenCategories() { 60 | Map> map = new HashMap>(); 61 | for (CoffeeScriptTokenId token : EnumSet.allOf(CoffeeScriptTokenId.class)) { 62 | Collection tokens = map.get(token.primaryCategory()); 63 | if (tokens == null) { 64 | tokens = new HashSet(); 65 | map.put(token.primaryCategory(), tokens); 66 | } 67 | tokens.add(token); 68 | } 69 | return map; 70 | } 71 | 72 | @Override 73 | protected EmbeddingPresence embeddingPresence(CoffeeScriptTokenId id) { 74 | if (id == CoffeeScriptTokenId.STRING) { 75 | return EmbeddingPresence.ALWAYS_QUERY; 76 | } 77 | if (id == CoffeeScriptTokenId.HEREGEX) { 78 | return EmbeddingPresence.ALWAYS_QUERY; 79 | } 80 | if (id == CoffeeScriptTokenId.JSTOKEN) { 81 | return EmbeddingPresence.ALWAYS_QUERY; 82 | } 83 | return null; 84 | } 85 | 86 | @Override 87 | protected LanguageEmbedding embedding(Token token, LanguagePath languagePath, InputAttributes inputAttributes) { 88 | if (token.id() == CoffeeScriptTokenId.STRING) { 89 | return LanguageEmbedding.create(CoffeeScriptStringLanguage.getLanguage(), 0, 0); 90 | } 91 | if (token.id() == CoffeeScriptTokenId.HEREGEX) { 92 | return LanguageEmbedding.create(CoffeeScriptRegexpLanguage.getLanguage(), 0, 0); 93 | } 94 | if (token.id() == CoffeeScriptTokenId.JSTOKEN) { 95 | Language javasSriptLanguage = Language.find("text/javascript"); 96 | if (javasSriptLanguage != null && token.length() > 2) { 97 | return LanguageEmbedding.create(javasSriptLanguage, 1, 1); 98 | } 99 | } 100 | return null; 101 | } 102 | 103 | @Override 104 | protected String mimeType() { 105 | return MIME_TYPE; 106 | } 107 | } -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptLexerBase.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import java.util.ArrayDeque; 17 | import java.util.Deque; 18 | import org.netbeans.api.lexer.Token; 19 | import org.netbeans.api.lexer.TokenId; 20 | import org.netbeans.spi.lexer.Lexer; 21 | import org.netbeans.spi.lexer.LexerInput; 22 | import org.netbeans.spi.lexer.TokenFactory; 23 | 24 | /** 25 | * 26 | * @author Denis Stepanov 27 | */ 28 | public abstract class CoffeeScriptLexerBase implements Lexer { 29 | 30 | protected LexerInput input; 31 | protected TokenFactory tokenFactory; 32 | 33 | public CoffeeScriptLexerBase(LexerInput input, TokenFactory tokenFactory) { 34 | this.input = input; 35 | this.tokenFactory = tokenFactory; 36 | } 37 | 38 | protected boolean balancedString(String last) { 39 | while (true) { 40 | if (inputMatch(last)) { 41 | return true; 42 | } 43 | int c = input.read(); 44 | if (c == '\\') { 45 | c = input.read(); 46 | } else if (c == LexerInput.EOF) { 47 | return false; 48 | } 49 | } 50 | } 51 | 52 | protected boolean balancedInterpolatedString(String last) { 53 | Deque stack = new ArrayDeque(); 54 | while (true) { 55 | if (stack.isEmpty() && inputMatch(last)) { 56 | return true; 57 | } 58 | boolean canBeInterpolated = stack.isEmpty() || !stack.isEmpty() && stack.element() == '"'; 59 | boolean inInterpolation = stack.isEmpty() && last.endsWith("}") || !stack.isEmpty() && stack.element() == '}'; 60 | int c = input.read(); 61 | if (!stack.isEmpty() && stack.element() == c) { 62 | stack.poll(); 63 | } else if (canBeInterpolated && c == '#' && inputMatch("{")) { 64 | stack.push('}'); 65 | } else if (inInterpolation && (c == '"' || c == '\'' || c == '{')) { 66 | stack.push(c == '{' ? '}' : (char) c); 67 | } else if (c == '\\') { 68 | c = input.read(); 69 | } else if (c == LexerInput.EOF) { 70 | return false; 71 | } 72 | } 73 | } 74 | 75 | protected boolean balancedRegex() { 76 | Deque stack = new ArrayDeque(); 77 | while (true) { 78 | int c = input.read(); 79 | if (stack.isEmpty() && c == '/') { 80 | return true; 81 | } 82 | if (!stack.isEmpty() && stack.element() == c) { 83 | stack.poll(); 84 | } else if (c == '[') { 85 | stack.push(']'); 86 | } else if (stack.isEmpty() && c == '\\') { 87 | // We don't need to escape things in square braces 88 | c = input.read(); 89 | } else if (c == '\n') { 90 | input.backup(1); 91 | return false; 92 | } else if (c == LexerInput.EOF) { 93 | return false; 94 | } 95 | } 96 | } 97 | 98 | protected boolean balancedJSToken() { 99 | Deque stack = new ArrayDeque(); 100 | while (true) { 101 | int c = input.read(); 102 | if (stack.isEmpty() && c == '`') { 103 | return true; 104 | } 105 | if (!stack.isEmpty() && stack.element() == c) { 106 | stack.poll(); 107 | } else if (c == '"' || c == '\'') { 108 | stack.push((char) c); 109 | } else if (c == '\\') { 110 | c = input.read(); 111 | } else if (c == LexerInput.EOF) { 112 | return false; 113 | } 114 | } 115 | } 116 | 117 | protected Token token(T token) { 118 | return tokenFactory.createToken(token); 119 | } 120 | 121 | protected boolean inputNotMatch(String string) { 122 | int readChars = 0; 123 | for (char c : string.toCharArray()) { 124 | readChars++; 125 | if (input.read() != c) { 126 | input.backup(readChars); 127 | return true; 128 | } 129 | } 130 | input.backup(readChars); 131 | return false; 132 | } 133 | 134 | protected boolean inputMatch(String string) { 135 | int readChars = 0; 136 | for (char c : string.toCharArray()) { 137 | readChars++; 138 | if (input.read() != c) { 139 | input.backup(readChars); 140 | return false; 141 | } 142 | } 143 | return true; 144 | } 145 | 146 | protected boolean inputMatch(char c) { 147 | if (input.read() != c) { 148 | input.backup(1); 149 | return false; 150 | } 151 | return true; 152 | } 153 | 154 | protected int peek() { 155 | int c = input.read(); 156 | input.backup(1); 157 | return c; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptMimeResolver.java: -------------------------------------------------------------------------------- 1 | package coffeescript.nb; 2 | 3 | import org.openide.filesystems.FileObject; 4 | import org.openide.filesystems.MIMEResolver; 5 | import org.openide.util.lookup.ServiceProvider; 6 | 7 | /** 8 | * 9 | * @author Denis Stepanov 10 | */ 11 | @ServiceProvider(service = MIMEResolver.class) 12 | public class CoffeeScriptMimeResolver extends MIMEResolver { 13 | 14 | public CoffeeScriptMimeResolver() { 15 | super(CoffeeScriptLanguage.MIME_TYPE); 16 | } 17 | 18 | @Override 19 | public String findMIMEType(FileObject fo) { 20 | if (fo.getExt().equals("coffee") || fo.getNameExt().equals("Cakefile") || fo.getExt().equals(CoffeeScriptLanguage.LITERATE_EXTENSION)) { 21 | return CoffeeScriptLanguage.MIME_TYPE; 22 | } 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptNodeJSCompiler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import coffeescript.nb.options.CoffeeScriptSettings; 17 | import java.io.BufferedReader; 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.InputStreamReader; 22 | import java.io.OutputStream; 23 | import java.io.Reader; 24 | import java.io.StringWriter; 25 | import java.io.Writer; 26 | import java.util.Map; 27 | import java.util.logging.Level; 28 | import java.util.logging.Logger; 29 | import java.util.regex.Matcher; 30 | import java.util.regex.Pattern; 31 | import org.openide.util.Exceptions; 32 | import org.openide.util.Utilities; 33 | 34 | /** 35 | * 36 | * @author Denis Stepanov 37 | */ 38 | public class CoffeeScriptNodeJSCompiler implements CoffeeScriptCompiler { 39 | 40 | private static Pattern ERROR_PATTERN1 = Pattern.compile("(.*) on line (\\d*)(.*)"); 41 | private static Pattern ERROR_PATTERN2 = Pattern.compile(".*:(\\d*):(\\d*):.*: (.*)"); 42 | 43 | private static CoffeeScriptNodeJSCompiler INSTANCE; 44 | private static final Logger logger = Logger.getLogger(CoffeeScriptNodeJSCompiler.class.getName()); 45 | 46 | private CoffeeScriptNodeJSCompiler() { 47 | } 48 | 49 | public static synchronized CoffeeScriptNodeJSCompiler get() { 50 | if (INSTANCE == null) { 51 | return (INSTANCE = new CoffeeScriptNodeJSCompiler()); 52 | } 53 | return INSTANCE; 54 | } 55 | 56 | private boolean isValidFile(String file) { 57 | File execFile = new File(file); 58 | if (!execFile.exists() || !execFile.isFile() || !execFile.canRead()) { 59 | if (Utilities.isWindows() && file.toLowerCase().contains("%appdata%")) { 60 | return true; // Allow to check file with appdata 61 | } 62 | return false; 63 | } 64 | return true; 65 | } 66 | 67 | public boolean isValid(String exec) { 68 | if (!isValidFile(exec)) { 69 | return false; 70 | } 71 | try { 72 | ExecResult result = exec( 73 | Utilities.isWindows() 74 | ? createValidateProcessBuilderWindows(exec) 75 | : createValidateProcessBuilderNix(exec)); 76 | String err = result.err; 77 | String out = result.out; 78 | if (!err.isEmpty()) { 79 | logger.log(Level.INFO, "Invalid exec\n{0}", err); 80 | return false; 81 | } 82 | if (!out.startsWith("CoffeeScript")) { 83 | logger.log(Level.INFO, "Not a coffee script ''coffee'', invalid output:\n{0}", out); 84 | } 85 | return true; 86 | } catch (Exception e) { 87 | logger.log(Level.INFO, "Invalid exec", e); 88 | } 89 | return false; 90 | } 91 | 92 | private ExecResult exec(ProcessBuilder pb) throws Exception { 93 | return exec(null, pb); 94 | } 95 | 96 | private ExecResult exec(String output, ProcessBuilder pb) throws Exception { 97 | return Utilities.isWindows() ? execWindows(output, pb) : execNix(output, pb); 98 | } 99 | 100 | private ExecResult execNix(final String output, ProcessBuilder pb) throws Exception { 101 | Map environment = pb.environment(); 102 | // Prevent "env: node: No such file or directory" 103 | environment.put("PATH", environment.get("PATH") + ":/usr/local/bin"); 104 | Process p = pb.start(); 105 | if (output != null) { 106 | OutputStream os = p.getOutputStream(); 107 | os.write(output.getBytes("UTF-8")); 108 | os.close(); 109 | } 110 | 111 | String out = getInputStreamAsString(p.getInputStream()); 112 | String err = getInputStreamAsString(p.getErrorStream()); 113 | 114 | p.destroy(); 115 | 116 | ExecResult result = new ExecResult(); 117 | result.err = err; 118 | result.out = out; 119 | return result; 120 | } 121 | 122 | private ExecResult execWindows(final String output, ProcessBuilder pb) throws Exception { 123 | 124 | final String[] errHolder = new String[1]; 125 | final String[] outHolder = new String[1]; 126 | 127 | final Process p = pb.start(); 128 | 129 | final InputStream errStream = p.getErrorStream(); 130 | final InputStream inputStream = p.getInputStream(); 131 | 132 | Thread outThread = new Thread(new Runnable() { 133 | 134 | public void run() { 135 | if (output != null) { 136 | try { 137 | OutputStream os = p.getOutputStream(); 138 | os.write(output.getBytes("UTF-8")); 139 | os.close(); 140 | } catch (Exception e) { 141 | Exceptions.printStackTrace(e); 142 | } 143 | } 144 | } 145 | }); 146 | Thread errThread = new Thread(new Runnable() { 147 | 148 | public void run() { 149 | errHolder[0] = getInputStreamAsString(errStream); 150 | 151 | } 152 | }); 153 | Thread inputThread = new Thread(new Runnable() { 154 | 155 | public void run() { 156 | outHolder[0] = getInputStreamAsString(inputStream); 157 | } 158 | }); 159 | 160 | errThread.start(); 161 | inputThread.start(); 162 | outThread.start(); 163 | p.waitFor(); 164 | errThread.join(); 165 | inputThread.join(); 166 | outThread.join(); 167 | p.destroy(); 168 | 169 | ExecResult result = new ExecResult(); 170 | result.err = errHolder[0]; 171 | result.out = outHolder[0]; 172 | return result; 173 | } 174 | 175 | public CompilerResult compile(final String code, boolean bare, boolean literate) { 176 | try { 177 | String exec = CoffeeScriptSettings.get().getCompilerExec(); 178 | ExecResult result = exec(code, 179 | Utilities.isWindows() 180 | ? createCompileProcessBuilderWindows(exec, bare, literate) 181 | : createCompileProcessBuilderNix(exec, bare, literate)); 182 | String err = result.err; 183 | String out = result.out; 184 | if (!err.isEmpty()) { 185 | int i = err.indexOf('\n'); 186 | if (i != -1) { 187 | err = err.substring(0, i); 188 | } 189 | Matcher matcher = ERROR_PATTERN1.matcher(err); 190 | if (matcher.matches()) { 191 | return new CompilerResult(new Error(Integer.valueOf(matcher.group(2)), matcher.group(1) + matcher.group(3), err)); 192 | } 193 | matcher = ERROR_PATTERN2.matcher(err); 194 | if (matcher.matches()) { 195 | return new CompilerResult(new Error(Integer.valueOf(matcher.group(1)), Integer.valueOf(matcher.group(2)), matcher.group(3), err)); 196 | } 197 | return new CompilerResult(new Error(-1, "", err)); 198 | } 199 | return new CompilerResult(out); 200 | } catch (Exception e) { 201 | Exceptions.printStackTrace(e); 202 | } 203 | return null; 204 | } 205 | 206 | protected String getInputStreamAsString(InputStream is) { 207 | char[] buffer = new char[1024]; 208 | Writer writer = new StringWriter(); 209 | try { 210 | Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); 211 | int n; 212 | while ((n = reader.read(buffer)) != -1) { 213 | writer.write(buffer, 0, n); 214 | } 215 | } catch (IOException e) { 216 | Exceptions.printStackTrace(e); 217 | } finally { 218 | try { 219 | is.close(); 220 | } catch (IOException e) { 221 | } 222 | } 223 | return writer.toString(); 224 | } 225 | 226 | protected ProcessBuilder createValidateProcessBuilderWindows(String exec) { 227 | return new ProcessBuilder("cmd", "/c", "\"\"" + exec + "\"" + " -v \""); 228 | } 229 | 230 | protected ProcessBuilder createValidateProcessBuilderNix(String exec) { 231 | return new ProcessBuilder(exec, "-v"); 232 | } 233 | 234 | protected ProcessBuilder createCompileProcessBuilderWindows(String exec, boolean bare, boolean literate) { 235 | return new ProcessBuilder("cmd", "/c", "\"\"" + exec + "\" -sc" + (bare ? "b" : "") + (literate ? "l" : "") + " \""); 236 | } 237 | 238 | protected ProcessBuilder createCompileProcessBuilderNix(String exec, boolean bare, boolean literate) { 239 | return new ProcessBuilder(exec, "-sc" + (bare ? "b" : "") + (literate ? "l" : "")); 240 | } 241 | 242 | private static class ExecResult { 243 | 244 | String out, err; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptParser.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import coffeescript.nb.options.CoffeeScriptSettings; 17 | import java.util.Collection; 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.concurrent.Callable; 21 | import java.util.concurrent.Future; 22 | import java.util.concurrent.TimeUnit; 23 | import javax.swing.event.ChangeListener; 24 | import javax.swing.text.StyledDocument; 25 | import org.netbeans.modules.csl.api.Error; 26 | import org.netbeans.modules.csl.api.Severity; 27 | import org.netbeans.modules.csl.spi.DefaultError; 28 | import org.netbeans.modules.csl.spi.ParserResult; 29 | import org.netbeans.modules.parsing.api.Snapshot; 30 | import org.netbeans.modules.parsing.api.Task; 31 | import org.netbeans.modules.parsing.spi.ParseException; 32 | import org.netbeans.modules.parsing.spi.Parser; 33 | import org.netbeans.modules.parsing.spi.ParserFactory; 34 | import org.netbeans.modules.parsing.spi.SourceModificationEvent; 35 | import org.openide.text.NbDocument; 36 | import org.openide.util.RequestProcessor; 37 | 38 | /** 39 | * 40 | * @author Denis Stepanov 41 | */ 42 | public class CoffeeScriptParser extends Parser { 43 | 44 | private static final RequestProcessor PARSER_TASK = new RequestProcessor(CoffeeScriptParser.class.getName(), 1); 45 | private Future future; 46 | 47 | public void parse(final Snapshot snapshot, Task task, SourceModificationEvent event) throws ParseException { 48 | future = PARSER_TASK.submit(new Callable() { 49 | 50 | public ParsingResult call() throws Exception { 51 | boolean literate; 52 | CharSequence text = snapshot.getText(); 53 | literate = snapshot.getSource().getFileObject().getExt().equals(CoffeeScriptLanguage.LITERATE_EXTENSION); 54 | CoffeeScriptCompiler.CompilerResult compilerResult = CoffeeScriptSettings.getCompiler().compile(text.toString(), CoffeeScriptSettings.get().isBare(), literate); 55 | return new ParsingResult(snapshot, compilerResult); 56 | } 57 | }); 58 | } 59 | 60 | public Result getResult(Task task) throws ParseException { 61 | try { 62 | return future.get(60, TimeUnit.SECONDS); 63 | } catch (Exception ex) { 64 | } // Ignore 65 | future.cancel(true); 66 | return null; 67 | } 68 | 69 | public void addChangeListener(ChangeListener changeListener) { 70 | } 71 | 72 | public void removeChangeListener(ChangeListener changeListener) { 73 | } 74 | 75 | public static class Factory extends ParserFactory { 76 | 77 | public Parser createParser(Collection snapshots) { 78 | return new CoffeeScriptParser(); 79 | } 80 | } 81 | 82 | public static class ParsingResult extends ParserResult { 83 | 84 | private CoffeeScriptRhinoCompiler.CompilerResult compilerResult; 85 | 86 | public ParsingResult(Snapshot snapshot, CoffeeScriptRhinoCompiler.CompilerResult compilerResult) { 87 | super(snapshot); 88 | this.compilerResult = compilerResult; 89 | } 90 | 91 | public CoffeeScriptCompiler.CompilerResult getCompilerResult() { 92 | return compilerResult; 93 | } 94 | 95 | @Override 96 | public List getDiagnostics() { 97 | if ((compilerResult != null) && (compilerResult.getError() != null)) { 98 | CoffeeScriptCompiler.Error error = compilerResult.getError(); 99 | int line = error.getLine() == -1 ? 0 : error.getLine(); 100 | String msg = error.getLine() == -1 ? error.getMessage() : error.getErrorName(); 101 | StyledDocument doc = (StyledDocument) getSnapshot().getSource().getDocument(true); 102 | if (!getSnapshot().getMimePath().getMimeType(0).equals(CoffeeScriptLanguage.MIME_TYPE)) { 103 | int originalOffset = getSnapshot().getOriginalOffset(0); 104 | line += NbDocument.findLineNumber(doc, originalOffset); 105 | } 106 | int lineOffset = NbDocument.findLineOffset(doc, Math.max(0, line - 1)); 107 | int offsetError = getSnapshot().getEmbeddedOffset(lineOffset + error.getColumn()); 108 | if (offsetError >= getSnapshot().getText().length()) { 109 | offsetError = lineOffset; 110 | } 111 | return Collections.singletonList(DefaultError.createDefaultError( 112 | "cs.key", msg, "", getSnapshot().getSource().getFileObject(), 113 | offsetError, -1, true, Severity.ERROR)); 114 | } 115 | return Collections.emptyList(); 116 | } 117 | 118 | protected void invalidate() { 119 | // compilerResult = null; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptRegexpLanguage.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import java.util.Collection; 18 | import java.util.EnumSet; 19 | import org.netbeans.api.lexer.InputAttributes; 20 | import org.netbeans.api.lexer.Language; 21 | import org.netbeans.api.lexer.LanguagePath; 22 | import org.netbeans.api.lexer.Token; 23 | import org.netbeans.spi.lexer.EmbeddingPresence; 24 | import org.netbeans.spi.lexer.LanguageEmbedding; 25 | import org.netbeans.spi.lexer.LanguageHierarchy; 26 | import org.netbeans.spi.lexer.Lexer; 27 | import org.netbeans.spi.lexer.LexerRestartInfo; 28 | 29 | /** 30 | * 31 | * @author Denis Stepanov 32 | */ 33 | public class CoffeeScriptRegexpLanguage extends LanguageHierarchy { 34 | 35 | public static final String MIME_TYPE = "text/coffeescript-regexp"; //NOI18N 36 | private static final Language LANGUAGE = new CoffeeScriptRegexpLanguage().language(); 37 | 38 | public static final Language getLanguage() { 39 | return LANGUAGE; 40 | } 41 | 42 | private CoffeeScriptRegexpLanguage() { 43 | } 44 | 45 | protected Collection createTokenIds() { 46 | return EnumSet.allOf(CoffeeScriptRegexpTokenId.class); 47 | } 48 | 49 | @Override 50 | protected Lexer createLexer(LexerRestartInfo lri) { 51 | return new CoffeeScriptRegexpLexer(lri); 52 | } 53 | 54 | @Override 55 | protected EmbeddingPresence embeddingPresence(CoffeeScriptRegexpTokenId id) { 56 | if (id == CoffeeScriptRegexpTokenId.EMBEDDED) { 57 | return EmbeddingPresence.ALWAYS_QUERY; 58 | } 59 | return null; 60 | } 61 | 62 | @Override 63 | protected LanguageEmbedding embedding(Token token, LanguagePath languagePath, InputAttributes inputAttributes) { 64 | if (token.id() == CoffeeScriptRegexpTokenId.EMBEDDED) { 65 | return LanguageEmbedding.create(CoffeeScriptLanguage.getLanguage(), 0, 0); 66 | } 67 | return null; 68 | } 69 | 70 | @Override 71 | protected String mimeType() { 72 | return MIME_TYPE; 73 | } 74 | } -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptRegexpLexer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import org.netbeans.api.lexer.Token; 18 | import org.netbeans.spi.lexer.LexerInput; 19 | import org.netbeans.spi.lexer.LexerRestartInfo; 20 | import static coffeescript.nb.CoffeeScriptRegexpTokenId.*; 21 | 22 | /** 23 | * 24 | * @author Denis Stepanov 25 | */ 26 | public class CoffeeScriptRegexpLexer extends CoffeeScriptLexerBase { 27 | 28 | private boolean inEmbedded; 29 | 30 | public CoffeeScriptRegexpLexer(LexerRestartInfo info) { 31 | super(info.input(), info.tokenFactory()); 32 | inEmbedded = info.state() instanceof Boolean ? (Boolean) info.state() : false; 33 | } 34 | 35 | public Token nextToken() { 36 | if (inEmbedded) { 37 | try { 38 | if (balancedInterpolatedString("}")) { 39 | if (input.readLength() > 1) { 40 | input.backup(1); 41 | return token(EMBEDDED); 42 | } else if (input.readLength() == 0) { 43 | return null; 44 | } 45 | } 46 | return token(REGEXP); 47 | } finally { 48 | inEmbedded = false; 49 | } 50 | } 51 | while (true) { 52 | int ch = input.read(); 53 | switch (ch) { 54 | case LexerInput.EOF: 55 | if (input.readLength() > 0) { 56 | return token(REGEXP); 57 | } else { 58 | return null; 59 | } 60 | case '#': 61 | if (inputMatch("{")) { 62 | inEmbedded = true; 63 | return token(REGEXP); 64 | } else { 65 | if (input.readLength() > 1) { 66 | input.backup(1); 67 | return token(REGEXP); 68 | } 69 | while (true) { 70 | int c = input.read(); 71 | if (c == '\n' || c == LexerInput.EOF) { 72 | return token(COMMENT); 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | public Object state() { 81 | return inEmbedded; 82 | } 83 | 84 | public void release() { 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptRegexpTokenId.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import org.netbeans.api.lexer.TokenId; 18 | 19 | /** 20 | * 21 | * @author Denis Stepanov 22 | */ 23 | public enum CoffeeScriptRegexpTokenId implements TokenId { 24 | 25 | REGEXP("regexp"), 26 | EMBEDDED("embedded"), 27 | COMMENT("comment"); 28 | // 29 | private String category; 30 | 31 | private CoffeeScriptRegexpTokenId(String category) { 32 | this.category = category; 33 | } 34 | 35 | public String primaryCategory() { 36 | return category; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptRhinoCompiler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import java.io.InputStream; 17 | import java.io.InputStreamReader; 18 | import java.io.Reader; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import org.mozilla.javascript.Context; 22 | import org.mozilla.javascript.IdScriptableObject; 23 | import org.mozilla.javascript.JavaScriptException; 24 | import org.mozilla.javascript.Script; 25 | import org.mozilla.javascript.Scriptable; 26 | import org.mozilla.javascript.ScriptableObject; 27 | import org.openide.util.Exceptions; 28 | 29 | /** 30 | * 31 | * @author Denis Stepanov 32 | */ 33 | public class CoffeeScriptRhinoCompiler implements CoffeeScriptCompiler { 34 | 35 | private final Map scriptCacheMap = new HashMap(1); 36 | private static CoffeeScriptRhinoCompiler INSTANCE; 37 | 38 | private CoffeeScriptRhinoCompiler() { 39 | } 40 | 41 | public static synchronized CoffeeScriptRhinoCompiler get() { 42 | if (INSTANCE == null) { 43 | return (INSTANCE = new CoffeeScriptRhinoCompiler()); 44 | } 45 | return INSTANCE; 46 | } 47 | 48 | 49 | public CompilerResult compile(String code, boolean bare, boolean literate) { 50 | try { 51 | return new CompilerResult(compileCode(code, bare, literate)); 52 | } catch (StoppedContextException e) { 53 | return null; // Canceled 54 | } catch (JavaScriptException e) { 55 | if (e.getValue() instanceof IdScriptableObject) { 56 | IdScriptableObject error = (IdScriptableObject) e.getValue(); 57 | String message = (String) ScriptableObject.getProperty(error, "message"); 58 | IdScriptableObject location = (IdScriptableObject) ScriptableObject.getProperty(error, "location"); 59 | Double line = (Double) ScriptableObject.getProperty(location, "first_line"); 60 | Double column = (Double) ScriptableObject.getProperty(location, "first_column"); 61 | return new CompilerResult(new Error(line == null ? -1 : line.intValue()+1, column == null ? 0 : column.intValue()+1, message, message)); 62 | } 63 | return new CompilerResult(new Error(-1, "", e.getMessage())); 64 | } 65 | } 66 | 67 | private String compileCode(String code, boolean bare, boolean literate) { 68 | Context.enter(); 69 | Context ctx = new StoppableContext(); 70 | try { 71 | ctx.setInstructionObserverThreshold(1); 72 | ctx.setOptimizationLevel(-1); 73 | Scriptable scope = ctx.newObject(ctx.initStandardObjects()); 74 | getScriptFromClasspath("coffeescript/nb/resources/coffee-script.js").exec(ctx, scope); 75 | scope.put("code", scope, code); 76 | String options = String.format("{bare: %b, literate: %b}", bare, literate); 77 | String script = String.format("CoffeeScript.compile(code, %s);", options); 78 | return (String) getScriptFromString(script).exec(ctx, scope); 79 | 80 | } finally { 81 | Context.exit(); 82 | } 83 | } 84 | 85 | private Script getScriptFromClasspath(String url) { 86 | try { 87 | InputStream inputStream = getClass().getClassLoader().getResourceAsStream(url); 88 | return getScriptFromReader(url, new InputStreamReader(inputStream, "UTF-8")); 89 | } catch (Exception e) { 90 | Exceptions.printStackTrace(e); 91 | } 92 | return null; 93 | } 94 | 95 | private Script getScriptFromReader(String key, Reader reader) { 96 | synchronized (scriptCacheMap) { 97 | Script script = scriptCacheMap.get(key); 98 | if (script == null) { 99 | Context ctx = Context.enter(); 100 | try { 101 | ctx.setOptimizationLevel(-1); 102 | script = ctx.compileReader(reader, "", 0, null); 103 | } catch (Exception e) { 104 | Exceptions.printStackTrace(e); 105 | } finally { 106 | Context.exit(); 107 | } 108 | scriptCacheMap.put(key, script); 109 | } 110 | return script; 111 | } 112 | } 113 | 114 | private Script getScriptFromString(String string) { 115 | synchronized (scriptCacheMap) { 116 | Script script = scriptCacheMap.get(string); 117 | if (script == null) { 118 | Context ctx = Context.enter(); 119 | try { 120 | ctx.setOptimizationLevel(-1); 121 | script = ctx.compileString(string, "", 0, null); 122 | } catch (Exception e) { 123 | Exceptions.printStackTrace(e); 124 | } finally { 125 | Context.exit(); 126 | } 127 | scriptCacheMap.put(string, script); 128 | } 129 | return script; 130 | } 131 | } 132 | 133 | public static class StoppableContext extends Context { 134 | 135 | @Override 136 | protected void observeInstructionCount(int instructionCount) { 137 | if (Thread.interrupted()) { 138 | throw new StoppedContextException(); 139 | } 140 | } 141 | } 142 | 143 | public static class StoppedContextException extends RuntimeException { 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptSourceEncodingQuery.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import coffeescript.nb.options.CoffeeScriptSettings; 17 | import java.nio.charset.Charset; 18 | import org.netbeans.spi.queries.FileEncodingQueryImplementation; 19 | import org.openide.filesystems.FileObject; 20 | 21 | /** 22 | * 23 | * @author Denis Stepanov 24 | */ 25 | public final class CoffeeScriptSourceEncodingQuery extends FileEncodingQueryImplementation 26 | { 27 | private static final Charset UTF8 = Charset.forName("UTF8"); 28 | 29 | @Override 30 | public Charset getEncoding(FileObject file) 31 | { 32 | return CoffeeScriptSettings.get().isUseUTF8Encoding() ? UTF8 : null; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptStringLanguage.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import java.util.Collection; 18 | import java.util.EnumSet; 19 | import org.netbeans.api.lexer.InputAttributes; 20 | import org.netbeans.api.lexer.Language; 21 | import org.netbeans.api.lexer.LanguagePath; 22 | import org.netbeans.api.lexer.Token; 23 | import org.netbeans.spi.lexer.EmbeddingPresence; 24 | import org.netbeans.spi.lexer.LanguageEmbedding; 25 | import org.netbeans.spi.lexer.LanguageHierarchy; 26 | import org.netbeans.spi.lexer.Lexer; 27 | import org.netbeans.spi.lexer.LexerRestartInfo; 28 | 29 | /** 30 | * 31 | * @author Denis Stepanov 32 | */ 33 | public class CoffeeScriptStringLanguage extends LanguageHierarchy { 34 | 35 | public static final String MIME_TYPE = "text/coffeescript-string"; //NOI18N 36 | private static final Language LANGUAGE = new CoffeeScriptStringLanguage().language(); 37 | 38 | public static final Language getLanguage() { 39 | return LANGUAGE; 40 | } 41 | 42 | private CoffeeScriptStringLanguage() { 43 | } 44 | 45 | protected Collection createTokenIds() { 46 | return EnumSet.allOf(CoffeeScriptStringTokenId.class); 47 | } 48 | 49 | @Override 50 | protected Lexer createLexer(LexerRestartInfo lri) { 51 | return new CoffeeScriptStringLexer(lri); 52 | } 53 | 54 | @Override 55 | protected EmbeddingPresence embeddingPresence(CoffeeScriptStringTokenId id) { 56 | if (id == CoffeeScriptStringTokenId.EMBEDDED) { 57 | return EmbeddingPresence.ALWAYS_QUERY; 58 | } 59 | return null; 60 | } 61 | 62 | @Override 63 | protected LanguageEmbedding embedding(Token token, LanguagePath languagePath, InputAttributes inputAttributes) { 64 | if (token.id() == CoffeeScriptStringTokenId.EMBEDDED) { 65 | return LanguageEmbedding.create(CoffeeScriptLanguage.getLanguage(), 0, 0); 66 | } 67 | return null; 68 | } 69 | 70 | @Override 71 | protected String mimeType() { 72 | return MIME_TYPE; 73 | } 74 | } -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptStringLexer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import org.netbeans.api.lexer.Token; 18 | import org.netbeans.spi.lexer.LexerInput; 19 | import org.netbeans.spi.lexer.LexerRestartInfo; 20 | 21 | /** 22 | * 23 | * @author Denis Stepanov 24 | */ 25 | public class CoffeeScriptStringLexer extends CoffeeScriptLexerBase { 26 | 27 | private boolean inEmbedded; 28 | 29 | public CoffeeScriptStringLexer(LexerRestartInfo info) { 30 | super(info.input(), info.tokenFactory()); 31 | inEmbedded = info.state() instanceof Boolean ? (Boolean) info.state() : false; 32 | } 33 | 34 | public Token nextToken() { 35 | if (inEmbedded) { 36 | try { 37 | if (balancedInterpolatedString("}")) { 38 | if (input.readLength() > 1) { 39 | input.backup(1); 40 | return token(CoffeeScriptStringTokenId.EMBEDDED); 41 | } else if (input.readLength() == 0) { 42 | return null; 43 | } 44 | } 45 | return token(CoffeeScriptStringTokenId.STRING); 46 | } finally { 47 | inEmbedded = false; 48 | } 49 | } 50 | while (true) { 51 | int ch = input.read(); 52 | switch (ch) { 53 | case LexerInput.EOF: 54 | if (input.readLength() > 0) { 55 | return token(CoffeeScriptStringTokenId.STRING); 56 | } else { 57 | return null; 58 | } 59 | case '\\': 60 | input.read(); 61 | break; 62 | case '#': 63 | if (inputMatch("{")) { 64 | inEmbedded = true; 65 | return token(CoffeeScriptStringTokenId.STRING); 66 | } 67 | } 68 | } 69 | } 70 | 71 | public Object state() { 72 | return inEmbedded; 73 | } 74 | 75 | public void release() { 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptStringTokenId.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coffeescript.nb; 16 | 17 | import org.netbeans.api.lexer.TokenId; 18 | 19 | /** 20 | * 21 | * @author Denis Stepanov 22 | */ 23 | public enum CoffeeScriptStringTokenId implements TokenId { 24 | 25 | STRING("string"), 26 | EMBEDDED("embedded"); 27 | // 28 | private String category; 29 | 30 | private CoffeeScriptStringTokenId(String category) { 31 | this.category = category; 32 | } 33 | 34 | public String primaryCategory() { 35 | return category; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptStructureScanner.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import java.util.ArrayDeque; 17 | import java.util.ArrayList; 18 | import java.util.Collections; 19 | import java.util.Deque; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import javax.swing.text.BadLocationException; 24 | import org.netbeans.api.lexer.Token; 25 | import org.netbeans.api.lexer.TokenHierarchy; 26 | import org.netbeans.api.lexer.TokenSequence; 27 | import org.netbeans.editor.BaseDocument; 28 | import org.netbeans.editor.Utilities; 29 | import org.netbeans.modules.csl.api.OffsetRange; 30 | import org.netbeans.modules.csl.api.StructureItem; 31 | import org.netbeans.modules.csl.api.StructureScanner; 32 | import org.netbeans.modules.csl.spi.ParserResult; 33 | import org.openide.util.Exceptions; 34 | 35 | /** 36 | * 37 | * @author Denis Stepanov 38 | */ 39 | public class CoffeeScriptStructureScanner implements StructureScanner { 40 | 41 | public List scan(ParserResult pr) { 42 | return Collections.emptyList(); 43 | } 44 | 45 | public Map> folds(ParserResult pr) { 46 | if (pr == null) { 47 | return Collections.emptyMap(); 48 | } 49 | try { 50 | Map> folds = new HashMap>(); 51 | BaseDocument document = (BaseDocument) pr.getSnapshot().getSource().getDocument(true); 52 | TokenHierarchy th = (TokenHierarchy) pr.getSnapshot().getTokenHierarchy(); 53 | TokenSequence ts = th.tokenSequence(CoffeeScriptLanguage.getLanguage()); 54 | List ranges = new ArrayList(); 55 | Deque indents = new ArrayDeque(); 56 | while (ts.moveNext()) { 57 | Token token = ts.token(); 58 | switch (token.id()) { 59 | // case COMMENT: 60 | // TokenSequence commentTS = ts.subSequence(ts.offset()); 61 | // int start = token.offset(th); 62 | // int end = start + token.length(); 63 | // while (commentTS.moveNext()) { 64 | // Token commentNextToken = commentTS.token(); 65 | // if (commentNextToken.id() == CoffeeScriptTokenId.COMMENT) { 66 | // end = commentNextToken.offset(th) + commentNextToken.length(); 67 | // continue; 68 | // } 69 | // if (commentNextToken.id().getCategory() == CoffeeScriptTokenId.Category.WHITESPACE_CAT) { 70 | // continue; 71 | // } 72 | // break; 73 | // } 74 | // addIndent(document, ranges, start, end); 75 | // break; 76 | case INDENT: 77 | Integer indent = (Integer) token.getProperty("indent"); 78 | indents.push(new IdentRegion(token.offset(th), indent)); 79 | break; 80 | case OUTDENT: 81 | Integer outdent = (Integer) token.getProperty("indent"); 82 | int to = token.offset(th) + token.length(); 83 | addIndent(document, ranges, indents, outdent, to); 84 | break; 85 | } 86 | } 87 | addIndent(document, ranges, indents, -1, document.getLength()); 88 | folds.put("codeblocks", ranges); 89 | return folds; 90 | } catch (Exception e) { 91 | Exceptions.printStackTrace(e); 92 | } 93 | return Collections.emptyMap(); 94 | } 95 | 96 | private void addIndent(BaseDocument document, List ranges, Deque indents, Integer outdent, int end) throws BadLocationException { 97 | while (!indents.isEmpty() && (indents.peek().indent > outdent)) { 98 | IdentRegion identRegion = indents.pop(); 99 | int from = Utilities.getFirstNonWhiteFwd(document, identRegion.start); 100 | int to = Utilities.getFirstNonWhiteBwd(document, end) + 1; 101 | addIndent(document, ranges, from, to); 102 | } 103 | } 104 | 105 | private void addIndent(BaseDocument document, List ranges, int start, int end) throws BadLocationException { 106 | if (Utilities.getRowCount(document, start, end) > 1) { 107 | ranges.add(new OffsetRange(start, end)); 108 | } 109 | } 110 | 111 | public Configuration getConfiguration() { 112 | return new Configuration(false, false); 113 | } 114 | 115 | private static class IdentRegion { 116 | 117 | int start; 118 | int indent; 119 | 120 | public IdentRegion(int start, int indent) { 121 | this.start = start; 122 | this.indent = indent; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptTokenId.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import org.netbeans.api.lexer.TokenId; 17 | import static coffeescript.nb.CoffeeScriptTokenId.Category.*; 18 | 19 | /** 20 | * 21 | * @author Denis Stepanov 22 | */ 23 | public enum CoffeeScriptTokenId implements TokenId { 24 | 25 | ERROR(null, ERROR_CAT), 26 | NEW("new", KEYWORD_CAT), 27 | IDENTIFIER(null, IDENTIFIER_CAT), 28 | REGEX(null, REGEXP_CAT), 29 | HEREGEX(null, REGEXP_CAT), 30 | NUMBER(null, NUMBER_CAT), 31 | STRING(null, STRING_CAT), 32 | SIMPLE_STRING(null, STRING_CAT), 33 | JSTOKEN(null, STRING_CAT), 34 | BOOL(null, KEYWORD_CAT), 35 | WHITESPACE(null, WHITESPACE_CAT), 36 | INDENT(null, WHITESPACE_CAT), 37 | OUTDENT(null, WHITESPACE_CAT), 38 | EOL(null, WHITESPACE_CAT), 39 | COMMENT(null, COMMENT_CAT), 40 | LPAREN("(", SEPARATOR_CAT), 41 | RPAREN(")", SEPARATOR_CAT), 42 | LBRACE("{", SEPARATOR_CAT), 43 | RBRACE("}", SEPARATOR_CAT), 44 | LBRACKET("[", SEPARATOR_CAT), 45 | RBRACKET("]", SEPARATOR_CAT), 46 | ANY_KEYWORD(null, KEYWORD_CAT), 47 | ANY_OPERATOR(null, OPERATOR_CAT), 48 | QM("?", OPERATOR_CAT), 49 | DOT(".", OPERATOR_CAT), 50 | QDOT("?.", OPERATOR_CAT), 51 | THIS("this", KEYWORD_CAT), 52 | FOR("for", KEYWORD_CAT), 53 | IF("if", KEYWORD_CAT), 54 | ELSE("else", KEYWORD_CAT), 55 | WHILE("while", KEYWORD_CAT), 56 | CASE("case", KEYWORD_CAT), 57 | DEFAULT("default", KEYWORD_CAT), 58 | BREAK("break", KEYWORD_CAT), 59 | SWITCH("switch", KEYWORD_CAT), 60 | COLON(":", OPERATOR_CAT), 61 | DOUBLE_COLON("::", OPERATOR_CAT), 62 | SEMI(";", OPERATOR_CAT), 63 | FIELD(null, FIELD_CAT), 64 | DIV("/", OPERATOR_CAT), 65 | INC("++", OPERATOR_CAT), 66 | DEC("--", OPERATOR_CAT), 67 | AT("@", KEYWORD_CAT); 68 | // 69 | private final String fixedText; 70 | private final Category category; 71 | 72 | CoffeeScriptTokenId(String fixedText, Category category) { 73 | this.fixedText = fixedText; 74 | this.category = category; 75 | } 76 | 77 | public String fixedText() { 78 | return fixedText; 79 | } 80 | 81 | public String primaryCategory() { 82 | return category.getName(); 83 | } 84 | 85 | public Category getCategory() { 86 | return category; 87 | } 88 | 89 | public static enum Category { 90 | 91 | COMMENT_CAT("comment"), 92 | KEYWORD_CAT("keyword"), 93 | REGEXP_CAT("regexp"), 94 | STRING_CAT("string"), 95 | WHITESPACE_CAT("whitespace"), 96 | OPERATOR_CAT("operator"), 97 | SEPARATOR_CAT("separator"), 98 | ERROR_CAT("error"), 99 | NUMBER_CAT("number"), 100 | IDENTIFIER_CAT("identifier"), 101 | FIELD_CAT("field"); 102 | private String name; 103 | 104 | private Category(String name) { 105 | this.name = name; 106 | } 107 | 108 | public String getName() { 109 | return name; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/coffeescript/nb/CoffeeScriptUtils.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb; 15 | 16 | import coffeescript.nb.options.CoffeeScriptSettings; 17 | import java.io.File; 18 | import java.io.OutputStream; 19 | import java.nio.charset.Charset; 20 | import org.netbeans.api.project.FileOwnerQuery; 21 | import org.netbeans.api.project.Project; 22 | import org.netbeans.api.queries.FileEncodingQuery; 23 | import org.openide.filesystems.FileObject; 24 | import org.openide.filesystems.FileUtil; 25 | import org.openide.util.Exceptions; 26 | 27 | /** 28 | * 29 | * @author Denis Stepanov 30 | */ 31 | public class CoffeeScriptUtils { 32 | 33 | public static void writeJS(final String js, String name, FileObject folder, Charset encoding) { 34 | String outputFolder = CoffeeScriptSettings.get().getOutputFolder(); 35 | try { 36 | Project project = FileOwnerQuery.getOwner(folder); 37 | if (outputFolder != null && outputFolder.trim().length() > 0) { 38 | if (project != null) { 39 | FileObject projectDirectory = project.getProjectDirectory(); 40 | String projectPath = projectDirectory.getPath(); 41 | String path = folder.getPath().replace(projectPath, outputFolder); 42 | File pathFolder = new File(path); 43 | if(!pathFolder.exists()) { 44 | pathFolder.mkdirs(); 45 | } 46 | folder = FileUtil.toFileObject(pathFolder); 47 | } else { 48 | folder = FileUtil.toFileObject(new File(outputFolder)); 49 | } 50 | } 51 | FileObject file = folder.getFileObject(name, "js"); 52 | if (file == null) { 53 | file = folder.createData(name, "js"); 54 | } 55 | if (!file.asText().equals(js)) { 56 | OutputStream out = file.getOutputStream(); 57 | try { 58 | out.write(js.getBytes(encoding)); 59 | out.flush(); 60 | } finally { 61 | if (out != null) { 62 | out.close(); 63 | } 64 | } 65 | } 66 | } catch (Exception e) { 67 | Exceptions.printStackTrace(e); 68 | } 69 | } 70 | 71 | public static void writeJSForCoffeeScriptFile(final String js, FileObject coffeeFile) { 72 | writeJS(js, coffeeFile.getName(), coffeeFile.getParent(), FileEncodingQuery.getEncoding(coffeeFile)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/coffeescript/nb/options/Bundle.properties: -------------------------------------------------------------------------------- 1 | CoffeeScriptOptionsPanel.compiler.install.help.text=
  • Mac, Linux: install node.js and coffee command
  • Windows: not supported yet
  • 2 | CoffeeScriptOptionsPanel.compilerLabel.text=CoffeeScript compiler: 3 | CoffeeScriptOptionsPanel.executablePathLabel.text=Executable path to 'coffee': 4 | CoffeeScriptOptionsPanel.executablePathTextField.text= 5 | CoffeeScriptOptionsPanel.compilerHelp.text= Node.js coffee compiler install node.js and coffee command

    Default 'coffee' command location:

    • Mac, Linux: '/usr/local/bin/coffee'
    • Windows: '%APPDATA%\\npm\\coffee.cmd'
    • 6 | CoffeeScriptOptionsPanel.compilerHelp.contentType=text/html 7 | CoffeeScriptOptionsPanel.outputFolderTextField.text= 8 | CoffeeScriptOptionsPanel.outputFolderLabel.text=Autocompile target folder (leave blank to put it in the same folder): 9 | CoffeeScriptOptionsPanel.browseButton.text=Browse... 10 | CoffeeScriptOptionsPanel.toolTipText= 11 | CoffeeScriptOptionsPanel.compilerSettings.border.title=Compiler settings 12 | CoffeeScriptOptionsPanel.utfCheckbox.text=Always use UTF8 as the default CoffeeScript file encoding 13 | -------------------------------------------------------------------------------- /src/coffeescript/nb/options/CoffeeScriptOptionsPanelController.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.options; 15 | 16 | import java.beans.PropertyChangeListener; 17 | import java.beans.PropertyChangeSupport; 18 | import javax.swing.JComponent; 19 | import org.netbeans.spi.options.OptionsPanelController; 20 | import org.openide.util.HelpCtx; 21 | import org.openide.util.Lookup; 22 | 23 | /** 24 | * 25 | * @author Denis Stepanov 26 | */ 27 | @OptionsPanelController.SubRegistration(id = "CoffeeScript", 28 | displayName = "coffeescript.nb.resources.Bundle#CoffeeScriptOptions.displayName", 29 | keywords = "coffeescript.nb.resources.Bundle#KW_CoffeeScriptOptions", 30 | keywordsCategory = "Advanced/CoffeeScript") 31 | public class CoffeeScriptOptionsPanelController extends OptionsPanelController { 32 | 33 | private final PropertyChangeSupport propertySupport = new PropertyChangeSupport(this); 34 | private CoffeeScriptOptionsPanel panel; 35 | private boolean changed; 36 | 37 | @Override 38 | public JComponent getComponent(Lookup masterLookup) { 39 | return getComponent(); 40 | } 41 | 42 | @Override 43 | public void update() { 44 | panel.update(); 45 | } 46 | 47 | @Override 48 | public void applyChanges() { 49 | panel.applyChanges(); 50 | changed = false; 51 | } 52 | 53 | @Override 54 | public void cancel() { 55 | } 56 | 57 | @Override 58 | public HelpCtx getHelpCtx() { 59 | return null; 60 | } 61 | 62 | @Override 63 | public boolean isChanged() { 64 | return changed; 65 | } 66 | 67 | @Override 68 | public boolean isValid() { 69 | return getComponent().isSettingsValid(); 70 | } 71 | 72 | @Override 73 | public void addPropertyChangeListener(PropertyChangeListener l) { 74 | propertySupport.addPropertyChangeListener(l); 75 | } 76 | 77 | @Override 78 | public void removePropertyChangeListener(PropertyChangeListener l) { 79 | propertySupport.removePropertyChangeListener(l); 80 | } 81 | 82 | void changed() { 83 | if (!changed) { 84 | changed = true; 85 | propertySupport.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true); 86 | } 87 | } 88 | 89 | void valid() { 90 | propertySupport.firePropertyChange(OptionsPanelController.PROP_VALID, null, null); 91 | } 92 | 93 | private synchronized CoffeeScriptOptionsPanel getComponent() { 94 | if (panel == null) { 95 | panel = new CoffeeScriptOptionsPanel(this); 96 | } 97 | return panel; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/coffeescript/nb/options/CoffeeScriptSettings.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.options; 15 | 16 | import coffeescript.nb.CoffeeScriptCompiler; 17 | import coffeescript.nb.CoffeeScriptNodeJSCompiler; 18 | import coffeescript.nb.CoffeeScriptRhinoCompiler; 19 | import java.util.prefs.Preferences; 20 | import org.openide.util.NbPreferences; 21 | 22 | /** 23 | * 24 | * @author Denis Stepanov 25 | */ 26 | public class CoffeeScriptSettings { 27 | 28 | private static CoffeeScriptSettings INSTANCE; 29 | 30 | private CoffeeScriptSettings() { 31 | } 32 | 33 | public static synchronized CoffeeScriptSettings get() { 34 | if (INSTANCE == null) { 35 | INSTANCE = new CoffeeScriptSettings(); 36 | } 37 | return INSTANCE; 38 | } 39 | 40 | private Preferences getPreferences() { 41 | return NbPreferences.forModule(CoffeeScriptSettings.class).node("settings"); 42 | } 43 | 44 | public boolean isBare() { 45 | return getPreferences().getBoolean("bare", true); 46 | } 47 | 48 | public void setBare(boolean bare) { 49 | getPreferences().put("bare", Boolean.toString(bare)); 50 | } 51 | 52 | public CompilerType getCompilerType() { 53 | return CompilerType.valueOf(getPreferences().get("compilerType", CompilerType.RHINO.name())); 54 | } 55 | 56 | public void setCompilerType(CompilerType compilerType) { 57 | getPreferences().put("compilerType", compilerType.name()); 58 | } 59 | 60 | public String getCompilerExec() { 61 | return getPreferences().get("compilerExec", ""); 62 | } 63 | 64 | public void setCompilerExec(String compilerExec) { 65 | getPreferences().put("compilerExec", compilerExec); 66 | } 67 | 68 | public String getOutputFolder() { 69 | return getPreferences().get("outputFolder", ""); 70 | } 71 | 72 | public void setOutputFolder(String outputFolder) { 73 | getPreferences().put("outputFolder", outputFolder); 74 | } 75 | 76 | public boolean isUseUTF8Encoding() { 77 | return getPreferences().getBoolean("useUTF8", false); 78 | } 79 | 80 | public void setUseUTF8Encoding(boolean useUTF8) { 81 | getPreferences().putBoolean("useUTF8", useUTF8); 82 | } 83 | 84 | public static CoffeeScriptCompiler getCompiler() { 85 | switch (CoffeeScriptSettings.get().getCompilerType()) { 86 | case NODEJS: 87 | return CoffeeScriptNodeJSCompiler.get(); 88 | case RHINO: 89 | return CoffeeScriptRhinoCompiler.get(); 90 | } 91 | return null; 92 | } 93 | 94 | public enum CompilerType { 95 | 96 | RHINO("Rhino (JavaScript for Java)"), 97 | NODEJS("CoffeeScript (Node.js)"); 98 | private final String label; 99 | 100 | private CompilerType(String label) { 101 | this.label = label; 102 | } 103 | 104 | public String getLabel() { 105 | return label; 106 | } 107 | 108 | @Override 109 | public String toString() { 110 | return label; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/CoffeeScriptLogicalView.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project; 15 | 16 | import java.awt.Image; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import javax.swing.Action; 20 | import org.netbeans.spi.project.ui.LogicalViewProvider; 21 | import org.netbeans.spi.project.ui.support.CommonProjectActions; 22 | import org.openide.filesystems.FileObject; 23 | import org.openide.loaders.DataFolder; 24 | import org.openide.loaders.DataObjectNotFoundException; 25 | import org.openide.nodes.AbstractNode; 26 | import org.openide.nodes.Children; 27 | import org.openide.nodes.FilterNode; 28 | import org.openide.nodes.Node; 29 | import org.openide.util.Exceptions; 30 | import org.openide.util.ImageUtilities; 31 | import org.openide.util.Lookup; 32 | import org.openide.util.Utilities; 33 | import org.openide.util.lookup.Lookups; 34 | import org.openide.util.lookup.ProxyLookup; 35 | 36 | /** 37 | * 38 | * @author Denis Stepanov 39 | */ 40 | public class CoffeeScriptLogicalView implements LogicalViewProvider { 41 | 42 | private final CoffeeScriptProject project; 43 | 44 | public CoffeeScriptLogicalView(CoffeeScriptProject project) { 45 | this.project = project; 46 | } 47 | 48 | @Override 49 | public org.openide.nodes.Node createLogicalView() { 50 | try { 51 | FileObject text = project.getConfigFile(true); 52 | DataFolder textDataObject = DataFolder.findFolder(text.getParent()); 53 | return new TextNode(textDataObject.getNodeDelegate(), project); 54 | } catch (DataObjectNotFoundException donfe) { 55 | Exceptions.printStackTrace(donfe); 56 | return new AbstractNode(Children.LEAF); 57 | } 58 | } 59 | 60 | @Override 61 | public Node findPath(Node root, Object target) { 62 | return null; 63 | } 64 | 65 | private static final class TextNode extends FilterNode { 66 | 67 | private final CoffeeScriptProject project; 68 | 69 | public TextNode(Node node, CoffeeScriptProject project) throws DataObjectNotFoundException { 70 | super(node, new FilterNode.Children(node), 71 | new ProxyLookup(new Lookup[]{ 72 | Lookups.singleton(project), 73 | node.getLookup() 74 | })); 75 | this.project = project; 76 | } 77 | 78 | @Override 79 | public Action[] getActions(boolean arg0) { 80 | List actions = new ArrayList(); 81 | actions.add(CommonProjectActions.newFileAction()); 82 | actions.add(null); 83 | actions.add(CommonProjectActions.setAsMainProjectAction()); 84 | actions.add(CommonProjectActions.closeProjectAction()); 85 | actions.add(null); 86 | actions.add(CommonProjectActions.renameProjectAction()); 87 | actions.add(CommonProjectActions.moveProjectAction()); 88 | actions.add(CommonProjectActions.copyProjectAction()); 89 | actions.add(CommonProjectActions.deleteProjectAction()); 90 | actions.add(null); 91 | actions.addAll(Utilities.actionsForPath("Projects/Actions")); //NOI18N 92 | actions.add(null); 93 | actions.add(CommonProjectActions.customizeProjectAction()); 94 | return actions.toArray(new Action[actions.size()]); 95 | } 96 | 97 | @Override 98 | public Image getIcon(int type) { 99 | return ImageUtilities.loadImage("coffeescript/nb/resources/coffeescript-icon.png"); 100 | } 101 | 102 | @Override 103 | public Image getOpenedIcon(int type) { 104 | return getIcon(type); 105 | } 106 | 107 | @Override 108 | public String getDisplayName() { 109 | return project.getProjectDirectory().getName(); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/CoffeeScriptPanelProvider.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project; 15 | 16 | import coffeescript.nb.project.ui.CoffeeScriptActionSettingsPanel; 17 | import javax.swing.JComponent; 18 | import org.netbeans.spi.project.ui.support.ProjectCustomizer; 19 | import org.netbeans.spi.project.ui.support.ProjectCustomizer.Category; 20 | import org.openide.util.Lookup; 21 | 22 | /** 23 | * 24 | * @author Denis Stepanov 25 | */ 26 | @ProjectCustomizer.CompositeCategoryProvider.Registration(projectType = "coffeescript-nb-project") 27 | public class CoffeeScriptPanelProvider implements ProjectCustomizer.CompositeCategoryProvider { 28 | 29 | public Category createCategory(Lookup context) { 30 | return ProjectCustomizer.Category.create("Build", " ", null); 31 | } 32 | 33 | public JComponent createComponent(Category category, Lookup context) { 34 | return new CoffeeScriptActionSettingsPanel(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/CoffeeScriptProject.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project; 15 | 16 | import coffeescript.nb.CoffeeScriptSourceEncodingQuery; 17 | import java.beans.PropertyChangeListener; 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import javax.swing.Icon; 24 | import javax.swing.ImageIcon; 25 | import org.netbeans.api.project.Project; 26 | import org.netbeans.api.project.ProjectInformation; 27 | import org.netbeans.spi.project.ActionProvider; 28 | import org.netbeans.spi.project.CopyOperationImplementation; 29 | import org.netbeans.spi.project.DeleteOperationImplementation; 30 | import org.netbeans.spi.project.ProjectState; 31 | import org.netbeans.spi.project.ui.support.DefaultProjectOperations; 32 | import org.openide.filesystems.FileObject; 33 | import org.openide.util.Exceptions; 34 | import org.openide.util.ImageUtilities; 35 | import org.openide.util.Lookup; 36 | import org.openide.util.lookup.Lookups; 37 | 38 | /** 39 | * 40 | * @author Denis Stepanov 41 | */ 42 | public class CoffeeScriptProject implements Project { 43 | 44 | private FileObject projectDirectory; 45 | private ProjectState state; 46 | private Lookup lookup; 47 | 48 | public CoffeeScriptProject(FileObject projectDirectory, ProjectState state) { 49 | this.projectDirectory = projectDirectory; 50 | this.state = state; 51 | } 52 | 53 | public FileObject getProjectDirectory() { 54 | return projectDirectory; 55 | } 56 | 57 | FileObject getConfigFile(boolean create) { 58 | FileObject result = 59 | projectDirectory.getFileObject(CoffeeScriptProjectFactory.PROJECT_CONFIG_FILE); 60 | if (result == null && create) { 61 | try { 62 | result = projectDirectory.createData(CoffeeScriptProjectFactory.PROJECT_CONFIG_FILE); 63 | } catch (IOException ioe) { 64 | Exceptions.printStackTrace(ioe); 65 | } 66 | } 67 | return result; 68 | } 69 | 70 | @Override 71 | public Lookup getLookup() { 72 | if (lookup == null) { 73 | lookup = Lookups.fixed(new Object[]{ 74 | this, 75 | state, 76 | new CoffeeScriptActionProvider(), 77 | new CoffeeScriptDeleteOperation(), 78 | new CoffeeScriptCopyOperation(this), 79 | new CoffeeScriptProjectInfo(), 80 | new CoffeeScriptLogicalView(this), 81 | new CoffeeScriptSourceEncodingQuery() 82 | // new CoffeeScriptProjectCustomizer(this) 83 | }); 84 | } 85 | return lookup; 86 | } 87 | 88 | private final class CoffeeScriptActionProvider implements ActionProvider { 89 | 90 | private String[] supported = new String[]{ 91 | ActionProvider.COMMAND_DELETE, 92 | ActionProvider.COMMAND_COPY,}; 93 | 94 | @Override 95 | public String[] getSupportedActions() { 96 | return supported; 97 | } 98 | 99 | @Override 100 | public void invokeAction(String string, Lookup lookup) throws IllegalArgumentException { 101 | if (string.equalsIgnoreCase(ActionProvider.COMMAND_DELETE)) { 102 | DefaultProjectOperations.performDefaultDeleteOperation(CoffeeScriptProject.this); 103 | } 104 | if (string.equalsIgnoreCase(ActionProvider.COMMAND_COPY)) { 105 | DefaultProjectOperations.performDefaultCopyOperation(CoffeeScriptProject.this); 106 | } 107 | } 108 | 109 | @Override 110 | public boolean isActionEnabled(String command, Lookup lookup) throws IllegalArgumentException { 111 | if ((command.equals(ActionProvider.COMMAND_DELETE))) { 112 | return true; 113 | } else if ((command.equals(ActionProvider.COMMAND_COPY))) { 114 | return true; 115 | } else { 116 | throw new IllegalArgumentException(command); 117 | } 118 | } 119 | } 120 | 121 | private final class CoffeeScriptDeleteOperation implements DeleteOperationImplementation { 122 | 123 | public void notifyDeleting() throws IOException { 124 | } 125 | 126 | public void notifyDeleted() throws IOException { 127 | } 128 | 129 | public List getMetadataFiles() { 130 | List dataFiles = new ArrayList(); 131 | return dataFiles; 132 | } 133 | 134 | public List getDataFiles() { 135 | List dataFiles = new ArrayList(); 136 | return dataFiles; 137 | } 138 | } 139 | 140 | private final class CoffeeScriptCopyOperation implements CopyOperationImplementation { 141 | 142 | private final CoffeeScriptProject project; 143 | private final FileObject projectDir; 144 | 145 | public CoffeeScriptCopyOperation(CoffeeScriptProject project) { 146 | this.project = project; 147 | this.projectDir = project.getProjectDirectory(); 148 | } 149 | 150 | public List getMetadataFiles() { 151 | return Collections.EMPTY_LIST; 152 | } 153 | 154 | public List getDataFiles() { 155 | return Collections.EMPTY_LIST; 156 | } 157 | 158 | public void notifyCopying() throws IOException { 159 | } 160 | 161 | public void notifyCopied(Project arg0, File arg1, String arg2) throws IOException { 162 | } 163 | } 164 | 165 | private final class CoffeeScriptProjectInfo implements ProjectInformation { 166 | 167 | @Override 168 | public Icon getIcon() { 169 | return new ImageIcon(ImageUtilities.loadImage( 170 | "coffeescript/nb/resources/coffeescript-icon.png")); 171 | } 172 | 173 | @Override 174 | public String getName() { 175 | return getProjectDirectory().getName(); 176 | } 177 | 178 | @Override 179 | public String getDisplayName() { 180 | return getName(); 181 | } 182 | 183 | @Override 184 | public void addPropertyChangeListener(PropertyChangeListener pcl) { 185 | //do nothing, won't change 186 | } 187 | 188 | @Override 189 | public void removePropertyChangeListener(PropertyChangeListener pcl) { 190 | //do nothing, won't change 191 | } 192 | 193 | @Override 194 | public Project getProject() { 195 | return CoffeeScriptProject.this; 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/CoffeeScriptProjectCustomizer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project; 15 | 16 | import java.awt.Dialog; 17 | import java.awt.event.ActionEvent; 18 | import java.awt.event.ActionListener; 19 | import java.awt.event.WindowAdapter; 20 | import java.awt.event.WindowEvent; 21 | import org.netbeans.spi.project.ui.CustomizerProvider; 22 | import org.netbeans.spi.project.ui.support.ProjectCustomizer; 23 | import org.openide.util.Lookup; 24 | import org.openide.util.Mutex; 25 | import org.openide.util.lookup.Lookups; 26 | 27 | /** 28 | * 29 | * @author Denis Stepanov 30 | */ 31 | public class CoffeeScriptProjectCustomizer implements CustomizerProvider { 32 | 33 | public static final String CUSTOMIZER_FOLDER_PATH = "Projects/coffeescript-nb-project/Customizer"; 34 | private final CoffeeScriptProject project; 35 | 36 | public CoffeeScriptProjectCustomizer(CoffeeScriptProject project) { 37 | this.project = project; 38 | } 39 | 40 | @Override 41 | public void showCustomizer() { 42 | showCustomizer(null); 43 | } 44 | 45 | public void showCustomizer(final String preselectedCategory) { 46 | Mutex.EVENT.readAccess(new Runnable() { 47 | 48 | @Override 49 | public void run() { 50 | 51 | OptionListener optionListener = new OptionListener(); 52 | StoreListener storeListener = new StoreListener(); 53 | Lookup context = Lookups.fixed(project); 54 | Dialog dialog = ProjectCustomizer.createCustomizerDialog(CUSTOMIZER_FOLDER_PATH, context, preselectedCategory, 55 | optionListener, storeListener, null); 56 | dialog.addWindowListener(optionListener); 57 | dialog.setTitle("Properties"); 58 | dialog.setVisible(true); 59 | } 60 | }); 61 | } 62 | 63 | private class StoreListener implements ActionListener { 64 | 65 | StoreListener() { 66 | } 67 | 68 | public void actionPerformed(ActionEvent e) { 69 | } 70 | } 71 | 72 | private static class OptionListener extends WindowAdapter implements ActionListener { 73 | 74 | OptionListener() { 75 | } 76 | 77 | @Override 78 | public void actionPerformed(ActionEvent e) { 79 | } 80 | 81 | @Override 82 | public void windowClosed(WindowEvent e) { 83 | } 84 | 85 | @Override 86 | public void windowClosing(WindowEvent e) { 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/CoffeeScriptProjectFactory.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project; 15 | 16 | import java.io.IOException; 17 | import org.netbeans.api.project.Project; 18 | import org.netbeans.api.project.ProjectManager.Result; 19 | import org.netbeans.spi.project.ProjectFactory; 20 | import org.netbeans.spi.project.ProjectFactory2; 21 | import org.netbeans.spi.project.ProjectState; 22 | import org.openide.filesystems.FileObject; 23 | 24 | /** 25 | * 26 | * @author Denis Stepanov 27 | */ 28 | @org.openide.util.lookup.ServiceProvider(service = ProjectFactory.class) 29 | public class CoffeeScriptProjectFactory implements ProjectFactory2 { 30 | 31 | public final static String PROJECT_CONFIG_FILE = "Cakefile"; 32 | 33 | public Result isProject2(FileObject projectDirectory) { 34 | return isProject(projectDirectory) ? new Result(null) : null; 35 | } 36 | 37 | public boolean isProject(FileObject projectDirectory) { 38 | return projectDirectory.getFileObject(PROJECT_CONFIG_FILE) != null; 39 | } 40 | 41 | public Project loadProject(FileObject projectDirectory, ProjectState state) throws IOException { 42 | return isProject(projectDirectory) ? new CoffeeScriptProject(projectDirectory, state) : null; 43 | } 44 | 45 | public void saveProject(Project project) throws IOException, ClassCastException { 46 | // FileObject projectRoot = project.getProjectDirectory(); 47 | // if (projectRoot.getFileObject(PROJECT_CONFIG_FILE) == null) { 48 | // throw new IOException("Project file " + projectRoot.getPath() 49 | // + " deleted," 50 | // + " cannot save project"); 51 | // } 52 | ((CoffeeScriptProject) project).getConfigFile(true); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/sample/Bundle.properties: -------------------------------------------------------------------------------- 1 | LBL_CreateProjectStep=Name and Location 2 | CoffeeScriptApplicationPanelVisual.projectLocationLabel.text=Project &Location: 3 | CoffeeScriptApplicationPanelVisual.browseButton.actionCommand=BROWSE 4 | CoffeeScriptApplicationPanelVisual.browseButton.text=Br&owse... 5 | CoffeeScriptApplicationPanelVisual.createdFolderLabel.text=Project &Folder: 6 | CoffeeScriptApplicationPanelVisual.projectNameLabel.text=Project &Name: 7 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/sample/CoffeeScriptApplicationPanelVisual.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 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 |
      123 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/sample/CoffeeScriptApplicationPanelVisual.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project.sample; 15 | 16 | import java.io.File; 17 | import javax.swing.JFileChooser; 18 | import javax.swing.JPanel; 19 | import javax.swing.event.DocumentEvent; 20 | import javax.swing.event.DocumentListener; 21 | import javax.swing.text.Document; 22 | import org.netbeans.spi.project.ui.support.ProjectChooser; 23 | import org.openide.WizardDescriptor; 24 | import org.openide.WizardValidationException; 25 | import org.openide.filesystems.FileUtil; 26 | 27 | public class CoffeeScriptApplicationPanelVisual extends JPanel implements DocumentListener { 28 | 29 | public static final String PROP_PROJECT_NAME = "projectName"; 30 | private CoffeeScriptApplicationWizardPanel panel; 31 | 32 | public CoffeeScriptApplicationPanelVisual(CoffeeScriptApplicationWizardPanel panel) { 33 | initComponents(); 34 | this.panel = panel; 35 | // Register listener on the textFields to make the automatic updates 36 | projectNameTextField.getDocument().addDocumentListener(this); 37 | projectLocationTextField.getDocument().addDocumentListener(this); 38 | } 39 | 40 | public String getProjectName() { 41 | return this.projectNameTextField.getText(); 42 | } 43 | 44 | /** 45 | * This method is called from within the constructor to initialize the form. 46 | * WARNING: Do NOT modify this code. The content of this method is always 47 | * regenerated by the Form Editor. 48 | */ 49 | // //GEN-BEGIN:initComponents 50 | private void initComponents() { 51 | 52 | projectNameLabel = new javax.swing.JLabel(); 53 | projectNameTextField = new javax.swing.JTextField(); 54 | projectLocationLabel = new javax.swing.JLabel(); 55 | projectLocationTextField = new javax.swing.JTextField(); 56 | browseButton = new javax.swing.JButton(); 57 | createdFolderLabel = new javax.swing.JLabel(); 58 | createdFolderTextField = new javax.swing.JTextField(); 59 | 60 | projectNameLabel.setLabelFor(projectNameTextField); 61 | org.openide.awt.Mnemonics.setLocalizedText(projectNameLabel, org.openide.util.NbBundle.getMessage(CoffeeScriptApplicationPanelVisual.class, "CoffeeScriptApplicationPanelVisual.projectNameLabel.text")); // NOI18N 62 | 63 | projectLocationLabel.setLabelFor(projectLocationTextField); 64 | org.openide.awt.Mnemonics.setLocalizedText(projectLocationLabel, org.openide.util.NbBundle.getMessage(CoffeeScriptApplicationPanelVisual.class, "CoffeeScriptApplicationPanelVisual.projectLocationLabel.text")); // NOI18N 65 | 66 | org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(CoffeeScriptApplicationPanelVisual.class, "CoffeeScriptApplicationPanelVisual.browseButton.text")); // NOI18N 67 | browseButton.setActionCommand(org.openide.util.NbBundle.getMessage(CoffeeScriptApplicationPanelVisual.class, "CoffeeScriptApplicationPanelVisual.browseButton.actionCommand")); // NOI18N 68 | browseButton.addActionListener(new java.awt.event.ActionListener() { 69 | public void actionPerformed(java.awt.event.ActionEvent evt) { 70 | browseButtonActionPerformed(evt); 71 | } 72 | }); 73 | 74 | createdFolderLabel.setLabelFor(createdFolderTextField); 75 | org.openide.awt.Mnemonics.setLocalizedText(createdFolderLabel, org.openide.util.NbBundle.getMessage(CoffeeScriptApplicationPanelVisual.class, "CoffeeScriptApplicationPanelVisual.createdFolderLabel.text")); // NOI18N 76 | 77 | createdFolderTextField.setEditable(false); 78 | 79 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); 80 | this.setLayout(layout); 81 | layout.setHorizontalGroup( 82 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 83 | .addGroup(layout.createSequentialGroup() 84 | .addContainerGap() 85 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 86 | .addComponent(projectNameLabel) 87 | .addComponent(projectLocationLabel) 88 | .addComponent(createdFolderLabel)) 89 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 90 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 91 | .addComponent(projectNameTextField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 191, Short.MAX_VALUE) 92 | .addComponent(projectLocationTextField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 191, Short.MAX_VALUE) 93 | .addComponent(createdFolderTextField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 191, Short.MAX_VALUE)) 94 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 95 | .addComponent(browseButton) 96 | .addContainerGap()) 97 | ); 98 | layout.setVerticalGroup( 99 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 100 | .addGroup(layout.createSequentialGroup() 101 | .addContainerGap() 102 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 103 | .addComponent(projectNameLabel) 104 | .addComponent(projectNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 105 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 106 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 107 | .addComponent(projectLocationLabel) 108 | .addComponent(projectLocationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 109 | .addComponent(browseButton)) 110 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 111 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 112 | .addComponent(createdFolderLabel) 113 | .addComponent(createdFolderTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 114 | .addContainerGap(213, Short.MAX_VALUE)) 115 | ); 116 | }// //GEN-END:initComponents 117 | 118 | private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed 119 | String command = evt.getActionCommand(); 120 | if ("BROWSE".equals(command)) { 121 | JFileChooser chooser = new JFileChooser(); 122 | FileUtil.preventFileChooserSymlinkTraversal(chooser, null); 123 | chooser.setDialogTitle("Select Project Location"); 124 | chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 125 | String path = this.projectLocationTextField.getText(); 126 | if (path.length() > 0) { 127 | File f = new File(path); 128 | if (f.exists()) { 129 | chooser.setSelectedFile(f); 130 | } 131 | } 132 | if (JFileChooser.APPROVE_OPTION == chooser.showOpenDialog(this)) { 133 | File projectDir = chooser.getSelectedFile(); 134 | projectLocationTextField.setText(FileUtil.normalizeFile(projectDir).getAbsolutePath()); 135 | } 136 | panel.fireChangeEvent(); 137 | } 138 | 139 | }//GEN-LAST:event_browseButtonActionPerformed 140 | // Variables declaration - do not modify//GEN-BEGIN:variables 141 | private javax.swing.JButton browseButton; 142 | private javax.swing.JLabel createdFolderLabel; 143 | private javax.swing.JTextField createdFolderTextField; 144 | private javax.swing.JLabel projectLocationLabel; 145 | private javax.swing.JTextField projectLocationTextField; 146 | private javax.swing.JLabel projectNameLabel; 147 | private javax.swing.JTextField projectNameTextField; 148 | // End of variables declaration//GEN-END:variables 149 | 150 | @Override 151 | public void addNotify() { 152 | super.addNotify(); 153 | //same problem as in 31086, initial focus on Cancel button 154 | projectNameTextField.requestFocus(); 155 | } 156 | 157 | boolean valid(WizardDescriptor wizardDescriptor) { 158 | 159 | if (projectNameTextField.getText().length() == 0) { 160 | // TODO if using org.openide.dialogs >= 7.8, can use WizardDescriptor.PROP_ERROR_MESSAGE: 161 | wizardDescriptor.putProperty("WizardPanel_errorMessage", 162 | "Project Name is not a valid folder name."); 163 | return false; // Display name not specified 164 | } 165 | File f = FileUtil.normalizeFile(new File(projectLocationTextField.getText()).getAbsoluteFile()); 166 | if (!f.isDirectory()) { 167 | String message = "Project Folder is not a valid path."; 168 | wizardDescriptor.putProperty("WizardPanel_errorMessage", message); 169 | return false; 170 | } 171 | final File destFolder = FileUtil.normalizeFile(new File(createdFolderTextField.getText()).getAbsoluteFile()); 172 | 173 | File projLoc = destFolder; 174 | while (projLoc != null && !projLoc.exists()) { 175 | projLoc = projLoc.getParentFile(); 176 | } 177 | if (projLoc == null || !projLoc.canWrite()) { 178 | wizardDescriptor.putProperty("WizardPanel_errorMessage", 179 | "Project Folder cannot be created."); 180 | return false; 181 | } 182 | 183 | if (FileUtil.toFileObject(projLoc) == null) { 184 | String message = "Project Folder is not a valid path."; 185 | wizardDescriptor.putProperty("WizardPanel_errorMessage", message); 186 | return false; 187 | } 188 | 189 | File[] kids = destFolder.listFiles(); 190 | if (destFolder.exists() && kids != null && kids.length > 0) { 191 | // Folder exists and is not empty 192 | wizardDescriptor.putProperty("WizardPanel_errorMessage", 193 | "Project Folder already exists and is not empty."); 194 | return false; 195 | } 196 | wizardDescriptor.putProperty("WizardPanel_errorMessage", ""); 197 | return true; 198 | } 199 | 200 | void store(WizardDescriptor d) { 201 | String name = projectNameTextField.getText().trim(); 202 | String folder = createdFolderTextField.getText().trim(); 203 | 204 | d.putProperty("projdir", new File(folder)); 205 | d.putProperty("name", name); 206 | } 207 | 208 | void read(WizardDescriptor settings) { 209 | File projectLocation = (File) settings.getProperty("projdir"); 210 | if (projectLocation == null || projectLocation.getParentFile() == null || !projectLocation.getParentFile().isDirectory()) { 211 | projectLocation = ProjectChooser.getProjectsFolder(); 212 | } else { 213 | projectLocation = projectLocation.getParentFile(); 214 | } 215 | this.projectLocationTextField.setText(projectLocation.getAbsolutePath()); 216 | 217 | String projectName = (String) settings.getProperty("name"); 218 | if (projectName == null) { 219 | projectName = "CoffeeScriptApplication"; 220 | } 221 | this.projectNameTextField.setText(projectName); 222 | this.projectNameTextField.selectAll(); 223 | } 224 | 225 | void validate(WizardDescriptor d) throws WizardValidationException { 226 | // nothing to validate 227 | } 228 | 229 | // Implementation of DocumentListener -------------------------------------- 230 | public void changedUpdate(DocumentEvent e) { 231 | updateTexts(e); 232 | if (this.projectNameTextField.getDocument() == e.getDocument()) { 233 | firePropertyChange(PROP_PROJECT_NAME, null, this.projectNameTextField.getText()); 234 | } 235 | } 236 | 237 | public void insertUpdate(DocumentEvent e) { 238 | updateTexts(e); 239 | if (this.projectNameTextField.getDocument() == e.getDocument()) { 240 | firePropertyChange(PROP_PROJECT_NAME, null, this.projectNameTextField.getText()); 241 | } 242 | } 243 | 244 | public void removeUpdate(DocumentEvent e) { 245 | updateTexts(e); 246 | if (this.projectNameTextField.getDocument() == e.getDocument()) { 247 | firePropertyChange(PROP_PROJECT_NAME, null, this.projectNameTextField.getText()); 248 | } 249 | } 250 | 251 | /** 252 | * Handles changes in the Project name and project directory, 253 | */ 254 | private void updateTexts(DocumentEvent e) { 255 | 256 | Document doc = e.getDocument(); 257 | 258 | if (doc == projectNameTextField.getDocument() || doc == projectLocationTextField.getDocument()) { 259 | // Change in the project name 260 | 261 | String projectName = projectNameTextField.getText(); 262 | String projectFolder = projectLocationTextField.getText(); 263 | 264 | //if (projectFolder.trim().length() == 0 || projectFolder.equals(oldName)) { 265 | createdFolderTextField.setText(projectFolder + File.separatorChar + projectName); 266 | //} 267 | 268 | } 269 | panel.fireChangeEvent(); // Notify that the panel changed 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/sample/CoffeeScriptApplicationProjectDescription.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | A simple CoffeeScript application using Cakefile. 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/sample/CoffeeScriptApplicationWizardIterator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project.sample; 15 | 16 | import java.awt.Component; 17 | import java.io.ByteArrayInputStream; 18 | import java.io.ByteArrayOutputStream; 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.text.MessageFormat; 24 | import java.util.Enumeration; 25 | import java.util.LinkedHashSet; 26 | import java.util.NoSuchElementException; 27 | import java.util.Set; 28 | import java.util.zip.ZipEntry; 29 | import java.util.zip.ZipInputStream; 30 | import javax.swing.JComponent; 31 | import javax.swing.event.ChangeListener; 32 | import org.netbeans.api.project.ProjectManager; 33 | import org.netbeans.spi.project.ui.support.ProjectChooser; 34 | import org.netbeans.spi.project.ui.templates.support.Templates; 35 | import org.openide.WizardDescriptor; 36 | import org.openide.filesystems.FileObject; 37 | import org.openide.filesystems.FileUtil; 38 | import org.openide.util.Exceptions; 39 | import org.openide.util.NbBundle; 40 | import org.openide.xml.XMLUtil; 41 | import org.w3c.dom.Document; 42 | import org.w3c.dom.Element; 43 | import org.w3c.dom.NodeList; 44 | import org.xml.sax.InputSource; 45 | 46 | public class CoffeeScriptApplicationWizardIterator implements WizardDescriptor./* 47 | * Progress 48 | */InstantiatingIterator { 49 | 50 | private int index; 51 | private WizardDescriptor.Panel[] panels; 52 | private WizardDescriptor wiz; 53 | 54 | public CoffeeScriptApplicationWizardIterator() { 55 | } 56 | 57 | public static CoffeeScriptApplicationWizardIterator createIterator() { 58 | return new CoffeeScriptApplicationWizardIterator(); 59 | } 60 | 61 | private WizardDescriptor.Panel[] createPanels() { 62 | return new WizardDescriptor.Panel[]{ 63 | new CoffeeScriptApplicationWizardPanel(),}; 64 | } 65 | 66 | private String[] createSteps() { 67 | return new String[]{ 68 | NbBundle.getMessage(CoffeeScriptApplicationWizardIterator.class, "LBL_CreateProjectStep") 69 | }; 70 | } 71 | 72 | public Set/* 73 | * 74 | */ instantiate(/* 75 | * ProgressHandle handle 76 | */) throws IOException { 77 | Set resultSet = new LinkedHashSet(); 78 | File dirF = FileUtil.normalizeFile((File) wiz.getProperty("projdir")); 79 | dirF.mkdirs(); 80 | 81 | FileObject template = Templates.getTemplate(wiz); 82 | FileObject dir = FileUtil.toFileObject(dirF); 83 | unZipFile(template.getInputStream(), dir); 84 | 85 | // Always open top dir as a project: 86 | resultSet.add(dir); 87 | // Look for nested projects to open as well: 88 | Enumeration e = dir.getFolders(true); 89 | while (e.hasMoreElements()) { 90 | FileObject subfolder = e.nextElement(); 91 | if (ProjectManager.getDefault().isProject(subfolder)) { 92 | resultSet.add(subfolder); 93 | } 94 | } 95 | 96 | File parent = dirF.getParentFile(); 97 | if (parent != null && parent.exists()) { 98 | ProjectChooser.setProjectsFolder(parent); 99 | } 100 | 101 | return resultSet; 102 | } 103 | 104 | public void initialize(WizardDescriptor wiz) { 105 | this.wiz = wiz; 106 | index = 0; 107 | panels = createPanels(); 108 | // Make sure list of steps is accurate. 109 | String[] steps = createSteps(); 110 | for (int i = 0; i < panels.length; i++) { 111 | Component c = panels[i].getComponent(); 112 | if (steps[i] == null) { 113 | // Default step name to component name of panel. 114 | // Mainly useful for getting the name of the target 115 | // chooser to appear in the list of steps. 116 | steps[i] = c.getName(); 117 | } 118 | if (c instanceof JComponent) { // assume Swing components 119 | JComponent jc = (JComponent) c; 120 | // Step #. 121 | // TODO if using org.openide.dialogs >= 7.8, can use WizardDescriptor.PROP_*: 122 | jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i)); 123 | // Step name (actually the whole list for reference). 124 | jc.putClientProperty("WizardPanel_contentData", steps); 125 | } 126 | } 127 | } 128 | 129 | public void uninitialize(WizardDescriptor wiz) { 130 | this.wiz.putProperty("projdir", null); 131 | this.wiz.putProperty("name", null); 132 | this.wiz = null; 133 | panels = null; 134 | } 135 | 136 | public String name() { 137 | return MessageFormat.format("{0} of {1}", 138 | new Object[]{new Integer(index + 1), new Integer(panels.length)}); 139 | } 140 | 141 | public boolean hasNext() { 142 | return index < panels.length - 1; 143 | } 144 | 145 | public boolean hasPrevious() { 146 | return index > 0; 147 | } 148 | 149 | public void nextPanel() { 150 | if (!hasNext()) { 151 | throw new NoSuchElementException(); 152 | } 153 | index++; 154 | } 155 | 156 | public void previousPanel() { 157 | if (!hasPrevious()) { 158 | throw new NoSuchElementException(); 159 | } 160 | index--; 161 | } 162 | 163 | public WizardDescriptor.Panel current() { 164 | return panels[index]; 165 | } 166 | 167 | // If nothing unusual changes in the middle of the wizard, simply: 168 | public final void addChangeListener(ChangeListener l) { 169 | } 170 | 171 | public final void removeChangeListener(ChangeListener l) { 172 | } 173 | 174 | private static void unZipFile(InputStream source, FileObject projectRoot) throws IOException { 175 | try { 176 | ZipInputStream str = new ZipInputStream(source); 177 | ZipEntry entry; 178 | while ((entry = str.getNextEntry()) != null) { 179 | if (entry.isDirectory()) { 180 | FileUtil.createFolder(projectRoot, entry.getName()); 181 | } else { 182 | FileObject fo = FileUtil.createData(projectRoot, entry.getName()); 183 | if ("nbproject/project.xml".equals(entry.getName())) { 184 | // Special handling for setting name of Ant-based projects; customize as needed: 185 | filterProjectXML(fo, str, projectRoot.getName()); 186 | } else { 187 | writeFile(str, fo); 188 | } 189 | } 190 | } 191 | } finally { 192 | source.close(); 193 | } 194 | } 195 | 196 | private static void writeFile(ZipInputStream str, FileObject fo) throws IOException { 197 | OutputStream out = fo.getOutputStream(); 198 | try { 199 | FileUtil.copy(str, out); 200 | } finally { 201 | out.close(); 202 | } 203 | } 204 | 205 | private static void filterProjectXML(FileObject fo, ZipInputStream str, String name) throws IOException { 206 | try { 207 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 208 | FileUtil.copy(str, baos); 209 | Document doc = XMLUtil.parse(new InputSource(new ByteArrayInputStream(baos.toByteArray())), false, false, null, null); 210 | NodeList nl = doc.getDocumentElement().getElementsByTagName("name"); 211 | if (nl != null) { 212 | for (int i = 0; i < nl.getLength(); i++) { 213 | Element el = (Element) nl.item(i); 214 | if (el.getParentNode() != null && "data".equals(el.getParentNode().getNodeName())) { 215 | NodeList nl2 = el.getChildNodes(); 216 | if (nl2.getLength() > 0) { 217 | nl2.item(0).setNodeValue(name); 218 | } 219 | break; 220 | } 221 | } 222 | } 223 | OutputStream out = fo.getOutputStream(); 224 | try { 225 | XMLUtil.write(doc, out, "UTF-8"); 226 | } finally { 227 | out.close(); 228 | } 229 | } catch (Exception ex) { 230 | Exceptions.printStackTrace(ex); 231 | writeFile(str, fo); 232 | } 233 | 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/sample/CoffeeScriptApplicationWizardPanel.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project.sample; 15 | 16 | import java.awt.Component; 17 | import java.util.HashSet; 18 | import java.util.Set; 19 | import javax.swing.event.ChangeEvent; 20 | import javax.swing.event.ChangeListener; 21 | import org.openide.WizardDescriptor; 22 | import org.openide.WizardValidationException; 23 | import org.openide.util.HelpCtx; 24 | import org.openide.util.NbBundle; 25 | 26 | /** 27 | * Panel just asking for basic info. 28 | */ 29 | public class CoffeeScriptApplicationWizardPanel implements WizardDescriptor.Panel, 30 | WizardDescriptor.ValidatingPanel, WizardDescriptor.FinishablePanel { 31 | 32 | private WizardDescriptor wizardDescriptor; 33 | private CoffeeScriptApplicationPanelVisual component; 34 | 35 | public CoffeeScriptApplicationWizardPanel() { 36 | } 37 | 38 | public Component getComponent() { 39 | if (component == null) { 40 | component = new CoffeeScriptApplicationPanelVisual(this); 41 | component.setName(NbBundle.getMessage(CoffeeScriptApplicationWizardPanel.class, "LBL_CreateProjectStep")); 42 | } 43 | return component; 44 | } 45 | 46 | public HelpCtx getHelp() { 47 | return new HelpCtx(CoffeeScriptApplicationWizardPanel.class); 48 | } 49 | 50 | public boolean isValid() { 51 | getComponent(); 52 | return component.valid(wizardDescriptor); 53 | } 54 | private final Set listeners = new HashSet(1); // or can use ChangeSupport in NB 6.0 55 | 56 | public final void addChangeListener(ChangeListener l) { 57 | synchronized (listeners) { 58 | listeners.add(l); 59 | } 60 | } 61 | 62 | public final void removeChangeListener(ChangeListener l) { 63 | synchronized (listeners) { 64 | listeners.remove(l); 65 | } 66 | } 67 | 68 | protected final void fireChangeEvent() { 69 | Set ls; 70 | synchronized (listeners) { 71 | ls = new HashSet(listeners); 72 | } 73 | ChangeEvent ev = new ChangeEvent(this); 74 | for (ChangeListener l : ls) { 75 | l.stateChanged(ev); 76 | } 77 | } 78 | 79 | public void readSettings(Object settings) { 80 | wizardDescriptor = (WizardDescriptor) settings; 81 | component.read(wizardDescriptor); 82 | } 83 | 84 | public void storeSettings(Object settings) { 85 | WizardDescriptor d = (WizardDescriptor) settings; 86 | component.store(d); 87 | } 88 | 89 | public boolean isFinishPanel() { 90 | return true; 91 | } 92 | 93 | public void validate() throws WizardValidationException { 94 | getComponent(); 95 | component.validate(wizardDescriptor); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/ui/CoffeeScriptActionSettingsPanel.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 | -------------------------------------------------------------------------------- /src/coffeescript/nb/project/ui/CoffeeScriptActionSettingsPanel.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Denis Stepanov 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package coffeescript.nb.project.ui; 15 | 16 | /** 17 | * 18 | * @author Denis Stepanov 19 | */ 20 | public class CoffeeScriptActionSettingsPanel extends javax.swing.JPanel { 21 | 22 | public CoffeeScriptActionSettingsPanel() { 23 | initComponents(); 24 | } 25 | 26 | @SuppressWarnings("unchecked") 27 | // //GEN-BEGIN:initComponents 28 | private void initComponents() 29 | { 30 | 31 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); 32 | this.setLayout(layout); 33 | layout.setHorizontalGroup( 34 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 35 | .addGap(0, 500, Short.MAX_VALUE) 36 | ); 37 | layout.setVerticalGroup( 38 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 39 | .addGap(0, 362, Short.MAX_VALUE) 40 | ); 41 | }// //GEN-END:initComponents 42 | // Variables declaration - do not modify//GEN-BEGIN:variables 43 | // End of variables declaration//GEN-END:variables 44 | } 45 | -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/Bundle.properties: -------------------------------------------------------------------------------- 1 | OpenIDE-Module-Display-Category=Editing 2 | OpenIDE-Module-Long-Description=\ 3 |

      CoffeeScript editor features:

      \n\n
        \n
      • \nJust works! \ 4 | (No need for additional dependencies)\n
      • \n
      • \nAdvanced syntax highlighting\n
      • \n
      • \nError checking by background compiling (Rhino JavaScript engine is used to compile *.coffee files)\n
      • \n
      • \nAutocompiling to a JavaScript file in the same directory\n
      • \n
      • \nSyntax highlighting and error checking in scripts (HTML and PHP files)\n
      • \n
      • \nIndentation & formatting, code folding\n
      • \n
      • \nOption to use Node.js with 'coffee' command for compiling. \ 5 | Supported platforms: Mac, Linux and Windows (new experimenting support)\n
      • \n
      • \nCakefile project support\n
      • \n
      6 | OpenIDE-Module-Name=CoffeeScript Netbeans 7 | OpenIDE-Module-Short-Description=CoffeeScript editor 8 | Services/MIMEResolver/CoffeeScriptResolver.xml=CoffeeScript Files 9 | Templates/Other/CoffeeScriptTemplate.coffee=Empty CoffeeScript file 10 | Templates/Project/CoffeeScript/CoffeeScriptApplicationProject.zip=CoffeeScript Application 11 | text/coffeescript=CoffeeScript 12 | CoffeeScriptAction=CoffeeScript 13 | 14 | error=Error 15 | regexp=Regular Expression 16 | comment=Comment 17 | identifier=Identifier 18 | keyword=Keyword 19 | whitespace=Whitespace 20 | number=Number 21 | operator=Operator 22 | string=String 23 | separator=Separator 24 | field=Class method or field 25 | 26 | CoffeeScriptOptions.displayName=CoffeeScript 27 | KW_CoffeeScriptOptions=CoffeeScript Options 28 | CoffeeScriptOptionsPanel.bareCheckbox.text=Compile the JavaScript without the top-level function safety wrapper 29 | -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/CoffeeScriptApplicationProject.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstepanov/coffeescript-netbeans/1af2eee5a4ad08b2db328274a001311732a46798/src/coffeescript/nb/resources/CoffeeScriptApplicationProject.zip -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/CoffeeScriptPreview.coffee: -------------------------------------------------------------------------------- 1 | # Assignment: 2 | number = 42 3 | opposite = true 4 | 5 | # Conditions: 6 | number = -42 if opposite 7 | 8 | # Functions: 9 | square = (x) -> x * x 10 | 11 | # Arrays: 12 | list = [1, 2, 3, 4, 5] 13 | 14 | # Objects: 15 | math = 16 | root: Math.sqrt 17 | square: square 18 | cube: (x) -> x * square x 19 | 20 | # Splats: 21 | race = (winner, runners...) -> 22 | print winner, runners 23 | 24 | # Existence: 25 | alert "I knew it!" if elvis? 26 | 27 | # Array comprehensions: 28 | cubes = (math.cube num for num in list) -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/CoffeeScriptTemplate.coffee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstepanov/coffeescript-netbeans/1af2eee5a4ad08b2db328274a001311732a46798/src/coffeescript/nb/resources/CoffeeScriptTemplate.coffee -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/FontAndColors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/coffeescript-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstepanov/coffeescript-netbeans/1af2eee5a4ad08b2db328274a001311732a46798/src/coffeescript/nb/resources/coffeescript-icon.png -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/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 | 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 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstepanov/coffeescript-netbeans/1af2eee5a4ad08b2db328274a001311732a46798/src/coffeescript/nb/resources/refresh.png -------------------------------------------------------------------------------- /src/coffeescript/nb/resources/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstepanov/coffeescript-netbeans/1af2eee5a4ad08b2db328274a001311732a46798/src/coffeescript/nb/resources/stop.png --------------------------------------------------------------------------------