Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can editor stretch to fit its content? .editor{height: auto} #794

Closed
GitKou opened this issue Apr 2, 2018 · 19 comments
Closed

Can editor stretch to fit its content? .editor{height: auto} #794

GitKou opened this issue Apr 2, 2018 · 19 comments
Assignees
Labels
feature-request Request for new features or functionality
Milestone

Comments

@GitKou
Copy link

GitKou commented Apr 2, 2018

monaco-editor version: 0.10.1
image
div.editor{height: auto}
It seems like the monaco-eidtor's height depends on its container's height,
but its container's height is auto, I want the container(div.editor)'s height could be stretched by its content.

@troy351
Copy link
Contributor

troy351 commented Apr 2, 2018

A specific height is needed for container. Code could be hundreds of lines, and makes no sense to stretch editor by its content.
btw, you could use editor.layout() to re-layout the editor

@acautin
Copy link

acautin commented Apr 23, 2018

@troy351 it is possible to get the size of the editor and then maybe decide to resize the container up to a certain limit, much like this github comment works ?

@troy351
Copy link
Contributor

troy351 commented Apr 23, 2018

const contentHeight = editor.getModel().getLineCount() * 19 ; // 19 is the line height of default theme.
then you can set your container's height with contentHeight and run editor.layout()

@David-Mulder
Copy link

@troy351 This will give incorrect results if you have collapsed content

@troy351
Copy link
Contributor

troy351 commented Oct 4, 2018

@David-Mulder Good catch.
I did a lot of search but didn't find a onDidFoldingChange listener or a getFoldedLineCount method.
After trying for a long time, I figured out a hacky way to make it work.

// disable scroll beyond last line
editor.updateOptions({ scrollBeyondLastLine: false });

const LINE_HEIGHT = 18;
const CONTAINER_GUTTER = 10;

const el = editor.domElement;
const codeContainer = el.getElementsByClassName('view-lines')[0];

let prevLineCount = 0;

editor.onDidChangeModelDecorations(() => {
  // wait until dom rendered
  setTimeout(() => {
    const height =
      codeContainer.childElementCount > prevLineCount
        ? codeContainer.offsetHeight // unfold
        : codeContainer.childElementCount * LINE_HEIGHT + CONTAINER_GUTTER; // fold
    prevLineCount = codeContainer.childElementCount;

    el.style.height = height + 'px';

    editor.layout();
  }, 0);
});

BTW, a debounce is suggested for the listener.

@abersnaze
Copy link

I added an extra check that if codeContainer.offsetHeight === 0 to reschedule the wait until dom timeout again.

@raedle
Copy link

raedle commented Feb 17, 2019

@abersnaze Could you provide the complete code sample? Thanks

@abersnaze
Copy link

@alexdima alexdima added the feature-request Request for new features or functionality label Mar 1, 2019
@alexdima alexdima added this to the On Deck milestone Mar 1, 2019
@BrianLeishman
Copy link

@troy351 this actually works with word wrap enabled as well, nice job

@bagdonas
Copy link

bagdonas commented Feb 7, 2020

In 0.19.3 the following seems to work without touching DOM to guess what the editor size is:

editor.onDidChangeModelDecorations(() => {
    updateEditorHeight() // typing
    requestAnimationFrame(updateEditorHeight) // folding
})

let prevHeight = 0

const updateEditorHeight = () => {
    const editorElement = editor.getDomNode()

    if (!editorElement) {
      return
    }

    const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight)
    const lineCount = editor.getModel()?.getLineCount() || 1
    const height = editor.getTopForLineNumber(lineCount + 1) + lineHeight

    if (prevHeight !== height) {
      prevHeight = height
      editorElement.style.height = `${height}px`
      editor.layout()
    }
  }

@alexdima
Copy link
Member

alexdima commented Sep 8, 2020

Starting with 0.20.x, there is editor.getContentHeight(). This can be queried and the outer container height can be set to this value: e.g.

