├── .gitignore ├── .travis.yml ├── COPYING ├── Makefile ├── README.md ├── src └── com │ └── dogcows │ ├── Editor.java │ ├── Util.java │ ├── VimCoder.java │ └── resources │ ├── C++Driver │ ├── C++Makefile │ ├── C++Template │ └── JavaTemplate └── t └── 01-basic.t /.gitignore: -------------------------------------------------------------------------------- 1 | *.jar 2 | /com 3 | /lib 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | - oraclejdk7 5 | - openjdk7 6 | - openjdk6 7 | matrix: 8 | fast_finish: true 9 | allow_failures: 10 | - jdk: openjdk6 11 | install: 12 | - make fetch 13 | script: 14 | - make 15 | - make test 16 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | The Simplified BSD License 3 | 4 | Copyright (c) 2010-2013, Charles McGarvey et al. 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Use this file with GNU make to compile and package VimCoder. 3 | # Supported targets: all clean distclean dist fetch jar 4 | 5 | project = VimCoder 6 | version = 0.3.6 7 | 8 | sources = $(wildcard src/com/dogcows/*.java) 9 | classes = $(sources:src/%.java=%.class) 10 | library = lib/ContestApplet.jar 11 | jarfile = $(project)-$(version).jar 12 | resource_path = com/dogcows/resources 13 | resources = $(wildcard src/$(resource_path)/*) 14 | 15 | JAVAC = javac 16 | JAVACFLAGS = -d . -sourcepath src -classpath $(library) 17 | 18 | PROVE = prove 19 | 20 | 21 | all: $(classes) $(resources:src/%=%) 22 | 23 | clean: 24 | rm -rf com 25 | 26 | distclean: clean 27 | rm -rf lib 28 | 29 | dist: 30 | git archive HEAD --prefix=vimcoder-$(version)/ | bzip2 >vimcoder-$(version).tar.bz2 31 | 32 | fetch: $(library) 33 | 34 | jar: $(jarfile) 35 | 36 | test: all 37 | $(PROVE) 38 | 39 | 40 | $(classes): $(sources) | $(library) 41 | $(JAVAC) $(JAVACFLAGS) $^ 42 | 43 | $(resource_path): 44 | mkdir -p "$@" 45 | 46 | $(resource_path)/%: src/$(resource_path)/% | $(resource_path) 47 | cp "$<" "$@" 48 | 49 | 50 | $(library): 51 | mkdir -p lib 52 | curl -o $@ http://www.topcoder.com/contest/classes/ContestApplet.jar 53 | 54 | $(jarfile): all 55 | rm -f $@ 56 | jar cvf $@ COPYING README.md com 57 | 58 | 59 | .PHONY: all clean distclean dist fetch jar test 60 | 61 | # vim:noet:ts=8 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Vim + TopCoder = VimCoder 3 | ========================= 4 | 5 | ![VimCoder Logo](http://chazmcgarvey.github.com/vimcoder/img/vimcoder.png) 6 | 7 | This plug-in makes it easy to use [Vim](http://www.vim.org/) as your 8 | text editor in the [TopCoder Competition Arena](http://topcoder.com/tc). 9 | It aims to be minimal in comparison to other editors such as 10 | [KawigiEdit](http://topcoder.yajags.com/) or 11 | [CodeProcessor](http://www.topcoder.com/tc?module=Static&d1=applet&d2=plugins) 12 | plug-in chains while also providing enough functionality to also be useful. 13 | 14 | Features 15 | -------- 16 | 17 | * Works on any platform that the TopCoder Arena applet itself supports. 18 | * Works with any language supported by TopCoder. 19 | * Keeps track of your code files locally so you always have a copy. 20 | * Downloads and stores a copy of the problem statement with your code for 21 | off-line viewing. 22 | * Has support for simple templates (default templates provided only for C++ 23 | and Java). 24 | * Test-case "drivers" can be generated locally with the example test-case data 25 | (currently C++ only). 26 | 27 | Build Status 28 | ------------ 29 | 30 | [![Build Status](https://travis-ci.org/chazmcgarvey/vimcoder.svg?branch=master)](https://travis-ci.org/chazmcgarvey/vimcoder) 31 | 32 | License 33 | ------- 34 | 35 | This software is licensed according to the terms and conditions of the 36 | [BSD 2-Clause License](http://www.opensource.org/licenses/bsd-license.php). 37 | Please see the COPYING file for more information. 38 | This project is neither supported nor endorsed by TopCoder, Inc. 39 | 40 | Download 41 | -------- 42 | 43 | The latest VimCoder jar file can be downloaded from the 44 | [vim.org script page](http://www.vim.org/scripts/script.php?script_id=3321). 45 | 46 | Install 47 | ------- 48 | 49 | Unfortunately, installation is a bit cumbersome, but it is what it is: 50 | 51 | 1. Download the latest version of the VimCoder jar file. 52 | 2. Run the TopCoder Arena applet and log in. 53 | 3. Click the "Options" menu and select "Editor" to show the editor 54 | preferences. 55 | 4. Click the "Add" button to bring up a new window. 56 | 5. For "Name," type "Vim" or whatever you want to represent this plug-in. 57 | 6. For "EntryPoint," type "com.dogcows.VimCoder" without the quotes. 58 | 7. For "ClassPath," click on "Browse" and locate the VimCoder jar file. The 59 | third field should now have the path to the jar file. 60 | 8. Click "OK" to close the window with the three fields. 61 | 9. Click "Save." 62 | 63 | You should now be able select "Vim" (or whatever you entered into the first 64 | field) as your editor from the pull-down list on any problem statement window. 65 | 66 | Configure 67 | --------- 68 | 69 | Depending on your preference or system attributes, you may want or need to 70 | first configure the plug-in so that it will work how you want it to. You can 71 | bring up the plug-in preferences window by following these steps: 72 | 73 | 1. Run the TopCoder Arena applet and log in. 74 | 2. Click the "Options" menu and select "Editor." 75 | 3. In the new window, make sure the entry for VimCoder is selected from the 76 | list, and click the "Configure" button. 77 | 78 | ![VimCoder Preferences](http://chazmcgarvey.github.com/vimcoder/img/prefs.png) 79 | 80 | ##### Storage Directory 81 | 82 | VimCoder saves the problem files and the code you're working on in 83 | a particular directory you can set. By default, this directory is `.vimcoder` 84 | in your home directory. This is an "invisible" directory on most systems. 85 | Within this storage directory are several sub-directories, one for each 86 | problem you open. Each sub-directory is named after the problem identifier 87 | and contains your source code and other files related to the problem. 88 | 89 | If you want to change the storage directory, click the "Browse" button in the 90 | VimCoder preferences window and navigate to the directory you would like to 91 | use. If you have already saved some problems to the previous storage 92 | directory, you may also want to actually move the directory to the new 93 | location so that VimCoder can find the work you've already done. 94 | 95 | Beginning with VimCoder 0.3.5, there is a new option for an alternative 96 | directory structure. It is not enabled by default, but it may be in the 97 | future. Rather than having directories named after problem identifiers, the 98 | new structure uses two levels of directories. On the first level, directories 99 | are named after the contest associated with the problem (e.g. SRM-144-DIV-1), 100 | and on the second level, directories are named after the problem's point value 101 | (e.g. 300). This directory structure may be preferable if you ever want to 102 | browse your repository since the contest name and point values are more easily 103 | identifiable than the problem identifier. 104 | 105 | If this new directory structure is enabled, it will only apply to new 106 | problems. VimCoder will not try to reorganize your current repository, though 107 | you are welcome to do it manually yourself if you would like to switch to the 108 | new directory structure. 109 | 110 | ##### Vim Command 111 | 112 | By default, VimCoder tries to invoke Vim using the `gvim` command (or 113 | `C:\WINDOWS\gvim.bat` on Windows). This will typically work just fine unless 114 | you don't have gvim in your PATH (or your installation of Vim on Windows 115 | didn't include the wrappers for the command line). If you get errors about 116 | the vim process not being able to run and no Vim session comes up when you use 117 | the VimCoder plug-in, you need to either make sure the Vim command exists in 118 | your PATH, or else change the Vim command in the VimCoder preferences window 119 | to something else. 120 | 121 | You may use an absolute path to your vim executable, such as 122 | `/usr/local/bin/gvim` or `C:\Program Files\Vim\vim73\gvim.exe` 123 | or wherever your actual Vim executable is. You may also invoke vim through 124 | some other command (e.g. `xterm -e vim` or `gnome-terminal -e vim --`). 125 | 126 | The xterm example above demonstrates using Vim without the GUI, running in 127 | a terminal emulator. You can enter any elaborate command you want as long as 128 | Vim ultimately gets executed with the arguments that will be appended to the 129 | command when it is invoked. After changing this value and saving your 130 | preferences, the command you enter will be used the next time you open 131 | a problem. 132 | 133 | Usage 134 | ----- 135 | 136 | To use VimCoder once it is installed and configured, go to a room in the 137 | TopCoder Arena applet and open one of the problems. If you have set VimCoder 138 | as your default editor, you will see the usual problem statement window come 139 | up as well as a separate Vim editor window. Otherwise, you can change the 140 | editor from the problem statement window, and the Vim editor window will come 141 | up. You will see that the area usually devoted to editor will be used for log 142 | messages; you will do your actual coding in the Vim window that comes up. 143 | 144 | Just enter your code into the Vim window and use the regular TopCoder Arena 145 | applet buttons to compile, test, and submit your code. 146 | 147 | **Pro Tip:** If you accidentally close your Vim session, you can get it back 148 | by switching to a different editor (such as the default editor) and then 149 | switching back to VimCoder. Alternatively, the session will also reappear 150 | (and load a buffer to a different source code file) if you switch languages. 151 | 152 | Storage Directory Structure 153 | --------------------------- 154 | 155 | Knowing about the files created by VimCoder is useful if you ever need to do 156 | anything advanced. When you open a problem, VimCoder will check to see if you 157 | have already opened that problem by looking for the problem and solution 158 | files. If these files are found, it will load your previous work. Otherwise, 159 | it will fill out the templates based on the problem class name, parameter 160 | types, and so on, and will create several files in a sub-directory of the main 161 | storage directory: 162 | 163 | ##### `$CLASSNAME$`.`$LANGUAGE$` 164 | 165 | This is the file where you write your solution code. If the class name for 166 | the problem was BinaryCode and your language was Java, the name of this file 167 | would be `BinaryCode.java`. When you open a problem, Vim will load this file 168 | into a new buffer so that you can start coding. If there is a template for 169 | the language you're using, that template will be used to fill in this file to 170 | give you a reasonable place to start. When you save your code to TopCoder or 171 | compile remotely, this is also the file that will be read to provide the code 172 | for your solution. 173 | 174 | ##### testcases.txt 175 | 176 | This file contains the example test cases that are associated with the 177 | problem. The format is pretty simple. For each test case, there is one line 178 | for the expected return value followed by the inputs (i.e. the method 179 | arguments), in order, each on its own line. The format of this file is meant 180 | to be easy for a human to write and easy for a program to read so that 181 | a driver program (more on this later) can easily be written to run the test 182 | cases against your code. 183 | 184 | While you are coding a solution, you may want to open this file in a new 185 | buffer (type ":e testcases.txt") and add additional test cases to make sure 186 | your code doesn't mess up on edge cases for which an example test case was not 187 | provided. 188 | 189 | ##### Problem.html 190 | 191 | This file contains the problem statement which is what you see in the top half 192 | of the problem window. You can load this in a browser to read the particulars 193 | of the problem when you aren't running the TopCoder Arena applet. You 194 | typically shouldn't edit this file, but it's up to you. 195 | 196 | ##### Makefile 197 | 198 | If there exists a Makefile template for the selected language, it will also be 199 | filled out and saved in the problem directory. The purpose of the Makefile is 200 | to compile your code locally. You can execute targets in the Makefile using 201 | Vim's `:make` command. You also shouldn't need to edit this file directly, 202 | but of course you can if the need does arise. Exactly what happens when you 203 | use the `:make` command depends on the Makefile template. 204 | 205 | If you are using the default Makefile template for C++, typing ":make" without 206 | any arguments will compile your code. Typing ":make run" will run all of the 207 | test cases against your code. Typing ":make test" will also run the test 208 | cases against your code, except it will abort at the first failed test. 209 | 210 | A Makefile template is not yet provided for any other language, but you can 211 | write one yourself if you are so inclined. Read on to learn how templates 212 | work. 213 | 214 | **Important:** Make sure you understand the difference between compiling 215 | locally and compiling remotely (on the TopCoder servers). If you use the 216 | Makefile to compile your solution (and maybe run the tests), you are not 217 | interacting with the TopCoder servers at all. When you compile *remotely*, 218 | you are sending a snapshot of your current solution to the servers for 219 | processing. The distinction becomes important when it comes time for you to 220 | submit your solution. When you push the "Submit" button, you are submitting 221 | the **last version that was uploaded to the TopCoder servers** (by compiling 222 | remotely), and that may be different from what is currently in your Vim 223 | buffer, even if your Vim buffer was saved to disk. Therefore, it is very 224 | important that you get into the habit of always pushing the "Compile" button 225 | right before you submit your code. This point can't be overemphasized. 226 | 227 | ##### driver.`$LANGUAGE$` 228 | 229 | If there exists a driver template for the selected language, it will also be 230 | filled out and saved in the problem directory. If the language was currently 231 | set to C++, the driver code would be in the driver.cc file. You normally 232 | don't have to do anything with this file. It just provides supporting code 233 | for running the test cases against your code. 234 | 235 | The driver should output TAP (Test Anything Protocol) so that tests can be run 236 | in a test harness such as [prove](http://search.cpan.org/perldoc?prove). The 237 | default Makefile template has a `prove` target (type ":make prove") that can 238 | run the tests in a test harness; the test harness is `prove` unless otherwise 239 | configured. TAP output is also very human-readable all by itself, so having 240 | a test harness isn't really required. 241 | 242 | A default driver template is currently only provided for the C++ language. 243 | You could write your own template if you wanted to. 244 | 245 | ##### `$CLASSNAME$` 246 | 247 | Sometimes the TopCoder Arena applet will pass back what source code it has 248 | saved. This will be saved in a file named after the class, without any file 249 | extension. You can open this file if you need to access this code for any 250 | reason (say, you messed up some code and need to revert back to the last time 251 | you saved from the TopCoder Arena applet). 252 | 253 | Templates 254 | --------- 255 | 256 | VimCoder comes with default templates for C++ and Java, but you can create 257 | your own customized templates for any language supported by TopCoder. To use 258 | your own template, you need to add a file to the storage directory with a file 259 | name depending on the language. The file name should start with the name of 260 | the language and end with "Template" with no file extension. For example, if 261 | you wanted to create a C# template and your storage directory was 262 | `/home/foo/.vimcoder`, you would need to create the file 263 | `/home/foo/.vimcoder/C#Template`. 264 | 265 | A template is like a regular source code file with special keywords that will 266 | be replaced as the template is "filled out" whenever you open a new problem. 267 | Keywords are surrounded by two dollar signs so they're not confused with other 268 | parts of the source code. The template expansion process is rather 269 | simplistic, so if you can't get the right format for the terms you need, you 270 | might have to change the plug-in source code to get the effect you're trying 271 | to achieve. Here are the possible keywords and replacement terms: 272 | 273 | ##### `$CLASSNAME$` 274 | 275 | This keyword is replaced by the name of the class you must use in your 276 | solution to the problem. 277 | 278 | ##### `$METHODNAME$` 279 | 280 | This keyword is replaced by the name of the public method your class needs to 281 | have. 282 | 283 | ##### `$RETURNTYPE$` 284 | 285 | This keyword is replaced by the type of the return variable of your public 286 | method. 287 | 288 | ##### `$METHODPARAMS$` 289 | 290 | This keyword is replaced by a comma-separated list of method parameter types 291 | and names. 292 | 293 | ---- 294 | 295 | Other keywords are also available, but the rest are intended to be used in 296 | driver or Makefile templates, though any keyword can be used in any type of 297 | template. You can create other types of templates by adding specially-named 298 | files to the storage directory. Driver templates are named starting with the 299 | name of the language and ending with "Driver" with no file extension. 300 | Similarly, Makefile templates are named starting with the name of the language 301 | and ending with "Makefile" with no file extension. 302 | 303 | Drivers provide additional code that allows the test cases to be run against 304 | your solution. Currently, Makefile and driver templates are only provided for 305 | the C++ language. Makefiles should have the commands needed to compile the 306 | solution source code and/or make a driver program that will perform the tests. 307 | If you want automatic building and testing for one of the other languages, you 308 | will need to create a driver and Makefile template for that language. Here 309 | are more keywords that may be useful for these types of templates: 310 | 311 | ##### `$METHODPARAMDECLARES$` 312 | 313 | This keyword is replaced by C-style declarations of the method parameters. In 314 | other words, each parameter is declared with its type on its own line 315 | terminated by a semicolon. 316 | 317 | ##### `$METHODPARAMNAMES$` 318 | 319 | This keyword is replaced by a comma-separated list of only the method 320 | parameter names. 321 | 322 | ##### `$METHODPARAMSTREAMOUT$` 323 | 324 | This keyword is replaced by a list of the method parameter names separated by 325 | the C++ output stream operator (<<). The C++ driver template uses this to 326 | display the input values of the test case data. 327 | 328 | ##### `$METHODPARAMSTREAMIN$` 329 | 330 | This keyword is replaced by a list of the method parameter names separated by 331 | the C++ input stream operator (>>). The C++ driver template uses this to read 332 | in the test case data from testcases.txt. 333 | 334 | ---- 335 | 336 | To give you an idea of how this all fits together, here is an example template 337 | for Java, similar to the built-in default Java template: 338 | 339 | ```java 340 | import static java.lang.Math.*; 341 | import static java.math.BigInteger.*; 342 | import static java.util.Arrays.*; 343 | import static java.util.Collections.*; 344 | import java.math.*; 345 | import java.util.*; 346 | 347 | public class $CLASSNAME$ { 348 | public $RETURNTYPE$ $METHODNAME$($METHODPARAMS$) { 349 | } 350 | } 351 | ``` 352 | 353 | Notice that it looks just like regular code but has some keywords surrounded 354 | by dollar signs that will be expanded to real values. Something like this 355 | could be saved in a filed named `JavaTemplate` in your VimCoder storage 356 | directory. 357 | 358 | Potential Pitfalls 359 | ------------------ 360 | 361 | ##### Vim Client/Server 362 | 363 | VimCoder requires Vim's client/server feature in order to work. If the log is 364 | showing errors with the invocation of Vim or if it's just not working and has 365 | other strange symptoms, make sure your version of Vim supports the 366 | client/server feature. If you are unsure, use Vim's `:version` command and 367 | look for "+clientserver" in the output. If you see "-clientserver" instead, 368 | then you'll need to get yourself another version of Vim. 369 | 370 | I think this feature was introduced in Vim 6.x, but I haven't done any testing 371 | with any versions of Vim less than 7.2. If you're still on 6.x, you should 372 | really upgrade anyway. 373 | 374 | ##### Vim Settings Not Applied 375 | 376 | The problem is that sometimes your settings (in your vimrc file) are not being 377 | applied as you would expect. This may be because you are using `setlocal` in 378 | your vimrc file rather than `set`. The `setlocal` command applies settings 379 | only to the current buffer or window (see `:help setlocal` for more 380 | information), but VimCoder works by first launching Vim and then loading 381 | a brand new buffer. 382 | 383 | The solution is to consider whether or not such settings should actually be 384 | global; if they should be global, change `setlocal` to `set` in your vimrc 385 | file. Alternatively, if you want certain settings to be set only for certain 386 | kinds of buffers, you can use the `autocmd` command to selectively set 387 | settings according to file path pattern and various events. 388 | See `:help autocmd` for more information. 389 | 390 | -------------------------------------------------------------------------------- /src/com/dogcows/Editor.java: -------------------------------------------------------------------------------- 1 | 2 | package com.dogcows; 3 | 4 | import java.io.File; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.util.*; 8 | 9 | import com.topcoder.client.contestant.ProblemComponentModel; 10 | import com.topcoder.shared.language.Language; 11 | import com.topcoder.shared.problem.DataType; 12 | import com.topcoder.shared.problem.Renderer; 13 | import com.topcoder.shared.problem.TestCase; 14 | 15 | /** 16 | * @author Charles McGarvey 17 | * The TopCoder Arena editor plug-in providing support for Vim. 18 | * 19 | * Distributable under the terms and conditions of the 2-clause BSD license; 20 | * see the file COPYING for a complete text of the license. 21 | */ 22 | public class Editor 23 | { 24 | /** 25 | * The problem ID number. 26 | */ 27 | private String id; 28 | 29 | /** 30 | * The name of the class. 31 | */ 32 | private String name; 33 | 34 | /** 35 | * The name of the contest. 36 | */ 37 | private String contestName; 38 | 39 | /** 40 | * The point value. 41 | */ 42 | private String points; 43 | 44 | /** 45 | * The path of the current source file. 46 | */ 47 | private File sourceFile; 48 | 49 | /** 50 | * The path of the problem directory. 51 | */ 52 | private File directory; 53 | 54 | 55 | /** 56 | * Map languages names to file extensions. 57 | */ 58 | private static final Map languageExtension = new HashMap(); 59 | static 60 | { 61 | languageExtension.put("Java", "java"); 62 | languageExtension.put("C++", "cc"); 63 | languageExtension.put("C#", "cs"); 64 | languageExtension.put("VB", "vb"); 65 | languageExtension.put("Python", "py"); 66 | } 67 | 68 | 69 | /** 70 | * Construct an editor with the problem objects given us by the Arena. 71 | * @param component A container for the particulars of the problem. 72 | * @param language The currently selected language. 73 | * @param renderer A helper object to help format the problem statement. 74 | * @throws Exception If the editor could not set itself up. 75 | */ 76 | public Editor(ProblemComponentModel component, 77 | Language language, Renderer renderer) throws Exception 78 | { 79 | this.id = String.valueOf(component.getProblem().getProblemID()); 80 | this.name = component.getClassName(); 81 | this.contestName = component.getProblem().getRound().getContestName().replaceAll(" ", "-"); 82 | this.points = String.valueOf(component.getPoints().intValue()); 83 | 84 | // Make sure the top-level vimcoder directory exists. 85 | File topDir = VimCoder.getStorageDirectory(); 86 | if (!topDir.isDirectory()) 87 | { 88 | if (!topDir.mkdirs()) throw new IOException(topDir.getPath()); 89 | } 90 | 91 | // Make sure the problem directory exists. 92 | File newStyleDirectory = new File(new File(topDir, contestName), points); 93 | File oldStyleDirectory = new File(topDir, id); 94 | if (newStyleDirectory.isDirectory()) 95 | { 96 | this.directory = newStyleDirectory; 97 | } 98 | else if (oldStyleDirectory.isDirectory()) 99 | { 100 | this.directory = oldStyleDirectory; 101 | } 102 | else if (VimCoder.isContestDirNames()) 103 | { 104 | this.directory = newStyleDirectory; 105 | if (!directory.mkdirs()) throw new IOException(directory.getPath()); 106 | } 107 | else 108 | { 109 | this.directory = oldStyleDirectory; 110 | if (!directory.mkdirs()) throw new IOException(directory.getPath()); 111 | } 112 | 113 | String lang = language.getName(); 114 | String ext = languageExtension.get(lang); 115 | 116 | // Set up the terms used for the template expansion. 117 | HashMap terms = new HashMap(); 118 | terms.put("RETURNTYPE", component.getReturnType().getDescriptor(language)); 119 | terms.put("CLASSNAME", name); 120 | terms.put("METHODNAME", component.getMethodName()); 121 | terms.put("METHODPARAMS", getMethodParams(component.getParamTypes(), component.getParamNames(), language)); 122 | terms.put("METHODPARAMNAMES", Util.join(component.getParamNames(), ", ")); 123 | terms.put("METHODPARAMSTREAMIN", Util.join(component.getParamNames(), " >> ")); 124 | terms.put("METHODPARAMSTREAMOUT", Util.join(component.getParamNames(), " << \", \" << ")); 125 | terms.put("METHODPARAMDECLARES", getMethodParamDeclarations(component.getParamTypes(), component.getParamNames(), language)); 126 | terms.put("VIMCODER", VimCoder.version); 127 | 128 | // Write the problem statement as an HTML file in the problem directory. 129 | File problemFile = new File(directory, "Problem.html"); 130 | if (!problemFile.canRead()) 131 | { 132 | FileWriter writer = new FileWriter(problemFile); 133 | try 134 | { 135 | writer.write(renderer.toHTML(language)); 136 | } 137 | finally 138 | { 139 | writer.close(); 140 | } 141 | } 142 | 143 | // Expand the template for the main class and write it to the current 144 | // source file. 145 | this.sourceFile = new File(directory, name + "." + ext); 146 | if (!sourceFile.canRead()) 147 | { 148 | String text = Util.expandTemplate(readTemplate(lang + "Template"), terms); 149 | FileWriter writer = new FileWriter(sourceFile); 150 | writer.write(text); 151 | writer.close(); 152 | } 153 | 154 | // Expand the driver template and write it to a source file. 155 | File driverFile = new File(directory, "driver." + ext); 156 | if (!driverFile.canRead()) 157 | { 158 | String text = Util.expandTemplate(readTemplate(lang + "Driver"), terms); 159 | FileWriter writer = new FileWriter(driverFile); 160 | writer.write(text); 161 | writer.close(); 162 | } 163 | 164 | // Write the test cases to a text file. The driver code can read this 165 | // file and perform the tests based on what it reads. 166 | File testcaseFile = new File(directory, "testcases.txt"); 167 | if (!testcaseFile.canRead()) 168 | { 169 | StringBuilder text = new StringBuilder(); 170 | if (component.hasTestCases()) 171 | { 172 | for (TestCase testCase : component.getTestCases()) 173 | { 174 | text.append(testCase.getOutput() + System.getProperty("line.separator")); 175 | for (String input : testCase.getInput()) 176 | { 177 | text.append(input + System.getProperty("line.separator")); 178 | } 179 | } 180 | } 181 | FileWriter writer = new FileWriter(testcaseFile); 182 | writer.write(text.toString()); 183 | writer.close(); 184 | } 185 | 186 | // Finally, expand the Makefile template and write it. 187 | File makeFile = new File(directory, "Makefile"); 188 | if (!makeFile.canRead()) 189 | { 190 | String text = Util.expandTemplate(readTemplate(lang + "Makefile"), terms); 191 | FileWriter writer = new FileWriter(makeFile); 192 | writer.write(text); 193 | writer.close(); 194 | } 195 | } 196 | 197 | /** 198 | * Save the source code provided by the server, and tell the Vim server to 199 | * edit the current source file. 200 | * @param source The source code. 201 | * @throws Exception If the source couldn't be written or the Vim server 202 | * had a problem. 203 | */ 204 | public void setSource(String source) throws Exception 205 | { 206 | FileWriter writer = new FileWriter(new File(directory, name)); 207 | writer.write(source); 208 | writer.close(); 209 | sendVimCommand("--remote-tab-silent", sourceFile.getPath()); 210 | } 211 | 212 | /** 213 | * Read the source code from the current source file. 214 | * @return The source code. 215 | * @throws IOException If the source file could not be read. 216 | */ 217 | public String getSource() throws IOException 218 | { 219 | return Util.readFile(sourceFile); 220 | } 221 | 222 | 223 | /** 224 | * Send a command to the Vim server. 225 | * If the server isn't running, it will be started with the name 226 | * VIMCODER#### where #### is the problem ID. 227 | * @param command The command to send to the server. 228 | * @param argument A single argument for the remote command. 229 | * @throws Exception If the command could not be sent. 230 | */ 231 | private void sendVimCommand(String command, String argument) throws Exception 232 | { 233 | String[] arguments = {argument}; 234 | sendVimCommand(command, arguments); 235 | } 236 | 237 | /** 238 | * Send a command to the Vim server. 239 | * If the server isn't running, it will be started with the name 240 | * VIMCODER#### where #### is the problem ID. 241 | * @param command The command to send to the server. 242 | * @param argument Arguments for the remote command. 243 | * @throws Exception If the command could not be sent. 244 | */ 245 | private void sendVimCommand(String command, String[] arguments) throws Exception 246 | { 247 | String[] vimCommand = VimCoder.getVimCommand().split("\\s"); 248 | String[] flags = {"--servername", "VimCoder" + id, command}; 249 | vimCommand = Util.concat(vimCommand, flags); 250 | vimCommand = Util.concat(vimCommand, arguments); 251 | Process child = Runtime.getRuntime().exec(vimCommand, null, directory); 252 | 253 | /* FIXME: This is a pretty bad hack. The problem is that the Vim 254 | * process doesn't fork to the background on some systems, so we 255 | * can't wait on the child. At the same time, calling this method 256 | * before the previous child could finish initializing the server 257 | * may result in multiple editor windows popping up. We'd also 258 | * like to be able to get the return code from the child if we can. 259 | * The workaround here is to stall the thread for a little while or 260 | * until we see that the child exits. If the child never exits 261 | * before the timeout, we will assume it is not backgrounding and 262 | * that everything worked. This works as long as the Vim server is 263 | * able to start within the stall period. */ 264 | long expire = System.currentTimeMillis() + 2500; 265 | while (System.currentTimeMillis() < expire) 266 | { 267 | Thread.yield(); 268 | try 269 | { 270 | int exitCode = child.exitValue(); 271 | if (exitCode != 0) throw new Exception("Vim process returned exit code " + exitCode + "."); 272 | break; 273 | } 274 | catch (IllegalThreadStateException exception) 275 | { 276 | // The child has not exited; intentionally ignoring exception. 277 | } 278 | } 279 | } 280 | 281 | 282 | /** 283 | * Read a template. 284 | * We first look in the storage directory. If we can't find one, we 285 | * look among the resources. 286 | * @param tName The name of the template. 287 | * @return The contents of the template file, or an empty string. 288 | */ 289 | private String readTemplate(String tName) 290 | { 291 | File templateFile = new File(VimCoder.getStorageDirectory(), tName); 292 | try 293 | { 294 | if (templateFile.canRead()) return Util.readFile(templateFile); 295 | return Util.readResource(tName); 296 | } 297 | catch (IOException exception) 298 | { 299 | return ""; 300 | } 301 | } 302 | 303 | 304 | /** 305 | * Convert an array of data types to an array of strings according to a 306 | * given language. 307 | * @param types The data types. 308 | * @param language The language to use in the conversion. 309 | * @return The array of string representations of the data types. 310 | */ 311 | private String[] getStringTypes(DataType[] types, Language language) 312 | { 313 | String[] strings = new String[types.length]; 314 | for (int i = 0; i < types.length; ++i) 315 | { 316 | strings[i] = types[i].getDescriptor(language); 317 | } 318 | return strings; 319 | } 320 | 321 | /** 322 | * Combine the data types and parameter names into a comma-separated list of 323 | * the method parameters. 324 | * The result could be used inside the parentheses of a method 325 | * declaration. 326 | * @param types The data types of the parameters. 327 | * @param names The names of the parameters. 328 | * @param language The language used for representing the data types. 329 | * @return The list of parameters. 330 | */ 331 | private String getMethodParams(DataType[] types, String[] names, Language language) 332 | { 333 | String[] typeStrings = getStringTypes(types, language); 334 | return Util.join(Util.combine(typeStrings, names, " "), ", "); 335 | } 336 | 337 | /** 338 | * Combine the data types and parameter names into a group of variable 339 | * declarations. 340 | * Each declaration is separated by a new line and terminated with a 341 | * semicolon. 342 | * @param types The data types of the parameters. 343 | * @param names The names of the parameters. 344 | * @param language The language used for representing the data types. 345 | * @return The parameters as a block of declarations. 346 | */ 347 | private String getMethodParamDeclarations(DataType[] types, String[] names, Language language) 348 | { 349 | final String end = ";" + System.getProperty("line.separator"); 350 | String[] typeStrings = getStringTypes(types, language); 351 | return Util.join(Util.combine(typeStrings, names, "\t"), end) + end; 352 | } 353 | } 354 | 355 | // vim:et:ts=8:sts=4:sw=4 356 | -------------------------------------------------------------------------------- /src/com/dogcows/Util.java: -------------------------------------------------------------------------------- 1 | 2 | package com.dogcows; 3 | 4 | import java.io.*; 5 | import java.util.Arrays; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author Charles McGarvey 10 | * The TopCoder Arena editor plug-in providing support for Vim. 11 | * 12 | * Distributable under the terms and conditions of the 2-clause BSD license; 13 | * see the file COPYING for a complete text of the license. 14 | */ 15 | public abstract class Util 16 | { 17 | /** 18 | * Concatenate two arrays into a single array. 19 | * @param a First array. 20 | * @param b Second array. 21 | * @return The combined array. 22 | */ 23 | public static T[] concat(T[] a, T[] b) 24 | { 25 | T[] result = Arrays.copyOf(a, a.length + b.length); 26 | System.arraycopy(b, 0, result, a.length, b.length); 27 | return result; 28 | } 29 | 30 | /** 31 | * Combined string elements from two arrays into a single array, gluing 32 | * together elements of the same index with a delimiter string. 33 | * @param a First string array. 34 | * @param b Second string array. 35 | * @param glue The delimiter string. 36 | * @return The combined array. 37 | */ 38 | public static String[] combine(String[] a, String[] b, String glue) 39 | { 40 | String[] result = new String[Math.min(a.length, b.length)]; 41 | for (int i = 0; i < result.length; ++i) 42 | { 43 | result[i] = a[i] + glue + b[i]; 44 | } 45 | return result; 46 | } 47 | 48 | /** 49 | * Join the elements of a string array with a delimiter. 50 | * @param a The array. 51 | * @param glue The delimiter string. 52 | * @return The joined string. 53 | */ 54 | public static String join(String[] a, String glue) 55 | { 56 | if (a.length == 0) return ""; 57 | StringBuilder result = new StringBuilder(); 58 | result.append(a[0]); 59 | for (int i = 1; i < a.length; ++i) result.append(glue).append(a[i]); 60 | return result.toString(); 61 | } 62 | 63 | /** 64 | * Escape a string by replacing prepending backslashes and double 65 | * quotation characters with an extra backslash. 66 | * @param The string to be escaped. 67 | * @return The escaped string. 68 | */ 69 | public static String escape(String a) 70 | { 71 | a = a.replaceAll("\\\\", "\\\\\\\\"); 72 | a = a.replaceAll("\"", "\\\\\\\""); 73 | return a; 74 | } 75 | 76 | /** 77 | * Simply read a file's contents into a string object. 78 | * @param file The file to read. 79 | * @return The contents of the file. 80 | * @throws IOException If the file is not readable. 81 | */ 82 | public static String readFile(File file) throws IOException 83 | { 84 | StringBuilder text = new StringBuilder(); 85 | 86 | BufferedReader reader = new BufferedReader(new FileReader(file.getPath())); 87 | try 88 | { 89 | String line = null; 90 | 91 | while ((line = reader.readLine()) != null) 92 | { 93 | text.append(line + System.getProperty("line.separator")); 94 | } 95 | } 96 | finally 97 | { 98 | reader.close(); 99 | } 100 | 101 | return text.toString(); 102 | } 103 | 104 | /** 105 | * Read a resource file into a string object. 106 | * The resources should be placed in the directory `resources' 107 | * underneath the parent directory of this class. Reading resources 108 | * packaged in a jar is allowable. 109 | * @param path Relative path to the resource. 110 | * @return The contents of the resource. 111 | * @throws IOException If the resource is not readable. 112 | */ 113 | public static String readResource(String path) throws IOException 114 | { 115 | StringBuilder text = new StringBuilder(); 116 | 117 | InputStream stream = Util.class.getResourceAsStream("resources/" + path); 118 | if (stream != null) 119 | { 120 | try 121 | { 122 | byte[] buffer = new byte[4096]; 123 | int numBytes = 0; 124 | while (0 < (numBytes = stream.read(buffer))) 125 | { 126 | text.append(new String(buffer, 0, numBytes)); 127 | } 128 | } 129 | finally 130 | { 131 | stream.close(); 132 | } 133 | } 134 | 135 | return text.toString().replaceAll("\n", System.getProperty("line.separator")); 136 | } 137 | 138 | /** 139 | * The poor man's template package. 140 | * Provide a template and a map of terms to build the result with the 141 | * terms expanded into the template. Terms in the template should 142 | * appear surrounded with dollar signs. For example, if $MYTERM$ 143 | * appears in the template, it will be replaced by the value into the 144 | * terms map with the key MYTERM (if it exists in the map). 145 | * @param template The template string. 146 | * @param terms A map of key/value terms. 147 | * @return The string expanded from the template and terms. 148 | */ 149 | public static String expandTemplate(String template, Map terms) 150 | { 151 | String text = template; 152 | for (String key : terms.keySet()) 153 | { 154 | text = text.replaceAll("\\$" + key + "\\$", Util.escape(terms.get(key))); 155 | } 156 | return text; 157 | } 158 | } 159 | 160 | // vim:et:ts=8:sts=4:sw=4 161 | -------------------------------------------------------------------------------- /src/com/dogcows/VimCoder.java: -------------------------------------------------------------------------------- 1 | 2 | package com.dogcows; 3 | 4 | import java.awt.*; 5 | import java.awt.event.*; 6 | import java.io.*; 7 | import java.text.SimpleDateFormat; 8 | import java.util.*; 9 | import javax.swing.*; 10 | import javax.swing.border.*; 11 | 12 | import com.topcoder.client.contestApplet.common.Common; 13 | import com.topcoder.client.contestApplet.common.LocalPreferences; 14 | import com.topcoder.client.contestant.ProblemComponentModel; 15 | import com.topcoder.shared.language.Language; 16 | import com.topcoder.shared.problem.Renderer; 17 | 18 | /** 19 | * @author Charles McGarvey 20 | * The TopCoder Arena editor plug-in providing support for Vim. 21 | * 22 | * Distributable under the terms and conditions of the 2-clause BSD license; 23 | * see the file COPYING for a complete text of the license. 24 | */ 25 | public class VimCoder 26 | { 27 | /** 28 | * The name and version of this plugin. 29 | */ 30 | public final static String version = "VimCoder 0.3.6"; 31 | 32 | 33 | /** 34 | * The first part of the command used to invoke the Vim server. 35 | */ 36 | private static String vimCommand = "gvim"; 37 | 38 | /** 39 | * The path to the main VimCoder directory. 40 | */ 41 | private static File rootDir; 42 | static 43 | { 44 | if (System.getProperty("os.name").toLowerCase().equals("win")) 45 | { 46 | vimCommand = "C:\\WINDOWS\\gvim.bat"; 47 | } 48 | rootDir = new File(System.getProperty("user.home") + System.getProperty("file.separator") + ".vimcoder"); 49 | } 50 | 51 | /** 52 | * Whether or not to use the contest name and point value as problem 53 | * directory names. 54 | */ 55 | private static boolean contestDirNames = false; 56 | 57 | 58 | /** 59 | * The panel given to the Arena applet when it is requested. 60 | */ 61 | private JPanel panel; 62 | 63 | /** 64 | * The text widget where log messages are appended. 65 | */ 66 | private JTextArea logArea; 67 | 68 | /** 69 | * The current editor object (or null if there is none). 70 | */ 71 | private Editor editor; 72 | 73 | /** 74 | * The configuration panel. 75 | */ 76 | private JDialog configDialog; 77 | 78 | 79 | /** 80 | * The key for the vim command preference. 81 | */ 82 | private final static String VIMCOMMAND = "com.dogcows.VimCoder.config.vimcommand"; 83 | 84 | /** 85 | * The key for the root directory preference. 86 | */ 87 | private final static String ROOTDIR = "com.dogcows.VimCoder.config.rootdir"; 88 | 89 | /** 90 | * The key for the problem directory name preference. 91 | */ 92 | private final static String CONTESTDIRNAMES = "com.dogcows.VimCoder.config.contestdirnames"; 93 | 94 | /** 95 | * The preferences object for storing plugin settings. 96 | */ 97 | private static LocalPreferences prefs = LocalPreferences.getInstance(); 98 | 99 | 100 | /** 101 | * Get the command for invoking vim. 102 | * @return The command. 103 | */ 104 | public static String getVimCommand() 105 | { 106 | return vimCommand; 107 | } 108 | 109 | /** 110 | * Get the storage directory. 111 | * @return The directory. 112 | */ 113 | public static File getStorageDirectory() 114 | { 115 | return rootDir; 116 | } 117 | 118 | /** 119 | * Get whether or not to save problems in a human-readable directory 120 | * structure. 121 | * @return The directory name setting. 122 | */ 123 | public static boolean isContestDirNames() 124 | { 125 | return contestDirNames; 126 | } 127 | 128 | 129 | /** 130 | * Instantiate the entry point of the editor plugin. 131 | * Sets up the log widget and panel. 132 | */ 133 | public VimCoder() 134 | { 135 | logArea = new JTextArea(); 136 | logArea.setForeground(Color.GREEN); 137 | logArea.setBackground(Color.BLACK); 138 | logArea.setEditable(false); 139 | Font font = new Font("Courier", Font.PLAIN, 12); 140 | if (font != null) logArea.setFont(font); 141 | 142 | panel = new JPanel(new BorderLayout()); 143 | panel.add(new JScrollPane(logArea), BorderLayout.CENTER); 144 | } 145 | 146 | 147 | /** 148 | * Called by the Arena when the plugin is about to be used. 149 | */ 150 | public void startUsing() 151 | { 152 | Runnable task = new Runnable() 153 | { 154 | public void run() 155 | { 156 | logArea.setText(""); 157 | } 158 | }; 159 | if (SwingUtilities.isEventDispatchThread()) 160 | { 161 | task.run(); 162 | } 163 | else 164 | { 165 | SwingUtilities.invokeLater(task); 166 | } 167 | loadConfiguration(); 168 | } 169 | 170 | /** 171 | * Called by the Arena when the plugin is no longer needed. 172 | */ 173 | public void stopUsing() 174 | { 175 | editor = null; 176 | } 177 | 178 | /** 179 | * Called by the Arena to obtain the editor panel which we will use to 180 | * show log messages. 181 | * @return The editor panel. 182 | */ 183 | public JPanel getEditorPanel() 184 | { 185 | return panel; 186 | } 187 | 188 | /** 189 | * Called by the Arena to obtain the current source. 190 | * This happens when the user is saving, compiling, and/or submitting. 191 | * @return The current source code. 192 | * @throws Exception If the source file edited by Vim couldn't be read. 193 | */ 194 | public String getSource() throws Exception 195 | { 196 | try 197 | { 198 | String source = editor.getSource(); 199 | logInfo("Source code uploaded to server."); 200 | return source; 201 | } 202 | catch (Exception exception) 203 | { 204 | logError("Failed to get source code: " + exception.getLocalizedMessage()); 205 | throw exception; 206 | } 207 | } 208 | 209 | /** 210 | * Called by the Arena to pass the source it has. 211 | * @param source The source code. 212 | */ 213 | public void setSource(String source) 214 | { 215 | try 216 | { 217 | editor.setSource(source); 218 | logInfo("Source code downloaded from server."); 219 | } 220 | catch (Exception exception) 221 | { 222 | logError("Failed to save the source given by the server: " + exception.getLocalizedMessage()); 223 | return; 224 | } 225 | } 226 | 227 | /** 228 | * Called by the Arena to pass along information about the current 229 | * problem. 230 | * @param component A container for the particulars of the problem. 231 | * @param language The currently selected language. 232 | * @param renderer A helper object to help format the problem 233 | * statement. 234 | */ 235 | public void setProblemComponent(ProblemComponentModel component, 236 | Language language, Renderer renderer) 237 | { 238 | try 239 | { 240 | editor = new Editor(component, language, renderer); 241 | } 242 | catch (Exception exception) 243 | { 244 | logError("An error occurred while loading the problem: " + exception.getLocalizedMessage()); 245 | } 246 | } 247 | 248 | /** 249 | * Called by the Arena when it's time to show our configuration panel. 250 | */ 251 | public void configure() 252 | { 253 | final int border = 10; 254 | final int inset = 2; 255 | 256 | loadConfiguration(); 257 | 258 | configDialog = new JDialog(); 259 | Container container = configDialog.getContentPane(); 260 | container.setForeground(Common.FG_COLOR); 261 | container.setBackground(Common.WPB_COLOR); 262 | 263 | JPanel pane = new JPanel(); 264 | container.add(pane); 265 | 266 | BoxLayout boxLayout = new BoxLayout(pane, BoxLayout.Y_AXIS); 267 | pane.setLayout(boxLayout); 268 | pane.setBorder(BorderFactory.createEmptyBorder(border, border, border, border)); 269 | 270 | JPanel fieldPanel = new JPanel(new GridBagLayout()); 271 | pane.add(fieldPanel); 272 | pane.add(Box.createRigidArea(new Dimension(0, border))); 273 | 274 | GridBagConstraints c = new GridBagConstraints(); 275 | c.fill = GridBagConstraints.HORIZONTAL; 276 | c.insets = new Insets(inset, inset, inset, inset); 277 | 278 | JLabel rootDirLabel = new JLabel("Storage Directory:"); 279 | rootDirLabel.setForeground(Common.FG_COLOR); 280 | c.gridx = 0; 281 | c.gridy = 0; 282 | c.gridwidth = 1; 283 | fieldPanel.add(rootDirLabel, c); 284 | 285 | final JTextField rootDirField = new JTextField(rootDir.getPath()); 286 | rootDirField.setPreferredSize(new Dimension(0, 24)); 287 | c.gridx = 1; 288 | c.gridy = 0; 289 | c.weightx = 1.0; 290 | fieldPanel.add(rootDirField, c); 291 | 292 | JButton browseButton = new JButton("Browse"); 293 | c.gridx = 2; 294 | c.gridy = 0; 295 | c.weightx = 0.0; 296 | c.anchor = GridBagConstraints.BASELINE_LEADING; 297 | fieldPanel.add(browseButton, c); 298 | 299 | final JCheckBox contestDirNamesButton = new JCheckBox( 300 | "Store problems according to contest name and point value.", 301 | contestDirNames 302 | ); 303 | contestDirNamesButton.setForeground(Common.FG_COLOR); 304 | contestDirNamesButton.setBackground(Common.WPB_COLOR); 305 | contestDirNamesButton.setFont(rootDirLabel.getFont()); 306 | c.gridx = 1; 307 | c.gridy = 1; 308 | c.gridwidth = 2; 309 | fieldPanel.add(contestDirNamesButton, c); 310 | 311 | JLabel vimCommandLabel = new JLabel("Vim Command:"); 312 | vimCommandLabel.setForeground(Common.FG_COLOR); 313 | c.gridx = 0; 314 | c.gridy = 2; 315 | c.gridwidth = 1; 316 | fieldPanel.add(vimCommandLabel, c); 317 | 318 | final JTextField vimCommandField = new JTextField(vimCommand); 319 | vimCommandField.setPreferredSize(new Dimension(0, 24)); 320 | c.gridx = 1; 321 | c.gridy = 2; 322 | c.weightx = 1.0; 323 | c.gridwidth = 2; 324 | fieldPanel.add(vimCommandField, c); 325 | 326 | JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, inset, inset)); 327 | buttonPanel.setPreferredSize(new Dimension(400, 24 + 2 * inset)); 328 | pane.add(buttonPanel); 329 | 330 | JButton saveButton = new JButton("Save"); 331 | buttonPanel.add(saveButton); 332 | buttonPanel.add(Box.createRigidArea(new Dimension(1, 0))); 333 | 334 | JButton closeButton = new JButton("Close"); 335 | buttonPanel.add(closeButton); 336 | 337 | browseButton.addActionListener(new ActionListener() 338 | { 339 | public void actionPerformed(ActionEvent actionEvent) 340 | { 341 | JFileChooser chooser = new JFileChooser(); 342 | chooser.setCurrentDirectory(new File(".")); 343 | chooser.setDialogTitle("Choose Storage Directory"); 344 | chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 345 | chooser.setAcceptAllFileFilterUsed(false); 346 | 347 | if (chooser.showOpenDialog(configDialog) == JFileChooser.APPROVE_OPTION) 348 | { 349 | rootDirField.setText(chooser.getSelectedFile().getPath()); 350 | } 351 | } 352 | }); 353 | 354 | closeButton.addActionListener(new ActionListener() 355 | { 356 | public void actionPerformed(ActionEvent actionEvent) 357 | { 358 | configDialog.dispose(); 359 | } 360 | }); 361 | 362 | saveButton.addActionListener(new ActionListener() 363 | { 364 | public void actionPerformed(ActionEvent actionEvent) 365 | { 366 | prefs.setProperty(VIMCOMMAND, vimCommandField.getText()); 367 | prefs.setProperty(ROOTDIR, rootDirField.getText()); 368 | prefs.setProperty(CONTESTDIRNAMES, String.valueOf(contestDirNamesButton.isSelected())); 369 | JOptionPane.showMessageDialog(null, "Preferences were saved successfully."); 370 | } 371 | }); 372 | 373 | configDialog.setTitle("VimCoder Preferences"); 374 | configDialog.pack(); 375 | configDialog.setLocationRelativeTo(null); // Center dialog in screen. 376 | configDialog.setModalityType(Dialog.DEFAULT_MODALITY_TYPE); 377 | configDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 378 | configDialog.setVisible(true); 379 | } 380 | 381 | 382 | /** 383 | * Load the local preferences related to this plugin. 384 | */ 385 | private void loadConfiguration() 386 | { 387 | String vc = prefs.getProperty(VIMCOMMAND); 388 | if (vc != null) vimCommand = vc; 389 | 390 | String dir = prefs.getProperty(ROOTDIR); 391 | if (dir != null) rootDir = new File(dir); 392 | 393 | String cn = prefs.getProperty(CONTESTDIRNAMES); 394 | if (cn != null) contestDirNames = Boolean.parseBoolean(cn); 395 | } 396 | 397 | 398 | /** 399 | * A generic logging function, appends text to the text area. A timestamp 400 | * is also prepended to the next text. 401 | * @param what The text to append. 402 | */ 403 | private void log(final String what) 404 | { 405 | Runnable task = new Runnable() 406 | { 407 | public void run() 408 | { 409 | SimpleDateFormat format = new SimpleDateFormat("kk:mm:ss"); 410 | logArea.append(format.format(new Date()) + ", " + what); 411 | } 412 | }; 413 | if (SwingUtilities.isEventDispatchThread()) 414 | { 415 | task.run(); 416 | } 417 | else 418 | { 419 | SwingUtilities.invokeLater(task); 420 | } 421 | } 422 | 423 | /** 424 | * Output non-critical messages to the log. 425 | * @param what The text of the message. 426 | */ 427 | private void logInfo(String what) 428 | { 429 | log(" INFO: " + what + System.getProperty("line.separator")); 430 | } 431 | 432 | /** 433 | * Output critical messages and errors to the log. 434 | * @param what The text of the message. 435 | */ 436 | private void logError(String what) 437 | { 438 | log("ERROR: " + what + System.getProperty("line.separator")); 439 | } 440 | } 441 | 442 | // vim:et:ts=8:sts=4:sw=4 443 | -------------------------------------------------------------------------------- /src/com/dogcows/resources/C++Driver: -------------------------------------------------------------------------------- 1 | 2 | #include "$CLASSNAME$.cc" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | const static double __EPSILON = 1e-9; 16 | static double __time = 0.0; 17 | 18 | static void __timer_start() 19 | { 20 | struct timeval tv; 21 | if (gettimeofday(&tv, NULL) == 0) 22 | { 23 | __time = double(tv.tv_sec) * 1000.0 + double(tv.tv_usec) * 0.001; 24 | } 25 | } 26 | 27 | static double __timer_stop() 28 | { 29 | double start = __time; 30 | __timer_start(); 31 | return __time - start; 32 | } 33 | 34 | 35 | static void __eat_whitespace(std::istream& in) 36 | { 37 | while (in.good() && std::isspace(in.peek())) in.get(); 38 | } 39 | 40 | 41 | std::ostream& operator << (std::ostream& out, const std::string& str) 42 | { 43 | out << '"' << str.c_str() << '"'; 44 | return out; 45 | } 46 | 47 | template 48 | std::ostream& operator << (std::ostream& out, const std::vector& vec) 49 | { 50 | out << '{'; 51 | if (0 < vec.size()) 52 | { 53 | out << vec[0]; 54 | for (size_t i = 1; i < vec.size(); ++i) out << ", " << vec[i]; 55 | } 56 | out << '}'; 57 | return out; 58 | } 59 | 60 | std::istream& operator >> (std::istream& in, std::string& str) 61 | { 62 | __eat_whitespace(in); 63 | 64 | int c; 65 | if (in.good() && (c = in.get()) == '"') 66 | { 67 | std::ostringstream s; 68 | while (in.good() && (c = in.get()) != '"') 69 | { 70 | s.put(char(c)); 71 | } 72 | str = s.str(); 73 | } 74 | 75 | return in; 76 | } 77 | 78 | template 79 | std::istream& operator >> (std::istream& in, std::vector& vec) 80 | { 81 | __eat_whitespace(in); 82 | 83 | int c; 84 | if (in.good() && (c = in.get()) == '{') 85 | { 86 | __eat_whitespace(in); 87 | vec.clear(); 88 | while (in.good() && (c = in.get()) != '}') 89 | { 90 | if (c != ',') in.putback(c); 91 | 92 | T t; 93 | in >> t; 94 | __eat_whitespace(in); 95 | 96 | vec.push_back(t); 97 | } 98 | } 99 | 100 | return in; 101 | } 102 | 103 | 104 | template 105 | bool __equals(const T& actual, const T& expected) 106 | { 107 | return actual == expected; 108 | } 109 | 110 | bool __equals(double actual, double expected) 111 | { 112 | if (std::abs(actual - expected) < __EPSILON) 113 | { 114 | return true; 115 | } 116 | else 117 | { 118 | double minimum = std::min(expected * (1.0 - __EPSILON), expected * (1.0 + __EPSILON)); 119 | double maximum = std::max(expected * (1.0 - __EPSILON), expected * (1.0 + __EPSILON)); 120 | return actual > minimum && actual < maximum; 121 | } 122 | } 123 | 124 | bool __equals(const std::vector& actual, const std::vector& expected) 125 | { 126 | if (actual.size() != expected.size()) 127 | { 128 | return false; 129 | } 130 | 131 | for (size_t i = 0; i < actual.size(); ++i) 132 | { 133 | if (!__equals(actual[i], expected[i])) 134 | { 135 | return false; 136 | } 137 | } 138 | 139 | return true; 140 | } 141 | 142 | 143 | int main(int argc, char* argv[]) 144 | { 145 | bool __abort_on_fail = false; 146 | int __pass = 0; 147 | int __fail = 0; 148 | 149 | if (1 < argc) __abort_on_fail = true; 150 | 151 | std::cout << "TAP version 13" << std::endl; 152 | std::cout.flush(); 153 | 154 | std::ifstream __in("testcases.txt"); 155 | for(;;) 156 | { 157 | int __testnum = __pass + __fail + 1; 158 | 159 | $RETURNTYPE$ __expected; 160 | $METHODPARAMDECLARES$ 161 | __in >> __expected >> $METHODPARAMSTREAMIN$; 162 | if (!__in.good()) break; 163 | 164 | std::cout << "# input for test " << __testnum << ": " << $METHODPARAMSTREAMOUT$ << std::endl; 165 | std::cout.flush(); 166 | 167 | __timer_start(); 168 | 169 | $CLASSNAME$ __object; 170 | $RETURNTYPE$ __actual = __object.$METHODNAME$($METHODPARAMNAMES$); 171 | 172 | double __t = __timer_stop(); 173 | 174 | std::cout << "# test completed in " << __t << "ms" << std::endl; 175 | std::cout.flush(); 176 | 177 | if (__equals(__actual, __expected)) 178 | { 179 | std::cout << "ok"; 180 | ++__pass; 181 | } 182 | else 183 | { 184 | std::cout << "not ok"; 185 | ++__fail; 186 | } 187 | 188 | std::cout << " " << __testnum << " - " << __actual << " must equal " << __expected << std::endl; 189 | std::cout.flush(); 190 | 191 | if (__abort_on_fail && 0 < __fail) std::abort(); 192 | } 193 | 194 | std::cout << "1.." << (__pass + __fail) << std::endl 195 | << "# passed: " << __pass << std::endl 196 | << "# failed: " << __fail << std::endl; 197 | 198 | if (__fail == 0) 199 | { 200 | std::cout << std::endl 201 | << "# Nice! Don't forget to compile remotely before submitting." << std::endl; 202 | } 203 | 204 | return __fail; 205 | } 206 | 207 | // vim:ft=cpp:noet:ts=8 208 | -------------------------------------------------------------------------------- /src/com/dogcows/resources/C++Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Set the command for your C++ compiler, and specify any compiler flags you want to use (e.g. -g 3 | # -Werror). 4 | CXX = g++ 5 | CXXFLAGS = -ggdb -Wall 6 | 7 | # The driver outputs TAP (Test Anything Protocol), so it can also be used with any TAP test harness 8 | # (e.g. prove). Set the path to your test harness here, then use the `prove' target to run the 9 | # tests with that harness. 10 | PROVE = prove 11 | PROVEFLAGS = -e "" 12 | 13 | 14 | ifeq ($(OS),Windows_NT) 15 | EXEEXT = .exe 16 | endif 17 | 18 | 19 | all: driver$(EXEEXT) 20 | 21 | clean: 22 | rm -f driver$(EXEEXT) 23 | 24 | distclean: clean 25 | rm -f $CLASSNAME$ 26 | 27 | run: all 28 | ./driver$(EXEEXT) 29 | 30 | test: all 31 | ./driver$(EXEEXT) --abort-on-fail 32 | 33 | prove: all 34 | $(PROVE) $(PROVEFLAGS) ./driver$(EXEEXT) 35 | 36 | 37 | %$(EXEEXT): %.cc 38 | $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ 39 | 40 | driver$(EXEEXT): $CLASSNAME$.cc 41 | 42 | .PHONY: all clean distclean run test prove 43 | 44 | # vim:ft=make:noet:ts=8 45 | -------------------------------------------------------------------------------- /src/com/dogcows/resources/C++Template: -------------------------------------------------------------------------------- 1 | 2 | // {{{ $VIMCODER$ <----------------------------------------------------- 3 | // vim:filetype=cpp:foldmethod=marker:foldmarker={{{,}}} 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | using namespace std; 26 | 27 | // }}} 28 | 29 | class $CLASSNAME$ 30 | { 31 | public: 32 | $RETURNTYPE$ $METHODNAME$($METHODPARAMS$) 33 | { 34 | return $RETURNTYPE$(); 35 | } 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /src/com/dogcows/resources/JavaTemplate: -------------------------------------------------------------------------------- 1 | 2 | // {{{ $VIMCODER$ <----------------------------------------------------- 3 | // vim:filetype=java:foldmethod=marker:foldmarker={{{,}}} 4 | 5 | import static java.lang.Math.*; 6 | import static java.math.BigInteger.*; 7 | import static java.util.Arrays.*; 8 | import static java.util.Collections.*; 9 | import java.math.*; 10 | import java.util.*; 11 | 12 | // }}} 13 | 14 | public class $CLASSNAME$ 15 | { 16 | public $RETURNTYPE$ $METHODNAME$($METHODPARAMS$) 17 | { 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /t/01-basic.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More; 7 | 8 | # placeholder for tests to be added in the future... someday 9 | pass; 10 | 11 | done_testing; 12 | 13 | --------------------------------------------------------------------------------