aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/frontend/src/editor/EditorErrors.tsx
blob: 40becf7ea4d90c14b7ff5f6d999325d9c2839f9d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
 *
 * SPDX-License-Identifier: EPL-2.0
 */

import { Diagnostic } from '@codemirror/lint';
import { type IReactionDisposer, makeAutoObservable, reaction } from 'mobx';

import type EditorStore from './EditorStore';

const HYSTERESIS_TIME_MS = 250;

export interface State {
  analyzing: boolean;
  errorCount: number;
  warningCount: number;
  infoCount: number;
  semanticsError: string | undefined;
}

export default class EditorErrors implements State {
  private readonly disposer: IReactionDisposer;

  private timer: number | undefined;

  analyzing = false;

  errorCount = 0;

  warningCount = 0;

  infoCount = 0;

  semanticsError: string | undefined;

  constructor(private readonly store: EditorStore) {
    this.updateImmediately(this.getNextState());
    makeAutoObservable<EditorErrors, 'disposer' | 'timer'>(this, {
      disposer: false,
      timer: false,
    });
    this.disposer = reaction(
      () => this.getNextState(),
      (nextState) => {
        if (this.timer !== undefined) {
          clearTimeout(this.timer);
          this.timer = undefined;
        }
        if (nextState.analyzing) {
          this.timer = setTimeout(
            () => this.updateImmediately(nextState),
            HYSTERESIS_TIME_MS,
          );
        } else {
          this.updateImmediately(nextState);
        }
      },
      { fireImmediately: true },
    );
  }

  get highestDiagnosticLevel(): Diagnostic['severity'] | undefined {
    if (this.errorCount > 0) {
      return 'error';
    }
    if (this.warningCount > 0) {
      return 'warning';
    }
    if (this.infoCount > 0) {
      return 'info';
    }
    return undefined;
  }

  private getNextState(): State {
    return {
      analyzing: this.store.analyzing,
      errorCount: this.store.errorCount,
      warningCount: this.store.warningCount,
      infoCount: this.store.infoCount,
      semanticsError: this.store.semanticsError,
    };
  }

  private updateImmediately(nextState: State) {
    Object.assign(this, nextState);
  }

  dispose() {
    this.disposer();
  }
}