Skip to content

Commit

Permalink
chore: fix output console regenerating monaco editor upon output upda…
Browse files Browse the repository at this point in the history
…te (#780)

* chore: fix output console regenerating monaco editor upon output update

* chore: utilize editor.getModel() to determine whether model exists

* chore: refactor console to use mobx observer

* chore: add additional test
  • Loading branch information
georgexu99 authored Jul 19, 2021
1 parent 9428869 commit e8c1434
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 58 deletions.
42 changes: 12 additions & 30 deletions src/renderer/components/output.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { autorun } from 'mobx';
import { autorun, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as MonacoType from 'monaco-editor';
import * as React from 'react';
Expand Down Expand Up @@ -35,16 +35,23 @@ export class Output extends React.Component<CommandsProps> {
public language = 'consoleOutputLanguage';

private outputRef = React.createRef<HTMLDivElement>();
private readonly model: MonacoType.editor.ITextModel;

constructor(props: CommandsProps) {
super(props);

const { monaco } = this.props;
this.language = 'consoleOutputLanguage';
this.model = monaco.editor.createModel('', this.language);
this.updateModel();
reaction(
() => props.appState.output.length,
() => this.updateModel(),
);
}

public async componentDidMount() {
autorun(async () => {
this.destroyMonacoEditor();
await this.initMonaco();
this.toggleConsole();
});
Expand All @@ -58,13 +65,6 @@ export class Output extends React.Component<CommandsProps> {
this.toggleConsole();
}

/**
* Set Monaco Editor's value.
*/
public async UNSAFE_componentWillReceiveProps(newProps: CommandsProps) {
await this.setContent(newProps.appState.output);
}

/**
* Initialize Monaco.
*/
Expand All @@ -79,11 +79,9 @@ export class Output extends React.Component<CommandsProps> {
readOnly: true,
contextmenu: false,
automaticLayout: true,
model: null,
model: this.model,
...monacoOptions,
});

await this.setContent(this.props.appState.output);
}
}

Expand Down Expand Up @@ -152,8 +150,8 @@ export class Output extends React.Component<CommandsProps> {
* @private
* @memberof Output
*/
private async setContent(output: OutputEntry[]) {
this.setEditorText(this.getOutputLines(output));
private async updateModel() {
this.model.setValue(this.getOutputLines(this.props.appState.output));
// have terminal always scroll to the bottom
this.editor?.revealLine(this.editor?.getScrollHeight());
}
Expand Down Expand Up @@ -182,22 +180,6 @@ export class Output extends React.Component<CommandsProps> {
return lines.join('\n');
}

/**
* Create a model and attach it to the editor
*
* @private
* @param {string} value
*/
private setEditorText(value: string) {
const { monaco } = this.props;
const model = monaco.editor.createModel(value, this.language);
model.updateOptions({
tabSize: 2,
});

this.editor?.setModel(model);
}

public render(): JSX.Element | null {
const { isConsoleShowing } = this.props.appState;

Expand Down
66 changes: 38 additions & 28 deletions tests/renderer/components/output-spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,6 @@ describe('Output component', () => {
expect(monaco.editor.create).toHaveBeenCalled();
expect(monaco.editor.createModel).toHaveBeenCalled();
});

it('initializes with a fixed tab size', async () => {
const wrapper = shallow(
<Output appState={store as any} monaco={monaco} monacoOptions={{}} />,
);
const instance: any = wrapper.instance();

instance.outputRef.current = 'ref';
await instance.initMonaco();

expect(monaco.latestModel.updateOptions).toHaveBeenCalledWith(
expect.objectContaining({
tabSize: 2,
}),
);
});
});

it('componentWillUnmount() attempts to dispose the editor', async () => {
Expand Down Expand Up @@ -120,7 +104,7 @@ describe('Output component', () => {
expect(wrapper.html()).not.toBe(null);
});

it('setContent updates model with correct values', async () => {
it('updateModel updates model with correct values', async () => {
store.output = [
{
timestamp: 1532704073130,
Expand All @@ -140,21 +124,47 @@ describe('Output component', () => {

instance.outputRef.current = 'ref';
await instance.initMonaco();
instance.updateModel();

instance.editor.setContent(store.output);
const expectedFormattedOutput =
new Date(store.output[0].timestamp).toLocaleTimeString() +
` Hi!\n` +
new Date(store.output[1].timestamp).toLocaleTimeString() +
' Hi!';
// makes sure setContent() is called with the right values
expect(monaco.editor.createModel).toHaveBeenCalledWith(
expectedFormattedOutput,
'consoleOutputLanguage',
);
expect(monaco.editor.createModel).toHaveBeenCalled();
expect(instance.editor.revealLine).toHaveBeenCalled();
});

it('updateModel correctly observes and gets called when output is updated', async () => {
store.output = [
{
timestamp: 1532704073130,
text: 'Hi!',
},
];

const wrapper = shallow(
<Output appState={store as any} monaco={monaco} monacoOptions={{}} />,
);

const instance: any = wrapper.instance();
const spy = jest.spyOn(instance, 'updateModel');

instance.outputRef.current = 'ref';
await instance.initMonaco();

instance.updateModel();

// new output
store.output = [
{
timestamp: 1532704073130,
text: 'Hi!',
},
{
timestamp: 1532704073130,
text: 'Hi!',
isNotPre: true,
},
];
expect(spy).toHaveBeenCalledTimes(2);
});

it('handles componentDidUpdate', async () => {
// set up component
const wrapper = shallow(
Expand Down

0 comments on commit e8c1434

Please sign in to comment.