ソースエディター

ソースエディタはGitLabでの編集体験を提供します。Monacoエディタの薄いラッパーで、必要なヘルパーや抽象化を提供し、エクステンションを使ってMonacoを拡張します。GitLabの複数の機能がこれを使用しています:

ソースエディタを使うとき

ソースエディタは、ユーザーがファイルの内容を編集する必要がある場合にのみ使用してください。ソースコードを表示するだけでよい場合は、BlobContent コンポーネントの使用を検討してください。

作業中のページがすでにソースエディタを読み込んでいる場合でも、ソースエディタで読み取り専用のコンテンツを表示することは有効なオプションです。

ソースエディターの使い方

Source Editorはフレームワークに依存せず、RailsとVueの両方を含むあらゆるアプリケーションで使用できます。インテグレーションを支援するために、専用の<source-editor> Vue コンポーネントを用意していますが、Source Editor のインテグレーションは一般的に簡単です:

  1. Source Editorをインポートします:

    import SourceEditor from '~/editor/source_editor';
    
  2. ビューのグローバル エディタを初期化します:

    const editor = new SourceEditor({
      // Editor Options.
      // The list of all accepted options can be found at
      // https://microsoft.github.io/monaco-editor/docs.html
    });
    
  3. エディタのインスタンスを作成します:

    editor.createInstance({
      // Source Editor configuration options.
    })
    

ソース エディターのインスタンスでは、以下の設定オプションを使用できます:

オプション必須ですか?説明
eltrue HTML Node:エディタをレンダリングする要素。
blobPathfalse String:エディタでレンダリングするファイル名で、そのファイルで使用する正しいシンタックスハイライター、または別のファイルタイプを識別するために使用します。*.js のようなワイルドカードを使用することができます。
blobContentfalse String:エディタでレンダリングする初期コンテンツ。
extensionsfalse Array:このインスタンスで使用するエクステンション。
blobGlobalIdfalse String:
注意:このプロパティは将来なくなる可能性があります。自分が何をしているのかわからない限り、blobGlobalId を渡さないでください。
エディターオプションfalse Object(s):上記のリスト以外のプロパティは、この特定のインスタンスのEditor Optionとして扱われます。インスタンスレベルでグローバルエディタオプションを上書きするには、このフィールドを使用します。エディターオプションの完全なインデックスが利用可能です。

API

Monacoエディタと同じ公開APIを使用し、インスタンスレベルで追加機能を使用します:

関数引数説明
updateModelLanguage path:文字列インスタンスのシンタックスハイライトを、渡されたpath の拡張子に従うように更新します。インスタンスレベルでのみ有効です。
useオブジェクトの配列インスタンスに適用する拡張子の配列。オブジェクトの配列のみを受け入れます。エクステンションのES6モジュールは、useに渡される前に、ビューまたはコンポーネントで取得され、解決されなければなりません。インスタンスレベルとグローバルエディタ(すべてのインスタンス)レベルで利用可能です。
モナコエディタのオプション ドキュメントを見るMonacoエディタのデフォルトオプション。

ヒント

  1. 編集者のロード状態

    読み込み状態はソースエディタに組み込まれているため、HTMLでスピナーやローダーが必要になることはほとんどありません。組み込みのローディング状態を利用するには、エディタを含むべきHTML要素にdata-editor-loading プロパティを設定します。起動時に、ソースエディタは自動的にローダーを表示します。

    Source Editor: loading state

  2. ファイル名が変更された場合、シンタックスハイライトを更新します。

    // fileNameEl here is the HTML input element that contains the file name
    fileNameEl.addEventListener('change', () => {
      this.editor.updateModelLanguage(fileNameEl.value);
    });
    
  3. エディタの内容を取得します。

    変更のたびにエディタにリスナーを設定することもありますが、急速に高価なオペレーションになる可能性があります。その代わりに、必要なときにエディタの内容を取得します。たとえば、フォームの送信時などです:

    form.addEventListener('submit', () => {
      my_content_variable = this.editor.getValue();
    });
    
  4. パフォーマンス

    Source Editor自体は非常にスリムですが、Monacoエディタに依存しているため、重さが増します。ビューに Source Editor を追加するたびに、JavaScript バンドルのサイズが大幅に増加し、ビューの読み込みパフォーマンスに影響します。どちらかであれば、オンデマンドでエディタをインポートすることをお勧めします:

    • ビューにエディタが必要かどうか不明な場合。
    • エディターはビューの二次的な要素です。

    オンデマンドでのソースエディタのロードは、他のモジュールのロードと同様に処理されます:

    someActionFunction() {
      import(/* webpackChunkName: 'SourceEditor' */ '~/editor/source_editor').
        then(({ default: SourceEditor }) => {
          const editor = new SourceEditor();
          ...
        });
      ...
    }
    

