Ace.jsにスニペットを追加する

##実行環境
・Electron
・Ace.js

#####2019/05/26 完全に書き換えました。
前回書いていたものはそれっぽく作ったもので、今回はちゃんと動くようになっています。

###### 2022/9/10 下の方に改正版作った。もう少し使いやすく出来ないかなと思って
---

Ace.jsはHTML上で動作するエディターを追加するものです
ちょっと独特な感じだけど

[前回の記事のコードをもとに構築してます](https://qiita.com/metalgearsolid3love/items/6e9491ee62dc13a21d76)


スニペット(Snippet)は

```javascript
fun
```
と入力すると

```javascript
function name() {
  // function
}
```
みたいになんか簡単に生成してくれるやつです(語彙力皆無)
~~その解説が何一つなかったので実際にそれらしく作ってみました~~
[探しまくってありました](https://groups.google.com/forum/#!topic/ace-discuss/BFbcl9b5Ma0)


```javascript
ace.config.loadModule('ace/ext/language_tools', function() {
	editor.setOptions({
		enableBasicAutocompletion: true,
		enableSnippets: true,
		enableLiveAutocompletion: true
	});
	const snippetManager = ace.require("ace/snippets").snippetManager;
	const config = ace.require("ace/config");
	ace.config.loadModule("ace/snippets/javascript", function(m) {
		if (m) {
			snippetManager.files.javascript = m;
			m.snippets = snippetManager.parseSnippetFile(m.snippetText);
			m.snippets.push(
				{
					"content": "const ${1:variable} = new Animation.base(x, y, z);",
					"name": "Animation",
					"tabTrigger": "Animation"
				}
			);
			snippetManager.register(m.snippets, m.scope);
		}
	});
});
```

##解説

```javascript
m.snippets.push(
	{
		"content": "const ${1:variable} = new Animation.base(x, y, z);",
		"name": "Animation",
		"tabTrigger": "Animation"
	}
);
```
${番号:名前}
これは変数名や引数等に付ける物で、
ユーザー側がTABを押したときに自動的に${番号:名前}の場所へ移動します。
説明難しいけどとにかく使ったらわかる(適当)



![スクリーンショット (324).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264677/ee1b3941-fab2-03e7-9335-038d65d2c452.png)
![スクリーンショット (325).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264677/ccaab960-794d-c01c-3e83-58847f9b3cfb.png)



##新しく作ったから改正版
```javascript
/* acejsに機能を追加する */

// メモリ空間の作成(何度も読み込まなくていいように)
ace.memory = {
  data: {},
  set(name, data)
  {
    // なかったら保存
    if(name in this.data) return null;
    this.data[name] = data;
  },
  get(name)
  {
    // あれば返却
    if(!name in this.data) return null;
    return this.data[name];
  }
};
/**
 * @module モジュールの読み込み
 * @param {string} name - モジュール名
 * @param {any} option - 省略可
 * @param {function} callback - コールバック関数
 */
ace.on = function(name, option, callback = null)
{
  // 省略した書き方に対応する
  if(!callback) callback = option;
  // 長いから省略
  const md = ace.config.loadModule;
  switch (name)
  {
    case "load":
      // aceに機能追加する為のモジュールを読み込む
      md("ace/ext/language_tools", callback);
    break;
    case "snippets":
      // スニペットを追加する為のモジュールを読み込む
      md(`ace/snippets/${option}`, callback);
    break;
  }
}

/**
 * @module スニペットを追加
 * @param {any} snippets - スニペット
 * @param {string} language - 言語を指定 省略可(ace.languageから取得)
 */
ace.customSnippets = function(snippets, language = null)
{
  // スニペットを追加出来るように設定
  editor.setOptions({
    enableBasicAutocompletion: true,
    enableSnippets: true,
    enableLiveAutocompletion: true
  });
  // メモリ空間に保存
  ace.memory.set("snippets", ace.require("ace/snippets").snippetManager);
  ace.memory.set("config", ace.require("ace/config"));
  // メモリ空間から取得
  const snippetManager = ace.memory.get("snippets");
  const config = ace.memory.get("ace/config");
  // スニペットモジュールの読み込み
  // languageを指定されたらそれを優先する
  ace.on("snippets", language ? language: ace.language, function(m)
  {
    if(!m) return;
    snippetManager.files.javascript = m;
    // 既存のスニペットファイルの読み込み
    m.snippets = snippetManager.parseSnippetFile(m.snippetText);
    // ここでスニペットに追加してる
    if(!Array.isArray(snippets)) m.snippets.push(snippets)
    else m.snippets.push(...snippets);
    snippetManager.register(m.snippets, m.scope);
  });
}


/* メイン処理 */

// 対応する言語をデフォルトに設定
ace.language = "javascript";

// editorの作成
const editor = ace.edit("editor", {
  theme: "ace/theme/monokai", // テーマ
  mode: `ace/mode/${ace.language}`, // 言語
  minLines: 5, // 初期行数
});

// モジュールのロード後
ace.on("load", function()
{
  // 一つだけスニペットを追加する
  // 言語はデフォルトのace.languageから参照
  ace.customSnippets({
    "content": "const ${1:variable} = new Animation.base(x, y, z);",
    "name": "Animation",
    "tabTrigger": "Animation"
  });
  // 配列で複数のスニペットを追加する
  ace.customSnippets([
    {
      "content": "const ${1:variable} = new Animation.base(x, y, z);",
      "name": "Aaaa",
      "tabTrigger": "Aaaa"
    },
    {
      "content": "const ${1:variable} = new Animation.base(x, y, z);",
      "name": "Bbbb",
      "tabTrigger": "Bbbb"
    }
  ]);
  // スニペットを登録する言語を指定する
  ace.customSnippets({
    "content": "<!html><head></head><body></body>",
    "name": "html",
    "tabTrigger": "html"
  }, "html");
});
```