aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main/js/editor/EditorStore.ts
blob: 167e1adec27a802e651b181c8bcb58bc77c45438 (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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { Editor, EditorConfiguration } from 'codemirror';
import {
  createAtom,
  makeAutoObservable,
  observable,
} from 'mobx';
import 'mode-problem';
import {
  IXtextOptions,
  IXtextServices,
  createServices,
  removeServices,
} from 'xtext/xtext-codemirror';

const xtextLang = 'problem';

const xtextOptions: IXtextOptions = {
  xtextLang,
  enableFormattingAction: true,
};

const codeMirrorGlobalOptions: EditorConfiguration = {
  mode: `xtext/${xtextLang}`,
  indentUnit: 2,
  theme: 'material-darker',
};

export default class EditorStore {
  _atom;
  editor?: Editor;
  xtextServices?: IXtextServices;
  value = '';
  showLineNumbers = false;

  constructor() {
    this._atom = createAtom('EditorStore');
    makeAutoObservable(this, {
      _atom: false,
      editor: observable.ref,
      xtextServices: observable.ref,
    });
  }

  /**
   * Attaches a new CodeMirror instance and creates Xtext services.
   *
   * The store will not subscribe to any CodeMirror events. Instead,
   * the editor component should subscribe to them and relay them to the store.
   *
   * @param newEditor The new CodeMirror instance
   */
  editorDidMount(newEditor: Editor) {
    if (this.editor) {
      throw new Error('CoreMirror editor mounted before unmounting');
    }
    this.editor = newEditor;
    this.xtextServices = createServices(newEditor, xtextOptions);
  }

  editorWillUnmount() {
    if (this.editor) {
      removeServices(this.editor);
    }
    this.editor = undefined;
    this.xtextServices = undefined;
  }

  /**
   * Updates the contents of the editor.
   *
   * @param newValue The new contents of the editor
   */
  updateValue(newValue: string) {
    this.value = newValue;
  }

  reportChanged() {
    this._atom.reportChanged();
  }

  _observeEditorChanges() {
    this._atom.reportObserved();
  }

  get codeMirrorOptions(): EditorConfiguration {
    return {
      ...codeMirrorGlobalOptions,
      lineNumbers: this.showLineNumbers,
    };
  }

  /**
   * @returns `true` if there is history to undo
   */
  get canUndo() {
    this._observeEditorChanges();
    if (!this.editor) {
      return false;
    }
    const { undo: undoSize } = this.editor.historySize();
    return undoSize > 0;
  }

  undo() {
    this.editor?.undo();
  }

  /**
   * @returns `true` if there is history to redo
   */
  get canRedo() {
    this._observeEditorChanges();
    if (!this.editor) {
      return false;
    }
    const { redo: redoSize } = this.editor.historySize();
    return redoSize > 0;
  }

  redo() {
    this.editor?.redo();
  }

  toggleLineNumbers() {
    this.showLineNumbers = !this.showLineNumbers;
  }
}