Resize CodeMirror dynamically

When you resize a window, CodeMirror will now resize dynamically
without needing a page refresh.

Video: https://imgur.com/a/VL9Zhax
(first vid is before, second is after)

Change-Id: Ia6823f9d7daef0cb6693a8768808e64b859d39b3
diff --git a/web/element/codemirror-element.ts b/web/element/codemirror-element.ts
index 7043344..7b1bf5a 100644
--- a/web/element/codemirror-element.ts
+++ b/web/element/codemirror-element.ts
@@ -62,6 +62,8 @@
 
   private initialized = false;
 
+  private onResize: (() => void) | null = null;
+
   static override get styles() {
     return [
       css`
@@ -120,20 +122,12 @@
     if (this.initialized) return;
     this.initialized = true;
 
-    const offsetTop = this.getBoundingClientRect().top;
-    const clientHeight = window.innerHeight ?? document.body.clientHeight;
-    // We are setting a fixed height, because for large files we want to
-    // benefit from CodeMirror's virtual scrolling.
-    // 80px is roughly the size of the bottom margins plus the footer height.
-    // This ensures the height of the textarea doesn't push out of screen.
-    const height = clientHeight - offsetTop - 80;
-
     const editor = new EditorView({
       state: EditorState.create({
         doc: this.fileContent ?? '',
         extensions: [
           ...extensions(
-            height,
+            this.calculateHeight(),
             this.prefs,
             this.fileType,
             this.fileContent ?? '',
@@ -200,6 +194,17 @@
     // Makes sure to show line number and column number on initial
     // load.
     this.updateCursorPosition(editor);
+
+    this.onResize = () => this.updateEditorHeight(editor);
+    window.addEventListener('resize', this.onResize);
+  }
+
+  override disconnectedCallback() {
+    super.disconnectedCallback();
+    if (this.onResize) {
+      window.removeEventListener('resize', this.onResize);
+      this.onResize = null;
+    }
   }
 
   setCursorToLine(view: EditorView, lineNum: number) {
@@ -227,4 +232,21 @@
       this.result.textContent = `Line: ${line.number}, Column: ${cursor - line.from + 1}`;
     }
   }
+
+  private calculateHeight() {
+    const offsetTop = this.getBoundingClientRect().top;
+    const clientHeight = window.innerHeight ?? document.body.clientHeight;
+    // We take offsetTop twice to ensure the height of the texarea doesn't push
+    // out of screen. We no longer do a hardcore value which was 80 before.
+    // offsetTop seems to be what we've been looking for to do it dynamically.
+    return Math.max(0, clientHeight - offsetTop - offsetTop);
+  }
+
+  private updateEditorHeight(editor: EditorView) {
+    const height = this.calculateHeight();
+    const editorElement = editor.dom;
+    if (editorElement) {
+      editorElement.style.height = `${height}px`;
+    }
+  }
 }