Add support for dark mode

Depends-On: I5e3105adf9f2989808417a16d0fbe86d6b485bad
Change-Id: I5bb16df0e794821d7a77cf4bd83d798ecd780196
diff --git a/web/BUILD b/web/BUILD
index 3c91428..2547585 100644
--- a/web/BUILD
+++ b/web/BUILD
@@ -74,6 +74,7 @@
         "@plugins_npm//@codemirror/lint",
         "@plugins_npm//@codemirror/search",
         "@plugins_npm//@codemirror/state",
+        "@plugins_npm//@codemirror/theme-one-dark",
         "@plugins_npm//@codemirror/view",
     ],
 )
diff --git a/web/element/codemirror-element.ts b/web/element/codemirror-element.ts
index d8afff6..8b036fa 100644
--- a/web/element/codemirror-element.ts
+++ b/web/element/codemirror-element.ts
@@ -50,6 +50,8 @@
 
   @property({type: Object}) prefs?: EditPreferencesInfo;
 
+  @property({type: Boolean}) darkMode = false;
+
   @query('#wrapper')
   wrapper!: HTMLElement;
 
@@ -63,10 +65,6 @@
             monospace;
           /* CodeMirror has a default z-index of 4. Set to 0 to avoid collisions with fixed header. */
           z-index: 0;
-          background: white;
-        }
-        .cm-lineNumbers {
-          background-color: #f1f3f4;
         }
         .CodeMirror-ruler {
           border-left: 1px solid #ddd;
@@ -109,13 +107,18 @@
             height,
             this.prefs,
             this.fileType,
-            this.fileContent ?? ''
+            this.fileContent ?? '',
+            this.darkMode
           ),
           EditorView.updateListener.of(update => {
             if (this.prefs?.line_length) {
               // This is required to be in the setTimeout() to ensure the
               // line is set as correctly as possible.
-              updateRulerWidth(this.prefs.line_length, update.view.defaultCharacterWidth, true);
+              updateRulerWidth(
+                  this.prefs.line_length,
+                  update.view.defaultCharacterWidth,
+                  true
+              );
             }
 
             if (update.docChanged) {
@@ -136,7 +139,7 @@
                 e.stopPropagation();
               }
             },
-          })
+          }),
         ],
       }),
       parent: this.wrapper as Element,
diff --git a/web/element/extensions.ts b/web/element/extensions.ts
index 41dcca2..398684c 100644
--- a/web/element/extensions.ts
+++ b/web/element/extensions.ts
@@ -28,13 +28,14 @@
   history,
   historyKeymap,
   indentWithTab,
-  insertTab
+  insertTab,
 } from '@codemirror/commands';
 import {searchKeymap, highlightSelectionMatches} from '@codemirror/search';
 import {closeBrackets, closeBracketsKeymap} from '@codemirror/autocomplete';
 import {rulerPlugin} from './ruler';
 import {language} from './language';
 import {EditPreferencesInfo} from './codemirror-element';
+import {oneDark} from '@codemirror/theme-one-dark';
 
 const trailingspace = () =>
   EditorView.theme({
@@ -78,21 +79,28 @@
     '.cm-scroller': {overflow: 'auto'},
   });
 
+const oneLight = EditorView.theme({
+  '&': {background: 'white'},
+  '.cm-lineNumbers': {'background-color': '#f1f3f4'},
+});
+
 export const extensions = (
     height: number,
     prefs?: EditPreferencesInfo,
     fileType?: string,
-    fileContent?: string
+    fileContent?: string,
+    darkMode?: boolean
 ) => {
   // This uses the preference to detect whether
   // to use 'tabs' when you use the tab button
   // or to use 'spaces' when using the tab button.
-  const tab = prefs?.indent_with_tabs ?
-    {
+  const tab = prefs?.indent_with_tabs
+    ? {
       key: 'Tab',
       preventDefault: true,
       run: insertTab,
-    } : indentWithTab;
+    }
+    : indentWithTab;
 
   const codeExtensions: Array<Extension> = [
     lineNumbers(),
@@ -110,7 +118,7 @@
       ...searchKeymap,
       ...historyKeymap,
       ...foldKeymap,
-      tab
+      tab,
     ]),
     trailingspace(),
     tabsOrSpaces(),
@@ -162,5 +170,11 @@
     codeExtensions.push(EditorState.lineSeparator.of('\r\n'));
   }
 
+  if (darkMode) {
+    codeExtensions.push(oneDark);
+  } else {
+    codeExtensions.push(oneLight);
+  }
+
   return codeExtensions;
 };
diff --git a/web/element/language.ts b/web/element/language.ts
index 50c1f03..0d72574 100644
--- a/web/element/language.ts
+++ b/web/element/language.ts
@@ -339,8 +339,6 @@
       return StreamLanguage.define(tiki);
     case 'text/x-toml':
       return StreamLanguage.define(toml);
-    case 'text/x-toml':
-      return StreamLanguage.define(toml);
     case 'application/typescript':
       return javascript({typescript: true});
     case 'text/x-ttcn':
diff --git a/web/element/ruler.ts b/web/element/ruler.ts
index 5f37d72..946fa29 100644
--- a/web/element/ruler.ts
+++ b/web/element/ruler.ts
@@ -45,7 +45,11 @@
     }
   }
 
-  function updateRulerWidth(newWidth: number, defaultWidth: number, force = false) {
+  function updateRulerWidth(
+      newWidth: number,
+      defaultWidth: number,
+      force = false
+  ) {
     if ((newWidth !== width || force) && rulerElement) {
       width = newWidth;
       rulerElement.style.left = `${width * defaultWidth}px`;
diff --git a/web/gr-editor.ts b/web/gr-editor.ts
index 6d04661..c461864 100644
--- a/web/gr-editor.ts
+++ b/web/gr-editor.ts
@@ -8,7 +8,6 @@
 import {PluginApi} from '@gerritcodereview/typescript-api/plugin';
 import {html, LitElement} from 'lit';
 import {customElement, property, query} from 'lit/decorators.js';
-
 import {setScriptSrc} from './safe-script';
 
 declare global {
@@ -33,6 +32,8 @@
 
   @property({type: Object}) plugin?: PluginApi;
 
+  @property({type: Boolean}) darkMode = false;
+
   @query('#codemirror') mirror?: HTMLScriptElement;
 
   override render() {
@@ -45,6 +46,7 @@
         .prefs=${this.prefs}
         .fileContent=${this.fileContent}
         .fileType=${this.fileType}
+        ?darkMode=${this.darkMode}
       >
       </codemirror-element>
     `;