Eclipseのビューを自分で作る【Eclipse + ChatGPT】

Eclipseのビューを自分で作成する方法を紹介したいと思います。
今回はEclipse2023 + JavaSE17 を使っていきます。
また後半ではEclipseとChatGPTという不思議な組み合わせを実装します。

# PDE (Plug-in Development Environment)のインストール
**「ヘルプ」 > 「新規ソフトウェアのインストール」**
から作業対象に
https://download.eclipse.org/releases/latest
と入力し、「一般用ツール」から「Eclipse プラグイン開発環境」をインストール

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/37a1719c-3a2a-4827-8fc3-60779d5ef0de.png)

表示される条項などに同意し、インストール後に再起動する (筆者はpleiades以下の”eclipse.exe -clean.cmd”によりキャッシュを削除して再起動しないと次のステップに行けませんでした)

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/9928a834-6885-5911-e768-f066d4752a6e.png)


# 2. プラグイン開発プロジェクトの作成
「ファイル」 > 「新規」 > 「その他」 > 「プラグイン開発」 > 「プラグイン・プロジェクト」
からビューを作成するためのプロジェクトを作成します。

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/48f9d285-e86f-8edb-b26f-0c05ccf666f6.png)

今回は”MyView”プロジェクトをworkspace直下に作成します

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/e02f27f4-8bbf-6f3e-e623-7028252a5fb9.png)

続けて設定し、「次へ」を押下します

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/8818ca1c-d829-ca09-3306-a734fabbecf7.png)

テンプレートは「カスタム・プラグイン・ウィザード」で作成します

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/20cd3500-33be-cca2-6a1c-6b4e842bbc6d.png)

今回は「example view extension」を選択します

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/a82553ba-54aa-2d88-b0c5-70b93553de3f.png)

ここはデフォルトで大丈夫だと思います

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/9b5f93a0-f6bf-6c12-af6f-b8f88d8c14e5.png)

完了を押下してプロジェクトを作成します
# 3. ビューを立ち上げてみる
プロジェクトを作成すると、このように特殊なエディタ画面が表示されます。

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/ec9201f7-379e-aa00-0b4e-f771519ca1c2.png)

ビューの立ち上げは、プロジェクトを「Eclipseアプリケーション」で実行することで実施できます

:::note info
上手く立ち上がらない場合は、エラーのログから問題を解消してください。筆者は”plugin.xml”から”org.eclipse.ui.views”を選んで追加を押下し、”org.eclipse.ui.views”を追加したところ解決に至ったことがあります。
:::

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/8b6230f9-8840-5ca2-b4be-69a22a51a73d.png)

実行するとEclipseが別ウィンドウで立ち上がりますので、
「Window」 > 「Show View」 > 「Other」 > 「サンプル・カテゴリー」 > 「サンプル・ビュー」 (ここらへんは命名や設定に依存すると思います)
から、src/myview/views/SampleView.java で定義したカスタムビューを表示できます。

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/442a9f5f-109d-c5b3-4e0d-a9fdb0b7ce72.png)

サンプル・ビューを実際に開くとこんな感じになると思います。

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/dabd91de-707d-fb1f-689e-75ed7d000e30.png)

# 4. カスタムビューのエクスポート
今のままだとプラグイン開発用エディタでしかカスタムビューを開けない状態なので、エクスポートして本番用のエディタでもビューを開けるようにします。
ビューのプロジェクトクリックしてから
「ファイル」 > 「エクスポート」 > 「プラグイン開発」 > 「デプロイ可能なプラグインおよびフラグメント」
を選択し、jarファイルを出力します

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/b27724e5-a7cf-0e7c-4985-7535da2c53b0.png)

出力先は”C:\pleiades\2023-12\eclipse\dropins\MyView”など、eclipse\dropins以下にすると、Eclipse側でインポートしてくれます。

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/0e60fc9d-0a17-417f-b40e-1bc20dd95ba7.png)

また、Java21未満を使っている方はオプションタブに切り替えて「ワークスペースのコンパイル済みクラス・ファイルを使用」にチェックを入れてからエクスポートしてください

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/c0a605e2-6136-e626-b87e-712ec74005d5.png)

