更新包+readme
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "com.huangzj"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
|
||||
repositories {
|
||||
// 国内镜像,失败再走中央仓库
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package com.huangzj.protoplugin.memo;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/** 备忘录条目(字段需可被持久化组件序列化)。 */
|
||||
public class MemoItem {
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
|
||||
public String id = "";
|
||||
public String title = "";
|
||||
public String content = "";
|
||||
@@ -23,4 +28,24 @@ public class MemoItem {
|
||||
}
|
||||
return "(无标题)";
|
||||
}
|
||||
|
||||
public String displayDate() {
|
||||
if (updated <= 0) {
|
||||
return "";
|
||||
}
|
||||
return DATE_FORMAT.format(new Date(updated));
|
||||
}
|
||||
|
||||
public String contentPreview() {
|
||||
if (content == null || content.trim().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
String trimmed = content.trim();
|
||||
int newlineIdx = trimmed.indexOf('\n');
|
||||
String firstLine = newlineIdx >= 0 ? trimmed.substring(0, newlineIdx) : trimmed;
|
||||
if (firstLine.length() > 50) {
|
||||
return firstLine.substring(0, 50) + "...";
|
||||
}
|
||||
return firstLine;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.huangzj.protoplugin.memo;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.ui.components.JBTextArea;
|
||||
@@ -13,13 +14,16 @@ import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
@@ -51,17 +55,25 @@ public final class MemoPanel extends JPanel {
|
||||
private final JPanel leftButtonBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
|
||||
private final JPanel themeChooserBar = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
|
||||
private final JPanel topStack = new JPanel(new BorderLayout());
|
||||
private final JSplitPane split;
|
||||
private final JBScrollPane listScroll;
|
||||
private final JBScrollPane contentScroll;
|
||||
private JSplitPane split;
|
||||
private JBScrollPane listScroll;
|
||||
private JBScrollPane contentScroll;
|
||||
private final JPanel titleBlock = new JPanel(new BorderLayout(4, 4));
|
||||
private final JPanel contentBlock = new JPanel(new BorderLayout(4, 4));
|
||||
private final JPanel right = new JPanel(new BorderLayout(8, 8));
|
||||
private final JBTextField searchField = new JBTextField();
|
||||
private final JLabel countLabel = new JLabel(" ");
|
||||
private final JLabel modifiedLabel = new JLabel(" ");
|
||||
private final JPanel leftPanel = new JPanel(new BorderLayout(0, 4));
|
||||
private final JPanel searchPanel = new JPanel(new BorderLayout(4, 0));
|
||||
private final JLabel searchIcon = new JLabel("\uD83D\uDD0D");
|
||||
private final JPanel editorStatusBar = new JPanel(new BorderLayout());
|
||||
|
||||
private boolean loadingSelection;
|
||||
private boolean themeComboSuppress;
|
||||
private MemoUiTheme currentTheme = MemoUiTheme.IDE_DEFAULT;
|
||||
private final Font defaultLabelFont;
|
||||
private List<MemoItem> allItems = new ArrayList<>();
|
||||
|
||||
public MemoPanel(@NotNull Project project) {
|
||||
super(new BorderLayout(8, 8));
|
||||
@@ -69,7 +81,25 @@ public final class MemoPanel extends JPanel {
|
||||
defaultLabelFont = titleLabel.getFont();
|
||||
setBorder(JBUI.Borders.empty(8));
|
||||
|
||||
initToolbar();
|
||||
initFlair();
|
||||
initTopStack();
|
||||
initSearchPanel();
|
||||
initList();
|
||||
initLeftPanel();
|
||||
initEditor();
|
||||
initSplit();
|
||||
initTheme();
|
||||
initListeners();
|
||||
|
||||
reloadListModel();
|
||||
}
|
||||
|
||||
private void initToolbar() {
|
||||
leftButtonBar.setOpaque(false);
|
||||
newBtn.setToolTipText("新建一条备忘录");
|
||||
saveBtn.setToolTipText("保存当前备忘录");
|
||||
deleteBtn.setToolTipText("删除选中的备忘录");
|
||||
leftButtonBar.add(newBtn);
|
||||
leftButtonBar.add(saveBtn);
|
||||
leftButtonBar.add(deleteBtn);
|
||||
@@ -81,21 +111,48 @@ public final class MemoPanel extends JPanel {
|
||||
toolbar.setOpaque(false);
|
||||
toolbar.add(leftButtonBar, BorderLayout.WEST);
|
||||
toolbar.add(themeChooserBar, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
private void initFlair() {
|
||||
flairLabel.setOpaque(true);
|
||||
flairLabel.setFont(flairLabel.getFont().deriveFont(Font.PLAIN, 12f));
|
||||
}
|
||||
|
||||
private void initTopStack() {
|
||||
topStack.setOpaque(false);
|
||||
topStack.add(flairLabel, BorderLayout.NORTH);
|
||||
topStack.add(toolbar, BorderLayout.CENTER);
|
||||
add(topStack, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
private void initSearchPanel() {
|
||||
searchField.getEmptyText().setText("搜索备忘录...");
|
||||
searchField.setToolTipText("输入关键字过滤备忘录标题和内容");
|
||||
searchPanel.setOpaque(false);
|
||||
searchPanel.add(searchIcon, BorderLayout.WEST);
|
||||
searchPanel.add(searchField, BorderLayout.CENTER);
|
||||
searchPanel.setBorder(JBUI.Borders.empty(0, 0, 4, 0));
|
||||
}
|
||||
|
||||
private void initList() {
|
||||
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
installListRenderer();
|
||||
|
||||
listScroll = new JBScrollPane(list);
|
||||
listScroll.setPreferredSize(new Dimension(200, 300));
|
||||
|
||||
countLabel.setFont(countLabel.getFont().deriveFont(Font.PLAIN, 11f));
|
||||
countLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
countLabel.setBorder(JBUI.Borders.empty(2, 4, 0, 4));
|
||||
}
|
||||
|
||||
private void initLeftPanel() {
|
||||
leftPanel.setOpaque(false);
|
||||
leftPanel.add(searchPanel, BorderLayout.NORTH);
|
||||
leftPanel.add(listScroll, BorderLayout.CENTER);
|
||||
leftPanel.add(countLabel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private void initEditor() {
|
||||
contentArea.setLineWrap(true);
|
||||
contentArea.setWrapStyleWord(true);
|
||||
contentScroll = new JBScrollPane(contentArea);
|
||||
@@ -108,22 +165,34 @@ public final class MemoPanel extends JPanel {
|
||||
contentBlock.add(contentLabel, BorderLayout.NORTH);
|
||||
contentBlock.add(contentScroll, BorderLayout.CENTER);
|
||||
|
||||
modifiedLabel.setFont(modifiedLabel.getFont().deriveFont(Font.PLAIN, 11f));
|
||||
modifiedLabel.setBorder(JBUI.Borders.empty(2, 0, 0, 0));
|
||||
editorStatusBar.setOpaque(false);
|
||||
editorStatusBar.add(modifiedLabel, BorderLayout.EAST);
|
||||
|
||||
right.setOpaque(false);
|
||||
right.add(titleBlock, BorderLayout.NORTH);
|
||||
right.add(contentBlock, BorderLayout.CENTER);
|
||||
right.add(editorStatusBar, BorderLayout.SOUTH);
|
||||
titleField.setPreferredSize(new Dimension(200, 28));
|
||||
}
|
||||
|
||||
split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listScroll, right);
|
||||
private void initSplit() {
|
||||
split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, right);
|
||||
split.setResizeWeight(0.28);
|
||||
split.setOpaque(false);
|
||||
add(split, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private void initTheme() {
|
||||
MemoUiTheme initial = MemoProjectService.getInstance(project).getMemoTheme();
|
||||
themeComboSuppress = true;
|
||||
themeCombo.setSelectedItem(initial);
|
||||
themeComboSuppress = false;
|
||||
applyTheme(initial);
|
||||
}
|
||||
|
||||
private void initListeners() {
|
||||
themeCombo.addActionListener(
|
||||
e -> {
|
||||
if (themeComboSuppress) {
|
||||
@@ -136,8 +205,6 @@ public final class MemoPanel extends JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
reloadListModel();
|
||||
|
||||
list.addListSelectionListener(
|
||||
new ListSelectionListener() {
|
||||
@Override
|
||||
@@ -172,6 +239,26 @@ public final class MemoPanel extends JPanel {
|
||||
refreshSelectedTitleInList();
|
||||
}
|
||||
});
|
||||
|
||||
searchField
|
||||
.getDocument()
|
||||
.addDocumentListener(
|
||||
new javax.swing.event.DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) {
|
||||
filterList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(javax.swing.event.DocumentEvent e) {
|
||||
filterList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(javax.swing.event.DocumentEvent e) {
|
||||
filterList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void installListRenderer() {
|
||||
@@ -184,27 +271,57 @@ public final class MemoPanel extends JPanel {
|
||||
int index,
|
||||
boolean isSelected,
|
||||
boolean cellHasFocus) {
|
||||
super.getListCellRendererComponent(
|
||||
JLabel label = (JLabel) super.getListCellRendererComponent(
|
||||
jList, value, index, isSelected, cellHasFocus);
|
||||
if (value instanceof MemoItem) {
|
||||
setText(((MemoItem) value).displayTitle());
|
||||
MemoItem item = (MemoItem) value;
|
||||
String title = escapeHtml(item.displayTitle());
|
||||
String date = item.displayDate();
|
||||
String preview = item.contentPreview();
|
||||
|
||||
StringBuilder html = new StringBuilder(128);
|
||||
html.append("<html><body style='width:160px'>");
|
||||
html.append("<b>").append(title).append("</b>");
|
||||
if (!date.isEmpty()) {
|
||||
html.append("<br><span style='font-size:10px;color:")
|
||||
.append(isSelected ? "#inherit" : "#888888")
|
||||
.append("'>").append(date).append("</span>");
|
||||
}
|
||||
if (!preview.isEmpty()) {
|
||||
html.append("<br><span style='font-size:10px;color:")
|
||||
.append(isSelected ? "#inherit" : "#AAAAAA")
|
||||
.append("'>").append(escapeHtml(preview)).append("</span>");
|
||||
}
|
||||
html.append("</body></html>");
|
||||
label.setText(html.toString());
|
||||
}
|
||||
if (currentTheme != MemoUiTheme.IDE_DEFAULT) {
|
||||
MemoThemeSpec spec = MemoThemeSpec.forTheme(currentTheme);
|
||||
setOpaque(true);
|
||||
label.setOpaque(true);
|
||||
if (isSelected) {
|
||||
setBackground(spec.listSelectedBg);
|
||||
setForeground(spec.listSelectedFg);
|
||||
label.setBackground(spec.listSelectedBg);
|
||||
label.setForeground(spec.listSelectedFg);
|
||||
} else {
|
||||
setBackground(spec.listBg);
|
||||
setForeground(spec.text);
|
||||
label.setBackground(spec.listBg);
|
||||
label.setForeground(spec.text);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
label.setBorder(JBUI.Borders.empty(4, 8, 4, 8));
|
||||
return label;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String escapeHtml(String text) {
|
||||
if (text == null) {
|
||||
return "";
|
||||
}
|
||||
return text.replace("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace("\"", """);
|
||||
}
|
||||
|
||||
private void applyTheme(@NotNull MemoUiTheme theme) {
|
||||
currentTheme = theme;
|
||||
installListRenderer();
|
||||
@@ -235,6 +352,9 @@ public final class MemoPanel extends JPanel {
|
||||
flairLabel.setBackground(panelBg);
|
||||
flairLabel.setForeground(spec.text);
|
||||
split.setBackground(panelBg);
|
||||
leftPanel.setBackground(panelBg);
|
||||
searchPanel.setBackground(panelBg);
|
||||
searchIcon.setForeground(spec.text);
|
||||
listScroll.setBackground(panelBg);
|
||||
listScroll.getViewport().setBackground(spec.listBg);
|
||||
list.setBackground(spec.listBg);
|
||||
@@ -244,12 +364,15 @@ public final class MemoPanel extends JPanel {
|
||||
contentBlock.setBackground(panelBg);
|
||||
titleLabel.setForeground(spec.text);
|
||||
contentLabel.setForeground(spec.text);
|
||||
countLabel.setForeground(spec.text);
|
||||
modifiedLabel.setForeground(spec.text);
|
||||
editorStatusBar.setBackground(panelBg);
|
||||
|
||||
if (theme == MemoUiTheme.CUTE_CANDY) {
|
||||
flairLabel.setVisible(true);
|
||||
flairLabel.setText("✨ 今天也要甜甜哒~ 选一条备忘,把想法留下来吧 💕");
|
||||
titleLabel.setText("📝 标题");
|
||||
contentLabel.setText("💗 正文");
|
||||
flairLabel.setText("\u2728 今天也要甜甜哒\uFF5E 选一条备忘,把想法留下来吧 \uD83D\uDC95");
|
||||
titleLabel.setText("\uD83D\uDCDD 标题");
|
||||
contentLabel.setText("\uD83D\uDC97 正文");
|
||||
titleLabel.setFont(defaultLabelFont.deriveFont(Font.BOLD, defaultLabelFont.getSize() + 1f));
|
||||
contentLabel.setFont(
|
||||
defaultLabelFont.deriveFont(Font.BOLD, defaultLabelFont.getSize() + 1f));
|
||||
@@ -263,6 +386,7 @@ public final class MemoPanel extends JPanel {
|
||||
}
|
||||
|
||||
styleTextField(titleField, spec, softField);
|
||||
styleTextField(searchField, spec, softField);
|
||||
styleTextArea(contentArea, contentScroll, spec, softField);
|
||||
|
||||
styleAccentButton(newBtn, spec);
|
||||
@@ -281,6 +405,7 @@ public final class MemoPanel extends JPanel {
|
||||
Color editorBg = UIUtil.getTextFieldBackground();
|
||||
Color fg = UIUtil.getTextFieldForeground();
|
||||
Color listBg = UIUtil.getListBackground();
|
||||
Color labelFg = UIUtil.getLabelForeground();
|
||||
|
||||
setBackground(panelBg);
|
||||
topStack.setOpaque(false);
|
||||
@@ -289,12 +414,15 @@ public final class MemoPanel extends JPanel {
|
||||
toolbar.setOpaque(false);
|
||||
leftButtonBar.setOpaque(false);
|
||||
themeChooserBar.setOpaque(false);
|
||||
themeChooserLabel.setForeground(UIUtil.getLabelForeground());
|
||||
themeChooserLabel.setForeground(labelFg);
|
||||
flairLabel.setVisible(false);
|
||||
flairLabel.setText(" ");
|
||||
flairLabel.setBackground(panelBg);
|
||||
flairLabel.setForeground(UIUtil.getLabelForeground());
|
||||
flairLabel.setForeground(labelFg);
|
||||
split.setBackground(panelBg);
|
||||
leftPanel.setBackground(panelBg);
|
||||
searchPanel.setBackground(panelBg);
|
||||
searchIcon.setForeground(labelFg);
|
||||
listScroll.setBackground(panelBg);
|
||||
listScroll.getViewport().setBackground(listBg);
|
||||
list.setBackground(listBg);
|
||||
@@ -306,14 +434,19 @@ public final class MemoPanel extends JPanel {
|
||||
contentLabel.setText("内容");
|
||||
titleLabel.setFont(defaultLabelFont);
|
||||
contentLabel.setFont(defaultLabelFont);
|
||||
titleLabel.setForeground(UIUtil.getLabelForeground());
|
||||
contentLabel.setForeground(UIUtil.getLabelForeground());
|
||||
titleLabel.setForeground(labelFg);
|
||||
contentLabel.setForeground(labelFg);
|
||||
countLabel.setForeground(JBColor.GRAY);
|
||||
modifiedLabel.setForeground(JBColor.GRAY);
|
||||
editorStatusBar.setBackground(panelBg);
|
||||
|
||||
resetFieldLook(titleField, editorBg, fg);
|
||||
resetFieldLook(searchField, editorBg, fg);
|
||||
resetFieldLook(contentArea, editorBg, fg);
|
||||
contentScroll.getViewport().setBackground(editorBg);
|
||||
contentArea.setCaretColor(fg);
|
||||
titleField.setCaretColor(fg);
|
||||
searchField.setCaretColor(fg);
|
||||
|
||||
resetButton(newBtn);
|
||||
resetButton(saveBtn);
|
||||
@@ -392,19 +525,68 @@ public final class MemoPanel extends JPanel {
|
||||
}
|
||||
|
||||
private void reloadListModel() {
|
||||
allItems = MemoProjectService.getInstance(project).getItems();
|
||||
filterList();
|
||||
}
|
||||
|
||||
private void filterList() {
|
||||
String query = searchField.getText().trim().toLowerCase();
|
||||
listModel.clear();
|
||||
List<MemoItem> items = MemoProjectService.getInstance(project).getItems();
|
||||
for (MemoItem m : items) {
|
||||
|
||||
List<MemoItem> filtered;
|
||||
if (query.isEmpty()) {
|
||||
filtered = allItems;
|
||||
} else {
|
||||
filtered = allItems.stream()
|
||||
.filter(m -> matchesQuery(m, query))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
for (MemoItem m : filtered) {
|
||||
listModel.addElement(m);
|
||||
}
|
||||
if (!items.isEmpty() && list.getSelectedIndex() < 0) {
|
||||
|
||||
updateCountLabel(filtered.size());
|
||||
|
||||
if (!filtered.isEmpty() && list.getSelectedIndex() < 0) {
|
||||
list.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
if (filtered.isEmpty() && !allItems.isEmpty() && !query.isEmpty()) {
|
||||
clearEditor();
|
||||
modifiedLabel.setText("无匹配结果");
|
||||
} else if (filtered.isEmpty() && allItems.isEmpty()) {
|
||||
clearEditor();
|
||||
modifiedLabel.setText(" ");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchesQuery(MemoItem m, String query) {
|
||||
if (m.title != null && m.title.toLowerCase().contains(query)) {
|
||||
return true;
|
||||
}
|
||||
if (m.content != null && m.content.toLowerCase().contains(query)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateCountLabel(int visibleCount) {
|
||||
int total = allItems.size();
|
||||
if (total == 0) {
|
||||
countLabel.setText(" ");
|
||||
} else if (visibleCount == total) {
|
||||
countLabel.setText("共 " + total + " 条");
|
||||
} else {
|
||||
countLabel.setText("显示 " + visibleCount + " / " + total + " 条");
|
||||
}
|
||||
}
|
||||
|
||||
private void createMemo() {
|
||||
MemoItem m = MemoProjectService.getInstance(project).createMemo();
|
||||
listModel.addElement(m);
|
||||
allItems = MemoProjectService.getInstance(project).getItems();
|
||||
searchField.setText("");
|
||||
filterList();
|
||||
list.setSelectedValue(m, true);
|
||||
titleField.requestFocusInWindow();
|
||||
}
|
||||
@@ -417,7 +599,9 @@ public final class MemoPanel extends JPanel {
|
||||
m.title = titleField.getText();
|
||||
m.content = contentArea.getText();
|
||||
m.updated = System.currentTimeMillis();
|
||||
allItems = MemoProjectService.getInstance(project).getItems();
|
||||
list.repaint();
|
||||
updateModifiedLabel(m);
|
||||
}
|
||||
|
||||
private void deleteCurrent() {
|
||||
@@ -425,9 +609,21 @@ public final class MemoPanel extends JPanel {
|
||||
if (m == null) {
|
||||
return;
|
||||
}
|
||||
String itemTitle = m.displayTitle();
|
||||
int result = JOptionPane.showConfirmDialog(
|
||||
this,
|
||||
"确定要删除「" + itemTitle + "」吗?\n此操作不可撤销。",
|
||||
"确认删除",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
if (result != JOptionPane.YES_OPTION) {
|
||||
return;
|
||||
}
|
||||
int idx = list.getSelectedIndex();
|
||||
MemoProjectService.getInstance(project).removeById(m.id);
|
||||
allItems = MemoProjectService.getInstance(project).getItems();
|
||||
listModel.removeElement(m);
|
||||
updateCountLabel(listModel.getSize());
|
||||
if (!listModel.isEmpty()) {
|
||||
int next = Math.min(idx, listModel.getSize() - 1);
|
||||
list.setSelectedIndex(Math.max(0, next));
|
||||
@@ -445,15 +641,26 @@ public final class MemoPanel extends JPanel {
|
||||
} else {
|
||||
titleField.setText(m.title != null ? m.title : "");
|
||||
contentArea.setText(m.content != null ? m.content : "");
|
||||
updateModifiedLabel(m);
|
||||
}
|
||||
} finally {
|
||||
loadingSelection = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateModifiedLabel(MemoItem m) {
|
||||
String date = m.displayDate();
|
||||
if (!date.isEmpty()) {
|
||||
modifiedLabel.setText("最后修改: " + date);
|
||||
} else {
|
||||
modifiedLabel.setText(" ");
|
||||
}
|
||||
}
|
||||
|
||||
private void clearEditor() {
|
||||
titleField.setText("");
|
||||
contentArea.setText("");
|
||||
modifiedLabel.setText(" ");
|
||||
}
|
||||
|
||||
private void refreshSelectedTitleInList() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<idea-plugin>
|
||||
<id>com.huangzj.protoPlugin</id>
|
||||
<name>Idea-Plugin</name>
|
||||
<name>Huangzhijun-Idea-Plugin</name>
|
||||
<vendor email="support@example.com" url="https://example.com">huangzj</vendor>
|
||||
|
||||
<description><![CDATA[
|
||||
|
||||
Reference in New Issue
Block a user