At https://microsoft.github.io/monaco-editor/playground.html

const width = 300;
const container = document.getElementById('container');
container.style.border = '1px solid #f00';
const editor = monaco.editor.create(container, {
	value: "function hello() {\n\talert('Hello world!');\n}",
	language: "javascript",
    scrollBeyondLastLine: false,
    wordWrap: 'on',
    wrappingStrategy: 'advanced',
    minimap: {
        enabled: false
    },
    overviewRulerLanes: 0
});
let ignoreEvent = false;
const updateHeight = () => {
	const contentHeight = Math.min(1000, editor.getContentHeight());
	container.style.width = `${width}px`;
	container.style.height = `${contentHeight}px`;
	try {
		ignoreEvent = true;
		editor.layout({ width, height: contentHeight });
	} finally {
		ignoreEvent = false;
	}
};
editor.onDidContentSizeChange(updateHeight);
updateHeight();

Kapture 2020-09-08 at 17 34 24

vivek1729 added a commit to vivek1729/nteract that referenced this issue Jul 24, 2021
contentSizeChange event, adopt the new padding option to do away with
the TopMargin hack

Monaco 0.20 introduced a `getContentHeight` API that enables us to get
the current editor height without resorting to hacks around line counts.
This also handles cases such as code folding. See this issue for
details: microsoft/monaco-editor#794

Also added more type checking in the editor instantiation, leveraged the
new padding option to do away with the TopMargin work around.
@ppazos
Copy link

ppazos commented May 27, 2022

@alexdima the solution doesn't seem to work for the diff editor. I get getContentHeight and onDidContentSizeChange are not defined for the diff editor. Any clues on how that could work?

@DavidDiao
Copy link

DavidDiao commented Sep 22, 2022

@alexdima the solution doesn't seem to work for the diff editor. I get getContentHeight and onDidContentSizeChange are not defined for the diff editor. Any clues on how that could work?

For diff editor, you can get the top of the last line of the two editors respectively and take the maximum value

container.style.height = Math.max(
  diffEditor.getOriginalEditor().getTopForLineNumber(diffEditor.getModel().original.getLineCount()),
  diffEditor.getModifiedEditor().getTopForLineNumber(diffEditor.getModel().modified.getLineCount()),
) + 19 + 'px';
diffEditor.layout();