拡張機能

Source Editorは、製品全体に普遍的で拡張可能な編集ツールを提供し、特定のグループに依存しません。Source EditorのコアはCreate::Editor FEチームが所有していますが、主要な機能要素である拡張機能はどのグループでも所有できます。ソースエディターの拡張機能の目標は、エディターのコアをスリムで安定した状態に保つことです。必要な機能は、このコアに拡張機能として追加することができます。どのグループも、Source Editorの変更によって編集機能が壊れたり上書きされたりすることを心配することなく、新しい編集機能を構築し、所有することができます。

拡張機能では他のモジュールに依存することができます。この構成により、必要なときだけ依存モジュールをインポートすることで、Source Editorのコアのサイズを抑えることができます。

構造的には、Source Editor の完全な実装は次の図のようになります:

graph TD; B[Extension 1]---A[Source Editor] C[Extension 2]---A[Source Editor] D[Extension 3]---A[Source Editor] E[...]---A[Source Editor] F[Extension N]---A[Source Editor] A[Source Editor]---Z[Monaco]

エクステンションは、JavaScriptオブジェクトをエクスポートするES6モジュールです:

import { Position } from 'monaco-editor';

export default {
  navigateFileStart() {
    this.setPosition(new Position(1, 1));
  },
};

拡張機能の関数では、this 現在の Source Editor インスタンスを参照 thisします。をthis 使用 thisすると、インスタンス全体の API (この特定のケースではsetPosition() メソッドなど) にアクセスできます。

既存の拡張機能の使用

ソースエディタのインスタンスにエクステンションを追加するには、以下の手順が必要です:

import SourceEditor from '~/editor/source_editor';
import MyExtension from '~/my_extension';

const editor = new SourceEditor().createInstance({
  ...
});
editor.use(MyExtension);

エクステンションの作成

最初のSource Editorエクステンションを作成しましょう。エクステンションは、Source Editorの機能を拡張するために使用される、基本的なObject をエクスポートするES6モジュールです。テストとして、Source Editor を拡張し、呼び出されるとエディターの内容をalertに出力する新しい関数を持つ拡張機能を作成してみましょう。

~/my_folder/my_fancy_extension.js:

export default {
  throwContentAtMe() {
    alert(this.getValue());
  },
};

コード例では、this はインスタンスを参照しています。インスタンスを参照することで、getValue()のような関数を含む、基本的なMonaco エディタ API のすべてにアクセスできます。

それでは、拡張機能を使ってみましょう:

~/my_folder/component_bundle.js:

import SourceEditor from '~/editor/source_editor';
import MyFancyExtension from './my_fancy_extension';

const editor = new SourceEditor().createInstance({
  ...
});
editor.use(MyFancyExtension);
...
someButton.addEventListener('click', () => {
  editor.throwContentAtMe();
});

まず最初に、Source Editorと新しいエクステンションをインポートします。そしてエディタとそのインスタンスを作成します。デフォルトではSource EditorはthrowContentAtMe メソッドを持っていません。しかし、editor.use(MyFancyExtension) の行によって、このメソッドがインスタンスに導入されます。その後、必要なときにいつでも使うことができます。この場合、理論的なボタンがクリックされたときに呼び出します。

このスクリプトは、someButton がクリックされると、エディタのコンテナを含むアラートを表示します。

Source Editor new extension's result

ヒント

  1. パフォーマンス

    ソースエディタ本体と同様に、ビューの読み込みパフォーマンスを損なわないように、任意の拡張機能をオンデマンドで読み込むことができます:

    const EditorPromise = import(
      /* webpackChunkName: 'SourceEditor' */ '~/editor/source_editor'
    );
    const MarkdownExtensionPromise = import('~/editor/source_editor_markdown_ext');
       
    Promise.all([EditorPromise, MarkdownExtensionPromise])
      .then(([{ default: SourceEditor }, { default: MarkdownExtension }]) => {
        const editor = new SourceEditor().createInstance({
          ...
        });
        editor.use(MarkdownExtension);
      });
    
  2. 複数のエクステンションの使用

    use メソッドに拡張子の配列を渡します:

    editor.use([FileTemplateExtension, MyFancyExtension]);