PageRenderTime 74ms CodeModel.GetById 31ms app.highlight 21ms RepoModel.GetById 19ms app.codeStats 0ms

/bundles/plugins-trunk/RubyPlugin/src/org/jedit/ruby/ri/RDocSeacher.java

#
Java | 235 lines | 161 code | 34 blank | 40 comment | 24 complexity | f5ca29865bed7203e047eec2375c58eb MD5 | raw file
  1/*
  2 * RDocSeacher.java - 
  3 *
  4 * Copyright 2005 Robert McKinnon
  5 *
  6 * This program is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU General Public License
  8 * as published by the Free Software Foundation; either version 2
  9 * of the License, or any later version.
 10 *
 11 * This program is distributed in the hope that it will be useful,
 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14 * GNU General Public License for more details.
 15 *
 16 * You should have received a copy of the GNU General Public License
 17 * along with this program; if not, write to the Free Software
 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 19 */
 20package org.jedit.ruby.ri;
 21
 22import org.gjt.sp.jedit.View;
 23import org.gjt.sp.jedit.jEdit;
 24import org.gjt.sp.jedit.Macros;
 25import org.jedit.ruby.ast.Member;
 26import org.jedit.ruby.ast.Method;
 27import org.jedit.ruby.structure.TypeAheadPopup;
 28import org.jedit.ruby.RubyPlugin;
 29import org.jedit.ruby.utils.CommandUtils;
 30
 31import javax.swing.*;
 32import java.awt.event.ActionEvent;
 33import java.awt.event.KeyEvent;
 34import java.io.*;
 35import java.util.*;
 36
 37/**
 38 * Allows user to search Ruby documentation using ri - Ruby interactive reference.<ul><li>
 39 *   Brings up dialog for user to enter search term.</li><li>
 40 *   Macro runs ri on term, and reports ri results in another dialog.</li><li>
 41 *   Remembers last term searched, and places it in search entry field.</li><li>
 42 *   If user has text selected, then that is placed in search entry field instead.</li></ul>
 43 *
 44 * @author robmckinnon at users.sourceforge.net
 45 */
 46public final class RDocSeacher {
 47
 48    private static final RDocSeacher instance = new RDocSeacher();
 49
 50    private final Map<String, String> termToResult;
 51
 52    /**
 53     * singleton private constructor
 54     */
 55    private RDocSeacher() {
 56        termToResult = new HashMap<String, String>();
 57    }
 58
 59    public static void doSearch(View view) throws IOException, InterruptedException {
 60        instance.performSearch(view);
 61    }
 62
 63    public static void doSearch(View view, String searchTerm) {
 64        try {
 65            instance.searchFor(searchTerm, view);
 66        } catch (IOException e) {
 67            e.printStackTrace();
 68            RubyPlugin.error(e, RDocSeacher.class);
 69        } catch (InterruptedException e) {
 70            e.printStackTrace();
 71            RubyPlugin.error(e, RDocSeacher.class);
 72        }
 73    }
 74
 75    /**
 76     * Performs Ruby documentation search.
 77     */
 78    private void performSearch(View view) throws IOException, InterruptedException {
 79        String term = getSearchTerm(view, view.getTextArea().getSelectedText());
 80        if (term != null) {
 81            jEdit.setProperty("ruby-ri-search-term", term);
 82            searchFor(term, view);
 83        }
 84    }
 85
 86    private void searchFor(String term, View view) throws IOException, InterruptedException {
 87        String result = ri(term);
 88
 89        if(result.startsWith("More than one method matched your request.")) {
 90            List<Member> methods = parseMultipleResults(result);
 91            Member[] members = methods.toArray(new Member[methods.size()]);
 92            new TypeAheadPopup(view, members, members[0], org.jedit.ruby.structure.TypeAheadPopup.SEARCH_POPUP);
 93        } else {
 94            showDialog(view, "", result);
 95        }
 96    }
 97
 98    public static List<Member> parseMultipleResults(String result) {
 99        StringTokenizer lines = new StringTokenizer(result, "\n");
100        List<Member> methods = new ArrayList<Member>();
101
102        while(lines.hasMoreTokens()) {
103            String line = lines.nextToken();
104            if(line.startsWith(" ")) {
105                StringTokenizer tokenizer = new StringTokenizer(line.trim(), ", ");
106                while(tokenizer.hasMoreTokens()) {
107                    String namespace = null;
108                    String methodName = tokenizer.nextToken();
109                    int index = methodName.lastIndexOf("#");
110                    int otherIndex = methodName.lastIndexOf("::");
111                    index = Math.max(index, otherIndex);
112                    boolean isClassMethod = index == otherIndex;
113                    int adj = isClassMethod ? 2 : 1;
114
115                    if (index != -1) {
116                        namespace = methodName.substring(0, index);
117                        methodName = methodName.substring(index + adj);
118                    }
119
120                    Method method = new Method(methodName, null, "none", "none", isClassMethod);
121                    method.setNamespace(namespace);
122                    methods.add(method);
123                }
124            }
125        }
126
127        Collections.sort(methods);
128        return methods;
129    }
130
131    /**
132     * Runs ri on supplied string search term and returns result string.
133     */
134    private String ri(String searchTerm) throws IOException, InterruptedException {
135        if (searchTerm.length() == 0) {
136            searchTerm = "-c";
137        }
138
139        if (termToResult.containsKey(searchTerm)) {
140            return termToResult.get(searchTerm);
141
142        } else {
143            String result;
144            if (CommandUtils.isWindows()) {
145                result = CommandUtils.getOutput("ri.bat -T " + '"' + searchTerm + '"', true);
146            } else {
147                result = CommandUtils.getOutput("ri -T " + searchTerm, true);
148            }
149
150            if (result.length() == 0) {
151                result = rri(searchTerm);
152            }
153
154            if (result == null) {
155                result = jEdit.getProperty("ruby.search-documentation.error");
156            } else {
157                termToResult.put(searchTerm, result);
158            }
159
160            return result;
161        }
162    }
163
164    private static String rri(String searchTerm) throws IOException, InterruptedException {
165        File resultFile = CommandUtils.getStoragePath("ri_result.txt");
166        String command = getRriCommand(resultFile, searchTerm);
167        CommandUtils.getOutput(command, false);
168        return RubyPlugin.readFile(resultFile);
169    }
170
171    private static String getRriCommand(File resultFile, String searchTerm) throws IOException, InterruptedException {
172        if (CommandUtils.isWindows()) {
173            String text = "exec ri.bat -T %1 > " + '"' + resultFile + '"' + '\n';
174            File commandFile = CommandUtils.getCommandFile("rri.bat", false, text);
175            return '"' + commandFile.getPath() + '"' + ' ' + '"' + searchTerm + '"';
176        } else {
177            String text = "#!/bin/sh\n"
178                                + "ri -T $1 > " + resultFile + '\n';
179            File commandFile = CommandUtils.getCommandFile("rri.sh", false, text);
180            return commandFile.getPath() + ' ' + searchTerm;
181        }
182    }
183
184    private static JScrollPane getScrollPane(JTextArea label, Action closeAction) {
185        JScrollPane scrollPane = new JScrollPane(label);
186        final String CLOSE = "close";
187        scrollPane.getActionMap().put(CLOSE, closeAction);
188
189        InputMap inputMap = scrollPane.getInputMap();
190        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), CLOSE);
191        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), CLOSE);
192        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), CLOSE);
193        return scrollPane;
194    }
195
196    private void showDialog(View frame, String title, String text) {
197        final JDialog dialog = new JDialog(frame, title, false);
198        JTextArea label = new JTextArea(text);
199        label.setEditable(true);
200        label.setBackground(dialog.getContentPane().getBackground());
201        JScrollPane pane = getScrollPane(label, new AbstractAction() {
202            public void actionPerformed(ActionEvent e) {
203                dialog.setVisible(false);
204                dialog.dispose();
205            }
206        });
207
208        dialog.setContentPane(pane);
209        dialog.pack();
210
211        int height = dialog.getHeight();
212        if (dialog.getHeight() > frame.getHeight() * .8) {
213            height = (int) (frame.getHeight() * .8);
214        }
215
216        dialog.setSize((int) (dialog.getWidth() * 1.05), height);
217        dialog.setLocationRelativeTo(frame);
218
219        dialog.setVisible(true);
220    }
221
222    /**
223     * Displays dialog for user to enter search term.
224     */
225    private static String getSearchTerm(View view, String term) {
226        if (term == null) {
227            term = jEdit.getProperty("ruby-ri-search-term", "");
228        }
229
230        String label = jEdit.getProperty("ruby.search-documentation.dialog.label");
231        term = Macros.input(view, label, term);
232        return term;
233    }
234
235}