]> Dogcows Code - chaz/vimcoder/commitdiff
initial commit v0.1
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 8 Nov 2010 05:17:45 +0000 (22:17 -0700)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 8 Nov 2010 05:17:45 +0000 (22:17 -0700)
.classpath [new file with mode: 0644]
.project [new file with mode: 0644]
COPYING [new file with mode: 0644]
README [new file with mode: 0644]
src/com/dogcows/VimCoder.java [new file with mode: 0644]
src/com/dogcows/resources/C++Driver [new file with mode: 0644]
src/com/dogcows/resources/C++Makefile [new file with mode: 0644]
src/com/dogcows/resources/C++Template [new file with mode: 0644]
src/com/dogcows/resources/C++Test [new file with mode: 0644]

diff --git a/.classpath b/.classpath
new file mode 100644 (file)
index 0000000..fc6d16a
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+       <classpathentry kind="lib" path="lib/ContestApplet.jar">
+               <attributes>
+                       <attribute name="javadoc_location" value="jar:platform:/resource/VimCoder/lib/plugin-javadocs.jar!/"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/.project b/.project
new file mode 100644 (file)
index 0000000..2880acc
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>VimCoder</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..0b94026
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,27 @@
+
+The Simplified BSD License
+
+Copyright (c) 2010, Charles McGarvey 
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..f7a4828
--- /dev/null
+++ b/README
@@ -0,0 +1,68 @@
+
+VimCoder
+--------
+
+The TopCoder Arena editor plug-in providing support for Vim.
+
+The version is very experimental, and I haven't gotten around to writing
+any substantial documentation.  It is also not very configurable without
+changing the code.  All of these problems will be remedied eventually.
+
+
+Features:
+
+- Use Vim to edit your TopCoder Arena problems!
+- Keeps track of code files locally and syncs with the server, so no more
+  copy and pasting to and from the Arena applet.
+- Works with any language.
+- Downloads and stores a copy of the problem statement with your code for
+  off-line viewing.
+- Basic support for templates (currently C++ only).
+- Local test-case drivers can be automatically created with the example
+  test-case data (currently C++ only, and has bugs).
+
+
+Compatibility:
+
+I've tested the plug-in only on my own computer which runs x86_64 Linux.
+It should work on other setups, but note the following:
+
+- The program "gvim" should be in your PATH.
+- Vim needs to be compiled with client/server support.  To check, make sure
+  the command "vim --version | grep +clientserver" prints something.
+- The test-case driver generated for C++ uses POSIX functions for timing,
+  so it may not compile on non-POSIX systems (i.e. Windows).  The C++Driver
+  template simply needs to be changed to support other timing functions.
+
+
+How To Install:
+
+Assuming you have the downloaded the VimCoder jar file:
+
+- Run the TopCoder Arena applet.
+- Click the "Options" menu and select "Editor" to show the editor
+  preferences.
+- Click the "Add" button to bring up a new window.
+- For "Name," type "Vim" or whatever you want to represent this plug-in.
+- For "EntryPoint," type "com.dogcows.VimCoder" without the quotes.
+- For "ClassPath," click on "Browse" and locate the VimCoder jar file.  The
+  third field should now have the path to the jar file.
+- Click "OK" to close the window with the three fields.
+- Click "Save."
+
+You should now be able select "Vim" (or whatever you entered the first
+field) to use the plug-in.  If it doesn't work (i.e. nothing happens), you
+may need to restart the applet or set the plug-in as the default editor.
+Your mileage may vary.
+
+
+Known Bugs:
+
+- Lack of documentation.  The code itself is also poorly documented.
+- Only C++ templates are provided.
+- The generated C++ driver code won't compile when the method has one or
+  more parameters that are vectors (i.e. vectors of strings).
+- The $CARAT$ token doesn't seem to work in the templates.
+- Directory where code and problem data is saved is hardcoded at
+  ~/.vimcoder.  This should be configurable.
+
diff --git a/src/com/dogcows/VimCoder.java b/src/com/dogcows/VimCoder.java
new file mode 100644 (file)
index 0000000..489e789
--- /dev/null
@@ -0,0 +1,403 @@
+
+package com.dogcows;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.*;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import com.topcoder.client.contestant.ProblemComponentModel;
+import com.topcoder.shared.language.*;
+import com.topcoder.shared.problem.*;
+import com.topcoder.shared.problem.Renderer;
+
+/**
+ * @author Charles McGarvey
+ * The TopCoder Arena editor plug-in providing support for Vim.
+ * 
+ * Distributable under the terms and conditions of the 2-clause BSD license;
+ * see the file COPYING for a complete text of the license.
+ */
+public class VimCoder
+{
+    /**
+     * 
+     */
+    private final static String     version = "VimCoder 0.1";
+    private final static String     website = "http://www.dogcows.com/vimcoder";
+    
+    private JPanel      panel;
+    private JTextArea   logArea;
+    
+    private Editor      editor;
+    
+    
+    private static final Map<String,String> languageExtension = new HashMap<String,String>();
+    static
+    {
+        languageExtension.put("Java",   "java");
+        languageExtension.put("C++",    "cc");
+        languageExtension.put("C#",     "cs");
+        languageExtension.put("VB",     "vb");
+        languageExtension.put("Python", "py");
+    }
+    
+    
+    private class Editor
+    {
+        private String  id;
+        private String  name;
+        
+        private File    sourceFile;
+        private File    directory;
+        
+        
+        public Editor(ProblemComponentModel component, Language language, Renderer renderer) throws IOException
+        {
+            this.id     = String.valueOf(component.getProblem().getProblemID());
+            this.name   = component.getClassName();
+            
+            File topDir = new File(System.getProperty("user.home"), ".vimcoder");
+            if (!topDir.isDirectory())
+            {
+                if (!topDir.mkdirs()) throw new IOException(topDir.getPath());
+            }
+            
+            this.directory = new File(topDir, String.valueOf(component.getProblem().getProblemID()));
+            if (!directory.isDirectory())
+            {
+                if (!directory.mkdirs()) throw new IOException(directory.getPath());
+            }
+            
+            String lang = language.getName();
+            String ext  = languageExtension.get(lang);
+            
+            HashMap<String,String> terms = new HashMap<String,String>();
+            terms.put("RETURNTYPE", component.getReturnType().getDescriptor(language));
+            terms.put("CLASSNAME",  component.getClassName());
+            terms.put("METHODNAME", component.getMethodName());
+            terms.put("METHODPARAMS", getMethodParams(component.getParamTypes(), component.getParamNames(), language));
+            terms.put("METHODPARAMNAMES", join(component.getParamNames(), ", "));
+            
+            File problemFile = new File(directory, "Problem.html");
+            if (!problemFile.canRead())
+            {
+                FileWriter writer = new FileWriter(problemFile);
+                try
+                {
+                    writer.write(renderer.toHTML(language));
+                }
+                catch (Exception exception)
+                {
+                }
+                writer.close();
+            }
+            
+            sourceFile = new File(directory, terms.get("CLASSNAME") + "." + ext);
+            if (!sourceFile.canRead())
+            {
+                String text = expandTemplate(readResource(lang + "Template"), terms);
+                FileWriter writer = new FileWriter(sourceFile);
+                writer.write(text);
+                writer.close();
+            }
+
+            File driverFile = new File(directory, "driver" + "." + ext);
+            if (!driverFile.canRead())
+            {
+                StringBuilder testCases = new StringBuilder();
+                if (component.hasTestCases())
+                {
+                    HashMap<String,String> testTerms = new HashMap<String,String>();
+                    testTerms.putAll(terms);
+                    String template = readResource(lang + "Test");
+                    for (TestCase testCase : component.getTestCases())
+                    {
+                        testTerms.put("TESTOUTPUT", "\"" + quote(testCase.getOutput()) + "\"");
+                        testTerms.put("TESTINPUTS", join(testCase.getInput(), ", "));
+                        testCases.append(expandTemplate(template, testTerms));
+                    }
+                }
+                terms.put("TESTCASES", testCases.toString());
+                
+                String text = expandTemplate(readResource(lang + "Driver"), terms);
+                FileWriter writer = new FileWriter(driverFile);
+                writer.write(text);
+                writer.close();
+            }
+            
+            File makeFile = new File(directory, "Makefile");
+            {
+                String text = expandTemplate(readResource(lang + "Makefile"), terms);
+                FileWriter writer = new FileWriter(makeFile);
+                writer.write(text);
+                writer.close();
+            }
+        }
+        
+        public void setSource(String source) throws IOException
+        {
+            String actualSource = readFile(sourceFile);
+            if (!actualSource.equals(source))
+            {
+                File actualFile = new File(directory, name);
+                FileWriter writer = new FileWriter(actualFile);
+                writer.write(source);
+                writer.close();
+            }
+            doVimCommand("--remote-tab-silent", sourceFile.getPath());
+            doVimCommand("--remote-send", "<C-\\><C-N>:if search('\\$CARAT\\\\$') != 0<CR>normal df$<CR>endif<CR>:redraw<CR>");
+        }
+
+        public String getSource() throws IOException
+        {
+            return readFile(sourceFile) + "\n// Edited by " + version + "\n// " + website + "\n\n";
+        }
+        
+        public void setTextEnabled(boolean enable)
+        {
+            doVimCommand("--remote-send", "<C-\\><C-N>:set readonly<CR>:echo \"The contest is over.\"<CR>");
+        }
+        
+        
+        private boolean doVimCommand(String command, String argument)
+        {
+            String[] arguments = {argument};
+            return doVimCommand(command, arguments);
+        }
+        
+        private boolean doVimCommand(String command, String[] arguments)
+        {
+            try
+            {
+                String[] exec = {"gvim", "--servername", "VimCoder" + id, command};
+                exec = concat(exec, arguments);
+                
+                Process child = Runtime.getRuntime().exec(exec);
+                if (child.waitFor() == 0)
+                {
+                    return true;
+                }
+                else
+                {
+                    logError("vim command failed");
+                }
+            }
+            catch (IOException exception)
+            {
+                logError("failed to launch external vim process");
+                return false;
+            }
+            catch (InterruptedException exception)
+            {
+                logWarning("interrupted while waiting on vim process");
+            }
+            return false;
+        }
+        
+        private String getMethodParams(DataType[] types, String[] names, Language language)
+        {
+            StringBuilder text = new StringBuilder();
+            
+            text.append(types[0].getDescriptor(language) + " " + names[0]);
+            for (int i = 1; i < names.length; ++i)
+            {
+                text.append(", " + types[i].getDescriptor(language) + " " + names[i]);
+            }
+            
+            return text.toString();
+        }
+        
+        private String readFile(File file) throws IOException
+        {
+            StringBuilder text = new StringBuilder();
+        
+            BufferedReader reader = new BufferedReader(new FileReader(file.getPath()));
+            try
+            {
+                String line = null;
+                
+                while ((line = reader.readLine()) != null)
+                {
+                    text.append(line + System.getProperty("line.separator"));
+                }
+            }
+            finally
+            {
+                reader.close();
+            }
+        
+            return text.toString();
+        }
+        
+        private String readResource(String path) throws IOException
+        {
+            StringBuilder text = new StringBuilder();
+            
+            InputStream stream = getClass().getResourceAsStream("resources/" + path);
+            if (stream != null)
+            {
+                try
+                {
+                    byte[]  buffer = new byte[4096];
+                    int     numBytes = 0;
+                    while (0 < (numBytes = stream.read(buffer))) text.append(new String(buffer, 0, numBytes));
+                }
+                finally
+                {
+                    stream.close();
+                }
+            }
+            
+            return text.toString();
+        }
+        
+        private String expandTemplate(String template, Map<String,String> terms)
+        {
+            String text = template;
+            for (String key : terms.keySet())
+            {
+                text = text.replaceAll("\\$" + key + "\\$", quote(terms.get(key)));
+            }
+            return text;
+        }
+    }
+    
+    
+    public static <T> T[] concat(T[] a, T[] b)
+    {
+        T[] result = Arrays.copyOf(a, a.length + b.length);
+        System.arraycopy(b, 0, result, a.length, b.length);
+        return result;
+    }
+    
+    public static String join(String[] a, String glue)
+    {
+        if (a.length == 0) return "";
+        StringBuilder result = new StringBuilder();
+        result.append(a[0]);
+        for (int i = 1; i < a.length; ++i) result.append(glue).append(a[i]);
+        return result.toString();
+    }
+    
+    public static String quote(String a)
+    {
+        a = a.replaceAll("\\\\", "\\\\\\\\");
+        a = a.replaceAll("\"",   "\\\\\\\"");
+        return a;
+    }
+
+   
+    public VimCoder()
+    {
+        logArea = new JTextArea();
+        logArea.setForeground(Color.GREEN);
+        logArea.setBackground(Color.BLACK);
+        logArea.setEditable(false);
+        Font font = new Font("Courier", Font.PLAIN, 12);
+        if (font != null) logArea.setFont(font);
+           
+        panel = new JPanel(new BorderLayout());
+        panel.add(new JScrollPane(logArea), BorderLayout.CENTER);
+    }
+
+    
+    public void startUsing()
+    {
+        logArea.setText("");
+    }
+    
+    public void stopUsing()
+    {
+        editor = null;
+    }
+    
+    public JPanel getEditorPanel()
+    {
+        return panel;
+    }
+   
+    public String getSource()
+    {
+        try
+        {
+            String source = editor.getSource();
+            logInfo("Source code uploaded to server.");
+            return source;
+        }
+        catch (IOException exception)
+        {
+            logError("failed to open file source file for reading");
+            return "";
+        }
+    }
+   
+    public void setSource(String source)
+    {
+        try
+        {
+            editor.setSource(source);
+            logInfo("source set");
+        }
+        catch (IOException exception)
+        {
+            logError("failed setting the source");
+            return;
+        }
+    }
+    
+    public void setProblemComponent(ProblemComponentModel component, Language language, Renderer renderer)
+    {
+        try
+        {
+            editor = new Editor(component, language, renderer);
+        }
+        catch (IOException exception)
+        {
+            logError("failed while loading the problem");
+        }
+    }
+
+    public void setTextEnabled(Boolean enable)
+    {
+        editor.setTextEnabled(enable);
+    }
+    
+    
+    private void log(String what)
+    {
+        SimpleDateFormat format = new SimpleDateFormat("kk:mm:ss");
+        String time = format.format(new Date());
+        logArea.append(time + ", " + what);
+    }
+    
+    private void logInfo(String what)
+    {
+        log(" INFO: " + what + "\n");
+    }
+    
+    private void logWarning(String what)
+    {
+        log(" WARN: " + what + "\n");
+    }
+    
+    private void logError(String what)
+    {
+        log("ERROR: " + what + "\n");
+    }
+    
+    
+    public static void main(String args[])
+    {
+        VimCoder plugin = new VimCoder();
+        
+        JFrame frame = new JFrame("VimCoder");
+        frame.add(plugin.getEditorPanel());
+        frame.setSize(640, 480);
+        frame.setVisible(true);
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        
+        plugin.startUsing();
+    }
+}
+
diff --git a/src/com/dogcows/resources/C++Driver b/src/com/dogcows/resources/C++Driver
new file mode 100644 (file)
index 0000000..9f25901
--- /dev/null
@@ -0,0 +1,102 @@
+
+#include "$CLASSNAME$.cc"
+
+#include <iostream>
+#include <sstream>
+#include <sys/time.h>
+
+static bool            __exit_on_fail = false;
+static int             __pass = 0;
+static int             __fail = 0;
+static double  __time = 0.0;
+
+static void __timer_start()
+{
+       struct timeval tv;
+       if (gettimeofday(&tv, NULL) == 0)
+       {
+               __time = (double)tv.tv_sec + (double)tv.tv_usec * 0.000001;
+       }
+}
+
+static double __timer_stop()
+{
+       double start = __time;
+       __timer_start();
+       return __time - start;
+}
+
+template <class T>
+std::string __encode(const T& in)
+{
+       std::ostringstream s;
+       s << in;
+       return s.str();
+}
+
+std::string __encode(const std::string& in)
+{
+       std::ostringstream s;
+       s << '"' << in << '"';
+       return s.str();
+}
+
+template <class T>
+std::string __join(const std::vector<T>& in, const std::string& glue)
+{
+       if (in.size() == 0) return "";
+       std::ostringstream s;
+       s << __encode(in[0]);
+       for (size_t i = 1; i < in.size(); ++i) s << glue << __encode(in[i]);
+       return s.str();
+}
+
+template <class T>
+std::string __encode(const std::vector<T>& in)
+{
+       std::ostringstream s;
+       s << "{ " << __join(in, ",  ") << " }";
+       return s.str();
+}
+
+void __do_test(const std::string& expected, $METHODPARAMS$)
+{
+       static int testNum = 0;
+       std::cout << "----------------------------------------" << std::endl
+                         << "Test " << testNum++ << ": ";
+
+       __timer_start();
+       
+       $CLASSNAME$ object;
+       $RETURNTYPE$ ret = object.$METHODNAME$($METHODPARAMNAMES$);
+       
+       double t = __timer_stop();
+       
+       std::string actual = __encode(ret);
+       if (actual == expected)
+       {
+               std::cout << "[PASS] in " << t << " seconds." << std::endl;
+               ++__pass;
+       }
+       else
+       {
+               std::cout << "[FAIL] in " << t << " seconds." << std::endl
+                                 << "   Actual: " << actual << std::endl
+                                 << " Expected: " << expected << std::endl;
+               ++__fail;
+               if (__exit_on_fail) exit(1);
+       }
+}
+
+int main(int argc, char* argv[])
+{
+       if (1 < argc) __exit_on_fail = true;
+
+$TESTCASES$
+       std::cout << "========================================" << std::endl
+                         << " Total Pass: " << __pass << std::endl
+                         << " Total Fail: " << __fail << std::endl;
+
+       return __fail;
+}
+
diff --git a/src/com/dogcows/resources/C++Makefile b/src/com/dogcows/resources/C++Makefile
new file mode 100644 (file)
index 0000000..b1cb4ed
--- /dev/null
@@ -0,0 +1,17 @@
+
+CXX                    := g++
+CXXFLAGS       := -O0 -g -Wall
+
+all: driver
+       
+run: all
+       ./driver
+       
+test: all
+       ./driver -exit_on_fail
+
+driver: driver.o
+       $(CXX) $(CXXFLAGS) $< -o $@ $(LDFLAGS) $(LIBS)
+
+.PHONY: all run test
+
diff --git a/src/com/dogcows/resources/C++Template b/src/com/dogcows/resources/C++Template
new file mode 100644 (file)
index 0000000..e106cb5
--- /dev/null
@@ -0,0 +1,42 @@
+
+// {{{ Boilerplate Code <--------------------------------------------------
+//
+// vim:filetype=cpp foldmethod=marker foldmarker={{{,}}}
+
+#include <algorithm>
+#include <bitset>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
+#include <deque>
+#include <functional>
+#include <iomanip>
+#include <iostream>
+#include <list>
+#include <map>
+#include <numeric>
+#include <queue>
+#include <set>
+#include <sstream>
+#include <stack>
+#include <utility>
+#include <vector>
+
+#define FOR(I,A,B)     for(int I = (A); I < (B); ++I)
+#define REP(I,N)       FOR(I,0,N)
+#define ALL(A)         (A).begin(), (A).end()
+
+using namespace std;
+
+// }}}
+
+class $CLASSNAME$
+{
+public:
+       $RETURNTYPE$ $METHODNAME$($METHODPARAMS$)
+       {
+               $CARAT$
+       }
+};
+
diff --git a/src/com/dogcows/resources/C++Test b/src/com/dogcows/resources/C++Test
new file mode 100644 (file)
index 0000000..4316d38
--- /dev/null
@@ -0,0 +1 @@
+       __do_test($TESTOUTPUT$, $TESTINPUTS$);
This page took 0.044342 seconds and 4 git commands to generate.