Skip to content
This repository was archived by the owner on Oct 5, 2022. It is now read-only.

Commit 961e6b0

Browse files
committed
Untie actions from tooltips
1 parent 78d87c5 commit 961e6b0

File tree

19 files changed

+255
-278
lines changed

19 files changed

+255
-278
lines changed

dist/main.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/main.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/actions-provider/index.ts

-54
This file was deleted.

src/actions-registry/index.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { TextEditor, Range, CompositeDisposable } from 'atom'
2+
import { PluginManager } from '../plugin-manager'
3+
import * as UPI from 'atom-haskell-upi'
4+
import TEventRangeType = UPI.TEventRangeType
5+
import { PriorityRegistry } from '../priority-registry'
6+
import { selectAction } from './select-action'
7+
8+
export class ActionRegistry extends PriorityRegistry<UPI.Action[]> {
9+
protected disposables = new CompositeDisposable()
10+
constructor(private readonly pluginManager: PluginManager) {
11+
super()
12+
this.disposables.add(
13+
atom.commands.add('atom-text-editor.ide-haskell', {
14+
'ide-haskell:show-actions': async ({ currentTarget }) => {
15+
const editor = currentTarget.getModel()
16+
const act = await this.pluginManager.actionRegistry.getActions(
17+
editor,
18+
TEventRangeType.context,
19+
editor.getSelectedBufferRange(),
20+
)
21+
if (act && act.length) {
22+
const choice = await selectAction(act)
23+
if (choice) await choice.apply()
24+
}
25+
},
26+
}),
27+
)
28+
}
29+
30+
public dispose() {
31+
super.dispose()
32+
this.disposables.dispose()
33+
}
34+
35+
public async getActions(
36+
editor: TextEditor,
37+
type: TEventRangeType,
38+
crange: Range,
39+
) {
40+
for (const { pluginName, handler, eventTypes } of this.providers) {
41+
if (!eventTypes.includes(type)) {
42+
continue
43+
}
44+
const awaiter = this.pluginManager.getAwaiter(pluginName)
45+
const actions = await awaiter(() => handler(editor, crange, type))
46+
if (actions === undefined) continue
47+
if (!actions.length) continue
48+
return actions
49+
}
50+
return undefined
51+
}
52+
}

src/check-results-provider/editor-control.ts

+5-18
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ import TEventRangeType = UPI.TEventRangeType
1313
import { ResultsDB, ResultItem } from '../results-db'
1414
import { PluginManager, IEditorController } from '../plugin-manager'
1515
import { listen, bufferPositionFromMouseEvent, handlePromise } from '../utils'
16-
import { TooltipRegistry } from '../tooltip-registry'
16+
import { TooltipManager } from '../tooltip-manager'
1717

