Skip to content

Commit 02eaab9

Browse files
committed
Add code snippets; use PlatformService to open URL
1 parent a22eb98 commit 02eaab9

File tree

7 files changed

+259
-17
lines changed

7 files changed

+259
-17
lines changed

src/main/java/org/scijava/search/Searcher.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ public interface Searcher extends SciJavaPlugin {
2222
*/
2323
String title();
2424

25+
/** Gets whether this plugin wants exclusive rights to the given text. */
26+
default boolean exclusive(@SuppressWarnings("unused") final String text) {
27+
return false;
28+
}
29+
2530
/** Searches for the given text. */
2631
List<SearchResult> search(String text, boolean fuzzy);
2732
}

src/main/java/org/scijava/search/module/ModuleSearchResult.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,13 @@ public class ModuleSearchResult implements SearchResult {
2222

2323
private final ModuleInfo info;
2424
private final String baseDir;
25-
private HashMap<String, String> props;
25+
private final HashMap<String, String> props;
2626

2727
public ModuleSearchResult(final ModuleInfo info, final String baseDir) {
2828
this.info = info;
2929
this.baseDir = baseDir;
3030

3131
props = new HashMap<>();
32-
props.put("Hello", "World");
3332
props.put("Title", info.getTitle());
3433
final MenuPath menuPath = info.getMenuPath();
3534
if (menuPath != null) {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
package org.scijava.search.snippet;
3+
4+
import org.scijava.plugin.Parameter;
5+
import org.scijava.plugin.Plugin;
6+
import org.scijava.script.ScriptREPL;
7+
import org.scijava.script.ScriptService;
8+
import org.scijava.search.DefaultSearchAction;
9+
import org.scijava.search.SearchAction;
10+
import org.scijava.search.SearchActionFactory;
11+
import org.scijava.search.SearchResult;
12+
13+
/**
14+
* Search action for executing a code snippet.
15+
*
16+
* @author Curtis Rueden
17+
*/
18+
@Plugin(type = SearchActionFactory.class)
19+
public class RunSnippetActionFactory implements SearchActionFactory {
20+
21+
/** The singleton REPL shared by all snippet executions. */
22+
private ScriptREPL repl;
23+
24+
@Parameter
25+
private ScriptService scriptService;
26+
27+
@Override
28+
public boolean supports(final SearchResult result) {
29+
return result instanceof SnippetSearchResult;
30+
}
31+
32+
@Override
33+
public SearchAction create(final SearchResult result) {
34+
final SnippetSearchResult snippetResult = (SnippetSearchResult) result;
35+
36+
if (repl == null) {
37+
repl = new ScriptREPL(scriptService.context());
38+
repl.initialize(); // TODO: initialize(false) once it exists.
39+
}
40+
41+
return new DefaultSearchAction("Evaluate", () -> {
42+
repl.lang(snippetResult.language().getLanguageName());
43+
repl.evaluate(snippetResult.snippet());
44+
});
45+
}
46+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package org.scijava.search.snippet;
2+
3+
import java.util.HashMap;
4+
import java.util.List;
5+
import java.util.Map;
6+
7+
import org.scijava.script.ScriptLanguage;
8+
import org.scijava.search.SearchResult;
9+
import org.scijava.search.module.ModuleSearcher;
10+
11+
/**
12+
* Search result for the {@link ModuleSearcher}.
13+
*
14+
* @author Curtis Rueden
15+
*/
16+
public class SnippetSearchResult implements SearchResult {
17+
18+
private final ScriptLanguage language;
19+
private final String snippet;
20+
private final HashMap<String, String> props;
21+
22+
public SnippetSearchResult(final ScriptLanguage language,
23+
final String snippet)
24+
{
25+
this.language = language;
26+
this.snippet = snippet;
27+
28+
props = new HashMap<>();
29+
props.put("Language", language.getLanguageName());
30+
props.put("Nicknames", s(language.getNames()));
31+
props.put("Extensions", s(language.getExtensions()));
32+
props.put("Engine Name", language.getEngineName());
33+
props.put("Engine Version", language.getEngineVersion());
34+
props.put("MIME Types", s(language.getMimeTypes()));
35+
}
36+
37+
public ScriptLanguage language() {
38+
return language;
39+
}
40+
41+
public String snippet() {
42+
return snippet;
43+
}
44+
45+
@Override
46+
public String name() {
47+
return language.getLanguageName() + ": " + snippet;
48+
}
49+
50+
@Override
51+
public String iconPath() {
52+
return null;
53+
// return "/icons/" + language.getNames().get(0);
54+
}
55+
56+
@Override
57+
public Map<String, String> properties() {
58+
return props;
59+
}
60+
61+
// -- Helper methods --
62+
63+
private String s(final List<String> names) {
64+
if (names == null || names.isEmpty()) return "<None>";
65+
StringBuilder sb = new StringBuilder();
66+
for (int i = 0; i < names.size(); i++) {
67+
if (i > 0) sb.append(", ");
68+
sb.append(names.get(i));
69+
}
70+
return sb.toString();
71+
}
72+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.search.snippet;
33+
34+
import java.util.ArrayList;
35+
import java.util.Collections;
36+
import java.util.List;
37+
import java.util.stream.Collectors;
38+
39+
import org.scijava.Priority;
40+
import org.scijava.plugin.Parameter;
41+
import org.scijava.plugin.Plugin;
42+
import org.scijava.script.ScriptLanguage;
43+
import org.scijava.script.ScriptService;
44+
import org.scijava.search.SearchResult;
45+
import org.scijava.search.Searcher;
46+
47+
/**
48+
* {@link Searcher} plugin for code snippets.
49+
*
50+
* @author Curtis Rueden
51+
*/
52+
@Plugin(type = Searcher.class, priority = Priority.EXTREMELY_HIGH)
53+
public class SnippetSearcher implements Searcher {
54+
55+
@Parameter
56+
private ScriptService scriptService;
57+
58+
@Override
59+
public String title() {
60+
// NB: A misnomer, but it's the term users are familiar with.
61+
return "Code snippets";
62+
}
63+
64+
@Override
65+
public boolean exclusive(final String text) {
66+
return text.startsWith("#!") || text.startsWith("!");
67+
}
68+
69+
@Override
70+
public List<SearchResult> search(final String text, final boolean fuzzy) {
71+
if (text.startsWith("#!")) {
72+
final String[] tokens = text.split("\\s", 2);
73+
if (tokens.length < 2) return Collections.emptyList();
74+
final String langHint = tokens[0];
75+
final String snippet = tokens[1];
76+
77+
return results(scriptService.getLanguages().stream().filter(language ->
78+
matches(langHint, language.getLanguageName()) ||
79+
matches(langHint, language.getNames()) ||
80+
matches(langHint, language.getExtensions())
81+
).collect(Collectors.toList()), snippet);
82+
}
83+
if (text.startsWith("!")) {
84+
return results(scriptService.getLanguages(), text.substring(1));
85+
}
86+
return Collections.emptyList();
87+
}
88+
89+
// -- Helper methods --
90+
91+
private boolean matches(String actual, List<String> desiredList) {
92+
return desiredList.stream().filter( //
93+
desired -> matches(actual, desired)).findAny().isPresent();
94+
}
95+
96+
private boolean matches(final String actual, final String desired) {
97+
return actual.toLowerCase().matches(".*" + desired + ".*");
98+
}
99+
100+
private List<SearchResult> results(final List<ScriptLanguage> languages,
101+
final String snippet)
102+
{
103+
final List<SearchResult> results = new ArrayList<>();
104+
for (final ScriptLanguage language : languages) {
105+
results.add(new SnippetSearchResult(language, snippet));
106+
}
107+
return results;
108+
}
109+
}
Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11

22
package org.scijava.search.web;
33

4-
import java.awt.Desktop;
54
import java.io.IOException;
6-
import java.net.URI;
7-
import java.net.URISyntaxException;
5+
import java.net.URL;
86

7+
import org.scijava.log.LogService;
8+
import org.scijava.platform.PlatformService;
9+
import org.scijava.plugin.Parameter;
910
import org.scijava.plugin.Plugin;
1011
import org.scijava.search.DefaultSearchAction;
1112
import org.scijava.search.SearchAction;
@@ -22,23 +23,30 @@ public class OpenInBrowserActionFactory implements
2223
SearchActionFactory
2324
{
2425

26+
@Parameter
27+
private PlatformService platformService;
28+
29+
@Parameter
30+
private LogService log;
31+
2532
@Override
2633
public boolean supports(final SearchResult result) {
2734
return result instanceof WebSearchResult;
2835
}
2936

3037
@Override
3138
public SearchAction create(final SearchResult result) {
32-
return new DefaultSearchAction("Open in Browser", () -> {
33-
try {
34-
Desktop.getDesktop().browse(new URI(result.properties().get("url")));
35-
}
36-
catch (final IOException e1) {
37-
e1.printStackTrace();
38-
}
39-
catch (final URISyntaxException e1) {
40-
e1.printStackTrace();
41-
}
42-
});
39+
return new DefaultSearchAction("Open in Browser", //
40+
() -> openURL(result));
41+
}
42+
43+
private void openURL(final SearchResult result) {
44+
try {
45+
final URL url = new URL(result.properties().get("url"));
46+
platformService.open(url);
47+
}
48+
catch (final IOException exc) {
49+
log.error(exc);
50+
}
4351
}
4452
}

src/main/java/org/scijava/ui/swing/search/SwingSearchBar.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,11 +429,14 @@ private void rebuild() {
429429
// Build the new list model.
430430
DefaultListModel<SearchResult> listModel = new DefaultListModel<>();
431431
for (final Searcher searcher : searchers) {
432+
final List<SearchResult> results = results(searcher);
433+
if (results.isEmpty()) continue;
434+
432435
// Add section header.
433436
listModel.addElement(new SearchResultHeader(searcher.title()));
434437

435438
// Add results as entries.
436-
for (final SearchResult result : results(searcher)) {
439+
for (final SearchResult result : results) {
437440
listModel.addElement(result);
438441
}
439442
}

0 commit comments

Comments
 (0)