MPS 2024.1 ヘルプ

進捗インジケーター

デフォルトでは、アクションは実行中は UI をブロックします。すぐに終了しないアクションの場合は、アクションがアクティブであることをユーザーに示し、進行状況と時間の見積もりに関するフィードバックをユーザーに提供する進行状況バーを表示することをお勧めします。このセクションでは、進行状況インジケーターを適切に表示および更新する方法、ユーザーがアクションを手動でキャンセルできるようにする方法、およびアクションをバックグラウンドに送信する方法について説明します。

非同期タスク

UI がフリーズしないように、すべてのアクションを迅速に行うことを目指す必要があります。長期的なアクティビティは、アクションから抽出して、1 つ以上の非同期で実行されるタスクに配置する必要があります。タスクは、com.intellij.openapi.progress @ java_stub からタスククラスを拡張し、モーダルダイアログを表示するか(Task.Modal クラス)、バックグラウンドに送信する(Task.Backgroundable クラス)ことができます。タスクは、ApplicationManager.getApplication()。invokeLater() メソッドを介して EDT で呼び出す必要があります。

action ModalProgressAction {   mnemonic: <no mnemonic>   execute outside command: false   also available in: << ... >>   caption: BackgroundableProgressAction   description: <no description>   icon: <no icon>     construction parameters     << ... >>   action context parameters ( always visible = false )     Project project key: PROJECT required     MPSProject mpsProject key: MPS_PROJECT required   <update block>     execute(event)->void {   boolean canBeCanceled = true;   Task.Modal modalTask = new Task.Modal(ModalProgressAction.this.project, "Modal cancelable task", canBeCanceled) {     public void run(@NotNull() final ProgressIndicator indicator) {      doWorkChunk1(); if (checkForCancellation()) return; doWorkChunk2();   if (checkForCancellation()) return; ...     }      @Override      public void onCancel() {        super.onCancel();      }    };    ApplicationManager.getApplication().invokeLater({ => ProgressManager.getInstance().run(modalTask); });  } }

タスクは通常、タスクを実行するための run() メソッドと、ユーザーによって呼び出されたキャンセルを処理するための onCancel() メソッドを提供します。実際のキャンセルロジックは、タスク実装者が実装する必要があります。run() での作業は、保留中のキャンセルをチェックするチャンクに編成する必要があります。onCancel() メソッドは、run() がキャンセルによって終了した後にのみ起動されるため、主にタスクによって実行された部分的な作業をクリーンアップするために使用されます。

キャンセル不可能なモーダルタスク(canBeCancelled パラメーターを false に設定)は、アクションが完全に終了するまでユーザーを待機させるため、キャンセルを適切に処理することが難しい重要なアクションに対してのみ、注意して使用する必要があります。

進捗状況の監視

タスクの run() メソッドは、IntelliJ の進行状況インジケーターのインスタンスをパラメーターとして取得します。jetbrains.mps.progress@java_stub からの ProgressMonitorAdapter でラップすることをお勧めします。ProgressMonitorAdapter は、視覚的な進行状況ダイアログを表し、実際の進行状況バーの値と、ユーザーに表示されるラベルを設定するためのメソッドを提供します。また、キャンセルに関する情報も保持しています。

