From a9fd906b5fbb22f91b127a1b472ced9b636e722a Mon Sep 17 00:00:00 2001
From: Michele Alberti <michele.alberti@cea.fr>
Date: Wed, 29 Jul 2020 10:05:54 +0200
Subject: [PATCH] [dome] Please the linter (and make it less strict).

---
 ivette/.eslintrc.js                           |   3 +
 ivette/src/dome/src/renderer/text/buffers.ts  |  83 ++++++------
 ivette/src/dome/src/renderer/text/editors.tsx | 122 +++++++++---------
 ivette/tsconfig.json                          |   2 +-
 4 files changed, 110 insertions(+), 100 deletions(-)

diff --git a/ivette/.eslintrc.js b/ivette/.eslintrc.js
index 8e0f32daf76..f6a5adc29a8 100644
--- a/ivette/.eslintrc.js
+++ b/ivette/.eslintrc.js
@@ -20,7 +20,10 @@ module.exports = {
     "import/core-modules": [ "electron" ]
   },
   rules: {
+    // Do not enforce a displayName
     "react/display-name": "off",
+    // Do not enforce component methods order
+    "react/sort-comp": "off",
     // Be more strict on usage of useMemo and useRef
     "react-hooks/exhaustive-deps": "error",
     // Allow type any, even if it should be avoided
diff --git a/ivette/src/dome/src/renderer/text/buffers.ts b/ivette/src/dome/src/renderer/text/buffers.ts
index c0db788100d..cd1e5c7b896 100644
--- a/ivette/src/dome/src/renderer/text/buffers.ts
+++ b/ivette/src/dome/src/renderer/text/buffers.ts
@@ -8,9 +8,9 @@
 */
 
 import Emitter from 'events';
-import CodeMirror from 'codemirror/lib/codemirror.js';
+import CodeMirror from 'codemirror/lib/codemirror';
 
-export type Range = { from: CodeMirror.Position, to: CodeMirror.Position };
+export type Range = { from: CodeMirror.Position; to: CodeMirror.Position };
 
 export interface Decorator {
   /** @return a className to apply on markers with the identifier. */
@@ -24,8 +24,10 @@ export interface TextMarkerProxy {
 }
 
 /**
-   Text Marker options. Inherits
-   CodeMirror [TextMerkerOptions](https://codemirror.net/doc/manual.html#api_marker).
+   Text Marker options.
+
+   Inherits CodeMirror
+   [TextMerkerOptions](https://codemirror.net/doc/manual.html#api_marker).
  */
 export interface MarkerProps extends CodeMirror.TextMarkerOptions {
   id?: string;
@@ -48,25 +50,25 @@ export interface CSSMarker {
 // --- Batched Update
 // --------------------------------------------------------------------------
 
-const BATCH_OPS = 500
-const BATCH_DELAY = 5
-const BATCH_RMAX = 1000 // max tag range for sorting
-const BATCH_MARGINS = 20 // visible lines above the viewport
+const BATCH_OPS = 500;
+const BATCH_DELAY = 5;
+const BATCH_RMAX = 1000; // max tag range for sorting
+const BATCH_MARGINS = 20; // visible lines above the viewport
 
 interface MarkerOptions {
-  id?: string,
-  hover?: boolean,
-  className?: string,
-  options: CodeMirror.TextMarkerOptions,
+  id?: string;
+  hover?: boolean;
+  className?: string;
+  options: CodeMirror.TextMarkerOptions;
 }
 
 interface StackedMarker extends MarkerOptions {
-  startIndex: number,
+  startIndex: number;
 }
 
 interface BufferedMarker extends MarkerOptions {
-  startIndex: number,
-  stopIndex: number,
+  startIndex: number;
+  stopIndex: number;
 }
 
 type BufferedTag = BufferedMarker | undefined;
@@ -91,7 +93,9 @@ function byVisibleTag(lmin: number, lmax: number) {
 
 export interface RichTextBufferProps {
 
-  /** CodeMirror [mode](https://codemirror.net/mode/index.html) specification. */
+  /**
+   * CodeMirror [mode](https://codemirror.net/mode/index.html) specification.
+   */
   mode?: any;
 
   /** Maximum number of lines in the buffer. */
@@ -124,9 +128,9 @@ export interface RichTextBufferProps {
    the _edited_ state to `false`, but sill emit an `'edited'` event if the
    buffer was not empty.
 
-   Buffers can also be updated programmatically by various methods. In addition to
-   specified CodeMirror modes, you can also attach text markers programmatically with
-   a push/pop API.
+   Buffers can also be updated programmatically by various methods. In addition
+   to specified CodeMirror modes, you can also attach text markers
+   programmatically with a push/pop API.
 
    Text markers can be associated with an identifier, that can be used for
    dynamic highlighting, called Decorations. Decorations are class names that
@@ -146,13 +150,13 @@ export interface RichTextBufferProps {
 export class RichTextBuffer extends Emitter {
 
   private document: CodeMirror.Doc;
-  private maxlines: number = 10000;
+  private maxlines = 10000;
   private editors: CodeMirror.Editor[] = [];
-  private cacheIndex: number = 0; // document index, negative if not computed
+  private cacheIndex = 0; // document index, negative if not computed
   private bufferedText: string; // buffered text to append
   private bufferedTags: BufferedTag[];
   private stacked: StackedMarker[] = [];
-  private batched: boolean = false;
+  private batched = false;
 
   // Indexed by CSS property dome-xHover-nnnn
   private cssmarkers = new Map<string, CSSMarker>();
@@ -222,9 +226,7 @@ export class RichTextBuffer extends Emitter {
       const start = doc.posFromIndex(Infinity);
       if (start.ch > 0) doc.replaceRange('\n', start, undefined, 'buffer');
       this.cacheIndex = -1;
-    } else {
-      if (buf[buf.length - 1] !== '\n') this.bufferedText += '\n';
-    }
+    } else if (buf[buf.length - 1] !== '\n') this.bufferedText += '\n';
   }
 
   /**
@@ -271,11 +273,11 @@ export class RichTextBuffer extends Emitter {
      inserted between its associated [[openTextMarker]] and [[closeTextMarker]]
      calls.
 
-     The returned text marker is actually a _proxy_ to the text marker that will be
-     eventually created by [[closeTextMarker]]. Its methods are automatically
-     forwarded to the actual `CodeMirror.TextMarker`
-     instance, once created.  Hence, you can safely invoke these methods on either
-     the _proxy_ or the _final_ text marker at your convenience.
+     The returned text marker is actually a _proxy_ to the text marker that will
+     be eventually created by [[closeTextMarker]]. Its methods are automatically
+     forwarded to the actual `CodeMirror.TextMarker` instance, once created.
+     Hence, you can safely invoke these methods on either the _proxy_ or the
+     _final_ text marker at your convenience.
   */
   openTextMarker(props: MarkerProps) {
     const { id, hover, className, ...options } = props;
@@ -459,7 +461,7 @@ export class RichTextBuffer extends Emitter {
 
   private onChange(
     _editor: CodeMirror.Editor,
-    change: CodeMirror.EditorChangeLinkedList
+    change: CodeMirror.EditorChangeLinkedList,
   ) {
     if (change.origin !== 'buffer') {
       this.setEdited(true);
@@ -477,7 +479,9 @@ export class RichTextBuffer extends Emitter {
      @param cm - code mirror instance to link this document in.
   */
   link(cm: CodeMirror.Editor) {
-    const newDoc = this.document.linkedDoc({ sharedHist: true, mode: undefined });
+    const newDoc = this.document.linkedDoc(
+      { sharedHist: true, mode: undefined },
+    );
     cm.swapDoc(newDoc);
     cm.on('change', this.onChange);
     this.editors.push(cm);
@@ -511,7 +515,7 @@ export class RichTextBuffer extends Emitter {
     this.editors.forEach((cm) => {
       try { fn(cm); } catch (e) { console.error('[Dome.text]', e); }
     });
-  };
+  }
 
   // --------------------------------------------------------------------------
   // --- Update Operations
@@ -540,7 +544,7 @@ export class RichTextBuffer extends Emitter {
         '',
         { line: p, ch: 0 },
         { line: q, ch: 0 },
-        'buffer'
+        'buffer',
       );
       this.cacheIndex = -1;
     }
@@ -551,7 +555,7 @@ export class RichTextBuffer extends Emitter {
     const { id, hover, className, startIndex, stopIndex } = tag;
     let markerId;
     if (id || hover) {
-      markerId = 'dome-xHover-' + (this.markid++);
+      markerId = `dome-xHover-${this.markid++}`;
       const cmark = {
         id,
         classNameId: markerId,
@@ -562,7 +566,7 @@ export class RichTextBuffer extends Emitter {
     }
     const fullClassName = [
       'dome-xMarked',
-      id && ('dome-xMark-' + id),
+      id && (`dome-xMark-${id}`),
       markerId,
       className,
     ].filter((s) => !!s).join(' ');
@@ -601,13 +605,12 @@ export class RichTextBuffer extends Emitter {
   }
 
   private getLastIndex() {
-    let idx = this.cacheIndex;
-    if (idx < 0) {
+    if (this.cacheIndex < 0) {
       const doc = this.document;
       const line = doc.lastLine() + 1;
-      this.cacheIndex = idx = doc.indexFromPos({ line, ch: 0 });
+      this.cacheIndex = doc.indexFromPos({ line, ch: 0 });
     }
-    return idx;
+    return this.cacheIndex;
   }
 
   // --------------------------------------------------------------------------
diff --git a/ivette/src/dome/src/renderer/text/editors.tsx b/ivette/src/dome/src/renderer/text/editors.tsx
index 6a59be9f1e2..0a5d45ec867 100644
--- a/ivette/src/dome/src/renderer/text/editors.tsx
+++ b/ivette/src/dome/src/renderer/text/editors.tsx
@@ -11,7 +11,7 @@ import _ from 'lodash';
 import React from 'react';
 import * as Dome from 'dome';
 import { Vfill } from 'dome/layout/boxes';
-import CodeMirror, { EditorConfiguration } from 'codemirror/lib/codemirror.js';
+import CodeMirror, { EditorConfiguration } from 'codemirror/lib/codemirror';
 import { RichTextBuffer, CSSMarker, Decorator } from './buffers';
 
 import './style.css';
@@ -133,33 +133,33 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
       // Mounting...
       const { buffer } = this.props;
       const config = getConfig(this.props);
-      const cm = this.codeMirror = CodeMirror(elt, { value: '' });
+      this.codeMirror = CodeMirror(elt, { value: '' });
       if (buffer) {
-        buffer.link(cm);
+        buffer.link(this.codeMirror);
         buffer.on('decorated', this.handleUpdate);
         buffer.on('scroll', this.handleScrollTo);
       }
       // Passing all options to constructor does not work (Cf. CodeMirror's BTS)
-      forEachOption(config, (opt) => cm.setOption(opt, config[opt]));
+      forEachOption(
+        config, (opt) => this.codeMirror?.setOption(opt, config[opt]),
+      );
       // Binding events to view
-      cm.on('update', this.handleUpdate);
-      cm.on('keyHandled', this.handleKey);
+      this.codeMirror.on('update', this.handleUpdate);
+      this.codeMirror.on('keyHandled', this.handleKey);
       Dome.on('dome.update', this.refresh);
       // Auto refresh
       this.refreshPolling = setInterval(this.autoRefresh, 250);
       this.handleUpdate();
     } else {
       // Unmounting...
-      const polling = this.refreshPolling;
-      if (polling) {
-        clearInterval(polling);
+      if (this.refreshPolling) {
+        clearInterval(this.refreshPolling);
         this.refreshPolling = undefined;
       }
-      const cm = this.codeMirror;
       Dome.off('dome.update', this.refresh);
       const { buffer } = this.props;
-      if (cm && buffer) {
-        buffer.unlink(cm);
+      if (this.codeMirror && buffer) {
+        buffer.unlink(this.codeMirror);
         buffer.off('decorated', this.handleUpdate);
         buffer.off('scroll', this.handleScrollTo);
       }
@@ -210,7 +210,7 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
   _findMarker(elt: Element): CSSMarker | undefined {
     const { buffer } = this.props;
     if (buffer) {
-      var best: CSSMarker | undefined;
+      let best: CSSMarker | undefined;
       elt.classList.forEach((name) => {
         const marker = buffer.findHover(name);
         if (marker && (!best || marker.length < best.length)) best = marker;
@@ -220,33 +220,35 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
     return undefined;
   }
 
+  // eslint-disable-next-line class-methods-use-this
   _findDecoration(
     classes: DOMTokenList,
     buffer: RichTextBuffer,
     decorator: Decorator,
   ) {
-    var best_marker: CSSMarker | undefined;
-    var best_decorated: CSSMarker | undefined;
-    var best_decoration: string | undefined;
+    let bestMarker: CSSMarker | undefined;
+    let bestDecorated: CSSMarker | undefined;
+    let bestDecoration: string | undefined;
     classes.forEach((name) => {
 
       const marker = buffer.findHover(name);
       const id = marker && marker.id;
       const decoration = id && decorator(id);
 
-      if (marker && (!best_marker || marker.length < best_marker.length)) {
-        best_marker = marker;
+      if (marker && (!bestMarker || marker.length < bestMarker.length)) {
+        bestMarker = marker;
       }
 
-      if (marker && decoration && (!best_decorated || marker.length < best_decorated.length)) {
-        best_decorated = marker;
-        best_decoration = decoration;
+      if (marker && decoration &&
+        (!bestDecorated || marker.length < bestDecorated.length)) {
+        bestDecorated = marker;
+        bestDecoration = decoration;
       }
 
     });
-    return best_marker ? {
-      classNameId: best_marker.classNameId,
-      decoration: best_decoration,
+    return bestMarker ? {
+      classNameId: bestMarker.classNameId,
+      decoration: bestDecoration,
     } : undefined;
   }
 
@@ -256,7 +258,7 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
     if (toMark) {
       const n = toMark.length;
       if (n === 0) return;
-      for (var k = 0; k < n; k++) toMark[k].classList.add(className);
+      for (let k = 0; k < n; k++) toMark[k].classList.add(className);
     }
   }
 
@@ -266,21 +268,21 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
     if (toUnmark) {
       const n = toUnmark.length;
       if (n === 0) return;
-      const elts: Element[] = new Array(n);;
-      for (var k = 0; k < n; k++) elts[k] = toUnmark[k];
+      const elts: Element[] = new Array(n);
+      for (let k = 0; k < n; k++) elts[k] = toUnmark[k];
       elts.forEach((elt) => elt.classList.remove(className));
     }
   }
 
   handleHover(target: Element) {
     // Throttled (see constructor)
-    const old_marker = this.marker;
-    const new_marker = this._findMarker(target);
-    if (old_marker !== new_marker) {
-      if (old_marker) this._unmarkElementsWith(CSS_HOVERED);
-      if (new_marker && new_marker.hover)
-        this._markElementsWith(new_marker.classNameId, CSS_HOVERED);
-      this.marker = new_marker;
+    const oldMarker = this.marker;
+    const newMarker = this._findMarker(target);
+    if (oldMarker !== newMarker) {
+      if (oldMarker) this._unmarkElementsWith(CSS_HOVERED);
+      if (newMarker && newMarker.hover)
+        this._markElementsWith(newMarker.classNameId, CSS_HOVERED);
+      this.marker = newMarker;
     }
   }
 
@@ -290,15 +292,15 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
     if (!marked) return;
     const n = marked.length;
     if (n === 0) return;
-    const marker = this.marker;
+    const { marker } = this;
     const hovered = (marker && marker.hover) ? marker.classNameId : undefined;
-    const selection = this.props.selection;
-    const selected = selection && ('dome-xMark-' + selection);
+    const { selection } = this.props;
+    const selected = selection && (`dome-xMark-${selection}`);
     const { buffer } = this.props;
     const decorator = buffer?.getDecorator();
     if (!hovered && !selection && !decorator) return;
     const newDecorations = new Map<string, string>();
-    for (var k = 0; k < n; k++) {
+    for (let k = 0; k < n; k++) {
       const elt = marked[k];
       const classes = elt.classList;
       if (hovered && classes.contains(hovered)) classes.add(CSS_HOVERED);
@@ -327,7 +329,7 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
 
   onMouseClick(evt: MouseEvt, callback: MarkerCallback | undefined) {
     // No need for throttling
-    const target = evt.target;
+    const { target } = evt;
     if (target instanceof Element && callback) {
       const marker = this._findMarker(target);
       if (marker && marker.id) callback(marker.id);
@@ -350,8 +352,10 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
   handleScrollTo(line: number) {
     try {
       const cm = this.codeMirror;
-      cm && cm.scrollIntoView({ line, ch: 0 });
-    } catch (_error) { } // Out of range
+      return cm && cm.scrollIntoView({ line, ch: 0 });
+    } catch (_error) {
+      console.warn(`[Dome] Unable to scroll to line ${line}: out of range.`);
+    }
   }
 
   // --------------------------------------------------------------------------
@@ -392,12 +396,12 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
       const {
         buffer: oldBuffer,
         selection: oldSelect,
-        fontSize: oldFont
+        fontSize: oldFont,
       } = this.props;
       const {
         buffer: newBuffer,
         selection: newSelect,
-        fontSize: newFont
+        fontSize: newFont,
       } = newProps;
       if (oldBuffer !== newBuffer) {
         if (oldBuffer) oldBuffer.unlink(cm);
@@ -423,7 +427,7 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
       });
       // Update selection
       if (oldSelect !== newSelect) {
-        const selected = 'dome-xMark-' + newSelect;
+        const selected = `dome-xMark-${newSelect}`;
         if (oldSelect) this._unmarkElementsWith(CSS_SELECTED);
         if (newSelect) this._markElementsWith(selected, CSS_SELECTED);
       }
@@ -436,7 +440,8 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
 
   render() {
     return (
-      <div className={'dome-xText'}
+      <div
+        className="dome-xText"
         ref={this.mountPoint}
         onClick={this.onClick}
         onContextMenu={this.onContextMenu}
@@ -444,7 +449,8 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
         onFocus={this.onFocus}
         onScroll={this.onScroll}
         onMouseMove={this.onMouseMove}
-      />);
+      />
+    );
   }
 
 }
@@ -459,23 +465,24 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
    A component rendering the content of a text buffer, that shall be instances
    of the `Buffer` base class.
 
-   The view is based on a [CodeMirror](https://codemirror.net) component linked with
-   the internal Code Mirror Document from the associated buffer.
+   The view is based on a [CodeMirror](https://codemirror.net) component linked
+   with the internal Code Mirror Document from the associated buffer.
 
-   Multiple views might share the same buffer as source content. The buffer will be
-   kept in sync with all its linked views.
+   Multiple views might share the same buffer as source content. The buffer will
+   be kept in sync with all its linked views.
 
-   The Text component never update its mounted NODE element, however, all property
-   modifications (including buffer) are propagated to the internal CodeMirror instance.
-   Undefined properties are set (or reset) to the CodeMirror defaults.
+   The Text component never update its mounted NODE element, however, all
+   property modifications (including buffer) are propagated to the internal
+   CodeMirror instance. Undefined properties are set (or reset) to the
+   CodeMirror defaults.
 
    #### Themes
 
    The CodeMirror `theme` option allow you to style your document,
    especially when using modes.
    Themes are only accessible if you load the associated CSS style sheet.
-   For instance, to use the `'ambiance'` theme provided with CodeMirror, you shall
-   import `'codemirror/theme/ambiance.css'` somewhere in your application.
+   For instance, to use the `'ambiance'` theme provided with CodeMirror, you
+   shall import `'codemirror/theme/ambiance.css'` somewhere in your application.
 
    #### Modes & Adds-On
 
@@ -491,16 +498,13 @@ class CodeMirrorWrapper extends React.Component<TextProps> {
    `import CodeMirror from 'codemirror/lib/codemirror.js'` ; using `from
    'codemirror'` returns a different instance of `CodeMirror` class and will
    not work.
-
  */
 export function Text(props: TextProps) {
   let { className, style, fontSize, ...cmprops } = props;
   if (fontSize !== undefined && fontSize < 4) fontSize = 4;
   if (fontSize !== undefined && fontSize > 48) fontSize = 48;
-  const theStyle = Object.assign({}, style);
-  theStyle.fontSize = fontSize;
   return (
-    <Vfill className={className} style={theStyle}>
+    <Vfill className={className} style={{ ...style, fontSize }}>
       <CodeMirrorWrapper fontSize={fontSize} {...cmprops} />
     </Vfill>
   );
diff --git a/ivette/tsconfig.json b/ivette/tsconfig.json
index f28789deebc..0cbb1f1af83 100644
--- a/ivette/tsconfig.json
+++ b/ivette/tsconfig.json
@@ -49,7 +49,7 @@
       "dome/system": [ "src/dome/src/misc/system.js" ],
       "dome/misc/*": [ "src/dome/src/misc/*"],
       "dome/*": [ "src/dome/src/renderer/*" ],
-      "codemirror/lib/codemirror.js": ["node_modules/@types/codemirror/index.d.ts"],
+      "codemirror/lib/codemirror": ["node_modules/@types/codemirror/index.d.ts"],
     },
     // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
     // "typeRoots": [],                       /* List of folders to include type definitions from. */
-- 
GitLab