Skip to content

Commit d6f720b

Browse files
committed
Initial commit
0 parents  commit d6f720b

File tree

6 files changed

+235
-0
lines changed

6 files changed

+235
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/.idea
2+
/out
3+
*.iml

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Evgeniy NeoFusion
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

resources/META-INF/plugin.xml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<idea-plugin url="https://github.com/NeoFusion/idea-external-tools-text-replace-plugin">
2+
<id>ru.neofusion.ExternalToolsTextReplace</id>
3+
<name>External Tools Text Replace</name>
4+
<version>0.1.0</version>
5+
<vendor email="evgeniy@neofusion.ru" url="http://neofusion.ru">Evgeniy NeoFusion</vendor>
6+
7+
<description><![CDATA[
8+
<p>Extension for <b>External Tools</b> allows to replace text selection with external program stdout result.</p>
9+
<br>
10+
<p><b>Usage:</b>
11+
<ul>
12+
<li>Add new external tool (use <i>SelectedText</i> as an argument)</li>
13+
<li>Select some text in the editor</li>
14+
<li>Invoke tool from the <b>External Tools Replace</b> context menu group</li>
15+
</ul>
16+
</p>
17+
]]></description>
18+
19+
<idea-version since-build="173.0"/>
20+
21+
<depends>com.intellij.modules.platform</depends>
22+
23+
<extensions defaultExtensionNs="com.intellij"/>
24+
25+
<actions>
26+
<group id="ExternalToolsReplaceGroup" class="ExternalToolsReplaceGroup" popup="true" text="External Tools Replace">
27+
<add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="CompareClipboardWithSelection"/>
28+
</group>
29+
</actions>
30+
31+
</idea-plugin>

src/ExternalToolsReplaceGroup.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import com.intellij.openapi.actionSystem.ActionGroup;
2+
import com.intellij.openapi.actionSystem.AnAction;
3+
import com.intellij.openapi.actionSystem.AnActionEvent;
4+
import com.intellij.openapi.actionSystem.CommonDataKeys;
5+
import com.intellij.openapi.editor.Editor;
6+
import com.intellij.openapi.project.Project;
7+
import com.intellij.tools.Tool;
8+
import com.intellij.tools.ToolManager;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
import java.util.List;
13+
14+
public class ExternalToolsReplaceGroup extends ActionGroup {
15+
@Override
16+
public void update(@NotNull AnActionEvent event) {
17+
final Project project = event.getProject();
18+
final Editor editor = event.getData(CommonDataKeys.EDITOR);
19+
event.getPresentation().setEnabledAndVisible(project != null && editor != null && editor.getSelectionModel().hasSelection());
20+
}
21+
22+
@NotNull
23+
@Override
24+
public AnAction[] getChildren(@Nullable AnActionEvent event) {
25+
List<Tool> list = ToolManager.getInstance().getTools();
26+
AnAction[] actions = new AnAction[list.size()];
27+
int n = 0;
28+
for (Tool tool: list) {
29+
actions[n] = new PopupAction(tool);
30+
n++;
31+
}
32+
return actions;
33+
}
34+
}

