1
- import type { TextEditor , TextChange , Disposable } from "atom"
2
- // TODO add to @types /atom
3
- type AggregatedTextChange = {
4
- changes : Array < TextChange >
5
- }
1
+ import { Range , CompositeDisposable , TextEditor , TextChange , Disposable , BufferStoppedChangingEvent } from "atom"
6
2
import type { TextEdit , BusySignalService } from "atom-ide-base"
7
3
import type {
8
4
FileCodeFormatProvider ,
@@ -14,57 +10,32 @@ import type {
14
10
import nullthrows from "nullthrows"
15
11
import { registerOnWillSave } from "@atom-ide-community/nuclide-commons-atom/FileEventHandlers"
16
12
import { getFormatOnSave , getFormatOnType } from "./config"
17
- import { Range } from "atom"
18
13
import { getLogger } from "log4js"
19
14
import { ProviderRegistry } from "atom-ide-base/commons-atom/ProviderRegistry"
20
15
import { applyTextEditsToBuffer } from "@atom-ide-community/nuclide-commons-atom/text-edit"
21
- import { observeEditorDestroy } from "@atom-ide-community/nuclide-commons-atom/text-editor"
22
- import { observableFromSubscribeFunction } from "@atom-ide-community/nuclide-commons/event"
23
16
import nuclideUri from "@atom-ide-community/nuclide-commons/nuclideUri"
24
- import { completingSwitchMap , microtask } from "@atom-ide-community/nuclide-commons/observable"
17
+ import { microtask } from "@atom-ide-community/nuclide-commons/observable"
25
18
import UniversalDisposable from "@atom-ide-community/nuclide-commons/UniversalDisposable"
26
19
import { Observable } from "rxjs-compat/bundles/rxjs-compat.umd.min.js"
27
- import type { Subscription } from "rxjs"
28
20
29
21
// Save events are critical, so don't allow providers to block them.
30
22
export const SAVE_TIMEOUT = 2500
31
23
32
- type FormatEvent =
33
- | {
34
- type : "command" | "save" | "new-save"
35
- editor : TextEditor
36
- }
37
- | {
38
- type : "type"
39
- editor : TextEditor
40
- edit : AggregatedTextChange
41
- }
42
-
43
24
export default class CodeFormatManager {
44
- _subscriptions = new UniversalDisposable ( )
25
+ _subscriptions : CompositeDisposable
45
26
_rangeProviders : ProviderRegistry < RangeCodeFormatProvider >
46
27
_fileProviders : ProviderRegistry < FileCodeFormatProvider >
47
28
_onTypeProviders : ProviderRegistry < OnTypeCodeFormatProvider >
48
29
_onSaveProviders : ProviderRegistry < OnSaveCodeFormatProvider >
49
30
_busySignalService : BusySignalService | undefined | null
50
31
51
32
constructor ( ) {
52
- this . _subscriptions = new UniversalDisposable (
53
- registerOnWillSave ( this . _onWillSaveProvider ( ) )
54
- )
55
- this . _rangeProviders = new ProviderRegistry ( )
56
- this . _fileProviders = new ProviderRegistry ( )
57
- this . _onTypeProviders = new ProviderRegistry ( )
58
- this . _onSaveProviders = new ProviderRegistry ( )
59
- }
60
-
61
- /**
62
- * Subscribe to all formatting events (commands, saves, edits) and dispatch formatters as necessary. By handling all
63
- * events in a central location, we ensure that no buffer runs into race conditions with simultaneous formatters.
64
- */
65
- _subscribeToEvents ( ) : Subscription {
66
- // Events from the explicit Atom command.
67
- this . _subscriptions . add (
33
+ /**
34
+ * Subscribe to all formatting events (commands, saves, edits) and dispatch formatters as necessary. By handling all
35
+ * events in a central location, we ensure that no buffer runs into race conditions with simultaneous formatters.
36
+ */
37
+ this . _subscriptions = new CompositeDisposable (
38
+ // Events from the explicit Atom command.
68
39
atom . commands . add ( "atom-text-editor" , "code-format:format-code" , async ( event ) => {
69
40
const editorElement = event . currentTarget
70
41
if ( ! editorElement ) {
@@ -84,41 +55,30 @@ export default class CodeFormatManager {
84
55
detail : err . detail ,
85
56
} )
86
57
}
87
- } )
88
- )
58
+ } ) ,
89
59
90
- // Events from editor actions (saving, typing).
91
- const editorEvents = observableFromSubscribeFunction ( ( cb ) => atom . workspace . observeTextEditors ( cb ) ) . mergeMap (
92
- ( editor ) => _getEditorEventStream ( editor )
93
- )
60
+ // Events from typing in the editor
61
+ atom . workspace . observeTextEditors ( ( editor ) => {
62
+ const onChangeSubs = editor . getBuffer ( ) . onDidStopChanging ( ( event ) => {
63
+ this . _formatCodeOnTypeInTextEditor ( editor , event ) . catch ( ( err ) => {
64
+ getLogger ( "code-format" ) . warn ( "Failed to format code on type:" , err )
65
+ } )
66
+ } )
67
+ // Make sure we halt everything when the editor gets destroyed.
68
+ // We need to capture when editors are about to be destroyed in order to
69
+ // interrupt any pending formatting operations. (Otherwise, we may end up
70
+ // attempting to save a destroyed editor!)
71
+ editor . onDidDestroy ( ( ) => onChangeSubs . dispose ( ) )
72
+ } ) ,
94
73
95
- return (
96
- editorEvents
97
- // Group events by buffer to prevent simultaneous formatting operations.
98
- . groupBy (
99
- ( event ) => event . editor . getBuffer ( ) ,
100
- ( event ) => event ,
101
- ( grouped ) => observableFromSubscribeFunction ( ( callback ) => grouped . key . onDidDestroy ( callback ) )
102
- )
103
- . mergeMap ( ( events ) =>
104
- // Make sure we halt everything when the editor gets destroyed.
105
- events . let ( completingSwitchMap ( ( event ) => this . _handleEvent ( event ) ) )
106
- )
107
- . subscribe ( )
74
+ // Format on save
75
+ registerOnWillSave ( this . _onWillSaveProvider ( ) )
108
76
)
109
- }
110
77
111
- async _handleEvent ( event : FormatEvent ) {
112
- const { editor } = event
113
- switch ( event . type ) {
114
- case "type" :
115
- return this . _formatCodeOnTypeInTextEditor ( editor , event . edit ) . catch ( ( err ) => {
116
- getLogger ( "code-format" ) . warn ( "Failed to format code on type:" , err )
117
- return Observable . empty ( )
118
- } )
119
- default :
120
- return Observable . throw ( `unknown event type ${ event . type } ` )
121
- }
78
+ this . _rangeProviders = new ProviderRegistry ( )
79
+ this . _fileProviders = new ProviderRegistry ( )
80
+ this . _onTypeProviders = new ProviderRegistry ( )
81
+ this . _onSaveProviders = new ProviderRegistry ( )
122
82
}
123
83
124
84
// Return the text edits used to format code in the editor specified.
@@ -177,7 +137,7 @@ export default class CodeFormatManager {
177
137
178
138
_formatCodeOnTypeInTextEditor (
179
139
editor : TextEditor ,
180
- aggregatedEvent : AggregatedTextChange
140
+ aggregatedEvent : BufferStoppedChangingEvent
181
141
) : Observable < Array < TextEdit > > {
182
142
return Observable . defer ( ( ) => {
183
143
// Don't try to format changes with multiple cursors.
@@ -359,19 +319,3 @@ function _checkContentsAreSame(before: string, after: string): void {
359
319
throw new Error ( "The file contents were changed before formatting was complete." )
360
320
}
361
321
}
362
-
363
- /** Returns a stream of all typing and saving operations from the editor. */
364
- function _getEditorEventStream ( editor : TextEditor ) : Observable < FormatEvent > {
365
- const changeEvents = observableFromSubscribeFunction ( ( callback ) => editor . getBuffer ( ) . onDidChangeText ( callback ) )
366
-
367
- // We need to capture when editors are about to be destroyed in order to
368
- // interrupt any pending formatting operations. (Otherwise, we may end up
369
- // attempting to save a destroyed editor!)
370
- const willDestroyEvents = observableFromSubscribeFunction ( ( cb ) => atom . workspace . onWillDestroyPaneItem ( cb ) ) . filter (
371
- ( event ) => event . item === editor
372
- )
373
-
374
- return Observable . merge ( changeEvents . map ( ( edit ) => ( { type : "type" , editor, edit } ) ) ) . takeUntil (
375
- Observable . merge ( observeEditorDestroy ( editor ) , willDestroyEvents )
376
- )
377
- }
0 commit comments