Or calculate from diffEditor.getLineChanges() (I didn't test this solution)

let lineCount = {
  original: diffEditor.getModel().original.getLineCount(),
  modified: diffEditor.getModel().modified.getLineCount(),
};
for (let change of diffEditor.getLineChanges()) {
  if (!change.modifiedEndLineNumber) lineCount.modified += change.originalEndLineNumber - change.originalStartLineNumber + 1;
  else if (!change.originalEndLineNumber) lineCount.original += change.modifiedEndLineNumber- change.modifiedStartLineNumber + 1;
  else {
    let diff = (change.originalEndLineNumber - change.originalStartLineNumber) - (change.modifiedEndLineNumber- change.modifiedStartLineNumber);
    if (diff > 0) lineCount.modified += diff;
    else lineCount.original -= diff;
  }
}
container.style.height = Math.max(
  lineCount.original,
  lineCount.modified,
) * 19 + 'px';
diffEditor.layout();

Note that IDiffEditor doesn't provide getOption() and two editors can have different settings, so I directly used default line height.

@zenatshockbyte
Copy link

zenatshockbyte commented Oct 11, 2022

For diff editor, you can get the top of the last line of the two editors respectively and take the maximum value

container.style.height = Math.max(
  diffEditor.getOriginalEditor().getTopForLineNumber(diffEditor.getModel().original.getLineCount()),
  diffEditor.getModifiedEditor().getTopForLineNumber(diffEditor.getModel().modified.getLineCount()),
) + 19 + 'px';
diffEditor.layout();

This is perfect with renderSideBySide: false but something else seems necessary for renderSideBySide: true

@zenatshockbyte
Copy link

zenatshockbyte commented Oct 12, 2022

const originalLineCount = editorInstance.getModel()?.original.getLineCount() || 0;

let finalLineCount = originalLineCount;

for (let change of editorInstance.getLineChanges() || []) {
    let diff = change.modifiedEndLineNumber - change.modifiedStartLineNumber + 1;
    finalLineCount += Math.abs(diff);
}
editor.value.style.height = finalLineCount * 18 + "px";

Seems to work for me, though haven't tested with many different diffs yet

@DavidDiao
Copy link

const originalLineCount = editorInstance.getModel()?.original.getLineCount() || 0;

let finalLineCount = originalLineCount;

for (let change of editorInstance.getLineChanges() || []) {
    let diff = change.modifiedEndLineNumber - change.modifiedStartLineNumber + 1;
    finalLineCount += Math.abs(diff);
}
editor.value.style.height = finalLineCount * 18 + "px";

Seems to work for me, though haven't tested with many different diffs yet

It doesn't work. For example, one empty line in original and two empty lines in modified got finalLineCount=1, but should be 2.
Here's another more complex example. original: 1\n3\n4\n5, modified: 0\n1\n2\n3\n5

@chengtie
Copy link

chengtie commented Jan 17, 2023

Starting with 0.20.x, there is editor.getContentHeight(). This can be queried and the outer container height can be set to this value: e.g.

At https://microsoft.github.io/monaco-editor/playground.html

const width = 300;
const container = document.getElementById('container');
container.style.border = '1px solid #f00';
const editor = monaco.editor.create(container, {
	value: "function hello() {\n\talert('Hello world!');\n}",
	language: "javascript",
    scrollBeyondLastLine: false,
    wordWrap: 'on',
    wrappingStrategy: 'advanced',
    minimap: {
        enabled: false
    },
    overviewRulerLanes: 0
});
let ignoreEvent = false;
const updateHeight = () => {
	const contentHeight = Math.min(1000, editor.getContentHeight());
	container.style.width = `${width}px`;
	container.style.height = `${contentHeight}px`;
	try {
		ignoreEvent = true;
		editor.layout({ width, height: contentHeight });
	} finally {
		ignoreEvent = false;
	}
};
editor.onDidContentSizeChange(updateHeight);
updateHeight();

Kapture 2020-09-08 at 17 34 24

I realized that if we remove wordWrap: 'on' from the options, updateHeight is triggered twice when we type 1 character in the editor, is it an expected behavior? In my editor, we need horizontal scrolling and don't want to set wordWrap: 'on'.

const width = 300;
const container = document.getElementById('container');
container.style.border = '1px solid #f00';
const editor = monaco.editor.create(container, {
	value: "abc",
	language: "javascript",
    scrollBeyondLastLine: false,
    wrappingStrategy: 'advanced',
    minimap: {
        enabled: false
    },
    overviewRulerLanes: 0
});
let ignoreEvent = false;
const updateHeight = () => {
        console.log("updateHeight is triggered")
	const contentHeight = Math.min(1000, editor.getContentHeight());
	container.style.width = `${width}px`;
	container.style.height = `${contentHeight}px`;
	try {
		ignoreEvent = true;
		editor.layout({ width, height: contentHeight });
	} finally {
		ignoreEvent = false;
	}
};
editor.onDidContentSizeChange(updateHeight);
updateHeight();

@xhd2015
Copy link

xhd2015 commented Jan 29, 2023

seems we can omit arguments for editor.layout() :

editor.layout({ width, height: contentHeight });

editor.layout();

@hediet hediet closed this as not planned Won't fix, can't repro, duplicate, stale Feb 21, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Apr 7, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature-request Request for new features or functionality
Projects
None yet
Development

No branches or pull requests