X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fvimcoder;a=blobdiff_plain;f=src%2Fcom%2Fdogcows%2FVimCoder.java;h=658bae13051ddc080a266bd9610cec8bf721929e;hp=489e78960499154a4b657f59d0c657b9ee9a29ce;hb=28c8fae03b2294d6486233cd62f4d4d9d11603ca;hpb=8a2b2bbbe6e13cee2f5118e1ce814bf17f058299 diff --git a/src/com/dogcows/VimCoder.java b/src/com/dogcows/VimCoder.java index 489e789..658bae1 100644 --- a/src/com/dogcows/VimCoder.java +++ b/src/com/dogcows/VimCoder.java @@ -1,14 +1,19 @@ package com.dogcows; -import javax.swing.*; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeListener; import java.io.*; import java.text.SimpleDateFormat; import java.util.*; +import javax.swing.*; + +import com.topcoder.client.contestApplet.common.Common; +import com.topcoder.client.contestApplet.common.LocalPreferences; import com.topcoder.client.contestant.ProblemComponentModel; -import com.topcoder.shared.language.*; -import com.topcoder.shared.problem.*; +import com.topcoder.shared.language.Language; import com.topcoder.shared.problem.Renderer; /** @@ -20,384 +25,379 @@ import com.topcoder.shared.problem.Renderer; */ 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 languageExtension = new HashMap(); - 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 terms = new HashMap(); - 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 testTerms = new HashMap(); - 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", ":if search('\\$CARAT\\\\$') != 0normal df$endif:redraw"); - } - - public String getSource() throws IOException - { - return readFile(sourceFile) + "\n// Edited by " + version + "\n// " + website + "\n\n"; - } - - public void setTextEnabled(boolean enable) - { - doVimCommand("--remote-send", ":set readonly:echo \"The contest is over.\""); - } - - - 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 terms) - { - String text = template; - for (String key : terms.keySet()) - { - text = text.replaceAll("\\$" + key + "\\$", quote(terms.get(key))); - } - return text; - } - } - - - public static 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(); - } + /** + * The name and version of this plugin. + */ + public final static String version = "VimCoder 0.3"; + + /** + * The website of the plugin project. + */ + public final static String website = "http://www.dogcows.com/vimcoder"; + + + /** + * The first part of the command used to invoke the Vim server. + */ + private static String vimCommand = "gvim"; + + /** + * The path to the main VimCoder directory. + */ + private static File rootDir; + static + { + if (System.getProperty("os.name").toLowerCase().equals("win")) + { + vimCommand = "C:\\WINDOWS\\gvim.bat"; + } + rootDir = new File(System.getProperty("user.home") + + System.getProperty("file.separator") + ".vimcoder"); + } + + + /** + * The panel given to the Arena applet when it is requested. + */ + private JPanel panel; + + /** + * The text widget where log messages are appended. + */ + private JTextArea logArea; + + /** + * The current editor object (or null if there is none). + */ + private Editor editor; + + /** + * The configuration panel. + */ + private JDialog configDialog; + + + /** + * The key for the vim command preference. + */ + private final static String VIMCOMMAND = "com.dogcows.VimCoder.config.vimcommand"; + + /** + * The key for the root directory preference. + */ + private final static String ROOTDIR = "com.dogcows.VimCoder.config.rootdir"; + + /** + * The preferences object for storing plugin settings. + */ + private static LocalPreferences prefs = LocalPreferences.getInstance(); + + + /** + * Get the command for invoking vim. + * @return The command. + */ + public static String getVimCommand() + { + return vimCommand; + } + + /** + * Get the storage directory. + * @return The directory. + */ + public static File getStorageDirectory() + { + return rootDir; + } + + + /** + * Instantiate the entry point of the editor plugin. + * Sets up the log widget and panel. + */ + 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); + } + + + /** + * Called by the Arena when the plugin is about to be used. + */ + public void startUsing() + { + Runnable task = new Runnable() + { + public void run() + { + logArea.setText(""); + } + }; + if (SwingUtilities.isEventDispatchThread()) + { + task.run(); + } + else + { + SwingUtilities.invokeLater(task); + } + loadConfiguration(); + } + + /** + * Called by the Arena when the plugin is no longer needed. + */ + public void stopUsing() + { + editor = null; + } + + /** + * Called by the Arena to obtain the editor panel which we will use to + * show log messages. + * @return The editor panel. + */ + public JPanel getEditorPanel() + { + return panel; + } + + /** + * Called by the Arena to obtain the current source. + * This happens when the user is saving, compiling, and/or submitting. + * @return The current source code. + * @throws Exception If the source file edited by Vim couldn't be read. + */ + public String getSource() throws Exception + { + try + { + String source = editor.getSource(); + logInfo("Source code uploaded to server."); + return source; + } + catch (Exception exception) + { + logError("Failed to get source code: " + + exception.getLocalizedMessage()); + throw exception; + } + } + + /** + * Called by the Arena to pass the source it has. + * @param source The source code. + */ + public void setSource(String source) + { + try + { + editor.setSource(source); + logInfo("Source code downloaded from server."); + } + catch (Exception exception) + { + logError("Failed to save the source given by the server: " + + exception.getLocalizedMessage()); + return; + } + } + + /** + * Called by the Arena to pass along information about the current + * problem. + * @param component A container for the particulars of the problem. + * @param language The currently selected language. + * @param renderer A helper object to help format the problem + * statement. + */ + public void setProblemComponent(ProblemComponentModel component, + Language language, + Renderer renderer) + { + try + { + editor = new Editor(component, language, renderer); + } + catch (Exception exception) + { + logError("An error occured while loading the problem: " + + exception.getLocalizedMessage()); + } + } + + /** + * Called by the Arena when it's time to show our configuration panel. + */ + public void configure() + { + loadConfiguration(); + + configDialog = new JDialog(); + Container pane = configDialog.getContentPane(); + + pane.setPreferredSize(new Dimension(550, 135)); + pane.setLayout(new GridBagLayout()); + pane.setForeground(Common.FG_COLOR); + pane.setBackground(Common.WPB_COLOR); + GridBagConstraints c = new GridBagConstraints(); + + JLabel rootDirLabel = new JLabel("Storage Directory:", SwingConstants.RIGHT); + rootDirLabel.setForeground(Common.FG_COLOR); + c.fill = GridBagConstraints.HORIZONTAL; + c.insets = new Insets(5, 5, 5, 5); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + pane.add(rootDirLabel, c); + + final JTextField rootDirField = new JTextField(rootDir.getPath(), 25); + c.gridx = 1; + c.gridy = 0; + pane.add(rootDirField, c); + + JButton browseButton = new JButton("Browse"); + c.gridx = 2; + c.gridy = 0; + c.anchor = GridBagConstraints.BASELINE_LEADING; + pane.add(browseButton, c); + + JLabel vimCommandLabel = new JLabel("Vim Command:", SwingConstants.RIGHT); + vimCommandLabel.setForeground(Common.FG_COLOR); + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 0; + c.gridy = 1; + pane.add(vimCommandLabel, c); + + final JTextField vimCommandField = new JTextField(vimCommand, 25); + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 2; + pane.add(vimCommandField, c); + + JButton closeButton = new JButton("Cancel"); + c.fill = GridBagConstraints.NONE; + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 1; + c.anchor = GridBagConstraints.EAST; + pane.add(closeButton, c); + + JButton saveButton = new JButton("Save"); + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 2; + c.gridy = 2; + c.anchor = GridBagConstraints.EAST; + pane.add(saveButton, c); + configDialog.getRootPane().setDefaultButton(saveButton); + + browseButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent actionEvent) + { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(".")); + chooser.setDialogTitle("Choose Storage Directory"); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + + if (chooser.showOpenDialog(configDialog) == JFileChooser.APPROVE_OPTION) + { + rootDirField.setText(chooser.getSelectedFile().getPath()); + } + } + }); + + closeButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent actionEvent) + { + configDialog.dispose(); + } + }); + + saveButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent actionEvent) + { + prefs.setProperty(VIMCOMMAND, vimCommandField.getText()); + prefs.setProperty(ROOTDIR, rootDirField.getText()); + configDialog.dispose(); + } + }); + + configDialog.setTitle("VimCoder Preferences"); + configDialog.pack(); + configDialog.setLocationByPlatform(true); + configDialog.setModalityType(Dialog.DEFAULT_MODALITY_TYPE); + configDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + configDialog.setVisible(true); + } + + + /** + * Load the local preferences related to this plugin. + */ + private void loadConfiguration() + { + String vc = prefs.getProperty(VIMCOMMAND); + if (vc != null) vimCommand = vc; + + String dir = prefs.getProperty(ROOTDIR); + if (dir != null) rootDir = new File(dir); + } + + + /** + * A generic logging function, appends text to the text area. A timestamp + * is also prepended to the next text. + * @param what The text to append. + */ + private void log(final String what) + { + Runnable task = new Runnable() + { + public void run() + { + SimpleDateFormat format = new SimpleDateFormat("kk:mm:ss"); + logArea.append(format.format(new Date()) + ", " + what); + } + }; + if (SwingUtilities.isEventDispatchThread()) + { + task.run(); + } + else + { + SwingUtilities.invokeLater(task); + } + } + + /** + * Output non-critical messages to the log. + * @param what The text of the message. + */ + private void logInfo(String what) + { + log(" INFO: " + what + System.getProperty("line.separator")); + } + + /** + * Output critical messages and errors to the log. + * @param what The text of the message. + */ + private void logError(String what) + { + log("ERROR: " + what + System.getProperty("line.separator")); + } }