Eclipseをcleanで再起動すると、こちらのエディターでも「ウィンドウ」から「サンプル・ビュー」が開けるようになっていると思います
# おまけ：ChatGPTのAPIを呼び出すビューを作る
Eclipse上でChatGPTを呼び出してみます。
以下はサンプルコードです

```
package copilotview.views;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.texteditor.ITextEditor;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class SampleView extends ViewPart {
    public static final String ID = "copilotview.views.SampleView";
    private Text inputText;
    private Text responseText;
    private Button copyFromEditorButton;
    private Button sendToChatGPTButton;
    @Override
    public void createPartControl(Composite parent) {
        parent.setLayout(new GridLayout(2, false));
        Composite leftComposite = new Composite(parent, SWT.NONE);
        leftComposite.setLayout(new GridLayout(1, false));
        leftComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        inputText = new Text(leftComposite, SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
        GridData inputTextGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        inputTextGridData.heightHint = 200;
        inputText.setLayoutData(inputTextGridData);
        Composite buttonComposite = new Composite(leftComposite, SWT.NONE);
        buttonComposite.setLayout(new GridLayout(2, false));
        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        copyFromEditorButton = new Button(buttonComposite, SWT.PUSH);
        copyFromEditorButton.setText("Copy from Editor");
        sendToChatGPTButton = new Button(buttonComposite, SWT.PUSH);
        sendToChatGPTButton.setText("Send to ChatGPT");
        responseText = new Text(parent, SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
        GridData responseTextGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        responseTextGridData.heightHint = 200;
        responseText.setLayoutData(responseTextGridData);
        copyFromEditorButton.addListener(SWT.Selection, event -> copyTextFromEditor());
        sendToChatGPTButton.addListener(SWT.Selection, event -> sendTextToChatGPT());
    }
    private void copyTextFromEditor() {
        IEditorPart editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
        if (editor instanceof ITextEditor) {
            ITextEditor textEditor = (ITextEditor) editor;
            FileEditorInput input = (FileEditorInput) textEditor.getEditorInput();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(input.getFile().getContents()))) {
                StringBuilder content = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                }
                inputText.setText(content.toString());
            } catch (Exception e) {
                showMessage("Failed to copy text from editor: " + e.getMessage());
            }
        } else {
            showMessage("No text editor is currently active.");
        }
    }
    private void sendTextToChatGPT() {
        String userInput = inputText.getText()
        		+ "\n日本語でコードの解説をしてください";
        try {
            String apiUrl = "https://api.openai.com/v1/chat/completions";
            String apiKey = "{OpenAIのAPIキー}";
            JsonObject requestBody = new JsonObject();
            requestBody.addProperty("model", "gpt-4o-mini");
            JsonObject message = new JsonObject();
            message.addProperty("role", "user");
            message.addProperty("content", userInput);
            requestBody.add("messages", new Gson().toJsonTree(new JsonObject[] { message }));
            URL url = new URL(apiUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Authorization", "Bearer " + apiKey);
            connection.setDoOutput(true);
            try (OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream())) {
                writer.write(new Gson().toJson(requestBody));
            }
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            JsonObject jsonResponse = new Gson().fromJson(response.toString(), JsonObject.class);
            String messageContent = jsonResponse
                .getAsJsonArray("choices")
                .get(0)
                .getAsJsonObject()
                .getAsJsonObject("message")
                .get("content")
                .getAsString()
                .replace("*", "").replace("`", "");
            responseText.setText(messageContent);
        } catch (Exception e) {
            showMessage("Failed to send text to ChatGPT: " + e.getMessage());
        }
    }
    private void showMessage(String message) {
        MessageDialog.openInformation(
            inputText.getShell(),
            "CopilotView",
            message);
    }
    @Override
    public void setFocus() {
        inputText.setFocus();
    }
}
```

importが上手くできない場合はplugin.xmlを開いて依存関係を調整してください。

以下は実際のビューです

![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841947/27965c29-b5b5-723e-33e7-7b6ea1605acd.png)

ここではボタンからエディタのテキストをコピペしてChatGPTに投げることができています。使い方をもっと工夫すれば色々できそうですね。