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