目录
第一步:定义Executor
第二步:定义表单元素
第三步:定义Runner
第四步:定义Action
第五步:配置 plugin.xml
相关文章推荐
idea插件开发资料实在太少,官方demo的例子也很少。
?官方demo:GitHub - JetBrains/intellij-sdk-code-samples: Mirror of the IntelliJ SDK Docs Code Sampleshttps://github.com/JetBrains/intellij-sdk-code-samples
这里笔者介绍一下官方demo中没有的例子——可编辑表单。
这只是一个demo,用于实际开发,还要结合官方手册来。
先看下效果图:
代码目录结构:
第一步:定义Executor
大家可以自己替换icon
package com.example.action.t6_table;
import com.intellij.execution.Executor;
import com.intellij.icons.AllIcons;
import org.jetbrains.annotations.NotNull;
import javax.swing.Icon;
public class TableExecutor extends Executor {
public static final String PLUGIN_ID = "TableExecutor";
public static final String TOOL_WINDOW_ID = "TableExecutor";
public static final String CONTEXT_ACTION_ID = "2222";
@NotNull
@Override
public String getId() {
return PLUGIN_ID;
}
@Override
public String getToolWindowId() {
return TOOL_WINDOW_ID;
}
@Override
public Icon getToolWindowIcon() {
return AllIcons.Actions.Redo;
}
@NotNull
@Override
public Icon getIcon() {
return AllIcons.Actions.Redo;
}
@Override
public Icon getDisabledIcon() {
return AllIcons.Actions.Redo;
}
@Override
public String getDescription() {
return TOOL_WINDOW_ID;
}
@NotNull
@Override
public String getActionName() {
return TOOL_WINDOW_ID;
}
@NotNull
@Override
public String getStartActionText() {
return TOOL_WINDOW_ID;
}
@Override
public String getContextActionId() {
return CONTEXT_ACTION_ID;
}
@Override
public String getHelpId() {
return TOOL_WINDOW_ID;
}
}
第二步:定义表单元素
?先定义一个抽象类:
package com.example.action.t6_table.table;
import com.intellij.ide.browsers.BrowserSpecificSettings;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public abstract class AbstractRow {
public AbstractRow() {
}
@NotNull
public abstract UUID getId();
@NotNull
public abstract String getName();
@NotNull
public abstract MyIconItem getIcon();
@Nullable
public abstract String getPath();
@Nullable
public abstract BrowserSpecificSettings getSpecificSettings();
}
定义table
package com.example.action.t6_table.table;
import com.intellij.ide.browsers.BrowserSpecificSettings;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.util.Function;
import com.intellij.util.PathUtil;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ListTableModel;
import com.intellij.util.ui.LocalPathCellEditor;
import com.intellij.util.ui.table.IconTableCellRenderer;
import com.intellij.util.ui.table.TableModelEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.JComponent;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import java.util.Comparator;
import java.util.UUID;
/**
* 参考
* {@link com.intellij.ide.browsers.BrowserSettingsPanel}
*/
public class MyTable {
private TableModelEditor<MyRow> browsersEditor;
private JComponent browsersTable;
public JComponent getBrowsersTable() {
return browsersTable;
}
private static final FileChooserDescriptor APP_FILE_CHOOSER_DESCRIPTOR = FileChooserDescriptorFactory.createSingleFileOrExecutableAppDescriptor();
private static final TableModelEditor.EditableColumnInfo<MyRow, Boolean> selectOptColumn = new TableModelEditor.EditableColumnInfo<MyRow, Boolean>("select") {
@Override
public Class getColumnClass() {
return Boolean.class;
}
@Override
public Boolean valueOf(MyRow item) {
return item.isActive();
}
@Override
public void setValue(MyRow item, Boolean value) {
item.setActive(value);
}
};
private static final TableModelEditor.EditableColumnInfo<MyRow, String> nameColumn = new TableModelEditor.EditableColumnInfo<MyRow, String>("Name") {
@Override
public String valueOf(MyRow item) {
return item.getName();
}
@Override
public void setValue(MyRow item, String value) {
item.setName(value);
}
/**
* 添加排序比较,支持列排序
*/
@NotNull
@Override
public Comparator<MyRow> getComparator() {
return new Comparator<MyRow>() {
@Override
public int compare(MyRow o1, MyRow o2) {
if (o1.getName().compareTo(o2.getName()) > 0) {
return 1;
} else if (o1.getName().compareTo(o2.getName()) == 0) {
return 0;
} else {
return -1;
}
}
};
}
};
private static final ColumnInfo<MyRow, MyIconItem> browserColumn = new ColumnInfo<MyRow, MyIconItem>("browser") {
@Override
public Class getColumnClass() {
return MyIconItem.class;
}
@Override
public MyIconItem valueOf(MyRow item) {
return item.getIcon();
}
@Override
public void setValue(MyRow item, MyIconItem value) {
item.setIcon(value);
item.setSpecificSettings(value.createBrowserSpecificSettings());
}
@NotNull
@Override
public TableCellRenderer getRenderer(MyRow item) {
return IconTableCellRenderer.ICONABLE;
}
};
private static final ColumnInfo[] COLUMNS = {
selectOptColumn,
nameColumn,
browserColumn,
new TableModelEditor.EditableColumnInfo<MyRow, String>("Path") {
@Override
public String valueOf(MyRow item) {
return PathUtil.toSystemDependentName(item.getPath());
}
@Override
public void setValue(MyRow item, String value) {
item.setPath(value);
}
@Nullable
@Override
public TableCellEditor getEditor(MyRow item) {
return new LocalPathCellEditor().fileChooserDescriptor(APP_FILE_CHOOSER_DESCRIPTOR).normalizePath(true);
}
}};
public MyTable() {
TableModelEditor.DialogItemEditor<MyRow> itemEditor = new TableModelEditor.DialogItemEditor<MyRow>() {
@NotNull
@Override
public Class<MyRow> getItemClass() {
return MyRow.class;
}
@Override
public MyRow clone(@NotNull MyRow item, boolean forInPlaceEditing) {
return new MyRow(forInPlaceEditing ? item.getId() : UUID.randomUUID(),
item.getIcon(), item.getName(), item.getPath(), item.isActive(),
forInPlaceEditing ? item.getSpecificSettings() : cloneSettings(item));
}
@Override
public void edit(@NotNull MyRow browser, @NotNull Function<MyRow, MyRow> mutator, boolean isAdd) {
BrowserSpecificSettings settings = cloneSettings(browser);
if (settings != null && ShowSettingsUtil.getInstance().editConfigurable(browsersTable, settings.createConfigurable())) {
mutator.fun(browser).setSpecificSettings(settings);
}
}
@Override
public void applyEdited(@NotNull MyRow oldItem, @NotNull MyRow newItem) {
oldItem.setSpecificSettings(newItem.getSpecificSettings());
}
@Override
public boolean isEditable(@NotNull MyRow browser) {
return false;
}
@Nullable
private BrowserSpecificSettings cloneSettings(@NotNull MyRow browser) {
BrowserSpecificSettings settings = browser.getSpecificSettings();
if (settings == null) {
return null;
}
BrowserSpecificSettings newSettings = browser.getIcon().createBrowserSpecificSettings();
assert newSettings != null;
TableModelEditor.cloneUsingXmlSerialization(settings, newSettings);
return newSettings;
}
};
TableModelEditor.DataChangedListener<MyRow> dataChangedListener = new TableModelEditor.DataChangedListener<MyRow>() {
@Override
public void tableChanged(@NotNull TableModelEvent event) {
}
@Override
public void dataChanged(@NotNull ColumnInfo<MyRow, ?> columnInfo, int rowIndex) {
}
};
browsersEditor = new TableModelEditor<>(COLUMNS, itemEditor, "No web browsers configured");
browsersEditor.modelListener(dataChangedListener);
ListTableModel<MyRow> model = this.browsersEditor.getModel();
MyRow myRow1 = new MyRow(
UUID.randomUUID(),
MyIconItem.CHROME,
"3",
"null",
true,
null
);
MyRow myRow2 = new MyRow(
UUID.randomUUID(),
MyIconItem.CHROME,
"4",
"null",
true,
null
);
model.addRow(myRow1);
model.addRow(myRow2);
browsersTable = browsersEditor.createComponent();
}
}
定义行元素 row
package com.example.action.t6_table.table;
import com.intellij.ide.browsers.BrowserSpecificSettings;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class MyRow extends AbstractRow {
private final UUID id;
@NotNull
private MyIconItem icon;
@NotNull
private String name;
private boolean active;
private String path;
/**
* 保存当前表格数据
*/
private BrowserSpecificSettings specificSettings;
@SuppressWarnings("UnusedDeclaration")
MyRow() {
this(UUID.randomUUID(), MyIconItem.CHROME);
}
MyRow(@NotNull UUID id, @NotNull MyIconItem icon) {
this(id, icon, icon.getName(), icon.getExecutionPath(), true, icon.createBrowserSpecificSettings());
}
public MyRow(@NotNull UUID id,
@NotNull MyIconItem icon,
@NotNull String name,
@Nullable String path,
boolean active,
@Nullable BrowserSpecificSettings specificSettings) {
this.id = id;
this.icon = icon;
this.name = name;
this.path = StringUtil.nullize(path);
this.active = active;
this.specificSettings = specificSettings;
}
public void setName(@NotNull String value) {
name = value;
}
public void setIcon(@NotNull MyIconItem value) {
icon = value;
}
@Nullable
@Override
public String getPath() {
return path;
}
public void setPath(@Nullable String value) {
path = PathUtil.toSystemIndependentName(StringUtil.nullize(value));
}
@Override
@Nullable
public BrowserSpecificSettings getSpecificSettings() {
return specificSettings;
}
public void setSpecificSettings(@Nullable BrowserSpecificSettings value) {
specificSettings = value;
}
public boolean isActive() {
return active;
}
public void setActive(boolean value) {
active = value;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MyRow)) {
return false;
}
MyRow browser = (MyRow) o;
return getId().equals(browser.getId()) &&
icon.equals(browser.icon) &&
active == browser.active &&
Comparing.strEqual(name, browser.name) &&
Comparing.equal(path, browser.path) &&
Comparing.equal(specificSettings, browser.specificSettings);
}
@Override
public int hashCode() {
return getId().hashCode();
}
@Override
@NotNull
public String getName() {
return name;
}
@Override
@NotNull
public final UUID getId() {
return id;
}
@Override
@NotNull
public MyIconItem getIcon() {
return icon;
}
@Override
public String toString() {
return getName() + " (" + getPath() + ")";
}
}
定义行元素中的特殊值:icon
package com.example.action.t6_table.table;
import com.intellij.icons.AllIcons.Xml.Browsers;
import com.intellij.ide.browsers.BrowserSpecificSettings;
import com.intellij.ide.browsers.chrome.ChromeSettings;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
public enum MyIconItem implements Iconable {
CHROME("chrome", "chrome", "google-chrome", "Google Chrome", Browsers.Chrome16) {
@Override
public BrowserSpecificSettings createBrowserSpecificSettings() {
return new ChromeSettings();
}
},
;
private final String myName;
private final String myWindowsPath;
private final String myUnixPath;
private final String myMacPath;
private final Icon myIcon;
private MyIconItem(@NotNull String name, @NotNull String windowsPath, @Nullable String unixPath, @Nullable String macPath, @NotNull Icon icon) {
this.myName = name;
this.myWindowsPath = windowsPath;
this.myUnixPath = unixPath;
this.myMacPath = macPath;
this.myIcon = icon;
}
@Nullable
public BrowserSpecificSettings createBrowserSpecificSettings() {
return null;
}
@Nullable
public String getExecutionPath() {
if (SystemInfo.isWindows) {
return this.myWindowsPath;
} else {
return SystemInfo.isMac ? this.myMacPath : this.myUnixPath;
}
}
public String getName() {
return this.myName;
}
public Icon getIcon() {
return this.myIcon;
}
public String toString() {
return this.myName;
}
public Icon getIcon(@IconFlags int flags) {
return this.getIcon();
}
}
第三步:定义Runner
我们会用到工具类:
import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistry;
public enum MyExecutorUtil {
;
/**
* 返回正在运行的 Executor
* @param id Executor id
*/
public static Executor getRunExecutorInstance(String id) {
return ExecutorRegistry.getInstance().getExecutorById(id);
}
}
?Runner代码如下:
package com.example.action.t6_table;
import com.example.action.t6_table.table.MyTable;
import com.example.utils.MyExecutorUtil;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.execution.ui.RunnerLayoutUi;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.Project;
import com.intellij.ui.content.Content;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
public class TableRunner {
/**
* Project
*/
private Project project = null;
public TableRunner(@NotNull Project project) {
this.project = project;
}
public void run() {
// 返回定义的 Executor
Executor executor = MyExecutorUtil.getRunExecutorInstance(TableExecutor.PLUGIN_ID);
if (executor == null) {
return;
}
// 创建 RunnerLayoutUi
final RunnerLayoutUi.Factory factory = RunnerLayoutUi.Factory.getInstance(project);
RunnerLayoutUi layoutUi = factory.create("id2", "title2", "session name2", project);
// 创建描述信息
RunContentDescriptor descriptor = new RunContentDescriptor(new RunProfile() {
@Nullable
@Override
public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException {
return null;
}
@NotNull
@Override
public String getName() {
return "name";
}
@Nullable
@Override
public Icon getIcon() {
return null;
}
}, new DefaultExecutionResult(), layoutUi);
descriptor.setExecutionId(System.nanoTime());
MyTable myTable = new MyTable();
final Content content = layoutUi.createContent("contentId", myTable.getBrowsersTable(), "displayName2", AllIcons.Debugger.Console, myTable.getBrowsersTable());
content.setCloseable(true);
layoutUi.addContent(content);
ExecutionManager.getInstance(project).getContentManager().showRunContent(executor, descriptor);
}
}
第四步:定义Action
我们会用到工具类:
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.project.Project;
public enum MyProjectUtil {
;
public static void setRunning(Project project, String key, boolean value) {
PropertiesComponent.getInstance(project).setValue(key, value);
}
public static boolean getRunning(Project project, String key) {
return PropertiesComponent.getInstance(project).getBoolean(key);
}
}
?Action代码如下:
package com.example.action.t6_table.action;
import com.example.action.t6_table.TableRunner;
import com.example.utils.MyProjectUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
public class TableAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
runExecutor(e.getProject());
}
public void runExecutor(Project project) {
if (project == null) {
return;
}
MyProjectUtil.setRunning(project, "running", true);
TableRunner executor = new TableRunner(project);
executor.run();
}
}
第五步:配置 plugin.xml
?plugin.xml:
<!-- 插件扩展 -->
<extensions defaultExtensionNs="com.intellij">
<!-- 自定义 底部view -->
<executor id="TableExecutor" order="last"
implementation="com.example.action.t6_table.TableExecutor"/>
</extensions>
<actions>
<!-- 自定义分组 -->
<group id="zhang.group1.id" text="Zhang-菜单汇总" description="Group1 description">
<add-to-group group-id="MainMenu" anchor="last"/>
<action id="zhang.bottom_tool.id3"
class="com.example.action.t6_table.action.TableAction"
text="底部-ToolWindow-表格" description="Description">
</action>
</group>
</actions>
笔者项目用的 build.gradle 配置如下:
plugins {
id 'org.jetbrains.intellij' version '0.4.10'
id "org.jetbrains.kotlin.jvm" version "1.3.41"
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
apply {
"java"
"terminal"
"ant"
}
group 'org.example'
version '1.0-SNAPSHOT'
// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
version '2019.3.3'
// 必须,否则无法找到 PsiClass 等
plugins = ['java', 'gradle']
intellij.updateSinceUntilBuild false
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply {
"java"
"terminal"
"ant"
}
test {
useJUnitPlatform()
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
buildPlugin {
buildSearchableOptions.enabled = false
}
相关文章推荐
笔者的开源插件:Java Mybatis SQL Scannerhttps://zhangxiaofan.blog.csdn.net/article/details/123094358
idea plugin插件开发1——idea底部窗口(带按钮)https://zhangxiaofan.blog.csdn.net/article/details/123093013
idea plugin插件开发2——预览代码(多窗口)https://zhangxiaofan.blog.csdn.net/article/details/123097015
|