src/PopupAction.java

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import com.intellij.execution.ExecutionException;
2+
import com.intellij.execution.configurations.GeneralCommandLine;
3+
import com.intellij.execution.process.NopProcessHandler;
4+
import com.intellij.execution.process.OSProcessHandler;
5+
import com.intellij.execution.process.ProcessEvent;
6+
import com.intellij.execution.process.ProcessListener;
7+
import com.intellij.execution.util.ExecutionErrorDialog;
8+
import com.intellij.ide.macro.MacroManager;
9+
import com.intellij.openapi.actionSystem.AnAction;
10+
import com.intellij.openapi.actionSystem.AnActionEvent;
11+
import com.intellij.openapi.actionSystem.CommonDataKeys;
12+
import com.intellij.openapi.actionSystem.DataContext;
13+
import com.intellij.openapi.editor.Document;
14+
import com.intellij.openapi.editor.Editor;
15+
import com.intellij.openapi.fileEditor.FileDocumentManager;
16+
import com.intellij.openapi.project.Project;
17+
import com.intellij.tools.HackyDataContext;
18+
import com.intellij.tools.Tool;
19+
import com.intellij.tools.ToolProcessAdapter;
20+
import com.intellij.tools.ToolsBundle;
21+
import com.intellij.tools.ToolsProvider;
22+
import org.jetbrains.annotations.NotNull;
23+
import org.jetbrains.annotations.Nullable;
24+
25+
public class PopupAction extends AnAction {
26+
private final String actionId;
27+
28+
public PopupAction(@NotNull Tool tool) {
29+
actionId = tool.getActionId();
30+
getTemplatePresentation().setText(tool.getName(), false);
31+
getTemplatePresentation().setDescription(tool.getDescription());
32+
}
33+
34+
@Override
35+
public void actionPerformed(@NotNull AnActionEvent event) {
36+
final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
37+
final Project project = event.getRequiredData(CommonDataKeys.PROJECT);
38+
final Document document = editor.getDocument();
39+
Tool tool = findTool(actionId, event.getDataContext());
40+
if (tool != null) {
41+
ReplaceAdapter processListener = new ReplaceAdapter(editor, project, document);
42+
execute(tool, new HackyDataContext(event.getDataContext()), processListener);
43+
}
44+
}
45+
46+
private static Tool findTool(String actionId, DataContext context) {
47+
MacroManager.getInstance().cacheMacrosPreview(context);
48+
for (Tool tool: ToolsProvider.getAllTools()) {
49+
if (actionId.equals(tool.getActionId())) {
50+
return tool;
51+
}
52+
}
53+
return null;
54+
}
55+
56+
private void execute(Tool tool, DataContext context, @Nullable final ProcessListener processListener) {
57+
if (!executeIfPossible(tool, context, processListener)) {
58+
notifyCouldNotStart(processListener);
59+
}
60+
}
61+
62+
private boolean executeIfPossible(Tool tool, DataContext context, @Nullable final ProcessListener processListener) {
63+
final Project project = CommonDataKeys.PROJECT.getData(context);
64+
if (project == null) {
65+
return false;
66+
}
67+
68+
FileDocumentManager.getInstance().saveAllDocuments();
69+
try {
70+
GeneralCommandLine commandLine = tool.createCommandLine(context);
71+
if (commandLine == null) {
72+
return false;
73+
}
74+
OSProcessHandler handler = new OSProcessHandler(commandLine);
75+
handler.addProcessListener(new ToolProcessAdapter(project, tool.synchronizeAfterExecution(), tool.getName()));
76+
if (processListener != null) {
77+
handler.addProcessListener(processListener);
78+
}
79+
handler.startNotify();
80+
} catch (ExecutionException ex) {
81+
ExecutionErrorDialog.show(ex, ToolsBundle.message("tools.process.start.error"), project);
82+
return false;
83+
}
84+
return true;
85+
}
86+
87+
private static void notifyCouldNotStart(@Nullable ProcessListener listener) {
88+
if (listener != null) listener.processTerminated(new ProcessEvent(new NopProcessHandler(), -1));
89+
}
90+
}

src/ReplaceAdapter.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import com.intellij.execution.process.ProcessAdapter;
2+
import com.intellij.execution.process.ProcessEvent;
3+
import com.intellij.execution.process.ProcessOutput;
4+
import com.intellij.execution.process.ProcessOutputTypes;
5+
import com.intellij.openapi.command.WriteCommandAction;
6+
import com.intellij.openapi.editor.Caret;
7+
import com.intellij.openapi.editor.Document;
8+
import com.intellij.openapi.editor.Editor;
9+
import com.intellij.openapi.project.Project;
10+
import com.intellij.openapi.util.Key;
11+
import com.intellij.openapi.vfs.VirtualFileManager;
12+
import org.jetbrains.annotations.NotNull;
13+
14+
public class ReplaceAdapter extends ProcessAdapter {
15+
private final ProcessOutput output;
16+
private final Editor editor;
17+
private final Project project;
18+
private final Document document;
19+
20+
public ReplaceAdapter(Editor editor, Project project, Document document) {
21+
this.output = new ProcessOutput();
22+
this.editor = editor;
23+
this.project = project;
24+
this.document = document;
25+
}
26+
27+
@Override
28+
public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
29+
addToOutput(event.getText(), outputType);
30+
}
31+
32+
protected void addToOutput(String text, Key outputType) {
33+
if (outputType == ProcessOutputTypes.STDOUT) {
34+
output.appendStdout(text);
35+
}
36+
else if (outputType == ProcessOutputTypes.STDERR) {
37+
output.appendStderr(text);
38+
}
39+
}
40+
41+
@Override
42+
public void processTerminated(@NotNull ProcessEvent event) {
43+
output.setExitCode(event.getExitCode());
44+
String text = output.getStdout();
45+
if (!text.isEmpty()) {
46+
VirtualFileManager.getInstance().asyncRefresh(() -> {
47+
Caret primaryCaret = editor.getCaretModel().getPrimaryCaret();
48+
int start = primaryCaret.getSelectionStart();
49+
int end = primaryCaret.getSelectionEnd();
50+
WriteCommandAction.runWriteCommandAction(project, null, null, () -> {
51+
document.replaceString(start, end, text);
52+
});
53+
});
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)