From dd077e9ffaffeb0f3ea0efad1bade296d9913449 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:20:58 -0500 Subject: [PATCH 01/22] feat: add FloatPane from atom-ide-ui --- src-commons-ui/float-pane/FloatPane.tsx | 281 ++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 src-commons-ui/float-pane/FloatPane.tsx diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx new file mode 100644 index 00000000..37efc3ed --- /dev/null +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -0,0 +1,281 @@ +import type { Datatip, PinnedDatatipPosition } from "./types" + +type Position = { + x: number, + y: number, +} + +import * as React from "react" +import ReactDOM from "react-dom" +import { Observable } from "rxjs" +import invariant from "assert" +import classnames from "classnames" +import Disposable from "atom" + +import { ViewContainer, DATATIP_ACTIONS } from "./ViewContainer" +import isScrollable from "./isScrollable" + +const LINE_END_MARGIN = 20 + +let _mouseMove$ +function documentMouseMove$(): Observable { + if (_mouseMove$ == null) { + _mouseMove$ = Observable.fromEvent(document, "mousemove") + } + return _mouseMove$ +} + +let _mouseUp$ +function documentMouseUp$(): Observable { + if (_mouseUp$ == null) { + _mouseUp$ = Observable.fromEvent(document, "mouseup") + } + return _mouseUp$ +} + +export type PinnedDatatipParams = { + onDispose: (pinnedDatatip: PinnedDatatip) => void, + hideDataTips: () => void, + // Defaults to 'end-of-line'. + position?: PinnedDatatipPosition, + // Defaults to true. + showRangeHighlight?: boolean, +} + +export class PinnedDatatip { + _boundDispose: Function + _boundHandleMouseDown: Function + _boundHandleMouseEnter: Function + _boundHandleMouseLeave: Function + _boundHandleCapturedClick: Function + _mouseUpTimeout: ?TimeoutID + _hostElement: HTMLElement + _marker: ?atom$Marker + _rangeDecoration: ?atom$Decoration + _mouseSubscription: ?rxjs$ISubscription + _subscriptions: Disposable + _datatip: Datatip + _editor: TextEditor + _hostElement: HTMLElement + _boundDispose: Function + _dragOrigin: ?Position + _isDragging: boolean + _offset: Position + _isHovering: boolean + _checkedScrollable: boolean + _isScrollable: boolean + _hideDataTips: () => void + _position: PinnedDatatipPosition + _showRangeHighlight: boolean + + constructor(datatip: Datatip, editor: TextEditor, params: PinnedDatatipParams) { + this._subscriptions = new Disposable() + this._subscriptions.add(new Disposable(() => params.onDispose(this))) + this._datatip = datatip + this._editor = editor + this._marker = null + this._rangeDecoration = null + this._hostElement = document.createElement("div") + this._hostElement.className = "datatip-element" + this._boundDispose = this.dispose.bind(this) + this._boundHandleMouseDown = this.handleMouseDown.bind(this) + this._boundHandleMouseEnter = this.handleMouseEnter.bind(this) + this._boundHandleMouseLeave = this.handleMouseLeave.bind(this) + this._boundHandleCapturedClick = this.handleCapturedClick.bind(this) + this._checkedScrollable = false + this._isScrollable = false + + this._subscriptions.add( + Observable.fromEvent(this._hostElement, "wheel").subscribe((e) => { + if (!this._checkedScrollable) { + this._isScrollable = isScrollable(this._hostElement, e) + this._checkedScrollable = true + } + if (this._isScrollable) { + e.stopPropagation() + } + }) + ) + this._hostElement.addEventListener("mouseenter", this._boundHandleMouseEnter) + this._hostElement.addEventListener("mouseleave", this._boundHandleMouseLeave) + this._subscriptions.add( + new Disposable(() => { + this._hostElement.removeEventListener("mouseenter", this._boundHandleMouseEnter) + this._hostElement.removeEventListener("mouseleave", this._boundHandleMouseLeave) + }) + ) + this._mouseUpTimeout = null + this._offset = { x: 0, y: 0 } + this._isDragging = false + this._dragOrigin = null + this._isHovering = false + this._hideDataTips = params.hideDataTips + this._position = params.position == null ? "end-of-line" : params.position + this._showRangeHighlight = params.showRangeHighlight == null ? true : params.showRangeHighlight + this.render() + } + + handleMouseEnter(event: MouseEvent): void { + this._isHovering = true + this._hideDataTips() + } + + handleMouseLeave(event: MouseEvent): void { + this._isHovering = false + } + + isHovering(): boolean { + return this._isHovering + } + + handleGlobalMouseMove(event: Event): void { + const evt: MouseEvent = (event: any) + const { _dragOrigin } = this + invariant(_dragOrigin) + this._isDragging = true + this._offset = { + x: evt.clientX - _dragOrigin.x, + y: evt.clientY - _dragOrigin.y, + } + this.render() + } + + handleGlobalMouseUp(): void { + // If the datatip was moved, push the effects of mouseUp to the next tick, + // in order to allow cancellation of captured events (e.g. clicks on child components). + this._mouseUpTimeout = setTimeout(() => { + this._isDragging = false + this._dragOrigin = null + this._mouseUpTimeout = null + this._ensureMouseSubscriptionDisposed() + this.render() + }, 0) + } + + _ensureMouseSubscriptionDisposed(): void { + if (this._mouseSubscription != null) { + this._mouseSubscription.unsubscribe() + this._mouseSubscription = null + } + } + + handleMouseDown(event: Event): void { + const evt: MouseEvent = (event: any) + this._dragOrigin = { + x: evt.clientX - this._offset.x, + y: evt.clientY - this._offset.y, + } + this._ensureMouseSubscriptionDisposed() + this._mouseSubscription = documentMouseMove$() + .takeUntil(documentMouseUp$()) + .subscribe( + (e: MouseEvent) => { + this.handleGlobalMouseMove(e) + }, + (error: any) => {}, + () => { + this.handleGlobalMouseUp() + } + ) + } + + handleCapturedClick(event: SyntheticEvent<>): void { + if (this._isDragging) { + event.stopPropagation() + } else { + // Have to re-check scrolling because the datatip size may have changed. + this._checkedScrollable = false + } + } + + // Update the position of the pinned datatip. + _updateHostElementPosition(): void { + const { _editor, _datatip, _hostElement, _offset, _position } = this + const { range } = _datatip + _hostElement.style.display = "block" + switch (_position) { + case "end-of-line": + const charWidth = _editor.getDefaultCharWidth() + const lineLength = _editor.getBuffer().getLines()[range.start.row].length + _hostElement.style.top = -_editor.getLineHeightInPixels() + _offset.y + "px" + _hostElement.style.left = (lineLength - range.end.column) * charWidth + LINE_END_MARGIN + _offset.x + "px" + break + case "above-range": + _hostElement.style.bottom = _editor.getLineHeightInPixels() + _hostElement.clientHeight - _offset.y + "px" + _hostElement.style.left = _offset.x + "px" + break + default: + ;(_position: empty) + throw new Error(`Unexpected PinnedDatatip position: ${this._position}`) + } + } + + async render(): Promise { + const { _editor, _datatip, _hostElement, _isDragging, _isHovering } = this + + let rangeClassname = "datatip-highlight-region" + if (_isHovering) { + rangeClassname += " datatip-highlight-region-active" + } + + if (this._marker == null) { + const marker: atom$Marker = _editor.markBufferRange(_datatip.range, { + invalidate: "never", + }) + this._marker = marker + _editor.decorateMarker(marker, { + type: "overlay", + position: "head", + class: "datatip-pinned-overlay", + item: this._hostElement, + // above-range datatips currently assume that the overlay is below. + avoidOverflow: this._position !== "above-range", + }) + if (this._showRangeHighlight) { + this._rangeDecoration = _editor.decorateMarker(marker, { + type: "highlight", + class: rangeClassname, + }) + } + await _editor.getElement().getNextUpdatePromise() + // Guard against disposals during the await. + if (marker.isDestroyed() || _editor.isDestroyed()) { + return + } + } else if (this._rangeDecoration != null) { + this._rangeDecoration.setProperties({ + type: "highlight", + class: rangeClassname, + }) + } + + ReactDOM.render( + , + _hostElement + ) + this._updateHostElementPosition() + } + + dispose(): void { + if (this._mouseUpTimeout != null) { + clearTimeout(this._mouseUpTimeout) + } + if (this._marker != null) { + this._marker.destroy() + } + if (this._mouseSubscription != null) { + this._mouseSubscription.unsubscribe() + } + ReactDOM.unmountComponentAtNode(this._hostElement) + this._hostElement.remove() + this._subscriptions.dispose() + } +} From 142590cbe7ab7941a1b8e30567f21fd01a87258d Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:21:18 -0500 Subject: [PATCH 02/22] fix: remove duplicate keys --- src-commons-ui/float-pane/FloatPane.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 37efc3ed..34cd6194 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -56,8 +56,6 @@ export class PinnedDatatip { _subscriptions: Disposable _datatip: Datatip _editor: TextEditor - _hostElement: HTMLElement - _boundDispose: Function _dragOrigin: ?Position _isDragging: boolean _offset: Position From 5f3c5a486a678fdf3bc4c4a2d42596715e383858 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:21:31 -0500 Subject: [PATCH 03/22] chore: use interface instead of type --- src-commons-ui/float-pane/FloatPane.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 34cd6194..4379ce68 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -33,7 +33,7 @@ function documentMouseUp$(): Observable { return _mouseUp$ } -export type PinnedDatatipParams = { +export interface PinnedDatatipParams { onDispose: (pinnedDatatip: PinnedDatatip) => void, hideDataTips: () => void, // Defaults to 'end-of-line'. From fe0d4db8b2096c30735f5df088c1ecf45a817cf4 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:24:04 -0500 Subject: [PATCH 04/22] chore: use Atom types --- src-commons-ui/float-pane/FloatPane.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 4379ce68..51207801 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -1,4 +1,4 @@ -import type { Datatip, PinnedDatatipPosition } from "./types" +import { Marker, Decoration, TextEditor } from "atom" type Position = { x: number, @@ -50,8 +50,8 @@ export class PinnedDatatip { _boundHandleCapturedClick: Function _mouseUpTimeout: ?TimeoutID _hostElement: HTMLElement - _marker: ?atom$Marker - _rangeDecoration: ?atom$Decoration + _marker: ?Marker + _rangeDecoration: ?Decoration _mouseSubscription: ?rxjs$ISubscription _subscriptions: Disposable _datatip: Datatip @@ -217,7 +217,7 @@ export class PinnedDatatip { } if (this._marker == null) { - const marker: atom$Marker = _editor.markBufferRange(_datatip.range, { + const marker: Marker = _editor.markBufferRange(_datatip.range, { invalidate: "never", }) this._marker = marker From 815210719138e68b186eaafb43549b55154e9043 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:25:20 -0500 Subject: [PATCH 05/22] chore: Subscription type from rxjs --- src-commons-ui/float-pane/FloatPane.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 51207801..a0dcff66 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -1,4 +1,5 @@ import { Marker, Decoration, TextEditor } from "atom" +import type {Subscription} from "rxjs" type Position = { x: number, @@ -52,7 +53,7 @@ export class PinnedDatatip { _hostElement: HTMLElement _marker: ?Marker _rangeDecoration: ?Decoration - _mouseSubscription: ?rxjs$ISubscription + _mouseSubscription: ?Subscription _subscriptions: Disposable _datatip: Datatip _editor: TextEditor From ef5bc397617c03591758987943b535908719c293 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:28:18 -0500 Subject: [PATCH 06/22] fix: use CompositeDisposable type from atom --- src-commons-ui/float-pane/FloatPane.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index a0dcff66..baf174c7 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -1,4 +1,4 @@ -import { Marker, Decoration, TextEditor } from "atom" +import { Marker, Decoration, TextEditor, Disposable, CompositeDisposable } from "atom" import type {Subscription} from "rxjs" type Position = { @@ -11,7 +11,6 @@ import ReactDOM from "react-dom" import { Observable } from "rxjs" import invariant from "assert" import classnames from "classnames" -import Disposable from "atom" import { ViewContainer, DATATIP_ACTIONS } from "./ViewContainer" import isScrollable from "./isScrollable" @@ -54,7 +53,7 @@ export class PinnedDatatip { _marker: ?Marker _rangeDecoration: ?Decoration _mouseSubscription: ?Subscription - _subscriptions: Disposable + _subscriptions: CompositeDisposable _datatip: Datatip _editor: TextEditor _dragOrigin: ?Position @@ -68,7 +67,7 @@ export class PinnedDatatip { _showRangeHighlight: boolean constructor(datatip: Datatip, editor: TextEditor, params: PinnedDatatipParams) { - this._subscriptions = new Disposable() + this._subscriptions = new CompositeDisposable() this._subscriptions.add(new Disposable(() => params.onDispose(this))) this._datatip = datatip this._editor = editor From b8321e9c8ba39d9ba82d3916370a5e395cf40b49 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:31:16 -0500 Subject: [PATCH 07/22] chore: import PinnedDatatipPosition type --- src-commons-ui/float-pane/FloatPane.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index baf174c7..cb9f8f57 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -1,10 +1,6 @@ import { Marker, Decoration, TextEditor, Disposable, CompositeDisposable } from "atom" import type {Subscription} from "rxjs" - -type Position = { - x: number, - y: number, -} +import { PinnedDatatipPosition } from "../../types-packages/main" import * as React from "react" import ReactDOM from "react-dom" @@ -33,6 +29,10 @@ function documentMouseUp$(): Observable { return _mouseUp$ } +interface Position { + x: number, + y: number, +} export interface PinnedDatatipParams { onDispose: (pinnedDatatip: PinnedDatatip) => void, hideDataTips: () => void, From 8e4ca88312c17c24c80b264f38bd796bb4180580 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:48:07 -0500 Subject: [PATCH 08/22] fix: fix rxjs fromEvent and mouse event types + convert subscription to disposable like --- src-commons-ui/float-pane/FloatPane.tsx | 36 ++++++++++++++----------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index cb9f8f57..b0987092 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -1,10 +1,10 @@ -import { Marker, Decoration, TextEditor, Disposable, CompositeDisposable } from "atom" +import { Marker, Decoration, TextEditor, Disposable, DisposableLike, CompositeDisposable } from "atom" +import { Observable, fromEvent } from "rxjs" import type {Subscription} from "rxjs" import { PinnedDatatipPosition } from "../../types-packages/main" import * as React from "react" import ReactDOM from "react-dom" -import { Observable } from "rxjs" import invariant from "assert" import classnames from "classnames" @@ -13,18 +13,18 @@ import isScrollable from "./isScrollable" const LINE_END_MARGIN = 20 -let _mouseMove$ +let _mouseMove$: Observable function documentMouseMove$(): Observable { if (_mouseMove$ == null) { - _mouseMove$ = Observable.fromEvent(document, "mousemove") + _mouseMove$ = fromEvent(document, "mousemove") } return _mouseMove$ } -let _mouseUp$ +let _mouseUp$: Observable function documentMouseUp$(): Observable { if (_mouseUp$ == null) { - _mouseUp$ = Observable.fromEvent(document, "mouseup") + _mouseUp$ = fromEvent(document, "mouseup") } return _mouseUp$ } @@ -48,7 +48,7 @@ export class PinnedDatatip { _boundHandleMouseEnter: Function _boundHandleMouseLeave: Function _boundHandleCapturedClick: Function - _mouseUpTimeout: ?TimeoutID + _mouseUpTimeout: ?number _hostElement: HTMLElement _marker: ?Marker _rangeDecoration: ?Decoration @@ -83,16 +83,20 @@ export class PinnedDatatip { this._checkedScrollable = false this._isScrollable = false + const _wheelSubscription = fromEvent(this._hostElement, "wheel").subscribe((e) => { + if (!this._checkedScrollable) { + this._isScrollable = isScrollable(this._hostElement, e) + this._checkedScrollable = true + } + if (this._isScrollable) { + e.stopPropagation() + } + }) + // convert to Atom API (rename unsubscribe to dispose) + const _wheelDisposable = {..._wheelSubscription, dispose: _wheelSubscription.unsubscribe} + this._subscriptions.add( - Observable.fromEvent(this._hostElement, "wheel").subscribe((e) => { - if (!this._checkedScrollable) { - this._isScrollable = isScrollable(this._hostElement, e) - this._checkedScrollable = true - } - if (this._isScrollable) { - e.stopPropagation() - } - }) + _wheelDisposable ) this._hostElement.addEventListener("mouseenter", this._boundHandleMouseEnter) this._hostElement.addEventListener("mouseleave", this._boundHandleMouseLeave) From b97ce851120383cdd791e00a7c263fd593da722e Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:53:45 -0500 Subject: [PATCH 09/22] fix: disposableFromSubscription --- src-commons-atom/disposable/index.ts | 7 +++++++ src-commons-ui/float-pane/FloatPane.tsx | 5 ++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 src-commons-atom/disposable/index.ts diff --git a/src-commons-atom/disposable/index.ts b/src-commons-atom/disposable/index.ts new file mode 100644 index 00000000..b9844d6d --- /dev/null +++ b/src-commons-atom/disposable/index.ts @@ -0,0 +1,7 @@ +import type {Subscription} from "rxjs" +import { DisposableLike } from "atom" + +// convert rxjs Subscription to Atom DisposableLike (rename unsubscribe to dispose) +export function disposableFromSubscription(subs: Subscription): DisposableLike { + return {...subs, dispose: subs.unsubscribe} +} diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index b0987092..25565656 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -1,6 +1,7 @@ import { Marker, Decoration, TextEditor, Disposable, DisposableLike, CompositeDisposable } from "atom" import { Observable, fromEvent } from "rxjs" import type {Subscription} from "rxjs" +import { disposableFromSubscription } from "../../src-commons-atom/disposable" import { PinnedDatatipPosition } from "../../types-packages/main" import * as React from "react" @@ -92,11 +93,9 @@ export class PinnedDatatip { e.stopPropagation() } }) - // convert to Atom API (rename unsubscribe to dispose) - const _wheelDisposable = {..._wheelSubscription, dispose: _wheelSubscription.unsubscribe} this._subscriptions.add( - _wheelDisposable + disposableFromSubscription(_wheelSubscription) ) this._hostElement.addEventListener("mouseenter", this._boundHandleMouseEnter) this._hostElement.addEventListener("mouseleave", this._boundHandleMouseLeave) From 3767070039e6adb664877ec38acd9677f60eacdf Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 02:59:40 -0500 Subject: [PATCH 10/22] fix: use lambda instead of bind --- src-commons-ui/float-pane/FloatPane.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 25565656..cda0d57c 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -46,8 +46,6 @@ export interface PinnedDatatipParams { export class PinnedDatatip { _boundDispose: Function _boundHandleMouseDown: Function - _boundHandleMouseEnter: Function - _boundHandleMouseLeave: Function _boundHandleCapturedClick: Function _mouseUpTimeout: ?number _hostElement: HTMLElement @@ -78,8 +76,6 @@ export class PinnedDatatip { this._hostElement.className = "datatip-element" this._boundDispose = this.dispose.bind(this) this._boundHandleMouseDown = this.handleMouseDown.bind(this) - this._boundHandleMouseEnter = this.handleMouseEnter.bind(this) - this._boundHandleMouseLeave = this.handleMouseLeave.bind(this) this._boundHandleCapturedClick = this.handleCapturedClick.bind(this) this._checkedScrollable = false this._isScrollable = false @@ -97,12 +93,12 @@ export class PinnedDatatip { this._subscriptions.add( disposableFromSubscription(_wheelSubscription) ) - this._hostElement.addEventListener("mouseenter", this._boundHandleMouseEnter) - this._hostElement.addEventListener("mouseleave", this._boundHandleMouseLeave) + this._hostElement.addEventListener("mouseenter", (e) => this.handleMouseEnter(e)) + this._hostElement.addEventListener("mouseleave", (e) => this.handleMouseLeave(e)) this._subscriptions.add( new Disposable(() => { - this._hostElement.removeEventListener("mouseenter", this._boundHandleMouseEnter) - this._hostElement.removeEventListener("mouseleave", this._boundHandleMouseLeave) + this._hostElement.removeEventListener("mouseenter", (e) => this.handleMouseEnter(e)) + this._hostElement.removeEventListener("mouseleave", (e) => this.handleMouseLeave(e)) }) ) this._mouseUpTimeout = null @@ -116,6 +112,8 @@ export class PinnedDatatip { this.render() } + // Mouse event hanlders: + handleMouseEnter(event: MouseEvent): void { this._isHovering = true this._hideDataTips() From e477319e21cd650d340c6730cf9128bb5339d337 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:01:47 -0500 Subject: [PATCH 11/22] fromevent --- src-commons-ui/float-pane/FloatPane.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index cda0d57c..2a365c00 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -80,7 +80,7 @@ export class PinnedDatatip { this._checkedScrollable = false this._isScrollable = false - const _wheelSubscription = fromEvent(this._hostElement, "wheel").subscribe((e) => { + const _wheelSubscription = fromEvent(this._hostElement, "wheel").subscribe((e) => { if (!this._checkedScrollable) { this._isScrollable = isScrollable(this._hostElement, e) this._checkedScrollable = true @@ -113,7 +113,7 @@ export class PinnedDatatip { } // Mouse event hanlders: - + handleMouseEnter(event: MouseEvent): void { this._isHovering = true this._hideDataTips() From d7b6fb2992922f3b3da2cc2689b6c22c8685416a Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:03:33 -0500 Subject: [PATCH 12/22] fix: remove excess assignment --- src-commons-ui/float-pane/FloatPane.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 2a365c00..5d876205 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -127,8 +127,7 @@ export class PinnedDatatip { return this._isHovering } - handleGlobalMouseMove(event: Event): void { - const evt: MouseEvent = (event: any) + handleGlobalMouseMove(evt: MouseEvent): void { const { _dragOrigin } = this invariant(_dragOrigin) this._isDragging = true From 99d9dfb90cbd168fc37c7b572bac460e718c4eaa Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:05:17 -0500 Subject: [PATCH 13/22] timeout --- src-commons-ui/float-pane/FloatPane.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 5d876205..b3371f5d 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -47,7 +47,7 @@ export class PinnedDatatip { _boundDispose: Function _boundHandleMouseDown: Function _boundHandleCapturedClick: Function - _mouseUpTimeout: ?number + _mouseUpTimeout: ?NodeJS.Timeout _hostElement: HTMLElement _marker: ?Marker _rangeDecoration: ?Decoration From 720f185fd862db4d6f39b03cd1960418c5bcaf73 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:06:00 -0500 Subject: [PATCH 14/22] remove excess assignm --- src-commons-ui/float-pane/FloatPane.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index b3371f5d..b274a767 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -157,8 +157,7 @@ export class PinnedDatatip { } } - handleMouseDown(event: Event): void { - const evt: MouseEvent = (event: any) + handleMouseDown(evt: MouseEvent): void { this._dragOrigin = { x: evt.clientX - this._offset.x, y: evt.clientY - this._offset.y, From c410851a4b0ba1693ae154bbc31ccf27d907fc9f Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:10:35 -0500 Subject: [PATCH 15/22] fix: use DisplayMarker instead of Marker --- src-commons-ui/float-pane/FloatPane.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index b274a767..ada3624d 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -1,4 +1,4 @@ -import { Marker, Decoration, TextEditor, Disposable, DisposableLike, CompositeDisposable } from "atom" +import { DisplayMarker, Decoration, TextEditor, Disposable, DisposableLike, CompositeDisposable } from "atom" import { Observable, fromEvent } from "rxjs" import type {Subscription} from "rxjs" import { disposableFromSubscription } from "../../src-commons-atom/disposable" @@ -49,7 +49,7 @@ export class PinnedDatatip { _boundHandleCapturedClick: Function _mouseUpTimeout: ?NodeJS.Timeout _hostElement: HTMLElement - _marker: ?Marker + _marker: ?DisplayMarker _rangeDecoration: ?Decoration _mouseSubscription: ?Subscription _subscriptions: CompositeDisposable @@ -216,7 +216,7 @@ export class PinnedDatatip { } if (this._marker == null) { - const marker: Marker = _editor.markBufferRange(_datatip.range, { + const marker: DisplayMarker = _editor.markBufferRange(_datatip.range, { invalidate: "never", }) this._marker = marker From ebc2b6cf7f20c2bc155d039477fe8c1f5595e229 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:16:19 -0500 Subject: [PATCH 16/22] feat: add missing Atom types --- src-commons-ui/float-pane/FloatPane.tsx | 3 +- types-packages/atom.d.ts | 113 ++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 types-packages/atom.d.ts diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index ada3624d..74a83bc9 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -1,4 +1,5 @@ -import { DisplayMarker, Decoration, TextEditor, Disposable, DisposableLike, CompositeDisposable } from "atom" +import { DisplayMarker, Decoration, TextEditor, Disposable, DisposableLike, CompositeDisposable, TextEditorElement } from "atom" +import "../../types-packages/atom" import { Observable, fromEvent } from "rxjs" import type {Subscription} from "rxjs" import { disposableFromSubscription } from "../../src-commons-atom/disposable" diff --git a/types-packages/atom.d.ts b/types-packages/atom.d.ts new file mode 100644 index 00000000..d111a5af --- /dev/null +++ b/types-packages/atom.d.ts @@ -0,0 +1,113 @@ +// TODO add to @types/Atom + +export {} + +// An {Object} with the following fields: +interface BufferChangeEvent { + // The deleted text + oldText: string + + // The {Range} of the deleted text before the change took place. + oldRange: Range + + // The inserted text + newText: string + + // The {Range} of the inserted text after the change took place. + newRange: Range +} + +type HighlightingChangeEvent = (range: Range) => void + +declare module "atom" { + interface TextEditor { + // Get the Element for the editor. + getElement(): TextEditorElement + + // Controls visibility based on the given {Boolean}. + setVisible(visible: boolean): void + + // Experimental: Get a notification when async tokenization is completed. + onDidTokenize(callback: () => any): Disposable + + component: { + getNextUpdatePromise(): Promise + } + + isDestroyed(): boolean + } + + interface LanguageMode { + // A {Function} that returns a {String} identifying the language. + getLanguageId(): string + + // A {Function} that is called whenever the buffer changes. + bufferDidChange(change: BufferChangeEvent): void + + // A {Function} that takes a callback {Function} and calls it with a {Range} argument whenever the syntax of a given part of the buffer is updated. + onDidChangeHighlighting(callback: HighlightingChangeEvent): void + + // A function that returns an iterator object with the following methods: + buildHighlightIterator(): { + // A {Function} that takes a {Point} and resets the iterator to that position. + seek(point: Point): any + + // A {Function} that advances the iterator to the next token + moveToSuccessor(): void + + // A {Function} that returns a {Point} representing the iterator's current position in the buffer. + getPosition(): Point + + // A {Function} that returns an {Array} of {Number}s representing tokens that end at the current position. + getCloseTags(): Array + + // A {Function} that returns an {Array} of {Number}s representing tokens that begin at the current position. + getOpenTags(): Array + } + } + + interface TextMateLanguageMode { + fullyTokenized: boolean + + // Get the suggested indentation level for an existing line in the buffer. + // + // * bufferRow - A {Number} indicating the buffer row + // + // Returns a {Number}. + suggestedIndentForBufferRow(bufferRow: number, tabLength: number, options: object): number + + // Get the suggested indentation level for a given line of text, if it were inserted at the given + // row in the buffer. + // + // * bufferRow - A {Number} indicating the buffer row + // + // Returns a {Number}. + suggestedIndentForLineAtBufferRow(bufferRow: number, line: number, tabLength: number): number + + // Get the suggested indentation level for a line in the buffer on which the user is currently + // typing. This may return a different result from {::suggestedIndentForBufferRow} in order + // to avoid unexpected changes in indentation. It may also return undefined if no change should + // be made. + // + // * bufferRow - The row {Number} + // + // Returns a {Number}. + suggestedIndentForEditedBufferRow(bufferRow: number, tabLength: number): number + } + + interface TextBuffer { + // Experimental: Get the language mode associated with this buffer. + // + // Returns a language mode {Object} (See {TextBuffer::setLanguageMode} for its interface). + getLanguageMode(): LanguageMode | TextMateLanguageMode + + // Experimental: Set the LanguageMode for this buffer. + // + // * `languageMode` - an {Object} with the following methods: + setLanguageMode(languageMode: LanguageMode | TextMateLanguageMode): void + } + + interface TextEditorElement { + setUpdatedSynchronously(val: boolean): void + } +} From 9044b58ec19f092c6912300c2bdf74d83357fcd6 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:17:24 -0500 Subject: [PATCH 17/22] fix: comment unkown syntax --- src-commons-ui/float-pane/FloatPane.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 74a83bc9..ebbe34c0 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -203,7 +203,7 @@ export class PinnedDatatip { _hostElement.style.left = _offset.x + "px" break default: - ;(_position: empty) + // ;(_position: empty) throw new Error(`Unexpected PinnedDatatip position: ${this._position}`) } } From 3581a6dfe7d96203a5e2baf6646b9e912e80e9b4 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:18:39 -0500 Subject: [PATCH 18/22] atom --- types-packages/atom.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types-packages/atom.d.ts b/types-packages/atom.d.ts index d111a5af..afab7b30 100644 --- a/types-packages/atom.d.ts +++ b/types-packages/atom.d.ts @@ -35,6 +35,8 @@ declare module "atom" { } isDestroyed(): boolean + + getDefaultCharWidth(): number } interface LanguageMode { From 3b4e485adeacd24f56c0e4d4cd1e99b3a9f1f61b Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:23:27 -0500 Subject: [PATCH 19/22] fix: use TypeScript style of optional properties --- src-commons-ui/float-pane/FloatPane.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index ebbe34c0..2b8e7ba9 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -48,15 +48,15 @@ export class PinnedDatatip { _boundDispose: Function _boundHandleMouseDown: Function _boundHandleCapturedClick: Function - _mouseUpTimeout: ?NodeJS.Timeout + _mouseUpTimeout: NodeJS.Timeout | null = null _hostElement: HTMLElement - _marker: ?DisplayMarker - _rangeDecoration: ?Decoration - _mouseSubscription: ?Subscription + _marker?: DisplayMarker + _rangeDecoration?: Decoration + _mouseSubscription: Subscription | null = null _subscriptions: CompositeDisposable _datatip: Datatip _editor: TextEditor - _dragOrigin: ?Position + _dragOrigin: Position | null = null _isDragging: boolean _offset: Position _isHovering: boolean @@ -71,8 +71,6 @@ export class PinnedDatatip { this._subscriptions.add(new Disposable(() => params.onDispose(this))) this._datatip = datatip this._editor = editor - this._marker = null - this._rangeDecoration = null this._hostElement = document.createElement("div") this._hostElement.className = "datatip-element" this._boundDispose = this.dispose.bind(this) @@ -102,10 +100,8 @@ export class PinnedDatatip { this._hostElement.removeEventListener("mouseleave", (e) => this.handleMouseLeave(e)) }) ) - this._mouseUpTimeout = null this._offset = { x: 0, y: 0 } this._isDragging = false - this._dragOrigin = null this._isHovering = false this._hideDataTips = params.hideDataTips this._position = params.position == null ? "end-of-line" : params.position From 6ff223a58bfa6ead77d8e15e93ea50036b64bbb7 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:29:38 -0500 Subject: [PATCH 20/22] disposable format --- src-commons-atom/disposable/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-commons-atom/disposable/index.ts b/src-commons-atom/disposable/index.ts index b9844d6d..6401bc94 100644 --- a/src-commons-atom/disposable/index.ts +++ b/src-commons-atom/disposable/index.ts @@ -1,7 +1,7 @@ -import type {Subscription} from "rxjs" +import type { Subscription } from "rxjs" import { DisposableLike } from "atom" // convert rxjs Subscription to Atom DisposableLike (rename unsubscribe to dispose) export function disposableFromSubscription(subs: Subscription): DisposableLike { - return {...subs, dispose: subs.unsubscribe} + return { ...subs, dispose: subs.unsubscribe } } From a40b8732372e8ead4898a806321abdb981f855b0 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:34:24 -0500 Subject: [PATCH 21/22] fix: add Datatip type --- src-commons-ui/float-pane/FloatPane.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 2b8e7ba9..09706547 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -3,7 +3,7 @@ import "../../types-packages/atom" import { Observable, fromEvent } from "rxjs" import type {Subscription} from "rxjs" import { disposableFromSubscription } from "../../src-commons-atom/disposable" -import { PinnedDatatipPosition } from "../../types-packages/main" +import { PinnedDatatipPosition, Datatip } from "../../types-packages/main" import * as React from "react" import ReactDOM from "react-dom" @@ -248,7 +248,7 @@ export class PinnedDatatip { action={DATATIP_ACTIONS.CLOSE} actionTitle="Close this datatip" className={classnames(_isDragging ? "datatip-dragging" : "", "datatip-pinned")} - datatip={_datatip} + {..._datatip} onActionClick={this._boundDispose} onMouseDown={this._boundHandleMouseDown} onClickCapture={this._boundHandleCapturedClick} From 1681006b85fbd60f70be0fb5a930d4469e1d6d40 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Oct 2020 03:37:54 -0500 Subject: [PATCH 22/22] fix: simplify constructor by initializing the properties in the class --- src-commons-ui/float-pane/FloatPane.tsx | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src-commons-ui/float-pane/FloatPane.tsx b/src-commons-ui/float-pane/FloatPane.tsx index 09706547..59319c3d 100644 --- a/src-commons-ui/float-pane/FloatPane.tsx +++ b/src-commons-ui/float-pane/FloatPane.tsx @@ -49,35 +49,31 @@ export class PinnedDatatip { _boundHandleMouseDown: Function _boundHandleCapturedClick: Function _mouseUpTimeout: NodeJS.Timeout | null = null - _hostElement: HTMLElement + _hostElement: HTMLElement = document.createElement("div") _marker?: DisplayMarker _rangeDecoration?: Decoration _mouseSubscription: Subscription | null = null - _subscriptions: CompositeDisposable + _subscriptions: CompositeDisposable = new CompositeDisposable() _datatip: Datatip _editor: TextEditor _dragOrigin: Position | null = null - _isDragging: boolean - _offset: Position - _isHovering: boolean - _checkedScrollable: boolean - _isScrollable: boolean + _isDragging: boolean = false + _offset: Position = { x: 0, y: 0 } + _isHovering: boolean = false + _checkedScrollable: boolean = false + _isScrollable: boolean = false _hideDataTips: () => void _position: PinnedDatatipPosition _showRangeHighlight: boolean constructor(datatip: Datatip, editor: TextEditor, params: PinnedDatatipParams) { - this._subscriptions = new CompositeDisposable() this._subscriptions.add(new Disposable(() => params.onDispose(this))) this._datatip = datatip this._editor = editor - this._hostElement = document.createElement("div") this._hostElement.className = "datatip-element" this._boundDispose = this.dispose.bind(this) this._boundHandleMouseDown = this.handleMouseDown.bind(this) this._boundHandleCapturedClick = this.handleCapturedClick.bind(this) - this._checkedScrollable = false - this._isScrollable = false const _wheelSubscription = fromEvent(this._hostElement, "wheel").subscribe((e) => { if (!this._checkedScrollable) { @@ -100,9 +96,6 @@ export class PinnedDatatip { this._hostElement.removeEventListener("mouseleave", (e) => this.handleMouseLeave(e)) }) ) - this._offset = { x: 0, y: 0 } - this._isDragging = false - this._isHovering = false this._hideDataTips = params.hideDataTips this._position = params.position == null ? "end-of-line" : params.position this._showRangeHighlight = params.showRangeHighlight == null ? true : params.showRangeHighlight