Task.Modal modalTask = new Task.Modal(ModalProgressAction.this.project, "Modal cancelable task", canBeCanceled) {  public void run(@NotNull() final ProgressIndicator indicator) {    final ProgressMonitorAdapter adapter = new ProgressMonitorAdapter(indicator);        adapter.start("Progress in progress...", 8);    int stepValue = 1;      adapter.step("Do work 1 ...");    do work chunk 1   adapter.advance(stepValue);    if (adapter.isCanceled()) { return; }        adapter.step("Do work 2 ...");    do work chunk 2   adapter.advance(stepValue);    if (adapter.isCanceled()) { return; }      ...           adapter.done();  }

いくつかのメモ:

  • タスクのコンストラクターには、進行状況のタイトルとして使用されるテキストが含まれていますダイアログ

  • start() メソッドは、進行状況バーの上に表示するテキストと、タスクを完了するためのいくつかのステップ / ポイントを提供します

  • step() メソッドは、進行中の進行状況バーに表示されるテキストラベルを変更しますダイアログ

  • advance() メソッドは、指定されたステップ / ポイント数だけプログレスバーを新しい値に移動します

  • ユーザーエクスペリエンスを向上させるために、進行手順をできる限り小さくします。ステップを小さくすることで、ユーザーはよりスムーズに操作できます

バックグラウンドで実行

Task.Backgroundable クラスは、バックグラウンドで処理できるタスクに使用する必要があります。

action BackgroundableProgressAction {   mnemonic: <no mnemonic>   execute outside command: false   also available in: << ... >>   caption: BackgroundableProgressAction   description: <no description>   icon: <no icon>     construction parameters     << ... >>   action context parameters ( always visible = false )     Project project key: PROJECT required     MPSProject mpsProject key: MPS_PROJECT required   <update block>     execute(event)->void {   boolean canBeCanceled = true;   PerformInBackgroundOption showProgress = PerformInBackgroundOption.DEAF;   Task.Backgroundable backgroundable = new Task.Backgroundable(BackgroundableProgressAction.this.project, "Backgroundable cancelable task", canBeCanceled, showProgress) {     public void run(@NotNull() final ProgressIndicator indicator) {  ...     }      @Override      public void onCancel() {        super.onCancel();      }    };    ApplicationManager.getApplication().invokeLater({ => ProgressManager.getInstance().run(backgroundable); });  }   additional methods   private void doWork() {      ...   } }

いくつかのメモ:

  • バックグラウンド可能なタスクはキャンセルを許可する場合と許可しない場合があります

  • PerformInBackgroundOption インターフェースを使用すると、フォアグラウンドとバックグラウンドで開始するタスクを作成できます。

  • ユーザーは、バックグラウンド可能なタスクをバックグラウンドだけでなくフォアグラウンドにも移動できます

  • PerformInBackgroundOption インターフェースの事前定義された定数は、DEAF(フォアグラウンドで開始)と ALWAYS_BACKGROUND(バックグラウンドで開始、ユーザーの注意をそらすダイアログが表示されないため、ユーザーが注意を払う必要のない重要でないアクションに役立ちます。UI は常に完全に使用可能です _)。_

リソースにアクセスするときの適切なロック

タスク内で MPS リポジトリへの読み取りおよび書き込みロックを取得し、コマンドを実行しても問題ありません。

// ReadAction in step is ok for display state ModalProgressAction.this.mpsProject.getRepository().getModelAccess().runReadAction(new Runnable() {    public void run() {      adapter.step("Do some work with Read Lock...");      ModalProgressAction.this.doWork();    }  }); adapter.advance(stepValue);  if (adapter.isCanceled()) { return; }    // WriteAction in step is ok for display state  ModalProgressAction.this.mpsProject.getRepository().getModelAccess().runWriteAction(new Runnable() {    public void run() {      adapter.step("Do some work with Write Lock...");      ModalProgressAction.this.doWork();    }  });  adapter.advance(stepValue);  if (adapter.isCanceled()) { return; }    // Command in step is ok for display state  ModalProgressAction.this.mpsProject.getRepository().getModelAccess().executeCommand(new Runnable() {    public void run() {      adapter.step("Do some work in command...");      ModalProgressAction.this.doWork();    }  });  adapter.advance(stepValue);  if (adapter.isCanceled()) { return; }

いくつかのメモ:

  • アクション内でロックする必要がある場合は、すべての変更を単一のロックブロックにグループ化することをお勧めします

  • 他の潜在的なユーザーアクションのブロックを回避するために、不要になったらすぐにロックを解除します

  • EDT スレッドで R/W アクションまたはコマンドを使用しないでください - これにより、進行状況の予測不可能な更新が発生し、UI がフリーズする可能性があります

取り消し不可能なアクション

モデルを変更するには、元に戻すことができるアクションが必要です。これは、executeCommandInEDT() メソッドを介して実行できます。ただし、ProgressIndicator のメソッド自体は EDT で実行されており、プログレスバーの変更に対するすべての要求はコマンドが終了するまで遅延するため、コマンド内から ProgressIndicator のメソッドを呼び出さないでください。推奨されるアプローチは、executeCommandInEDT() でコマンドを呼び出す前に、メインアクションのスレッドからプログレスバーに指示し、コマンドが終了するまで、おそらく CyclicBarrier または他の同期プリミティブを使用してメインアクションのスレッドをブロックすることです。

adapter.step("Do some work in command ...");  final CyclicBarrier barrier = new CyclicBarrier(2);    ModalProgressAction.this.mpsProject.getRepository().getModelAccess().executeCommandInEDT(new Runnable() {    public void run() {     try {         model m = model/myModel/;        m.new root node(MyRootConceptDeclaration);      } finally {        try {          barrier.await();        } catch (BrokenBarrierException e) {          <no statements>        } catch (InterruptedException e) {          <no statements>        }      }    }  });  try {    barrier.await();  } catch (InterruptedException e) {    <no statements>  } catch (BrokenBarrierException e) {    <no statements>  }  adapter.advance(stepValue);  if (adapter.isCanceled()) { return; }

追加の方法セクションを活用して、アクションの本体からロック実装コードを抽出できます。

private void block(CyclicBarrier barrier) {    try {      barrier.await();    } catch (BrokenBarrierException e) {      <no statements>    } catch (InterruptedException e) {      <no statements>    }  }