適当なpluginとかそもそもデフォルト機能であるはず。
と思ったのに見当たらなかったので。
結論
サンプルをちょっといじって使う
# サンプルコードのダウンロード
git clone https://github.com/JetBrains/intellij-sdk-code-samples.git
サンプルに添付のパッチを当てる
cd intellij-sdk-code-samples/
patch -p1 < xxxxx.txt
パッチ(適当な名前で保存してください)
diff --git a/editor_basics/build.gradle b/editor_basics/build.gradle
index 98436d90..ff5da234 100644
--- a/editor_basics/build.gradle
+++ b/editor_basics/build.gradle
@@ -3,8 +3,11 @@
plugins {
id 'java'
id 'org.jetbrains.intellij' version '1.3.1'
+ id 'org.jetbrains.kotlin.jvm' version '1.3.41'
+}
+tasks.withType(JavaCompile) {
+ options.encoding = 'UTF-8'
}
-
group 'org.intellij.sdk'
version '2.0.0'
diff --git a/editor_basics/src/main/java/org/intellij/sdk/editor/DocStringTemplate.kt b/editor_basics/src/main/java/org/intellij/sdk/editor/DocStringTemplate.kt
new file mode 100644
index 00000000..b0768526
--- /dev/null
+++ b/editor_basics/src/main/java/org/intellij/sdk/editor/DocStringTemplate.kt
@@ -0,0 +1,24 @@
+package org.intellij.sdk.editor
+
+object DocStringTemplate {
+ @JvmStatic
+ val s = """
+ \"\"\"
+ Parameters
+ ----------
+ {%parameters}
+
+ Returns
+ -------
+ :
+ \"\"\"
+ """.trimIndent().replace("\\","")
+
+}
+
+object ParameterTemplate {
+ @JvmStatic
+ val s = """
+ {%parameter} : {%type}{%default}
+ """.trimIndent()
+}
\ No newline at end of file
diff --git a/editor_basics/src/main/java/org/intellij/sdk/editor/EditorAreaIllustration.java b/editor_basics/src/main/java/org/intellij/sdk/editor/EditorAreaIllustration.java
index 5cfa1c5f..3deb8e2d 100644
--- a/editor_basics/src/main/java/org/intellij/sdk/editor/EditorAreaIllustration.java
+++ b/editor_basics/src/main/java/org/intellij/sdk/editor/EditorAreaIllustration.java
@@ -16,7 +16,6 @@ import org.jetbrains.annotations.NotNull;
* @see AnAction
*/
public class EditorAreaIllustration extends AnAction {
-
/**
* Displays a message with information about the current caret.
*
@@ -24,6 +23,8 @@ public class EditorAreaIllustration extends AnAction {
*/
@Override
public void actionPerformed(@NotNull final AnActionEvent e) {
+ if (true) return;
+ e.getInputEvent().isAltDown();
// Get access to the editor and caret model. update() validated editor's existence.
final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
final CaretModel caretModel = editor.getCaretModel();
@@ -50,6 +51,7 @@ public class EditorAreaIllustration extends AnAction {
*/
@Override
public void update(@NotNull final AnActionEvent e) {
+ if (true) return;
// Get required data keys
final Project project = e.getProject();
final Editor editor = e.getData(CommonDataKeys.EDITOR);
diff --git a/editor_basics/src/main/java/org/intellij/sdk/editor/EditorIllustrationAction.java b/editor_basics/src/main/java/org/intellij/sdk/editor/EditorIllustrationAction.java
index 1664cd02..d5609b0b 100644
--- a/editor_basics/src/main/java/org/intellij/sdk/editor/EditorIllustrationAction.java
+++ b/editor_basics/src/main/java/org/intellij/sdk/editor/EditorIllustrationAction.java
@@ -19,50 +19,51 @@ import org.jetbrains.annotations.NotNull;
*/
public class EditorIllustrationAction extends AnAction {
- /**
- * Replaces the run of text selected by the primary caret with a fixed string.
- *
- * @param e Event related to this action
- */
- @Override
- public void actionPerformed(@NotNull final AnActionEvent e) {
- // Get all the required data from data keys
- // Editor and Project were verified in update(), so they are not null.
- final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
- final Project project = e.getRequiredData(CommonDataKeys.PROJECT);
- final Document document = editor.getDocument();
- // Work off of the primary caret to get the selection info
- Caret primaryCaret = editor.getCaretModel().getPrimaryCaret();
- int start = primaryCaret.getSelectionStart();
- int end = primaryCaret.getSelectionEnd();
- // Replace the selection with a fixed string.
- // Must do this document change in a write action context.
- WriteCommandAction.runWriteCommandAction(project, () ->
- document.replaceString(start, end, "editor_basics")
- );
- // De-select the text range that was just replaced
- primaryCaret.removeSelection();
- }
+ /**
+ * Replaces the run of text selected by the primary caret with a fixed string.
+ *
+ * @param e Event related to this action
+ */
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent e) {
+ if (true) return;
+ // Get all the required data from data keys
+ // Editor and Project were verified in update(), so they are not null.
+ final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
+ final Project project = e.getRequiredData(CommonDataKeys.PROJECT);
+ final Document document = editor.getDocument();
+ // Work off of the primary caret to get the selection info
+ Caret primaryCaret = editor.getCaretModel().getPrimaryCaret();
+ int start = primaryCaret.getSelectionStart();
+ int end = primaryCaret.getSelectionEnd();
+ // Replace the selection with a fixed string.
+ // Must do this document change in a write action context.
+ WriteCommandAction.runWriteCommandAction(project, () ->
+ document.replaceString(start, end, "editor_basics")
+ );
+ // De-select the text range that was just replaced
+ primaryCaret.removeSelection();
+ }
- /**
- * Sets visibility and enables this action menu item if:
- * <ul>
- * <li>a project is open</li>
- * <li>an editor is active</li>
- * <li>some characters are selected</li>
- * </ul>
- *
- * @param e Event related to this action
- */
- @Override
- public void update(@NotNull final AnActionEvent e) {
- // Get required data keys
- final Project project = e.getProject();
- final Editor editor = e.getData(CommonDataKeys.EDITOR);
- // Set visibility and enable only in case of existing project and editor and if a selection exists
- e.getPresentation().setEnabledAndVisible(
- project != null && editor != null && editor.getSelectionModel().hasSelection()
- );
- }
+ /**
+ * Sets visibility and enables this action menu item if:
+ * <ul>
+ * <li>a project is open</li>
+ * <li>an editor is active</li>
+ * <li>some characters are selected</li>
+ * </ul>
+ *
+ * @param e Event related to this action
+ */
+ @Override
+ public void update(@NotNull final AnActionEvent e) {
+ // Get required data keys
+ final Project project = e.getProject();
+ final Editor editor = e.getData(CommonDataKeys.EDITOR);
+ // Set visibility and enable only in case of existing project and editor and if a selection exists
+ e.getPresentation().setEnabledAndVisible(
+ project != null && editor != null && editor.getSelectionModel().hasSelection()
+ );
+ }
}
diff --git a/editor_basics/src/main/java/org/intellij/sdk/editor/MyTypedHandler.java b/editor_basics/src/main/java/org/intellij/sdk/editor/MyTypedHandler.java
index defcc830..2316f9e7 100644
--- a/editor_basics/src/main/java/org/intellij/sdk/editor/MyTypedHandler.java
+++ b/editor_basics/src/main/java/org/intellij/sdk/editor/MyTypedHandler.java
@@ -4,29 +4,279 @@ package org.intellij.sdk.editor;
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate;
import com.intellij.openapi.command.WriteCommandAction;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.*;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
+import java.io.BufferedReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* This is a custom {@link TypedHandlerDelegate} that handles actions activated keystrokes in the editor.
* The execute method inserts a fixed string at Offset 0 of the document.
* Document changes are made in the context of a write action.
*/
class MyTypedHandler extends TypedHandlerDelegate {
+ static long first = 0;
+ static long count = 0;
+
+ @NotNull
+ @Override
+ public Result charTyped(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
+// FileWriter writer;
+ int offset;
+ String contents;
+ String insert_str;
+ int curColumn;
+// try {
+// writer = new FileWriter("D:\\out.txt", StandardCharsets.UTF_8, true);
+// writer.write("c:" + c + "\n");
+ if (c != ';') {
+// writer.close();
+ return Result.CONTINUE;
+ }
+ long cur = System.currentTimeMillis();
+// writer.write("cur:" + cur + " first:" + first);
+ if ((cur - first) > 1500) {
+ first = cur;
+ count = 1;
+// writer.write("is first");
+// writer.close();
+ return Result.STOP;
+ } else {
+ count++;
+// writer.write("not first:" + count);
+ if (count != 3) {
+// writer.close();
+ return Result.STOP;
+ }
+ }
+// writer.write("count is 3");
+ CaretModel caret = editor.getCaretModel();
+ VisualPosition position = caret.getVisualPosition();
+// LogicalPosition position = caret.getLogicalPosition();
+// offset = editor.logicalPositionToOffset(position);
+
+ offset = editor.visualPositionToOffset(position);
+ contents = file.getText();
+
+ String befLine = null;
+ String curLine = null;
+ try (BufferedReader reader = new BufferedReader(new StringReader(contents))) {
+ String line;
+ int cnt = 0;
+ while ((line = reader.readLine()) != null) {
+ if ((position.line - 1) == cnt) {
+ befLine = line;
+ } else if (position.line == cnt) {
+ curLine = line;
+ }
+ cnt++;
+ }
+ } catch (IOException ignored) {
+// writer.write("exception\n");
+// writer.close();
+ return Result.CONTINUE;
+ }
+ if ((befLine == null) || (curLine == null)) {
+// writer.write("line check ng");
+// writer.close();
+ return Result.CONTINUE;
+ }
+
+ // 上が空行は無視
+ if (befLine.matches("^\\s*$")) {
+// writer.write("above is null");
+// writer.close();
+ return Result.CONTINUE;
+ }
+
+ // 他に入力文字が有ったら無視
+ if (!curLine.matches("^\\s*;;\\s*$")) {
+// writer.write("not ;;");
+// writer.close();
+ return Result.CONTINUE;
+ }
+
+ curColumn = position.column;
+// writer.write("ccc" + curColumn + "\n");
+// writer.close();
+ insert_str = getInsertStr(befLine, curColumn - (curColumn % 4));
+// } catch (IOException ignored) {
+// return Result.CONTINUE;
+// }
+
+
+ // Get the document and project
+ final Document document = editor.getDocument();
+ // Construct the runnable to substitute the string at offset 0 in the document
+ document.replaceString((offset - 3), offset, "");
+ String finalInsert_str = insert_str;
+// try {
+// writer = new FileWriter("D:\\out.txt", StandardCharsets.UTF_8, true);
+// writer.write("insert:" + finalInsert_str + "\n");
+// writer.close();
+// } catch (IOException ignored) {
+// }
+ Runnable runnable = () -> document.insertString(offset - 3, finalInsert_str + "\n");
+ // Make the document change in the context of a write action.
+ WriteCommandAction.runWriteCommandAction(project, runnable);
+ return Result.STOP;
+ }
+
+ /**
+ * 複数行の文字列の1行毎にインデントを追加する
+ *
+ * @param str 複数行の文字列
+ * @param indent 挿入するスペース数
+ * @return インデントを追加した文字列
+ */
+ String addIndent(String str, int indent) {
+ String indent_str = getIndentStr(indent);
+ StringBuilder retStr = new StringBuilder();
+ try (BufferedReader reader = new BufferedReader(new StringReader(str))) {
+ String line;
+ int cnt = 0;
+ while ((line = reader.readLine()) != null) {
+ if (cnt++ > 0) {
+ retStr.append(indent_str);
+ }
+ retStr.append(line).append("\n");
+ }
+ } catch (IOException ignored) {
+ }
+
+ return retStr.toString();
+ }
+
+
+ /**
+ * 指定した数値に応じた連続スペースを返却する
+ *
+ * @param indent インデント位置
+ * @return 連続スペース
+ */
+ String getIndentStr(int indent) {
+ if (indent <= 0) return "";
+ return " ".repeat(indent);
+ }
+
+ /**
+ * すぐ上の行に応じたdocstringを返す
+ *
+ * @param befLine カーソルの上の行
+ * @return docstring
+ */
+ String getInsertStr(String befLine, int indent) {
+ String pattern = "^\\s*def\\s.*\\((.*)\\).*\\s*$";
+ Pattern p = Pattern.compile(pattern);
+ Matcher m = p.matcher(befLine);
+ String template = DocStringTemplate.getS();
+ String str = null;
+// FileWriter writer;
+
+// try {
+// writer = new FileWriter("D:\\out.txt", StandardCharsets.UTF_8, true);
+ if (m.find()) {
+ // 関数の場合
+ String paramTemplate = ParameterTemplate.getS();
+
+ String args_str = m.group(1).replaceAll("\\s", "");
+// writer.write("args_str:" + args_str + "\n");
+ String[] args_ary = args_str.split(",");
+ List<Arg> args = new ArrayList<>();
+// writer.write("args_ary:" + args_ary.length);
+ for (String arg_str : args_ary) {
+ if (arg_str.length() == 0) continue;
+ String[] temp = arg_str.split("=");
+// writer.write("temp=:" + temp[0] + "\n");
+ Arg arg = new Arg();
+ if (temp.length > 1) {
+ arg.def = temp[1];
+ }
+ temp = temp[0].split(":");
+// writer.write("temp::" + temp[0] + "\n");
+ if (temp.length > 1) {
+ arg.type = temp[1];
+ }
+ arg.name = temp[0];
+ if (!"self".equals(arg.name))
+ args.add(arg);
+ }
- @NotNull
- @Override
- public Result charTyped(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
- // Get the document and project
- final Document document = editor.getDocument();
- // Construct the runnable to substitute the string at offset 0 in the document
- Runnable runnable = () -> document.insertString(0, "editor_basics\n");
- // Make the document change in the context of a write action.
- WriteCommandAction.runWriteCommandAction(project, runnable);
- return Result.STOP;
- }
+ if (args.size() > 0) {
+// writer.write("args.size > 0\n");
+ // 引数有りパターン
+ StringBuilder strParams = new StringBuilder();
+ for (Arg arg : args) {
+// writer.write("name:" + arg.name + " type:" + arg.type + " def:" + arg.type + "\n");
+ String strParam = paramTemplate;
+ strParam = strParam.replaceAll("\\{%parameter}", arg.name);
+// writer.write("pend:" + strParam +"\n");
+ strParam = strParam.replaceAll("\\{%type}", arg.type);
+ String def = arg.def.length() > 0 ? ", default " + arg.def : "";
+ strParam = strParam.replaceAll("\\{%default}", def);
+// writer.write("comp:" + strParam +"\n");
+ strParams.append(strParam).append("\n");
+ }
+ str = template.replaceAll("\\{%parameters}", strParams.toString());
+ } else {
+// writer.write("args.size == 0");
+ // 引数なしパターン
+ // java は複数行の部分置換が出来ない
+ String p_str = "Parameters";
+ String r_str = "Returns";
+ int p_len = p_str.length();
+ int r_len = r_str.length();
+ int len = template.length();
+ int d_start = 0;
+ int d_end = 0;
+ for (int i = 0; i < len - r_len; i++) {
+ if (((len - i) > p_len) && (p_str.equals(template.substring(i, i + p_len)))) {
+ d_start = i;
+ } else if (((len - i) > r_len) && (r_str.equals(template.substring(i, i + r_len)))) {
+ d_end = i;
+ break;
+ }
+ }
+ if (d_end > d_start) {
+ str = template.substring(0, d_start) + template.substring(d_end);
+ }
+ }
+ } else {
+ // 関数以外の場合
+ str = "\"\"\"\n" + "\"\"\"\n";
+ }
+// writer.write("indent:" + getIndentStr(indent) + "\n");
+// writer.close();
+// } catch (IOException ignored) {
+// }
+ return addIndent(str, indent);
+ }
+ /**
+ * python の 引数1つ分
+ */
+ static class Arg {
+ /**
+ * 引数名
+ */
+ String name = "";
+ /**
+ * 型ヒント
+ */
+ String type = "";
+ /**
+ * default値
+ */
+ String def = "";
+ }
}
ビルドする
# JAVAのパスは環境に合わせる。
cd editor_basics
JAVA_HOME=/d/Java/jdk-11.0.12/ ./gradlew buildPlugin
editor_basics/build/distributions/editor-2.0.0.zip が作成される
PyCharmのpluginに設定する
PyCharmからFile -> Settings -> Plugins と選ぶ。
Installedの右の歯車からInstall Plugin from Diskを選択する。
作成されたzipファイルを選択する。
OKを押してSettings画面を閉じる。
使い方
;;;(関数定義の直下の行でセミコロンを1.5s以内に3連打するとコメントテンプレートが挿入される)
1から作ろうとして断念した経緯
- src 右クリでactionが出てこない
- gradleとかxmlの編集とかよくわからない
とかの理由で誰かが作ってるの編集した方が速いのでは?と思いました。
はまったポイント
gradle実行時のjavaのversionが11(以上かな?)で無いと動かない
"(ダブルクォーテーション)は飛んでこない