Fix "indent with tabs" being ignored

* Fixes it so pressing enter to go onto a new line, will make it
  so that it'll go underneith the previous code. Whether it's a tab
  or space.
* Fixes it so that when you press shift+tab, it'll remove it once
  rather than going back to the start.
* Fixes it so you can either ident by spaces or tabs again.

Downstream task: https://phabricator.wikimedia.org/T355816

Change-Id: I592fcc42d4c5271ea8207d4e291248f57c373c87
diff --git a/web/element/codemirror-element.ts b/web/element/codemirror-element.ts
index 206d7fd..d608598 100644
--- a/web/element/codemirror-element.ts
+++ b/web/element/codemirror-element.ts
@@ -149,7 +149,7 @@
               // press ctrl+s/cmd+s after, it would trigger the
               // web browsers file browser rather then gr-editor-view
               // intercepting ctrl+s/cmd+s.
-              if ((e.metaKey || e.ctrlKey) && e.key == 'v') {
+              if ((e.metaKey || e.ctrlKey) && e.key === 'v') {
                 e.stopPropagation();
               }
             },
diff --git a/web/element/extensions.ts b/web/element/extensions.ts
index 9aa8082..5d0593a 100644
--- a/web/element/extensions.ts
+++ b/web/element/extensions.ts
@@ -28,7 +28,6 @@
   history,
   historyKeymap,
   indentWithTab,
-  insertTab,
 } from '@codemirror/commands';
 import {searchKeymap, highlightSelectionMatches} from '@codemirror/search';
 import {closeBrackets, closeBracketsKeymap} from '@codemirror/autocomplete';
@@ -60,15 +59,16 @@
     },
   });
 
-const tabsOrSpaces = () =>
+const fixedHeightEditor = (height: number) =>
   EditorView.theme({
-    '.cm-tab:before': {
-      color: '#5f6368',
-      content: "'\\2192'",
-      position: 'absolute',
-    },
+    '&': {height: `${height}px`},
+    '.cm-scroller': {overflow: 'auto'},
+  });
 
+const hideTabsAndSpaces = () =>
+  EditorView.theme({
     // Class is created and used by highlightWhitespace()
+    // This hides tabs unless show_tabs is enabled in prefs.
     '.cm-highlightTab': {
       'background-image': 'none',
       'background-size': 'none',
@@ -77,21 +77,25 @@
       display: 'inline-block',
       'text-decoration': 'inherit',
     },
+    '.cm-highlightSpace': {
+      'background-image': 'none',
+      'background-size': 'none',
+    },
+  });
+
+const tabTheme = () =>
+  EditorView.theme({
+    '.cm-tab:before': {
+      color: '#5f6368',
+      content: "'\\2192'",
+      position: 'absolute',
+    },
+    // Class is created and used by highlightWhitespace()
     '.cm-highlightTab:before': {
       color: '#5f6368',
       content: "'\\2192'",
       position: 'absolute',
     },
-    ".cm-highlightSpace": {
-      'background-image': 'none',
-      'background-size': 'none'
-    },
-  });
-
-const fixedHeightEditor = (height: number) =>
-  EditorView.theme({
-    '&': {height: `${height}px`},
-    '.cm-scroller': {overflow: 'auto'},
   });
 
 export const extensions = (
@@ -101,17 +105,6 @@
   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
-    ? {
-        key: 'Tab',
-        preventDefault: true,
-        run: insertTab,
-      }
-    : indentWithTab;
-
   const codeExtensions: Array<Extension> = [
     lineNumbers(),
     highlightActiveLineGutter(),
@@ -128,28 +121,25 @@
       ...searchKeymap,
       ...historyKeymap,
       ...foldKeymap,
-      tab,
+      indentWithTab,
     ]),
     trailingspace(),
-    tabsOrSpaces(),
     fixedHeightEditor(height),
     colorTheme(darkMode ?? false),
+    EditorState.tabSize.of(prefs?.tab_size ?? 0),
+    highlightWhitespace(),
+    hideTabsAndSpaces(),
   ];
 
   if (!prefs) return codeExtensions;
 
-  if (prefs.line_length && prefs.line_length > 0) {
+  if (prefs.line_length) {
     codeExtensions.push(rulerPlugin);
   }
 
   if (prefs.auto_close_brackets) {
     codeExtensions.push(closeBrackets());
   }
-
-  if (prefs.indent_unit && prefs.indent_unit >= 0) {
-    codeExtensions.push(indentUnit.of(' '.repeat(prefs.indent_unit)));
-  }
-
   if (prefs.line_wrapping) {
     codeExtensions.push(EditorView.lineWrapping);
   }
@@ -166,17 +156,19 @@
   }
 
   if (prefs.show_tabs) {
-    codeExtensions.push(highlightWhitespace());
+    codeExtensions.push(tabTheme());
+  }
+
+  if (prefs.indent_with_tabs) {
+    codeExtensions.push(indentUnit.of('\t'));
+  } else {
+    codeExtensions.push(indentUnit.of(' '.repeat(prefs.indent_unit ?? 0)));
   }
 
   if (prefs.show_whitespace_errors) {
     codeExtensions.push(highlightTrailingWhitespace());
   }
 
-  if (prefs.tab_size && prefs.tab_size >= 0) {
-    codeExtensions.push(EditorState.tabSize.of(prefs.tab_size));
-  }
-
   if (fileContent?.includes('\r\n')) {
     codeExtensions.push(EditorState.lineSeparator.of('\r\n'));
   }
diff --git a/web/element/ruler.ts b/web/element/ruler.ts
index 946fa29..3d5bf76 100644
--- a/web/element/ruler.ts
+++ b/web/element/ruler.ts
@@ -37,7 +37,7 @@
       `;
       // TODO: This should be equal to the amount of padding on a line.
       // This value should be extracted from CodeMirror rather than hardcoded.
-      rulerElement.style.width = `4px`;
+      rulerElement.style.width = '4px';
     }
 
     destroy() {
@@ -46,9 +46,9 @@
   }
 
   function updateRulerWidth(
-      newWidth: number,
-      defaultWidth: number,
-      force = false
+    newWidth: number,
+    defaultWidth: number,
+    force = false
   ) {
     if ((newWidth !== width || force) && rulerElement) {
       width = newWidth;