1818
export class CREditorControl implements IEditorController {
1919
private gutter: Gutter
2020
private gutterElement: HTMLElement
2121
private markers: DisplayMarkerLayer
2222
private disposables: CompositeDisposable
2323
private markerProps: WeakMap<DisplayMarker, ResultItem>
24-
private tooltipRegistry: TooltipRegistry
24+
private tooltipManager: TooltipManager
2525
private resultsDB: ResultsDB
2626
constructor(private editor: TextEditor, pluginManager: PluginManager) {
2727
const gutter = this.editor.gutterWithName('ide-haskell-check-results')
@@ -36,7 +36,7 @@ export class CREditorControl implements IEditorController {
3636
this.gutterElement = atom.views.getView(this.gutter)
3737

3838
this.resultsDB = pluginManager.resultsDB
39-
this.tooltipRegistry = pluginManager.tooltipRegistry
39+
this.tooltipManager = pluginManager.tooltipManager
4040

4141
this.disposables = new CompositeDisposable()
4242
this.markers = editor.addMarkerLayer({
@@ -87,19 +87,6 @@ export class CREditorControl implements IEditorController {
8787
}
8888
}
8989

90-
public async getActionAt(
91-
pos: Point,
92-
type: TEventRangeType | 'gutter',
93-
): Promise<UPI.Action[]> {
94-
return ([] as UPI.Action[]).concat(
95-
...(await Promise.all(
96-
Array.from(this.getResultAt(pos, type)).map((res) =>
97-
res.actions ? res.actions() : [],
98-
),
99-
)),
100-
)
101-
}
102-
10390
private registerGutterEvents() {
10491
this.disposables.add(
10592
listen(this.gutterElement, 'mouseover', '.decoration', (e) => {
@@ -111,7 +98,7 @@ export class CREditorControl implements IEditorController {
11198
const msg = Array.from(this.getMessageAt(bufferPt, 'gutter'))
11299
if (msg.length > 0) {
113100
handlePromise(
114-
this.tooltipRegistry.showTooltip(
101+
this.tooltipManager.showTooltip(
115102
this.editor,
116103
TEventRangeType.mouse,
117104
{
@@ -129,7 +116,7 @@ export class CREditorControl implements IEditorController {
129116
)
130117
this.disposables.add(
131118
listen(this.gutterElement, 'mouseout', '.decoration', () =>
132-
this.tooltipRegistry.hideTooltip(
119+
this.tooltipManager.hideTooltip(
133120
this.editor,
134121
TEventRangeType.mouse,
135122
'builtin:check-results',

src/check-results-provider/index.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ export class CheckResultsProvider {
4747
return undefined
4848
}
4949
const msg = Array.from(controller.getMessageAt(crange.start, type))
50-
const actions = () => controller.getActionAt(crange.start, type)
5150
if (msg.length > 0) {
52-
return { range: crange, text: msg, actions }
51+
return { range: crange, text: msg }
5352
}
5453
return undefined
5554
}

src/editor-control/tooltip-manager.ts renamed to src/editor-control/editor-overlay-manager.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { TooltipMessage } from './tooltip-view'
21
import { EventTable } from './event-table'
32
import * as AtomTypes from 'atom'
43
import * as UPI from 'atom-haskell-upi'
54
import TEventRangeType = UPI.TEventRangeType
6-
import { MessageObject } from '../utils'
75

86
type Range = AtomTypes.Range
97
type TextEditor = AtomTypes.TextEditor
@@ -13,7 +11,7 @@ export interface IMarkerProperties extends AtomTypes.FindDisplayMarkerOptions {
1311
persistent: boolean
1412
}
1513

16-
export class TooltipManager {
14+
export class EditorOverlayManager {
1715
private markers: EventTable
1816
private editorElement: HTMLElement
1917
constructor(private editor: TextEditor) {
@@ -31,18 +29,17 @@ export class TooltipManager {
3129

3230
public show(
3331
range: Range,
34-
text: MessageObject | MessageObject[],
35-
actions: Promise<JSX.Element | undefined> | undefined,
3632
type: TEventRangeType,
3733
source: string,
3834
detail: IMarkerProperties,
35+
view: object,
3936
) {
4037
this.hide(type, source)
4138
const highlightMarker = this.markers
4239
.get(type, source)
4340
.markBufferRange(range)
4441
highlightMarker.setProperties(detail)
45-
this.decorate(highlightMarker, new TooltipMessage(source, text, actions))
42+
this.decorate(highlightMarker, view)
4643
this.editorElement.classList.add('ide-haskell--has-tooltips')
4744
}
4845

@@ -83,7 +80,7 @@ export class TooltipManager {
8380
}
8481
}
8582

86-
private decorate(marker: DisplayMarker, tooltipView: TooltipMessage) {
83+
private decorate(marker: DisplayMarker, tooltipView: object) {
8784
this.editor.decorateMarker(marker, {
8885
type: 'overlay',
8986
position: 'tail',

src/editor-control/index.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Range, TextEditor, Point, CompositeDisposable, Disposable } from 'atom'
22
import { bufferPositionFromMouseEvent, listen, handlePromise } from '../utils'
3-
import { TooltipManager } from './tooltip-manager'
4-
import { TooltipRegistry } from '../tooltip-registry'
3+
import { EditorOverlayManager } from './editor-overlay-manager'
4+
import { TooltipManager as GlobalTooltipManager } from '../tooltip-manager'
55
import { PluginManager, IEditorController } from '../plugin-manager'
66
import * as UPI from 'atom-haskell-upi'
77
import TEventRangeType = UPI.TEventRangeType
@@ -11,7 +11,7 @@ export type TEventRangeResult =
1111
| undefined
1212

1313
export class EditorControl implements IEditorController {
14-
public readonly tooltips: TooltipManager
14+
public readonly tooltips: EditorOverlayManager
1515
private readonly disposables: CompositeDisposable
1616
private lastMouseBufferPt?: Point
1717
private exprTypeTimeout?: number
@@ -28,15 +28,15 @@ export class EditorControl implements IEditorController {
2828
height: number
2929
}
3030
}
31-
private readonly tooltipRegistry: TooltipRegistry
31+
private readonly tooltipManager: GlobalTooltipManager
3232
constructor(
3333
private readonly editor: TextEditor,
3434
pluginManager: PluginManager,
3535
) {
3636
this.disposables = new CompositeDisposable()
37-
this.tooltips = new TooltipManager(this.editor)
37+
this.tooltips = new EditorOverlayManager(this.editor)
3838
this.disposables.add(this.tooltips)
39-
this.tooltipRegistry = pluginManager.tooltipRegistry
39+
this.tooltipManager = pluginManager.tooltipManager
4040

4141
this.editorElement = atom.views.getView(this.editor) as any
4242

@@ -130,7 +130,7 @@ export class EditorControl implements IEditorController {
130130
) {
131131
this.tooltips.hide(type)
132132
} else {
133-
handlePromise(this.tooltipRegistry.showTooltip(this.editor, type))
133+
handlePromise(this.tooltipManager.showTooltip(this.editor, type))
134134
}
135135
}
136136

@@ -177,7 +177,7 @@ export class EditorControl implements IEditorController {
177177
}
178178

179179
handlePromise(
180-
this.tooltipRegistry.showTooltip(this.editor, TEventRangeType.keyboard),
180+
this.tooltipManager.showTooltip(this.editor, TEventRangeType.keyboard),
181181
)
182182
if (
183183
atom.config.get('ide-haskell.onCursorMove', {

src/plugin-manager.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import * as UPI from 'atom-haskell-upi'
2121
import * as Linter from 'atom/linter'
2222
import * as StatusBar from 'atom/status-bar'
2323
import { handlePromise } from './utils'
24-
import { ActionsProvider } from './actions-provider'
24+
import { ActionRegistry } from './actions-registry'
25+
import { TooltipManager } from './tooltip-manager'
2526

2627
export { IParamState, IOutputViewState }
2728

@@ -55,6 +56,8 @@ export class PluginManager {
5556
public readonly resultsDB: ResultsDB
5657
public readonly configParamManager: ConfigParamManager
5758
public readonly tooltipRegistry: TooltipRegistry
59+
public readonly actionRegistry: ActionRegistry
60+
public readonly tooltipManager: TooltipManager
5861
private readonly checkResultsProvider?: CheckResultsProvider
5962
private linterSupport?: LinterSupport
6063
private readonly disposables = new CompositeDisposable()
@@ -73,16 +76,16 @@ export class PluginManager {
7376
ECMap<IEditorController>
7477
>()
7578
private readonly backendStatusController = new BackendStatusController()
76-
private readonly actionsProvider: ActionsProvider
7779

7880
constructor(state: IState, public outputPanel: OutputPanel) {
7981
this.disposables.add(this.emitter)
8082

8183
this.resultsDB = new ResultsDB()
8284
this.outputPanel.connectResults(this.resultsDB)
8385
this.outputPanel.connectBsc(this.backendStatusController)
84-
this.actionsProvider = new ActionsProvider(this)
85-
this.tooltipRegistry = new TooltipRegistry(this, this.actionsProvider)
86+
this.actionRegistry = new ActionRegistry(this)
87+
this.tooltipRegistry = new TooltipRegistry(this)
88+
this.tooltipManager = new TooltipManager(this)
8689
this.configParamManager = new ConfigParamManager(
8790
this.outputPanel,
8891
state.configParams,
@@ -93,7 +96,7 @@ export class PluginManager {
9396
this.addEditorController(PrettifyEditorController),
9497
this.addEditorController(EditorMarkControl),
9598
this.tooltipRegistry,
96-
this.actionsProvider,
99+
this.actionRegistry,
97100
)
98101
if (atom.config.get('ide-haskell.messageDisplayFrontend') === 'builtin') {
99102
this.checkResultsProvider = new CheckResultsProvider(this)

src/priority-registry/index.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { TextEditor, Disposable, Range } from 'atom'
2+
import * as UPI from 'atom-haskell-upi'
3+
import TEventRangeType = UPI.TEventRangeType
4+
5+
interface SpecDefinition<T> {
6+
priority: number
7+
eventTypes?: TEventRangeType[]
8+
handler: (
9+
editor: TextEditor,
10+
crange: Range,
11+
type: TEventRangeType,
12+
) => T | undefined | Promise<T | undefined>
13+
}
14+
15+
export class PriorityRegistry<Spec> {
16+
protected providers: Array<
17+
SpecDefinition<Spec> & {
18+
pluginName: string
19+
eventTypes: TEventRangeType[]
20+
}
21+
> = []
22+
23+
public dispose() {
24+
this.providers = []
25+
}
26+
27+
public register(
28+
pluginName: string,
29+
provider: SpecDefinition<Spec>,
30+
): Disposable {
31+
const idx = this.providers.findIndex(
32+
({ priority }) => priority < provider.priority,
33+
)
34+
const defaultEvT: TEventRangeType[] = [
35+
TEventRangeType.selection,
36+
TEventRangeType.mouse,
37+
]
38+
const record = {
39+
pluginName,
40+
eventTypes: provider.eventTypes ? provider.eventTypes : defaultEvT,
41+
priority: provider.priority,
42+
handler: provider.handler,
43+
}
44+
if (idx === -1) {
45+
this.providers.push(record)
46+
} else {
47+
this.providers.splice(idx, 0, record)
48+
}
49+
return new Disposable(() => {
50+
const ix = this.providers.indexOf(record)
51+
this.providers.splice(ix, 1)
52+
})
53+
}
54+
}

0 commit comments

Comments
 (0)