From 40c05eb01cd02bc3b7f6cd7c9c501d8f35f5b000 Mon Sep 17 00:00:00 2001 From: hansonw Date: Tue, 17 Jul 2018 21:15:04 -0700 Subject: [PATCH] Release v0.13.0 --- DEVELOPMENT | 0 flow-libs/nuclide.flow.js | 23 +- index.js | 116 +- .../atom-ide-ui/__atom_tests__/empty-test.js | 7 +- modules/atom-ide-ui/__tests__/empty-test.js | 7 +- modules/atom-ide-ui/index.js | 155 +- modules/atom-ide-ui/jest.config.atom.js | 15 +- modules/atom-ide-ui/jest.config.js | 9 +- modules/atom-ide-ui/jest.config.node.js | 15 +- .../__atom_tests__/BusySignalInstance-test.js | 149 +- .../lib/BusyMessageInstance.js | 116 +- .../lib/BusySignalSingleton.js | 34 +- .../atom-ide-busy-signal/lib/MessageStore.js | 151 +- .../atom-ide-busy-signal/lib/StatusBarTile.js | 195 +- .../pkg/atom-ide-busy-signal/lib/main.js | 88 +- .../pkg/atom-ide-busy-signal/lib/types.js | 58 +- .../__atom_tests__/CodeActionManager-test.js | 111 +- .../lib/CodeActionManager.js | 298 ++- .../pkg/atom-ide-code-actions/lib/main.js | 49 +- .../pkg/atom-ide-code-actions/lib/types.js | 43 +- .../__atom_tests__/CodeFormatManager-test.js | 170 +- .../lib/CodeFormatManager.js | 553 ++-- .../pkg/atom-ide-code-format/lib/config.js | 41 +- .../pkg/atom-ide-code-format/lib/main.js | 73 +- .../pkg/atom-ide-code-format/lib/types.js | 98 +- .../CodeHighlightManager-test.js | 119 +- .../lib/CodeHighlightManager.js | 214 +- .../pkg/atom-ide-code-highlight/lib/main.js | 41 +- .../pkg/atom-ide-code-highlight/lib/types.js | 21 +- .../__atom_tests__/ConsoleView-test.js | 115 +- .../atom-ide-console/__tests__/Epics-test.js | 84 +- .../__tests__/Reducers-test.js | 210 +- .../__tests__/getCurrentExecutorId-test.js | 66 +- .../__tests__/parseText-test.js | 24 +- .../lib/ConsoleServiceClient.js | 44 +- .../lib/getCurrentExecutorId.js | 20 +- .../pkg/atom-ide-console/lib/main.js | 549 ++-- .../pkg/atom-ide-console/lib/parseText.js | 105 +- .../atom-ide-console/lib/recordsChanged.js | 26 +- .../pkg/atom-ide-console/lib/redux/Actions.js | 192 +- .../pkg/atom-ide-console/lib/redux/Epics.js | 234 +- .../atom-ide-console/lib/redux/Reducers.js | 362 +-- .../pkg/atom-ide-console/lib/types.js | 311 +-- .../pkg/atom-ide-console/lib/ui/Console.js | 781 +++--- .../atom-ide-console/lib/ui/ConsoleHeader.js | 371 +-- .../atom-ide-console/lib/ui/ConsoleView.js | 624 ++--- .../lib/ui/FilteredMessagesReminder.js | 65 +- .../pkg/atom-ide-console/lib/ui/InputArea.js | 322 +-- .../lib/ui/NewMessagesNotification.js | 64 +- .../atom-ide-console/lib/ui/OutputTable.js | 456 ++-- .../atom-ide-console/lib/ui/PromptButton.js | 96 +- .../pkg/atom-ide-console/lib/ui/RecordView.js | 392 +-- .../atom-ide-datatip/lib/DatatipComponent.js | 127 +- .../atom-ide-datatip/lib/DatatipManager.js | 954 ++++--- .../lib/MarkedStringDatatip.js | 98 +- .../lib/MarkedStringSnippet.js | 104 +- .../pkg/atom-ide-datatip/lib/PinnedDatatip.js | 365 +-- .../atom-ide-datatip/lib/getModifierKeys.js | 54 +- .../pkg/atom-ide-datatip/lib/isScrollable.js | 27 +- .../pkg/atom-ide-datatip/lib/main.js | 41 +- .../pkg/atom-ide-datatip/lib/types.js | 83 +- .../lib/AtomServiceContainer.js | 141 +- .../lib/BreakpointDisplayController.js | 468 ++-- .../lib/BreakpointManager.js | 76 +- .../atom-ide-debugger/lib/DebuggerDatatip.js | 113 +- .../atom-ide-debugger/lib/DebuggerUiModel.js | 135 +- .../lib/RemoteControlService.js | 138 +- .../pkg/atom-ide-debugger/lib/constants.js | 48 +- .../lib/evaluationExpression.js | 62 +- .../pkg/atom-ide-debugger/lib/logger.js | 25 +- .../pkg/atom-ide-debugger/lib/main.js | 1346 +++++----- .../pkg/atom-ide-debugger/lib/types.js | 505 +--- .../lib/ui/BreakpointConfigComponent.js | 331 ++- .../lib/ui/BreakpointListComponent.js | 574 ++-- .../lib/ui/BreakpointsView.js | 76 +- .../lib/ui/DebuggerCallstackComponent.js | 405 +-- .../lib/ui/DebuggerControllerView.js | 113 +- .../lib/ui/DebuggerControlsView.js | 265 +- .../lib/ui/DebuggerDatatipComponent.js | 112 +- .../lib/ui/DebuggerLaunchAttachUI.js | 486 ++-- .../lib/ui/DebuggerLayoutManager.js | 1106 ++++---- .../lib/ui/DebuggerPaneContainerViewModel.js | 347 +-- .../lib/ui/DebuggerPaneViewModel.js | 84 +- .../lib/ui/DebuggerProcessComponent.js | 178 +- .../lib/ui/DebuggerProcessTreeView.js | 80 +- .../lib/ui/DebuggerSteppingComponent.js | 551 ++-- .../lib/ui/DebuggerThreadsComponent.js | 524 ++-- .../atom-ide-debugger/lib/ui/FrameTreeNode.js | 97 +- .../lib/ui/ProcessTreeNode.js | 226 +- .../lib/ui/ScopesComponent.js | 456 ++-- .../atom-ide-debugger/lib/ui/ScopesView.js | 141 +- .../lib/ui/ThreadTreeNode.js | 463 ++-- .../atom-ide-debugger/lib/ui/ThreadsView.js | 144 +- .../lib/ui/WatchExpressionComponent.js | 366 +-- .../pkg/atom-ide-debugger/lib/ui/WatchView.js | 170 +- .../pkg/atom-ide-debugger/lib/utils.js | 184 +- .../atom-ide-debugger/lib/vsp/DebugService.js | 2309 ++++++++--------- .../lib/vsp/DebuggerModel.js | 1168 ++++----- .../DefinitionHyperclick-test.js | 330 +-- .../lib/DefinitionCache.js | 93 +- .../getPreviewDatatipFromDefinitionResult.js | 127 +- .../pkg/atom-ide-definitions/lib/main.js | 353 +-- .../pkg/atom-ide-definitions/lib/types.js | 66 +- .../__atom_tests__/DiagnosticsView-test.js | 111 +- .../__tests__/DiagnosticMessageTest-test.js | 177 +- .../lib/DiagnosticsViewModel.js | 487 ++-- .../atom-ide-diagnostics-ui/lib/GroupUtils.js | 74 +- .../lib/KeyboardShortcuts.js | 190 +- .../pkg/atom-ide-diagnostics-ui/lib/aim.js | 102 +- .../lib/getDiagnosticDatatip.js | 115 +- .../pkg/atom-ide-diagnostics-ui/lib/gutter.js | 456 ++-- .../pkg/atom-ide-diagnostics-ui/lib/main.js | 774 +++--- .../lib/showActionsMenu.js | 203 +- .../lib/showAtomLinterWarning.js | 154 +- .../lib/sortDiagnostics.js | 166 +- .../pkg/atom-ide-diagnostics-ui/lib/types.js | 41 +- .../lib/ui/DiagnosticsCodeActions.js | 108 +- .../lib/ui/DiagnosticsMessage.js | 168 +- .../lib/ui/DiagnosticsMessageText.js | 157 +- .../lib/ui/DiagnosticsPopup.js | 218 +- .../lib/ui/DiagnosticsTable.js | 708 ++--- .../lib/ui/DiagnosticsTraceItem.js | 76 +- .../lib/ui/DiagnosticsView.js | 468 ++-- .../lib/ui/FilterButton.js | 75 +- .../lib/ui/SettingsModal.js | 115 +- .../lib/ui/StatusBarTile.js | 276 +- .../IndieLinterRegistry-test.js | 50 +- .../__atom_tests__/LinterAdapter-test.js | 471 ++-- .../LinterAdapterFactory-test.js | 64 +- .../MessageRangeTracker-test.js | 214 +- .../__atom_tests__/createStore-test.js | 435 ++-- .../__tests__/Epics-test.js | 118 +- .../lib/MessageRangeTracker.js | 151 +- .../pkg/atom-ide-diagnostics/lib/main.js | 213 +- .../atom-ide-diagnostics/lib/redux/Actions.js | 158 +- .../atom-ide-diagnostics/lib/redux/Epics.js | 344 +-- .../lib/redux/Reducers.js | 271 +- .../lib/redux/Selectors.js | 140 +- .../lib/redux/createStore.js | 170 +- .../lib/services/DiagnosticUpdater.js | 186 +- .../lib/services/IndieLinterRegistry.js | 142 +- .../lib/services/LinterAdapter.js | 402 +-- .../lib/services/LinterAdapterFactory.js | 84 +- .../pkg/atom-ide-diagnostics/lib/types.js | 309 +-- .../FindReferencesModel-test.js | 268 +- .../lib/FindReferencesModel.js | 210 +- .../lib/FindReferencesViewModel.js | 105 +- .../pkg/atom-ide-find-references/lib/main.js | 419 +-- .../pkg/atom-ide-find-references/lib/types.js | 65 +- .../lib/view/FileReferencesView.js | 194 +- .../lib/view/FindReferencesView.js | 183 +- .../lib/backports/installTextEditorStyles.js | 104 +- .../pkg/atom-ide-global/lib/main.js | 48 +- .../OutlineViewSearchTest-test.js | 360 +-- .../atom-ide-outline-view/lib/OutlineView.js | 744 +++--- .../lib/OutlineViewPanel.js | 118 +- .../lib/OutlineViewSearch.js | 338 +-- .../lib/createOutlines.js | 246 +- .../pkg/atom-ide-outline-view/lib/main.js | 203 +- .../pkg/atom-ide-outline-view/lib/types.js | 72 +- .../__atom_tests__/refactorEpics-test.js | 177 +- .../__atom_tests__/refactorStore-test.js | 540 ++-- .../components/ConfirmRefactorComponent.js | 204 +- .../lib/components/DiffPreviewComponent.js | 53 +- .../components/FreeformRefactorComponent.js | 249 +- .../lib/components/MainRefactorComponent.js | 221 +- .../lib/components/PickRefactorComponent.js | 164 +- .../lib/components/ProgressComponent.js | 61 +- .../lib/components/RenameComponent.js | 194 +- .../pkg/atom-ide-refactor/lib/main.js | 334 ++- .../atom-ide-refactor/lib/refactorActions.js | 148 +- .../atom-ide-refactor/lib/refactorEpics.js | 576 ++-- .../atom-ide-refactor/lib/refactorReducers.js | 255 +- .../atom-ide-refactor/lib/refactorStore.js | 115 +- .../pkg/atom-ide-refactor/lib/refactorUIs.js | 393 +-- .../pkg/atom-ide-refactor/lib/types.js | 383 +-- .../SignatureHelpManager-test.js | 177 +- .../getSignatureDatatip-test.js | 141 +- .../lib/SignatureHelpManager.js | 412 +-- .../lib/getSignatureDatatip.js | 103 +- .../pkg/atom-ide-signature-help/lib/main.js | 71 +- .../pkg/atom-ide-signature-help/lib/types.js | 60 +- .../__atom_tests__/FocusManager-test.js | 85 +- .../__atom_tests__/initial-input-test.js | 52 +- .../__tests__/PtyService-test.js | 58 +- .../__tests__/nuclide-terminal-uri-test.js | 165 +- .../__tests__/shellConfig-test.js | 47 +- .../atom-ide-terminal/__tests__/sink-test.js | 59 +- .../lib/AtomServiceContainer.js | 83 +- .../pkg/atom-ide-terminal/lib/FocusManager.js | 141 +- .../pkg/atom-ide-terminal/lib/config.js | 80 +- .../atom-ide-terminal/lib/createTerminal.js | 209 +- .../pkg/atom-ide-terminal/lib/main.js | 326 ++- .../lib/measure-performance.js | 181 +- .../lib/nuclide-terminal-uri.js | 163 +- .../lib/pty-service/PtyService.js | 278 +- .../lib/pty-service/rpc-types.js | 36 +- .../lib/pty-service/shellConfig.js | 97 +- .../pkg/atom-ide-terminal/lib/sink.js | 60 +- .../atom-ide-terminal/lib/terminal-view.js | 1044 ++++---- .../pkg/atom-ide-terminal/lib/types.js | 46 +- .../pkg/hyperclick/lib/Hyperclick.js | 165 +- .../hyperclick/lib/HyperclickForTextEditor.js | 526 ++-- .../pkg/hyperclick/lib/SuggestionList.js | 77 +- .../hyperclick/lib/SuggestionListElement.js | 250 +- .../atom-ide-ui/pkg/hyperclick/lib/main.js | 84 +- .../lib/showTriggerConflictWarning.js | 81 +- .../atom-ide-ui/pkg/hyperclick/lib/types.js | 50 +- .../pkg/hyperclick/spec/Hyperclick-spec.js | 922 ++++--- .../nuclide-fake-atom-package-1/file.js | 3 +- .../nuclide-fake-atom-package-1/index.js | 3 +- .../nuclide-fake-atom-package-2/file.js | 3 +- .../nuclide-fake-atom-package-2/index.js | 3 +- .../nuclide-fake-node-apm-package-1/file.js | 3 +- .../nuclide-fake-node-apm-package-1/index.js | 3 +- .../nuclide-fake-node-apm-package-2/file.js | 3 +- .../nuclide-fake-node-apm-package-2/index.js | 3 +- .../nuclide-fake-node-npm-package-1/file.js | 3 +- .../nuclide-fake-node-npm-package-1/index.js | 3 +- .../nuclide-fake-node-npm-package-2/file.js | 3 +- .../nuclide-fake-node-npm-package-2/index.js | 3 +- .../jest-atom-runner/src/AtomTestWorker.js | 225 +- .../src/AtomTestWorkerFarm.js | 95 +- .../jest-atom-runner/src/atomTestRunner.js | 202 +- modules/jest-atom-runner/src/environment.js | 41 +- modules/jest-atom-runner/src/index.js | 97 +- modules/jest-atom-runner/src/ipc-client.js | 83 +- modules/jest-atom-runner/src/ipc-server.js | 58 +- modules/jest-atom-runner/src/types.js | 68 +- modules/jest-atom-runner/src/utils.js | 139 +- .../__tests__/parsePsOutput-test.js | 59 +- modules/nuclide-adb/lib/Adb.js | 574 ++-- modules/nuclide-adb/lib/AdbDevicePoller.js | 201 +- modules/nuclide-adb/lib/AdbService.js | 303 +-- modules/nuclide-adb/lib/Tunneling.js | 377 +-- modules/nuclide-adb/lib/common/Processes.js | 250 +- modules/nuclide-adb/lib/common/ps.js | 21 +- modules/nuclide-adb/lib/main.js | 57 +- modules/nuclide-adb/lib/types.js | 75 +- modules/nuclide-adb/lib/utils.js | 69 +- .../ActiveEditorRegistry.js | 320 +-- modules/nuclide-commons-atom/ContextMenu.js | 197 +- modules/nuclide-commons-atom/FeatureLoader.js | 610 +++-- .../nuclide-commons-atom/FocusBoomerang.js | 36 +- .../nuclide-commons-atom/ProjectManager.js | 328 +-- modules/nuclide-commons-atom/ProjectUtils.js | 118 +- .../nuclide-commons-atom/ProviderRegistry.js | 68 +- .../ActiveEditorRegistry-test.js | 225 +- .../__atom_tests__/ContextMenu-test.js | 495 ++-- .../__atom_tests__/FeatureLoader-test.js | 119 +- .../__atom_tests__/debounced-test.js | 256 +- .../__atom_tests__/feature-config-test.js | 84 +- .../__atom_tests__/go-to-location-test.js | 159 +- .../observe-grammar-for-text-editors-test.js | 122 +- .../__atom_tests__/projects-test.js | 113 +- .../__atom_tests__/range-test.js | 30 +- .../__atom_tests__/text-edit-diff-test.js | 156 +- .../__atom_tests__/text-edit-test.js | 146 +- .../__atom_tests__/text-editor-test.js | 91 +- .../__atom_tests__/text-event-test.js | 159 +- .../__tests__/ProjectManager-test.js | 64 +- .../__tests__/ProjectUtils-test.js | 30 +- .../__tests__/ProviderRegistry-test.js | 62 +- .../__tests__/createPackage-test.js | 65 +- .../__tests__/humanizePath-test.js | 67 +- .../consumeFirstProvider.js | 33 +- .../create-pane-container.js | 21 +- modules/nuclide-commons-atom/createPackage.js | 75 +- modules/nuclide-commons-atom/debounced.js | 129 +- modules/nuclide-commons-atom/debugger.js | 32 +- .../nuclide-commons-atom/destroyItemWhere.js | 16 +- .../nuclide-commons-atom/dock-for-location.js | 17 +- .../experimental-packages/MessageRouter.js | 153 +- .../experimental-packages/PackageRunners.js | 206 +- .../experimental-packages/activatePackage.js | 50 +- .../experimental-packages/activatePackages.js | 219 +- .../experimental-packages/run-package.js | 103 +- .../experimental-packages/types.js | 85 +- .../nuclide-commons-atom/feature-config.js | 164 +- .../findKeyBindingsForCommand.js | 40 +- .../getElementFilePath.js | 47 +- .../getFragmentGrammar.js | 19 +- .../nuclide-commons-atom/go-to-location.js | 118 +- modules/nuclide-commons-atom/humanizePath.js | 79 +- .../nuclide-commons-atom/mouse-to-position.js | 31 +- .../observe-grammar-for-text-editors.js | 79 +- .../observePaneItemVisibility.js | 242 +- modules/nuclide-commons-atom/pane-item.js | 41 +- modules/nuclide-commons-atom/parseOpenable.js | 25 +- modules/nuclide-commons-atom/projects.js | 187 +- modules/nuclide-commons-atom/range.js | 107 +- .../reduxLoggerMiddleware.js | 75 +- modules/nuclide-commons-atom/test-helpers.js | 51 +- .../nuclide-commons-atom/text-edit-diff.js | 120 +- modules/nuclide-commons-atom/text-edit.js | 151 +- modules/nuclide-commons-atom/text-editor.js | 200 +- modules/nuclide-commons-atom/text-event.js | 332 +-- modules/nuclide-commons-atom/types.js | 16 +- modules/nuclide-commons-ui/Ansi.js | 110 +- modules/nuclide-commons-ui/AtomInput.js | 389 ++- modules/nuclide-commons-ui/AtomTextEditor.js | 309 +-- modules/nuclide-commons-ui/Atomicon.js | 81 +- modules/nuclide-commons-ui/Block.js | 29 +- .../BoundSettingsControl.js | 99 +- modules/nuclide-commons-ui/Button.example.js | 305 ++- modules/nuclide-commons-ui/Button.js | 161 +- modules/nuclide-commons-ui/ButtonGroup.js | 73 +- modules/nuclide-commons-ui/ButtonToolbar.js | 49 +- .../nuclide-commons-ui/Checkbox.example.js | 127 +- modules/nuclide-commons-ui/Checkbox.js | 170 +- .../ClickOutsideBoundary.js | 105 +- modules/nuclide-commons-ui/CodeSnippet.js | 158 +- .../nuclide-commons-ui/DragResizeContainer.js | 196 +- .../nuclide-commons-ui/Dropdown.example.js | 206 +- modules/nuclide-commons-ui/Dropdown.js | 323 ++- modules/nuclide-commons-ui/EmptyState.js | 41 +- .../nuclide-commons-ui/FileChanges.example.js | 72 +- modules/nuclide-commons-ui/FileChanges.js | 517 ++-- modules/nuclide-commons-ui/HR.js | 20 +- .../nuclide-commons-ui/Highlight.example.js | 82 +- modules/nuclide-commons-ui/Highlight.js | 84 +- .../HighlightedCode.example.js | 129 +- modules/nuclide-commons-ui/HighlightedCode.js | 236 +- modules/nuclide-commons-ui/HighlightedText.js | 140 +- modules/nuclide-commons-ui/Icon.example.js | 98 +- modules/nuclide-commons-ui/Icon.js | 114 +- modules/nuclide-commons-ui/Keybinding.js | 45 +- .../LazyNestedValueComponent.js | 815 +++--- .../nuclide-commons-ui/ListView.example.js | 259 +- modules/nuclide-commons-ui/ListView.js | 155 +- modules/nuclide-commons-ui/LoadingSpinner.js | 118 +- .../nuclide-commons-ui/MeasuredComponent.js | 102 +- modules/nuclide-commons-ui/Message.example.js | 93 +- modules/nuclide-commons-ui/Message.js | 65 +- modules/nuclide-commons-ui/Modal.js | 166 +- .../nuclide-commons-ui/ModalMultiSelect.js | 312 ++- modules/nuclide-commons-ui/MultiSelectList.js | 227 +- modules/nuclide-commons-ui/NuclideLogo.js | 53 +- .../PanelComponentScroller.js | 78 +- .../PathWithFileIcon.example.js | 177 +- .../nuclide-commons-ui/PathWithFileIcon.js | 311 +-- modules/nuclide-commons-ui/ProgressBar.js | 35 +- .../ProgressIndicators.example.js | 123 +- .../nuclide-commons-ui/PulseButton.example.js | 91 +- modules/nuclide-commons-ui/PulseButton.js | 92 +- .../PulseButtonWithTooltip.js | 193 +- .../nuclide-commons-ui/RadioGroup.example.js | 89 +- modules/nuclide-commons-ui/RadioGroup.js | 90 +- .../ReactMountRootElement.js | 50 +- modules/nuclide-commons-ui/ReadOnlyNotice.js | 98 +- .../RegExpFilter.example.js | 114 +- modules/nuclide-commons-ui/RegExpFilter.js | 253 +- .../ResizeSensitiveContainer.js | 312 +-- modules/nuclide-commons-ui/Section.js | 151 +- modules/nuclide-commons-ui/SelectableTree.js | 842 +++--- .../nuclide-commons-ui/SettingsCheckbox.js | 81 +- .../nuclide-commons-ui/SettingsColorInput.js | 88 +- modules/nuclide-commons-ui/SettingsControl.js | 190 +- modules/nuclide-commons-ui/SettingsInput.js | 206 +- modules/nuclide-commons-ui/SettingsSelect.js | 107 +- modules/nuclide-commons-ui/SettingsUtils.js | 64 +- .../SimpleValueComponent.js | 196 +- .../nuclide-commons-ui/SplitButtonDropdown.js | 188 +- modules/nuclide-commons-ui/StyleSheet.js | 41 +- .../TabbableContainer.example.js | 214 +- .../nuclide-commons-ui/TabbableContainer.js | 280 +- modules/nuclide-commons-ui/Table.example.js | 328 ++- modules/nuclide-commons-ui/Table.js | 1049 ++++---- modules/nuclide-commons-ui/Tabs.example.js | 143 +- modules/nuclide-commons-ui/Tabs.js | 159 +- .../nuclide-commons-ui/TextEditorBanner.js | 183 +- .../nuclide-commons-ui/TextInputs.example.js | 212 +- modules/nuclide-commons-ui/TextRenderer.js | 39 +- modules/nuclide-commons-ui/Toggle.js | 125 +- modules/nuclide-commons-ui/Toolbar.example.js | 185 +- modules/nuclide-commons-ui/Toolbar.js | 69 +- modules/nuclide-commons-ui/ToolbarCenter.js | 31 +- modules/nuclide-commons-ui/ToolbarLeft.js | 31 +- modules/nuclide-commons-ui/ToolbarRight.js | 31 +- modules/nuclide-commons-ui/Tree.example.js | 174 +- modules/nuclide-commons-ui/Tree.js | 304 +-- modules/nuclide-commons-ui/TruncatedButton.js | 90 +- modules/nuclide-commons-ui/UnstyledButton.js | 89 +- .../ValueComponentClassNames.js | 21 +- modules/nuclide-commons-ui/View.js | 58 +- .../__atom_tests__/AtomInput-test.js | 97 +- .../__atom_tests__/AtomTextEditor-test.js | 185 +- .../__atom_tests__/Checkbox-test.js | 82 +- .../ClickOutsideBoundry-test.js | 148 +- .../__atom_tests__/Dropdown-test.js | 70 +- .../__atom_tests__/HighlightedCode-test.js | 185 +- .../__atom_tests__/NuclideRadioGroup-test.js | 117 +- .../ReactMountRootElement-test.js | 16 +- .../__atom_tests__/Table-test.js | 158 +- .../__atom_tests__/Tabs-test.js | 85 +- .../__atom_tests__/highlightText-test.js | 45 +- .../__atom_tests__/observable-dom-test.js | 262 +- .../__atom_tests__/openPreview-test.js | 210 +- modules/nuclide-commons-ui/addTooltip.js | 84 +- .../bindObservableAsProps.js | 44 +- .../nuclide-commons-ui/highlightOnUpdate.js | 88 +- modules/nuclide-commons-ui/highlightText.js | 30 +- .../ignoreTextSelectionEvents.js | 20 +- modules/nuclide-commons-ui/index.js | 86 +- modules/nuclide-commons-ui/observable-dom.js | 348 ++- modules/nuclide-commons-ui/openPreview.js | 169 +- modules/nuclide-commons-ui/renderReactRoot.js | 34 +- modules/nuclide-commons-ui/scrollIntoView.js | 35 +- .../nuclide-commons-ui/showModal.example.js | 73 +- modules/nuclide-commons-ui/showModal.js | 228 +- modules/nuclide-commons/AbortController.js | 63 +- .../nuclide-commons/BatchProcessedQueue.js | 35 +- modules/nuclide-commons/ConfigCache.js | 151 +- modules/nuclide-commons/Hasher.js | 49 +- modules/nuclide-commons/Model.js | 54 +- modules/nuclide-commons/ObservablePool.js | 145 +- .../SafeStreamMessageReader.js | 32 +- modules/nuclide-commons/SimpleCache.js | 61 +- .../nuclide-commons/UniversalDisposable.js | 80 +- .../symbol-definition-preview-sample.js | 40 +- .../__mocks__/fixtures/throw.js | 7 +- .../__mocks__/fixtures/toBeMocked.js | 14 +- .../__mocks__/fixtures/toBeTested.js | 28 +- .../__tests__/AbortController-test.js | 34 +- .../__tests__/BatchProcessedQueue-test.js | 27 +- .../__tests__/ConfigCache-test.js | 113 +- .../nuclide-commons/__tests__/Hasher-test.js | 35 +- .../nuclide-commons/__tests__/Model-test.js | 67 +- .../__tests__/ObservablePool-test.js | 144 +- .../__tests__/SafeStreamMessageReader-test.js | 62 +- .../__tests__/UniversalDisposable-test.js | 158 +- .../nuclide-commons/__tests__/cache-test.js | 62 +- .../__tests__/collection-test.js | 624 ++--- .../__tests__/debounce-test.js | 60 +- .../nuclide-commons/__tests__/event-test.js | 71 +- .../__tests__/fsPromise-test.js | 309 ++- .../__tests__/humanizeKeystroke-test.js | 123 +- .../__tests__/matchIndexesToRanges-test.js | 34 +- .../__tests__/memoizeUntilChanged-test.js | 45 +- .../nuclide-commons/__tests__/nice-test.js | 82 +- .../__tests__/nuclideUri-test.js | 1084 +++----- .../__tests__/observable-test.js | 642 ++--- .../nuclide-commons/__tests__/process-test.js | 774 +++--- .../nuclide-commons/__tests__/promise-test.js | 504 ++-- .../nuclide-commons/__tests__/range-test.js | 56 +- .../__tests__/sanitizeHtml-test.js | 50 +- .../__tests__/shell-quote-test.js | 57 +- .../nuclide-commons/__tests__/stream-test.js | 82 +- .../nuclide-commons/__tests__/string-test.js | 354 ++- .../symbol-definition-preview-test.js | 236 +- .../__tests__/test-helpers-test.js | 213 +- .../nuclide-commons/__tests__/which-test.js | 74 +- modules/nuclide-commons/analytics.js | 231 +- modules/nuclide-commons/cache.js | 84 +- modules/nuclide-commons/collection.js | 405 +-- modules/nuclide-commons/debounce.js | 49 +- modules/nuclide-commons/event.js | 46 +- modules/nuclide-commons/expected.js | 88 +- modules/nuclide-commons/fsPromise.js | 464 ++-- modules/nuclide-commons/humanizeKeystroke.js | 67 +- .../nuclide-commons/matchIndexesToRanges.js | 24 +- .../nuclide-commons/memoizeUntilChanged.js | 81 +- modules/nuclide-commons/menuUtils.js | 47 +- modules/nuclide-commons/nice.js | 103 +- modules/nuclide-commons/nuclideUri.js | 704 +++-- modules/nuclide-commons/observable.js | 593 ++--- modules/nuclide-commons/package.js | 35 +- modules/nuclide-commons/path-uri.js | 29 +- modules/nuclide-commons/performanceNow.js | 20 +- modules/nuclide-commons/process.js | 1163 ++++----- modules/nuclide-commons/promise.js | 366 ++- modules/nuclide-commons/range.js | 68 +- modules/nuclide-commons/redux-observable.js | 79 +- modules/nuclide-commons/sanitizeHtml.js | 53 +- modules/nuclide-commons/serverPort.js | 27 +- modules/nuclide-commons/stream.js | 104 +- modules/nuclide-commons/string.js | 244 +- .../symbol-definition-preview.js | 138 +- modules/nuclide-commons/test-helpers.js | 220 +- modules/nuclide-commons/tokenized-text.js | 63 +- modules/nuclide-commons/which.js | 64 +- .../AdbDeviceSelector.js | 180 +- .../AutoGenLaunchAttachProvider.js | 139 +- .../AutoGenLaunchAttachUiComponent.js | 1013 ++++---- .../DebuggerConfigSerializer.js | 44 +- .../DebuggerLaunchAttachProvider.js | 54 +- .../DeviceAndPackage.js | 197 +- .../DeviceAndProcess.js | 460 ++-- .../SelectableFilterableProcessTable.js | 409 ++- .../nuclide-debugger-common/SourceSelector.js | 220 +- modules/nuclide-debugger-common/V8Protocol.js | 126 +- .../VSCodeDebuggerAdapterService.js | 133 +- .../VsAdapterSpawner.js | 88 +- .../nuclide-debugger-common/VsDebugSession.js | 635 +++-- .../nuclide-debugger-common/autogen-utils.js | 157 +- modules/nuclide-debugger-common/constants.js | 28 +- .../debug-adapter-service.js | 51 +- .../debugger-registry.js | 291 +-- modules/nuclide-debugger-common/main.js | 195 +- modules/nuclide-debugger-common/processors.js | 92 +- modules/nuclide-debugger-common/types.js | 339 +-- modules/nuclide-debugger-common/utils.js | 64 +- .../__tests__/FallbackMatcher-test.js | 68 +- .../__tests__/QueryItem-test.js | 151 +- .../__tests__/TopScores-test.js | 140 +- .../__tests__/fuzzy-native-test.js | 17 +- .../__tests__/utils-test.js | 105 +- .../lib/FallbackMatcher.js | 66 +- modules/nuclide-fuzzy-native/lib/QueryItem.js | 118 +- .../nuclide-fuzzy-native/lib/QueryScore.js | 18 +- modules/nuclide-fuzzy-native/lib/TopScores.js | 69 +- modules/nuclide-fuzzy-native/lib/main.js | 33 +- modules/nuclide-fuzzy-native/lib/utils.js | 35 +- modules/nuclide-jasmine/lib/faketimer.js | 47 +- modules/nuclide-jasmine/lib/focused.js | 67 +- modules/nuclide-jasmine/lib/unspy.js | 13 +- .../nuclide-jasmine/lib/waitsForPromise.js | 79 +- modules/nuclide-jest/atom-reporter.js | 110 +- modules/nuclide-jest/emptyObject.js | 7 +- modules/nuclide-jest/frontend/Jest.js | 244 +- modules/nuclide-jest/types.js | 142 +- .../__mocks__/fixtures/modern-syntax.js | 15 +- package.json | 3 +- 523 files changed, 49551 insertions(+), 46456 deletions(-) delete mode 100644 DEVELOPMENT diff --git a/DEVELOPMENT b/DEVELOPMENT deleted file mode 100644 index e69de29b..00000000 diff --git a/flow-libs/nuclide.flow.js b/flow-libs/nuclide.flow.js index 3d5be7f4..9a390c31 100644 --- a/flow-libs/nuclide.flow.js +++ b/flow-libs/nuclide.flow.js @@ -1,22 +1 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the license found in the LICENSE file in - * the root directory of this source tree. - * - * @flow strict - * @format - */ - -/* eslint-disable no-undef */ - -declare interface nuclide$CwdApi { - setCwd(path: string): void; - observeCwd(callback: (path: ?string) => void): IDisposable; - getCwd(): ?string; -} - -declare interface nuclide$RpcService { - getServiceByNuclideUri(serviceName: string, uri: ?string): any; -} +"use strict"; \ No newline at end of file diff --git a/index.js b/index.js index 81907b42..1735ad0e 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,41 @@ +"use strict"; + +var _fs = _interopRequireDefault(require("fs")); + +var _path = _interopRequireDefault(require("path")); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("./modules/nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _FeatureLoader() { + const data = _interopRequireDefault(require("./modules/nuclide-commons-atom/FeatureLoader")); + + _FeatureLoader = function () { + return data; + }; + + return data; +} + +function _displayNuclideWarning() { + const data = _interopRequireDefault(require("./display-nuclide-warning")); + + _displayNuclideWarning = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,75 +44,67 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-disable nuclide-internal/no-commonjs */ - -import fs from 'fs'; // eslint-disable-next-line nuclide-internal/prefer-nuclide-uri -import path from 'path'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import FeatureLoader from 'nuclide-commons-atom/FeatureLoader'; -import displayNuclideWarning from './display-nuclide-warning'; - -const featureDir = path.join(__dirname, 'modules/atom-ide-ui/pkg'); -const features = fs - .readdirSync(featureDir) - .map(item => { - const dirname = path.join(featureDir, item); - try { - const pkgJson = fs.readFileSync( - path.join(dirname, 'package.json'), - 'utf8', - ); - return { - path: dirname, - pkg: JSON.parse(pkgJson), - }; - } catch (err) { - if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { - throw err; - } - } - }) - .filter(Boolean); +const featureDir = _path.default.join(__dirname, 'modules/atom-ide-ui/pkg'); + +const features = _fs.default.readdirSync(featureDir).map(item => { + const dirname = _path.default.join(featureDir, item); + try { + const pkgJson = _fs.default.readFileSync(_path.default.join(dirname, 'package.json'), 'utf8'); + + return { + path: dirname, + pkg: JSON.parse(pkgJson) + }; + } catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + throw err; + } + } +}).filter(Boolean); /** * Use a unified package loader to load all the feature packages. * See the following post for more context: * https://nuclide.io/blog/2016/01/13/Nuclide-v0.111.0-The-Unified-Package/ */ -let disposables: ?UniversalDisposable; -const featureLoader = new FeatureLoader({ + + +let disposables; +const featureLoader = new (_FeatureLoader().default)({ path: __dirname, config: {}, - features, + features }); featureLoader.load(); - module.exports = { config: featureLoader.getConfig(), + activate() { - disposables = new UniversalDisposable( - require('nuclide-commons-ui'), - atom.packages.onDidActivatePackage(pkg => { - if (pkg.name === 'nuclide') { - displayNuclideWarning(); - } - }), - ); + disposables = new (_UniversalDisposable().default)(require("./modules/nuclide-commons-ui"), atom.packages.onDidActivatePackage(pkg => { + if (pkg.name === 'nuclide') { + (0, _displayNuclideWarning().default)(); + } + })); featureLoader.activate(); }, + deactivate() { featureLoader.deactivate(); + if (disposables != null) { disposables.dispose(); disposables = null; } }, + serialize() { featureLoader.serialize(); - }, -}; + } + +}; \ No newline at end of file diff --git a/modules/atom-ide-ui/__atom_tests__/empty-test.js b/modules/atom-ide-ui/__atom_tests__/empty-test.js index 4a1b36bf..1530f1e6 100644 --- a/modules/atom-ide-ui/__atom_tests__/empty-test.js +++ b/modules/atom-ide-ui/__atom_tests__/empty-test.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,10 +8,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - test('test', () => { expect(2).toMatchSnapshot(); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/__tests__/empty-test.js b/modules/atom-ide-ui/__tests__/empty-test.js index 88e4f576..e4c58853 100644 --- a/modules/atom-ide-ui/__tests__/empty-test.js +++ b/modules/atom-ide-ui/__tests__/empty-test.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,10 +8,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - test('test', () => { expect(1).toMatchSnapshot(); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/index.js b/modules/atom-ide-ui/index.js index 454bdbf8..f66bb918 100644 --- a/modules/atom-ide-ui/index.js +++ b/modules/atom-ide-ui/index.js @@ -1,134 +1,21 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local - * @format - */ - -export type { - BusySignalOptions, - BusySignalService, -} from './pkg/atom-ide-busy-signal/lib/types'; - -export type { - CodeAction, - CodeActionProvider, -} from './pkg/atom-ide-code-actions/lib/types'; - -export type { - CodeFormatProvider, - RangeCodeFormatProvider, - FileCodeFormatProvider, - OnTypeCodeFormatProvider, - OnSaveCodeFormatProvider, -} from './pkg/atom-ide-code-format/lib/types'; - -export type { - CodeHighlightProvider, -} from './pkg/atom-ide-code-highlight/lib/types'; - -export type { - Datatip, - DatatipProvider, - DatatipService, - MarkedString, - ModifierDatatipProvider, - ModifierKey, -} from './pkg/atom-ide-datatip/lib/types'; - -export type { - Definition, - DefinitionProvider, - DefinitionPreviewProvider, - DefinitionQueryResult, -} from './pkg/atom-ide-definitions/lib/types'; - -export type { - CallbackDiagnosticProvider, - DiagnosticFix, - DiagnosticInvalidationCallback, - DiagnosticInvalidationMessage, - DiagnosticMessage, - DiagnosticMessages, - DiagnosticMessageKind, - DiagnosticMessageType, - DiagnosticProvider, - DiagnosticProviderUpdate, - DiagnosticTrace, - DiagnosticUpdateCallback, - IndieLinterDelegate, - LinterMessage, - LinterMessageV1, - LinterMessageV2, - LinterProvider, - LinterTrace, - ObservableDiagnosticProvider, - RegisterIndieLinter, -} from './pkg/atom-ide-diagnostics/lib/types'; - -export type { - FindReferencesProvider, - FindReferencesReturn, - Reference, -} from './pkg/atom-ide-find-references/lib/types'; - -export type { - AvailableRefactoring, - RefactorResponse, - RefactorProvider, - RefactorRequest, -} from './pkg/atom-ide-refactor/lib/types'; - -export type { - Outline, - OutlineProvider, - OutlineTree, - ResultsStreamProvider, -} from './pkg/atom-ide-outline-view/lib/types'; - -export type { - Signature, - SignatureHelp, - SignatureHelpProvider, - SignatureHelpRegistry, - SignatureParameter, -} from './pkg/atom-ide-signature-help/lib/types'; - -export type { - HyperclickProvider, - HyperclickSuggestion, -} from './pkg/hyperclick/lib/types'; - -export type { - ConsoleService, - ConsoleApi, - Level as ConsoleLevel, - Message as ConsoleMessage, - SourceInfo as ConsoleSourceInfo, - OutputProviderStatus, -} from './pkg/atom-ide-console/lib/types'; - -// Deprecated console types. Exported only for legacy users. -export type { - OutputService, - RegisterExecutorFunction, -} from './pkg/atom-ide-console/lib/types'; - -export { - RemoteDebuggerService as DebuggerService, -} from './pkg/atom-ide-debugger/lib/types'; - -export type { - TerminalInfo, - TerminalInstance, - TerminalApi, -} from './pkg/atom-ide-terminal/lib/types'; - -export type { - Command as TerminalCommand, -} from './pkg/atom-ide-terminal/lib/pty-service/rpc-types'; +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "DebuggerService", { + enumerable: true, + get: function () { + return _types().RemoteDebuggerService; + } +}); + +function _types() { + const data = require("./pkg/atom-ide-debugger/lib/types"); + + _types = function () { + return data; + }; + + return data; +} \ No newline at end of file diff --git a/modules/atom-ide-ui/jest.config.atom.js b/modules/atom-ide-ui/jest.config.atom.js index 6e7928ce..7c614e96 100644 --- a/modules/atom-ide-ui/jest.config.atom.js +++ b/modules/atom-ide-ui/jest.config.atom.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,17 +8,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-disable nuclide-internal/no-commonjs */ -/* eslint-disable nuclide-internal/modules-dependencies */ -const nodeConfig = require('../../jest/jest.config.atom'); +/* eslint-disable nuclide-internal/modules-dependencies */ +const nodeConfig = require("../../jest/jest.config.atom"); -module.exports = { - ...nodeConfig, +module.exports = Object.assign({}, nodeConfig, { rootDir: './', - roots: [''], -}; + roots: [''] +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/jest.config.js b/modules/atom-ide-ui/jest.config.js index 86dc8edb..a617a1a9 100644 --- a/modules/atom-ide-ui/jest.config.js +++ b/modules/atom-ide-ui/jest.config.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,14 +8,13 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ /* eslint-disable nuclide-internal/no-commonjs */ - module.exports = { rootDir: './', projects: ['/jest.config.atom.js', '/jest.config.node.js'], - testFailureExitCode: 0, -}; + testFailureExitCode: 0 +}; \ No newline at end of file diff --git a/modules/atom-ide-ui/jest.config.node.js b/modules/atom-ide-ui/jest.config.node.js index a49676e2..d8bb8d4d 100644 --- a/modules/atom-ide-ui/jest.config.node.js +++ b/modules/atom-ide-ui/jest.config.node.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,17 +8,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-disable nuclide-internal/no-commonjs */ -/* eslint-disable nuclide-internal/modules-dependencies */ -const nodeConfig = require('../../jest/jest.config.node'); +/* eslint-disable nuclide-internal/modules-dependencies */ +const nodeConfig = require("../../jest/jest.config.node"); -module.exports = { - ...nodeConfig, +module.exports = Object.assign({}, nodeConfig, { rootDir: './', - roots: [''], -}; + roots: [''] +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/__atom_tests__/BusySignalInstance-test.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/__atom_tests__/BusySignalInstance-test.js index 36febd29..d5dd3d48 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/__atom_tests__/BusySignalInstance-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/__atom_tests__/BusySignalInstance-test.js @@ -1,3 +1,47 @@ +"use strict"; + +function _fsPromise() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _MessageStore() { + const data = require("../lib/MessageStore"); + + _MessageStore = function () { + return data; + }; + + return data; +} + +function _BusySignalSingleton() { + const data = _interopRequireDefault(require("../lib/BusySignalSingleton")); + + _BusySignalSingleton = function () { + return data; + }; + + return data; +} + +function _waits_for() { + const data = _interopRequireDefault(require("../../../../../jest/waits_for")); + + _waits_for = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,144 +50,99 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {BusySignalOptions} from '../lib/types'; - -import fsPromise from 'nuclide-commons/fsPromise'; -import {MessageStore} from '../lib/MessageStore'; -import BusySignalSingleton from '../lib/BusySignalSingleton'; -import waitsFor from '../../../../../jest/waits_for'; - describe('BusySignalSingleton', () => { - let messageStore: MessageStore; - let singleton: BusySignalSingleton; - let messages: Array>; - const options: BusySignalOptions = {debounce: false}; - + let messageStore; + let singleton; + let messages; + const options = { + debounce: false + }; beforeEach(() => { - messageStore = new MessageStore(); - singleton = new BusySignalSingleton(messageStore); + messageStore = new (_MessageStore().MessageStore)(); + singleton = new (_BusySignalSingleton().default)(messageStore); messages = []; - messageStore - .getMessageStream() - .skip(1) - .subscribe(elements => { - const strings = [...elements].map(element => { - const titleElement = element.getTitleElement(); - const child = - titleElement != null && titleElement.childNodes.length >= 1 - ? titleElement.childNodes[0] - : {}; - return child.data != null && typeof child.data === 'string' - ? child.data - : ''; - }); - messages.push(strings); + messageStore.getMessageStream().skip(1).subscribe(elements => { + const strings = [...elements].map(element => { + const titleElement = element.getTitleElement(); + const child = titleElement != null && titleElement.childNodes.length >= 1 ? titleElement.childNodes[0] : {}; + return child.data != null && typeof child.data === 'string' ? child.data : ''; }); + messages.push(strings); + }); }); - it('should record messages before and after a call', async () => { expect(messages.length).toBe(0); singleton.reportBusyWhile('foo', () => Promise.resolve(5), options); expect(messages.length).toBe(1); - await waitsFor( - () => messages.length === 2, - 'It should publish a second message', - 100, - ); + await (0, _waits_for().default)(() => messages.length === 2, 'It should publish a second message', 100); }); - it("should send the 'done' message even if the promise rejects", async () => { - singleton - .reportBusyWhile('foo', () => Promise.reject(new Error()), options) - .catch(() => {}); + singleton.reportBusyWhile('foo', () => Promise.reject(new Error()), options).catch(() => {}); expect(messages.length).toBe(1); - await waitsFor( - () => messages.length === 2, - 'It should publish a second message', - 100, - ); + await (0, _waits_for().default)(() => messages.length === 2, 'It should publish a second message', 100); }); - it('should properly display duplicate messages', () => { const dispose1 = singleton.reportBusy('foo', options); expect(messages.length).toBe(1); expect(messages[0]).toEqual(['foo']); - const dispose2 = singleton.reportBusy('foo', options); expect(messages.length).toBe(2); expect(messages[1]).toEqual(['foo', 'foo']); - dispose2.dispose(); expect(messages.length).toBe(3); expect(messages[2]).toEqual(['foo']); - dispose1.dispose(); expect(messages.length).toBe(4); expect(messages[3]).toEqual([]); }); - describe('when onlyForFile is provided', () => { - let editor1: atom$TextEditor = (null: any); - let editor2: atom$TextEditor = (null: any); - let editor3: atom$TextEditor = (null: any); + let editor1 = null; + let editor2 = null; + let editor3 = null; let file2; - beforeEach(async () => { - editor1 = await atom.workspace.open(await fsPromise.tempfile()); - file2 = await fsPromise.tempfile(); + editor1 = await atom.workspace.open((await _fsPromise().default.tempfile())); + file2 = await _fsPromise().default.tempfile(); editor2 = await atom.workspace.open(file2); editor3 = await atom.workspace.open(); }); - afterEach(() => { [editor1, editor2, editor3].forEach(editor => editor.destroy()); }); - it('should only display for the proper text editor', () => { atom.workspace.getActivePane().activateItem(editor1); - - const disposable = singleton.reportBusy('foo', { - onlyForFile: file2, - ...options, - }); + const disposable = singleton.reportBusy('foo', Object.assign({ + onlyForFile: file2 + }, options)); expect(messages).toEqual([]); - atom.workspace.getActivePane().activateItem(editor2); expect(messages.length).toBe(1); expect(messages[0]).toEqual(['foo']); - atom.workspace.getActivePane().activateItem(editor3); expect(messages.length).toBe(2); expect(messages[1]).toEqual([]); - atom.workspace.getActivePane().activateItem(editor2); expect(messages.length).toBe(3); expect(messages[2]).toEqual(['foo']); - disposable.dispose(); expect(messages.length).toBe(4); expect(messages[3]).toEqual([]); }); }); - it('correctly sets revealTooltip when provided', async () => { function getCurrentMessages() { - return messageStore - .getMessageStream() - .take(1) - .toPromise(); + return messageStore.getMessageStream().take(1).toPromise(); } singleton.reportBusy('foo', { debounce: false, - revealTooltip: true, + revealTooltip: true }); const curMessages = await getCurrentMessages(); expect(curMessages.length).toBe(1); expect(curMessages[0].shouldRevealTooltip()).toBe(true); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusyMessageInstance.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusyMessageInstance.js index cec5474a..99be03e3 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusyMessageInstance.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusyMessageInstance.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.BusyMessageInstance = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,36 +25,18 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {BusyMessage} from './types'; - -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -export class BusyMessageInstance { +class BusyMessageInstance { // These things are set at construction-time: - _publishCallback: () => void; - _creationOrder: number; - _waitingFor: 'computer' | 'user'; - _onDidClick: ?() => void; - _disposables: UniversalDisposable; - _titleElement: HTMLElement = document.createElement('span'); // These things might be modified afterwards: - _currentTitle: ?string = null; - _isVisibleForDebounce: boolean = true; - _isVisibleForFile: boolean = true; - _revealTooltip: boolean = false; - - constructor( - publishCallback: () => void, - creationOrder: number, - waitingFor: 'computer' | 'user', - onDidClick: ?() => void, - disposables: UniversalDisposable, - ) { + constructor(publishCallback, creationOrder, waitingFor, onDidClick, disposables) { + this._titleElement = document.createElement('span'); + this._currentTitle = null; + this._isVisibleForDebounce = true; + this._isVisibleForFile = true; + this._revealTooltip = false; this._publishCallback = publishCallback; this._creationOrder = creationOrder; this._waitingFor = waitingFor; @@ -43,75 +44,94 @@ export class BusyMessageInstance { this._disposables = disposables; } - get waitingFor(): 'computer' | 'user' { + get waitingFor() { return this._waitingFor; } - setTitle(val: string): void { - invariant(!this._disposables.disposed); + setTitle(val) { + if (!!this._disposables.disposed) { + throw new Error("Invariant violation: \"!this._disposables.disposed\""); + } + if (this._currentTitle === val) { return; } + this._currentTitle = val; + while (this._titleElement.firstChild != null) { this._titleElement.removeChild(this._titleElement.firstChild); } + if (this._onDidClick == null) { this._titleElement.appendChild(document.createTextNode(val)); } else { const anchor = document.createElement('a'); anchor.onclick = this._onDidClick; anchor.appendChild(document.createTextNode(val)); + this._titleElement.appendChild(anchor); } + if (this.isVisible()) { this._publishCallback(); } } - getTitleElement(): ?HTMLElement { + getTitleElement() { return this._titleElement; } - setIsVisibleForDebounce(val: boolean): void { - invariant(!this._disposables.disposed); + setIsVisibleForDebounce(val) { + if (!!this._disposables.disposed) { + throw new Error("Invariant violation: \"!this._disposables.disposed\""); + } + this._isVisibleForDebounce = val; + this._publishCallback(); } - setIsVisibleForFile(val: boolean): void { - invariant(!this._disposables.disposed); + setIsVisibleForFile(val) { + if (!!this._disposables.disposed) { + throw new Error("Invariant violation: \"!this._disposables.disposed\""); + } + this._isVisibleForFile = val; + this._publishCallback(); } - isVisible(): boolean { - invariant(!this._disposables.disposed); - return ( - this._isVisibleForFile && - this._isVisibleForDebounce && - this._currentTitle != null - ); + isVisible() { + if (!!this._disposables.disposed) { + throw new Error("Invariant violation: \"!this._disposables.disposed\""); + } + + return this._isVisibleForFile && this._isVisibleForDebounce && this._currentTitle != null; } - setRevealTooltip(val: boolean): void { + setRevealTooltip(val) { this._revealTooltip = val; } - shouldRevealTooltip(): boolean { + shouldRevealTooltip() { return this._revealTooltip; } - compare(that: BusyMessageInstance): number { + compare(that) { return this._creationOrder - that._creationOrder; } - dispose(): void { + dispose() { this._disposables.dispose(); + this._currentTitle = null; + this._publishCallback(); } -} -// This is how we declare that a type fulfills an interface in Flow: -(((null: any): BusyMessageInstance): BusyMessage); +} // This is how we declare that a type fulfills an interface in Flow: + + +exports.BusyMessageInstance = BusyMessageInstance; +null; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusySignalSingleton.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusySignalSingleton.js index 8de7bfb6..e6c937af 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusySignalSingleton.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusySignalSingleton.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,26 +13,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {BusySignalOptions, BusyMessage} from './types'; -import type {MessageStore} from './MessageStore'; - -export default class BusySignalSingleton { - _messageStore: MessageStore; - - constructor(messageStore: MessageStore) { +class BusySignalSingleton { + constructor(messageStore) { this._messageStore = messageStore; } dispose() {} - reportBusy(title: string, options?: BusySignalOptions): BusyMessage { + reportBusy(title, options) { return this._messageStore.add(title, options || {}); } - /** * Publishes a 'busy' message with the given string. Marks it as done when the * promise returned by the given function is resolved or rejected. @@ -33,16 +33,18 @@ export default class BusySignalSingleton { * Used to indicate that some work is ongoing while the given asynchronous * function executes. */ - async reportBusyWhile( - title: string, - f: () => Promise, - options?: BusySignalOptions, - ): Promise { + + + async reportBusyWhile(title, f, options) { const busySignal = this.reportBusy(title, options); + try { return await f(); } finally { busySignal.dispose(); } } + } + +exports.default = BusySignalSingleton; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/MessageStore.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/MessageStore.js index 7a54f563..d3cb3aa0 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/MessageStore.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/MessageStore.js @@ -1,3 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.MessageStore = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _BusyMessageInstance() { + const data = require("./BusyMessageInstance"); + + _BusyMessageInstance = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,100 +47,92 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {BusyMessage, BusySignalOptions} from './types'; - -import invariant from 'assert'; -import {Observable, BehaviorSubject} from 'rxjs'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {arrayEqual} from 'nuclide-commons/collection'; -import {BusyMessageInstance} from './BusyMessageInstance'; - // The "busy debounce delay" is for busy messages that were created with the // 'debounce' option set to true. The icon and tooltip message won't appear // until this many milliseconds have elapsed; if the busy message gets disposed // before this time, then the user won't see anything. const BUSY_DEBOUNCE_DELAY = 300; -export class MessageStore { - _counter: number = 0; - _messages: Set = new Set(); - _currentVisibleMessages: Array = []; - _messageStream: BehaviorSubject< - Array, - > = new BehaviorSubject([]); +class MessageStore { + constructor() { + this._counter = 0; + this._messages = new Set(); + this._currentVisibleMessages = []; + this._messageStream = new _RxMin.BehaviorSubject([]); + } - getMessageStream(): Observable> { + getMessageStream() { return this._messageStream; } - dispose(): void { + dispose() { const messagesToDispose = [...this._messages]; + for (const message of messagesToDispose) { message.dispose(); } - invariant(this._messages.size === 0); + + if (!(this._messages.size === 0)) { + throw new Error("Invariant violation: \"this._messages.size === 0\""); + } + this._messageStream.complete(); } - _publish(): void { - const visibleMessages = [...this._messages] - .filter(m => m.isVisible()) - .sort((m1, m2) => m1.compare(m2)); - - // We only send out on messageStream when the list of visible + _publish() { + const visibleMessages = [...this._messages].filter(m => m.isVisible()).sort((m1, m2) => m1.compare(m2)); // We only send out on messageStream when the list of visible // BusyMessageInstance object identities has changed, e.g. when ones // are made visible or invisible or new ones are created. We don't send // out just on title change. - if (!arrayEqual(this._currentVisibleMessages, visibleMessages)) { + + if (!(0, _collection().arrayEqual)(this._currentVisibleMessages, visibleMessages)) { this._messageStream.next(visibleMessages); + this._currentVisibleMessages = visibleMessages; } } - add(title: string, options: BusySignalOptions): BusyMessage { + add(title, options) { this._counter++; - const creationOrder = this._counter; - const waitingFor = - options != null && options.waitingFor != null - ? options.waitingFor - : 'computer'; + const waitingFor = options != null && options.waitingFor != null ? options.waitingFor : 'computer'; const onDidClick = options == null ? null : options.onDidClick; - const messageDisposables = new UniversalDisposable(); - - const message = new BusyMessageInstance( - this._publish.bind(this), - creationOrder, - waitingFor, - onDidClick, - messageDisposables, - ); + const messageDisposables = new (_UniversalDisposable().default)(); + const message = new (_BusyMessageInstance().BusyMessageInstance)(this._publish.bind(this), creationOrder, waitingFor, onDidClick, messageDisposables); + this._messages.add(message); - messageDisposables.add(() => this._messages.delete(message)); - // debounce defaults 'true' for busy-signal, and 'false' for action-required - const debounceRaw: ?boolean = options == null ? null : options.debounce; - const debounce: boolean = - debounceRaw == null ? waitingFor === 'computer' : debounceRaw; + messageDisposables.add(() => this._messages.delete(message)); // debounce defaults 'true' for busy-signal, and 'false' for action-required + + const debounceRaw = options == null ? null : options.debounce; + const debounce = debounceRaw == null ? waitingFor === 'computer' : debounceRaw; + if (debounce) { - message.setIsVisibleForDebounce(false); - // After the debounce time, we'll check whether the messageId is still + message.setIsVisibleForDebounce(false); // After the debounce time, we'll check whether the messageId is still // around (i.e. hasn't yet been disposed), and if so we'll display it. - let timeoutId = ((0: any): TimeoutID); + + let timeoutId = 0; + const teardown = () => clearTimeout(timeoutId); + timeoutId = setTimeout(() => { - invariant(!messageDisposables.disposed); - invariant(this._messages.has(message)); - // If the message was disposed, then it should have already called + if (!!messageDisposables.disposed) { + throw new Error("Invariant violation: \"!messageDisposables.disposed\""); + } + + if (!this._messages.has(message)) { + throw new Error("Invariant violation: \"this._messages.has(message)\""); + } // If the message was disposed, then it should have already called // clearTimeout, so this timeout handler shouldn't have been invoked. // And also the message should have been removed from this._messages. // So both tests above should necessary fail. // If the messageStore was disposed, then every message in it should // already have been disposed, as per above. + + messageDisposables.remove(teardown); message.setIsVisibleForDebounce(true); }, BUSY_DEBOUNCE_DELAY); @@ -110,10 +143,7 @@ export class MessageStore { message.setIsVisibleForFile(false); const file = options.onlyForFile; const teardown = atom.workspace.observeActivePaneItem(item => { - const activePane = - item != null && typeof item.getPath === 'function' - ? String(item.getPath()) - : null; + const activePane = item != null && typeof item.getPath === 'function' ? String(item.getPath()) : null; const newVisible = activePane === file; message.setIsVisibleForFile(newVisible); }); @@ -124,12 +154,13 @@ export class MessageStore { message.setRevealTooltip(true); } - message.setTitle(title); - - // Quick note that there aren't races in the above code! 'message' will not + message.setTitle(title); // Quick note that there aren't races in the above code! 'message' will not // be displayed until it has a title. So we can set visibility all we like, // and then when the title is set, it will display or not as appropriate. return message; } + } + +exports.MessageStore = MessageStore; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/StatusBarTile.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/StatusBarTile.js index 553e662c..a2ea9bc6 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/StatusBarTile.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/StatusBarTile.js @@ -1,3 +1,58 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _Icon() { + const data = require("../../../../nuclide-commons-ui/Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _BusyMessageInstance() { + const data = require("./BusyMessageInstance"); + + _BusyMessageInstance = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,125 +61,104 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {Observable} from 'rxjs'; - -import invariant from 'assert'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {arrayCompact} from 'nuclide-commons/collection'; -import {Icon} from 'nuclide-commons-ui/Icon'; -import {BusyMessageInstance} from './BusyMessageInstance'; - // We want to be the furthest left on the right side of the status bar so as not to leave a // conspicuous gap (or cause jitter) when nothing is busy. const STATUS_BAR_PRIORITY = 1000; -type Props = { - waitingForComputer: boolean, - waitingForUser: boolean, - onDidClick: ?() => void, -}; - -function StatusBarTileComponent(props: Props) { +function StatusBarTileComponent(props) { let element; + if (props.waitingForUser) { - element = ; + element = React.createElement(_Icon().Icon, { + className: "busy-signal-status-bar", + icon: "unverified" + }); } else if (props.waitingForComputer) { - element =
; + element = React.createElement("div", { + className: "busy-signal-status-bar loading-spinner-tiny" + }); } else { element = null; } if (props.onDidClick != null) { - element = {element}; + element = React.createElement("a", { + onClick: props.onDidClick + }, element); } return element; } -export default class StatusBarTile { - _item: HTMLElement; - _tile: atom$StatusBarTile; - _tooltip: ?IDisposable; - _disposables: UniversalDisposable; - _messages: Array = []; - _isMouseOverItem: boolean = false; - _isMouseOverTooltip: number = 0; - _leaveTimeoutId: ?TimeoutID; - - constructor( - statusBar: atom$StatusBar, - messageStream: Observable>, - ) { +class StatusBarTile { + constructor(statusBar, messageStream) { + this._messages = []; + this._isMouseOverItem = false; + this._isMouseOverTooltip = 0; this._item = document.createElement('div'); this._tile = this._createTile(statusBar); - this._disposables = new UniversalDisposable( - messageStream.subscribe(messages => this._handleMessages(messages)), - ); + this._disposables = new (_UniversalDisposable().default)(messageStream.subscribe(messages => this._handleMessages(messages))); } - dispose(): void { - ReactDOM.unmountComponentAtNode(this._item); + dispose() { + _reactDom.default.unmountComponentAtNode(this._item); + this._tile.destroy(); + if (this._tooltip != null) { this._tooltip.dispose(); } + this._disposables.dispose(); } - _createTile(statusBar: atom$StatusBar): atom$StatusBarTile { + _createTile(statusBar) { const item = this._item; item.className = 'inline-block'; item.addEventListener('mouseenter', () => { this._isMouseOverItem = true; + this._stopLeaveTimeout(); + this._ensureTooltip(); }); item.addEventListener('mouseleave', () => { this._isMouseOverItem = false; + this._startLeaveTimeoutIfNecessary(); }); const tile = statusBar.addRightTile({ item, - priority: STATUS_BAR_PRIORITY, + priority: STATUS_BAR_PRIORITY }); return tile; } - _handleMessages(messages: Array): void { + _handleMessages(messages) { this._messages = messages; - - const onDidClicks = arrayCompact(messages.map(m => m._onDidClick)); - - const props: Props = { + const onDidClicks = (0, _collection().arrayCompact)(messages.map(m => m._onDidClick)); + const props = { waitingForComputer: messages.some(m => m.waitingFor === 'computer'), waitingForUser: messages.some(m => m.waitingFor === 'user'), - onDidClick: - onDidClicks.length > 0 - ? () => onDidClicks.forEach(callback => callback()) - : null, + onDidClick: onDidClicks.length > 0 ? () => onDidClicks.forEach(callback => callback()) : null }; - ReactDOM.render(, this._item); - const revealTooltip = messages.some(message => - message.shouldRevealTooltip(), - ); + _reactDom.default.render(React.createElement(StatusBarTileComponent, props), this._item); + + const revealTooltip = messages.some(message => message.shouldRevealTooltip()); + if (this._tooltip != null) { // If the user already had the tooltip up, then we'll either // refresh it or hide it. No matter what, we'll have to unmount it. - this._disposeTooltip(); - // There are two reasons to refresh the tooltip (bringing it back): + this._disposeTooltip(); // There are two reasons to refresh the tooltip (bringing it back): // 1) the mouse was previously over the tile or the tooltip // 2) one of the messages is marked with 'reveal tooltip' - if ( - messages.length > 0 && - (revealTooltip || this._isMouseOverItem || this._isMouseOverTooltip) - ) { + + + if (messages.length > 0 && (revealTooltip || this._isMouseOverItem || this._isMouseOverTooltip)) { this._ensureTooltip(); } else { this._isMouseOverItem = false; @@ -134,67 +168,78 @@ export default class StatusBarTile { } } - _disposeTooltip(): void { + _disposeTooltip() { if (this._tooltip != null) { this._tooltip.dispose(); + this._tooltip = null; this._isMouseOverTooltip = 0; } } - _ensureTooltip(): void { + _ensureTooltip() { if (this._tooltip != null) { return; } + const body = document.createElement('div'); + for (const message of this._messages) { if (body.childElementCount > 0) { body.appendChild(document.createElement('br')); } + const titleElement = message.getTitleElement(); - invariant(titleElement != null); + + if (!(titleElement != null)) { + throw new Error("Invariant violation: \"titleElement != null\""); + } + body.appendChild(titleElement); } this._tooltip = atom.tooltips.add(this._item, { item: body, delay: 0, - trigger: 'manual', + trigger: 'manual' }); const tooltipAtomObjects = atom.tooltips.tooltips.get(this._item); + if (tooltipAtomObjects != null) { for (const tooltipAtomObject of tooltipAtomObjects) { const div = tooltipAtomObject.getTooltipElement(); div.addEventListener('mouseenter', () => { this._isMouseOverTooltip++; + this._stopLeaveTimeout(); }); div.addEventListener('mouseleave', () => { this._isMouseOverTooltip--; + this._startLeaveTimeoutIfNecessary(); }); } } } - _startLeaveTimeoutIfNecessary(): void { - if ( - !this._isMouseOverItem && - this._isMouseOverTooltip === 0 && - this._leaveTimeoutId == null - ) { + _startLeaveTimeoutIfNecessary() { + if (!this._isMouseOverItem && this._isMouseOverTooltip === 0 && this._leaveTimeoutId == null) { this._leaveTimeoutId = setTimeout(() => { - this._disposeTooltip(); - // Currently visible messages should no longer reveal the tooltip again. + this._disposeTooltip(); // Currently visible messages should no longer reveal the tooltip again. + + this._messages.forEach(message => message.setRevealTooltip(false)); }, 200); } } - _stopLeaveTimeout(): void { + _stopLeaveTimeout() { if (this._leaveTimeoutId != null) { clearTimeout(this._leaveTimeoutId); this._leaveTimeoutId = null; } } + } + +exports.default = StatusBarTile; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/main.js index 698cadaf..b3e838e0 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/main.js @@ -1,3 +1,57 @@ +"use strict"; + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _BusySignalSingleton() { + const data = _interopRequireDefault(require("./BusySignalSingleton")); + + _BusySignalSingleton = function () { + return data; + }; + + return data; +} + +function _MessageStore() { + const data = require("./MessageStore"); + + _MessageStore = function () { + return data; + }; + + return data; +} + +function _StatusBarTile() { + const data = _interopRequireDefault(require("./StatusBarTile")); + + _StatusBarTile = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,45 +60,33 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {BusySignalService} from './types'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import BusySignalSingleton from './BusySignalSingleton'; -import {MessageStore} from './MessageStore'; -import StatusBarTile from './StatusBarTile'; - class Activation { - _disposables: UniversalDisposable; - _service: BusySignalService; - _messageStore: MessageStore; - constructor() { - this._messageStore = new MessageStore(); - this._service = new BusySignalSingleton(this._messageStore); - this._disposables = new UniversalDisposable(this._messageStore); + this._messageStore = new (_MessageStore().MessageStore)(); + this._service = new (_BusySignalSingleton().default)(this._messageStore); + this._disposables = new (_UniversalDisposable().default)(this._messageStore); } dispose() { this._disposables.dispose(); } - consumeStatusBar(statusBar: atom$StatusBar): IDisposable { + consumeStatusBar(statusBar) { // Avoid retaining StatusBarTile by wrapping it. - const disposable = new UniversalDisposable( - new StatusBarTile(statusBar, this._messageStore.getMessageStream()), - ); + const disposable = new (_UniversalDisposable().default)(new (_StatusBarTile().default)(statusBar, this._messageStore.getMessageStream())); + this._disposables.add(disposable); + return disposable; } - provideBusySignal(): BusySignalService { + provideBusySignal() { return this._service; } + } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/types.js index 38a0a1cd..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/types.js @@ -1,57 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -export type BusySignalOptions = {| - // Can say that a busy signal will only appear when a given file is open. - // Default = null, meaning the busy signal applies to all files. - onlyForFile?: NuclideUri, - // Is user waiting for computer to finish a task? (traditional busy spinner) - // or is the computer waiting for user to finish a task? (action required) - // Default = spinner. - waitingFor?: 'computer' | 'user', - // Debounce it? default = true for busy-signal, and false for action-required. - debounce?: boolean, - // If onClick is set, then the tooltip will be clickable. Default = null. - onDidClick?: () => void, - // If set to true, the busy signal tooltip will be immediately revealed - // when it first becomes visible (without explicit mouse interaction). - revealTooltip?: boolean, -|}; - -export type BusySignalService = { - // Activates the busy signal with the given title and returns the promise - // from the provided callback. - // The busy signal automatically deactivates when the returned promise - // either resolves or rejects. - reportBusyWhile( - title: string, - f: () => Promise, - options?: BusySignalOptions, - ): Promise, - - // Activates the busy signal. Set the title in the returned BusySignal - // object (you can update the title multiple times) and dispose it when done. - reportBusy(title: string, options?: BusySignalOptions): BusyMessage, - - // This is a no-op. When someone consumes the busy service, they get back a - // reference to the single shared instance, so disposing of it would be wrong. - dispose(): void, -}; - -export type BusyMessage = { - // You can set/update the title. - setTitle(title: string): void, - // Dispose of the signal when done to make it go away. - dispose(): void, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-actions/__atom_tests__/CodeActionManager-test.js b/modules/atom-ide-ui/pkg/atom-ide-code-actions/__atom_tests__/CodeActionManager-test.js index 400dbb5f..d62f572f 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-actions/__atom_tests__/CodeActionManager-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-actions/__atom_tests__/CodeActionManager-test.js @@ -1,3 +1,49 @@ +"use strict"; + +var _os = _interopRequireDefault(require("os")); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _waits_for() { + const data = _interopRequireDefault(require("../../../../../jest/waits_for")); + + _waits_for = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("../../../../nuclide-commons/promise"); + + _promise = function () { + return data; + }; + + return data; +} + +function _CodeActionManager() { + const data = require("../lib/CodeActionManager"); + + _CodeActionManager = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,73 +52,54 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import os from 'os'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import waitsFor from '../../../../../jest/waits_for'; -import {sleep} from 'nuclide-commons/promise'; - -import {CodeActionManager} from '../lib/CodeActionManager'; - describe('CodeActionManager', () => { let manager; let provider; let delegate; let editor; beforeEach(async () => { - editor = await atom.workspace.open( - nuclideUri.join(os.tmpdir(), 'test.txt'), - ); + editor = await atom.workspace.open(_nuclideUri().default.join(_os.default.tmpdir(), 'test.txt')); editor.setText('abc\ndef\nghi'); - - manager = new CodeActionManager(); + manager = new (_CodeActionManager().CodeActionManager)(); provider = { priority: 1, grammarScopes: ['text.plain.null-grammar'], + async getCodeActions(_e, _r, _d) { return []; - }, + } + }; delegate = { clearMessages: () => {}, - setAllMessages: _messages => {}, + setAllMessages: _messages => {} }; - manager._linterDelegate = (delegate: any); + manager._linterDelegate = delegate; manager.addProvider(provider); }); - it('finds code actions on highlight change and updates linter', async () => { - const actions = [ - { - apply() {}, - async getTitle() { - return 'Mock action'; - }, - dispose() {}, + const actions = [{ + apply() {}, + + async getTitle() { + return 'Mock action'; }, - ]; - const spyActions = jest - .spyOn(provider, 'getCodeActions') - .mockReturnValue(actions); - const spyLinter = jest.spyOn(delegate, 'setAllMessages'); - await sleep(1); // trigger debounce - editor.selectAll(); - await sleep(501); + dispose() {} - await waitsFor( - () => spyLinter.mock.calls.length > 0, - 'should have called setAllMessages', - ); + }]; + const spyActions = jest.spyOn(provider, 'getCodeActions').mockReturnValue(actions); + const spyLinter = jest.spyOn(delegate, 'setAllMessages'); + await (0, _promise().sleep)(1); // trigger debounce + editor.selectAll(); + await (0, _promise().sleep)(501); + await (0, _waits_for().default)(() => spyLinter.mock.calls.length > 0, 'should have called setAllMessages'); expect(spyActions).toHaveBeenCalled(); expect(spyLinter).toHaveBeenCalled(); - expect( - (spyLinter.mock.calls[spyLinter.mock.calls.length - 1]: any)[0][0] - .solutions.length, - ).toEqual(1); + expect(spyLinter.mock.calls[spyLinter.mock.calls.length - 1][0][0].solutions.length).toEqual(1); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/CodeActionManager.js b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/CodeActionManager.js index 5ed74eb4..eb914005 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/CodeActionManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/CodeActionManager.js @@ -1,3 +1,74 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.CodeActionManager = void 0; + +function _debounced() { + const data = require("../../../../nuclide-commons-atom/debounced"); + + _debounced = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,201 +77,156 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {observeActiveEditorsDebounced} from 'nuclide-commons-atom/debounced'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {arrayCompact, arrayFlatten} from 'nuclide-commons/collection'; -import {Observable} from 'rxjs'; -import {getLogger} from 'log4js'; - -import type { - RegisterIndieLinter, - IndieLinterDelegate, - LinterMessageV2, -} from '../../../index'; -import type { - DiagnosticMessage, - DiagnosticUpdater, -} from '../../atom-ide-diagnostics/lib/types'; -import type {CodeAction, CodeActionProvider, CodeActionFetcher} from './types'; - const TIP_DELAY_MS = 500; -async function actionsToMessage( - location: {file: string, position: atom$RangeLike}, - actions: Array, -): Promise { +async function actionsToMessage(location, actions) { const titles = await Promise.all(actions.map(r => r.getTitle())); const solutions = titles.map((title, i) => ({ title, position: location.position, - apply: actions[i].apply.bind(actions[i]), + apply: actions[i].apply.bind(actions[i]) })); return { location, solutions, excerpt: 'Select an action', severity: 'info', - kind: 'action', + kind: 'action' }; } -export class CodeActionManager { - _providerRegistry: ProviderRegistry; - _disposables: UniversalDisposable; - _linterDelegate: ?IndieLinterDelegate; - _diagnosticUpdater: ?DiagnosticUpdater; - +class CodeActionManager { constructor() { - this._providerRegistry = new ProviderRegistry(); - this._disposables = new UniversalDisposable(this._selectionSubscriber()); + this._providerRegistry = new (_ProviderRegistry().default)(); + this._disposables = new (_UniversalDisposable().default)(this._selectionSubscriber()); } dispose() { this._disposables.dispose(); } - addProvider(provider: CodeActionProvider): IDisposable { + addProvider(provider) { const disposable = this._providerRegistry.addProvider(provider); + this._disposables.add(disposable); + return disposable; } - consumeDiagnosticUpdates(diagnosticUpdater: DiagnosticUpdater): IDisposable { + consumeDiagnosticUpdates(diagnosticUpdater) { this._diagnosticUpdater = diagnosticUpdater; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { this._diagnosticUpdater = null; }); } - consumeIndie(register: RegisterIndieLinter): IDisposable { + consumeIndie(register) { const linterDelegate = register({ name: 'Code Actions', - supportedMessageKinds: ['action'], + supportedMessageKinds: ['action'] }); + this._disposables.add(linterDelegate); + this._linterDelegate = linterDelegate; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { this._disposables.remove(linterDelegate); + this._linterDelegate = null; }); } - async _genAllCodeActions( - editor: atom$TextEditor, - range: atom$Range, - diagnostics: Array, - ): Promise> { + async _genAllCodeActions(editor, range, diagnostics) { const codeActionRequests = []; - for (const provider of this._providerRegistry.getAllProvidersForEditor( - editor, - )) { - codeActionRequests.push( - provider.getCodeActions(editor, range, diagnostics), - ); + + for (const provider of this._providerRegistry.getAllProvidersForEditor(editor)) { + codeActionRequests.push(provider.getCodeActions(editor, range, diagnostics)); } - return arrayFlatten(arrayCompact(await Promise.all(codeActionRequests))); + + return (0, _collection().arrayFlatten)((0, _collection().arrayCompact)((await Promise.all(codeActionRequests)))); } - createCodeActionFetcher(): CodeActionFetcher { + createCodeActionFetcher() { return { getCodeActionForDiagnostic: (diagnostic, editor) => { if (diagnostic.range) { - const {range} = diagnostic; + const { + range + } = diagnostic; return this._genAllCodeActions(editor, range, [diagnostic]); } + return Promise.resolve([]); - }, + } }; - } - - // Listen to buffer range selection changes and trigger code action providers + } // Listen to buffer range selection changes and trigger code action providers // when ranges change. - _selectionSubscriber(): rxjs$Subscription { + + + _selectionSubscriber() { // Patterned after highlightEditors of CodeHighlightManager. - return observeActiveEditorsDebounced(0) - .switchMap( - // Get selections for the active editor. - editor => { - if (editor == null) { - return Observable.empty(); - } - const destroyEvents = observableFromSubscribeFunction( - editor.onDidDestroy.bind(editor), - ); - const selections = observableFromSubscribeFunction( - editor.onDidChangeSelectionRange.bind(editor), - ) - .switchMap( - event => - // Remove 0-character selections since it's just cursor movement. - event.newBufferRange.isEmpty() - ? Observable.of(null) - : Observable.of(event.newBufferRange) - .delay(TIP_DELAY_MS) // Delay the emission of the range. - .startWith(null), // null the range immediately when selection changes. - ) - .distinctUntilChanged() - .takeUntil(destroyEvents); - return selections.map( - range => (range == null ? null : {editor, range}), - ); - }, - ) - .switchMap( - // Get a message for the provided selection. - (selection: ?{editor: atom$TextEditor, range: atom$Range}) => { - if (selection == null) { - return Observable.of(null); - } - const {editor, range} = selection; - const file = editor.getBuffer().getPath(); - if (file == null) { - return Observable.empty(); - } - const diagnostics = - this._diagnosticUpdater == null - ? [] - : this._diagnosticUpdater - .getFileMessageUpdates(file) - .messages.filter( - message => - message.range && message.range.intersectsWith(range), - ); - return Observable.fromPromise( - this._genAllCodeActions(editor, range, diagnostics), - ).switchMap(actions => { - // Only produce a message if we have actions to display. - if (actions.length > 0) { - return actionsToMessage({file, position: range}, actions); - } else { - return Observable.empty(); - } - }); - }, - ) - .distinctUntilChanged() - .catch((e, caught) => { - getLogger('code-actions').error( - 'Error getting code actions on selection', - e, - ); - return caught; - }) - .subscribe(message => { - if (this._linterDelegate == null) { - return; - } - if (message == null) { - this._linterDelegate.clearMessages(); + return (0, _debounced().observeActiveEditorsDebounced)(0).switchMap( // Get selections for the active editor. + editor => { + if (editor == null) { + return _RxMin.Observable.empty(); + } + + const destroyEvents = (0, _event().observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor)); + const selections = (0, _event().observableFromSubscribeFunction)(editor.onDidChangeSelectionRange.bind(editor)).switchMap(event => // Remove 0-character selections since it's just cursor movement. + event.newBufferRange.isEmpty() ? _RxMin.Observable.of(null) : _RxMin.Observable.of(event.newBufferRange).delay(TIP_DELAY_MS) // Delay the emission of the range. + .startWith(null) // null the range immediately when selection changes. + ).distinctUntilChanged().takeUntil(destroyEvents); + return selections.map(range => range == null ? null : { + editor, + range + }); + }).switchMap( // Get a message for the provided selection. + selection => { + if (selection == null) { + return _RxMin.Observable.of(null); + } + + const { + editor, + range + } = selection; + const file = editor.getBuffer().getPath(); + + if (file == null) { + return _RxMin.Observable.empty(); + } + + const diagnostics = this._diagnosticUpdater == null ? [] : this._diagnosticUpdater.getFileMessageUpdates(file).messages.filter(message => message.range && message.range.intersectsWith(range)); + return _RxMin.Observable.fromPromise(this._genAllCodeActions(editor, range, diagnostics)).switchMap(actions => { + // Only produce a message if we have actions to display. + if (actions.length > 0) { + return actionsToMessage({ + file, + position: range + }, actions); } else { - this._linterDelegate.setAllMessages([message]); + return _RxMin.Observable.empty(); } }); + }).distinctUntilChanged().catch((e, caught) => { + (0, _log4js().getLogger)('code-actions').error('Error getting code actions on selection', e); + return caught; + }).subscribe(message => { + if (this._linterDelegate == null) { + return; + } + + if (message == null) { + this._linterDelegate.clearMessages(); + } else { + this._linterDelegate.setAllMessages([message]); + } + }); } + } + +exports.CodeActionManager = CodeActionManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/main.js index 483761bc..58337881 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/main.js @@ -1,3 +1,27 @@ +"use strict"; + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _CodeActionManager() { + const data = require("./CodeActionManager"); + + _CodeActionManager = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,43 +30,34 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import createPackage from 'nuclide-commons-atom/createPackage'; -import {CodeActionManager} from './CodeActionManager'; - -import type {RegisterIndieLinter} from '../../../index'; -import type {CodeActionProvider, CodeActionFetcher} from './types'; -import type {DiagnosticUpdater} from '../../atom-ide-diagnostics/lib/types'; - class Activation { - _codeActionManager: CodeActionManager; - constructor() { - this._codeActionManager = new CodeActionManager(); + this._codeActionManager = new (_CodeActionManager().CodeActionManager)(); } dispose() { this._codeActionManager.dispose(); } - consumeCodeActionProvider(provider: CodeActionProvider) { + consumeCodeActionProvider(provider) { return this._codeActionManager.addProvider(provider); } - consumeDiagnosticUpdates(diagnosticUpdater: DiagnosticUpdater) { + consumeDiagnosticUpdates(diagnosticUpdater) { return this._codeActionManager.consumeDiagnosticUpdates(diagnosticUpdater); } - provideCodeActionFetcher(): CodeActionFetcher { + provideCodeActionFetcher() { return this._codeActionManager.createCodeActionFetcher(); } - consumeIndie(register: RegisterIndieLinter) { + consumeIndie(register) { return this._codeActionManager.consumeIndie(register); } + } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/types.js index 50d863cb..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/types.js @@ -1,42 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local - * @format - */ - -import type {DiagnosticMessage} from '../../../pkg/atom-ide-diagnostics/lib/types'; - -export interface CodeAction { - apply(): Promise; - getTitle(): Promise; - dispose(): void; -} - -export type CodeActionProvider = { - +grammarScopes?: Array, - priority: number, - getCodeActions( - editor: atom$TextEditor, - range: atom$Range, - diagnostics: Array, - ): Promise>, -}; - -/** - * atom-ide-code-actions provides a CodeActionFetcher which offers an API to - * request CodeActions from all CodeAction providers. For now, CodeActionFetcher - * can only fetch CodeActions for a Diagnostic. In the future, this API can be - * extended to provide a stream of CodeActions based on the cursor position. - */ -export type CodeActionFetcher = { - getCodeActionForDiagnostic: ( - diagnostic: DiagnosticMessage, - editor: atom$TextEditor, - ) => Promise>, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/__atom_tests__/CodeFormatManager-test.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/__atom_tests__/CodeFormatManager-test.js index 0669434d..37624090 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/__atom_tests__/CodeFormatManager-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/__atom_tests__/CodeFormatManager-test.js @@ -1,3 +1,51 @@ +"use strict"; + +var _atom = require("atom"); + +function _temp() { + const data = _interopRequireDefault(require("temp")); + + _temp = function () { + return data; + }; + + return data; +} + +function config() { + const data = _interopRequireWildcard(require("../lib/config")); + + config = function () { + return data; + }; + + return data; +} + +function _CodeFormatManager() { + const data = _interopRequireDefault(require("../lib/CodeFormatManager")); + + _CodeFormatManager = function () { + return data; + }; + + return data; +} + +function _waits_for() { + const data = _interopRequireDefault(require("../../../../../jest/waits_for")); + + _waits_for = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,140 +54,106 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {Range} from 'atom'; -import temp from 'temp'; -import * as config from '../lib/config'; -import CodeFormatManager from '../lib/CodeFormatManager'; -import waitsFor from '../../../../../jest/waits_for'; - const sleep = n => new Promise(r => setTimeout(r, n)); describe('CodeFormatManager', () => { let textEditor; beforeEach(async () => { - temp.track(); - const file = temp.openSync(); + _temp().default.track(); + + const file = _temp().default.openSync(); + textEditor = await atom.workspace.open(file.path); }); - it('formats an editor on request', async () => { - const manager = new CodeFormatManager(); + const manager = new (_CodeFormatManager().default)(); manager.addRangeProvider({ grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatCode: () => - Promise.resolve([ - { - oldRange: new Range([0, 0], [0, 3]), - oldText: 'abc', - newText: 'def', - }, - ]), + formatCode: () => Promise.resolve([{ + oldRange: new _atom.Range([0, 0], [0, 3]), + oldText: 'abc', + newText: 'def' + }]) }); - textEditor.setText('abc'); - atom.commands.dispatch( - atom.views.getView(textEditor), - 'code-format:format-code', - ); - await waitsFor(() => textEditor.getText() === 'def'); + atom.commands.dispatch(atom.views.getView(textEditor), 'code-format:format-code'); + await (0, _waits_for().default)(() => textEditor.getText() === 'def'); }); - it('format an editor using formatEntireFile', async () => { - const manager = new CodeFormatManager(); + const manager = new (_CodeFormatManager().default)(); manager.addFileProvider({ grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatEntireFile: () => Promise.resolve({formatted: 'ghi'}), + formatEntireFile: () => Promise.resolve({ + formatted: 'ghi' + }) }); - textEditor.setText('abc'); - atom.commands.dispatch( - atom.views.getView(textEditor), - 'code-format:format-code', - ); - await waitsFor(() => textEditor.getText() === 'ghi'); + atom.commands.dispatch(atom.views.getView(textEditor), 'code-format:format-code'); + await (0, _waits_for().default)(() => textEditor.getText() === 'ghi'); }); - it('formats an editor on type', async () => { - jest.spyOn(config, 'getFormatOnType').mockReturnValue(true); - const manager = new CodeFormatManager(); + jest.spyOn(config(), 'getFormatOnType').mockReturnValue(true); + const manager = new (_CodeFormatManager().default)(); const provider = { grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatAtPosition: () => - Promise.resolve([ - { - oldRange: new Range([0, 0], [0, 3]), - oldText: 'abc', - newText: 'def', - }, - ]), - keepCursorPosition: false, + formatAtPosition: () => Promise.resolve([{ + oldRange: new _atom.Range([0, 0], [0, 3]), + oldText: 'abc', + newText: 'def' + }]), + keepCursorPosition: false }; const spy = jest.spyOn(provider, 'formatAtPosition'); manager.addOnTypeProvider(provider); - textEditor.setText('a'); textEditor.setCursorBufferPosition([0, 1]); textEditor.insertText('b'); textEditor.insertText('c'); + await (0, _waits_for().default)(() => textEditor.getText() === 'def'); // Debouncing should ensure only one format call. - await waitsFor(() => textEditor.getText() === 'def'); - // Debouncing should ensure only one format call. expect(spy.mock.calls.length).toBe(1); }); - it('formats an editor on save', async () => { - jest.spyOn(config, 'getFormatOnSave').mockReturnValue(true); - const manager = new CodeFormatManager(); + jest.spyOn(config(), 'getFormatOnSave').mockReturnValue(true); + const manager = new (_CodeFormatManager().default)(); manager.addOnSaveProvider({ grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatOnSave: () => - Promise.resolve([ - { - oldRange: new Range([0, 0], [0, 3]), - oldText: 'abc', - newText: 'def', - }, - ]), + formatOnSave: () => Promise.resolve([{ + oldRange: new _atom.Range([0, 0], [0, 3]), + oldText: 'abc', + newText: 'def' + }]) }); - textEditor.setText('abc'); await textEditor.save(); expect(textEditor.getText()).toBe('def'); }); - it('should still save on timeout', async () => { - jest.spyOn(config, 'getFormatOnSave').mockReturnValue(true); - const manager = new CodeFormatManager(); + jest.spyOn(config(), 'getFormatOnSave').mockReturnValue(true); + const manager = new (_CodeFormatManager().default)(); manager.addRangeProvider({ grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatCode: () => new Promise(() => {}), + formatCode: () => new Promise(() => {}) }); - const spy = jest.spyOn(textEditor.getBuffer(), 'save'); textEditor.save(); - const savePromise = Promise.resolve(textEditor.save()); - - // The first save should be pushed through after the 2nd. - await waitsFor(() => spy.mock.calls.length === 1); + const savePromise = Promise.resolve(textEditor.save()); // The first save should be pushed through after the 2nd. - await sleep(3000); + await (0, _waits_for().default)(() => spy.mock.calls.length === 1); + await sleep(3000); // Hitting the timeout will force the 2nd save through. - // Hitting the timeout will force the 2nd save through. - await waitsFor(() => spy.mock.calls.length === 2); + await (0, _waits_for().default)(() => spy.mock.calls.length === 2); // The real save should still go through. - // The real save should still go through. - await (() => savePromise)(); + await (() => savePromise)(); // Sanity check. - // Sanity check. expect(spy.mock.calls.length).toBe(2); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/CodeFormatManager.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/CodeFormatManager.js index 333a9541..af9ba47d 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/CodeFormatManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/CodeFormatManager.js @@ -1,3 +1,106 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _config() { + const data = require("./config"); + + _config = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _textEdit() { + const data = require("../../../../nuclide-commons-atom/text-edit"); + + _textEdit = function () { + return data; + }; + + return data; +} + +function _textEditor() { + const data = require("../../../../nuclide-commons-atom/text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,219 +109,151 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {TextEdit} from 'nuclide-commons-atom/text-edit'; -import type {BusySignalService} from '../../atom-ide-busy-signal/lib/types'; -import type { - FileCodeFormatProvider, - OnSaveCodeFormatProvider, - OnTypeCodeFormatProvider, - RangeCodeFormatProvider, -} from './types'; - -import {getFormatOnSave, getFormatOnType} from './config'; -import {Range} from 'atom'; -import {getLogger} from 'log4js'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {applyTextEditsToBuffer} from 'nuclide-commons-atom/text-edit'; -import {observeEditorDestroy} from 'nuclide-commons-atom/text-editor'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {completingSwitchMap, microtask} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Observable, Subject} from 'rxjs'; - // Save events are critical, so don't allow providers to block them. const SAVE_TIMEOUT = 2500; -type FormatEvent = - | { - type: 'command' | 'save' | 'new-save', - editor: atom$TextEditor, - } - | { - type: 'type', - editor: atom$TextEditor, - edit: atom$AggregatedTextEditEvent, - }; - -export default class CodeFormatManager { - _subscriptions: UniversalDisposable; - _rangeProviders: ProviderRegistry; - _fileProviders: ProviderRegistry; - _onTypeProviders: ProviderRegistry; - _onSaveProviders: ProviderRegistry; - _busySignalService: ?BusySignalService; - +class CodeFormatManager { constructor() { - this._subscriptions = new UniversalDisposable(this._subscribeToEvents()); - this._rangeProviders = new ProviderRegistry(); - this._fileProviders = new ProviderRegistry(); - this._onTypeProviders = new ProviderRegistry(); - this._onSaveProviders = new ProviderRegistry(); + this._subscriptions = new (_UniversalDisposable().default)(this._subscribeToEvents()); + this._rangeProviders = new (_ProviderRegistry().default)(); + this._fileProviders = new (_ProviderRegistry().default)(); + this._onTypeProviders = new (_ProviderRegistry().default)(); + this._onSaveProviders = new (_ProviderRegistry().default)(); } - /** * Subscribe to all formatting events (commands, saves, edits) and dispatch * formatters as necessary. * By handling all events in a central location, we ensure that no buffer * runs into race conditions with simultaneous formatters. */ - _subscribeToEvents(): rxjs$Subscription { + + + _subscribeToEvents() { // Events from the explicit Atom command. - const commandEvents = observableFromSubscribeFunction(callback => - atom.commands.add( - 'atom-text-editor', - 'code-format:format-code', - callback, - ), - ).switchMap(() => { + const commandEvents = (0, _event().observableFromSubscribeFunction)(callback => atom.commands.add('atom-text-editor', 'code-format:format-code', callback)).switchMap(() => { const editor = atom.workspace.getActiveTextEditor(); + if (!editor) { - return Observable.empty(); + return _RxMin.Observable.empty(); } - return Observable.of({type: 'command', editor}); - }); - // Events from editor actions (saving, typing). - const editorEvents = observableFromSubscribeFunction(cb => - atom.workspace.observeTextEditors(cb), - ).mergeMap(editor => this._getEditorEventStream(editor)); - - return ( - Observable.merge(commandEvents, editorEvents) - // Group events by buffer to prevent simultaneous formatting operations. - .groupBy( - event => event.editor.getBuffer(), - event => event, - grouped => - observableFromSubscribeFunction(callback => - grouped.key.onDidDestroy(callback), - ), - ) - .mergeMap(events => - // Make sure we halt everything when the editor gets destroyed. - events.let(completingSwitchMap(event => this._handleEvent(event))), - ) - .subscribe() - ); - } + return _RxMin.Observable.of({ + type: 'command', + editor + }); + }); // Events from editor actions (saving, typing). + const editorEvents = (0, _event().observableFromSubscribeFunction)(cb => atom.workspace.observeTextEditors(cb)).mergeMap(editor => this._getEditorEventStream(editor)); + return _RxMin.Observable.merge(commandEvents, editorEvents) // Group events by buffer to prevent simultaneous formatting operations. + .groupBy(event => event.editor.getBuffer(), event => event, grouped => (0, _event().observableFromSubscribeFunction)(callback => grouped.key.onDidDestroy(callback))).mergeMap(events => // Make sure we halt everything when the editor gets destroyed. + events.let((0, _observable().completingSwitchMap)(event => this._handleEvent(event)))).subscribe(); + } /** * Returns a stream of all typing and saving operations from the editor. */ - _getEditorEventStream(editor: atom$TextEditor): Observable { - const changeEvents = observableFromSubscribeFunction(callback => - editor.getBuffer().onDidChangeText(callback), - ); - const saveEvents = Observable.create(observer => { + + _getEditorEventStream(editor) { + const changeEvents = (0, _event().observableFromSubscribeFunction)(callback => editor.getBuffer().onDidChangeText(callback)); + + const saveEvents = _RxMin.Observable.create(observer => { const realSave = editor.save; - const newSaves = new Subject(); - // HACK: intercept the real TextEditor.save and handle it ourselves. + const newSaves = new _RxMin.Subject(); // HACK: intercept the real TextEditor.save and handle it ourselves. // Atom has no way of injecting content into the buffer asynchronously // before a save operation. // If we try to format after the save, and then save again, // it's a poor user experience (and also races the text buffer's reload). - const editor_ = (editor: any); + + const editor_ = editor; + editor_.save = () => { newSaves.next('new-save'); - return this._safeFormatCodeOnSave(editor) - .takeUntil(newSaves) - .toPromise() - .then(() => realSave.call(editor)); + return this._safeFormatCodeOnSave(editor).takeUntil(newSaves).toPromise().then(() => realSave.call(editor)); }; + const subscription = newSaves.subscribe(observer); return () => { // Restore the save function when we're done. editor_.save = realSave; subscription.unsubscribe(); }; - }); - - // We need to capture when editors are about to be destroyed in order to + }); // We need to capture when editors are about to be destroyed in order to // interrupt any pending formatting operations. (Otherwise, we may end up // attempting to save a destroyed editor!) - const willDestroyEvents = observableFromSubscribeFunction(cb => - atom.workspace.onWillDestroyPaneItem(cb), - ).filter(event => event.item === editor); - - return Observable.merge( - changeEvents.map(edit => ({type: 'type', editor, edit})), - saveEvents.map(type => ({type, editor})), - ).takeUntil( - Observable.merge(observeEditorDestroy(editor), willDestroyEvents), - ); + + + const willDestroyEvents = (0, _event().observableFromSubscribeFunction)(cb => atom.workspace.onWillDestroyPaneItem(cb)).filter(event => event.item === editor); + return _RxMin.Observable.merge(changeEvents.map(edit => ({ + type: 'type', + editor, + edit + })), saveEvents.map(type => ({ + type, + editor + }))).takeUntil(_RxMin.Observable.merge((0, _textEditor().observeEditorDestroy)(editor), willDestroyEvents)); } - _handleEvent(event: FormatEvent): Observable { - const {editor} = event; + _handleEvent(event) { + const { + editor + } = event; + switch (event.type) { case 'command': - return this._formatCodeInTextEditor(editor) - .map(result => { - if (!result) { - throw new Error('No code formatting providers found!'); - } - }) - .catch(err => { - atom.notifications.addError( - `Failed to format code: ${err.message}`, - { - detail: err.detail, - }, - ); - return Observable.empty(); + return this._formatCodeInTextEditor(editor).map(result => { + if (!result) { + throw new Error('No code formatting providers found!'); + } + }).catch(err => { + atom.notifications.addError(`Failed to format code: ${err.message}`, { + detail: err.detail }); + return _RxMin.Observable.empty(); + }); + case 'type': - return this._formatCodeOnTypeInTextEditor(editor, event.edit).catch( - err => { - getLogger('code-format').warn( - 'Failed to format code on type:', - err, - ); - return Observable.empty(); - }, - ); + return this._formatCodeOnTypeInTextEditor(editor, event.edit).catch(err => { + (0, _log4js().getLogger)('code-format').warn('Failed to format code on type:', err); + return _RxMin.Observable.empty(); + }); + case 'save': - return ( - this._safeFormatCodeOnSave(editor) - // Fire-and-forget the original save function. - // This is actually async for remote files, but we don't use the result. - // NOTE: finally is important, as saves should still fire on unsubscribe. - .finally(() => editor.getBuffer().save()) - ); + return this._safeFormatCodeOnSave(editor) // Fire-and-forget the original save function. + // This is actually async for remote files, but we don't use the result. + // NOTE: finally is important, as saves should still fire on unsubscribe. + .finally(() => editor.getBuffer().save()); + case 'new-save': - return Observable.empty(); + return _RxMin.Observable.empty(); + default: - return Observable.throw(`unknown event type ${event.type}`); + return _RxMin.Observable.throw(`unknown event type ${event.type}`); } - } - - // Checks whether contents are same in the buffer post-format, throwing if + } // Checks whether contents are same in the buffer post-format, throwing if // anything has changed. - _checkContentsAreSame(before: string, after: string): void { + + + _checkContentsAreSame(before, after) { if (before !== after) { - throw new Error( - 'The file contents were changed before formatting was complete.', - ); + throw new Error('The file contents were changed before formatting was complete.'); } - } - - // Formats code in the editor specified, returning whether or not a + } // Formats code in the editor specified, returning whether or not a // code formatter completed successfully. - _formatCodeInTextEditor( - editor: atom$TextEditor, - range?: atom$Range, - ): Observable { - return Observable.defer(() => { + + + _formatCodeInTextEditor(editor, range) { + return _RxMin.Observable.defer(() => { const buffer = editor.getBuffer(); const selectionRange = range || editor.getSelectedBufferRange(); - const {start: selectionStart, end: selectionEnd} = selectionRange; - let formatRange: atom$Range; + const { + start: selectionStart, + end: selectionEnd + } = selectionRange; + let formatRange; + if (selectionRange.isEmpty()) { // If no selection is done, then, the whole file is wanted to be formatted. formatRange = buffer.getRange(); @@ -230,86 +265,73 @@ export default class CodeFormatManager { // or (2) at the first column of the line AFTER their selection. In both cases // we snap the formatRange to end at the first column of the line after their // selection.) - formatRange = new Range( - [selectionStart.row, 0], - selectionEnd.column === 0 ? selectionEnd : [selectionEnd.row + 1, 0], - ); + formatRange = new _atom.Range([selectionStart.row, 0], selectionEnd.column === 0 ? selectionEnd : [selectionEnd.row + 1, 0]); } + const rangeProvider = this._rangeProviders.getProviderForEditor(editor); + const fileProvider = this._fileProviders.getProviderForEditor(editor); + const contents = editor.getText(); - if ( - rangeProvider != null && - // When formatting the entire file, prefer file-based providers. - (!formatRange.isEqual(buffer.getRange()) || fileProvider == null) - ) { - return Observable.defer(() => - this._reportBusy( - editor, - rangeProvider.formatCode(editor, formatRange), - ), - ).map(edits => { + + if (rangeProvider != null && ( // When formatting the entire file, prefer file-based providers. + !formatRange.isEqual(buffer.getRange()) || fileProvider == null)) { + return _RxMin.Observable.defer(() => this._reportBusy(editor, rangeProvider.formatCode(editor, formatRange))).map(edits => { // Throws if contents have changed since the time of triggering format code. this._checkContentsAreSame(contents, editor.getText()); - if (!applyTextEditsToBuffer(editor.getBuffer(), edits)) { + + if (!(0, _textEdit().applyTextEditsToBuffer)(editor.getBuffer(), edits)) { throw new Error('Could not apply edits to text buffer.'); } + return true; }); } else if (fileProvider != null) { - return Observable.defer(() => - this._reportBusy( - editor, - fileProvider.formatEntireFile(editor, formatRange), - ), - ).map(({newCursor, formatted}) => { + return _RxMin.Observable.defer(() => this._reportBusy(editor, fileProvider.formatEntireFile(editor, formatRange))).map(({ + newCursor, + formatted + }) => { // Throws if contents have changed since the time of triggering format code. this._checkContentsAreSame(contents, editor.getText()); - buffer.setTextViaDiff(formatted); - const newPosition = - newCursor != null - ? buffer.positionForCharacterIndex(newCursor) - : editor.getCursorBufferPosition(); - - // We call setCursorBufferPosition even when there is no newCursor, + buffer.setTextViaDiff(formatted); + const newPosition = newCursor != null ? buffer.positionForCharacterIndex(newCursor) : editor.getCursorBufferPosition(); // We call setCursorBufferPosition even when there is no newCursor, // because it unselects the text selection. + editor.setCursorBufferPosition(newPosition); return true; }); } else { - return Observable.of(false); + return _RxMin.Observable.of(false); } }); } - _formatCodeOnTypeInTextEditor( - editor: atom$TextEditor, - aggregatedEvent: atom$AggregatedTextEditEvent, - ): Observable> { - return Observable.defer(() => { + _formatCodeOnTypeInTextEditor(editor, aggregatedEvent) { + return _RxMin.Observable.defer(() => { // Don't try to format changes with multiple cursors. if (aggregatedEvent.changes.length !== 1) { - return Observable.empty(); - } - const event = aggregatedEvent.changes[0]; - // This also ensures the non-emptiness of event.newText for below. - if (!shouldFormatOnType(event) || !getFormatOnType()) { - return Observable.empty(); + return _RxMin.Observable.empty(); } - // In the case of bracket-matching, we use the last character because that's + + const event = aggregatedEvent.changes[0]; // This also ensures the non-emptiness of event.newText for below. + + if (!shouldFormatOnType(event) || !(0, _config().getFormatOnType)()) { + return _RxMin.Observable.empty(); + } // In the case of bracket-matching, we use the last character because that's // the character that will usually cause a reformat (i.e. `}` instead of `{`). + + const character = event.newText[event.newText.length - 1]; const provider = this._onTypeProviders.getProviderForEditor(editor); + if (provider == null) { - return Observable.empty(); + return _RxMin.Observable.empty(); } const contents = editor.getText(); - const cursorPosition = editor.getCursorBufferPosition().copy(); - - // The bracket-matching package basically overwrites + const cursorPosition = editor.getCursorBufferPosition().copy(); // The bracket-matching package basically overwrites // // editor.insertText('{'); // @@ -321,98 +343,83 @@ export default class CodeFormatManager { // We want to wait until the cursor has actually moved before we issue a // format request, so that we format at the right position (and potentially // also let any other event handlers have their go). - return microtask - .switchMap(() => - provider.formatAtPosition( - editor, - editor.getCursorBufferPosition(), - character, - ), - ) - .do(edits => { - if (edits.length === 0) { - return; - } - this._checkContentsAreSame(contents, editor.getText()); - // Note that this modification is not in a transaction, so it applies as a - // separate editing event than the character typing. This means that you - // can undo just the formatting by attempting to undo once, and then undo - // your actual code by undoing again. - if (!applyTextEditsToBuffer(editor.getBuffer(), edits)) { - throw new Error('Could not apply edits to text buffer.'); - } - if (provider.keepCursorPosition) { - editor.setCursorBufferPosition(cursorPosition); - } - }); + return _observable().microtask.switchMap(() => provider.formatAtPosition(editor, editor.getCursorBufferPosition(), character)).do(edits => { + if (edits.length === 0) { + return; + } + + this._checkContentsAreSame(contents, editor.getText()); // Note that this modification is not in a transaction, so it applies as a + // separate editing event than the character typing. This means that you + // can undo just the formatting by attempting to undo once, and then undo + // your actual code by undoing again. + + + if (!(0, _textEdit().applyTextEditsToBuffer)(editor.getBuffer(), edits)) { + throw new Error('Could not apply edits to text buffer.'); + } + + if (provider.keepCursorPosition) { + editor.setCursorBufferPosition(cursorPosition); + } + }); }); } - _safeFormatCodeOnSave(editor: atom$TextEditor): Observable { - return this._formatCodeOnSaveInTextEditor(editor) - .timeout(SAVE_TIMEOUT) - .catch(err => { - getLogger('code-format').warn('Failed to format code on save:', err); - return Observable.empty(); - }); + _safeFormatCodeOnSave(editor) { + return this._formatCodeOnSaveInTextEditor(editor).timeout(SAVE_TIMEOUT).catch(err => { + (0, _log4js().getLogger)('code-format').warn('Failed to format code on save:', err); + return _RxMin.Observable.empty(); + }); } - _formatCodeOnSaveInTextEditor(editor: atom$TextEditor): Observable { + _formatCodeOnSaveInTextEditor(editor) { const saveProvider = this._onSaveProviders.getProviderForEditor(editor); + if (saveProvider != null) { - return Observable.defer(() => - this._reportBusy(editor, saveProvider.formatOnSave(editor), false), - ).map(edits => { - applyTextEditsToBuffer(editor.getBuffer(), edits); + return _RxMin.Observable.defer(() => this._reportBusy(editor, saveProvider.formatOnSave(editor), false)).map(edits => { + (0, _textEdit().applyTextEditsToBuffer)(editor.getBuffer(), edits); }); - } else if (getFormatOnSave(editor)) { - return this._formatCodeInTextEditor( - editor, - editor.getBuffer().getRange(), - ).ignoreElements(); + } else if ((0, _config().getFormatOnSave)(editor)) { + return this._formatCodeInTextEditor(editor, editor.getBuffer().getRange()).ignoreElements(); } - return Observable.empty(); + + return _RxMin.Observable.empty(); } - _reportBusy( - editor: atom$TextEditor, - promise: Promise, - revealTooltip?: boolean = true, - ): Promise { + _reportBusy(editor, promise, revealTooltip = true) { const busySignalService = this._busySignalService; + if (busySignalService != null) { const path = editor.getPath(); - const displayPath = - path != null ? nuclideUri.basename(path) : ''; - return busySignalService.reportBusyWhile( - `Formatting code in ${displayPath}`, - () => promise, - {revealTooltip}, - ); + const displayPath = path != null ? _nuclideUri().default.basename(path) : ''; + return busySignalService.reportBusyWhile(`Formatting code in ${displayPath}`, () => promise, { + revealTooltip + }); } + return promise; } - addRangeProvider(provider: RangeCodeFormatProvider): IDisposable { + addRangeProvider(provider) { return this._rangeProviders.addProvider(provider); } - addFileProvider(provider: FileCodeFormatProvider): IDisposable { + addFileProvider(provider) { return this._fileProviders.addProvider(provider); } - addOnTypeProvider(provider: OnTypeCodeFormatProvider): IDisposable { + addOnTypeProvider(provider) { return this._onTypeProviders.addProvider(provider); } - addOnSaveProvider(provider: OnSaveCodeFormatProvider): IDisposable { + addOnSaveProvider(provider) { return this._onSaveProviders.addProvider(provider); } - consumeBusySignal(busySignalService: BusySignalService): IDisposable { + consumeBusySignal(busySignalService) { this._busySignalService = busySignalService; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { this._busySignalService = null; }); } @@ -420,9 +427,12 @@ export default class CodeFormatManager { dispose() { this._subscriptions.dispose(); } + } -function shouldFormatOnType(event: atom$TextEditEvent): boolean { +exports.default = CodeFormatManager; + +function shouldFormatOnType(event) { // There's not a direct way to figure out what caused this edit event. There // are three cases that we want to pay attention to: // @@ -448,20 +458,21 @@ function shouldFormatOnType(event: atom$TextEditEvent): boolean { } else if (event.newText.length > 1 && !isBracketPair(event.newText)) { return false; } + return true; } - /** * We can't tell the difference between a paste and the bracket-matcher package * inserting an extra bracket, so we just assume that any pair of brackets that * bracket-matcher recognizes was a pair matched by the package. */ -function isBracketPair(typedText: string): boolean { + + +function isBracketPair(typedText) { if (atom.packages.getActivePackage('bracket-matcher') == null) { return false; } - const validBracketPairs: Array = (atom.config.get( - 'bracket-matcher.autocompleteCharacters', - ): any); + + const validBracketPairs = atom.config.get('bracket-matcher.autocompleteCharacters'); return validBracketPairs.indexOf(typedText) !== -1; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/config.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/config.js index b80cc1e3..158b0d1c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/config.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/config.js @@ -1,3 +1,23 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getFormatOnSave = getFormatOnSave; +exports.getFormatOnType = getFormatOnType; + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,22 +26,17 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +function getFormatOnSave(editor) { + const formatOnSave = _featureConfig().default.get('atom-ide-code-format.formatOnSave', { + scope: editor.getRootScopeDescriptor() + }); -import featureConfig from 'nuclide-commons-atom/feature-config'; - -export function getFormatOnSave(editor: atom$TextEditor): boolean { - const formatOnSave = (featureConfig.get('atom-ide-code-format.formatOnSave', { - scope: editor.getRootScopeDescriptor(), - }): any); return formatOnSave == null ? false : formatOnSave; } -export function getFormatOnType(): boolean { - return featureConfig.getWithDefaults( - 'atom-ide-code-format.formatOnType', - false, - ); -} +function getFormatOnType() { + return _featureConfig().default.getWithDefaults('atom-ide-code-format.formatOnType', false); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/main.js index 8e84d5e9..956a480a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/main.js @@ -1,3 +1,27 @@ +"use strict"; + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _CodeFormatManager() { + const data = _interopRequireDefault(require("./CodeFormatManager")); + + _CodeFormatManager = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,41 +30,20 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {BusySignalService} from '../../atom-ide-busy-signal/lib/types'; -import type { - CodeFormatProvider, - RangeCodeFormatProvider, - FileCodeFormatProvider, - OnTypeCodeFormatProvider, - OnSaveCodeFormatProvider, -} from './types'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import CodeFormatManager from './CodeFormatManager'; - class Activation { - codeFormatManager: CodeFormatManager; - constructor() { - this.codeFormatManager = new CodeFormatManager(); + this.codeFormatManager = new (_CodeFormatManager().default)(); } - consumeLegacyProvider(provider: CodeFormatProvider): IDisposable { + consumeLegacyProvider(provider) { // Legacy providers used `selector` / `inclusionPriority`. - provider.grammarScopes = - provider.grammarScopes || - (provider.selector != null ? provider.selector.split(', ') : null); - provider.priority = - provider.priority != null - ? provider.priority - : // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) - provider.inclusionPriority != null - ? provider.inclusionPriority - : 0; + provider.grammarScopes = provider.grammarScopes || (provider.selector != null ? provider.selector.split(', ') : null); + provider.priority = provider.priority != null ? provider.priority : // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + provider.inclusionPriority != null ? provider.inclusionPriority : 0; + if (provider.formatCode) { return this.consumeRangeProvider(provider); } else if (provider.formatEntireFile) { @@ -50,32 +53,34 @@ class Activation { } else if (provider.formatOnSave) { return this.consumeOnSaveProvider(provider); } + throw new Error('Invalid code format provider'); } - consumeRangeProvider(provider: RangeCodeFormatProvider): IDisposable { + consumeRangeProvider(provider) { return this.codeFormatManager.addRangeProvider(provider); } - consumeFileProvider(provider: FileCodeFormatProvider): IDisposable { + consumeFileProvider(provider) { return this.codeFormatManager.addFileProvider(provider); } - consumeOnTypeProvider(provider: OnTypeCodeFormatProvider): IDisposable { + consumeOnTypeProvider(provider) { return this.codeFormatManager.addOnTypeProvider(provider); } - consumeOnSaveProvider(provider: OnSaveCodeFormatProvider): IDisposable { + consumeOnSaveProvider(provider) { return this.codeFormatManager.addOnSaveProvider(provider); } - consumeBusySignal(busySignalService: BusySignalService): IDisposable { + consumeBusySignal(busySignalService) { return this.codeFormatManager.consumeBusySignal(busySignalService); } dispose() { this.codeFormatManager.dispose(); } + } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/types.js index 7a8c2a20..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/types.js @@ -1,97 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local - * @format - */ - -import type {TextEdit} from 'nuclide-commons-atom/text-edit'; - -/** - * A brief overview of the different code formatting providers: - * - * == Range formatters == - * These accept a range to format and return a list of edits to apply. - * These will always be preferred over file formatters when a range is selected. - * - * == File formatters == - * These always return the result of formatting the entire file. - * To compensate, they can return a custom cursor position to avoid disruption. - * These will be preferred over range formatters for whole-file formatting. - * - * == onType formatters == - * These are run after every typing event in a selected editor. - * - * == onSave formatters == - * These are run whenever a selected editor is saved. - * If the global format-on-save option is enabled, then file/range formatters - * will be triggered on save (even if no save formatters are provided). - * Obviously, save formatters are preferred in this case. - */ - -/** - * Formats the range specified, and returns a list of text edits to apply. - * Text edits must be non-overlapping and preferably in reverse-sorted order. - */ -export type RangeCodeFormatProvider = {| - formatCode: ( - editor: atom$TextEditor, - range: atom$Range, - ) => Promise>, - priority: number, - grammarScopes: Array, -|}; - -/** - * Formats the range specified, but returns the entire file (along with the new cursor position). - * Useful for less-flexible providers like clang-format. - */ -export type FileCodeFormatProvider = {| - formatEntireFile: ( - editor: atom$TextEditor, - range: atom$Range, - ) => Promise<{ - newCursor?: number, - formatted: string, - }>, - priority: number, - grammarScopes: Array, -|}; - -/** - * Formats around the given position, and returns a list of text edits to - * apply, similar to `formatCode`. The provider determines the exact - * range to format based on what's at that position. - * - * This will automatically triggered after every typing event. - */ -export type OnTypeCodeFormatProvider = {| - formatAtPosition: ( - editor: atom$TextEditor, - position: atom$Point, - triggerCharacter: string, - ) => Promise>, - priority: number, - grammarScopes: Array, - keepCursorPosition: boolean, -|}; - -/** - * Formats files after save events. - */ -export type OnSaveCodeFormatProvider = {| - formatOnSave: (editor: atom$TextEditor) => Promise>, - priority: number, - grammarScopes: Array, -|}; - -export type CodeFormatProvider = - | RangeCodeFormatProvider - | FileCodeFormatProvider - | OnTypeCodeFormatProvider - | OnSaveCodeFormatProvider; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/__atom_tests__/CodeHighlightManager-test.js b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/__atom_tests__/CodeHighlightManager-test.js index 9cc9973a..451fec97 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/__atom_tests__/CodeHighlightManager-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/__atom_tests__/CodeHighlightManager-test.js @@ -1,3 +1,51 @@ +"use strict"; + +var _os = _interopRequireDefault(require("os")); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _waits_for() { + const data = _interopRequireDefault(require("../../../../../jest/waits_for")); + + _waits_for = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("../../../../nuclide-commons/promise"); + + _promise = function () { + return data; + }; + + return data; +} + +function _CodeHighlightManager() { + const data = _interopRequireDefault(require("../lib/CodeHighlightManager")); + + _CodeHighlightManager = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,83 +54,62 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import os from 'os'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {Point, Range} from 'atom'; -import waitsFor from '../../../../../jest/waits_for'; -import {sleep} from 'nuclide-commons/promise'; - -import CodeHighlightManager from '../lib/CodeHighlightManager'; - describe('CodeHighlightManager', () => { let manager; let provider; let editor; beforeEach(async () => { jest.restoreAllMocks(); - editor = await atom.workspace.open( - nuclideUri.join(os.tmpdir(), 'test.txt'), - ); + editor = await atom.workspace.open(_nuclideUri().default.join(_os.default.tmpdir(), 'test.txt')); editor.setText('abc\ndef\nghi'); - - manager = new CodeHighlightManager(); + manager = new (_CodeHighlightManager().default)(); provider = { priority: 1, grammarScopes: ['text.plain.null-grammar'], - highlight: (_editor, position) => Promise.resolve([]), + highlight: (_editor, position) => Promise.resolve([]) }; manager.addProvider(provider); }); - it.skip('updates highlights on cursor move', async () => { - const ranges = [new Range([0, 0], [0, 3])]; - const spy = jest.spyOn(provider, 'highlight').mockReturnValue(ranges); + const ranges = [new _atom.Range([0, 0], [0, 3])]; + const spy = jest.spyOn(provider, 'highlight').mockReturnValue(ranges); // Just opening the editor should trigger highlights. - // Just opening the editor should trigger highlights. - await sleep(1); // editor debounce - expect(spy).toHaveBeenCalled(); + await (0, _promise().sleep)(1); // editor debounce - // (once the promise resolves). - await waitsFor(() => manager._markers.length === 1); + expect(spy).toHaveBeenCalled(); // (once the promise resolves). - ranges[0] = new Range([1, 0], [1, 3]); - editor.setCursorBufferPosition(new Point(1, 0)); - await sleep(300); // trigger debounce + await (0, _waits_for().default)(() => manager._markers.length === 1); + ranges[0] = new _atom.Range([1, 0], [1, 3]); + editor.setCursorBufferPosition(new _atom.Point(1, 0)); + await (0, _promise().sleep)(300); // trigger debounce // Old markers should be cleared immediately. + expect(manager._markers.length).toBe(0); expect(spy.mock.calls.length).toBe(2); + await (0, _waits_for().default)(() => manager._markers.length === 1); // If we're still inside the range, don't fire a new event. - await waitsFor(() => manager._markers.length === 1); - - // If we're still inside the range, don't fire a new event. - editor.setCursorBufferPosition(new Point(1, 1)); + editor.setCursorBufferPosition(new _atom.Point(1, 1)); expect(spy.mock.calls.length).toBe(2); + atom.workspace.open(_nuclideUri().default.join(_os.default.tmpdir(), 'test2.txt')); // Opening a new editor should clear out old markers. - atom.workspace.open(nuclideUri.join(os.tmpdir(), 'test2.txt')); - - // Opening a new editor should clear out old markers. - await sleep(1); + await (0, _promise().sleep)(1); expect(manager._markers.length).toBe(0); }); - it('updates highlights on change', async () => { - const ranges = [new Range([0, 0], [0, 1])]; + const ranges = [new _atom.Range([0, 0], [0, 1])]; const spy = jest.spyOn(provider, 'highlight').mockReturnValue(ranges); - - await sleep(1); + await (0, _promise().sleep)(1); editor.insertText('a'); - await sleep(3000); // trigger typing debounce - expect(spy).toHaveBeenCalled(); + await (0, _promise().sleep)(3000); // trigger typing debounce + + expect(spy).toHaveBeenCalled(); // Wait for the promise to resolve. - // Wait for the promise to resolve. - await waitsFor(() => manager._markers.length === 1); + await (0, _waits_for().default)(() => manager._markers.length === 1); + editor.insertText('b'); // Clear out immediately. - editor.insertText('b'); - // Clear out immediately. expect(manager._markers.length).toBe(0); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/CodeHighlightManager.js b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/CodeHighlightManager.js index cb79ee91..6172b96a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/CodeHighlightManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/CodeHighlightManager.js @@ -1,3 +1,74 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _debounced() { + const data = require("../../../../nuclide-commons-atom/debounced"); + + _debounced = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,98 +77,56 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const CURSOR_DELAY_MS = 250; // Apply a much higher debounce to text changes to avoid disrupting the typing experience. -import type {CodeHighlightProvider} from './types'; - -import {getLogger} from 'log4js'; -import {Observable} from 'rxjs'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {fastDebounce, toggle} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {observeActiveEditorsDebounced} from 'nuclide-commons-atom/debounced'; - -const CURSOR_DELAY_MS = 250; -// Apply a much higher debounce to text changes to avoid disrupting the typing experience. const CHANGE_TOGGLE_MS = 2500; -export default class CodeHighlightManager { - _subscriptions: UniversalDisposable; - _providers: ProviderRegistry; - _markers: Array; - +class CodeHighlightManager { constructor() { - this._providers = new ProviderRegistry(); + this._providers = new (_ProviderRegistry().default)(); this._markers = []; - this._subscriptions = new UniversalDisposable(this._highlightEditors()); + this._subscriptions = new (_UniversalDisposable().default)(this._highlightEditors()); } - _highlightEditors(): rxjs$Subscription { - return observeActiveEditorsDebounced(0) - .do(() => this._destroyMarkers()) - .switchMap(editor => { - if (editor == null) { - return Observable.empty(); - } - const cursorPositions = observableFromSubscribeFunction( - editor.onDidChangeCursorPosition.bind(editor), - ) - .filter( - // If we're moving around inside highlighted ranges, that's fine. - event => - !this._isPositionInHighlightedRanges( - editor, - event.newBufferPosition, - ), - ) - .do(() => this._destroyMarkers()) // Immediately clear previous markers. - .let(fastDebounce(CURSOR_DELAY_MS)) - .startWith((null: any)) // Immediately kick off a highlight event. - .map(() => editor.getCursorBufferPosition()); - - // Changing text triggers a CHANGE_TOGGLE_MS period in which cursor changes are ignored. - // We'll model this as one stream that emits 'false' and another that debounces 'true's. - const changeEvents = observableFromSubscribeFunction( - editor.onDidChange.bind(editor), - ) - .do(() => this._destroyMarkers()) - .share(); - - const changeToggles = Observable.merge( - Observable.of(true), - changeEvents.mapTo(false), - changeEvents.let(fastDebounce(CHANGE_TOGGLE_MS)).mapTo(true), - ); - - const destroyEvents = observableFromSubscribeFunction( - editor.onDidDestroy.bind(editor), - ); - - return cursorPositions - .let(toggle(changeToggles)) - .switchMap(async position => { - return { - editor, - ranges: await this._getHighlightedRanges(editor, position), - }; - }) - .takeUntil(destroyEvents); - }) - .subscribe(({editor, ranges}) => { - if (ranges != null) { - this._highlightRanges(editor, ranges); - } - }); + _highlightEditors() { + return (0, _debounced().observeActiveEditorsDebounced)(0).do(() => this._destroyMarkers()).switchMap(editor => { + if (editor == null) { + return _RxMin.Observable.empty(); + } + + const cursorPositions = (0, _event().observableFromSubscribeFunction)(editor.onDidChangeCursorPosition.bind(editor)).filter( // If we're moving around inside highlighted ranges, that's fine. + event => !this._isPositionInHighlightedRanges(editor, event.newBufferPosition)).do(() => this._destroyMarkers()) // Immediately clear previous markers. + .let((0, _observable().fastDebounce)(CURSOR_DELAY_MS)).startWith(null) // Immediately kick off a highlight event. + .map(() => editor.getCursorBufferPosition()); // Changing text triggers a CHANGE_TOGGLE_MS period in which cursor changes are ignored. + // We'll model this as one stream that emits 'false' and another that debounces 'true's. + + const changeEvents = (0, _event().observableFromSubscribeFunction)(editor.onDidChange.bind(editor)).do(() => this._destroyMarkers()).share(); + + const changeToggles = _RxMin.Observable.merge(_RxMin.Observable.of(true), changeEvents.mapTo(false), changeEvents.let((0, _observable().fastDebounce)(CHANGE_TOGGLE_MS)).mapTo(true)); + + const destroyEvents = (0, _event().observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor)); + return cursorPositions.let((0, _observable().toggle)(changeToggles)).switchMap(async position => { + return { + editor, + ranges: await this._getHighlightedRanges(editor, position) + }; + }).takeUntil(destroyEvents); + }).subscribe(({ + editor, + ranges + }) => { + if (ranges != null) { + this._highlightRanges(editor, ranges); + } + }); } - async _getHighlightedRanges( - editor: atom$TextEditor, - position: atom$Point, - ): Promise> { + async _getHighlightedRanges(editor, position) { const provider = this._providers.getProviderForEditor(editor); + if (!provider) { return null; } @@ -105,41 +134,42 @@ export default class CodeHighlightManager { try { return await provider.highlight(editor, position); } catch (e) { - getLogger('code-highlight').error('Error getting code highlights', e); + (0, _log4js().getLogger)('code-highlight').error('Error getting code highlights', e); return null; } } - _highlightRanges(editor: atom$TextEditor, ranges: Array): void { + _highlightRanges(editor, ranges) { this._destroyMarkers(); + this._markers = ranges.map(range => editor.markBufferRange(range, {})); + this._markers.forEach(marker => { editor.decorateMarker(marker, { type: 'highlight', - class: 'code-highlight-marker', + class: 'code-highlight-marker' }); }); } - _isPositionInHighlightedRanges( - editor: atom$TextEditor, - position: atom$Point, - ): boolean { - return this._markers - .map(marker => marker.getBufferRange()) - .some(range => range.containsPoint(position)); + _isPositionInHighlightedRanges(editor, position) { + return this._markers.map(marker => marker.getBufferRange()).some(range => range.containsPoint(position)); } - _destroyMarkers(): void { + _destroyMarkers() { this._markers.splice(0).forEach(marker => marker.destroy()); } - addProvider(provider: CodeHighlightProvider): IDisposable { + addProvider(provider) { return this._providers.addProvider(provider); } dispose() { this._subscriptions.dispose(); + this._destroyMarkers(); } + } + +exports.default = CodeHighlightManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/main.js index 2444af61..5db10c8c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/main.js @@ -1,3 +1,27 @@ +"use strict"; + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _CodeHighlightManager() { + const data = _interopRequireDefault(require("./CodeHighlightManager")); + + _CodeHighlightManager = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,29 +30,22 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {CodeHighlightProvider} from './types'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import CodeHighlightManager from './CodeHighlightManager'; - class Activation { - _codeHighlightManager: CodeHighlightManager; - constructor() { - this._codeHighlightManager = new CodeHighlightManager(); + this._codeHighlightManager = new (_CodeHighlightManager().default)(); } dispose() { this._codeHighlightManager.dispose(); } - addProvider(provider: CodeHighlightProvider): IDisposable { + addProvider(provider) { return this._codeHighlightManager.addProvider(provider); } + } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/types.js index 5a398375..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/types.js @@ -1,20 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict - * @format - */ - -export type CodeHighlightProvider = { - highlight( - editor: atom$TextEditor, - bufferPosition: atom$Point, - ): Promise>, - priority: number, - grammarScopes: Array, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/__atom_tests__/ConsoleView-test.js b/modules/atom-ide-ui/pkg/atom-ide-console/__atom_tests__/ConsoleView-test.js index b92f846e..1097aef3 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/__atom_tests__/ConsoleView-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/__atom_tests__/ConsoleView-test.js @@ -1,3 +1,33 @@ +"use strict"; + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _testUtils() { + const data = _interopRequireDefault(require("react-dom/test-utils")); + + _testUtils = function () { + return data; + }; + + return data; +} + +function _ConsoleView() { + const data = _interopRequireDefault(require("../lib/ui/ConsoleView")); + + _ConsoleView = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,60 +36,59 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import * as React from 'react'; -import ReactDom from 'react-dom'; -import TestUtils from 'react-dom/test-utils'; -import ConsoleView from '../lib/ui/ConsoleView'; - describe('ConsoleView', () => { it('focuses the filter when "/" is pressed inside the console-scroll-pane-wrapper div', () => { - const consoleView: ConsoleView = (TestUtils.renderIntoDocument( - {}} - createPaste={null} - currentExecutor={null} - displayableRecords={[]} - enableRegExpFilter={true} - execute={() => {}} - executors={new Map()} - filterText={''} - filteredRecordCount={0} - fontSize={12} - getProvider={() => {}} - history={[]} - invalidFilterInput={false} - onDisplayableRecordHeightChange={() => {}} - resetAllFilters={() => {}} - selectExecutor={() => {}} - selectSources={() => {}} - selectedSourceIds={[]} - sources={[]} - updateFilter={() => {}} - watchEditor={null} - />, - ): any); + const consoleView = _testUtils().default.renderIntoDocument(React.createElement(_ConsoleView().default, { + clearRecords: () => {}, + createPaste: null, + currentExecutor: null, + displayableRecords: [], + enableRegExpFilter: true, + execute: () => {}, + executors: new Map(), + filterText: '', + filteredRecordCount: 0, + fontSize: 12, + getProvider: () => {}, + history: [], + invalidFilterInput: false, + onDisplayableRecordHeightChange: () => {}, + resetAllFilters: () => {}, + selectExecutor: () => {}, + selectSources: () => {}, + selectedSourceIds: [], + sources: [], + updateFilter: () => {}, + watchEditor: null + })); const workspaceEl = atom.views.getView(atom.workspace); - const consoleViewNode = ReactDom.findDOMNode(consoleView); - invariant(consoleViewNode != null); - workspaceEl.appendChild(consoleViewNode); + const consoleViewNode = _reactDom.default.findDOMNode(consoleView); + + if (!(consoleViewNode != null)) { + throw new Error("Invariant violation: \"consoleViewNode != null\""); + } + + workspaceEl.appendChild(consoleViewNode); const consoleHeaderComponent = consoleView._consoleHeaderComponent; - invariant(consoleHeaderComponent != null); + + if (!(consoleHeaderComponent != null)) { + throw new Error("Invariant violation: \"consoleHeaderComponent != null\""); + } + const filterFocusSpy = jest.spyOn(consoleHeaderComponent, 'focusFilter'); + const consoleScrollPaneTarget = workspaceEl.querySelector('.console-scroll-pane-wrapper'); - const consoleScrollPaneTarget = workspaceEl.querySelector( - '.console-scroll-pane-wrapper', - ); - invariant(consoleScrollPaneTarget != null); - atom.commands.dispatch(consoleScrollPaneTarget, 'atom-ide:filter'); + if (!(consoleScrollPaneTarget != null)) { + throw new Error("Invariant violation: \"consoleScrollPaneTarget != null\""); + } + atom.commands.dispatch(consoleScrollPaneTarget, 'atom-ide:filter'); expect(filterFocusSpy).toHaveBeenCalled(); workspaceEl.removeChild(consoleViewNode); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/Epics-test.js b/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/Epics-test.js index 2a6f1143..1c6dff08 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/Epics-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/Epics-test.js @@ -1,3 +1,39 @@ +"use strict"; + +function _reduxObservable() { + const data = require("../../../../nuclide-commons/redux-observable"); + + _reduxObservable = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("../lib/redux/Actions")); + + Actions = function () { + return data; + }; + + return data; +} + +function Epics() { + const data = _interopRequireWildcard(require("../lib/redux/Epics")); + + Epics = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,54 +42,46 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {AppState} from '../lib/types'; - -import {ActionsObservable} from 'nuclide-commons/redux-observable'; -import * as Actions from '../lib/redux/Actions'; -import * as Epics from '../lib/redux/Epics'; -import invariant from 'assert'; -import {Observable} from 'rxjs'; - describe('Epics', () => { describe('registerOutputProviderEpic', () => { it('observes the status', () => { const mockStore = { dispatch: () => {}, - getState: () => (({}: any): AppState), + getState: () => ({}) }; let setStatus; const provider = { id: 'test', - messages: Observable.never(), + messages: _RxMin.Observable.never(), observeStatus: cb => { setStatus = cb; }, start: () => {}, - stop: () => {}, + stop: () => {} }; - const actions = new ActionsObservable( - Observable.of(Actions.registerOutputProvider(provider)), - ); + const actions = new (_reduxObservable().ActionsObservable)(_RxMin.Observable.of(Actions().registerOutputProvider(provider))); let results = []; - Epics.registerRecordProviderEpic(actions, mockStore).subscribe( - results.push.bind(results), - ); - invariant(setStatus != null); + Epics().registerRecordProviderEpic(actions, mockStore).subscribe(results.push.bind(results)); + + if (!(setStatus != null)) { + throw new Error("Invariant violation: \"setStatus != null\""); + } + setStatus('running'); setStatus('stopped'); setStatus('running'); - results = results.filter(action => action.type === Actions.UPDATE_STATUS); + results = results.filter(action => action.type === Actions().UPDATE_STATUS); expect(results.length).toBe(3); - expect( - results.map(action => { - invariant(action.type === Actions.UPDATE_STATUS); - return action.payload.status; - }), - ).toEqual(['running', 'stopped', 'running']); + expect(results.map(action => { + if (!(action.type === Actions().UPDATE_STATUS)) { + throw new Error("Invariant violation: \"action.type === Actions.UPDATE_STATUS\""); + } + + return action.payload.status; + })).toEqual(['running', 'stopped', 'running']); }); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/Reducers-test.js b/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/Reducers-test.js index febf34f8..c75cc226 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/Reducers-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/Reducers-test.js @@ -1,3 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.createDummyExecutor = createDummyExecutor; + +function Actions() { + const data = _interopRequireWildcard(require("../lib/redux/Actions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _Reducers() { + const data = _interopRequireDefault(require("../lib/redux/Reducers")); + + _Reducers = function () { + return data; + }; + + return data; +} + +function Immutable() { + const data = _interopRequireWildcard(require("immutable")); + + Immutable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,17 +49,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {Action, Executor} from '../lib/types'; - -import * as Actions from '../lib/redux/Actions'; -import Reducers from '../lib/redux/Reducers'; -import * as Immutable from 'immutable'; -import {Observable} from 'rxjs'; - const emptyAppState = { createPasteFunction: null, currentExecutorId: null, @@ -25,177 +60,140 @@ const emptyAppState = { providers: new Map(), providerStatuses: new Map(), providerSubscriptions: new Map(), - records: Immutable.List(), - history: [], + records: Immutable().List(), + history: [] }; - describe('createStateStream', () => { describe('RECORD_RECEIVED', () => { let finalState; let initialRecords; - beforeEach(() => { - initialRecords = Immutable.List(); - const initialState = { - ...emptyAppState, + initialRecords = Immutable().List(); + const initialState = Object.assign({}, emptyAppState, { maxMessageCount: 2, - records: initialRecords, - }; + records: initialRecords + }); const actions = []; + for (let i = 0; i < 5; i++) { actions.push({ - type: Actions.RECORD_RECEIVED, + type: Actions().RECORD_RECEIVED, payload: { record: { level: 'info', - text: i.toString(), - }, - }, + text: i.toString() + } + } }); } - finalState = ((actions: any): Array).reduce( - Reducers, - initialState, - ); - }); + finalState = actions.reduce(_Reducers().default, initialState); + }); it('adds records', () => { expect(finalState.records.size).toBeGreaterThan(0); }); - it('truncates the record list using `maxMessageCount`', () => { expect(finalState.records.size).toBe(2); }); - it('truncates the least recent records', () => { - expect(finalState.records.map(record => record.text).toArray()).toEqual([ - '3', - '4', - ]); + expect(finalState.records.map(record => record.text).toArray()).toEqual(['3', '4']); }); - it("doesn't mutate the original records list", () => { expect(initialRecords.size).toBe(0); }); }); - describe('REGISTER_SOURCE', () => { let initialProviders; let finalState; - beforeEach(() => { initialProviders = new Map(); - const initialState = { - ...emptyAppState, - providers: initialProviders, - }; - const actions = [ - { - type: Actions.REGISTER_SOURCE, - payload: { - source: { - id: 'test', - records: Observable.empty(), - }, - }, - }, - ]; - finalState = ((actions: any): Array).reduce( - Reducers, - initialState, - ); + const initialState = Object.assign({}, emptyAppState, { + providers: initialProviders + }); + const actions = [{ + type: Actions().REGISTER_SOURCE, + payload: { + source: { + id: 'test', + records: _RxMin.Observable.empty() + } + } + }]; + finalState = actions.reduce(_Reducers().default, initialState); }); - it('adds providers to the registry', () => { expect(finalState.providers.size).toBe(1); }); - it("doesn't mutate the original provider map", () => { expect(initialProviders.size).toBe(0); }); }); - describe('CLEAR_RECORDS', () => { let initialRecords; let finalState; - beforeEach(() => { - initialRecords = Immutable.List([ - { - kind: 'message', - sourceId: 'Test', - level: 'info', - text: 'test', - scopeName: null, - timestamp: new Date('2017-01-01T12:34:56.789Z'), - data: null, - repeatCount: 1, - }, - ]); - const initialState = { - ...emptyAppState, - records: initialRecords, - }; - const actions = [{type: Actions.CLEAR_RECORDS}]; - finalState = actions.reduce(Reducers, initialState); + initialRecords = Immutable().List([{ + kind: 'message', + sourceId: 'Test', + level: 'info', + text: 'test', + scopeName: null, + timestamp: new Date('2017-01-01T12:34:56.789Z'), + data: null, + repeatCount: 1 + }]); + const initialState = Object.assign({}, emptyAppState, { + records: initialRecords + }); + const actions = [{ + type: Actions().CLEAR_RECORDS + }]; + finalState = actions.reduce(_Reducers().default, initialState); }); - it('clears the records', () => { expect(finalState.records.size).toBe(0); }); - it("doesn't mutate the original records list", () => { expect(initialRecords.size).toBe(1); }); }); - describe('executor registration', () => { let dummyExecutor; let initialExecutors; let initialState; let finalState; - beforeEach(() => { dummyExecutor = createDummyExecutor('a'); initialExecutors = new Map([['a', dummyExecutor]]); - initialState = { - ...emptyAppState, - executors: initialExecutors, - }; + initialState = Object.assign({}, emptyAppState, { + executors: initialExecutors + }); }); - describe('REGISTER_EXECUTOR', () => { beforeEach(() => { - const actions = [ - { - type: Actions.REGISTER_EXECUTOR, - payload: { - executor: createDummyExecutor('b'), - }, - }, - ]; - finalState = actions.reduce(Reducers, initialState); + const actions = [{ + type: Actions().REGISTER_EXECUTOR, + payload: { + executor: createDummyExecutor('b') + } + }]; + finalState = actions.reduce(_Reducers().default, initialState); }); - it('adds an executor', () => { expect(finalState.executors.size).toBe(2); }); - it("doesn't mutate the original executor map", () => { expect(initialExecutors.size).toBe(1); }); }); - describe('unregisterExecutor', () => { beforeEach(() => { - const actions = [Actions.unregisterExecutor(dummyExecutor)]; - finalState = actions.reduce(Reducers, initialState); + const actions = [Actions().unregisterExecutor(dummyExecutor)]; + finalState = actions.reduce(_Reducers().default, initialState); }); - it('removes an executor', () => { expect(finalState.executors.size).toBe(0); }); - it("doesn't mutate the original executor map", () => { expect(initialExecutors.size).toBe(1); }); @@ -203,12 +201,12 @@ describe('createStateStream', () => { }); }); -export function createDummyExecutor(id: string): Executor { +function createDummyExecutor(id) { return { id, name: id, scopeName: 'text.plain', - send: (code: string) => {}, - output: Observable.create(observer => {}), + send: code => {}, + output: _RxMin.Observable.create(observer => {}) }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/getCurrentExecutorId-test.js b/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/getCurrentExecutorId-test.js index d2326a20..d66adaf8 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/getCurrentExecutorId-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/getCurrentExecutorId-test.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.createDummyExecutor = createDummyExecutor; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _getCurrentExecutorId() { + const data = _interopRequireDefault(require("../lib/getCurrentExecutorId")); + + _getCurrentExecutorId = function () { + return data; + }; + + return data; +} + +function Immutable() { + const data = _interopRequireWildcard(require("immutable")); + + Immutable = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,22 +39,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {Observable} from 'rxjs'; -import type {Executor} from '../lib/types'; -import getCurrentExecutorId from '../lib/getCurrentExecutorId'; -import * as Immutable from 'immutable'; - -export function createDummyExecutor(id: string): Executor { +function createDummyExecutor(id) { return { id, name: id, scopeName: 'text.plain', - send: (code: string) => {}, - output: Observable.create(observer => {}), + send: code => {}, + output: _RxMin.Observable.create(observer => {}) }; } @@ -32,20 +59,17 @@ const baseAppState = { executors: new Map([['a', createDummyExecutor('a')]]), providers: new Map(), providerStatuses: new Map(), - records: Immutable.List(), - history: [], + records: Immutable().List(), + history: [] }; - describe('getCurrentExecutorId', () => { it('gets the current executor', () => { - expect(getCurrentExecutorId(baseAppState)).toBe('a'); + expect((0, _getCurrentExecutorId().default)(baseAppState)).toBe('a'); }); - it('returns an executor even if the current id is null', () => { - const appState = { - ...baseAppState, - currentExecutorId: null, - }; - expect(getCurrentExecutorId(appState)).toBe('a'); + const appState = Object.assign({}, baseAppState, { + currentExecutorId: null + }); + expect((0, _getCurrentExecutorId().default)(appState)).toBe('a'); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/parseText-test.js b/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/parseText-test.js index 7c1d0495..90c94451 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/parseText-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/__tests__/parseText-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _parseText() { + const data = _interopRequireDefault(require("../lib/parseText")); + + _parseText = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,19 +20,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import parseText from '../lib/parseText'; - describe('parseText', () => { it('parses url pattern', () => { - const chunks = parseText('Message: https://facebook.com'); + const chunks = (0, _parseText().default)('Message: https://facebook.com'); expect(chunks.length).toBe(3); expect(chunks[0]).toBe('Message: '); expect(chunks[2]).toBe(''); - const reactElement = chunks[1]; expect(typeof reactElement).toBe('object'); // type React.Element @@ -28,4 +38,4 @@ describe('parseText', () => { expect(reactElement.props.children).toBe('https://facebook.com'); } }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ConsoleServiceClient.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ConsoleServiceClient.js index 370a78f6..e28e0755 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ConsoleServiceClient.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ConsoleServiceClient.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = _default; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +13,41 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {Observable} from 'rxjs'; -import type {Level} from './types'; - -type Send = (event: Object) => void; -type Events = Observable; - -export default function(send: Send, eventsFromService: Events) { +function _default(send, eventsFromService) { return { // TODO: Update these to be `(object: any, ...objects: Array): void` to allow for logging objects. - log(...args: Array): void { + log(...args) { send(createMessageEvent('log', args)); }, - error(...args: Array): void { + + error(...args) { send(createMessageEvent('error', args)); }, - warn(...args: Array): void { + + warn(...args) { send(createMessageEvent('warning', args)); }, - info(...args: Array): void { + + info(...args) { send(createMessageEvent('info', args)); }, - success(...args: Array): void { + + success(...args) { send(createMessageEvent('success', args)); - }, + } + }; } -function createMessageEvent(level: Level, args: Array) { +function createMessageEvent(level, args) { return { type: 'message', - data: {level, args}, + data: { + level, + args + } }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/getCurrentExecutorId.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/getCurrentExecutorId.js index 9efcc7ba..14274702 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/getCurrentExecutorId.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/getCurrentExecutorId.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = getCurrentExecutorId; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,17 +13,18 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +function getCurrentExecutorId(state) { + let { + currentExecutorId + } = state; -import type {AppState} from './types'; - -export default function getCurrentExecutorId(state: AppState): ?string { - let {currentExecutorId} = state; if (currentExecutorId == null) { const firstExecutor = Array.from(state.executors.values())[0]; currentExecutorId = firstExecutor && firstExecutor.id; } + return currentExecutorId; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/main.js index af5beda9..4367dafb 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/main.js @@ -1,114 +1,165 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type { - AppState, - ConsolePersistedState, - ConsoleService, - SourceInfo, - Message, - OutputProvider, - OutputProviderStatus, - OutputService, - Record, - RegisterExecutorFunction, - Store, -} from './types'; -import type {CreatePasteFunction} from './types'; - -import {List} from 'immutable'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import {destroyItemWhere} from 'nuclide-commons-atom/destroyItemWhere'; -import {Observable} from 'rxjs'; -import { - combineEpics, - createEpicMiddleware, -} from 'nuclide-commons/redux-observable'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as Actions from './redux/Actions'; -import * as Epics from './redux/Epics'; -import Reducers from './redux/Reducers'; -import {Console, WORKSPACE_VIEW_URI} from './ui/Console'; -import invariant from 'assert'; -import {applyMiddleware, createStore} from 'redux'; - -const MAXIMUM_SERIALIZED_MESSAGES_CONFIG = - 'atom-ide-console.maximumSerializedMessages'; -const MAXIMUM_SERIALIZED_HISTORY_CONFIG = - 'atom-ide-console.maximumSerializedHistory'; +"use strict"; -class Activation { - _disposables: UniversalDisposable; - _rawState: ?Object; - _store: Store; +function _immutable() { + const data = require("immutable"); + + _immutable = function () { + return data; + }; + + return data; +} + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _destroyItemWhere() { + const data = require("../../../../nuclide-commons-atom/destroyItemWhere"); + + _destroyItemWhere = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _reduxObservable() { + const data = require("../../../../nuclide-commons/redux-observable"); + + _reduxObservable = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; - constructor(rawState: ?Object) { + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("./redux/Actions")); + + Actions = function () { + return data; + }; + + return data; +} + +function Epics() { + const data = _interopRequireWildcard(require("./redux/Epics")); + + Epics = function () { + return data; + }; + + return data; +} + +function _Reducers() { + const data = _interopRequireDefault(require("./redux/Reducers")); + + _Reducers = function () { + return data; + }; + + return data; +} + +function _Console() { + const data = require("./ui/Console"); + + _Console = function () { + return data; + }; + + return data; +} + +function _reduxMin() { + const data = require("redux/dist/redux.min.js"); + + _reduxMin = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +const MAXIMUM_SERIALIZED_MESSAGES_CONFIG = 'atom-ide-console.maximumSerializedMessages'; +const MAXIMUM_SERIALIZED_HISTORY_CONFIG = 'atom-ide-console.maximumSerializedHistory'; + +class Activation { + constructor(rawState) { this._rawState = rawState; - this._disposables = new UniversalDisposable( - atom.contextMenu.add({ - '.console-record': [ - { - label: 'Copy Message', - command: 'console:copy-message', - }, - ], - }), - atom.commands.add('.console-record', 'console:copy-message', event => { - const el = event.target; - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) - if (el == null || typeof el.innerText !== 'string') { - return; - } - atom.clipboard.write(el.innerText); - }), - atom.commands.add('atom-workspace', 'console:clear', () => - this._getStore().dispatch(Actions.clearRecords()), - ), - featureConfig.observe( - 'atom-ide-console.maximumMessageCount', - (maxMessageCount: any) => { - this._getStore().dispatch( - Actions.setMaxMessageCount(maxMessageCount), - ); - }, - ), - Observable.combineLatest( - observableFromSubscribeFunction(cb => - atom.config.observe('editor.fontSize', cb), - ), - featureConfig.observeAsStream('atom-ide-console.fontScale'), - (fontSize, fontScale) => fontSize * parseFloat(fontScale), - ) - .map(Actions.setFontSize) - .subscribe(this._store.dispatch), - this._registerCommandAndOpener(), - ); + this._disposables = new (_UniversalDisposable().default)(atom.contextMenu.add({ + '.console-record': [{ + label: 'Copy Message', + command: 'console:copy-message' + }] + }), atom.commands.add('.console-record', 'console:copy-message', event => { + const el = event.target; // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + + if (el == null || typeof el.innerText !== 'string') { + return; + } + + atom.clipboard.write(el.innerText); + }), atom.commands.add('atom-workspace', 'console:clear', () => this._getStore().dispatch(Actions().clearRecords())), _featureConfig().default.observe('atom-ide-console.maximumMessageCount', maxMessageCount => { + this._getStore().dispatch(Actions().setMaxMessageCount(maxMessageCount)); + }), _RxMin.Observable.combineLatest((0, _event().observableFromSubscribeFunction)(cb => atom.config.observe('editor.fontSize', cb)), _featureConfig().default.observeAsStream('atom-ide-console.fontScale'), (fontSize, fontScale) => fontSize * parseFloat(fontScale)).map(Actions().setFontSize).subscribe(this._store.dispatch), this._registerCommandAndOpener()); } - _getStore(): Store { + _getStore() { if (this._store == null) { const initialState = deserializeAppState(this._rawState); - const epics = Object.keys(Epics) - .map(k => Epics[k]) - .filter(epic => typeof epic === 'function'); - const rootEpic = combineEpics(...epics); - this._store = createStore( - Reducers, - initialState, - applyMiddleware(createEpicMiddleware(rootEpic)), - ); + const epics = Object.keys(Epics()).map(k => Epics()[k]).filter(epic => typeof epic === 'function'); + const rootEpic = (0, _reduxObservable().combineEpics)(...epics); + this._store = (0, _reduxMin().createStore)(_Reducers().default, initialState, (0, _reduxMin().applyMiddleware)((0, _reduxObservable().createEpicMiddleware)(rootEpic))); } + return this._store; } @@ -116,82 +167,88 @@ class Activation { this._disposables.dispose(); } - consumeToolBar(getToolBar: toolbar$GetToolbar): void { + consumeToolBar(getToolBar) { const toolBar = getToolBar('nuclide-console'); toolBar.addButton({ icon: 'nuclicon-console', callback: 'console:toggle', tooltip: 'Toggle Console', - priority: 700, + priority: 700 }); + this._disposables.add(() => { toolBar.removeItems(); }); } - consumePasteProvider(provider: any): IDisposable { - const createPaste: CreatePasteFunction = provider.createPaste; - this._getStore().dispatch(Actions.setCreatePasteFunction(createPaste)); - return new UniversalDisposable(() => { + consumePasteProvider(provider) { + const createPaste = provider.createPaste; + + this._getStore().dispatch(Actions().setCreatePasteFunction(createPaste)); + + return new (_UniversalDisposable().default)(() => { if (this._getStore().getState().createPasteFunction === createPaste) { - this._getStore().dispatch(Actions.setCreatePasteFunction(null)); + this._getStore().dispatch(Actions().setCreatePasteFunction(null)); } }); } - consumeWatchEditor(watchEditor: atom$AutocompleteWatchEditor): IDisposable { - this._getStore().dispatch(Actions.setWatchEditor(watchEditor)); - return new UniversalDisposable(() => { + consumeWatchEditor(watchEditor) { + this._getStore().dispatch(Actions().setWatchEditor(watchEditor)); + + return new (_UniversalDisposable().default)(() => { // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) if (this._getStore().getState().watchEditor === watchEditor) { - this._getStore().dispatch(Actions.setWatchEditor(null)); + this._getStore().dispatch(Actions().setWatchEditor(null)); } }); } - provideAutocomplete(): atom$AutocompleteProvider { + provideAutocomplete() { const activation = this; return { labels: ['nuclide-console'], selector: '*', // Copies Chrome devtools and puts history suggestions at the bottom. suggestionPriority: -1, + async getSuggestions(request) { // History provides suggestion only on exact match to current input. const prefix = request.editor.getText(); - const history = activation._getStore().getState().history; - // Use a set to remove duplicates. + + const history = activation._getStore().getState().history; // Use a set to remove duplicates. + + const seen = new Set(history); - return Array.from(seen) - .filter(text => text.startsWith(prefix)) - .map(text => ({text, replacementPrefix: prefix})); - }, + return Array.from(seen).filter(text => text.startsWith(prefix)).map(text => ({ + text, + replacementPrefix: prefix + })); + } + }; } - _registerCommandAndOpener(): UniversalDisposable { - return new UniversalDisposable( - atom.workspace.addOpener(uri => { - if (uri === WORKSPACE_VIEW_URI) { - return new Console({store: this._getStore()}); - } - }), - () => destroyItemWhere(item => item instanceof Console), - atom.commands.add('atom-workspace', 'console:toggle', () => { - atom.workspace.toggle(WORKSPACE_VIEW_URI); - }), - ); + _registerCommandAndOpener() { + return new (_UniversalDisposable().default)(atom.workspace.addOpener(uri => { + if (uri === _Console().WORKSPACE_VIEW_URI) { + return new (_Console().Console)({ + store: this._getStore() + }); + } + }), () => (0, _destroyItemWhere().destroyItemWhere)(item => item instanceof _Console().Console), atom.commands.add('atom-workspace', 'console:toggle', () => { + atom.workspace.toggle(_Console().WORKSPACE_VIEW_URI); + })); } - deserializeConsole(state: ConsolePersistedState): Console { - return new Console({ + deserializeConsole(state) { + return new (_Console().Console)({ store: this._getStore(), initialFilterText: state.filterText, initialEnableRegExpFilter: state.enableRegExpFilter, - initialUnselectedSourceIds: state.unselectedSourceIds, + initialUnselectedSourceIds: state.unselectedSourceIds }); } - /** * This service provides a factory for creating a console object tied to a particular source. If * the consumer wants to expose starting and stopping functionality through the Console UI (for @@ -203,176 +260,212 @@ class Activation { * package is disabled). This will remove the source from the Console UI's filter list (as long as * there aren't any remaining messages from the source). */ - provideConsole(): ConsoleService { + + + provideConsole() { // Create a local, nullable reference so that the service consumers don't keep the Activation // instance in memory. let activation = this; + this._disposables.add(() => { activation = null; }); - return (sourceInfo: SourceInfo) => { - invariant(activation != null); + return sourceInfo => { + if (!(activation != null)) { + throw new Error("Invariant violation: \"activation != null\""); + } + let disposed; - activation._getStore().dispatch(Actions.registerSource(sourceInfo)); + + activation._getStore().dispatch(Actions().registerSource(sourceInfo)); + const console = { // TODO: Update these to be (object: any, ...objects: Array): void. - log(object: string): void { - console.append({text: object, level: 'log'}); + log(object) { + console.append({ + text: object, + level: 'log' + }); }, - warn(object: string): void { - console.append({text: object, level: 'warning'}); + + warn(object) { + console.append({ + text: object, + level: 'warning' + }); }, - error(object: string): void { - console.append({text: object, level: 'error'}); + + error(object) { + console.append({ + text: object, + level: 'error' + }); }, - info(object: string): void { - console.append({text: object, level: 'info'}); + + info(object) { + console.append({ + text: object, + level: 'info' + }); }, - success(object: string): void { - console.append({text: object, level: 'success'}); + + success(object) { + console.append({ + text: object, + level: 'success' + }); }, - append(message: Message): void { - invariant(activation != null && !disposed); - activation._getStore().dispatch( - Actions.recordReceived({ - text: message.text, - level: message.level, - format: message.format, - data: message.data, - tags: message.tags, - scopeName: message.scopeName, - sourceId: sourceInfo.id, - kind: message.kind || 'message', - timestamp: new Date(), // TODO: Allow this to come with the message? - repeatCount: 1, - }), - ); + + append(message) { + if (!(activation != null && !disposed)) { + throw new Error("Invariant violation: \"activation != null && !disposed\""); + } + + activation._getStore().dispatch(Actions().recordReceived({ + text: message.text, + level: message.level, + format: message.format, + data: message.data, + tags: message.tags, + scopeName: message.scopeName, + sourceId: sourceInfo.id, + kind: message.kind || 'message', + timestamp: new Date(), + // TODO: Allow this to come with the message? + repeatCount: 1 + })); }, - setStatus(status: OutputProviderStatus): void { - invariant(activation != null && !disposed); - activation - ._getStore() - .dispatch(Actions.updateStatus(sourceInfo.id, status)); + + setStatus(status) { + if (!(activation != null && !disposed)) { + throw new Error("Invariant violation: \"activation != null && !disposed\""); + } + + activation._getStore().dispatch(Actions().updateStatus(sourceInfo.id, status)); }, - dispose(): void { - invariant(activation != null); + + dispose() { + if (!(activation != null)) { + throw new Error("Invariant violation: \"activation != null\""); + } + if (!disposed) { disposed = true; - activation - ._getStore() - .dispatch(Actions.removeSource(sourceInfo.id)); + + activation._getStore().dispatch(Actions().removeSource(sourceInfo.id)); } - }, + } + }; return console; }; } - provideOutputService(): OutputService { + provideOutputService() { // Create a local, nullable reference so that the service consumers don't keep the Activation // instance in memory. let activation = this; + this._disposables.add(() => { activation = null; }); return { - registerOutputProvider(outputProvider: OutputProvider): IDisposable { - invariant(activation != null, 'Output service used after deactivation'); - activation - ._getStore() - .dispatch(Actions.registerOutputProvider(outputProvider)); - return new UniversalDisposable(() => { + registerOutputProvider(outputProvider) { + if (!(activation != null)) { + throw new Error('Output service used after deactivation'); + } + + activation._getStore().dispatch(Actions().registerOutputProvider(outputProvider)); + + return new (_UniversalDisposable().default)(() => { if (activation != null) { - activation - ._getStore() - .dispatch(Actions.unregisterOutputProvider(outputProvider)); + activation._getStore().dispatch(Actions().unregisterOutputProvider(outputProvider)); } }); - }, + } + }; } - provideRegisterExecutor(): RegisterExecutorFunction { + provideRegisterExecutor() { // Create a local, nullable reference so that the service consumers don't keep the Activation // instance in memory. let activation = this; + this._disposables.add(() => { activation = null; }); return executor => { - invariant( - activation != null, - 'Executor registration attempted after deactivation', - ); - activation._getStore().dispatch(Actions.registerExecutor(executor)); - return new UniversalDisposable(() => { + if (!(activation != null)) { + throw new Error('Executor registration attempted after deactivation'); + } + + activation._getStore().dispatch(Actions().registerExecutor(executor)); + + return new (_UniversalDisposable().default)(() => { if (activation != null) { - activation._getStore().dispatch(Actions.unregisterExecutor(executor)); + activation._getStore().dispatch(Actions().unregisterExecutor(executor)); } }); }; } - serialize(): Object { + serialize() { if (this._store == null) { return {}; } - const maximumSerializedMessages: number = (featureConfig.get( - MAXIMUM_SERIALIZED_MESSAGES_CONFIG, - ): any); - const maximumSerializedHistory: number = (featureConfig.get( - MAXIMUM_SERIALIZED_HISTORY_CONFIG, - ): any); + + const maximumSerializedMessages = _featureConfig().default.get(MAXIMUM_SERIALIZED_MESSAGES_CONFIG); + + const maximumSerializedHistory = _featureConfig().default.get(MAXIMUM_SERIALIZED_HISTORY_CONFIG); + return { - records: this._store - .getState() - .records.slice(-maximumSerializedMessages) - .toArray() - .map(record => { - // `Executor` is not serializable. Make sure to remove it first. - const {executor, ...rest} = record; - return rest; - }), - history: this._store.getState().history.slice(-maximumSerializedHistory), + records: this._store.getState().records.slice(-maximumSerializedMessages).toArray().map(record => { + // `Executor` is not serializable. Make sure to remove it first. + const { + executor + } = record, + rest = _objectWithoutProperties(record, ["executor"]); + + return rest; + }), + history: this._store.getState().history.slice(-maximumSerializedHistory) }; } + } -function deserializeAppState(rawState: ?Object): AppState { +function deserializeAppState(rawState) { return { executors: new Map(), createPasteFunction: null, currentExecutorId: null, - records: - rawState && rawState.records - ? List(rawState.records.map(deserializeRecord)) - : List(), + records: rawState && rawState.records ? (0, _immutable().List)(rawState.records.map(deserializeRecord)) : (0, _immutable().List)(), history: rawState && rawState.history ? rawState.history : [], providers: new Map(), providerStatuses: new Map(), - // This value will be replaced with the value form the config. We just use `POSITIVE_INFINITY` // here to conform to the AppState type defintion. - maxMessageCount: Number.POSITIVE_INFINITY, + maxMessageCount: Number.POSITIVE_INFINITY }; } -function deserializeRecord(record: Object): Record { - return { - ...record, - timestamp: parseDate(record.timestamp) || new Date(0), - }; +function deserializeRecord(record) { + return Object.assign({}, record, { + timestamp: parseDate(record.timestamp) || new Date(0) + }); } -function parseDate(raw: ?string): ?Date { +function parseDate(raw) { if (raw == null) { return null; } + const date = new Date(raw); return isNaN(date.getTime()) ? null : date; } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/parseText.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/parseText.js index ce1066e1..faf56a67 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/parseText.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/parseText.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = parseText; + +var React = _interopRequireWildcard(require("react")); + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _string() { + const data = require("../../../../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,17 +39,11 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import * as React from 'react'; -import featureConfig from 'nuclide-commons-atom/feature-config'; - -import {URL_REGEX} from 'nuclide-commons/string'; const DIFF_PATTERN = '\\b[dD][1-9][0-9]{5,}\\b'; const TASK_PATTERN = '\\b[tT]\\d+\\b'; - /** * This does NOT contain a pattern to match file references. This is because file paths do not * contain sufficient information to locate a file. Consider `b/c/d.txt`. What is it relative to? @@ -26,56 +53,51 @@ const TASK_PATTERN = '\\b[tT]\\d+\\b'; * solution probably requires both standardization on remote path (e.g. NuclideUris) and some * mechanism for message creators to mark up their messages with this information (e.g. ``). */ -const CLICKABLE_PATTERNS = `(${DIFF_PATTERN})|(${TASK_PATTERN})|(${ - URL_REGEX.source -})`; + +const CLICKABLE_PATTERNS = `(${DIFF_PATTERN})|(${TASK_PATTERN})|(${_string().URL_REGEX.source})`; const CLICKABLE_RE = new RegExp(CLICKABLE_PATTERNS, 'g'); -function toString(value: mixed): string { +function toString(value) { return typeof value === 'string' ? value : ''; } - /** * Parse special entities into links. In the future, it would be great to add a service so that we * could add new clickable things and to allow providers to mark specific ranges as links to things * that only they can know (e.g. relative paths output in BUCK messages). For now, however, we'll * just use some pattern settings and hardcode the patterns we care about. */ -export default function parseText( - text: string, -): Array> { + + +function parseText(text) { const chunks = []; let lastIndex = 0; let index = 0; + while (true) { const match = CLICKABLE_RE.exec(text); + if (match == null) { break; } - const matchedText = match[0]; + const matchedText = match[0]; // Add all the text since our last match. - // Add all the text since our last match. - chunks.push( - text.slice(lastIndex, CLICKABLE_RE.lastIndex - matchedText.length), - ); + chunks.push(text.slice(lastIndex, CLICKABLE_RE.lastIndex - matchedText.length)); lastIndex = CLICKABLE_RE.lastIndex; - let href; let handleOnClick; + if (match[1] != null) { // It's a diff - const url = toString( - featureConfig.get('atom-ide-console.diffUrlPattern'), - ); + const url = toString(_featureConfig().default.get('atom-ide-console.diffUrlPattern')); + if (url !== '') { href = url.replace('%s', matchedText); } } else if (match[2] != null) { // It's a task - const url = toString( - featureConfig.get('atom-ide-console.taskUrlPattern'), - ); + const url = toString(_featureConfig().default.get('atom-ide-console.taskUrlPattern')); + if (url !== '') { href = url.replace('%s', matchedText.slice(1)); } @@ -84,26 +106,17 @@ export default function parseText( href = matchedText; } - chunks.push( - // flowlint-next-line sketchy-null-string:off - href ? ( - - {matchedText} - - ) : ( - matchedText - ), - ); - + chunks.push( // flowlint-next-line sketchy-null-string:off + href ? React.createElement("a", { + key: `r${index}`, + href: href, + target: "_blank", + onClick: handleOnClick + }, matchedText) : matchedText); index++; - } + } // Add any remaining text. - // Add any remaining text. - chunks.push(text.slice(lastIndex)); + chunks.push(text.slice(lastIndex)); return chunks; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/recordsChanged.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/recordsChanged.js index 7a2c5dde..03d6f41a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/recordsChanged.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/recordsChanged.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = recordsChanged; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,25 +13,18 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {DisplayableRecord} from './types'; - -import idx from 'idx'; - /** * Check to see if the records have changed. This is optimized to take advantage of the knowledge * knowledge that record lists are only ever appended. */ -export default function recordsChanged( - a: Array, - b: Array, -): boolean { - return ( - a.length !== b.length || idx(last(a), _ => _.id) !== idx(last(b), _ => _.id) - ); +function recordsChanged(a, b) { + var _ref, _ref2; + + return a.length !== b.length || ((_ref = last(a)) != null ? _ref.id : _ref) !== ((_ref2 = last(b)) != null ? _ref2.id : _ref2); } -const last = arr => arr[arr.length - 1]; +const last = arr => arr[arr.length - 1]; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Actions.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Actions.js index 7d581b6a..c0e30476 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Actions.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Actions.js @@ -1,3 +1,27 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.clearRecords = clearRecords; +exports.recordReceived = recordReceived; +exports.registerExecutor = registerExecutor; +exports.execute = execute; +exports.registerOutputProvider = registerOutputProvider; +exports.registerRecordProvider = registerRecordProvider; +exports.registerSource = registerSource; +exports.unregisterRecordProvider = unregisterRecordProvider; +exports.unregisterOutputProvider = unregisterOutputProvider; +exports.selectExecutor = selectExecutor; +exports.setMaxMessageCount = setMaxMessageCount; +exports.removeSource = removeSource; +exports.unregisterExecutor = unregisterExecutor; +exports.updateStatus = updateStatus; +exports.setCreatePasteFunction = setCreatePasteFunction; +exports.setWatchEditor = setWatchEditor; +exports.setFontSize = setFontSize; +exports.SET_FONT_SIZE = exports.UPDATE_STATUS = exports.REMOVE_SOURCE = exports.REGISTER_SOURCE = exports.RECORD_RECEIVED = exports.SET_MAX_MESSAGE_COUNT = exports.SELECT_EXECUTOR = exports.REGISTER_RECORD_PROVIDER = exports.EXECUTE = exports.REGISTER_EXECUTOR = exports.SET_WATCH_EDITOR_FUNCTION = exports.SET_CREATE_PASTE_FUNCTION = exports.CLEAR_RECORDS = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,67 +30,74 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +const CLEAR_RECORDS = 'CLEAR_RECORDS'; +exports.CLEAR_RECORDS = CLEAR_RECORDS; +const SET_CREATE_PASTE_FUNCTION = 'SET_CREATE_PASTE_FUNCTION'; +exports.SET_CREATE_PASTE_FUNCTION = SET_CREATE_PASTE_FUNCTION; +const SET_WATCH_EDITOR_FUNCTION = 'SET_WATCH_EDITOR_FUNCTION'; +exports.SET_WATCH_EDITOR_FUNCTION = SET_WATCH_EDITOR_FUNCTION; +const REGISTER_EXECUTOR = 'REGISTER_EXECUTOR'; +exports.REGISTER_EXECUTOR = REGISTER_EXECUTOR; +const EXECUTE = 'EXECUTE'; +exports.EXECUTE = EXECUTE; +const REGISTER_RECORD_PROVIDER = 'REGISTER_RECORD_PROVIDER'; +exports.REGISTER_RECORD_PROVIDER = REGISTER_RECORD_PROVIDER; +const SELECT_EXECUTOR = 'SELECT_EXECUTOR'; +exports.SELECT_EXECUTOR = SELECT_EXECUTOR; +const SET_MAX_MESSAGE_COUNT = 'SET_MAX_MESSAGE_COUNT'; +exports.SET_MAX_MESSAGE_COUNT = SET_MAX_MESSAGE_COUNT; +const RECORD_RECEIVED = 'RECORD_RECEIVED'; +exports.RECORD_RECEIVED = RECORD_RECEIVED; +const REGISTER_SOURCE = 'REGISTER_SOURCE'; +exports.REGISTER_SOURCE = REGISTER_SOURCE; +const REMOVE_SOURCE = 'REMOVE_SOURCE'; +exports.REMOVE_SOURCE = REMOVE_SOURCE; +const UPDATE_STATUS = 'UPDATE_STATUS'; +exports.UPDATE_STATUS = UPDATE_STATUS; +const SET_FONT_SIZE = 'SET_FONT_SIZE'; +exports.SET_FONT_SIZE = SET_FONT_SIZE; + +function clearRecords() { + return { + type: CLEAR_RECORDS + }; +} -import type { - Action, - Executor, - OutputProvider, - OutputProviderStatus, - Record, - RecordProvider, - SourceInfo, -} from '../types'; - -import type {CreatePasteFunction} from '../types'; - -export const CLEAR_RECORDS = 'CLEAR_RECORDS'; -export const SET_CREATE_PASTE_FUNCTION = 'SET_CREATE_PASTE_FUNCTION'; -export const SET_WATCH_EDITOR_FUNCTION = 'SET_WATCH_EDITOR_FUNCTION'; -export const REGISTER_EXECUTOR = 'REGISTER_EXECUTOR'; -export const EXECUTE = 'EXECUTE'; -export const REGISTER_RECORD_PROVIDER = 'REGISTER_RECORD_PROVIDER'; -export const SELECT_EXECUTOR = 'SELECT_EXECUTOR'; -export const SET_MAX_MESSAGE_COUNT = 'SET_MAX_MESSAGE_COUNT'; -export const RECORD_RECEIVED = 'RECORD_RECEIVED'; -export const REGISTER_SOURCE = 'REGISTER_SOURCE'; -export const REMOVE_SOURCE = 'REMOVE_SOURCE'; -export const UPDATE_STATUS = 'UPDATE_STATUS'; -export const SET_FONT_SIZE = 'SET_FONT_SIZE'; - -export function clearRecords(): Action { - return {type: CLEAR_RECORDS}; -} - -export function recordReceived(record: Record): Action { +function recordReceived(record) { return { type: RECORD_RECEIVED, - payload: {record}, + payload: { + record + } }; } -export function registerExecutor(executor: Executor): Action { +function registerExecutor(executor) { return { type: REGISTER_EXECUTOR, - payload: {executor}, + payload: { + executor + } }; } -export function execute(code: string): Action { +function execute(code) { return { type: EXECUTE, - payload: {code}, + payload: { + code + } }; } -export function registerOutputProvider(outputProvider: OutputProvider): Action { +function registerOutputProvider(outputProvider) { // Transform the messages into actions and merge them into the action stream. // TODO: Add enabling/disabling of registered source and only subscribe when enabled. That // way, we won't trigger cold observer side-effects when we don't need the results. - return registerRecordProvider({ - ...outputProvider, + return registerRecordProvider(Object.assign({}, outputProvider, { records: outputProvider.messages.map(message => ({ // We duplicate the properties here instead of using spread because Flow (currently) has some // issues with spread. @@ -75,98 +106,105 @@ export function registerOutputProvider(outputProvider: OutputProvider): Action { data: message.data, tags: message.tags, repeatCount: 1, - kind: 'message', sourceId: outputProvider.id, scopeName: null, // Eventually, we'll want to allow providers to specify custom timestamps for records. - timestamp: new Date(), - })), - }); + timestamp: new Date() + })) + })); } -export function registerRecordProvider(recordProvider: RecordProvider): Action { +function registerRecordProvider(recordProvider) { return { type: REGISTER_RECORD_PROVIDER, - payload: {recordProvider}, + payload: { + recordProvider + } }; } -export function registerSource(source: SourceInfo): Action { +function registerSource(source) { return { type: REGISTER_SOURCE, - payload: {source}, + payload: { + source + } }; } -export function unregisterRecordProvider( - recordProvider: RecordProvider, -): Action { +function unregisterRecordProvider(recordProvider) { return removeSource(recordProvider.id); } -export function unregisterOutputProvider( - outputProvider: OutputProvider, -): Action { +function unregisterOutputProvider(outputProvider) { return removeSource(outputProvider.id); } -export function selectExecutor(executorId: string): Action { +function selectExecutor(executorId) { return { type: SELECT_EXECUTOR, - payload: {executorId}, + payload: { + executorId + } }; } -export function setMaxMessageCount(maxMessageCount: number): Action { +function setMaxMessageCount(maxMessageCount) { return { type: SET_MAX_MESSAGE_COUNT, - payload: {maxMessageCount}, + payload: { + maxMessageCount + } }; } -export function removeSource(sourceId: string): Action { +function removeSource(sourceId) { return { type: REMOVE_SOURCE, - payload: {sourceId}, + payload: { + sourceId + } }; } -export function unregisterExecutor(executor: Executor): Action { +function unregisterExecutor(executor) { return removeSource(executor.id); } -export function updateStatus( - providerId: string, - status: OutputProviderStatus, -): Action { +function updateStatus(providerId, status) { return { type: UPDATE_STATUS, - payload: {providerId, status}, + payload: { + providerId, + status + } }; } -export function setCreatePasteFunction( - createPasteFunction: ?CreatePasteFunction, -): Action { +function setCreatePasteFunction(createPasteFunction) { return { type: SET_CREATE_PASTE_FUNCTION, - payload: {createPasteFunction}, + payload: { + createPasteFunction + } }; } -export function setWatchEditor( - watchEditor: ?atom$AutocompleteWatchEditor, -): Action { +function setWatchEditor(watchEditor) { return { type: SET_WATCH_EDITOR_FUNCTION, - payload: {watchEditor}, + payload: { + watchEditor + } }; } -export function setFontSize(fontSize: number): Action { +function setFontSize(fontSize) { return { type: SET_FONT_SIZE, - payload: {fontSize}, + payload: { + fontSize + } }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Epics.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Epics.js index ec0c9614..5f3eccc4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Epics.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Epics.js @@ -1,3 +1,59 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.registerExecutorEpic = registerExecutorEpic; +exports.executeEpic = executeEpic; +exports.trackEpic = trackEpic; +exports.registerRecordProviderEpic = registerRecordProviderEpic; + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("./Actions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _getCurrentExecutorId() { + const data = _interopRequireDefault(require("../getCurrentExecutorId")); + + _getCurrentExecutorId = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _analytics() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,133 +62,113 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {Action, Store} from '../types'; -import type {ActionsObservable} from 'nuclide-commons/redux-observable'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import * as Actions from './Actions'; -import getCurrentExecutorId from '../getCurrentExecutorId'; -import invariant from 'assert'; -import {Observable} from 'rxjs'; -import analytics from 'nuclide-commons/analytics'; - /** * Register a record provider for every executor. */ -export function registerExecutorEpic( - actions: ActionsObservable, - store: Store, -): Observable { - return actions.ofType(Actions.REGISTER_EXECUTOR).map(action => { - invariant(action.type === Actions.REGISTER_EXECUTOR); - const {executor} = action.payload; - return Actions.registerRecordProvider({ +function registerExecutorEpic(actions, store) { + return actions.ofType(Actions().REGISTER_EXECUTOR).map(action => { + if (!(action.type === Actions().REGISTER_EXECUTOR)) { + throw new Error("Invariant violation: \"action.type === Actions.REGISTER_EXECUTOR\""); + } + + const { + executor + } = action.payload; + return Actions().registerRecordProvider({ id: executor.id, // $FlowIssue: Flow is having some trouble with the spread here. - records: executor.output.map(message => ({ - ...message, + records: executor.output.map(message => Object.assign({}, message, { kind: 'response', sourceId: executor.id, - scopeName: null, // The output won't be in the language's grammar. + scopeName: null, + // The output won't be in the language's grammar. // Eventually, we'll want to allow providers to specify custom timestamps for records. timestamp: new Date(), - executor, - })), + executor + })) }); }); } - /** * Execute the provided code using the current executor. */ -export function executeEpic( - actions: ActionsObservable, - store: Store, -): Observable { - return actions.ofType(Actions.EXECUTE).flatMap(action => { - invariant(action.type === Actions.EXECUTE); - const {code} = action.payload; - const currentExecutorId = getCurrentExecutorId(store.getState()); - // flowlint-next-line sketchy-null-string:off - invariant(currentExecutorId); + + +function executeEpic(actions, store) { + return actions.ofType(Actions().EXECUTE).flatMap(action => { + if (!(action.type === Actions().EXECUTE)) { + throw new Error("Invariant violation: \"action.type === Actions.EXECUTE\""); + } + + const { + code + } = action.payload; + const currentExecutorId = (0, _getCurrentExecutorId().default)(store.getState()); // flowlint-next-line sketchy-null-string:off + + if (!currentExecutorId) { + throw new Error("Invariant violation: \"currentExecutorId\""); + } const executor = store.getState().executors.get(currentExecutorId); - invariant(executor != null); - // TODO: Is this the best way to do this? Might want to go through nuclide-executors and have + if (!(executor != null)) { + throw new Error("Invariant violation: \"executor != null\""); + } // TODO: Is this the best way to do this? Might want to go through nuclide-executors and have // that register output sources? - return ( - Observable.of( - Actions.recordReceived({ - // Eventually, we'll want to allow providers to specify custom timestamps for records. - timestamp: new Date(), - sourceId: currentExecutorId, - kind: 'request', - level: 'log', - text: code, - scopeName: executor.scopeName, - data: null, - repeatCount: 1, - }), - ) - // Execute the code as a side-effect. - .finally(() => { - executor.send(code); - }) - ); + + + return _RxMin.Observable.of(Actions().recordReceived({ + // Eventually, we'll want to allow providers to specify custom timestamps for records. + timestamp: new Date(), + sourceId: currentExecutorId, + kind: 'request', + level: 'log', + text: code, + scopeName: executor.scopeName, + data: null, + repeatCount: 1 + })) // Execute the code as a side-effect. + .finally(() => { + executor.send(code); + }); }); } -export function trackEpic( - actions: ActionsObservable, - store: Store, -): Observable { - return actions - .ofType(Actions.EXECUTE) - .map(action => ({type: 'console:execute'})) - .do(analytics.trackEvent) - .ignoreElements(); +function trackEpic(actions, store) { + return actions.ofType(Actions().EXECUTE).map(action => ({ + type: 'console:execute' + })).do(_analytics().default.trackEvent).ignoreElements(); } -export function registerRecordProviderEpic( - actions: ActionsObservable, - store: Store, -): Observable { - return actions.ofType(Actions.REGISTER_RECORD_PROVIDER).flatMap(action => { - invariant(action.type === Actions.REGISTER_RECORD_PROVIDER); - const {recordProvider} = action.payload; +function registerRecordProviderEpic(actions, store) { + return actions.ofType(Actions().REGISTER_RECORD_PROVIDER).flatMap(action => { + if (!(action.type === Actions().REGISTER_RECORD_PROVIDER)) { + throw new Error("Invariant violation: \"action.type === Actions.REGISTER_RECORD_PROVIDER\""); + } - // Transform the messages into actions and merge them into the action stream. + const { + recordProvider + } = action.payload; // Transform the messages into actions and merge them into the action stream. // TODO: Add enabling/disabling of registered source and only subscribe when enabled. That // way, we won't trigger cold observer side-effects when we don't need the results. - const messageActions = recordProvider.records.map(Actions.recordReceived); - - // TODO: Can this be delayed until sometime after registration? - const statusActions = - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) - typeof recordProvider.observeStatus === 'function' - ? observableFromSubscribeFunction(recordProvider.observeStatus).map( - status => Actions.updateStatus(recordProvider.id, status), - ) - : Observable.empty(); - - const unregisteredEvents = actions - .ofType(Actions.REMOVE_SOURCE) - .filter(a => { - invariant(a.type === Actions.REMOVE_SOURCE); - return a.payload.sourceId === recordProvider.id; - }); - - return Observable.merge( - Observable.of( - Actions.registerSource({...recordProvider, name: recordProvider.id}), - ), - messageActions, - statusActions, - ).takeUntil(unregisteredEvents); + + const messageActions = recordProvider.records.map(Actions().recordReceived); // TODO: Can this be delayed until sometime after registration? + + const statusActions = // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + typeof recordProvider.observeStatus === 'function' ? (0, _event().observableFromSubscribeFunction)(recordProvider.observeStatus).map(status => Actions().updateStatus(recordProvider.id, status)) : _RxMin.Observable.empty(); + const unregisteredEvents = actions.ofType(Actions().REMOVE_SOURCE).filter(a => { + if (!(a.type === Actions().REMOVE_SOURCE)) { + throw new Error("Invariant violation: \"a.type === Actions.REMOVE_SOURCE\""); + } + + return a.payload.sourceId === recordProvider.id; + }); + return _RxMin.Observable.merge(_RxMin.Observable.of(Actions().registerSource(Object.assign({}, recordProvider, { + name: recordProvider.id + }))), messageActions, statusActions).takeUntil(unregisteredEvents); }); -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Reducers.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Reducers.js index 62d3a727..784202eb 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Reducers.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Reducers.js @@ -1,3 +1,42 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = accumulateState; + +function _immutable() { + const data = require("immutable"); + + _immutable = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("./Actions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,191 +45,180 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +const RECORD_PROPERTIES_TO_COMPARE = ['text', 'level', 'format', 'scopeName', 'sourceId', 'kind']; -import type {Action, AppState, Record} from '../types'; - -import {List} from 'immutable'; -import {arrayEqual} from 'nuclide-commons/collection'; -import * as Actions from './Actions'; - -const RECORD_PROPERTIES_TO_COMPARE = [ - 'text', - 'level', - 'format', - 'scopeName', - 'sourceId', - 'kind', -]; - -function shouldAccumulateRecordCount( - recordA: Record, - recordB: Record, -): boolean { - if ( - String(recordA.sourceId) - .toLowerCase() - .includes('debugger') || - String(recordB.sourceId) - .toLowerCase() - .includes('debugger') - ) { +function shouldAccumulateRecordCount(recordA, recordB) { + if (String(recordA.sourceId).toLowerCase().includes('debugger') || String(recordB.sourceId).toLowerCase().includes('debugger')) { return false; } - const areRelevantPropertiesEqual = RECORD_PROPERTIES_TO_COMPARE.every( - prop => recordA[prop] === recordB[prop], - ); - // if data exists, we should not accumulate this into the previous record - const doesDataExist = recordA.data || recordB.data; + const areRelevantPropertiesEqual = RECORD_PROPERTIES_TO_COMPARE.every(prop => recordA[prop] === recordB[prop]); // if data exists, we should not accumulate this into the previous record + const doesDataExist = recordA.data || recordB.data; const recATags = recordA.tags; const recBTags = recordB.tags; - const areTagsEqual = - (!recATags && !recBTags) || - (recATags && recBTags && arrayEqual(recATags, recBTags)); - - return ( - areRelevantPropertiesEqual && - !Boolean(doesDataExist) && - Boolean(areTagsEqual) - ); + const areTagsEqual = !recATags && !recBTags || recATags && recBTags && (0, _collection().arrayEqual)(recATags, recBTags); + return areRelevantPropertiesEqual && !Boolean(doesDataExist) && Boolean(areTagsEqual); } -export default function accumulateState( - state: AppState, - action: Action, -): AppState { +function accumulateState(state, action) { switch (action.type) { - case Actions.RECORD_RECEIVED: { - const {record} = action.payload; - let nextRecords = state.records; - - // check if the message is exactly the same as the previous one, if so - // we add a count to it. - const lastRecord = nextRecords.last(); - if ( - lastRecord != null && - shouldAccumulateRecordCount(lastRecord, record) - ) { - // Update the last record. Don't use `splice()` because that's O(n) - const updatedRecord: Record = { - ...lastRecord, - repeatCount: lastRecord.repeatCount + 1, - timestamp: record.timestamp, - }; - nextRecords = nextRecords.pop().push(updatedRecord); - } else { - nextRecords = nextRecords.push(record); + case Actions().RECORD_RECEIVED: + { + const { + record + } = action.payload; + let nextRecords = state.records; // check if the message is exactly the same as the previous one, if so + // we add a count to it. + + const lastRecord = nextRecords.last(); + + if (lastRecord != null && shouldAccumulateRecordCount(lastRecord, record)) { + // Update the last record. Don't use `splice()` because that's O(n) + const updatedRecord = Object.assign({}, lastRecord, { + repeatCount: lastRecord.repeatCount + 1, + timestamp: record.timestamp + }); + nextRecords = nextRecords.pop().push(updatedRecord); + } else { + nextRecords = nextRecords.push(record); + } + + if (nextRecords.size > state.maxMessageCount) { + // We could only have gone over by one. + nextRecords = nextRecords.shift(); + } + + return Object.assign({}, state, { + records: nextRecords + }); } - if (nextRecords.size > state.maxMessageCount) { - // We could only have gone over by one. - nextRecords = nextRecords.shift(); + case Actions().SET_MAX_MESSAGE_COUNT: + { + const { + maxMessageCount + } = action.payload; + + if (maxMessageCount <= 0) { + return state; + } + + return Object.assign({}, state, { + maxMessageCount, + records: state.records.slice(-maxMessageCount) + }); + } + + case Actions().REGISTER_SOURCE: + { + const { + source + } = action.payload; + return Object.assign({}, state, { + providers: new Map(state.providers).set(source.id, Object.assign({}, source, { + name: source.name || source.id + })) + }); + } + + case Actions().CLEAR_RECORDS: + { + return Object.assign({}, state, { + records: (0, _immutable().List)() + }); } - return { - ...state, - records: nextRecords, - }; - } - case Actions.SET_MAX_MESSAGE_COUNT: { - const {maxMessageCount} = action.payload; - if (maxMessageCount <= 0) { - return state; + case Actions().REGISTER_EXECUTOR: + { + const { + executor + } = action.payload; + return Object.assign({}, state, { + executors: new Map(state.executors).set(executor.id, executor) + }); } - return { - ...state, - maxMessageCount, - records: state.records.slice(-maxMessageCount), - }; - } - case Actions.REGISTER_SOURCE: { - const {source} = action.payload; - return { - ...state, - providers: new Map(state.providers).set(source.id, { - ...source, - name: source.name || source.id, - }), - }; - } - case Actions.CLEAR_RECORDS: { - return { - ...state, - records: List(), - }; - } - case Actions.REGISTER_EXECUTOR: { - const {executor} = action.payload; - return { - ...state, - executors: new Map(state.executors).set(executor.id, executor), - }; - } - case Actions.SELECT_EXECUTOR: { - const {executorId} = action.payload; - return { - ...state, - currentExecutorId: executorId, - }; - } - case Actions.REMOVE_SOURCE: { - const {sourceId} = action.payload; - const providers = new Map(state.providers); - const providerStatuses = new Map(state.providerStatuses); - const executors = new Map(state.executors); - providers.delete(sourceId); - providerStatuses.delete(sourceId); - executors.delete(sourceId); - return { - ...state, - providers, - providerStatuses, - executors, - }; - } - case Actions.UPDATE_STATUS: { - const {status, providerId} = action.payload; - return { - ...state, - providerStatuses: new Map(state.providerStatuses).set( - providerId, + + case Actions().SELECT_EXECUTOR: + { + const { + executorId + } = action.payload; + return Object.assign({}, state, { + currentExecutorId: executorId + }); + } + + case Actions().REMOVE_SOURCE: + { + const { + sourceId + } = action.payload; + const providers = new Map(state.providers); + const providerStatuses = new Map(state.providerStatuses); + const executors = new Map(state.executors); + providers.delete(sourceId); + providerStatuses.delete(sourceId); + executors.delete(sourceId); + return Object.assign({}, state, { + providers, + providerStatuses, + executors + }); + } + + case Actions().UPDATE_STATUS: + { + const { status, - ), - }; - } - case Actions.EXECUTE: { - const command = action.payload.code; - return { - ...state, - history: state.history.concat(command).slice(-1000), - }; - } - case Actions.SET_CREATE_PASTE_FUNCTION: { - const {createPasteFunction} = action.payload; - return { - ...state, - createPasteFunction, - }; - } - case Actions.SET_WATCH_EDITOR_FUNCTION: { - const {watchEditor} = action.payload; - return { - ...state, - watchEditor, - }; - } - case Actions.SET_FONT_SIZE: { - const {fontSize} = action.payload; - return { - ...state, - fontSize, - }; - } + providerId + } = action.payload; + return Object.assign({}, state, { + providerStatuses: new Map(state.providerStatuses).set(providerId, status) + }); + } + + case Actions().EXECUTE: + { + const command = action.payload.code; + return Object.assign({}, state, { + history: state.history.concat(command).slice(-1000) + }); + } + + case Actions().SET_CREATE_PASTE_FUNCTION: + { + const { + createPasteFunction + } = action.payload; + return Object.assign({}, state, { + createPasteFunction + }); + } + + case Actions().SET_WATCH_EDITOR_FUNCTION: + { + const { + watchEditor + } = action.payload; + return Object.assign({}, state, { + watchEditor + }); + } + + case Actions().SET_FONT_SIZE: + { + const { + fontSize + } = action.payload; + return Object.assign({}, state, { + fontSize + }); + } } return state; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/types.js index 517dcda6..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/types.js @@ -1,310 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {Observable} from 'rxjs'; -import type {List} from 'immutable'; -import type {EvaluationResult} from 'nuclide-commons-ui/TextRenderer'; -import type {ExpansionResult} from 'nuclide-commons-ui/LazyNestedValueComponent'; - -// The type of the object passed to your package's `consumeConsole()` function. -export type ConsoleService = (options: SourceInfo) => ConsoleApi; - -// The console API. An object of this type is returned when you invoke the function provided by the -// console service. -export type ConsoleApi = { - // The primary means of interacting with the console. - // TODO: Update these to be `(object: any, ...objects: Array): void` to allow for logging objects. - log(object: string, _: void): void, - error(object: string, _: void): void, - warn(object: string, _: void): void, - info(object: string, _: void): void, - success(object: string, _: void): void, - - // A generic API for sending a message of any level (log, error, etc.). - append(message: Message): void, - - // Dispose of the console. Invoke this when your package is disabled. - dispose(): void, - - // Set the status of the source. See "Stoppable Sources" below. - setStatus(status: OutputProviderStatus): void, -}; - -// A type representing the possible values for the `console.setStatus()` API. -export type OutputProviderStatus = 'starting' | 'running' | 'stopped'; - -// The shape of the argument to the `ConsoleService` function. -export type SourceInfo = { - id: string, // A unique identifier representing this source. - name: string, // A human-readable name for the source. This will be used in the UI. - - // `start()` and `stop()` functions can optionally be provided. See [User-Controllable Console - // Sources](https://github.com/facebook-atom/atom-ide-ui/blob/master/docs/console.md#user-controllable-console-sources) - // for more information. - start?: () => void, - stop?: () => void, -}; - -// Message levels. For use with the `console.append()` API. -export type Level = - | 'info' - | 'log' - | 'warning' - | 'error' - | 'debug' - | 'success' - | Color; -type Color = - | 'red' - | 'orange' - | 'yellow' - | 'green' - | 'blue' - | 'purple' - | 'violet' - | 'rainbow'; - -// A message object, for use with the `console.append()` API. -export type Message = { - text: string, - level: Level, - format?: MessageFormat, - - // Internally used properties. These are subject to change so don't use em! - data?: EvaluationResult, - tags?: ?Array, - kind?: ?MessageKind, - scopeName?: ?string, -}; - -// -// -// The following types are part of deprecated APIs and shouldn't be used outside of this package. -// -// - -type BasicOutputProvider = { - messages: Observable, - // The source can't be part of the message because we want to be able to populate a filter menu - // before we even have any messages. - id: string, - getProperties?: (objectId: string) => Observable, -}; - -type ControllableOutputProviderProps = { - observeStatus(callback: (status: OutputProviderStatus) => mixed): IDisposable, - start(): void, - stop(): void, -}; - -type ControllableOutputProvider = BasicOutputProvider & - ControllableOutputProviderProps; - -export type OutputProvider = BasicOutputProvider | ControllableOutputProvider; - -export type OutputService = { - registerOutputProvider(outputProvider: OutputProvider): IDisposable, -}; - -// -// -// The following types aren't part of public API but rather are used within the package. -// -// - -type MessageKind = 'message' | 'request' | 'response'; -type MessageFormat = 'ansi'; - -// A normalized type used internally to represent all possible kinds of messages. Responses and -// Messages are transformed into these. -// Make sure shouldAccumulateRecordCount in Reducers.js is up to date with these fields -export type Record = { - text: string, - level: Level, - format?: MessageFormat, - tags?: ?Array, - repeatCount: number, - - kind: MessageKind, - sourceId: string, - scopeName: ?string, - data?: ?EvaluationResult, - timestamp: Date, - - executor?: Executor, -}; - -export type AppState = { - createPasteFunction: ?CreatePasteFunction, - currentExecutorId: ?string, - executors: Map, - maxMessageCount: number, - // We use Immutable for the records list so that adding an item is O(1). However, rendering the - // items after the addition is O(n), so it's important that we schedule and throttle our renders - // or we'll lose the benefit of an O(1) insertion. - records: List, - history: Array, - providers: Map, - providerStatuses: Map, -}; - -// A special type used internally by the Console component to represent each record that is -// displayed with its height. This is stored at the component level since the expansion state of any -// record (which affects its height) is unique to each Console pane (whereas the records themselves -// are shared between all Console panes). The height is needed for partial rendering. -export type DisplayableRecord = { - id: number, - record: Record, - height: number, - expansionStateId: Object, -}; - -export type RecordHeightChangeHandler = ( - recordId: number, - newHeight: number, - callback: () => void, -) => void; - -export type Source = { - id: string, - name: string, - status: OutputProviderStatus, - start?: () => void, - stop?: () => void, -}; - -type BasicRecordProvider = { - records: Observable, - id: string, - getProperties?: (objectId: string) => Observable, -}; - -type ControllableRecordProvider = BasicRecordProvider & - ControllableOutputProviderProps; - -export type RecordProvider = BasicRecordProvider | ControllableRecordProvider; - -// Serialized state specific to each instance of the console view. For example, each instance has -// its own, distinct filter, so that's here. They don't, however, have distinct records, so they -// aren't. -export type ConsolePersistedState = { - deserializer: 'nuclide.Console', - filterText?: string, - enableRegExpFilter?: boolean, - unselectedSourceIds?: Array, -}; - -export type Executor = { - id: string, - name: string, - send(message: string): void, - output: Observable, - scopeName: string, - provideSymbols?: (prefix: string) => Array, - getProperties?: (objectId: string) => Observable, -}; - -export type RegisterExecutorFunction = (executor: Executor) => IDisposable; - -export type PasteOptions = { - language?: ?string, - title?: ?string, -}; - -export type CreatePasteFunction = ( - message: string, - options: PasteOptions, - source: string, -) => Promise; - -export type Store = { - getState(): AppState, - dispatch(action: Action): void, -}; - -export type Action = - | { - type: 'CLEAR_RECORDS', - } - | { - type: 'REGISTER_EXECUTOR', - payload: { - executor: Executor, - }, - } - | { - type: 'EXECUTE', - payload: { - code: string, - }, - } - | { - type: 'RECORD_RECEIVED', - payload: { - record: Record, - }, - } - | { - type: 'REGISTER_RECORD_PROVIDER', - payload: { - recordProvider: RecordProvider, - }, - } - | { - type: 'REGISTER_SOURCE', - payload: { - source: SourceInfo, - }, - } - | { - type: 'REMOVE_SOURCE', - payload: { - sourceId: string, - }, - } - | { - type: 'SELECT_EXECUTOR', - payload: { - executorId: string, - }, - } - | { - type: 'SET_CREATE_PASTE_FUNCTION', - payload: { - createPasteFunction: ?CreatePasteFunction, - }, - } - | { - type: 'SET_WATCH_EDITOR_FUNCTION', - payload: { - watchEditor: ?atom$AutocompleteWatchEditor, - }, - } - | { - type: 'SET_MAX_MESSAGE_COUNT', - payload: { - maxMessageCount: number, - }, - } - | { - type: 'UPDATE_STATUS', - payload: { - providerId: string, - status: OutputProviderStatus, - }, - } - | { - type: 'SET_FONT_SIZE', - payload: { - fontSize: number, - }, - }; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/Console.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/Console.js index 0c5b5f41..e2152466 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/Console.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/Console.js @@ -1,3 +1,148 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Console = exports.WORKSPACE_VIEW_URI = void 0; + +function _observePaneItemVisibility() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-atom/observePaneItemVisibility")); + + _observePaneItemVisibility = function () { + return data; + }; + + return data; +} + +function _Model() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/Model")); + + _Model = function () { + return data; + }; + + return data; +} + +function _shallowequal() { + const data = _interopRequireDefault(require("shallowequal")); + + _shallowequal = function () { + return data; + }; + + return data; +} + +function _bindObservableAsProps() { + const data = require("../../../../../nuclide-commons-ui/bindObservableAsProps"); + + _bindObservableAsProps = function () { + return data; + }; + + return data; +} + +function _renderReactRoot() { + const data = require("../../../../../nuclide-commons-ui/renderReactRoot"); + + _renderReactRoot = function () { + return data; + }; + + return data; +} + +function _memoizeUntilChanged() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/memoizeUntilChanged")); + + _memoizeUntilChanged = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _RegExpFilter() { + const data = require("../../../../../nuclide-commons-ui/RegExpFilter"); + + _RegExpFilter = function () { + return data; + }; + + return data; +} + +function _getCurrentExecutorId() { + const data = _interopRequireDefault(require("../getCurrentExecutorId")); + + _getCurrentExecutorId = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("../redux/Actions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _ConsoleView() { + const data = _interopRequireDefault(require("./ConsoleView")); + + _ConsoleView = function () { + return data; + }; + + return data; +} + +function _immutable() { + const data = require("immutable"); + + _immutable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,145 +151,143 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-env browser */ - -import type { - ConsolePersistedState, - DisplayableRecord, - OutputProviderStatus, - Record, - Source, - Store, - SourceInfo, -} from '../types'; -import type {CreatePasteFunction} from '../types'; -import type {RegExpFilterChange} from 'nuclide-commons-ui/RegExpFilter'; -import type {Executor} from '../types'; - -import observePaneItemVisibility from 'nuclide-commons-atom/observePaneItemVisibility'; -import Model from 'nuclide-commons/Model'; -import shallowEqual from 'shallowequal'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {renderReactRoot} from 'nuclide-commons-ui/renderReactRoot'; -import memoizeUntilChanged from 'nuclide-commons/memoizeUntilChanged'; -import {toggle} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {nextAnimationFrame} from 'nuclide-commons/observable'; -import {getFilterPattern} from 'nuclide-commons-ui/RegExpFilter'; -import getCurrentExecutorId from '../getCurrentExecutorId'; -import * as Actions from '../redux/Actions'; -import ConsoleView from './ConsoleView'; -import {List} from 'immutable'; -import * as React from 'react'; -import {Observable, ReplaySubject} from 'rxjs'; - -type Options = { - store: Store, - initialFilterText?: string, - initialEnableRegExpFilter?: boolean, - initialUnselectedSourceIds?: Array, -}; - -// -// State unique to this particular Console instance -// -type State = { - displayableRecords: Array, - filterText: string, - enableRegExpFilter: boolean, - unselectedSourceIds: Array, -}; - -type BoundActionCreators = { - execute: (code: string) => void, - selectExecutor: (executorId: string) => void, - clearRecords: () => void, -}; - // Other Nuclide packages (which cannot import this) depend on this URI. If this // needs to be changed, grep for CONSOLE_VIEW_URI and ensure that the URIs match. -export const WORKSPACE_VIEW_URI = 'atom://nuclide/console'; - -const ERROR_TRANSCRIBING_MESSAGE = - "// Nuclide couldn't find the right text to display"; +const WORKSPACE_VIEW_URI = 'atom://nuclide/console'; +exports.WORKSPACE_VIEW_URI = WORKSPACE_VIEW_URI; +const ERROR_TRANSCRIBING_MESSAGE = "// Nuclide couldn't find the right text to display"; const INITIAL_RECORD_HEIGHT = 21; - /** * An Atom "view model" for the console. This object is responsible for creating a stateful view * (via `getElement()`). That view is bound to both global state (from the store) and view-specific * state (from this instance's `_model`). */ -export class Console { - _actionCreators: BoundActionCreators; +class Console { // Associates Records with their display state (height, expansionStateId). - _displayableRecords: WeakMap; + constructor(options) { + this._getSourcesMemoized = (0, _memoizeUntilChanged().default)(getSources, opts => opts, (a, b) => (0, _shallowequal().default)(a, b)); + + this._resetAllFilters = () => { + this._selectSources(this._getSources().map(s => s.id)); + + this._model.setState({ + filterText: '' + }); + }; + + this._createPaste = async () => { + const displayableRecords = this._getDisplayableRecords(); + + const createPasteImpl = this._store.getState().createPasteFunction; + + if (createPasteImpl == null) { + return; + } + + return createPaste(createPasteImpl, displayableRecords); + }; + + this._selectSources = selectedSourceIds => { + const sourceIds = this._getSources().map(source => source.id); - _nextRecordId: number; - _titleChanges: Observable; - _model: Model; - _store: Store; - _element: ?HTMLElement; - _destroyed: ReplaySubject; + const unselectedSourceIds = sourceIds.filter(sourceId => selectedSourceIds.indexOf(sourceId) === -1); + + this._model.setState({ + unselectedSourceIds + }); + }; + + this._updateFilter = change => { + const { + text, + isRegExp + } = change; + + this._model.setState({ + filterText: text, + enableRegExpFilter: isRegExp + }); + }; + + this._handleDisplayableRecordHeightChange = (recordId, newHeight, callback) => { + const { + records + } = this._store.getState(); + + const nextDisplayableRecords = Array(records.size); + records.forEach((record, i) => { + let displayableRecord = this._toDisplayableRecord(record); + + if (displayableRecord.id === recordId) { + // Update the record with the new height. + displayableRecord = Object.assign({}, displayableRecord, { + height: newHeight + }); + + this._displayableRecords.set(record, displayableRecord); + } + + nextDisplayableRecords[i] = displayableRecord; + }); + + this._model.setState({ + displayableRecords: nextDisplayableRecords + }); + + requestAnimationFrame(callback); + }; - constructor(options: Options) { const { store, initialFilterText, initialEnableRegExpFilter, - initialUnselectedSourceIds, + initialUnselectedSourceIds } = options; - this._model = new Model({ + this._model = new (_Model().default)({ displayableRecords: [], filterText: initialFilterText == null ? '' : initialFilterText, enableRegExpFilter: Boolean(initialEnableRegExpFilter), - unselectedSourceIds: - initialUnselectedSourceIds == null ? [] : initialUnselectedSourceIds, + unselectedSourceIds: initialUnselectedSourceIds == null ? [] : initialUnselectedSourceIds }); - this._store = store; this._nextRecordId = 0; this._displayableRecords = new WeakMap(); - this._destroyed = new ReplaySubject(1); - - this._titleChanges = Observable.combineLatest( - this._model.toObservable(), - // $FlowIssue: Flow doesn't know about Symbol.observable - Observable.from(store), - ) - .takeUntil(this._destroyed) - .map(() => this.getTitle()) - .distinctUntilChanged() - .share(); + this._destroyed = new _RxMin.ReplaySubject(1); + this._titleChanges = _RxMin.Observable.combineLatest(this._model.toObservable(), // $FlowIssue: Flow doesn't know about Symbol.observable + _RxMin.Observable.from(store)).takeUntil(this._destroyed).map(() => this.getTitle()).distinctUntilChanged().share(); } - getIconName(): string { + getIconName() { return 'nuclicon-console'; - } - - // Get the pane item's title. If there's only one source selected, we'll use that to make a more + } // Get the pane item's title. If there's only one source selected, we'll use that to make a more // descriptive title. - getTitle(): string { + + + getTitle() { const enabledProviderCount = this._store.getState().providers.size; - const {unselectedSourceIds} = this._model.state; - // Calling `_getSources()` is (currently) expensive because it needs to search all the records + const { + unselectedSourceIds + } = this._model.state; // Calling `_getSources()` is (currently) expensive because it needs to search all the records // for sources that have been disabled but still have records. We try to avoid calling it if we // already know that there's more than one selected source. + if (enabledProviderCount - unselectedSourceIds.length > 1) { return 'Console'; - } + } // If there's only one source selected, use its name in the tab title. + - // If there's only one source selected, use its name in the tab title. const sources = this._getSources(); + if (sources.length - unselectedSourceIds.length === 1) { - const selectedSource = sources.find( - source => unselectedSourceIds.indexOf(source.id) === -1, - ); + const selectedSource = sources.find(source => unselectedSourceIds.indexOf(source.id) === -1); + if (selectedSource) { return `Console: ${selectedSource.name}`; } @@ -153,287 +296,216 @@ export class Console { return 'Console'; } - getDefaultLocation(): string { + getDefaultLocation() { return 'bottom'; } - getURI(): string { + getURI() { return WORKSPACE_VIEW_URI; } - onDidChangeTitle(callback: (title: string) => mixed): IDisposable { - return new UniversalDisposable(this._titleChanges.subscribe(callback)); - } - - _getSources(): Array { - const {providers, providerStatuses, records} = this._store.getState(); - return this._getSourcesMemoized({providers, providerStatuses, records}); + onDidChangeTitle(callback) { + return new (_UniversalDisposable().default)(this._titleChanges.subscribe(callback)); } - // Memoize `getSources()`. Unfortunately, since we look for unrepresented sources in the record + _getSources() { + const { + providers, + providerStatuses, + records + } = this._store.getState(); + + return this._getSourcesMemoized({ + providers, + providerStatuses, + records + }); + } // Memoize `getSources()`. Unfortunately, since we look for unrepresented sources in the record // list, this still needs to be called whenever the records change. // TODO: Consider removing records when their source is removed. This will likely require adding // the ability to enable and disable sources so, for example, when the debugger is no longer // active, it still remains in the source list. - _getSourcesMemoized = memoizeUntilChanged( - getSources, - opts => opts, - (a, b) => shallowEqual(a, b), - ); - destroy(): void { + + destroy() { this._destroyed.next(); } - copy(): Console { + copy() { return new Console({ store: this._store, initialFilterText: this._model.state.filterText, initialEnableRegExpFilter: this._model.state.enableRegExpFilter, - initialUnselectedSourceIds: this._model.state.unselectedSourceIds, + initialUnselectedSourceIds: this._model.state.unselectedSourceIds }); } - _getBoundActionCreators(): BoundActionCreators { + _getBoundActionCreators() { if (this._actionCreators == null) { this._actionCreators = { execute: code => { - this._store.dispatch(Actions.execute(code)); + this._store.dispatch(Actions().execute(code)); }, selectExecutor: executorId => { - this._store.dispatch(Actions.selectExecutor(executorId)); + this._store.dispatch(Actions().selectExecutor(executorId)); }, clearRecords: () => { - this._store.dispatch(Actions.clearRecords()); - }, + this._store.dispatch(Actions().clearRecords()); + } }; } + return this._actionCreators; } - _resetAllFilters = (): void => { - this._selectSources(this._getSources().map(s => s.id)); - this._model.setState({filterText: ''}); - }; - - _createPaste = async (): Promise => { - const displayableRecords = this._getDisplayableRecords(); - const createPasteImpl = this._store.getState().createPasteFunction; - if (createPasteImpl == null) { - return; - } - return createPaste(createPasteImpl, displayableRecords); - }; + _getFilterInfo() { + const { + pattern, + invalid + } = (0, _RegExpFilter().getFilterPattern)(this._model.state.filterText, this._model.state.enableRegExpFilter); - _getFilterInfo(): { - invalid: boolean, - selectedSourceIds: Array, - filteredRecords: Array, - } { - const {pattern, invalid} = getFilterPattern( - this._model.state.filterText, - this._model.state.enableRegExpFilter, - ); const sources = this._getSources(); - const selectedSourceIds = sources - .map(source => source.id) - .filter( - sourceId => - this._model.state.unselectedSourceIds.indexOf(sourceId) === -1, - ); - - const filteredRecords = filterRecords( - this._getDisplayableRecords(), - selectedSourceIds, - pattern, - sources.length !== selectedSourceIds.length, - ); + const selectedSourceIds = sources.map(source => source.id).filter(sourceId => this._model.state.unselectedSourceIds.indexOf(sourceId) === -1); + const filteredRecords = filterRecords(this._getDisplayableRecords(), selectedSourceIds, pattern, sources.length !== selectedSourceIds.length); return { invalid, selectedSourceIds, - filteredRecords, + filteredRecords }; } - getElement(): HTMLElement { + getElement() { if (this._element != null) { return this._element; } const actionCreators = this._getBoundActionCreators(); - const props = Observable.combineLatest( - this._model.toObservable(), - // $FlowIssue: Flow doesn't know about Symbol.observable - Observable.from(this._store), - ) - // Don't re-render when the console isn't visible. - .let(toggle(observePaneItemVisibility(this))) - .audit(() => nextAnimationFrame) - .map(([localState, globalState]) => { - const { - invalid, - selectedSourceIds, - filteredRecords, - } = this._getFilterInfo(); - - const currentExecutorId = getCurrentExecutorId(globalState); - const currentExecutor = - currentExecutorId != null - ? globalState.executors.get(currentExecutorId) - : null; - - return { - invalidFilterInput: invalid, - execute: actionCreators.execute, - selectExecutor: actionCreators.selectExecutor, - clearRecords: actionCreators.clearRecords, - createPaste: - globalState.createPasteFunction == null ? null : this._createPaste, - watchEditor: globalState.watchEditor, - currentExecutor, - unselectedSourceIds: localState.unselectedSourceIds, - filterText: localState.filterText, - enableRegExpFilter: localState.enableRegExpFilter, - displayableRecords: filteredRecords, - filteredRecordCount: - globalState.records.size - filteredRecords.length, - history: globalState.history, - sources: this._getSources(), - selectedSourceIds, - selectSources: this._selectSources, - executors: globalState.executors, - getProvider: id => globalState.providers.get(id), - updateFilter: this._updateFilter, - onDisplayableRecordHeightChange: this - ._handleDisplayableRecordHeightChange, - resetAllFilters: this._resetAllFilters, - fontSize: globalState.fontSize, - }; - }); - const StatefulConsoleView = bindObservableAsProps(props, ConsoleView); - return (this._element = renderReactRoot()); + const props = _RxMin.Observable.combineLatest(this._model.toObservable(), // $FlowIssue: Flow doesn't know about Symbol.observable + _RxMin.Observable.from(this._store)) // Don't re-render when the console isn't visible. + .let((0, _observable().toggle)((0, _observePaneItemVisibility().default)(this))).audit(() => _observable().nextAnimationFrame).map(([localState, globalState]) => { + const { + invalid, + selectedSourceIds, + filteredRecords + } = this._getFilterInfo(); + + const currentExecutorId = (0, _getCurrentExecutorId().default)(globalState); + const currentExecutor = currentExecutorId != null ? globalState.executors.get(currentExecutorId) : null; + return { + invalidFilterInput: invalid, + execute: actionCreators.execute, + selectExecutor: actionCreators.selectExecutor, + clearRecords: actionCreators.clearRecords, + createPaste: globalState.createPasteFunction == null ? null : this._createPaste, + watchEditor: globalState.watchEditor, + currentExecutor, + unselectedSourceIds: localState.unselectedSourceIds, + filterText: localState.filterText, + enableRegExpFilter: localState.enableRegExpFilter, + displayableRecords: filteredRecords, + filteredRecordCount: globalState.records.size - filteredRecords.length, + history: globalState.history, + sources: this._getSources(), + selectedSourceIds, + selectSources: this._selectSources, + executors: globalState.executors, + getProvider: id => globalState.providers.get(id), + updateFilter: this._updateFilter, + onDisplayableRecordHeightChange: this._handleDisplayableRecordHeightChange, + resetAllFilters: this._resetAllFilters, + fontSize: globalState.fontSize + }; + }); + + const StatefulConsoleView = (0, _bindObservableAsProps().bindObservableAsProps)(props, _ConsoleView().default); + return this._element = (0, _renderReactRoot().renderReactRoot)(React.createElement(StatefulConsoleView, null)); } - serialize(): ConsolePersistedState { + serialize() { const { filterText, enableRegExpFilter, - unselectedSourceIds, + unselectedSourceIds } = this._model.state; return { deserializer: 'nuclide.Console', filterText, enableRegExpFilter, - unselectedSourceIds, + unselectedSourceIds }; } - _selectSources = (selectedSourceIds: Array): void => { - const sourceIds = this._getSources().map(source => source.id); - const unselectedSourceIds = sourceIds.filter( - sourceId => selectedSourceIds.indexOf(sourceId) === -1, - ); - this._model.setState({unselectedSourceIds}); - }; - /** Unselects the sources from the given IDs */ - unselectSources(ids: Array): void { - const newIds = ids.filter( - id => !this._model.state.unselectedSourceIds.includes(id), - ); - this._model.setState({ - unselectedSourceIds: this._model.state.unselectedSourceIds.concat(newIds), - }); - } + unselectSources(ids) { + const newIds = ids.filter(id => !this._model.state.unselectedSourceIds.includes(id)); - _updateFilter = (change: RegExpFilterChange): void => { - const {text, isRegExp} = change; this._model.setState({ - filterText: text, - enableRegExpFilter: isRegExp, - }); - }; - - _handleDisplayableRecordHeightChange = ( - recordId: number, - newHeight: number, - callback: () => void, - ): void => { - const {records} = this._store.getState(); - const nextDisplayableRecords = Array(records.size); - records.forEach((record, i) => { - let displayableRecord = this._toDisplayableRecord(record); - if (displayableRecord.id === recordId) { - // Update the record with the new height. - displayableRecord = { - ...displayableRecord, - height: newHeight, - }; - this._displayableRecords.set(record, displayableRecord); - } - nextDisplayableRecords[i] = displayableRecord; + unselectedSourceIds: this._model.state.unselectedSourceIds.concat(newIds) }); + } - this._model.setState({displayableRecords: nextDisplayableRecords}); - requestAnimationFrame(callback); - }; + _getDisplayableRecords() { + const { + records + } = this._store.getState(); - _getDisplayableRecords(): Array { - const {records} = this._store.getState(); const displayableRecords = Array(records.size); records.forEach((record, i) => { displayableRecords[i] = this._toDisplayableRecord(record); }); return displayableRecords; } - /** * Transforms the Records from the store into DisplayableRecords. This caches the result * per-Console instance because the same record can have different heights in different * containers. */ - _toDisplayableRecord(record: Record): DisplayableRecord { + + + _toDisplayableRecord(record) { const displayableRecord = this._displayableRecords.get(record); + if (displayableRecord != null) { return displayableRecord; } + const newDisplayableRecord = { id: this._nextRecordId++, record, height: INITIAL_RECORD_HEIGHT, - expansionStateId: {}, + expansionStateId: {} }; + this._displayableRecords.set(record, newDisplayableRecord); + return newDisplayableRecord; } -} -function getSources(options: { - records: List, - providers: Map, - providerStatuses: Map, -}): Array { - const {providers, providerStatuses, records} = options; - - // Convert the providers to a map of sources. - const mapOfSources = new Map( - Array.from(providers.entries()).map(([k, provider]) => { - const source = { - id: provider.id, - name: provider.name, - status: providerStatuses.get(provider.id) || 'stopped', - start: - typeof provider.start === 'function' ? provider.start : undefined, - stop: typeof provider.stop === 'function' ? provider.stop : undefined, - }; - return [k, source]; - }), - ); +} - // Some providers may have been unregistered, but still have records. Add sources for them too. +exports.Console = Console; + +function getSources(options) { + const { + providers, + providerStatuses, + records + } = options; // Convert the providers to a map of sources. + + const mapOfSources = new Map(Array.from(providers.entries()).map(([k, provider]) => { + const source = { + id: provider.id, + name: provider.name, + status: providerStatuses.get(provider.id) || 'stopped', + start: typeof provider.start === 'function' ? provider.start : undefined, + stop: typeof provider.stop === 'function' ? provider.stop : undefined + }; + return [k, source]; + })); // Some providers may have been unregistered, but still have records. Add sources for them too. // TODO: Iterating over all the records to get this every time we get a new record is inefficient. + records.forEach((record, i) => { if (!mapOfSources.has(record.sourceId)) { mapOfSources.set(record.sourceId, { @@ -441,62 +513,40 @@ function getSources(options: { name: record.sourceId, status: 'stopped', start: undefined, - stop: undefined, + stop: undefined }); } }); - return Array.from(mapOfSources.values()); } -function filterRecords( - displayableRecords: Array, - selectedSourceIds: Array, - filterPattern: ?RegExp, - filterSources: boolean, -): Array { +function filterRecords(displayableRecords, selectedSourceIds, filterPattern, filterSources) { if (!filterSources && filterPattern == null) { return displayableRecords; } - return displayableRecords.filter(({record}) => { + return displayableRecords.filter(({ + record + }) => { // Only filter regular messages if (record.kind !== 'message') { return true; } const sourceMatches = selectedSourceIds.indexOf(record.sourceId) !== -1; - return ( - sourceMatches && - (filterPattern == null || filterPattern.test(record.text)) - ); + return sourceMatches && (filterPattern == null || filterPattern.test(record.text)); }); } -async function serializeRecordObject( - executor: Executor, - visited: Set, - data: { - objectId?: string, - description?: string, - value?: string, - }, - text: string, - level: number, -): Promise { +async function serializeRecordObject(executor, visited, data, text, level) { const getText = record => { let indent = ''; + for (let i = 0; i < level; i++) { indent += '\t'; } - return ( - indent + - (record.description != null - ? record.description - : record.value != null - ? record.value - : '') - ); + + return indent + (record.description != null ? record.description : record.value != null ? record.value : ''); }; if (data.objectId == null) { @@ -505,6 +555,7 @@ async function serializeRecordObject( } const id = data.objectId; + if (visited.has(id)) { // Guard against cycles. return text; @@ -518,98 +569,56 @@ async function serializeRecordObject( const childProperties = (await executor.getProperties(id).toPromise()) || []; const serializedProps = childProperties.map(childProp => { - return serializeRecordObject( - executor, - visited, - childProp.value, - '', - level + 1, - ); + return serializeRecordObject(executor, visited, childProp.value, '', level + 1); }); return getText(data) + '\n' + (await Promise.all(serializedProps)).join('\n'); } -async function createPaste( - createPasteImpl: CreatePasteFunction, - records: Array, -): Promise { - const linePromises = records - .filter( - displayable => - displayable.record.kind === 'message' || - displayable.record.kind === 'request' || - displayable.record.kind === 'response', - ) - .map(async displayable => { - const record = displayable.record; - const level = - record.level != null ? record.level.toString().toUpperCase() : 'LOG'; - const timestamp = record.timestamp.toLocaleString(); - let text = - record.text || - (record.data && record.data.value) || - ERROR_TRANSCRIBING_MESSAGE; - - if ( - record.kind === 'response' && - record.data != null && - record.data.objectId != null && - record.data.objectId !== '' - ) { - const executor = record.executor; - if (executor != null) { - // If the record has a data object, and the object has an ID, - // recursively expand the nodes of the object and serialize it - // for the paste. - text = await serializeRecordObject( - executor, - new Set(), - record.data, - '', - 0, - ); - } +async function createPaste(createPasteImpl, records) { + const linePromises = records.filter(displayable => displayable.record.kind === 'message' || displayable.record.kind === 'request' || displayable.record.kind === 'response').map(async displayable => { + const record = displayable.record; + const level = record.level != null ? record.level.toString().toUpperCase() : 'LOG'; + const timestamp = record.timestamp.toLocaleString(); + let text = record.text || record.data && record.data.value || ERROR_TRANSCRIBING_MESSAGE; + + if (record.kind === 'response' && record.data != null && record.data.objectId != null && record.data.objectId !== '') { + const executor = record.executor; + + if (executor != null) { + // If the record has a data object, and the object has an ID, + // recursively expand the nodes of the object and serialize it + // for the paste. + text = await serializeRecordObject(executor, new Set(), record.data, '', 0); } + } - return `[${level}][${record.sourceId}][${timestamp}]\t ${text}`; - }); - + return `[${level}][${record.sourceId}][${timestamp}]\t ${text}`; + }); const lines = (await Promise.all(linePromises)).join('\n'); if (lines === '') { // Can't create an empty paste! - atom.notifications.addWarning( - 'There is nothing in your console to Paste! Check your console filters and try again.', - ); + atom.notifications.addWarning('There is nothing in your console to Paste! Check your console filters and try again.'); return; } atom.notifications.addInfo('Creating Paste...'); try { - const uri = await createPasteImpl( - lines, - { - title: 'Nuclide Console Paste', - }, - 'console paste', - ); + const uri = await createPasteImpl(lines, { + title: 'Nuclide Console Paste' + }, 'console paste'); atom.notifications.addSuccess(`Created Paste at ${uri}`); } catch (error) { if (error.stdout == null) { - atom.notifications.addError( - `Failed to create paste: ${String(error.message || error)}`, - ); + atom.notifications.addError(`Failed to create paste: ${String(error.message || error)}`); return; } - const errorMessages = error.stdout - .trim() - .split('\n') - .map(JSON.parse) - .map(e => e.message); + + const errorMessages = error.stdout.trim().split('\n').map(JSON.parse).map(e => e.message); atom.notifications.addError('Failed to create paste', { detail: errorMessages.join('\n'), - dismissable: true, + dismissable: true }); } -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleHeader.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleHeader.js index 13c7cc6c..a85d74c2 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleHeader.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleHeader.js @@ -1,3 +1,96 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _LoadingSpinner() { + const data = require("../../../../../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _ModalMultiSelect() { + const data = require("../../../../../nuclide-commons-ui/ModalMultiSelect"); + + _ModalMultiSelect = function () { + return data; + }; + + return data; +} + +function _RegExpFilter() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/RegExpFilter")); + + _RegExpFilter = function () { + return data; + }; + + return data; +} + +function _Toolbar() { + const data = require("../../../../../nuclide-commons-ui/Toolbar"); + + _Toolbar = function () { + return data; + }; + + return data; +} + +function _ToolbarLeft() { + const data = require("../../../../../nuclide-commons-ui/ToolbarLeft"); + + _ToolbarLeft = function () { + return data; + }; + + return data; +} + +function _ToolbarRight() { + const data = require("../../../../../nuclide-commons-ui/ToolbarRight"); + + _ToolbarRight = function () { + return data; + }; + + return data; +} + +function _addTooltip() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/addTooltip")); + + _addTooltip = function () { + return data; + }; + + return data; +} + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,202 +99,148 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class ConsoleHeader extends React.Component { + constructor(...args) { + var _temp; -import type {Source} from '../types'; -import type {RegExpFilterChange} from 'nuclide-commons-ui/RegExpFilter'; - -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; -import * as React from 'react'; -import {ModalMultiSelect} from 'nuclide-commons-ui/ModalMultiSelect'; -import RegExpFilter from 'nuclide-commons-ui/RegExpFilter'; -import {Toolbar} from 'nuclide-commons-ui/Toolbar'; -import {ToolbarLeft} from 'nuclide-commons-ui/ToolbarLeft'; -import {ToolbarRight} from 'nuclide-commons-ui/ToolbarRight'; -import addTooltip from 'nuclide-commons-ui/addTooltip'; - -import {Button, ButtonSizes} from 'nuclide-commons-ui/Button'; -import invariant from 'assert'; - -type Props = { - clear: () => void, - createPaste: ?() => Promise, - invalidFilterInput: boolean, - enableRegExpFilter: boolean, - onFilterChange: (change: RegExpFilterChange) => void, - selectedSourceIds: Array, - sources: Array, - onSelectedSourcesChange: (sourceIds: Array) => void, - filterText: string, -}; - -export default class ConsoleHeader extends React.Component { - _filterComponent: ?RegExpFilter; - - focusFilter = (): void => { - if (this._filterComponent != null) { - this._filterComponent.focus(); - } - }; - - _handleClearButtonClick = (event: SyntheticMouseEvent<>): void => { - this.props.clear(); - }; - - _handleCreatePasteButtonClick = (event: SyntheticMouseEvent<>): void => { - if (this.props.createPaste != null) { - this.props.createPaste(); - } - }; + return _temp = super(...args), this.focusFilter = () => { + if (this._filterComponent != null) { + this._filterComponent.focus(); + } + }, this._handleClearButtonClick = event => { + this.props.clear(); + }, this._handleCreatePasteButtonClick = event => { + if (this.props.createPaste != null) { + this.props.createPaste(); + } + }, this._handleFilterChange = value => { + this.props.onFilterChange(value); + }, this._renderOption = optionProps => { + const { + option + } = optionProps; + const source = this.props.sources.find(s => s.id === option.value); + + if (!(source != null)) { + throw new Error("Invariant violation: \"source != null\""); + } - _handleFilterChange = (value: RegExpFilterChange): void => { - this.props.onFilterChange(value); - }; + const startingSpinner = source.status !== 'starting' ? null : React.createElement(_LoadingSpinner().LoadingSpinner, { + className: "inline-block console-process-starting-spinner", + size: "EXTRA_SMALL" + }); + return React.createElement("span", null, option.label, startingSpinner, this._renderProcessControlButton(source)); + }, _temp; + } - _renderProcessControlButton(source: Source): ?React.Element { + _renderProcessControlButton(source) { let action; let label; let icon; + switch (source.status) { case 'starting': - case 'running': { - action = source.stop; - label = 'Stop Process'; - icon = 'primitive-square'; - break; - } - case 'stopped': { - action = source.start; - label = 'Start Process'; - icon = 'triangle-right'; - break; - } + case 'running': + { + action = source.stop; + label = 'Stop Process'; + icon = 'primitive-square'; + break; + } + + case 'stopped': + { + action = source.start; + label = 'Start Process'; + icon = 'triangle-right'; + break; + } } + if (action == null) { return; } + const clickHandler = event => { event.stopPropagation(); - invariant(action != null); + + if (!(action != null)) { + throw new Error("Invariant violation: \"action != null\""); + } + action(); }; - return ( - - ); - } - _renderOption = (optionProps: { - option: {label: string, value: string}, - }): React.Element => { - const {option} = optionProps; - const source = this.props.sources.find(s => s.id === option.value); - invariant(source != null); - const startingSpinner = - source.status !== 'starting' ? null : ( - - ); - return ( - - {option.label} - {startingSpinner} - {this._renderProcessControlButton(source)} - - ); - }; + return React.createElement(_Button().Button, { + className: "pull-right console-process-control-button", + icon: icon, + onClick: clickHandler + }, label); + } - render(): React.Node { - const options = this.props.sources - .slice() - .sort((a, b) => sortAlpha(a.name, b.name)) - .map(source => ({ - label: source.name, - value: source.id, - })); - - const sourceButton = - options.length === 0 ? null : ( - - ); - - const pasteButton = - this.props.createPaste == null ? null : ( - - ); - - return ( - - - {sourceButton} - (this._filterComponent = component)} - value={{ - text: this.props.filterText, - isRegExp: this.props.enableRegExpFilter, - invalid: this.props.invalidFilterInput, - }} - onChange={this._handleFilterChange} - /> - - - {pasteButton} - - - - ); + render() { + const options = this.props.sources.slice().sort((a, b) => sortAlpha(a.name, b.name)).map(source => ({ + label: source.name, + value: source.id + })); + const sourceButton = options.length === 0 ? null : React.createElement(_ModalMultiSelect().ModalMultiSelect, { + labelComponent: MultiSelectLabel, + optionComponent: this._renderOption, + size: _Button().ButtonSizes.SMALL, + options: options, + value: this.props.selectedSourceIds, + onChange: this.props.onSelectedSourcesChange, + className: "inline-block" + }); + const pasteButton = this.props.createPaste == null ? null : React.createElement(_Button().Button, { + className: "inline-block", + size: _Button().ButtonSizes.SMALL, + onClick: this._handleCreatePasteButtonClick // eslint-disable-next-line nuclide-internal/jsx-simple-callback-refs + , + ref: (0, _addTooltip().default)({ + title: 'Creates a Paste from the current contents of the console' + }) + }, "Create Paste"); + return React.createElement(_Toolbar().Toolbar, { + location: "top" + }, React.createElement(_ToolbarLeft().ToolbarLeft, null, sourceButton, React.createElement(_RegExpFilter().default, { + ref: component => this._filterComponent = component, + value: { + text: this.props.filterText, + isRegExp: this.props.enableRegExpFilter, + invalid: this.props.invalidFilterInput + }, + onChange: this._handleFilterChange + })), React.createElement(_ToolbarRight().ToolbarRight, null, pasteButton, React.createElement(_Button().Button, { + size: _Button().ButtonSizes.SMALL, + onClick: this._handleClearButtonClick + }, "Clear"))); } + } -function sortAlpha(a: string, b: string): number { +exports.default = ConsoleHeader; + +function sortAlpha(a, b) { const aLower = a.toLowerCase(); const bLower = b.toLowerCase(); + if (aLower < bLower) { return -1; } else if (aLower > bLower) { return 1; } + return 0; } -type LabelProps = { - selectedOptions: Array<{value: string, label: string}>, -}; - -function MultiSelectLabel(props: LabelProps): React.Element { - const {selectedOptions} = props; - const label = - selectedOptions.length === 1 - ? selectedOptions[0].label - : `${selectedOptions.length} Sources`; - return Showing: {label}; -} +function MultiSelectLabel(props) { + const { + selectedOptions + } = props; + const label = selectedOptions.length === 1 ? selectedOptions[0].label : `${selectedOptions.length} Sources`; + return React.createElement("span", null, "Showing: ", label); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleView.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleView.js index 1a22c088..51000d95 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleView.js @@ -1,3 +1,138 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _observable() { + const data = require("../../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _FilteredMessagesReminder() { + const data = _interopRequireDefault(require("./FilteredMessagesReminder")); + + _FilteredMessagesReminder = function () { + return data; + }; + + return data; +} + +function _OutputTable() { + const data = _interopRequireDefault(require("./OutputTable")); + + _OutputTable = function () { + return data; + }; + + return data; +} + +function _ConsoleHeader() { + const data = _interopRequireDefault(require("./ConsoleHeader")); + + _ConsoleHeader = function () { + return data; + }; + + return data; +} + +function _InputArea() { + const data = _interopRequireDefault(require("./InputArea")); + + _InputArea = function () { + return data; + }; + + return data; +} + +function _PromptButton() { + const data = _interopRequireDefault(require("./PromptButton")); + + _PromptButton = function () { + return data; + }; + + return data; +} + +function _NewMessagesNotification() { + const data = _interopRequireDefault(require("./NewMessagesNotification")); + + _NewMessagesNotification = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _shallowequal() { + const data = _interopRequireDefault(require("shallowequal")); + + _shallowequal = function () { + return data; + }; + + return data; +} + +function _recordsChanged() { + const data = _interopRequireDefault(require("../recordsChanged")); + + _recordsChanged = function () { + return data; + }; + + return data; +} + +function _StyleSheet() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/StyleSheet")); + + _StyleSheet = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,381 +141,272 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - DisplayableRecord, - Executor, - OutputProvider, - RecordHeightChangeHandler, - Source, -} from '../types'; -import type {RegExpFilterChange} from 'nuclide-commons-ui/RegExpFilter'; - -import {macrotask} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import {Observable} from 'rxjs'; -import FilteredMessagesReminder from './FilteredMessagesReminder'; -import OutputTable from './OutputTable'; -import ConsoleHeader from './ConsoleHeader'; -import InputArea from './InputArea'; -import PromptButton from './PromptButton'; -import NewMessagesNotification from './NewMessagesNotification'; -import invariant from 'assert'; -import nullthrows from 'nullthrows'; -import shallowEqual from 'shallowequal'; -import recordsChanged from '../recordsChanged'; -import StyleSheet from 'nuclide-commons-ui/StyleSheet'; - -type Props = { - displayableRecords: Array, - history: Array, - clearRecords: () => void, - createPaste: ?() => Promise, - watchEditor: ?atom$AutocompleteWatchEditor, - execute: (code: string) => void, - currentExecutor: ?Executor, - executors: Map, - invalidFilterInput: boolean, - enableRegExpFilter: boolean, - selectedSourceIds: Array, - selectExecutor: (executorId: string) => void, - selectSources: (sourceIds: Array) => void, - sources: Array, - updateFilter: (change: RegExpFilterChange) => void, - getProvider: (id: string) => ?OutputProvider, - onDisplayableRecordHeightChange: RecordHeightChangeHandler, - filteredRecordCount: number, - filterText: string, - resetAllFilters: () => void, - fontSize: number, -}; - -type State = { - unseenMessages: boolean, -}; - // Maximum time (ms) for the console to try scrolling to the bottom. const MAXIMUM_SCROLLING_TIME = 3000; - let count = 0; -export default class ConsoleView extends React.Component { - _consoleScrollPaneEl: ?HTMLDivElement; - _consoleHeaderComponent: ?ConsoleHeader; - _disposables: UniversalDisposable; - _isScrolledNearBottom: boolean; - _id: number; - _inputArea: ?InputArea; - +class ConsoleView extends React.Component { // Used when _scrollToBottom is called. The console optimizes message loading // so scrolling to the bottom once doesn't always scroll to the bottom since // more messages can be loaded after. - _continuouslyScrollToBottom: boolean; - _scrollingThrottle: ?rxjs$Subscription; + constructor(props) { + super(props); - _outputTable: ?OutputTable; + this._getExecutor = id => { + return this.props.executors.get(id); + }; + + this._getProvider = id => { + return this.props.getProvider(id); + }; + + this._executePrompt = code => { + this.props.execute(code); // Makes the console to scroll to the bottom. + + this._isScrolledNearBottom = true; + }; + + this._handleScroll = (offsetHeight, scrollHeight, scrollTop) => { + this._handleScrollEnd(offsetHeight, scrollHeight, scrollTop); + }; + + this._handleOutputTable = ref => { + this._outputTable = ref; + }; + + this._scrollToBottom = () => { + if (!this._outputTable) { + return; + } + + this._outputTable.scrollToBottom(); + + this.setState({ + unseenMessages: false + }); + }; + + this._startScrollToBottom = () => { + if (!this._continuouslyScrollToBottom) { + this._continuouslyScrollToBottom = true; + this._scrollingThrottle = _RxMin.Observable.timer(MAXIMUM_SCROLLING_TIME).subscribe(() => { + this._stopScrollToBottom(); + }); + } + + this._scrollToBottom(); + }; + + this._stopScrollToBottom = () => { + this._continuouslyScrollToBottom = false; + + if (this._scrollingThrottle != null) { + this._scrollingThrottle.unsubscribe(); + } + }; + + this._shouldScrollToBottom = () => { + return this._isScrolledNearBottom || this._continuouslyScrollToBottom; + }; - constructor(props: Props) { - super(props); this.state = { - unseenMessages: false, + unseenMessages: false }; - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); this._isScrolledNearBottom = true; this._continuouslyScrollToBottom = false; this._id = count++; } - componentDidMount(): void { - this._disposables.add( - // Wait for `` to render itself via react-virtualized before scrolling and - // re-measuring; Otherwise, the scrolled location will be inaccurate, preventing the Console - // from auto-scrolling. - macrotask.subscribe(() => { - this._startScrollToBottom(); - }), - () => { - if (this._scrollingThrottle != null) { - this._scrollingThrottle.unsubscribe(); + componentDidMount() { + this._disposables.add( // Wait for `` to render itself via react-virtualized before scrolling and + // re-measuring; Otherwise, the scrolled location will be inaccurate, preventing the Console + // from auto-scrolling. + _observable().macrotask.subscribe(() => { + this._startScrollToBottom(); + }), () => { + if (this._scrollingThrottle != null) { + this._scrollingThrottle.unsubscribe(); + } + }, atom.commands.add('atom-workspace', { + // eslint-disable-next-line nuclide-internal/atom-apis + 'atom-ide-console:focus-console-prompt': () => { + if (this._inputArea != null) { + this._inputArea.focus(); } - }, - atom.commands.add('atom-workspace', { - // eslint-disable-next-line nuclide-internal/atom-apis - 'atom-ide-console:focus-console-prompt': () => { - if (this._inputArea != null) { - this._inputArea.focus(); - } - }, - }), - atom.commands.add('atom-workspace', { - // eslint-disable-next-line nuclide-internal/atom-apis - 'atom-ide-console:scroll-to-bottom': () => { - this._scrollToBottom(); - }, - }), - atom.commands.add( - nullthrows(this._consoleScrollPaneEl), - 'atom-ide:filter', - () => this._focusFilter(), - ), - ); + } + }), atom.commands.add('atom-workspace', { + // eslint-disable-next-line nuclide-internal/atom-apis + 'atom-ide-console:scroll-to-bottom': () => { + this._scrollToBottom(); + } + }), atom.commands.add((0, _nullthrows().default)(this._consoleScrollPaneEl), 'atom-ide:filter', () => this._focusFilter())); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - componentDidUpdate(prevProps: Props): void { + componentDidUpdate(prevProps) { // If records are added while we're scrolled to the bottom (or very very close, at least), // automatically scroll. - if ( - this._isScrolledNearBottom && - recordsChanged( - prevProps.displayableRecords, - this.props.displayableRecords, - ) - ) { + if (this._isScrolledNearBottom && (0, _recordsChanged().default)(prevProps.displayableRecords, this.props.displayableRecords)) { this._startScrollToBottom(); } } - _focusFilter(): void { + _focusFilter() { if (this._consoleHeaderComponent != null) { this._consoleHeaderComponent.focusFilter(); } } - _renderPromptButton(): React.Element { - invariant(this.props.currentExecutor != null); - const {currentExecutor} = this.props; + _renderPromptButton() { + if (!(this.props.currentExecutor != null)) { + throw new Error("Invariant violation: \"this.props.currentExecutor != null\""); + } + + const { + currentExecutor + } = this.props; const options = Array.from(this.props.executors.values()).map(executor => ({ id: executor.id, - label: executor.name, + label: executor.name })); - return ( - - ); + return React.createElement(_PromptButton().default, { + value: currentExecutor.id, + onChange: this.props.selectExecutor, + options: options, + children: currentExecutor.name + }); } - _isScrolledToBottom( - offsetHeight: number, - scrollHeight: number, - scrollTop: number, - ): boolean { + _isScrolledToBottom(offsetHeight, scrollHeight, scrollTop) { return scrollHeight - (offsetHeight + scrollTop) < 5; } - UNSAFE_componentWillReceiveProps(nextProps: Props): void { + UNSAFE_componentWillReceiveProps(nextProps) { // If the messages were cleared, hide the notification. if (nextProps.displayableRecords.length === 0) { this._isScrolledNearBottom = true; - this.setState({unseenMessages: false}); - } else if ( - // If we receive new messages after we've scrolled away from the bottom, show the "new - // messages" notification. - !this._isScrolledNearBottom && - recordsChanged( - this.props.displayableRecords, - nextProps.displayableRecords, - ) - ) { - this.setState({unseenMessages: true}); + this.setState({ + unseenMessages: false + }); + } else if ( // If we receive new messages after we've scrolled away from the bottom, show the "new + // messages" notification. + !this._isScrolledNearBottom && (0, _recordsChanged().default)(this.props.displayableRecords, nextProps.displayableRecords)) { + this.setState({ + unseenMessages: true + }); } } - shouldComponentUpdate(nextProps: Props, nextState: State): boolean { - return ( - !shallowEqual(this.props, nextProps) || - !shallowEqual(this.state, nextState) - ); + shouldComponentUpdate(nextProps, nextState) { + return !(0, _shallowequal().default)(this.props, nextProps) || !(0, _shallowequal().default)(this.state, nextState); } - _getExecutor = (id: string): ?Executor => { - return this.props.executors.get(id); - }; - - _getProvider = (id: string): ?OutputProvider => { - return this.props.getProvider(id); - }; - - render(): React.Node { - return ( -
- - (this._consoleHeaderComponent = component)} - selectedSourceIds={this.props.selectedSourceIds} - sources={this.props.sources} - onFilterChange={this.props.updateFilter} - onSelectedSourcesChange={this.props.selectSources} - /> - {/* - We need an extra wrapper element here in order to have the new messages notification stick - to the bottom of the scrollable area (and not scroll with it). - - console-font-size is defined in main.js and updated via a user setting - */} -
-
(this._consoleScrollPaneEl = el)}> - - =0.53.0) Flow suppress - ref={this._handleOutputTable} - displayableRecords={this.props.displayableRecords} - showSourceLabels={this.props.selectedSourceIds.length > 1} - fontSize={this.props.fontSize} - getExecutor={this._getExecutor} - getProvider={this._getProvider} - onScroll={this._handleScroll} - onDisplayableRecordHeightChange={ - this.props.onDisplayableRecordHeightChange - } - shouldScrollToBottom={this._shouldScrollToBottom} - /> - -
- {this._renderPrompt()} -
-
- ); + ` + }), React.createElement(_ConsoleHeader().default, { + clear: this.props.clearRecords, + createPaste: this.props.createPaste, + invalidFilterInput: this.props.invalidFilterInput, + enableRegExpFilter: this.props.enableRegExpFilter, + filterText: this.props.filterText, + ref: component => this._consoleHeaderComponent = component, + selectedSourceIds: this.props.selectedSourceIds, + sources: this.props.sources, + onFilterChange: this.props.updateFilter, + onSelectedSourcesChange: this.props.selectSources + }), React.createElement("div", { + className: "console-body", + id: 'console-font-size-' + this._id + }, React.createElement("div", { + className: "console-scroll-pane-wrapper atom-ide-filterable", + ref: el => this._consoleScrollPaneEl = el + }, React.createElement(_FilteredMessagesReminder().default, { + filteredRecordCount: this.props.filteredRecordCount, + onReset: this.props.resetAllFilters + }), React.createElement(_OutputTable().default // $FlowFixMe(>=0.53.0) Flow suppress + , { + ref: this._handleOutputTable, + displayableRecords: this.props.displayableRecords, + showSourceLabels: this.props.selectedSourceIds.length > 1, + fontSize: this.props.fontSize, + getExecutor: this._getExecutor, + getProvider: this._getProvider, + onScroll: this._handleScroll, + onDisplayableRecordHeightChange: this.props.onDisplayableRecordHeightChange, + shouldScrollToBottom: this._shouldScrollToBottom + }), React.createElement(_NewMessagesNotification().default, { + visible: this.state.unseenMessages, + onClick: this._startScrollToBottom + })), this._renderPrompt())); } - _getMultiLineTip(): string { - const {currentExecutor} = this.props; + _getMultiLineTip() { + const { + currentExecutor + } = this.props; + if (currentExecutor == null) { return ''; } - const keyCombo = - process.platform === 'darwin' - ? // Option + Enter on Mac - '\u2325 + \u23CE' - : // Shift + Enter on Windows and Linux. - 'Shift + Enter'; + const keyCombo = process.platform === 'darwin' ? // Option + Enter on Mac + '\u2325 + \u23CE' : // Shift + Enter on Windows and Linux. + 'Shift + Enter'; return `Tip: ${keyCombo} to insert a newline`; } - _renderPrompt(): ?React.Element { - const {currentExecutor} = this.props; + _renderPrompt() { + const { + currentExecutor + } = this.props; + if (currentExecutor == null) { return; } - return ( -
- {this._renderPromptButton()} - (this._inputArea = component)} - scopeName={currentExecutor.scopeName} - onSubmit={this._executePrompt} - history={this.props.history} - watchEditor={this.props.watchEditor} - placeholderText={this._getMultiLineTip()} - /> -
- ); - } - - _executePrompt = (code: string): void => { - this.props.execute(code); - // Makes the console to scroll to the bottom. - this._isScrolledNearBottom = true; - }; - _handleScroll = ( - offsetHeight: number, - scrollHeight: number, - scrollTop: number, - ): void => { - this._handleScrollEnd(offsetHeight, scrollHeight, scrollTop); - }; + return React.createElement("div", { + className: "console-prompt" + }, this._renderPromptButton(), React.createElement(_InputArea().default, { + ref: component => this._inputArea = component, + scopeName: currentExecutor.scopeName, + onSubmit: this._executePrompt, + history: this.props.history, + watchEditor: this.props.watchEditor, + placeholderText: this._getMultiLineTip() + })); + } - _handleScrollEnd( - offsetHeight: number, - scrollHeight: number, - scrollTop: number, - ): void { - const isScrolledToBottom = this._isScrolledToBottom( - offsetHeight, - scrollHeight, - scrollTop, - ); + _handleScrollEnd(offsetHeight, scrollHeight, scrollTop) { + const isScrolledToBottom = this._isScrolledToBottom(offsetHeight, scrollHeight, scrollTop); this._isScrolledNearBottom = isScrolledToBottom; + this._stopScrollToBottom(); + this.setState({ // TODO: (wbinnssmith) T30771435 this setState depends on current state // and should use an updater function rather than an object // eslint-disable-next-line react/no-access-state-in-setstate - unseenMessages: this.state.unseenMessages && !this._isScrolledNearBottom, + unseenMessages: this.state.unseenMessages && !this._isScrolledNearBottom }); } - _handleOutputTable = (ref: OutputTable): void => { - this._outputTable = ref; - }; - - _scrollToBottom = (): void => { - if (!this._outputTable) { - return; - } - - this._outputTable.scrollToBottom(); - - this.setState({unseenMessages: false}); - }; - - _startScrollToBottom = (): void => { - if (!this._continuouslyScrollToBottom) { - this._continuouslyScrollToBottom = true; - - this._scrollingThrottle = Observable.timer( - MAXIMUM_SCROLLING_TIME, - ).subscribe(() => { - this._stopScrollToBottom(); - }); - } - - this._scrollToBottom(); - }; - - _stopScrollToBottom = (): void => { - this._continuouslyScrollToBottom = false; - if (this._scrollingThrottle != null) { - this._scrollingThrottle.unsubscribe(); - } - }; - - _shouldScrollToBottom = (): boolean => { - return this._isScrolledNearBottom || this._continuouslyScrollToBottom; - }; } + +exports.default = ConsoleView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/FilteredMessagesReminder.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/FilteredMessagesReminder.js index 8f9e510b..477ccc76 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/FilteredMessagesReminder.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/FilteredMessagesReminder.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,42 +17,40 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +class FilteredMessagesReminder extends React.Component { + constructor(...args) { + var _temp; -import * as React from 'react'; - -type Props = { - filteredRecordCount: number, - onReset: () => void, -}; + return _temp = super(...args), this.handleClick = e => { + e.preventDefault(); + this.props.onReset(); + }, _temp; + } -export default class FilteredMessagesReminder extends React.Component { - handleClick = (e: SyntheticEvent<>) => { - e.preventDefault(); - this.props.onReset(); - }; + render() { + const { + filteredRecordCount + } = this.props; - render(): React.Node { - const {filteredRecordCount} = this.props; if (filteredRecordCount === 0) { return null; } - return ( -
-
-
-            {filteredRecordCount}{' '}
-            {filteredRecordCount === 1 ? 'message is' : 'messages are'} hidden
-            by filters.
-          
-
- -
Show all messages.
-
-
- ); + return React.createElement("div", { + className: "console-filtered-reminder" + }, React.createElement("div", { + style: { + flex: 1 + } + }, React.createElement("pre", null, filteredRecordCount, ' ', filteredRecordCount === 1 ? 'message is' : 'messages are', " hidden by filters.")), React.createElement("a", { + href: "#", + onClick: this.handleClick + }, React.createElement("pre", null, "Show all messages."))); } + } + +exports.default = FilteredMessagesReminder; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/InputArea.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/InputArea.js index 08ed7a59..a0d84aae 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/InputArea.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/InputArea.js @@ -1,3 +1,40 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _AtomTextEditor() { + const data = require("../../../../../nuclide-commons-ui/AtomTextEditor"); + + _AtomTextEditor = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,177 +43,162 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import {AtomTextEditor} from 'nuclide-commons-ui/AtomTextEditor'; -import {Observable} from 'rxjs'; - -type Props = { - onSubmit: (value: string) => mixed, - scopeName: ?string, - history: Array, - watchEditor: ?atom$AutocompleteWatchEditor, - onDidTextBufferChange?: (event: atom$AggregatedTextEditEvent) => mixed, - placeholderText?: string, -}; - -type State = { - historyIndex: number, - draft: string, -}; - const ENTER_KEY_CODE = 13; const UP_KEY_CODE = 38; const DOWN_KEY_CODE = 40; -export default class InputArea extends React.Component { - _keySubscription: ?rxjs$ISubscription; - _textEditorModel: ?atom$TextEditor; - - constructor(props: Props) { +class InputArea extends React.Component { + constructor(props) { super(props); - this.state = { - historyIndex: -1, - draft: '', + + this.focus = () => { + if (this._textEditorModel != null) { + this._textEditorModel.getElement().focus(); + } }; - } - focus = (): void => { - if (this._textEditorModel != null) { - this._textEditorModel.getElement().focus(); - } - }; + this._submit = () => { + // Clear the text and trigger the `onSubmit` callback + const editor = this._textEditorModel; - _submit = (): void => { - // Clear the text and trigger the `onSubmit` callback - const editor = this._textEditorModel; - if (editor == null) { - return; - } - - const text = editor.getText(); - if (text === '') { - return; - } - - editor.setText(''); // Clear the text field. - this.props.onSubmit(text); - this.setState({historyIndex: -1}); - }; + if (editor == null) { + return; + } - _attachLabel = (editor: atom$TextEditor): IDisposable => { - const {watchEditor} = this.props; - const disposable = new UniversalDisposable(); - if (watchEditor) { - disposable.add(watchEditor(editor, ['nuclide-console'])); - } - return disposable; - }; + const text = editor.getText(); - _handleTextEditor = (component: ?AtomTextEditor): void => { - if (this._keySubscription) { - this._textEditorModel = null; - this._keySubscription.unsubscribe(); - } - if (component) { - this._textEditorModel = component.getModel(); - const el = ReactDOM.findDOMNode(component); - this._keySubscription = Observable.fromEvent(el, 'keydown').subscribe( - this._handleKeyDown, - ); - } - }; + if (text === '') { + return; + } - _handleKeyDown = (event: KeyboardEvent): void => { - const editor = this._textEditorModel; - // Detect AutocompletePlus menu element: https://git.io/vddLi - const isAutocompleteOpen = - document.querySelector('autocomplete-suggestion-list') != null; - if (editor == null) { - return; - } - if (event.which === ENTER_KEY_CODE) { - if (!isAutocompleteOpen) { - event.preventDefault(); - event.stopImmediatePropagation(); + editor.setText(''); // Clear the text field. - if (event.ctrlKey || event.altKey || event.shiftKey) { - editor.insertNewline(); - return; - } + this.props.onSubmit(text); + this.setState({ + historyIndex: -1 + }); + }; - this._submit(); + this._attachLabel = editor => { + const { + watchEditor + } = this.props; + const disposable = new (_UniversalDisposable().default)(); + + if (watchEditor) { + disposable.add(watchEditor(editor, ['nuclide-console'])); } - } else if ( - event.which === UP_KEY_CODE && - (editor.getLineCount() <= 1 || editor.getCursorBufferPosition().row === 0) - ) { - if (this.props.history.length === 0 || isAutocompleteOpen) { - return; + + return disposable; + }; + + this._handleTextEditor = component => { + if (this._keySubscription) { + this._textEditorModel = null; + + this._keySubscription.unsubscribe(); } - event.preventDefault(); - event.stopImmediatePropagation(); - const historyIndex = Math.min( - this.state.historyIndex + 1, - this.props.history.length - 1, - ); - if (this.state.historyIndex === -1) { - this.setState({historyIndex, draft: editor.getText()}); - } else { - this.setState({historyIndex}); + + if (component) { + this._textEditorModel = component.getModel(); + + const el = _reactDom.default.findDOMNode(component); + + this._keySubscription = _RxMin.Observable.fromEvent(el, 'keydown').subscribe(this._handleKeyDown); } - editor.setText( - this.props.history[this.props.history.length - historyIndex - 1], - ); - } else if ( - event.which === DOWN_KEY_CODE && - (editor.getLineCount() <= 1 || - editor.getCursorBufferPosition().row === editor.getLineCount() - 1) - ) { - if (this.props.history.length === 0 || isAutocompleteOpen) { + }; + + this._handleKeyDown = event => { + const editor = this._textEditorModel; // Detect AutocompletePlus menu element: https://git.io/vddLi + + const isAutocompleteOpen = document.querySelector('autocomplete-suggestion-list') != null; + + if (editor == null) { return; } - event.preventDefault(); - event.stopImmediatePropagation(); - // TODO: (wbinnssmith) T30771435 this setState depends on current state - // and should use an updater function rather than an object - // eslint-disable-next-line react/no-access-state-in-setstate - const historyIndex = Math.max(this.state.historyIndex - 1, -1); - this.setState({historyIndex}); - if (historyIndex === -1) { - editor.setText(this.state.draft); - } else { - editor.setText( - this.props.history[this.props.history.length - historyIndex - 1], - ); + + if (event.which === ENTER_KEY_CODE) { + if (!isAutocompleteOpen) { + event.preventDefault(); + event.stopImmediatePropagation(); + + if (event.ctrlKey || event.altKey || event.shiftKey) { + editor.insertNewline(); + return; + } + + this._submit(); + } + } else if (event.which === UP_KEY_CODE && (editor.getLineCount() <= 1 || editor.getCursorBufferPosition().row === 0)) { + if (this.props.history.length === 0 || isAutocompleteOpen) { + return; + } + + event.preventDefault(); + event.stopImmediatePropagation(); + const historyIndex = Math.min(this.state.historyIndex + 1, this.props.history.length - 1); + + if (this.state.historyIndex === -1) { + this.setState({ + historyIndex, + draft: editor.getText() + }); + } else { + this.setState({ + historyIndex + }); + } + + editor.setText(this.props.history[this.props.history.length - historyIndex - 1]); + } else if (event.which === DOWN_KEY_CODE && (editor.getLineCount() <= 1 || editor.getCursorBufferPosition().row === editor.getLineCount() - 1)) { + if (this.props.history.length === 0 || isAutocompleteOpen) { + return; + } + + event.preventDefault(); + event.stopImmediatePropagation(); // TODO: (wbinnssmith) T30771435 this setState depends on current state + // and should use an updater function rather than an object + // eslint-disable-next-line react/no-access-state-in-setstate + + const historyIndex = Math.max(this.state.historyIndex - 1, -1); + this.setState({ + historyIndex + }); + + if (historyIndex === -1) { + editor.setText(this.state.draft); + } else { + editor.setText(this.props.history[this.props.history.length - historyIndex - 1]); + } } - } - }; + }; - render(): React.Node { - const grammar = - this.props.scopeName == null - ? null - : atom.grammars.grammarForScopeName(this.props.scopeName); - return ( -
- -
- ); + this.state = { + historyIndex: -1, + draft: '' + }; } + + render() { + const grammar = this.props.scopeName == null ? null : atom.grammars.grammarForScopeName(this.props.scopeName); + return React.createElement("div", { + className: "console-input-wrapper" + }, React.createElement(_AtomTextEditor().AtomTextEditor, { + ref: this._handleTextEditor, + grammar: grammar, + gutterHidden: true, + autoGrow: true, + lineNumberGutterVisible: false, + onConfirm: this._submit, + onInitialized: this._attachLabel, + onDidTextBufferChange: this.props.onDidTextBufferChange, + placeholderText: this.props.placeholderText + })); + } + } + +exports.default = InputArea; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/NewMessagesNotification.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/NewMessagesNotification.js index f0ed5085..6f4e25c5 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/NewMessagesNotification.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/NewMessagesNotification.js @@ -1,3 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,33 +29,22 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import classnames from 'classnames'; -import * as React from 'react'; - -type Props = { - onClick: () => mixed, - visible: boolean, -}; - -export default class NewMessagesNotification extends React.Component { - render(): React.Node { - const className = classnames( - 'console-new-messages-notification', - 'badge', - 'badge-info', - { - visible: this.props.visible, - }, - ); - return ( -
- - New Messages -
- ); +class NewMessagesNotification extends React.Component { + render() { + const className = (0, _classnames().default)('console-new-messages-notification', 'badge', 'badge-info', { + visible: this.props.visible + }); + return React.createElement("div", { + className: className, + onClick: this.props.onClick + }, React.createElement("span", { + className: "console-new-messages-notification-icon icon icon-nuclicon-arrow-down" + }), "New Messages"); } + } + +exports.default = NewMessagesNotification; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/OutputTable.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/OutputTable.js index 1342b586..2e9e1cd7 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/OutputTable.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/OutputTable.js @@ -1,3 +1,88 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _observableDom() { + const data = require("../../../../../nuclide-commons-ui/observable-dom"); + + _observableDom = function () { + return data; + }; + + return data; +} + +function _Hasher() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/Hasher")); + + _Hasher = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _List() { + const data = _interopRequireDefault(require("react-virtualized/dist/commonjs/List")); + + _List = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _RecordView() { + const data = _interopRequireDefault(require("./RecordView")); + + _RecordView = function () { + return data; + }; + + return data; +} + +function _recordsChanged() { + const data = _interopRequireDefault(require("../recordsChanged")); + + _recordsChanged = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,121 +91,157 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - DisplayableRecord, - Executor, - OutputProvider, - Record, - RecordHeightChangeHandler, -} from '../types'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import nullThrows from 'nullthrows'; -import {ResizeObservable} from 'nuclide-commons-ui/observable-dom'; -import Hasher from 'nuclide-commons/Hasher'; -import * as React from 'react'; -import List from 'react-virtualized/dist/commonjs/List'; -import {Subject} from 'rxjs'; -import RecordView from './RecordView'; -import recordsChanged from '../recordsChanged'; - -type Props = { - displayableRecords: Array, - showSourceLabels: boolean, - fontSize: number, - getExecutor: (id: string) => ?Executor, - getProvider: (id: string) => ?OutputProvider, - onScroll: ( - offsetHeight: number, - scrollHeight: number, - scrollTop: number, - ) => void, - onDisplayableRecordHeightChange: RecordHeightChangeHandler, - shouldScrollToBottom: () => boolean, -}; - -type State = { - width: number, - height: number, -}; - -type RowRendererParams = { - index: number, - key: string, - style: Object, - isScrolling: boolean, -}; - -type RowHeightParams = { - index: number, -}; - -/* eslint-disable react/no-unused-prop-types */ -type OnScrollParams = { - clientHeight: number, - scrollHeight: number, - scrollTop: number, -}; /* eslint-enable react/no-unused-prop-types */ - // The number of extra rows to render beyond what is visible const OVERSCAN_COUNT = 5; -export default class OutputTable extends React.Component { - _disposable: UniversalDisposable; - _hasher: Hasher; +class OutputTable extends React.Component { // This is a from react-virtualized (untyped library) - _list: ?React.Element; - _wrapper: ?HTMLElement; - _renderedRecords: Map; - // The currently rendered range. - _startIndex: number; - _stopIndex: number; - _refs: Subject; - - constructor(props: Props) { + constructor(props) { super(props); - this._disposable = new UniversalDisposable(); - this._hasher = new Hasher(); + + this._handleRef = node => { + this._refs.next(node); + }; + + this._handleListRender = opts => { + this._startIndex = opts.startIndex; + this._stopIndex = opts.stopIndex; + }; + + this._getExecutor = id => { + return this.props.getExecutor(id); + }; + + this._getProvider = id => { + return this.props.getProvider(id); + }; + + this._renderRow = rowMetadata => { + const { + index, + style + } = rowMetadata; + const displayableRecord = this.props.displayableRecords[index]; + const { + record + } = displayableRecord; + return React.createElement("div", { + key: this._hasher.getHash(displayableRecord.record), + className: "console-table-row-wrapper", + style: style + }, React.createElement(_RecordView().default // eslint-disable-next-line nuclide-internal/jsx-simple-callback-refs + , { + ref: view => { + if (view != null) { + this._renderedRecords.set(record, view); + } else { + this._renderedRecords.delete(record); + } + }, + getExecutor: this._getExecutor, + getProvider: this._getProvider, + displayableRecord: displayableRecord, + showSourceLabel: this.props.showSourceLabels, + onHeightChange: this._handleRecordHeightChange + })); + }; + + this._getRowHeight = ({ + index + }) => { + return this.props.displayableRecords[index].height; + }; + + this._handleTableWrapper = tableWrapper => { + this._wrapper = tableWrapper; + }; + + this._handleListRef = listRef => { + this._list = listRef; + }; + + this._handleResize = (height, width) => { + if (height === this.state.height && width === this.state.width) { + return; + } + + this.setState({ + width, + height + }); // When this component resizes, the inner records will + // also resize and potentially have their heights change + // So we measure all of their heights again here + + this._renderedRecords.forEach(recordView => recordView.measureAndNotifyHeight()); + }; + + this._handleRecordHeightChange = (recordId, newHeight) => { + this.props.onDisplayableRecordHeightChange(recordId, newHeight, () => { + // The react-virtualized List component is provided the row heights + // through a function, so it has no way of knowing that a row's height + // has changed unless we explicitly notify it to recompute the heights. + if (this._list == null) { + return; + } // $FlowIgnore Untyped react-virtualized List component method + + + this._list.recomputeRowHeights(); // If we are already scrolled to the bottom, scroll to ensure that the scrollbar remains at + // the bottom. This is important not just for if the last record changes height through user + // interaction (e.g. expanding a debugger variable), but also because this is the mechanism + // through which the record's true initial height is reported. Therefore, we may have scrolled + // to the bottom, and only afterwards received its true height. In this case, it's important + // that we then scroll to the new bottom. + + + if (this.props.shouldScrollToBottom()) { + this.scrollToBottom(); + } + }); + }; + + this._onScroll = ({ + clientHeight, + scrollHeight, + scrollTop + }) => { + this.props.onScroll(clientHeight, scrollHeight, scrollTop); + }; + + this._disposable = new (_UniversalDisposable().default)(); + this._hasher = new (_Hasher().default)(); this._renderedRecords = new Map(); this.state = { width: 0, - height: 0, + height: 0 }; this._startIndex = 0; this._stopIndex = 0; - this._refs = new Subject(); - this._disposable.add( - this._refs - .filter(Boolean) - .switchMap(node => new ResizeObservable(nullThrows(node)).mapTo(node)) - .subscribe(node => { - const {offsetHeight, offsetWidth} = nullThrows(node); - this._handleResize(offsetHeight, offsetWidth); - }), - ); + this._refs = new _RxMin.Subject(); + + this._disposable.add(this._refs.filter(Boolean).switchMap(node => new (_observableDom().ResizeObservable)((0, _nullthrows().default)(node)).mapTo(node)).subscribe(node => { + const { + offsetHeight, + offsetWidth + } = (0, _nullthrows().default)(node); + + this._handleResize(offsetHeight, offsetWidth); + })); } - componentDidUpdate(prevProps: Props, prevState: State): void { - if ( - this._list != null && - recordsChanged( - prevProps.displayableRecords, - this.props.displayableRecords, - ) - ) { + componentDidUpdate(prevProps, prevState) { + if (this._list != null && (0, _recordsChanged().default)(prevProps.displayableRecords, this.props.displayableRecords)) { // $FlowIgnore Untyped react-virtualized List method this._list.recomputeRowHeights(); } + if (prevProps.fontSize !== this.props.fontSize) { - this._renderedRecords.forEach(recordView => - recordView.measureAndNotifyHeight(), - ); + this._renderedRecords.forEach(recordView => recordView.measureAndNotifyHeight()); } } @@ -128,143 +249,36 @@ export default class OutputTable extends React.Component { this._disposable.dispose(); } - _handleRef = (node: ?HTMLElement) => { - this._refs.next(node); - }; - - render(): React.Node { - return ( -
- {this._containerRendered() ? ( - =0.53.0) Flow suppress - ref={this._handleListRef} - height={this.state.height} - width={this.state.width} - rowCount={this.props.displayableRecords.length} - rowHeight={this._getRowHeight} - rowRenderer={this._renderRow} - overscanRowCount={OVERSCAN_COUNT} - onScroll={this._onScroll} - onRowsRendered={this._handleListRender} - /> - ) : null} -
- ); + render() { + return React.createElement("div", { + className: "console-table-wrapper native-key-bindings", + ref: this._handleRef, + tabIndex: "1" + }, this._containerRendered() ? React.createElement(_List().default // $FlowFixMe(>=0.53.0) Flow suppress + , { + ref: this._handleListRef, + height: this.state.height, + width: this.state.width, + rowCount: this.props.displayableRecords.length, + rowHeight: this._getRowHeight, + rowRenderer: this._renderRow, + overscanRowCount: OVERSCAN_COUNT, + onScroll: this._onScroll, + onRowsRendered: this._handleListRender + }) : null); } - _handleListRender = (opts: {startIndex: number, stopIndex: number}): void => { - this._startIndex = opts.startIndex; - this._stopIndex = opts.stopIndex; - }; - - scrollToBottom(): void { + scrollToBottom() { if (this._list != null) { // $FlowIgnore Untyped react-virtualized List method this._list.scrollToRow(this.props.displayableRecords.length - 1); } } - _getExecutor = (id: string): ?Executor => { - return this.props.getExecutor(id); - }; - - _getProvider = (id: string): ?OutputProvider => { - return this.props.getProvider(id); - }; - - _renderRow = (rowMetadata: RowRendererParams): React.Element => { - const {index, style} = rowMetadata; - const displayableRecord = this.props.displayableRecords[index]; - const {record} = displayableRecord; - return ( -
- { - if (view != null) { - this._renderedRecords.set(record, view); - } else { - this._renderedRecords.delete(record); - } - }} - getExecutor={this._getExecutor} - getProvider={this._getProvider} - displayableRecord={displayableRecord} - showSourceLabel={this.props.showSourceLabels} - onHeightChange={this._handleRecordHeightChange} - /> -
- ); - }; - - _containerRendered(): boolean { + _containerRendered() { return this.state.width !== 0 && this.state.height !== 0; } - _getRowHeight = ({index}: RowHeightParams): number => { - return this.props.displayableRecords[index].height; - }; - - _handleTableWrapper = (tableWrapper: HTMLElement): void => { - this._wrapper = tableWrapper; - }; - - _handleListRef = (listRef: React.Element): void => { - this._list = listRef; - }; - - _handleResize = (height: number, width: number): void => { - if (height === this.state.height && width === this.state.width) { - return; - } - this.setState({ - width, - height, - }); - - // When this component resizes, the inner records will - // also resize and potentially have their heights change - // So we measure all of their heights again here - this._renderedRecords.forEach(recordView => - recordView.measureAndNotifyHeight(), - ); - }; - - _handleRecordHeightChange = (recordId: number, newHeight: number): void => { - this.props.onDisplayableRecordHeightChange(recordId, newHeight, () => { - // The react-virtualized List component is provided the row heights - // through a function, so it has no way of knowing that a row's height - // has changed unless we explicitly notify it to recompute the heights. - if (this._list == null) { - return; - } - // $FlowIgnore Untyped react-virtualized List component method - this._list.recomputeRowHeights(); - - // If we are already scrolled to the bottom, scroll to ensure that the scrollbar remains at - // the bottom. This is important not just for if the last record changes height through user - // interaction (e.g. expanding a debugger variable), but also because this is the mechanism - // through which the record's true initial height is reported. Therefore, we may have scrolled - // to the bottom, and only afterwards received its true height. In this case, it's important - // that we then scroll to the new bottom. - if (this.props.shouldScrollToBottom()) { - this.scrollToBottom(); - } - }); - }; - - _onScroll = ({ - clientHeight, - scrollHeight, - scrollTop, - }: OnScrollParams): void => { - this.props.onScroll(clientHeight, scrollHeight, scrollTop); - }; } + +exports.default = OutputTable; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/PromptButton.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/PromptButton.js index 84ba1cbe..6f6a0cb6 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/PromptButton.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/PromptButton.js @@ -1,3 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +var _electron = _interopRequireDefault(require("electron")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,31 +21,40 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const { + remote +} = _electron.default; -import invariant from 'assert'; -import * as React from 'react'; -import electron from 'electron'; - -const {remote} = electron; -invariant(remote != null); +if (!(remote != null)) { + throw new Error("Invariant violation: \"remote != null\""); +} -type PromptOption = { - id: string, - label: string, -}; +class PromptButton extends React.Component { + constructor(...args) { + var _temp; -type Props = { - value: string, - onChange: (value: string) => void, - children: ?any, - options: Array, -}; + return _temp = super(...args), this._handleClick = event => { + const menu = new remote.Menu(); // TODO: Sort alphabetically by label -export default class PromptButton extends React.Component { - _menu: ?electron$Menu; + this.props.options.forEach(option => { + menu.append(new remote.MenuItem({ + type: 'checkbox', + checked: this.props.value === option.id, + label: option.label, + click: () => this.props.onChange(option.id) + })); + }); + menu.popup({ + x: event.clientX, + y: event.clientY, + async: true + }); + this._menu = menu; + }, _temp; + } componentWillUnmount() { if (this._menu != null) { @@ -38,29 +62,17 @@ export default class PromptButton extends React.Component { } } - render(): React.Node { - return ( - - {this.props.children} - - - ); + render() { + return React.createElement("span", { + className: "console-prompt-wrapper", + onClick: this._handleClick + }, React.createElement("span", { + className: "console-prompt-label" + }, this.props.children), React.createElement("span", { + className: "icon icon-chevron-right" + })); } - _handleClick = (event: SyntheticMouseEvent<>): void => { - const menu = new remote.Menu(); - // TODO: Sort alphabetically by label - this.props.options.forEach(option => { - menu.append( - new remote.MenuItem({ - type: 'checkbox', - checked: this.props.value === option.id, - label: option.label, - click: () => this.props.onChange(option.id), - }), - ); - }); - menu.popup({x: event.clientX, y: event.clientY, async: true}); - this._menu = menu; - }; } + +exports.default = PromptButton; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/RecordView.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/RecordView.js index cb2e8d97..7b8677d4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/RecordView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/RecordView.js @@ -1,3 +1,116 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _MeasuredComponent() { + const data = require("../../../../../nuclide-commons-ui/MeasuredComponent"); + + _MeasuredComponent = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _LazyNestedValueComponent() { + const data = require("../../../../../nuclide-commons-ui/LazyNestedValueComponent"); + + _LazyNestedValueComponent = function () { + return data; + }; + + return data; +} + +function _SimpleValueComponent() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/SimpleValueComponent")); + + _SimpleValueComponent = function () { + return data; + }; + + return data; +} + +function _shallowequal() { + const data = _interopRequireDefault(require("shallowequal")); + + _shallowequal = function () { + return data; + }; + + return data; +} + +function _Ansi() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/Ansi")); + + _Ansi = function () { + return data; + }; + + return data; +} + +function _TextRenderer() { + const data = require("../../../../../nuclide-commons-ui/TextRenderer"); + + _TextRenderer = function () { + return data; + }; + + return data; +} + +function _debounce() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/debounce")); + + _debounce = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _parseText() { + const data = _interopRequireDefault(require("../parseText")); + + _parseText = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,62 +119,61 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - Level, - Record, - DisplayableRecord, - Executor, - OutputProvider, -} from '../types'; - -import type {RenderSegmentProps} from 'nuclide-commons-ui/Ansi'; - -import classnames from 'classnames'; -import {MeasuredComponent} from 'nuclide-commons-ui/MeasuredComponent'; -import * as React from 'react'; -import {LazyNestedValueComponent} from 'nuclide-commons-ui/LazyNestedValueComponent'; -import SimpleValueComponent from 'nuclide-commons-ui/SimpleValueComponent'; -import shallowEqual from 'shallowequal'; -import Ansi from 'nuclide-commons-ui/Ansi'; -import {TextRenderer} from 'nuclide-commons-ui/TextRenderer'; -import debounce from 'nuclide-commons/debounce'; -import {nextAnimationFrame} from 'nuclide-commons/observable'; -import parseText from '../parseText'; - -type Props = { - displayableRecord: DisplayableRecord, - showSourceLabel: boolean, - getExecutor: (id: string) => ?Executor, - getProvider: (id: string) => ?OutputProvider, - onHeightChange: (recordId: number, newHeight: number) => void, -}; - -const AnsiRenderSegment = ({key, style, content}: RenderSegmentProps) => ( - - {parseText(content)} - -); +const AnsiRenderSegment = ({ + key, + style, + content +}) => React.createElement("span", { + key: key, + style: style, + className: "nuclide-console-default-text-colors" +}, (0, _parseText().default)(content)); const ONE_DAY = 1000 * 60 * 60 * 24; -export default class RecordView extends React.Component { - _wrapper: ?HTMLElement; - _debouncedMeasureAndNotifyHeight: () => void; - _rafDisposable: ?rxjs$Subscription; - - constructor(props: Props) { - super(props); - // The MeasuredComponent can call this many times in quick succession as the +class RecordView extends React.Component { + constructor(props) { + super(props); // The MeasuredComponent can call this many times in quick succession as the // child components render, so we debounce it since we only want to know about // the height change once everything has settled down - (this: any)._debouncedMeasureAndNotifyHeight = debounce( - this.measureAndNotifyHeight, - 10, - ); + + this.measureAndNotifyHeight = () => { + // This method is called after the necessary DOM mutations have + // already occurred, however it is possible that the updates have + // not been flushed to the screen. So the height change update + // is deferred until the rendering is complete so that + // this._wrapper.offsetHeight gives us the correct final height + if (this._rafDisposable != null) { + this._rafDisposable.unsubscribe(); + } + + this._rafDisposable = _observable().nextAnimationFrame.subscribe(() => { + if (this._wrapper == null) { + return; + } + + const { + offsetHeight + } = this._wrapper; + const { + displayableRecord, + onHeightChange + } = this.props; + + if (offsetHeight !== displayableRecord.height) { + onHeightChange(displayableRecord.id, offsetHeight); + } + }); + }; + + this._handleRecordWrapper = wrapper => { + this._wrapper = wrapper; + }; + + this._debouncedMeasureAndNotifyHeight = (0, _debounce().default)(this.measureAndNotifyHeight, 10); } componentDidMount() { @@ -76,13 +188,16 @@ export default class RecordView extends React.Component { } } - _renderContent(displayableRecord: DisplayableRecord): React.Element { - const {record} = displayableRecord; + _renderContent(displayableRecord) { + const { + record + } = displayableRecord; + if (record.kind === 'request') { // TODO: We really want to use a text editor to render this so that we can get syntax // highlighting, but they're just too expensive. Figure out a less-expensive way to get syntax // highlighting. - return
{record.text || ' '}
; + return React.createElement("pre", null, record.text || ' '); } else if (record.kind === 'response') { const executor = this.props.getExecutor(record.sourceId); return this._renderNestedValueComponent(displayableRecord, executor); @@ -94,159 +209,142 @@ export default class RecordView extends React.Component { const text = record.text || ' '; if (record.format === 'ansi') { - return {text}; + return React.createElement(_Ansi().default, { + renderSegment: AnsiRenderSegment + }, text); } - return
{parseText(text)}
; + + return React.createElement("pre", null, (0, _parseText().default)(text)); } } - shouldComponentUpdate(nextProps: Props): boolean { - return !shallowEqual(this.props, nextProps); + shouldComponentUpdate(nextProps) { + return !(0, _shallowequal().default)(this.props, nextProps); } - _renderNestedValueComponent( - displayableRecord: DisplayableRecord, - provider: ?OutputProvider | ?Executor, - ): React.Element { - const {record, expansionStateId} = displayableRecord; + _renderNestedValueComponent(displayableRecord, provider) { + const { + record, + expansionStateId + } = displayableRecord; const getProperties = provider == null ? null : provider.getProperties; const type = record.data == null ? null : record.data.type; const simpleValueComponent = getComponent(type); - return ( - - ); + return React.createElement(_LazyNestedValueComponent().LazyNestedValueComponent, { + className: "console-lazy-nested-value", + evaluationResult: record.data, + fetchChildren: getProperties, + simpleValueComponent: simpleValueComponent, + shouldCacheChildren: true, + expansionStateId: expansionStateId + }); } - render(): React.Node { - const {displayableRecord} = this.props; - const {record} = displayableRecord; - const {level, kind, timestamp, sourceId} = record; - - const classNames = classnames('console-record', `level-${level || 'log'}`, { + render() { + const { + displayableRecord + } = this.props; + const { + record + } = displayableRecord; + const { + level, + kind, + timestamp, + sourceId + } = record; + const classNames = (0, _classnames().default)('console-record', `level-${level || 'log'}`, { request: kind === 'request', - response: kind === 'response', + response: kind === 'response' }); + const iconName = getIconName(record); // flowlint-next-line sketchy-null-string:off - const iconName = getIconName(record); - // flowlint-next-line sketchy-null-string:off - const icon = iconName ? : null; - const sourceLabel = this.props.showSourceLabel ? ( - - {sourceId} - - ) : null; + const icon = iconName ? React.createElement("span", { + className: `icon icon-${iconName}` + }) : null; + const sourceLabel = this.props.showSourceLabel ? React.createElement("span", { + className: `console-record-source-label ${getHighlightClassName(level)}` + }, sourceId) : null; let renderedTimestamp; + if (timestamp != null) { - const timestampLabel = - Date.now() - timestamp > ONE_DAY - ? timestamp.toLocaleString() - : timestamp.toLocaleTimeString(); - renderedTimestamp = ( -
{timestampLabel}
- ); + const timestampLabel = Date.now() - timestamp > ONE_DAY ? timestamp.toLocaleString() : timestamp.toLocaleTimeString(); + renderedTimestamp = React.createElement("div", { + className: "console-record-timestamp" + }, timestampLabel); } - return ( - - {/* $FlowFixMe(>=0.53.0) Flow suppress */} -
- {icon} -
- {displayableRecord.record.repeatCount > 1 && ( -
- {displayableRecord.record.repeatCount} -
- )} -
- {this._renderContent(displayableRecord)} -
-
- {sourceLabel} - {renderedTimestamp} -
-
- ); - } - measureAndNotifyHeight = () => { - // This method is called after the necessary DOM mutations have - // already occurred, however it is possible that the updates have - // not been flushed to the screen. So the height change update - // is deferred until the rendering is complete so that - // this._wrapper.offsetHeight gives us the correct final height - if (this._rafDisposable != null) { - this._rafDisposable.unsubscribe(); - } - this._rafDisposable = nextAnimationFrame.subscribe(() => { - if (this._wrapper == null) { - return; - } - const {offsetHeight} = this._wrapper; - const {displayableRecord, onHeightChange} = this.props; - if (offsetHeight !== displayableRecord.height) { - onHeightChange(displayableRecord.id, offsetHeight); - } - }); - }; + return React.createElement(_MeasuredComponent().MeasuredComponent, { + onMeasurementsChanged: this._debouncedMeasureAndNotifyHeight + }, React.createElement("div", { + ref: this._handleRecordWrapper, + className: classNames + }, icon, React.createElement("div", { + className: "console-record-content-wrapper" + }, displayableRecord.record.repeatCount > 1 && React.createElement("div", { + className: "console-record-duplicate-number" + }, displayableRecord.record.repeatCount), React.createElement("div", { + className: "console-record-content" + }, this._renderContent(displayableRecord))), sourceLabel, renderedTimestamp)); + } - _handleRecordWrapper = (wrapper: HTMLElement) => { - this._wrapper = wrapper; - }; } -function getComponent(type: ?string): React.ComponentType { +exports.default = RecordView; + +function getComponent(type) { switch (type) { case 'text': - return props => TextRenderer(props.evaluationResult); + return props => (0, _TextRenderer().TextRenderer)(props.evaluationResult); + case 'boolean': case 'string': case 'number': case 'object': default: - return SimpleValueComponent; + return _SimpleValueComponent().default; } } -function getHighlightClassName(level: Level): string { +function getHighlightClassName(level) { switch (level) { case 'info': return 'highlight-info'; + case 'success': return 'highlight-success'; + case 'warning': return 'highlight-warning'; + case 'error': return 'highlight-error'; + default: return 'highlight'; } } -function getIconName(record: Record): ?string { +function getIconName(record) { switch (record.kind) { case 'request': return 'chevron-right'; + case 'response': return 'arrow-small-left'; } + switch (record.level) { case 'info': return 'info'; + case 'success': return 'check'; + case 'warning': return 'alert'; + case 'error': return 'stop'; } -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipComponent.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipComponent.js index 2d420476..512999f0 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipComponent.js @@ -1,81 +1,96 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {Datatip} from './types'; - -import * as React from 'react'; - -import {maybeToString} from 'nuclide-commons/string'; -import MarkedStringDatatip from './MarkedStringDatatip'; - -export const DATATIP_ACTIONS = Object.freeze({ - PIN: 'PIN', - CLOSE: 'CLOSE', +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true }); +exports.DatatipComponent = exports.DATATIP_ACTIONS = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _string() { + const data = require("../../../../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _MarkedStringDatatip() { + const data = _interopRequireDefault(require("./MarkedStringDatatip")); + + _MarkedStringDatatip = function () { + return data; + }; + + return data; +} +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +const DATATIP_ACTIONS = Object.freeze({ + PIN: 'PIN', + CLOSE: 'CLOSE' +}); +exports.DATATIP_ACTIONS = DATATIP_ACTIONS; const IconsForAction = { [DATATIP_ACTIONS.PIN]: 'pin', - [DATATIP_ACTIONS.CLOSE]: 'x', + [DATATIP_ACTIONS.CLOSE]: 'x' }; -type DatatipComponentProps = { - action: string, - actionTitle: string, - className?: string, - datatip: Datatip, - onActionClick: Function, -}; +class DatatipComponent extends React.Component { + constructor(...args) { + var _temp; -export class DatatipComponent extends React.Component { - handleActionClick = (event: SyntheticEvent<>) => { - this.props.onActionClick(); - }; + return _temp = super(...args), this.handleActionClick = event => { + this.props.onActionClick(); + }, _temp; + } - render(): React.Node { - const { + render() { + const _this$props = this.props, + { className, action, actionTitle, datatip, - onActionClick, - ...props - } = this.props; + onActionClick + } = _this$props, + props = _objectWithoutProperties(_this$props, ["className", "action", "actionTitle", "datatip", "onActionClick"]); let content; + if (datatip.component != null) { - content = ; + content = React.createElement(datatip.component, null); } else if (datatip.markedStrings != null) { - content = ; + content = React.createElement(_MarkedStringDatatip().default, { + markedStrings: datatip.markedStrings + }); } let actionButton = null; + if (action != null && IconsForAction[action] != null) { const actionIcon = IconsForAction[action]; - actionButton = ( -
- ); + actionButton = React.createElement("div", { + className: `datatip-pin-button icon-${actionIcon}`, + onClick: this.handleActionClick, + title: actionTitle + }); } - return ( -
-
{content}
- {actionButton} -
- ); + return React.createElement("div", Object.assign({ + className: `${(0, _string().maybeToString)(className)} datatip-container` + }, props), React.createElement("div", { + className: "datatip-content" + }, content), actionButton); } + } + +exports.DatatipComponent = DatatipComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipManager.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipManager.js index d79793f4..0bb19629 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipManager.js @@ -1,3 +1,142 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DatatipManager = void 0; + +var _atom = require("atom"); + +function _promise() { + const data = require("../../../../nuclide-commons/promise"); + + _promise = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _debounce() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/debounce")); + + _debounce = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _performanceNow() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/performanceNow")); + + _performanceNow = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _getModifierKeys() { + const data = require("./getModifierKeys"); + + _getModifierKeys = function () { + return data; + }; + + return data; +} + +function _DatatipComponent() { + const data = require("./DatatipComponent"); + + _DatatipComponent = function () { + return data; + }; + + return data; +} + +function _isScrollable() { + const data = _interopRequireDefault(require("./isScrollable")); + + _isScrollable = function () { + return data; + }; + + return data; +} + +function _PinnedDatatip() { + const data = require("./PinnedDatatip"); + + _PinnedDatatip = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,263 +145,163 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* global performance */ - -import type { - AnyDatatipProvider, - Datatip, - DatatipProvider, - ModifierDatatipProvider, - ModifierKey, - PinnedDatatipOptions, -} from './types'; - -import {Range} from 'atom'; -import {asyncFind} from 'nuclide-commons/promise'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import analytics from 'nuclide-commons/analytics'; -import debounce from 'nuclide-commons/debounce'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import idx from 'idx'; -import performanceNow from 'nuclide-commons/performanceNow'; -import {Observable} from 'rxjs'; -import {getLogger} from 'log4js'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import { - getModifierKeysFromMouseEvent, - getModifierKeyFromKeyboardEvent, -} from './getModifierKeys'; - -import {DatatipComponent, DATATIP_ACTIONS} from './DatatipComponent'; -import isScrollable from './isScrollable'; -import {PinnedDatatip} from './PinnedDatatip'; - const DEFAULT_DATATIP_DEBOUNCE_DELAY = 1000; const DEFAULT_DATATIP_INTERACTED_DEBOUNCE_DELAY = 1000; const TRACK_SAMPLE_RATE = 10; -type PinClickHandler = (editor: atom$TextEditor, datatip: Datatip) => void; - -type DatatipResult = { - datatip: Datatip, - provider: AnyDatatipProvider, -}; - -function getProviderName(provider: AnyDatatipProvider): string { +function getProviderName(provider) { if (provider.providerName == null) { - getLogger('datatip').error('Datatip provider has no name', provider); + (0, _log4js().getLogger)('datatip').error('Datatip provider has no name', provider); return 'unknown'; } + return provider.providerName; } -function getBufferPosition( - editor: TextEditor, - editorView: atom$TextEditorElement, - event: ?MouseEvent, -): null | atom$Point { +function getBufferPosition(editor, editorView, event) { if (!event) { return null; } const text = editorView.component; + if (!text) { return null; } const screenPosition = text.screenPositionForMouseEvent(event); const pixelPosition = text.pixelPositionForMouseEvent(event); - const pixelPositionFromScreenPosition = text.pixelPositionForScreenPosition( - screenPosition, - ); - // Distance (in pixels) between screenPosition and the cursor. - const horizontalDistance = - pixelPosition.left - pixelPositionFromScreenPosition.left; - // `screenPositionForMouseEvent.column` cannot exceed the current line length. + const pixelPositionFromScreenPosition = text.pixelPositionForScreenPosition(screenPosition); // Distance (in pixels) between screenPosition and the cursor. + + const horizontalDistance = pixelPosition.left - pixelPositionFromScreenPosition.left; // `screenPositionForMouseEvent.column` cannot exceed the current line length. // This is essentially a heuristic for "mouse cursor is to the left or right // of text content". - if ( - pixelPosition.left <= 0 || - horizontalDistance > editor.getDefaultCharWidth() - ) { + + if (pixelPosition.left <= 0 || horizontalDistance > editor.getDefaultCharWidth()) { return null; } + return editor.bufferPositionForScreenPosition(screenPosition); } -async function getDatatipResults( - providers: ProviderRegistry, - editor: atom$TextEditor, - position: atom$Point, - invoke: TProvider => Promise, -): Promise> { - const filteredDatatipProviders = Array.from( - providers.getAllProvidersForEditor(editor), - ); +async function getDatatipResults(providers, editor, position, invoke) { + const filteredDatatipProviders = Array.from(providers.getAllProvidersForEditor(editor)); + if (filteredDatatipProviders.length === 0) { return []; } - const promises = filteredDatatipProviders.map( - async (provider: TProvider): Promise => { - const name = getProviderName(provider); - try { - return await analytics.trackTimingSampled( - name + '.datatip', - async (): Promise => { - const datatip: ?Datatip = await invoke(provider); - if (!datatip) { - return null; - } - return {datatip, provider}; - }, - TRACK_SAMPLE_RATE, - {path: editor.getPath()}, - ); - } catch (e) { - getLogger('datatip').error( - `Error getting datatip from provider ${name}`, - e, - ); - return null; - } - }, - ); - if (featureConfig.get('atom-ide-datatip.onlyTopDatatip')) { - const result = await asyncFind(promises, x => x); + const promises = filteredDatatipProviders.map(async provider => { + const name = getProviderName(provider); + + try { + return await _analytics().default.trackTimingSampled(name + '.datatip', async () => { + const datatip = await invoke(provider); + + if (!datatip) { + return null; + } + + return { + datatip, + provider + }; + }, TRACK_SAMPLE_RATE, { + path: editor.getPath() + }); + } catch (e) { + (0, _log4js().getLogger)('datatip').error(`Error getting datatip from provider ${name}`, e); + return null; + } + }); + + if (_featureConfig().default.get('atom-ide-datatip.onlyTopDatatip')) { + const result = await (0, _promise().asyncFind)(promises, x => x); return result != null ? [result] : []; } else { return (await Promise.all(promises)).filter(Boolean); } } -type PinnableDatatipProps = { - datatip: Datatip, - editor: atom$TextEditor, - onPinClick: PinClickHandler, -}; - function PinnableDatatip({ datatip, editor, - onPinClick, -}: PinnableDatatipProps): React.Element { + onPinClick +}) { let action; - let actionTitle; - // Datatips are pinnable by default, unless explicitly specified + let actionTitle; // Datatips are pinnable by default, unless explicitly specified // otherwise. + if (datatip.pinnable !== false) { - action = DATATIP_ACTIONS.PIN; + action = _DatatipComponent().DATATIP_ACTIONS.PIN; actionTitle = 'Pin this Datatip'; } - return ( - // $FlowFixMe(>=0.53.0) Flow suppress - onPinClick(editor, datatip)} - /> + return (// $FlowFixMe(>=0.53.0) Flow suppress + React.createElement(_DatatipComponent().DatatipComponent, { + action: action, + actionTitle: actionTitle, + datatip: datatip, + onActionClick: () => onPinClick(editor, datatip) + }) ); } -function mountDatatipWithMarker( - editor: atom$TextEditor, - element: HTMLElement, - range: atom$Range, - renderedProviders: React.Element, - position: atom$Point, -): IDisposable { +function mountDatatipWithMarker(editor, element, range, renderedProviders, position) { // Highlight the text indicated by the datatip's range. const highlightMarker = editor.markBufferRange(range, { - invalidate: 'never', + invalidate: 'never' }); editor.decorateMarker(highlightMarker, { type: 'highlight', - class: 'datatip-highlight-region', - }); + class: 'datatip-highlight-region' + }); // The actual datatip should appear at the trigger position. - // The actual datatip should appear at the trigger position. - const overlayMarker = editor.markBufferRange(new Range(position, position), { - invalidate: 'never', + const overlayMarker = editor.markBufferRange(new _atom.Range(position, position), { + invalidate: 'never' }); + return new (_UniversalDisposable().default)(() => highlightMarker.destroy(), () => overlayMarker.destroy(), // The editor may not mount the marker until the next update. + // It's not safe to render anything until that point, as datatips + // often need to measure their size in the DOM. + _RxMin.Observable.from(editor.getElement().getNextUpdatePromise()).subscribe(() => { + editor.decorateMarker(overlayMarker, { + type: 'overlay', + position: 'tail', + item: element + }); + element.style.display = 'block'; - return new UniversalDisposable( - () => highlightMarker.destroy(), - () => overlayMarker.destroy(), - // The editor may not mount the marker until the next update. - // It's not safe to render anything until that point, as datatips - // often need to measure their size in the DOM. - Observable.from(editor.getElement().getNextUpdatePromise()).subscribe( - () => { - editor.decorateMarker(overlayMarker, { - type: 'overlay', - position: 'tail', - item: element, - }); - element.style.display = 'block'; - ReactDOM.render(renderedProviders, element); - }, - ), - ); + _reactDom.default.render(renderedProviders, element); + })); } const DatatipState = Object.freeze({ HIDDEN: 'HIDDEN', FETCHING: 'FETCHING', - VISIBLE: 'VISIBLE', + VISIBLE: 'VISIBLE' }); -type State = $Keys; -function ensurePositiveNumber(value: any, defaultValue: number): number { +function ensurePositiveNumber(value, defaultValue) { if (typeof value !== 'number' || value < 0) { return defaultValue; } + return value; } class DatatipManagerForEditor { - _blacklistedPosition: ?atom$Point; - _datatipElement: HTMLElement; - _datatipProviders: ProviderRegistry; - _modifierDatatipProviders: ProviderRegistry; - _datatipState: State; - _editor: atom$TextEditor; - _editorView: atom$TextEditorElement; - _insideDatatip: boolean; - _lastHiddenTime: number; - _lastFetchedFromCursorPosition: boolean; - _lastMoveEvent: ?MouseEvent; - _lastPosition: ?atom$Point; - _lastResultsPromise: ?Promise>; - _heldKeys: Set; - _markerDisposable: ?IDisposable; - _pinnedDatatips: Set; - _range: ?atom$Range; - _shouldDropNextMouseMoveAfterFocus: boolean; - _startFetchingDebounce: () => void; - _hideIfOutsideDebounce: () => void; - _subscriptions: UniversalDisposable; - _interactedWith: boolean; - _checkedScrollable: boolean; - _isScrollable: boolean; - - constructor( - editor: atom$TextEditor, - datatipProviders: ProviderRegistry, - modifierDatatipProviders: ProviderRegistry, - ) { + constructor(editor, datatipProviders, modifierDatatipProviders) { + _initialiseProps.call(this); + this._editor = editor; this._editorView = atom.views.getView(editor); this._pinnedDatatips = new Set(); - this._subscriptions = new UniversalDisposable(); + this._subscriptions = new (_UniversalDisposable().default)(); this._datatipProviders = datatipProviders; this._modifierDatatipProviders = modifierDatatipProviders; this._datatipElement = document.createElement('div'); @@ -276,230 +315,198 @@ class DatatipManagerForEditor { this._lastFetchedFromCursorPosition = false; this._shouldDropNextMouseMoveAfterFocus = false; - this._subscriptions.add( - featureConfig.observe('atom-ide-datatip.datatipDebounceDelay', () => - this._setStartFetchingDebounce(), - ), - featureConfig.observe( - 'atom-ide-datatip.datatipInteractedWithDebounceDelay', - () => this._setHideIfOutsideDebounce(), - ), - Observable.fromEvent(this._editorView, 'focus').subscribe(e => { - this._shouldDropNextMouseMoveAfterFocus = true; - if (!this._insideDatatip) { - this._setState(DatatipState.HIDDEN); - } - }), - Observable.fromEvent(this._editorView, 'blur').subscribe(e => { - if (!this._insideDatatip) { - this._setState(DatatipState.HIDDEN); - } - }), - Observable.fromEvent(this._editorView, 'mousemove').subscribe(e => { - this._lastFetchedFromCursorPosition = false; - if (this._shouldDropNextMouseMoveAfterFocus) { - this._shouldDropNextMouseMoveAfterFocus = false; - return; - } + this._subscriptions.add(_featureConfig().default.observe('atom-ide-datatip.datatipDebounceDelay', () => this._setStartFetchingDebounce()), _featureConfig().default.observe('atom-ide-datatip.datatipInteractedWithDebounceDelay', () => this._setHideIfOutsideDebounce()), _RxMin.Observable.fromEvent(this._editorView, 'focus').subscribe(e => { + this._shouldDropNextMouseMoveAfterFocus = true; - this._lastMoveEvent = e; - this._heldKeys = getModifierKeysFromMouseEvent(e); - if (this._datatipState === DatatipState.HIDDEN) { - this._startFetchingDebounce(); - } else { - this._hideIfOutside(); - } - }), - Observable.fromEvent(this._editorView, 'mouseleave').subscribe(() => { - this._lastMoveEvent = null; + if (!this._insideDatatip) { + this._setState(DatatipState.HIDDEN); + } + }), _RxMin.Observable.fromEvent(this._editorView, 'blur').subscribe(e => { + if (!this._insideDatatip) { + this._setState(DatatipState.HIDDEN); + } + }), _RxMin.Observable.fromEvent(this._editorView, 'mousemove').subscribe(e => { + this._lastFetchedFromCursorPosition = false; + + if (this._shouldDropNextMouseMoveAfterFocus) { + this._shouldDropNextMouseMoveAfterFocus = false; + return; + } + + this._lastMoveEvent = e; + this._heldKeys = (0, _getModifierKeys().getModifierKeysFromMouseEvent)(e); + + if (this._datatipState === DatatipState.HIDDEN) { + this._startFetchingDebounce(); + } else { this._hideIfOutside(); - }), - Observable.fromEvent(this._editorView, 'mousedown').subscribe(e => { - let node = e.target; - while (node != null) { - if (node === this._datatipElement) { - return; - } - node = node.parentNode; - } + } + }), _RxMin.Observable.fromEvent(this._editorView, 'mouseleave').subscribe(() => { + this._lastMoveEvent = null; - this._hideOrCancel(); - }), - Observable.fromEvent(this._editorView, 'keydown').subscribe(e => { - const modifierKey = getModifierKeyFromKeyboardEvent(e); - if (modifierKey) { - // On Windows, key repeat applies to modifier keys too! - // So it's quite possible that we hit this twice without hitting keyup. - if (this._heldKeys.has(modifierKey)) { - return; - } - this._heldKeys.add(modifierKey); - if (this._datatipState !== DatatipState.HIDDEN) { - this._fetchInResponseToKeyPress(); - } - } else { - this._hideOrCancel(); + this._hideIfOutside(); + }), _RxMin.Observable.fromEvent(this._editorView, 'mousedown').subscribe(e => { + let node = e.target; + + while (node != null) { + if (node === this._datatipElement) { + return; } - }), - Observable.fromEvent(this._editorView, 'keyup').subscribe(e => { - const modifierKey = getModifierKeyFromKeyboardEvent(e); - if (modifierKey) { - this._heldKeys.delete(modifierKey); - if (this._datatipState !== DatatipState.HIDDEN) { - this._fetchInResponseToKeyPress(); - } + + node = node.parentNode; + } + + this._hideOrCancel(); + }), _RxMin.Observable.fromEvent(this._editorView, 'keydown').subscribe(e => { + const modifierKey = (0, _getModifierKeys().getModifierKeyFromKeyboardEvent)(e); + + if (modifierKey) { + // On Windows, key repeat applies to modifier keys too! + // So it's quite possible that we hit this twice without hitting keyup. + if (this._heldKeys.has(modifierKey)) { + return; } - }), - Observable.fromEvent(this._datatipElement, 'wheel').subscribe(e => { - // We'll mark this as an 'interaction' only if the scroll target was scrollable. - // This requires going over the ancestors, so only check this once. - // If it comes back as false, we won't bother checking again. - if (!this._checkedScrollable) { - this._isScrollable = isScrollable(this._datatipElement, e); - this._checkedScrollable = true; + + this._heldKeys.add(modifierKey); + + if (this._datatipState !== DatatipState.HIDDEN) { + this._fetchInResponseToKeyPress(); } - if (this._isScrollable) { - this._interactedWith = true; - e.stopPropagation(); + } else { + this._hideOrCancel(); + } + }), _RxMin.Observable.fromEvent(this._editorView, 'keyup').subscribe(e => { + const modifierKey = (0, _getModifierKeys().getModifierKeyFromKeyboardEvent)(e); + + if (modifierKey) { + this._heldKeys.delete(modifierKey); + + if (this._datatipState !== DatatipState.HIDDEN) { + this._fetchInResponseToKeyPress(); } - }), - Observable.fromEvent(this._datatipElement, 'mousedown').subscribe(() => { + } + }), _RxMin.Observable.fromEvent(this._datatipElement, 'wheel').subscribe(e => { + // We'll mark this as an 'interaction' only if the scroll target was scrollable. + // This requires going over the ancestors, so only check this once. + // If it comes back as false, we won't bother checking again. + if (!this._checkedScrollable) { + this._isScrollable = (0, _isScrollable().default)(this._datatipElement, e); + this._checkedScrollable = true; + } + + if (this._isScrollable) { this._interactedWith = true; - }), - Observable.fromEvent(this._datatipElement, 'mouseenter').subscribe(() => { - this._insideDatatip = true; - this._hideIfOutside(); - }), - Observable.fromEvent(this._datatipElement, 'mouseleave').subscribe(() => { - this._insideDatatip = false; - this._hideIfOutside(); - }), - this._editorView.onDidChangeScrollTop(() => { - this._lastMoveEvent = null; - if (this._datatipState === DatatipState.VISIBLE) { - this._setState(DatatipState.HIDDEN); - } - }), - this._editor.getBuffer().onDidChangeText(() => { - if (this._datatipState === DatatipState.VISIBLE) { - this._setState(DatatipState.HIDDEN); - } - }), - atom.commands.add( - 'atom-text-editor', - 'datatip:toggle', - this._toggleDatatip, - ), - atom.commands.add( - 'atom-text-editor', - 'datatip:copy-to-clipboard', - this._copyDatatipToClipboard, - ), - ); + e.stopPropagation(); + } + }), _RxMin.Observable.fromEvent(this._datatipElement, 'mousedown').subscribe(() => { + this._interactedWith = true; + }), _RxMin.Observable.fromEvent(this._datatipElement, 'mouseenter').subscribe(() => { + this._insideDatatip = true; + + this._hideIfOutside(); + }), _RxMin.Observable.fromEvent(this._datatipElement, 'mouseleave').subscribe(() => { + this._insideDatatip = false; + + this._hideIfOutside(); + }), this._editorView.onDidChangeScrollTop(() => { + this._lastMoveEvent = null; + + if (this._datatipState === DatatipState.VISIBLE) { + this._setState(DatatipState.HIDDEN); + } + }), this._editor.getBuffer().onDidChangeText(() => { + if (this._datatipState === DatatipState.VISIBLE) { + this._setState(DatatipState.HIDDEN); + } + }), atom.commands.add('atom-text-editor', 'datatip:toggle', this._toggleDatatip), atom.commands.add('atom-text-editor', 'datatip:copy-to-clipboard', this._copyDatatipToClipboard)); } _fetchInResponseToKeyPress() { if (this._lastFetchedFromCursorPosition) { this._startFetching(() => this._editor.getCursorBufferPosition()); } else { - this._startFetching(() => - getBufferPosition(this._editor, this._editorView, this._lastMoveEvent), - ); + this._startFetching(() => getBufferPosition(this._editor, this._editorView, this._lastMoveEvent)); } } - _setStartFetchingDebounce(): void { - this._startFetchingDebounce = debounce( - () => { - this._startFetching(() => - getBufferPosition( - this._editor, - this._editorView, - this._lastMoveEvent, - ), - ); - }, - ensurePositiveNumber( - (featureConfig.get('atom-ide-datatip.datatipDebounceDelay'): any), - DEFAULT_DATATIP_DEBOUNCE_DELAY, - ), - /* immediate */ false, - ); + _setStartFetchingDebounce() { + this._startFetchingDebounce = (0, _debounce().default)(() => { + this._startFetching(() => getBufferPosition(this._editor, this._editorView, this._lastMoveEvent)); + }, ensurePositiveNumber(_featureConfig().default.get('atom-ide-datatip.datatipDebounceDelay'), DEFAULT_DATATIP_DEBOUNCE_DELAY), + /* immediate */ + false); } - _setHideIfOutsideDebounce(): void { - this._hideIfOutsideDebounce = debounce( - () => { - this._hideIfOutsideImmediate(); - }, - ensurePositiveNumber( - (featureConfig.get( - 'atom-ide-datatip.datatipInteractedWithDebounceDelay', - ): any), - DEFAULT_DATATIP_INTERACTED_DEBOUNCE_DELAY, - ), - /* immediate */ false, - ); + _setHideIfOutsideDebounce() { + this._hideIfOutsideDebounce = (0, _debounce().default)(() => { + this._hideIfOutsideImmediate(); + }, ensurePositiveNumber(_featureConfig().default.get('atom-ide-datatip.datatipInteractedWithDebounceDelay'), DEFAULT_DATATIP_INTERACTED_DEBOUNCE_DELAY), + /* immediate */ + false); } - dispose(): void { + dispose() { this._setState(DatatipState.HIDDEN); + this._subscriptions.dispose(); + this._datatipElement.remove(); } - _setState(newState: State): void { + _setState(newState) { const oldState = this._datatipState; this._datatipState = newState; if (newState === DatatipState.HIDDEN) { this._blacklistedPosition = null; + if (oldState !== DatatipState.HIDDEN) { this._hideDatatip(); } } } - async _startFetching(getPosition: () => ?atom$Point): Promise { + async _startFetching(getPosition) { const position = getPosition(); + if (!position) { return; } const data = await this._fetchAndRender(position); + if (data == null) { this._setState(DatatipState.HIDDEN); + return; } + if (this._datatipState !== DatatipState.FETCHING) { this._setState(DatatipState.HIDDEN); } - if ( - this._blacklistedPosition && - data.range && - data.range.containsPoint(this._blacklistedPosition) - ) { + if (this._blacklistedPosition && data.range && data.range.containsPoint(this._blacklistedPosition)) { this._setState(DatatipState.HIDDEN); + return; } const currentPosition = getPosition(); - if ( - !currentPosition || - !data.range || - !data.range.containsPoint(currentPosition) - ) { + + if (!currentPosition || !data.range || !data.range.containsPoint(currentPosition)) { this._setState(DatatipState.HIDDEN); + return; } if (this._isHoveringOverPinnedTip()) { this._setState(DatatipState.HIDDEN); + return; } this._setState(DatatipState.VISIBLE); + this._interactedWith = false; this._checkedScrollable = false; this._range = data.range; @@ -507,123 +514,94 @@ class DatatipManagerForEditor { if (this._markerDisposable) { this._markerDisposable.dispose(); } - this._markerDisposable = mountDatatipWithMarker( - this._editor, - this._datatipElement, - data.range, - data.renderedProviders, - currentPosition, - ); + + this._markerDisposable = mountDatatipWithMarker(this._editor, this._datatipElement, data.range, data.renderedProviders, currentPosition); } - async _fetch(position: atom$Point): Promise> { + async _fetch(position) { this._setState(DatatipState.FETCHING); - let results: Promise>; - if ( - this._lastPosition != null && - position.isEqual(this._lastPosition) && - this._lastResultsPromise != null - ) { + let results; + + if (this._lastPosition != null && position.isEqual(this._lastPosition) && this._lastResultsPromise != null) { results = this._lastResultsPromise; } else { - this._lastResultsPromise = getDatatipResults( - this._datatipProviders, - this._editor, - position, - provider => provider.datatip(this._editor, position), - ); + this._lastResultsPromise = getDatatipResults(this._datatipProviders, this._editor, position, provider => provider.datatip(this._editor, position)); results = this._lastResultsPromise; this._lastPosition = position; } - return (await results).concat( - await getDatatipResults( - this._modifierDatatipProviders, - this._editor, - position, - provider => - provider.modifierDatatip(this._editor, position, this._heldKeys), - ), - ); + return (await results).concat((await getDatatipResults(this._modifierDatatipProviders, this._editor, position, provider => provider.modifierDatatip(this._editor, position, this._heldKeys)))); } - async _fetchAndRender( - position: atom$Point, - ): Promise, - }> { + async _fetchAndRender(position) { const datatipsAndProviders = await this._fetch(position); + if (datatipsAndProviders.length === 0) { return null; } const range = datatipsAndProviders[0].datatip.range; - analytics.track('datatip-popup', { + + _analytics().default.track('datatip-popup', { scope: this._editor.getGrammar().scopeName, providerName: getProviderName(datatipsAndProviders[0].provider), rangeStartRow: String(range.start.row), rangeStartColumn: String(range.start.column), rangeEndRow: String(range.end.row), - rangeEndColumn: String(range.end.column), + rangeEndColumn: String(range.end.column) }); - const renderedProviders = ( -
- {datatipsAndProviders.map(({datatip, provider}) => ( - - ))} -
- ); - + const renderedProviders = React.createElement("div", null, datatipsAndProviders.map(({ + datatip, + provider + }) => React.createElement(PinnableDatatip, { + datatip: datatip, + editor: this._editor, + key: getProviderName(provider), + onPinClick: this._handlePinClicked + }))); return { range, - renderedProviders, + renderedProviders }; } - _isHoveringOverPinnedTip(): boolean { + _isHoveringOverPinnedTip() { const pinnedDataTips = Array.from(this._pinnedDatatips.values()); const hoveringTips = pinnedDataTips.filter(dt => dt.isHovering()); return hoveringTips != null && hoveringTips.length > 0; } - _hideDatatip(): void { + _hideDatatip() { this._lastHiddenTime = performance.now(); + if (this._markerDisposable) { this._markerDisposable.dispose(); + this._markerDisposable = null; } + this._range = null; - ReactDOM.unmountComponentAtNode(this._datatipElement); + + _reactDom.default.unmountComponentAtNode(this._datatipElement); + this._datatipElement.style.display = 'none'; } - _hideOrCancel(): void { - if ( - this._datatipState === DatatipState.HIDDEN || - this._datatipState === DatatipState.FETCHING - ) { + _hideOrCancel() { + if (this._datatipState === DatatipState.HIDDEN || this._datatipState === DatatipState.FETCHING) { if (this._blacklistedPosition == null) { - this._blacklistedPosition = getBufferPosition( - this._editor, - this._editorView, - this._lastMoveEvent, - ); + this._blacklistedPosition = getBufferPosition(this._editor, this._editorView, this._lastMoveEvent); } + return; } this._setState(DatatipState.HIDDEN); } - _hideIfOutside(): void { + _hideIfOutside() { if (this._datatipState !== DatatipState.VISIBLE) { return; } @@ -635,189 +613,175 @@ class DatatipManagerForEditor { } } - _hideIfOutsideImmediate(): void { + _hideIfOutsideImmediate() { if (this._datatipState !== DatatipState.VISIBLE) { return; } + if (this._insideDatatip) { return; } if (this._isHoveringOverPinnedTip()) { this._setState(DatatipState.HIDDEN); + return; } - const currentPosition = getBufferPosition( - this._editor, - this._editorView, - this._lastMoveEvent, - ); - if ( - currentPosition && - this._range && - this._range.containsPoint(currentPosition) - ) { + const currentPosition = getBufferPosition(this._editor, this._editorView, this._lastMoveEvent); + + if (currentPosition && this._range && this._range.containsPoint(currentPosition)) { return; } this._setState(DatatipState.HIDDEN); } - createPinnedDataTip( - datatip: Datatip, - editor: TextEditor, - options?: PinnedDatatipOptions, - ): PinnedDatatip { - const pinnedDatatip = new PinnedDatatip(datatip, editor, { - ...options, + createPinnedDataTip(datatip, editor, options) { + const pinnedDatatip = new (_PinnedDatatip().PinnedDatatip)(datatip, editor, Object.assign({}, options, { onDispose: () => { this._pinnedDatatips.delete(pinnedDatatip); }, hideDataTips: () => { this._hideDatatip(); - }, - }); + } + })); return pinnedDatatip; } - _handlePinClicked = (editor: TextEditor, datatip: Datatip) => { - analytics.track('datatip-pinned-open'); - const startTime = performanceNow(); +} + +var _initialiseProps = function () { + this._handlePinClicked = (editor, datatip) => { + _analytics().default.track('datatip-pinned-open'); + + const startTime = (0, _performanceNow().default)(); + this._setState(DatatipState.HIDDEN); - this._pinnedDatatips.add( - new PinnedDatatip(datatip, editor, { - onDispose: pinnedDatatip => { - this._pinnedDatatips.delete(pinnedDatatip); - analytics.track('datatip-pinned-close', { - duration: performanceNow() - startTime, - }); - }, - hideDataTips: () => { - this._hideDatatip(); - }, - position: 'end-of-line', - }), - ); + + this._pinnedDatatips.add(new (_PinnedDatatip().PinnedDatatip)(datatip, editor, { + onDispose: pinnedDatatip => { + this._pinnedDatatips.delete(pinnedDatatip); + + _analytics().default.track('datatip-pinned-close', { + duration: (0, _performanceNow().default)() - startTime + }); + }, + hideDataTips: () => { + this._hideDatatip(); + }, + position: 'end-of-line' + })); }; - _toggleDatatip = (e?: atom$CustomEvent) => { + this._toggleDatatip = e => { + var _ref; + if (atom.workspace.getActiveTextEditor() !== this._editor) { return; - } - - // Note that we don't need to hide the tooltip, we already hide it on + } // Note that we don't need to hide the tooltip, we already hide it on // keydown, which is going to be triggered before the key binding which is // evaluated on keyup. // $FlowFixMe (v0.54.1 <) - const maybeEventType = idx(e, _ => _.originalEvent.type); - // Unfortunately, when you do keydown of the shortcut, it's going to + + const maybeEventType = (_ref = e) != null ? (_ref = _ref.originalEvent) != null ? _ref.type : _ref : _ref; // Unfortunately, when you do keydown of the shortcut, it's going to // hide it, we need to make sure that when we do keyup, it doesn't show // it up right away. We assume that a keypress is done within 100ms // and don't show it again if it was hidden so soon. - const forceShow = - maybeEventType === 'keydown' && - performance.now() - this._lastHiddenTime > 100; + + const forceShow = maybeEventType === 'keydown' && performance.now() - this._lastHiddenTime > 100; const forceHide = maybeEventType === 'keyup'; - const forceToggle = - maybeEventType !== 'keydown' && maybeEventType !== 'keyup'; - - if ( - // if we have event information, prefer that for determining show/hide - forceShow || - (forceToggle && this._datatipState === DatatipState.HIDDEN) - ) { + const forceToggle = maybeEventType !== 'keydown' && maybeEventType !== 'keyup'; + + if ( // if we have event information, prefer that for determining show/hide + forceShow || forceToggle && this._datatipState === DatatipState.HIDDEN) { this._lastFetchedFromCursorPosition = true; + this._startFetching(() => this._editor.getCursorScreenPosition()); } else if (forceHide || forceToggle) { this._hideOrCancel(); } }; - _copyDatatipToClipboard = async () => { + this._copyDatatipToClipboard = async () => { + var _ref2; + if (atom.workspace.getActiveTextEditor() !== this._editor) { return; } const pos = this._editor.getCursorScreenPosition(); + if (pos == null) { return; } - const results: Array = await this._fetch(pos); + + const results = await this._fetch(pos); + this._setState(DatatipState.HIDDEN); - const tip = idx(results, _ => _[0].datatip); + const tip = (_ref2 = results) != null ? (_ref2 = _ref2[0]) != null ? _ref2.datatip : _ref2 : _ref2; + if (tip == null || tip.markedStrings == null) { return; } const markedStrings = tip.markedStrings; + if (markedStrings == null) { return; } const value = markedStrings.map(string => string.value).join(); + if (value === '') { return; } atom.clipboard.write(value); - atom.notifications.addInfo( - `Copied data tip to clipboard: \`\`\`${value}\`\`\``, - ); + atom.notifications.addInfo(`Copied data tip to clipboard: \`\`\`${value}\`\`\``); }; -} - -export class DatatipManager { - _datatipProviders: ProviderRegistry; - _modifierDatatipProviders: ProviderRegistry; - _editorManagers: WeakMap; - _subscriptions: UniversalDisposable; +}; +class DatatipManager { constructor() { - this._subscriptions = new UniversalDisposable(); + this._subscriptions = new (_UniversalDisposable().default)(); this._editorManagers = new WeakMap(); - this._datatipProviders = new ProviderRegistry(); - this._modifierDatatipProviders = new ProviderRegistry(); - - this._subscriptions.add( - atom.workspace.observeTextEditors(editor => { - const manager = new DatatipManagerForEditor( - editor, - this._datatipProviders, - this._modifierDatatipProviders, - ); - this._editorManagers.set(editor, manager); - this._subscriptions.addUntilDestroyed(editor, manager); - }), - ); + this._datatipProviders = new (_ProviderRegistry().default)(); + this._modifierDatatipProviders = new (_ProviderRegistry().default)(); + + this._subscriptions.add(atom.workspace.observeTextEditors(editor => { + const manager = new DatatipManagerForEditor(editor, this._datatipProviders, this._modifierDatatipProviders); + + this._editorManagers.set(editor, manager); + + this._subscriptions.addUntilDestroyed(editor, manager); + })); } - addProvider(provider: DatatipProvider): IDisposable { + addProvider(provider) { return this._datatipProviders.addProvider(provider); } - addModifierProvider(provider: ModifierDatatipProvider): IDisposable { + addModifierProvider(provider) { return this._modifierDatatipProviders.addProvider(provider); } - createPinnedDataTip( - datatip: Datatip, - editor: TextEditor, - options?: PinnedDatatipOptions, - ): PinnedDatatip { + createPinnedDataTip(datatip, editor, options) { const manager = this._editorManagers.get(editor); + if (!manager) { - throw new Error( - 'Trying to create a pinned data tip on an editor that has ' + - 'no datatip manager', - ); + throw new Error('Trying to create a pinned data tip on an editor that has ' + 'no datatip manager'); } + return manager.createPinnedDataTip(datatip, editor, options); } - dispose(): void { + dispose() { this._subscriptions.dispose(); } + } + +exports.DatatipManager = DatatipManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringDatatip.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringDatatip.js index 7336a48a..da8e22e7 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringDatatip.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringDatatip.js @@ -1,3 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _marked() { + const data = _interopRequireDefault(require("marked")); + + _marked = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _MarkedStringSnippet() { + const data = _interopRequireDefault(require("./MarkedStringSnippet")); + + _MarkedStringSnippet = function () { + return data; + }; + + return data; +} + +function _dompurify() { + const data = _interopRequireDefault(require("dompurify")); + + _dompurify = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,46 +49,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const domPurify = (0, _dompurify().default)(); -import type {MarkedString} from './types'; - -import marked from 'marked'; -import * as React from 'react'; - -import MarkedStringSnippet from './MarkedStringSnippet'; -import createDOMPurify from 'dompurify'; - -const domPurify = createDOMPurify(); - -type Props = { - markedStrings: Array, -}; - -export default class MarkedStringDatatip extends React.PureComponent { - render(): React.Node { +class MarkedStringDatatip extends React.PureComponent { + render() { const elements = this.props.markedStrings.map((chunk, i) => { if (chunk.type === 'markdown') { - return ( -
- ); + return React.createElement("div", { + className: "datatip-marked-container", + dangerouslySetInnerHTML: { + __html: domPurify.sanitize((0, _marked().default)(chunk.value, { + breaks: true + })) + }, + key: i + }); } else { - return ; + return React.createElement(_MarkedStringSnippet().default, Object.assign({ + key: i + }, chunk)); } }); - - return
{elements}
; + return React.createElement("div", { + className: "datatip-marked" + }, elements); } + } + +exports.default = MarkedStringDatatip; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringSnippet.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringSnippet.js index cd89f42e..372521da 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringSnippet.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringSnippet.js @@ -1,3 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _atom = require("atom"); + +var React = _interopRequireWildcard(require("react")); + +function _AtomTextEditor() { + const data = require("../../../../nuclide-commons-ui/AtomTextEditor"); + + _AtomTextEditor = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,57 +29,50 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {TextBuffer} from 'atom'; -import * as React from 'react'; -import {AtomTextEditor} from 'nuclide-commons-ui/AtomTextEditor'; - // Complex types can end up being super long. Truncate them. const MAX_LENGTH = 100; -type Props = { - value: string, - grammar: atom$Grammar, -}; - -type State = { - isExpanded: boolean, -}; +class MarkedStringSnippet extends React.Component { + constructor(...args) { + var _temp; -export default class MarkedStringSnippet extends React.Component { - state = { - isExpanded: false, - }; + return _temp = super(...args), this.state = { + isExpanded: false + }, _temp; + } - render(): React.Node { - const {grammar, value} = this.props; + render() { + const { + grammar, + value + } = this.props; const shouldTruncate = value.length > MAX_LENGTH && !this.state.isExpanded; - const buffer = new TextBuffer( - shouldTruncate ? value.substr(0, MAX_LENGTH) + '...' : value, - ); - return ( -
) => { - // TODO: (wbinnssmith) T30771435 this setState depends on current state - // and should use an updater function rather than an object - // eslint-disable-next-line react/no-access-state-in-setstate - this.setState({isExpanded: !this.state.isExpanded}); - e.stopPropagation(); - }}> - -
- ); + const buffer = new _atom.TextBuffer(shouldTruncate ? value.substr(0, MAX_LENGTH) + '...' : value); + return React.createElement("div", { + className: "datatip-marked-text-editor-container", + onClick: e => { + // TODO: (wbinnssmith) T30771435 this setState depends on current state + // and should use an updater function rather than an object + // eslint-disable-next-line react/no-access-state-in-setstate + this.setState({ + isExpanded: !this.state.isExpanded + }); + e.stopPropagation(); + } + }, React.createElement(_AtomTextEditor().AtomTextEditor, { + className: "datatip-marked-text-editor", + gutterHidden: true, + readOnly: true, + syncTextContents: false, + autoGrow: true, + grammar: grammar, + textBuffer: buffer + })); } + } + +exports.default = MarkedStringSnippet; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/PinnedDatatip.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/PinnedDatatip.js index f01828d6..e5c32de9 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/PinnedDatatip.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/PinnedDatatip.js @@ -1,3 +1,60 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.PinnedDatatip = void 0; + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _DatatipComponent() { + const data = require("./DatatipComponent"); + + _DatatipComponent = function () { + return data; + }; + + return data; +} + +function _isScrollable() { + const data = _interopRequireDefault(require("./isScrollable")); + + _isScrollable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,89 +63,37 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -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 UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -import {DatatipComponent, DATATIP_ACTIONS} from './DatatipComponent'; -import isScrollable from './isScrollable'; - const LINE_END_MARGIN = 20; let _mouseMove$; -function documentMouseMove$(): Observable { + +function documentMouseMove$() { if (_mouseMove$ == null) { - _mouseMove$ = Observable.fromEvent(document, 'mousemove'); + _mouseMove$ = _RxMin.Observable.fromEvent(document, 'mousemove'); } + return _mouseMove$; } let _mouseUp$; -function documentMouseUp$(): Observable { + +function documentMouseUp$() { if (_mouseUp$ == null) { - _mouseUp$ = Observable.fromEvent(document, 'mouseup'); + _mouseUp$ = _RxMin.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: UniversalDisposable; - _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 UniversalDisposable(); - this._subscriptions.add( - new UniversalDisposable(() => params.onDispose(this)), - ); +class PinnedDatatip { + constructor(datatip, editor, params) { + this._subscriptions = new (_UniversalDisposable().default)(); + + this._subscriptions.add(new (_UniversalDisposable().default)(() => params.onDispose(this))); + this._datatip = datatip; this._editor = editor; this._marker = null; @@ -103,223 +108,237 @@ export class PinnedDatatip { 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 UniversalDisposable(() => { - this._hostElement.removeEventListener( - 'mouseenter', - this._boundHandleMouseEnter, - ); - this._hostElement.removeEventListener( - 'mouseleave', - this._boundHandleMouseLeave, - ); - }), - ); + this._subscriptions.add(_RxMin.Observable.fromEvent(this._hostElement, 'wheel').subscribe(e => { + if (!this._checkedScrollable) { + this._isScrollable = (0, _isScrollable().default)(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 (_UniversalDisposable().default)(() => { + this._hostElement.removeEventListener('mouseenter', this._boundHandleMouseEnter); + + this._hostElement.removeEventListener('mouseleave', this._boundHandleMouseLeave); + })); + this._mouseUpTimeout = null; - this._offset = {x: 0, y: 0}; + 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._showRangeHighlight = params.showRangeHighlight == null ? true : params.showRangeHighlight; this.render(); } - handleMouseEnter(event: MouseEvent): void { + handleMouseEnter(event) { this._isHovering = true; + this._hideDataTips(); } - handleMouseLeave(event: MouseEvent): void { + handleMouseLeave(event) { this._isHovering = false; } - isHovering(): boolean { + isHovering() { return this._isHovering; } - handleGlobalMouseMove(event: Event): void { - const evt: MouseEvent = (event: any); - const {_dragOrigin} = this; - invariant(_dragOrigin); + handleGlobalMouseMove(event) { + const evt = event; + const { + _dragOrigin + } = this; + + if (!_dragOrigin) { + throw new Error("Invariant violation: \"_dragOrigin\""); + } + this._isDragging = true; this._offset = { x: evt.clientX - _dragOrigin.x, - y: evt.clientY - _dragOrigin.y, + y: evt.clientY - _dragOrigin.y }; this.render(); } - handleGlobalMouseUp(): void { + handleGlobalMouseUp() { // 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 { + _ensureMouseSubscriptionDisposed() { if (this._mouseSubscription != null) { this._mouseSubscription.unsubscribe(); + this._mouseSubscription = null; } } - handleMouseDown(event: Event): void { - const evt: MouseEvent = (event: any); + handleMouseDown(event) { + const evt = event; this._dragOrigin = { x: evt.clientX - this._offset.x, - y: evt.clientY - this._offset.y, + y: evt.clientY - this._offset.y }; + this._ensureMouseSubscriptionDisposed(); - this._mouseSubscription = documentMouseMove$() - .takeUntil(documentMouseUp$()) - .subscribe( - (e: MouseEvent) => { - this.handleGlobalMouseMove(e); - }, - (error: any) => {}, - () => { - this.handleGlobalMouseUp(); - }, - ); + + this._mouseSubscription = documentMouseMove$().takeUntil(documentMouseUp$()).subscribe(e => { + this.handleGlobalMouseMove(e); + }, error => {}, () => { + this.handleGlobalMouseUp(); + }); } - handleCapturedClick(event: SyntheticEvent<>): void { + handleCapturedClick(event) { 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. - // Update the position of the pinned datatip. - _updateHostElementPosition(): void { - const {_editor, _datatip, _hostElement, _offset, _position} = this; - const {range} = _datatip; + + _updateHostElementPosition() { + 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'; + + 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.bottom = _editor.getLineHeightInPixels() + _hostElement.clientHeight - _offset.y + 'px'; _hostElement.style.left = _offset.x + 'px'; break; + default: - (_position: empty); + _position; throw new Error(`Unexpected PinnedDatatip position: ${this._position}`); } } - async render(): Promise { - const {_editor, _datatip, _hostElement, _isDragging, _isHovering} = this; - + async render() { + 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', + const marker = _editor.markBufferRange(_datatip.range, { + invalidate: 'never' }); + this._marker = marker; + _editor.decorateMarker(marker, { type: 'overlay', position: 'head', class: 'overlay-no-events', item: this._hostElement, // above-range datatips currently assume that the overlay is below. - avoidOverflow: this._position !== 'above-range', + avoidOverflow: this._position !== 'above-range' }); + if (this._showRangeHighlight) { this._rangeDecoration = _editor.decorateMarker(marker, { type: 'highlight', - class: rangeClassname, + class: rangeClassname }); } - await _editor.getElement().getNextUpdatePromise(); - // Guard against disposals during the await. + + 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, + class: rangeClassname }); } - ReactDOM.render( - , - _hostElement, - ); + _reactDom.default.render(React.createElement(_DatatipComponent().DatatipComponent, { + action: _DatatipComponent().DATATIP_ACTIONS.CLOSE, + actionTitle: "Close this datatip", + className: (0, _classnames().default)(_isDragging ? 'datatip-dragging' : '', 'datatip-pinned'), + datatip: _datatip, + onActionClick: this._boundDispose, + onMouseDown: this._boundHandleMouseDown, + onClickCapture: this._boundHandleCapturedClick + }), _hostElement); + this._updateHostElementPosition(); } - dispose(): void { + dispose() { 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); + + _reactDom.default.unmountComponentAtNode(this._hostElement); + this._hostElement.remove(); + this._subscriptions.dispose(); } + } + +exports.PinnedDatatip = PinnedDatatip; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/getModifierKeys.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/getModifierKeys.js index 09a76ffe..f812ecdf 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/getModifierKeys.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/getModifierKeys.js @@ -1,3 +1,21 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getModifierKeysFromMouseEvent = getModifierKeysFromMouseEvent; +exports.getModifierKeyFromKeyboardEvent = getModifierKeyFromKeyboardEvent; + +function _types() { + const data = require("./types"); + + _types = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +24,38 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {ModifierKey} from './types'; -import {ModifierKeys} from './types'; - const KEYNAME_TO_PROPERTY = { - Meta: ModifierKeys.META, - Shift: ModifierKeys.SHIFT, - Alt: ModifierKeys.ALT, - Control: ModifierKeys.CTRL, + Meta: _types().ModifierKeys.META, + Shift: _types().ModifierKeys.SHIFT, + Alt: _types().ModifierKeys.ALT, + Control: _types().ModifierKeys.CTRL }; -export function getModifierKeysFromMouseEvent(e: MouseEvent): Set { - const keys: Set = new Set(); +function getModifierKeysFromMouseEvent(e) { + const keys = new Set(); + if (e.metaKey) { - keys.add(ModifierKeys.META); + keys.add(_types().ModifierKeys.META); } + if (e.shiftKey) { - keys.add(ModifierKeys.SHIFT); + keys.add(_types().ModifierKeys.SHIFT); } + if (e.altKey) { - keys.add(ModifierKeys.ALT); + keys.add(_types().ModifierKeys.ALT); } + if (e.ctrlKey) { - keys.add(ModifierKeys.CTRL); + keys.add(_types().ModifierKeys.CTRL); } return keys; } -export function getModifierKeyFromKeyboardEvent( - e: KeyboardEvent, -): ?ModifierKey { +function getModifierKeyFromKeyboardEvent(e) { return KEYNAME_TO_PROPERTY[e.key]; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/isScrollable.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/isScrollable.js index 2e3fcf0a..45c5d52a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/isScrollable.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/isScrollable.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = isScrollable; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,23 +13,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +function isScrollable(element, wheelEvent) { + let node = wheelEvent.target; -export default function isScrollable( - element: Element, - wheelEvent: WheelEvent, -): boolean { - let node: ?Element = ((wheelEvent.target: any): Element); while (node != null && node !== element) { - if ( - node.scrollHeight > node.clientHeight || - node.scrollWidth > node.clientWidth - ) { + if (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth) { return true; } - node = ((node.parentNode: any): Element); + + node = node.parentNode; } + return false; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/main.js index 1e4239c7..babe23b1 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/main.js @@ -1,3 +1,27 @@ +"use strict"; + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _DatatipManager() { + const data = require("./DatatipManager"); + + _DatatipManager = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,29 +30,22 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DatatipService} from './types'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import {DatatipManager} from './DatatipManager'; - class Activation { - _datatipManager: DatatipManager; - constructor() { - this._datatipManager = new DatatipManager(); + this._datatipManager = new (_DatatipManager().DatatipManager)(); } - provideDatatipService(): DatatipService { + provideDatatipService() { return this._datatipManager; } dispose() { this._datatipManager.dispose(); } + } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/types.js index aad7d4f8..172e8f0b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/types.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ModifierKeys = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +13,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -15,79 +22,11 @@ * You can register providers (which will be triggered on mouseover) or manually * create pinned datatips on-demand. */ -export type DatatipService = { - addProvider(provider: DatatipProvider): IDisposable, - addModifierProvider(provider: ModifierDatatipProvider): IDisposable, - createPinnedDataTip( - datatip: Datatip, - editor: TextEditor, - options?: PinnedDatatipOptions, - ): IDisposable, -}; - -export type PinnedDatatipOptions = {| - // Defaults to 'end-of-line'. - position?: PinnedDatatipPosition, - // Defaults to true. - showRangeHighlight?: boolean, -|}; - -export type PinnedDatatipPosition = 'end-of-line' | 'above-range'; - -export type DatatipProvider = { - priority: number, - grammarScopes?: Array, - // A unique name for the provider to be used for analytics. - // It is recommended that it be the name of the provider's package. - providerName: string, - datatip( - editor: atom$TextEditor, - bufferPosition: atom$Point, - ): Promise, -}; - -export type ModifierDatatipProvider = { - priority: number, - grammarScopes?: Array, - providerName: string, - modifierDatatip( - editor: atom$TextEditor, - bufferPosition: atom$Point, - heldKeys: Set, - ): Promise, -}; - -export type AnyDatatipProvider = DatatipProvider | ModifierDatatipProvider; - -export type Datatip = - | {| - component: React$ComponentType, - range: atom$Range, - pinnable?: boolean, - |} - | {| - markedStrings: Array, - range: atom$Range, - pinnable?: boolean, - |}; - // Borrowed from the LSP API. -export type MarkedString = - | { - type: 'markdown', - value: string, - } - | { - type: 'snippet', - grammar: atom$Grammar, - value: string, - }; - -export const ModifierKeys = Object.freeze({ +const ModifierKeys = Object.freeze({ META: 'metaKey', SHIFT: 'shiftKey', ALT: 'altKey', - CTRL: 'ctrlKey', + CTRL: 'ctrlKey' }); - -export type ModifierKey = 'metaKey' | 'shiftKey' | 'altKey' | 'ctrlKey'; +exports.ModifierKeys = ModifierKeys; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/AtomServiceContainer.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/AtomServiceContainer.js index 0ef07e51..2a4c2078 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/AtomServiceContainer.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/AtomServiceContainer.js @@ -1,3 +1,35 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.setConsoleService = setConsoleService; +exports.getConsoleService = getConsoleService; +exports.setConsoleRegisterExecutor = setConsoleRegisterExecutor; +exports.getConsoleRegisterExecutor = getConsoleRegisterExecutor; +exports.setDatatipService = setDatatipService; +exports.getDatatipService = getDatatipService; +exports.setNotificationService = setNotificationService; +exports.getNotificationService = getNotificationService; +exports.setTerminalService = setTerminalService; +exports.getTerminalService = getTerminalService; +exports.setRpcService = setRpcService; +exports.isNuclideEnvironment = isNuclideEnvironment; +exports.addDebugConfigurationProvider = addDebugConfigurationProvider; +exports.resolveDebugConfiguration = resolveDebugConfiguration; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,132 +38,97 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +let _raiseNativeNotification = null; +let _registerExecutor = null; +let _datatipService = null; +let _createConsole = null; +let _terminalService = null; +let _rpcService = null; -import type { - DatatipService, - ConsoleService, - RegisterExecutorFunction, - TerminalApi, -} from 'atom-ide-ui'; -import type { - DebuggerConfigurationProvider, - IProcessConfig, - VsAdapterType, -} from 'nuclide-debugger-common'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -type raiseNativeNotificationFunc = ?( - title: string, - body: string, - timeout: number, - raiseIfAtomHasFocus: boolean, -) => ?IDisposable; - -let _raiseNativeNotification: ?raiseNativeNotificationFunc = null; -let _registerExecutor: ?RegisterExecutorFunction = null; -let _datatipService: ?DatatipService = null; -let _createConsole: ?ConsoleService = null; -let _terminalService: ?TerminalApi = null; -let _rpcService: ?nuclide$RpcService = null; -const _configurationProviders: Map< - VsAdapterType, - DebuggerConfigurationProvider, -> = new Map(); - -export function setConsoleService(createConsole: ConsoleService): IDisposable { +const _configurationProviders = new Map(); + +function setConsoleService(createConsole) { _createConsole = createConsole; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { _createConsole = null; }); } -export function getConsoleService(): ?ConsoleService { +function getConsoleService() { return _createConsole; } -export function setConsoleRegisterExecutor( - registerExecutor: RegisterExecutorFunction, -): IDisposable { +function setConsoleRegisterExecutor(registerExecutor) { _registerExecutor = registerExecutor; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { _registerExecutor = null; }); } -export function getConsoleRegisterExecutor(): ?RegisterExecutorFunction { +function getConsoleRegisterExecutor() { return _registerExecutor; } -export function setDatatipService(datatipService: DatatipService): IDisposable { +function setDatatipService(datatipService) { _datatipService = datatipService; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { _datatipService = null; }); } -export function getDatatipService(): ?DatatipService { +function getDatatipService() { return _datatipService; } -export function setNotificationService( - raiseNativeNotification: raiseNativeNotificationFunc, -): void { +function setNotificationService(raiseNativeNotification) { _raiseNativeNotification = raiseNativeNotification; } -export function getNotificationService(): ?raiseNativeNotificationFunc { +function getNotificationService() { return _raiseNativeNotification; } -export function setTerminalService(terminalService: TerminalApi): IDisposable { +function setTerminalService(terminalService) { _terminalService = terminalService; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { _terminalService = null; }); } -export function getTerminalService(): ?TerminalApi { +function getTerminalService() { return _terminalService; } -export function setRpcService(rpcService: nuclide$RpcService): IDisposable { +function setRpcService(rpcService) { _rpcService = rpcService; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { _rpcService = null; }); } -export function isNuclideEnvironment(): boolean { +function isNuclideEnvironment() { return _rpcService != null; } -export function addDebugConfigurationProvider( - provider: DebuggerConfigurationProvider, -): IDisposable { +function addDebugConfigurationProvider(provider) { const existingProvider = _configurationProviders.get(provider.adapterType); + if (existingProvider != null) { - throw new Error( - 'Debug Configuration Provider already exists for adapter type: ' + - provider.adapterType, - ); + throw new Error('Debug Configuration Provider already exists for adapter type: ' + provider.adapterType); } + _configurationProviders.set(provider.adapterType, provider); - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { _configurationProviders.delete(provider.adapterType); }); } -export async function resolveDebugConfiguration( - configuration: IProcessConfig, -): Promise { - const existingProvider = _configurationProviders.get( - configuration.adapterType, - ); - return existingProvider != null - ? existingProvider.resolveConfiguration(configuration) - : configuration; -} +async function resolveDebugConfiguration(configuration) { + const existingProvider = _configurationProviders.get(configuration.adapterType); + + return existingProvider != null ? existingProvider.resolveConfiguration(configuration) : configuration; +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/BreakpointDisplayController.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/BreakpointDisplayController.js index 985117e0..1b2f42a4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/BreakpointDisplayController.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/BreakpointDisplayController.js @@ -1,3 +1,92 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _mouseToPosition() { + const data = require("../../../../nuclide-commons-atom/mouse-to-position"); + + _mouseToPosition = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _ContextMenu() { + const data = require("../../../../nuclide-commons-atom/ContextMenu"); + + _ContextMenu = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("./constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,184 +95,104 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {IBreakpoint, IDebugService} from './types'; - -import invariant from 'assert'; -import {bufferPositionForMouseEvent} from 'nuclide-commons-atom/mouse-to-position'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {fastDebounce} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {showMenuForEvent} from 'nuclide-commons-atom/ContextMenu'; -import classnames from 'classnames'; -import {DebuggerMode} from './constants'; -import featureConfig from 'nuclide-commons-atom/feature-config'; - -/** - * A single delegate which handles events from the object. - * - * This is simpler than registering handlers using emitter events directly, as - * there's less messy bookkeeping regarding lifetimes of the unregister - * Disposable objects. - */ -type BreakpointDisplayControllerDelegate = { - +handleTextEditorDestroyed: (controller: BreakpointDisplayController) => void, -}; - -type BreakpointMarkerProperties = {| - enabled: boolean, - resolved: boolean, - conditional: boolean, -|}; - /** * Handles displaying breakpoints and processing events for a single text * editor. */ -export default class BreakpointDisplayController { - _service: IDebugService; - _delegate: BreakpointDisplayControllerDelegate; - _disposables: UniversalDisposable; - _editor: atom$TextEditor; - _gutter: ?atom$Gutter; - _markers: Array; - _markerInfo: Map; - _lastShadowBreakpointMarker: ?atom$Marker; - _boundGlobalMouseMoveHandler: (event: MouseEvent) => void; - _boundCreateContextMenuHandler: (event: MouseEvent) => void; - _debugging: boolean; - - constructor( - delegate: BreakpointDisplayControllerDelegate, - service: IDebugService, - editor: atom$TextEditor, - ) { +class BreakpointDisplayController { + constructor(delegate, service, editor) { this._delegate = delegate; - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); this._service = service; this._editor = editor; this._markers = []; this._markerInfo = new Map(); this._lastShadowBreakpointMarker = null; this._boundGlobalMouseMoveHandler = this._handleGlobalMouseLeave.bind(this); - this._boundCreateContextMenuHandler = this._handleCreateContextMenu.bind( - this, - ); - this._debugging = this._isDebugging(); + this._boundCreateContextMenuHandler = this._handleCreateContextMenu.bind(this); + this._debugging = this._isDebugging(); // Configure the gutter. - // Configure the gutter. const gutter = editor.addGutter({ name: 'debugger-breakpoint', visible: false, // Priority is -200 by default and 0 is the line number - priority: -1100, + priority: -1100 }); + const debuggerModel = this._service.getModel(); + this._gutter = gutter; - this._disposables.addUntilDestroyed( - editor, - gutter.onDidDestroy(this._handleGutterDestroyed.bind(this)), - editor.observeGutters(this._registerGutterMouseHandlers.bind(this)), - observableFromSubscribeFunction( - debuggerModel.onDidChangeBreakpoints.bind(debuggerModel), - ) - // Debounce to account for bulk updates and not block the UI - .let(fastDebounce(10)) - .startWith(null) - .subscribe(this._update.bind(this)), - this._editor.onDidDestroy(this._handleTextEditorDestroyed.bind(this)), - this._registerEditorContextMenuHandler(), - ); + + this._disposables.addUntilDestroyed(editor, gutter.onDidDestroy(this._handleGutterDestroyed.bind(this)), editor.observeGutters(this._registerGutterMouseHandlers.bind(this)), (0, _event().observableFromSubscribeFunction)(debuggerModel.onDidChangeBreakpoints.bind(debuggerModel)) // Debounce to account for bulk updates and not block the UI + .let((0, _observable().fastDebounce)(10)).startWith(null).subscribe(this._update.bind(this)), this._editor.onDidDestroy(this._handleTextEditorDestroyed.bind(this)), this._registerEditorContextMenuHandler()); } - _isDebugging(): boolean { - return this._service.getDebuggerMode() !== DebuggerMode.STOPPED; + _isDebugging() { + return this._service.getDebuggerMode() !== _constants().DebuggerMode.STOPPED; } - _registerEditorContextMenuHandler(): IDisposable { + _registerEditorContextMenuHandler() { const editorElement = atom.views.getView(this._editor); - editorElement.addEventListener( - 'contextmenu', - this._boundCreateContextMenuHandler, - ); - return new UniversalDisposable(() => - editorElement.removeEventListener( - 'contextmenu', - this._boundCreateContextMenuHandler, - ), - ); + editorElement.addEventListener('contextmenu', this._boundCreateContextMenuHandler); + return new (_UniversalDisposable().default)(() => editorElement.removeEventListener('contextmenu', this._boundCreateContextMenuHandler)); } - _registerGutterMouseHandlers(gutter: atom$Gutter): void { + _registerGutterMouseHandlers(gutter) { const gutterView = atom.views.getView(gutter); - if ( - gutter.name !== 'line-number' && - gutter.name !== 'debugger-breakpoint' - ) { + + if (gutter.name !== 'line-number' && gutter.name !== 'debugger-breakpoint') { return; } + const boundClickHandler = this._handleGutterClick.bind(this); + const boundMouseMoveHandler = this._handleGutterMouseMove.bind(this); + const boundMouseEnterHandler = this._handleGutterMouseEnter.bind(this); - const boundMouseLeaveHandler = this._handleGutterMouseLeave.bind(this); - // Add mouse listeners gutter for setting breakpoints. + + const boundMouseLeaveHandler = this._handleGutterMouseLeave.bind(this); // Add mouse listeners gutter for setting breakpoints. + + gutterView.addEventListener('click', boundClickHandler); gutterView.addEventListener('mousemove', boundMouseMoveHandler); gutterView.addEventListener('mouseenter', boundMouseEnterHandler); gutterView.addEventListener('mouseleave', boundMouseLeaveHandler); - gutterView.addEventListener( - 'contextmenu', - this._boundCreateContextMenuHandler, - ); - this._disposables.add( - () => gutterView.removeEventListener('click', boundClickHandler), - () => gutterView.removeEventListener('mousemove', boundMouseMoveHandler), - () => - gutterView.removeEventListener('mouseenter', boundMouseEnterHandler), - () => - gutterView.removeEventListener('mouseleave', boundMouseLeaveHandler), - () => - gutterView.removeEventListener( - 'contextmenu', - this._boundCreateContextMenuHandler, - ), - () => - window.removeEventListener( - 'mousemove', - this._boundGlobalMouseMoveHandler, - ), - ); + gutterView.addEventListener('contextmenu', this._boundCreateContextMenuHandler); + + this._disposables.add(() => gutterView.removeEventListener('click', boundClickHandler), () => gutterView.removeEventListener('mousemove', boundMouseMoveHandler), () => gutterView.removeEventListener('mouseenter', boundMouseEnterHandler), () => gutterView.removeEventListener('mouseleave', boundMouseLeaveHandler), () => gutterView.removeEventListener('contextmenu', this._boundCreateContextMenuHandler), () => window.removeEventListener('mousemove', this._boundGlobalMouseMoveHandler)); } - _handleCreateContextMenu(event: MouseEvent): void { + _handleCreateContextMenu(event) { if (event.button !== 2 || !this._isDebugging()) { return; } event.preventDefault(); event.stopPropagation(); - const menuTemplate = atom.contextMenu.templateForEvent(event); - const debuggerGroupIndex = menuTemplate.findIndex( - item => item.label === 'Debugger', - ); + const debuggerGroupIndex = menuTemplate.findIndex(item => item.label === 'Debugger'); const [debuggerGroup] = menuTemplate.splice(debuggerGroupIndex, 1); - menuTemplate.unshift(...debuggerGroup.submenu, {type: 'separator'}); - showMenuForEvent(event, menuTemplate); + menuTemplate.unshift(...debuggerGroup.submenu, { + type: 'separator' + }); + (0, _ContextMenu().showMenuForEvent)(event, menuTemplate); } dispose() { this._disposables.dispose(); + this._markers.forEach(marker => marker.destroy()); + if (this._gutter) { this._gutter.destroy(); } } - getEditor(): atom$TextEditor { + getEditor() { return this._editor; } @@ -191,6 +200,7 @@ export default class BreakpointDisplayController { // Gutter.destroy seems to fail after text editor is destroyed, and // Gutter.onDidDestroy doesn't seem to be called in that case. this._gutter = null; + this._delegate.handleTextEditorDestroyed(this); } @@ -200,7 +210,7 @@ export default class BreakpointDisplayController { this._gutter = null; } - _needsUpdate(line: number, bp: ?IBreakpoint): boolean { + _needsUpdate(line, bp) { // Checks if an existing marker no longer matches the properties of the breakpoint // it corresponds to. if (bp == null) { @@ -208,34 +218,30 @@ export default class BreakpointDisplayController { } const info = this._markerInfo.get(line); + if (info == null) { return true; } - if ( - info.enabled !== bp.enabled || - info.resolved !== bp.verified || - info.conditional !== (bp.condition != null) - ) { + if (info.enabled !== bp.enabled || info.resolved !== bp.verified || info.conditional !== (bp.condition != null)) { return true; } return false; } - _getLineForBp(bp: IBreakpoint): number { + _getLineForBp(bp) { // Zero-based breakpoints line map (to match UI markers). - return ( - (bp.endLine != null && !Number.isNaN(bp.endLine) ? bp.endLine : bp.line) - - 1 - ); + return (bp.endLine != null && !Number.isNaN(bp.endLine) ? bp.endLine : bp.line) - 1; } - /** * Update the display with the current set of breakpoints for this editor. */ - _update(): void { + + + _update() { const gutter = this._gutter; + if (gutter == null) { return; } @@ -243,45 +249,43 @@ export default class BreakpointDisplayController { const debugging = this._isDebugging(); const path = this._editor.getPath(); + if (path == null) { return; } + const allBreakpoints = this._service.getModel().getBreakpoints(); + const breakpoints = allBreakpoints.filter(bp => bp.uri === path); - const lineMap = new Map( - breakpoints.map(bp => [this._getLineForBp(bp), bp]), - ); + const lineMap = new Map(breakpoints.map(bp => [this._getLineForBp(bp), bp])); // A mutable unhandled lines map. - // A mutable unhandled lines map. const unhandledLines = new Set(lineMap.keys()); - const markersToKeep = []; + const markersToKeep = []; // Destroy markers that no longer correspond to breakpoints. - // Destroy markers that no longer correspond to breakpoints. this._markers.forEach(marker => { const line = marker.getStartBufferPosition().row; const bp = lineMap.get(line); - if ( - debugging === this._debugging && - unhandledLines.has(line) && - !this._needsUpdate(line, bp) - ) { + + if (debugging === this._debugging && unhandledLines.has(line) && !this._needsUpdate(line, bp)) { markersToKeep.push(marker); unhandledLines.delete(line); } else { this._markerInfo.delete(line); + marker.destroy(); } }); this._debugging = debugging; - const fileLength = this._editor.getLineCount(); + const fileLength = this._editor.getLineCount(); // Add new markers for breakpoints without corresponding markers. + - // Add new markers for breakpoints without corresponding markers. for (const [line, breakpoint] of lineMap) { // Remove any breakpoints that are past the end of the file. if (line >= fileLength) { this._service.removeBreakpoints(breakpoint.getId()); + continue; } @@ -289,19 +293,18 @@ export default class BreakpointDisplayController { // This line has been handled. continue; } - const marker = this._createBreakpointMarkerAtLine( - line, - false, // isShadow - breakpoint, - ); - // Remember the properties of the marker at this line so it's easy to tell if it + const marker = this._createBreakpointMarkerAtLine(line, false, // isShadow + breakpoint); // Remember the properties of the marker at this line so it's easy to tell if it // needs to be updated when the breakpoint properties change. + + this._markerInfo.set(line, { enabled: breakpoint.enabled, resolved: breakpoint.verified, - conditional: breakpoint.condition != null, + conditional: breakpoint.condition != null }); + marker.onDidChange(this._handleMarkerChange.bind(this, breakpoint)); markersToKeep.push(marker); } @@ -309,75 +312,63 @@ export default class BreakpointDisplayController { gutter.show(); this._markers = markersToKeep; } - /** * Handler for marker movements due to text being edited. */ - _handleMarkerChange( - breakpoint: IBreakpoint, - event: atom$MarkerChangeEvent, - ): void { + + + _handleMarkerChange(breakpoint, event) { const path = this._editor.getPath(); + if (path == null || path.length === 0) { return; } + if (!event.isValid) { this._service.removeBreakpoints(breakpoint.getId()); - } else if ( - event.oldHeadBufferPosition.row !== event.newHeadBufferPosition.row - ) { + } else if (event.oldHeadBufferPosition.row !== event.newHeadBufferPosition.row) { this._service.updateBreakpoints(breakpoint.uri, { - [breakpoint.getId()]: { - ...breakpoint, + [breakpoint.getId()]: Object.assign({}, breakpoint, { // VSP is 1-based line numbers. - line: event.newHeadBufferPosition.row + 1, - }, + line: event.newHeadBufferPosition.row + 1 + }) }); } } - _handleGutterClick(event: Event): void { + _handleGutterClick(event) { // classList isn't in the defs of EventTarget... - const target: HTMLElement = (event.target: any); + const target = event.target; + if (target.classList.contains('icon-right')) { return; } - const path = this._editor.getPath(); - // flowlint-next-line sketchy-null-string:off + const path = this._editor.getPath(); // flowlint-next-line sketchy-null-string:off + + if (!path) { return; - } - - // Don't toggle a breakpoint if the user clicked on something in the gutter that is not + } // Don't toggle a breakpoint if the user clicked on something in the gutter that is not // the debugger, such as clicking on a line number to select the line. - if ( - !target.classList.contains('debugger-shadow-breakpoint-icon') && - !target.classList.contains('debugger-breakpoint-icon') && - !target.classList.contains('debugger-breakpoint-icon-disabled') && - !target.classList.contains('debugger-breakpoint-icon-unresolved') && - !target.classList.contains('debugger-breakpoint-icon-conditional') - ) { + + + if (!target.classList.contains('debugger-shadow-breakpoint-icon') && !target.classList.contains('debugger-breakpoint-icon') && !target.classList.contains('debugger-breakpoint-icon-disabled') && !target.classList.contains('debugger-breakpoint-icon-unresolved') && !target.classList.contains('debugger-breakpoint-icon-conditional')) { return; } try { const curLine = this._getCurrentMouseEventLine(event); + this._service.toggleSourceBreakpoint(path, curLine + 1); - if ( - this._service.getModel().getBreakpointAtLine(path, curLine + 1) != null - ) { + if (this._service.getModel().getBreakpointAtLine(path, curLine + 1) != null) { // If a breakpoint was added and showDebuggerOnBpSet config setting // is true, show the debugger. - if (featureConfig.get('atom-ide-debugger.showDebuggerOnBpSet')) { - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'debugger:show', - { - showOnlyIfHidden: true, - }, - ); + if (_featureConfig().default.get('atom-ide-debugger.showDebuggerOnBpSet')) { + atom.commands.dispatch(atom.views.getView(atom.workspace), 'debugger:show', { + showOnlyIfHidden: true + }); } } } catch (e) { @@ -385,119 +376,107 @@ export default class BreakpointDisplayController { } } - _getCurrentMouseEventLine(event: Event): number { + _getCurrentMouseEventLine(event) { // $FlowIssue - const bufferPos = bufferPositionForMouseEvent(event, this._editor); + const bufferPos = (0, _mouseToPosition().bufferPositionForMouseEvent)(event, this._editor); return bufferPos.row; } - _handleGutterMouseMove(event: Event): void { + _handleGutterMouseMove(event) { try { const curLine = this._getCurrentMouseEventLine(event); + if (this._isLineOverLastShadowBreakpoint(curLine)) { return; - } - // User moves to a new line we need to delete the old shadow breakpoint + } // User moves to a new line we need to delete the old shadow breakpoint // and create a new one. + + this._removeLastShadowBreakpoint(); + this._createShadowBreakpointAtLine(this._editor, curLine); } catch (e) { return; } } - _handleGutterMouseEnter(event: Event): void { + _handleGutterMouseEnter(event) { window.addEventListener('mousemove', this._boundGlobalMouseMoveHandler); - } - - // This is a giant hack to make sure that the breakpoint actually disappears. + } // This is a giant hack to make sure that the breakpoint actually disappears. // The issue is that mouseleave event is sometimes not triggered on the gutter // I(vjeux) and matthewithanm spent multiple entire days trying to figure out // why without success, so this is going to have to do :( - _handleGlobalMouseLeave(event: MouseEvent): void { + + + _handleGlobalMouseLeave(event) { if (!this._editor) { return; } + const view = atom.views.getView(this._editor); const rect = view.getBoundingClientRect(); - if ( - event.clientX < rect.left || - event.clientX > rect.right || - event.clientY < rect.top || - event.clientY > rect.bottom - ) { + + if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) { this._removeLastShadowBreakpoint(); - window.removeEventListener( - 'mousemove', - this._boundGlobalMouseMoveHandler, - ); + + window.removeEventListener('mousemove', this._boundGlobalMouseMoveHandler); } } - _handleGutterMouseLeave(event: Event): void { + _handleGutterMouseLeave(event) { this._removeLastShadowBreakpoint(); } - _isLineOverLastShadowBreakpoint(curLine: number): boolean { + _isLineOverLastShadowBreakpoint(curLine) { const shadowBreakpointMarker = this._lastShadowBreakpointMarker; - return ( - shadowBreakpointMarker != null && - shadowBreakpointMarker.getStartBufferPosition().row === curLine - ); + return shadowBreakpointMarker != null && shadowBreakpointMarker.getStartBufferPosition().row === curLine; } - _removeLastShadowBreakpoint(): void { + _removeLastShadowBreakpoint() { if (this._lastShadowBreakpointMarker != null) { this._lastShadowBreakpointMarker.destroy(); + this._lastShadowBreakpointMarker = null; } } - _createShadowBreakpointAtLine(editor: TextEditor, line: number): void { - const breakpointsAtLine = this._markers.filter( - marker => marker.getStartBufferPosition().row === line, - ); + _createShadowBreakpointAtLine(editor, line) { + const breakpointsAtLine = this._markers.filter(marker => marker.getStartBufferPosition().row === line); // Don't create a shadow breakpoint at a line that already has a breakpoint. + - // Don't create a shadow breakpoint at a line that already has a breakpoint. if (breakpointsAtLine.length === 0) { - this._lastShadowBreakpointMarker = this._createBreakpointMarkerAtLine( - line, - true, // isShadow - null, - ); + this._lastShadowBreakpointMarker = this._createBreakpointMarkerAtLine(line, true, // isShadow + null); } } - _createBreakpointMarkerAtLine( - line: number, - isShadow: boolean, - breakpoint: ?IBreakpoint, - ): atom$Marker { + _createBreakpointMarkerAtLine(line, isShadow, breakpoint) { const enabled = breakpoint != null ? breakpoint.enabled : true; const resolved = breakpoint != null ? breakpoint.verified : false; const condition = breakpoint != null ? breakpoint.condition : null; - const marker = this._editor.markBufferPosition([line, 0], { - invalidate: 'never', - }); - // If the debugger is not attached, display all breakpoints as resolved. + const marker = this._editor.markBufferPosition([line, 0], { + invalidate: 'never' + }); // If the debugger is not attached, display all breakpoints as resolved. // Once the debugger attaches, it will determine what's actually resolved or not. + + const unresolved = this._debugging && !resolved; const conditional = condition != null; - const elem: HTMLElement = document.createElement('span'); + const elem = document.createElement('span'); elem.dataset.line = line.toString(); if (breakpoint != null) { elem.dataset.bpId = breakpoint.getId(); } - elem.className = classnames({ + elem.className = (0, _classnames().default)({ 'debugger-breakpoint-icon': !isShadow && enabled && !unresolved, 'debugger-breakpoint-icon-conditional': conditional, 'debugger-breakpoint-icon-nonconditional': !conditional, 'debugger-shadow-breakpoint-icon': isShadow, 'debugger-breakpoint-icon-disabled': !isShadow && !enabled, - 'debugger-breakpoint-icon-unresolved': !isShadow && enabled && unresolved, + 'debugger-breakpoint-icon-unresolved': !isShadow && enabled && unresolved }); if (!isShadow) { @@ -514,8 +493,17 @@ export default class BreakpointDisplayController { } } - invariant(this._gutter != null); - this._gutter.decorateMarker(marker, {item: elem}); + if (!(this._gutter != null)) { + throw new Error("Invariant violation: \"this._gutter != null\""); + } + + this._gutter.decorateMarker(marker, { + item: elem + }); + return marker; } + } + +exports.default = BreakpointDisplayController; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/BreakpointManager.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/BreakpointManager.js index 1bfa4089..e4eba1f3 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/BreakpointManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/BreakpointManager.js @@ -1,3 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _BreakpointDisplayController() { + const data = _interopRequireDefault(require("./BreakpointDisplayController")); + + _BreakpointDisplayController = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,57 +35,50 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {IDebugService} from './types'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import BreakpointDisplayController from './BreakpointDisplayController'; - -export default class BreakpointManager { - _service: IDebugService; - _displayControllers: Map; - _disposables: UniversalDisposable; - - constructor(service: IDebugService) { +class BreakpointManager { + constructor(service) { this._service = service; this._displayControllers = new Map(); - this._disposables = new UniversalDisposable( - atom.workspace.observeTextEditors(this._handleTextEditor.bind(this)), - ); + this._disposables = new (_UniversalDisposable().default)(atom.workspace.observeTextEditors(this._handleTextEditor.bind(this))); } - dispose(): void { + dispose() { this._disposables.dispose(); + this._displayControllers.forEach(controller => controller.dispose()); + this._displayControllers.clear(); } - /** * Used for testing. */ - getDisplayControllers(): Map { + + + getDisplayControllers() { return this._displayControllers; } - /** * Delegate callback from BreakpointDisplayController. */ - handleTextEditorDestroyed(controller: BreakpointDisplayController) { + + + handleTextEditorDestroyed(controller) { controller.dispose(); + this._displayControllers.delete(controller.getEditor()); } - _handleTextEditor(editor: atom$TextEditor) { + _handleTextEditor(editor) { if (!this._displayControllers.has(editor)) { - const controller = new BreakpointDisplayController( - this, - this._service, - editor, - ); + const controller = new (_BreakpointDisplayController().default)(this, this._service, editor); + this._displayControllers.set(editor, controller); } } + } + +exports.default = BreakpointManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/DebuggerDatatip.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/DebuggerDatatip.js index 0e7c634f..cca0d292 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/DebuggerDatatip.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/DebuggerDatatip.js @@ -1,3 +1,62 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.debuggerDatatip = debuggerDatatip; + +function _bindObservableAsProps() { + const data = require("../../../../nuclide-commons-ui/bindObservableAsProps"); + + _bindObservableAsProps = function () { + return data; + }; + + return data; +} + +function _evaluationExpression() { + const data = require("./evaluationExpression"); + + _evaluationExpression = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("./constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _DebuggerDatatipComponent() { + const data = _interopRequireDefault(require("./ui/DebuggerDatatipComponent")); + + _DebuggerDatatipComponent = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,53 +65,47 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {Datatip} from 'atom-ide-ui'; -import type {IDebugService} from './types'; - -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {getDefaultEvaluationExpression} from './evaluationExpression'; -import {DebuggerMode} from './constants'; -import DebuggerDatatipComponent from './ui/DebuggerDatatipComponent'; -import {expressionAsEvaluationResultStream} from './utils'; - -export async function debuggerDatatip( - service: IDebugService, - editor: TextEditor, - position: atom$Point, -): Promise { - if (service.getDebuggerMode() !== DebuggerMode.PAUSED) { +async function debuggerDatatip(service, editor, position) { + if (service.getDebuggerMode() !== _constants().DebuggerMode.PAUSED) { return null; } + const activeEditor = atom.workspace.getActiveTextEditor(); + if (activeEditor == null) { return null; } - const evaluationExpression = getDefaultEvaluationExpression(editor, position); + + const evaluationExpression = (0, _evaluationExpression().getDefaultEvaluationExpression)(editor, position); + if (evaluationExpression == null) { return null; } - const {expression, range} = evaluationExpression; - const {focusedProcess, focusedStackFrame} = service.viewModel; + + const { + expression, + range + } = evaluationExpression; + const { + focusedProcess, + focusedStackFrame + } = service.viewModel; + if (expression == null || focusedProcess == null) { // TODO respect session.capabilities.supportsEvaluateForHovers // and fallback to scopes variables resolution. return null; } - const propStream = expressionAsEvaluationResultStream( - service.createExpression(expression), - focusedProcess, - focusedStackFrame, - 'hover', - ).map(evaluationResult => ({ + + const propStream = (0, _utils().expressionAsEvaluationResultStream)(service.createExpression(expression), focusedProcess, focusedStackFrame, 'hover').map(evaluationResult => ({ expression, - evaluationResult, + evaluationResult })); return { - component: bindObservableAsProps(propStream, DebuggerDatatipComponent), - range, + component: (0, _bindObservableAsProps().bindObservableAsProps)(propStream, _DebuggerDatatipComponent().default), + range }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/DebuggerUiModel.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/DebuggerUiModel.js index f2e34d61..e360d937 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/DebuggerUiModel.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/DebuggerUiModel.js @@ -1,3 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.WORKSPACE_VIEW_URI = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,127 +37,117 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type { - DebuggerLaunchAttachProvider, - NuclideDebuggerProvider, -} from 'nuclide-debugger-common'; -import type {IDebugService} from './types'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Emitter} from 'atom'; -import nuclideUri from 'nuclide-commons/nuclideUri'; - -export const WORKSPACE_VIEW_URI = 'atom://nuclide/debugger'; - +const WORKSPACE_VIEW_URI = 'atom://nuclide/debugger'; +exports.WORKSPACE_VIEW_URI = WORKSPACE_VIEW_URI; const CONNECTIONS_UPDATED_EVENT = 'CONNECTIONS_UPDATED_EVENT'; const PROVIDERS_UPDATED_EVENT = 'PROVIDERS_UPDATED_EVENT'; - /** * Atom ViewProvider compatible model object. */ -export default class DebuggerModel { - _disposables: UniversalDisposable; - _service: IDebugService; - _emitter: Emitter; +class DebuggerModel { // Debugger providers - _debuggerProviders: Set; - _connections: Array; - - constructor(service: IDebugService) { + constructor(service) { this._service = service; + this._emitter = new _atom.Emitter(); + this._debuggerProviders = new Set(); // There is always a local connection. - this._emitter = new Emitter(); - this._debuggerProviders = new Set(); - // There is always a local connection. this._connections = ['local']; - - this._disposables = new UniversalDisposable(this._listenForProjectChange()); + this._disposables = new (_UniversalDisposable().default)(this._listenForProjectChange()); } - _listenForProjectChange(): IDisposable { + _listenForProjectChange() { return atom.project.onDidChangePaths(() => { this._updateConnections(); }); } - /** * Utility for getting refreshed connections. * TODO: refresh connections when new directories are removed/added in file-tree. */ - _updateConnections(): void { - const connections = this._getRemoteConnections(); - // Always have one single local connection. + + + _updateConnections() { + const connections = this._getRemoteConnections(); // Always have one single local connection. + + connections.push('local'); this._connections = connections; + this._emitter.emit(CONNECTIONS_UPDATED_EVENT); } - /** * Get remote connections without duplication. */ - _getRemoteConnections(): Array { + + + _getRemoteConnections() { // TODO: move this logic into RemoteConnection package. - return atom.project - .getPaths() - .filter(path => { - return nuclideUri.isRemote(path); - }) - .map(remotePath => { - const {hostname} = nuclideUri.parseRemoteUri(remotePath); - return nuclideUri.createRemoteUri(hostname, '/'); - }) - .filter((path, index, inputArray) => { - return inputArray.indexOf(path) === index; - }); + return atom.project.getPaths().filter(path => { + return _nuclideUri().default.isRemote(path); + }).map(remotePath => { + const { + hostname + } = _nuclideUri().default.parseRemoteUri(remotePath); + + return _nuclideUri().default.createRemoteUri(hostname, '/'); + }).filter((path, index, inputArray) => { + return inputArray.indexOf(path) === index; + }); } dispose() { this._disposables.dispose(); } - addDebuggerProvider(provider: NuclideDebuggerProvider): void { + addDebuggerProvider(provider) { this._debuggerProviders.add(provider); + this._emitter.emit(PROVIDERS_UPDATED_EVENT); } - removeDebuggerProvider(provider: NuclideDebuggerProvider): void { + removeDebuggerProvider(provider) { this._debuggerProviders.delete(provider); } - /** * Subscribe to new connection updates from DebuggerActions. */ - onConnectionsUpdated(callback: () => void): IDisposable { + + + onConnectionsUpdated(callback) { return this._emitter.on(CONNECTIONS_UPDATED_EVENT, callback); } - onProvidersUpdated(callback: () => void): IDisposable { + onProvidersUpdated(callback) { return this._emitter.on(PROVIDERS_UPDATED_EVENT, callback); } - getConnections(): Array { + getConnections() { return this._connections; } - /** * Return available launch/attach provider for input connection. * Caller is responsible for disposing the results. */ - getLaunchAttachProvidersForConnection( - connection: string, - ): Array { + + + getLaunchAttachProvidersForConnection(connection) { const availableLaunchAttachProviders = []; + for (const provider of this._debuggerProviders) { const launchAttachProvider = provider.getLaunchAttachProvider(connection); + if (launchAttachProvider != null) { availableLaunchAttachProviders.push(launchAttachProvider); } } + return availableLaunchAttachProviders; } + } + +exports.default = DebuggerModel; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/RemoteControlService.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/RemoteControlService.js index d6a8f87f..572aa8c9 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/RemoteControlService.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/RemoteControlService.js @@ -1,3 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function DebugProtocol() { + const data = _interopRequireWildcard(require("vscode-debugprotocol")); + + DebugProtocol = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("./constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,93 +47,67 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {Observable} from 'rxjs'; -import type {IDebugService, IProcess, RemoteDebuggerService} from './types'; -import type {IProcessConfig, IVspInstance} from 'nuclide-debugger-common'; -import * as DebugProtocol from 'vscode-debugprotocol'; - -import {DebuggerMode} from './constants'; -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -export default class RemoteControlService implements RemoteDebuggerService { - _service: IDebugService; - _disposables: UniversalDisposable; - - constructor(service: IDebugService) { +class RemoteControlService { + constructor(service) { this._service = service; - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); } - dispose(): void { + dispose() { this._disposables.dispose(); } - onDidStartDebugSession( - callback: (config: IProcessConfig) => mixed, - ): IDisposable { + onDidStartDebugSession(callback) { return this._service.onDidStartDebugSession(callback); } - _onSessionEnd( - focusedProcess: IProcess, - disposables: UniversalDisposable, - ): void { - disposables.add( - this._service.viewModel.onDidFocusProcess(() => { - if ( - !this._service - .getModel() - .getProcesses() - .includes(focusedProcess) - ) { - disposables.dispose(); - } - }), - ); + _onSessionEnd(focusedProcess, disposables) { + disposables.add(this._service.viewModel.onDidFocusProcess(() => { + if (!this._service.getModel().getProcesses().includes(focusedProcess)) { + disposables.dispose(); + } + })); } - async startVspDebugging(config: IProcessConfig): Promise { + async startVspDebugging(config) { await this._service.startDebugging(config); + const { + viewModel + } = this._service; + const { + focusedProcess + } = viewModel; - const {viewModel} = this._service; - const {focusedProcess} = viewModel; - invariant(focusedProcess != null); + if (!(focusedProcess != null)) { + throw new Error("Invariant violation: \"focusedProcess != null\""); + } - const isFocusedProcess = (): boolean => { - return ( - this._service.getDebuggerMode() !== DebuggerMode.STOPPED && - viewModel.focusedProcess === focusedProcess - ); + const isFocusedProcess = () => { + return this._service.getDebuggerMode() !== _constants().DebuggerMode.STOPPED && viewModel.focusedProcess === focusedProcess; }; - const customRequest = async ( - request: string, - args: any, - ): Promise => { + const customRequest = async (request, args) => { if (!isFocusedProcess()) { - throw new Error( - 'Cannot send custom requests to a no longer active debug session!', - ); + throw new Error('Cannot send custom requests to a no longer active debug session!'); } + return focusedProcess.session.custom(request, args); }; - const observeCustomEvents = (): Observable => { + const observeCustomEvents = () => { if (!isFocusedProcess()) { - throw new Error( - 'Cannot send custom requests to a no longer active debug session!', - ); + throw new Error('Cannot send custom requests to a no longer active debug session!'); } + return focusedProcess.session.observeCustomEvents(); }; - const disposables = new UniversalDisposable(); - const addCustomDisposable = (disposable: IDisposable): void => { + const disposables = new (_UniversalDisposable().default)(); + + const addCustomDisposable = disposable => { disposables.add(disposable); }; @@ -101,7 +116,10 @@ export default class RemoteControlService implements RemoteDebuggerService { return Object.freeze({ customRequest, observeCustomEvents, - addCustomDisposable, + addCustomDisposable }); } + } + +exports.default = RemoteControlService; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/constants.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/constants.js index 6989c80d..4123f943 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/constants.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/constants.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DEBUG_SOURCES_URI = exports.UNKNOWN_SOURCE = exports.BreakpointEventReasons = exports.DEBUGGER_PANELS_DEFAULT_WIDTH_PX = exports.DEBUGGER_PANELS_DEFAULT_LOCATION = exports.DebuggerMode = exports.AnalyticsEvents = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,13 +13,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DebuggerModeType} from './types'; - -export const AnalyticsEvents = Object.freeze({ +const AnalyticsEvents = Object.freeze({ ANDROID_DEBUGGER_SDK_SOURCES: 'android-debugger-sdk-sources', DEBUGGER_BREAKPOINT_ADD: 'debugger-breakpoint-add', DEBUGGER_BREAKPOINT_DELETE: 'debugger-breakpoint-delete', @@ -42,28 +46,30 @@ export const AnalyticsEvents = Object.freeze({ DEBUGGER_WATCH_REMOVE_EXPRESSION: 'debugger-watch-remove-expression', DEBUGGER_WATCH_UPDATE_EXPRESSION: 'debugger-watch-update-expression', DEBUGGER_EDIT_BREAKPOINT_FROM_ICON: 'debugger-edit-breakpoint-from-icon', - DEBUGGER_DELETE_BREAKPOINT_FROM_ICON: 'debugger-delete-breakpoint-from-icon', + DEBUGGER_DELETE_BREAKPOINT_FROM_ICON: 'debugger-delete-breakpoint-from-icon' }); - -export const DebuggerMode = Object.freeze({ +exports.AnalyticsEvents = AnalyticsEvents; +const DebuggerMode = Object.freeze({ STARTING: 'starting', RUNNING: 'running', PAUSED: 'paused', STOPPED: 'stopped', - STOPPING: 'stopping', -}); - -// This is to work around flow's missing support of enums. -(DebuggerMode: {[key: string]: DebuggerModeType}); + STOPPING: 'stopping' +}); // This is to work around flow's missing support of enums. -export const DEBUGGER_PANELS_DEFAULT_LOCATION = 'right'; -export const DEBUGGER_PANELS_DEFAULT_WIDTH_PX = 500; - -export const BreakpointEventReasons = Object.freeze({ +exports.DebuggerMode = DebuggerMode; +DebuggerMode; +const DEBUGGER_PANELS_DEFAULT_LOCATION = 'right'; +exports.DEBUGGER_PANELS_DEFAULT_LOCATION = DEBUGGER_PANELS_DEFAULT_LOCATION; +const DEBUGGER_PANELS_DEFAULT_WIDTH_PX = 500; +exports.DEBUGGER_PANELS_DEFAULT_WIDTH_PX = DEBUGGER_PANELS_DEFAULT_WIDTH_PX; +const BreakpointEventReasons = Object.freeze({ NEW: 'new', CHANGED: 'changed', - REMOVED: 'removed', + REMOVED: 'removed' }); - -export const UNKNOWN_SOURCE = 'Unknown'; -export const DEBUG_SOURCES_URI = 'atom://debug-sources'; +exports.BreakpointEventReasons = BreakpointEventReasons; +const UNKNOWN_SOURCE = 'Unknown'; +exports.UNKNOWN_SOURCE = UNKNOWN_SOURCE; +const DEBUG_SOURCES_URI = 'atom://debug-sources'; +exports.DEBUG_SOURCES_URI = DEBUG_SOURCES_URI; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/evaluationExpression.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/evaluationExpression.js index 27eb7605..b91f45e3 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/evaluationExpression.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/evaluationExpression.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getDefaultEvaluationExpression = getDefaultEvaluationExpression; + +var _atom = require("atom"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +15,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ @@ -37,27 +46,16 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -import {Range} from 'atom'; - -export function getDefaultEvaluationExpression( - editor: atom$TextEditor, - position: atom$Point, -): ?{ - expression: string, - range: atom$Range, -} { +function getDefaultEvaluationExpression(editor, position) { const lineContent = editor.lineTextForBufferRow(position.row); - let matchingExpression: ?string; - let startOffset = 0; - - // Some example supported expressions: myVar.prop, a.b.c.d, myVar?.prop, myVar->prop, MyClass::StaticProp, *myVar + let matchingExpression; + let startOffset = 0; // Some example supported expressions: myVar.prop, a.b.c.d, myVar?.prop, myVar->prop, MyClass::StaticProp, *myVar // Match any character except a set of characters which often break interesting sub-expressions + const expression = /([^()[\]{}<>\s+\-/%~#^;=|,`!]|->)+/g; - let result; + let result; // First find the full expression under the cursor - // First find the full expression under the cursor - while ((result = expression.exec(lineContent))) { + while (result = expression.exec(lineContent)) { const start = result.index + 1; const end = start + result[0].length; @@ -66,29 +64,24 @@ export function getDefaultEvaluationExpression( startOffset = start; break; } - } - - // If there are non-word characters after the cursor, we want to truncate the expression then. + } // If there are non-word characters after the cursor, we want to truncate the expression then. // For example in expression 'a.b.c.d', if the focus was under 'b', 'a.b' would be evaluated. + + if (matchingExpression != null) { const subExpression = /\w+/g; let subExpressionResult; - while ((subExpressionResult = subExpression.exec(matchingExpression))) { - const subEnd = - subExpressionResult.index + - 1 + - startOffset + - subExpressionResult[0].length; + + while (subExpressionResult = subExpression.exec(matchingExpression)) { + const subEnd = subExpressionResult.index + 1 + startOffset + subExpressionResult[0].length; + if (subEnd >= position.column + 1) { break; } } if (subExpressionResult) { - matchingExpression = matchingExpression.substring( - 0, - subExpression.lastIndex, - ); + matchingExpression = matchingExpression.substring(0, subExpression.lastIndex); } } @@ -98,9 +91,6 @@ export function getDefaultEvaluationExpression( return { expression: matchingExpression, - range: new Range( - [position.row, startOffset - 1], - [position.row, startOffset + matchingExpression.length - 1], - ), + range: new _atom.Range([position.row, startOffset - 1], [position.row, startOffset + matchingExpression.length - 1]) }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/logger.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/logger.js index fe1da999..ea3d5a48 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/logger.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/logger.js @@ -1,3 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,11 +23,11 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +const DEBUGGER_LOGGER_CATEGORY = 'atom-debugger'; -import {getLogger} from 'log4js'; +var _default = (0, _log4js().getLogger)(DEBUGGER_LOGGER_CATEGORY); -const DEBUGGER_LOGGER_CATEGORY = 'atom-debugger'; -export default getLogger(DEBUGGER_LOGGER_CATEGORY); +exports.default = _default; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/main.js index a2498c20..b69d1370 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/main.js @@ -1,3 +1,245 @@ +"use strict"; + +function _projects() { + const data = require("../../../../nuclide-commons-atom/projects"); + + _projects = function () { + return data; + }; + + return data; +} + +function _BreakpointManager() { + const data = _interopRequireDefault(require("./BreakpointManager")); + + _BreakpointManager = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("./constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _BreakpointConfigComponent() { + const data = _interopRequireDefault(require("./ui/BreakpointConfigComponent")); + + _BreakpointConfigComponent = function () { + return data; + }; + + return data; +} + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _RemoteControlService() { + const data = _interopRequireDefault(require("./RemoteControlService")); + + _RemoteControlService = function () { + return data; + }; + + return data; +} + +function _DebuggerUiModel() { + const data = _interopRequireDefault(require("./DebuggerUiModel")); + + _DebuggerUiModel = function () { + return data; + }; + + return data; +} + +function _DebugService() { + const data = _interopRequireDefault(require("./vsp/DebugService")); + + _DebugService = function () { + return data; + }; + + return data; +} + +function _DebuggerDatatip() { + const data = require("./DebuggerDatatip"); + + _DebuggerDatatip = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _DebuggerLaunchAttachUI() { + const data = _interopRequireDefault(require("./ui/DebuggerLaunchAttachUI")); + + _DebuggerLaunchAttachUI = function () { + return data; + }; + + return data; +} + +function _renderReactRoot() { + const data = require("../../../../nuclide-commons-ui/renderReactRoot"); + + _renderReactRoot = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _AtomServiceContainer() { + const data = require("./AtomServiceContainer"); + + _AtomServiceContainer = function () { + return data; + }; + + return data; +} + +function _range() { + const data = require("../../../../nuclide-commons-atom/range"); + + _range = function () { + return data; + }; + + return data; +} + +function _DebuggerLayoutManager() { + const data = _interopRequireDefault(require("./ui/DebuggerLayoutManager")); + + _DebuggerLayoutManager = function () { + return data; + }; + + return data; +} + +function _DebuggerPaneViewModel() { + const data = _interopRequireDefault(require("./ui/DebuggerPaneViewModel")); + + _DebuggerPaneViewModel = function () { + return data; + }; + + return data; +} + +function _DebuggerPaneContainerViewModel() { + const data = _interopRequireDefault(require("./ui/DebuggerPaneContainerViewModel")); + + _DebuggerPaneContainerViewModel = function () { + return data; + }; + + return data; +} + +var _os = _interopRequireDefault(require("os")); + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _ReactMountRootElement() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-ui/ReactMountRootElement")); + + _ReactMountRootElement = function () { + return data; + }; + + return data; +} + +function _menuUtils() { + const data = require("../../../../nuclide-commons/menuUtils"); + + _menuUtils = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,460 +248,313 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - DebuggerConfigAction, - DebuggerLaunchAttachProvider, - NuclideDebuggerProvider, - DebuggerConfigurationProvider, -} from 'nuclide-debugger-common'; -import type { - ConsoleService, - DatatipProvider, - DatatipService, - RegisterExecutorFunction, - TerminalApi, -} from 'atom-ide-ui'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {SerializedState, IBreakpoint} from './types'; -import type {GatekeeperService} from 'nuclide-commons-atom/types'; - -import idx from 'idx'; -import {observeRemovedHostnames} from 'nuclide-commons-atom/projects'; -import BreakpointManager from './BreakpointManager'; -import {AnalyticsEvents, DebuggerMode} from './constants'; -import BreakpointConfigComponent from './ui/BreakpointConfigComponent'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import {getLineForEvent} from './utils'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import invariant from 'assert'; -import {track} from 'nuclide-commons/analytics'; -import RemoteControlService from './RemoteControlService'; -import DebuggerUiModel from './DebuggerUiModel'; -import DebugService from './vsp/DebugService'; -import {debuggerDatatip} from './DebuggerDatatip'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import DebuggerLaunchAttachUI from './ui/DebuggerLaunchAttachUI'; -import {renderReactRoot} from 'nuclide-commons-ui/renderReactRoot'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import { - setNotificationService, - setConsoleService, - setConsoleRegisterExecutor, - setDatatipService, - setTerminalService, - setRpcService, - addDebugConfigurationProvider, -} from './AtomServiceContainer'; -import {wordAtPosition, trimRange} from 'nuclide-commons-atom/range'; -import DebuggerLayoutManager from './ui/DebuggerLayoutManager'; -import DebuggerPaneViewModel from './ui/DebuggerPaneViewModel'; -import DebuggerPaneContainerViewModel from './ui/DebuggerPaneContainerViewModel'; -import os from 'os'; -import nullthrows from 'nullthrows'; -import ReactMountRootElement from 'nuclide-commons-ui/ReactMountRootElement'; -import {sortMenuGroups} from 'nuclide-commons/menuUtils'; - const DATATIP_PACKAGE_NAME = 'debugger-datatip'; -type LaunchAttachDialogArgs = { - dialogMode: DebuggerConfigAction, - selectedTabName?: string, - config?: {[string]: mixed}, -}; - class Activation { - _disposables: UniversalDisposable; - _uiModel: DebuggerUiModel; - _breakpointManager: BreakpointManager; - _service: DebugService; - _layoutManager: DebuggerLayoutManager; - _selectedDebugConnection: ?string; - _visibleLaunchAttachDialogMode: ?DebuggerConfigAction; - _lauchAttachDialogCloser: ?() => void; - _connectionProviders: Map>; - - constructor(state: ?SerializedState) { - atom.views.addViewProvider(DebuggerPaneViewModel, createDebuggerView); - atom.views.addViewProvider( - DebuggerPaneContainerViewModel, - createDebuggerView, - ); - this._service = new DebugService(state); - this._uiModel = new DebuggerUiModel(this._service); - this._breakpointManager = new BreakpointManager(this._service); + constructor(state) { + atom.views.addViewProvider(_DebuggerPaneViewModel().default, createDebuggerView); + atom.views.addViewProvider(_DebuggerPaneContainerViewModel().default, createDebuggerView); + this._service = new (_DebugService().default)(state); + this._uiModel = new (_DebuggerUiModel().default)(this._service); + this._breakpointManager = new (_BreakpointManager().default)(this._service); this._selectedDebugConnection = null; this._visibleLaunchAttachDialogMode = null; this._lauchAttachDialogCloser = null; this._connectionProviders = new Map(); - this._layoutManager = new DebuggerLayoutManager(this._service, state); + this._layoutManager = new (_DebuggerLayoutManager().default)(this._service, state); // Manually manipulate the `Debugger` top level menu order. + + const insertIndex = atom.menu.template.findIndex(item => item.role === 'window' || item.role === 'help'); - // Manually manipulate the `Debugger` top level menu order. - const insertIndex = atom.menu.template.findIndex( - item => item.role === 'window' || item.role === 'help', - ); if (insertIndex !== -1) { - const deuggerIndex = atom.menu.template.findIndex( - item => item.label === 'Debugger', - ); + const deuggerIndex = atom.menu.template.findIndex(item => item.label === 'Debugger'); const menuItem = atom.menu.template.splice(deuggerIndex, 1)[0]; - const newIndex = - insertIndex > deuggerIndex ? insertIndex - 1 : insertIndex; + const newIndex = insertIndex > deuggerIndex ? insertIndex - 1 : insertIndex; atom.menu.template.splice(newIndex, 0, menuItem); atom.menu.update(); } - const removedHostnames = observeRemovedHostnames(); - - this._disposables = new UniversalDisposable( - this._layoutManager, - this._service, - this._uiModel, - this._breakpointManager, - removedHostnames.subscribe(hostname => { - const debuggerProcess = this._service.viewModel.focusedProcess; - if (debuggerProcess == null) { - return; // Nothing to do if we're not debugging. - } - const debuggeeTargetUri = debuggerProcess.configuration.targetUri; - if (nuclideUri.isLocal(debuggeeTargetUri)) { - return; // Nothing to do if our debug session is local. - } - if (nuclideUri.getHostname(debuggeeTargetUri) === hostname) { - this._service.stopProcess(); - } - }), - this._uiModel.onConnectionsUpdated(() => { - const newConnections = this._uiModel.getConnections(); - const keys = Array.from(this._connectionProviders.keys()); - - const removedConnections = keys.filter( - connection => - newConnections.find(item => item === connection) == null, - ); - const addedConnections = newConnections.filter( - connection => keys.find(item => item === connection) == null, - ); - - for (const key of removedConnections) { - this._connectionProviders.delete(key); - } + const removedHostnames = (0, _projects().observeRemovedHostnames)(); + this._disposables = new (_UniversalDisposable().default)(this._layoutManager, this._service, this._uiModel, this._breakpointManager, removedHostnames.subscribe(hostname => { + const debuggerProcess = this._service.viewModel.focusedProcess; + + if (debuggerProcess == null) { + return; // Nothing to do if we're not debugging. + } + + const debuggeeTargetUri = debuggerProcess.configuration.targetUri; + + if (_nuclideUri().default.isLocal(debuggeeTargetUri)) { + return; // Nothing to do if our debug session is local. + } + + if (_nuclideUri().default.getHostname(debuggeeTargetUri) === hostname) { + this._service.stopProcess(); + } + }), this._uiModel.onConnectionsUpdated(() => { + const newConnections = this._uiModel.getConnections(); + + const keys = Array.from(this._connectionProviders.keys()); + const removedConnections = keys.filter(connection => newConnections.find(item => item === connection) == null); + const addedConnections = newConnections.filter(connection => keys.find(item => item === connection) == null); + + for (const key of removedConnections) { + this._connectionProviders.delete(key); + } - for (const connection of addedConnections) { - this._setProvidersForConnection(connection); + for (const connection of addedConnections) { + this._setProvidersForConnection(connection); + } + }), this._uiModel.onProvidersUpdated(() => { + const connections = this._uiModel.getConnections(); + + for (const connection of connections) { + this._setProvidersForConnection(connection); + } + }), // Commands. + atom.commands.add('atom-workspace', { + 'debugger:show-attach-dialog': event => { + var _ref, _ref2; + + const selectedTabName = (_ref = event) != null ? (_ref = _ref.detail) != null ? _ref.selectedTabName : _ref : _ref; + const config = (_ref2 = event) != null ? (_ref2 = _ref2.detail) != null ? _ref2.config : _ref2 : _ref2; + + this._showLaunchAttachDialog({ + dialogMode: 'attach', + selectedTabName, + config + }); + } + }), atom.commands.add('atom-workspace', { + 'debugger:show-launch-dialog': event => { + var _event$detail, _event$detail2; + + const selectedTabName = event === null || event === void 0 ? void 0 : (_event$detail = event.detail) === null || _event$detail === void 0 ? void 0 : _event$detail.selectedTabName; + const config = event === null || event === void 0 ? void 0 : (_event$detail2 = event.detail) === null || _event$detail2 === void 0 ? void 0 : _event$detail2.config; + + this._showLaunchAttachDialog({ + dialogMode: 'launch', + selectedTabName, + config + }); + } + }), atom.commands.add('atom-workspace', { + 'debugger:continue-debugging': this._continue.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:stop-debugging': this._stop.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:restart-debugging': this._restart.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:step-over': this._stepOver.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:step-into': this._stepInto.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:step-out': this._stepOut.bind(this) + }), atom.commands.add('atom-workspace', { + // eslint-disable-next-line nuclide-internal/atom-apis + 'debugger:add-breakpoint': this._addBreakpoint.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:toggle-breakpoint': this._toggleBreakpoint.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:toggle-breakpoint-enabled': this._toggleBreakpointEnabled.bind(this) + }), atom.commands.add('atom-workspace', { + // eslint-disable-next-line nuclide-internal/atom-apis + 'debugger:edit-breakpoint': this._configureBreakpoint.bind(this) + }), atom.commands.add('.debugger-thread-list-item', { + 'debugger:terminate-thread': this._terminateThread.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:remove-all-breakpoints': this._deleteAllBreakpoints.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:enable-all-breakpoints': this._enableAllBreakpoints.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:disable-all-breakpoints': this._disableAllBreakpoints.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:remove-breakpoint': this._deleteBreakpoint.bind(this) + }), atom.commands.add('atom-workspace', { + // eslint-disable-next-line nuclide-internal/atom-apis + 'debugger:add-to-watch': this._addToWatch.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:run-to-location': this._runToLocation.bind(this) + }), atom.commands.add('.debugger-expression-value-list', { + 'debugger:copy-debugger-expression-value': this._copyDebuggerExpressionValue.bind(this) + }), atom.commands.add('atom-workspace', { + 'debugger:copy-debugger-callstack': this._copyDebuggerCallstack.bind(this) + }), // Context Menu Items. + atom.contextMenu.add({ + '.debugger-breakpoint-list': [{ + label: 'Enable All Breakpoints', + command: 'debugger:enable-all-breakpoints' + }, { + label: 'Disable All Breakpoints', + command: 'debugger:disable-all-breakpoints' + }, { + label: 'Remove All Breakpoints', + command: 'debugger:remove-all-breakpoints' + }, { + type: 'separator' + }], + '.debugger-breakpoint': [{ + label: 'Edit breakpoint...', + command: 'debugger:edit-breakpoint', + shouldDisplay: event => { + const bp = this._getBreakpointFromEvent(event); + + return bp != null && this._supportsConditionalBreakpoints(); } - }), - this._uiModel.onProvidersUpdated(() => { - const connections = this._uiModel.getConnections(); - for (const connection of connections) { - this._setProvidersForConnection(connection); + }, { + label: 'Remove Breakpoint', + command: 'debugger:remove-breakpoint' + }, { + type: 'separator' + }], + '.debugger-thread-list-item': [{ + label: 'Terminate thread', + command: 'debugger:terminate-thread', + shouldDisplay: event => { + const target = event.target; + + if (target.dataset.threadid) { + const threadId = parseInt(target.dataset.threadid, 10); + + if (!Number.isNaN(threadId)) { + return this._supportsTerminateThreadsRequest(); + } + } + + return false; } - }), - // Commands. - atom.commands.add('atom-workspace', { - 'debugger:show-attach-dialog': event => { - const selectedTabName: any = idx( - event, - _ => _.detail.selectedTabName, - ); - const config: any = idx(event, _ => _.detail.config); - this._showLaunchAttachDialog({ - dialogMode: 'attach', - selectedTabName, - config, - }); - }, - }), - atom.commands.add('atom-workspace', { - 'debugger:show-launch-dialog': event => { - const selectedTabName: any = event?.detail?.selectedTabName; - const config: any = event?.detail?.config; - this._showLaunchAttachDialog({ - dialogMode: 'launch', - selectedTabName, - config, - }); - }, - }), - atom.commands.add('atom-workspace', { - 'debugger:continue-debugging': this._continue.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:stop-debugging': this._stop.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:restart-debugging': this._restart.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:step-over': this._stepOver.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:step-into': this._stepInto.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:step-out': this._stepOut.bind(this), - }), - atom.commands.add('atom-workspace', { - // eslint-disable-next-line nuclide-internal/atom-apis - 'debugger:add-breakpoint': this._addBreakpoint.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:toggle-breakpoint': this._toggleBreakpoint.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:toggle-breakpoint-enabled': this._toggleBreakpointEnabled.bind( - this, - ), - }), - atom.commands.add('atom-workspace', { - // eslint-disable-next-line nuclide-internal/atom-apis - 'debugger:edit-breakpoint': this._configureBreakpoint.bind(this), - }), - atom.commands.add('.debugger-thread-list-item', { - 'debugger:terminate-thread': this._terminateThread.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:remove-all-breakpoints': this._deleteAllBreakpoints.bind( - this, - ), - }), - atom.commands.add('atom-workspace', { - 'debugger:enable-all-breakpoints': this._enableAllBreakpoints.bind( - this, - ), - }), - atom.commands.add('atom-workspace', { - 'debugger:disable-all-breakpoints': this._disableAllBreakpoints.bind( - this, - ), - }), - atom.commands.add('atom-workspace', { - 'debugger:remove-breakpoint': this._deleteBreakpoint.bind(this), - }), - atom.commands.add('atom-workspace', { - // eslint-disable-next-line nuclide-internal/atom-apis - 'debugger:add-to-watch': this._addToWatch.bind(this), - }), - atom.commands.add('atom-workspace', { - 'debugger:run-to-location': this._runToLocation.bind(this), - }), - atom.commands.add('.debugger-expression-value-list', { - 'debugger:copy-debugger-expression-value': this._copyDebuggerExpressionValue.bind( - this, - ), - }), - atom.commands.add('atom-workspace', { - 'debugger:copy-debugger-callstack': this._copyDebuggerCallstack.bind( - this, - ), - }), - // Context Menu Items. - atom.contextMenu.add({ - '.debugger-breakpoint-list': [ - { - label: 'Enable All Breakpoints', - command: 'debugger:enable-all-breakpoints', - }, - { - label: 'Disable All Breakpoints', - command: 'debugger:disable-all-breakpoints', - }, - { - label: 'Remove All Breakpoints', - command: 'debugger:remove-all-breakpoints', - }, - {type: 'separator'}, - ], - '.debugger-breakpoint': [ - { - label: 'Edit breakpoint...', - command: 'debugger:edit-breakpoint', - shouldDisplay: event => { - const bp = this._getBreakpointFromEvent(event); - return bp != null && this._supportsConditionalBreakpoints(); - }, - }, - { - label: 'Remove Breakpoint', - command: 'debugger:remove-breakpoint', - }, - {type: 'separator'}, - ], - '.debugger-thread-list-item': [ - { - label: 'Terminate thread', - command: 'debugger:terminate-thread', - shouldDisplay: event => { - const target: HTMLElement = event.target; - if (target.dataset.threadid) { - const threadId = parseInt(target.dataset.threadid, 10); - if (!Number.isNaN(threadId)) { - return this._supportsTerminateThreadsRequest(); - } - } + }], + '.debugger-callstack-table': [{ + label: 'Copy Callstack', + command: 'debugger:copy-debugger-callstack' + }], + '.debugger-expression-value-list': [{ + label: 'Copy', + command: 'debugger:copy-debugger-expression-value' + }], + 'atom-text-editor': [{ + type: 'separator' + }, { + label: 'Debugger', + submenu: [{ + label: 'Toggle Breakpoint', + command: 'debugger:toggle-breakpoint' + }, { + label: 'Toggle Breakpoint enabled/disabled', + command: 'debugger:toggle-breakpoint-enabled', + shouldDisplay: event => this._executeWithEditorPath(event, (filePath, line) => this._service.getModel().getBreakpointAtLine(filePath, line) != null) || false + }, { + label: 'Edit Breakpoint...', + command: 'debugger:edit-breakpoint', + shouldDisplay: event => this._executeWithEditorPath(event, (filePath, line) => { + const bp = this._service.getModel().getBreakpointAtLine(filePath, line); + + return bp != null && this._supportsConditionalBreakpoints(); + }) || false + }, { + label: 'Add to Watch', + command: 'debugger:add-to-watch', + shouldDisplay: event => { + const textEditor = atom.workspace.getActiveTextEditor(); + + if (this._service.getDebuggerMode() === _constants().DebuggerMode.STOPPED || textEditor == null) { return false; - }, - }, - ], - '.debugger-callstack-table': [ - { - label: 'Copy Callstack', - command: 'debugger:copy-debugger-callstack', - }, - ], - '.debugger-expression-value-list': [ - { - label: 'Copy', - command: 'debugger:copy-debugger-expression-value', - }, - ], - 'atom-text-editor': [ - {type: 'separator'}, - { - label: 'Debugger', - submenu: [ - { - label: 'Toggle Breakpoint', - command: 'debugger:toggle-breakpoint', - }, - { - label: 'Toggle Breakpoint enabled/disabled', - command: 'debugger:toggle-breakpoint-enabled', - shouldDisplay: event => - this._executeWithEditorPath( - event, - (filePath, line) => - this._service - .getModel() - .getBreakpointAtLine(filePath, line) != null, - ) || false, - }, - { - label: 'Edit Breakpoint...', - command: 'debugger:edit-breakpoint', - shouldDisplay: event => - this._executeWithEditorPath(event, (filePath, line) => { - const bp = this._service - .getModel() - .getBreakpointAtLine(filePath, line); - return bp != null && this._supportsConditionalBreakpoints(); - }) || false, - }, - { - label: 'Add to Watch', - command: 'debugger:add-to-watch', - shouldDisplay: event => { - const textEditor = atom.workspace.getActiveTextEditor(); - if ( - this._service.getDebuggerMode() === DebuggerMode.STOPPED || - textEditor == null - ) { - return false; - } - return ( - textEditor.getSelections().length === 1 && - !textEditor.getSelectedBufferRange().isEmpty() - ); - }, - }, - { - label: 'Run to Location', - command: 'debugger:run-to-location', - shouldDisplay: event => - this._service.getDebuggerMode() === DebuggerMode.PAUSED, - }, - ], - }, - {type: 'separator'}, - ], - }), - this._registerCommandsContextMenuAndOpener(), - ); - - sortMenuGroups(['Debugger']); - } - - _supportsConditionalBreakpoints(): boolean { + } + + return textEditor.getSelections().length === 1 && !textEditor.getSelectedBufferRange().isEmpty(); + } + }, { + label: 'Run to Location', + command: 'debugger:run-to-location', + shouldDisplay: event => this._service.getDebuggerMode() === _constants().DebuggerMode.PAUSED + }] + }, { + type: 'separator' + }] + }), this._registerCommandsContextMenuAndOpener()); + (0, _menuUtils().sortMenuGroups)(['Debugger']); + } + + _supportsConditionalBreakpoints() { // If currently debugging, return whether or not the current debugger supports this. - const {focusedProcess} = this._service.viewModel; + const { + focusedProcess + } = this._service.viewModel; + if (focusedProcess == null) { // If not currently debugging, return if any of the debuggers that support // the file extension this bp is in support conditions. // TODO(ericblue): have providers register their file extensions and filter correctly here. return true; } else { - return Boolean( - focusedProcess.session.capabilities.supportsConditionalBreakpoints, - ); + return Boolean(focusedProcess.session.capabilities.supportsConditionalBreakpoints); } } - _supportsTerminateThreadsRequest(): boolean { + _supportsTerminateThreadsRequest() { // If currently debugging, return whether or not the current debugger supports this. - const {focusedProcess} = this._service.viewModel; + const { + focusedProcess + } = this._service.viewModel; + if (focusedProcess == null) { return false; } else { - return Boolean( - focusedProcess.session.capabilities.supportsTerminateThreadsRequest, - ); + return Boolean(focusedProcess.session.capabilities.supportsTerminateThreadsRequest); } } - _setProvidersForConnection(connection: NuclideUri): void { - const key = nuclideUri.isRemote(connection) - ? nuclideUri.getHostname(connection) - : 'local'; - const availableProviders = this._uiModel.getLaunchAttachProvidersForConnection( - connection, - ); + _setProvidersForConnection(connection) { + const key = _nuclideUri().default.isRemote(connection) ? _nuclideUri().default.getHostname(connection) : 'local'; + + const availableProviders = this._uiModel.getLaunchAttachProvidersForConnection(connection); + this._connectionProviders.set(key, availableProviders); } - async _getSuggestions( - request: atom$AutocompleteRequest, - ): Promise> { + async _getSuggestions(request) { let text = request.editor.getText(); const lines = text.split('\n'); - const {row} = request.bufferPosition; - // Only keep the lines up to and including the buffer position row. + const { + row + } = request.bufferPosition; // Only keep the lines up to and including the buffer position row. + text = lines.slice(0, row + 1).join('\n'); - const {focusedStackFrame, focusedProcess} = this._service.viewModel; + const { + focusedStackFrame, + focusedProcess + } = this._service.viewModel; + if (focusedProcess == null || focusedStackFrame == null) { return []; - } else if ( - !Boolean(focusedProcess.session.capabilities.supportsCompletionsRequest) - ) { + } else if (!Boolean(focusedProcess.session.capabilities.supportsCompletionsRequest)) { const scopes = await focusedStackFrame.getScopes(); - return scopes.map(scope => ({text: scope.name, type: 'variable'})); + return scopes.map(scope => ({ + text: scope.name, + type: 'variable' + })); } else { - const completions = await focusedProcess.completions( - focusedStackFrame.frameId, - text, - request.bufferPosition, - 0, - ); + const completions = await focusedProcess.completions(focusedStackFrame.frameId, text, request.bufferPosition, 0); return completions.map(item => ({ displayText: item.label, text: item.text == null ? item.label : item.text, - type: item.type, + type: item.type })); } } - serialize(): SerializedState { + serialize() { const model = this._service.getModel(); + const state = { sourceBreakpoints: model.getBreakpoints(), functionBreakpoints: model.getFunctionBreakpoints(), exceptionBreakpoints: model.getExceptionBreakpoints(), watchExpressions: model.getWatchExpressions().map(e => e.name), showDebugger: this._layoutManager.isDebuggerVisible(), - workspaceDocksVisibility: this._layoutManager.getWorkspaceDocksVisibility(), + workspaceDocksVisibility: this._layoutManager.getWorkspaceDocksVisibility() }; return state; } @@ -468,81 +563,65 @@ class Activation { this._disposables.dispose(); } - consumeGatekeeperService(service: GatekeeperService): IDisposable { + consumeGatekeeperService(service) { const disposable = this._layoutManager.consumeGatekeeperService(service); + disposable.add(this._service.consumeGatekeeperService(service)); return disposable; } - _registerCommandsContextMenuAndOpener(): UniversalDisposable { - const disposable = new UniversalDisposable( - atom.workspace.addOpener(uri => { - return this._layoutManager.getModelForDebuggerUri(uri); - }), - () => { - this._layoutManager.hideDebuggerViews(false); - }, - atom.commands.add('atom-workspace', { - 'debugger:show': event => { - const detail = event.detail; - const show = - detail == null || - Boolean(detail.showOnlyIfHidden) === false || - !this._layoutManager.isDebuggerVisible(); - if (show) { - this._layoutManager.showDebuggerViews(); - } - }, - }), - atom.commands.add('atom-workspace', { - 'debugger:hide': () => { - this._layoutManager.hideDebuggerViews(false); - this._service.stopProcess(); - }, - }), - atom.commands.add('atom-workspace', 'debugger:toggle', () => { - if (this._layoutManager.isDebuggerVisible() === true) { - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'debugger:hide', - ); - } else { - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'debugger:show', - ); + _registerCommandsContextMenuAndOpener() { + const disposable = new (_UniversalDisposable().default)(atom.workspace.addOpener(uri => { + return this._layoutManager.getModelForDebuggerUri(uri); + }), () => { + this._layoutManager.hideDebuggerViews(false); + }, atom.commands.add('atom-workspace', { + 'debugger:show': event => { + const detail = event.detail; + const show = detail == null || Boolean(detail.showOnlyIfHidden) === false || !this._layoutManager.isDebuggerVisible(); + + if (show) { + this._layoutManager.showDebuggerViews(); } - }), - this._service.onDidChangeMode(() => - this._layoutManager.debuggerModeChanged(), - ), - atom.commands.add('atom-workspace', { - 'debugger:reset-layout': () => { - this._layoutManager.resetLayout(); - }, - }), - atom.contextMenu.add({ - '.debugger-container': [ - { - label: 'Debugger Views', - submenu: [ - { - label: 'Reset Layout', - command: 'debugger:reset-layout', - }, - ], - }, - ], - }), - ); + } + }), atom.commands.add('atom-workspace', { + 'debugger:hide': () => { + this._layoutManager.hideDebuggerViews(false); + + this._service.stopProcess(); + } + }), atom.commands.add('atom-workspace', 'debugger:toggle', () => { + if (this._layoutManager.isDebuggerVisible() === true) { + atom.commands.dispatch(atom.views.getView(atom.workspace), 'debugger:hide'); + } else { + atom.commands.dispatch(atom.views.getView(atom.workspace), 'debugger:show'); + } + }), this._service.onDidChangeMode(() => this._layoutManager.debuggerModeChanged()), atom.commands.add('atom-workspace', { + 'debugger:reset-layout': () => { + this._layoutManager.resetLayout(); + } + }), atom.contextMenu.add({ + '.debugger-container': [{ + label: 'Debugger Views', + submenu: [{ + label: 'Reset Layout', + command: 'debugger:reset-layout' + }] + }] + })); + this._layoutManager.registerContextMenus(); + return disposable; } _continue() { - const {focusedThread} = this._service.viewModel; + const { + focusedThread + } = this._service.viewModel; + if (focusedThread != null) { - track(AnalyticsEvents.DEBUGGER_STEP_CONTINUE); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_CONTINUE); focusedThread.continue(); } } @@ -556,42 +635,51 @@ class Activation { } _stepOver() { - const {focusedThread} = this._service.viewModel; + const { + focusedThread + } = this._service.viewModel; + if (focusedThread != null) { - track(AnalyticsEvents.DEBUGGER_STEP_OVER); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_OVER); focusedThread.next(); } } _stepInto() { - const {focusedThread} = this._service.viewModel; + const { + focusedThread + } = this._service.viewModel; + if (focusedThread != null) { - track(AnalyticsEvents.DEBUGGER_STEP_INTO); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_INTO); focusedThread.stepIn(); } } _stepOut() { - const {focusedThread} = this._service.viewModel; + const { + focusedThread + } = this._service.viewModel; + if (focusedThread != null) { - track(AnalyticsEvents.DEBUGGER_STEP_OUT); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_OUT); focusedThread.stepOut(); } } - _addBreakpoint(event: any) { + _addBreakpoint(event) { return this._executeWithEditorPath(event, (filePath, lineNumber) => { this._service.addSourceBreakpoint(filePath, lineNumber); }); } - _toggleBreakpoint(event: any) { + _toggleBreakpoint(event) { return this._executeWithEditorPath(event, (filePath, lineNumber) => { this._service.toggleSourceBreakpoint(filePath, lineNumber); }); } - _toggleBreakpointEnabled(event: any) { + _toggleBreakpointEnabled(event) { this._executeWithEditorPath(event, (filePath, line) => { const bp = this._service.getModel().getBreakpointAtLine(filePath, line); @@ -601,9 +689,10 @@ class Activation { }); } - _getBreakpointFromEvent(event: any): ?IBreakpoint { - const target: HTMLElement = event.target; + _getBreakpointFromEvent(event) { + const target = event.target; let bp = null; + if (target != null && target.dataset != null) { if (target.dataset.bpid != null) { const bpId = target.dataset.bpid; @@ -613,6 +702,7 @@ class Activation { if (bp == null) { const path = target.dataset.path; const line = parseInt(target.dataset.line, 10); + if (path != null && line != null) { bp = this._service.getModel().getBreakpointAtLine(path, line); } @@ -622,180 +712,172 @@ class Activation { return bp; } - _configureBreakpoint(event: any) { + _configureBreakpoint(event) { const bp = this._getBreakpointFromEvent(event); + if (bp != null && this._supportsConditionalBreakpoints()) { // Open the configuration dialog. - const container = new ReactMountRootElement(); - ReactDOM.render( - { - ReactDOM.unmountComponentAtNode(container); - }} - />, - container, - ); + const container = new (_ReactMountRootElement().default)(); + + _reactDom.default.render(React.createElement(_BreakpointConfigComponent().default, { + breakpoint: bp, + service: this._service, + onDismiss: () => { + _reactDom.default.unmountComponentAtNode(container); + } + }), container); } } - _terminateThread(event: any) { - const target: HTMLElement = event.target; + _terminateThread(event) { + const target = event.target; + if (target.dataset.threadid) { const threadId = parseInt(target.dataset.threadid, 10); + if (!Number.isNaN(threadId) && this._supportsTerminateThreadsRequest()) { this._service.terminateThreads([threadId]); } } } - _executeWithEditorPath( - event: any, - fn: (filePath: string, line: number) => T, - ): ?T { + _executeWithEditorPath(event, fn) { const editor = atom.workspace.getActiveTextEditor(); + if (!editor || !editor.getPath()) { return null; } - const line = getLineForEvent(editor, event) + 1; - return fn(nullthrows(editor.getPath()), line); + const line = (0, _utils().getLineForEvent)(editor, event) + 1; + return fn((0, _nullthrows().default)(editor.getPath()), line); } - _deleteBreakpoint(event: any): void { + _deleteBreakpoint(event) { const breakpoint = this._getBreakpointFromEvent(event); + if (breakpoint != null) { this._service.removeBreakpoints(breakpoint.getId()); } } - _deleteAllBreakpoints(): void { + _deleteAllBreakpoints() { this._service.removeBreakpoints(); } - _enableAllBreakpoints(): void { + _enableAllBreakpoints() { this._service.enableOrDisableBreakpoints(true); } - _disableAllBreakpoints(): void { + _disableAllBreakpoints() { this._service.enableOrDisableBreakpoints(false); } - _renderConfigDialog( - panel: atom$Panel, - args: LaunchAttachDialogArgs, - dialogCloser: () => void, - ): void { + _renderConfigDialog(panel, args, dialogCloser) { if (this._selectedDebugConnection == null) { // If no connection is selected yet, default to the local connection. this._selectedDebugConnection = 'local'; } - invariant(this._selectedDebugConnection != null); - - const options = this._uiModel - .getConnections() - .map(connection => { - const displayName = nuclideUri.isRemote(connection) - ? nuclideUri.getHostname(connection) - : 'localhost'; - return { - value: connection, - label: displayName, - }; - }) - .filter(item => item.value != null && item.value !== '') - .sort((a, b) => a.label.localeCompare(b.label)); - - // flowlint-next-line sketchy-null-string:off + if (!(this._selectedDebugConnection != null)) { + throw new Error("Invariant violation: \"this._selectedDebugConnection != null\""); + } + + const options = this._uiModel.getConnections().map(connection => { + const displayName = _nuclideUri().default.isRemote(connection) ? _nuclideUri().default.getHostname(connection) : 'localhost'; + return { + value: connection, + label: displayName + }; + }).filter(item => item.value != null && item.value !== '').sort((a, b) => a.label.localeCompare(b.label)); // flowlint-next-line sketchy-null-string:off + + const connection = this._selectedDebugConnection || 'local'; - ReactDOM.render( - { - this._selectedDebugConnection = newValue; - this._renderConfigDialog( - panel, - {dialogMode: args.dialogMode}, - dialogCloser, - ); - }} - connection={connection} - connectionOptions={options} - dialogCloser={dialogCloser} - providers={this._connectionProviders} - />, - panel.getItem(), - ); - } - - _showLaunchAttachDialog(args: LaunchAttachDialogArgs): void { - const {dialogMode} = args; - if ( - this._visibleLaunchAttachDialogMode != null && - this._visibleLaunchAttachDialogMode !== dialogMode - ) { + _reactDom.default.render(React.createElement(_DebuggerLaunchAttachUI().default, { + dialogMode: args.dialogMode, + initialSelectedTabName: args.selectedTabName, + initialProviderConfig: args.config, + connectionChanged: newValue => { + this._selectedDebugConnection = newValue; + + this._renderConfigDialog(panel, { + dialogMode: args.dialogMode + }, dialogCloser); + }, + connection: connection, + connectionOptions: options, + dialogCloser: dialogCloser, + providers: this._connectionProviders + }), panel.getItem()); + } + + _showLaunchAttachDialog(args) { + const { + dialogMode + } = args; + + if (this._visibleLaunchAttachDialogMode != null && this._visibleLaunchAttachDialogMode !== dialogMode) { // If the dialog is already visible, but isn't the correct mode, close it before // re-opening the correct mode. - invariant(this._lauchAttachDialogCloser != null); + if (!(this._lauchAttachDialogCloser != null)) { + throw new Error("Invariant violation: \"this._lauchAttachDialogCloser != null\""); + } + this._lauchAttachDialogCloser(); } - const disposables = new UniversalDisposable(); + const disposables = new (_UniversalDisposable().default)(); const hostEl = document.createElement('div'); const pane = atom.workspace.addModalPanel({ item: hostEl, - className: 'debugger-config-dialog', + className: 'debugger-config-dialog' }); + const parentEl = hostEl.parentElement; + parentEl.style.maxWidth = '100em'; // Function callback that closes the dialog and frees all of its resources. - const parentEl: HTMLElement = (hostEl.parentElement: any); - parentEl.style.maxWidth = '100em'; - - // Function callback that closes the dialog and frees all of its resources. this._renderConfigDialog(pane, args, () => disposables.dispose()); + this._lauchAttachDialogCloser = () => disposables.dispose(); - disposables.add( - pane.onDidChangeVisible(visible => { - if (!visible) { - disposables.dispose(); - } - }), - ); + + disposables.add(pane.onDidChangeVisible(visible => { + if (!visible) { + disposables.dispose(); + } + })); disposables.add(() => { this._disposables.remove(disposables); + this._visibleLaunchAttachDialogMode = null; this._lauchAttachDialogCloser = null; - track(AnalyticsEvents.DEBUGGER_TOGGLE_ATTACH_DIALOG, { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_TOGGLE_ATTACH_DIALOG, { visible: false, - dialogMode, + dialogMode }); - ReactDOM.unmountComponentAtNode(hostEl); + + _reactDom.default.unmountComponentAtNode(hostEl); + pane.destroy(); }); - - track(AnalyticsEvents.DEBUGGER_TOGGLE_ATTACH_DIALOG, { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_TOGGLE_ATTACH_DIALOG, { visible: true, - dialogMode, + dialogMode }); this._visibleLaunchAttachDialogMode = dialogMode; + this._disposables.add(disposables); } _addToWatch() { const editor = atom.workspace.getActiveTextEditor(); + if (!editor) { return; } - const selectedText = editor.getTextInBufferRange( - trimRange(editor, editor.getSelectedBufferRange()), - ); - const expr = wordAtPosition(editor, editor.getCursorBufferPosition()); - const watchExpression = selectedText || (expr && expr.wordMatch[0]); + const selectedText = editor.getTextInBufferRange((0, _range().trimRange)(editor, editor.getSelectedBufferRange())); + const expr = (0, _range().wordAtPosition)(editor, editor.getCursorBufferPosition()); + const watchExpression = selectedText || expr && expr.wordMatch[0]; + if (watchExpression != null && watchExpression.length > 0) { this._service.addWatchExpression(watchExpression); } @@ -807,162 +889,158 @@ class Activation { }); } - _copyDebuggerExpressionValue(event: Event) { - const clickedElement: HTMLElement = (event.target: any); + _copyDebuggerExpressionValue(event) { + const clickedElement = event.target; const copyElement = clickedElement.closest('.nuclide-ui-lazy-nested-value'); + if (copyElement != null) { atom.clipboard.write(copyElement.textContent); } } - _copyDebuggerCallstack(event: Event) { - const {focusedThread} = this._service.viewModel; + _copyDebuggerCallstack(event) { + const { + focusedThread + } = this._service.viewModel; + if (focusedThread != null) { let callstackText = ''; focusedThread.getCallStack().forEach((item, i) => { - const path = nuclideUri.basename(item.source.uri); - callstackText += `${i}\t${item.name}\t${path}:${item.range.start.row}${ - os.EOL - }`; - }); + const path = _nuclideUri().default.basename(item.source.uri); + callstackText += `${i}\t${item.name}\t${path}:${item.range.start.row}${_os.default.EOL}`; + }); atom.clipboard.write(callstackText.trim()); } } - consumeCurrentWorkingDirectory(cwdApi: nuclide$CwdApi): IDisposable { + consumeCurrentWorkingDirectory(cwdApi) { const updateSelectedConnection = directory => { this._selectedDebugConnection = directory; + if (this._selectedDebugConnection != null) { const conn = this._selectedDebugConnection; - if (nuclideUri.isRemote(conn)) { + + if (_nuclideUri().default.isRemote(conn)) { // Use root instead of current directory as launch point for debugger. - this._selectedDebugConnection = nuclideUri.createRemoteUri( - nuclideUri.getHostname(conn), - '/', - ); + this._selectedDebugConnection = _nuclideUri().default.createRemoteUri(_nuclideUri().default.getHostname(conn), '/'); } else { // Use null instead of local path to use local debugger downstream. this._selectedDebugConnection = null; } } }; + const disposable = cwdApi.observeCwd(updateSelectedConnection); + this._disposables.add(disposable); - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { disposable.dispose(); + this._disposables.remove(disposable); }); } - createAutocompleteProvider(): atom$AutocompleteProvider { + createAutocompleteProvider() { return { labels: ['nuclide-console'], selector: '*', filterSuggestions: true, - getSuggestions: this._getSuggestions.bind(this), + getSuggestions: this._getSuggestions.bind(this) }; } - consumeConsole(createConsole: ConsoleService): IDisposable { - return setConsoleService(createConsole); + consumeConsole(createConsole) { + return (0, _AtomServiceContainer().setConsoleService)(createConsole); } - consumeTerminal(terminalApi: TerminalApi): IDisposable { - return setTerminalService(terminalApi); + consumeTerminal(terminalApi) { + return (0, _AtomServiceContainer().setTerminalService)(terminalApi); } - consumeRpcService(rpcService: nuclide$RpcService): IDisposable { - return setRpcService(rpcService); + consumeRpcService(rpcService) { + return (0, _AtomServiceContainer().setRpcService)(rpcService); } - consumeRegisterExecutor( - registerExecutor: RegisterExecutorFunction, - ): IDisposable { - return setConsoleRegisterExecutor(registerExecutor); + consumeRegisterExecutor(registerExecutor) { + return (0, _AtomServiceContainer().setConsoleRegisterExecutor)(registerExecutor); } - consumeDebuggerProvider(provider: NuclideDebuggerProvider): IDisposable { + consumeDebuggerProvider(provider) { this._uiModel.addDebuggerProvider(provider); - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { this._uiModel.removeDebuggerProvider(provider); }); } - consumeDebuggerConfigurationProviders( - providers: Array, - ): IDisposable { - invariant(Array.isArray(providers)); - const disposable = new UniversalDisposable(); - providers.forEach(provider => - disposable.add(addDebugConfigurationProvider(provider)), - ); + consumeDebuggerConfigurationProviders(providers) { + if (!Array.isArray(providers)) { + throw new Error("Invariant violation: \"Array.isArray(providers)\""); + } + + const disposable = new (_UniversalDisposable().default)(); + providers.forEach(provider => disposable.add((0, _AtomServiceContainer().addDebugConfigurationProvider)(provider))); return disposable; } - consumeToolBar(getToolBar: toolbar$GetToolbar): IDisposable { + consumeToolBar(getToolBar) { const toolBar = getToolBar('debugger'); toolBar.addButton({ iconset: 'icon-nuclicon', icon: 'debugger', callback: 'debugger:show-attach-dialog', tooltip: 'Attach Debugger', - priority: 500, + priority: 500 }).element; - const disposable = new UniversalDisposable(() => { + const disposable = new (_UniversalDisposable().default)(() => { toolBar.removeItems(); }); + this._disposables.add(disposable); + return disposable; } - consumeNotifications( - raiseNativeNotification: ( - title: string, - body: string, - timeout: number, - raiseIfAtomHasFocus: boolean, - ) => ?IDisposable, - ): void { - setNotificationService(raiseNativeNotification); + consumeNotifications(raiseNativeNotification) { + (0, _AtomServiceContainer().setNotificationService)(raiseNativeNotification); } - provideRemoteControlService(): RemoteControlService { - return new RemoteControlService(this._service); + provideRemoteControlService() { + return new (_RemoteControlService().default)(this._service); } - consumeDatatipService(service: DatatipService): IDisposable { - const disposable = new UniversalDisposable( - service.addProvider(this._createDatatipProvider()), - setDatatipService(service), - ); + consumeDatatipService(service) { + const disposable = new (_UniversalDisposable().default)(service.addProvider(this._createDatatipProvider()), (0, _AtomServiceContainer().setDatatipService)(service)); + this._disposables.add(disposable); + return disposable; } - _createDatatipProvider(): DatatipProvider { + _createDatatipProvider() { return { // Eligibility is determined online, based on registered EvaluationExpression providers. providerName: DATATIP_PACKAGE_NAME, priority: 1, - datatip: (editor: TextEditor, position: atom$Point) => { - return debuggerDatatip(this._service, editor, position); - }, + datatip: (editor, position) => { + return (0, _DebuggerDatatip().debuggerDatatip)(this._service, editor, position); + } }; } + } -function createDebuggerView(model: mixed): ?HTMLElement { +function createDebuggerView(model) { let view = null; - if ( - model instanceof DebuggerPaneViewModel || - model instanceof DebuggerPaneContainerViewModel - ) { + + if (model instanceof _DebuggerPaneViewModel().default || model instanceof _DebuggerPaneContainerViewModel().default) { view = model.createView(); } if (view != null) { - const elem = renderReactRoot(view); + const elem = (0, _renderReactRoot().renderReactRoot)(view); elem.className = 'debugger-container'; return elem; } @@ -970,4 +1048,4 @@ function createDebuggerView(model: mixed): ?HTMLElement { return null; } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/types.js index fbefc931..ae037529 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/types.js @@ -1,502 +1,13 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +"use strict"; -/** - * The following interfaces models a debug service and data model layer built on top of - * VSCode debugger protocol and were modeled after VSCode's debugger implementation - * in https://github.com/Microsoft/vscode/tree/master/src/vs/workbench/parts/debug +function DebugProtocol() { + const data = _interopRequireWildcard(require("vscode-debugprotocol")); -MIT License + DebugProtocol = function () { + return data; + }; -Copyright (c) 2015 - present Microsoft Corporation - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import type {Observable} from 'rxjs'; -import * as DebugProtocol from 'vscode-debugprotocol'; -import type {IProcessConfig, IVspInstance} from 'nuclide-debugger-common'; - -export interface RemoteDebuggerService { - startVspDebugging(config: IProcessConfig): Promise; - onDidStartDebugSession( - callback: (config: IProcessConfig) => mixed, - ): IDisposable; -} - -export interface ITreeElement { - getId(): string; -} - -export interface ISource { - available: boolean; - +name: ?string; - +uri: string; - +origin: ?string; - +presentationHint: ?SourcePresentationHint; - +raw: DebugProtocol.Source; - +reference: ?number; - +inMemory: boolean; - openInEditor(): Promise; -} - -export type SourcePresentationHint = 'normal' | 'emphasize' | 'deemphasize'; - -export interface IExpressionContainer extends ITreeElement { - hasChildren(): boolean; - getChildren(): Promise>; -} - -export interface IExpression extends IExpressionContainer { - available: boolean; - name: string; - getValue(): string; - +type: ?string; - toString(): string; -} - -export type ContextType = 'hover' | 'watch' | 'repl'; - -export interface IEvaluatableExpression extends IExpression { - evaluate( - process: ?IProcess, - stackFrame: ?IStackFrame, - context: ContextType, - ): Promise; -} - -export interface IVariable extends IExpression { - setVariable(newValue: string): Promise; -} - -export interface ISession { - stackTrace( - args: DebugProtocol.StackTraceArguments, - ): Promise; - exceptionInfo( - args: DebugProtocol.ExceptionInfoArguments, - ): Promise; - scopes( - args: DebugProtocol.ScopesArguments, - ): Promise; - variables( - args: DebugProtocol.VariablesArguments, - ): Promise; - evaluate( - args: DebugProtocol.EvaluateArguments, - ): Promise; - capabilities: DebugProtocol.Capabilities; - disconnect(restart?: boolean, force?: boolean): Promise; - custom(request: string, args: any): Promise; - observeInitializeEvents(): Observable; - observeCustomEvents(): Observable; - observeStopEvents(): Observable; - restartFrame( - args: DebugProtocol.RestartFrameArguments, - threadId: number, - ): Promise; - next(args: DebugProtocol.NextArguments): Promise; - stepIn( - args: DebugProtocol.StepInArguments, - ): Promise; - stepOut( - args: DebugProtocol.StepOutArguments, - ): Promise; - continue( - args: DebugProtocol.ContinueArguments, - ): Promise; - pause( - args: DebugProtocol.PauseArguments, - ): Promise; - stepBack( - args: DebugProtocol.StepBackArguments, - ): Promise; - reverseContinue( - args: DebugProtocol.ReverseContinueArguments, - ): Promise; - completions( - args: DebugProtocol.CompletionsArguments, - ): Promise; - setVariable( - args: DebugProtocol.SetVariableArguments, - ): Promise; - source( - args: DebugProtocol.SourceArguments, - ): Promise; -} - -export interface IThread extends ITreeElement { - /** - * Process the thread belongs to - */ - +process: IProcess; - - /** - * Id of the thread generated by the debug adapter backend. - */ - +threadId: number; - - /** - * Name of the thread. - */ - name: string; - - /** - * Information about the current thread stop event. Null if thread is not stopped. - */ - stoppedDetails: ?IRawStoppedDetails; - - /** - * Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise null. - */ - exceptionInfo(): Promise; - - /** - * Gets the already-fetched callstack from the debug adapter. - */ - getCallStack(): IStackFrame[]; - - /** - * Invalidates the callstack cache. - */ - clearCallStack(): void; - - /** - * Fetches more callstack items on user demand - */ - fetchCallStack(levels?: number): Promise; - - /** - * Indicates whether this thread is stopped. The callstack for stopped - * threads can be retrieved from the debug adapter. - */ - stopped: boolean; - - next(): Promise; - stepIn(): Promise; - stepOut(): Promise; - stepBack(): Promise; - continue(): Promise; - pause(): Promise; - reverseContinue(): Promise; -} - -export interface IScope extends IExpressionContainer { - +name: string; - +expensive: boolean; - +range: ?atom$Range; -} - -export interface IProcess extends ITreeElement { - +configuration: IProcessConfig; - +session: ISession & ITreeElement; - +sources: Map; - getThread(threadId: number): ?IThread; - getAllThreads(): IThread[]; - getSource(raw: ?DebugProtocol.Source): ISource; - completions( - frameId: number, - text: string, - position: atom$Point, - overwriteBefore: number, - ): Promise>; -} - -export interface IEnableable extends ITreeElement { - enabled: boolean; -} - -export interface IRawBreakpoint { - line: number; - column?: number; - enabled?: boolean; - condition?: string; - hitCondition?: string; -} - -export interface IExceptionBreakpoint extends IEnableable { - +filter: string; - +label: string; -} - -export type IExceptionInfo = { - id: ?string, - description: ?string, - breakMode: ?string, - details: ?DebugProtocol.ExceptionDetails, -}; - -export interface IViewModel { - /** - * Returns the focused debug process or null if no process is stopped. - */ - +focusedProcess: ?IProcess; - - /** - * Returns the focused thread or null if no thread is stopped. - */ - +focusedThread: ?IThread; - - /** - * Returns the focused stack frame or null if there are no stack frames. - */ - +focusedStackFrame: ?IStackFrame; - isMultiProcessView(): boolean; - - onDidFocusProcess(callback: (process: ?IProcess) => mixed): IDisposable; - onDidFocusStackFrame( - callback: (data: {stackFrame: ?IStackFrame, explicit: boolean}) => mixed, - ): IDisposable; - onDidChangeExpressionContext( - callback: (data: {stackFrame: ?IStackFrame, explicit: boolean}) => mixed, - ): IDisposable; -} - -export interface IModel extends ITreeElement { - getProcesses(): IProcess[]; - getBreakpoints(): IBreakpoint[]; - getBreakpointAtLine(uri: string, line: number): ?IBreakpoint; - getBreakpointById(id: string): ?IBreakpoint; - - areBreakpointsActivated(): boolean; - getFunctionBreakpoints(): IFunctionBreakpoint[]; - getExceptionBreakpoints(): IExceptionBreakpoint[]; - getWatchExpressions(): IEvaluatableExpression[]; - fetchCallStack(thread: IThread): Promise; - - onDidChangeBreakpoints( - callback: (event: ?IBreakpointsChangeEvent) => mixed, - ): IDisposable; - onDidChangeCallStack(callback: () => mixed): IDisposable; - onDidChangeWatchExpressions( - callback: (expression: ?IExpression) => mixed, - ): IDisposable; -} - -export interface IBreakpointsChangeEvent { - added?: (IBreakpoint | IFunctionBreakpoint)[]; - removed?: (IBreakpoint | IFunctionBreakpoint)[]; - changed?: (IBreakpoint | IFunctionBreakpoint)[]; -} - -/* Debugger mode */ -export type DebuggerModeType = - | 'starting' - | 'running' - | 'paused' - | 'stopping' - | 'stopped'; - -export interface IDebugService { - +viewModel: IViewModel; - getDebuggerMode(): DebuggerModeType; - - onDidChangeMode(callback: (mode: DebuggerModeType) => mixed): IDisposable; - onDidStartDebugSession( - callback: (config: IProcessConfig) => mixed, - ): IDisposable; - onDidCustomEvent( - callback: (event: DebugProtocol.DebugEvent) => mixed, - ): IDisposable; - - /** - * Sets the focused stack frame and evaluates all expressions against the newly focused stack frame, - */ - focusStackFrame( - stackFrame: ?IStackFrame, - thread: ?IThread, - process: ?IProcess, - explicit?: boolean, - ): void; - - /** - * Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes. - */ - addBreakpoints(uri: string, rawBreakpoints: IRawBreakpoint[]): Promise; - - /** - * Updates the breakpoints. - */ - updateBreakpoints( - uri: string, - data: {[id: string]: DebugProtocol.Breakpoint}, - ): void; - - /** - * Enables or disables all breakpoints. If breakpoint is passed only enables or disables the passed breakpoint. - * Notifies debug adapter of breakpoint changes. - */ - enableOrDisableBreakpoints( - enable: boolean, - breakpoint?: IEnableable, - ): Promise; - - toggleSourceBreakpoint(uri: string, line: number): Promise; - - /** - * Sets the global activated property for all breakpoints. - * Notifies debug adapter of breakpoint changes. - */ - setBreakpointsActivated(activated: boolean): Promise; - - /** - * Removes all breakpoints. If id is passed only removes the breakpoint associated with that id. - * Notifies debug adapter of breakpoint changes. - */ - removeBreakpoints(id?: string): Promise; - - /** - * Adds a new no name function breakpoint. The function breakpoint should be renamed once user enters the name. - */ - addFunctionBreakpoint(): void; - - /** - * Renames an already existing function breakpoint. - * Notifies debug adapter of breakpoint changes. - */ - renameFunctionBreakpoint(id: string, newFunctionName: string): Promise; - - /** - * Removes all function breakpoints. If id is passed only removes the function breakpoint with the passed id. - * Notifies debug adapter of breakpoint changes. - */ - removeFunctionBreakpoints(id?: string): Promise; - - /** - * Adds a new watch expression and evaluates it against the debug adapter. - */ - addWatchExpression(name: string): void; - - /** - * Creates an expression to be evaluated. - */ - createExpression(rawExpression: string): IEvaluatableExpression; - - /** - * Renames a watch expression and evaluates it against the debug adapter. - */ - renameWatchExpression(id: string, newName: string): void; - - /** - * Removes all watch expressions. If id is passed only removes the watch expression with the passed id. - */ - removeWatchExpressions(id?: string): void; - - /** - * Starts debugging. If the configOrName is not passed uses the selected configuration in the debug dropdown. - * Also saves all files, manages if compounds are present in the configuration - * and resolveds configurations via DebugConfigurationProviders. - */ - startDebugging(config: IProcessConfig): Promise; - - /** - * Restarts a process or creates a new one if there is no active session. - */ - restartProcess(): Promise; - - /** - * Stops the process. If the process does not exist then stops all processes. - */ - stopProcess(): Promise; - - /** - * Gets the current debug model. - */ - getModel(): IModel; -} - -export interface IStackFrame extends ITreeElement { - thread: IThread; - name: string; - presentationHint: ?string; - frameId: number; - range: atom$Range; - source: ISource; - getScopes(): Promise; - getMostSpecificScopes(range: atom$Range): Promise; - restart(): Promise; - toString(): string; - openInEditor(): Promise; -} - -export interface IBreakpoint extends IEnableable { - uri: string; - line: number; - endLine: ?number; - column: number; - endColumn: ?number; - condition: ?string; - hitCondition: ?string; - verified: boolean; - idFromAdapter: ?number; - message: ?string; - adapterData?: any; -} - -export interface IFunctionBreakpoint extends IEnableable { - name: string; - verified: boolean; - idFromAdapter: ?number; - condition?: ?string; - hitCondition?: ?string; -} - -export type IRawStopppedUpdate = { - sessionId: string, - threadId: ?number, - stoppedDetails: IRawStoppedDetails, -}; - -export type IRawThreadUpdate = { - sessionId: string, - thread: DebugProtocol.Thread, -}; - -export type IRawModelUpdate = IRawStopppedUpdate | IRawThreadUpdate; - -export interface IRawStoppedDetails { - reason?: string; - preserveFocusHint?: boolean; - description?: string; - threadId?: number; - text?: string; - totalFrames?: number; - allThreadsStopped?: boolean; - framesErrorMessage?: string; + return data; } -export type SerializedState = { - sourceBreakpoints: ?Array, - functionBreakpoints: ?Array, - exceptionBreakpoints: ?Array, - watchExpressions: ?Array, - showDebugger: boolean, - workspaceDocksVisibility: Array, -}; +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointConfigComponent.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointConfigComponent.js index 1fcd25a9..ce62c899 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointConfigComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointConfigComponent.js @@ -1,3 +1,118 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _AtomInput() { + const data = require("../../../../../nuclide-commons-ui/AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _ButtonGroup() { + const data = require("../../../../../nuclide-commons-ui/ButtonGroup"); + + _ButtonGroup = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _Checkbox() { + const data = require("../../../../../nuclide-commons-ui/Checkbox"); + + _Checkbox = function () { + return data; + }; + + return data; +} + +function _Modal() { + const data = require("../../../../../nuclide-commons-ui/Modal"); + + _Modal = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _analytics() { + const data = require("../../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,177 +121,119 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {IBreakpoint, IRawBreakpoint, IDebugService} from '../types'; - -import {AtomInput} from 'nuclide-commons-ui/AtomInput'; -import * as React from 'react'; -import {Button, ButtonTypes} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import nullthrows from 'nullthrows'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Checkbox} from 'nuclide-commons-ui/Checkbox'; -import {Modal} from 'nuclide-commons-ui/Modal'; -import {Observable} from 'rxjs'; -import {track} from 'nuclide-commons/analytics'; -import {AnalyticsEvents} from '../constants'; - -type PropsType = { - onDismiss: () => void, - breakpoint: IBreakpoint, - service: IDebugService, -}; - -type StateType = { - bpId: string, -}; - -export default class BreakpointConfigComponent extends React.Component< - PropsType, - StateType, -> { - _condition: ?AtomInput; - props: PropsType; - state: StateType; - _disposables: UniversalDisposable; - - constructor(props: PropsType) { +class BreakpointConfigComponent extends React.Component { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); this.state = { - bpId: this.props.breakpoint.getId(), + bpId: this.props.breakpoint.getId() }; - const model = this.props.service.getModel(); - this._disposables.add( - model.onDidChangeBreakpoints(() => { - const breakpoint = model - .getBreakpoints() - .filter(bp => bp.getId() === this.state.bpId); - if (breakpoint == null) { - // Breakpoint no longer exists. - this.props.onDismiss(); - } - this.forceUpdate(); - }), - ); + + this._disposables.add(model.onDidChangeBreakpoints(() => { + const breakpoint = model.getBreakpoints().filter(bp => bp.getId() === this.state.bpId); + + if (breakpoint == null) { + // Breakpoint no longer exists. + this.props.onDismiss(); + } + + this.forceUpdate(); + })); } - componentDidMount(): void { - track(AnalyticsEvents.DEBUGGER_BREAKPOINT_CONFIG_UI_SHOW, { - fileExtension: nuclideUri.extname(this.props.breakpoint.uri), + componentDidMount() { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_BREAKPOINT_CONFIG_UI_SHOW, { + fileExtension: _nuclideUri().default.extname(this.props.breakpoint.uri) }); - this._disposables.add( - atom.commands.add('atom-workspace', 'core:cancel', this.props.onDismiss), - atom.commands.add( - 'atom-workspace', - 'core:confirm', - this._updateBreakpoint.bind(this), - ), - Observable.timer(100).subscribe(() => { - if (this._condition != null) { - this._condition.focus(); - } - }), - ); + + this._disposables.add(atom.commands.add('atom-workspace', 'core:cancel', this.props.onDismiss), atom.commands.add('atom-workspace', 'core:confirm', this._updateBreakpoint.bind(this)), _RxMin.Observable.timer(100).subscribe(() => { + if (this._condition != null) { + this._condition.focus(); + } + })); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - async _updateBreakpoint(): Promise { - const {breakpoint, service} = this.props; - const condition = nullthrows(this._condition) - .getText() - .trim(); + async _updateBreakpoint() { + const { + breakpoint, + service + } = this.props; + const condition = (0, _nullthrows().default)(this._condition).getText().trim(); + if (condition === (breakpoint.condition || '')) { this.props.onDismiss(); return; } await service.removeBreakpoints(breakpoint.getId()); - - const bp: IRawBreakpoint = { + const bp = { line: breakpoint.line, column: breakpoint.column, - enabled: breakpoint.enabled, + enabled: breakpoint.enabled }; + if (condition !== '') { bp.condition = condition; } await service.addBreakpoints(breakpoint.uri, [bp]); - track(AnalyticsEvents.DEBUGGER_BREAKPOINT_UPDATE_CONDITION, { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_BREAKPOINT_UPDATE_CONDITION, { path: breakpoint.uri, line: breakpoint.line, condition, - fileExtension: nuclideUri.extname(breakpoint.uri), + fileExtension: _nuclideUri().default.extname(breakpoint.uri) }); this.props.onDismiss(); } - render(): React.Node { - return ( - -
-

Edit breakpoint

-
- -
-
- { - track(AnalyticsEvents.DEBUGGER_BREAKPOINT_TOGGLE_ENABLED, { - enabled: isChecked, - }); - this.props.service.enableOrDisableBreakpoints( - isChecked, - this.props.breakpoint, - ); - }} - checked={this.props.breakpoint.enabled} - label="Enable breakpoint" - /> -
-
- { - this._condition = input; - }} - autofocus={true} - /> -
- -
- - - - -
-
-
- ); + render() { + return React.createElement(_Modal().Modal, { + onDismiss: this.props.onDismiss + }, React.createElement("div", { + className: "padded debugger-bp-dialog" + }, React.createElement("h1", { + className: "debugger-bp-config-header" + }, "Edit breakpoint"), React.createElement("div", { + className: "block" + }, React.createElement("label", null, "Breakpoint at ", _nuclideUri().default.basename(this.props.breakpoint.uri), ":", this.props.breakpoint.endLine != null ? this.props.breakpoint.endLine : this.props.breakpoint.line)), React.createElement("div", { + className: "block" + }, React.createElement(_Checkbox().Checkbox, { + onChange: isChecked => { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_BREAKPOINT_TOGGLE_ENABLED, { + enabled: isChecked + }); + this.props.service.enableOrDisableBreakpoints(isChecked, this.props.breakpoint); + }, + checked: this.props.breakpoint.enabled, + label: "Enable breakpoint" + })), React.createElement("div", { + className: "block" + }, React.createElement(_AtomInput().AtomInput, { + placeholderText: "Breakpoint hit condition...", + value: this.props.breakpoint.condition || '', + size: "sm", + ref: input => { + this._condition = input; + }, + autofocus: true + })), React.createElement("label", null, "This expression will be evaluated each time the corresponding line is hit, but the debugger will only break execution if the expression evaluates to true."), React.createElement("div", { + className: "debugger-bp-config-actions" + }, React.createElement(_ButtonGroup().ButtonGroup, null, React.createElement(_Button().Button, { + onClick: this.props.onDismiss + }, "Cancel"), React.createElement(_Button().Button, { + buttonType: _Button().ButtonTypes.PRIMARY, + onClick: this._updateBreakpoint.bind(this) + }, "Update"))))); } + } + +exports.default = BreakpointConfigComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointListComponent.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointListComponent.js index 3536ce24..284926a2 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointListComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointListComponent.js @@ -1,3 +1,136 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _Checkbox() { + const data = require("../../../../../nuclide-commons-ui/Checkbox"); + + _Checkbox = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _ListView() { + const data = require("../../../../../nuclide-commons-ui/ListView"); + + _ListView = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _Icon() { + const data = require("../../../../../nuclide-commons-ui/Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("../utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _Section() { + const data = require("../../../../../nuclide-commons-ui/Section"); + + _Section = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _projects() { + const data = require("../../../../../nuclide-commons-atom/projects"); + + _projects = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,306 +139,213 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {IBreakpoint, IDebugService, IExceptionBreakpoint} from '../types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import invariant from 'assert'; -import * as React from 'react'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {Checkbox} from 'nuclide-commons-ui/Checkbox'; -import {track} from 'nuclide-commons/analytics'; -import {ListView, ListViewItem} from 'nuclide-commons-ui/ListView'; -import classnames from 'classnames'; -import {Icon} from 'nuclide-commons-ui/Icon'; -import {AnalyticsEvents} from '../constants'; -import {openSourceLocation} from '../utils'; -import {Section} from 'nuclide-commons-ui/Section'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {observeProjectPathsAll} from 'nuclide-commons-atom/projects'; - -type Props = {| - service: IDebugService, -|}; - -type State = { - supportsConditionalBreakpoints: boolean, - breakpoints: IBreakpoint[], - exceptionBreakpoints: IExceptionBreakpoint[], - exceptionBreakpointsCollapsed: boolean, - activeProjects: NuclideUri[], -}; - -export default class BreakpointListComponent extends React.Component< - Props, - State, -> { - _disposables: UniversalDisposable; - - constructor(props: Props) { +class BreakpointListComponent extends React.Component { + constructor(props) { super(props); + + this._handleBreakpointEnabledChange = (breakpoint, enabled) => { + this.props.service.enableOrDisableBreakpoints(enabled, breakpoint); + }; + + this._handleBreakpointClick = (breakpointIndex, breakpoint) => { + if (!(breakpoint != null)) { + throw new Error("Invariant violation: \"breakpoint != null\""); + } + + const { + uri, + line + } = breakpoint; // Debugger model is 1-based while Atom UI is zero-based. + + (0, _utils().openSourceLocation)(uri, line - 1); + }; + + this._setExceptionCollapsed = collapsed => { + _featureConfig().default.set('debugger-exceptionBreakpointsCollapsed', collapsed); + + this.setState({ + exceptionBreakpointsCollapsed: collapsed + }); + }; + this.state = this._computeState(); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); } - _computeState(): State { - const {service} = this.props; - const {focusedProcess} = service.viewModel; + _computeState() { + const { + service + } = this.props; + const { + focusedProcess + } = service.viewModel; const model = service.getModel(); - - const exceptionBreakpointsCollapsed = Boolean( - featureConfig.get('debugger-exceptionBreakpointsCollapsed'), - ); - + const exceptionBreakpointsCollapsed = Boolean(_featureConfig().default.get('debugger-exceptionBreakpointsCollapsed')); let newActiveProjects = []; + if (this.state != null) { - const {activeProjects} = this.state; + const { + activeProjects + } = this.state; + if (activeProjects != null) { newActiveProjects = activeProjects; } } return { - supportsConditionalBreakpoints: - focusedProcess != null && - Boolean( - focusedProcess.session.capabilities.supportsConditionalBreakpoints, - ), + supportsConditionalBreakpoints: focusedProcess != null && Boolean(focusedProcess.session.capabilities.supportsConditionalBreakpoints), breakpoints: model.getBreakpoints(), exceptionBreakpoints: model.getExceptionBreakpoints(), exceptionBreakpointsCollapsed, - activeProjects: newActiveProjects, + activeProjects: newActiveProjects }; } - componentDidMount(): void { + componentDidMount() { const model = this.props.service.getModel(); - this._disposables.add( - model.onDidChangeBreakpoints(() => { - this.setState(this._computeState()); - }), - observeProjectPathsAll(projectPaths => - this.setState({activeProjects: projectPaths}), - ), - ); + + this._disposables.add(model.onDidChangeBreakpoints(() => { + this.setState(this._computeState()); + }), (0, _projects().observeProjectPathsAll)(projectPaths => this.setState({ + activeProjects: projectPaths + }))); } - componentWillUnmount(): void { + componentWillUnmount() { if (this._disposables != null) { this._disposables.dispose(); } } - _handleBreakpointEnabledChange = ( - breakpoint: IBreakpoint, - enabled: boolean, - ): void => { - this.props.service.enableOrDisableBreakpoints(enabled, breakpoint); - }; - - _handleBreakpointClick = ( - breakpointIndex: number, - breakpoint: ?IBreakpoint, - ): void => { - invariant(breakpoint != null); - const {uri, line} = breakpoint; - // Debugger model is 1-based while Atom UI is zero-based. - openSourceLocation(uri, line - 1); - }; - - _setExceptionCollapsed = (collapsed: boolean): void => { - featureConfig.set('debugger-exceptionBreakpointsCollapsed', collapsed); - this.setState({exceptionBreakpointsCollapsed: collapsed}); - }; - - render(): React.Node { + render() { const { exceptionBreakpoints, supportsConditionalBreakpoints, - activeProjects, + activeProjects } = this.state; - const breakpoints = this.state.breakpoints.filter(breakpoint => - activeProjects.some(projectPath => - breakpoint.uri.startsWith(projectPath), - ), - ); - const {service} = this.props; - const items = breakpoints - .sort((breakpointA, breakpointB) => { - const fileA = nuclideUri.basename(breakpointA.uri); - const fileB = nuclideUri.basename(breakpointB.uri); - if (fileA !== fileB) { - return fileA.localeCompare(fileB); - } + const breakpoints = this.state.breakpoints.filter(breakpoint => activeProjects.some(projectPath => breakpoint.uri.startsWith(projectPath))); + const { + service + } = this.props; + const items = breakpoints.sort((breakpointA, breakpointB) => { + const fileA = _nuclideUri().default.basename(breakpointA.uri); - const lineA = - breakpointA.endLine != null ? breakpointA.endLine : breakpointA.line; - const lineB = - breakpointB.endLine != null ? breakpointB.endLine : breakpointB.line; - return lineA - lineB; - }) - .map((breakpoint, i) => { - const basename = nuclideUri.basename(breakpoint.uri); - const {line, endLine, enabled, verified, uri: path} = breakpoint; - const dataLine = - endLine != null && !Number.isNaN(endLine) ? endLine : line; - const bpId = breakpoint.getId(); - const label = `${basename}:${dataLine}`; - const title = !enabled - ? 'Disabled breakpoint' - : !verified - ? 'Unresolved Breakpoint' - : `Breakpoint at ${label} (resolved)`; - - const conditionElement = - supportsConditionalBreakpoints && breakpoint.condition != null ? ( -
{ - atom.commands.dispatch( - event.target, - 'debugger:edit-breakpoint', - ); - }}> - Condition: {breakpoint.condition} -
- ) : null; - - const content = ( -
-
- ) => event.stopPropagation()} - title={title} - className={classnames( - verified ? '' : 'debugger-breakpoint-unresolved', - 'debugger-breakpoint-checkbox', - )} - /> - -
- { - track(AnalyticsEvents.DEBUGGER_EDIT_BREAKPOINT_FROM_ICON); - atom.commands.dispatch( - event.target, - 'debugger:edit-breakpoint', - ); - }} - /> - { - track( - AnalyticsEvents.DEBUGGER_DELETE_BREAKPOINT_FROM_ICON, - ); - atom.commands.dispatch( - event.target, - 'debugger:remove-breakpoint', - ); - event.stopPropagation(); - }} - /> -
- {label} -
- {conditionElement} -
-
- ); - return ( - - {content} - - ); - }); - const separator = - breakpoints.length !== 0 && - !this.state.exceptionBreakpointsCollapsed && - exceptionBreakpoints.length !== 0 ? ( -
- ) : null; - return ( -
-
- {exceptionBreakpoints.map(exceptionBreakpoint => { - return ( -
- - service.enableOrDisableBreakpoints( - enabled, - exceptionBreakpoint, - ) - } - checked={exceptionBreakpoint.enabled} - /> - {exceptionBreakpoint.label || - `${exceptionBreakpoint.filter} exceptions`} -
- ); - })} -
- {separator} - - {items} - -
- ); + const fileB = _nuclideUri().default.basename(breakpointB.uri); + + if (fileA !== fileB) { + return fileA.localeCompare(fileB); + } + + const lineA = breakpointA.endLine != null ? breakpointA.endLine : breakpointA.line; + const lineB = breakpointB.endLine != null ? breakpointB.endLine : breakpointB.line; + return lineA - lineB; + }).map((breakpoint, i) => { + const basename = _nuclideUri().default.basename(breakpoint.uri); + + const { + line, + endLine, + enabled, + verified, + uri: path + } = breakpoint; + const dataLine = endLine != null && !Number.isNaN(endLine) ? endLine : line; + const bpId = breakpoint.getId(); + const label = `${basename}:${dataLine}`; + const title = !enabled ? 'Disabled breakpoint' : !verified ? 'Unresolved Breakpoint' : `Breakpoint at ${label} (resolved)`; + const conditionElement = supportsConditionalBreakpoints && breakpoint.condition != null ? React.createElement("div", { + className: "debugger-breakpoint-condition", + title: `Breakpoint condition: ${breakpoint.condition}`, + "data-path": path, + "data-line": line, + "data-bpid": bpId, + onClick: event => { + atom.commands.dispatch(event.target, 'debugger:edit-breakpoint'); + } + }, "Condition: ", breakpoint.condition) : null; + const content = React.createElement("div", { + className: "inline-block" + }, React.createElement("div", { + className: (0, _classnames().default)({ + 'debugger-breakpoint-disabled': !enabled, + 'debugger-breakpoint-with-condition': Boolean(breakpoint.condition) + }), + key: i + }, React.createElement(_Checkbox().Checkbox, { + checked: enabled, + onChange: this._handleBreakpointEnabledChange.bind(this, breakpoint), + onClick: event => event.stopPropagation(), + title: title, + className: (0, _classnames().default)(verified ? '' : 'debugger-breakpoint-unresolved', 'debugger-breakpoint-checkbox') + }), React.createElement("span", { + title: title, + "data-path": path, + "data-bpid": bpId, + "data-line": line + }, React.createElement("div", { + className: "debugger-breakpoint-condition-controls" + }, React.createElement(_Icon().Icon, { + icon: "pencil", + className: "debugger-breakpoint-condition-control", + "data-path": path, + "data-bpid": bpId, + "data-line": line, + onClick: event => { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_EDIT_BREAKPOINT_FROM_ICON); + atom.commands.dispatch(event.target, 'debugger:edit-breakpoint'); + } + }), React.createElement(_Icon().Icon, { + icon: "x", + className: "debugger-breakpoint-condition-control", + "data-path": path, + "data-bpid": bpId, + "data-line": line, + onClick: event => { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_DELETE_BREAKPOINT_FROM_ICON); + atom.commands.dispatch(event.target, 'debugger:remove-breakpoint'); + event.stopPropagation(); + } + })), label), conditionElement)); + return React.createElement(_ListView().ListViewItem, { + key: label, + index: i, + value: breakpoint, + "data-path": path, + "data-bpid": bpId, + "data-line": line, + title: title, + className: "debugger-breakpoint" + }, content); + }); + const separator = breakpoints.length !== 0 && !this.state.exceptionBreakpointsCollapsed && exceptionBreakpoints.length !== 0 ? React.createElement("hr", { + className: "nuclide-ui-hr debugger-breakpoint-separator" + }) : null; + return React.createElement("div", null, React.createElement(_Section().Section, { + className: "debugger-breakpoint-section", + headline: "Exception breakpoints", + collapsable: true, + onChange: this._setExceptionCollapsed, + collapsed: this.state.exceptionBreakpointsCollapsed + }, exceptionBreakpoints.map(exceptionBreakpoint => { + return React.createElement("div", { + className: "debugger-breakpoint", + key: exceptionBreakpoint.getId() + }, React.createElement(_Checkbox().Checkbox, { + className: (0, _classnames().default)('debugger-breakpoint-checkbox', 'debugger-exception-checkbox'), + onChange: enabled => service.enableOrDisableBreakpoints(enabled, exceptionBreakpoint), + checked: exceptionBreakpoint.enabled + }), exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`); + })), separator, React.createElement(_ListView().ListView, { + alternateBackground: true, + onSelect: this._handleBreakpointClick, + selectable: true + }, items)); } + } + +exports.default = BreakpointListComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointsView.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointsView.js index 4ff6fd08..a0ff81db 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointsView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/BreakpointsView.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _BreakpointListComponent() { + const data = _interopRequireDefault(require("./BreakpointListComponent")); + + _BreakpointListComponent = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,34 +39,23 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {IDebugService} from '../types'; - -import classnames from 'classnames'; -import * as React from 'react'; -import BreakpointListComponent from './BreakpointListComponent'; - -type Props = { - service: IDebugService, -}; - -export default class BreakpointsView extends React.PureComponent { - render(): React.Node { - const {service} = this.props; - - return ( -
-
- -
-
- ); +class BreakpointsView extends React.PureComponent { + render() { + const { + service + } = this.props; + return React.createElement("div", { + className: (0, _classnames().default)('debugger-container-new', 'debugger-breakpoint-list') + }, React.createElement("div", { + className: "debugger-pane-content " + }, React.createElement(_BreakpointListComponent().default, { + service: service + }))); } + } + +exports.default = BreakpointsView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerCallstackComponent.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerCallstackComponent.js index 7a6c918d..6427fd5a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerCallstackComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerCallstackComponent.js @@ -1,3 +1,118 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _Table() { + const data = require("../../../../../nuclide-commons-ui/Table"); + + _Table = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _observable() { + const data = require("../../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _AtomInput() { + const data = require("../../../../../nuclide-commons-ui/AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _LoadingSpinner() { + const data = require("../../../../../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,196 +121,158 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {DebuggerModeType, IDebugService, IStackFrame} from '../types'; -import * as React from 'react'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {DebuggerMode} from '../constants'; -import {Table} from 'nuclide-commons-ui/Table'; -import {Observable} from 'rxjs'; -import {fastDebounce} from 'nuclide-commons/observable'; -import nullthrows from 'nullthrows'; // eslint-disable-next-line nuclide-internal/prefer-nuclide-uri -import classnames from 'classnames'; -import idx from 'idx'; -import {AtomInput} from 'nuclide-commons-ui/AtomInput'; -import {Button, ButtonSizes} from 'nuclide-commons-ui/Button'; -import { - LoadingSpinner, - LoadingSpinnerSizes, -} from 'nuclide-commons-ui/LoadingSpinner'; - -type Props = { - service: IDebugService, -}; - -type State = { - mode: DebuggerModeType, - callstack: Array, - selectedCallFrameId: number, - callStackLevels: number, - isFechingStackFrames: boolean, -}; - -export default class DebuggerCallstackComponent extends React.Component< - Props, - State, -> { - _disposables: UniversalDisposable; - - constructor(props: Props) { +class DebuggerCallstackComponent extends React.Component { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); + + this._handleStackFrameClick = (clickedRow, callFrameIndex) => { + this.props.service.focusStackFrame(clickedRow.frame, null, null, true); + }; + + this._disposables = new (_UniversalDisposable().default)(); this.state = this._getState(); } - _getState(): State { - const {service} = this.props; - const {focusedStackFrame, focusedThread} = service.viewModel; + _getState() { + const { + service + } = this.props; + const { + focusedStackFrame, + focusedThread + } = service.viewModel; return { callStackLevels: this.state == null ? 20 : this.state.callStackLevels, mode: service.getDebuggerMode(), callstack: focusedThread == null ? [] : focusedThread.getCallStack(), - selectedCallFrameId: - focusedStackFrame == null ? -1 : focusedStackFrame.frameId, - isFechingStackFrames: false, + selectedCallFrameId: focusedStackFrame == null ? -1 : focusedStackFrame.frameId, + isFechingStackFrames: false }; } - componentDidMount(): void { - const {service} = this.props; + componentDidMount() { + const { + service + } = this.props; const model = service.getModel(); - const {viewModel} = service; - this._disposables.add( - Observable.merge( - observableFromSubscribeFunction(model.onDidChangeCallStack.bind(model)), - observableFromSubscribeFunction( - viewModel.onDidFocusStackFrame.bind(viewModel), - ), - observableFromSubscribeFunction(service.onDidChangeMode.bind(service)), - ) - .let(fastDebounce(15)) - .subscribe(() => this.setState(this._getState())), - ); + const { + viewModel + } = service; + + this._disposables.add(_RxMin.Observable.merge((0, _event().observableFromSubscribeFunction)(model.onDidChangeCallStack.bind(model)), (0, _event().observableFromSubscribeFunction)(viewModel.onDidFocusStackFrame.bind(viewModel)), (0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service))).let((0, _observable().fastDebounce)(15)).subscribe(() => this.setState(this._getState()))); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - _handleStackFrameClick = ( - clickedRow: {frame: IStackFrame}, - callFrameIndex: number, - ): void => { - this.props.service.focusStackFrame(clickedRow.frame, null, null, true); - }; + render() { + const { + callstack, + mode + } = this.state; + const rows = callstack == null ? [] : callstack.map((stackFrame, index) => { + const isSelected = this.state.selectedCallFrameId === stackFrame.frameId; + const cellData = { + data: { + frameId: index + 1, + address: stackFrame.name, + frame: stackFrame, + isSelected + } + }; - render(): React.Node { - const {callstack, mode} = this.state; - const rows = - callstack == null - ? [] - : callstack.map((stackFrame, index) => { - const isSelected = - this.state.selectedCallFrameId === stackFrame.frameId; - const cellData = { - data: { - frameId: index + 1, - address: stackFrame.name, - frame: stackFrame, - isSelected, - }, - }; - - if (isSelected) { - // $FlowIssue className is an optional property of a table row - cellData.className = 'debugger-callstack-item-selected'; - } - - return cellData; - }); + if (isSelected) { + // $FlowIssue className is an optional property of a table row + cellData.className = 'debugger-callstack-item-selected'; + } - const columns = [ - { - title: '', - key: 'frameId', - width: 0.05, - }, - { - title: 'Address', - key: 'address', - width: 0.95, - }, - ]; - - const emptyComponent = () => ( -
callstack unavailable
- ); - - return ( -
-
- cellData.frame.source.available} - resizable={true} - onSelect={this._handleStackFrameClick} - sortable={false} - /> - {this._renderLoadMoreStackFrames()} - - - ); + return cellData; + }); + const columns = [{ + title: '', + key: 'frameId', + width: 0.05 + }, { + title: 'Address', + key: 'address', + width: 0.95 + }]; + + const emptyComponent = () => React.createElement("div", { + className: "debugger-callstack-list-empty" + }, "callstack unavailable"); + + return React.createElement("div", { + className: (0, _classnames().default)('debugger-container-new', { + 'debugger-container-new-disabled': mode === _constants().DebuggerMode.RUNNING + }) + }, React.createElement("div", { + className: "debugger-pane-content" + }, React.createElement(_Table().Table, { + className: "debugger-callstack-table", + columns: columns, + emptyComponent: emptyComponent, + rows: rows, + selectable: cellData => cellData.frame.source.available, + resizable: true, + onSelect: this._handleStackFrameClick, + sortable: false + }), this._renderLoadMoreStackFrames())); } - _renderLoadMoreStackFrames(): ?React.Element { - const {viewModel} = this.props.service; - const {callstack, isFechingStackFrames} = this.state; - const totalFrames = - idx(viewModel, _ => _.focusedThread.stoppedDetails.totalFrames) || 0; + _renderLoadMoreStackFrames() { + var _ref; + + const { + viewModel + } = this.props.service; + const { + callstack, + isFechingStackFrames + } = this.state; + const totalFrames = ((_ref = viewModel) != null ? (_ref = _ref.focusedThread) != null ? (_ref = _ref.stoppedDetails) != null ? _ref.totalFrames : _ref : _ref : _ref) || 0; + if (totalFrames <= callstack.length || callstack.length <= 1) { return null; } - return ( -
- - { - if (!isNaN(value)) { - this.setState({callStackLevels: parseInt(value, 10)}); - } - }} - /> - - {isFechingStackFrames ? ( - - ) : null} -
- ); + + return React.createElement("div", { + style: { + display: 'flex' + } + }, React.createElement(_Button().Button, { + size: _Button().ButtonSizes.EXTRA_SMALL, + disabled: isFechingStackFrames, + onClick: () => { + this.setState({ + isFechingStackFrames: true + }); + (0, _nullthrows().default)(viewModel.focusedThread).fetchCallStack(this.state.callStackLevels).then(() => this.setState(this._getState())); + } + }, "More Stack Frames"), React.createElement(_AtomInput().AtomInput, { + style: { + 'flex-grow': '1' + }, + placeholderText: "Number of stack frames", + initialValue: String(this.state.callStackLevels), + size: "xs", + onDidChange: value => { + if (!isNaN(value)) { + this.setState({ + callStackLevels: parseInt(value, 10) + }); + } + } + }), React.createElement(_AtomInput().AtomInput, null), isFechingStackFrames ? React.createElement(_LoadingSpinner().LoadingSpinner, { + size: _LoadingSpinner().LoadingSpinnerSizes.EXTRA_SMALL + }) : null); } + } + +exports.default = DebuggerCallstackComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerControllerView.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerControllerView.js index 22cade4c..0d647b5f 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerControllerView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerControllerView.js @@ -1,3 +1,56 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _LoadingSpinner() { + const data = require("../../../../../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,54 +59,42 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {IDebugService} from '../types'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import * as React from 'react'; -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {DebuggerMode} from '../constants'; - -type Props = { - service: IDebugService, -}; - -export default class DebuggerControllerView extends React.Component { - _disposables: UniversalDisposable; - - constructor(props: Props) { +class DebuggerControllerView extends React.Component { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); } componentDidMount() { - const {service} = this.props; - this._disposables.add( - observableFromSubscribeFunction( - service.onDidChangeMode.bind(service), - ).subscribe(mode => this.forceUpdate()), - ); + const { + service + } = this.props; + + this._disposables.add((0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service)).subscribe(mode => this.forceUpdate())); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - render(): React.Node { - if (this.props.service.getDebuggerMode() === DebuggerMode.STARTING) { - return ( -
-
- Starting Debugger... - -
-
- ); + render() { + if (this.props.service.getDebuggerMode() === _constants().DebuggerMode.STARTING) { + return React.createElement("div", { + className: "debugger-starting-message" + }, React.createElement("div", null, React.createElement("span", { + className: "inline-block" + }, "Starting Debugger..."), React.createElement(_LoadingSpinner().LoadingSpinner, { + className: "inline-block", + size: "EXTRA_SMALL" + }))); } + return null; } + } + +exports.default = DebuggerControllerView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerControlsView.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerControlsView.js index ba5e191b..5aa4153b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerControlsView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerControlsView.js @@ -1,3 +1,86 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _TruncatedButton() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/TruncatedButton")); + + _TruncatedButton = function () { + return data; + }; + + return data; +} + +function _DebuggerSteppingComponent() { + const data = _interopRequireDefault(require("./DebuggerSteppingComponent")); + + _DebuggerSteppingComponent = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _DebuggerControllerView() { + const data = _interopRequireDefault(require("./DebuggerControllerView")); + + _DebuggerControllerView = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,141 +89,87 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DebuggerModeType, IDebugService} from '../types'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import TruncatedButton from 'nuclide-commons-ui/TruncatedButton'; -import DebuggerSteppingComponent from './DebuggerSteppingComponent'; -import {DebuggerMode} from '../constants'; -import DebuggerControllerView from './DebuggerControllerView'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; - const DEVICE_PANEL_URL = 'atom://nuclide/devices'; -type Props = { - service: IDebugService, - passesMultiGK: boolean, -}; - -type State = { - mode: DebuggerModeType, - hasDevicePanelService: boolean, -}; - -export default class DebuggerControlsView extends React.PureComponent< - Props, - State, -> { - _disposables: UniversalDisposable; - - constructor(props: Props) { +class DebuggerControlsView extends React.PureComponent { + constructor(props) { super(props); - - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); this.state = { mode: props.service.getDebuggerMode(), - hasDevicePanelService: false, + hasDevicePanelService: false }; } - componentDidMount(): void { - const {service} = this.props; - this._disposables.add( - observableFromSubscribeFunction( - service.onDidChangeMode.bind(service), - ).subscribe(mode => this.setState({mode})), - atom.packages.serviceHub.consume('nuclide.devices', '0.0.0', provider => - this.setState({ - hasDevicePanelService: true, - }), - ), - ); + componentDidMount() { + const { + service + } = this.props; + + this._disposables.add((0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service)).subscribe(mode => this.setState({ + mode + })), atom.packages.serviceHub.consume('nuclide.devices', '0.0.0', provider => this.setState({ + hasDevicePanelService: true + }))); } - componentWillUnmount(): void { + componentWillUnmount() { this._dispose(); } - _dispose(): void { + _dispose() { this._disposables.dispose(); } - render(): React.Node { - const {service, passesMultiGK} = this.props; - const {mode} = this.state; - const debuggerStoppedNotice = - mode !== DebuggerMode.STOPPED ? null : ( -
-
- The debugger is not attached. -
-
- ); - - const debuggerRunningNotice = - mode !== DebuggerMode.RUNNING ? null : ( -
-
- {(service.viewModel.focusedProcess == null || - service.viewModel.focusedProcess.configuration.processName == null - ? 'The debug target' - : service.viewModel.focusedProcess.configuration.processName) + - ' is currently running.'} -
-
- ); - - const debuggerNotice = - mode !== DebuggerMode.STOPPED && !passesMultiGK ? null : ( -
- - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'debugger:show-attach-dialog', - ) - } - icon="nuclicon-debugger" - label="Attach debugger..." - /> - - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'debugger:show-launch-dialog', - ) - } - icon="nuclicon-debugger" - label="Launch debugger..." - /> - {this.state.hasDevicePanelService ? ( - goToLocation(DEVICE_PANEL_URL)} - icon="device-mobile" - label="Manage devices..." - /> - ) : null} -
- ); - - return ( -
-
- -
-
- -
- {debuggerRunningNotice} - {debuggerStoppedNotice} - {debuggerNotice} -
- ); + render() { + const { + service, + passesMultiGK + } = this.props; + const { + mode + } = this.state; + const debuggerStoppedNotice = mode !== _constants().DebuggerMode.STOPPED ? null : React.createElement("div", { + className: "debugger-pane-content" + }, React.createElement("div", { + className: "debugger-state-notice" + }, React.createElement("span", null, "The debugger is not attached."))); + const debuggerRunningNotice = mode !== _constants().DebuggerMode.RUNNING ? null : React.createElement("div", { + className: "debugger-pane-content" + }, React.createElement("div", { + className: "debugger-state-notice" + }, (service.viewModel.focusedProcess == null || service.viewModel.focusedProcess.configuration.processName == null ? 'The debug target' : service.viewModel.focusedProcess.configuration.processName) + ' is currently running.')); + const debuggerNotice = mode !== _constants().DebuggerMode.STOPPED && !passesMultiGK ? null : React.createElement("div", { + className: "padded" + }, React.createElement(_TruncatedButton().default, { + onClick: () => atom.commands.dispatch(atom.views.getView(atom.workspace), 'debugger:show-attach-dialog'), + icon: "nuclicon-debugger", + label: "Attach debugger..." + }), React.createElement(_TruncatedButton().default, { + onClick: () => atom.commands.dispatch(atom.views.getView(atom.workspace), 'debugger:show-launch-dialog'), + icon: "nuclicon-debugger", + label: "Launch debugger..." + }), this.state.hasDevicePanelService ? React.createElement(_TruncatedButton().default, { + onClick: () => (0, _goToLocation().goToLocation)(DEVICE_PANEL_URL), + icon: "device-mobile", + label: "Manage devices..." + }) : null); + return React.createElement("div", { + className: "debugger-container-new" + }, React.createElement("div", { + className: "debugger-section-header" + }, React.createElement(_DebuggerControllerView().default, { + service: service + })), React.createElement("div", { + className: "debugger-section-header debugger-controls-section" + }, React.createElement(_DebuggerSteppingComponent().default, { + service: service + })), debuggerRunningNotice, debuggerStoppedNotice, debuggerNotice); } + } + +exports.default = DebuggerControlsView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerDatatipComponent.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerDatatipComponent.js index acc853ee..5f103325 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerDatatipComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerDatatipComponent.js @@ -1,3 +1,56 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _LoadingSpinner() { + const data = require("../../../../../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _LazyNestedValueComponent() { + const data = require("../../../../../nuclide-commons-ui/LazyNestedValueComponent"); + + _LazyNestedValueComponent = function () { + return data; + }; + + return data; +} + +function _SimpleValueComponent() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/SimpleValueComponent")); + + _SimpleValueComponent = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("../utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,42 +59,39 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {EvaluationResult} from 'nuclide-commons-ui/TextRenderer'; - -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; -import * as React from 'react'; -import {LazyNestedValueComponent} from 'nuclide-commons-ui/LazyNestedValueComponent'; -import SimpleValueComponent from 'nuclide-commons-ui/SimpleValueComponent'; -import {fetchChildrenForLazyComponent} from '../utils'; - -type Props = {| - +expression: string, - +evaluationResult: ?EvaluationResult, -|}; - -export default class DebuggerDatatipComponent extends React.Component { - render(): React.Node { - const {expression, evaluationResult} = this.props; +class DebuggerDatatipComponent extends React.Component { + render() { + const { + expression, + evaluationResult + } = this.props; let datatipElement; + if (evaluationResult == null) { - datatipElement = ; + datatipElement = React.createElement(_LoadingSpinner().LoadingSpinner, { + delay: 100, + size: "EXTRA_SMALL" + }); } else { - datatipElement = ( - - - - ); + datatipElement = React.createElement("span", { + className: "debugger-datatip-value" + }, React.createElement(_LazyNestedValueComponent().LazyNestedValueComponent, { + evaluationResult: evaluationResult, + expression: expression, + fetchChildren: _utils().fetchChildrenForLazyComponent, + simpleValueComponent: _SimpleValueComponent().default, + expansionStateId: this + })); } - return
{datatipElement}
; + + return React.createElement("div", { + className: "debugger-datatip" + }, datatipElement); } + } + +exports.default = DebuggerDatatipComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerLaunchAttachUI.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerLaunchAttachUI.js index ee8cc248..3a84a6d1 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerLaunchAttachUI.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerLaunchAttachUI.js @@ -1,3 +1,88 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _ButtonGroup() { + const data = require("../../../../../nuclide-commons-ui/ButtonGroup"); + + _ButtonGroup = function () { + return data; + }; + + return data; +} + +function _Dropdown() { + const data = require("../../../../../nuclide-commons-ui/Dropdown"); + + _Dropdown = function () { + return data; + }; + + return data; +} + +function _Tabs() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/Tabs")); + + _Tabs = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _AtomServiceContainer() { + const data = require("../AtomServiceContainer"); + + _AtomServiceContainer = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,166 +91,103 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -/* global localStorage */ - -import type { - DebuggerConfigAction, - DebuggerLaunchAttachProvider, -} from 'nuclide-debugger-common'; -import type {Tab} from 'nuclide-commons-ui/Tabs'; - -import * as React from 'react'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {Button, ButtonTypes} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; -import {Dropdown} from 'nuclide-commons-ui/Dropdown'; -import Tabs from 'nuclide-commons-ui/Tabs'; -import {Observable} from 'rxjs'; -import invariant from 'assert'; -import {isNuclideEnvironment} from '../AtomServiceContainer'; - -type ConnectionOption = { - value: string, - label: string, -}; - -type EnabledProvider = {| - provider: DebuggerLaunchAttachProvider, - tabName: string, -|}; - -type Props = {| - +dialogMode: DebuggerConfigAction, - +initialSelectedTabName: ?string, - +initialProviderConfig: ?{[string]: mixed}, - +connection: string, - +connectionChanged: (newValue: ?string) => void, - // $FlowFixMe - +connectionOptions: Array, - +providers: Map>, - +dialogCloser: () => void, -|}; - -type State = { - selectedProviderTab: ?string, - configIsValid: boolean, - enabledProviders: Array, -}; +/* global localStorage */ // TODO those should be managed by the debugger store state -function setLastUsedDebugger( - host: string, - action: DebuggerConfigAction, - debuggerDisplayName: string, -): void { +function setLastUsedDebugger(host, action, debuggerDisplayName) { const key = 'DEBUGGER_LAST_USED_' + host + '_' + action; localStorage.setItem(key, debuggerDisplayName); } -function getLastUsedDebugger( - host: string, - action: DebuggerConfigAction, -): ?string { +function getLastUsedDebugger(host, action) { const key = 'DEBUGGER_LAST_USED_' + host + '_' + action; return localStorage.getItem(key); -} - -// Older published debugger packages did not provide `getTabName()`. +} // Older published debugger packages did not provide `getTabName()`. // TODO(most): Remove this once newer debugger versions get adoption. -function getTabName(provider: DebuggerLaunchAttachProvider): string { + + +function getTabName(provider) { + var _provider$_debuggingT; + if (typeof provider.getTabName === 'function') { return provider.getTabName(); } - return provider._debuggingTypeName ?? ''; -} -export default class DebuggerLaunchAttachUI extends React.Component< - Props, - State, -> { - props: Props; - state: State; - _disposables: UniversalDisposable; + return (_provider$_debuggingT = provider._debuggingTypeName) !== null && _provider$_debuggingT !== void 0 ? _provider$_debuggingT : ''; +} - constructor(props: Props) { +class DebuggerLaunchAttachUI extends React.Component { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); - this._disposables.add( - atom.commands.add('atom-workspace', { - 'core:confirm': () => { - if (this.state.configIsValid) { - this._rememberTab(); - - // Close the dialog, but do it on the next tick so that the child - // component gets to handle the event first (and start the debugger). - process.nextTick(this.props.dialogCloser); - } - }, - }), - atom.commands.add('atom-workspace', { - 'core:cancel': () => { - this._rememberTab(); - this.props.dialogCloser(); - }, - }), - ); + this._setConfigValid = valid => { + this.setState({ + configIsValid: valid + }); + }; + + this._disposables = new (_UniversalDisposable().default)(); + + this._disposables.add(atom.commands.add('atom-workspace', { + 'core:confirm': () => { + if (this.state.configIsValid) { + this._rememberTab(); // Close the dialog, but do it on the next tick so that the child + // component gets to handle the event first (and start the debugger). + + + process.nextTick(this.props.dialogCloser); + } + } + }), atom.commands.add('atom-workspace', { + 'core:cancel': () => { + this._rememberTab(); + + this.props.dialogCloser(); + } + })); this.state = { selectedProviderTab: null, configIsValid: false, - enabledProviders: [], + enabledProviders: [] }; } - _rememberTab(): void { + _rememberTab() { // Remember the last tab the user used for this connection when the "launch/attach" // button is clicked. - const host = nuclideUri.isRemote(this.props.connection) - ? nuclideUri.getHostname(this.props.connection) - : 'local'; + const host = _nuclideUri().default.isRemote(this.props.connection) ? _nuclideUri().default.getHostname(this.props.connection) : 'local'; + if (this.state.selectedProviderTab != null) { - setLastUsedDebugger( - host, - this.props.dialogMode, - this.state.selectedProviderTab || '', - ); + setLastUsedDebugger(host, this.props.dialogMode, this.state.selectedProviderTab || ''); } } UNSAFE_componentWillMount() { - const host = nuclideUri.isRemote(this.props.connection) - ? nuclideUri.getHostname(this.props.connection) - : 'local'; + const host = _nuclideUri().default.isRemote(this.props.connection) ? _nuclideUri().default.getHostname(this.props.connection) : 'local'; + const selectedProvider = (this.props.providers.get(host) || []).find(p => getTabName(p) === this.props.initialSelectedTabName); - const selectedProvider = (this.props.providers.get(host) || []).find( - p => getTabName(p) === this.props.initialSelectedTabName, - ); if (selectedProvider != null) { - setLastUsedDebugger( - host, - this.props.dialogMode, - getTabName(selectedProvider), - ); + setLastUsedDebugger(host, this.props.dialogMode, getTabName(selectedProvider)); } + this._filterProviders(host); + this.setState({ - selectedProviderTab: getLastUsedDebugger(host, this.props.dialogMode), + selectedProviderTab: getLastUsedDebugger(host, this.props.dialogMode) }); } - UNSAFE_componentWillReceiveProps(nextProps: Props) { - const host = nuclideUri.isRemote(nextProps.connection) - ? nuclideUri.getHostname(nextProps.connection) - : 'local'; + UNSAFE_componentWillReceiveProps(nextProps) { + const host = _nuclideUri().default.isRemote(nextProps.connection) ? _nuclideUri().default.getHostname(nextProps.connection) : 'local'; this._filterProviders(host); + this.setState({ - selectedProviderTab: getLastUsedDebugger(host, nextProps.dialogMode), + selectedProviderTab: getLastUsedDebugger(host, nextProps.dialogMode) }); } @@ -173,187 +195,127 @@ export default class DebuggerLaunchAttachUI extends React.Component< this._disposables.dispose(); } - async _getProviderIfEnabled( - provider: DebuggerLaunchAttachProvider, - ): Promise { - const enabled = await provider - .getCallbacksForAction(this.props.dialogMode) - .isEnabled(); + async _getProviderIfEnabled(provider) { + const enabled = await provider.getCallbacksForAction(this.props.dialogMode).isEnabled(); return enabled ? provider : null; } - _filterProviders(key: string): void { + _filterProviders(key) { this.setState({ - enabledProviders: [], + enabledProviders: [] }); - Observable.merge( - ...(this.props.providers.get(key) || []).map(provider => - Observable.fromPromise(this._getProviderIfEnabled(provider)), - ), - ) - .filter(provider => provider != null) - .map(provider => { - invariant(provider != null); - const tabName = getTabName(provider); - return { - provider, - tabName, - }; - }) - .scan((arr, provider) => arr.concat(provider), []) - .subscribe(enabledProviders => { - this.setState({enabledProviders}); - }); - } + _RxMin.Observable.merge(...(this.props.providers.get(key) || []).map(provider => _RxMin.Observable.fromPromise(this._getProviderIfEnabled(provider)))).filter(provider => provider != null).map(provider => { + if (!(provider != null)) { + throw new Error("Invariant violation: \"provider != null\""); + } - _setConfigValid = (valid: boolean): void => { - this.setState({ - configIsValid: valid, + const tabName = getTabName(provider); + return { + provider, + tabName + }; + }).scan((arr, provider) => arr.concat(provider), []).subscribe(enabledProviders => { + this.setState({ + enabledProviders + }); }); - }; + } - _getTabsFromEnabledProviders(enabledProviders: EnabledProvider[]): Tab[] { - const tabs = this.state.enabledProviders - .map(debuggerType => ({ - name: debuggerType.tabName, - tabContent: ( - - {debuggerType.tabName} - - ), - })) - .sort((a, b) => a.name.localeCompare(b.name)); + _getTabsFromEnabledProviders(enabledProviders) { + const tabs = this.state.enabledProviders.map(debuggerType => ({ + name: debuggerType.tabName, + tabContent: React.createElement("span", { + title: debuggerType.tabName, + className: "debugger-provider-tab" + }, debuggerType.tabName) + })).sort((a, b) => a.name.localeCompare(b.name)); return tabs; } - setState( - partialState: $Shape | ((State, Props) => $Shape | void), - callback?: () => mixed, - ): void { + setState(partialState, callback) { if (typeof partialState === 'function') { super.setState(partialState, callback); } else { - const fullState = { - ...this.state, - ...partialState, - }; + const fullState = Object.assign({}, this.state, partialState); + if (fullState.selectedProviderTab == null) { - const tabs = this._getTabsFromEnabledProviders( - fullState.enabledProviders, - ); + const tabs = this._getTabsFromEnabledProviders(fullState.enabledProviders); + if (tabs.length > 0) { const firstTab = tabs[0]; fullState.selectedProviderTab = firstTab.name; } } + super.setState(fullState, callback); } } - render(): React.Node { + render() { const tabs = this._getTabsFromEnabledProviders(this.state.enabledProviders); + let providerContent = null; + if (tabs.length > 0) { - let selectedTab = - this.state.selectedProviderTab != null - ? this.state.selectedProviderTab - : this.state.enabledProviders[0].tabName; - let provider = this.state.enabledProviders.find( - p => p.tabName === selectedTab, - ); + let selectedTab = this.state.selectedProviderTab != null ? this.state.selectedProviderTab : this.state.enabledProviders[0].tabName; + let provider = this.state.enabledProviders.find(p => p.tabName === selectedTab); + if (provider == null) { provider = this.state.enabledProviders[0]; selectedTab = provider.tabName; } - const defaultConfig = - selectedTab != null && selectedTab === this.props.initialSelectedTabName - ? this.props.initialProviderConfig - : null; - - const debuggerConfigPage = provider.provider - .getCallbacksForAction(this.props.dialogMode) - .getComponent( - selectedTab, - valid => this._setConfigValid(valid), - defaultConfig, - ); - - providerContent = ( -
- { - this._setConfigValid(false); - this.setState({selectedProviderTab: newTab.name}); - }} - /> -
- {debuggerConfigPage} -
-
- ); + const defaultConfig = selectedTab != null && selectedTab === this.props.initialSelectedTabName ? this.props.initialProviderConfig : null; + const debuggerConfigPage = provider.provider.getCallbacksForAction(this.props.dialogMode).getComponent(selectedTab, valid => this._setConfigValid(valid), defaultConfig); + providerContent = React.createElement("div", null, React.createElement(_Tabs().default, { + className: "debugger-launch-attach-tabs", + tabs: tabs, + growable: true, + activeTabName: this.state.selectedProviderTab, + triggeringEvent: "onClick", + onActiveTabChange: newTab => { + this._setConfigValid(false); + + this.setState({ + selectedProviderTab: newTab.name + }); + } + }), React.createElement("div", { + className: "debugger-launch-attach-tabcontent" + }, debuggerConfigPage)); } else { // No debugging providers available. - providerContent = ( -
- No debuggers installed, look for available debuggers on{' '} - - atom.io/packages - -
- ); + providerContent = React.createElement("div", { + className: "debugger-launch-attach-tabcontent" + }, "No debuggers installed, look for available debuggers on", ' ', React.createElement("a", { + href: "https://atom.io/packages/search?q=atom-ide-debugger-" + }, "atom.io/packages")); } - return ( -
- {isNuclideEnvironment() ? ( -

- - {this.props.dialogMode === 'attach' - ? 'Attach debugger to ' - : 'Launch debugger on '} - - this.props.connectionChanged(value)} - size="xs" - value={this.props.connection} - /> -

- ) : null} - {providerContent} -
- - - - -
-
- ); + return React.createElement("div", { + className: "padded debugger-launch-attach-container" + }, (0, _AtomServiceContainer().isNuclideEnvironment)() ? React.createElement("h1", { + className: "debugger-launch-attach-header" + }, React.createElement("span", { + className: "padded" + }, this.props.dialogMode === 'attach' ? 'Attach debugger to ' : 'Launch debugger on '), React.createElement(_Dropdown().Dropdown, { + className: "inline", + options: this.props.connectionOptions, + onChange: value => this.props.connectionChanged(value), + size: "xs", + value: this.props.connection + })) : null, providerContent, React.createElement("div", { + className: "debugger-launch-attach-actions" + }, React.createElement(_ButtonGroup().ButtonGroup, null, React.createElement(_Button().Button, { + onClick: () => atom.commands.dispatch(atom.views.getView(atom.workspace), 'core:cancel') + }, "Cancel"), React.createElement(_Button().Button, { + buttonType: _Button().ButtonTypes.PRIMARY, + disabled: !this.state.configIsValid, + onClick: () => atom.commands.dispatch(atom.views.getView(atom.workspace), 'core:confirm') + }, this.props.dialogMode === 'attach' ? 'Attach' : 'Launch')))); } + } + +exports.default = DebuggerLaunchAttachUI; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerLayoutManager.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerLayoutManager.js index 0a03cc5d..5240c6a1 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerLayoutManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerLayoutManager.js @@ -1,3 +1,156 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _DebuggerPaneViewModel() { + const data = _interopRequireDefault(require("./DebuggerPaneViewModel")); + + _DebuggerPaneViewModel = function () { + return data; + }; + + return data; +} + +function _DebuggerPaneContainerViewModel() { + const data = _interopRequireDefault(require("./DebuggerPaneContainerViewModel")); + + _DebuggerPaneContainerViewModel = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _createPaneContainer() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-atom/create-pane-container")); + + _createPaneContainer = function () { + return data; + }; + + return data; +} + +function _destroyItemWhere() { + const data = require("../../../../../nuclide-commons-atom/destroyItemWhere"); + + _destroyItemWhere = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _DebuggerControlsView() { + const data = _interopRequireDefault(require("./DebuggerControlsView")); + + _DebuggerControlsView = function () { + return data; + }; + + return data; +} + +function _ThreadsView() { + const data = _interopRequireDefault(require("./ThreadsView")); + + _ThreadsView = function () { + return data; + }; + + return data; +} + +function _DebuggerProcessTreeView() { + const data = _interopRequireDefault(require("./DebuggerProcessTreeView")); + + _DebuggerProcessTreeView = function () { + return data; + }; + + return data; +} + +function _DebuggerCallstackComponent() { + const data = _interopRequireDefault(require("./DebuggerCallstackComponent")); + + _DebuggerCallstackComponent = function () { + return data; + }; + + return data; +} + +function _BreakpointsView() { + const data = _interopRequireDefault(require("./BreakpointsView")); + + _BreakpointsView = function () { + return data; + }; + + return data; +} + +function _ScopesView() { + const data = _interopRequireDefault(require("./ScopesView")); + + _ScopesView = function () { + return data; + }; + + return data; +} + +function _WatchView() { + const data = _interopRequireDefault(require("./WatchView")); + + _WatchView = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,288 +159,184 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -/* global localStorage */ - -import type {DebuggerModeType, IDebugService, SerializedState} from '../types'; -import type {GatekeeperService} from 'nuclide-commons-atom/types'; - -import * as React from 'react'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import DebuggerPaneViewModel from './DebuggerPaneViewModel'; -import DebuggerPaneContainerViewModel from './DebuggerPaneContainerViewModel'; -import {DebuggerMode, DEBUGGER_PANELS_DEFAULT_LOCATION} from '../constants'; -import invariant from 'assert'; -import createPaneContainer from 'nuclide-commons-atom/create-pane-container'; -import {destroyItemWhere} from 'nuclide-commons-atom/destroyItemWhere'; -import nullthrows from 'nullthrows'; +/* global localStorage */ // Debugger views -import DebuggerControlsView from './DebuggerControlsView'; -import ThreadsView from './ThreadsView'; -import DebuggerProcessTreeView from './DebuggerProcessTreeView'; -import DebuggerCallstackComponent from './DebuggerCallstackComponent'; -import BreakpointsView from './BreakpointsView'; -import ScopesView from './ScopesView'; -import WatchView from './WatchView'; - const CONSOLE_VIEW_URI = 'atom://nuclide/console'; const DEBUGGER_URI_BASE = 'atom://nuclide/debugger-'; -export type DebuggerPaneLocation = { - dock: string, - layoutIndex: number, - userHidden: boolean, - userCustomized?: boolean, -}; - -// Configuration that defines a debugger pane. This controls what gets added -// to the workspace when starting debugging. -export type DebuggerPaneConfig = { - // Each pane must provide a unique URI. - uri: string, - - // Function that returns the title for the pane. Some panes (like Threads) need - // to change their title depending on the debug target (ex "Threads" for C++ but - // "Requests" for PHP). - title: () => string, - - // Optional function that indicates if the pane is enabled for the current debug - // session. If not enabled, the pane won't be added to the workspace. - isEnabled?: () => boolean, - - // Boolean indicating if the debug session lifetime should be tied to this view. - // If true, the debug session will be terminated if this view is destroyed. - isLifetimeView: boolean, - - // Function that returns a view for Atom to use for the workspace pane. - createView: () => React.Element, - - // Optional filter function that lets panes specify that they should be shown - // or hidden depending on the debugger mode (ex don't show threads when stopped). - debuggerModeFilter?: (mode: DebuggerModeType) => boolean, - - // Structure to remember the pane's previous location if the user moved it around. - previousLocation?: ?DebuggerPaneLocation, - - // Location to use for layout if no user previous location is set. - defaultLocation: string, - - // Previous default location, used to track if the saved location was not - // explicitly chosen by the user. - previousDefaultLocation?: string, - - // Optional callback to be invoked when the pane is being resized (flex scale changed). - onPaneResize?: (pane: atom$Pane, newFlexScale: number) => boolean, -}; - -let _gkService: ?GatekeeperService; - -export default class DebuggerLayoutManager { - _disposables: UniversalDisposable; - _service: IDebugService; - _debuggerPanes: Array; - _previousDebuggerMode: DebuggerModeType; - _paneHiddenWarningShown: boolean; - _leftPaneContainerModel: ?DebuggerPaneContainerViewModel; - _rightPaneContainerModel: ?DebuggerPaneContainerViewModel; - _debuggerVisible: boolean; - _isInMultiGK: boolean; - - constructor(service: IDebugService, state: ?SerializedState) { - this._disposables = new UniversalDisposable(); +let _gkService; + +class DebuggerLayoutManager { + constructor(service, state) { + this._disposables = new (_UniversalDisposable().default)(); this._service = service; - this._previousDebuggerMode = DebuggerMode.STOPPED; + this._previousDebuggerMode = _constants().DebuggerMode.STOPPED; this._paneHiddenWarningShown = false; this._leftPaneContainerModel = null; this._rightPaneContainerModel = null; this._debuggerVisible = false; + this._initializeDebuggerPanes(); + this._reshowDebuggerPanes(state); + this._isInMultiGK = false; this._disposables.add(() => { if (this._leftPaneContainerModel != null) { this._leftPaneContainerModel.dispose(); } + if (this._rightPaneContainerModel != null) { this._rightPaneContainerModel.dispose(); } }); } - dispose(): void { + dispose() { this._disposables.dispose(); } - registerContextMenus(): void { + registerContextMenus() { // Add context menus to let the user restore hidden panes. this._debuggerPanes.forEach(pane => { const command = `debugger:show-window-${pane.title().replace(/ /g, '-')}`; - this._disposables.add( - atom.commands.add('atom-workspace', { - [String(command)]: () => this.showHiddenDebuggerPane(pane.uri), - }), - ); - - this._disposables.add( - atom.contextMenu.add({ - '.debugger-container': [ - { - label: 'Debugger Views', - submenu: [ - { - label: `Show ${pane.title()} window`, - command, - shouldDisplay: event => { - const debuggerPane = this._debuggerPanes.find( - p => p.uri === pane.uri, - ); - if ( - debuggerPane != null && - (debuggerPane.isEnabled == null || - debuggerPane.isEnabled()) - ) { - return ( - debuggerPane.previousLocation != null && - debuggerPane.previousLocation.userHidden - ); - } - return false; - }, - }, - ], - }, - ], - }), - ); + + this._disposables.add(atom.commands.add('atom-workspace', { + [String(command)]: () => this.showHiddenDebuggerPane(pane.uri) + })); + + this._disposables.add(atom.contextMenu.add({ + '.debugger-container': [{ + label: 'Debugger Views', + submenu: [{ + label: `Show ${pane.title()} window`, + command, + shouldDisplay: event => { + const debuggerPane = this._debuggerPanes.find(p => p.uri === pane.uri); + + if (debuggerPane != null && (debuggerPane.isEnabled == null || debuggerPane.isEnabled())) { + return debuggerPane.previousLocation != null && debuggerPane.previousLocation.userHidden; + } + + return false; + } + }] + }] + })); }); } - _overridePaneInitialHeight( - dockPane: atom$Pane, - newFlexScale: number, - desiredHeight: number, - ): void { - invariant(dockPane.element != null); + _overridePaneInitialHeight(dockPane, newFlexScale, desiredHeight) { + if (!(dockPane.element != null)) { + throw new Error("Invariant violation: \"dockPane.element != null\""); + } if (newFlexScale === 1) { // newFlexScale === 1 when the pane is added the first time. // $FlowFixMe - dockPane.element.style['flex-grow'] = '0'; - // $FlowFixMe - dockPane.element.style['flex-basis'] = 'auto'; - // $FlowFixMe - dockPane.element.style['overflow-y'] = 'scroll'; - // $FlowFixMe + dockPane.element.style['flex-grow'] = '0'; // $FlowFixMe + + dockPane.element.style['flex-basis'] = 'auto'; // $FlowFixMe + + dockPane.element.style['overflow-y'] = 'scroll'; // $FlowFixMe + dockPane.element.style['min-height'] = String(desiredHeight) + 'px'; } else { // Otherwise, the user must have resized the pane. Remove the override styles // and let it behave normally, the user is in control of the layout now. // $FlowFixMe - dockPane.element.style['min-height'] = '0px'; - // $FlowFixMe + dockPane.element.style['min-height'] = '0px'; // $FlowFixMe + dockPane.element.style['flex-basis'] = ''; } } - _initializeDebuggerPanes(): void { - const getFocusedProcess = () => this._service.viewModel.focusedProcess; - - // This configures the debugger panes. By default, they'll appear below the stepping + _initializeDebuggerPanes() { + const getFocusedProcess = () => this._service.viewModel.focusedProcess; // This configures the debugger panes. By default, they'll appear below the stepping // controls from top to bottom in the order they're defined here. After that, the // user is free to move them around. - this._debuggerPanes = [ - { - uri: DEBUGGER_URI_BASE + 'controls', - isLifetimeView: true, - title: () => 'Debugger', - defaultLocation: DEBUGGER_PANELS_DEFAULT_LOCATION, - isEnabled: () => true, - createView: () => ( - - ), - onPaneResize: (dockPane, newFlexScale) => { - // If the debugger is stopped, let the controls pane keep its default - // layout to make room for the buttons and additional content. Otherwise, - // override the layout to shrink the pane and remove extra vertical whitespace. - const debuggerMode = this._service.getDebuggerMode(); - if (debuggerMode !== DebuggerMode.STOPPED) { - this._overridePaneInitialHeight(dockPane, newFlexScale, 250); - } - // If newFlexScale !== 1, that means the user must have resized this pane. - // Return true to unhook this callback and let the pane resize per Atom's - // default behavior. The user is now responsible for the pane's height. - return newFlexScale !== 1; - }, - }, - { - uri: DEBUGGER_URI_BASE + 'callstack', - isLifetimeView: false, - defaultLocation: DEBUGGER_PANELS_DEFAULT_LOCATION, - title: () => 'Call Stack', - isEnabled: () => true, - createView: () => ( - - ), - debuggerModeFilter: (mode: DebuggerModeType) => - mode !== DebuggerMode.STOPPED, - }, - { - uri: DEBUGGER_URI_BASE + 'breakpoints', - isLifetimeView: false, - defaultLocation: DEBUGGER_PANELS_DEFAULT_LOCATION, - title: () => 'Breakpoints', - isEnabled: () => true, - createView: () => , - }, - { - uri: DEBUGGER_URI_BASE + 'scopes', - isLifetimeView: false, - defaultLocation: DEBUGGER_PANELS_DEFAULT_LOCATION, - title: () => 'Scopes', - isEnabled: () => true, - createView: () => , - debuggerModeFilter: (mode: DebuggerModeType) => - mode !== DebuggerMode.STOPPED, - }, - { - uri: DEBUGGER_URI_BASE + 'watch-expressions', - isLifetimeView: false, - defaultLocation: 'bottom', - previousDefaultLocation: DEBUGGER_PANELS_DEFAULT_LOCATION, - title: () => 'Watch Expressions', - isEnabled: () => true, - createView: () => , - }, - { - uri: DEBUGGER_URI_BASE + 'threads', - isLifetimeView: false, - defaultLocation: DEBUGGER_PANELS_DEFAULT_LOCATION, - title: () => - getFocusedProcess() == null - ? 'Threads' - : nullthrows(getFocusedProcess()).configuration - .threadsComponentTitle || 'Threads', - isEnabled: () => - getFocusedProcess() == null - ? false - : nullthrows(getFocusedProcess()).configuration.showThreads == null - ? true - : Boolean( - nullthrows(getFocusedProcess()).configuration.showThreads, - ), - createView: () => , - debuggerModeFilter: (mode: DebuggerModeType) => - mode !== DebuggerMode.STOPPED, - }, - ]; + + this._debuggerPanes = [{ + uri: DEBUGGER_URI_BASE + 'controls', + isLifetimeView: true, + title: () => 'Debugger', + defaultLocation: _constants().DEBUGGER_PANELS_DEFAULT_LOCATION, + isEnabled: () => true, + createView: () => React.createElement(_DebuggerControlsView().default, { + service: this._service, + passesMultiGK: this._isInMultiGK + }), + onPaneResize: (dockPane, newFlexScale) => { + // If the debugger is stopped, let the controls pane keep its default + // layout to make room for the buttons and additional content. Otherwise, + // override the layout to shrink the pane and remove extra vertical whitespace. + const debuggerMode = this._service.getDebuggerMode(); + + if (debuggerMode !== _constants().DebuggerMode.STOPPED) { + this._overridePaneInitialHeight(dockPane, newFlexScale, 250); + } // If newFlexScale !== 1, that means the user must have resized this pane. + // Return true to unhook this callback and let the pane resize per Atom's + // default behavior. The user is now responsible for the pane's height. + + + return newFlexScale !== 1; + } + }, { + uri: DEBUGGER_URI_BASE + 'callstack', + isLifetimeView: false, + defaultLocation: _constants().DEBUGGER_PANELS_DEFAULT_LOCATION, + title: () => 'Call Stack', + isEnabled: () => true, + createView: () => React.createElement(_DebuggerCallstackComponent().default, { + service: this._service + }), + debuggerModeFilter: mode => mode !== _constants().DebuggerMode.STOPPED + }, { + uri: DEBUGGER_URI_BASE + 'breakpoints', + isLifetimeView: false, + defaultLocation: _constants().DEBUGGER_PANELS_DEFAULT_LOCATION, + title: () => 'Breakpoints', + isEnabled: () => true, + createView: () => React.createElement(_BreakpointsView().default, { + service: this._service + }) + }, { + uri: DEBUGGER_URI_BASE + 'scopes', + isLifetimeView: false, + defaultLocation: _constants().DEBUGGER_PANELS_DEFAULT_LOCATION, + title: () => 'Scopes', + isEnabled: () => true, + createView: () => React.createElement(_ScopesView().default, { + service: this._service + }), + debuggerModeFilter: mode => mode !== _constants().DebuggerMode.STOPPED + }, { + uri: DEBUGGER_URI_BASE + 'watch-expressions', + isLifetimeView: false, + defaultLocation: 'bottom', + previousDefaultLocation: _constants().DEBUGGER_PANELS_DEFAULT_LOCATION, + title: () => 'Watch Expressions', + isEnabled: () => true, + createView: () => React.createElement(_WatchView().default, { + service: this._service + }) + }, { + uri: DEBUGGER_URI_BASE + 'threads', + isLifetimeView: false, + defaultLocation: _constants().DEBUGGER_PANELS_DEFAULT_LOCATION, + title: () => getFocusedProcess() == null ? 'Threads' : (0, _nullthrows().default)(getFocusedProcess()).configuration.threadsComponentTitle || 'Threads', + isEnabled: () => getFocusedProcess() == null ? false : (0, _nullthrows().default)(getFocusedProcess()).configuration.showThreads == null ? true : Boolean((0, _nullthrows().default)(getFocusedProcess()).configuration.showThreads), + createView: () => React.createElement(_ThreadsView().default, { + service: this._service + }), + debuggerModeFilter: mode => mode !== _constants().DebuggerMode.STOPPED + }]; if (_gkService != null) { this.convertToDebuggerTreePanes(); @@ -296,43 +345,29 @@ export default class DebuggerLayoutManager { this._restoreDebuggerPaneLocations(); } - _reshowDebuggerPanes(state: ?SerializedState): void { + _reshowDebuggerPanes(state) { if (state && state.showDebugger) { this.showDebuggerViews(); + this._getWorkspaceDocks().forEach((dock, index) => { - if ( - dock.dock.isVisible != null && - state.workspaceDocksVisibility != null && - !state.workspaceDocksVisibility[index] && - dock.dock.isVisible() && - dock.dock.hide != null - ) { + if (dock.dock.isVisible != null && state.workspaceDocksVisibility != null && !state.workspaceDocksVisibility[index] && dock.dock.isVisible() && dock.dock.hide != null) { dock.dock.hide(); } - }); - - // Hiding the docks might have changed the visibility of the debugger + }); // Hiding the docks might have changed the visibility of the debugger // if the only docks containing debugger panes are now hidden. + + this._updateDebuggerVisibility(); } } - _updateDebuggerVisibility(): void { - this._debuggerVisible = false; + _updateDebuggerVisibility() { + this._debuggerVisible = false; // See if any visible docks contain a pane that contains a debugger pane. - // See if any visible docks contain a pane that contains a debugger pane. this._getWorkspaceDocks().forEach(dock => { if (dock.dock.isVisible != null && dock.dock.isVisible()) { dock.dock.getPanes().forEach(pane => { - if ( - pane - .getItems() - .find( - item => - item instanceof DebuggerPaneViewModel || - item instanceof DebuggerPaneContainerViewModel, - ) != null - ) { + if (pane.getItems().find(item => item instanceof _DebuggerPaneViewModel().default || item instanceof _DebuggerPaneContainerViewModel().default) != null) { this._debuggerVisible = true; } }); @@ -340,8 +375,9 @@ export default class DebuggerLayoutManager { }); } - showHiddenDebuggerPane(uri: string): void { + showHiddenDebuggerPane(uri) { const pane = this._debuggerPanes.find(p => p.uri === uri); + if (pane != null && pane.previousLocation != null) { pane.previousLocation.userHidden = false; } @@ -349,52 +385,58 @@ export default class DebuggerLayoutManager { this.showDebuggerViews(); } - getModelForDebuggerUri(uri: string): any { + getModelForDebuggerUri(uri) { const config = this._debuggerPanes.find(pane => pane.uri === uri); + if (config != null) { - return new DebuggerPaneViewModel(config, config.isLifetimeView, pane => - this._paneDestroyed(pane), - ); + return new (_DebuggerPaneViewModel().default)(config, config.isLifetimeView, pane => this._paneDestroyed(pane)); } return null; } - _getWorkspaceDocks(): Array<{ - name: string, - dock: atom$AbstractPaneContainer, - orientation: string, - }> { + _getWorkspaceDocks() { const docks = new Array(4); - invariant(atom.workspace.getLeftDock != null); + if (!(atom.workspace.getLeftDock != null)) { + throw new Error("Invariant violation: \"atom.workspace.getLeftDock != null\""); + } + docks[0] = { name: 'left', dock: atom.workspace.getLeftDock(), - orientation: 'vertical', + orientation: 'vertical' }; - invariant(atom.workspace.getBottomDock != null); + if (!(atom.workspace.getBottomDock != null)) { + throw new Error("Invariant violation: \"atom.workspace.getBottomDock != null\""); + } + docks[1] = { name: 'bottom', dock: atom.workspace.getBottomDock(), - orientation: 'horizontal', + orientation: 'horizontal' }; - invariant(atom.workspace.getCenter != null); + if (!(atom.workspace.getCenter != null)) { + throw new Error("Invariant violation: \"atom.workspace.getCenter != null\""); + } + docks[2] = { name: 'center', dock: atom.workspace.getCenter(), - orientation: 'horizontal', + orientation: 'horizontal' }; - invariant(atom.workspace.getRightDock != null); + if (!(atom.workspace.getRightDock != null)) { + throw new Error("Invariant violation: \"atom.workspace.getRightDock != null\""); + } + docks[3] = { name: 'right', dock: atom.workspace.getRightDock(), - orientation: 'vertical', + orientation: 'vertical' }; - return docks; } @@ -405,24 +447,23 @@ export default class DebuggerLayoutManager { this._debuggerPanes.splice(1, 0, { uri: DEBUGGER_URI_BASE + 'debuggertree', isLifetimeView: false, - defaultLocation: DEBUGGER_PANELS_DEFAULT_LOCATION, + defaultLocation: _constants().DEBUGGER_PANELS_DEFAULT_LOCATION, title: () => 'Processes', isEnabled: () => true, - createView: () => ( - - ), - debuggerModeFilter: (mode: DebuggerModeType) => - mode !== DebuggerMode.STOPPED, + createView: () => React.createElement(_DebuggerProcessTreeView().default, { + service: this._service + }), + debuggerModeFilter: mode => mode !== _constants().DebuggerMode.STOPPED }); + for (let i = 0; i < this._debuggerPanes.length; i++) { const uri = this._debuggerPanes[i].uri; - if ( - uri === DEBUGGER_URI_BASE + 'callstack' || - uri === DEBUGGER_URI_BASE + 'threads' - ) { + + if (uri === DEBUGGER_URI_BASE + 'callstack' || uri === DEBUGGER_URI_BASE + 'threads') { this._debuggerPanes.splice(i, 1); } } + if (this._debuggerVisible) { this.showDebuggerViews(); } @@ -431,56 +472,56 @@ export default class DebuggerLayoutManager { } } - consumeGatekeeperService(service: GatekeeperService): UniversalDisposable { + consumeGatekeeperService(service) { _gkService = service; this.convertToDebuggerTreePanes(); + if (_gkService != null) { _gkService.passesGK('nuclide_multitarget_debugging').then(passes => { this._isInMultiGK = passes; }); } - return new UniversalDisposable(() => (_gkService = null)); - } - _isDockEmpty(dock: atom$AbstractPaneContainer): boolean { - const panes = dock.getPanes(); + return new (_UniversalDisposable().default)(() => _gkService = null); + } - // A dock is empty for our purposes if it has nothing visible in it. If a dock + _isDockEmpty(dock) { + const panes = dock.getPanes(); // A dock is empty for our purposes if it has nothing visible in it. If a dock // with no items is left open, Atom implicitly adds a single pane with no items // in it, so check for no panes, or a single pane with no items. - return ( - panes.length === 0 || - (panes.length === 1 && panes[0].getItems().length === 0) - ); + + return panes.length === 0 || panes.length === 1 && panes[0].getItems().length === 0; } - _appendItemToDock( - paneConfig: ?DebuggerPaneConfig, - dock: atom$AbstractPaneContainer, - item: Object, - debuggerItemsPerDock: Map, - ): void { + _appendItemToDock(paneConfig, dock, item, debuggerItemsPerDock) { const panes = dock.getPanes(); - invariant(panes.length >= 1); + + if (!(panes.length >= 1)) { + throw new Error("Invariant violation: \"panes.length >= 1\""); + } const dockPane = panes[panes.length - 1]; + if (this._isDockEmpty(dock)) { dockPane.addItem(item); } else { let dockConfig = this._getWorkspaceDocks().find(d => d.dock === dock); + if (dockConfig == null) { // This item is being added to a nested PaneContainer rather than // directly to a dock. This is only done for vertical layouts. - dockConfig = {orientation: 'vertical'}; + dockConfig = { + orientation: 'vertical' + }; } if (dockConfig.orientation === 'horizontal') { // Add the item as a new tab in the existing pane to the right of the current active pane for the dock. dockPane.addItem(item); + try { dockPane.activateItem(item); - } catch (e) { - // During testing, I saw some cases where Atom threw trying to activate an item + } catch (e) {// During testing, I saw some cases where Atom threw trying to activate an item // that was still in progress of being added. This was tested on a Beta release // and may indicate a temporary bug. However, there is no reason to throw here // and stop laying out the debugger if an item could not be set as active. @@ -494,16 +535,16 @@ export default class DebuggerLayoutManager { dockPane.activateItem(item); } else { dockPane.splitDown({ - items: [item], + items: [item] }); } } - } - - // Keep track of which dock(s) we've appended debugger panes into. This + } // Keep track of which dock(s) we've appended debugger panes into. This // allows us to quickly check if the dock needs to be split to separate // debugger panes and pre-existing panes that have nothing to do with // the debugger. + + if (debuggerItemsPerDock.get(dock) == null) { debuggerItemsPerDock.set(dock, 1); } else { @@ -513,62 +554,66 @@ export default class DebuggerLayoutManager { if (dock.isVisible != null && dock.show != null && !dock.isVisible()) { dock.show(); - } + } // If the debugger pane config has a custom layout callback, hook it up now. + - // If the debugger pane config has a custom layout callback, hook it up now. if (paneConfig != null && paneConfig.onPaneResize != null) { - const disposables = new UniversalDisposable(); + const disposables = new (_UniversalDisposable().default)(); disposables.add(dockPane.onWillDestroy(() => disposables.dispose())); - disposables.add( - dockPane.onDidChangeFlexScale(newFlexScale => { - invariant(paneConfig.onPaneResize != null); - if (paneConfig.onPaneResize(dockPane, newFlexScale)) { - // The callback has requested to be unregistered. - disposables.dispose(); - } - }), - ); + disposables.add(dockPane.onDidChangeFlexScale(newFlexScale => { + if (!(paneConfig.onPaneResize != null)) { + throw new Error("Invariant violation: \"paneConfig.onPaneResize != null\""); + } + + if (paneConfig.onPaneResize(dockPane, newFlexScale)) { + // The callback has requested to be unregistered. + disposables.dispose(); + } + })); } } - resetLayout(): void { + resetLayout() { // Remove all debugger panes from the UI. - this.hideDebuggerViews(false); + this.hideDebuggerViews(false); // Forget all their previous locations. - // Forget all their previous locations. for (const debuggerPane of this._debuggerPanes) { debuggerPane.previousLocation = null; + const key = this._getPaneStorageKey(debuggerPane.uri); + localStorage.setItem(key, ''); - } + } // Forget all previous dock sizes; + - // Forget all previous dock sizes; for (const dockInfo of this._getWorkspaceDocks()) { - const {name} = dockInfo; + const { + name + } = dockInfo; + const key = this._getPaneStorageKey('dock-size' + name); + localStorage.removeItem(key); - } + } // Pop the debugger open with the default layout. + - // Pop the debugger open with the default layout. this._debuggerPanes = []; this._paneHiddenWarningShown = false; + this._initializeDebuggerPanes(); + this.showDebuggerViews(); } - _getPaneStorageKey(uri: string): string { + _getPaneStorageKey(uri) { return 'debugger-pane-location-' + uri; } - _deserializeSavedLocation(savedItem: string): ?DebuggerPaneLocation { + _deserializeSavedLocation(savedItem) { try { const obj = JSON.parse(savedItem); - if ( - obj != null && - obj.dock != null && - obj.layoutIndex != null && - obj.userHidden != null - ) { + + if (obj != null && obj.dock != null && obj.layoutIndex != null && obj.userHidden != null) { return obj; } } catch (e) {} @@ -576,43 +621,44 @@ export default class DebuggerLayoutManager { return null; } - _restoreDebuggerPaneLocations(): void { + _restoreDebuggerPaneLocations() { // See if there are saved previous locations for the debugger panes. for (const debuggerPane of this._debuggerPanes) { - const savedItem = localStorage.getItem( - this._getPaneStorageKey(debuggerPane.uri), - ); + const savedItem = localStorage.getItem(this._getPaneStorageKey(debuggerPane.uri)); + if (savedItem != null) { - debuggerPane.previousLocation = this._deserializeSavedLocation( - savedItem, - ); + debuggerPane.previousLocation = this._deserializeSavedLocation(savedItem); } } } - _saveDebuggerPaneLocations(): void { + _saveDebuggerPaneLocations() { for (const dockInfo of this._getWorkspaceDocks()) { - const {name, dock} = dockInfo; + const { + name, + dock + } = dockInfo; const panes = dock.getPanes(); let layoutIndex = 0; let dockContainsDebuggerItem = false; + for (const pane of panes) { for (const item of pane.getItems()) { const paneItems = []; - if (item instanceof DebuggerPaneContainerViewModel) { + + if (item instanceof _DebuggerPaneContainerViewModel().default) { paneItems.push(...item.getAllItems()); } else { paneItems.push(item); } for (const itemToSave of paneItems) { - if (itemToSave instanceof DebuggerPaneViewModel) { + if (itemToSave instanceof _DebuggerPaneViewModel().default) { const location = { dock: name, layoutIndex, - userHidden: false, + userHidden: false }; - dockContainsDebuggerItem = true; itemToSave.getConfig().previousLocation = location; layoutIndex++; @@ -622,6 +668,7 @@ export default class DebuggerLayoutManager { } const key = this._getPaneStorageKey('dock-size' + name); + if (dockContainsDebuggerItem && dock.state != null) { // Save the size of a dock only if it contains a debugger item. const sizeInfo = JSON.stringify(dock.state.size); @@ -629,67 +676,56 @@ export default class DebuggerLayoutManager { } else { localStorage.removeItem(key); } - } + } // Serialize to storage. - // Serialize to storage. - for (const debuggerPane of this._debuggerPanes) { - const key = this._getPaneStorageKey(debuggerPane.uri); - // If the location is the pane's default location, no need to store + for (const debuggerPane of this._debuggerPanes) { + const key = this._getPaneStorageKey(debuggerPane.uri); // If the location is the pane's default location, no need to store // it explicitly. This is also helpful if the default changes in the // future. - if ( - debuggerPane.previousLocation != null && - !debuggerPane.previousLocation.userHidden && - (debuggerPane.previousLocation.dock === debuggerPane.defaultLocation || - (debuggerPane.previousLocation.dock === - debuggerPane.previousDefaultLocation && - !debuggerPane.previousLocation.userCustomized)) - ) { + + + if (debuggerPane.previousLocation != null && !debuggerPane.previousLocation.userHidden && (debuggerPane.previousLocation.dock === debuggerPane.defaultLocation || debuggerPane.previousLocation.dock === debuggerPane.previousDefaultLocation && !debuggerPane.previousLocation.userCustomized)) { localStorage.removeItem(key); } else { if (debuggerPane.previousLocation != null) { debuggerPane.previousLocation.userCustomized = true; } + const loc = JSON.stringify(debuggerPane.previousLocation); localStorage.setItem(key, loc); } } } - _shouldDestroyPaneItem(mode: DebuggerModeType, item: atom$PaneItem): boolean { - if (item instanceof DebuggerPaneViewModel) { + _shouldDestroyPaneItem(mode, item) { + if (item instanceof _DebuggerPaneViewModel().default) { const config = item.getConfig(); - if ( - config.debuggerModeFilter != null && - !config.debuggerModeFilter(mode) - ) { + + if (config.debuggerModeFilter != null && !config.debuggerModeFilter(mode)) { item.setRemovedFromLayout(true); return true; } } + return false; } - debuggerModeChanged(): void { - const mode = this._service.getDebuggerMode(); - - // Most panes disappear when the debugger is stopped, only keep + debuggerModeChanged() { + const mode = this._service.getDebuggerMode(); // Most panes disappear when the debugger is stopped, only keep // the ones that should still be shown. - if ( - mode === DebuggerMode.STOPPING && - this._previousDebuggerMode !== DebuggerMode.STOPPED - ) { + + + if (mode === _constants().DebuggerMode.STOPPING && this._previousDebuggerMode !== _constants().DebuggerMode.STOPPED) { this._saveDebuggerPaneLocations(); - } else if (mode === DebuggerMode.STOPPED) { - destroyItemWhere(item => { - if (item instanceof DebuggerPaneContainerViewModel) { + } else if (mode === _constants().DebuggerMode.STOPPED) { + (0, _destroyItemWhere().destroyItemWhere)(item => { + if (item instanceof _DebuggerPaneContainerViewModel().default) { // Forward the destruction logic to the contianer. - item.destroyWhere(innerItem => - this._shouldDestroyPaneItem(mode, innerItem), - ); + item.destroyWhere(innerItem => this._shouldDestroyPaneItem(mode, innerItem)); this._destroyContainerIfEmpty(item); + return false; } @@ -700,44 +736,33 @@ export default class DebuggerLayoutManager { this._previousDebuggerMode = mode; } - _countPanesForTargetDock(dockName: string, defaultDockName: string): number { + _countPanesForTargetDock(dockName, defaultDockName) { const mode = this._service.getDebuggerMode(); - return this._debuggerPanes - .filter( - // Filter out any panes that the user has hidden or that aren't visible - // in the current debug mode. - debuggerPane => - (debuggerPane.previousLocation == null || - !debuggerPane.previousLocation.userHidden) && - (debuggerPane.debuggerModeFilter == null || - debuggerPane.debuggerModeFilter(mode)), - ) - .map(debuggerPane => { - // Map each debugger pane to the name of the dock it will belong to. - if (debuggerPane.previousLocation != null) { - const previousDock = this._getWorkspaceDocks().find( - d => - debuggerPane.previousLocation != null && - d.name === debuggerPane.previousLocation.dock, - ); - if (previousDock != null) { - return previousDock.name; - } + + return this._debuggerPanes.filter( // Filter out any panes that the user has hidden or that aren't visible + // in the current debug mode. + debuggerPane => (debuggerPane.previousLocation == null || !debuggerPane.previousLocation.userHidden) && (debuggerPane.debuggerModeFilter == null || debuggerPane.debuggerModeFilter(mode))).map(debuggerPane => { + // Map each debugger pane to the name of the dock it will belong to. + if (debuggerPane.previousLocation != null) { + const previousDock = this._getWorkspaceDocks().find(d => debuggerPane.previousLocation != null && d.name === debuggerPane.previousLocation.dock); + + if (previousDock != null) { + return previousDock.name; } - return defaultDockName; - }) - .filter(targetDockName => targetDockName === dockName).length; + } + + return defaultDockName; + }).filter(targetDockName => targetDockName === dockName).length; } - _getSavedDebuggerPaneSize(dock: { - name: string, - dock: atom$AbstractPaneContainer, - orientation: string, - }): ?number { + _getSavedDebuggerPaneSize(dock) { const key = this._getPaneStorageKey('dock-size' + dock.name); + const savedItem = localStorage.getItem(key); + if (savedItem != null) { const sizeInfo = JSON.parse(savedItem); + if (!Number.isNaN(sizeInfo)) { return sizeInfo; } @@ -746,192 +771,172 @@ export default class DebuggerLayoutManager { return null; } - showDebuggerViews(): void { + showDebuggerViews() { // Hide any debugger panes other than the controls so we have a known // starting point for preparing the layout. this.hideDebuggerViews(true); - const addedItemsByDock = new Map(); - const defaultDock = this._getWorkspaceDocks().find( - d => d.name === DEBUGGER_PANELS_DEFAULT_LOCATION, - ); - invariant(defaultDock != null); + + const defaultDock = this._getWorkspaceDocks().find(d => d.name === _constants().DEBUGGER_PANELS_DEFAULT_LOCATION); + + if (!(defaultDock != null)) { + throw new Error("Invariant violation: \"defaultDock != null\""); + } const leftDock = this._getWorkspaceDocks().find(d => d.name === 'left'); - invariant(leftDock != null); + + if (!(leftDock != null)) { + throw new Error("Invariant violation: \"leftDock != null\""); + } let leftPaneContainer = null; + if (this._countPanesForTargetDock(leftDock.name, defaultDock.name) > 0) { - leftPaneContainer = createPaneContainer(); + leftPaneContainer = (0, _createPaneContainer().default)(); + const size = this._getSavedDebuggerPaneSize(leftDock); - this._leftPaneContainerModel = this._addPaneContainerToWorkspace( - leftPaneContainer, - leftDock.dock, - addedItemsByDock, - size, - ); + + this._leftPaneContainerModel = this._addPaneContainerToWorkspace(leftPaneContainer, leftDock.dock, addedItemsByDock, size); } const rightDock = this._getWorkspaceDocks().find(d => d.name === 'right'); - invariant(rightDock != null); + + if (!(rightDock != null)) { + throw new Error("Invariant violation: \"rightDock != null\""); + } let rightPaneContainer = null; + if (this._countPanesForTargetDock(rightDock.name, defaultDock.name) > 0) { - rightPaneContainer = createPaneContainer(); + rightPaneContainer = (0, _createPaneContainer().default)(); + const size = this._getSavedDebuggerPaneSize(rightDock); - this._rightPaneContainerModel = this._addPaneContainerToWorkspace( - rightPaneContainer, - rightDock.dock, - addedItemsByDock, - size, - ); - } - // Lay out the remaining debugger panes according to their configurations. + this._rightPaneContainerModel = this._addPaneContainerToWorkspace(rightPaneContainer, rightDock.dock, addedItemsByDock, size); + } // Lay out the remaining debugger panes according to their configurations. // Sort the debugger panes by the index at which they appeared the last // time they were positioned, so we maintain the relative ordering of // debugger panes within the same dock. + + const mode = this._service.getDebuggerMode(); - this._debuggerPanes - .slice() - .sort((a, b) => { - const aPos = - a.previousLocation == null ? 0 : a.previousLocation.layoutIndex; - const bPos = - b.previousLocation == null ? 0 : b.previousLocation.layoutIndex; - return aPos - bPos; - }) - .filter( - debuggerPane => - (debuggerPane.isEnabled == null || debuggerPane.isEnabled()) && - (debuggerPane.previousLocation == null || - !debuggerPane.previousLocation.userHidden), - ) - .forEach(debuggerPane => { - let targetDock = defaultDock; - - // If this pane had a previous location, restore to the previous dock. - const loc = - debuggerPane.previousLocation != null - ? debuggerPane.previousLocation.dock - : debuggerPane.defaultLocation; - const previousDock = this._getWorkspaceDocks().find( - d => d.name === loc, - ); - if (previousDock != null) { - targetDock = previousDock; - } - // Render to a nested pane container for the two vertical docks - // rather than adding the item directly to the dock itself. - let targetContainer = targetDock.dock; - if (targetDock.name === 'left') { - targetContainer = leftPaneContainer; - } else if (targetDock.name === 'right') { - targetContainer = rightPaneContainer; - } + this._debuggerPanes.slice().sort((a, b) => { + const aPos = a.previousLocation == null ? 0 : a.previousLocation.layoutIndex; + const bPos = b.previousLocation == null ? 0 : b.previousLocation.layoutIndex; + return aPos - bPos; + }).filter(debuggerPane => (debuggerPane.isEnabled == null || debuggerPane.isEnabled()) && (debuggerPane.previousLocation == null || !debuggerPane.previousLocation.userHidden)).forEach(debuggerPane => { + let targetDock = defaultDock; // If this pane had a previous location, restore to the previous dock. + + const loc = debuggerPane.previousLocation != null ? debuggerPane.previousLocation.dock : debuggerPane.defaultLocation; + + const previousDock = this._getWorkspaceDocks().find(d => d.name === loc); + + if (previousDock != null) { + targetDock = previousDock; + } // Render to a nested pane container for the two vertical docks + // rather than adding the item directly to the dock itself. + - if ( - debuggerPane.debuggerModeFilter == null || - debuggerPane.debuggerModeFilter(mode) - ) { - invariant(targetContainer != null); - const size = this._getSavedDebuggerPaneSize(targetDock); - this._appendItemToDock( - debuggerPane, - targetContainer, - new DebuggerPaneViewModel( - debuggerPane, - debuggerPane.isLifetimeView, - pane => this._paneDestroyed(pane), - size, - ), - addedItemsByDock, - ); + let targetContainer = targetDock.dock; + + if (targetDock.name === 'left') { + targetContainer = leftPaneContainer; + } else if (targetDock.name === 'right') { + targetContainer = rightPaneContainer; + } + + if (debuggerPane.debuggerModeFilter == null || debuggerPane.debuggerModeFilter(mode)) { + if (!(targetContainer != null)) { + throw new Error("Invariant violation: \"targetContainer != null\""); } - }); - this._debuggerVisible = true; + const size = this._getSavedDebuggerPaneSize(targetDock); - // Re-focus the console pane after layout so that it remains visible + this._appendItemToDock(debuggerPane, targetContainer, new (_DebuggerPaneViewModel().default)(debuggerPane, debuggerPane.isLifetimeView, pane => this._paneDestroyed(pane), size), addedItemsByDock); + } + }); + + this._debuggerVisible = true; // Re-focus the console pane after layout so that it remains visible // even if we added debugger panes to the console's dock. // eslint-disable-next-line nuclide-internal/atom-apis - atom.workspace.open(CONSOLE_VIEW_URI, {searchAllPanes: true}); + + atom.workspace.open(CONSOLE_VIEW_URI, { + searchAllPanes: true + }); } - _addPaneContainerToWorkspace( - container: atom$PaneContainer, - dock: atom$AbstractPaneContainer, - addedItemsByDock: Map, - dockSize: ?number, - ): DebuggerPaneContainerViewModel { - const containerModel = new DebuggerPaneContainerViewModel( - container, - dockSize, - ); + _addPaneContainerToWorkspace(container, dock, addedItemsByDock, dockSize) { + const containerModel = new (_DebuggerPaneContainerViewModel().default)(container, dockSize); + this._appendItemToDock(null, dock, containerModel, addedItemsByDock); return containerModel; } - _paneDestroyed(pane: DebuggerPaneConfig): void { + _paneDestroyed(pane) { if (pane.isLifetimeView) { // Lifetime views are not hidden and remembered like the unimportant views. // This view being destroyed means the debugger is exiting completely, and // this view is never remembered as "hidden by the user" because it's reqiured // for running the debugger. const mode = this._service.getDebuggerMode(); - if (mode === DebuggerMode.RUNNING || mode === DebuggerMode.PAUSED) { + + if (mode === _constants().DebuggerMode.RUNNING || mode === _constants().DebuggerMode.PAUSED) { this._saveDebuggerPaneLocations(); } this.hideDebuggerViews(false); + this._service.stopProcess(); - return; - } - // Views can be selectively hidden by the user while the debugger is + return; + } // Views can be selectively hidden by the user while the debugger is // running and that preference should be remembered. + + const config = this._debuggerPanes.find(p => p.uri === pane.uri); - invariant(config != null); + + if (!(config != null)) { + throw new Error("Invariant violation: \"config != null\""); + } if (config.previousLocation == null) { config.previousLocation = { dock: '', layoutIndex: 0, - userHidden: false, + userHidden: false }; } if (config.isEnabled == null || config.isEnabled()) { const mode = this._service.getDebuggerMode(); - if ( - config.debuggerModeFilter == null || - config.debuggerModeFilter(mode) - ) { - invariant(config.previousLocation != null); - config.previousLocation.userHidden = true; - - // Show a notification telling the user how to get the pane back + + if (config.debuggerModeFilter == null || config.debuggerModeFilter(mode)) { + if (!(config.previousLocation != null)) { + throw new Error("Invariant violation: \"config.previousLocation != null\""); + } + + config.previousLocation.userHidden = true; // Show a notification telling the user how to get the pane back // only once per session. + if (!this._paneHiddenWarningShown) { this._paneHiddenWarningShown = true; - - atom.notifications.addInfo( - `${config.title()} has been hidden. Right click any Debugger pane to bring it back.`, - ); + atom.notifications.addInfo(`${config.title()} has been hidden. Right click any Debugger pane to bring it back.`); } } - } + } // If hiding this view left an empty debugger pane container, destroy the container. + - // If hiding this view left an empty debugger pane container, destroy the container. this._destroyContainerIfEmpty(this._leftPaneContainerModel); + this._destroyContainerIfEmpty(this._rightPaneContainerModel); } - _destroyContainerIfEmpty(container: ?DebuggerPaneContainerViewModel): void { + _destroyContainerIfEmpty(container) { if (container != null && container.getAllItems().length === 0) { const parent = container.getParentPane(); + if (parent != null) { parent.removeItem(container); container.destroy(); @@ -939,69 +944,76 @@ export default class DebuggerLayoutManager { } } - hideDebuggerViews(performingLayout: boolean): void { + hideDebuggerViews(performingLayout) { // Docks do not toggle closed automatically when we remove all their items. // They can contain things other than the debugger items though, and could // have been left open and empty by the user. Toggle closed any docks that // end up empty only as a result of closing the debugger. const docks = this._getWorkspaceDocks(); - const previouslyEmpty = docks.map(dock => this._isDockEmpty(dock.dock)); - // Find and destroy all debugger items, and the panes that contained them. + const previouslyEmpty = docks.map(dock => this._isDockEmpty(dock.dock)); // Find and destroy all debugger items, and the panes that contained them. + atom.workspace.getPanes().forEach(pane => { pane.getItems().forEach(item => { - if ( - item instanceof DebuggerPaneViewModel || - item instanceof DebuggerPaneContainerViewModel - ) { + if (item instanceof _DebuggerPaneViewModel().default || item instanceof _DebuggerPaneContainerViewModel().default) { // Remove the view model. item.setRemovedFromLayout(true); - pane.destroyItem(item); + pane.destroyItem(item); // If removing the model left an empty pane, remove the pane. - // If removing the model left an empty pane, remove the pane. if (pane.getItems().length === 0) { pane.destroy(); } } }); - }); + }); // If any docks became empty as a result of closing those panes, hide the dock. - // If any docks became empty as a result of closing those panes, hide the dock. if (!performingLayout) { - docks - .map(dock => this._isDockEmpty(dock.dock)) - .forEach((empty, index) => { - if (empty && !previouslyEmpty[index]) { - docks[index].dock.hide(); - } - }); + docks.map(dock => this._isDockEmpty(dock.dock)).forEach((empty, index) => { + if (empty && !previouslyEmpty[index]) { + docks[index].dock.hide(); + } + }); } if (this._leftPaneContainerModel != null) { this._leftPaneContainerModel.setRemovedFromLayout(true); - invariant(this._leftPaneContainerModel != null); + + if (!(this._leftPaneContainerModel != null)) { + throw new Error("Invariant violation: \"this._leftPaneContainerModel != null\""); + } + this._leftPaneContainerModel.dispose(); + this._leftPaneContainerModel = null; } if (this._rightPaneContainerModel != null) { this._rightPaneContainerModel.setRemovedFromLayout(true); - invariant(this._rightPaneContainerModel != null); + + if (!(this._rightPaneContainerModel != null)) { + throw new Error("Invariant violation: \"this._rightPaneContainerModel != null\""); + } + this._rightPaneContainerModel.dispose(); + this._rightPaneContainerModel = null; } this._debuggerVisible = false; } - isDebuggerVisible(): boolean { + isDebuggerVisible() { return this._debuggerVisible; } - getWorkspaceDocksVisibility(): Array { + getWorkspaceDocksVisibility() { this._saveDebuggerPaneLocations(); + return this._getWorkspaceDocks().map(dock => { return dock.dock.isVisible != null && dock.dock.isVisible(); }); } + } + +exports.default = DebuggerLayoutManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerPaneContainerViewModel.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerPaneContainerViewModel.js index 8ef73020..3b75570b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerPaneContainerViewModel.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerPaneContainerViewModel.js @@ -1,3 +1,66 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _DebuggerPaneViewModel() { + const data = _interopRequireDefault(require("./DebuggerPaneViewModel")); + + _DebuggerPaneViewModel = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _tabBarView() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/VendorLib/atom-tabs/lib/tab-bar-view")); + + _tabBarView = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _View() { + const data = require("../../../../../nuclide-commons-ui/View"); + + _View = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +69,14 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import { - DEBUGGER_PANELS_DEFAULT_WIDTH_PX, - DEBUGGER_PANELS_DEFAULT_LOCATION, -} from '../constants'; -import DebuggerPaneViewModel from './DebuggerPaneViewModel'; -import invariant from 'assert'; -import * as React from 'react'; -import TabBarView from 'nuclide-commons-ui/VendorLib/atom-tabs/lib/tab-bar-view'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {View} from 'nuclide-commons-ui/View'; - const DEBUGGER_TAB_TITLE = 'Debugger'; -export default class DebuggerPaneContainerViewModel { - _container: atom$PaneContainer; - _disposables: UniversalDisposable; - _paneEvents: Map; - _removedFromLayout: boolean; - _preferredWidth: ?number; - - constructor(paneContainer: atom$PaneContainer, preferredWidth: ?number) { - this._disposables = new UniversalDisposable(); +class DebuggerPaneContainerViewModel { + constructor(paneContainer, preferredWidth) { + this._disposables = new (_UniversalDisposable().default)(); this._paneEvents = new Map(); this._removedFromLayout = false; this._container = paneContainer; @@ -39,118 +84,120 @@ export default class DebuggerPaneContainerViewModel { for (const pane of this._container.getPanes()) { this._deferredAddTabBarToEmptyPane(pane); + this._addManagedPane(pane); } - this._disposables.add( - () => { - this._forEachChildPaneItem((item: atom$PaneItem) => { - invariant( - item instanceof DebuggerPaneViewModel || - item instanceof DebuggerPaneContainerViewModel, - ); - item.setRemovedFromLayout(this._removedFromLayout); - item.destroy(); - }); - this._container.destroy(); - }, - paneContainer.onDidAddPane(event => { - const pane = event.pane; - - this._kickOutNonDebuggerItems(pane); - if (this._container.getPanes().indexOf(pane) < 0) { - return; + this._disposables.add(() => { + this._forEachChildPaneItem(item => { + if (!(item instanceof _DebuggerPaneViewModel().default || item instanceof DebuggerPaneContainerViewModel)) { + throw new Error("Invariant violation: \"item instanceof DebuggerPaneViewModel ||\\n item instanceof DebuggerPaneContainerViewModel\""); } - if (!this._conditionallyAddTabBarToPane(pane)) { - // Wait until the item(s) are added to the pane, and then add a tab bar - // above them if and only if the item's title is not the same as the - // container tabs title (we don't want duplicate tabs right beneath each other). - this._deferredAddTabBarToEmptyPane(pane); - } + item.setRemovedFromLayout(this._removedFromLayout); + item.destroy(); + }); - this._addManagedPane(pane); - }), - paneContainer.onWillDestroyPane(event => { - const disposables = this._paneEvents.get(event.pane); - if (disposables != null) { - disposables.dispose(); - this._paneEvents.delete(event.pane); - } - }), - paneContainer.onDidDestroyPane(event => { - // If this container is now empty, destroy it! - const panes = this._container.getPanes(); - if ( - panes.length === 0 || - (panes.length === 1 && panes[0].getItems().length === 0) - ) { - const parent = this.getParentPane(); - if (parent != null) { - parent.removeItem(this); - } + this._container.destroy(); + }, paneContainer.onDidAddPane(event => { + const pane = event.pane; + + this._kickOutNonDebuggerItems(pane); + + if (this._container.getPanes().indexOf(pane) < 0) { + return; + } + + if (!this._conditionallyAddTabBarToPane(pane)) { + // Wait until the item(s) are added to the pane, and then add a tab bar + // above them if and only if the item's title is not the same as the + // container tabs title (we don't want duplicate tabs right beneath each other). + this._deferredAddTabBarToEmptyPane(pane); + } + + this._addManagedPane(pane); + }), paneContainer.onWillDestroyPane(event => { + const disposables = this._paneEvents.get(event.pane); + + if (disposables != null) { + disposables.dispose(); + + this._paneEvents.delete(event.pane); + } + }), paneContainer.onDidDestroyPane(event => { + // If this container is now empty, destroy it! + const panes = this._container.getPanes(); + + if (panes.length === 0 || panes.length === 1 && panes[0].getItems().length === 0) { + const parent = this.getParentPane(); + + if (parent != null) { + parent.removeItem(this); } - }), - ); + } + })); } - _addManagedPane(pane: atom$Pane): void { + _addManagedPane(pane) { let disposables = this._paneEvents.get(pane); + if (disposables == null) { - disposables = new UniversalDisposable(); + disposables = new (_UniversalDisposable().default)(); + this._paneEvents.set(pane, disposables); } - disposables.add( - pane.onDidAddItem(event => { - this._kickOutNonDebuggerItems(pane); - }), - ); - - // Split operations on the child panes of this container are also being + disposables.add(pane.onDidAddItem(event => { + this._kickOutNonDebuggerItems(pane); + })); // Split operations on the child panes of this container are also being // executed on the parent pane that contains this container, which results // in very unexpected behavior. Prevent the parent pane from splitting. + const parent = this.getParentPane(); + if (parent != null) { // $FlowFixMe parent.split = () => {}; } - } - - // If a pane is initially empty, don't add the tab bar until the first item + } // If a pane is initially empty, don't add the tab bar until the first item // is added to the pane, otherwise we don't know what title to give the tab! - _deferredAddTabBarToEmptyPane(pane: atom$Pane): void { - const pendingAddTabDisposable = new UniversalDisposable(); - pendingAddTabDisposable.add( - pane.onDidAddItem(event => { - if (this._conditionallyAddTabBarToPane(pane)) { - this._disposables.remove(pendingAddTabDisposable); - pendingAddTabDisposable.dispose(); - } - }), - ); + + + _deferredAddTabBarToEmptyPane(pane) { + const pendingAddTabDisposable = new (_UniversalDisposable().default)(); + pendingAddTabDisposable.add(pane.onDidAddItem(event => { + if (this._conditionallyAddTabBarToPane(pane)) { + this._disposables.remove(pendingAddTabDisposable); + + pendingAddTabDisposable.dispose(); + } + })); + this._disposables.add(pendingAddTabDisposable); } - _conditionallyAddTabBarToPane(pane: atom$Pane): boolean { + _conditionallyAddTabBarToPane(pane) { const items = pane.getItems(); + if (items.length > 0) { const item = items[0]; - if (item instanceof DebuggerPaneViewModel) { + + if (item instanceof _DebuggerPaneViewModel().default) { if (item.getTitle() !== this.getTitle() || items.length > 1) { this._addTabBarToPane(pane); + return true; } } } return false; - } - - // Don't let the user add a non-debugger item to the debugger pane container. This is because + } // Don't let the user add a non-debugger item to the debugger pane container. This is because // the container will get destroyed by the debugger going away or redoing layout, and we wouldn't // be able to preserve the user's other items. - _kickOutNonDebuggerItems(pane: atom$Pane): void { + + + _kickOutNonDebuggerItems(pane) { for (const item of pane.getItems()) { if (item instanceof DebuggerPaneContainerViewModel) { if (item === this) { @@ -159,63 +206,65 @@ export default class DebuggerPaneContainerViewModel { // the debugger layout. // TODO: Better solution here. process.nextTick(() => { - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'debugger:show', - ); + atom.commands.dispatch(atom.views.getView(atom.workspace), 'debugger:show'); }); } else { // This is another debugger pane container, which contains other debugger // panes. Move all the other container's items to this container, and\ // then destroy the other container. const otherPanes = item._container.getPanes(); + for (const otherPane of otherPanes) { for (const otherItem of otherPane.getItems()) { const idx = pane.getItems().indexOf(item); otherPane.moveItemToPane(otherItem, pane, idx); otherPane.activateItemAtIndex(idx); } - } + } // Destroy the (now empty) other pane container. + - // Destroy the (now empty) other pane container. process.nextTick(() => { pane.destroyItem(item); }); } } else { // Kick the item out to the parent pane. - if (!(item instanceof DebuggerPaneViewModel)) { + if (!(item instanceof _DebuggerPaneViewModel().default)) { this._moveItemToParentPane(item, pane); } } } } - _moveItemToParentPane(item: atom$PaneItem, pane: atom$Pane): void { + _moveItemToParentPane(item, pane) { const parentPane = this.getParentPane(); - invariant(parentPane != null); - // Kick the item out to the parent pane, which must be done on next tick because the drag + if (!(parentPane != null)) { + throw new Error("Invariant violation: \"parentPane != null\""); + } // Kick the item out to the parent pane, which must be done on next tick because the drag // operation currently in progress needs the item not to be destroyed before the drag // completes. + + process.nextTick(() => { - invariant(parentPane != null); - pane.moveItemToPane( - item, - parentPane, - parentPane.getItems().indexOf(this) + 1, - ); - - // TODO: Atom bug? This is here because when setting this item active immediately after + if (!(parentPane != null)) { + throw new Error("Invariant violation: \"parentPane != null\""); + } + + pane.moveItemToPane(item, parentPane, parentPane.getItems().indexOf(this) + 1); // TODO: Atom bug? This is here because when setting this item active immediately after // moving, it sometimes (but not always) renders a blank pane... + process.nextTick(() => { - invariant(parentPane != null); + if (!(parentPane != null)) { + throw new Error("Invariant violation: \"parentPane != null\""); + } + parentPane.setActiveItem(item); }); }); } - getParentPane(): ?atom$Pane { + getParentPane() { for (const pane of atom.workspace.getPanes()) { for (const item of pane.getItems()) { if (item === this) { @@ -223,27 +272,26 @@ export default class DebuggerPaneContainerViewModel { } } } + return null; } - _addTabBarToPane(pane: atom$Pane): void { - const tabBarView = new TabBarView(pane); + _addTabBarToPane(pane) { + const tabBarView = new (_tabBarView().default)(pane); const paneElement = atom.views.getView(pane); - paneElement.insertBefore(tabBarView.element, paneElement.firstChild); - - // moveItemBetweenPanes conflicts with the parent tab's moveItemBetweenPanes. + paneElement.insertBefore(tabBarView.element, paneElement.firstChild); // moveItemBetweenPanes conflicts with the parent tab's moveItemBetweenPanes. // Empty it out to get the correct behavior. + tabBarView.moveItemBetweenPanes = () => {}; - tabBarView.element.classList.add( - 'nuclide-workspace-views-panel-location-tabs', - ); + + tabBarView.element.classList.add('nuclide-workspace-views-panel-location-tabs'); } - dispose(): void { + dispose() { this._disposables.dispose(); } - destroy(): void { + destroy() { if (!this._removedFromLayout) { // We need to differentiate between the case where destroying this pane hides one or more // non-essential debugger views, and where it means the user is closing the debugger. @@ -253,7 +301,7 @@ export default class DebuggerPaneContainerViewModel { // contained within this pane, which is accomplished by disposing this. for (const pane of this._container.getPanes()) { for (const item of pane.getItems()) { - if (item instanceof DebuggerPaneViewModel) { + if (item instanceof _DebuggerPaneViewModel().default) { if (item.isLifetimeView()) { item.destroy(); return; @@ -266,7 +314,7 @@ export default class DebuggerPaneContainerViewModel { this.dispose(); } - destroyWhere(callback: (item: atom$PaneItem) => mixed) { + destroyWhere(callback) { this._forEachChildPaneItem((innerItem, pane) => { if (callback(innerItem)) { pane.destroyItem(innerItem); @@ -274,46 +322,43 @@ export default class DebuggerPaneContainerViewModel { }); } - getTitle(): string { + getTitle() { return DEBUGGER_TAB_TITLE; } - getIconName(): string { + getIconName() { return 'nuclicon-debugger'; } - getDefaultLocation(): string { - return DEBUGGER_PANELS_DEFAULT_LOCATION; + getDefaultLocation() { + return _constants().DEBUGGER_PANELS_DEFAULT_LOCATION; } - getURI(): string { + getURI() { return 'atom://nuclide/debugger-container'; } - getPreferredWidth(): number { - return this._preferredWidth == null - ? DEBUGGER_PANELS_DEFAULT_WIDTH_PX - : this._preferredWidth; + getPreferredWidth() { + return this._preferredWidth == null ? _constants().DEBUGGER_PANELS_DEFAULT_WIDTH_PX : this._preferredWidth; } - createView(): React.Element { - return ; + createView() { + return React.createElement(_View().View, { + item: this._container + }); } - setRemovedFromLayout(removed: boolean): void { - this._removedFromLayout = removed; + setRemovedFromLayout(removed) { + this._removedFromLayout = removed; // Propagate this command to the children of the pane container. - // Propagate this command to the children of the pane container. this._forEachChildPaneItem(item => { - if (item instanceof DebuggerPaneViewModel) { + if (item instanceof _DebuggerPaneViewModel().default) { item.setRemovedFromLayout(removed); } }); } - _forEachChildPaneItem( - callback: (item: atom$PaneItem, pane: atom$Pane) => void, - ): void { + _forEachChildPaneItem(callback) { for (const pane of this._container.getPanes()) { pane.getItems().forEach(item => { callback(item, pane); @@ -321,8 +366,9 @@ export default class DebuggerPaneContainerViewModel { } } - getAllItems(): Array { + getAllItems() { const items = []; + this._forEachChildPaneItem(item => { items.push(item); }); @@ -330,11 +376,14 @@ export default class DebuggerPaneContainerViewModel { return items; } - serialize(): Object { + serialize() { return {}; } - copy(): boolean { + copy() { return false; } + } + +exports.default = DebuggerPaneContainerViewModel; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerPaneViewModel.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerPaneViewModel.js index e40184ef..8009d1c3 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerPaneViewModel.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerPaneViewModel.js @@ -1,3 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,35 +27,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {DebuggerPaneConfig} from './DebuggerLayoutManager'; -import * as React from 'react'; -import { - DEBUGGER_PANELS_DEFAULT_WIDTH_PX, - DEBUGGER_PANELS_DEFAULT_LOCATION, -} from '../constants'; - // A model that will serve as the view model for all debugger panes. We must provide // a unique instance of a view model for each pane, which Atom can destroy when the // pane that contains it is destroyed. We therefore cannot give it the actual debugger // model directly, since there is only one and its lifetime is tied to the lifetime // of the debugging session. -export default class DebuggerPaneViewModel { - _config: DebuggerPaneConfig; - _isLifetimeView: boolean; - _paneDestroyed: (pane: DebuggerPaneConfig) => void; - _removedFromLayout: boolean; - _preferredWidth: ?number; - - constructor( - config: DebuggerPaneConfig, - isLifetimeView: boolean, - paneDestroyed: (pane: DebuggerPaneConfig) => void, - preferredWidth: ?number, - ) { +class DebuggerPaneViewModel { + constructor(config, isLifetimeView, paneDestroyed, preferredWidth) { this._config = config; this._isLifetimeView = isLifetimeView; this._paneDestroyed = paneDestroyed; @@ -42,57 +44,59 @@ export default class DebuggerPaneViewModel { this._preferredWidth = preferredWidth; } - dispose(): void {} + dispose() {} - destroy(): void { + destroy() { if (!this._removedFromLayout) { this._paneDestroyed(this._config); } } - getTitle(): string { + getTitle() { return this._config.title(); } - getDefaultLocation(): string { - return DEBUGGER_PANELS_DEFAULT_LOCATION; + getDefaultLocation() { + return _constants().DEBUGGER_PANELS_DEFAULT_LOCATION; } - getURI(): string { + getURI() { return this._config.uri; } - getPreferredWidth(): number { - return this._preferredWidth == null - ? DEBUGGER_PANELS_DEFAULT_WIDTH_PX - : this._preferredWidth; + getPreferredWidth() { + return this._preferredWidth == null ? _constants().DEBUGGER_PANELS_DEFAULT_WIDTH_PX : this._preferredWidth; } - createView(): React.Element { + createView() { if (this._config.previousLocation != null) { this._config.previousLocation.userHidden = false; } + return this._config.createView(); } - getConfig(): DebuggerPaneConfig { + getConfig() { return this._config; } - isLifetimeView(): boolean { + isLifetimeView() { return this._isLifetimeView; } - setRemovedFromLayout(removed: boolean): void { + setRemovedFromLayout(removed) { this._removedFromLayout = removed; - } + } // Atom view needs to provide this, otherwise Atom throws an exception splitting panes for the view. - // Atom view needs to provide this, otherwise Atom throws an exception splitting panes for the view. - serialize(): Object { + + serialize() { return {}; } - copy(): boolean { + copy() { return false; } + } + +exports.default = DebuggerPaneViewModel; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerProcessComponent.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerProcessComponent.js index 16b558d4..389d4f31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerProcessComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerProcessComponent.js @@ -1,3 +1,68 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _Tree() { + const data = require("../../../../../nuclide-commons-ui/Tree"); + + _Tree = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _ProcessTreeNode() { + const data = _interopRequireDefault(require("./ProcessTreeNode")); + + _ProcessTreeNode = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,93 +71,68 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class DebuggerProcessComponent extends React.PureComponent { + constructor(props) { + super(props); -import type {IDebugService, IProcess} from '../types'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import * as React from 'react'; -import {TreeList} from 'nuclide-commons-ui/Tree'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {fastDebounce} from 'nuclide-commons/observable'; -import {Observable} from 'rxjs'; -import ProcessTreeNode from './ProcessTreeNode'; - -type Props = { - service: IDebugService, -}; - -type State = { - processList: Array, -}; - -export default class DebuggerProcessComponent extends React.PureComponent< - Props, - State, -> { - _disposables: UniversalDisposable; - _treeView: ?TreeList; + this._handleThreadsChanged = () => { + this.setState(this._getState()); + }; - constructor(props: Props) { - super(props); this.state = this._getState(); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); } - componentDidMount(): void { - const {service} = this.props; - const {viewModel} = service; + componentDidMount() { + const { + service + } = this.props; + const { + viewModel + } = service; const model = service.getModel(); - this._disposables.add( - Observable.merge( - observableFromSubscribeFunction( - viewModel.onDidFocusStackFrame.bind(viewModel), - ), - observableFromSubscribeFunction(model.onDidChangeCallStack.bind(model)), - observableFromSubscribeFunction(service.onDidChangeMode.bind(service)), - ) - .let(fastDebounce(150)) - .subscribe(this._handleThreadsChanged), - ); + + this._disposables.add(_RxMin.Observable.merge((0, _event().observableFromSubscribeFunction)(viewModel.onDidFocusStackFrame.bind(viewModel)), (0, _event().observableFromSubscribeFunction)(model.onDidChangeCallStack.bind(model)), (0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service))).let((0, _observable().fastDebounce)(150)).subscribe(this._handleThreadsChanged)); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - _handleThreadsChanged = (): void => { - this.setState(this._getState()); - }; - - _getState(): $Shape { + _getState() { return { - processList: this.props.service - .getModel() - .getProcesses() - .slice(), + processList: this.props.service.getModel().getProcesses().slice() }; } - render(): React.Node { - const {processList} = this.state; - const {service} = this.props; + render() { + const { + processList + } = this.state; + const { + service + } = this.props; const processElements = processList.map((process, processIndex) => { - const {adapterType, processName} = process.configuration; - return process == null ? ( - 'No processes are currently being debugged' - ) : ( - - ); + const { + adapterType, + processName + } = process.configuration; + return process == null ? 'No processes are currently being debugged' : React.createElement(_ProcessTreeNode().default, { + title: processName != null ? processName : adapterType, + key: processIndex, + childItems: process.getAllThreads(), + process: process, + service: service + }); }); - - return {processElements}; + return React.createElement(_Tree().TreeList, { + showArrows: true + }, processElements); } + } + +exports.default = DebuggerProcessComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerProcessTreeView.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerProcessTreeView.js index fabc84c9..b4a6fd3a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerProcessTreeView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerProcessTreeView.js @@ -1,3 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = DebuggerProcessTreeView; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _Block() { + const data = require("../../../../../nuclide-commons-ui/Block"); + + _Block = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _DebuggerProcessComponent() { + const data = _interopRequireDefault(require("./DebuggerProcessComponent")); + + _DebuggerProcessComponent = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +49,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {IDebugService} from '../types'; - -import classnames from 'classnames'; -import {Block} from 'nuclide-commons-ui/Block'; -import * as React from 'react'; -import DebuggerProcessComponent from './DebuggerProcessComponent'; - -export default function DebuggerProcessTreeView(props: { - service: IDebugService, -}): React.Node { - return ( -
-
- - - -
-
- ); -} +function DebuggerProcessTreeView(props) { + return React.createElement("div", { + className: (0, _classnames().default)('debugger-container-new', 'debugger-breakpoint-list', 'debugger-tree') + }, React.createElement("div", { + className: "debugger-pane-content " + }, React.createElement(_Block().Block, null, React.createElement(_DebuggerProcessComponent().default, { + service: props.service + })))); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerSteppingComponent.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerSteppingComponent.js index 32f6be71..40e14fab 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerSteppingComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/DebuggerSteppingComponent.js @@ -1,3 +1,108 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _LoadingSpinner() { + const data = require("../../../../../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _ButtonGroup() { + const data = require("../../../../../nuclide-commons-ui/ButtonGroup"); + + _ButtonGroup = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _logger() { + const data = _interopRequireDefault(require("../logger")); + + _logger = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,153 +111,121 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {DebuggerModeType, IDebugService, IThread} from '../types'; -import type {ControlButtonSpecification} from 'nuclide-debugger-common'; -import { - LoadingSpinner, - LoadingSpinnerSizes, -} from 'nuclide-commons-ui/LoadingSpinner'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {fastDebounce} from 'nuclide-commons/observable'; -import * as React from 'react'; -import {Button} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; -import {Observable} from 'rxjs'; -import {DebuggerMode} from '../constants'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import logger from '../logger'; -import nullthrows from 'nullthrows'; - -type DebuggerSteppingComponentProps = { - service: IDebugService, +const defaultTooltipOptions = { + placement: 'bottom' }; +const STEP_OVER_ICON = React.createElement("svg", { + viewBox: "0 0 100 100" +}, React.createElement("circle", { + cx: "46", + cy: "63", + r: "10" +}), React.createElement("path", { + d: 'M83.8,54.7c-6.5-16.6-20.7-28.1-37.2-28.1c-19.4,0-35.6,16-39.9,' + '37.3l11.6,2.9c3-16.2,14.5-28.2,28.2-28.2 c11,0,20.7,7.8,25.6,' + '19.3l-9.6,2.7l20.8,14.7L93.7,52L83.8,54.7z' +})); +const STEP_INTO_ICON = React.createElement("svg", { + viewBox: "0 0 100 100" +}, React.createElement("circle", { + cx: "50", + cy: "75", + r: "10" +}), React.createElement("polygon", { + points: "42,20 57,20 57,40 72,40 50,60 28,40 42,40" +})); +const STEP_OUT_ICON = React.createElement("svg", { + viewBox: "0 0 100 100" +}, React.createElement("circle", { + cx: "50", + cy: "75", + r: "10" +}), React.createElement("polygon", { + points: "42,20 57,20 57,40 72,40 50,60 28,40 42,40", + transform: "rotate(180, 50, 40)" +})); -type DebuggerSteppingComponentState = { - debuggerMode: DebuggerModeType, - waitingForPause: boolean, - customControlButtons: Array, -}; +function SVGButton(props) { + return React.createElement(_Button().Button, { + className: "debugger-stepping-svg-button", + onClick: props.onClick, + disabled: props.disabled, + tooltip: props.tooltip + }, React.createElement("div", null, props.icon)); +} -const defaultTooltipOptions = { - placement: 'bottom', -}; +class DebuggerSteppingComponent extends React.Component { + constructor(props) { + super(props); + + this._togglePauseState = () => { + const pausableThread = this._getPausableThread(); + + if (pausableThread == null) { + _logger().default.error('No thread to pause/resume'); -const STEP_OVER_ICON = ( - - - - -); - -const STEP_INTO_ICON = ( - - - - -); - -const STEP_OUT_ICON = ( - - - - -); - -function SVGButton(props: { - onClick: () => mixed, - tooltip: atom$TooltipsAddOptions, - icon: React.Element, - disabled: boolean, -}): React.Element { - return ( - - ); -} -export default class DebuggerSteppingComponent extends React.Component< - DebuggerSteppingComponentProps, - DebuggerSteppingComponentState, -> { - _disposables: UniversalDisposable; + if (pausableThread.stopped) { + pausableThread.continue(); + } else { + this._setWaitingForPause(true); - constructor(props: DebuggerSteppingComponentProps) { - super(props); + pausableThread.pause(); + } + }; - this._disposables = new UniversalDisposable(); - const {service} = props; + this._disposables = new (_UniversalDisposable().default)(); + const { + service + } = props; this.state = { debuggerMode: service.getDebuggerMode(), waitingForPause: false, - customControlButtons: [], + customControlButtons: [] }; } - componentDidMount(): void { - const {service} = this.props; + componentDidMount() { + const { + service + } = this.props; const model = service.getModel(); - this._disposables.add( - Observable.merge( - observableFromSubscribeFunction(service.onDidChangeMode.bind(service)), - observableFromSubscribeFunction(model.onDidChangeCallStack.bind(model)), - observableFromSubscribeFunction( - service.viewModel.onDidFocusStackFrame.bind(service.viewModel), - ), - ) - .startWith(null) - .let(fastDebounce(10)) - .subscribe(() => { - const debuggerMode = service.getDebuggerMode(); - const {focusedProcess} = service.viewModel; - - this.setState({ - debuggerMode, - customControlButtons: - focusedProcess == null - ? [] - : focusedProcess.configuration.customControlButtons || [], - }); - if ( - this.state.waitingForPause && - debuggerMode !== DebuggerMode.RUNNING - ) { - this._setWaitingForPause(false); - } - }), - ); + + this._disposables.add(_RxMin.Observable.merge((0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service)), (0, _event().observableFromSubscribeFunction)(model.onDidChangeCallStack.bind(model)), (0, _event().observableFromSubscribeFunction)(service.viewModel.onDidFocusStackFrame.bind(service.viewModel))).startWith(null).let((0, _observable().fastDebounce)(10)).subscribe(() => { + const debuggerMode = service.getDebuggerMode(); + const { + focusedProcess + } = service.viewModel; + this.setState({ + debuggerMode, + customControlButtons: focusedProcess == null ? [] : focusedProcess.configuration.customControlButtons || [] + }); + + if (this.state.waitingForPause && debuggerMode !== _constants().DebuggerMode.RUNNING) { + this._setWaitingForPause(false); + } + })); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - _setWaitingForPause(waiting: boolean): void { + _setWaitingForPause(waiting) { this.setState({ - waitingForPause: waiting, + waitingForPause: waiting }); } - _getPausableThread(): ?IThread { - const {focusedThread, focusedProcess} = this.props.service.viewModel; + _getPausableThread() { + const { + focusedThread, + focusedProcess + } = this.props.service.viewModel; + if (focusedThread != null) { return focusedThread; } else if (focusedProcess != null) { @@ -162,80 +235,56 @@ export default class DebuggerSteppingComponent extends React.Component< } } - _togglePauseState = () => { - const pausableThread = this._getPausableThread(); - if (pausableThread == null) { - logger.error('No thread to pause/resume'); - return; - } + render() { + const { + debuggerMode, + waitingForPause, + customControlButtons + } = this.state; + const { + service + } = this.props; + const { + focusedThread + } = service.viewModel; - if (pausableThread.stopped) { - pausableThread.continue(); - } else { - this._setWaitingForPause(true); - pausableThread.pause(); - } - }; + const isPaused = debuggerMode === _constants().DebuggerMode.PAUSED; + + const isStopped = debuggerMode === _constants().DebuggerMode.STOPPED; + + const isPausing = debuggerMode === _constants().DebuggerMode.RUNNING && waitingForPause; + const playPauseIcon = isPausing ? null : React.createElement("span", { + className: isPaused ? 'icon-playback-play' : 'icon-playback-pause' + }); + const loadingIndicator = !isPausing ? null : React.createElement(_LoadingSpinner().LoadingSpinner, { + className: "debugger-stepping-playpause-button-loading", + size: _LoadingSpinner().LoadingSpinnerSizes.EXTRA_SMALL + }); + const restartDebuggerButton = debuggerMode !== _constants().DebuggerMode.STOPPED ? React.createElement(_Button().Button, { + icon: "sync", + className: "debugger-stepping-button-separated", + disabled: isStopped, + tooltip: Object.assign({}, defaultTooltipOptions, { + title: 'Restart the debugger using the same settings as the current debug session', + keyBindingCommand: 'debugger:restart-debugging' + }), + onClick: () => service.restartProcess() + }) : null; - render(): React.Node { - const {debuggerMode, waitingForPause, customControlButtons} = this.state; - const {service} = this.props; - const {focusedThread} = service.viewModel; - const isPaused = debuggerMode === DebuggerMode.PAUSED; - const isStopped = debuggerMode === DebuggerMode.STOPPED; - const isPausing = debuggerMode === DebuggerMode.RUNNING && waitingForPause; - const playPauseIcon = isPausing ? null : ( - - ); - - const loadingIndicator = !isPausing ? null : ( - - ); - - const restartDebuggerButton = - debuggerMode !== DebuggerMode.STOPPED ? ( - - nullthrows(focusedThread).next()} - /> - nullthrows(focusedThread).stepIn()} - /> - nullthrows(focusedThread).stepOut()} - /> -
cellData.stopped} - resizable={true} - onSelect={this._handleSelectThread} - sortable={true} - onSort={this._handleSort} - sortedColumn={this.state.sortedColumn} - sortDescending={this.state.sortDescending} - ref={table => { - this._threadTable = table; - }} - /> - ); + return React.createElement(_Table().Table, { + columns: columns, + emptyComponent: emptyComponent, + rows: this._sortRows(rows, this.state.sortedColumn, this.state.sortDescending), + selectable: cellData => cellData.stopped, + resizable: true, + onSelect: this._handleSelectThread, + sortable: true, + onSort: this._handleSort, + sortedColumn: this.state.sortedColumn, + sortDescending: this.state.sortDescending, + ref: table => { + this._threadTable = table; + } + }); } + } + +exports.default = DebuggerThreadsComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/FrameTreeNode.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/FrameTreeNode.js index 7052d6de..be669178 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/FrameTreeNode.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/FrameTreeNode.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Tree() { + const data = require("../../../../../nuclide-commons-ui/Tree"); + + _Tree = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,57 +39,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class FrameTreeNode extends React.Component { + constructor(props) { + super(props); -import type {IStackFrame, IDebugService} from '../types'; - -import * as React from 'react'; -import {TreeItem} from 'nuclide-commons-ui/Tree'; -import classnames from 'classnames'; - -type Props = { - frame: IStackFrame, // The frame that this node represents. - service: IDebugService, -}; + this.handleSelect = () => { + this.props.service.focusStackFrame(this.props.frame, null, null, true); + }; -export default class FrameTreeNode extends React.Component { - constructor(props: Props) { - super(props); this.handleSelect = this.handleSelect.bind(this); } - handleSelect = () => { - this.props.service.focusStackFrame(this.props.frame, null, null, true); - }; - - render(): React.Node { - const {frame, service} = this.props; + render() { + const { + frame, + service + } = this.props; const activeFrame = service.viewModel.focusedStackFrame; - const className = (activeFrame == null - ? false - : frame === activeFrame) - ? classnames('debugger-tree-frame-selected', 'debugger-tree-frame') - : 'debugger-tree-frame'; - - const treeItem = ( - - {frame.name} - - ); - + const className = (activeFrame == null ? false : frame === activeFrame) ? (0, _classnames().default)('debugger-tree-frame-selected', 'debugger-tree-frame') : 'debugger-tree-frame'; + const treeItem = React.createElement(_Tree().TreeItem, { + className: className, + onSelect: this.handleSelect, + title: `Frame ID: ${frame.frameId}, Name: ${frame.name}` + (frame.thread.stopped && frame.thread.getCallStack()[0] === frame && frame.source != null && frame.source.name != null ? `, Stopped at: ${frame.source.name}: ${frame.range.end.row}` : '') + }, frame.name); return treeItem; } + } + +exports.default = FrameTreeNode; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ProcessTreeNode.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ProcessTreeNode.js index 7662a706..1ec46f5c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ProcessTreeNode.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ProcessTreeNode.js @@ -1,3 +1,68 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _Tree() { + const data = require("../../../../../nuclide-commons-ui/Tree"); + + _Tree = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _ThreadTreeNode() { + const data = _interopRequireDefault(require("./ThreadTreeNode")); + + _ThreadTreeNode = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,137 +71,102 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class ProcessTreeNode extends React.Component { + constructor(props) { + super(props); -import type {IProcess, IDebugService, IThread} from '../types'; - -import {TreeItem, NestedTreeItem} from 'nuclide-commons-ui/Tree'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {fastDebounce} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import {Observable} from 'rxjs'; -import ThreadTreeNode from './ThreadTreeNode'; - -type Props = { - process: IProcess, - service: IDebugService, - title: string, -}; - -type State = { - isCollapsed: boolean, - childItems: Array, - isFocused: boolean, -}; + this._handleThreadsChanged = () => { + this.setState(prevState => this._getState(!(this._computeIsFocused() || !prevState.isCollapsed))); + }; -export default class ProcessTreeNode extends React.Component { - _disposables: UniversalDisposable; + this.handleSelect = () => { + this.setState(prevState => this._getState(!prevState.isCollapsed)); + }; - constructor(props: Props) { - super(props); this.state = this._getState(); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); this.handleSelect = this.handleSelect.bind(this); } - componentDidMount(): void { - const {service} = this.props; + componentDidMount() { + const { + service + } = this.props; const model = service.getModel(); - const {viewModel} = service; - this._disposables.add( - Observable.merge( - observableFromSubscribeFunction(model.onDidChangeCallStack.bind(model)), - observableFromSubscribeFunction( - viewModel.onDidFocusStackFrame.bind(viewModel), - ), - observableFromSubscribeFunction(service.onDidChangeMode.bind(service)), - ) - .let(fastDebounce(15)) - .subscribe(this._handleThreadsChanged), - ); + const { + viewModel + } = service; + + this._disposables.add(_RxMin.Observable.merge((0, _event().observableFromSubscribeFunction)(model.onDidChangeCallStack.bind(model)), (0, _event().observableFromSubscribeFunction)(viewModel.onDidFocusStackFrame.bind(viewModel)), (0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service))).let((0, _observable().fastDebounce)(15)).subscribe(this._handleThreadsChanged)); } componentWillUnmount() { this._disposables.dispose(); } - _handleThreadsChanged = (): void => { - this.setState(prevState => - this._getState(!(this._computeIsFocused() || !prevState.isCollapsed)), - ); - }; - - _computeIsFocused(): boolean { - const {service, process} = this.props; + _computeIsFocused() { + const { + service, + process + } = this.props; const focusedProcess = service.viewModel.focusedProcess; return process === focusedProcess; } - _getState(shouldBeCollapsed: ?boolean) { - const {process} = this.props; + _getState(shouldBeCollapsed) { + const { + process + } = this.props; + const isFocused = this._computeIsFocused(); - const isCollapsed = - shouldBeCollapsed != null ? shouldBeCollapsed : !isFocused; + + const isCollapsed = shouldBeCollapsed != null ? shouldBeCollapsed : !isFocused; return { isFocused, childItems: process.getAllThreads(), - isCollapsed, + isCollapsed }; } - handleSelect = () => { - this.setState(prevState => this._getState(!prevState.isCollapsed)); - }; - render() { - const {service, title, process} = this.props; - const {childItems, isFocused} = this.state; - - const tooltipTitle = - service.viewModel.focusedProcess == null || - service.viewModel.focusedProcess.configuration.adapterExecutable == null - ? 'Unknown Command' - : service.viewModel.focusedProcess.configuration.adapterExecutable - .command + - service.viewModel.focusedProcess.configuration.adapterExecutable.args.join( - ' ', - ); + const { + service, + title, + process + } = this.props; + const { + childItems, + isFocused + } = this.state; + const tooltipTitle = service.viewModel.focusedProcess == null || service.viewModel.focusedProcess.configuration.adapterExecutable == null ? 'Unknown Command' : service.viewModel.focusedProcess.configuration.adapterExecutable.command + service.viewModel.focusedProcess.configuration.adapterExecutable.args.join(' '); const handleTitleClick = event => { service.focusStackFrame(null, null, process, true); event.stopPropagation(); }; - const formattedTitle = ( - - {title} - - ); - - return childItems == null || childItems.length === 0 ? ( - {formattedTitle} - ) : ( - - {childItems.map((thread, threadIndex) => { - return ( - - ); - })} - - ); + const formattedTitle = React.createElement("span", { + onClick: handleTitleClick, + className: isFocused ? 'debugger-tree-process-thread-selected' : '', + title: tooltipTitle + }, title); + return childItems == null || childItems.length === 0 ? React.createElement(_Tree().TreeItem, null, formattedTitle) : React.createElement(_Tree().NestedTreeItem, { + title: formattedTitle, + collapsed: this.state.isCollapsed, + onSelect: this.handleSelect + }, childItems.map((thread, threadIndex) => { + return React.createElement(_ThreadTreeNode().default, { + key: threadIndex, + childItems: thread.getCallStack(), + thread: thread, + service: service + }); + })); } + } + +exports.default = ProcessTreeNode; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ScopesComponent.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ScopesComponent.js index 86d8e426..dbd04450 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ScopesComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ScopesComponent.js @@ -1,3 +1,108 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _bindObservableAsProps() { + const data = require("../../../../../nuclide-commons-ui/bindObservableAsProps"); + + _bindObservableAsProps = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _LazyNestedValueComponent() { + const data = require("../../../../../nuclide-commons-ui/LazyNestedValueComponent"); + + _LazyNestedValueComponent = function () { + return data; + }; + + return data; +} + +function _SimpleValueComponent() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/SimpleValueComponent")); + + _SimpleValueComponent = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _Section() { + const data = require("../../../../../nuclide-commons-ui/Section"); + + _Section = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("../utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _expected() { + const data = require("../../../../../nuclide-commons/expected"); + + _expected = function () { + return data; + }; + + return data; +} + +function _LoadingSpinner() { + const data = require("../../../../../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,227 +111,194 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const NO_VARIABLES = React.createElement("div", { + className: "debugger-expression-value-row" +}, React.createElement("span", { + className: "debugger-expression-value-content" +}, "(no variables)")); +const LOADING = React.createElement("div", { + className: "debugger-expression-value-row" +}, React.createElement("span", { + className: "debugger-expression-value-content" +}, React.createElement(_LoadingSpinner().LoadingSpinner, { + size: "MEDIUM" +}))); -import type {IDebugService, IScope, IVariable} from '../types'; -import type {Expected} from 'nuclide-commons/expected'; - -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import * as React from 'react'; -import {LazyNestedValueComponent} from 'nuclide-commons-ui/LazyNestedValueComponent'; -import SimpleValueComponent from 'nuclide-commons-ui/SimpleValueComponent'; -import invariant from 'assert'; -import {Observable} from 'rxjs'; -import {Section} from 'nuclide-commons-ui/Section'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import { - fetchChildrenForLazyComponent, - expressionAsEvaluationResult, -} from '../utils'; -import {Expect} from 'nuclide-commons/expected'; -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; - -type Props = {| - +service: IDebugService, -|}; - -const NO_VARIABLES = ( -
- (no variables) -
-); - -const LOADING = ( -
- - - -
-); - -type State = {| - scopes: Expected>, - expandedScopes: Set, -|}; - -export default class ScopesComponent extends React.Component { - _disposables: UniversalDisposable; - _expansionStates: Map< - string /* expression */, - Object /* unique reference for expression */, - >; - - constructor(props: Props) { +class ScopesComponent extends React.Component { + constructor(props) { super(props); + + this._getExpansionStateIdForExpression = expression => { + let expansionStateId = this._expansionStates.get(expression); + + if (expansionStateId == null) { + expansionStateId = {}; + + this._expansionStates.set(expression, expansionStateId); + } + + return expansionStateId; + }; + this.state = { - scopes: Expect.value([]), + scopes: _expected().Expect.value([]), // UX: Local scope names should be expanded by default. - expandedScopes: new Set(['Local', 'Locals']), + expandedScopes: new Set(['Local', 'Locals']) }; this._expansionStates = new Map(); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); } - componentDidMount(): void { - const {viewModel} = this.props.service; - this._disposables.add( - Observable.merge( - observableFromSubscribeFunction( - viewModel.onDidFocusStackFrame.bind(viewModel), - ), - observableFromSubscribeFunction( - viewModel.onDidChangeExpressionContext.bind(viewModel), - ), - ) - .debounceTime(100) - .startWith(null) - .switchMap(() => this._getScopes()) - .subscribe(scopes => { - this.setState({scopes}); - }), - ); + componentDidMount() { + const { + viewModel + } = this.props.service; + + this._disposables.add(_RxMin.Observable.merge((0, _event().observableFromSubscribeFunction)(viewModel.onDidFocusStackFrame.bind(viewModel)), (0, _event().observableFromSubscribeFunction)(viewModel.onDidChangeExpressionContext.bind(viewModel))).debounceTime(100).startWith(null).switchMap(() => this._getScopes()).subscribe(scopes => { + this.setState({ + scopes + }); + })); } - _getScopes(): Observable>> { - const {focusedStackFrame} = this.props.service.viewModel; + _getScopes() { + const { + focusedStackFrame + } = this.props.service.viewModel; + if (focusedStackFrame == null) { - return Observable.of(Expect.value([])); + return _RxMin.Observable.of(_expected().Expect.value([])); } else { - return Observable.of(Expect.pending()).concat( - Observable.fromPromise( - focusedStackFrame - .getScopes() - .then(scopes => Expect.value(scopes), error => Expect.error(error)), - ), - ); + return _RxMin.Observable.of(_expected().Expect.pending()).concat(_RxMin.Observable.fromPromise(focusedStackFrame.getScopes().then(scopes => _expected().Expect.value(scopes), error => _expected().Expect.error(error)))); } } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - _renderScopeSection(scope: IScope): ?React.Element { + _renderScopeSection(scope) { // Non-local scopes should be collapsed by default since users typically care less about them. const expanded = this._isScopeExpanded(scope); - const {focusedProcess} = this.props.service.viewModel; - const canSetVariables = - focusedProcess != null && - focusedProcess.session.capabilities.supportsSetVariable; + + const { + focusedProcess + } = this.props.service.viewModel; + const canSetVariables = focusedProcess != null && focusedProcess.session.capabilities.supportsSetVariable; let ScopeBodyComponent = () => null; + if (expanded) { - ScopeBodyComponent = bindObservableAsProps( - this._getScopeVariables(scope).map(variables => ({ - variables, - canSetVariables, - getExpansionStateIdForExpression: this - ._getExpansionStateIdForExpression, - })), - ScopeComponent, - ); + ScopeBodyComponent = (0, _bindObservableAsProps().bindObservableAsProps)(this._getScopeVariables(scope).map(variables => ({ + variables, + canSetVariables, + getExpansionStateIdForExpression: this._getExpansionStateIdForExpression + })), ScopeComponent); } - return ( -
this._setScopeExpanded(scope, !isCollapsed)} - headline={scope.name} - size="small"> - -
- ); - } - _getExpansionStateIdForExpression = (expression: string): Object => { - let expansionStateId = this._expansionStates.get(expression); - if (expansionStateId == null) { - expansionStateId = {}; - this._expansionStates.set(expression, expansionStateId); - } - return expansionStateId; - }; + return React.createElement(_Section().Section, { + key: scope.getId(), + collapsable: true, + collapsed: !expanded, + onChange: isCollapsed => this._setScopeExpanded(scope, !isCollapsed), + headline: scope.name, + size: "small" + }, React.createElement(ScopeBodyComponent, null)); + } - _getScopeVariables(scope: IScope): Observable>> { - return Observable.of(Expect.pending()).concat( - Observable.fromPromise( - scope - .getChildren() - .then( - variables => Expect.value(variables), - error => Expect.error(error), - ), - ), - ); + _getScopeVariables(scope) { + return _RxMin.Observable.of(_expected().Expect.pending()).concat(_RxMin.Observable.fromPromise(scope.getChildren().then(variables => _expected().Expect.value(variables), error => _expected().Expect.error(error)))); } - _isScopeExpanded(scope: IScope): boolean { + _isScopeExpanded(scope) { return this.state.expandedScopes.has(scope.name); } - _setScopeExpanded(scope: IScope, expanded: boolean): void { + _setScopeExpanded(scope, expanded) { if (expanded === this.state.expandedScopes.has(scope.name)) { return; - } - // TODO: (wbinnssmith) T30771435 this setState depends on current state + } // TODO: (wbinnssmith) T30771435 this setState depends on current state // and should use an updater function rather than an object // eslint-disable-next-line react/no-access-state-in-setstate + + const expandedScopes = new Set(this.state.expandedScopes); + if (expanded) { expandedScopes.add(scope.name); } else { expandedScopes.delete(scope.name); } - this.setState({expandedScopes}); + + this.setState({ + expandedScopes + }); } - render(): React.Node { - const {scopes} = this.state; - const {service} = this.props; + render() { + const { + scopes + } = this.state; + const { + service + } = this.props; + if (scopes.isError) { - return Error fetching scopes: {scopes.error.toString()}; + return React.createElement("span", null, "Error fetching scopes: ", scopes.error.toString()); } else if (scopes.isPending) { return LOADING; } else if (scopes.value.length === 0) { - return (no variables); + return React.createElement("span", null, "(no variables)"); } - const scopeSections = scopes.value.map(scope => - this._renderScopeSection(scope), - ); - const processName = - (service.viewModel.focusedProcess == null || - service.viewModel.focusedProcess.configuration.processName == null - ? 'Unknown Process' - : service.viewModel.focusedProcess.configuration.processName) + - (service.viewModel.focusedStackFrame == null - ? ' (Unknown Frame)' - : ' (' + service.viewModel.focusedStackFrame.name + ')'); - return ( -
- {processName} -
{scopeSections}
-
- ); + + const scopeSections = scopes.value.map(scope => this._renderScopeSection(scope)); + const processName = (service.viewModel.focusedProcess == null || service.viewModel.focusedProcess.configuration.processName == null ? 'Unknown Process' : service.viewModel.focusedProcess.configuration.processName) + (service.viewModel.focusedStackFrame == null ? ' (Unknown Frame)' : ' (' + service.viewModel.focusedStackFrame.name + ')'); + return React.createElement("div", null, React.createElement("span", null, processName), React.createElement("div", { + className: "debugger-expression-value-list" + }, scopeSections)); } + } -type ScopeProps = { - variables: Expected>, - canSetVariables: boolean, - getExpansionStateIdForExpression: (name: string) => Object, -}; +exports.default = ScopesComponent; + +class ScopeComponent extends React.Component { + constructor(...args) { + var _temp; + + return _temp = super(...args), this._setVariable = (expression, newValue) => { + const { + variables + } = this.props; + + if (!Boolean(expression) || !Boolean(newValue) || variables.isError || variables.isPending) { + return; + } + + const variable = variables.value.find(v => v.name === expression); + + if (variable == null) { + return; + } + + if (!(newValue != null)) { + throw new Error("Invariant violation: \"newValue != null\""); + } + + variable.setVariable(newValue).then(() => this.forceUpdate()); + }, _temp; + } -class ScopeComponent extends React.Component { render() { - const {variables} = this.props; + const { + variables + } = this.props; + if (variables.isError) { - return ( -
Error fetching scope variables {variables.error.toString()}
- ); + return React.createElement("div", null, "Error fetching scope variables ", variables.error.toString()); } else if (variables.isPending) { return LOADING; } else if (variables.value.length === 0) { @@ -236,42 +308,20 @@ class ScopeComponent extends React.Component { } } - _setVariable = (expression: ?string, newValue: ?string): void => { - const {variables} = this.props; - if ( - !Boolean(expression) || - !Boolean(newValue) || - variables.isError || - variables.isPending - ) { - return; - } - const variable = variables.value.find(v => v.name === expression); - if (variable == null) { - return; - } - invariant(newValue != null); - variable.setVariable(newValue).then(() => this.forceUpdate()); - }; - - _renderVariable(expression: IVariable): ?React.Element { - return ( -
-
- -
-
- ); + _renderVariable(expression) { + return React.createElement("div", { + className: "debugger-expression-value-row debugger-scope native-key-bindings", + key: expression.getId() + }, React.createElement("div", { + className: "debugger-expression-value-content" + }, React.createElement(_LazyNestedValueComponent().LazyNestedValueComponent, { + expression: expression.name, + evaluationResult: (0, _utils().expressionAsEvaluationResult)(expression), + fetchChildren: _utils().fetchChildrenForLazyComponent, + simpleValueComponent: _SimpleValueComponent().default, + expansionStateId: this.props.getExpansionStateIdForExpression(expression.name), + setVariable: this.props.canSetVariables ? this._setVariable : null + }))); } -} + +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ScopesView.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ScopesView.js index 0bd45007..0aa4165f 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ScopesView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ScopesView.js @@ -1,3 +1,66 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _ScopesComponent() { + const data = _interopRequireDefault(require("./ScopesComponent")); + + _ScopesComponent = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,63 +69,49 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {DebuggerModeType, IDebugService} from '../types'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import classnames from 'classnames'; -import ScopesComponent from './ScopesComponent'; -import {DebuggerMode} from '../constants'; - -type Props = { - service: IDebugService, -}; -type State = { - mode: DebuggerModeType, -}; - -export default class ScopesView extends React.PureComponent { - _scopesComponentWrapped: React.ComponentType; - _disposables: UniversalDisposable; - - constructor(props: Props) { +class ScopesView extends React.PureComponent { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); this.state = { - mode: props.service.getDebuggerMode(), + mode: props.service.getDebuggerMode() }; } - componentDidMount(): void { - const {service} = this.props; - this._disposables.add( - observableFromSubscribeFunction( - service.onDidChangeMode.bind(service), - ).subscribe(mode => this.setState({mode})), - ); + componentDidMount() { + const { + service + } = this.props; + + this._disposables.add((0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service)).subscribe(mode => this.setState({ + mode + }))); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - render(): React.Node { - const {service} = this.props; - const {mode} = this.state; - const disabledClass = - mode !== DebuggerMode.RUNNING ? '' : ' debugger-container-new-disabled'; - - return ( -
-
- -
-
- ); + render() { + const { + service + } = this.props; + const { + mode + } = this.state; + const disabledClass = mode !== _constants().DebuggerMode.RUNNING ? '' : ' debugger-container-new-disabled'; + return React.createElement("div", { + className: (0, _classnames().default)('debugger-container-new', disabledClass) + }, React.createElement("div", { + className: "debugger-pane-content" + }, React.createElement(_ScopesComponent().default, { + service: service + }))); } + } + +exports.default = ScopesView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ThreadTreeNode.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ThreadTreeNode.js index 50a795b9..5a197e3e 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ThreadTreeNode.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ThreadTreeNode.js @@ -1,3 +1,98 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _LoadingSpinner() { + const data = require("../../../../../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +function _Table() { + const data = require("../../../../../nuclide-commons-ui/Table"); + + _Table = function () { + return data; + }; + + return data; +} + +function _Tree() { + const data = require("../../../../../nuclide-commons-ui/Tree"); + + _Tree = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _expected() { + const data = require("../../../../../nuclide-commons/expected"); + + _expected = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,59 +101,50 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const LOADING = React.createElement("div", { + className: (0, _classnames().default)('debugger-expression-value-row', 'debugger-tree-no-frames') +}, React.createElement("span", { + className: "debugger-expression-value-content" +}, React.createElement(_LoadingSpinner().LoadingSpinner, { + size: "SMALL" +}))); -import type {IThread, IStackFrame, IDebugService} from '../types'; -import type {Expected} from 'nuclide-commons/expected'; - -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; -import {Table} from 'nuclide-commons-ui/Table'; -import {NestedTreeItem, TreeItem} from 'nuclide-commons-ui/Tree'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import * as React from 'react'; -import {Observable, Subject} from 'rxjs'; -import {DebuggerMode} from '../constants'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Expect} from 'nuclide-commons/expected'; -import classnames from 'classnames'; - -const LOADING = ( -
- - - -
-); - -type Props = { - thread: IThread, - service: IDebugService, -}; - -type State = { - isCollapsed: boolean, - childItems: Expected>, -}; - -export default class ThreadTreeNode extends React.Component { - _disposables: UniversalDisposable; - _selectTrigger: Subject; - - constructor(props: Props) { +class ThreadTreeNode extends React.Component { + constructor(props) { super(props); - this._selectTrigger = new Subject(); + + this.handleSelect = () => { + if (!this.state.isCollapsed) { + this.setState({ + isCollapsed: true + }); + } else { + this.setState({ + isCollapsed: false, + childItems: _expected().Expect.pending() + }); + + this._selectTrigger.next(); + } + }; + + this._handleStackFrameClick = (clickedRow, callFrameIndex) => { + this.props.service.focusStackFrame(clickedRow.frame, null, null, true); + }; + + this._selectTrigger = new _RxMin.Subject(); this.state = this._getInitialState(); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); } - _computeIsFocused(): boolean { - const {service, thread} = this.props; + _computeIsFocused() { + const { + service, + thread + } = this.props; const focusedThread = service.viewModel.focusedThread; return focusedThread != null && thread.threadId === focusedThread.threadId; } @@ -66,218 +152,159 @@ export default class ThreadTreeNode extends React.Component { _getInitialState() { return { isCollapsed: true, - childItems: Expect.pending(), + childItems: _expected().Expect.pending() }; } - _getFrames(fetch: boolean = false): Observable>> { - const {thread} = this.props; - const getValue = () => Observable.of(Expect.value(thread.getCallStack())); - if ( - fetch || - (!this.state.childItems.isPending && - !this.state.childItems.isError && - this.state.childItems.value.length === 0) - ) { - return Observable.of(Expect.pending()).concat( - Observable.fromPromise( - (async () => { - await thread.fetchCallStack(); - return Expect.value(thread.getCallStack()); - })(), - ), - ); + _getFrames(fetch = false) { + const { + thread + } = this.props; + + const getValue = () => _RxMin.Observable.of(_expected().Expect.value(thread.getCallStack())); + + if (fetch || !this.state.childItems.isPending && !this.state.childItems.isError && this.state.childItems.value.length === 0) { + return _RxMin.Observable.of(_expected().Expect.pending()).concat(_RxMin.Observable.fromPromise((async () => { + await thread.fetchCallStack(); + return _expected().Expect.value(thread.getCallStack()); + })())); } + return getValue(); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - componentDidMount(): void { - const {service} = this.props; + componentDidMount() { + const { + service + } = this.props; const model = service.getModel(); - const {viewModel} = service; - this._disposables.add( - Observable.merge( - observableFromSubscribeFunction( - viewModel.onDidFocusStackFrame.bind(viewModel), - ), - observableFromSubscribeFunction(service.onDidChangeMode.bind(service)), - ).subscribe(() => { - const {isCollapsed} = this.state; - - const newIsCollapsed = isCollapsed && !this._computeIsFocused(); - this.setState({ - isCollapsed: newIsCollapsed, - }); - }), - this._selectTrigger - .asObservable() - .switchMap(() => this._getFrames(true)) - .subscribe(frames => { - this.setState({ - childItems: frames, - }); - }), - observableFromSubscribeFunction(model.onDidChangeCallStack.bind(model)) - .debounceTime(100) - .startWith(null) - .switchMap(() => - this._getFrames().switchMap(frames => { - if ( - !this.state.isCollapsed && - !frames.isPending && - !frames.isError && - frames.value.length === 0 - ) { - return this._getFrames(true); - } - return Observable.of(frames); - }), - ) - .subscribe(frames => { - const {isCollapsed} = this.state; - - this.setState({ - childItems: frames, - isCollapsed: isCollapsed && !this._computeIsFocused(), - }); - }), - ); - } + const { + viewModel + } = service; - handleSelect = () => { - if (!this.state.isCollapsed) { + this._disposables.add(_RxMin.Observable.merge((0, _event().observableFromSubscribeFunction)(viewModel.onDidFocusStackFrame.bind(viewModel)), (0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service))).subscribe(() => { + const { + isCollapsed + } = this.state; + const newIsCollapsed = isCollapsed && !this._computeIsFocused(); this.setState({ - isCollapsed: true, + isCollapsed: newIsCollapsed }); - } else { + }), this._selectTrigger.asObservable().switchMap(() => this._getFrames(true)).subscribe(frames => { this.setState({ - isCollapsed: false, - childItems: Expect.pending(), + childItems: frames }); - this._selectTrigger.next(); - } - }; + }), (0, _event().observableFromSubscribeFunction)(model.onDidChangeCallStack.bind(model)).debounceTime(100).startWith(null).switchMap(() => this._getFrames().switchMap(frames => { + if (!this.state.isCollapsed && !frames.isPending && !frames.isError && frames.value.length === 0) { + return this._getFrames(true); + } - _handleStackFrameClick = ( - clickedRow: {frame: IStackFrame}, - callFrameIndex: number, - ): void => { - this.props.service.focusStackFrame(clickedRow.frame, null, null, true); - }; + return _RxMin.Observable.of(frames); + })).subscribe(frames => { + const { + isCollapsed + } = this.state; + this.setState({ + childItems: frames, + isCollapsed: isCollapsed && !this._computeIsFocused() + }); + })); + } - _generateTable(childItems: Array) { - const {service} = this.props; + _generateTable(childItems) { + const { + service + } = this.props; const rows = childItems.map((frame, frameIndex) => { const activeFrame = service.viewModel.focusedStackFrame; const isSelected = activeFrame != null ? frame === activeFrame : false; const cellData = { data: { name: frame.name, - source: - frame.source != null && frame.source.name != null - ? `${frame.source.name}` - : '', + source: frame.source != null && frame.source.name != null ? `${frame.source.name}` : '', line: `${frame.range.end.row}`, frame, - isSelected, + isSelected }, - className: isSelected ? 'debugger-callstack-item-selected' : undefined, + className: isSelected ? 'debugger-callstack-item-selected' : undefined }; return cellData; }); - const columns = [ - { - title: 'Name', - key: 'name', - width: 0.5, - }, - { - title: 'Source', - key: 'source', - width: 0.35, - }, - { - title: 'Line', - key: 'line', - width: 0.15, - }, - ]; - return ( -
-
-
cellData.frame.source.available} - resizable={true} - onSelect={this._handleStackFrameClick} - sortable={false} - /> - - - ); + const columns = [{ + title: 'Name', + key: 'name', + width: 0.5 + }, { + title: 'Source', + key: 'source', + width: 0.35 + }, { + title: 'Line', + key: 'line', + width: 0.15 + }]; + return React.createElement("div", { + className: (0, _classnames().default)({ + 'debugger-container-new-disabled': service.getDebuggerMode() === _constants().DebuggerMode.RUNNING + }) + }, React.createElement("div", { + className: "debugger-callstack-table-div" + }, React.createElement(_Table().Table, { + className: "debugger-callstack-table", + columns: columns, + rows: rows, + selectable: cellData => cellData.frame.source.available, + resizable: true, + onSelect: this._handleStackFrameClick, + sortable: false + }))); } - render(): React.Node { - const {thread, service} = this.props; - const {childItems} = this.state; + render() { + const { + thread, + service + } = this.props; + const { + childItems + } = this.state; + const isFocused = this._computeIsFocused(); + const handleTitleClick = event => { if (thread.stopped) { service.focusStackFrame(null, thread, null, true); } + event.stopPropagation(); }; - const formattedTitle = ( - - {thread.name + - (thread.stoppedDetails == null ? ' (Running)' : ' (Paused)')} - - ); - - if ( - !childItems.isPending && - !childItems.isError && - childItems.value.length === 0 - ) { - return ( - - {formattedTitle} - - ); + + const formattedTitle = React.createElement("span", { + onClick: handleTitleClick, + className: isFocused ? (0, _classnames().default)('debugger-tree-process-thread-selected') : '', + title: 'Thread ID: ' + thread.threadId + ', Name: ' + thread.name + }, thread.name + (thread.stoppedDetails == null ? ' (Running)' : ' (Paused)')); + + if (!childItems.isPending && !childItems.isError && childItems.value.length === 0) { + return React.createElement(_Tree().TreeItem, { + className: "debugger-tree-no-frames" + }, formattedTitle); } - const callFramesElements = childItems.isPending ? ( - LOADING - ) : childItems.isError ? ( - - Error fetching stack frames {childItems.error.toString()} - - ) : ( - this._generateTable(childItems.value) - ); - - return ( - - {callFramesElements} - - ); + const callFramesElements = childItems.isPending ? LOADING : childItems.isError ? React.createElement("span", { + className: "debugger-tree-no-frames" + }, "Error fetching stack frames ", childItems.error.toString()) : this._generateTable(childItems.value); + return React.createElement(_Tree().NestedTreeItem, { + title: formattedTitle, + collapsed: this.state.isCollapsed, + onSelect: this.handleSelect + }, callFramesElements); } + } + +exports.default = ThreadTreeNode; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ThreadsView.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ThreadsView.js index b934c5ba..bf570f4a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ThreadsView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/ThreadsView.js @@ -1,3 +1,66 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _DebuggerThreadsComponent() { + const data = _interopRequireDefault(require("./DebuggerThreadsComponent")); + + _DebuggerThreadsComponent = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,68 +69,53 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DebuggerModeType, IDebugService} from '../types'; - -import classnames from 'classnames'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import DebuggerThreadsComponent from './DebuggerThreadsComponent'; -import {DebuggerMode} from '../constants'; - -type Props = { - service: IDebugService, -}; - -export default class ThreadsView extends React.PureComponent< - Props, - { - mode: DebuggerModeType, - }, -> { - _disposables: UniversalDisposable; - - constructor(props: Props) { +class ThreadsView extends React.PureComponent { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); this.state = { - mode: props.service.getDebuggerMode(), + mode: props.service.getDebuggerMode() }; } - componentDidMount(): void { - const {service} = this.props; - this._disposables.add( - observableFromSubscribeFunction( - service.onDidChangeMode.bind(service), - ).subscribe(mode => this.setState({mode})), - ); + componentDidMount() { + const { + service + } = this.props; + + this._disposables.add((0, _event().observableFromSubscribeFunction)(service.onDidChangeMode.bind(service)).subscribe(mode => this.setState({ + mode + }))); } - componentWillUnmount(): void { + componentWillUnmount() { this._dispose(); } - _dispose(): void { + _dispose() { this._disposables.dispose(); } - render(): React.Node { - const {service} = this.props; - const {mode} = this.state; - const disabledClass = - mode !== DebuggerMode.RUNNING ? '' : ' debugger-container-new-disabled'; - - return ( -
-
- -
-
- ); + render() { + const { + service + } = this.props; + const { + mode + } = this.state; + const disabledClass = mode !== _constants().DebuggerMode.RUNNING ? '' : ' debugger-container-new-disabled'; + return React.createElement("div", { + className: (0, _classnames().default)('debugger-container-new', disabledClass) + }, React.createElement("div", { + className: "debugger-pane-content" + }, React.createElement(_DebuggerThreadsComponent().default, { + service: service + }))); } + } + +exports.default = ThreadsView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/WatchExpressionComponent.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/WatchExpressionComponent.js index 9de17530..82e44aec 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/WatchExpressionComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/WatchExpressionComponent.js @@ -1,3 +1,98 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +var React = _interopRequireWildcard(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _AtomInput() { + const data = require("../../../../../nuclide-commons-ui/AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +function _bindObservableAsProps() { + const data = require("../../../../../nuclide-commons-ui/bindObservableAsProps"); + + _bindObservableAsProps = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _LazyNestedValueComponent() { + const data = require("../../../../../nuclide-commons-ui/LazyNestedValueComponent"); + + _LazyNestedValueComponent = function () { + return data; + }; + + return data; +} + +function _SimpleValueComponent() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/SimpleValueComponent")); + + _SimpleValueComponent = function () { + return data; + }; + + return data; +} + +function _Icon() { + const data = require("../../../../../nuclide-commons-ui/Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("../utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,205 +101,150 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {IEvaluatableExpression, IStackFrame, IProcess} from '../types'; - -import {Observable} from 'rxjs'; -import * as React from 'react'; -import classnames from 'classnames'; -import {AtomInput} from 'nuclide-commons-ui/AtomInput'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import nullthrows from 'nullthrows'; -import {LazyNestedValueComponent} from 'nuclide-commons-ui/LazyNestedValueComponent'; -import SimpleValueComponent from 'nuclide-commons-ui/SimpleValueComponent'; -import {Icon} from 'nuclide-commons-ui/Icon'; -import { - expressionAsEvaluationResultStream, - fetchChildrenForLazyComponent, -} from '../utils'; - -type Props = { - watchExpressions: Array, - focusedStackFrame: ?IStackFrame, - focusedProcess: ?IProcess, - onAddWatchExpression: (expression: string) => void, - onRemoveWatchExpression: (id: string) => void, - onUpdateWatchExpression: (id: string, newExpression: string) => void, -}; - -type State = { - rowBeingEdited: ?string, -}; - -export default class WatchExpressionComponent extends React.Component< - Props, - State, -> { - coreCancelDisposable: ?IDisposable; - _newExpressionEditor: ?AtomInput; - _editExpressionEditor: ?AtomInput; - _expansionStates: Map< - string /* expression */, - /* unique reference for expression */ Object, - >; - - constructor(props: Props) { +class WatchExpressionComponent extends React.Component { + constructor(props) { super(props); + + this._onConfirmNewExpression = () => { + const text = (0, _nullthrows().default)(this._newExpressionEditor).getText(); + this.addExpression(text); + (0, _nullthrows().default)(this._newExpressionEditor).setText(''); + }; + + this._resetExpressionEditState = () => { + if (this.coreCancelDisposable) { + this.coreCancelDisposable.dispose(); + this.coreCancelDisposable = null; + } + + this.setState({ + rowBeingEdited: null + }); + }; + + this._renderExpression = watchExpression => { + const { + focusedProcess, + focusedStackFrame + } = this.props; + const id = watchExpression.getId(); + let evalResult; + + if (id === this.state.rowBeingEdited) { + return React.createElement(_AtomInput().AtomInput, { + className: "debugger-watch-expression-input", + autofocus: true, + startSelected: true, + key: id, + onConfirm: this._onConfirmExpressionEdit.bind(this, id), + onCancel: this._resetExpressionEditState, + onBlur: this._resetExpressionEditState, + ref: input => { + this._editExpressionEditor = input; + }, + size: "sm", + initialValue: watchExpression.name + }); + } else if (focusedProcess == null) { + evalResult = _RxMin.Observable.of(null); + } else { + evalResult = (0, _utils().expressionAsEvaluationResultStream)(watchExpression, focusedProcess, focusedStackFrame, 'watch'); + } + + const ValueComponent = (0, _bindObservableAsProps().bindObservableAsProps)(evalResult.map(evaluationResult => ({ + evaluationResult + })), _LazyNestedValueComponent().LazyNestedValueComponent); + return React.createElement("div", { + className: (0, _classnames().default)('debugger-expression-value-row', 'debugger-watch-expression-row'), + key: id + }, React.createElement("div", { + className: (0, _classnames().default)('debugger-expression-value-content', 'debugger-watch-expression-value-content'), + onDoubleClick: this._setRowBeingEdited.bind(this, id) + }, React.createElement(ValueComponent, { + expression: watchExpression.name, + fetchChildren: _utils().fetchChildrenForLazyComponent, + simpleValueComponent: _SimpleValueComponent().default, + expansionStateId: this._getExpansionStateIdForExpression(watchExpression.name) + })), React.createElement("div", { + className: "debugger-watch-expression-controls" + }, React.createElement(_Icon().Icon, { + icon: "pencil", + className: "debugger-watch-expression-control", + onClick: this._setRowBeingEdited.bind(this, id) + }), React.createElement(_Icon().Icon, { + icon: "x", + className: "debugger-watch-expression-control", + onClick: this.removeExpression.bind(this, id) + }))); + }; + this._expansionStates = new Map(); this.state = { - rowBeingEdited: null, + rowBeingEdited: null }; } - _getExpansionStateIdForExpression(expression: string): Object { + _getExpansionStateIdForExpression(expression) { let expansionStateId = this._expansionStates.get(expression); + if (expansionStateId == null) { expansionStateId = {}; + this._expansionStates.set(expression, expansionStateId); } + return expansionStateId; } - removeExpression(id: string, event: MouseEvent): void { + removeExpression(id, event) { event.stopPropagation(); this.props.onRemoveWatchExpression(id); } - addExpression(expression: string): void { + addExpression(expression) { this.props.onAddWatchExpression(expression); } - _onConfirmNewExpression = (): void => { - const text = nullthrows(this._newExpressionEditor).getText(); - this.addExpression(text); - nullthrows(this._newExpressionEditor).setText(''); - }; - - _onConfirmExpressionEdit(id: string): void { - const text = nullthrows(this._editExpressionEditor).getText(); + _onConfirmExpressionEdit(id) { + const text = (0, _nullthrows().default)(this._editExpressionEditor).getText(); this.props.onUpdateWatchExpression(id, text); + this._resetExpressionEditState(); } - _setRowBeingEdited(id: string): void { + _setRowBeingEdited(id) { this.setState({ - rowBeingEdited: id, + rowBeingEdited: id }); + if (this.coreCancelDisposable) { this.coreCancelDisposable.dispose(); } + this.coreCancelDisposable = atom.commands.add('atom-workspace', { - 'core:cancel': () => this._resetExpressionEditState(), + 'core:cancel': () => this._resetExpressionEditState() }); } - _resetExpressionEditState = (): void => { - if (this.coreCancelDisposable) { - this.coreCancelDisposable.dispose(); - this.coreCancelDisposable = null; - } - this.setState({rowBeingEdited: null}); - }; - - _renderExpression = ( - watchExpression: IEvaluatableExpression, - ): React.Element => { - const {focusedProcess, focusedStackFrame} = this.props; - const id = watchExpression.getId(); - let evalResult; - if (id === this.state.rowBeingEdited) { - return ( - { - this._editExpressionEditor = input; - }} - size="sm" - initialValue={watchExpression.name} - /> - ); - } else if (focusedProcess == null) { - evalResult = Observable.of(null); - } else { - evalResult = expressionAsEvaluationResultStream( - watchExpression, - focusedProcess, - focusedStackFrame, - 'watch', - ); - } - const ValueComponent = bindObservableAsProps( - evalResult.map(evaluationResult => ({evaluationResult})), - LazyNestedValueComponent, - ); - return ( -
-
- -
-
- - -
-
- ); - }; - - render(): React.Node { + render() { const expressions = this.props.watchExpressions.map(this._renderExpression); - const addNewExpressionInput = ( - { - this._newExpressionEditor = input; - }} - size="sm" - placeholderText="Add new watch expression" - /> - ); - return ( -
- {expressions} - {addNewExpressionInput} -
- ); + const addNewExpressionInput = React.createElement(_AtomInput().AtomInput, { + className: (0, _classnames().default)('debugger-watch-expression-input', 'debugger-watch-expression-add-new-input'), + onConfirm: this._onConfirmNewExpression, + ref: input => { + this._newExpressionEditor = input; + }, + size: "sm", + placeholderText: "Add new watch expression" + }); + return React.createElement("div", { + className: "debugger-expression-value-list" + }, expressions, addNewExpressionInput); } + } + +exports.default = WatchExpressionComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/WatchView.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/WatchView.js index c0c90fc2..484bad25 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/WatchView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/ui/WatchView.js @@ -1,3 +1,68 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _bindObservableAsProps() { + const data = require("../../../../../nuclide-commons-ui/bindObservableAsProps"); + + _bindObservableAsProps = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _WatchExpressionComponent() { + const data = _interopRequireDefault(require("./WatchExpressionComponent")); + + _WatchExpressionComponent = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,81 +71,46 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {IDebugService} from '../types'; - -import classnames from 'classnames'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {Observable} from 'rxjs'; -import WatchExpressionComponent from './WatchExpressionComponent'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; - -type Props = { - service: IDebugService, -}; - -export default class WatchView extends React.PureComponent { - _watchExpressionComponentWrapped: React.ComponentType; - _disposables: UniversalDisposable; - - constructor(props: Props) { +class WatchView extends React.PureComponent { + constructor(props) { super(props); - const {service} = props; - const {viewModel} = service; + const { + service + } = props; + const { + viewModel + } = service; const model = service.getModel(); - const watchExpressionChanges = observableFromSubscribeFunction( - model.onDidChangeWatchExpressions.bind(model), - ); - const focusedProcessChanges = observableFromSubscribeFunction( - viewModel.onDidFocusProcess.bind(viewModel), - ); - const focusedStackFrameChanges = observableFromSubscribeFunction( - viewModel.onDidFocusStackFrame.bind(viewModel), - ); - const expressionContextChanges = observableFromSubscribeFunction( - viewModel.onDidChangeExpressionContext.bind(viewModel), - ); - this._watchExpressionComponentWrapped = bindObservableAsProps( - Observable.merge( - watchExpressionChanges, - focusedProcessChanges, - focusedStackFrameChanges, - expressionContextChanges, - ) - .startWith(null) - .map(() => ({ - focusedProcess: viewModel.focusedProcess, - focusedStackFrame: viewModel.focusedStackFrame, - watchExpressions: model.getWatchExpressions(), - })), - WatchExpressionComponent, - ); + const watchExpressionChanges = (0, _event().observableFromSubscribeFunction)(model.onDidChangeWatchExpressions.bind(model)); + const focusedProcessChanges = (0, _event().observableFromSubscribeFunction)(viewModel.onDidFocusProcess.bind(viewModel)); + const focusedStackFrameChanges = (0, _event().observableFromSubscribeFunction)(viewModel.onDidFocusStackFrame.bind(viewModel)); + const expressionContextChanges = (0, _event().observableFromSubscribeFunction)(viewModel.onDidChangeExpressionContext.bind(viewModel)); + this._watchExpressionComponentWrapped = (0, _bindObservableAsProps().bindObservableAsProps)(_RxMin.Observable.merge(watchExpressionChanges, focusedProcessChanges, focusedStackFrameChanges, expressionContextChanges).startWith(null).map(() => ({ + focusedProcess: viewModel.focusedProcess, + focusedStackFrame: viewModel.focusedStackFrame, + watchExpressions: model.getWatchExpressions() + })), _WatchExpressionComponent().default); } - render(): React.Node { - const {service} = this.props; - const WatchExpressionComponentWrapped = this - ._watchExpressionComponentWrapped; - - return ( -
-
- -
-
- ); + render() { + const { + service + } = this.props; + const WatchExpressionComponentWrapped = this._watchExpressionComponentWrapped; + return React.createElement("div", { + className: (0, _classnames().default)('debugger-container-new') + }, React.createElement("div", { + className: "debugger-pane-content" + }, React.createElement(WatchExpressionComponentWrapped, { + onAddWatchExpression: service.addWatchExpression.bind(service), + onRemoveWatchExpression: service.removeWatchExpressions.bind(service), + onUpdateWatchExpression: service.renameWatchExpression.bind(service) + }))); } + } + +exports.default = WatchView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/utils.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/utils.js index 0153524f..aa0667f3 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/utils.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/utils.js @@ -1,3 +1,42 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.openSourceLocation = openSourceLocation; +exports.getLineForEvent = getLineForEvent; +exports.isLocalScopeName = isLocalScopeName; +exports.expressionAsEvaluationResult = expressionAsEvaluationResult; +exports.expressionAsEvaluationResultStream = expressionAsEvaluationResultStream; +exports.fetchChildrenForLazyComponent = fetchChildrenForLazyComponent; +exports.onUnexpectedError = onUnexpectedError; +exports.capitalize = capitalize; +exports.notifyOpenDebugSession = notifyOpenDebugSession; + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _logger() { + const data = _interopRequireDefault(require("./logger")); + + _logger = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,26 +45,12 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - IExpression, - IEvaluatableExpression, - IProcess, - IStackFrame, - ContextType, -} from './types'; -import type {EvaluationResult} from 'nuclide-commons-ui/TextRenderer'; -import type {ExpansionResult} from 'nuclide-commons-ui/LazyNestedValueComponent'; - -import nullthrows from 'nullthrows'; -import {Observable} from 'rxjs'; -import logger from './logger'; - -function getGutterLineNumber(target: HTMLElement): ?number { +function getGutterLineNumber(target) { const eventLine = parseInt(target.dataset.line, 10); + if (eventLine != null && eventLine >= 0 && !isNaN(Number(eventLine))) { return eventLine; } @@ -33,85 +58,79 @@ function getGutterLineNumber(target: HTMLElement): ?number { const SCREEN_ROW_ATTRIBUTE_NAME = 'data-screen-row'; -function getEditorLineNumber( - editor: atom$TextEditor, - target: HTMLElement, -): ?number { +function getEditorLineNumber(editor, target) { let node = target; + while (node != null) { if (node.hasAttribute(SCREEN_ROW_ATTRIBUTE_NAME)) { const screenRow = Number(node.getAttribute(SCREEN_ROW_ATTRIBUTE_NAME)); + try { return editor.bufferPositionForScreenPosition([screenRow, 0]).row; } catch (error) { return null; } } + node = node.parentElement; } } -export async function openSourceLocation( - path: string, - line: number, -): Promise { +async function openSourceLocation(path, line) { // eslint-disable-next-line nuclide-internal/atom-apis const editor = await atom.workspace.open(path, { searchAllPanes: true, - pending: true, + pending: true }); + if (editor == null) { // Failed to open file. Return an empty text editor. // eslint-disable-next-line nuclide-internal/atom-apis return atom.workspace.open(); } - editor.scrollToBufferPosition([line, 0]); - editor.setCursorBufferPosition([line, 0]); - // Put the focus back in the console prompt. - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'atom-ide-console:focus-console-prompt', - ); + editor.scrollToBufferPosition([line, 0]); + editor.setCursorBufferPosition([line, 0]); // Put the focus back in the console prompt. + atom.commands.dispatch(atom.views.getView(atom.workspace), 'atom-ide-console:focus-console-prompt'); return editor; } function firstNonNull(...args) { - return nullthrows(args.find(arg => arg != null)); + return (0, _nullthrows().default)(args.find(arg => arg != null)); } -export function getLineForEvent(editor: atom$TextEditor, event: any): number { +function getLineForEvent(editor, event) { const cursorLine = editor.getLastCursor().getBufferRow(); - const target = event ? (event.target: HTMLElement) : null; + const target = event ? event.target : null; + if (target == null) { return cursorLine; - } - // toggleLine is the line the user clicked in the gutter next to, as opposed + } // toggleLine is the line the user clicked in the gutter next to, as opposed // to the line the editor's cursor happens to be in. If this command was invoked // from the menu, then the cursor position is the target line. - return firstNonNull( - getGutterLineNumber(target), - getEditorLineNumber(editor, target), - // fall back to the line the cursor is on. - cursorLine, - ); + + + return firstNonNull(getGutterLineNumber(target), getEditorLineNumber(editor, target), // fall back to the line the cursor is on. + cursorLine); } -export function isLocalScopeName(scopeName: string): boolean { +function isLocalScopeName(scopeName) { return ['Local', 'Locals'].indexOf(scopeName) !== -1; } -export function expressionAsEvaluationResult( - expression: IExpression, -): EvaluationResult { +function expressionAsEvaluationResult(expression) { const value = expression.getValue(); + if (!expression.available) { - return {type: 'error', value}; + return { + type: 'error', + value + }; } else if (!expression.hasChildren()) { return { type: typeForSimpleValue(value), - value, + value }; } else { return { @@ -119,25 +138,16 @@ export function expressionAsEvaluationResult( description: value, // Used a means to get children when requested later. // $FlowFixMe: that isn't an object ID, - objectId: expression, + objectId: expression }; } } -export function expressionAsEvaluationResultStream( - expression: IEvaluatableExpression, - focusedProcess: IProcess, - focusedStackFrame: ?IStackFrame, - context: ContextType, -): Observable { - return Observable.fromPromise( - expression.evaluate(focusedProcess, focusedStackFrame, context), - ) - .map(() => expressionAsEvaluationResult(expression)) - .startWith(null); +function expressionAsEvaluationResultStream(expression, focusedProcess, focusedStackFrame, context) { + return _RxMin.Observable.fromPromise(expression.evaluate(focusedProcess, focusedStackFrame, context)).map(() => expressionAsEvaluationResult(expression)).startWith(null); } -function typeForSimpleValue(value: string): string { +function typeForSimpleValue(value) { if (value === 'undefined' || value === 'null') { return value; } else { @@ -145,41 +155,29 @@ function typeForSimpleValue(value: string): string { } } -export function fetchChildrenForLazyComponent( - expression: IExpression, -): Observable { - return Observable.fromPromise( - expression.getChildren().then( - children => - children.map(child => ({ - name: child.name, - value: expressionAsEvaluationResult(child), - })), - error => null, - ), - ); +function fetchChildrenForLazyComponent(expression) { + return _RxMin.Observable.fromPromise(expression.getChildren().then(children => children.map(child => ({ + name: child.name, + value: expressionAsEvaluationResult(child) + })), error => null)); } -export function onUnexpectedError(error: any) { +function onUnexpectedError(error) { const errorMessage = error.stack || error.message || String(error); - logger.error('Unexpected error', error); - atom.notifications.addError( - 'Atom debugger ran into an unexpected error - please file a bug!', - { - detail: errorMessage, - }, - ); + + _logger().default.error('Unexpected error', error); + + atom.notifications.addError('Atom debugger ran into an unexpected error - please file a bug!', { + detail: errorMessage + }); } -export function capitalize(str: string): string { +function capitalize(str) { return str[0].toUpperCase() + str.slice(1); } -export function notifyOpenDebugSession(): void { - atom.notifications.addInfo( - "Received a debug request, but there's an open debug session already!", - { - detail: 'Please terminate your existing debug session', - }, - ); -} +function notifyOpenDebugSession() { + atom.notifications.addInfo("Received a debug request, but there's an open debug session already!", { + detail: 'Please terminate your existing debug session' + }); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/vsp/DebugService.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/vsp/DebugService.js index 3b8a9bb3..8b05a187 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/vsp/DebugService.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/vsp/DebugService.js @@ -1,3 +1,224 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function DebugProtocol() { + const data = _interopRequireWildcard(require("vscode-debugprotocol")); + + DebugProtocol = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _Icon() { + const data = require("../../../../../nuclide-commons-ui/Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("../../../../../nuclide-commons/promise"); + + _promise = function () { + return data; + }; + + return data; +} + +function _nuclideDebuggerCommon() { + const data = require("../../../../../nuclide-debugger-common"); + + _nuclideDebuggerCommon = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _TextEditorBanner() { + const data = require("../../../../../nuclide-commons-ui/TextEditorBanner"); + + _TextEditorBanner = function () { + return data; + }; + + return data; +} + +function _ReadOnlyNotice() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/ReadOnlyNotice")); + + _ReadOnlyNotice = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _AtomServiceContainer() { + const data = require("../AtomServiceContainer"); + + _AtomServiceContainer = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("../utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _DebuggerModel() { + const data = require("./DebuggerModel"); + + _DebuggerModel = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _collection() { + const data = require("../../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _uuid() { + const data = _interopRequireDefault(require("uuid")); + + _uuid = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _logger() { + const data = _interopRequireDefault(require("../logger")); + + _logger = function () { + return data; + }; + + return data; +} + +function _stripAnsi() { + const data = _interopRequireDefault(require("strip-ansi")); + + _stripAnsi = function () { + return data; + }; + + return data; +} + +var _url = _interopRequireDefault(require("url")); + +var _os = _interopRequireDefault(require("os")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +227,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -38,317 +259,315 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -import type {ConsoleMessage} from 'atom-ide-ui'; -import type {GatekeeperService} from 'nuclide-commons-atom/types'; -import type {TerminalInfo} from '../../../atom-ide-terminal/lib/types'; -import type { - DebuggerModeType, - IDebugService, - IModel, - IViewModel, - IProcess, - IThread, - IEnableable, - IEvaluatableExpression, - IRawBreakpoint, - IStackFrame, - SerializedState, -} from '../types'; -import type { - IProcessConfig, - MessageProcessor, - VSAdapterExecutableInfo, -} from 'nuclide-debugger-common'; -import type {EvaluationResult} from 'nuclide-commons-ui/TextRenderer'; -import type {TimingTracker} from 'nuclide-commons/analytics'; -import * as DebugProtocol from 'vscode-debugprotocol'; -import * as React from 'react'; - -import invariant from 'assert'; -import {Icon} from 'nuclide-commons-ui/Icon'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {splitStream} from 'nuclide-commons/observable'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {sleep, serializeAsyncCall} from 'nuclide-commons/promise'; -import { - VsDebugSession, - localToRemoteProcessor, - remoteToLocalProcessor, - getVSCodeDebuggerAdapterServiceByNuclideUri, -} from 'nuclide-debugger-common'; -import {Observable, Subject, TimeoutError} from 'rxjs'; -import {TextEditorBanner} from 'nuclide-commons-ui/TextEditorBanner'; -import ReadOnlyNotice from 'nuclide-commons-ui/ReadOnlyNotice'; -import {track, startTracking} from 'nuclide-commons/analytics'; -import nullthrows from 'nullthrows'; -import { - getConsoleRegisterExecutor, - getConsoleService, - getNotificationService, - getDatatipService, - getTerminalService, - resolveDebugConfiguration, -} from '../AtomServiceContainer'; -import { - expressionAsEvaluationResultStream, - fetchChildrenForLazyComponent, - capitalize, -} from '../utils'; -import { - Model, - ExceptionBreakpoint, - FunctionBreakpoint, - Breakpoint, - Expression, - Process, -} from './DebuggerModel'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Emitter, TextBuffer} from 'atom'; -import {distinct, mapFromObject} from 'nuclide-commons/collection'; -import {onUnexpectedError} from '../utils'; -import uuid from 'uuid'; -import { - BreakpointEventReasons, - DebuggerMode, - AnalyticsEvents, - DEBUG_SOURCES_URI, -} from '../constants'; -import logger from '../logger'; -import stripAnsi from 'strip-ansi'; -import url from 'url'; -import os from 'os'; -import idx from 'idx'; - const CONSOLE_VIEW_URI = 'atom://nuclide/console'; - const CUSTOM_DEBUG_EVENT = 'CUSTOM_DEBUG_EVENT'; const CHANGE_DEBUG_MODE = 'CHANGE_DEBUG_MODE'; const START_DEBUG_SESSION = 'START_DEBUG_SESSION'; - const CHANGE_FOCUSED_PROCESS = 'CHANGE_FOCUSED_PROCESS'; const CHANGE_FOCUSED_STACKFRAME = 'CHANGE_FOCUSED_STACKFRAME'; -const CHANGE_EXPRESSION_CONTEXT = 'CHANGE_EXPRESSION_CONTEXT'; +const CHANGE_EXPRESSION_CONTEXT = 'CHANGE_EXPRESSION_CONTEXT'; // Berakpoint events may arrive sooner than breakpoint responses. -// Berakpoint events may arrive sooner than breakpoint responses. const MAX_BREAKPOINT_EVENT_DELAY_MS = 5 * 1000; -let _gkService: ?GatekeeperService; - -class ViewModel implements IViewModel { - _focusedProcess: ?IProcess; - _focusedThread: ?IThread; - _focusedStackFrame: ?IStackFrame; - _emitter: Emitter; +let _gkService; +class ViewModel { constructor() { this._focusedProcess = null; this._focusedThread = null; this._focusedStackFrame = null; - this._emitter = new Emitter(); + this._emitter = new _atom.Emitter(); } - get focusedProcess(): ?IProcess { + get focusedProcess() { return this._focusedProcess; } - get focusedThread(): ?IThread { - return this._focusedStackFrame != null - ? this._focusedStackFrame.thread - : this._focusedThread; + get focusedThread() { + return this._focusedStackFrame != null ? this._focusedStackFrame.thread : this._focusedThread; } - get focusedStackFrame(): ?IStackFrame { + get focusedStackFrame() { return this._focusedStackFrame; } - onDidFocusProcess(callback: (process: ?IProcess) => mixed): IDisposable { + onDidFocusProcess(callback) { return this._emitter.on(CHANGE_FOCUSED_PROCESS, callback); } - onDidFocusStackFrame( - callback: (data: {stackFrame: ?IStackFrame, explicit: boolean}) => mixed, - ): IDisposable { + onDidFocusStackFrame(callback) { return this._emitter.on(CHANGE_FOCUSED_STACKFRAME, callback); } - onDidChangeExpressionContext( - callback: (data: {stackFrame: ?IStackFrame, explicit: boolean}) => mixed, - ): IDisposable { + onDidChangeExpressionContext(callback) { return this._emitter.on(CHANGE_EXPRESSION_CONTEXT, callback); } - isMultiProcessView(): boolean { + isMultiProcessView() { return false; } - setFocus( - stackFrame: ?IStackFrame, - thread: ?IThread, - process: ?IProcess, - explicit: boolean, - ): void { - const shouldEmit = - this._focusedProcess !== process || - this._focusedThread !== thread || - this._focusedStackFrame !== stackFrame || - explicit; + setFocus(stackFrame, thread, process, explicit) { + const shouldEmit = this._focusedProcess !== process || this._focusedThread !== thread || this._focusedStackFrame !== stackFrame || explicit; + if (this._focusedProcess !== process) { this._focusedProcess = process; + this._emitter.emit(CHANGE_FOCUSED_PROCESS, process); } + this._focusedThread = thread; this._focusedStackFrame = stackFrame; if (shouldEmit) { - this._emitter.emit(CHANGE_FOCUSED_STACKFRAME, {stackFrame, explicit}); + this._emitter.emit(CHANGE_FOCUSED_STACKFRAME, { + stackFrame, + explicit + }); } else { // The focused stack frame didn't change, but something about the // context did, so interested listeners should re-evaluate expressions. - this._emitter.emit(CHANGE_EXPRESSION_CONTEXT, {stackFrame, explicit}); + this._emitter.emit(CHANGE_EXPRESSION_CONTEXT, { + stackFrame, + explicit + }); } } + } -function getDebuggerName(adapterType: string): string { - return `${capitalize(adapterType)} Debugger`; +function getDebuggerName(adapterType) { + return `${(0, _utils().capitalize)(adapterType)} Debugger`; } -export default class DebugService implements IDebugService { - _model: Model; - _disposables: UniversalDisposable; - _sessionEndDisposables: UniversalDisposable; - _consoleDisposables: IDisposable; - _debuggerMode: DebuggerModeType; - _emitter: Emitter; - _viewModel: ViewModel; - _timer: ?TimingTracker; - _breakpointsToSendOnSave: Set; - - constructor(state: ?SerializedState) { - this._disposables = new UniversalDisposable(); - this._sessionEndDisposables = new UniversalDisposable(); - this._consoleDisposables = new UniversalDisposable(); - this._emitter = new Emitter(); - this._debuggerMode = DebuggerMode.STOPPED; +class DebugService { + constructor(state) { + this._runInTerminal = async args => { + const terminalService = (0, _AtomServiceContainer().getTerminalService)(); + + if (terminalService == null) { + throw new Error('Unable to launch in terminal since the service is not available'); + } + + const process = this._getCurrentProcess(); + + if (process == null) { + throw new Error("There's no debug process to create a terminal for!"); + } + + const { + adapterType, + targetUri + } = process.configuration; + const key = `targetUri=${targetUri}&command=${args.args[0]}`; // Ensure any previous instances of this same target are closed before + // opening a new terminal tab. We don't want them to pile up if the + // user keeps running the same app over and over. + + terminalService.close(key); + const title = args.title != null ? args.title : getDebuggerName(adapterType); + + const hostname = _nuclideUri().default.getHostnameOpt(targetUri); + + const cwd = hostname == null ? args.cwd : _nuclideUri().default.createRemoteUri(hostname, args.cwd); + const info = { + key, + title, + cwd, + command: { + file: args.args[0], + args: args.args.slice(1) + }, + environmentVariables: args.env != null ? (0, _collection().mapFromObject)(args.env) : undefined, + preservedCommands: ['debugger:continue-debugging', 'debugger:stop-debugging', 'debugger:restart-debugging', 'debugger:step-over', 'debugger:step-into', 'debugger:step-out'], + remainOnCleanExit: true, + icon: 'nuclicon-debugger', + defaultLocation: 'bottom' + }; + const terminal = await terminalService.open(info); + terminal.setProcessExitCallback(() => { + // This callback is invoked if the target process dies first, ensuring + // we tear down the debugger. + this.stopProcess(); + }); + + this._sessionEndDisposables.add(() => { + // This termination path is invoked if the debugger dies first, ensuring + // we terminate the target process. This can happen if the user hits stop, + // or if the debugger crashes. + terminal.setProcessExitCallback(() => {}); + terminal.terminateProcess(); + }); + }; + + this._onSessionEnd = givenSession => { + const session = givenSession == null ? this._getCurrentSession() : givenSession; + + if (session == null) { + return; + } + + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STOP); + + const removedProcesses = this._model.removeProcess(session.getId()); + + if (this._model.getProcesses() == null || this._model.getProcesses().length === 0) { + this._sessionEndDisposables.dispose(); + + this._consoleDisposables.dispose(); + + this.focusStackFrame(null, null, null); + + this._updateModeAndEmit(_constants().DebuggerMode.STOPPED); + } else { + if (this._viewModel.focusedProcess != null && this._viewModel.focusedProcess.getId() === session.getId()) { + const processToFocus = this._model.getProcesses()[this._model.getProcesses().length - 1]; + + const threadToFocus = processToFocus.getAllThreads().length > 0 ? processToFocus.getAllThreads()[0] : null; + const frameToFocus = threadToFocus != null && threadToFocus.getCallStack.length > 0 ? threadToFocus.getCallStack()[0] : null; + this.focusStackFrame(frameToFocus, threadToFocus, processToFocus); + } + } + + const createConsole = (0, _AtomServiceContainer().getConsoleService)(); + + if (createConsole != null) { + const name = 'Nuclide Debugger'; + const consoleApi = createConsole({ + id: name, + name + }); + removedProcesses.forEach(p => consoleApi.append({ + text: 'Process exited' + (p.configuration.processName == null ? '' : ' (' + p.configuration.processName + ')'), + level: 'log' + })); + } + + if (this._timer != null) { + this._timer.onSuccess(); + + this._timer = null; + } // set breakpoints back to unverified since the session ended. + + + const data = {}; + + this._model.getBreakpoints().forEach(bp => { + data[bp.getId()] = { + line: bp.line, + verified: false, + column: bp.column, + endLine: bp.endLine == null ? undefined : bp.endLine, + endColumn: bp.endColumn == null ? undefined : bp.endColumn + }; + }); + + this._model.updateBreakpoints(data); + }; + + this._disposables = new (_UniversalDisposable().default)(); + this._sessionEndDisposables = new (_UniversalDisposable().default)(); + this._consoleDisposables = new (_UniversalDisposable().default)(); + this._emitter = new _atom.Emitter(); + this._debuggerMode = _constants().DebuggerMode.STOPPED; this._viewModel = new ViewModel(); this._breakpointsToSendOnSave = new Set(); + this._model = new (_DebuggerModel().Model)(this._loadBreakpoints(state), true, this._loadFunctionBreakpoints(state), this._loadExceptionBreakpoints(state), this._loadWatchExpressions(state)); - this._model = new Model( - this._loadBreakpoints(state), - true, - this._loadFunctionBreakpoints(state), - this._loadExceptionBreakpoints(state), - this._loadWatchExpressions(state), - ); this._disposables.add(this._model); + this._registerListeners(); } - get viewModel(): IViewModel { + get viewModel() { return this._viewModel; } - getDebuggerMode(): DebuggerModeType { + getDebuggerMode() { return this._debuggerMode; } - _registerListeners(): void { - this._disposables.add( - atom.workspace.addOpener(uri => { - if (uri.startsWith(DEBUG_SOURCES_URI)) { - if (this._debuggerMode !== DebuggerMode.STOPPED) { - return this._openSourceView(uri); - } + _registerListeners() { + this._disposables.add(atom.workspace.addOpener(uri => { + if (uri.startsWith(_constants().DEBUG_SOURCES_URI)) { + if (this._debuggerMode !== _constants().DebuggerMode.STOPPED) { + return this._openSourceView(uri); } - }), - ); + } + })); } - async _openSourceView(uri: string): Promise { - const query = (url.parse(uri).path || '').split('/'); + async _openSourceView(uri) { + const query = (_url.default.parse(uri).path || '').split('/'); const [, sessionId, sourceReferenceRaw] = query; const sourceReference = parseInt(sourceReferenceRaw, 10); - const process = - this._model.getProcesses().find(p => p.getId() === sessionId) || - this._viewModel.focusedProcess; + const process = this._model.getProcesses().find(p => p.getId() === sessionId) || this._viewModel.focusedProcess; + if (process == null) { throw new Error(`No debug session for source: ${sourceReference}`); } const source = process.getSource({ path: uri, - sourceReference, + sourceReference }); - let content = ''; + try { const response = await process.session.source({ sourceReference, - source: source.raw, + source: source.raw }); content = response.body.content; } catch (error) { this._sourceIsNotAvailable(uri); + throw new Error('Debug source is not available'); } const editor = atom.workspace.buildTextEditor({ buffer: new DebugSourceTextBufffer(content, uri), autoHeight: false, - readOnly: true, - }); + readOnly: true + }); // $FlowFixMe Debugger source views shouldn't persist between reload. - // $FlowFixMe Debugger source views shouldn't persist between reload. editor.serialize = () => null; + editor.setGrammar(atom.grammars.selectGrammar(source.name || '', content)); - const textEditorBanner = new TextEditorBanner(editor); - textEditorBanner.render( - , - ); - - this._sessionEndDisposables.addUntilDestroyed( - editor, - editor, - textEditorBanner, - ); + const textEditorBanner = new (_TextEditorBanner().TextEditorBanner)(editor); + textEditorBanner.render(React.createElement(_ReadOnlyNotice().default, { + detailedMessage: "This is a debug source view that may not exist on the filesystem.", + canEditAnyway: false, + onDismiss: textEditorBanner.dispose.bind(textEditorBanner) + })); + + this._sessionEndDisposables.addUntilDestroyed(editor, editor, textEditorBanner); return editor; } - /** * Stops the process. If the process does not exist then stops all processes. */ - async stopProcess(): Promise { - if ( - this._debuggerMode === DebuggerMode.STOPPING || - this._debuggerMode === DebuggerMode.STOPPED - ) { + + + async stopProcess() { + if (this._debuggerMode === _constants().DebuggerMode.STOPPING || this._debuggerMode === _constants().DebuggerMode.STOPPED) { return; } + this._onSessionEnd(); } - _tryToAutoFocusStackFrame(thread: IThread): void { + _tryToAutoFocusStackFrame(thread) { const callStack = thread.getCallStack(); - if ( - callStack.length === 0 || - (this._viewModel.focusedStackFrame && - this._viewModel.focusedStackFrame.thread.getId() === thread.getId() && - callStack.includes(this._viewModel.focusedStackFrame)) - ) { + + if (callStack.length === 0 || this._viewModel.focusedStackFrame && this._viewModel.focusedStackFrame.thread.getId() === thread.getId() && callStack.includes(this._viewModel.focusedStackFrame)) { return; - } + } // Focus first stack frame from top that has source location if no other stack frame is focused + + + const stackFrameToFocus = callStack.find(sf => sf.source != null && sf.source.available); - // Focus first stack frame from top that has source location if no other stack frame is focused - const stackFrameToFocus = callStack.find( - sf => sf.source != null && sf.source.available, - ); if (stackFrameToFocus == null) { return; } @@ -356,11 +575,11 @@ export default class DebugService implements IDebugService { this.focusStackFrame(stackFrameToFocus, null, null); } - _registerMarkers(process: IProcess): IDisposable { - let selectedFrameMarker: ?atom$Marker = null; - let threadChangeDatatip: ?IDisposable; - let lastFocusedThreadId: ?number; - let lastFocusedProcess: ?IProcess; + _registerMarkers(process) { + let selectedFrameMarker = null; + let threadChangeDatatip; + let lastFocusedThreadId; + let lastFocusedProcess; const cleaupMarkers = () => { if (selectedFrameMarker != null) { @@ -374,611 +593,481 @@ export default class DebugService implements IDebugService { } }; - return new UniversalDisposable( - observableFromSubscribeFunction( - this._viewModel.onDidFocusStackFrame.bind(this._viewModel), - ) - .concatMap(event => { - cleaupMarkers(); - - const {stackFrame, explicit} = event; - - if (stackFrame == null || !stackFrame.source.available) { - if (explicit && this._debuggerMode === DebuggerMode.PAUSED) { - atom.notifications.addWarning( - 'No source available for the selected stack frame', - ); - } - return Observable.empty(); - } - return Observable.fromPromise(stackFrame.openInEditor()).switchMap( - editor => { - if (editor == null) { - atom.notifications.addError( - 'Failed to open source file for stack frame!', - ); - return Observable.empty(); - } - return Observable.of({editor, explicit, stackFrame}); - }, - ); - }) - .subscribe(({editor, explicit, stackFrame}) => { - const line = stackFrame.range.start.row; - selectedFrameMarker = editor.markBufferRange( - [[line, 0], [line, Infinity]], - { - invalidate: 'never', - }, - ); - editor.decorateMarker(selectedFrameMarker, { - type: 'line', - class: 'debugger-current-line-highlight', - }); + return new (_UniversalDisposable().default)((0, _event().observableFromSubscribeFunction)(this._viewModel.onDidFocusStackFrame.bind(this._viewModel)).concatMap(event => { + cleaupMarkers(); + const { + stackFrame, + explicit + } = event; - const datatipService = getDatatipService(); - if (datatipService == null) { - return; - } + if (stackFrame == null || !stackFrame.source.available) { + if (explicit && this._debuggerMode === _constants().DebuggerMode.PAUSED) { + atom.notifications.addWarning('No source available for the selected stack frame'); + } - if ( - lastFocusedThreadId != null && - !explicit && - stackFrame.thread.threadId !== lastFocusedThreadId && - process === lastFocusedProcess - ) { - let message = `Active thread changed from ${lastFocusedThreadId} to ${ - stackFrame.thread.threadId - }`; - const newFocusedProcess = stackFrame.thread.process; - if ( - lastFocusedProcess != null && - !explicit && - newFocusedProcess !== lastFocusedProcess - ) { - if ( - lastFocusedProcess.configuration.processName != null && - newFocusedProcess.configuration.processName != null - ) { - message = - 'Active process changed from ' + - lastFocusedProcess.configuration.processName + - ' to ' + - newFocusedProcess.configuration.processName + - ' AND ' + - message; - } else { - message = 'Active process changed AND ' + message; - } - } - threadChangeDatatip = datatipService.createPinnedDataTip( - { - component: () => ( -
- - {message} -
- ), - range: stackFrame.range, - pinnable: true, - }, - editor, - ); + return _RxMin.Observable.empty(); + } + + return _RxMin.Observable.fromPromise(stackFrame.openInEditor()).switchMap(editor => { + if (editor == null) { + atom.notifications.addError('Failed to open source file for stack frame!'); + return _RxMin.Observable.empty(); + } + + return _RxMin.Observable.of({ + editor, + explicit, + stackFrame + }); + }); + }).subscribe(({ + editor, + explicit, + stackFrame + }) => { + const line = stackFrame.range.start.row; + selectedFrameMarker = editor.markBufferRange([[line, 0], [line, Infinity]], { + invalidate: 'never' + }); + editor.decorateMarker(selectedFrameMarker, { + type: 'line', + class: 'debugger-current-line-highlight' + }); + const datatipService = (0, _AtomServiceContainer().getDatatipService)(); + + if (datatipService == null) { + return; + } + + if (lastFocusedThreadId != null && !explicit && stackFrame.thread.threadId !== lastFocusedThreadId && process === lastFocusedProcess) { + let message = `Active thread changed from ${lastFocusedThreadId} to ${stackFrame.thread.threadId}`; + const newFocusedProcess = stackFrame.thread.process; + + if (lastFocusedProcess != null && !explicit && newFocusedProcess !== lastFocusedProcess) { + if (lastFocusedProcess.configuration.processName != null && newFocusedProcess.configuration.processName != null) { + message = 'Active process changed from ' + lastFocusedProcess.configuration.processName + ' to ' + newFocusedProcess.configuration.processName + ' AND ' + message; + } else { + message = 'Active process changed AND ' + message; } - lastFocusedThreadId = stackFrame.thread.threadId; - lastFocusedProcess = stackFrame.thread.process; - }), + } - cleaupMarkers, - ); + threadChangeDatatip = datatipService.createPinnedDataTip({ + component: () => React.createElement("div", { + className: "debugger-thread-switch-alert" + }, React.createElement(_Icon().Icon, { + icon: "alert" + }), message), + range: stackFrame.range, + pinnable: true + }, editor); + } + + lastFocusedThreadId = stackFrame.thread.threadId; + lastFocusedProcess = stackFrame.thread.process; + }), cleaupMarkers); } - _registerSessionListeners(process: Process, session: VsDebugSession): void { - this._sessionEndDisposables = new UniversalDisposable(session); + _registerSessionListeners(process, session) { + this._sessionEndDisposables = new (_UniversalDisposable().default)(session); + this._sessionEndDisposables.add(this._registerMarkers(process)); const sessionId = session.getId(); - - const threadFetcher = serializeAsyncCall(async () => { + const threadFetcher = (0, _promise().serializeAsyncCall)(async () => { const response = await session.threads(); + if (response && response.body && response.body.threads) { response.body.threads.forEach(thread => { this._model.rawUpdate({ sessionId, - thread, + thread }); }); } }); - - const openFilesSaved = observableFromSubscribeFunction( - atom.workspace.observeTextEditors.bind(atom.workspace), - ).flatMap(editor => { - return observableFromSubscribeFunction(editor.onDidSave.bind(editor)) - .map(() => editor.getPath()) - .takeUntil( - observableFromSubscribeFunction(editor.onDidDestroy.bind(editor)), - ); + const openFilesSaved = (0, _event().observableFromSubscribeFunction)(atom.workspace.observeTextEditors.bind(atom.workspace)).flatMap(editor => { + return (0, _event().observableFromSubscribeFunction)(editor.onDidSave.bind(editor)).map(() => editor.getPath()).takeUntil((0, _event().observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor))); }); - this._sessionEndDisposables.add( - openFilesSaved.subscribe(async filePath => { - if (filePath == null || !this._breakpointsToSendOnSave.has(filePath)) { - return; - } - this._breakpointsToSendOnSave.delete(filePath); - await this._sendBreakpoints(filePath, true); - }), - ); - - this._sessionEndDisposables.add( - session.observeInitializeEvents().subscribe(async event => { - const sendConfigurationDone = async () => { - if ( - session && - session.getCapabilities().supportsConfigurationDoneRequest - ) { - return session - .configurationDone() - .then(_ => { - this._updateModeAndEmit(DebuggerMode.RUNNING); - }) - .catch(e => { - // Disconnect the debug session on configuration done error #10596 - this._onSessionEnd(); - session.disconnect().catch(onUnexpectedError); - atom.notifications.addError( - 'Failed to configure debugger. This is often because either ' + - 'the process you tried to attach to has already terminated, or ' + - 'you do not have permissions (the process is running as root or ' + - 'another user.)', - { - detail: e.message, - }, - ); - }); - } - }; + this._sessionEndDisposables.add(openFilesSaved.subscribe(async filePath => { + if (filePath == null || !this._breakpointsToSendOnSave.has(filePath)) { + return; + } - try { - await this._sendAllBreakpoints().then( - sendConfigurationDone, - sendConfigurationDone, - ); - await threadFetcher(); - } catch (error) { - onUnexpectedError(error); - } - }), - ); - - const toFocusThreads = new Subject(); - - const observeContinuedTo = (threadId: ?number) => { - return session - .observeContinuedEvents() - .filter( - continued => - continued.body.allThreadsContinued || - (threadId != null && threadId === continued.body.threadId), - ) - .take(1); - }; + this._breakpointsToSendOnSave.delete(filePath); - this._sessionEndDisposables.add( - session.observeStopEvents().subscribe(() => { - this._updateModeAndEmit(DebuggerMode.PAUSED); - }), - session - .observeStopEvents() - .flatMap(event => - Observable.fromPromise(threadFetcher()) - .ignoreElements() - .concat(Observable.of(event)) - .catch(error => { - onUnexpectedError(error); - return Observable.empty(); - }) - // Proceeed processing the stopped event only if there wasn't - // a continued event while we're fetching the threads - .takeUntil(observeContinuedTo(event.body.threadId)), - ) - .subscribe((event: DebugProtocol.StoppedEvent) => { - const {threadId} = event.body; - // Updating stopped state needs to happen after fetching the threads - this._model.rawUpdate({ - sessionId, - stoppedDetails: (event.body: any), - threadId, - }); + await this._sendBreakpoints(filePath, true); + })); - if (threadId == null) { - return; - } - const thread = process.getThread(threadId); - if (thread != null) { - toFocusThreads.next(thread); - } - }), - - toFocusThreads - .concatMap(thread => { - const {focusedThread} = this._viewModel; - const preserveFocusHint = - idx(thread, _ => _.stoppedDetails.preserveFocusHint) || false; - - if ( - focusedThread != null && - focusedThread.stopped && - focusedThread.getId() !== thread.getId() && - preserveFocusHint - ) { - // The debugger is already stopped elsewhere. - return Observable.empty(); - } + this._sessionEndDisposables.add(session.observeInitializeEvents().subscribe(async event => { + const sendConfigurationDone = async () => { + if (session && session.getCapabilities().supportsConfigurationDoneRequest) { + return session.configurationDone().then(_ => { + this._updateModeAndEmit(_constants().DebuggerMode.RUNNING); + }).catch(e => { + // Disconnect the debug session on configuration done error #10596 + this._onSessionEnd(); - // UX: That'll fetch the top stack frame first (to allow the UI to focus on it), - // then the rest of the call stack. - return ( - Observable.fromPromise(this._model.fetchCallStack(thread)) - .ignoreElements() - .concat(Observable.of(thread)) - // Avoid focusing a continued thread. - .takeUntil(observeContinuedTo(thread.threadId)) - // Verify the thread is still stopped. - .filter(() => thread.stopped) - .catch(error => { - onUnexpectedError(error); - return Observable.empty(); - }) - ); - }) - .subscribe(thread => { - this._tryToAutoFocusStackFrame(thread); - this._scheduleNativeNotification(); - }), - ); - - this._sessionEndDisposables.add( - session.observeThreadEvents().subscribe(async event => { - if (event.body.reason === 'started') { - await threadFetcher(); - } else if (event.body.reason === 'exited') { - this._model.clearThreads(session.getId(), true, event.body.threadId); - } - }), - ); - - this._sessionEndDisposables.add( - session.observeTerminateDebugeeEvents().subscribe(event => { - if (event.body && event.body.restart) { - this.restartProcess().catch(err => { - atom.notifications.addError('Failed to restart debugger', { - detail: err.stack || String(err), + session.disconnect().catch(_utils().onUnexpectedError); + atom.notifications.addError('Failed to configure debugger. This is often because either ' + 'the process you tried to attach to has already terminated, or ' + 'you do not have permissions (the process is running as root or ' + 'another user.)', { + detail: e.message }); }); - } else { - this._onSessionEnd(session); - session.disconnect().catch(onUnexpectedError); } - }), - ); - - this._sessionEndDisposables.add( - session.observeContinuedEvents().subscribe(event => { - const threadId = - event.body.allThreadsContinued !== false - ? undefined - : event.body.threadId; - this._model.clearThreads(session.getId(), false, threadId); - this.focusStackFrame(null, this._viewModel.focusedThread, null); - this._updateModeAndEmit(this._computeDebugMode()); - }), - ); - - const createConsole = getConsoleService(); + }; + + try { + await this._sendAllBreakpoints().then(sendConfigurationDone, sendConfigurationDone); + await threadFetcher(); + } catch (error) { + (0, _utils().onUnexpectedError)(error); + } + })); + + const toFocusThreads = new _RxMin.Subject(); + + const observeContinuedTo = threadId => { + return session.observeContinuedEvents().filter(continued => continued.body.allThreadsContinued || threadId != null && threadId === continued.body.threadId).take(1); + }; + + this._sessionEndDisposables.add(session.observeStopEvents().subscribe(() => { + this._updateModeAndEmit(_constants().DebuggerMode.PAUSED); + }), session.observeStopEvents().flatMap(event => _RxMin.Observable.fromPromise(threadFetcher()).ignoreElements().concat(_RxMin.Observable.of(event)).catch(error => { + (0, _utils().onUnexpectedError)(error); + return _RxMin.Observable.empty(); + }) // Proceeed processing the stopped event only if there wasn't + // a continued event while we're fetching the threads + .takeUntil(observeContinuedTo(event.body.threadId))).subscribe(event => { + const { + threadId + } = event.body; // Updating stopped state needs to happen after fetching the threads + + this._model.rawUpdate({ + sessionId, + stoppedDetails: event.body, + threadId + }); + + if (threadId == null) { + return; + } + + const thread = process.getThread(threadId); + + if (thread != null) { + toFocusThreads.next(thread); + } + }), toFocusThreads.concatMap(thread => { + var _ref; + + const { + focusedThread + } = this._viewModel; + const preserveFocusHint = ((_ref = thread) != null ? (_ref = _ref.stoppedDetails) != null ? _ref.preserveFocusHint : _ref : _ref) || false; + + if (focusedThread != null && focusedThread.stopped && focusedThread.getId() !== thread.getId() && preserveFocusHint) { + // The debugger is already stopped elsewhere. + return _RxMin.Observable.empty(); + } // UX: That'll fetch the top stack frame first (to allow the UI to focus on it), + // then the rest of the call stack. + + + return _RxMin.Observable.fromPromise(this._model.fetchCallStack(thread)).ignoreElements().concat(_RxMin.Observable.of(thread)) // Avoid focusing a continued thread. + .takeUntil(observeContinuedTo(thread.threadId)) // Verify the thread is still stopped. + .filter(() => thread.stopped).catch(error => { + (0, _utils().onUnexpectedError)(error); + return _RxMin.Observable.empty(); + }); + }).subscribe(thread => { + this._tryToAutoFocusStackFrame(thread); + + this._scheduleNativeNotification(); + })); + + this._sessionEndDisposables.add(session.observeThreadEvents().subscribe(async event => { + if (event.body.reason === 'started') { + await threadFetcher(); + } else if (event.body.reason === 'exited') { + this._model.clearThreads(session.getId(), true, event.body.threadId); + } + })); + + this._sessionEndDisposables.add(session.observeTerminateDebugeeEvents().subscribe(event => { + if (event.body && event.body.restart) { + this.restartProcess().catch(err => { + atom.notifications.addError('Failed to restart debugger', { + detail: err.stack || String(err) + }); + }); + } else { + this._onSessionEnd(session); + + session.disconnect().catch(_utils().onUnexpectedError); + } + })); + + this._sessionEndDisposables.add(session.observeContinuedEvents().subscribe(event => { + const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId; + + this._model.clearThreads(session.getId(), false, threadId); + + this.focusStackFrame(null, this._viewModel.focusedThread, null); + + this._updateModeAndEmit(this._computeDebugMode()); + })); + + const createConsole = (0, _AtomServiceContainer().getConsoleService)(); + if (createConsole != null) { const name = getDebuggerName(process.configuration.adapterType); const consoleApi = createConsole({ id: name, - name, + name }); + this._sessionEndDisposables.add(consoleApi); - const outputEvents = session - .observeOutputEvents() - .filter( - event => event.body != null && typeof event.body.output === 'string', - ) - .share(); - const KNOWN_CATEGORIES = new Set([ - 'stderr', - 'console', - 'telemetry', - 'success', - ]); - const logStream = splitStream( - outputEvents - .filter(e => !KNOWN_CATEGORIES.has(e.body.category)) - .map(e => stripAnsi(e.body.output)), - ); - const [errorStream, warningsStream, successStream] = [ - 'stderr', - 'console', - 'success', - ].map(category => - splitStream( - outputEvents - .filter(e => category === e.body.category) - .map(e => stripAnsi(e.body.output)), - ), - ); - const notificationStream = outputEvents - .filter(e => e.body.category === 'nuclide_notification') - .map(e => ({ - type: nullthrows(e.body.data).type, - message: e.body.output, - })); - this._sessionEndDisposables.add( - errorStream.subscribe(line => { - consoleApi.append({text: line, level: 'error'}); - }), - warningsStream.subscribe(line => { - consoleApi.append({text: line, level: 'warning'}); - }), - successStream.subscribe(line => { - consoleApi.append({text: line, level: 'success'}); - }), - logStream.subscribe(line => { - consoleApi.append({text: line, level: 'log'}); - }), - notificationStream.subscribe(({type, message}) => { - atom.notifications.add(type, message); - }), - // TODO handle non string output (e.g. files & objects) + + const outputEvents = session.observeOutputEvents().filter(event => event.body != null && typeof event.body.output === 'string').share(); + const KNOWN_CATEGORIES = new Set(['stderr', 'console', 'telemetry', 'success']); + const logStream = (0, _observable().splitStream)(outputEvents.filter(e => !KNOWN_CATEGORIES.has(e.body.category)).map(e => (0, _stripAnsi().default)(e.body.output))); + const [errorStream, warningsStream, successStream] = ['stderr', 'console', 'success'].map(category => (0, _observable().splitStream)(outputEvents.filter(e => category === e.body.category).map(e => (0, _stripAnsi().default)(e.body.output)))); + const notificationStream = outputEvents.filter(e => e.body.category === 'nuclide_notification').map(e => ({ + type: (0, _nullthrows().default)(e.body.data).type, + message: e.body.output + })); + + this._sessionEndDisposables.add(errorStream.subscribe(line => { + consoleApi.append({ + text: line, + level: 'error' + }); + }), warningsStream.subscribe(line => { + consoleApi.append({ + text: line, + level: 'warning' + }); + }), successStream.subscribe(line => { + consoleApi.append({ + text: line, + level: 'success' + }); + }), logStream.subscribe(line => { + consoleApi.append({ + text: line, + level: 'log' + }); + }), notificationStream.subscribe(({ + type, + message + }) => { + atom.notifications.add(type, message); + }) // TODO handle non string output (e.g. files & objects) ); } - this._sessionEndDisposables.add( - session - .observeBreakpointEvents() - .flatMap(event => { - const {breakpoint, reason} = event.body; - if ( - reason !== BreakpointEventReasons.CHANGED && - reason !== BreakpointEventReasons.REMOVED - ) { - return Observable.of({ - reason, - breakpoint, - sourceBreakpoint: null, - functionBreakpoint: null, - }); + this._sessionEndDisposables.add(session.observeBreakpointEvents().flatMap(event => { + const { + breakpoint, + reason + } = event.body; + + if (reason !== _constants().BreakpointEventReasons.CHANGED && reason !== _constants().BreakpointEventReasons.REMOVED) { + return _RxMin.Observable.of({ + reason, + breakpoint, + sourceBreakpoint: null, + functionBreakpoint: null + }); + } // Breakpoint events may arrive sooner than their responses. + // Hence, we'll keep them cached and try re-processing on every change to the model's breakpoints + // for a set maximum time, then discard. + + + return (0, _event().observableFromSubscribeFunction)(this._model.onDidChangeBreakpoints.bind(this._model)).startWith(null).switchMap(() => { + const sourceBreakpoint = this._model.getBreakpoints().filter(b => b.idFromAdapter === breakpoint.id).pop(); + + const functionBreakpoint = this._model.getFunctionBreakpoints().filter(b => b.idFromAdapter === breakpoint.id).pop(); + + if (sourceBreakpoint == null && functionBreakpoint == null) { + return _RxMin.Observable.empty(); + } else { + return _RxMin.Observable.of({ + reason, + breakpoint, + sourceBreakpoint, + functionBreakpoint + }); + } + }).take(1).timeout(MAX_BREAKPOINT_EVENT_DELAY_MS).catch(error => { + if (error instanceof _RxMin.TimeoutError) { + _logger().default.error('Timed out breakpoint event handler', process.configuration.adapterType, reason, breakpoint); + } + + return _RxMin.Observable.empty(); + }); + }).subscribe(({ + reason, + breakpoint, + sourceBreakpoint, + functionBreakpoint + }) => { + if (reason === _constants().BreakpointEventReasons.NEW && breakpoint.source) { + const source = process.getSource(breakpoint.source); + + const bps = this._model.addBreakpoints(source.uri, [{ + column: breakpoint.column || 0, + enabled: true, + line: breakpoint.line == null ? -1 : breakpoint.line + }], false); + + if (bps.length === 1) { + this._model.updateBreakpoints({ + [bps[0].getId()]: breakpoint + }); + } + } else if (reason === _constants().BreakpointEventReasons.REMOVED) { + if (sourceBreakpoint != null) { + this._model.removeBreakpoints([sourceBreakpoint]); + } + + if (functionBreakpoint != null) { + this._model.removeFunctionBreakpoints(functionBreakpoint.getId()); + } + } else if (reason === _constants().BreakpointEventReasons.CHANGED) { + if (sourceBreakpoint != null) { + if (!sourceBreakpoint.column) { + breakpoint.column = undefined; } - // Breakpoint events may arrive sooner than their responses. - // Hence, we'll keep them cached and try re-processing on every change to the model's breakpoints - // for a set maximum time, then discard. - return observableFromSubscribeFunction( - this._model.onDidChangeBreakpoints.bind(this._model), - ) - .startWith(null) - .switchMap(() => { - const sourceBreakpoint = this._model - .getBreakpoints() - .filter(b => b.idFromAdapter === breakpoint.id) - .pop(); - const functionBreakpoint = this._model - .getFunctionBreakpoints() - .filter(b => b.idFromAdapter === breakpoint.id) - .pop(); - if (sourceBreakpoint == null && functionBreakpoint == null) { - return Observable.empty(); - } else { - return Observable.of({ - reason, - breakpoint, - sourceBreakpoint, - functionBreakpoint, - }); - } - }) - .take(1) - .timeout(MAX_BREAKPOINT_EVENT_DELAY_MS) - .catch(error => { - if (error instanceof TimeoutError) { - logger.error( - 'Timed out breakpoint event handler', - process.configuration.adapterType, - reason, - breakpoint, - ); - } - return Observable.empty(); - }); - }) - .subscribe( - ({reason, breakpoint, sourceBreakpoint, functionBreakpoint}) => { - if (reason === BreakpointEventReasons.NEW && breakpoint.source) { - const source = process.getSource(breakpoint.source); - const bps = this._model.addBreakpoints( - source.uri, - [ - { - column: breakpoint.column || 0, - enabled: true, - line: breakpoint.line == null ? -1 : breakpoint.line, - }, - ], - false, - ); - if (bps.length === 1) { - this._model.updateBreakpoints({ - [bps[0].getId()]: breakpoint, - }); - } - } else if (reason === BreakpointEventReasons.REMOVED) { - if (sourceBreakpoint != null) { - this._model.removeBreakpoints([sourceBreakpoint]); - } - if (functionBreakpoint != null) { - this._model.removeFunctionBreakpoints( - functionBreakpoint.getId(), - ); - } - } else if (reason === BreakpointEventReasons.CHANGED) { - if (sourceBreakpoint != null) { - if (!sourceBreakpoint.column) { - breakpoint.column = undefined; - } - this._model.updateBreakpoints({ - [sourceBreakpoint.getId()]: breakpoint, - }); - } - if (functionBreakpoint != null) { - this._model.updateFunctionBreakpoints({ - [functionBreakpoint.getId()]: breakpoint, - }); - } - } else { - logger.warn('Unknown breakpoint event', reason, breakpoint); - } - }, - ), - ); - - this._sessionEndDisposables.add( - session.observeAdapterExitedEvents().subscribe(event => { - // 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905 - this._onSessionEnd(session); - }), - ); + this._model.updateBreakpoints({ + [sourceBreakpoint.getId()]: breakpoint + }); + } + + if (functionBreakpoint != null) { + this._model.updateFunctionBreakpoints({ + [functionBreakpoint.getId()]: breakpoint + }); + } + } else { + _logger().default.warn('Unknown breakpoint event', reason, breakpoint); + } + })); + + this._sessionEndDisposables.add(session.observeAdapterExitedEvents().subscribe(event => { + // 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905 + this._onSessionEnd(session); + })); + + this._sessionEndDisposables.add(session.observeCustomEvents().subscribe(event => { + this._emitter.emit(CUSTOM_DEBUG_EVENT, event); + })); // Clear in memory breakpoints. - this._sessionEndDisposables.add( - session.observeCustomEvents().subscribe(event => { - this._emitter.emit(CUSTOM_DEBUG_EVENT, event); - }), - ); - // Clear in memory breakpoints. this._sessionEndDisposables.add(() => { - const sourceRefBreakpoints = this._model - .getBreakpoints() - .filter(bp => bp.uri.startsWith(DEBUG_SOURCES_URI)); + const sourceRefBreakpoints = this._model.getBreakpoints().filter(bp => bp.uri.startsWith(_constants().DEBUG_SOURCES_URI)); + if (sourceRefBreakpoints.length > 0) { this._model.removeBreakpoints(sourceRefBreakpoints); } }); } - _scheduleNativeNotification(): void { - const raiseNativeNotification = getNotificationService(); + _scheduleNativeNotification() { + const raiseNativeNotification = (0, _AtomServiceContainer().getNotificationService)(); + if (raiseNativeNotification != null) { - const pendingNotification = raiseNativeNotification( - 'Debugger', - 'Paused at a breakpoint', - 3000, - false, - ); + const pendingNotification = raiseNativeNotification('Debugger', 'Paused at a breakpoint', 3000, false); + if (pendingNotification != null) { this._sessionEndDisposables.add(pendingNotification); } } } - onDidStartDebugSession( - callback: (config: IProcessConfig) => mixed, - ): IDisposable { + onDidStartDebugSession(callback) { return this._emitter.on(START_DEBUG_SESSION, callback); } - onDidCustomEvent( - callback: (event: DebugProtocol.DebugEvent) => mixed, - ): IDisposable { + onDidCustomEvent(callback) { return this._emitter.on(CUSTOM_DEBUG_EVENT, callback); } - onDidChangeMode(callback: (mode: DebuggerModeType) => mixed): IDisposable { + onDidChangeMode(callback) { return this._emitter.on(CHANGE_DEBUG_MODE, callback); } - _loadBreakpoints(state: ?SerializedState): Breakpoint[] { - let result: Breakpoint[] = []; + _loadBreakpoints(state) { + let result = []; + if (state == null || state.sourceBreakpoints == null) { return result; } + try { result = state.sourceBreakpoints.map(breakpoint => { - return new Breakpoint( - breakpoint.uri, - breakpoint.line, - breakpoint.column, - breakpoint.enabled, - breakpoint.condition, - breakpoint.hitCondition, - breakpoint.adapterData, - ); + return new (_DebuggerModel().Breakpoint)(breakpoint.uri, breakpoint.line, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.adapterData); }); } catch (e) {} return result; } - _loadFunctionBreakpoints(state: ?SerializedState): FunctionBreakpoint[] { - let result: FunctionBreakpoint[] = []; + _loadFunctionBreakpoints(state) { + let result = []; + if (state == null || state.functionBreakpoints == null) { return result; } + try { result = state.functionBreakpoints.map(fb => { - return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition); + return new (_DebuggerModel().FunctionBreakpoint)(fb.name, fb.enabled, fb.hitCondition); }); } catch (e) {} return result; } - _loadExceptionBreakpoints(state: ?SerializedState): ExceptionBreakpoint[] { - let result: ExceptionBreakpoint[] = []; + _loadExceptionBreakpoints(state) { + let result = []; + if (state == null || state.exceptionBreakpoints == null) { return result; } + try { result = state.exceptionBreakpoints.map(exBreakpoint => { - return new ExceptionBreakpoint( - exBreakpoint.filter, - exBreakpoint.label, - exBreakpoint.enabled, - ); + return new (_DebuggerModel().ExceptionBreakpoint)(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled); }); } catch (e) {} return result; } - _loadWatchExpressions(state: ?SerializedState): Expression[] { - let result: Expression[] = []; + _loadWatchExpressions(state) { + let result = []; + if (state == null || state.watchExpressions == null) { return result; } + try { - result = state.watchExpressions.map(name => new Expression(name)); + result = state.watchExpressions.map(name => new (_DebuggerModel().Expression)(name)); } catch (e) {} return result; } - _updateModeAndEmit(debugMode: DebuggerModeType): void { + _updateModeAndEmit(debugMode) { this._debuggerMode = debugMode; + this._emitter.emit(CHANGE_DEBUG_MODE, debugMode); } - focusStackFrame( - stackFrame: ?IStackFrame, - thread: ?IThread, - process: ?IProcess, - explicit?: boolean = false, - ): void { + focusStackFrame(stackFrame, thread, process, explicit = false) { let focusProcess = process; + if (focusProcess == null) { if (stackFrame != null) { focusProcess = stackFrame.thread.process; @@ -988,7 +1077,8 @@ export default class DebugService implements IDebugService { focusProcess = this._model.getProcesses()[0]; } } - let focusThread: ?IThread = thread; + + let focusThread = thread; let focusStackFrame = stackFrame; if (focusThread == null && stackFrame != null) { @@ -1001,279 +1091,285 @@ export default class DebugService implements IDebugService { focusStackFrame = thread.getCallStack()[0]; } - this._viewModel.setFocus( - focusStackFrame, - focusThread, - focusProcess, - explicit, - ); + this._viewModel.setFocus(focusStackFrame, focusThread, focusProcess, explicit); + this._updateModeAndEmit(this._computeDebugMode()); } - _computeDebugMode(): DebuggerModeType { - const {focusedThread, focusedStackFrame} = this._viewModel; - if ( - focusedStackFrame != null || - (focusedThread != null && focusedThread.stopped) - ) { - return DebuggerMode.PAUSED; + _computeDebugMode() { + const { + focusedThread, + focusedStackFrame + } = this._viewModel; + + if (focusedStackFrame != null || focusedThread != null && focusedThread.stopped) { + return _constants().DebuggerMode.PAUSED; } else if (this._getCurrentProcess() == null) { - return DebuggerMode.STOPPED; - } else if (this._debuggerMode === DebuggerMode.STARTING) { - return DebuggerMode.STARTING; + return _constants().DebuggerMode.STOPPED; + } else if (this._debuggerMode === _constants().DebuggerMode.STARTING) { + return _constants().DebuggerMode.STARTING; } else { - return DebuggerMode.RUNNING; + return _constants().DebuggerMode.RUNNING; } } - enableOrDisableBreakpoints( - enable: boolean, - breakpoint?: IEnableable, - ): Promise { + enableOrDisableBreakpoints(enable, breakpoint) { if (breakpoint != null) { this._model.setEnablement(breakpoint, enable); - if (breakpoint instanceof Breakpoint) { + + if (breakpoint instanceof _DebuggerModel().Breakpoint) { return this._sendBreakpoints(breakpoint.uri); - } else if (breakpoint instanceof FunctionBreakpoint) { + } else if (breakpoint instanceof _DebuggerModel().FunctionBreakpoint) { return this._sendFunctionBreakpoints(); } else { - track(AnalyticsEvents.DEBUGGER_TOGGLE_EXCEPTION_BREAKPOINT); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_TOGGLE_EXCEPTION_BREAKPOINT); return this._sendExceptionBreakpoints(); } } this._model.enableOrDisableAllBreakpoints(enable); + return this._sendAllBreakpoints(); } - addBreakpoints(uri: string, rawBreakpoints: IRawBreakpoint[]): Promise { - track(AnalyticsEvents.DEBUGGER_BREAKPOINT_ADD); + addBreakpoints(uri, rawBreakpoints) { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_BREAKPOINT_ADD); + this._model.addBreakpoints(uri, rawBreakpoints); + return this._sendBreakpoints(uri); } - addSourceBreakpoint(uri: string, line: number): Promise { - track(AnalyticsEvents.DEBUGGER_BREAKPOINT_SINGLE_ADD); + addSourceBreakpoint(uri, line) { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_BREAKPOINT_SINGLE_ADD); + const existing = this._model.getBreakpointAtLine(uri, line); + if (existing == null) { - return this.addBreakpoints(uri, [{line}]); + return this.addBreakpoints(uri, [{ + line + }]); } + return Promise.resolve(undefined); } - toggleSourceBreakpoint(uri: string, line: number): Promise { - track(AnalyticsEvents.DEBUGGER_BREAKPOINT_TOGGLE); + toggleSourceBreakpoint(uri, line) { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_BREAKPOINT_TOGGLE); + const existing = this._model.getBreakpointAtLine(uri, line); + if (existing == null) { - return this.addBreakpoints(uri, [{line}]); + return this.addBreakpoints(uri, [{ + line + }]); } else { return this.removeBreakpoints(existing.getId(), true); } } - updateBreakpoints( - uri: string, - data: {[id: string]: DebugProtocol.Breakpoint}, - ) { + updateBreakpoints(uri, data) { this._model.updateBreakpoints(data); + this._breakpointsToSendOnSave.add(uri); } - async removeBreakpoints( - id?: string, - skipAnalytics?: boolean = false, - ): Promise { - const toRemove = this._model - .getBreakpoints() - .filter(bp => id == null || bp.getId() === id); - const urisToClear = distinct(toRemove, bp => bp.uri).map(bp => bp.uri); + async removeBreakpoints(id, skipAnalytics = false) { + const toRemove = this._model.getBreakpoints().filter(bp => id == null || bp.getId() === id); + + const urisToClear = (0, _collection().distinct)(toRemove, bp => bp.uri).map(bp => bp.uri); this._model.removeBreakpoints(toRemove); if (id == null) { - track(AnalyticsEvents.DEBUGGER_BREAKPOINT_DELETE_ALL); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_BREAKPOINT_DELETE_ALL); } else if (!skipAnalytics) { - track(AnalyticsEvents.DEBUGGER_BREAKPOINT_DELETE); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_BREAKPOINT_DELETE); } await Promise.all(urisToClear.map(uri => this._sendBreakpoints(uri))); } - setBreakpointsActivated(activated: boolean): Promise { + setBreakpointsActivated(activated) { this._model.setBreakpointsActivated(activated); + return this._sendAllBreakpoints(); } - addFunctionBreakpoint(): void { + addFunctionBreakpoint() { this._model.addFunctionBreakpoint(''); } - renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { - this._model.updateFunctionBreakpoints({[id]: {name: newFunctionName}}); + renameFunctionBreakpoint(id, newFunctionName) { + this._model.updateFunctionBreakpoints({ + [id]: { + name: newFunctionName + } + }); + return this._sendFunctionBreakpoints(); } - removeFunctionBreakpoints(id?: string): Promise { + removeFunctionBreakpoints(id) { this._model.removeFunctionBreakpoints(id); + return this._sendFunctionBreakpoints(); } - async terminateThreads(threadIds: Array): Promise { - const {focusedProcess} = this.viewModel; + async terminateThreads(threadIds) { + const { + focusedProcess + } = this.viewModel; + if (focusedProcess == null) { return; } const session = focusedProcess.session; - track(AnalyticsEvents.DEBUGGER_TERMINATE_THREAD); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_TERMINATE_THREAD); + if (Boolean(session.capabilities.supportsTerminateThreadsRequest)) { await session.custom('terminateThreads', { - threadIds, + threadIds }); } } - async runToLocation(uri: string, line: number): Promise { - const {focusedThread, focusedProcess} = this.viewModel; + async runToLocation(uri, line) { + const { + focusedThread, + focusedProcess + } = this.viewModel; + if (focusedThread == null || focusedProcess == null) { return; } const session = focusedProcess.session; + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_RUN_TO_LOCATION); - track(AnalyticsEvents.DEBUGGER_STEP_RUN_TO_LOCATION); if (Boolean(session.capabilities.supportsContinueToLocation)) { await session.custom('continueToLocation', { - source: focusedProcess.getSource({path: uri}).raw, + source: focusedProcess.getSource({ + path: uri + }).raw, line, - threadId: focusedThread.threadId, + threadId: focusedThread.threadId }); return; } + const existing = this._model.getBreakpointAtLine(uri, line); + if (existing == null) { - await this.addBreakpoints(uri, [{line}]); - const runToLocationBreakpoint = this._model.getBreakpointAtLine( - uri, - line, - ); - invariant(runToLocationBreakpoint != null); + await this.addBreakpoints(uri, [{ + line + }]); + + const runToLocationBreakpoint = this._model.getBreakpointAtLine(uri, line); + + if (!(runToLocationBreakpoint != null)) { + throw new Error("Invariant violation: \"runToLocationBreakpoint != null\""); + } const removeBreakpoint = () => { - this.removeBreakpoints( - runToLocationBreakpoint.getId(), - true /* skip analytics */, - ).catch(error => - onUnexpectedError( - `Failed to clear run-to-location breakpoint! - ${String(error)}`, - ), - ); + this.removeBreakpoints(runToLocationBreakpoint.getId(), true + /* skip analytics */ + ).catch(error => (0, _utils().onUnexpectedError)(`Failed to clear run-to-location breakpoint! - ${String(error)}`)); removeBreakpointDisposable.dispose(); + this._sessionEndDisposables.remove(removeBreakpointDisposable); + this._sessionEndDisposables.remove(removeBreakpoint); - }; + }; // Remove if the debugger stopped at any location. - // Remove if the debugger stopped at any location. - const removeBreakpointDisposable = new UniversalDisposable( - session - .observeStopEvents() - .take(1) - .subscribe(removeBreakpoint), - ); - // Remove if the session has ended without hitting it. - this._sessionEndDisposables.add( - removeBreakpointDisposable, - removeBreakpoint, - ); + + const removeBreakpointDisposable = new (_UniversalDisposable().default)(session.observeStopEvents().take(1).subscribe(removeBreakpoint)); // Remove if the session has ended without hitting it. + + this._sessionEndDisposables.add(removeBreakpointDisposable, removeBreakpoint); } + await focusedThread.continue(); } - addWatchExpression(name: string): void { - track(AnalyticsEvents.DEBUGGER_WATCH_ADD_EXPRESSION); + addWatchExpression(name) { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_WATCH_ADD_EXPRESSION); return this._model.addWatchExpression(name); } - renameWatchExpression(id: string, newName: string): void { - track(AnalyticsEvents.DEBUGGER_WATCH_UPDATE_EXPRESSION); + renameWatchExpression(id, newName) { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_WATCH_UPDATE_EXPRESSION); return this._model.renameWatchExpression(id, newName); } - removeWatchExpressions(id?: string): void { - track(AnalyticsEvents.DEBUGGER_WATCH_REMOVE_EXPRESSION); + removeWatchExpressions(id) { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_WATCH_REMOVE_EXPRESSION); + this._model.removeWatchExpressions(id); } - createExpression(rawExpression: string): IEvaluatableExpression { - return new Expression(rawExpression); + createExpression(rawExpression) { + return new (_DebuggerModel().Expression)(rawExpression); } - async _doCreateProcess( - rawConfiguration: IProcessConfig, - sessionId: string, - ): Promise { - let process: ?IProcess; - let session: ?VsDebugSession; - const errorHandler = (error: Error) => { + async _doCreateProcess(rawConfiguration, sessionId) { + let process; + let session; + + const errorHandler = error => { if (this._timer != null) { this._timer.onError(error); + this._timer = null; } - track(AnalyticsEvents.DEBUGGER_START_FAIL, {}); + + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_START_FAIL, {}); const errorMessage = error instanceof Error ? error.message : error; - atom.notifications.addError( - `Failed to start debugger process: ${errorMessage}`, - ); - if ( - this._model.getProcesses() == null || - this._model.getProcesses().length === 0 - ) { + atom.notifications.addError(`Failed to start debugger process: ${errorMessage}`); + + if (this._model.getProcesses() == null || this._model.getProcesses().length === 0) { this._consoleDisposables.dispose(); - this._updateModeAndEmit(DebuggerMode.STOPPED); + + this._updateModeAndEmit(_constants().DebuggerMode.STOPPED); } + if (session != null && !session.isDisconnected()) { this._onSessionEnd(); - session.disconnect().catch(onUnexpectedError); + + session.disconnect().catch(_utils().onUnexpectedError); } + if (process != null) { this._model.removeProcess(process.getId()); } }; try { - const adapterExecutable = await this._resolveAdapterExecutable( - rawConfiguration, - ); - const configuration = await resolveDebugConfiguration({ - ...rawConfiguration, - adapterExecutable, - }); + const adapterExecutable = await this._resolveAdapterExecutable(rawConfiguration); + const configuration = await (0, _AtomServiceContainer().resolveDebugConfiguration)(Object.assign({}, rawConfiguration, { + adapterExecutable + })); const { adapterType, onInitializeCallback, - customDisposable, + customDisposable } = configuration; - - track(AnalyticsEvents.DEBUGGER_START, { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_START, { serviceName: configuration.adapterType, - clientType: 'VSP', + clientType: 'VSP' }); - const createInitializeSession = async (config: IProcessConfig) => { - const newSession = await this._createVsDebugSession( - config, - config.adapterExecutable || adapterExecutable, - sessionId, - ); - + const createInitializeSession = async config => { + const newSession = await this._createVsDebugSession(config, config.adapterExecutable || adapterExecutable, sessionId); process = this._model.addProcess(config, newSession); + this._emitter.emit(START_DEBUG_SESSION, config); + this.focusStackFrame(null, null, process); + this._registerSessionListeners(process, newSession); - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'debugger:show', - ); + + atom.commands.dispatch(atom.views.getView(atom.workspace), 'debugger:show'); await newSession.initialize({ clientID: 'atom', adapterID: adapterType, @@ -1282,69 +1378,46 @@ export default class DebugService implements IDebugService { columnsStartAt1: true, supportsVariableType: true, supportsVariablePaging: false, - supportsRunInTerminalRequest: getTerminalService() != null, - locale: 'en-us', + supportsRunInTerminalRequest: (0, _AtomServiceContainer().getTerminalService)() != null, + locale: 'en-us' }); if (onInitializeCallback != null) { await onInitializeCallback(newSession); } - this._model.setExceptionBreakpoints( - newSession.getCapabilities().exceptionBreakpointFilters || [], - ); + this._model.setExceptionBreakpoints(newSession.getCapabilities().exceptionBreakpointFilters || []); + return newSession; }; - session = await createInitializeSession(configuration); - - // We're not awaiting launch/attach to finish because some debug adapters + session = await createInitializeSession(configuration); // We're not awaiting launch/attach to finish because some debug adapters // need to do custom work for launch/attach to work (e.g. mobilejs) + this._launchOrAttachTarget(session, configuration).catch(async error => { - if ( - configuration.debugMode === 'attach' && - configuration.adapterExecutable != null && - configuration.adapterExecutable.command !== 'sudo' && - // sudo is not supported on Windows, and currently remote projects - // are not supported on Windows, so a remote URI must be *nix. - (os.platform() !== 'win32' || - nuclideUri.isRemote(configuration.targetUri)) - ) { - configuration.adapterExecutable.args = [ - configuration.adapterExecutable.command, - ...configuration.adapterExecutable.args, - ]; + if (configuration.debugMode === 'attach' && configuration.adapterExecutable != null && configuration.adapterExecutable.command !== 'sudo' && ( // sudo is not supported on Windows, and currently remote projects + // are not supported on Windows, so a remote URI must be *nix. + _os.default.platform() !== 'win32' || _nuclideUri().default.isRemote(configuration.targetUri))) { + configuration.adapterExecutable.args = [configuration.adapterExecutable.command, ...configuration.adapterExecutable.args]; configuration.adapterExecutable.command = 'sudo'; - const errorMessage = error instanceof Error ? error.message : error; - atom.notifications.addWarning( - `The debugger was unable to attach to the target process: ${errorMessage}. ` + - 'Attempting to re-launch the debugger as root...', - ); - + atom.notifications.addWarning(`The debugger was unable to attach to the target process: ${errorMessage}. ` + 'Attempting to re-launch the debugger as root...'); session = await createInitializeSession(configuration); - this._launchOrAttachTarget(session, configuration).catch( - errorHandler, - ); + + this._launchOrAttachTarget(session, configuration).catch(errorHandler); } else { errorHandler(error); } - }); - - // make sure to add the configuration.customDisposable to dispose on + }); // make sure to add the configuration.customDisposable to dispose on // session end + + if (customDisposable != null) { - customDisposable.add( - this.viewModel.onDidFocusProcess(() => { - if ( - !this.getModel() - .getProcesses() - .includes(process) - ) { - customDisposable.dispose(); - } - }), - ); + customDisposable.add(this.viewModel.onDidFocusProcess(() => { + if (!this.getModel().getProcesses().includes(process)) { + customDisposable.dispose(); + } + })); } return process; @@ -1354,55 +1427,46 @@ export default class DebugService implements IDebugService { } } - async _resolveAdapterExecutable( - configuration: IProcessConfig, - ): Promise { + async _resolveAdapterExecutable(configuration) { if (configuration.adapterExecutable != null) { return configuration.adapterExecutable; } - return getVSCodeDebuggerAdapterServiceByNuclideUri( - configuration.targetUri, - ).getAdapterExecutableInfo(configuration.adapterType); - } - - async _createVsDebugSession( - configuration: IProcessConfig, - adapterExecutable: VSAdapterExecutableInfo, - sessionId: string, - ): Promise { - const {targetUri} = configuration; - const service = getVSCodeDebuggerAdapterServiceByNuclideUri(targetUri); + + return (0, _nuclideDebuggerCommon().getVSCodeDebuggerAdapterServiceByNuclideUri)(configuration.targetUri).getAdapterExecutableInfo(configuration.adapterType); + } + + async _createVsDebugSession(configuration, adapterExecutable, sessionId) { + const { + targetUri + } = configuration; + const service = (0, _nuclideDebuggerCommon().getVSCodeDebuggerAdapterServiceByNuclideUri)(targetUri); const spawner = await service.createVsRawAdapterSpawnerService(); + const clientPreprocessors = []; + const adapterPreprocessors = []; - const clientPreprocessors: Array = []; - const adapterPreprocessors: Array = []; if (configuration.clientPreprocessor != null) { clientPreprocessors.push(configuration.clientPreprocessor); } + if (configuration.adapterPreprocessor != null) { adapterPreprocessors.push(configuration.adapterPreprocessor); } - const isRemote = nuclideUri.isRemote(targetUri); + + const isRemote = _nuclideUri().default.isRemote(targetUri); + if (isRemote) { - clientPreprocessors.push(remoteToLocalProcessor()); - adapterPreprocessors.push(localToRemoteProcessor(targetUri)); + clientPreprocessors.push((0, _nuclideDebuggerCommon().remoteToLocalProcessor)()); + adapterPreprocessors.push((0, _nuclideDebuggerCommon().localToRemoteProcessor)(targetUri)); } - return new VsDebugSession( - sessionId, - logger, - adapterExecutable, - {adapter: configuration.adapterType, host: 'debugService', isRemote}, - spawner, - clientPreprocessors, - adapterPreprocessors, - this._runInTerminal, - ); - } - - async _launchOrAttachTarget( - session: VsDebugSession, - configuration: IProcessConfig, - ): Promise { + + return new (_nuclideDebuggerCommon().VsDebugSession)(sessionId, _logger().default, adapterExecutable, { + adapter: configuration.adapterType, + host: 'debugService', + isRemote + }, spawner, clientPreprocessors, adapterPreprocessors, this._runInTerminal); + } + + async _launchOrAttachTarget(session, configuration) { if (configuration.debugMode === 'attach') { await session.attach(configuration.config); } else { @@ -1411,286 +1475,137 @@ export default class DebugService implements IDebugService { } } - _sourceIsNotAvailable(uri: string): void { + _sourceIsNotAvailable(uri) { this._model.sourceIsNotAvailable(uri); } - _runInTerminal = async ( - args: DebugProtocol.RunInTerminalRequestArguments, - ): Promise => { - const terminalService = getTerminalService(); - if (terminalService == null) { - throw new Error( - 'Unable to launch in terminal since the service is not available', - ); - } + async restartProcess() { const process = this._getCurrentProcess(); - if (process == null) { - throw new Error("There's no debug process to create a terminal for!"); - } - const {adapterType, targetUri} = process.configuration; - const key = `targetUri=${targetUri}&command=${args.args[0]}`; - - // Ensure any previous instances of this same target are closed before - // opening a new terminal tab. We don't want them to pile up if the - // user keeps running the same app over and over. - terminalService.close(key); - - const title = - args.title != null ? args.title : getDebuggerName(adapterType); - const hostname = nuclideUri.getHostnameOpt(targetUri); - const cwd = - hostname == null - ? args.cwd - : nuclideUri.createRemoteUri(hostname, args.cwd); - - const info: TerminalInfo = { - key, - title, - cwd, - command: { - file: args.args[0], - args: args.args.slice(1), - }, - environmentVariables: - args.env != null ? mapFromObject(args.env) : undefined, - preservedCommands: [ - 'debugger:continue-debugging', - 'debugger:stop-debugging', - 'debugger:restart-debugging', - 'debugger:step-over', - 'debugger:step-into', - 'debugger:step-out', - ], - remainOnCleanExit: true, - icon: 'nuclicon-debugger', - defaultLocation: 'bottom', - }; - const terminal = await terminalService.open(info); - terminal.setProcessExitCallback(() => { - // This callback is invoked if the target process dies first, ensuring - // we tear down the debugger. - this.stopProcess(); - }); - this._sessionEndDisposables.add(() => { - // This termination path is invoked if the debugger dies first, ensuring - // we terminate the target process. This can happen if the user hits stop, - // or if the debugger crashes. - terminal.setProcessExitCallback(() => {}); - terminal.terminateProcess(); - }); - }; - - async restartProcess(): Promise { - const process = this._getCurrentProcess(); if (process == null) { return; } + if (process.session.capabilities.supportsRestartRequest) { await process.session.custom('restart', null); } + await process.session.disconnect(true); - await sleep(300); + await (0, _promise().sleep)(300); await this.startDebugging(process.configuration); } - /** * Starts debugging. If the configOrName is not passed uses the selected configuration in the debug dropdown. * Also saves all files, manages if compounds are present in the configuration * and resolveds configurations via DebugConfigurationProviders. */ - async startDebugging(config: IProcessConfig): Promise { - this._timer = startTracking('debugger-atom:startDebugging'); + + + async startDebugging(config) { + this._timer = (0, _analytics().startTracking)('debugger-atom:startDebugging'); if (this._viewModel.focusedProcess != null) { // We currently support only running only one debug session at a time, // so stop the current debug session. - if (_gkService != null) { - const passesMultiGK = await _gkService.passesGK( - 'nuclide_multitarget_debugging', - ); + const passesMultiGK = await _gkService.passesGK('nuclide_multitarget_debugging'); + if (!passesMultiGK) { this.stopProcess(); } - _gkService - .passesGK('nuclide_processtree_debugging') - .then(passesProcessTree => { - if (passesProcessTree) { - track(AnalyticsEvents.DEBUGGER_TREE_OPENED); - } - }); + + _gkService.passesGK('nuclide_processtree_debugging').then(passesProcessTree => { + if (passesProcessTree) { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_TREE_OPENED); + } + }); } else { this.stopProcess(); } } - this._updateModeAndEmit(DebuggerMode.STARTING); - // Open the console window if it's not already opened. + this._updateModeAndEmit(_constants().DebuggerMode.STARTING); // Open the console window if it's not already opened. // eslint-disable-next-line nuclide-internal/atom-apis - atom.workspace.open(CONSOLE_VIEW_URI, {searchAllPanes: true}); + + + atom.workspace.open(CONSOLE_VIEW_URI, { + searchAllPanes: true + }); this._consoleDisposables = this._registerConsoleExecutor(); - await this._doCreateProcess(config, uuid.v4()); + await this._doCreateProcess(config, _uuid().default.v4()); + if (this._model.getProcesses().length > 1) { const debuggerTypes = []; + this._model.getProcesses().forEach(process => { debuggerTypes.push(process.configuration.adapterType); }); - track(AnalyticsEvents.DEBUGGER_MULTITARGET, { + + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_MULTITARGET, { processesCount: this._model.getProcesses().length, - debuggerTypes, + debuggerTypes }); } } - consumeGatekeeperService(service: GatekeeperService): IDisposable { + consumeGatekeeperService(service) { _gkService = service; - return new UniversalDisposable(() => (_gkService = null)); + return new (_UniversalDisposable().default)(() => _gkService = null); } - _onSessionEnd = (givenSession: ?VsDebugSession): void => { - const session = - givenSession == null ? this._getCurrentSession() : givenSession; - if (session == null) { - return; - } - track(AnalyticsEvents.DEBUGGER_STOP); - const removedProcesses = this._model.removeProcess(session.getId()); - if ( - this._model.getProcesses() == null || - this._model.getProcesses().length === 0 - ) { - this._sessionEndDisposables.dispose(); - this._consoleDisposables.dispose(); - this.focusStackFrame(null, null, null); - this._updateModeAndEmit(DebuggerMode.STOPPED); - } else { - if ( - this._viewModel.focusedProcess != null && - this._viewModel.focusedProcess.getId() === session.getId() - ) { - const processToFocus = this._model.getProcesses()[ - this._model.getProcesses().length - 1 - ]; - const threadToFocus = - processToFocus.getAllThreads().length > 0 - ? processToFocus.getAllThreads()[0] - : null; - const frameToFocus = - threadToFocus != null && threadToFocus.getCallStack.length > 0 - ? threadToFocus.getCallStack()[0] - : null; - - this.focusStackFrame(frameToFocus, threadToFocus, processToFocus); - } - } - - const createConsole = getConsoleService(); - if (createConsole != null) { - const name = 'Nuclide Debugger'; - const consoleApi = createConsole({ - id: name, - name, - }); - - removedProcesses.forEach(p => - consoleApi.append({ - text: - 'Process exited' + - (p.configuration.processName == null - ? '' - : ' (' + p.configuration.processName + ')'), - level: 'log', - }), - ); - } - - if (this._timer != null) { - this._timer.onSuccess(); - this._timer = null; - } - - // set breakpoints back to unverified since the session ended. - const data: { - [id: string]: DebugProtocol.Breakpoint, - } = {}; - this._model.getBreakpoints().forEach(bp => { - data[bp.getId()] = { - line: bp.line, - verified: false, - column: bp.column, - endLine: bp.endLine == null ? undefined : bp.endLine, - endColumn: bp.endColumn == null ? undefined : bp.endColumn, - }; - }); - this._model.updateBreakpoints(data); - }; - - getModel(): IModel { + getModel() { return this._model; } - async _sendAllBreakpoints(): Promise { - await Promise.all( - distinct(this._model.getBreakpoints(), bp => bp.uri).map(bp => - this._sendBreakpoints(bp.uri, false), - ), - ); - await this._sendFunctionBreakpoints(); - // send exception breakpoints at the end since some debug adapters rely on the order + async _sendAllBreakpoints() { + await Promise.all((0, _collection().distinct)(this._model.getBreakpoints(), bp => bp.uri).map(bp => this._sendBreakpoints(bp.uri, false))); + await this._sendFunctionBreakpoints(); // send exception breakpoints at the end since some debug adapters rely on the order + await this._sendExceptionBreakpoints(); } - async _sendBreakpoints( - uri: string, - sourceModified?: boolean = false, - ): Promise { + async _sendBreakpoints(uri, sourceModified = false) { const process = this._getCurrentProcess(); + const session = this._getCurrentSession(); - if ( - process == null || - session == null || - !session.isReadyForBreakpoints() - ) { + + if (process == null || session == null || !session.isReadyForBreakpoints()) { return; } - const breakpointsToSend = this._model - .getBreakpoints() - .filter( - bp => - this._model.areBreakpointsActivated() && bp.enabled && bp.uri === uri, - ); + const breakpointsToSend = this._model.getBreakpoints().filter(bp => this._model.areBreakpointsActivated() && bp.enabled && bp.uri === uri); const rawSource = process.getSource({ path: uri, - name: nuclideUri.basename(uri), + name: _nuclideUri().default.basename(uri) }).raw; if (breakpointsToSend.length && !rawSource.adapterData) { rawSource.adapterData = breakpointsToSend[0].adapterData; - } + } // The UI is 0-based, while VSP is 1-based. + - // The UI is 0-based, while VSP is 1-based. const response = await session.setBreakpoints({ - source: (rawSource: any), + source: rawSource, lines: breakpointsToSend.map(bp => bp.line), breakpoints: breakpointsToSend.map(bp => ({ line: bp.line, column: bp.column, condition: bp.condition, - hitCondition: bp.hitCondition, + hitCondition: bp.hitCondition })), - sourceModified, + sourceModified }); + if (response == null || response.body == null) { return; } - const data: {[id: string]: DebugProtocol.Breakpoint} = {}; + const data = {}; + for (let i = 0; i < breakpointsToSend.length; i++) { data[breakpointsToSend[i].getId()] = response.body.breakpoints[i]; + if (!breakpointsToSend[i].column) { // If there was no column sent ignore the breakpoint column response from the adapter data[breakpointsToSend[i].getId()].column = undefined; @@ -1700,39 +1615,33 @@ export default class DebugService implements IDebugService { this._model.updateBreakpoints(data); } - _getCurrentSession(): ?VsDebugSession { - return this._viewModel.focusedProcess == null - ? null - : (this._viewModel.focusedProcess.session: any); + _getCurrentSession() { + return this._viewModel.focusedProcess == null ? null : this._viewModel.focusedProcess.session; } - _getCurrentProcess(): ?IProcess { + _getCurrentProcess() { return this._viewModel.focusedProcess; } - async _sendFunctionBreakpoints(): Promise { + async _sendFunctionBreakpoints() { const session = this._getCurrentSession(); - if ( - session == null || - !session.isReadyForBreakpoints() || - !session.getCapabilities().supportsFunctionBreakpoints - ) { + + if (session == null || !session.isReadyForBreakpoints() || !session.getCapabilities().supportsFunctionBreakpoints) { return; } - const breakpointsToSend: any = this._model - .getFunctionBreakpoints() - .filter(fbp => fbp.enabled && this._model.areBreakpointsActivated()); - const response: DebugProtocol.SetFunctionBreakpointsResponse = await session.setFunctionBreakpoints( - { - breakpoints: breakpointsToSend, - }, - ); + const breakpointsToSend = this._model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this._model.areBreakpointsActivated()); + + const response = await session.setFunctionBreakpoints({ + breakpoints: breakpointsToSend + }); + if (response == null || response.body == null) { return; } const data = {}; + for (let i = 0; i < breakpointsToSend.length; i++) { data[breakpointsToSend[i].getId()] = response.body.breakpoints[i]; } @@ -1740,98 +1649,93 @@ export default class DebugService implements IDebugService { this._model.updateFunctionBreakpoints(data); } - async _sendExceptionBreakpoints(): Promise { + async _sendExceptionBreakpoints() { const session = this._getCurrentSession(); - if ( - session == null || - !session.isReadyForBreakpoints() || - this._model.getExceptionBreakpoints().length === 0 - ) { + + if (session == null || !session.isReadyForBreakpoints() || this._model.getExceptionBreakpoints().length === 0) { return; } - const enabledExceptionBps = this._model - .getExceptionBreakpoints() - .filter(exb => exb.enabled); + const enabledExceptionBps = this._model.getExceptionBreakpoints().filter(exb => exb.enabled); + await session.setExceptionBreakpoints({ - filters: enabledExceptionBps.map(exb => exb.filter), + filters: enabledExceptionBps.map(exb => exb.filter) }); } - _registerConsoleExecutor(): IDisposable { - const disposables = new UniversalDisposable(); - const registerExecutor = getConsoleRegisterExecutor(); + _registerConsoleExecutor() { + const disposables = new (_UniversalDisposable().default)(); + const registerExecutor = (0, _AtomServiceContainer().getConsoleRegisterExecutor)(); + if (registerExecutor == null) { return disposables; } - const output: Subject< - ConsoleMessage | {result?: EvaluationResult}, - > = new Subject(); + + const output = new _RxMin.Subject(); + const evaluateExpression = rawExpression => { - const expression = new Expression(rawExpression); - const {focusedProcess, focusedStackFrame} = this._viewModel; + const expression = new (_DebuggerModel().Expression)(rawExpression); + const { + focusedProcess, + focusedStackFrame + } = this._viewModel; + if (focusedProcess == null) { - logger.error('Cannot evaluate while there is no active debug session'); + _logger().default.error('Cannot evaluate while there is no active debug session'); + return; } - disposables.add( - // We filter here because the first value in the BehaviorSubject is null no matter what, and - // we want the console to unsubscribe the stream after the first non-null value. - expressionAsEvaluationResultStream( - expression, - focusedProcess, - focusedStackFrame, - 'repl', - ) - .skip(1) // Skip the first pending null value. - .subscribe(result => { - // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. - this.focusStackFrame( - this._viewModel.focusedStackFrame, - this._viewModel.focusedThread, - null, - false, - ); - - if (result == null || !expression.available) { - const message: ConsoleMessage = { - text: expression.getValue(), - level: 'error', - }; - output.next(message); - } else { - output.next({data: result}); - } - }), - ); + + disposables.add( // We filter here because the first value in the BehaviorSubject is null no matter what, and + // we want the console to unsubscribe the stream after the first non-null value. + (0, _utils().expressionAsEvaluationResultStream)(expression, focusedProcess, focusedStackFrame, 'repl').skip(1) // Skip the first pending null value. + .subscribe(result => { + // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. + this.focusStackFrame(this._viewModel.focusedStackFrame, this._viewModel.focusedThread, null, false); + + if (result == null || !expression.available) { + const message = { + text: expression.getValue(), + level: 'error' + }; + output.next(message); + } else { + output.next({ + data: result + }); + } + })); }; - disposables.add( - registerExecutor({ - id: 'debugger', - name: 'Debugger', - scopeName: 'text.plain', - send(expression: string) { - evaluateExpression(expression); - }, - output, - getProperties: (fetchChildrenForLazyComponent: any), - }), - ); + disposables.add(registerExecutor({ + id: 'debugger', + name: 'Debugger', + scopeName: 'text.plain', + + send(expression) { + evaluateExpression(expression); + }, + + output, + getProperties: _utils().fetchChildrenForLazyComponent + })); return disposables; } - dispose(): void { + dispose() { this._disposables.dispose(); + this._consoleDisposables.dispose(); + this._sessionEndDisposables.dispose(); } + } -class DebugSourceTextBufffer extends TextBuffer { - _uri: string; +exports.default = DebugService; - constructor(contents: string, uri: string) { +class DebugSourceTextBufffer extends _atom.TextBuffer { + constructor(contents, uri) { super(contents); this._uri = uri; } @@ -1847,4 +1751,5 @@ class DebugSourceTextBufffer extends TextBuffer { isModified() { return false; } -} + +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/vsp/DebuggerModel.js b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/vsp/DebuggerModel.js index 6eecf01e..4c6df418 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/vsp/DebuggerModel.js +++ b/modules/atom-ide-ui/pkg/atom-ide-debugger/lib/vsp/DebuggerModel.js @@ -1,3 +1,98 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Model = exports.ExceptionBreakpoint = exports.FunctionBreakpoint = exports.Breakpoint = exports.Process = exports.Thread = exports.StackFrame = exports.Scope = exports.Variable = exports.Expression = exports.Source = void 0; + +function DebugProtocol() { + const data = _interopRequireWildcard(require("vscode-debugprotocol")); + + DebugProtocol = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _uuid() { + const data = _interopRequireDefault(require("uuid")); + + _uuid = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("../constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("../utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +101,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -38,123 +133,64 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -import type { - IExpression, - IExpressionContainer, - IEvaluatableExpression, - IStackFrame, - IBreakpoint, - IRawModelUpdate, - IRawStopppedUpdate, - IRawThreadUpdate, - ISession, - IThread, - IModel, - IScope, - ISource, - IProcess, - IRawStoppedDetails, - IEnableable, - IBreakpointsChangeEvent, - IRawBreakpoint, - IExceptionInfo, - IExceptionBreakpoint, - IFunctionBreakpoint, - ITreeElement, - IVariable, - SourcePresentationHint, -} from '../types'; -import type {IProcessConfig} from 'nuclide-debugger-common'; -import * as DebugProtocol from 'vscode-debugprotocol'; - -import {Observable} from 'rxjs'; -import uuid from 'uuid'; -import nullthrows from 'nullthrows'; -import invariant from 'assert'; -import {Emitter, Range} from 'atom'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {track} from 'nuclide-commons/analytics'; -import {AnalyticsEvents, UNKNOWN_SOURCE, DEBUG_SOURCES_URI} from '../constants'; -import {openSourceLocation, onUnexpectedError} from '../utils'; -import {distinct} from 'nuclide-commons/collection'; - -export class Source implements ISource { - +uri: string; - available: boolean; - _raw: DebugProtocol.Source; - - constructor(raw: ?DebugProtocol.Source, sessionId: string) { +class Source { + constructor(raw, sessionId) { if (raw == null) { - this._raw = {name: UNKNOWN_SOURCE}; + this._raw = { + name: _constants().UNKNOWN_SOURCE + }; } else { this._raw = raw; } + if (this._raw.sourceReference != null && this._raw.sourceReference > 0) { - this.uri = `${DEBUG_SOURCES_URI}/${sessionId}/${ - this._raw.sourceReference - }/${this._raw.name == null ? UNKNOWN_SOURCE : this._raw.name}`; + this.uri = `${_constants().DEBUG_SOURCES_URI}/${sessionId}/${this._raw.sourceReference}/${this._raw.name == null ? _constants().UNKNOWN_SOURCE : this._raw.name}`; } else { this.uri = this._raw.path || ''; } + this.available = this.uri !== ''; } - get name(): ?string { + get name() { return this._raw.name; } - get origin(): ?string { + get origin() { return this._raw.origin; } - get presentationHint(): ?SourcePresentationHint { + get presentationHint() { return this._raw.presentationHint; } - get raw(): DebugProtocol.Source { + get raw() { return this._raw; } - get reference(): ?number { + get reference() { return this._raw.sourceReference; } - get inMemory(): boolean { - return this.uri.startsWith(DEBUG_SOURCES_URI); + get inMemory() { + return this.uri.startsWith(_constants().DEBUG_SOURCES_URI); } - openInEditor(): Promise { + openInEditor() { // eslint-disable-next-line nuclide-internal/atom-apis return atom.workspace.open(this.uri, { searchAllPanes: true, - pending: true, + pending: true }); } + } -class ExpressionContainer implements IExpressionContainer { - static allValues: Map = new Map(); +exports.Source = Source; + +class ExpressionContainer { // Use chunks to support variable paging #9537 - static BASE_CHUNK_SIZE = 100; - - _value: string; - _children: ?Promise; - process: ?IProcess; - _reference: number; - _id: string; - _namedVariables: number; - _indexedVariables: number; - _startOfVariables: number; - - constructor( - process: ?IProcess, - reference: number, - id: string, - namedVariables: ?number, - indexedVariables: ?number, - startOfVariables: ?number, - ) { + constructor(process, reference, id, namedVariables, indexedVariables, startOfVariables) { this.process = process; this._reference = reference; this._id = id; @@ -163,16 +199,16 @@ class ExpressionContainer implements IExpressionContainer { this._startOfVariables = startOfVariables || 0; } - get reference(): number { + get reference() { return this._reference; } - set reference(value: number) { + set reference(value) { this._reference = value; this._children = null; } - getChildren(): Promise { + getChildren() { if (this._children == null) { this._children = this._doGetChildren(); } @@ -180,7 +216,7 @@ class ExpressionContainer implements IExpressionContainer { return this._children; } - async _doGetChildren(): Promise { + async _doGetChildren() { if (!this.hasChildren()) { return []; } @@ -188,193 +224,131 @@ class ExpressionContainer implements IExpressionContainer { if (!this.getChildrenInChunks) { const variables = await this._fetchVariables(); return variables; - } + } // Check if object has named variables, fetch them independent from indexed variables #9670 + + + let childrenArray = []; - // Check if object has named variables, fetch them independent from indexed variables #9670 - let childrenArray: Array = []; if (Boolean(this._namedVariables)) { childrenArray = await this._fetchVariables(undefined, undefined, 'named'); - } + } // Use a dynamic chunk size based on the number of elements #9774 + - // Use a dynamic chunk size based on the number of elements #9774 let chunkSize = ExpressionContainer.BASE_CHUNK_SIZE; - while ( - this._indexedVariables > - chunkSize * ExpressionContainer.BASE_CHUNK_SIZE - ) { + + while (this._indexedVariables > chunkSize * ExpressionContainer.BASE_CHUNK_SIZE) { chunkSize *= ExpressionContainer.BASE_CHUNK_SIZE; } if (this._indexedVariables > chunkSize) { // There are a lot of children, create fake intermediate values that represent chunks #9537 const numberOfChunks = Math.ceil(this._indexedVariables / chunkSize); + for (let i = 0; i < numberOfChunks; i++) { const start = this._startOfVariables + i * chunkSize; - const count = Math.min( - chunkSize, - this._indexedVariables - i * chunkSize, - ); - childrenArray.push( - new Variable( - this.process, - this, - this.reference, - `[${start}..${start + count - 1}]`, - '', - '', - null, - count, - {kind: 'virtual'}, - null, - true, - start, - ), - ); + const count = Math.min(chunkSize, this._indexedVariables - i * chunkSize); + childrenArray.push(new Variable(this.process, this, this.reference, `[${start}..${start + count - 1}]`, '', '', null, count, { + kind: 'virtual' + }, null, true, start)); } return childrenArray; } - const variables = await this._fetchVariables( - this._startOfVariables, - this._indexedVariables, - 'indexed', - ); + const variables = await this._fetchVariables(this._startOfVariables, this._indexedVariables, 'indexed'); return childrenArray.concat(variables); } - getId(): string { + getId() { return this._id; } - getValue(): string { + getValue() { return this._value; } - hasChildren(): boolean { + hasChildren() { // only variables with reference > 0 have children. return this.reference > 0; } - async _fetchVariables( - start?: number, - count?: number, - filter?: 'indexed' | 'named', - ): Promise { + async _fetchVariables(start, count, filter) { const process = this.process; - invariant(process); + + if (!process) { + throw new Error("Invariant violation: \"process\""); + } + try { - const response: DebugProtocol.VariablesResponse = await process.session.variables( - { - variablesReference: this.reference, - start, - count, - filter, - }, - ); - const variables = distinct( - response.body.variables.filter(v => v != null && v.name), - v => v.name, - ); - return variables.map( - v => - new Variable( - this.process, - this, - v.variablesReference, - v.name, - v.evaluateName, - v.value, - v.namedVariables, - v.indexedVariables, - v.presentationHint, - v.type, - ), - ); + const response = await process.session.variables({ + variablesReference: this.reference, + start, + count, + filter + }); + const variables = (0, _collection().distinct)(response.body.variables.filter(v => v != null && v.name), v => v.name); + return variables.map(v => new Variable(this.process, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type)); } catch (e) { - return [ - new Variable( - this.process, - this, - 0, - null, - e.message, - '', - 0, - 0, - {kind: 'virtual'}, - null, - false, - ), - ]; + return [new Variable(this.process, this, 0, null, e.message, '', 0, 0, { + kind: 'virtual' + }, null, false)]; } - } + } // The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked. - // The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked. - get getChildrenInChunks(): boolean { + + get getChildrenInChunks() { return Boolean(this._indexedVariables); } - setValue(value: string) { + setValue(value) { this._value = value; ExpressionContainer.allValues.set(this.getId(), value); } - toString(): string { + toString() { return this._value; } -} -export class Expression extends ExpressionContainer - implements IEvaluatableExpression { - static DEFAULT_VALUE = 'not available'; +} - available: boolean; - _type: ?string; - name: string; +ExpressionContainer.allValues = new Map(); +ExpressionContainer.BASE_CHUNK_SIZE = 100; - constructor(name: string, id?: string = uuid.v4()) { +class Expression extends ExpressionContainer { + constructor(name, id = _uuid().default.v4()) { super(null, 0, id); this.name = name; this.available = false; - this._type = null; - // name is not set if the expression is just being added + this._type = null; // name is not set if the expression is just being added // in that case do not set default value to prevent flashing #14499 + if (name) { this._value = Expression.DEFAULT_VALUE; } } - get type(): ?string { + get type() { return this._type; } - async evaluate( - process: ?IProcess, - stackFrame: ?IStackFrame, - context: string, - ): Promise { - if (process == null || (stackFrame == null && context !== 'repl')) { - this._value = - context === 'repl' - ? 'Please start a debug session to evaluate' - : Expression.DEFAULT_VALUE; + async evaluate(process, stackFrame, context) { + if (process == null || stackFrame == null && context !== 'repl') { + this._value = context === 'repl' ? 'Please start a debug session to evaluate' : Expression.DEFAULT_VALUE; this.available = false; this.reference = 0; return; } this.process = process; - try { - const response: DebugProtocol.EvaluateResponse = await process.session.evaluate( - { - expression: this.name, - frameId: stackFrame ? stackFrame.frameId : undefined, - context, - }, - ); + try { + const response = await process.session.evaluate({ + expression: this.name, + frameId: stackFrame ? stackFrame.frameId : undefined, + context + }); this.available = response != null && response.body != null; + if (response && response.body) { this._value = response.body.result; this.reference = response.body.variablesReference || 0; @@ -389,44 +363,20 @@ export class Expression extends ExpressionContainer } } - toString(): string { + toString() { return `${this.name}\n${this._value}`; } + } -export class Variable extends ExpressionContainer implements IExpression { +exports.Expression = Expression; +Expression.DEFAULT_VALUE = 'not available'; + +class Variable extends ExpressionContainer { // Used to show the error message coming from the adapter when setting the value #7807 - errorMessage: ?string; - parent: ExpressionContainer; - name: string; - evaluateName: ?string; - presentationHint: ?DebugProtocol.VariablePresentationHint; - _type: ?string; - available: boolean; - - constructor( - process: ?IProcess, - parent: ExpressionContainer, - reference: number, - name: ?string, - evaluateName: ?string, - value: string, - namedVariables: ?number, - indexedVariables: ?number, - presentationHint: ?DebugProtocol.VariablePresentationHint, - type: ?string, - available?: boolean = true, - _startOfVariables: ?number, - ) { - super( - process, - reference, - // flowlint-next-line sketchy-null-string:off - `variable:${parent.getId()}:${name || 'no_name'}`, - namedVariables, - indexedVariables, - _startOfVariables, - ); + constructor(process, parent, reference, name, evaluateName, value, namedVariables, indexedVariables, presentationHint, type, available = true, _startOfVariables) { + super(process, reference, // flowlint-next-line sketchy-null-string:off + `variable:${parent.getId()}:${name || 'no_name'}`, namedVariables, indexedVariables, _startOfVariables); this.parent = parent; this.name = name == null ? 'no_name' : name; this.evaluateName = evaluateName; @@ -436,25 +386,26 @@ export class Variable extends ExpressionContainer implements IExpression { this._value = value; } - get type(): ?string { + get type() { return this._type; } - async setVariable(value: string): Promise { - const process = nullthrows(this.process); - track(AnalyticsEvents.DEBUGGER_EDIT_VARIABLE, { - language: process.configuration.adapterType, + async setVariable(value) { + const process = (0, _nullthrows().default)(this.process); + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_EDIT_VARIABLE, { + language: process.configuration.adapterType }); + try { const response = await process.session.setVariable({ - name: nullthrows(this.name), + name: (0, _nullthrows().default)(this.name), value, - variablesReference: this.parent.reference, + variablesReference: this.parent.reference }); + if (response && response.body) { this._value = response.body.value; - this._type = - response.body.type == null ? this._type : response.body.type; + this._type = response.body.type == null ? this._type : response.body.type; this.reference = response.body.variablesReference || 0; this._namedVariables = response.body.namedVariables || 0; this._indexedVariables = response.body.indexedVariables || 0; @@ -464,58 +415,28 @@ export class Variable extends ExpressionContainer implements IExpression { } } - toString(): string { + toString() { return `${this.name}: ${this._value}`; } + } -export class Scope extends ExpressionContainer implements IScope { - +name: string; - +expensive: boolean; - +range: ?atom$Range; - - constructor( - stackFrame: IStackFrame, - index: number, - name: string, - reference: number, - expensive: boolean, - namedVariables: ?number, - indexedVariables: ?number, - range: ?atom$Range, - ) { - super( - stackFrame.thread.process, - reference, - `scope:${stackFrame.getId()}:${name}:${index}`, - namedVariables, - indexedVariables, - ); +exports.Variable = Variable; + +class Scope extends ExpressionContainer { + constructor(stackFrame, index, name, reference, expensive, namedVariables, indexedVariables, range) { + super(stackFrame.thread.process, reference, `scope:${stackFrame.getId()}:${name}:${index}`, namedVariables, indexedVariables); this.name = name; this.expensive = expensive; this.range = range; } + } -export class StackFrame implements IStackFrame { - scopes: ?Promise; - thread: IThread; - frameId: number; - source: ISource; - name: string; - presentationHint: ?string; - range: atom$Range; - index: number; - - constructor( - thread: IThread, - frameId: number, - source: ISource, - name: string, - presentationHint: ?string, - range: atom$Range, - index: number, - ) { +exports.Scope = Scope; + +class StackFrame { + constructor(thread, frameId, source, name, presentationHint, range, index) { this.thread = thread; this.frameId = frameId; this.source = source; @@ -526,103 +447,74 @@ export class StackFrame implements IStackFrame { this.scopes = null; } - getId(): string { + getId() { return `stackframe:${this.thread.getId()}:${this.frameId}:${this.index}`; } - async getScopes(): Promise { + async getScopes() { if (this.scopes == null) { this.scopes = this._getScopesImpl(); } - return (this.scopes: any); + + return this.scopes; } - async _getScopesImpl(): Promise { + async _getScopesImpl() { try { const { - body: {scopes}, + body: { + scopes + } } = await this.thread.process.session.scopes({ - frameId: this.frameId, + frameId: this.frameId }); - return scopes.map( - (rs, index) => - new Scope( - this, - index, - rs.name, - rs.variablesReference, - rs.expensive, - rs.namedVariables, - rs.indexedVariables, - rs.line != null - ? new Range( - [rs.line - 1, (rs.column != null ? rs.column : 1) - 1], - [ - (rs.endLine != null ? rs.endLine : rs.line) - 1, - (rs.endColumn != null ? rs.endColumn : 1) - 1, - ], - ) - : null, - ), - ); + return scopes.map((rs, index) => new Scope(this, index, rs.name, rs.variablesReference, rs.expensive, rs.namedVariables, rs.indexedVariables, rs.line != null ? new _atom.Range([rs.line - 1, (rs.column != null ? rs.column : 1) - 1], [(rs.endLine != null ? rs.endLine : rs.line) - 1, (rs.endColumn != null ? rs.endColumn : 1) - 1]) : null)); } catch (err) { return []; } } - async getMostSpecificScopes(range: atom$Range): Promise { - const scopes: Array = (await this.getScopes()).filter( - s => !s.expensive, - ); + async getMostSpecificScopes(range) { + const scopes = (await this.getScopes()).filter(s => !s.expensive); const haveRangeInfo = scopes.some(s => s.range != null); + if (!haveRangeInfo) { return scopes; } - const scopesContainingRange = scopes - .filter(scope => scope.range != null && scope.range.containsRange(range)) - .sort((first, second) => { - const firstRange = nullthrows(first.range); - const secondRange = nullthrows(second.range); - // prettier-ignore - return (firstRange.end.row - firstRange.start.row) - - (secondRange.end.row - secondRange.end.row); - }); + const scopesContainingRange = scopes.filter(scope => scope.range != null && scope.range.containsRange(range)).sort((first, second) => { + const firstRange = (0, _nullthrows().default)(first.range); + const secondRange = (0, _nullthrows().default)(second.range); // prettier-ignore + + return firstRange.end.row - firstRange.start.row - (secondRange.end.row - secondRange.end.row); + }); return scopesContainingRange.length ? scopesContainingRange : scopes; } - async restart(): Promise { - await this.thread.process.session.restartFrame( - {frameId: this.frameId}, - this.thread.threadId, - ); + async restart() { + await this.thread.process.session.restartFrame({ + frameId: this.frameId + }, this.thread.threadId); } - toString(): string { - return `${this.name} (${ - this.source.inMemory ? nullthrows(this.source.name) : this.source.uri - }:${this.range.start.row})`; + toString() { + return `${this.name} (${this.source.inMemory ? (0, _nullthrows().default)(this.source.name) : this.source.uri}:${this.range.start.row})`; } - async openInEditor(): Promise { + async openInEditor() { if (this.source.available) { - return openSourceLocation(this.source.uri, this.range.start.row); + return (0, _utils().openSourceLocation)(this.source.uri, this.range.start.row); } else { return null; } } + } -export class Thread implements IThread { - _callStack: IStackFrame[]; - _staleCallStack: IStackFrame[]; - stoppedDetails: ?IRawStoppedDetails; - stopped: boolean; - +process: IProcess; - +threadId: number; - name: string; +exports.StackFrame = StackFrame; - constructor(process: IProcess, name: string, threadId: number) { +class Thread { + constructor(process, name, threadId) { this.process = process; this.name = name; this.threadId = threadId; @@ -632,25 +524,25 @@ export class Thread implements IThread { this.stopped = false; } - getId(): string { + getId() { return `thread:${this.process.getId()}:${this.threadId}`; } - clearCallStack(): void { + clearCallStack() { if (this._callStack.length > 0) { this._staleCallStack = this._callStack; } + this._callStack = []; } - getCallStack(): IStackFrame[] { + getCallStack() { return this._callStack; } - getStaleCallStack(): IStackFrame[] { + getStaleCallStack() { return this._staleCallStack; } - /** * Queries the debug adapter for the callstack and returns a promise * which completes once the call stack has been retrieved. @@ -658,58 +550,44 @@ export class Thread implements IThread { * Only fetches the first stack frame for performance reasons. Calling this method consecutive times * gets the remainder of the call stack. */ - async fetchCallStack(levels?: number = 20): Promise { + + + async fetchCallStack(levels = 20) { if (!this.stopped) { return; } const start = this._callStack.length; const callStack = await this._getCallStackImpl(start, levels); + if (start < this._callStack.length) { // Set the stack frames for exact position we requested. To make sure no concurrent requests create duplicate stack frames #30660 this._callStack.splice(start, this._callStack.length - start); } + this._callStack = this._callStack.concat(callStack || []); } - async _getCallStackImpl( - startFrame: number, - levels: number, - ): Promise { + async _getCallStackImpl(startFrame, levels) { try { - const response: DebugProtocol.StackTraceResponse = await this.process.session.stackTrace( - { - threadId: this.threadId, - startFrame, - levels, - }, - ); + const response = await this.process.session.stackTrace({ + threadId: this.threadId, + startFrame, + levels + }); + if (response == null || response.body == null) { return []; } + if (this.stoppedDetails != null) { this.stoppedDetails.totalFrames = response.body.totalFrames; } return response.body.stackFrames.map((rsf, index) => { const source = this.process.getSource(rsf.source); - - return new StackFrame( - this, - rsf.id, - source, - rsf.name, - rsf.presentationHint, - // The UI is 0-based while VSP is 1-based. - new Range( - [rsf.line - 1, (rsf.column || 1) - 1], - [ - (rsf.endLine != null ? rsf.endLine : rsf.line) - 1, - (rsf.endColumn != null ? rsf.endColumn : 1) - 1, - ], - ), - startFrame + index, - ); + return new StackFrame(this, rsf.id, source, rsf.name, rsf.presentationHint, // The UI is 0-based while VSP is 1-based. + new _atom.Range([rsf.line - 1, (rsf.column || 1) - 1], [(rsf.endLine != null ? rsf.endLine : rsf.line) - 1, (rsf.endColumn != null ? rsf.endColumn : 1) - 1]), startFrame + index); }); } catch (err) { if (this.stoppedDetails != null) { @@ -719,31 +597,33 @@ export class Thread implements IThread { return []; } } - /** * Returns exception info promise if the exception was thrown, otherwise null */ - async exceptionInfo(): Promise { + + + async exceptionInfo() { const session = this.process.session; - if ( - this.stoppedDetails == null || - this.stoppedDetails.reason !== 'exception' - ) { + + if (this.stoppedDetails == null || this.stoppedDetails.reason !== 'exception') { return null; } + const stoppedDetails = this.stoppedDetails; + if (!session.capabilities.supportsExceptionInfoRequest) { return { id: null, details: null, description: stoppedDetails.description, - breakMode: null, + breakMode: null }; } - const exception: DebugProtocol.ExceptionInfoResponse = await session.exceptionInfo( - {threadId: this.threadId}, - ); + const exception = await session.exceptionInfo({ + threadId: this.threadId + }); + if (exception == null) { return null; } @@ -752,74 +632,87 @@ export class Thread implements IThread { id: exception.body.exceptionId, description: exception.body.description, breakMode: exception.body.breakMode, - details: exception.body.details, + details: exception.body.details }; } - async next(): Promise { - track(AnalyticsEvents.DEBUGGER_STEP_OVER); - await this.process.session.next({threadId: this.threadId}); + async next() { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_OVER); + await this.process.session.next({ + threadId: this.threadId + }); } - async stepIn(): Promise { - track(AnalyticsEvents.DEBUGGER_STEP_INTO); - await this.process.session.stepIn({threadId: this.threadId}); + async stepIn() { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_INTO); + await this.process.session.stepIn({ + threadId: this.threadId + }); } - async stepOut(): Promise { - track(AnalyticsEvents.DEBUGGER_STEP_OUT); - await this.process.session.stepOut({threadId: this.threadId}); + async stepOut() { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_OUT); + await this.process.session.stepOut({ + threadId: this.threadId + }); } - async stepBack(): Promise { - track(AnalyticsEvents.DEBUGGER_STEP_BACK); - await this.process.session.stepBack({threadId: this.threadId}); + async stepBack() { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_BACK); + await this.process.session.stepBack({ + threadId: this.threadId + }); } - async continue(): Promise { - track(AnalyticsEvents.DEBUGGER_STEP_CONTINUE); - await this.process.session.continue({threadId: this.threadId}); + async continue() { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_CONTINUE); + await this.process.session.continue({ + threadId: this.threadId + }); } - async pause(): Promise { - track(AnalyticsEvents.DEBUGGER_STEP_PAUSE); - await this.process.session.pause({threadId: this.threadId}); + async pause() { + (0, _analytics().track)(_constants().AnalyticsEvents.DEBUGGER_STEP_PAUSE); + await this.process.session.pause({ + threadId: this.threadId + }); } - async reverseContinue(): Promise { - await this.process.session.reverseContinue({threadId: this.threadId}); + async reverseContinue() { + await this.process.session.reverseContinue({ + threadId: this.threadId + }); } + } -export class Process implements IProcess { - _sources: Map; - _threads: Map; - _session: ISession & ITreeElement; - _configuration: IProcessConfig; +exports.Thread = Thread; - constructor(configuration: IProcessConfig, session: ISession & ITreeElement) { +class Process { + constructor(configuration, session) { this._configuration = configuration; this._session = session; this._threads = new Map(); this._sources = new Map(); } - get sources(): Map { + get sources() { return this._sources; } - get session(): ISession & ITreeElement { + get session() { return this._session; } - get configuration(): IProcessConfig { + get configuration() { return this._configuration; } - getSource(raw: ?DebugProtocol.Source): ISource { + getSource(raw) { let source = new Source(raw, this.getId()); + if (this._sources.has(source.uri)) { - source = nullthrows(this._sources.get(source.uri)); + source = (0, _nullthrows().default)(this._sources.get(source.uri)); } else { this._sources.set(source.uri, source); } @@ -827,60 +720,67 @@ export class Process implements IProcess { return source; } - getThread(threadId: number): ?Thread { + getThread(threadId) { return this._threads.get(threadId); } - getAllThreads(): IThread[] { + getAllThreads() { return Array.from(this._threads.values()); } - getId(): string { + getId() { return this._session.getId(); } - rawStoppedUpdate(data: IRawStopppedUpdate): void { - const {threadId, stoppedDetails} = data; + rawStoppedUpdate(data) { + const { + threadId, + stoppedDetails + } = data; + if (threadId != null && !this._threads.has(threadId)) { // We're being asked to update a thread we haven't seen yet, so // create it const thread = new Thread(this, 'PENDING_UPDATE', threadId); - this._threads.set(threadId, thread); - } - // Set the availability of the threads' callstacks depending on + this._threads.set(threadId, thread); + } // Set the availability of the threads' callstacks depending on // whether the thread is stopped or not + + if (stoppedDetails.allThreadsStopped) { this._threads.forEach(thread => { - thread.stoppedDetails = - thread.threadId === threadId ? stoppedDetails : thread.stoppedDetails; + thread.stoppedDetails = thread.threadId === threadId ? stoppedDetails : thread.stoppedDetails; thread.stopped = true; thread.clearCallStack(); }); } else if (threadId != null) { // One thread is stopped, only update that thread. - const thread = nullthrows(this._threads.get(threadId)); + const thread = (0, _nullthrows().default)(this._threads.get(threadId)); thread.stoppedDetails = stoppedDetails; thread.clearCallStack(); thread.stopped = true; } } - rawThreadUpdate(data: IRawThreadUpdate): void { - const {thread} = data; + rawThreadUpdate(data) { + const { + thread + } = data; + if (!this._threads.has(thread.id)) { // A new thread came in, initialize it. this._threads.set(thread.id, new Thread(this, thread.name, thread.id)); } else if (thread.name) { // Just the thread name got updated #18244 - nullthrows(this._threads.get(thread.id)).name = thread.name; + (0, _nullthrows().default)(this._threads.get(thread.id)).name = thread.name; } } - clearThreads(removeThreads: boolean, reference?: number): void { + clearThreads(removeThreads, reference) { if (reference != null) { if (this._threads.has(reference)) { - const thread = nullthrows(this._threads.get(reference)); + const thread = (0, _nullthrows().default)(this._threads.get(reference)); thread.clearCallStack(); thread.stoppedDetails = null; thread.stopped = false; @@ -898,27 +798,25 @@ export class Process implements IProcess { if (removeThreads) { this._threads.clear(); + ExpressionContainer.allValues.clear(); } } } - async completions( - frameId: number, - text: string, - position: atom$Point, - overwriteBefore: number, - ): Promise> { + async completions(frameId, text, position, overwriteBefore) { if (!this._session.capabilities.supportsCompletionsRequest) { return []; } + try { const response = await this._session.completions({ frameId, text, column: position.column, - line: position.row, + line: position.row }); + if (response && response.body && response.body.targets) { return response.body.targets; } else { @@ -928,32 +826,13 @@ export class Process implements IProcess { return []; } } + } -export class Breakpoint implements IBreakpoint { - verified: boolean; - idFromAdapter: ?number; - message: ?string; - endLine: ?number; - endColumn: ?number; - id: string; - uri: string; - line: number; - column: number; - enabled: boolean; - condition: ?string; - hitCondition: ?string; - adapterData: any; - - constructor( - uri: string, - line: number, - column: ?number, - enabled: ?boolean, - condition: ?string, - hitCondition: ?string, - adapterData?: any, - ) { +exports.Process = Process; + +class Breakpoint { + constructor(uri, line, column, enabled, condition, hitCondition, adapterData) { this.uri = uri; this.line = line; this.column = column == null ? 1 : column; @@ -962,79 +841,58 @@ export class Breakpoint implements IBreakpoint { this.hitCondition = hitCondition; this.adapterData = adapterData; this.verified = false; - this.id = uuid.v4(); + this.id = _uuid().default.v4(); this.endLine = null; } - getId(): string { + getId() { return this.id; } + } -export class FunctionBreakpoint implements IFunctionBreakpoint { - id: string; - verified: boolean; - idFromAdapter: ?number; - name: string; - enabled: boolean; - hitCondition: ?string; - condition: ?string; +exports.Breakpoint = Breakpoint; - constructor(name: string, enabled: boolean, hitCondition: ?string) { +class FunctionBreakpoint { + constructor(name, enabled, hitCondition) { this.name = name; this.enabled = enabled; this.hitCondition = hitCondition; this.condition = null; this.verified = false; this.idFromAdapter = null; - this.id = uuid.v4(); + this.id = _uuid().default.v4(); } - getId(): string { + getId() { return this.id; } + } -export class ExceptionBreakpoint implements IExceptionBreakpoint { - _id: string; - +filter: string; - +label: string; - enabled: boolean; +exports.FunctionBreakpoint = FunctionBreakpoint; - constructor(filter: string, label: string, enabled: ?boolean) { +class ExceptionBreakpoint { + constructor(filter, label, enabled) { this.filter = filter; this.label = label; this.enabled = enabled == null ? false : enabled; - this._id = uuid.v4(); + this._id = _uuid().default.v4(); } - getId(): string { + getId() { return this._id; } + } +exports.ExceptionBreakpoint = ExceptionBreakpoint; const BREAKPOINTS_CHANGED = 'BREAKPOINTS_CHANGED'; const CALLSTACK_CHANGED = 'CALLSTACK_CHANGED'; const WATCH_EXPRESSIONS_CHANGED = 'WATCH_EXPRESSIONS_CHANGED'; -export class Model implements IModel { - _processes: Process[]; - _schedulers: Map; - _breakpoints: Breakpoint[]; - _breakpointsActivated: boolean; - _functionBreakpoints: FunctionBreakpoint[]; - _exceptionBreakpoints: ExceptionBreakpoint[]; - _watchExpressions: Expression[]; - _disposables: UniversalDisposable; - _emitter: Emitter; - - constructor( - breakpoints: Breakpoint[], - breakpointsActivated: boolean, - functionBreakpoints: FunctionBreakpoint[], - exceptionBreakpoints: ExceptionBreakpoint[], - watchExpressions: Expression[], - ) { +class Model { + constructor(breakpoints, breakpointsActivated, functionBreakpoints, exceptionBreakpoints, watchExpressions) { this._processes = []; this._schedulers = new Map(); this._breakpoints = breakpoints; @@ -1042,28 +900,27 @@ export class Model implements IModel { this._functionBreakpoints = functionBreakpoints; this._exceptionBreakpoints = exceptionBreakpoints; this._watchExpressions = watchExpressions; - this._emitter = new Emitter(); - this._disposables = new UniversalDisposable(this._emitter); + this._emitter = new _atom.Emitter(); + this._disposables = new (_UniversalDisposable().default)(this._emitter); } - getId(): string { + getId() { return 'root'; } - getProcesses(): IProcess[] { - return (this._processes: any); + getProcesses() { + return this._processes; } - addProcess( - configuration: IProcessConfig, - session: ISession & ITreeElement, - ): Process { + addProcess(configuration, session) { const process = new Process(configuration, session); + this._processes.push(process); + return process; } - removeProcess(id: string): Array { + removeProcess(id) { const removedProcesses = []; this._processes = this._processes.filter(p => { if (p.getId() === id) { @@ -1073,175 +930,153 @@ export class Model implements IModel { return true; } }); + this._emitter.emit(CALLSTACK_CHANGED); + return removedProcesses; } - onDidChangeBreakpoints( - callback: (event: IBreakpointsChangeEvent) => mixed, - ): IDisposable { + onDidChangeBreakpoints(callback) { return this._emitter.on(BREAKPOINTS_CHANGED, callback); } - onDidChangeCallStack(callback: () => mixed): IDisposable { + onDidChangeCallStack(callback) { return this._emitter.on(CALLSTACK_CHANGED, callback); } - onDidChangeWatchExpressions( - callback: (expression: ?IExpression) => mixed, - ): IDisposable { + onDidChangeWatchExpressions(callback) { return this._emitter.on(WATCH_EXPRESSIONS_CHANGED, callback); } - rawUpdate(data: IRawModelUpdate): void { - const process = this._processes - .filter(p => p.getId() === data.sessionId) - .pop(); + rawUpdate(data) { + const process = this._processes.filter(p => p.getId() === data.sessionId).pop(); + if (process == null) { return; } + if (data.stoppedDetails != null) { - process.rawStoppedUpdate((data: any)); + process.rawStoppedUpdate(data); } else { - process.rawThreadUpdate((data: any)); + process.rawThreadUpdate(data); } this._emitter.emit(CALLSTACK_CHANGED); } - clearThreads(id: string, removeThreads: boolean, reference?: number): void { + clearThreads(id, removeThreads, reference) { const process = this._processes.filter(p => p.getId() === id).pop(); + this._schedulers.forEach(scheduler => scheduler.unsubscribe()); + this._schedulers.clear(); if (process != null) { process.clearThreads(removeThreads, reference); + this._emitter.emit(CALLSTACK_CHANGED); } } - async fetchCallStack(threadI: IThread): Promise { - const thread: Thread = (threadI: any); - if ( - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) - nullthrows(thread.process).session.capabilities - .supportsDelayedStackTraceLoading - ) { + async fetchCallStack(threadI) { + const thread = threadI; + + if ( // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + (0, _nullthrows().default)(thread.process).session.capabilities.supportsDelayedStackTraceLoading) { // For improved performance load the first stack frame and then load the rest async. await thread.fetchCallStack(1); + if (!this._schedulers.has(thread.getId())) { - this._schedulers.set( - thread.getId(), - Observable.timer(500).subscribe(() => { - thread - .fetchCallStack(19) - .then( - () => this._emitter.emit(CALLSTACK_CHANGED), - onUnexpectedError, - ); - }), - ); + this._schedulers.set(thread.getId(), _RxMin.Observable.timer(500).subscribe(() => { + thread.fetchCallStack(19).then(() => this._emitter.emit(CALLSTACK_CHANGED), _utils().onUnexpectedError); + })); } } else { thread.clearCallStack(); await thread.fetchCallStack(); } + this._emitter.emit(CALLSTACK_CHANGED); } - getBreakpoints(): IBreakpoint[] { - return (this._breakpoints: any); + getBreakpoints() { + return this._breakpoints; } - getBreakpointAtLine(uri: string, line: number): ?IBreakpoint { + getBreakpointAtLine(uri, line) { // Since we show calibrated breakpoints at their end line, prefer an end line // match. If there is no such breakpoint, try a start line match. - let breakpoint = this._breakpoints.find( - bp => bp.uri === uri && bp.endLine === line, - ); + let breakpoint = this._breakpoints.find(bp => bp.uri === uri && bp.endLine === line); + if (breakpoint == null) { - breakpoint = this._breakpoints.find( - bp => bp.uri === uri && bp.line === line, - ); + breakpoint = this._breakpoints.find(bp => bp.uri === uri && bp.line === line); } + return breakpoint; } - getBreakpointById(id: string): ?IBreakpoint { + getBreakpointById(id) { return this._breakpoints.find(bp => bp.getId() === id); } - getFunctionBreakpoints(): IFunctionBreakpoint[] { - return (this._functionBreakpoints: any); + getFunctionBreakpoints() { + return this._functionBreakpoints; } - getExceptionBreakpoints(): IExceptionBreakpoint[] { - return (this._exceptionBreakpoints: any); + getExceptionBreakpoints() { + return this._exceptionBreakpoints; } - setExceptionBreakpoints( - data: DebugProtocol.ExceptionBreakpointsFilter[], - ): void { + setExceptionBreakpoints(data) { this._exceptionBreakpoints = data.map(d => { - const ebp = this._exceptionBreakpoints - .filter(bp => bp.filter === d.filter) - .pop(); - return new ExceptionBreakpoint( - d.filter, - d.label, - ebp ? ebp.enabled : d.default, - ); + const ebp = this._exceptionBreakpoints.filter(bp => bp.filter === d.filter).pop(); + + return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : d.default); }); + this._emitter.emit(BREAKPOINTS_CHANGED); } - areBreakpointsActivated(): boolean { + areBreakpointsActivated() { return this._breakpointsActivated; } - setBreakpointsActivated(activated: boolean): void { + setBreakpointsActivated(activated) { this._breakpointsActivated = activated; + this._emitter.emit(BREAKPOINTS_CHANGED); } - addBreakpoints( - uri: string, - rawData: IRawBreakpoint[], - fireEvent?: boolean = true, - ): Breakpoint[] { - const newBreakpoints = rawData.map( - rawBp => - new Breakpoint( - uri, - rawBp.line, - rawBp.column, - rawBp.enabled, - rawBp.condition, - rawBp.hitCondition, - ), - ); + addBreakpoints(uri, rawData, fireEvent = true) { + const newBreakpoints = rawData.map(rawBp => new Breakpoint(uri, rawBp.line, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition)); this._breakpoints = this._breakpoints.concat(newBreakpoints); this._breakpointsActivated = true; + this._sortAndDeDup(); if (fireEvent) { - this._emitter.emit(BREAKPOINTS_CHANGED, {added: newBreakpoints}); + this._emitter.emit(BREAKPOINTS_CHANGED, { + added: newBreakpoints + }); } return newBreakpoints; } - removeBreakpoints(toRemove: IBreakpoint[]): void { - this._breakpoints = this._breakpoints.filter( - bp => !toRemove.some(r => r.getId() === bp.getId()), - ); - this._emitter.emit(BREAKPOINTS_CHANGED, {removed: toRemove}); + removeBreakpoints(toRemove) { + this._breakpoints = this._breakpoints.filter(bp => !toRemove.some(r => r.getId() === bp.getId())); + + this._emitter.emit(BREAKPOINTS_CHANGED, { + removed: toRemove + }); } - updateBreakpoints(data: {[id: string]: DebugProtocol.Breakpoint}): void { - const updated: IBreakpoint[] = []; + updateBreakpoints(data) { + const updated = []; + this._breakpoints.forEach(bp => { const bpData = data[bp.getId()]; + if (bpData != null) { bp.line = bpData.line != null ? bpData.line : bp.line; bp.endLine = bpData.endLine != null ? bpData.endLine : bp.endLine; @@ -1250,156 +1085,169 @@ export class Model implements IModel { bp.verified = bpData.verified != null ? bpData.verified : bp.verified; bp.idFromAdapter = bpData.id; bp.message = bpData.message; - bp.adapterData = bpData.source - ? bpData.source.adapterData - : bp.adapterData; + bp.adapterData = bpData.source ? bpData.source.adapterData : bp.adapterData; updated.push(bp); } }); + this._sortAndDeDup(); - this._emitter.emit(BREAKPOINTS_CHANGED, {changed: updated}); + + this._emitter.emit(BREAKPOINTS_CHANGED, { + changed: updated + }); } - _sortAndDeDup(): void { + _sortAndDeDup() { this._breakpoints = this._breakpoints.sort((first, second) => { if (first.uri !== second.uri) { return first.uri.localeCompare(second.uri); } + if (first.line === second.line) { return first.column - second.column; } return first.line - second.line; }); - this._breakpoints = distinct( - this._breakpoints, - bp => - `${bp.uri}:${bp.endLine != null ? bp.endLine : bp.line}:${bp.column}`, - ); - } - - setEnablement(element: IEnableable, enable: boolean): void { - const changed: Array = []; - if ( - element.enabled !== enable && - (element instanceof Breakpoint || element instanceof FunctionBreakpoint) - ) { + this._breakpoints = (0, _collection().distinct)(this._breakpoints, bp => `${bp.uri}:${bp.endLine != null ? bp.endLine : bp.line}:${bp.column}`); + } + + setEnablement(element, enable) { + const changed = []; + + if (element.enabled !== enable && (element instanceof Breakpoint || element instanceof FunctionBreakpoint)) { changed.push(element); } element.enabled = enable; + if (element instanceof Breakpoint && !element.enabled) { element.verified = false; } - this._emitter.emit(BREAKPOINTS_CHANGED, {changed}); + this._emitter.emit(BREAKPOINTS_CHANGED, { + changed + }); } - enableOrDisableAllBreakpoints(enable: boolean): void { - const changed: (IBreakpoint | IFunctionBreakpoint)[] = []; + enableOrDisableAllBreakpoints(enable) { + const changed = []; + this._breakpoints.forEach(bp => { if (bp.enabled !== enable) { changed.push(bp); } + bp.enabled = enable; + if (!enable) { bp.verified = false; } }); + this._functionBreakpoints.forEach(fbp => { if (fbp.enabled !== enable) { changed.push(fbp); } + fbp.enabled = enable; }); - this._emitter.emit(BREAKPOINTS_CHANGED, {changed}); + this._emitter.emit(BREAKPOINTS_CHANGED, { + changed + }); } - addFunctionBreakpoint(functionName: string): FunctionBreakpoint { - const newFunctionBreakpoint = new FunctionBreakpoint( - functionName, - true, - null, - ); + addFunctionBreakpoint(functionName) { + const newFunctionBreakpoint = new FunctionBreakpoint(functionName, true, null); + this._functionBreakpoints.push(newFunctionBreakpoint); - this._emitter.emit(BREAKPOINTS_CHANGED, {added: [newFunctionBreakpoint]}); + + this._emitter.emit(BREAKPOINTS_CHANGED, { + added: [newFunctionBreakpoint] + }); + return newFunctionBreakpoint; } - updateFunctionBreakpoints(data: { - [id: string]: { - name?: string, - verified?: boolean, - id?: number, - hitCondition?: string, - }, - }): void { - const changed: IFunctionBreakpoint[] = []; + updateFunctionBreakpoints(data) { + const changed = []; this._functionBreakpoints.forEach(fbp => { const fbpData = data[fbp.getId()]; + if (fbpData != null) { fbp.name = fbpData.name != null ? fbpData.name : fbp.name; fbp.verified = fbpData.verified || fbp.verified; fbp.idFromAdapter = fbpData.id; fbp.hitCondition = fbpData.hitCondition; - changed.push(fbp); } }); - this._emitter.emit(BREAKPOINTS_CHANGED, {changed}); + this._emitter.emit(BREAKPOINTS_CHANGED, { + changed + }); } - removeFunctionBreakpoints(id?: string): void { - let removed: FunctionBreakpoint[]; + removeFunctionBreakpoints(id) { + let removed; + if (id != null) { removed = this._functionBreakpoints.filter(fbp => fbp.getId() === id); - this._functionBreakpoints = this._functionBreakpoints.filter( - fbp => fbp.getId() !== id, - ); + this._functionBreakpoints = this._functionBreakpoints.filter(fbp => fbp.getId() !== id); } else { removed = this._functionBreakpoints; this._functionBreakpoints = []; } - this._emitter.emit(BREAKPOINTS_CHANGED, {removed}); + + this._emitter.emit(BREAKPOINTS_CHANGED, { + removed + }); } - getWatchExpressions(): IEvaluatableExpression[] { - return (this._watchExpressions: any); + getWatchExpressions() { + return this._watchExpressions; } - addWatchExpression(name: string): void { + addWatchExpression(name) { const we = new Expression(name); + this._watchExpressions.push(we); + this._emitter.emit(WATCH_EXPRESSIONS_CHANGED, we); } - renameWatchExpression(id: string, newName: string): void { + renameWatchExpression(id, newName) { const filtered = this._watchExpressions.filter(we => we.getId() === id); + if (filtered.length === 1) { filtered[0].name = newName; + this._emitter.emit(WATCH_EXPRESSIONS_CHANGED, filtered[0]); } } - removeWatchExpressions(id: ?string): void { - this._watchExpressions = - id != null ? this._watchExpressions.filter(we => we.getId() !== id) : []; + removeWatchExpressions(id) { + this._watchExpressions = id != null ? this._watchExpressions.filter(we => we.getId() !== id) : []; + this._emitter.emit(WATCH_EXPRESSIONS_CHANGED); } - sourceIsNotAvailable(uri: string): void { + sourceIsNotAvailable(uri) { this._processes.forEach(p => { if (p.sources.has(uri)) { - nullthrows(p.sources.get(uri)).available = false; + (0, _nullthrows().default)(p.sources.get(uri)).available = false; } }); + this._emitter.emit(CALLSTACK_CHANGED); } - dispose(): void { + dispose() { this._disposables.dispose(); } + } + +exports.Model = Model; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/__atom_tests__/DefinitionHyperclick-test.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/__atom_tests__/DefinitionHyperclick-test.js index e9671ee9..191d0fc2 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/__atom_tests__/DefinitionHyperclick-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/__atom_tests__/DefinitionHyperclick-test.js @@ -1,3 +1,19 @@ +"use strict"; + +var _atom = require("atom"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,163 +22,160 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - HyperclickProvider, - HyperclickSuggestion, -} from '../../hyperclick/lib/types'; -import type {DefinitionProvider} from '../lib/types'; - -import {Point, Range, TextEditor} from 'atom'; -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - // Package activation is not supported yet describe.skip('DefinitionHyperclick', () => { - let provider: ?HyperclickProvider; - const definitionProvider: DefinitionProvider = { + let provider; + const definitionProvider = { priority: 20, name: '', grammarScopes: ['text.plain.null-grammar'], wordRegExp: null, - getDefinition: () => Promise.resolve(null), + getDefinition: () => Promise.resolve(null) }; - let editor: TextEditor; - const position = new Point(0, 0); + let editor; + const position = new _atom.Point(0, 0); let goToLocation; let disposables; - beforeEach(() => { atom.packages.activatePackage('atom-ide-definitions'); - editor = new TextEditor(); - - goToLocation = jest.spyOn( - require('nuclide-commons-atom/go-to-location'), - 'goToLocation', - ); - - disposables = new UniversalDisposable( - atom.packages.serviceHub.provide( - 'definitions', - '0.1.0', - definitionProvider, - ), - atom.packages.serviceHub.consume('hyperclick', '0.1.0', x => { - provider = x; - }), - ); + editor = new _atom.TextEditor(); + goToLocation = jest.spyOn(require("../../../../nuclide-commons-atom/go-to-location"), 'goToLocation'); + disposables = new (_UniversalDisposable().default)(atom.packages.serviceHub.provide('definitions', '0.1.0', definitionProvider), atom.packages.serviceHub.consume('hyperclick', '0.1.0', x => { + provider = x; + })); }); - afterEach(() => { disposables.dispose(); }); - it('no definition service', async () => { - jest.spyOn(editor, 'getGrammar').mockReturnValue({scopeName: 'blah'}); - invariant(provider != null); - invariant(provider.getSuggestion != null); + jest.spyOn(editor, 'getGrammar').mockReturnValue({ + scopeName: 'blah' + }); + + if (!(provider != null)) { + throw new Error("Invariant violation: \"provider != null\""); + } + + if (!(provider.getSuggestion != null)) { + throw new Error("Invariant violation: \"provider.getSuggestion != null\""); + } + const result = await provider.getSuggestion(editor, position); expect(result).toBe(null); }); - it('no definition', async () => { - const spy = jest - .spyOn(definitionProvider, 'getDefinition') - .mockReturnValue(null); - invariant(provider != null); - invariant(provider.getSuggestion != null); - const result = await provider.getSuggestion(editor, position); + const spy = jest.spyOn(definitionProvider, 'getDefinition').mockReturnValue(null); + + if (!(provider != null)) { + throw new Error("Invariant violation: \"provider != null\""); + } + + if (!(provider.getSuggestion != null)) { + throw new Error("Invariant violation: \"provider.getSuggestion != null\""); + } + const result = await provider.getSuggestion(editor, position); expect(result).toBe(null); expect(spy).toHaveBeenCalledWith(editor, position); }); - it('definition - single', async () => { const definition = { - queryRange: [new Range(new Point(1, 1), new Point(1, 5))], - definitions: [ - { - path: 'path1', - position: new Point(1, 2), - range: null, - id: 'symbol-name', - name: null, - projectRoot: null, - }, - ], + queryRange: [new _atom.Range(new _atom.Point(1, 1), new _atom.Point(1, 5))], + definitions: [{ + path: 'path1', + position: new _atom.Point(1, 2), + range: null, + id: 'symbol-name', + name: null, + projectRoot: null + }] }; - const spy = jest - .spyOn(definitionProvider, 'getDefinition') - .mockReturnValue(Promise.resolve(definition)); + const spy = jest.spyOn(definitionProvider, 'getDefinition').mockReturnValue(Promise.resolve(definition)); + + if (!(provider != null)) { + throw new Error("Invariant violation: \"provider != null\""); + } + + if (!(provider.getSuggestion != null)) { + throw new Error("Invariant violation: \"provider.getSuggestion != null\""); + } - invariant(provider != null); - invariant(provider.getSuggestion != null); const result = await provider.getSuggestion(editor, position); - invariant(result != null); + if (!(result != null)) { + throw new Error("Invariant violation: \"result != null\""); + } + expect(result.range).toEqual(definition.queryRange); expect(spy).toHaveBeenCalledWith(editor, position); expect(goToLocation).not.toHaveBeenCalled(); - invariant(result != null); - invariant(result.callback != null); - invariant(typeof result.callback === 'function'); + if (!(result != null)) { + throw new Error("Invariant violation: \"result != null\""); + } + + if (!(result.callback != null)) { + throw new Error("Invariant violation: \"result.callback != null\""); + } + + if (!(typeof result.callback === 'function')) { + throw new Error("Invariant violation: \"typeof result.callback === 'function'\""); + } + result.callback(); - expect(goToLocation).toHaveBeenCalledWith('path1', {line: 1, column: 2}); + expect(goToLocation).toHaveBeenCalledWith('path1', { + line: 1, + column: 2 + }); }); - it('definition - multiple', async () => { const defs = { - queryRange: [new Range(new Point(1, 1), new Point(1, 5))], - definitions: [ - { - path: '/a/b/path1', - position: new Point(1, 2), - range: null, - id: 'symbol-name', - name: 'd1', - projectRoot: '/a', - }, - { - path: '/a/b/path2', - position: new Point(3, 4), - range: null, - id: 'symbol-name2', - name: 'd2', - projectRoot: '/a', - }, - { - path: '/a/b/path3', - position: new Point(3, 4), - range: null, - id: 'symbol-without-name', - projectRoot: '/a', - }, - ], + queryRange: [new _atom.Range(new _atom.Point(1, 1), new _atom.Point(1, 5))], + definitions: [{ + path: '/a/b/path1', + position: new _atom.Point(1, 2), + range: null, + id: 'symbol-name', + name: 'd1', + projectRoot: '/a' + }, { + path: '/a/b/path2', + position: new _atom.Point(3, 4), + range: null, + id: 'symbol-name2', + name: 'd2', + projectRoot: '/a' + }, { + path: '/a/b/path3', + position: new _atom.Point(3, 4), + range: null, + id: 'symbol-without-name', + projectRoot: '/a' + }] }; - const spy = jest - .spyOn(definitionProvider, 'getDefinition') - .mockReturnValue(Promise.resolve(defs)); - - invariant(provider != null); - invariant(provider.getSuggestion != null); - const result: ?HyperclickSuggestion = await provider.getSuggestion( - editor, - position, - ); - - invariant(result != null); + const spy = jest.spyOn(definitionProvider, 'getDefinition').mockReturnValue(Promise.resolve(defs)); + + if (!(provider != null)) { + throw new Error("Invariant violation: \"provider != null\""); + } + + if (!(provider.getSuggestion != null)) { + throw new Error("Invariant violation: \"provider.getSuggestion != null\""); + } + + const result = await provider.getSuggestion(editor, position); + + if (!(result != null)) { + throw new Error("Invariant violation: \"result != null\""); + } + expect(result.range).toEqual(defs.queryRange); expect(spy).toHaveBeenCalledWith(editor, position); expect(goToLocation).not.toHaveBeenCalled(); - const callbacks: Array<{ - title: string, - callback: () => mixed, - }> = (result.callback: any); - + const callbacks = result.callback; expect(callbacks.length).toBe(3); expect(callbacks[0].title).toBe('d1 (b/path1)'); expect(typeof callbacks[0].callback).toBe('function'); @@ -170,74 +183,93 @@ describe.skip('DefinitionHyperclick', () => { expect(typeof callbacks[1].callback).toBe('function'); expect(callbacks[2].title).toBe('b/path3:4'); expect(typeof callbacks[2].callback).toBe('function'); - callbacks[1].callback(); expect(goToLocation).toHaveBeenCalledWith('/a/b/path2', { line: 3, - column: 4, + column: 4 }); }); - it('falls back to lower-priority providers', async () => { const def = { - queryRange: [new Range(new Point(1, 1), new Point(1, 5))], - definitions: [ - { - path: 'path1', - position: new Point(1, 2), - range: null, - id: 'symbol-name', - name: null, - projectRoot: null, - }, - ], + queryRange: [new _atom.Range(new _atom.Point(1, 1), new _atom.Point(1, 5))], + definitions: [{ + path: 'path1', + position: new _atom.Point(1, 2), + range: null, + id: 'symbol-name', + name: null, + projectRoot: null + }] }; const newProvider = { priority: 10, name: '', grammarScopes: ['text.plain.null-grammar'], - getDefinition: () => Promise.resolve(def), + getDefinition: () => Promise.resolve(def) }; atom.packages.serviceHub.provide('definitions', '0.1.0', newProvider); - invariant(provider != null); - invariant(provider.getSuggestion != null); + + if (!(provider != null)) { + throw new Error("Invariant violation: \"provider != null\""); + } + + if (!(provider.getSuggestion != null)) { + throw new Error("Invariant violation: \"provider.getSuggestion != null\""); + } + const result = await provider.getSuggestion(editor, position); expect(result).not.toBe(null); - invariant(result != null); + + if (!(result != null)) { + throw new Error("Invariant violation: \"result != null\""); + } + expect(result.range).toEqual(def.queryRange); }); - it('does not cache null values', async () => { editor.setText('test'); const def = { - queryRange: [new Range(new Point(1, 1), new Point(1, 5))], - definitions: [ - { - path: 'path1', - position: new Point(1, 2), - range: null, - id: 'symbol-name', - name: null, - projectRoot: null, - }, - ], + queryRange: [new _atom.Range(new _atom.Point(1, 1), new _atom.Point(1, 5))], + definitions: [{ + path: 'path1', + position: new _atom.Point(1, 2), + range: null, + id: 'symbol-name', + name: null, + projectRoot: null + }] }; const newProvider = { priority: 10, name: '', grammarScopes: ['text.plain.null-grammar'], - getDefinition: () => Promise.resolve(null), + getDefinition: () => Promise.resolve(null) }; atom.packages.serviceHub.provide('definitions', '0.1.0', newProvider); - invariant(provider != null); - invariant(provider.getSuggestion != null); + + if (!(provider != null)) { + throw new Error("Invariant violation: \"provider != null\""); + } + + if (!(provider.getSuggestion != null)) { + throw new Error("Invariant violation: \"provider.getSuggestion != null\""); + } + let result = await provider.getSuggestion(editor, position); expect(result).toBe(null); newProvider.getDefinition = () => Promise.resolve(def); - invariant(provider.getSuggestion != null); + + if (!(provider.getSuggestion != null)) { + throw new Error("Invariant violation: \"provider.getSuggestion != null\""); + } + result = await provider.getSuggestion(editor, position); - invariant(result != null); + + if (!(result != null)) { + throw new Error("Invariant violation: \"result != null\""); + } + expect(result.range).toEqual(def.queryRange); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/DefinitionCache.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/DefinitionCache.js index 1107537e..24cb4ba4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/DefinitionCache.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/DefinitionCache.js @@ -1,3 +1,42 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _range() { + const data = require("../../../../nuclide-commons-atom/range"); + + _range = function () { + return data; + }; + + return data; +} + +function _range2() { + const data = require("../../../../nuclide-commons/range"); + + _range2 = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,52 +45,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {DefinitionQueryResult} from './types'; -import {wordAtPosition} from 'nuclide-commons-atom/range'; -import {isPositionInRange} from 'nuclide-commons/range'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - // An atom$Range-aware, single-item cache for the common case of requerying // a definition (such as previewing hyperclick and then jumping to the // destination). It invalidates whenever the originating editor changes. class DefinitionCache { - _cachedResultEditor: ?atom$TextEditor; - _cachedResultPromise: ?Promise; - _cachedResultRange: ?atom$Range; - _disposables: UniversalDisposable = new UniversalDisposable(); + constructor() { + this._disposables = new (_UniversalDisposable().default)(); + } dispose() { this._disposables.dispose(); } - getCached( - editor: atom$TextEditor, - position: atom$Point, - ): ?Promise { - if ( - this._cachedResultRange != null && - this._cachedResultEditor === editor && - isPositionInRange(position, this._cachedResultRange) - ) { + getCached(editor, position) { + if (this._cachedResultRange != null && this._cachedResultEditor === editor && (0, _range2().isPositionInRange)(position, this._cachedResultRange)) { return this._cachedResultPromise; } } - async get( - editor: atom$TextEditor, - position: atom$Point, - getImpl: () => Promise, - ): Promise { + async get(editor, position, getImpl) { const cached = this.getCached(editor, position); + if (cached != null) { return cached; - } + } // invalidate whenever the buffer changes + - // invalidate whenever the buffer changes const invalidateAndStopListening = () => { // Make sure we don't invalidate a newer cache result. if (this._cachedResultPromise === promise) { @@ -59,16 +81,17 @@ class DefinitionCache { this._cachedResultRange = null; this._cachedResultPromise = null; } + this._disposables.remove(editorDisposables); + editorDisposables.dispose(); }; - const editorDisposables = new UniversalDisposable( - editor.getBuffer().onDidChangeText(invalidateAndStopListening), - editor.onDidDestroy(invalidateAndStopListening), - ); + + const editorDisposables = new (_UniversalDisposable().default)(editor.getBuffer().onDidChangeText(invalidateAndStopListening), editor.onDidDestroy(invalidateAndStopListening)); + this._disposables.add(editorDisposables); - const wordGuess = wordAtPosition(editor, position); + const wordGuess = (0, _range().wordAtPosition)(editor, position); this._cachedResultRange = wordGuess && wordGuess.range; this._cachedResultEditor = editor; const promise = getImpl().then(result => { @@ -77,12 +100,14 @@ class DefinitionCache { if (result == null) { invalidateAndStopListening(); } + return result; }); this._cachedResultPromise = promise; - return this._cachedResultPromise; } + } -export default DefinitionCache; +var _default = DefinitionCache; +exports.default = _default; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/getPreviewDatatipFromDefinitionResult.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/getPreviewDatatipFromDefinitionResult.js index b8a9be9b..2a0b6d1c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/getPreviewDatatipFromDefinitionResult.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/getPreviewDatatipFromDefinitionResult.js @@ -1,3 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _analytics() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _symbolDefinitionPreview() { + const data = require("../../../../nuclide-commons/symbol-definition-preview"); + + _symbolDefinitionPreview = function () { + return data; + }; + + return data; +} + +var _react = _interopRequireDefault(require("react")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,92 +37,64 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import analytics from 'nuclide-commons/analytics'; -import {getDefinitionPreview as getLocalFileDefinitionPreview} from 'nuclide-commons/symbol-definition-preview'; - -import React from 'react'; -import type {Datatip} from '../../atom-ide-datatip/lib/types'; - -import type {Definition, DefinitionPreviewProvider} from './types'; - -export default (async function getPreviewDatatipFromDefinition( - range: atom$Range, - definitions: Array, - definitionPreviewProvider: ?DefinitionPreviewProvider, - grammar: atom$Grammar, -): Promise { +var getPreviewDatatipFromDefinition = async function getPreviewDatatipFromDefinition(range, definitions, definitionPreviewProvider, grammar) { if (definitions.length === 1) { - const definition = definitions[0]; - // Some providers (e.g. Flow) return negative positions. + const definition = definitions[0]; // Some providers (e.g. Flow) return negative positions. + if (definition.position.row < 0) { return null; } - const definitionPreview = await getPreview( - definition, - definitionPreviewProvider, - ); + const definitionPreview = await getPreview(definition, definitionPreviewProvider); if (definitionPreview == null) { return null; - } - - // if mimetype is image return image component with base-64 encoded + } // if mimetype is image return image component with base-64 encoded // image contents, otherwise use markedStrings + + if (definitionPreview.mime.startsWith('image/')) { return { - component: () => ( - - ), - range, + component: () => _react.default.createElement("img", { + src: `data:${definitionPreview.mime};${definitionPreview.encoding},${definitionPreview.contents}` + }), + range }; } + return { - markedStrings: [ - { - type: 'snippet', - value: definitionPreview.contents, - grammar, - }, - ], - range, + markedStrings: [{ + type: 'snippet', + value: definitionPreview.contents, + grammar + }], + range }; } return { - markedStrings: [ - { - type: 'markdown', - value: `${definitions.length} definitions found. Click to jump.`, - grammar, - }, - ], - range, + markedStrings: [{ + type: 'markdown', + value: `${definitions.length} definitions found. Click to jump.`, + grammar + }], + range }; -}); +}; -async function getPreview( - definition: Definition, - definitionPreviewProvider: ?DefinitionPreviewProvider, -) { +exports.default = getPreviewDatatipFromDefinition; + +async function getPreview(definition, definitionPreviewProvider) { let getDefinitionPreviewFn; + if (definitionPreviewProvider == null) { - getDefinitionPreviewFn = getLocalFileDefinitionPreview; + getDefinitionPreviewFn = _symbolDefinitionPreview().getDefinitionPreview; } else { - getDefinitionPreviewFn = definitionPreviewProvider.getDefinitionPreview.bind( - definitionPreviewProvider, - ); + getDefinitionPreviewFn = definitionPreviewProvider.getDefinitionPreview.bind(definitionPreviewProvider); } - return analytics.trackTiming('hyperclickPreview.getDefinitionPreview', () => - getDefinitionPreviewFn(definition), - ); -} + return _analytics().default.trackTiming('hyperclickPreview.getDefinitionPreview', () => getDefinitionPreviewFn(definition)); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/main.js index b865a175..89da180d 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/main.js @@ -1,3 +1,129 @@ +"use strict"; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _analytics() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _range() { + const data = require("../../../../nuclide-commons-atom/range"); + + _range = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _performanceNow() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/performanceNow")); + + _performanceNow = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _DefinitionCache() { + const data = _interopRequireDefault(require("./DefinitionCache")); + + _DefinitionCache = function () { + return data; + }; + + return data; +} + +function _getPreviewDatatipFromDefinitionResult() { + const data = _interopRequireDefault(require("./getPreviewDatatipFromDefinitionResult")); + + _getPreviewDatatipFromDefinitionResult = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,178 +132,117 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - // This package provides Hyperclick results for any language which provides a // DefinitionProvider. - -import type { - HyperclickProvider, - HyperclickSuggestion, -} from '../../hyperclick/lib/types'; - -import type { - Datatip, - DatatipService, - ModifierDatatipProvider, - ModifierKey, -} from '../../atom-ide-datatip/lib/types'; - -import type { - DefinitionQueryResult, - DefinitionProvider, - DefinitionPreviewProvider, -} from './types'; - -import invariant from 'assert'; -import {getLogger} from 'log4js'; -import {Range} from 'atom'; - -import analytics from 'nuclide-commons/analytics'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import FeatureConfig from 'nuclide-commons-atom/feature-config'; -import {wordAtPosition} from 'nuclide-commons-atom/range'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import performanceNow from 'nuclide-commons/performanceNow'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; - -import DefinitionCache from './DefinitionCache'; -import getPreviewDatatipFromDefinitionResult from './getPreviewDatatipFromDefinitionResult'; - const TRACK_TIMING_SAMPLE_RATIO = 0.1; class Activation { - _providers: ProviderRegistry; - _definitionPreviewProvider: ?DefinitionPreviewProvider; - _definitionCache: DefinitionCache; - _disposables: UniversalDisposable; - _triggerKeys: Set; - constructor() { - this._providers = new ProviderRegistry(); - this._definitionCache = new DefinitionCache(); + this._providers = new (_ProviderRegistry().default)(); + this._definitionCache = new (_DefinitionCache().default)(); this._triggerKeys = new Set(); - - this._disposables = new UniversalDisposable( - FeatureConfig.observe( - getPlatformKeys(process.platform), - (newValue: ?string) => { - this._triggerKeys = (new Set( - // flowlint-next-line sketchy-null-string:off - newValue ? newValue.split(',') : null, - ): Set); - }, - ), - ); + this._disposables = new (_UniversalDisposable().default)(_featureConfig().default.observe(getPlatformKeys(process.platform), newValue => { + this._triggerKeys = new Set( // flowlint-next-line sketchy-null-string:off + newValue ? newValue.split(',') : null); + })); } dispose() { this._disposables.dispose(); } - async _getDefinition( - editor: atom$TextEditor, - position: atom$Point, - ): Promise { + async _getDefinition(editor, position) { for (const provider of this._providers.getAllProvidersForEditor(editor)) { try { // eslint-disable-next-line no-await-in-loop const result = await provider.getDefinition(editor, position); + if (result != null) { if (result.queryRange == null) { - const match = wordAtPosition( - editor, - position, - provider.wordRegExp != null - ? provider.wordRegExp - : { - includeNonWordCharacters: false, - }, - ); - result.queryRange = [ - match != null ? match.range : new Range(position, position), - ]; + const match = (0, _range().wordAtPosition)(editor, position, provider.wordRegExp != null ? provider.wordRegExp : { + includeNonWordCharacters: false + }); + result.queryRange = [match != null ? match.range : new _atom.Range(position, position)]; } + return result; } } catch (err) { - getLogger('atom-ide-definitions').error( - `Error getting definition for ${String(editor.getPath())}`, - err, - ); + (0, _log4js().getLogger)('atom-ide-definitions').error(`Error getting definition for ${String(editor.getPath())}`, err); } } + return null; } - _getDefinitionCached( - editor: atom$TextEditor, - position: atom$Point, - ): Promise { + _getDefinitionCached(editor, position) { return this._definitionCache.get(editor, position, () => { - return analytics.trackTimingSampled( - 'get-definition', - () => this._getDefinition(editor, position), - TRACK_TIMING_SAMPLE_RATIO, - {path: editor.getPath()}, - ); + return _analytics().default.trackTimingSampled('get-definition', () => this._getDefinition(editor, position), TRACK_TIMING_SAMPLE_RATIO, { + path: editor.getPath() + }); }); } - async getSuggestion( - editor: atom$TextEditor, - position: atom$Point, - ): Promise { - const startTime = performanceNow(); + async getSuggestion(editor, position) { + const startTime = (0, _performanceNow().default)(); const result = await this._getDefinitionCached(editor, position); - const duration = performanceNow() - startTime; + const duration = (0, _performanceNow().default)() - startTime; + if (result == null) { return null; } - const {queryRange, definitions} = result; - invariant(definitions.length > 0); - // queryRange might be null coming out of the provider, but the output + const { + queryRange, + definitions + } = result; + + if (!(definitions.length > 0)) { + throw new Error("Invariant violation: \"definitions.length > 0\""); + } // queryRange might be null coming out of the provider, but the output // of _getDefinition has ensured it's not null. - invariant(queryRange != null); + + + if (!(queryRange != null)) { + throw new Error("Invariant violation: \"queryRange != null\""); + } function createCallback(definition) { return () => { - goToLocation(definition.path, { + (0, _goToLocation().goToLocation)(definition.path, { line: definition.position.row, - column: definition.position.column, + column: definition.position.column }); - analytics.track('go-to-definition', { + + _analytics().default.track('go-to-definition', { path: definition.path, line: definition.position.row, column: definition.position.column, from: editor.getPath(), name: definition.name, - duration, + duration }); }; } function createTitle(definition) { - const filePath = - definition.projectRoot == null - ? definition.path - : nuclideUri.relative(definition.projectRoot, definition.path); + const filePath = definition.projectRoot == null ? definition.path : _nuclideUri().default.relative(definition.projectRoot, definition.path); + if (definition.name == null) { // Fall back to just displaying the path:line. return `${filePath}:${definition.position.row + 1}`; } + return `${definition.name} (${filePath})`; } if (definitions.length === 1) { return { range: queryRange, - callback: createCallback(definitions[0]), + callback: createCallback(definitions[0]) }; } else { return { @@ -185,91 +250,82 @@ class Activation { callback: definitions.map(definition => { return { title: createTitle(definition), - callback: createCallback(definition), + callback: createCallback(definition) }; - }), + }) }; } } - async getPreview( - editor: atom$TextEditor, - position: atom$Point, - heldKeys: Set, - ): Promise { - if ( - !this._triggerKeys || - // are the required keys held down? - !Array.from(this._triggerKeys).every(key => heldKeys.has(key)) - ) { + async getPreview(editor, position, heldKeys) { + if (!this._triggerKeys || // are the required keys held down? + !Array.from(this._triggerKeys).every(key => heldKeys.has(key))) { return; - } - - // Datatips are debounced, so this request should always come in after the getDefinition request. + } // Datatips are debounced, so this request should always come in after the getDefinition request. // Thus we should always be able to rely on the value being in the cache. // If it's not in the cache, this implies that a newer getDefinition request came in, // in which case the result of this function will be ignored anyway. + + const result = await this._definitionCache.getCached(editor, position); + if (result == null) { return null; } - const queryRange = result.queryRange; - // queryRange might be null coming out of the provider, but the output + + const queryRange = result.queryRange; // queryRange might be null coming out of the provider, but the output // of _getDefinition has ensured it's not null. - invariant(queryRange != null); + + if (!(queryRange != null)) { + throw new Error("Invariant violation: \"queryRange != null\""); + } const grammar = editor.getGrammar(); - const previewDatatip = getPreviewDatatipFromDefinitionResult( - queryRange[0], - result.definitions, - this._definitionPreviewProvider, - grammar, - ); - - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + const previewDatatip = (0, _getPreviewDatatipFromDefinitionResult().default)(queryRange[0], result.definitions, this._definitionPreviewProvider, grammar); // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + if (previewDatatip != null && previewDatatip.markedStrings) { - analytics.track('hyperclick-preview-popup', { + _analytics().default.track('hyperclick-preview-popup', { grammar: grammar.name, - definitionCount: result.definitions.length, + definitionCount: result.definitions.length }); } return previewDatatip; } - consumeDefinitionProvider(provider: DefinitionProvider): IDisposable { + consumeDefinitionProvider(provider) { const disposable = this._providers.addProvider(provider); + this._disposables.add(disposable); + return disposable; } - consumeDefinitionPreviewProvider(provider: DefinitionPreviewProvider) { + consumeDefinitionPreviewProvider(provider) { this._definitionPreviewProvider = provider; } - consumeDatatipService(service: DatatipService): IDisposable { - const datatipProvider: ModifierDatatipProvider = { + consumeDatatipService(service) { + const datatipProvider = { providerName: 'hyperclick-preview', priority: 1, - modifierDatatip: ( - editor: atom$TextEditor, - bufferPosition: atom$Point, - heldKeys: Set, - ) => this.getPreview(editor, bufferPosition, heldKeys), + modifierDatatip: (editor, bufferPosition, heldKeys) => this.getPreview(editor, bufferPosition, heldKeys) }; - const disposable = service.addModifierProvider(datatipProvider); + this._disposables.add(disposable); + return disposable; } - getHyperclickProvider(): HyperclickProvider { + getHyperclickProvider() { return { priority: 20, providerName: 'atom-ide-definitions', - getSuggestion: (editor, position) => this.getSuggestion(editor, position), + getSuggestion: (editor, position) => this.getSuggestion(editor, position) }; } + } function getPlatformKeys(platform) { @@ -278,7 +334,8 @@ function getPlatformKeys(platform) { } else if (platform === 'win32') { return 'hyperclick.win32TriggerKeys'; } + return 'hyperclick.linuxTriggerKeys'; } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/types.js index bb742f4e..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/types.js @@ -1,65 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -export type Definition = { - // Path of the file in which the definition is located. - path: NuclideUri, - // First character of the definition's identifier. - // e.g. "F" in `class Foo {}` - position: atom$Point, - // Optional: the range of the entire definition. - // e.g. "c" to "}" in `class Foo {}` - range?: atom$Range, - // Optional: `name` and `projectRoot` can be provided to display a more human-readable title - // inside of Hyperclick when there are multiple definitions. - name?: string, - // If provided, `projectRoot` will be used to display a relativized version of `path`. - projectRoot?: NuclideUri, - // `language` may be used by consumers to identify the source of definitions. - language: string, -}; - -// Definition queries supply a point. -// The returned queryRange is the range within which the returned definition is valid. -// Typically queryRange spans the containing identifier around the query point. -// (If a null queryRange is returned, the range of the word containing the point is used.) -export type DefinitionQueryResult = { - queryRange: ?Array, - definitions: Array, // Must be non-empty. -}; - -// Provides definitions for a set of language grammars. -export type DefinitionProvider = { - // If there are multiple providers for a given grammar, - // the one with the highest priority will be used. - priority: number, - grammarScopes: Array, - wordRegExp: ?RegExp, - // Obtains the definition in an editor at the given point. - // This should return null if no definition is available. - getDefinition: ( - editor: TextEditor, - position: atom$Point, - ) => Promise, -}; - -export type DefinitionPreviewProvider = { - getDefinitionPreview( - definition: Definition, - ): Promise, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/__atom_tests__/DiagnosticsView-test.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/__atom_tests__/DiagnosticsView-test.js index 3ddaf730..db9e9942 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/__atom_tests__/DiagnosticsView-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/__atom_tests__/DiagnosticsView-test.js @@ -1,3 +1,33 @@ +"use strict"; + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _testUtils() { + const data = _interopRequireDefault(require("react-dom/test-utils")); + + _testUtils = function () { + return data; + }; + + return data; +} + +function _DiagnosticsView() { + const data = _interopRequireDefault(require("../lib/ui/DiagnosticsView")); + + _DiagnosticsView = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,56 +36,59 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import * as React from 'react'; -import ReactDom from 'react-dom'; -import TestUtils from 'react-dom/test-utils'; -import DiagnosticsView from '../lib/ui/DiagnosticsView'; - describe('DiagnosticsView', () => { it('focuses the filter when "/" is pressed', () => { - const diagnosticsView: DiagnosticsView = (TestUtils.renderIntoDocument( - {}} - hiddenGroups={new Set()} - onFilterByActiveTextEditorChange={() => {}} - isVisible={true} - onShowTracesChange={() => {}} - onTextFilterChange={() => {}} - onTypeFilterChange={() => {}} - selectMessage={() => {}} - selectedMessage={null} - showDirectoryColumn={false} - showTraces={true} - supportedMessageKinds={new Set()} - textFilter={{text: 'test', isRegExp: true, invalid: false}} - uiConfig={[]} - />, - ): any); + const diagnosticsView = _testUtils().default.renderIntoDocument(React.createElement(_DiagnosticsView().default, { + autoVisibility: true, + diagnostics: [], + filterByActiveTextEditor: false, + gotoMessageLocation: () => {}, + hiddenGroups: new Set(), + onFilterByActiveTextEditorChange: () => {}, + isVisible: true, + onShowTracesChange: () => {}, + onTextFilterChange: () => {}, + onTypeFilterChange: () => {}, + selectMessage: () => {}, + selectedMessage: null, + showDirectoryColumn: false, + showTraces: true, + supportedMessageKinds: new Set(), + textFilter: { + text: 'test', + isRegExp: true, + invalid: false + }, + uiConfig: [] + })); const workspaceEl = atom.views.getView(atom.workspace); - const diagnosticsViewNode = ReactDom.findDOMNode(diagnosticsView); - invariant(diagnosticsViewNode != null); - workspaceEl.appendChild(diagnosticsViewNode); + const diagnosticsViewNode = _reactDom.default.findDOMNode(diagnosticsView); + + if (!(diagnosticsViewNode != null)) { + throw new Error("Invariant violation: \"diagnosticsViewNode != null\""); + } + + workspaceEl.appendChild(diagnosticsViewNode); const filterComponent = diagnosticsView._filterComponent; - invariant(filterComponent != null); + + if (!(filterComponent != null)) { + throw new Error("Invariant violation: \"filterComponent != null\""); + } + const filterFocusSpy = jest.spyOn(filterComponent, 'focus'); + const diagnosticsTableTarget = workspaceEl.querySelector('.atom-ide-filterable'); - const diagnosticsTableTarget = workspaceEl.querySelector( - '.atom-ide-filterable', - ); - invariant(diagnosticsTableTarget != null); - atom.commands.dispatch(diagnosticsTableTarget, 'atom-ide:filter'); + if (!(diagnosticsTableTarget != null)) { + throw new Error("Invariant violation: \"diagnosticsTableTarget != null\""); + } + atom.commands.dispatch(diagnosticsTableTarget, 'atom-ide:filter'); expect(filterFocusSpy).toHaveBeenCalled(); workspaceEl.removeChild(diagnosticsViewNode); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/__tests__/DiagnosticMessageTest-test.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/__tests__/DiagnosticMessageTest-test.js index d6bbf7f3..74df6b96 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/__tests__/DiagnosticMessageTest-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/__tests__/DiagnosticMessageTest-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _DiagnosticsMessageText() { + const data = require("../lib/ui/DiagnosticsMessageText"); + + _DiagnosticsMessageText = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,97 +18,116 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {separateUrls} from '../lib/ui/DiagnosticsMessageText'; - describe('DiagnosticsMessageText', () => { it('should leave text unchanged', () => { - expect(separateUrls('hello')).toEqual([{isUrl: false, text: 'hello'}]); + expect((0, _DiagnosticsMessageText().separateUrls)('hello')).toEqual([{ + isUrl: false, + text: 'hello' + }]); }); - it('should handle a lone URL', () => { - expect(separateUrls('http://example.com')).toEqual([ - {isUrl: false, text: ''}, - {isUrl: true, url: 'http://example.com'}, - {isUrl: false, text: ''}, - ]); + expect((0, _DiagnosticsMessageText().separateUrls)('http://example.com')).toEqual([{ + isUrl: false, + text: '' + }, { + isUrl: true, + url: 'http://example.com' + }, { + isUrl: false, + text: '' + }]); }); - it('should separate URLs', () => { - expect( - separateUrls( - 'foo https://example.com/short-link bar https://example.com/abc_def0 baz', - ), - ).toEqual([ - {isUrl: false, text: 'foo '}, - {isUrl: true, url: 'https://example.com/short-link'}, - {isUrl: false, text: ' bar '}, - {isUrl: true, url: 'https://example.com/abc_def0'}, - {isUrl: false, text: ' baz'}, - ]); + expect((0, _DiagnosticsMessageText().separateUrls)('foo https://example.com/short-link bar https://example.com/abc_def0 baz')).toEqual([{ + isUrl: false, + text: 'foo ' + }, { + isUrl: true, + url: 'https://example.com/short-link' + }, { + isUrl: false, + text: ' bar ' + }, { + isUrl: true, + url: 'https://example.com/abc_def0' + }, { + isUrl: false, + text: ' baz' + }]); }); - it('should handle URLs at the beginning', () => { - expect(separateUrls('https://example.com/123 end')).toEqual([ - {isUrl: false, text: ''}, - {isUrl: true, url: 'https://example.com/123'}, - {isUrl: false, text: ' end'}, - ]); + expect((0, _DiagnosticsMessageText().separateUrls)('https://example.com/123 end')).toEqual([{ + isUrl: false, + text: '' + }, { + isUrl: true, + url: 'https://example.com/123' + }, { + isUrl: false, + text: ' end' + }]); }); - it('should handle URLs at the end', () => { - expect(separateUrls('beginning https://example.com/foo.html')).toEqual([ - {isUrl: false, text: 'beginning '}, - {isUrl: true, url: 'https://example.com/foo.html'}, - {isUrl: false, text: ''}, - ]); + expect((0, _DiagnosticsMessageText().separateUrls)('beginning https://example.com/foo.html')).toEqual([{ + isUrl: false, + text: 'beginning ' + }, { + isUrl: true, + url: 'https://example.com/foo.html' + }, { + isUrl: false, + text: '' + }]); }); - it('should not include trailing periods in URLs', () => { - expect(separateUrls('hello https://example.com/short-link.')).toEqual([ - {isUrl: false, text: 'hello '}, - {isUrl: true, url: 'https://example.com/short-link'}, - {isUrl: false, text: '.'}, - ]); + expect((0, _DiagnosticsMessageText().separateUrls)('hello https://example.com/short-link.')).toEqual([{ + isUrl: false, + text: 'hello ' + }, { + isUrl: true, + url: 'https://example.com/short-link' + }, { + isUrl: false, + text: '.' + }]); }); - it('should handle URLs that include escaped characters', () => { - expect( - separateUrls('https://example.com/give%20me%20some%20space/'), - ).toEqual([ - {isUrl: false, text: ''}, - {isUrl: true, url: 'https://example.com/give%20me%20some%20space/'}, - {isUrl: false, text: ''}, - ]); + expect((0, _DiagnosticsMessageText().separateUrls)('https://example.com/give%20me%20some%20space/')).toEqual([{ + isUrl: false, + text: '' + }, { + isUrl: true, + url: 'https://example.com/give%20me%20some%20space/' + }, { + isUrl: false, + text: '' + }]); }); - it('should handle URLs that include a query', () => { - expect( - separateUrls( - 'help: https://example.com/foo.html?q=%20+!&-._~:@/?-hmm. (weird url huh?)', - ), - ).toEqual([ - {isUrl: false, text: 'help: '}, - {isUrl: true, url: 'https://example.com/foo.html?q=%20+!&-._~:@/?-hmm'}, - {isUrl: false, text: '. (weird url huh?)'}, - ]); + expect((0, _DiagnosticsMessageText().separateUrls)('help: https://example.com/foo.html?q=%20+!&-._~:@/?-hmm. (weird url huh?)')).toEqual([{ + isUrl: false, + text: 'help: ' + }, { + isUrl: true, + url: 'https://example.com/foo.html?q=%20+!&-._~:@/?-hmm' + }, { + isUrl: false, + text: '. (weird url huh?)' + }]); }); - it('should handle URLs that include a fragment', () => { - expect( - separateUrls( - 'help: https://example.com/foo.html#frag%20+!&=-._~:@/?-name. (weird url huh?)', - ), - ).toEqual([ - {isUrl: false, text: 'help: '}, - { - isUrl: true, - url: 'https://example.com/foo.html#frag%20+!&=-._~:@/?-name', - }, - {isUrl: false, text: '. (weird url huh?)'}, - ]); + expect((0, _DiagnosticsMessageText().separateUrls)('help: https://example.com/foo.html#frag%20+!&=-._~:@/?-name. (weird url huh?)')).toEqual([{ + isUrl: false, + text: 'help: ' + }, { + isUrl: true, + url: 'https://example.com/foo.html#frag%20+!&=-._~:@/?-name' + }, { + isUrl: false, + text: '. (weird url huh?)' + }]); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/DiagnosticsViewModel.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/DiagnosticsViewModel.js index 6b0a0686..b64f522e 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/DiagnosticsViewModel.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/DiagnosticsViewModel.js @@ -1,3 +1,168 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsViewModel = exports.WORKSPACE_VIEW_URI = void 0; + +function _dockForLocation() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/dock-for-location")); + + _dockForLocation = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _memoizeUntilChanged() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/memoizeUntilChanged")); + + _memoizeUntilChanged = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _observePaneItemVisibility() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/observePaneItemVisibility")); + + _observePaneItemVisibility = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _react = _interopRequireDefault(require("react")); + +function _analytics() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _Model() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/Model")); + + _Model = function () { + return data; + }; + + return data; +} + +function _renderReactRoot() { + const data = require("../../../../nuclide-commons-ui/renderReactRoot"); + + _renderReactRoot = function () { + return data; + }; + + return data; +} + +function _bindObservableAsProps() { + const data = require("../../../../nuclide-commons-ui/bindObservableAsProps"); + + _bindObservableAsProps = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _RegExpFilter() { + const data = require("../../../../nuclide-commons-ui/RegExpFilter"); + + _RegExpFilter = function () { + return data; + }; + + return data; +} + +function GroupUtils() { + const data = _interopRequireWildcard(require("./GroupUtils")); + + GroupUtils = function () { + return data; + }; + + return data; +} + +function _DiagnosticsView() { + const data = _interopRequireDefault(require("./ui/DiagnosticsView")); + + _DiagnosticsView = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,137 +171,64 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const WORKSPACE_VIEW_URI = 'atom://nuclide/diagnostics'; +exports.WORKSPACE_VIEW_URI = WORKSPACE_VIEW_URI; -import type {IconName} from 'nuclide-commons-ui/Icon'; -import type {Props} from './ui/DiagnosticsView'; -import type {DiagnosticGroup, GlobalViewState} from './types'; -import type {DiagnosticMessage} from '../../atom-ide-diagnostics/lib/types'; -import type {RegExpFilterChange} from 'nuclide-commons-ui/RegExpFilter'; - -import dockForLocation from 'nuclide-commons-atom/dock-for-location'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import memoizeUntilChanged from 'nuclide-commons/memoizeUntilChanged'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import observePaneItemVisibility from 'nuclide-commons-atom/observePaneItemVisibility'; -import {arrayEqual, areSetsEqual} from 'nuclide-commons/collection'; -import {fastDebounce} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import React from 'react'; -import analytics from 'nuclide-commons/analytics'; -import Model from 'nuclide-commons/Model'; -import {renderReactRoot} from 'nuclide-commons-ui/renderReactRoot'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {Observable} from 'rxjs'; -import {getFilterPattern} from 'nuclide-commons-ui/RegExpFilter'; -import * as GroupUtils from './GroupUtils'; -import DiagnosticsView from './ui/DiagnosticsView'; - -type SerializedDiagnosticsViewModel = { - deserializer: 'atom-ide-ui.DiagnosticsViewModel', - state: { - hiddenGroups: Array, - }, -}; +class DiagnosticsViewModel { + constructor(globalStates) { + _initialiseProps.call(this); -type State = {| - hiddenGroups: Set, - selectedMessage: ?DiagnosticMessage, - textFilter: {| - text: string, - isRegExp: boolean, - invalid: boolean, - pattern: ?RegExp, - |}, -|}; - -export const WORKSPACE_VIEW_URI = 'atom://nuclide/diagnostics'; - -export class DiagnosticsViewModel { - _element: ?HTMLElement; - _model: Model; - _props: Observable; - _disposables: IDisposable; - - constructor(globalStates: Observable) { // Memoize `_filterDiagnostics()` - (this: any)._filterDiagnostics = memoizeUntilChanged( - this._filterDiagnostics, - (diagnostics, pattern, hiddenGroups, filterPath) => ({ - diagnostics, - pattern, - hiddenGroups, - filterPath, - }), - (a, b) => - patternsAreEqual(a.pattern, b.pattern) && - areSetsEqual(a.hiddenGroups, b.hiddenGroups) && - arrayEqual(a.diagnostics, b.diagnostics) && - a.filterPath === b.filterPath, - ); - - const {pattern, invalid} = getFilterPattern('', false); - this._model = new Model({ + this._filterDiagnostics = (0, _memoizeUntilChanged().default)(this._filterDiagnostics, (diagnostics, pattern, hiddenGroups, filterPath) => ({ + diagnostics, + pattern, + hiddenGroups, + filterPath + }), (a, b) => patternsAreEqual(a.pattern, b.pattern) && (0, _collection().areSetsEqual)(a.hiddenGroups, b.hiddenGroups) && (0, _collection().arrayEqual)(a.diagnostics, b.diagnostics) && a.filterPath === b.filterPath); + const { + pattern, + invalid + } = (0, _RegExpFilter().getFilterPattern)('', false); + this._model = new (_Model().default)({ // TODO: Get this from constructor/serialization. hiddenGroups: new Set(), - textFilter: {text: '', isRegExp: false, pattern, invalid}, - selectedMessage: null, + textFilter: { + text: '', + isRegExp: false, + pattern, + invalid + }, + selectedMessage: null }); - const visibility = observePaneItemVisibility(this).distinctUntilChanged(); - this._disposables = new UniversalDisposable( - visibility - .let(fastDebounce(1000)) - .distinctUntilChanged() - .filter(Boolean) - .subscribe(() => { - analytics.track('diagnostics-show-table'); - }), - ); - - // Combine the state that's shared between instances, the state that's unique to this instance, + const visibility = (0, _observePaneItemVisibility().default)(this).distinctUntilChanged(); + this._disposables = new (_UniversalDisposable().default)(visibility.let((0, _observable().fastDebounce)(1000)).distinctUntilChanged().filter(Boolean).subscribe(() => { + _analytics().default.track('diagnostics-show-table'); + })); // Combine the state that's shared between instances, the state that's unique to this instance, // and unchanging callbacks, to get the props for our component. - const props = Observable.combineLatest( - globalStates, - this._model.toObservable(), - visibility, - (globalState, instanceState, isVisible) => ({ - ...globalState, - ...instanceState, - isVisible, - diagnostics: this._filterDiagnostics( - globalState.diagnostics, - instanceState.textFilter.pattern, - instanceState.hiddenGroups, - globalState.filterByActiveTextEditor - ? globalState.pathToActiveTextEditor - : null, - ), - onTypeFilterChange: this._handleTypeFilterChange, - onTextFilterChange: this._handleTextFilterChange, - selectMessage: this._selectMessage, - gotoMessageLocation: goToDiagnosticLocation, - supportedMessageKinds: globalState.supportedMessageKinds, - }), - ); + + const props = _RxMin.Observable.combineLatest(globalStates, this._model.toObservable(), visibility, (globalState, instanceState, isVisible) => Object.assign({}, globalState, instanceState, { + isVisible, + diagnostics: this._filterDiagnostics(globalState.diagnostics, instanceState.textFilter.pattern, instanceState.hiddenGroups, globalState.filterByActiveTextEditor ? globalState.pathToActiveTextEditor : null), + onTypeFilterChange: this._handleTypeFilterChange, + onTextFilterChange: this._handleTextFilterChange, + selectMessage: this._selectMessage, + gotoMessageLocation: goToDiagnosticLocation, + supportedMessageKinds: globalState.supportedMessageKinds + })); this._props = this._trackVisibility(props); - } + } // If autoVisibility setting is on, then automatically show/hide on changes. - // If autoVisibility setting is on, then automatically show/hide on changes. - _trackVisibility(props: Observable): Observable { + + _trackVisibility(props) { let lastDiagnostics = []; return props.do(newProps => { - if ( - newProps.autoVisibility && - !arrayEqual( - newProps.diagnostics, - lastDiagnostics, - (a, b) => a.text === b.text, - ) - ) { + if (newProps.autoVisibility && !(0, _collection().arrayEqual)(newProps.diagnostics, lastDiagnostics, (a, b) => a.text === b.text)) { const pane = atom.workspace.paneForItem(this); + if (newProps.diagnostics.length > 0 && !newProps.isVisible) { // We want to call workspace.open but it has no option to // show the new pane without activating it. @@ -144,7 +236,8 @@ export class DiagnosticsViewModel { // https://github.com/atom/atom/issues/16007 if (pane != null) { pane.activateItem(this); - const dock = dockForLocation(pane.getContainer().getLocation()); + const dock = (0, _dockForLocation().default)(pane.getContainer().getLocation()); + if (dock != null) { dock.show(); } @@ -153,153 +246,171 @@ export class DiagnosticsViewModel { // Only hide the diagnostics if it's the only item in its pane. if (pane != null) { const items = pane.getItems(); - if ( - items.length === 1 && - items[0] instanceof DiagnosticsViewModel - ) { + + if (items.length === 1 && items[0] instanceof DiagnosticsViewModel) { atom.workspace.hide(this); } } } + lastDiagnostics = newProps.diagnostics; } }); } - destroy(): void { + destroy() { this._disposables.dispose(); } - getTitle(): string { + getTitle() { return 'Diagnostics'; } - getIconName(): IconName { + getIconName() { return 'law'; } - getURI(): string { + getURI() { return WORKSPACE_VIEW_URI; } - getDefaultLocation(): string { + getDefaultLocation() { return 'bottom'; } - serialize(): SerializedDiagnosticsViewModel { - const {hiddenGroups} = this._model.state; + serialize() { + const { + hiddenGroups + } = this._model.state; return { deserializer: 'atom-ide-ui.DiagnosticsViewModel', state: { - hiddenGroups: [...hiddenGroups], - }, + hiddenGroups: [...hiddenGroups] + } }; } - getElement(): HTMLElement { + getElement() { if (this._element == null) { - const Component = bindObservableAsProps(this._props, DiagnosticsView); - const element = renderReactRoot(); + const Component = (0, _bindObservableAsProps().bindObservableAsProps)(this._props, _DiagnosticsView().default); + const element = (0, _renderReactRoot().renderReactRoot)(_react.default.createElement(Component, null)); element.classList.add('diagnostics-ui'); this._element = element; } + return this._element; } - /** * Toggle the filter. */ - _handleTypeFilterChange = (type: DiagnosticGroup): void => { - const {hiddenGroups} = this._model.state; + + + _filterDiagnostics(diagnostics, pattern, hiddenGroups, filterByPath) { + return diagnostics.filter(message => { + if (hiddenGroups.has(GroupUtils().getGroup(message))) { + return false; + } + + if (filterByPath != null && message.filePath !== filterByPath) { + return false; + } + + if (pattern == null) { + return true; + } + + return message.text != null && pattern.test(message.text) || message.html != null && pattern.test(message.html) || pattern.test(message.providerName) || pattern.test(message.filePath); + }); + } + +} + +exports.DiagnosticsViewModel = DiagnosticsViewModel; + +var _initialiseProps = function () { + this._handleTypeFilterChange = type => { + const { + hiddenGroups + } = this._model.state; const hidden = hiddenGroups.has(type); const nextHiddenTypes = new Set(hiddenGroups); + if (hidden) { nextHiddenTypes.delete(type); } else { nextHiddenTypes.add(type); } - this._model.setState({hiddenGroups: nextHiddenTypes}); - analytics.track('diagnostics-panel-change-filter'); - }; - _handleTextFilterChange = (value: RegExpFilterChange): void => { - const {text, isRegExp} = value; - // TODO: Fuzzy if !isRegExp? - const {invalid, pattern} = getFilterPattern(text, isRegExp); this._model.setState({ - textFilter: {text, isRegExp, invalid, pattern}, + hiddenGroups: nextHiddenTypes }); - analytics.track('diagnostics-panel-change-filter'); + + _analytics().default.track('diagnostics-panel-change-filter'); }; - _filterDiagnostics( - diagnostics: Array, - pattern: ?RegExp, - hiddenGroups: Set, - filterByPath: ?string, - ): Array { - return diagnostics.filter(message => { - if (hiddenGroups.has(GroupUtils.getGroup(message))) { - return false; - } - if (filterByPath != null && message.filePath !== filterByPath) { - return false; - } - if (pattern == null) { - return true; + this._handleTextFilterChange = value => { + const { + text, + isRegExp + } = value; // TODO: Fuzzy if !isRegExp? + + const { + invalid, + pattern + } = (0, _RegExpFilter().getFilterPattern)(text, isRegExp); + + this._model.setState({ + textFilter: { + text, + isRegExp, + invalid, + pattern } - return ( - (message.text != null && pattern.test(message.text)) || - (message.html != null && pattern.test(message.html)) || - pattern.test(message.providerName) || - pattern.test(message.filePath) - ); }); - } - _selectMessage = (message: DiagnosticMessage): void => { - this._model.setState({selectedMessage: message}); + _analytics().default.track('diagnostics-panel-change-filter'); }; -} -function goToDiagnosticLocation( - message: DiagnosticMessage, - options: {|focusEditor: boolean|}, -): void { + this._selectMessage = message => { + this._model.setState({ + selectedMessage: message + }); + }; +}; + +function goToDiagnosticLocation(message, options) { // TODO: what should we do for project-path diagnostics? - if (nuclideUri.endsWithSeparator(message.filePath)) { + if (_nuclideUri().default.endsWithSeparator(message.filePath)) { return; } - analytics.track('diagnostics-panel-goto-location'); + _analytics().default.track('diagnostics-panel-goto-location'); - const uri = message.filePath; - // If initialLine is N, Atom will navigate to line N+1. + const uri = message.filePath; // If initialLine is N, Atom will navigate to line N+1. // Flow sometimes reports a row of -1, so this ensures the line is at least one. + const line = Math.max(message.range ? message.range.start.row : 0, 0); const column = 0; - goToLocation(uri, { + (0, _goToLocation().goToLocation)(uri, { line, column, activatePane: options.focusEditor, - pending: true, + pending: true }); } -function patternsAreEqual(a: ?RegExp, b: ?RegExp) { +function patternsAreEqual(a, b) { if (a === b) { return true; } + if (a == null && b == null) { return true; } + if (a == null || b == null) { return false; } - return ( - a.source === b.source && - a.global === b.global && - a.multiline === b.multiline && - a.ignoreCase === b.ignoreCase - ); -} + + return a.source === b.source && a.global === b.global && a.multiline === b.multiline && a.ignoreCase === b.ignoreCase; +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/GroupUtils.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/GroupUtils.js index 21b9dacf..fee76e28 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/GroupUtils.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/GroupUtils.js @@ -1,3 +1,13 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getGroup = getGroup; +exports.getDisplayName = getDisplayName; +exports.getIcon = getIcon; +exports.getHighestPriorityGroup = getHighestPriorityGroup; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,96 +16,104 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +const PRIORITIZED_GROUPS = ['review', 'errors', 'warnings', 'info', 'action']; -import type {IconName} from 'nuclide-commons-ui/Icon'; -import type {DiagnosticMessage} from '../../atom-ide-diagnostics/lib/types'; -import type {DiagnosticGroup} from './types'; - -import invariant from 'assert'; +function getGroup(message) { + const { + kind + } = message; -const PRIORITIZED_GROUPS: Array = [ - 'review', - 'errors', - 'warnings', - 'info', - 'action', -]; - -export function getGroup(message: DiagnosticMessage): DiagnosticGroup { - const {kind} = message; switch (kind) { case 'lint': case null: case undefined: - invariant(message.type !== 'Hint'); - // We have a separate button for each severity. + if (!(message.type !== 'Hint')) { + throw new Error("Invariant violation: \"message.type !== 'Hint'\""); + } // We have a separate button for each severity. + + switch (message.type) { case 'Error': return 'errors'; + case 'Warning': return 'warnings'; + case 'Info': return 'info'; + default: - (message.type: empty); + message.type; throw new Error(`Invalid message severity: ${message.type}`); } + case 'review': return 'review'; + case 'action': return 'action'; + default: - (kind: empty); + kind; throw new Error(`Invalid message kind: ${kind}`); } } -export function getDisplayName(group: DiagnosticGroup): string { +function getDisplayName(group) { switch (group) { case 'errors': return 'Errors'; + case 'warnings': return 'Warnings'; + case 'info': return 'Info'; + case 'review': return 'Review'; + case 'action': return 'Actions'; + default: - (group: empty); + group; throw new Error(`Invalid group: ${group}`); } } -export function getIcon(group: DiagnosticGroup): IconName { +function getIcon(group) { switch (group) { case 'errors': return 'nuclicon-error'; + case 'warnings': return 'nuclicon-warning'; + case 'info': return 'info'; + case 'review': return 'nuclicon-comment-discussion'; + case 'action': return 'nuclicon-lightbulb-filled'; + default: - (group: empty); + group; throw new Error(`Invalid filter type: ${group}`); } } -export function getHighestPriorityGroup( - groups: Set, -): DiagnosticGroup { +function getHighestPriorityGroup(groups) { for (const group of PRIORITIZED_GROUPS) { if (groups.has(group)) { return group; } } + throw new Error(`Invalid group set: ${[...groups].toString()}`); -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/KeyboardShortcuts.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/KeyboardShortcuts.js index e8a4f704..9f73dafd 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/KeyboardShortcuts.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/KeyboardShortcuts.js @@ -1,3 +1,42 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,169 +45,140 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type { - DiagnosticTrace, - DiagnosticMessage, - DiagnosticUpdater, -} from '../../atom-ide-diagnostics/lib/types'; - -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; - // TODO(peterhal): The current index should really live in the DiagnosticStore. -export default class KeyboardShortcuts { - _subscriptions: UniversalDisposable; - _diagnostics: Array; - _index: ?number; - _traceIndex: ?number; - - constructor(diagnosticUpdater: DiagnosticUpdater) { +class KeyboardShortcuts { + constructor(diagnosticUpdater) { this._index = null; this._diagnostics = []; - - this._subscriptions = new UniversalDisposable(); + this._subscriptions = new (_UniversalDisposable().default)(); const first = () => this.setIndex(0); + const last = () => this.setIndex(this._diagnostics.length - 1); - this._subscriptions.add( - observableFromSubscribeFunction( - diagnosticUpdater.observeMessages, - ).subscribe(diagnostics => { - this._index = null; - this._traceIndex = null; - this._diagnostics = diagnostics; - }), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-first-diagnostic', - first, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-last-diagnostic', - last, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-next-diagnostic', - () => { - this._index == null ? first() : this.setIndex(this._index + 1); - }, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-previous-diagnostic', - () => { - this._index == null ? last() : this.setIndex(this._index - 1); - }, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-next-diagnostic-trace', - () => { - this.nextTrace(); - }, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-previous-diagnostic-trace', - () => { - this.previousTrace(); - }, - ), - ); + + this._subscriptions.add((0, _event().observableFromSubscribeFunction)(diagnosticUpdater.observeMessages).subscribe(diagnostics => { + this._index = null; + this._traceIndex = null; + this._diagnostics = diagnostics; + }), atom.commands.add('atom-workspace', 'diagnostics:go-to-first-diagnostic', first), atom.commands.add('atom-workspace', 'diagnostics:go-to-last-diagnostic', last), atom.commands.add('atom-workspace', 'diagnostics:go-to-next-diagnostic', () => { + this._index == null ? first() : this.setIndex(this._index + 1); + }), atom.commands.add('atom-workspace', 'diagnostics:go-to-previous-diagnostic', () => { + this._index == null ? last() : this.setIndex(this._index - 1); + }), atom.commands.add('atom-workspace', 'diagnostics:go-to-next-diagnostic-trace', () => { + this.nextTrace(); + }), atom.commands.add('atom-workspace', 'diagnostics:go-to-previous-diagnostic-trace', () => { + this.previousTrace(); + })); } - setIndex(index: number): void { + setIndex(index) { this._traceIndex = null; + if (this._diagnostics.length === 0) { this._index = null; return; } + this._index = Math.max(0, Math.min(index, this._diagnostics.length - 1)); this.gotoCurrentIndex(); } - gotoCurrentIndex(): void { - invariant(this._index != null); - invariant(this._traceIndex == null); + gotoCurrentIndex() { + if (!(this._index != null)) { + throw new Error("Invariant violation: \"this._index != null\""); + } + + if (!(this._traceIndex == null)) { + throw new Error("Invariant violation: \"this._traceIndex == null\""); + } + const diagnostic = this._diagnostics[this._index]; const range = diagnostic.range; + if (range == null) { - goToLocation(diagnostic.filePath); + (0, _goToLocation().goToLocation)(diagnostic.filePath); } else { - goToLocation(diagnostic.filePath, { + (0, _goToLocation().goToLocation)(diagnostic.filePath, { line: range.start.row, - column: range.start.column, + column: range.start.column }); } } - nextTrace(): void { + nextTrace() { const traces = this.currentTraces(); + if (traces == null) { return; } + let candidateTrace = this._traceIndex == null ? 0 : this._traceIndex + 1; + while (candidateTrace < traces.length) { if (this.trySetCurrentTrace(traces, candidateTrace)) { return; } + candidateTrace++; } + this._traceIndex = null; this.gotoCurrentIndex(); } - previousTrace(): void { + previousTrace() { const traces = this.currentTraces(); + if (traces == null) { return; } - let candidateTrace = - this._traceIndex == null ? traces.length - 1 : this._traceIndex - 1; + + let candidateTrace = this._traceIndex == null ? traces.length - 1 : this._traceIndex - 1; + while (candidateTrace >= 0) { if (this.trySetCurrentTrace(traces, candidateTrace)) { return; } + candidateTrace--; } + this._traceIndex = null; this.gotoCurrentIndex(); } - currentTraces(): ?Array { + currentTraces() { if (this._index == null) { return null; } + const diagnostic = this._diagnostics[this._index]; return diagnostic.trace; - } + } // TODO: Should filter out traces whose location matches the main diagnostic's location? - // TODO: Should filter out traces whose location matches the main diagnostic's location? - trySetCurrentTrace( - traces: Array, - traceIndex: number, - ): boolean { + + trySetCurrentTrace(traces, traceIndex) { const trace = traces[traceIndex]; + if (trace.filePath != null && trace.range != null) { this._traceIndex = traceIndex; - goToLocation(trace.filePath, { + (0, _goToLocation().goToLocation)(trace.filePath, { line: trace.range.start.row, - column: trace.range.start.column, + column: trace.range.start.column }); return true; } + return false; } - dispose(): void { + dispose() { this._subscriptions.dispose(); } + } + +exports.default = KeyboardShortcuts; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/aim.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/aim.js index 0d47fc72..0493789e 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/aim.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/aim.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.hoveringOrAiming = hoveringOrAiming; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,78 +15,61 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import {Observable} from 'rxjs'; - -type Point = {x: number, y: number}; - const VECTOR_DURATION = 100; -const distance = (a: Point, b: Point): number => { +const distance = (a, b) => { return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); }; -const eventToPoint = (e: MouseEvent): Point => ({ +const eventToPoint = e => ({ x: e.clientX, - y: e.clientY, -}); + y: e.clientY +}); // Combine mouseenter and mouseleave to create an observable of hovering state. + -// Combine mouseenter and mouseleave to create an observable of hovering state. -function areHovering(element: HTMLElement): Observable { - return Observable.merge( - Observable.fromEvent(element, 'mouseenter').mapTo(true), - Observable.fromEvent(element, 'mouseleave').mapTo(false), - ); +function areHovering(element) { + return _RxMin.Observable.merge(_RxMin.Observable.fromEvent(element, 'mouseenter').mapTo(true), _RxMin.Observable.fromEvent(element, 'mouseleave').mapTo(false)); } -function findCorners(node: HTMLElement): [Point, Point, Point, Point] { - const {left, width, top, height} = node.getBoundingClientRect(); - return [ - {x: left, y: top}, // Top left - {x: left + width, y: top}, // Top right - {x: left, y: top + height}, // Bottom left - {x: left + width, y: top + height}, // Bottom right - ]; +function findCorners(node) { + const { + left, + width, + top, + height + } = node.getBoundingClientRect(); + return [{ + x: left, + y: top + }, // Top left + { + x: left + width, + y: top + }, // Top right + { + x: left, + y: top + height + }, // Bottom left + { + x: left + width, + y: top + height + }]; } -function areAiming(from: HTMLElement, to: HTMLElement): Observable { +function areAiming(from, to) { const [topLeft, topRight, bottomLeft, bottomRight] = findCorners(to); - - const toBelowFrom = - to.getBoundingClientRect().top >= from.getBoundingClientRect().bottom; - - // For now we assume that `to` is always to the right of `from` and that + const toBelowFrom = to.getBoundingClientRect().top >= from.getBoundingClientRect().bottom; // For now we assume that `to` is always to the right of `from` and that // `from` is always strictly above or below `to`. A more robust solution would // be to find the two corner of `to` that form the largest angle from the // center of `from` - const [cornerA, cornerB] = toBelowFrom - ? [topRight, bottomLeft] - : [topLeft, bottomRight]; - return Observable.fromEvent(document, 'mousemove') - .map(eventToPoint) - .auditTime(VECTOR_DURATION) - .map(mouse => distance(mouse, cornerA) + distance(mouse, cornerB)) - .pairwise() - .map(([prevDist, currentDist]) => prevDist > currentDist) - .distinctUntilChanged(); + const [cornerA, cornerB] = toBelowFrom ? [topRight, bottomLeft] : [topLeft, bottomRight]; + return _RxMin.Observable.fromEvent(document, 'mousemove').map(eventToPoint).auditTime(VECTOR_DURATION).map(mouse => distance(mouse, cornerA) + distance(mouse, cornerB)).pairwise().map(([prevDist, currentDist]) => prevDist > currentDist).distinctUntilChanged(); } -export function hoveringOrAiming( - from: HTMLElement, - to: HTMLElement, -): Observable { - return Observable.concat( - areHovering(from) - .startWith(true) - .takeWhile(Boolean), - Observable.combineLatest( - areAiming(from, to).startWith(true), - areHovering(to).startWith(false), - (aiming, hovering) => aiming || hovering, - ), - ).distinctUntilChanged(); -} +function hoveringOrAiming(from, to) { + return _RxMin.Observable.concat(areHovering(from).startWith(true).takeWhile(Boolean), _RxMin.Observable.combineLatest(areAiming(from, to).startWith(true), areHovering(to).startWith(false), (aiming, hovering) => aiming || hovering)).distinctUntilChanged(); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js index 8aacca29..527ef060 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js @@ -1,3 +1,54 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _bindObservableAsProps() { + const data = require("../../../../nuclide-commons-ui/bindObservableAsProps"); + + _bindObservableAsProps = function () { + return data; + }; + + return data; +} + +function _DiagnosticsPopup() { + const data = require("./ui/DiagnosticsPopup"); + + _DiagnosticsPopup = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,60 +57,44 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +const gotoLine = (file, line) => (0, _goToLocation().goToLocation)(file, { + line +}); -import type {Datatip} from '../../atom-ide-datatip/lib/types'; -import type { - DiagnosticUpdater, - DiagnosticMessage, -} from '../../atom-ide-diagnostics/lib/types'; - -import invariant from 'assert'; -import * as React from 'react'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {DiagnosticsPopup} from './ui/DiagnosticsPopup'; - -const gotoLine = (file: string, line: number) => goToLocation(file, {line}); - -function makeDatatipComponent( - messages: Array, - diagnosticUpdater: DiagnosticUpdater, -): React.ComponentType { +function makeDatatipComponent(messages, diagnosticUpdater) { const fixer = message => diagnosticUpdater.applyFix(message); - return bindObservableAsProps( - observableFromSubscribeFunction(cb => - diagnosticUpdater.observeCodeActionsForMessage(cb), - ).map(codeActionsForMessage => ({ - messages, - fixer, - goToLocation: gotoLine, - codeActionsForMessage, - })), - DiagnosticsPopup, - ); + + return (0, _bindObservableAsProps().bindObservableAsProps)((0, _event().observableFromSubscribeFunction)(cb => diagnosticUpdater.observeCodeActionsForMessage(cb)).map(codeActionsForMessage => ({ + messages, + fixer, + goToLocation: gotoLine, + codeActionsForMessage + })), _DiagnosticsPopup().DiagnosticsPopup); } -export default (async function getDiagnosticDatatip( - editor: TextEditor, - position: atom$Point, - messagesAtPosition: Array, - diagnosticUpdater: DiagnosticUpdater, -): Promise { +var getDiagnosticDatatip = async function getDiagnosticDatatip(editor, position, messagesAtPosition, diagnosticUpdater) { let range = null; + for (const message of messagesAtPosition) { if (message.range != null) { range = range == null ? message.range : message.range.union(range); } } + diagnosticUpdater.fetchCodeActions(editor, messagesAtPosition); - invariant(range != null); + + if (!(range != null)) { + throw new Error("Invariant violation: \"range != null\""); + } + return { component: makeDatatipComponent(messagesAtPosition, diagnosticUpdater), pinnable: false, - range, + range }; -}); +}; + +exports.default = getDiagnosticDatatip; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js index 72890ce4..0f34136d 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js @@ -1,3 +1,132 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.applyUpdateToEditor = applyUpdateToEditor; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _range() { + const data = require("../../../../nuclide-commons-atom/range"); + + _range = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _bindObservableAsProps() { + const data = require("../../../../nuclide-commons-ui/bindObservableAsProps"); + + _bindObservableAsProps = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _DiagnosticsPopup() { + const data = require("./ui/DiagnosticsPopup"); + + _DiagnosticsPopup = function () { + return data; + }; + + return data; +} + +function GroupUtils() { + const data = _interopRequireWildcard(require("./GroupUtils")); + + GroupUtils = function () { + return data; + }; + + return data; +} + +function _aim() { + const data = require("./aim"); + + _aim = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,37 +135,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - DiagnosticUpdater, - DiagnosticMessage, - DiagnosticMessages, -} from '../../atom-ide-diagnostics/lib/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import classnames from 'classnames'; -import {Range} from 'atom'; -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {completingSwitchMap} from 'nuclide-commons/observable'; -import {goToLocation as atomGoToLocation} from 'nuclide-commons-atom/go-to-location'; -import {wordAtPosition} from 'nuclide-commons-atom/range'; -import analytics from 'nuclide-commons/analytics'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {Observable, Subject} from 'rxjs'; -import {DiagnosticsPopup} from './ui/DiagnosticsPopup'; -import * as GroupUtils from './GroupUtils'; -import {hoveringOrAiming} from './aim'; - -const GUTTER_ID = 'diagnostics-gutter'; - -// TODO(mbolin): Make it so that when mousing over an element with this CSS class (or specifically, +const GUTTER_ID = 'diagnostics-gutter'; // TODO(mbolin): Make it so that when mousing over an element with this CSS class (or specifically, // the child element with the "region" CSS class), we also do a showPopupFor(). This seems to be // tricky given how the DOM of a TextEditor works today. There are div.tile elements, each of which // has its own div.highlights element and many div.line elements. The div.highlights element has 0 @@ -46,113 +148,97 @@ const GUTTER_ID = 'diagnostics-gutter'; // might have to listen for mouseover events on TextEditor and then use its own APIs, such as // decorationsForScreenRowRange(), to see if there is a hit target instead. Since this will be // happening onmousemove, we also have to be careful to make sure this is not expensive. -const HIGHLIGHT_CSS = 'diagnostics-gutter-ui-highlight'; +const HIGHLIGHT_CSS = 'diagnostics-gutter-ui-highlight'; const HIGHLIGHT_CSS_LEVELS = { Error: 'diagnostics-gutter-ui-highlight-error', Warning: 'diagnostics-gutter-ui-highlight-warning', Info: 'diagnostics-gutter-ui-highlight-info', - Hint: '', + Hint: '' }; - const GUTTER_CSS_GROUPS = { review: 'diagnostics-gutter-ui-gutter-review', errors: 'diagnostics-gutter-ui-gutter-error', warnings: 'diagnostics-gutter-ui-gutter-warning', info: 'diagnostics-gutter-ui-gutter-info', action: 'diagnostics-gutter-ui-gutter-action', - hidden: '', + hidden: '' }; - -const editorToMarkers: WeakMap> = new WeakMap(); -const itemToEditor: WeakMap = new WeakMap(); -const handleMouseEnterSpawnPopupEvents = new Subject(); - -const handleMouseEnterSpawnPopupEventsObservable = handleMouseEnterSpawnPopupEvents - .switchMap(({messages, diagnosticUpdater, gutter, item}) => { - return spawnPopup(messages, diagnosticUpdater, gutter, item) - .let( - completingSwitchMap((popupElement: HTMLElement) => { - const innerPopupElement = popupElement.firstChild; - invariant(innerPopupElement instanceof HTMLElement); - // Events which should cause the popup to close. - return Observable.merge( - hoveringOrAiming(item, innerPopupElement), - // This makes sure that the popup disappears when you ctrl+tab to switch tabs. - observableFromSubscribeFunction(cb => - atom.workspace.onDidChangeActivePaneItem(cb), - ).mapTo(false), - ); - }), - ) - - .takeWhile(Boolean); - }) - .share(); - -export function applyUpdateToEditor( - editor: TextEditor, - update: DiagnosticMessages, - diagnosticUpdater: DiagnosticUpdater, -): void { +const editorToMarkers = new WeakMap(); +const itemToEditor = new WeakMap(); +const handleMouseEnterSpawnPopupEvents = new _RxMin.Subject(); +const handleMouseEnterSpawnPopupEventsObservable = handleMouseEnterSpawnPopupEvents.switchMap(({ + messages, + diagnosticUpdater, + gutter, + item +}) => { + return spawnPopup(messages, diagnosticUpdater, gutter, item).let((0, _observable().completingSwitchMap)(popupElement => { + const innerPopupElement = popupElement.firstChild; + + if (!(innerPopupElement instanceof HTMLElement)) { + throw new Error("Invariant violation: \"innerPopupElement instanceof HTMLElement\""); + } // Events which should cause the popup to close. + + + return _RxMin.Observable.merge((0, _aim().hoveringOrAiming)(item, innerPopupElement), // This makes sure that the popup disappears when you ctrl+tab to switch tabs. + (0, _event().observableFromSubscribeFunction)(cb => atom.workspace.onDidChangeActivePaneItem(cb)).mapTo(false)); + })).takeWhile(Boolean); +}).share(); + +function applyUpdateToEditor(editor, update, diagnosticUpdater) { let gutter = editor.gutterWithName(GUTTER_ID); + if (!gutter) { // TODO(jessicalin): Determine an appropriate priority so that the gutter: // (1) Shows up to the right of the line numbers. // (2) Shows the items that are added to it right away. // Using a value of 10 fixes (1), but breaks (2). This seems like it is likely a bug in Atom. - // By default, a gutter will be destroyed when its editor is destroyed, // so there is no need to register a callback via onDidDestroy(). gutter = editor.addGutter({ name: GUTTER_ID, visible: false, // Priority is -200 by default and 0 is the line number - priority: -1000, + priority: -1000 }); } let marker; - let markers = editorToMarkers.get(editor); - - // TODO: Consider a more efficient strategy that does not blindly destroy all of the + let markers = editorToMarkers.get(editor); // TODO: Consider a more efficient strategy that does not blindly destroy all of the // existing markers. + if (markers) { for (marker of markers) { marker.destroy(); } + markers.clear(); } else { markers = new Set(); } - const rowToMessage: Map> = new Map(); - function addMessageForRow(message: DiagnosticMessage, row: number) { + const rowToMessage = new Map(); + + function addMessageForRow(message, row) { let messages = rowToMessage.get(row); + if (!messages) { messages = []; rowToMessage.set(row, messages); } + messages.push(message); } for (const message of update.messages) { - const wordRange = - message.range != null && message.range.isEmpty() - ? wordAtPosition(editor, message.range.start) - : null; + const wordRange = message.range != null && message.range.isEmpty() ? (0, _range().wordAtPosition)(editor, message.range.start) : null; const range = wordRange != null ? wordRange.range : message.range; - - const highlightCssClass = classnames( - HIGHLIGHT_CSS, - HIGHLIGHT_CSS_LEVELS[message.type], - ); - + const highlightCssClass = (0, _classnames().default)(HIGHLIGHT_CSS, HIGHLIGHT_CSS_LEVELS[message.type]); let highlightMarker; - if (range) { - addMessageForRow(message, range.start.row); - // There is no API in Atom to say: I want to put an underline on all the + if (range) { + addMessageForRow(message, range.start.row); // There is no API in Atom to say: I want to put an underline on all the // lines in this range. The closest is "highlight" which splits your range // into three boxes: the part of the first line, all the lines in between // and the part of the last line. @@ -162,12 +248,11 @@ export function applyUpdateToEditor( // // To fix this, we can manually split it line by line and give to atom // those ranges. + for (let line = range.start.row; line <= range.end.row; line++) { let start; let end; - const lineText = editor.getTextInBufferRange( - new Range([line, 0], [line + 1, 0]), - ); + const lineText = editor.getTextInBufferRange(new _atom.Range([line, 0], [line + 1, 0])); if (line === range.start.row) { start = range.start.column; @@ -184,181 +269,172 @@ export function applyUpdateToEditor( end = lineText.length; } - highlightMarker = editor.markBufferRange( - new Range([line, start], [line, end]), - ); + highlightMarker = editor.markBufferRange(new _atom.Range([line, start], [line, end])); editor.decorateMarker(highlightMarker, { type: 'highlight', - class: highlightCssClass, + class: highlightCssClass }); markers.add(highlightMarker); } } else { addMessageForRow(message, 0); } - } + } // Find all of the gutter markers for the same row and combine them into one marker/popup. + - // Find all of the gutter markers for the same row and combine them into one marker/popup. for (const [row, messages] of rowToMessage.entries()) { // This marker adds some UI to the gutter. - const {item, dispose} = createGutterItem( - messages, - diagnosticUpdater, - gutter, - ); + const { + item, + dispose + } = createGutterItem(messages, diagnosticUpdater, gutter); itemToEditor.set(item, editor); const gutterMarker = editor.markBufferPosition([row, 0]); - gutter.decorateMarker(gutterMarker, {item}); + gutter.decorateMarker(gutterMarker, { + item + }); gutterMarker.onDidDestroy(dispose); markers.add(gutterMarker); } - editorToMarkers.set(editor, markers); - - // Once the gutter is shown for the first time, it is displayed for the lifetime of the + editorToMarkers.set(editor, markers); // Once the gutter is shown for the first time, it is displayed for the lifetime of the // TextEditor. + if (update.messages.length > 0) { gutter.show(); - analytics.track('diagnostics-show-editor-diagnostics'); + + _analytics().default.track('diagnostics-show-editor-diagnostics'); } } -function createGutterItem( - messages: Array, - diagnosticUpdater: DiagnosticUpdater, - gutter: atom$Gutter, -): {item: HTMLElement, dispose: () => void} { +function createGutterItem(messages, diagnosticUpdater, gutter) { // Determine which group to display. const messageGroups = new Set(); - messages.forEach(msg => messageGroups.add(GroupUtils.getGroup(msg))); - const group = GroupUtils.getHighestPriorityGroup(messageGroups); - + messages.forEach(msg => messageGroups.add(GroupUtils().getGroup(msg))); + const group = GroupUtils().getHighestPriorityGroup(messageGroups); const item = document.createElement('span'); const groupClassName = GUTTER_CSS_GROUPS[group]; - item.className = `diagnostics-gutter-ui-item ${groupClassName || ''}`; + item.className = `diagnostics-gutter-ui-item ${groupClassName || ''}`; // Add the icon - // Add the icon const icon = document.createElement('span'); - icon.className = `icon icon-${GroupUtils.getIcon(group)}`; + icon.className = `icon icon-${GroupUtils().getIcon(group)}`; item.appendChild(icon); - - const disposable = new UniversalDisposable( - handleMouseEnterSpawnPopupEventsObservable.subscribe(), - Observable.fromEvent(item, 'mouseenter').subscribe(() => { - handleMouseEnterSpawnPopupEvents.next({ - messages, - diagnosticUpdater, - gutter, - item, - }); - }), - ); - + const disposable = new (_UniversalDisposable().default)(handleMouseEnterSpawnPopupEventsObservable.subscribe(), _RxMin.Observable.fromEvent(item, 'mouseenter').subscribe(() => { + handleMouseEnterSpawnPopupEvents.next({ + messages, + diagnosticUpdater, + gutter, + item + }); + })); return { item, + dispose() { disposable.dispose(); - }, + } + }; } -function spawnPopup( - messages: Array, - diagnosticUpdater: DiagnosticUpdater, - gutter: atom$Gutter, - item: HTMLElement, -): Observable { - return Observable.create(observer => { - const goToLocation = (path: string, line: number) => { +function spawnPopup(messages, diagnosticUpdater, gutter, item) { + return _RxMin.Observable.create(observer => { + const goToLocation = (path, line) => { // Before we jump to the location, we want to close the popup. const column = 0; - atomGoToLocation(path, {line, column}); + (0, _goToLocation().goToLocation)(path, { + line, + column + }); observer.complete(); }; - const popupElement = showPopupFor( - messages, - item, - goToLocation, - diagnosticUpdater, - gutter, - ); + const popupElement = showPopupFor(messages, item, goToLocation, diagnosticUpdater, gutter); observer.next(popupElement); - return () => { - ReactDOM.unmountComponentAtNode(popupElement); - invariant(popupElement.parentNode != null); + _reactDom.default.unmountComponentAtNode(popupElement); + + if (!(popupElement.parentNode != null)) { + throw new Error("Invariant violation: \"popupElement.parentNode != null\""); + } + popupElement.parentNode.removeChild(popupElement); }; }); } - /** * Shows a popup for the diagnostic just below the specified item. */ -function showPopupFor( - messages: Array, - item: HTMLElement, - goToLocation: (filePath: NuclideUri, line: number) => mixed, - diagnosticUpdater: DiagnosticUpdater, - gutter: atom$Gutter, -): HTMLElement { + + +function showPopupFor(messages, item, goToLocation, diagnosticUpdater, gutter) { // The popup will be an absolutely positioned child element of so that it appears // on top of everything. - const workspaceElement = atom.views.getView((atom.workspace: Object)); + const workspaceElement = atom.views.getView(atom.workspace); const hostElement = document.createElement('div'); - hostElement.classList.add('diagnostics-gutter-popup'); - // $FlowFixMe check parentNode for null - workspaceElement.parentNode.appendChild(hostElement); + hostElement.classList.add('diagnostics-gutter-popup'); // $FlowFixMe check parentNode for null + workspaceElement.parentNode.appendChild(hostElement); const { bottom: itemBottom, top: itemTop, - height: itemHeight, - } = item.getBoundingClientRect(); - // $FlowFixMe atom$Gutter.getElement is not a documented API, but it beats using a query selector. + height: itemHeight + } = item.getBoundingClientRect(); // $FlowFixMe atom$Gutter.getElement is not a documented API, but it beats using a query selector. + const gutterContainer = gutter.getElement(); - const {right: gutterRight} = gutterContainer.getBoundingClientRect(); + const { + right: gutterRight + } = gutterContainer.getBoundingClientRect(); const trackedFixer = (...args) => { diagnosticUpdater.applyFix(...args); - analytics.track('diagnostics-gutter-autofix'); + + _analytics().default.track('diagnostics-gutter-autofix'); }; - const trackedGoToLocation = (filePath: NuclideUri, line: number) => { + + const trackedGoToLocation = (filePath, line) => { goToLocation(filePath, line); - analytics.track('diagnostics-gutter-goto-location'); + + _analytics().default.track('diagnostics-gutter-goto-location'); }; const editor = itemToEditor.get(item); - invariant(editor != null); - diagnosticUpdater.fetchCodeActions(editor, messages); + if (!(editor != null)) { + throw new Error("Invariant violation: \"editor != null\""); + } + + diagnosticUpdater.fetchCodeActions(editor, messages); const popupTop = itemBottom; - const BoundPopup = bindObservableAsProps( - observableFromSubscribeFunction(cb => - diagnosticUpdater.observeCodeActionsForMessage(cb), - ).map(codeActionsForMessage => ({ - style: {left: gutterRight, top: popupTop, position: 'absolute'}, - messages, - fixer: trackedFixer, - goToLocation: trackedGoToLocation, - codeActionsForMessage, - })), - DiagnosticsPopup, - ); - ReactDOM.render(, hostElement); - - // Check to see whether the popup is within the bounds of the TextEditor. If not, display it above + const BoundPopup = (0, _bindObservableAsProps().bindObservableAsProps)((0, _event().observableFromSubscribeFunction)(cb => diagnosticUpdater.observeCodeActionsForMessage(cb)).map(codeActionsForMessage => ({ + style: { + left: gutterRight, + top: popupTop, + position: 'absolute' + }, + messages, + fixer: trackedFixer, + goToLocation: trackedGoToLocation, + codeActionsForMessage + })), _DiagnosticsPopup().DiagnosticsPopup); + + _reactDom.default.render(React.createElement(BoundPopup, null), hostElement); // Check to see whether the popup is within the bounds of the TextEditor. If not, display it above // the glyph rather than below it. + + const editorElement = atom.views.getView(editor); const { top: editorTop, - height: editorHeight, + height: editorHeight } = editorElement.getBoundingClientRect(); - const popupElement = hostElement.firstElementChild; - invariant(popupElement instanceof HTMLElement); + + if (!(popupElement instanceof HTMLElement)) { + throw new Error("Invariant violation: \"popupElement instanceof HTMLElement\""); + } + const popupHeight = popupElement.clientHeight; + if (itemTop + itemHeight + popupHeight > editorTop + editorHeight) { popupElement.style.top = `${popupTop - popupHeight - itemHeight}px`; } @@ -367,11 +443,11 @@ function showPopupFor( return hostElement; } finally { messages.forEach(message => { - analytics.track('diagnostics-gutter-show-popup', { + _analytics().default.track('diagnostics-gutter-show-popup', { 'diagnostics-provider': message.providerName, // flowlint-next-line sketchy-null-string:off - 'diagnostics-message': message.text || message.html || '', + 'diagnostics-message': message.text || message.html || '' }); }); } -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/main.js index 7434ddf7..1d875fbc 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/main.js @@ -1,3 +1,199 @@ +"use strict"; + +function _analytics() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _KeyboardShortcuts() { + const data = _interopRequireDefault(require("./KeyboardShortcuts")); + + _KeyboardShortcuts = function () { + return data; + }; + + return data; +} + +function _Model() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/Model")); + + _Model = function () { + return data; + }; + + return data; +} + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _DiagnosticsViewModel() { + const data = require("./DiagnosticsViewModel"); + + _DiagnosticsViewModel = function () { + return data; + }; + + return data; +} + +function _StatusBarTile() { + const data = _interopRequireDefault(require("./ui/StatusBarTile")); + + _StatusBarTile = function () { + return data; + }; + + return data; +} + +function _gutter() { + const data = require("./gutter"); + + _gutter = function () { + return data; + }; + + return data; +} + +function _getDiagnosticDatatip() { + const data = _interopRequireDefault(require("./getDiagnosticDatatip")); + + _getDiagnosticDatatip = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _destroyItemWhere() { + const data = require("../../../../nuclide-commons-atom/destroyItemWhere"); + + _destroyItemWhere = function () { + return data; + }; + + return data; +} + +function _textEditor() { + const data = require("../../../../nuclide-commons-atom/text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _showActionsMenu() { + const data = _interopRequireDefault(require("./showActionsMenu")); + + _showActionsMenu = function () { + return data; + }; + + return data; +} + +function _showAtomLinterWarning() { + const data = _interopRequireDefault(require("./showAtomLinterWarning")); + + _showAtomLinterWarning = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,502 +202,342 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - DatatipProvider, - DatatipService, -} from '../../atom-ide-datatip/lib/types'; - -import type { - DiagnosticMessage, - DiagnosticMessages, - DiagnosticUpdater, -} from '../../atom-ide-diagnostics/lib/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {GlobalViewState} from './types'; - -import invariant from 'assert'; - -import analytics from 'nuclide-commons/analytics'; - -import idx from 'idx'; -import {areSetsEqual} from 'nuclide-commons/collection'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {fastDebounce} from 'nuclide-commons/observable'; -import KeyboardShortcuts from './KeyboardShortcuts'; -import Model from 'nuclide-commons/Model'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {DiagnosticsViewModel, WORKSPACE_VIEW_URI} from './DiagnosticsViewModel'; -import StatusBarTile from './ui/StatusBarTile'; -import {applyUpdateToEditor} from './gutter'; -import getDiagnosticDatatip from './getDiagnosticDatatip'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {destroyItemWhere} from 'nuclide-commons-atom/destroyItemWhere'; -import {isValidTextEditor} from 'nuclide-commons-atom/text-editor'; -import {Observable} from 'rxjs'; -import showActionsMenu from './showActionsMenu'; -import showAtomLinterWarning from './showAtomLinterWarning'; - const MAX_OPEN_ALL_FILES = 20; const SHOW_TRACES_SETTING = 'atom-ide-diagnostics-ui.showDiagnosticTraces'; -type ActivationState = {| - filterByActiveTextEditor: boolean, -|}; - -type DiagnosticsState = {| - ...ActivationState, - diagnosticUpdater: ?DiagnosticUpdater, -|}; - class Activation { - _subscriptions: UniversalDisposable; - _model: Model; - _statusBarTile: ?StatusBarTile; - _fileDiagnostics: WeakMap>; - _globalViewStates: ?Observable; - - constructor(state: ?Object): void { - this._subscriptions = new UniversalDisposable( - this.registerOpenerAndCommand(), - this._registerActionsMenu(), - showAtomLinterWarning(), - ); - this._model = new Model({ - filterByActiveTextEditor: - idx(state, _ => _.filterByActiveTextEditor) === true, - diagnosticUpdater: null, + constructor(state) { + var _ref; + + this._subscriptions = new (_UniversalDisposable().default)(this.registerOpenerAndCommand(), this._registerActionsMenu(), (0, _showAtomLinterWarning().default)()); + this._model = new (_Model().default)({ + filterByActiveTextEditor: ((_ref = state) != null ? _ref.filterByActiveTextEditor : _ref) === true, + diagnosticUpdater: null }); this._fileDiagnostics = new WeakMap(); } - consumeDatatipService(service: DatatipService): IDisposable { - const datatipProvider: DatatipProvider = { + consumeDatatipService(service) { + const datatipProvider = { // show this datatip for every type of file providerName: 'diagnostics-datatip', // Diagnostic datatips should have higher priority than most other datatips. priority: 10, datatip: (editor, position) => { - const messagesAtPosition = this._getMessagesAtPosition( - editor, - position, - ); - const {diagnosticUpdater} = this._model.state; + const messagesAtPosition = this._getMessagesAtPosition(editor, position); + + const { + diagnosticUpdater + } = this._model.state; + if (messagesAtPosition.length === 0 || diagnosticUpdater == null) { return Promise.resolve(null); } - return getDiagnosticDatatip( - editor, - position, - messagesAtPosition, - diagnosticUpdater, - ); - }, + + return (0, _getDiagnosticDatatip().default)(editor, position, messagesAtPosition, diagnosticUpdater); + } }; const disposable = service.addProvider(datatipProvider); + this._subscriptions.add(disposable); + return disposable; } - consumeDiagnosticUpdates(diagnosticUpdater: DiagnosticUpdater): IDisposable { + consumeDiagnosticUpdates(diagnosticUpdater) { this._getStatusBarTile().consumeDiagnosticUpdates(diagnosticUpdater); - this._subscriptions.add(gutterConsumeDiagnosticUpdates(diagnosticUpdater)); - // Currently, the DiagnosticsView is designed to work with only one DiagnosticUpdater. + this._subscriptions.add(gutterConsumeDiagnosticUpdates(diagnosticUpdater)); // Currently, the DiagnosticsView is designed to work with only one DiagnosticUpdater. + + if (this._model.state.diagnosticUpdater != null) { - return new UniversalDisposable(); + return new (_UniversalDisposable().default)(); } - this._model.setState({diagnosticUpdater}); + + this._model.setState({ + diagnosticUpdater + }); + const atomCommandsDisposable = addAtomCommands(diagnosticUpdater); + this._subscriptions.add(atomCommandsDisposable); - this._subscriptions.add( - // Track diagnostics for all active editors. - atom.workspace.observeTextEditors((editor: TextEditor) => { - this._fileDiagnostics.set(editor, []); - // TODO: this is actually inefficient - this filters all file events - // by their path, so this is actually O(N^2) in the number of editors. - // We should merge the store and UI packages to get direct access. - const subscription = getEditorDiagnosticUpdates( - editor, - diagnosticUpdater, - ) - .finally(() => { - this._subscriptions.remove(subscription); - this._fileDiagnostics.delete(editor); - }) - .subscribe(update => { - this._fileDiagnostics.set(editor, update.messages); - }); - this._subscriptions.add(subscription); - }), - ); - return new UniversalDisposable(atomCommandsDisposable, () => { - invariant(this._model.state.diagnosticUpdater === diagnosticUpdater); - this._model.setState({diagnosticUpdater: null}); + + this._subscriptions.add( // Track diagnostics for all active editors. + atom.workspace.observeTextEditors(editor => { + this._fileDiagnostics.set(editor, []); // TODO: this is actually inefficient - this filters all file events + // by their path, so this is actually O(N^2) in the number of editors. + // We should merge the store and UI packages to get direct access. + + + const subscription = getEditorDiagnosticUpdates(editor, diagnosticUpdater).finally(() => { + this._subscriptions.remove(subscription); + + this._fileDiagnostics.delete(editor); + }).subscribe(update => { + this._fileDiagnostics.set(editor, update.messages); + }); + + this._subscriptions.add(subscription); + })); + + return new (_UniversalDisposable().default)(atomCommandsDisposable, () => { + if (!(this._model.state.diagnosticUpdater === diagnosticUpdater)) { + throw new Error("Invariant violation: \"this._model.state.diagnosticUpdater === diagnosticUpdater\""); + } + + this._model.setState({ + diagnosticUpdater: null + }); }); } - consumeStatusBar(statusBar: atom$StatusBar): void { + consumeStatusBar(statusBar) { this._getStatusBarTile().consumeStatusBar(statusBar); } - deserializeDiagnosticsViewModel(): DiagnosticsViewModel { + deserializeDiagnosticsViewModel() { return this._createDiagnosticsViewModel(); } - dispose(): void { + dispose() { this._subscriptions.dispose(); + if (this._statusBarTile) { this._statusBarTile.dispose(); + this._statusBarTile = null; } } - serialize(): ActivationState { - const {filterByActiveTextEditor} = this._model.state; + serialize() { + const { + filterByActiveTextEditor + } = this._model.state; return { - filterByActiveTextEditor, + filterByActiveTextEditor }; } - _createDiagnosticsViewModel(): DiagnosticsViewModel { - return new DiagnosticsViewModel(this._getGlobalViewStates()); + _createDiagnosticsViewModel() { + return new (_DiagnosticsViewModel().DiagnosticsViewModel)(this._getGlobalViewStates()); } - /** * An observable of the state that's shared between all panel copies. State that's unique to a * single copy of the diagnostics panel is managed in DiagnosticsViewModel. Generally, users will * only have one copy of the diagnostics panel so this is mostly a theoretical distinction, * however, each copy should have its own sorting, filtering, etc. */ - _getGlobalViewStates(): Observable { + + + _getGlobalViewStates() { if (this._globalViewStates == null) { const packageStates = this._model.toObservable(); - const updaters = packageStates - .map(state => state.diagnosticUpdater) - .distinctUntilChanged(); - - const diagnosticsStream = updaters - .switchMap( - updater => - updater == null - ? Observable.of([]) - : observableFromSubscribeFunction(updater.observeMessages), - ) - .map(diagnostics => diagnostics.filter(d => d.type !== 'Hint')) - .let(fastDebounce(100)) - .startWith([]); - - const showTracesStream: Observable< - boolean, - > = (featureConfig.observeAsStream(SHOW_TRACES_SETTING): any); + + const updaters = packageStates.map(state => state.diagnosticUpdater).distinctUntilChanged(); + const diagnosticsStream = updaters.switchMap(updater => updater == null ? _RxMin.Observable.of([]) : (0, _event().observableFromSubscribeFunction)(updater.observeMessages)).map(diagnostics => diagnostics.filter(d => d.type !== 'Hint')).let((0, _observable().fastDebounce)(100)).startWith([]); + + const showTracesStream = _featureConfig().default.observeAsStream(SHOW_TRACES_SETTING); + const setShowTraces = showTraces => { - featureConfig.set(SHOW_TRACES_SETTING, showTraces); + _featureConfig().default.set(SHOW_TRACES_SETTING, showTraces); }; - const showDirectoryColumnStream: Observable< - boolean, - > = (featureConfig.observeAsStream( - 'atom-ide-diagnostics-ui.showDirectoryColumn', - ): any); + const showDirectoryColumnStream = _featureConfig().default.observeAsStream('atom-ide-diagnostics-ui.showDirectoryColumn'); - const autoVisibilityStream: Observable< - boolean, - > = (featureConfig.observeAsStream( - 'atom-ide-diagnostics-ui.autoVisibility', - ): any); + const autoVisibilityStream = _featureConfig().default.observeAsStream('atom-ide-diagnostics-ui.autoVisibility'); const pathToActiveTextEditorStream = getActiveEditorPaths(); + const filterByActiveTextEditorStream = packageStates.map(state => state.filterByActiveTextEditor).distinctUntilChanged(); - const filterByActiveTextEditorStream = packageStates - .map(state => state.filterByActiveTextEditor) - .distinctUntilChanged(); const setFilterByActiveTextEditor = filterByActiveTextEditor => { - this._model.setState({filterByActiveTextEditor}); + this._model.setState({ + filterByActiveTextEditor + }); }; - const supportedMessageKindsStream = updaters - .switchMap( - updater => - updater == null - ? Observable.of(new Set(['lint'])) - : observableFromSubscribeFunction( - updater.observeSupportedMessageKinds.bind(updater), - ), - ) - .distinctUntilChanged(areSetsEqual); - - const uiConfigStream = updaters.switchMap( - updater => - updater == null - ? Observable.of([]) - : observableFromSubscribeFunction( - updater.observeUiConfig.bind(updater), - ), - ); - - this._globalViewStates = Observable.combineLatest( - diagnosticsStream, - filterByActiveTextEditorStream, - pathToActiveTextEditorStream, - showTracesStream, - showDirectoryColumnStream, - autoVisibilityStream, - supportedMessageKindsStream, - uiConfigStream, - // $FlowFixMe - ( - diagnostics, - filterByActiveTextEditor, - pathToActiveTextEditor, - showTraces, - showDirectoryColumn, - autoVisibility, - supportedMessageKinds, - uiConfig, - ) => ({ - diagnostics, - filterByActiveTextEditor, - pathToActiveTextEditor, - showTraces, - showDirectoryColumn, - autoVisibility, - onShowTracesChange: setShowTraces, - onFilterByActiveTextEditorChange: setFilterByActiveTextEditor, - supportedMessageKinds, - uiConfig, - }), - ); + const supportedMessageKindsStream = updaters.switchMap(updater => updater == null ? _RxMin.Observable.of(new Set(['lint'])) : (0, _event().observableFromSubscribeFunction)(updater.observeSupportedMessageKinds.bind(updater))).distinctUntilChanged(_collection().areSetsEqual); + const uiConfigStream = updaters.switchMap(updater => updater == null ? _RxMin.Observable.of([]) : (0, _event().observableFromSubscribeFunction)(updater.observeUiConfig.bind(updater))); + this._globalViewStates = _RxMin.Observable.combineLatest(diagnosticsStream, filterByActiveTextEditorStream, pathToActiveTextEditorStream, showTracesStream, showDirectoryColumnStream, autoVisibilityStream, supportedMessageKindsStream, uiConfigStream, // $FlowFixMe + (diagnostics, filterByActiveTextEditor, pathToActiveTextEditor, showTraces, showDirectoryColumn, autoVisibility, supportedMessageKinds, uiConfig) => ({ + diagnostics, + filterByActiveTextEditor, + pathToActiveTextEditor, + showTraces, + showDirectoryColumn, + autoVisibility, + onShowTracesChange: setShowTraces, + onFilterByActiveTextEditorChange: setFilterByActiveTextEditor, + supportedMessageKinds, + uiConfig + })); } + return this._globalViewStates; } - registerOpenerAndCommand(): IDisposable { - const commandDisposable = atom.commands.add( - 'atom-workspace', - 'diagnostics:toggle-table', - () => { - atom.workspace.toggle(WORKSPACE_VIEW_URI); - }, - ); - return new UniversalDisposable( - atom.workspace.addOpener(uri => { - if (uri === WORKSPACE_VIEW_URI) { - return this._createDiagnosticsViewModel(); - } - }), - () => { - destroyItemWhere(item => item instanceof DiagnosticsViewModel); - }, - commandDisposable, - ); + registerOpenerAndCommand() { + const commandDisposable = atom.commands.add('atom-workspace', 'diagnostics:toggle-table', () => { + atom.workspace.toggle(_DiagnosticsViewModel().WORKSPACE_VIEW_URI); + }); + return new (_UniversalDisposable().default)(atom.workspace.addOpener(uri => { + if (uri === _DiagnosticsViewModel().WORKSPACE_VIEW_URI) { + return this._createDiagnosticsViewModel(); + } + }), () => { + (0, _destroyItemWhere().destroyItemWhere)(item => item instanceof _DiagnosticsViewModel().DiagnosticsViewModel); + }, commandDisposable); } - _registerActionsMenu(): IDisposable { - return atom.commands.add( - 'atom-text-editor', - 'diagnostics:show-actions-at-position', - () => { - const editor = atom.workspace.getActiveTextEditor(); - const {diagnosticUpdater} = this._model.state; - if (editor == null || diagnosticUpdater == null) { - return; - } - const position = editor.getCursorBufferPosition(); - const messagesAtPosition = this._getMessagesAtPosition( - editor, - position, - ); - if (messagesAtPosition.length === 0) { - return; - } - showActionsMenu( - editor, - position, - messagesAtPosition, - diagnosticUpdater, - ); - }, - ); + _registerActionsMenu() { + return atom.commands.add('atom-text-editor', 'diagnostics:show-actions-at-position', () => { + const editor = atom.workspace.getActiveTextEditor(); + const { + diagnosticUpdater + } = this._model.state; + + if (editor == null || diagnosticUpdater == null) { + return; + } + + const position = editor.getCursorBufferPosition(); + + const messagesAtPosition = this._getMessagesAtPosition(editor, position); + + if (messagesAtPosition.length === 0) { + return; + } + + (0, _showActionsMenu().default)(editor, position, messagesAtPosition, diagnosticUpdater); + }); } - _getStatusBarTile(): StatusBarTile { + _getStatusBarTile() { if (!this._statusBarTile) { - this._statusBarTile = new StatusBarTile(); + this._statusBarTile = new (_StatusBarTile().default)(); } + return this._statusBarTile; } - _getMessagesAtPosition( - editor: atom$TextEditor, - position: atom$Point, - ): Array { + _getMessagesAtPosition(editor, position) { const messagesForFile = this._fileDiagnostics.get(editor); + if (messagesForFile == null) { return []; } - return messagesForFile.filter( - message => message.range != null && message.range.containsPoint(position), - ); + + return messagesForFile.filter(message => message.range != null && message.range.containsPoint(position)); } + } -function gutterConsumeDiagnosticUpdates( - diagnosticUpdater: DiagnosticUpdater, -): IDisposable { - const subscriptions = new UniversalDisposable(); - subscriptions.add( - atom.workspace.observeTextEditors((editor: TextEditor) => { - const subscription = getEditorDiagnosticUpdates(editor, diagnosticUpdater) - .finally(() => { - subscriptions.remove(subscription); - }) - .subscribe(update => { - // Although the subscription should be cleaned up on editor destroy, - // the very act of destroying the editor can trigger diagnostic updates. - // Thus this callback can still be triggered after the editor is destroyed. - if (!editor.isDestroyed()) { - applyUpdateToEditor(editor, update, diagnosticUpdater); - } - }); - subscriptions.add(subscription); - }), - ); +function gutterConsumeDiagnosticUpdates(diagnosticUpdater) { + const subscriptions = new (_UniversalDisposable().default)(); + subscriptions.add(atom.workspace.observeTextEditors(editor => { + const subscription = getEditorDiagnosticUpdates(editor, diagnosticUpdater).finally(() => { + subscriptions.remove(subscription); + }).subscribe(update => { + // Although the subscription should be cleaned up on editor destroy, + // the very act of destroying the editor can trigger diagnostic updates. + // Thus this callback can still be triggered after the editor is destroyed. + if (!editor.isDestroyed()) { + (0, _gutter().applyUpdateToEditor)(editor, update, diagnosticUpdater); + } + }); + subscriptions.add(subscription); + })); return subscriptions; } -function addAtomCommands(diagnosticUpdater: DiagnosticUpdater): IDisposable { +function addAtomCommands(diagnosticUpdater) { const fixAllInCurrentFile = () => { const editor = atom.workspace.getActiveTextEditor(); + if (editor == null) { return; } + const path = editor.getPath(); + if (path == null) { return; } - analytics.track('diagnostics-autofix-all-in-file'); + + _analytics().default.track('diagnostics-autofix-all-in-file'); + diagnosticUpdater.applyFixesForFile(path); }; const openAllFilesWithErrors = () => { - analytics.track('diagnostics-panel-open-all-files-with-errors'); - observableFromSubscribeFunction(diagnosticUpdater.observeMessages) - .first() - .subscribe((messages: Array) => { - const errorsToOpen = getTopMostErrorLocationsByFilePath(messages); - - if (errorsToOpen.size > MAX_OPEN_ALL_FILES) { - atom.notifications.addError( - `Diagnostics: Will not open more than ${MAX_OPEN_ALL_FILES} files`, - ); - return; - } + _analytics().default.track('diagnostics-panel-open-all-files-with-errors'); - const column = 0; - errorsToOpen.forEach((line, uri) => goToLocation(uri, {line, column})); - }); + (0, _event().observableFromSubscribeFunction)(diagnosticUpdater.observeMessages).first().subscribe(messages => { + const errorsToOpen = getTopMostErrorLocationsByFilePath(messages); + + if (errorsToOpen.size > MAX_OPEN_ALL_FILES) { + atom.notifications.addError(`Diagnostics: Will not open more than ${MAX_OPEN_ALL_FILES} files`); + return; + } + + const column = 0; + errorsToOpen.forEach((line, uri) => (0, _goToLocation().goToLocation)(uri, { + line, + column + })); + }); }; - return new UniversalDisposable( - atom.commands.add( - 'atom-workspace', - 'diagnostics:fix-all-in-current-file', - fixAllInCurrentFile, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:open-all-files-with-errors', - openAllFilesWithErrors, - ), - new KeyboardShortcuts(diagnosticUpdater), - ); + return new (_UniversalDisposable().default)(atom.commands.add('atom-workspace', 'diagnostics:fix-all-in-current-file', fixAllInCurrentFile), atom.commands.add('atom-workspace', 'diagnostics:open-all-files-with-errors', openAllFilesWithErrors), new (_KeyboardShortcuts().default)(diagnosticUpdater)); } -function getTopMostErrorLocationsByFilePath( - messages: Array, -): Map { - const errorLocations: Map = new Map(); - +function getTopMostErrorLocationsByFilePath(messages) { + const errorLocations = new Map(); messages.forEach(message => { const filePath = message.filePath; - if (nuclideUri.endsWithSeparator(filePath)) { - return; - } - // If initialLine is N, Atom will navigate to line N+1. + if (_nuclideUri().default.endsWithSeparator(filePath)) { + return; + } // If initialLine is N, Atom will navigate to line N+1. // Flow sometimes reports a row of -1, so this ensures the line is at least one. - let line = Math.max(message.range ? message.range.start.row : 0, 0); + + let line = Math.max(message.range ? message.range.start.row : 0, 0); const prevMinLine = errorLocations.get(filePath); + if (prevMinLine != null) { line = Math.min(prevMinLine, line); } errorLocations.set(filePath, line); }); - return errorLocations; } -function getActiveEditorPaths(): Observable { +function getActiveEditorPaths() { const center = atom.workspace.getCenter(); - return ( - observableFromSubscribeFunction(center.observeActivePaneItem.bind(center)) - .map(paneItem => (isValidTextEditor(paneItem) ? paneItem : null)) - // We want the stream to contain the last valid text editor. Normally that means just ignoring - // non-editors, except initially, when there hasn't been an active editor yet. - .filter((paneItem, index) => paneItem != null || index === 0) - .switchMap(textEditor_ => { - const textEditor: ?atom$TextEditor = (textEditor_: any); - if (textEditor == null) { - return Observable.of(null); - } - // An observable that emits the editor path and then, when the editor's destroyed, null. - return Observable.concat( - Observable.of(textEditor.getPath()), - observableFromSubscribeFunction( - textEditor.onDidDestroy.bind(textEditor), - ) - .take(1) - .mapTo(null), - ); - }) - .distinctUntilChanged() - ); + return (0, _event().observableFromSubscribeFunction)(center.observeActivePaneItem.bind(center)).map(paneItem => (0, _textEditor().isValidTextEditor)(paneItem) ? paneItem : null) // We want the stream to contain the last valid text editor. Normally that means just ignoring + // non-editors, except initially, when there hasn't been an active editor yet. + .filter((paneItem, index) => paneItem != null || index === 0).switchMap(textEditor_ => { + const textEditor = textEditor_; + + if (textEditor == null) { + return _RxMin.Observable.of(null); + } // An observable that emits the editor path and then, when the editor's destroyed, null. + + + return _RxMin.Observable.concat(_RxMin.Observable.of(textEditor.getPath()), (0, _event().observableFromSubscribeFunction)(textEditor.onDidDestroy.bind(textEditor)).take(1).mapTo(null)); + }).distinctUntilChanged(); } -function getEditorDiagnosticUpdates( - editor: atom$TextEditor, - diagnosticUpdater: DiagnosticUpdater, -): Observable { - return observableFromSubscribeFunction(editor.onDidChangePath.bind(editor)) - .startWith(editor.getPath()) - .switchMap( - filePath => - filePath != null - ? observableFromSubscribeFunction(cb => - diagnosticUpdater.observeFileMessages(filePath, cb), - ) - : Observable.empty(), - ) - .map(diagnosticMessages => { - return { - ...diagnosticMessages, - messages: diagnosticMessages.messages.filter( - diagnostic => diagnostic.type !== 'Hint', - ), - }; - }) - .takeUntil( - observableFromSubscribeFunction(editor.onDidDestroy.bind(editor)), - ); +function getEditorDiagnosticUpdates(editor, diagnosticUpdater) { + return (0, _event().observableFromSubscribeFunction)(editor.onDidChangePath.bind(editor)).startWith(editor.getPath()).switchMap(filePath => filePath != null ? (0, _event().observableFromSubscribeFunction)(cb => diagnosticUpdater.observeFileMessages(filePath, cb)) : _RxMin.Observable.empty()).map(diagnosticMessages => { + return Object.assign({}, diagnosticMessages, { + messages: diagnosticMessages.messages.filter(diagnostic => diagnostic.type !== 'Hint') + }); + }).takeUntil((0, _event().observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor))); } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showActionsMenu.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showActionsMenu.js index 32ec4abd..aa9b1a41 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showActionsMenu.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showActionsMenu.js @@ -1,3 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = showActionsMenu; + +var _electron = _interopRequireDefault(require("electron")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _collection() { + const data = require("../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,105 +49,77 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +const { + remote +} = _electron.default; -import type { - DiagnosticUpdater, - DiagnosticMessage, -} from '../../atom-ide-diagnostics/lib/types'; - -import invariant from 'assert'; -import electron from 'electron'; -import {Observable} from 'rxjs'; -import {arrayCompact, arrayFlatten} from 'nuclide-commons/collection'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -const {remote} = electron; -invariant(remote != null); +if (!(remote != null)) { + throw new Error("Invariant violation: \"remote != null\""); +} const CODE_ACTIONS_TIMEOUT = 2000; -export default function showActionsMenu( - editor: TextEditor, - position: atom$Point, - messagesAtPosition: Array, - diagnosticUpdater: DiagnosticUpdater, -): IDisposable { +function showActionsMenu(editor, position, messagesAtPosition, diagnosticUpdater) { diagnosticUpdater.fetchCodeActions(editor, messagesAtPosition); + return new (_UniversalDisposable().default)((0, _event().observableFromSubscribeFunction)(cb => diagnosticUpdater.observeCodeActionsForMessage(cb)).filter(codeActionsForMessage => { + return messagesAtPosition.every(message => codeActionsForMessage.has(message)); + }).take(1).race(_RxMin.Observable.of(new WeakMap()).delay(CODE_ACTIONS_TIMEOUT)).subscribe(codeActionsForMessage => { + const menu = new remote.Menu(); + const fixes = (0, _collection().arrayCompact)(messagesAtPosition.map(message => { + const { + fix + } = message; - return new UniversalDisposable( - observableFromSubscribeFunction(cb => - diagnosticUpdater.observeCodeActionsForMessage(cb), - ) - .filter(codeActionsForMessage => { - return messagesAtPosition.every(message => - codeActionsForMessage.has(message), - ); - }) - .take(1) - .race(Observable.of(new WeakMap()).delay(CODE_ACTIONS_TIMEOUT)) - .subscribe(codeActionsForMessage => { - const menu = new remote.Menu(); - const fixes = arrayCompact( - messagesAtPosition.map(message => { - const {fix} = message; - if (fix == null) { - return null; - } - const fixTitle = fix.title == null ? 'Fix' : fix.title; - return { - title: `${fixTitle} (${message.providerName})`, - apply: () => diagnosticUpdater.applyFix(message), - }; - }), - ); - const actions = arrayFlatten( - messagesAtPosition.map(message => { - const codeActions = codeActionsForMessage.get(message); - if (codeActions == null) { - return []; - } - return Array.from(codeActions).map(([title, codeAction]) => ({ - title, - apply: () => codeAction.apply(), - })); - }), - ); - - [...fixes, ...actions].forEach(action => { - menu.append( - new remote.MenuItem({ - type: 'normal', - label: action.title, - click: () => { - action.apply(); - }, - }), - ); - }); - - const screenPosition = editor.screenPositionForBufferPosition(position); - const editorView = atom.views.getView(editor); - const pixelPosition = editorView.pixelPositionForScreenPosition( - screenPosition, - ); - // Pixel coordinates are relative to the editor's scroll view. - const scrollView = editorView.querySelector('.scroll-view'); - invariant(scrollView != null); - const boundingRect = scrollView.getBoundingClientRect(); - menu.popup({ - x: Math.round( - boundingRect.left + pixelPosition.left - editorView.getScrollLeft(), - ), - y: Math.round( - boundingRect.top + pixelPosition.top - editorView.getScrollTop(), - ), - positioningItem: 0, - async: true, - }); - }), - ); -} + if (fix == null) { + return null; + } + + const fixTitle = fix.title == null ? 'Fix' : fix.title; + return { + title: `${fixTitle} (${message.providerName})`, + apply: () => diagnosticUpdater.applyFix(message) + }; + })); + const actions = (0, _collection().arrayFlatten)(messagesAtPosition.map(message => { + const codeActions = codeActionsForMessage.get(message); + + if (codeActions == null) { + return []; + } + + return Array.from(codeActions).map(([title, codeAction]) => ({ + title, + apply: () => codeAction.apply() + })); + })); + [...fixes, ...actions].forEach(action => { + menu.append(new remote.MenuItem({ + type: 'normal', + label: action.title, + click: () => { + action.apply(); + } + })); + }); + const screenPosition = editor.screenPositionForBufferPosition(position); + const editorView = atom.views.getView(editor); + const pixelPosition = editorView.pixelPositionForScreenPosition(screenPosition); // Pixel coordinates are relative to the editor's scroll view. + + const scrollView = editorView.querySelector('.scroll-view'); + + if (!(scrollView != null)) { + throw new Error("Invariant violation: \"scrollView != null\""); + } + + const boundingRect = scrollView.getBoundingClientRect(); + menu.popup({ + x: Math.round(boundingRect.left + pixelPosition.left - editorView.getScrollLeft()), + y: Math.round(boundingRect.top + pixelPosition.top - editorView.getScrollTop()), + positioningItem: 0, + async: true + }); + })); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showAtomLinterWarning.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showAtomLinterWarning.js index 38ec763f..b8efdcab 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showAtomLinterWarning.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showAtomLinterWarning.js @@ -1,3 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = showAtomLinterWarning; + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,85 +47,58 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Observable} from 'rxjs'; - const LINTER_PACKAGE = 'linter'; -function observePackageIsEnabled(): Observable { - return Observable.merge( - Observable.of(atom.packages.isPackageActive(LINTER_PACKAGE)), - observableFromSubscribeFunction( - atom.packages.onDidActivatePackage.bind(atom.packages), - ) - .filter(pkg => pkg.name === LINTER_PACKAGE) - .mapTo(true), - observableFromSubscribeFunction( - atom.packages.onDidDeactivatePackage.bind(atom.packages), - ) - .filter(pkg => pkg.name === LINTER_PACKAGE) - .mapTo(false), - ); +function observePackageIsEnabled() { + return _RxMin.Observable.merge(_RxMin.Observable.of(atom.packages.isPackageActive(LINTER_PACKAGE)), (0, _event().observableFromSubscribeFunction)(atom.packages.onDidActivatePackage.bind(atom.packages)).filter(pkg => pkg.name === LINTER_PACKAGE).mapTo(true), (0, _event().observableFromSubscribeFunction)(atom.packages.onDidDeactivatePackage.bind(atom.packages)).filter(pkg => pkg.name === LINTER_PACKAGE).mapTo(false)); } -function disableLinter(): void { +function disableLinter() { atom.packages.disablePackage(LINTER_PACKAGE); } -function disableDiagnostics(): void { - featureConfig.set('use.atom-ide-diagnostics-ui', false); +function disableDiagnostics() { + _featureConfig().default.set('use.atom-ide-diagnostics-ui', false); } -export default function showAtomLinterWarning(): IDisposable { - const packageName = featureConfig.getPackageName(); - return new UniversalDisposable( - observePackageIsEnabled() - .distinctUntilChanged() - .switchMap(enabled => { - if (!enabled) { - return Observable.empty(); +function showAtomLinterWarning() { + const packageName = _featureConfig().default.getPackageName(); + + return new (_UniversalDisposable().default)(observePackageIsEnabled().distinctUntilChanged().switchMap(enabled => { + if (!enabled) { + return _RxMin.Observable.empty(); + } + + const notification = atom.notifications.addInfo('Choose a linter UI', { + description: 'You have both `linter` and `atom-ide-diagnostics` enabled, which will both ' + 'display lint results for Linter-based packages.\n\n' + 'To avoid duplicate results, please disable one of the packages.' + (packageName === 'nuclide' ? '\n\nNote that Flow and Hack errors are not compatible with `linter`.' : ''), + dismissable: true, + buttons: [{ + text: 'Disable Linter', + + onDidClick() { + disableLinter(); } - const notification = atom.notifications.addInfo('Choose a linter UI', { - description: - 'You have both `linter` and `atom-ide-diagnostics` enabled, which will both ' + - 'display lint results for Linter-based packages.\n\n' + - 'To avoid duplicate results, please disable one of the packages.' + - (packageName === 'nuclide' - ? '\n\nNote that Flow and Hack errors are not compatible with `linter`.' - : ''), - dismissable: true, - buttons: [ - { - text: 'Disable Linter', - onDidClick() { - disableLinter(); - }, - }, - { - text: 'Disable Diagnostics', - onDidClick() { - disableDiagnostics(); - atom.notifications.addInfo('Re-enabling Diagnostics', { - description: - 'To re-enable diagnostics, please enable "Diagnostics" under the "Enabled Features" ' + - `section in \`${packageName}\` settings.`, - }); - }, - }, - ], - }); - return Observable.create(() => ({ - unsubscribe() { - notification.dismiss(); - }, - })); - }) - .subscribe(), - ); -} + + }, { + text: 'Disable Diagnostics', + + onDidClick() { + disableDiagnostics(); + atom.notifications.addInfo('Re-enabling Diagnostics', { + description: 'To re-enable diagnostics, please enable "Diagnostics" under the "Enabled Features" ' + `section in \`${packageName}\` settings.` + }); + } + + }] + }); + return _RxMin.Observable.create(() => ({ + unsubscribe() { + notification.dismiss(); + } + + })); + }).subscribe()); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/sortDiagnostics.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/sortDiagnostics.js index caed4a0d..4ae0fc34 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/sortDiagnostics.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/sortDiagnostics.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sortDiagnostics; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,122 +13,61 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {Row} from 'nuclide-commons-ui/Table'; -import type { - DiagnosticMessageKind, - DiagnosticMessageType, -} from '../../atom-ide-diagnostics/lib/types'; -import type {DisplayDiagnostic} from './ui/DiagnosticsTable'; - -import invariant from 'assert'; - -type DiagnosticsComparison = ( - a: Row, - b: Row, -) => number; - /* * Sorts the diagnostics according to given column and sort direction */ -export default function sortDiagnostics( - diagnostics: Array>, - sortedColumnName: $Keys, - sortDescending: boolean, -): Array> { +function sortDiagnostics(diagnostics, sortedColumnName, sortDescending) { const compare = SORT_FUNCTIONS[sortedColumnName]; - invariant(compare != null); - // Don't sort in place. - const sorted = diagnostics.slice().sort(compare); - // We can't just reverse the sign of the comparison function because that would maintain the + + if (!(compare != null)) { + throw new Error("Invariant violation: \"compare != null\""); + } // Don't sort in place. + + + const sorted = diagnostics.slice().sort(compare); // We can't just reverse the sign of the comparison function because that would maintain the // ordering of "equal" items with respect to eachother. + return sortDescending ? sorted.reverse() : sorted; } const SORT_FUNCTIONS = { - classification: compose( - compareClassification, - compareSource, - comparePath, - compareDescription, - ), - providerName: compose( - compareSource, - compareClassification, - compareDescription, - comparePath, - ), - description: compose( - compareDescription, - compareSource, - compareClassification, - comparePath, - ), - dir: compose( - comparePath, - compareSource, - compareClassification, - compareDescription, - ), - location: compose( - compareBasename, - comparePath, - compareClassification, - compareSource, - compareDescription, - ), - line: compose( - compareBasename, - comparePath, - compareClassification, - compareSource, - compareDescription, - ), + classification: compose(compareClassification, compareSource, comparePath, compareDescription), + providerName: compose(compareSource, compareClassification, compareDescription, comparePath), + description: compose(compareDescription, compareSource, compareClassification, comparePath), + dir: compose(comparePath, compareSource, compareClassification, compareDescription), + location: compose(compareBasename, comparePath, compareClassification, compareSource, compareDescription), + line: compose(compareBasename, comparePath, compareClassification, compareSource, compareDescription) }; - /** * Compose comparison functions so that, when one identifies the items as equal, the subsequent * functions are used to resolve the abiguity. */ -function compose( - ...comparisons: Array -): DiagnosticsComparison { + +function compose(...comparisons) { return (a, b) => { for (const compare of comparisons) { const val = compare(a, b); + if (val !== 0) { return val; } } + return 0; }; } -function compareClassification( - a: Row, - b: Row, -): number { - return ( - compareClassificationKind( - a.data.classification.kind, - b.data.classification.kind, - ) || - compareClassificationSeverity( - a.data.classification.severity, - b.data.classification.severity, - ) - ); +function compareClassification(a, b) { + return compareClassificationKind(a.data.classification.kind, b.data.classification.kind) || compareClassificationSeverity(a.data.classification.severity, b.data.classification.severity); } const KIND_ORDER = ['review', 'lint']; -function compareClassificationKind( - a: ?DiagnosticMessageKind, - b: ?DiagnosticMessageKind, -): number { +function compareClassificationKind(a, b) { const aKind = a || 'lint'; const bKind = b || 'lint'; return KIND_ORDER.indexOf(aKind) - KIND_ORDER.indexOf(bKind); @@ -129,78 +75,68 @@ function compareClassificationKind( const SEVERITY_ORDER = ['Info', 'Warning', 'Error']; -function compareClassificationSeverity( - a: DiagnosticMessageType, - b: DiagnosticMessageType, -): number { +function compareClassificationSeverity(a, b) { return SEVERITY_ORDER.indexOf(a) - SEVERITY_ORDER.indexOf(b); } -function compareSource( - a: Row, - b: Row, -): number { +function compareSource(a, b) { return compareStrings(a.data.providerName, b.data.providerName); } -function compareDescription( - a: Row, - b: Row, -): number { +function compareDescription(a, b) { return compareStrings(a.data.description.text, b.data.description.text); } -function comparePath( - a: Row, - b: Row, -): number { +function comparePath(a, b) { const aLocation = a.data.location; const bLocation = b.data.location; + if (aLocation == null && bLocation == null) { return 0; } + if (aLocation == null) { return -1; } + if (bLocation == null) { return 1; } + const pathComparison = compareStrings(aLocation.fullPath, bLocation.fullPath); + if (pathComparison !== 0) { return pathComparison; } - const aLine = - aLocation.locationInFile == null ? 0 : aLocation.locationInFile.line; - const bLine = - bLocation.locationInFile == null ? 0 : bLocation.locationInFile.line; + + const aLine = aLocation.locationInFile == null ? 0 : aLocation.locationInFile.line; + const bLine = bLocation.locationInFile == null ? 0 : bLocation.locationInFile.line; return compareNumbers(aLine, bLine); } -function compareBasename( - a: Row, - b: Row, -): number { +function compareBasename(a, b) { const aLocationInFile = a.data.location && a.data.location.locationInFile; const bLocationInFile = b.data.location && b.data.location.locationInFile; + if (aLocationInFile == null && bLocationInFile == null) { return 0; } + if (aLocationInFile == null) { return -1; } + if (bLocationInFile == null) { return 1; } - return ( - compareStrings(aLocationInFile.basename, bLocationInFile.basename) || - compareNumbers(aLocationInFile.line, bLocationInFile.line) - ); + + return compareStrings(aLocationInFile.basename, bLocationInFile.basename) || compareNumbers(aLocationInFile.line, bLocationInFile.line); } -function compareStrings(a: string, b: string): number { +function compareStrings(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); } -function compareNumbers(a: number, b: number): number { +function compareNumbers(a, b) { return a - b; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/types.js index 3c7575bc..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/types.js @@ -1,40 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type { - DiagnosticMessage, - DiagnosticMessageKind, - UiConfig, -} from '../../atom-ide-diagnostics/lib/types'; - -// We group diagnostics based on kind and severity. -export type DiagnosticGroup = - | 'errors' - | 'warnings' - | 'info' - | 'review' - | 'action'; - -// State that's shared between every diagnostics panel instance. -export type GlobalViewState = { - diagnostics: Array, - pathToActiveTextEditor: ?NuclideUri, - filterByActiveTextEditor: boolean, - onFilterByActiveTextEditorChange: (isChecked: boolean) => mixed, - showDirectoryColumn: boolean, - autoVisibility: boolean, - showTraces: boolean, - onShowTracesChange: (isChecked: boolean) => mixed, - supportedMessageKinds: Set, - uiConfig: UiConfig, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsCodeActions.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsCodeActions.js index 4465e985..ef94b866 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsCodeActions.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsCodeActions.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = DiagnosticsCodeActions; + +var _atom = require("atom"); + +var React = _interopRequireWildcard(require("react")); + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _ButtonGroup() { + const data = require("../../../../../nuclide-commons-ui/ButtonGroup"); + + _ButtonGroup = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,59 +39,42 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {CodeAction} from '../../../atom-ide-code-actions/lib/types'; - -import {TextEditor} from 'atom'; -import * as React from 'react'; -import {Button} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; - // Maximum number of CodeActions to show for a given Diagnostic. const MAX_CODE_ACTIONS = 4; -export default function DiagnosticsCodeActions(props: { - codeActions: Map, -}): React.Element { - return ( -
- {Array.from(props.codeActions.entries()) - .splice(0, MAX_CODE_ACTIONS) - // TODO: (seansegal) T21130259 Display a "more" indicator when there are many CodeActions. - .map(([title, codeAction], i) => { - return ( - - - - ); - })} -
- ); +function DiagnosticsCodeActions(props) { + return React.createElement("div", { + className: "diagnostics-code-actions" + }, Array.from(props.codeActions.entries()).splice(0, MAX_CODE_ACTIONS) // TODO: (seansegal) T21130259 Display a "more" indicator when there are many CodeActions. + .map(([title, codeAction], i) => { + return React.createElement(_ButtonGroup().ButtonGroup, { + key: i + }, React.createElement(_Button().Button, { + className: "diagnostics-code-action-button", + size: "EXTRA_SMALL", + onClick: () => { + // TODO: (seansegal) T21130332 Display CodeAction status indicators + codeAction.apply().catch(handleCodeActionFailure).then(() => { + // Return focus to the editor after clicking. + const activeItem = atom.workspace.getActivePaneItem(); + + if (activeItem && activeItem instanceof _atom.TextEditor) { + activeItem.element.focus(); + } + }); + } + }, React.createElement("span", { + className: "inline-block" + }, title))); + })); } -function handleCodeActionFailure(error: ?Error) { +function handleCodeActionFailure(error) { atom.notifications.addWarning('Code action could not be applied', { description: error ? error.message : '', - dismissable: true, + dismissable: true }); -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js index 05d6032e..1d8541db 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js @@ -1,3 +1,54 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsMessageNoHeader = exports.DiagnosticsMessage = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _ButtonGroup() { + const data = require("../../../../../nuclide-commons-ui/ButtonGroup"); + + _ButtonGroup = function () { + return data; + }; + + return data; +} + +function _DiagnosticsMessageText() { + const data = require("./DiagnosticsMessageText"); + + _DiagnosticsMessageText = function () { + return data; + }; + + return data; +} + +function _DiagnosticsTraceItem() { + const data = require("./DiagnosticsTraceItem"); + + _DiagnosticsTraceItem = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,93 +57,76 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DiagnosticMessage} from '../../../atom-ide-diagnostics/lib/types'; - -import * as React from 'react'; -import {Button, ButtonTypes} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; -import {DiagnosticsMessageText} from './DiagnosticsMessageText'; -import {DiagnosticsTraceItem} from './DiagnosticsTraceItem'; - -type DiagnosticsMessageProps = { - // these are processed in traceElements below - /* eslint-disable react/no-unused-prop-types */ - message: DiagnosticMessage, - goToLocation: (path: string, line: number) => mixed, - fixer: (message: DiagnosticMessage) => void, - children?: React.Node, - /* eslint-enable react/no-unused-prop-types */ -}; - const PROVIDER_CLASS_NAME = { Error: 'highlight-error', Warning: 'highlight-warning', Info: 'highlight-info', - Hint: '', + Hint: '' }; -function diagnosticHeader(props: DiagnosticsMessageProps) { - const {message, fixer} = props; +function diagnosticHeader(props) { + const { + message, + fixer + } = props; const providerClassName = PROVIDER_CLASS_NAME[message.type]; let fixButton = null; + if (message.fix != null) { const applyFix = () => { fixer(message); }; + const speculative = message.fix.speculative === true; - const buttonType = speculative ? undefined : ButtonTypes.SUCCESS; - fixButton = ( - - ); + const buttonType = speculative ? undefined : _Button().ButtonTypes.SUCCESS; + fixButton = React.createElement(_Button().Button, { + buttonType: buttonType, + size: "EXTRA_SMALL", + onClick: applyFix + }, // flowlint-next-line sketchy-null-string:off + message.fix.title || 'Fix'); } - return ( -
- {fixButton} - {message.providerName} -
- ); + + return React.createElement("div", { + className: "diagnostics-popup-header" + }, React.createElement(_ButtonGroup().ButtonGroup, null, fixButton), React.createElement("span", { + className: providerClassName + }, message.providerName)); } -function traceElements(props: DiagnosticsMessageProps) { - const {message, goToLocation} = props; - return message.trace && message.trace.length ? ( -
- {message.trace.map((traceItem, i) => ( - - ))} -
- ) : null; +function traceElements(props) { + const { + message, + goToLocation + } = props; + return message.trace && message.trace.length ? React.createElement("div", { + className: "diagnostics-popup-trace" + }, message.trace.map((traceItem, i) => React.createElement(_DiagnosticsTraceItem().DiagnosticsTraceItem, { + key: i, + trace: traceItem, + goToLocation: goToLocation + }))) : null; } -export const DiagnosticsMessage = (props: DiagnosticsMessageProps) => { - return ( -
- {diagnosticHeader(props)} -
- -
- {traceElements(props)} - {props.children} -
- ); +const DiagnosticsMessage = props => { + return React.createElement("div", null, diagnosticHeader(props), React.createElement("div", { + className: "diagnostics-popup-message" + }, React.createElement(_DiagnosticsMessageText().DiagnosticsMessageText, { + message: props.message + })), traceElements(props), props.children); }; -export const DiagnosticsMessageNoHeader = (props: DiagnosticsMessageProps) => { - return ( -
- - {traceElements(props)} -
- ); +exports.DiagnosticsMessage = DiagnosticsMessage; + +const DiagnosticsMessageNoHeader = props => { + return React.createElement("div", { + className: "diagnostics-full-description-message" + }, React.createElement(_DiagnosticsMessageText().DiagnosticsMessageText, { + message: props.message + }), traceElements(props)); }; + +exports.DiagnosticsMessageNoHeader = DiagnosticsMessageNoHeader; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js index affbe472..3d8a218d 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js @@ -1,3 +1,29 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.separateUrls = separateUrls; +exports.DiagnosticsMessageText = void 0; + +var React = _interopRequireWildcard(require("react")); + +var _electron = require("electron"); + +function _dompurify() { + const data = _interopRequireDefault(require("dompurify")); + + _dompurify = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,126 +32,93 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import * as React from 'react'; -import {shell} from 'electron'; -import createDOMPurify from 'dompurify'; - -const domPurify = createDOMPurify(); - -type DiagnosticsMessageTextProps = { - preserveNewlines?: boolean, // defaults to true - message: { - html?: string, - text?: string, - }, -}; - -type UrlOrText = - | { - isUrl: true, - url: string, - } - | { - isUrl: false, - text: string, - }; +const domPurify = (0, _dompurify().default)(); // Exported for testing. -export function separateUrls(message: string): Array { +function separateUrls(message) { // Don't match periods at the end of URLs, because those are usually just to // end the sentence and not actually part of the URL. Optionally match // parameters following a question mark. - // first bit before query/fragment - const mainUrl = /https?:\/\/[\w/.%-]*[\w/-]/.source; - // characters allowed in query/fragment, disallow `.` at the end - const queryChars = /[\w-~%&+.!=:@/?]*[\w-~%&+!=:@/?]/.source; - const urlRegex = new RegExp( - `${mainUrl}(?:\\?${queryChars})?(?:#${queryChars})?`, - 'g', - ); + const mainUrl = /https?:\/\/[\w/.%-]*[\w/-]/.source; // characters allowed in query/fragment, disallow `.` at the end + const queryChars = /[\w-~%&+.!=:@/?]*[\w-~%&+!=:@/?]/.source; + const urlRegex = new RegExp(`${mainUrl}(?:\\?${queryChars})?(?:#${queryChars})?`, 'g'); const urls = message.match(urlRegex); const nonUrls = message.split(urlRegex); + const parts = [{ + isUrl: false, + text: nonUrls[0] + }]; - const parts: Array = [ - { - isUrl: false, - text: nonUrls[0], - }, - ]; for (let i = 1; i < nonUrls.length; i++) { - invariant(urls != null); + if (!(urls != null)) { + throw new Error("Invariant violation: \"urls != null\""); + } + parts.push({ isUrl: true, - url: urls[i - 1], + url: urls[i - 1] }); parts.push({ isUrl: false, - text: nonUrls[i], + text: nonUrls[i] }); } + return parts; } const LEADING_WHITESPACE_RE = /^\s+/; const NBSP = '\xa0'; -function renderRowWithLinks( - message: string, - rowIndex: number, - rows: Array, -): React.Element { - const messageWithWhitespace = message.replace( - LEADING_WHITESPACE_RE, - whitespace => NBSP.repeat(whitespace.length), - ); + +function renderRowWithLinks(message, rowIndex, rows) { + const messageWithWhitespace = message.replace(LEADING_WHITESPACE_RE, whitespace => NBSP.repeat(whitespace.length)); const parts = separateUrls(messageWithWhitespace).map((part, index) => { if (!part.isUrl) { return part.text; } else { const openUrl = () => { - shell.openExternal(part.url); + _electron.shell.openExternal(part.url); }; - return ( - - {part.url} - - ); + + return React.createElement("a", { + href: "#", + key: index, + onClick: openUrl + }, part.url); } }); - - return ( - // We need to use a span here instead of a div so that `text-overflow: ellipsis` works. - - {parts} - {rowIndex !== rows.length - 1 &&
} -
+ return (// We need to use a span here instead of a div so that `text-overflow: ellipsis` works. + React.createElement("span", { + key: rowIndex + }, parts, rowIndex !== rows.length - 1 && React.createElement("br", null)) ); } -export const DiagnosticsMessageText = (props: DiagnosticsMessageTextProps) => { - const {message} = props; +const DiagnosticsMessageText = props => { + const { + message + } = props; + if (message.html != null) { - return ( - - ); + return React.createElement("span", { + title: message.text, + dangerouslySetInnerHTML: { + __html: domPurify.sanitize(message.html) + } + }); } else if (message.text != null) { - const rows = - props.preserveNewlines !== false - ? message.text.split('\n') - : [message.text]; - return {rows.map(renderRowWithLinks)}; + const rows = props.preserveNewlines !== false ? message.text.split('\n') : [message.text]; + return React.createElement("span", { + title: message.text + }, rows.map(renderRowWithLinks)); } else { - return Diagnostic lacks message.; + return React.createElement("span", null, "Diagnostic lacks message."); } }; + +exports.DiagnosticsMessageText = DiagnosticsMessageText; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js index 1401c90d..9a2eb1dc 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js @@ -1,122 +1,144 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {DiagnosticMessage} from '../../../atom-ide-diagnostics/lib/types'; -import type {CodeAction} from '../../../atom-ide-code-actions/lib/types'; - -import * as React from 'react'; -import classnames from 'classnames'; -import analytics from 'nuclide-commons/analytics'; -import {mapUnion} from 'nuclide-commons/collection'; -import {DiagnosticsMessage} from './DiagnosticsMessage'; -import DiagnosticsCodeActions from './DiagnosticsCodeActions'; - -type DiagnosticsPopupProps = { - messages: Array, - goToLocation: (filePath: NuclideUri, line: number) => mixed, - fixer: (message: DiagnosticMessage) => void, - codeActionsForMessage?: Map>, -}; - -function renderMessage( - fixer: (message: DiagnosticMessage) => void, - goToLocation: (filePath: NuclideUri, line: number) => mixed, - codeActionsForMessage: ?Map>, - message: DiagnosticMessage, - index: number, -): React.Element { - const className = classnames( - // native-key-bindings and tabIndex=-1 are both needed to allow copying the text in the popup. - 'native-key-bindings', - 'diagnostics-popup-diagnostic', - { - 'diagnostics-popup-error': message.type === 'Error', - 'diagnostics-popup-warning': message.type === 'Warning', - 'diagnostics-popup-info': message.type === 'Info', - }, - ); +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsPopup = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _DiagnosticsMessage() { + const data = require("./DiagnosticsMessage"); + + _DiagnosticsMessage = function () { + return data; + }; + + return data; +} + +function _DiagnosticsCodeActions() { + const data = _interopRequireDefault(require("./DiagnosticsCodeActions")); + + _DiagnosticsCodeActions = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +function renderMessage(fixer, goToLocation, codeActionsForMessage, message, index) { + const className = (0, _classnames().default)( // native-key-bindings and tabIndex=-1 are both needed to allow copying the text in the popup. + 'native-key-bindings', 'diagnostics-popup-diagnostic', { + 'diagnostics-popup-error': message.type === 'Error', + 'diagnostics-popup-warning': message.type === 'Warning', + 'diagnostics-popup-info': message.type === 'Info' + }); const codeActions = getCodeActions(message, codeActionsForMessage); - return ( -
- - {codeActions && codeActions.size ? ( - - ) : null} - -
- ); + return React.createElement("div", { + className: className, + key: index, + tabIndex: -1 + }, React.createElement(_DiagnosticsMessage().DiagnosticsMessage, { + fixer: fixer, + goToLocation: goToLocation, + message: message + }, codeActions && codeActions.size ? React.createElement(_DiagnosticsCodeActions().default, { + codeActions: codeActions + }) : null)); } -function getCodeActions( - message: DiagnosticMessage, - codeActionsForMessage: ?Map>, -): ?Map { +function getCodeActions(message, codeActionsForMessage) { const codeActionMaps = []; + if (message.actions != null && message.actions.length > 0) { - codeActionMaps.push( - new Map( - message.actions.map(action => { - return [ - action.title, - { - async getTitle() { - return action.title; - }, - async apply() { - action.apply(); - }, - dispose() {}, - }, - ]; - }), - ), - ); + codeActionMaps.push(new Map(message.actions.map(action => { + return [action.title, { + async getTitle() { + return action.title; + }, + + async apply() { + action.apply(); + }, + + dispose() {} + + }]; + }))); } + if (codeActionsForMessage) { const actions = codeActionsForMessage.get(message); + if (actions != null) { codeActionMaps.push(actions); } } - return codeActionMaps.length > 0 ? mapUnion(...codeActionMaps) : null; -} -// TODO move LESS styles to nuclide-ui -export class DiagnosticsPopup extends React.Component { + return codeActionMaps.length > 0 ? (0, _collection().mapUnion)(...codeActionMaps) : null; +} // TODO move LESS styles to nuclide-ui + + +class DiagnosticsPopup extends React.Component { componentDidMount() { - analytics.track('diagnostics-show-popup', { + _analytics().default.track('diagnostics-show-popup', { // Note: there could be multiple providers here (but it's less common). - providerName: this.props.messages[0].providerName, + providerName: this.props.messages[0].providerName }); } render() { - const { + const _this$props = this.props, + { fixer, goToLocation, codeActionsForMessage, - messages, - ...rest - } = this.props; - return ( -
- {messages.map( - renderMessage.bind(null, fixer, goToLocation, codeActionsForMessage), - )} -
- ); + messages + } = _this$props, + rest = _objectWithoutProperties(_this$props, ["fixer", "goToLocation", "codeActionsForMessage", "messages"]); + + return React.createElement("div", Object.assign({ + className: "diagnostics-popup" + }, rest), messages.map(renderMessage.bind(null, fixer, goToLocation, codeActionsForMessage))); } + } + +exports.DiagnosticsPopup = DiagnosticsPopup; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js index 1b893bd0..80065bcf 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js @@ -1,3 +1,126 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _memoizeUntilChanged() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/memoizeUntilChanged")); + + _memoizeUntilChanged = function () { + return data; + }; + + return data; +} + +function _humanizePath() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-atom/humanizePath")); + + _humanizePath = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _Table() { + const data = require("../../../../../nuclide-commons-ui/Table"); + + _Table = function () { + return data; + }; + + return data; +} + +function _sortDiagnostics() { + const data = _interopRequireDefault(require("../sortDiagnostics")); + + _sortDiagnostics = function () { + return data; + }; + + return data; +} + +function _DiagnosticsMessage() { + const data = require("./DiagnosticsMessage"); + + _DiagnosticsMessage = function () { + return data; + }; + + return data; +} + +function _DiagnosticsMessageText() { + const data = require("./DiagnosticsMessageText"); + + _DiagnosticsMessageText = function () { + return data; + }; + + return data; +} + +function _Icon() { + const data = require("../../../../../nuclide-commons-ui/Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,161 +129,45 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - DiagnosticMessage, - DiagnosticMessageKind, - DiagnosticMessageType, -} from '../../../atom-ide-diagnostics/lib/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {Column, Row} from 'nuclide-commons-ui/Table'; -import type {IconName} from 'nuclide-commons-ui/Icon'; - -import classnames from 'classnames'; -import invariant from 'assert'; -import idx from 'idx'; -import memoizeUntilChanged from 'nuclide-commons/memoizeUntilChanged'; -import humanizePath from 'nuclide-commons-atom/humanizePath'; -import {insideOut, arrayEqual} from 'nuclide-commons/collection'; -import * as React from 'react'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import {Table} from 'nuclide-commons-ui/Table'; -import sortDiagnostics from '../sortDiagnostics'; -import {DiagnosticsMessageNoHeader} from './DiagnosticsMessage'; -import {DiagnosticsMessageText} from './DiagnosticsMessageText'; -import {Icon} from 'nuclide-commons-ui/Icon'; - const DIAGNOSTICS_TO_ROWS_TRACES_MAP = new WeakMap(); -const DIAGNOSTICS_TO_ROWS_NO_TRACES_MAP = new WeakMap(); - -// text is always used for sorting, while we render the element. -type DescriptionField = { - diagnostic: DiagnosticMessage, - showTraces: boolean, - text: string, - isPlainText: boolean, -}; - -type Location = {| - fullPath: NuclideUri, - locationInFile: ?{| - basename: string, - line: number, - |}, -|}; - -export type DisplayDiagnostic = { - +classification: { - kind: DiagnosticMessageKind, - severity: DiagnosticMessageType, - }, - +providerName: string, - +description: { - showTraces: boolean, - diagnostic: DiagnosticMessage, - text: string, - isPlainText: boolean, - }, - +dir: string, - +location: ?Location, - +line: ?number, -}; - -type ColumnName = $Keys; +const DIAGNOSTICS_TO_ROWS_NO_TRACES_MAP = new WeakMap(); // text is always used for sorting, while we render the element. // Maximum number of results to render in the table before truncating and displaying a "Max results // reached" message. const MAX_RESULTS_COUNT = 1000; -type Props = { - diagnostics: Array, - selectedMessage: ?DiagnosticMessage, - gotoMessageLocation: ( - message: DiagnosticMessage, - options: {|focusEditor: boolean|}, - ) => void, - selectMessage: (message: DiagnosticMessage) => void, - showFileName: ?boolean, - showDirectoryColumn: boolean, - showTraces: boolean, -}; - -type State = { - sortDescending: boolean, - sortedColumn: ColumnName, -}; +class DiagnosticsTable extends React.PureComponent { + constructor(props) { + super(props); // Memoize `_getRows()` -export default class DiagnosticsTable extends React.PureComponent< - Props, - State, -> { - _previousSelectedIndex: number = -1; - _table: ?Table; - - constructor(props: Props) { - super(props); - - // Memoize `_getRows()` - (this: any)._getRows = memoizeUntilChanged( - this._getRows, - (diagnostics, showTraces) => ({diagnostics, showTraces}), - (a, b) => - a.showTraces === b.showTraces && - arrayEqual(a.diagnostics, b.diagnostics), - ); + _initialiseProps.call(this); + this._getRows = (0, _memoizeUntilChanged().default)(this._getRows, (diagnostics, showTraces) => ({ + diagnostics, + showTraces + }), (a, b) => a.showTraces === b.showTraces && (0, _collection().arrayEqual)(a.diagnostics, b.diagnostics)); this.state = { sortDescending: true, - sortedColumn: 'classification', + sortedColumn: 'classification' }; } - _handleSort = (sortedColumn: ColumnName, sortDescending: boolean): void => { - this.setState({ - sortedColumn, - sortDescending, - }); - }; - - _handleSelectTableRow = ( - item: {diagnostic: DiagnosticMessage}, - index: number, - event: Event | SyntheticEvent<*>, - ): void => { - this.props.selectMessage(item.diagnostic); - // Users navigating with the keyboard may just be moving through items on their way to another. - // If they have pending pane items enabled, it's not a big deal if we open the editor anyway. - // But if they don't, we could wind up opening a ton of files they didn't even care about so, - // to be safe, we won't do anything in that case. - if ( - event.type !== 'click' && - !atom.config.get('core.allowPendingPaneItems') - ) { - return; - } - this.props.gotoMessageLocation(item.diagnostic, {focusEditor: false}); - }; - - _handleConfirmTableRow = (item: {diagnostic: DiagnosticMessage}): void => { - this.props.gotoMessageLocation(item.diagnostic, {focusEditor: true}); - }; - - _getColumns(): Array> { - const {showFileName, showDirectoryColumn} = this.props; - - // These need to add up to 1. + _getColumns() { + const { + showFileName, + showDirectoryColumn + } = this.props; // These need to add up to 1. // TODO: Update the Table component so that we can have more control over this (and provide // explicit pixel widths) + const TYPE_WIDTH = 0; const SOURCE_WIDTH = 0; const FILENAME_WIDTH = 0.3; const DIR_WIDTH = 0.15; const LINE_NUMBER_WIDTH = 0; - const filePathColumns = []; let descriptionWidth = 1 - (TYPE_WIDTH + SOURCE_WIDTH); @@ -172,7 +179,7 @@ export default class DiagnosticsTable extends React.PureComponent< title: 'Path', width: DIR_WIDTH, shouldRightAlign: true, - cellClassName: 'nuclide-diagnostics-ui-cell-dir', + cellClassName: 'nuclide-diagnostics-ui-cell-dir' }); descriptionWidth -= DIR_WIDTH; } @@ -182,7 +189,7 @@ export default class DiagnosticsTable extends React.PureComponent< key: 'location', title: 'File Name', width: FILENAME_WIDTH, - cellClassName: 'nuclide-diagnostics-ui-cell-filename', + cellClassName: 'nuclide-diagnostics-ui-cell-filename' }); descriptionWidth -= FILENAME_WIDTH; } else { @@ -193,151 +200,136 @@ export default class DiagnosticsTable extends React.PureComponent< title: 'Line', shouldRightAlign: true, width: LINE_NUMBER_WIDTH, - minWidth: 60, + minWidth: 60 }); descriptionWidth -= LINE_NUMBER_WIDTH; } - return [ - { - component: TypeComponent, - key: 'classification', - title: 'Type', - width: TYPE_WIDTH, - minWidth: 55, - cellClassName: 'nuclide-diagnostics-ui-cell-classification', - }, - { - key: 'providerName', - title: 'Source', - width: SOURCE_WIDTH, - minWidth: 100, - }, - { - component: this._renderDescription, - key: 'description', - title: 'Description', - width: descriptionWidth, - cellClassName: 'nuclide-diagnostics-ui-cell-description', - }, - ...filePathColumns, - ]; - } - - // False positive for this lint rule? + return [{ + component: TypeComponent, + key: 'classification', + title: 'Type', + width: TYPE_WIDTH, + minWidth: 55, + cellClassName: 'nuclide-diagnostics-ui-cell-classification' + }, { + key: 'providerName', + title: 'Source', + width: SOURCE_WIDTH, + minWidth: 100 + }, { + component: this._renderDescription, + key: 'description', + title: 'Description', + width: descriptionWidth, + cellClassName: 'nuclide-diagnostics-ui-cell-description' + }, ...filePathColumns]; + } // False positive for this lint rule? // eslint-disable-next-line react/no-unused-prop-types - _renderDescription = (props: {data: DescriptionField}) => { - const {showTraces, diagnostic, text, isPlainText} = props.data; - return showTraces - ? DiagnosticsMessageNoHeader({ - message: diagnostic, - goToLocation: (file: string, line: number) => - goToLocation(file, {line}), - fixer: () => {}, - }) - : DiagnosticsMessageText({ - preserveNewlines: showTraces, - message: {text, html: isPlainText ? undefined : text}, - }); - }; - _getSortOptions( - columns: Array>, - ): {|sortedColumn: ColumnName, sortDescending: boolean|} { + + _getSortOptions(columns) { // If the column the user sorted by has been removed, return the default sorting. We do this // (instead of updating the state) so that if the column gets added back we can return to // sorting by that. const columnKeys = columns.map(column => column.key); + if (!columnKeys.includes(this.state.sortedColumn)) { return { sortedColumn: 'classification', - sortDescending: true, + sortDescending: true }; - } - // Otherwise, return the sorting they've chosen. + } // Otherwise, return the sorting they've chosen. + + return { sortedColumn: this.state.sortedColumn, - sortDescending: this.state.sortDescending, + sortDescending: this.state.sortDescending }; } - render(): React.Node { - const {diagnostics, selectedMessage, showTraces} = this.props; + render() { + const { + diagnostics, + selectedMessage, + showTraces + } = this.props; + const columns = this._getColumns(); - const {sortedColumn, sortDescending} = this._getSortOptions(columns); - const diagnosticRows = this._getRows(diagnostics, showTraces); - let sortedRows = this._sortRows( - diagnosticRows, + + const { sortedColumn, - sortDescending, - ); + sortDescending + } = this._getSortOptions(columns); + + const diagnosticRows = this._getRows(diagnostics, showTraces); + + let sortedRows = this._sortRows(diagnosticRows, sortedColumn, sortDescending); + let maxResultsMessage; + if (sortedRows.length > MAX_RESULTS_COUNT) { sortedRows = sortedRows.slice(0, MAX_RESULTS_COUNT); - maxResultsMessage = ( -
- Max results ({MAX_RESULTS_COUNT}) reached. Fix diagnostics or show - only diagnostics for the current file to view more. -
- ); + maxResultsMessage = React.createElement("div", { + className: "highlight-warning diagnostics-ui-table-message" + }, "Max results (", MAX_RESULTS_COUNT, ") reached. Fix diagnostics or show only diagnostics for the current file to view more."); } + const selectedIndex = this._findSelectedIndex(selectedMessage, sortedRows); - return ( -
-
{ - this._table = table; - }} - collapsable={true} - columns={columns} - emptyComponent={EmptyComponent} - fixedHeader={true} - maxBodyHeight="99999px" - rows={sortedRows} - sortable={true} - onSort={this._handleSort} - sortedColumn={sortedColumn} - sortDescending={sortDescending} - selectable={true} - selectedIndex={selectedIndex} - onSelect={this._handleSelectTableRow} - onConfirm={this._handleConfirmTableRow} - enableKeyboardNavigation={true} - /> - {maxResultsMessage} - - ); + + return React.createElement("div", { + className: (0, _classnames().default)({ + 'diagnostics-ui-table-container': true, + 'diagnostics-ui-table-container-empty': sortedRows.length === 0 + }) + }, React.createElement(_Table().Table, { + ref: table => { + this._table = table; + }, + collapsable: true, + columns: columns, + emptyComponent: EmptyComponent, + fixedHeader: true, + maxBodyHeight: "99999px", + rows: sortedRows, + sortable: true, + onSort: this._handleSort, + sortedColumn: sortedColumn, + sortDescending: sortDescending, + selectable: true, + selectedIndex: selectedIndex, + onSelect: this._handleSelectTableRow, + onConfirm: this._handleConfirmTableRow, + enableKeyboardNavigation: true + }), maxResultsMessage); } - focus(): void { + focus() { if (this._table != null) { this._table.focus(); } } - _findSelectedIndex( - selectedMessage: ?DiagnosticMessage, - rows: Array>, - ): number { + _findSelectedIndex(selectedMessage, rows) { if (selectedMessage == null) { return -1; } let bestRank = -1; - let bestRankedIndex = -1; + let bestRankedIndex = -1; // Look for the closest match, starting with the previously selected index. + + for (const [row, i] of (0, _collection().insideOut)(rows, this._previousSelectedIndex)) { + const { + diagnostic + } = row.data.description; - // Look for the closest match, starting with the previously selected index. - for (const [row, i] of insideOut(rows, this._previousSelectedIndex)) { - const {diagnostic} = row.data.description; if (diagnostic === selectedMessage) { bestRankedIndex = i; break; } + const rank = compareMessages(diagnostic, selectedMessage); + if (rank != null && rank > bestRank) { bestRank = rank; bestRankedIndex = i; @@ -352,96 +344,153 @@ export default class DiagnosticsTable extends React.PureComponent< return bestRankedIndex; } - _getRows( - diagnostics: Array, - showTraces: boolean, - ): Array> { - const diagnosticsToRows = showTraces - ? DIAGNOSTICS_TO_ROWS_TRACES_MAP - : DIAGNOSTICS_TO_ROWS_NO_TRACES_MAP; + _getRows(diagnostics, showTraces) { + const diagnosticsToRows = showTraces ? DIAGNOSTICS_TO_ROWS_TRACES_MAP : DIAGNOSTICS_TO_ROWS_NO_TRACES_MAP; return diagnostics.map(diagnostic => { let row = diagnosticsToRows.get(diagnostic); + if (row == null) { - const {dir, location} = getLocation(diagnostic); + var _ref; + + const { + dir, + location + } = getLocation(diagnostic); row = { data: { classification: { kind: diagnostic.kind || 'lint', - severity: diagnostic.type, + severity: diagnostic.type }, providerName: diagnostic.providerName, - description: { + description: Object.assign({ showTraces, - diagnostic, - ...getMessageContent(diagnostic, showTraces), - }, + diagnostic + }, getMessageContent(diagnostic, showTraces)), dir, location, diagnostic, - line: idx(location, _ => _.locationInFile.line), - }, + line: (_ref = location) != null ? (_ref = _ref.locationInFile) != null ? _ref.line : _ref : _ref + } }; diagnosticsToRows.set(diagnostic, row); } + return row; }); - } + } // TODO: Memoize this so we don't recompute unnecessarily. + - // TODO: Memoize this so we don't recompute unnecessarily. - _sortRows( - rows: Array>, - sortedColumn: $Keys, - descending: boolean, - ): Array> { - return sortDiagnostics(rows, sortedColumn, descending); + _sortRows(rows, sortedColumn, descending) { + return (0, _sortDiagnostics().default)(rows, sortedColumn, descending); } + } -const EmptyComponent = () => ( -
No diagnostic messages
-); +exports.default = DiagnosticsTable; + +var _initialiseProps = function () { + this._previousSelectedIndex = -1; -type Classification = { - kind: DiagnosticMessageKind, - severity: DiagnosticMessageType, + this._handleSort = (sortedColumn, sortDescending) => { + this.setState({ + sortedColumn, + sortDescending + }); + }; + + this._handleSelectTableRow = (item, index, event) => { + this.props.selectMessage(item.diagnostic); // Users navigating with the keyboard may just be moving through items on their way to another. + // If they have pending pane items enabled, it's not a big deal if we open the editor anyway. + // But if they don't, we could wind up opening a ton of files they didn't even care about so, + // to be safe, we won't do anything in that case. + + if (event.type !== 'click' && !atom.config.get('core.allowPendingPaneItems')) { + return; + } + + this.props.gotoMessageLocation(item.diagnostic, { + focusEditor: false + }); + }; + + this._handleConfirmTableRow = item => { + this.props.gotoMessageLocation(item.diagnostic, { + focusEditor: true + }); + }; + + this._renderDescription = props => { + const { + showTraces, + diagnostic, + text, + isPlainText + } = props.data; + return showTraces ? (0, _DiagnosticsMessage().DiagnosticsMessageNoHeader)({ + message: diagnostic, + goToLocation: (file, line) => (0, _goToLocation().goToLocation)(file, { + line + }), + fixer: () => {} + }) : (0, _DiagnosticsMessageText().DiagnosticsMessageText)({ + preserveNewlines: showTraces, + message: { + text, + html: isPlainText ? undefined : text + } + }); + }; }; -function TypeComponent(props: {data: Classification}): React.Element { +const EmptyComponent = () => React.createElement("div", { + className: "diagnostics-ui-empty-component" +}, "No diagnostic messages"); + +function TypeComponent(props) { const classification = props.data; const iconName = getIconName(classification); - return ; + return React.createElement(_Icon().Icon, { + icon: iconName + }); } -function getIconName(classification: Classification): IconName { - const {kind, severity} = classification; +function getIconName(classification) { + const { + kind, + severity + } = classification; + if (kind === 'review') { return 'nuclicon-comment-discussion'; } - invariant(severity !== 'Hint'); + + if (!(severity !== 'Hint')) { + throw new Error("Invariant violation: \"severity !== 'Hint'\""); + } + switch (severity) { case 'Warning': return 'nuclicon-warning'; + case 'Error': return 'nuclicon-error'; + case 'Info': return 'info'; + default: - (severity: empty); + severity; throw new Error(`Invalid severity: ${severity}`); } } -function getMessageContent( - diagnostic: DiagnosticMessage, - showTraces: boolean, -): {text: string, isPlainText: boolean} { +function getMessageContent(diagnostic, showTraces) { let text = ''; let isPlainText = true; const traces = diagnostic.trace || []; - const allMessages: Array<{html?: string, text?: string}> = [ - diagnostic, - ...traces, - ]; + const allMessages = [diagnostic, ...traces]; + for (const message of allMessages) { // TODO: A mix of html and text diagnostics will yield a wonky sort ordering. if (message.html != null) { @@ -453,100 +502,111 @@ function getMessageContent( throw new Error('Neither text nor html property defined on message'); } } - return {text: text.trim(), isPlainText}; + + return { + text: text.trim(), + isPlainText + }; } -function DirComponent(props: {data: string}): React.Element { - return ( - // We're abusing `direction: rtl` here so we need the LRM to keep the slash on the right. -
- ‎{nuclideUri.normalizeDir(props.data)}‎ -
+function DirComponent(props) { + return (// We're abusing `direction: rtl` here so we need the LRM to keep the slash on the right. + React.createElement("div", { + className: "nuclide-diagnostics-ui-dir-cell-contents" + }, "\u200E", _nuclideUri().default.normalizeDir(props.data), "\u200E") ); } -function FilenameComponent(props: {data: ?Location}): React.Element { +function FilenameComponent(props) { const locationInFile = props.data && props.data.locationInFile; + if (locationInFile == null) { // This is a project diagnostic. - return {DASH}; + return React.createElement("span", null, DASH); } - const {basename, line} = locationInFile; - return ( - - {basename} - :{line} - - ); + + const { + basename, + line + } = locationInFile; + return React.createElement("span", null, basename, React.createElement("span", { + className: "nuclide-diagnostics-ui-line-number" + }, ":", line)); } -function LineNumberComponent(props: {data: ?number}): React.Element { - const line = props.data; - // Show a dash if this is a project diagnostic. - return {line == null ? DASH : line}; +function LineNumberComponent(props) { + const line = props.data; // Show a dash if this is a project diagnostic. + + return React.createElement("span", null, line == null ? DASH : line); } -function getLocation( - diagnostic: DiagnosticMessage, -): {dir: string, location: ?Location} { - const {filePath, range} = diagnostic; +function getLocation(diagnostic) { + const { + filePath, + range + } = diagnostic; const line = range ? range.start.row + 1 : 0; + const humanized = (0, _humanizePath().default)(filePath); - const humanized = humanizePath(filePath); - if (nuclideUri.endsWithSeparator(humanized)) { + if (_nuclideUri().default.endsWithSeparator(humanized)) { // It's a directory. return { dir: humanized, location: { fullPath: filePath, - locationInFile: null, - }, + locationInFile: null + } }; } - const {dir, base: basename} = nuclideUri.parsePath(humanized); + const { + dir, + base: basename + } = _nuclideUri().default.parsePath(humanized); + return { dir, location: { fullPath: filePath, - locationInFile: {basename, line}, - }, + locationInFile: { + basename, + line + } + } }; } - /** * Compute a number indicating the relative similarity of two messages. The smaller the number, the * more similar. (`null` indicates not at all similar.) */ -function compareMessages(a: DiagnosticMessage, b: DiagnosticMessage): ?number { + + +function compareMessages(a, b) { const aKind = a.kind || 'lint'; const bKind = b.kind || 'lint'; const aFilePath = a.filePath; const bFilePath = b.filePath; - if ( - aKind !== bKind || - a.providerName !== b.providerName || - a.type !== b.type || - aFilePath !== bFilePath - ) { + + if (aKind !== bKind || a.providerName !== b.providerName || a.type !== b.type || aFilePath !== bFilePath) { return null; } + const aRange = a.range; const bRange = b.range; if (Boolean(aRange) !== Boolean(bRange)) { return null; - } + } // Neither has a range, but they have the same text and they're for the same file. + - // Neither has a range, but they have the same text and they're for the same file. if (aRange == null || bRange == null) { return 0; - } - - // TODO: This could be better if we also took into account the column and end start and column, + } // TODO: This could be better if we also took into account the column and end start and column, // but it's probably good enough. (How likely are messages with the same text starting on the same // row?) + + return Math.abs(aRange.start.row - bRange.start.row); } -const DASH = '\u2014'; +const DASH = '\u2014'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTraceItem.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTraceItem.js index 562bb244..c44ad789 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTraceItem.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTraceItem.js @@ -1,3 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsTraceItem = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _DiagnosticsMessageText() { + const data = require("./DiagnosticsMessageText"); + + _DiagnosticsMessageText = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,50 +27,41 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +// TODO move LESS styles to nuclide-ui +const DiagnosticsTraceItem = props => { + const { + trace, + goToLocation + } = props; + let locSpan = null; // Local variable so that the type refinement holds in the onClick handler. -import type {DiagnosticTrace} from '../../../atom-ide-diagnostics/lib/types'; - -import * as React from 'react'; -import {DiagnosticsMessageText} from './DiagnosticsMessageText'; - -type DiagnosticsTraceItemProps = { - trace: DiagnosticTrace, - goToLocation: (path: string, line: number) => mixed, -}; + const path = trace.filePath; // flowlint-next-line sketchy-null-string:off -// TODO move LESS styles to nuclide-ui -export const DiagnosticsTraceItem = (props: DiagnosticsTraceItemProps) => { - const {trace, goToLocation} = props; - let locSpan = null; - // Local variable so that the type refinement holds in the onClick handler. - const path = trace.filePath; - // flowlint-next-line sketchy-null-string:off if (path) { const [, relativePath] = atom.project.relativizePath(path); let locString = relativePath; + if (trace.range) { locString += `:${trace.range.start.row + 1}`; } - const onClick = (event: SyntheticMouseEvent<>) => { + + const onClick = event => { event.stopPropagation(); goToLocation(path, Math.max(trace.range ? trace.range.start.row : 0, 0)); }; - locSpan = ( - - :{' '} - - {locString} - - - ); + + locSpan = React.createElement("span", null, ":", ' ', React.createElement("a", { + href: "#", + onClick: onClick + }, locString)); } - return ( -
- - {locSpan} -
- ); + + return React.createElement("div", null, React.createElement(_DiagnosticsMessageText().DiagnosticsMessageText, { + message: trace + }), locSpan); }; + +exports.DiagnosticsTraceItem = DiagnosticsTraceItem; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsView.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsView.js index c3bd83c6..24ec3926 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsView.js @@ -1,3 +1,156 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _analytics() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _DiagnosticsTable() { + const data = _interopRequireDefault(require("./DiagnosticsTable")); + + _DiagnosticsTable = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _showModal() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/showModal")); + + _showModal = function () { + return data; + }; + + return data; +} + +function _Toggle() { + const data = require("../../../../../nuclide-commons-ui/Toggle"); + + _Toggle = function () { + return data; + }; + + return data; +} + +function _Toolbar() { + const data = require("../../../../../nuclide-commons-ui/Toolbar"); + + _Toolbar = function () { + return data; + }; + + return data; +} + +function _ToolbarLeft() { + const data = require("../../../../../nuclide-commons-ui/ToolbarLeft"); + + _ToolbarLeft = function () { + return data; + }; + + return data; +} + +function _ToolbarRight() { + const data = require("../../../../../nuclide-commons-ui/ToolbarRight"); + + _ToolbarRight = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _ButtonGroup() { + const data = require("../../../../../nuclide-commons-ui/ButtonGroup"); + + _ButtonGroup = function () { + return data; + }; + + return data; +} + +function _FilterButton() { + const data = _interopRequireDefault(require("./FilterButton")); + + _FilterButton = function () { + return data; + }; + + return data; +} + +function _RegExpFilter() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/RegExpFilter")); + + _RegExpFilter = function () { + return data; + }; + + return data; +} + +function _SettingsModal() { + const data = _interopRequireDefault(require("./SettingsModal")); + + _SettingsModal = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,230 +159,155 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - DiagnosticMessage, - DiagnosticMessageKind, - UiConfig, -} from '../../../atom-ide-diagnostics/lib/types'; -import type {DiagnosticGroup} from '../types'; -import type { - RegExpFilterChange, - RegExpFilterValue, -} from 'nuclide-commons-ui/RegExpFilter'; - -import analytics from 'nuclide-commons/analytics'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import DiagnosticsTable from './DiagnosticsTable'; -import nullthrows from 'nullthrows'; -import showModal from 'nuclide-commons-ui/showModal'; -import {Toggle} from 'nuclide-commons-ui/Toggle'; -import {Toolbar} from 'nuclide-commons-ui/Toolbar'; -import {ToolbarLeft} from 'nuclide-commons-ui/ToolbarLeft'; -import {ToolbarRight} from 'nuclide-commons-ui/ToolbarRight'; -import * as React from 'react'; -import {Button, ButtonSizes} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; -import FilterButton from './FilterButton'; -import RegExpFilter from 'nuclide-commons-ui/RegExpFilter'; -import SettingsModal from './SettingsModal'; - -export type Props = { - diagnostics: Array, - filterByActiveTextEditor: boolean, - onFilterByActiveTextEditorChange: (isChecked: boolean) => mixed, - showDirectoryColumn: boolean, - showTraces: boolean, - onShowTracesChange: (isChecked: boolean) => mixed, - gotoMessageLocation: ( - message: DiagnosticMessage, - options: {|focusEditor: boolean|}, - ) => void, - selectMessage: (message: DiagnosticMessage) => void, - selectedMessage: ?DiagnosticMessage, - supportedMessageKinds: Set, - uiConfig: UiConfig, - isVisible: boolean, - // Used by the DiagnosticsViewModel. - autoVisibility: boolean, // eslint-disable-line react/no-unused-prop-types - - hiddenGroups: Set, - onTypeFilterChange: (type: DiagnosticGroup) => mixed, - textFilter: RegExpFilterValue, - onTextFilterChange: (change: RegExpFilterChange) => mixed, -}; - /** * Dismissable panel that displays the diagnostics from nuclide-diagnostics-store. */ -export default class DiagnosticsView extends React.Component { - _diagnosticsTableWrapperEl: ?HTMLDivElement; - _disposables: ?UniversalDisposable; - _filterComponent: ?RegExpFilter; - _table: ?DiagnosticsTable; +class DiagnosticsView extends React.Component { + constructor(...args) { + var _temp; + + return _temp = super(...args), this._showSettings = () => { + (0, _showModal().default)(() => React.createElement(_SettingsModal().default, { + config: this.props.uiConfig + })); + }, this._handleShowTracesChange = isChecked => { + _analytics().default.track('diagnostics-panel-toggle-show-traces', { + isChecked: isChecked.toString() + }); + + this.props.onShowTracesChange.call(null, isChecked); + }, this._handleFilterByActiveTextEditorChange = shouldFilter => { + _analytics().default.track('diagnostics-panel-toggle-current-file', { + isChecked: shouldFilter.toString() + }); + + this.props.onFilterByActiveTextEditorChange.call(null, shouldFilter); + }, this._openAllFilesWithErrors = () => { + atom.commands.dispatch(atom.views.getView(atom.workspace), 'diagnostics:open-all-files-with-errors'); + }, this._handleFocus = event => { + if (this._table == null) { + return; + } - shouldComponentUpdate(nextProps: Props): boolean { + let el = event.target; + + while (el != null) { + if (el.tagName === 'INPUT' || el.tagName === 'BUTTON') { + return; + } + + el = el.parentElement; + } + + this._table.focus(); + }, _temp; + } + + shouldComponentUpdate(nextProps) { return nextProps.isVisible; } componentDidMount() { - this._disposables = new UniversalDisposable( - atom.commands.add( - nullthrows(this._diagnosticsTableWrapperEl), - 'atom-ide:filter', - () => this._focusFilter(), - ), - ); + this._disposables = new (_UniversalDisposable().default)(atom.commands.add((0, _nullthrows().default)(this._diagnosticsTableWrapperEl), 'atom-ide:filter', () => this._focusFilter())); } componentWillUnmount() { - nullthrows(this._disposables).dispose(); + (0, _nullthrows().default)(this._disposables).dispose(); } - render(): React.Element { - const {diagnostics, showDirectoryColumn, showTraces} = this.props; - + render() { + const { + diagnostics, + showDirectoryColumn, + showTraces + } = this.props; const groups = ['errors', 'warnings', 'info']; + if (this.props.supportedMessageKinds.has('review')) { groups.push('review'); } + if (this.props.supportedMessageKinds.has('action')) { groups.push('action'); } - const showFullDescriptionToggle = diagnostics.find( - diagnostic => - // flowlint-next-line sketchy-null-string:off - diagnostic.trace || (diagnostic.text && diagnostic.text.includes('\n')), - ); - - return ( -
- - - - {groups.map(group => ( - { - this.props.onTypeFilterChange(group); - }} - /> - ))} - - (this._filterComponent = component)} - value={this.props.textFilter} - onChange={this.props.onTextFilterChange} - /> - {/* TODO: This will probably change to a dropdown to also accommodate Head Changes */} - - - - {showFullDescriptionToggle ? ( - - ) : null} - -
- ); + const showFullDescriptionToggle = diagnostics.find(diagnostic => // flowlint-next-line sketchy-null-string:off + diagnostic.trace || diagnostic.text && diagnostic.text.includes('\n')); + return React.createElement("div", { + onFocus: this._handleFocus, + tabIndex: -1, + style: { + display: 'flex', + flex: 1, + flexDirection: 'column', + width: '100%' + } + }, React.createElement(_Toolbar().Toolbar, { + location: "top" + }, React.createElement(_ToolbarLeft().ToolbarLeft, null, React.createElement(_ButtonGroup().ButtonGroup, { + className: "inline-block" + }, groups.map(group => React.createElement(_FilterButton().default, { + key: group, + group: group, + selected: !this.props.hiddenGroups.has(group), + onClick: () => { + this.props.onTypeFilterChange(group); + } + }))), React.createElement(_RegExpFilter().default, { + ref: component => this._filterComponent = component, + value: this.props.textFilter, + onChange: this.props.onTextFilterChange + }), React.createElement(_Toggle().Toggle, { + className: "inline-block", + onChange: this._handleFilterByActiveTextEditorChange, + toggled: this.props.filterByActiveTextEditor, + label: "Current File Only" + })), React.createElement(_ToolbarRight().ToolbarRight, null, showFullDescriptionToggle ? React.createElement(_Toggle().Toggle, { + className: "inline-block", + onChange: this._handleShowTracesChange, + toggled: this.props.showTraces, + label: "Full Description" + }) : null, React.createElement(_Button().Button, { + onClick: this._openAllFilesWithErrors, + size: _Button().ButtonSizes.SMALL, + disabled: diagnostics.length === 0, + className: "inline-block", + title: "Open All" + }, "Open All"), React.createElement(_Button().Button, { + icon: "gear", + size: _Button().ButtonSizes.SMALL, + onClick: this._showSettings + }))), React.createElement("div", { + className: "atom-ide-filterable", + ref: el => this._diagnosticsTableWrapperEl = el, + style: { + display: 'flex', + flexDirection: 'column' + } + }, React.createElement(_DiagnosticsTable().default, { + ref: table => { + this._table = table; + }, + showFileName: !this.props.filterByActiveTextEditor, + diagnostics: diagnostics, + showDirectoryColumn: showDirectoryColumn, + showTraces: showTraces, + selectedMessage: this.props.selectedMessage, + selectMessage: this.props.selectMessage, + gotoMessageLocation: this.props.gotoMessageLocation + }))); } - _showSettings = (): void => { - showModal(() => ); - }; - - _handleShowTracesChange = (isChecked: boolean): void => { - analytics.track('diagnostics-panel-toggle-show-traces', { - isChecked: isChecked.toString(), - }); - this.props.onShowTracesChange.call(null, isChecked); - }; - - _handleFilterByActiveTextEditorChange = (shouldFilter: boolean): void => { - analytics.track('diagnostics-panel-toggle-current-file', { - isChecked: shouldFilter.toString(), - }); - this.props.onFilterByActiveTextEditorChange.call(null, shouldFilter); - }; - - _openAllFilesWithErrors = (): void => { - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'diagnostics:open-all-files-with-errors', - ); - }; - - _focusFilter(): void { + _focusFilter() { if (this._filterComponent != null) { this._filterComponent.focus(); } } - _handleFocus = (event: SyntheticMouseEvent<*>): void => { - if (this._table == null) { - return; - } - let el = event.target; - while (el != null) { - if (el.tagName === 'INPUT' || el.tagName === 'BUTTON') { - return; - } - el = (el: any).parentElement; - } - this._table.focus(); - }; } + +exports.default = DiagnosticsView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/FilterButton.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/FilterButton.js index a7885750..1bc4cbf9 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/FilterButton.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/FilterButton.js @@ -1,3 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = FilterButton; + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function GroupUtils() { + const data = _interopRequireWildcard(require("../GroupUtils")); + + GroupUtils = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,33 +37,23 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {DiagnosticGroup} from '../types'; - -import {Button, ButtonSizes} from 'nuclide-commons-ui/Button'; -import * as React from 'react'; -import * as GroupUtils from '../GroupUtils'; - -type Props = {| - group: DiagnosticGroup, - selected: boolean, - onClick: () => mixed, -|}; - -export default function FilterButton(props: Props): React.Node { - const {selected, group} = props; - const displayName = GroupUtils.getDisplayName(group); +function FilterButton(props) { + const { + selected, + group + } = props; + const displayName = GroupUtils().getDisplayName(group); const title = props.selected ? `Hide ${displayName}` : `Show ${displayName}`; - return ( - - - - ); + } // TODO: display actual diff output here. + + + return React.createElement("div", null, "This refactoring will affect ", editCount.size, " files. Confirm?", React.createElement("div", { + // Make the text copyable + selectable. + className: "nuclide-refactorizer-confirm-list native-key-bindings", + tabIndex: -1 + }, React.createElement(_Tree().TreeList, null, Array.from(editCount).map(([path, count]) => React.createElement(_Tree().TreeItem, { + key: path + }, React.createElement(_PathWithFileIcon().default, { + className: 'nuclide-refactorizer-confirm-list-item', + path: path + }, React.createElement(_Icon().Icon, { + className: "nuclide-refactorizer-diff-preview-icon", + onClick: () => { + this._diffPreview(path, response); + }, + icon: "diff" + }), React.createElement("span", { + className: "nuclide-refactorizer-confirm-list-path" + }, (0, _projects().getAtomProjectRelativePath)(path)), ' ', "(", count, " ", (0, _string().pluralize)('change', count), ")"))))), React.createElement("div", { + style: { + display: 'flex', + justifyContent: 'flex-end' + } + }, React.createElement(_Button().Button, { + buttonType: _Button().ButtonTypes.PRIMARY, + onClick: this._execute, + autoFocus: true + }, "Confirm"))); } + } + +exports.ConfirmRefactorComponent = ConfirmRefactorComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/DiffPreviewComponent.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/DiffPreviewComponent.js index b6493116..0f98c5e8 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/DiffPreviewComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/DiffPreviewComponent.js @@ -1,3 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiffPreviewComponent = void 0; + +function _FileChanges() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons-ui/FileChanges")); + + _FileChanges = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,24 +29,20 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DiffPreviewPhase} from '../types'; - -import FileChanges from 'nuclide-commons-ui/FileChanges'; -import * as React from 'react'; - -type Props = { - phase: DiffPreviewPhase, -}; - -export class DiffPreviewComponent extends React.Component { - render(): React.Node { - const {diffs} = this.props.phase; - return ( -
{diffs.map((diff, i) => )}
- ); +class DiffPreviewComponent extends React.Component { + render() { + const { + diffs + } = this.props.phase; + return React.createElement("div", null, diffs.map((diff, i) => React.createElement(_FileChanges().default, { + key: i, + diff: diff + }))); } + } + +exports.DiffPreviewComponent = DiffPreviewComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/FreeformRefactorComponent.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/FreeformRefactorComponent.js index ce6671aa..78b8057d 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/FreeformRefactorComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/FreeformRefactorComponent.js @@ -1,3 +1,64 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.FreeformRefactorComponent = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _AtomInput() { + const data = require("../../../../../nuclide-commons-ui/AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _Checkbox() { + const data = require("../../../../../nuclide-commons-ui/Checkbox"); + + _Checkbox = function () { + return data; + }; + + return data; +} + +function _Dropdown() { + const data = require("../../../../../nuclide-commons-ui/Dropdown"); + + _Dropdown = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("../refactorActions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,30 +67,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {Store, FreeformPhase, FreeformRefactoringArgument} from '../types'; - -import * as React from 'react'; - -import {AtomInput} from 'nuclide-commons-ui/AtomInput'; -import {Button, ButtonTypes} from 'nuclide-commons-ui/Button'; -import {Checkbox} from 'nuclide-commons-ui/Checkbox'; -import {Dropdown} from 'nuclide-commons-ui/Dropdown'; - -import * as Actions from '../refactorActions'; - -type Props = { - phase: FreeformPhase, - store: Store, -}; - -type State = { - args: Map, -}; - -function getDefault(arg: FreeformRefactoringArgument): string | boolean { +function getDefault(arg) { if (arg.default != null) { return arg.default; } @@ -37,8 +78,10 @@ function getDefault(arg: FreeformRefactoringArgument): string | boolean { switch (arg.type) { case 'string': return ''; + case 'boolean': return false; + case 'enum': return arg.options[0].value; } @@ -46,103 +89,97 @@ function getDefault(arg: FreeformRefactoringArgument): string | boolean { throw new Error('unreachable'); } -export class FreeformRefactorComponent extends React.Component { - constructor(props: Props) { +class FreeformRefactorComponent extends React.Component { + constructor(props) { super(props); - const defaultArgs = new Map( - props.phase.refactoring.arguments.map(arg => [arg.name, getDefault(arg)]), - ); + + this._execute = () => { + const { + editor, + originalRange, + refactoring + } = this.props.phase; + this.props.store.dispatch(Actions().execute(this.props.phase.provider, { + kind: 'freeform', + editor, + originalRange, + id: refactoring.id, + range: refactoring.range, + arguments: this.state.args + })); + }; + + const defaultArgs = new Map(props.phase.refactoring.arguments.map(arg => [arg.name, getDefault(arg)])); this.state = { - args: defaultArgs, + args: defaultArgs }; } - render(): React.Node { - return ( -
- {this._getControls()} -
- -
-
- ); + render() { + return React.createElement("div", null, this._getControls(), React.createElement("div", { + style: { + display: 'flex', + justifyContent: 'flex-end' + } + }, React.createElement(_Button().Button, { + className: "nuclide-refactorizer-execute-button", + buttonType: _Button().ButtonTypes.PRIMARY, + onClick: this._execute + }, "Execute"))); } _getControls() { - return this.props.phase.refactoring.arguments - .map((arg, index) => { - switch (arg.type) { - case 'string': - return [ -
- {arg.description} -
, - this._updateArg(arg.name, text)} - onConfirm={this._execute} - />, - ]; - case 'boolean': - return ( - this._updateArg(arg.name, checked)} - /> - ); - case 'enum': - return [ -
- {arg.description} -
, - ({ - value: val.value, - label: val.description, - }))} - onChange={value => this._updateArg(arg.name, value)} - />, - ]; - } - }) - .map((elem, index) => { - return ( -
- {elem} -
- ); - }); + return this.props.phase.refactoring.arguments.map((arg, index) => { + switch (arg.type) { + case 'string': + return [React.createElement("div", { + key: "label", + className: "nuclide-refactorizer-freeform-label" + }, arg.description), React.createElement(_AtomInput().AtomInput, { + key: "input", + autofocus: index === 0, + startSelected: index === 0, + className: "nuclide-refactorizer-freeform-editor", + value: String(this.state.args.get(arg.name)), + onDidChange: text => this._updateArg(arg.name, text), + onConfirm: this._execute + })]; + + case 'boolean': + return React.createElement(_Checkbox().Checkbox, { + label: arg.description, + checked: Boolean(this.state.args.get(arg.name)), + onChange: checked => this._updateArg(arg.name, checked) + }); + + case 'enum': + return [React.createElement("div", { + key: "label", + className: "nuclide-refactorizer-freeform-label" + }, arg.description), React.createElement(_Dropdown().Dropdown, { + key: "dropdown", + value: this.state.args.get(arg.name) || arg.options[0], + options: arg.options.map(val => ({ + value: val.value, + label: val.description + })), + onChange: value => this._updateArg(arg.name, value) + })]; + } + }).map((elem, index) => { + return React.createElement("div", { + key: index, + className: "nuclide-refactorizer-freeform-group" + }, elem); + }); } - _updateArg(name: string, value: mixed) { + _updateArg(name, value) { // A bit hacky, but immutability isn't a requirement here. this.state.args.set(name, value); this.forceUpdate(); } - _execute = () => { - const {editor, originalRange, refactoring} = this.props.phase; - this.props.store.dispatch( - Actions.execute(this.props.phase.provider, { - kind: 'freeform', - editor, - originalRange, - id: refactoring.id, - range: refactoring.range, - arguments: this.state.args, - }), - ); - }; } + +exports.FreeformRefactorComponent = FreeformRefactorComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/MainRefactorComponent.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/MainRefactorComponent.js index 2e91e5b1..9f267c54 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/MainRefactorComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/MainRefactorComponent.js @@ -1,3 +1,94 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.MainRefactorComponent = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Button() { + const data = require("../../../../../nuclide-commons-ui/Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _ButtonGroup() { + const data = require("../../../../../nuclide-commons-ui/ButtonGroup"); + + _ButtonGroup = function () { + return data; + }; + + return data; +} + +function _ConfirmRefactorComponent() { + const data = require("./ConfirmRefactorComponent"); + + _ConfirmRefactorComponent = function () { + return data; + }; + + return data; +} + +function _DiffPreviewComponent() { + const data = require("./DiffPreviewComponent"); + + _DiffPreviewComponent = function () { + return data; + }; + + return data; +} + +function _FreeformRefactorComponent() { + const data = require("./FreeformRefactorComponent"); + + _FreeformRefactorComponent = function () { + return data; + }; + + return data; +} + +function _PickRefactorComponent() { + const data = require("./PickRefactorComponent"); + + _PickRefactorComponent = function () { + return data; + }; + + return data; +} + +function _ProgressComponent() { + const data = require("./ProgressComponent"); + + _ProgressComponent = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("../refactorActions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +97,11 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {Store, RefactorState} from '../types'; - -import * as React from 'react'; -import invariant from 'assert'; - -import {Button} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; - -import {ConfirmRefactorComponent} from './ConfirmRefactorComponent'; -import {DiffPreviewComponent} from './DiffPreviewComponent'; -import {FreeformRefactorComponent} from './FreeformRefactorComponent'; -import {PickRefactorComponent} from './PickRefactorComponent'; -import {ProgressComponent} from './ProgressComponent'; -import * as Actions from '../refactorActions'; - -type Props = { - appState: RefactorState, - store: Store, -}; - -export class MainRefactorComponent extends React.Component { - render(): React.Node | null { +class MainRefactorComponent extends React.Component { + render() { if (this.props.appState.type === 'closed') { return null; } else { @@ -41,72 +111,81 @@ export class MainRefactorComponent extends React.Component { } } - _render(): React.Node { - return ( -
- {this.getHeaderElement()} - {this.getInnerElement()} -
- ); + _render() { + return React.createElement("div", null, this.getHeaderElement(), this.getInnerElement()); } - _getBackButton(): React.Node | null { + _getBackButton() { const appState = this.props.appState; - const previousPhase = - (appState.phase && appState.phase.previousPhase) || null; - return previousPhase ? ( - - ) : null; + const previousPhase = appState.phase && appState.phase.previousPhase || null; + return previousPhase ? React.createElement(_Button().Button, { + onClick: () => this.props.store.dispatch(Actions().backFromDiffPreview(previousPhase)) + }, "Back") : null; } - getHeaderElement(): React.Node { + getHeaderElement() { const appState = this.props.appState; - invariant(appState.type === 'open'); - return ( -
- Refactor - - {this._getBackButton()} - - -
- ); + + if (!(appState.type === 'open')) { + throw new Error("Invariant violation: \"appState.type === 'open'\""); + } + + return React.createElement("div", { + className: "nuclide-refactorizer-header" + }, React.createElement("span", null, "Refactor"), React.createElement(_ButtonGroup().ButtonGroup, null, this._getBackButton(), React.createElement(_Button().Button, { + onClick: () => this.props.store.dispatch(Actions().close()) + }, "Close"))); } - getInnerElement(): React.Node { + getInnerElement() { const appState = this.props.appState; - invariant(appState.type === 'open'); + + if (!(appState.type === 'open')) { + throw new Error("Invariant violation: \"appState.type === 'open'\""); + } + const phase = appState.phase; + switch (phase.type) { case 'get-refactorings': - return
Waiting for refactorings...
; + return React.createElement("div", null, "Waiting for refactorings..."); + case 'pick': - return ( - - ); + return React.createElement(_PickRefactorComponent().PickRefactorComponent, { + pickPhase: phase, + store: this.props.store + }); + case 'freeform': - return ( - - ); + return React.createElement(_FreeformRefactorComponent().FreeformRefactorComponent, { + phase: phase, + store: this.props.store + }); + case 'execute': - return
Executing refactoring...
; + return React.createElement("div", null, "Executing refactoring..."); + case 'confirm': - return ( - - ); + return React.createElement(_ConfirmRefactorComponent().ConfirmRefactorComponent, { + phase: phase, + store: this.props.store + }); + case 'progress': - return ; + return React.createElement(_ProgressComponent().ProgressComponent, { + phase: phase + }); + case 'diff-preview': - return ; + return React.createElement(_DiffPreviewComponent().DiffPreviewComponent, { + phase: phase + }); + default: - return
; + return React.createElement("div", null); } } + } + +exports.MainRefactorComponent = MainRefactorComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/PickRefactorComponent.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/PickRefactorComponent.js index c2f46a32..f85257a0 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/PickRefactorComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/PickRefactorComponent.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.PickRefactorComponent = void 0; + +var React = _interopRequireWildcard(require("react")); + +function Actions() { + const data = _interopRequireWildcard(require("../refactorActions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,113 +39,98 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +class PickRefactorComponent extends React.Component { + constructor(...args) { + var _temp; -import type {Store, PickPhase, AvailableRefactoring} from '../types'; - -import * as React from 'react'; -import * as Actions from '../refactorActions'; -import classNames from 'classnames'; - -type State = { - selectedRefactoring: ?AvailableRefactoring, -}; - -export class PickRefactorComponent extends React.Component< - { - pickPhase: PickPhase, - store: Store, - }, - State, -> { - state: State = { - selectedRefactoring: null, - }; + return _temp = super(...args), this.state = { + selectedRefactoring: null + }, _temp; + } + + render() { + const { + availableRefactorings + } = this.props.pickPhase; - render(): React.Node { - const {availableRefactorings} = this.props.pickPhase; if (availableRefactorings.length === 0) { - return
No refactorings available at this location
; + return React.createElement("div", null, "No refactorings available at this location"); } - const elements = availableRefactorings.map(r => - this._renderRefactorOption(r), - ); - - return ( -
-
    {elements}
-
- ); + const elements = availableRefactorings.map(r => this._renderRefactorOption(r)); + return React.createElement("div", { + className: "select-list nuclide-refactorizer-pick-refactor" + }, React.createElement("ol", { + className: "list-group" + }, elements)); } - _pickRefactor(refactoring: AvailableRefactoring): void { + _pickRefactor(refactoring) { if (refactoring.kind === 'freeform' && refactoring.arguments.length === 0) { - this.props.store.dispatch( - Actions.execute(this.props.pickPhase.provider, { - kind: 'freeform', - editor: this.props.pickPhase.editor, - originalRange: this.props.pickPhase.originalRange, - id: refactoring.id, - range: refactoring.range, - arguments: new Map(), - }), - ); + this.props.store.dispatch(Actions().execute(this.props.pickPhase.provider, { + kind: 'freeform', + editor: this.props.pickPhase.editor, + originalRange: this.props.pickPhase.originalRange, + id: refactoring.id, + range: refactoring.range, + arguments: new Map() + })); return; } - this.props.store.dispatch(Actions.pickedRefactor(refactoring)); + + this.props.store.dispatch(Actions().pickedRefactor(refactoring)); } - _select(selectedRefactoring: AvailableRefactoring): void { + _select(selectedRefactoring) { this.setState({ - selectedRefactoring, + selectedRefactoring }); } - _renderRefactorOption(refactoring: AvailableRefactoring): React.Node { + _renderRefactorOption(refactoring) { switch (refactoring.kind) { case 'freeform': const selectable = !refactoring.disabled; - const selected = - selectable && refactoring === this.state.selectedRefactoring; + const selected = selectable && refactoring === this.state.selectedRefactoring; const props = {}; - props.className = classNames('two-lines', { + props.className = (0, _classnames().default)('two-lines', { 'nuclide-refactorizer-selectable': selectable, 'nuclide-refactorizer-selected': selected, - 'nuclide-refactorizer-unselectable': !selectable, + 'nuclide-refactorizer-unselectable': !selectable }); + props.onMouseEnter = () => this._select(refactoring); + if (refactoring.disabled == null || refactoring.disabled === false) { props.onClick = () => { this._pickRefactor(refactoring); }; } - const refactoringOption = ( -
  • -
    - {refactoring.name} -
    -
    - {refactoring.description} -
    -
  • - ); + + const refactoringOption = React.createElement("li", props, React.createElement("div", { + className: (0, _classnames().default)({ + 'nuclide-refactorizer-selectable-text': selectable, + 'nuclide-refactorizer-selected-text': selected, + 'nuclide-refactorizer-unselectable-text': !selectable + }) + }, refactoring.name), React.createElement("div", { + className: (0, _classnames().default)('text-smaller', { + 'nuclide-refactorizer-selectable-text': selectable, + 'nuclide-refactorizer-selected-text': selected, + 'nuclide-refactorizer-unselectable-text': !selectable + }) + }, refactoring.description)); return refactoringOption; + default: - (refactoring.kind: empty); + refactoring.kind; throw new Error(`Unknown refactoring kind ${refactoring.kind}`); } } + } + +exports.PickRefactorComponent = PickRefactorComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/ProgressComponent.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/ProgressComponent.js index e819638d..4cb1223e 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/ProgressComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/ProgressComponent.js @@ -1,3 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ProgressComponent = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _ProgressBar() { + const data = require("../../../../../nuclide-commons-ui/ProgressBar"); + + _ProgressBar = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,30 +27,24 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {ProgressPhase} from '../types'; - -import * as React from 'react'; - -import {ProgressBar} from 'nuclide-commons-ui/ProgressBar'; - -type Props = { - phase: ProgressPhase, -}; - -export class ProgressComponent extends React.Component { - render(): React.Node { - const {message, value, max} = this.props.phase; - return ( -
    - {message} ({value} / {max}) -
    - -
    -
    - ); +class ProgressComponent extends React.Component { + render() { + const { + message, + value, + max + } = this.props.phase; + return React.createElement("div", null, message, " (", value, " / ", max, ")", React.createElement("div", { + className: "nuclide-refactorizer-progress" + }, React.createElement(_ProgressBar().ProgressBar, { + value: value, + max: max + }))); } + } + +exports.ProgressComponent = ProgressComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/RenameComponent.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/RenameComponent.js index bbe35420..92c80dfb 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/RenameComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/components/RenameComponent.js @@ -1,3 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _AtomInput() { + const data = require("../../../../../nuclide-commons-ui/AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("../refactorActions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,120 +37,105 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +class RenameComponent extends React.Component { + constructor(props) { + super(props); -import type {RefactorProvider, RenameRequest} from '../types'; + this._forceActivateInsertMode = () => { + const { + parentEditor + } = this.props; -import * as React from 'react'; -import {AtomInput} from 'nuclide-commons-ui/AtomInput'; -import * as Actions from '../refactorActions'; + if (parentEditor != null) { + atom.commands.dispatch(atom.views.getView(parentEditor), 'vim-mode-plus:activate-insert-mode'); + } + }; -export type Props = { - selectedText: string, - provider: RefactorProvider, - parentEditor: atom$TextEditor, - store: Store, - symbolPosition: atom$Point, -}; + this._highlightTextWithin = () => { + if (this._atomInput == null) { + return; + } -type State = { - newName: string, -}; + const editor = this._atomInput.getTextEditor(); -export default class RenameComponent extends React.Component { - _atomInput: ?AtomInput; + editor.selectAll(); + }; - constructor(props: Props) { - super(props); + this._handleSubmit = event => { + if (event == null) { + return; + } + + const { + newName + } = this.state; + const { + store + } = this.props; + const renameRequest = { + kind: 'rename', + newName, + editor: this.props.parentEditor, + position: this.props.symbolPosition + }; + return newName === '' ? store.dispatch(Actions().close()) : store.dispatch(Actions().execute(this.props.provider, renameRequest)); + }; + + this._handleCancel = event => { + if (event == null) { + return; + } + + this.props.store.dispatch(Actions().close()); + }; + + this._handleChange = text => { + this.setState({ + newName: text + }); + }; + + this._handleBlur = event => { + this.props.store.dispatch(Actions().close()); + }; this.state = { - newName: this.props.selectedText, + newName: this.props.selectedText }; } componentDidMount() { this._forceActivateInsertMode(); - this._highlightTextWithin(); - } - // When using the `vim-mode-plus` package, the user has to press 'i' in 'normal-mode' + this._highlightTextWithin(); + } // When using the `vim-mode-plus` package, the user has to press 'i' in 'normal-mode' // to begin inserting text in an atom-text-editor - doing so sends // an 'activate-insert-mode' command. // However, when the user wants to type in embedded text editors, // we must first activate `insert-mode` in the parent editor. - _forceActivateInsertMode = (): void => { - const {parentEditor} = this.props; - - if (parentEditor != null) { - atom.commands.dispatch( - atom.views.getView(parentEditor), - 'vim-mode-plus:activate-insert-mode', - ); - } - }; - - _highlightTextWithin = (): void => { - if (this._atomInput == null) { - return; - } - const editor = this._atomInput.getTextEditor(); - editor.selectAll(); - }; - - _handleSubmit = (event: ?Event): void => { - if (event == null) { - return; - } - const {newName} = this.state; - const {store} = this.props; - - const renameRequest: RenameRequest = { - kind: 'rename', - newName, - editor: this.props.parentEditor, - position: this.props.symbolPosition, - }; - - return newName === '' - ? store.dispatch(Actions.close()) - : store.dispatch(Actions.execute(this.props.provider, renameRequest)); - }; - _handleCancel = (event: ?Event): void => { - if (event == null) { - return; - } - this.props.store.dispatch(Actions.close()); - }; - - _handleChange = (text: string): void => { - this.setState({newName: text}); - }; - - _handleBlur = (event: Event): void => { - this.props.store.dispatch(Actions.close()); - }; - - render(): React.Node { + render() { // TODO: Have a min-width, but expand the actual width as necessary based on the length of the selected word // (What VSCode does) const widthStyle = { - minWidth: '150px', + minWidth: '150px' }; - return ( - (this._atomInput = atomInput)} - style={widthStyle} - autofocus={true} - value={this.state.newName} - onDidChange={this._handleChange} - onBlur={this._handleBlur} - onConfirm={this._handleSubmit} - onCancel={this._handleCancel} - /> - ); + return React.createElement(_AtomInput().AtomInput, { + ref: atomInput => this._atomInput = atomInput, + style: widthStyle, + autofocus: true, + value: this.state.newName, + onDidChange: this._handleChange, + onBlur: this._handleBlur, + onConfirm: this._handleSubmit, + onCancel: this._handleCancel + }); } + } + +exports.default = RenameComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/main.js index 01229fd3..304a8255 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/main.js @@ -1,3 +1,109 @@ +"use strict"; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _observeGrammarForTextEditors() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/observe-grammar-for-text-editors")); + + _observeGrammarForTextEditors = function () { + return data; + }; + + return data; +} + +function _mouseToPosition() { + const data = require("../../../../nuclide-commons-atom/mouse-to-position"); + + _mouseToPosition = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _ContextMenu() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ContextMenu")); + + _ContextMenu = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("./refactorActions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _refactorStore() { + const data = require("./refactorStore"); + + _refactorStore = function () { + return data; + }; + + return data; +} + +function _refactorUIs() { + const data = require("./refactorUIs"); + + _refactorUIs = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,160 +112,100 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ /* * WARNING: This package is still experimental and in early development. Use it at your own risk. */ -import type {Store} from './types'; -import type {RefactorProvider} from './types'; - -import invariant from 'assert'; -import {getLogger} from 'log4js'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import observeGrammarForTextEditors from 'nuclide-commons-atom/observe-grammar-for-text-editors'; -import {bufferPositionForMouseEvent} from 'nuclide-commons-atom/mouse-to-position'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import ContextMenu from 'nuclide-commons-atom/ContextMenu'; - -import * as Actions from './refactorActions'; -import {getStore} from './refactorStore'; -import {initRefactorUIs} from './refactorUIs'; - const CONTEXT_MENU_CLASS = 'enable-nuclide-refactorizer'; class Activation { - _disposables: UniversalDisposable; - _store: Store; - _providerRegistry: ProviderRegistry; - constructor() { - this._providerRegistry = new ProviderRegistry(); - - this._store = getStore(this._providerRegistry); - + this._providerRegistry = new (_ProviderRegistry().default)(); + this._store = (0, _refactorStore().getStore)(this._providerRegistry); let lastMouseEvent = null; - this._disposables = new UniversalDisposable( - initRefactorUIs(this._store), - atom.commands.add( - 'atom-workspace', - 'nuclide-refactorizer:refactorize', - () => { - this._store.dispatch(Actions.open('generic')); - }, - ), - atom.commands.add( - 'atom-text-editor', - // We don't actually want people calling this directly. - // eslint-disable-next-line nuclide-internal/atom-commands - 'nuclide-refactorizer:refactorize-from-context-menu', - () => { - const mouseEvent = lastMouseEvent; - lastMouseEvent = null; - invariant( - mouseEvent != null, - 'No mouse event found. Do not invoke this command directly. ' + - 'If you did use the context menu, please report this issue.', - ); - const editor = atom.workspace.getActiveTextEditor(); - invariant(editor != null); - const bufferPosition = bufferPositionForMouseEvent( - mouseEvent, - editor, - ); - // If the user selected some text and clicked within it, - // we'll treat it as a 'range refactor'. - const currentSelection = editor.getSelectedBufferRange(); - if (!currentSelection.containsPoint(bufferPosition)) { - editor.setCursorBufferPosition(bufferPosition); - } - - this._store.dispatch(Actions.open('generic')); - }, - ), - atom.contextMenu.add({ - 'atom-text-editor:not(.mini).enable-nuclide-refactorizer': [ - { - label: 'Refactor', - command: 'nuclide-refactorizer:refactorize-from-context-menu', - created: event => { - lastMouseEvent = event; - }, - }, - ], - }), - atom.commands.add( - 'atom-text-editor', - 'nuclide-refactorizer:rename', - event => { - const editor = atom.workspace.getActiveTextEditor(); - if (!editor) { - return null; - } - const mouseEvent = ContextMenu.isEventFromContextMenu(event) - ? lastMouseEvent - : null; - - const position = - mouseEvent != null - ? bufferPositionForMouseEvent(mouseEvent, editor) - : editor.getCursorBufferPosition(); - - editor.setCursorBufferPosition(position); - editor.selectWordsContainingCursors(); - const selectedText = editor.getSelectedText().trim(); - if (selectedText === '') { - return null; - } - - const mountPosition = editor.getSelectedBufferRange().start; - - const provider = Array.from( - this._providerRegistry.getAllProvidersForEditor(editor), - ).find(p => p.rename != null); - - if (provider == null) { - getLogger('rename').error('Rename Provider Not Found'); - return null; - } - - this._store.dispatch( - Actions.displayRename( - editor, - provider, - selectedText, - mountPosition, - position, - ), - ); - }, - ), - atom.contextMenu.add({ - 'atom-text-editor': [ - { - label: 'Rename', - command: 'nuclide-refactorizer:rename', - created: event => { - lastMouseEvent = event; - }, - }, - ], - }), - observeGrammarForTextEditors(editor => - this._addContextMenuIfEligible(editor), - ), - ); + this._disposables = new (_UniversalDisposable().default)((0, _refactorUIs().initRefactorUIs)(this._store), atom.commands.add('atom-workspace', 'nuclide-refactorizer:refactorize', () => { + this._store.dispatch(Actions().open('generic')); + }), atom.commands.add('atom-text-editor', // We don't actually want people calling this directly. + // eslint-disable-next-line nuclide-internal/atom-commands + 'nuclide-refactorizer:refactorize-from-context-menu', () => { + const mouseEvent = lastMouseEvent; + lastMouseEvent = null; + + if (!(mouseEvent != null)) { + throw new Error('No mouse event found. Do not invoke this command directly. ' + 'If you did use the context menu, please report this issue.'); + } + + const editor = atom.workspace.getActiveTextEditor(); + + if (!(editor != null)) { + throw new Error("Invariant violation: \"editor != null\""); + } + + const bufferPosition = (0, _mouseToPosition().bufferPositionForMouseEvent)(mouseEvent, editor); // If the user selected some text and clicked within it, + // we'll treat it as a 'range refactor'. + + const currentSelection = editor.getSelectedBufferRange(); + + if (!currentSelection.containsPoint(bufferPosition)) { + editor.setCursorBufferPosition(bufferPosition); + } + + this._store.dispatch(Actions().open('generic')); + }), atom.contextMenu.add({ + 'atom-text-editor:not(.mini).enable-nuclide-refactorizer': [{ + label: 'Refactor', + command: 'nuclide-refactorizer:refactorize-from-context-menu', + created: event => { + lastMouseEvent = event; + } + }] + }), atom.commands.add('atom-text-editor', 'nuclide-refactorizer:rename', event => { + const editor = atom.workspace.getActiveTextEditor(); + + if (!editor) { + return null; + } + + const mouseEvent = _ContextMenu().default.isEventFromContextMenu(event) ? lastMouseEvent : null; + const position = mouseEvent != null ? (0, _mouseToPosition().bufferPositionForMouseEvent)(mouseEvent, editor) : editor.getCursorBufferPosition(); + editor.setCursorBufferPosition(position); + editor.selectWordsContainingCursors(); + const selectedText = editor.getSelectedText().trim(); + + if (selectedText === '') { + return null; + } + + const mountPosition = editor.getSelectedBufferRange().start; + const provider = Array.from(this._providerRegistry.getAllProvidersForEditor(editor)).find(p => p.rename != null); + + if (provider == null) { + (0, _log4js().getLogger)('rename').error('Rename Provider Not Found'); + return null; + } + + this._store.dispatch(Actions().displayRename(editor, provider, selectedText, mountPosition, position)); + }), atom.contextMenu.add({ + 'atom-text-editor': [{ + label: 'Rename', + command: 'nuclide-refactorizer:rename', + created: event => { + lastMouseEvent = event; + } + }] + }), (0, _observeGrammarForTextEditors().default)(editor => this._addContextMenuIfEligible(editor))); } dispose() { this._disposables.dispose(); } - _addContextMenuIfEligible(editor: atom$TextEditor): void { + _addContextMenuIfEligible(editor) { const element = atom.views.getView(editor); + if (this._providerRegistry.getProviderForEditor(editor) != null) { element.classList.add(CONTEXT_MENU_CLASS); } else { @@ -167,20 +213,22 @@ class Activation { } } - _checkAllEditorContextMenus(): void { - atom.workspace - .getTextEditors() - .forEach(editor => this._addContextMenuIfEligible(editor)); + _checkAllEditorContextMenus() { + atom.workspace.getTextEditors().forEach(editor => this._addContextMenuIfEligible(editor)); } - consumeRefactorProvider(provider: RefactorProvider): IDisposable { + consumeRefactorProvider(provider) { this._providerRegistry.addProvider(provider); + this._checkAllEditorContextMenus(); - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { this._providerRegistry.removeProvider(provider); + this._checkAllEditorContextMenus(); }); } + } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorActions.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorActions.js index e24a8024..0a39e768 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorActions.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorActions.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.open = open; +exports.gotRefactorings = gotRefactorings; +exports.error = error; +exports.backFromDiffPreview = backFromDiffPreview; +exports.pickedRefactor = pickedRefactor; +exports.execute = execute; +exports.confirm = confirm; +exports.loadDiffPreview = loadDiffPreview; +exports.displayDiffPreview = displayDiffPreview; +exports.displayRename = displayRename; +exports.apply = apply; +exports.progress = progress; +exports.close = close; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,137 +25,96 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {RefactorRequest, RefactorProvider} from './types'; -import type {AvailableRefactoring, RefactorEditResponse} from './types'; -import type { - ApplyAction, - BackFromDiffPreviewAction, - CloseAction, - ConfirmAction, - DisplayDiffPreviewAction, - ErrorAction, - ErrorSource, - ExecuteAction, - GotRefactoringsAction, - OpenAction, - Phase, - ProgressAction, - PickedRefactorAction, - RefactorUI, - LoadDiffPreviewAction, - DisplayRenameAction, -} from './types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -export function open(ui: RefactorUI): OpenAction { +function open(ui) { return { type: 'open', - ui, + ui }; } -export function gotRefactorings( - editor: atom$TextEditor, - originalRange: atom$Range, - provider: RefactorProvider, - availableRefactorings: Array, -): GotRefactoringsAction { +function gotRefactorings(editor, originalRange, provider, availableRefactorings) { return { type: 'got-refactorings', payload: { editor, originalRange, provider, - availableRefactorings, - }, + availableRefactorings + } }; } -export function error(source: ErrorSource, err: Error): ErrorAction { +function error(source, err) { return { type: 'error', payload: { source, - error: err, - }, + error: err + } }; } -export function backFromDiffPreview(phase: Phase): BackFromDiffPreviewAction { +function backFromDiffPreview(phase) { return { type: 'back-from-diff-preview', payload: { - phase, - }, + phase + } }; } -export function pickedRefactor( - refactoring: AvailableRefactoring, -): PickedRefactorAction { +function pickedRefactor(refactoring) { return { type: 'picked-refactor', payload: { - refactoring, - }, + refactoring + } }; } -export function execute( - provider: RefactorProvider, - refactoring: RefactorRequest, -): ExecuteAction { +function execute(provider, refactoring) { return { type: 'execute', payload: { provider, - refactoring, - }, + refactoring + } }; } -export function confirm(response: RefactorEditResponse): ConfirmAction { +function confirm(response) { return { type: 'confirm', - payload: {response}, + payload: { + response + } }; } -export function loadDiffPreview( - previousPhase: Phase, - uri: NuclideUri, - response: RefactorEditResponse, -): LoadDiffPreviewAction { +function loadDiffPreview(previousPhase, uri, response) { return { type: 'load-diff-preview', payload: { previousPhase, uri, - response, - }, + response + } }; } -export function displayDiffPreview( - diffs: Array, -): DisplayDiffPreviewAction { +function displayDiffPreview(diffs) { return { type: 'display-diff-preview', - payload: {diffs}, + payload: { + diffs + } }; } -export function displayRename( - editor: TextEditor, - provider: RefactorProvider, - selectedText: string, - mountPosition: atom$Point, - symbolPosition: atom$Point, -): DisplayRenameAction { +function displayRename(editor, provider, selectedText, mountPosition, symbolPosition) { return { type: 'display-rename', payload: { @@ -144,31 +122,33 @@ export function displayRename( provider, selectedText, mountPosition, - symbolPosition, - }, + symbolPosition + } }; } -export function apply(response: RefactorEditResponse): ApplyAction { +function apply(response) { return { type: 'apply', - payload: {response}, + payload: { + response + } }; } -export function progress( - message: string, - value: number, - max: number, -): ProgressAction { +function progress(message, value, max) { return { type: 'progress', - payload: {message, value, max}, + payload: { + message, + value, + max + } }; } -export function close(): CloseAction { +function close() { return { - type: 'close', + type: 'close' }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorEpics.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorEpics.js index 9782eed2..25a88398 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorEpics.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorEpics.js @@ -1,3 +1,109 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getEpics = getEpics; +exports.applyRefactoring = applyRefactoring; + +function _projects() { + const data = require("../../../../nuclide-commons-atom/projects"); + + _projects = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +var _atom = require("atom"); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _diffparser() { + const data = _interopRequireDefault(require("diffparser")); + + _diffparser = function () { + return data; + }; + + return data; +} + +function _textEdit() { + const data = require("../../../../nuclide-commons-atom/text-edit"); + + _textEdit = function () { + return data; + }; + + return data; +} + +function _textEditDiff() { + const data = require("../../../../nuclide-commons-atom/text-edit-diff"); + + _textEditDiff = function () { + return data; + }; + + return data; +} + +function _textEditor() { + const data = require("../../../../nuclide-commons-atom/text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/analytics")); + + _analytics = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("./refactorActions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,238 +112,175 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +function getEpics(providers) { + return [function getRefactoringsEpic(actions) { + return actions.ofType('open').switchMap(() => { + return _RxMin.Observable.fromPromise(getRefactorings(providers)).takeUntil(actions); + }); + }, function executeRefactoringEpic(actions) { + return actions.ofType('execute').switchMap(action => { + // Flow doesn't understand the implications of ofType :( + if (!(action.type === 'execute')) { + throw new Error("Invariant violation: \"action.type === 'execute'\""); + } -import type {ActionsObservable, Epic} from 'nuclide-commons/redux-observable'; -import type ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {getFileForPath} from 'nuclide-commons-atom/projects'; -import type {TextEdit} from 'nuclide-commons-atom/text-edit'; -import type { - ApplyAction, - RefactorAction, - RefactorState, - ExecuteAction, -} from './types'; -import type {ExternalTextEdit, RefactorEditResponse} from './types'; -import type {RefactorProvider} from './types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import invariant from 'assert'; -import {Observable} from 'rxjs'; -import {Range, TextBuffer} from 'atom'; - -import nuclideUri from 'nuclide-commons/nuclideUri'; -import parse from 'diffparser'; -import {applyTextEditsToBuffer} from 'nuclide-commons-atom/text-edit'; -import {toUnifiedDiff} from 'nuclide-commons-atom/text-edit-diff'; -import {existingEditorForUri} from 'nuclide-commons-atom/text-editor'; -import {getLogger} from 'log4js'; -import analytics from 'nuclide-commons/analytics'; - -import * as Actions from './refactorActions'; - -export function getEpics( - providers: ProviderRegistry, -): Array> { - return [ - function getRefactoringsEpic( - actions: ActionsObservable, - ): Observable { - return actions.ofType('open').switchMap(() => { - return Observable.fromPromise(getRefactorings(providers)).takeUntil( - actions, - ); - }); - }, - - function executeRefactoringEpic( - actions: ActionsObservable, - ): Observable { - return actions.ofType('execute').switchMap(action => { - // Flow doesn't understand the implications of ofType :( - invariant(action.type === 'execute'); - return executeRefactoring(action) - .concat( - // Default handler if we don't get a result. - Observable.of( - Actions.error('execute', Error('Could not refactor.')), - ), - ) - .takeUntil(actions.filter(x => x.type !== 'progress')); - }); - }, - - function applyRefactoringEpic( - actions: ActionsObservable, - ): Observable { - return actions.ofType('apply').switchMap(action => { - invariant(action.type === 'apply'); - return applyRefactoring(action).takeUntil(actions.ofType('close')); - }); - }, - - function loadDiffPreviewEpic( - actions: ActionsObservable, - ): Observable { - return actions.ofType('load-diff-preview').switchMap(action => { - invariant(action.type === 'load-diff-preview'); - return Observable.fromPromise( - loadDiffPreview(action.payload.uri, action.payload.response), - ); - }); - }, - - function handleErrors( - actions: ActionsObservable, - ): Observable { - return actions.ofType('error').map(action => { - invariant(action.type === 'error'); - const {source, error} = action.payload; - const sourceName = - source === 'got-refactorings' - ? 'getting refactors' - : 'executing refactor'; - getLogger('nuclide-refactorizer').error(`Error ${sourceName}:`, error); - atom.notifications.addError(`Error ${sourceName}`, { - description: error.message, - dismissable: true, - }); - return Actions.close(); + return executeRefactoring(action).concat( // Default handler if we don't get a result. + _RxMin.Observable.of(Actions().error('execute', Error('Could not refactor.')))).takeUntil(actions.filter(x => x.type !== 'progress')); + }); + }, function applyRefactoringEpic(actions) { + return actions.ofType('apply').switchMap(action => { + if (!(action.type === 'apply')) { + throw new Error("Invariant violation: \"action.type === 'apply'\""); + } + + return applyRefactoring(action).takeUntil(actions.ofType('close')); + }); + }, function loadDiffPreviewEpic(actions) { + return actions.ofType('load-diff-preview').switchMap(action => { + if (!(action.type === 'load-diff-preview')) { + throw new Error("Invariant violation: \"action.type === 'load-diff-preview'\""); + } + + return _RxMin.Observable.fromPromise(loadDiffPreview(action.payload.uri, action.payload.response)); + }); + }, function handleErrors(actions) { + return actions.ofType('error').map(action => { + if (!(action.type === 'error')) { + throw new Error("Invariant violation: \"action.type === 'error'\""); + } + + const { + source, + error + } = action.payload; + const sourceName = source === 'got-refactorings' ? 'getting refactors' : 'executing refactor'; + (0, _log4js().getLogger)('nuclide-refactorizer').error(`Error ${sourceName}:`, error); + atom.notifications.addError(`Error ${sourceName}`, { + description: error.message, + dismissable: true }); - }, - ]; + return Actions().close(); + }); + }]; } -async function getRefactorings( - providers: ProviderRegistry, -): Promise { - analytics.track('nuclide-refactorizer:get-refactorings'); +async function getRefactorings(providers) { + _analytics().default.track('nuclide-refactorizer:get-refactorings'); + const editor = atom.workspace.getActiveTextEditor(); + if (editor == null || editor.getPath() == null) { - return Actions.error( - 'get-refactorings', - Error('Must be run from a saved file.'), - ); + return Actions().error('get-refactorings', Error('Must be run from a saved file.')); } + try { const selectedRange = editor.getSelectedBufferRange(); - - const provider = Array.from( - providers.getAllProvidersForEditor(editor), - ).find(p => p.refactorings != null); + const provider = Array.from(providers.getAllProvidersForEditor(editor)).find(p => p.refactorings != null); if (provider == null || provider.refactorings == null) { - return Actions.error('get-refactorings', Error('No providers found.')); + return Actions().error('get-refactorings', Error('No providers found.')); } - const availableRefactorings = await provider.refactorings( - editor, - selectedRange, - ); - availableRefactorings.sort( - (x, y) => (x.disabled === true ? 1 : 0) - (y.disabled === true ? 1 : 0), - ); - return Actions.gotRefactorings( - editor, - selectedRange, - provider, - availableRefactorings, - ); + const availableRefactorings = await provider.refactorings(editor, selectedRange); + availableRefactorings.sort((x, y) => (x.disabled === true ? 1 : 0) - (y.disabled === true ? 1 : 0)); + return Actions().gotRefactorings(editor, selectedRange, provider, availableRefactorings); } catch (e) { - return Actions.error('get-refactorings', e); + return Actions().error('get-refactorings', e); } } -function executeRefactoring(action: ExecuteAction): Observable { - const {refactoring, provider} = action.payload; +function executeRefactoring(action) { + const { + refactoring, + provider + } = action.payload; + if (provider.refactor != null && refactoring.kind === 'freeform') { - return provider - .refactor(refactoring) - .map(response => { - switch (response.type) { - case 'progress': - return Actions.progress( - response.message, - response.value, - response.max, - ); - case 'edit': - case 'external-edit': - case 'rename-external-edit': - if (response.edits.size <= 1) { - return Actions.apply(response); - } - return Actions.confirm(response); - default: - (response: empty); - throw new Error(); - } - }) - .catch(e => Observable.of(Actions.error('execute', e))); - } else if (provider.rename != null && refactoring.kind === 'rename') { - const {editor, position, newName} = refactoring; + return provider.refactor(refactoring).map(response => { + switch (response.type) { + case 'progress': + return Actions().progress(response.message, response.value, response.max); + + case 'edit': + case 'external-edit': + case 'rename-external-edit': + if (response.edits.size <= 1) { + return Actions().apply(response); + } + + return Actions().confirm(response); - return Observable.fromPromise( - provider.rename(editor, position, newName), - ).map(edits => { + default: + response; + throw new Error(); + } + }).catch(e => _RxMin.Observable.of(Actions().error('execute', e))); + } else if (provider.rename != null && refactoring.kind === 'rename') { + const { + editor, + position, + newName + } = refactoring; + return _RxMin.Observable.fromPromise(provider.rename(editor, position, newName)).map(edits => { if (edits == null || edits.size === 0) { - return Actions.close(); + return Actions().close(); } + const currentFilePath = editor.getPath(); - invariant(currentFilePath != null); - // If the map only has 1 key (a single unique NuclideURI) and it matches the + if (!(currentFilePath != null)) { + throw new Error("Invariant violation: \"currentFilePath != null\""); + } // If the map only has 1 key (a single unique NuclideURI) and it matches the // currently opened file, then all the TextEdits must be local. + + let response; + if (edits.size === 1 && edits.keys().next().value === currentFilePath) { response = { type: 'edit', - edits, + edits }; - return Actions.apply(response); + return Actions().apply(response); } else { response = { type: 'rename-external-edit', - edits, + edits }; - return Actions.confirm(response); + return Actions().confirm(response); } }); } else { - return Observable.of( - Actions.error('execute', Error('No appropriate provider found.')), - ); + return _RxMin.Observable.of(Actions().error('execute', Error('No appropriate provider found.'))); } -} - -// This offers two different options for applying edits: +} // This offers two different options for applying edits: // 1. Apply changes to open files only without saving // 2. Apply changes to all files, open or unopened, directly to disk. // In both cases, the format of the edits are the same (TextEdits) + + const FILE_IO_CONCURRENCY = 4; -export function applyRefactoring( - action: ApplyAction, -): Observable { - return Observable.defer(() => { - const {response} = action.payload; - let editStream = Observable.empty(); +function applyRefactoring(action) { + return _RxMin.Observable.defer(() => { + const { + response + } = action.payload; + + let editStream = _RxMin.Observable.empty(); + if (response.type === 'edit') { // Regular edits are applied directly to open buffers. - // Note that all files must actually be open. for (const [path, edits] of response.edits) { - const editor = existingEditorForUri(path); + const editor = (0, _textEditor().existingEditorForUri)(path); + if (editor != null) { - applyTextEditsToBuffer(editor.getBuffer(), edits); + (0, _textEdit().applyTextEditsToBuffer)(editor.getBuffer(), edits); } else { - return Observable.of( - Actions.error( - 'execute', - Error(`Local Rename: Expected file ${path} to be open.`), - ), - ); + return _RxMin.Observable.of(Actions().error('execute', Error(`Local Rename: Expected file ${path} to be open.`))); } } } else { @@ -245,144 +288,115 @@ export function applyRefactoring( // type of the edit. In order to give it this information, we had // no choice but to be SUPER hacky and assign a type to each edit. let typedEdits; + switch (response.type) { case 'external-edit': - typedEdits = Array.from(response.edits.entries()).map( - ([path, edits]) => [ - path, - edits.map(edit => { - return { - type: 'external-edit', - edit, - }; - }), - ], - ); + typedEdits = Array.from(response.edits.entries()).map(([path, edits]) => [path, edits.map(edit => { + return { + type: 'external-edit', + edit + }; + })]); break; + case 'rename-external-edit': - typedEdits = Array.from(response.edits.entries()).map( - ([path, edits]) => [ - path, - edits.map(edit => { - return { - type: 'rename-external-edit', - edit, - }; - }), - ], - ); + typedEdits = Array.from(response.edits.entries()).map(([path, edits]) => [path, edits.map(edit => { + return { + type: 'rename-external-edit', + edit + }; + })]); break; + default: throw new Error(`Unhandled response type: ${response.type}`); - } - - // External text edits are converted into absolute character offsets + } // External text edits are converted into absolute character offsets // and applied directly to disk. - editStream = Observable.from(typedEdits) - .mergeMap(async ([path, textEdits]) => { - const file = getFileForPath(path); - if (file == null) { - throw new Error(`Could not read file ${path}`); + + + editStream = _RxMin.Observable.from(typedEdits).mergeMap(async ([path, textEdits]) => { + const file = (0, _projects().getFileForPath)(path); + + if (file == null) { + throw new Error(`Could not read file ${path}`); + } + + let data = await file.read(); + const buffer = new _atom.TextBuffer(data); + const edits = textEdits.map(textEdit => { + switch (textEdit.type) { + case 'rename-external-edit': + return toAbsoluteCharacterOffsets(buffer, textEdit.edit); + + case 'external-edit': + return textEdit.edit; + + default: + throw new Error(`Unhandled response type: ${response.type}`); } - let data = await file.read(); - const buffer = new TextBuffer(data); - - const edits = textEdits.map(textEdit => { - switch (textEdit.type) { - case 'rename-external-edit': - return toAbsoluteCharacterOffsets(buffer, textEdit.edit); - case 'external-edit': - return textEdit.edit; - default: - throw new Error(`Unhandled response type: ${response.type}`); - } - }); - - edits.sort((a, b) => a.startOffset - b.startOffset); - edits.reverse().forEach(edit => { - if (edit.oldText != null) { - const oldText = data.substring(edit.startOffset, edit.endOffset); - if (oldText !== edit.oldText) { - throw new Error( - `Cannot apply refactor: file contents of ${path} have changed!`, - ); - } + }); + edits.sort((a, b) => a.startOffset - b.startOffset); + edits.reverse().forEach(edit => { + if (edit.oldText != null) { + const oldText = data.substring(edit.startOffset, edit.endOffset); + + if (oldText !== edit.oldText) { + throw new Error(`Cannot apply refactor: file contents of ${path} have changed!`); } - data = - data.slice(0, edit.startOffset) + - edit.newText + - data.slice(edit.endOffset); - }); - await file.write(data); - }, FILE_IO_CONCURRENCY) - .scan((done, _) => done + 1, 0) - .startWith(0) - .map(done => - Actions.progress('Applying edits...', done, response.edits.size), - ); + } + + data = data.slice(0, edit.startOffset) + edit.newText + data.slice(edit.endOffset); + }); + await file.write(data); + }, FILE_IO_CONCURRENCY).scan((done, _) => done + 1, 0).startWith(0).map(done => Actions().progress('Applying edits...', done, response.edits.size)); } - return Observable.concat( - editStream, - Observable.of(Actions.close()).do(() => - analytics.track('nuclide-refactorizer:success'), - ), - ); + + return _RxMin.Observable.concat(editStream, _RxMin.Observable.of(Actions().close()).do(() => _analytics().default.track('nuclide-refactorizer:success'))); }); } -async function loadDiffPreview( - uri: NuclideUri, - response: RefactorEditResponse, -): Promise { - const file = getFileForPath(uri); +async function loadDiffPreview(uri, response) { + const file = (0, _projects().getFileForPath)(uri); + if (file == null) { throw new Error(`Could not read file ${uri}`); } - const buffer = new TextBuffer(await file.read()); - const edits = getEdits(uri, buffer, response); - const diffString = toUnifiedDiff(nuclideUri.basename(uri), buffer, edits); - return Actions.displayDiffPreview(parse(diffString)); + const buffer = new _atom.TextBuffer((await file.read())); + const edits = getEdits(uri, buffer, response); + const diffString = (0, _textEditDiff().toUnifiedDiff)(_nuclideUri().default.basename(uri), buffer, edits); + return Actions().displayDiffPreview((0, _diffparser().default)(diffString)); } -function getEdits( - uri: NuclideUri, - buffer: atom$TextBuffer, - response: RefactorEditResponse, -): Array { +function getEdits(uri, buffer, response) { switch (response.type) { case 'edit': case 'rename-external-edit': return response.edits.get(uri) || []; + case 'external-edit': return (response.edits.get(uri) || []).map(e => toTextEdit(buffer, e)); + default: return []; } } -function toTextEdit(buffer: atom$TextBuffer, edit: ExternalTextEdit): TextEdit { +function toTextEdit(buffer, edit) { return { - oldRange: new Range( - buffer.positionForCharacterIndex(edit.startOffset), - buffer.positionForCharacterIndex(edit.endOffset), - ), + oldRange: new _atom.Range(buffer.positionForCharacterIndex(edit.startOffset), buffer.positionForCharacterIndex(edit.endOffset)), oldText: edit.oldText, - newText: edit.newText, + newText: edit.newText }; } -function toAbsoluteCharacterOffsets( - buffer: atom$TextBuffer, - edit: TextEdit, -): ExternalTextEdit { +function toAbsoluteCharacterOffsets(buffer, edit) { const startingPoint = edit.oldRange.start; const endingPoint = edit.oldRange.end; - return { startOffset: buffer.characterIndexForPosition(startingPoint), endOffset: buffer.characterIndexForPosition(endingPoint), newText: edit.newText, - oldText: edit.oldText, + oldText: edit.oldText }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorReducers.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorReducers.js index 28c89ed2..da984038 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorReducers.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorReducers.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = refactorReducers; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +13,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {RefactorProvider, AvailableRefactoring} from './types'; - -import type { - BackFromDiffPreviewAction, - ConfirmAction, - DisplayDiffPreviewAction, - ExecuteAction, - GotRefactoringsAction, - LoadDiffPreviewAction, - DisplayRenameAction, - OpenAction, - PickedRefactorAction, - ProgressAction, - RefactorAction, - RefactoringPhase, - RefactorState, -} from './types'; - -import invariant from 'assert'; - -export default function refactorReducers( - state_: ?RefactorState, - action: RefactorAction, -): RefactorState { +function refactorReducers(state_, action) { let state = state_; + if (state == null) { - state = {type: 'closed'}; - } + state = { + type: 'closed' + }; + } // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) if (action.error) { // We handle errors in epics, display an appropriate message, and then send an ordinary action // to update the state appropriately. @@ -49,52 +35,69 @@ export default function refactorReducers( switch (action.type) { case 'open': return open(state, action); + case 'got-refactorings': return gotRefactorings(state, action); + case 'close': return close(state); + case 'back-from-diff-preview': return backFromDiffPreview(state, action); + case 'picked-refactor': return pickedRefactor(state, action); + case 'execute': return executeRefactor(state, action); + case 'confirm': return confirmRefactor(state, action); + case 'load-diff-preview': return loadDiffPreview(state, action); + case 'display-diff-preview': return displayDiffPreview(state, action); + case 'display-rename': return displayRename(state, action); + case 'progress': return progress(state, action); + default: return state; } } -function open(state: RefactorState, action: OpenAction): RefactorState { - invariant(state.type === 'closed'); +function open(state, action) { + if (!(state.type === 'closed')) { + throw new Error("Invariant violation: \"state.type === 'closed'\""); + } return { type: 'open', ui: action.ui, phase: { - type: 'get-refactorings', - }, + type: 'get-refactorings' + } }; } -function gotRefactorings( - state: RefactorState, - action: GotRefactoringsAction, -): RefactorState { - invariant(state.type === 'open'); - invariant(state.phase.type === 'get-refactorings'); +function gotRefactorings(state, action) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } - const {editor, originalRange} = action.payload; + if (!(state.phase.type === 'get-refactorings')) { + throw new Error("Invariant violation: \"state.phase.type === 'get-refactorings'\""); + } + const { + editor, + originalRange + } = action.payload; return { type: 'open', ui: state.ui, @@ -103,53 +106,56 @@ function gotRefactorings( provider: action.payload.provider, editor, originalRange, - availableRefactorings: action.payload.availableRefactorings, - }, + availableRefactorings: action.payload.availableRefactorings + } }; } -function close(state: RefactorState): RefactorState { - invariant(state.type === 'open'); +function close(state) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } + return { - type: 'closed', + type: 'closed' }; } -function backFromDiffPreview( - state: RefactorState, - action: BackFromDiffPreviewAction, -): RefactorState { - invariant(state.type === 'open'); +function backFromDiffPreview(state, action) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } - return { - ...state, - phase: action.payload.phase, - }; + return Object.assign({}, state, { + phase: action.payload.phase + }); } -function pickedRefactor( - state: RefactorState, - action: PickedRefactorAction, -): RefactorState { - invariant(state.type === 'open'); - invariant(state.phase.type === 'pick'); +function pickedRefactor(state, action) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } - const {refactoring} = action.payload; - const {provider, editor, originalRange} = state.phase; + if (!(state.phase.type === 'pick')) { + throw new Error("Invariant violation: \"state.phase.type === 'pick'\""); + } + const { + refactoring + } = action.payload; + const { + provider, + editor, + originalRange + } = state.phase; return { type: 'open', ui: state.ui, - phase: getRefactoringPhase(refactoring, provider, editor, originalRange), + phase: getRefactoringPhase(refactoring, provider, editor, originalRange) }; } -function getRefactoringPhase( - refactoring: AvailableRefactoring, - provider: RefactorProvider, - editor: atom$TextEditor, - originalRange: atom$Range, -): RefactoringPhase { +function getRefactoringPhase(refactoring, provider, editor, originalRange) { switch (refactoring.kind) { case 'freeform': return { @@ -157,110 +163,111 @@ function getRefactoringPhase( provider, editor, originalRange, - refactoring, + refactoring }; + default: - invariant(false, `Unexpected refactoring kind ${refactoring.kind}`); + if (!false) { + throw new Error(`Unexpected refactoring kind ${refactoring.kind}`); + } + } } -function executeRefactor( - state: RefactorState, - action: ExecuteAction, -): RefactorState { - invariant(state.type === 'open'); +function executeRefactor(state, action) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } + return { type: 'open', ui: state.ui, phase: { - type: 'execute', - }, + type: 'execute' + } }; } -function confirmRefactor( - state: RefactorState, - action: ConfirmAction, -): RefactorState { - invariant(state.type === 'open'); +function confirmRefactor(state, action) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } + return { type: 'open', ui: state.ui, phase: { type: 'confirm', - response: action.payload.response, - }, + response: action.payload.response + } }; } -function loadDiffPreview( - state: RefactorState, - action: LoadDiffPreviewAction, -): RefactorState { - invariant(state.type === 'open'); +function loadDiffPreview(state, action) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } - return { - ...state, + return Object.assign({}, state, { phase: { type: 'diff-preview', loading: true, diffs: [], - previousPhase: action.payload.previousPhase, - }, - }; + previousPhase: action.payload.previousPhase + } + }); } -function displayDiffPreview( - state: RefactorState, - action: DisplayDiffPreviewAction, -): RefactorState { - invariant(state.type === 'open'); - invariant(state.phase.type === 'diff-preview'); +function displayDiffPreview(state, action) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } - return { - ...state, - phase: { - ...state.phase, + if (!(state.phase.type === 'diff-preview')) { + throw new Error("Invariant violation: \"state.phase.type === 'diff-preview'\""); + } + + return Object.assign({}, state, { + phase: Object.assign({}, state.phase, { loading: false, - diffs: action.payload.diffs, - }, - }; + diffs: action.payload.diffs + }) + }); } -function displayRename( - state: RefactorState, - action: DisplayRenameAction, -): RefactorState { +function displayRename(state, action) { const { provider, editor, selectedText, mountPosition, - symbolPosition, + symbolPosition } = action.payload; - return { type: 'open', - ui: 'rename', // Rename doesn't use MainRefactorComponent so we forgo `state.ui` + ui: 'rename', + // Rename doesn't use MainRefactorComponent so we forgo `state.ui` phase: { type: 'rename', provider, editor, selectedText, mountPosition, - symbolPosition, - }, + symbolPosition + } }; } -function progress(state: RefactorState, action: ProgressAction): RefactorState { - invariant(state.type === 'open'); +function progress(state, action) { + if (!(state.type === 'open')) { + throw new Error("Invariant violation: \"state.type === 'open'\""); + } + return { type: 'open', ui: state.ui, - phase: { - type: 'progress', - ...action.payload, - }, + phase: Object.assign({ + type: 'progress' + }, action.payload) }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorStore.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorStore.js index a3655c4b..89680b43 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorStore.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorStore.js @@ -1,3 +1,65 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getStore = getStore; +exports.getErrors = getErrors; + +function _reduxMin() { + const data = require("redux/dist/redux.min.js"); + + _reduxMin = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _reduxObservable() { + const data = require("../../../../nuclide-commons/redux-observable"); + + _reduxObservable = function () { + return data; + }; + + return data; +} + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _refactorReducers() { + const data = _interopRequireDefault(require("./refactorReducers")); + + _refactorReducers = function () { + return data; + }; + + return data; +} + +function _refactorEpics() { + const data = require("./refactorEpics"); + + _refactorEpics = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,49 +68,26 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {RefactorProvider} from './types'; -import type {Observable} from 'rxjs'; - -import type {Store} from './types'; - // $FlowFixMe - Redux is currently untyped!! -import {createStore, applyMiddleware} from 'redux'; -import {Subject} from 'rxjs'; - -import type ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import { - createEpicMiddleware, - combineEpics, -} from 'nuclide-commons/redux-observable'; -import {getLogger} from 'log4js'; - -import refactorReducers from './refactorReducers'; -import {getEpics} from './refactorEpics'; - // TODO create this lazily -const errors: Subject = new Subject(); +const errors = new _RxMin.Subject(); -function handleError(error: mixed): void { - getLogger('nuclide-refactorizer').error( - 'Uncaught exception in refactoring:', - error, - ); +function handleError(error) { + (0, _log4js().getLogger)('nuclide-refactorizer').error('Uncaught exception in refactoring:', error); errors.next(error); } -export function getStore(providers: ProviderRegistry): Store { +function getStore(providers) { const rootEpic = (actions, store) => { - return combineEpics(...getEpics(providers))(actions, store).catch( - (error, stream) => { - handleError(error); - return stream; - }, - ); + return (0, _reduxObservable().combineEpics)(...(0, _refactorEpics().getEpics)(providers))(actions, store).catch((error, stream) => { + handleError(error); + return stream; + }); }; + const exceptionHandler = store => next => action => { try { return next(action); @@ -56,12 +95,10 @@ export function getStore(providers: ProviderRegistry): Store { handleError(e); } }; - return createStore( - refactorReducers, - applyMiddleware(exceptionHandler, createEpicMiddleware(rootEpic)), - ); + + return (0, _reduxMin().createStore)(_refactorReducers().default, (0, _reduxMin().applyMiddleware)(exceptionHandler, (0, _reduxObservable().createEpicMiddleware)(rootEpic))); } -export function getErrors(): Observable { +function getErrors() { return errors.asObservable(); -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorUIs.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorUIs.js index cda71f21..8cc8a04b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorUIs.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/refactorUIs.js @@ -1,3 +1,72 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.initRefactorUIs = initRefactorUIs; + +function _ReactMountRootElement() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-ui/ReactMountRootElement")); + + _ReactMountRootElement = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +var _atom = require("atom"); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _RenameComponent() { + const data = _interopRequireDefault(require("./components/RenameComponent")); + + _RenameComponent = function () { + return data; + }; + + return data; +} + +function _MainRefactorComponent() { + const data = require("./components/MainRefactorComponent"); + + _MainRefactorComponent = function () { + return data; + }; + + return data; +} + +function Actions() { + const data = _interopRequireWildcard(require("./refactorActions")); + + Actions = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,163 +75,133 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +const refactorUIFactories = [genericRefactorUI, closeOnEscape, focusEditorOnClose, renameShortcut]; -import type {RefactorProvider} from './types'; -import type {RefactorUIFactory, Store, RefactorState} from './types'; - -import ReactMountRootElement from 'nuclide-commons-ui/ReactMountRootElement'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import invariant from 'assert'; -import {Range} from 'atom'; - -import {Observable} from 'rxjs'; -import RenameComponent from './components/RenameComponent'; -import type {Props as RenameComponentPropsType} from './components/RenameComponent'; -import {MainRefactorComponent} from './components/MainRefactorComponent'; -import * as Actions from './refactorActions'; - -const refactorUIFactories: Array = [ - genericRefactorUI, - closeOnEscape, - focusEditorOnClose, - renameShortcut, -]; - -export function initRefactorUIs(store: Store): IDisposable { +function initRefactorUIs(store) { const disposables = refactorUIFactories.map(uiFn => uiFn(store)); - return new UniversalDisposable(...disposables); + return new (_UniversalDisposable().default)(...disposables); } -function genericRefactorUI(store: Store): IDisposable { - const genericRenderer: GenericUIRenderer = new GenericUIRenderer(store); - const inlineRenameRenderer: InlineRenameRenderer = new InlineRenameRenderer( - store, - ); - const disposeFn: () => void = store.subscribe(() => { +function genericRefactorUI(store) { + const genericRenderer = new GenericUIRenderer(store); + const inlineRenameRenderer = new InlineRenameRenderer(store); + const disposeFn = store.subscribe(() => { const state = store.getState(); - if ( - state.type === 'closed' || - (state.type === 'open' && - (state.ui === 'generic' || state.ui === 'rename')) - ) { + + if (state.type === 'closed' || state.type === 'open' && (state.ui === 'generic' || state.ui === 'rename')) { genericRenderer.renderState(state); inlineRenameRenderer.renderState(state); } }); - return new UniversalDisposable( - disposeFn, - genericRenderer, - inlineRenameRenderer, - ); + return new (_UniversalDisposable().default)(disposeFn, genericRenderer, inlineRenameRenderer); } -function closeOnEscape(store: Store): IDisposable { - let escapeSubscription: ?IDisposable = null; - return new UniversalDisposable( - store.subscribe(() => { - const state = store.getState(); - if (state.type === 'open' && escapeSubscription == null) { - escapeSubscription = atom.commands.add('body', 'core:cancel', () => { - store.dispatch(Actions.close()); - }); - } else if (state.type === 'closed') { - invariant(escapeSubscription != null); - escapeSubscription.dispose(); - escapeSubscription = null; +function closeOnEscape(store) { + let escapeSubscription = null; + return new (_UniversalDisposable().default)(store.subscribe(() => { + const state = store.getState(); + + if (state.type === 'open' && escapeSubscription == null) { + escapeSubscription = atom.commands.add('body', 'core:cancel', () => { + store.dispatch(Actions().close()); + }); + } else if (state.type === 'closed') { + if (!(escapeSubscription != null)) { + throw new Error("Invariant violation: \"escapeSubscription != null\""); } - }), - ); + + escapeSubscription.dispose(); + escapeSubscription = null; + } + })); } -function focusEditorOnClose(store: Store): IDisposable { - return new UniversalDisposable( - store.subscribe(() => { - const state = store.getState(); - if (state.type === 'closed') { - const editor = atom.workspace.getActiveTextEditor(); - if (editor == null) { - return; - } - const pane = atom.workspace.paneForItem(editor); - if (pane == null) { - return; - } - pane.activate(); - pane.activateItem(editor); +function focusEditorOnClose(store) { + return new (_UniversalDisposable().default)(store.subscribe(() => { + const state = store.getState(); + + if (state.type === 'closed') { + const editor = atom.workspace.getActiveTextEditor(); + + if (editor == null) { + return; } - }), - ); -} -function renameShortcut(store: Store): IDisposable { - const renderer: GenericUIRenderer = new GenericUIRenderer(store); - return new UniversalDisposable( - store.subscribe(() => { - const state = store.getState(); - if (state.type === 'closed') { - renderer.renderState(state); + const pane = atom.workspace.paneForItem(editor); + + if (pane == null) { return; } - if (state.ui === 'rename') { - const {phase} = state; - switch (phase.type) { - case 'pick': - let renameRefactoring = null; - for (const refactoring of phase.availableRefactorings) { - if ( - refactoring.kind === 'rename' || - (refactoring.kind === 'freeform' && - refactoring.disabled !== false && - refactoring.name.match(/rename/i)) - ) { - renameRefactoring = refactoring; - break; - } - } - if (renameRefactoring == null) { - atom.notifications.addWarning( - 'Unable to rename at this location', - ); - store.dispatch(Actions.close()); - } else { - store.dispatch(Actions.pickedRefactor(renameRefactoring)); + + pane.activate(); + pane.activateItem(editor); + } + })); +} + +function renameShortcut(store) { + const renderer = new GenericUIRenderer(store); + return new (_UniversalDisposable().default)(store.subscribe(() => { + const state = store.getState(); + + if (state.type === 'closed') { + renderer.renderState(state); + return; + } + + if (state.ui === 'rename') { + const { + phase + } = state; + + switch (phase.type) { + case 'pick': + let renameRefactoring = null; + + for (const refactoring of phase.availableRefactorings) { + if (refactoring.kind === 'rename' || refactoring.kind === 'freeform' && refactoring.disabled !== false && refactoring.name.match(/rename/i)) { + renameRefactoring = refactoring; + break; } - break; - default: - renderer.renderState(state); - } + } + + if (renameRefactoring == null) { + atom.notifications.addWarning('Unable to rename at this location'); + store.dispatch(Actions().close()); + } else { + store.dispatch(Actions().pickedRefactor(renameRefactoring)); + } + + break; + + default: + renderer.renderState(state); } - }), - ); + } + })); } class GenericUIRenderer { - _panel: ?atom$Panel; - _store: Store; - - constructor(store: Store) { + constructor(store) { this._store = store; } - renderState(state: RefactorState) { - if ( - state.type === 'open' && - state.phase.type !== 'rename' && - !(state.ui === 'rename' && state.phase.type === 'execute') - ) { + renderState(state) { + if (state.type === 'open' && state.phase.type !== 'rename' && !(state.ui === 'rename' && state.phase.type === 'execute')) { if (this._panel == null) { const element = document.createElement('div'); - this._panel = atom.workspace.addModalPanel({item: element}); + this._panel = atom.workspace.addModalPanel({ + item: element + }); } - ReactDOM.render( - , - this._panel.getItem(), - ); + + _reactDom.default.render(React.createElement(_MainRefactorComponent().MainRefactorComponent, { + appState: state, + store: this._store + }), this._panel.getItem()); } else { this.dispose(); } @@ -171,108 +210,74 @@ class GenericUIRenderer { dispose() { if (this._panel != null) { const panel = this._panel; - ReactDOM.unmountComponentAtNode(panel.getItem()); + + _reactDom.default.unmountComponentAtNode(panel.getItem()); + panel.destroy(); this._panel = null; } } + } class InlineRenameRenderer { - _store: Store; - _disposable: ?IDisposable; - - constructor(store: Store) { + constructor(store) { this._store = store; } - renderRenameInput( - editor: atom$TextEditor, - selectedText: string, - provider: RefactorProvider, - symbolPosition: atom$Point, - ): React.Element> { - return ( - - ); + renderRenameInput(editor, selectedText, provider, symbolPosition) { + return React.createElement(_RenameComponent().default, { + selectedText: selectedText, + provider: provider, + parentEditor: editor, + store: this._store, + symbolPosition: symbolPosition + }); } - mountRenameInput( - editor: atom$TextEditor, - mountPosition: atom$Point, - container: ReactMountRootElement, - element: React.Element>, - ): IDisposable { - const overlayMarker = editor.markBufferRange( - new Range(mountPosition, mountPosition), - { - invalidate: 'never', - }, - ); - + mountRenameInput(editor, mountPosition, container, element) { + const overlayMarker = editor.markBufferRange(new _atom.Range(mountPosition, mountPosition), { + invalidate: 'never' + }); editor.decorateMarker(overlayMarker, { type: 'overlay', position: 'tail', - item: container, + item: container }); + return new (_UniversalDisposable().default)(() => overlayMarker.destroy(), () => _reactDom.default.unmountComponentAtNode(container), // The editor may not mount the marker until the next update. + // It's not safe to render anything until that point, as overlayed containers + // often need to measure their size in the DOM. + _RxMin.Observable.from(editor.getElement().getNextUpdatePromise()).subscribe(() => { + container.style.display = 'block'; - return new UniversalDisposable( - () => overlayMarker.destroy(), - () => ReactDOM.unmountComponentAtNode(container), - - // The editor may not mount the marker until the next update. - // It's not safe to render anything until that point, as overlayed containers - // often need to measure their size in the DOM. - Observable.from(editor.getElement().getNextUpdatePromise()).subscribe( - () => { - container.style.display = 'block'; - ReactDOM.render(element, container); - }, - ), - ); + _reactDom.default.render(element, container); + })); } - renderState(state: RefactorState) { + renderState(state) { if (state.type === 'open' && state.phase.type === 'rename') { - const container = new ReactMountRootElement(); + const container = new (_ReactMountRootElement().default)(); container.className = 'nuclide-refactorizer-rename-container'; - const { provider, editor, selectedText, mountPosition, - symbolPosition, + symbolPosition } = state.phase; - - const renameElement = this.renderRenameInput( - editor, - selectedText, - provider, - symbolPosition, - ); - - this._disposable = this.mountRenameInput( - editor, - mountPosition, - container, - renameElement, - ); + const renameElement = this.renderRenameInput(editor, selectedText, provider, symbolPosition); + this._disposable = this.mountRenameInput(editor, mountPosition, container, renameElement); } else { this.dispose(); } } - dispose(): void { + dispose() { if (this._disposable != null) { this._disposable.dispose(); + this._disposable = null; } } -} + +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/types.js index eb45c222..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-refactor/lib/types.js @@ -1,382 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local - * @format - */ - -/* - * This file houses types that are internal to this package. Types that are part of its public - * interface are exported from main.js - */ -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {Observable} from 'rxjs'; -import type {TextEdit} from 'nuclide-commons-atom/text-edit'; - -export type Store = { - // Returns unsubscribe function - subscribe(fn: () => mixed): () => void, - dispatch(action: RefactorAction): void, - getState(): RefactorState, -}; - -export type RefactorUIFactory = (store: Store) => IDisposable; - -export type RefactorUI = 'generic' | 'rename'; - -// Server Response & RPC Requests to Apply them - -export type FreeformEnumValue = { - value: string, - description: string, -}; - -// Factoring out `description` confuses Flow when filtering on the type. -export type FreeformRefactoringArgument = - | { - type: 'string', - name: string, - description: string, - default?: string, - } - | { - type: 'boolean', - name: string, - description: string, - default?: boolean, - } - | { - type: 'enum', - name: string, - description: string, - options: Array, - default?: string, - }; - -// A freeform refactoring type. -// This allows providers to define completely new refactoring commands, -// as well as ask for arbitrary arguments to the refactoring command. -export type FreeformRefactoring = { - kind: 'freeform', - // Unique identifier which will be used in the request. - id: string, - // Display name of the refactoring. - name: string, - // User-friendly description of what the refactoring does. - description: string, - // Full affected range of the refactoring. - range: atom$Range, - // Additional arguments to be requested from the user. - // The `name`s should be unique identifiers, which will be used in the request. - arguments: Array, - // Providers can return disabled refactorings to improve discoverability. - disabled?: boolean, -}; - -export type AvailableRefactoring = FreeformRefactoring; - -// For edits outside of Atom editors, it's easier and more efficient to use -// absolute character offsets rather than line/column ranges. -export type ExternalTextEdit = { - startOffset: number, - endOffset: number, - newText: string, - // If included, this will be used to verify that the edit still applies cleanly. - oldText?: string, -}; - -// Regular "edits" are intended for changes inside open files. -// These will be applied to the buffer and will not be immediately saved. -// This is appropriate for small-scale changes to a set of files. -export type EditResponse = { - type: 'edit', - edits: Map>, -}; - -// ExternalEdits & RenameExternalEdits are intended for changes that include unopened files. -// These edits will be written directly to disk, bypassing Atom. - -// The format of RenameExternaledits is the same as that of regular "edits". -// However, during their application, they will first be converted -// into absolute character offsets. -export type RenameExternalEditResponse = { - type: 'rename-external-edit', - edits: Map>, -}; - -// The format of ExternalEdits are those of ExternalTextEdit, and are -// already formatted as absolute character offsets. These are responses -// to the requests sent by the FreeformRefactorComponent. -export type ExternalEditResponse = { - type: 'external-edit', - edits: Map>, -}; -// An intermediate response to display progress in the UI. -export type ProgressResponse = { - type: 'progress', - message: string, - value: number, - max: number, -}; - -export type RefactorEditResponse = - | EditResponse - | ExternalEditResponse - | RenameExternalEditResponse; - -export type RefactorResponse = RefactorEditResponse | ProgressResponse; - -// State - -export type ClosedState = {| - type: 'closed', -|}; - -export type OpenState = {| - type: 'open', - ui: RefactorUI, - phase: Phase, -|}; - -export type RefactorState = ClosedState | OpenState; - -export type GetRefactoringsPhase = {| - type: 'get-refactorings', -|}; - -export type PickPhase = {| - type: 'pick', - provider: RefactorProvider, - originalRange: atom$Range, - editor: atom$TextEditor, - availableRefactorings: Array, -|}; - -export type FreeformPhase = {| - type: 'freeform', - provider: RefactorProvider, - editor: atom$TextEditor, - originalRange: atom$Range, - refactoring: FreeformRefactoring, -|}; - -export type ExecutePhase = {| - type: 'execute', -|}; - -// For multi-file changes, add a confirmation step. -export type ConfirmPhase = {| - type: 'confirm', - response: RefactorEditResponse, -|}; - -export type DiffPreviewPhase = {| - type: 'diff-preview', - loading: boolean, - diffs: Array, - previousPhase: Phase, -|}; - -export type RenamePhase = {| - type: 'rename', - provider: RefactorProvider, - editor: TextEditor, - selectedText: string, - mountPosition: atom$Point, - symbolPosition: atom$Point, -|}; - -export type ProgressPhase = {| - type: 'progress', - message: string, - value: number, - max: number, -|}; - -export type Phase = - | GetRefactoringsPhase - | PickPhase - | RenamePhase - | FreeformPhase - | ExecutePhase - | ConfirmPhase - | DiffPreviewPhase - | RenamePhase - | ProgressPhase; - -export type RefactoringPhase = RenamePhase | FreeformPhase; - -// Actions - -export type OpenAction = {| - type: 'open', - ui: RefactorUI, -|}; - -export type BackFromDiffPreviewAction = {| - type: 'back-from-diff-preview', - payload: { - phase: Phase, - }, -|}; - -export type GotRefactoringsAction = {| - type: 'got-refactorings', - payload: { - originalRange: atom$Range, - editor: atom$TextEditor, - provider: RefactorProvider, - availableRefactorings: Array, - }, -|}; - -export type ErrorSource = 'get-refactorings' | 'execute'; - -export type ErrorAction = {| - type: 'error', - payload: { - source: ErrorSource, - error: Error, - }, -|}; - -export type CloseAction = {| - type: 'close', -|}; - -export type PickedRefactorAction = {| - type: 'picked-refactor', - payload: { - refactoring: AvailableRefactoring, - }, -|}; - -export type ExecuteAction = {| - type: 'execute', - payload: { - provider: RefactorProvider, - refactoring: RefactorRequest, - }, -|}; - -export type ConfirmAction = {| - type: 'confirm', - payload: { - response: RefactorEditResponse, - }, -|}; - -export type LoadDiffPreviewAction = {| - type: 'load-diff-preview', - payload: { - previousPhase: Phase, - uri: NuclideUri, - response: RefactorEditResponse, - }, -|}; - -export type DisplayDiffPreviewAction = {| - type: 'display-diff-preview', - payload: { - diffs: Array, - }, -|}; - -export type DisplayRenameAction = {| - type: 'display-rename', - payload: { - editor: TextEditor, - provider: RefactorProvider, - selectedText: string, - mountPosition: atom$Point, - symbolPosition: atom$Point, - }, -|}; - -export type ApplyAction = {| - type: 'apply', - payload: { - response: RefactorEditResponse, - }, -|}; - -export type ProgressAction = {| - type: 'progress', - payload: { - message: string, - value: number, - max: number, - }, -|}; - -export type RefactorAction = - | OpenAction - | CloseAction - | BackFromDiffPreviewAction - | PickedRefactorAction - | GotRefactoringsAction - | ErrorAction - | ExecuteAction - | ConfirmAction - | LoadDiffPreviewAction - | DisplayDiffPreviewAction - | DisplayRenameAction - | ApplyAction - | ProgressAction; - -// Provider - -export type RenameRefactorKind = 'rename'; -export type FreeformRefactorKind = 'freeform'; - -export type RefactorKind = RenameRefactorKind | FreeformRefactorKind; - -export type RenameRequest = { - kind: RenameRefactorKind, - newName: string, - editor: TextEditor, - position: atom$Point, -}; - -export type FreeformRefactorRequest = {| - kind: FreeformRefactorKind, - editor: atom$TextEditor, - originalRange: atom$Range, - // Echoes FreeformRefactoring.id. - id: string, - // Echoes FreeformRefactoring.range. - range: atom$Range, - // Arguments provided by the user. - arguments: Map, -|}; - -export type RefactorRequest = RenameRequest | FreeformRefactorRequest; - -export type RefactorProvider = { - priority: number, - grammarScopes: Array, - - refactorings?: ( - editor: atom$TextEditor, - range: atom$Range, - ) => Promise>, - - // Providers may stream back progress responses. - // Note that the stream will terminate once an edit response is received. - // If no edit response is received, an error will be raised. - refactor?: (request: RefactorRequest) => Observable, - - // This method is linked to LSP in the LSP version of RefactorProvider - // Obtains a mapping of document paths to their text edits. - // Each text edit is a rename of the same subject - rename?: ( - editor: TextEditor, - position: atom$Point, - newName: string, - ) => Promise>>, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-signature-help/__atom_tests__/SignatureHelpManager-test.js b/modules/atom-ide-ui/pkg/atom-ide-signature-help/__atom_tests__/SignatureHelpManager-test.js index 5bcc1608..49858a03 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-signature-help/__atom_tests__/SignatureHelpManager-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-signature-help/__atom_tests__/SignatureHelpManager-test.js @@ -1,3 +1,51 @@ +"use strict"; + +var _atom = require("atom"); + +function _testHelpers() { + const data = require("../../../../nuclide-commons-atom/test-helpers"); + + _testHelpers = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("../../../../nuclide-commons/promise"); + + _promise = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _waits_for() { + const data = _interopRequireDefault(require("../../../../../jest/waits_for")); + + _waits_for = function () { + return data; + }; + + return data; +} + +var _path = _interopRequireDefault(require("path")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,185 +54,152 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DatatipService} from '../../atom-ide-datatip/lib/types'; -import type {SignatureHelp, SignatureHelpProvider} from '../lib/types'; - -import {Point, Range} from 'atom'; -import {attachWorkspace} from 'nuclide-commons-atom/test-helpers'; -import {nextTick} from 'nuclide-commons/promise'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import waitsFor from '../../../../../jest/waits_for'; -import path from 'path'; - const sleep = n => new Promise(r => setTimeout(r, n)); describe.skip('SignatureHelpManager', () => { - let disposable: IDisposable; - let testProvider: SignatureHelpProvider; - let mockDatatipService: DatatipService; - let editor: atom$TextEditor; - + let disposable; + let testProvider; + let mockDatatipService; + let editor; beforeEach(async () => { - attachWorkspace(); - atom.packages.activatePackage(path.resolve(__dirname, '../')); - + (0, _testHelpers().attachWorkspace)(); + atom.packages.activatePackage(_path.default.resolve(__dirname, '../')); editor = await atom.workspace.open(); - testProvider = { priority: 1, grammarScopes: ['text.plain.null-grammar'], triggerCharacters: new Set(['(']), - getSignatureHelp: jest.fn().mockReturnValue( - Promise.resolve( - ({ - signatures: [ - { - label: 'test signature', - }, - ], - }: SignatureHelp), - ), - ), + getSignatureHelp: jest.fn().mockReturnValue(Promise.resolve({ + signatures: [{ + label: 'test signature' + }] + })) }; mockDatatipService = { addProvider() { throw new Error(); }, + addModifierProvider() { throw new Error(); }, - createPinnedDataTip: jest.fn().mockReturnValue(new UniversalDisposable()), + + createPinnedDataTip: jest.fn().mockReturnValue(new (_UniversalDisposable().default)()) }; atom.packages.serviceHub.consume('signature-help', '0.1.0', registry => { disposable = registry(testProvider); }); - atom.packages.serviceHub.provide('datatip', '0.1.0', mockDatatipService); + atom.packages.serviceHub.provide('datatip', '0.1.0', mockDatatipService); // Active editor debounce. - // Active editor debounce. await sleep(500); }); - afterEach(() => { disposable.dispose(); }); - it('responds to manual triggers', async () => { editor.insertText('test'); atom.commands.dispatch(editor.getElement(), 'signature-help:show'); - - await waitsFor(() => testProvider.getSignatureHelp.mock.calls.length > 0); + await (0, _waits_for().default)(() => testProvider.getSignatureHelp.mock.calls.length > 0); const signatureSpy = testProvider.getSignatureHelp; expect(signatureSpy.mock.calls.length).toBe(1); - expect(signatureSpy.calls[0].args).toEqual([editor, new Point(0, 4)]); - - // Wait for promise to be resolved. - await nextTick(); + expect(signatureSpy.calls[0].args).toEqual([editor, new _atom.Point(0, 4)]); // Wait for promise to be resolved. + await (0, _promise().nextTick)(); const datatipSpy = mockDatatipService.createPinnedDataTip; expect(datatipSpy.mock.calls.length).toBe(1); - expect(datatipSpy.calls[0][0].range).toEqual(new Range([0, 4], [0, 4])); + expect(datatipSpy.calls[0][0].range).toEqual(new _atom.Range([0, 4], [0, 4])); // Moving the cursor should immediately move the datatip and query again. - // Moving the cursor should immediately move the datatip and query again. editor.setCursorBufferPosition([0, 3]); - expect(datatipSpy.mock.calls.length).toBe(2); - expect(datatipSpy.calls[1][0].range).toEqual(new Range([0, 3], [0, 3])); + expect(datatipSpy.calls[1][0].range).toEqual(new _atom.Range([0, 3], [0, 3])); // Compensate for the debounce. - // Compensate for the debounce. await sleep(500); expect(signatureSpy.mock.calls.length).toBe(2); - expect(signatureSpy.calls[1].args).toEqual([editor, new Point(0, 3)]); + expect(signatureSpy.calls[1].args).toEqual([editor, new _atom.Point(0, 3)]); // Wait for promise to be resolved. - // Wait for promise to be resolved. - await nextTick(); - expect(datatipSpy.mock.calls.length).toBe(3); + await (0, _promise().nextTick)(); + expect(datatipSpy.mock.calls.length).toBe(3); // Once the signature returns null, abort the flow. - // Once the signature returns null, abort the flow. signatureSpy.mockReturnValue(Promise.resolve(null)); - editor.setCursorBufferPosition([0, 0]); + editor.setCursorBufferPosition([0, 0]); // No repositioning when the cursor moves too far. - // No repositioning when the cursor moves too far. - expect(datatipSpy.mock.calls.length).toBe(3); + expect(datatipSpy.mock.calls.length).toBe(3); // Compensate for the debounce. - // Compensate for the debounce. await sleep(500); expect(signatureSpy.mock.calls.length).toBe(3); - - await nextTick(); + await (0, _promise().nextTick)(); expect(datatipSpy.mock.calls.length).toBe(3); - editor.setCursorBufferPosition([0, 1]); await sleep(500); expect(signatureSpy.mock.calls.length).toBe(3); }); - it('responds to typing trigger characters', async () => { editor.insertText('a'); await sleep(1); // debounce + const signatureSpy = testProvider.getSignatureHelp; expect(signatureSpy.mock.calls.length).toBe(0); - editor.insertText('('); await sleep(1); // debounce + expect(signatureSpy.mock.calls.length).toBe(1); - expect(signatureSpy.calls[0].args).toEqual([editor, new Point(0, 2)]); + expect(signatureSpy.calls[0].args).toEqual([editor, new _atom.Point(0, 2)]); // We've tested the regular flow above; test cancellation too. - // We've tested the regular flow above; test cancellation too. /* global KeyboardEvent */ - const escape = new KeyboardEvent('keydown'); - // No APi to set keyCode :( - Object.defineProperty(escape, 'keyCode', {value: 27}); - editor.getElement().dispatchEvent(escape); - // Contains a trigger character, but has the wrong cursor. + const escape = new KeyboardEvent('keydown'); // No APi to set keyCode :( + + Object.defineProperty(escape, 'keyCode', { + value: 27 + }); + editor.getElement().dispatchEvent(escape); // Contains a trigger character, but has the wrong cursor. + editor.insertText('x(y'); await sleep(500); // debounce - expect(signatureSpy.mock.calls.length).toBe(1); - // Test the autocomplete-plus scenario: insertion + selection + expect(signatureSpy.mock.calls.length).toBe(1); // Test the autocomplete-plus scenario: insertion + selection + const startCol = editor.getCursorBufferPosition().column; editor.insertText('abc(arg1, arg2)'); editor.setSelectedBufferRange([[0, startCol + 4], [0, startCol + 7]]); await sleep(1); // debounce + expect(signatureSpy.mock.calls.length).toBe(2); }); - it('responds to typing over a selection', async () => { editor.setText('(abcdef'); await sleep(1); // debounce + const signatureSpy = testProvider.getSignatureHelp; expect(signatureSpy.mock.calls.length).toBe(0); - - editor.setSelectedBufferRange(new Range([0, 0], [0, 1])); + editor.setSelectedBufferRange(new _atom.Range([0, 0], [0, 1])); editor.insertText('('); expect(editor.getText()).toBe('(abcdef'); await sleep(1); // debounce + expect(signatureSpy.mock.calls.length).toBe(1); - expect(signatureSpy.calls[0].args).toEqual([editor, new Point(0, 1)]); + expect(signatureSpy.calls[0].args).toEqual([editor, new _atom.Point(0, 1)]); }); - it('can be dynamically toggled via config', async () => { atom.config.set('atom-ide-signature-help.enable', false); - editor.setText('test'); await sleep(1); // debounce + const signatureSpy = testProvider.getSignatureHelp; expect(signatureSpy.mock.calls.length).toBe(0); - editor.insertText('('); expect(editor.getText()).toBe('test('); await sleep(1); // debounce - expect(signatureSpy.mock.calls.length).toBe(0); + expect(signatureSpy.mock.calls.length).toBe(0); atom.config.set('atom-ide-signature-help.enable', true); - editor.insertText('('); expect(editor.getText()).toBe('test(('); await sleep(1); // debounce + expect(signatureSpy.mock.calls.length).toBe(1); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-signature-help/__atom_tests__/getSignatureDatatip-test.js b/modules/atom-ide-ui/pkg/atom-ide-signature-help/__atom_tests__/getSignatureDatatip-test.js index 3ca8bff6..cf6c4da1 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-signature-help/__atom_tests__/getSignatureDatatip-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-signature-help/__atom_tests__/getSignatureDatatip-test.js @@ -1,3 +1,39 @@ +"use strict"; + +function _marked() { + const data = _interopRequireDefault(require("marked")); + + _marked = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _string() { + const data = require("../../../../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _getSignatureDatatip() { + const data = _interopRequireDefault(require("../lib/getSignatureDatatip")); + + _getSignatureDatatip = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,76 +42,59 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import marked from 'marked'; -import {Point, Range} from 'atom'; -import {escapeMarkdown} from 'nuclide-commons/string'; -import getSignatureDatatip from '../lib/getSignatureDatatip'; - describe('getSignatureDatatip', () => { - const point = new Point(0, 0); - const range = new Range(point, point); - + const point = new _atom.Point(0, 0); + const range = new _atom.Range(point, point); it('is able to escape markdown in the label', () => { - expect( - getSignatureDatatip( - { - signatures: [ - { - label: 'f(__arg__, *args, **kwargs)', - documentation: '**real markdown**', - parameters: [ - { - label: '**kwargs', - documentation: 'parameter test', - }, - ], - }, - ], - }, - point, - ), - ).toEqual({ - markedStrings: [ - { - type: 'markdown', - value: - 'f<T>\\(\\_\\_arg\\_\\_, \\*args, **\\*\\*kwargs**\\)', - }, - {type: 'markdown', value: 'parameter test'}, - {type: 'markdown', value: '**real markdown**'}, - ], - range, + expect((0, _getSignatureDatatip().default)({ + signatures: [{ + label: 'f(__arg__, *args, **kwargs)', + documentation: '**real markdown**', + parameters: [{ + label: '**kwargs', + documentation: 'parameter test' + }] + }] + }, point)).toEqual({ + markedStrings: [{ + type: 'markdown', + value: 'f<T>\\(\\_\\_arg\\_\\_, \\*args, **\\*\\*kwargs**\\)' + }, { + type: 'markdown', + value: 'parameter test' + }, { + type: 'markdown', + value: '**real markdown**' + }], + range }); }); - it('escapes all markdown correctly', () => { const s = 'atoz0TO9 !#()*+-.[\\]_`{}'; - expect(marked(escapeMarkdown(s))).toEqual(`

    ${s}

    \n`); + expect((0, _marked().default)((0, _string().escapeMarkdown)(s))).toEqual(`

    ${s}

    \n`); }); - it('is able to bolden ambiguous parameters', () => { - expect( - getSignatureDatatip( - { - signatures: [ - { - label: 'path(path, path, path)', - parameters: [{label: 'path'}, {label: 'path'}, {label: 'path'}], - }, - ], - activeParameter: 1, - }, - point, - ), - ).toEqual({ - markedStrings: [ - {type: 'markdown', value: 'path\\(path, **path**, path\\)'}, - ], - range, + expect((0, _getSignatureDatatip().default)({ + signatures: [{ + label: 'path(path, path, path)', + parameters: [{ + label: 'path' + }, { + label: 'path' + }, { + label: 'path' + }] + }], + activeParameter: 1 + }, point)).toEqual({ + markedStrings: [{ + type: 'markdown', + value: 'path\\(path, **path**, path\\)' + }], + range }); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/SignatureHelpManager.js b/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/SignatureHelpManager.js index 166f8933..e6c0083b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/SignatureHelpManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/SignatureHelpManager.js @@ -1,3 +1,104 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _debounced() { + const data = require("../../../../nuclide-commons-atom/debounced"); + + _debounced = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _textEditor() { + const data = require("../../../../nuclide-commons-atom/text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _getSignatureDatatip() { + const data = _interopRequireDefault(require("./getSignatureDatatip")); + + _getSignatureDatatip = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,59 +107,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DatatipService} from '../../atom-ide-datatip/lib/types'; -import type {SignatureHelp, SignatureHelpProvider} from './types'; - -import {getLogger} from 'log4js'; -import {observeActiveEditorsDebounced} from 'nuclide-commons-atom/debounced'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {getCursorPositions} from 'nuclide-commons-atom/text-editor'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {completingSwitchMap, fastDebounce} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Observable, Subject} from 'rxjs'; -import getSignatureDatatip from './getSignatureDatatip'; - // Chosen to be a little more than the default key repeat rate. -const CURSOR_DEBOUNCE_TIME = 200; +const CURSOR_DEBOUNCE_TIME = 200; // Aggressively timeout signature requests to avoid 'stuck' signatures. -// Aggressively timeout signature requests to avoid 'stuck' signatures. const SIGNATURE_TIMEOUT = 2500; -export default class SignatureHelpManager { - _disposables: UniversalDisposable; - _datatipService: ?DatatipService; - _providerRegistry: ProviderRegistry; - _commands: Subject = new Subject(); - +class SignatureHelpManager { constructor() { - this._providerRegistry = new ProviderRegistry(); - this._disposables = new UniversalDisposable( - this._subscribeToEditors(), - // Share the command subscription between all editors. - atom.commands.add('atom-text-editor', 'signature-help:show', () => { - this._commands.next(atom.workspace.getActiveTextEditor()); - }), - ); + this._commands = new _RxMin.Subject(); + this._providerRegistry = new (_ProviderRegistry().default)(); + this._disposables = new (_UniversalDisposable().default)(this._subscribeToEditors(), // Share the command subscription between all editors. + atom.commands.add('atom-text-editor', 'signature-help:show', () => { + this._commands.next(atom.workspace.getActiveTextEditor()); + })); } dispose() { this._disposables.dispose(); } - setDatatipService(service: ?DatatipService) { + setDatatipService(service) { this._datatipService = service; } - consumeSignatureHelp(provider: SignatureHelpProvider) { + consumeSignatureHelp(provider) { return this._providerRegistry.addProvider(provider); } - /** * The signature help flow looks like this: * 1) Wait for signature help to be triggered. @@ -68,186 +145,139 @@ export default class SignatureHelpManager { * New signatures are continously fetched as the cursor moves. * 3) Signature help stops once we get a null signature, or once the user hits 'escape'. */ - _subscribeToEditors(): rxjs$Subscription { - return observeActiveEditorsDebounced(0) - .switchMap(editor => { - if (editor == null) { - return Observable.of({editor, signatureHelp: null}); - } - return featureConfig - .observeAsStream('atom-ide-signature-help.enable', { - scope: editor.getRootScopeDescriptor(), - }) - .switchMap(enabled => { - if (enabled === false) { - return Observable.empty(); - } - return this._signatureHelpTriggers(editor) - .exhaustMap(() => this._getSignatureStream(editor)) - .takeUntil( - observableFromSubscribeFunction( - editor.onDidDestroy.bind(editor), - ), - ) - .map(signatureHelp => ({editor, signatureHelp})); - }); - }) - .switchMap(({editor, signatureHelp}) => { - if (editor != null && signatureHelp != null) { - return this._showSignatureDatatip(editor, signatureHelp); + + + _subscribeToEditors() { + return (0, _debounced().observeActiveEditorsDebounced)(0).switchMap(editor => { + if (editor == null) { + return _RxMin.Observable.of({ + editor, + signatureHelp: null + }); + } + + return _featureConfig().default.observeAsStream('atom-ide-signature-help.enable', { + scope: editor.getRootScopeDescriptor() + }).switchMap(enabled => { + if (enabled === false) { + return _RxMin.Observable.empty(); } - return Observable.empty(); - }) - .subscribe(); - } + return this._signatureHelpTriggers(editor).exhaustMap(() => this._getSignatureStream(editor)).takeUntil((0, _event().observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor))).map(signatureHelp => ({ + editor, + signatureHelp + })); + }); + }).switchMap(({ + editor, + signatureHelp + }) => { + if (editor != null && signatureHelp != null) { + return this._showSignatureDatatip(editor, signatureHelp); + } + + return _RxMin.Observable.empty(); + }).subscribe(); + } /** * A stream of all signature help triggers. */ - _signatureHelpTriggers(editor: atom$TextEditor): Observable { - return Observable.merge( - // 1) Any keypresses that match a triggerCharacter. - observableFromSubscribeFunction(cb => - editor.getBuffer().onDidChangeText(cb), - ) - // The change events and cursor changes are often sequential. - // We need to make sure we use the final cursor position. - .let(fastDebounce(0)) - .filter(edit => { - if (edit.changes.length !== 1) { - return false; - } - const change = edit.changes[0]; - // Use the start of the current selection as the cursor position. - // (Autocomplete often inserts a placeholder and puts the cursor at the end.) - const cursorPosition = editor.getSelectedBufferRange().start; - if ( - change.newText.length === 0 || - // Don't allow multi-line changes. - change.newRange.start.row !== change.newRange.end.row || - // The change should cover the current cursor position. - !change.newRange.containsPoint(cursorPosition) - ) { - return false; - } - // Use the character before the cursor as the 'trigger character'. - const index = Math.max( - 0, - cursorPosition.column - change.newRange.start.column - 1, - ); - for (const provider of this._providerRegistry.getAllProvidersForEditor( - editor, - )) { - if (provider.triggerCharacters != null) { - if (provider.triggerCharacters.has(change.newText[index])) { - return true; - } - } + + + _signatureHelpTriggers(editor) { + return _RxMin.Observable.merge( // 1) Any keypresses that match a triggerCharacter. + (0, _event().observableFromSubscribeFunction)(cb => editor.getBuffer().onDidChangeText(cb)) // The change events and cursor changes are often sequential. + // We need to make sure we use the final cursor position. + .let((0, _observable().fastDebounce)(0)).filter(edit => { + if (edit.changes.length !== 1) { + return false; + } + + const change = edit.changes[0]; // Use the start of the current selection as the cursor position. + // (Autocomplete often inserts a placeholder and puts the cursor at the end.) + + const cursorPosition = editor.getSelectedBufferRange().start; + + if (change.newText.length === 0 || // Don't allow multi-line changes. + change.newRange.start.row !== change.newRange.end.row || // The change should cover the current cursor position. + !change.newRange.containsPoint(cursorPosition)) { + return false; + } // Use the character before the cursor as the 'trigger character'. + + + const index = Math.max(0, cursorPosition.column - change.newRange.start.column - 1); + + for (const provider of this._providerRegistry.getAllProvidersForEditor(editor)) { + if (provider.triggerCharacters != null) { + if (provider.triggerCharacters.has(change.newText[index])) { + return true; } - return false; - }), - // 2) Explicit usage of the Atom command. - this._commands.filter(e => e === editor), - ); - } + } + } + return false; + }), // 2) Explicit usage of the Atom command. + this._commands.filter(e => e === editor)); + } /** * A stream of all signatures from an editor. */ - _getSignatureStream(editor: atom$TextEditor): Observable { - return Observable.concat( - // Start with a null signature to clear out any prior signatures. - Observable.of(null), - // Immediately start fetching signatures for the current position. - Observable.concat( - Observable.defer(() => Observable.of(editor.getCursorBufferPosition())), - // Further cursor changes will be debounced. - getCursorPositions(editor).let(fastDebounce(CURSOR_DEBOUNCE_TIME)), - ) - .distinctUntilChanged((a, b) => a.isEqual(b)) - .switchMap(point => this._getSignatures(editor, point)) - // Stop once we get a null result. - .takeWhile(Boolean) - // Stop once the escape key is pressed. - // NOTE: we can't use core:cancel because plugins like vim-mode-plus override it. - .takeUntil( - Observable.fromEvent(editor.getElement(), 'keydown').filter( - (evt: KeyboardEvent) => evt.keyCode === 27, - ), - ), - // Terminate with a null signature to clear any visible signatures. - Observable.of(null), - ); - } + + _getSignatureStream(editor) { + return _RxMin.Observable.concat( // Start with a null signature to clear out any prior signatures. + _RxMin.Observable.of(null), // Immediately start fetching signatures for the current position. + _RxMin.Observable.concat(_RxMin.Observable.defer(() => _RxMin.Observable.of(editor.getCursorBufferPosition())), // Further cursor changes will be debounced. + (0, _textEditor().getCursorPositions)(editor).let((0, _observable().fastDebounce)(CURSOR_DEBOUNCE_TIME))).distinctUntilChanged((a, b) => a.isEqual(b)).switchMap(point => this._getSignatures(editor, point)) // Stop once we get a null result. + .takeWhile(Boolean) // Stop once the escape key is pressed. + // NOTE: we can't use core:cancel because plugins like vim-mode-plus override it. + .takeUntil(_RxMin.Observable.fromEvent(editor.getElement(), 'keydown').filter(evt => evt.keyCode === 27)), // Terminate with a null signature to clear any visible signatures. + _RxMin.Observable.of(null)); + } /** * Retrieve a signature from all providers for an editor + point. * (Effectively just a cancellable promise). */ - _getSignatures( - editor: atom$TextEditor, - point: atom$Point, - ): Observable { + + + _getSignatures(editor, point) { // Take the highest-priority non-empty result. - return Observable.defer(() => - Observable.from(this._providerRegistry.getAllProvidersForEditor(editor)), - ) - .concatMap(provider => { - return provider.getSignatureHelp(editor, point).catch(err => { - const editorPath = editor.getPath() || ''; - getLogger('atom-ide-signature-help').error( - `Caught error from signature help provider for ${editorPath}`, - err, - ); - return null; - }); - }) - .filter(x => x != null && x.signatures.length > 0) - .timeoutWith(SIGNATURE_TIMEOUT, Observable.of(null)) - .take(1) - .defaultIfEmpty(null); + return _RxMin.Observable.defer(() => _RxMin.Observable.from(this._providerRegistry.getAllProvidersForEditor(editor))).concatMap(provider => { + return provider.getSignatureHelp(editor, point).catch(err => { + const editorPath = editor.getPath() || ''; + (0, _log4js().getLogger)('atom-ide-signature-help').error(`Caught error from signature help provider for ${editorPath}`, err); + return null; + }); + }).filter(x => x != null && x.signatures.length > 0).timeoutWith(SIGNATURE_TIMEOUT, _RxMin.Observable.of(null)).take(1).defaultIfEmpty(null); } - /** * Displays the signature datatip and wraps its lifetime in an Observable. */ - _showSignatureDatatip( - editor: atom$TextEditor, - signatureHelp: SignatureHelp, - ): Observable { - return Observable.defer(() => { + + + _showSignatureDatatip(editor, signatureHelp) { + return _RxMin.Observable.defer(() => { const datatipService = this._datatipService; + if (datatipService == null || signatureHelp.signatures.length === 0) { - return Observable.empty(); + return _RxMin.Observable.empty(); } - const currentPosition = editor.getCursorBufferPosition(); - // Make sure the datatip follows the cursor position. - return ( - getCursorPositions(editor) - // But don't go too far. - .takeWhile( - position => - Math.abs(currentPosition.row - position.row) + - Math.abs(currentPosition.column - position.column) <= - 2, - ) - .let( - completingSwitchMap(point => { - return Observable.create(() => { - const disposable = datatipService.createPinnedDataTip( - getSignatureDatatip(signatureHelp, point), - editor, - { - position: 'above-range', - showRangeHighlight: false, - }, - ); - return new UniversalDisposable(disposable); - }); - }), - ) - ); + const currentPosition = editor.getCursorBufferPosition(); // Make sure the datatip follows the cursor position. + + return (0, _textEditor().getCursorPositions)(editor) // But don't go too far. + .takeWhile(position => Math.abs(currentPosition.row - position.row) + Math.abs(currentPosition.column - position.column) <= 2).let((0, _observable().completingSwitchMap)(point => { + return _RxMin.Observable.create(() => { + const disposable = datatipService.createPinnedDataTip((0, _getSignatureDatatip().default)(signatureHelp, point), editor, { + position: 'above-range', + showRangeHighlight: false + }); + return new (_UniversalDisposable().default)(disposable); + }); + })); }); } + } + +exports.default = SignatureHelpManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/getSignatureDatatip.js b/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/getSignatureDatatip.js index 664c110e..aa3db4f2 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/getSignatureDatatip.js +++ b/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/getSignatureDatatip.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = getSignatureDatatip; + +var _atom = require("atom"); + +function _string() { + const data = require("../../../../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,80 +25,56 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {Datatip} from '../../atom-ide-datatip/lib/types'; -import type {SignatureHelp, SignatureParameter} from './types'; - -import {Range} from 'atom'; -import {escapeMarkdown} from 'nuclide-commons/string'; - /** * WIP: This is just what VSCode displays. We can likely make this more Atom-y. */ -export default function getSignatureDatatip( - signatureHelp: SignatureHelp, - point: atom$Point, -): Datatip { +function getSignatureDatatip(signatureHelp, point) { // Note: empty signatures have already been filtered out above. - const activeSignature = - signatureHelp.signatures[signatureHelp.activeSignature || 0]; - const markedStrings = [ - { - type: 'markdown', - value: escapeMarkdown(activeSignature.label), - }, - ]; + const activeSignature = signatureHelp.signatures[signatureHelp.activeSignature || 0]; + const markedStrings = [{ + type: 'markdown', + value: (0, _string().escapeMarkdown)(activeSignature.label) + }]; + if (activeSignature.parameters != null) { const activeParameterIndex = signatureHelp.activeParameter || 0; const activeParameter = activeSignature.parameters[activeParameterIndex]; + if (activeParameter != null) { - if ( - activeParameter.documentation != null && - activeParameter.documentation !== '' - ) { + if (activeParameter.documentation != null && activeParameter.documentation !== '') { markedStrings.push({ type: 'markdown', - value: activeParameter.documentation, + value: activeParameter.documentation }); - } - // Find the label inside the signature label, and bolden it. + } // Find the label inside the signature label, and bolden it. + + if (activeParameter.label !== '') { - const idx = findIndex( - activeSignature.label, - activeSignature.parameters, - activeParameterIndex, - ); + const idx = findIndex(activeSignature.label, activeSignature.parameters, activeParameterIndex); + if (idx !== -1) { - markedStrings[0].value = - escapeMarkdown(activeSignature.label.substr(0, idx)) + - '**' + - escapeMarkdown(activeParameter.label) + - '**' + - escapeMarkdown( - activeSignature.label.substr(idx + activeParameter.label.length), - ); + markedStrings[0].value = (0, _string().escapeMarkdown)(activeSignature.label.substr(0, idx)) + '**' + (0, _string().escapeMarkdown)(activeParameter.label) + '**' + (0, _string().escapeMarkdown)(activeSignature.label.substr(idx + activeParameter.label.length)); } } } } - if ( - activeSignature.documentation != null && - activeSignature.documentation !== '' - ) { + + if (activeSignature.documentation != null && activeSignature.documentation !== '') { markedStrings.push({ type: 'markdown', - value: activeSignature.documentation, + value: activeSignature.documentation }); } + return { markedStrings, - range: new Range(point, point), + range: new _atom.Range(point, point) }; } - /** * Find the index in the label corresponding to the active parameter's label. * This isn't as straightforward as it seems, because parameters could have names @@ -89,25 +84,29 @@ export default function getSignatureDatatip( * * @returns -1 on failure. */ -function findIndex( - label: string, - parameters: Array, - activeParameterIndex: number, -): number { + + +function findIndex(label, parameters, activeParameterIndex) { let lastIndex = undefined; + for (let i = parameters.length - 1; i >= activeParameterIndex; i--) { if (lastIndex != null) { // Parameter labels need to be disjoint, so leave some room. lastIndex -= parameters[i].label.length; + if (lastIndex < 0) { return -1; } } + const nextIndex = label.lastIndexOf(parameters[i].label, lastIndex); + if (nextIndex === -1) { return -1; } + lastIndex = nextIndex; } + return lastIndex != null ? lastIndex : -1; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/main.js index d86a8973..50e2ef14 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/main.js @@ -1,3 +1,37 @@ +"use strict"; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _SignatureHelpManager() { + const data = _interopRequireDefault(require("./SignatureHelpManager")); + + _SignatureHelpManager = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,21 +40,14 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DatatipService} from '../../atom-ide-datatip/lib/types'; -import type {SignatureHelpRegistry} from './types'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import SignatureHelpManager from './SignatureHelpManager'; - class Activation { - // Lazily initialize SignatureHelpManager once we actually get a provider. - _manager: ?SignatureHelpManager = null; - _datatipService: ?DatatipService = null; + constructor() { + this._manager = null; + this._datatipService = null; + } dispose() { if (this._manager != null) { @@ -28,36 +55,44 @@ class Activation { } } - consumeDatatip(datatipService: DatatipService): IDisposable { + consumeDatatip(datatipService) { this._datatipService = datatipService; + if (this._manager != null) { this._manager.setDatatipService(datatipService); } - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { this._datatipService = null; + if (this._manager != null) { this._manager.setDatatipService(null); } }); } - provideSignatureHelp(): SignatureHelpRegistry { + provideSignatureHelp() { return provider => { const manager = this._getSignatureHelpManager(); + return manager.consumeSignatureHelp(provider); }; } - _getSignatureHelpManager(): SignatureHelpManager { + _getSignatureHelpManager() { if (this._manager != null) { return this._manager; } - this._manager = new SignatureHelpManager(); + + this._manager = new (_SignatureHelpManager().default)(); + if (this._datatipService != null) { this._manager.setDatatipService(this._datatipService); } + return this._manager; } + } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/types.js index af6d9710..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-signature-help/lib/types.js @@ -1,59 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict - * @format - */ - -export type SignatureHelpRegistry = ( - provider: SignatureHelpProvider, -) => IDisposable; - -/** - * Signature help is activated when: - * - upon keystroke, any provider with a matching grammar scope contains - * the pressed key inside its triggerCharacters set - * - the signature-help:show command is manually activated - * - * Once signature help has been triggered, the provider will be queried immediately - * with the current cursor position, and then repeatedly upon cursor movements - * until a null/empty signature is returned. - * - * Returned signatures will be displayed in a small datatip at the current cursor. - * The highest-priority provider with a non-null result will be used. - */ -export type SignatureHelpProvider = { - priority: number, - grammarScopes: Array, - - // A set of characters that will trigger signature help when typed. - // If a null/empty set is provided, only manual activation of the command works. - triggerCharacters?: Set, - - getSignatureHelp( - editor: atom$TextEditor, - point: atom$Point, - ): Promise, -}; - -export type SignatureHelp = { - signatures: Array, - activeSignature?: ?number, - activeParameter?: ?number, -}; - -export type Signature = { - label: string, - documentation?: ?string, - parameters?: ?Array, -}; - -export type SignatureParameter = { - label: string, - documentation?: ?string, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/__atom_tests__/FocusManager-test.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/__atom_tests__/FocusManager-test.js index fba8b43b..10bfbe4c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/__atom_tests__/FocusManager-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/__atom_tests__/FocusManager-test.js @@ -1,3 +1,17 @@ +"use strict"; + +var _atom = require("atom"); + +function _FocusManager() { + const data = require("../lib/FocusManager"); + + _FocusManager = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,101 +20,90 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {Emitter} from 'atom'; -import {LastItemManager} from '../lib/FocusManager'; - describe('LastItemManager', () => { it('ensure subscriptions are disposed, as appropriate', () => { const a = {}; const b = {}; const c = {}; const pane = new FakePane([a, b, c]); - jest - .spyOn(atom.workspace, 'paneForItem') - .mockImplementation(item => (pane.hasItem(item) ? pane : null)); + jest.spyOn(atom.workspace, 'paneForItem').mockImplementation(item => pane.hasItem(item) ? pane : null); // Verify initial state. - // Verify initial state. - const manager = new LastItemManager(); - expect(manager.item).toBe(null); + const manager = new (_FocusManager().LastItemManager)(); + expect(manager.item).toBe(null); // Verify initial active item. - // Verify initial active item. manager.onActiveItem(a); expect(manager.item).toBe(a); const aDisposable = pane.lastDisposable; - expect(aDisposable.disposed).toBe(false); + expect(aDisposable.disposed).toBe(false); // Activating a new item should cancel the old item's subscription. - // Activating a new item should cancel the old item's subscription. manager.onActiveItem(b); expect(manager.item).toBe(b); const bDisposable = pane.lastDisposable; expect(aDisposable.disposed).toBe(true); - expect(bDisposable.disposed).toBe(false); - - // If the active item is destroyed, then its subscription should be + expect(bDisposable.disposed).toBe(false); // If the active item is destroyed, then its subscription should be // canceled. + pane.removeItem(b); expect(manager.item).toBe(null); - expect(bDisposable.disposed).toBe(true); - - // Activating c has no effect on b's subscription because it was already + expect(bDisposable.disposed).toBe(true); // Activating c has no effect on b's subscription because it was already // canceled. + manager.onActiveItem(c); expect(manager.item).toBe(c); const cDisposable = pane.lastDisposable; - expect(cDisposable.disposed).toBe(false); - - // In the odd event the item does not belong to a pane, LastItemManager + expect(cDisposable.disposed).toBe(false); // In the odd event the item does not belong to a pane, LastItemManager // should not change its state. + const itemNotInAnyPane = {}; manager.onActiveItem(itemNotInAnyPane); expect(cDisposable.disposed).toBe(false); - expect(pane.lastDisposable).toBe(cDisposable); - - // Activating the existing item should not cause LastItemManager to change + expect(pane.lastDisposable).toBe(cDisposable); // Activating the existing item should not cause LastItemManager to change // its state. + manager.onActiveItem(c); expect(cDisposable.disposed).toBe(false); expect(pane.lastDisposable).toBe(cDisposable); }); }); - const WILL_REMOVE_ITEM = 'will-remove-item'; - /** Fake atom$Pane for testing. */ -class FakePane { - _items: Array = []; - _emitter: Emitter = new Emitter(); +class FakePane { /** Disposable returned by the last call to onWillRemoveItem(). */ - lastDisposable: atom$Disposable; + constructor(items) { + this._items = []; + this._emitter = new _atom.Emitter(); - constructor(items: Array) { this._items.push(...items); } - hasItem(item: Object): boolean { + hasItem(item) { return this._items.indexOf(item) >= 0; } - removeItem(item: Object) { + removeItem(item) { const index = this._items.indexOf(item); + if (index < 0) { return; } - this._emitter.emit(WILL_REMOVE_ITEM, {item, index}); + this._emitter.emit(WILL_REMOVE_ITEM, { + item, + index + }); + this._items.splice(index, 1); } - onWillRemoveItem( - callback: (event: {item: Object, index: number}) => void, - ): IDisposable { + onWillRemoveItem(callback) { const disposable = this._emitter.on(WILL_REMOVE_ITEM, callback); - this.lastDisposable = ((disposable: any): atom$Disposable); + + this.lastDisposable = disposable; return disposable; } -} + +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/__atom_tests__/initial-input-test.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/__atom_tests__/initial-input-test.js index 65b2e7e8..ae167891 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/__atom_tests__/initial-input-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/__atom_tests__/initial-input-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _terminalView() { + const data = require("../lib/terminal-view"); + + _terminalView = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,43 +18,21 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {getSafeInitialInput} from '../lib/terminal-view'; - describe('initial input sanitizer', () => { it('accepts inputs with valid characters', () => { - const goodInput = - 'echo hello my dear world i am a good string!! "\'yolo\'"'; - expect(getSafeInitialInput(goodInput)).toBe(goodInput); + const goodInput = 'echo hello my dear world i am a good string!! "\'yolo\'"'; + expect((0, _terminalView().getSafeInitialInput)(goodInput)).toBe(goodInput); }); - it('rejects inputs with potentially malicious characters', () => { - expect( - getSafeInitialInput( - 'echo goodbye world mwahaha' + String.fromCharCode(10), - ), - ).toBe(''); - expect( - getSafeInitialInput( - 'echo goodbye world mwahaha' + String.fromCharCode(13), - ), - ).toBe(''); - expect( - getSafeInitialInput( - 'echo goodbye world mwahaha' + String.fromCharCode(127), - ), - ).toBe(''); - expect( - getSafeInitialInput( - 'echo goodbye world mwahaha' + String.fromCharCode(180), - ), - ).toBe(''); + expect((0, _terminalView().getSafeInitialInput)('echo goodbye world mwahaha' + String.fromCharCode(10))).toBe(''); + expect((0, _terminalView().getSafeInitialInput)('echo goodbye world mwahaha' + String.fromCharCode(13))).toBe(''); + expect((0, _terminalView().getSafeInitialInput)('echo goodbye world mwahaha' + String.fromCharCode(127))).toBe(''); + expect((0, _terminalView().getSafeInitialInput)('echo goodbye world mwahaha' + String.fromCharCode(180))).toBe(''); }); - it('works for empty strings', () => { - expect(getSafeInitialInput('')).toBe(''); + expect((0, _terminalView().getSafeInitialInput)('')).toBe(''); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/PtyService-test.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/PtyService-test.js index fcb1b11c..d3ce32d7 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/PtyService-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/PtyService-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _PtyService() { + const data = require("../lib/pty-service/PtyService"); + + _PtyService = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,36 +18,31 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {PtyClient} from '../lib/pty-service/rpc-types'; -import invariant from 'assert'; - -import {spawn} from '../lib/pty-service/PtyService'; - describe('PtyService', () => { describe('spawn', () => { let ptyInfo; let runner; - beforeEach(() => { ptyInfo = { terminalType: 'xterm', command: { file: '', - args: [], - }, + args: [] + } }; runner = new LocalRunner(); }); - it('adds numbers in bash', async () => { - invariant(ptyInfo.command != null); + if (!(ptyInfo.command != null)) { + throw new Error("Invariant violation: \"ptyInfo.command != null\""); + } + ptyInfo.command.file = '/bin/bash'; ptyInfo.command.args = ['--norc', '-c', 'echo $((1 + 1))']; - await spawn(ptyInfo, runner); + await (0, _PtyService().spawn)(ptyInfo, runner); const result = await runner.promise; expect(result.output.trim()).toBe('2'); expect(result.code).toBe(0); @@ -43,17 +50,7 @@ describe('PtyService', () => { }); }); -type PtyResult = { - output: string, - code: number, - signal: number, -}; - -class LocalRunner implements PtyClient { - promise: Promise; - _output: string; - _resolve: (result: PtyResult) => void; - +class LocalRunner { constructor() { this.promise = new Promise((resolve, reject) => { this._resolve = resolve; @@ -61,13 +58,18 @@ class LocalRunner implements PtyClient { this._output = ''; } - onOutput(data: string): void { + onOutput(data) { this._output += data; } - onExit(code: number, signal: number): void { - this._resolve({output: this._output, code, signal}); + onExit(code, signal) { + this._resolve({ + output: this._output, + code, + signal + }); } dispose() {} -} + +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/nuclide-terminal-uri-test.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/nuclide-terminal-uri-test.js index 5b390c9d..9b2cfee0 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/nuclide-terminal-uri-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/nuclide-terminal-uri-test.js @@ -1,3 +1,19 @@ +"use strict"; + +var _url = _interopRequireDefault(require("url")); + +function _nuclideTerminalUri() { + const data = require("../lib/nuclide-terminal-uri"); + + _nuclideTerminalUri = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,37 +22,28 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import url from 'url'; - -import { - infoFromUri, - uriFromInfo, - TERMINAL_DEFAULT_LOCATION, - TERMINAL_DEFAULT_ICON, -} from '../lib/nuclide-terminal-uri'; - const defaultInfo = { remainOnCleanExit: false, - defaultLocation: TERMINAL_DEFAULT_LOCATION, - icon: TERMINAL_DEFAULT_ICON, + defaultLocation: _nuclideTerminalUri().TERMINAL_DEFAULT_LOCATION, + icon: _nuclideTerminalUri().TERMINAL_DEFAULT_ICON }; -function uriFromCwd(cwd: ?string) { - return uriFromInfo(cwd != null ? {cwd} : {}); +function uriFromCwd(cwd) { + return (0, _nuclideTerminalUri().uriFromInfo)(cwd != null ? { + cwd + } : {}); } describe('main', () => { - describe('infoFromUri', () => { - // This is verified via round-tripping below + describe('infoFromUri', () => {// This is verified via round-tripping below }); describe('uriFromCwd', () => { it('creates a default uri', () => { const uri = uriFromCwd(null); - const info = infoFromUri(uri); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).not.toBeDefined(); expect(info.command).not.toBeDefined(); @@ -44,7 +51,7 @@ describe('main', () => { }); it('creates a uri with local cwd', () => { const uri = uriFromCwd('/home/username'); - const info = infoFromUri(uri); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).toEqual('/home/username'); expect(info.command).not.toBeDefined(); @@ -52,7 +59,7 @@ describe('main', () => { }); it('creates a uri with remote cwd', () => { const uri = uriFromCwd('nuclide://home/username'); - const info = infoFromUri(uri); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).toEqual('nuclide://home/username'); expect(info.command).not.toBeDefined(); @@ -61,25 +68,32 @@ describe('main', () => { }); describe('uriFromInfo', () => { it('creates a default uri', () => { - const uri = uriFromInfo(defaultInfo); - const info = infoFromUri(uri); + const uri = (0, _nuclideTerminalUri().uriFromInfo)(defaultInfo); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).not.toBeDefined(); expect(info.command).not.toBeDefined(); expect(info.title).not.toBeDefined(); }); it('creates a uri with remote cwd only', () => { - const uri = uriFromInfo({...defaultInfo, cwd: 'nuclide://home/username'}); - const info = infoFromUri(uri); + const uri = (0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + cwd: 'nuclide://home/username' + })); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).toEqual('nuclide://home/username'); expect(info.command).not.toBeDefined(); expect(info.title).not.toBeDefined(); }); it('creates a uri with command only', () => { - const command = {file: '/usr/bin/env', args: ['cowsay', 'hi']}; - const uri = uriFromInfo({...defaultInfo, command}); - const info = infoFromUri(uri); + const command = { + file: '/usr/bin/env', + args: ['cowsay', 'hi'] + }; + const uri = (0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + command + })); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).not.toBeDefined(); expect(info.command).toEqual(command); @@ -87,8 +101,10 @@ describe('main', () => { }); it('creates a uri with a title only', () => { const title = 'The Brothers Karamazov'; - const uri = uriFromInfo({...defaultInfo, title}); - const info = infoFromUri(uri); + const uri = (0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + title + })); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).not.toBeDefined(); expect(info.command).not.toBeDefined(); @@ -96,10 +112,17 @@ describe('main', () => { }); it('creates a uri with everything defined', () => { const cwd = 'nuclide://home/username'; - const command = {file: '/usr/bin/env', args: ['cowsay', 'hi']}; + const command = { + file: '/usr/bin/env', + args: ['cowsay', 'hi'] + }; const title = 'The Hymn of Acxiom'; - const uri = uriFromInfo({...defaultInfo, cwd, command, title}); - const info = infoFromUri(uri); + const uri = (0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + cwd, + command, + title + })); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).toEqual(cwd); expect(info.command).toEqual(command); @@ -107,73 +130,69 @@ describe('main', () => { }); it('ignores cwd with incorrect trustToken', () => { const cwd = 'nuclide://unexpected/directory'; - const uri = breakTrustToken( - uriFromInfo({ - ...defaultInfo, - cwd, - }), - ); - const info = infoFromUri(uri); + const uri = breakTrustToken((0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + cwd + }))); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.cwd).toEqual(''); }); }); it('ignores command with incorrect trustToken', () => { - const command = {file: '/bin/bash', args: ['-c', 'echo rm -rf /']}; - const uri = breakTrustToken( - uriFromInfo({ - ...defaultInfo, - command, - }), - ); - const info = infoFromUri(uri); + const command = { + file: '/bin/bash', + args: ['-c', 'echo rm -rf /'] + }; + const uri = breakTrustToken((0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + command + }))); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.command).toBeUndefined(); }); it('ignores environment with incorrect trustToken', () => { const environmentVariables = new Map([['PATH', '/unexpected/path']]); - const uri = breakTrustToken( - uriFromInfo({ - ...defaultInfo, - environmentVariables, - }), - ); - const info = infoFromUri(uri); + const uri = breakTrustToken((0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + environmentVariables + }))); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.environmentVariables).toBeUndefined(); }); it('ignores preservedCommands with incorrect trustToken', () => { const preservedCommands = ['unexpected:key-binding']; - const uri = breakTrustToken( - uriFromInfo({ - ...defaultInfo, - preservedCommands, - }), - ); - const info = infoFromUri(uri); + const uri = breakTrustToken((0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + preservedCommands + }))); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.preservedCommands).toEqual([]); }); it('ignores initialInput with incorrect trustToken', () => { const initialInput = 'echo rm -rf /'; - const uri = breakTrustToken( - uriFromInfo({ - ...defaultInfo, - initialInput, - }), - ); - const info = infoFromUri(uri); + const uri = breakTrustToken((0, _nuclideTerminalUri().uriFromInfo)(Object.assign({}, defaultInfo, { + initialInput + }))); + const info = (0, _nuclideTerminalUri().infoFromUri)(uri); expect(info).not.toBeNull(); expect(info.initialInput).toEqual(''); }); - function breakTrustToken(uri: string): string { - const {protocol, host, slashes, query} = url.parse(uri, true); - return url.format({ + function breakTrustToken(uri) { + const { + protocol, + host, + slashes, + query + } = _url.default.parse(uri, true); + + return _url.default.format({ protocol, host, slashes, - query: {...query, ...{trustToken: 'invalid'}}, + query: Object.assign({}, query, { + trustToken: 'invalid' + }) }); } -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/shellConfig-test.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/shellConfig-test.js index b5405611..264acf81 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/shellConfig-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/shellConfig-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _shellConfig() { + const data = require("../lib/pty-service/shellConfig"); + + _shellConfig = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,43 +18,44 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {parseConfig} from '../lib/pty-service/shellConfig'; - describe('shellConfig', () => { describe('parseConfig', () => { it('throws on non-JSON', () => { - const parse = () => parseConfig('#include '); + const parse = () => (0, _shellConfig().parseConfig)('#include '); + expect(parse).toThrow(); }); - it('throws on non-object', () => { - const parse = () => parseConfig('[3]'); + const parse = () => (0, _shellConfig().parseConfig)('[3]'); + expect(parse).toThrow(); }); - it('parses space-separated command string', () => { - const config = parseConfig(JSON.stringify({command: 'a b c'})); + const config = (0, _shellConfig().parseConfig)(JSON.stringify({ + command: 'a b c' + })); expect(config.command).toEqual({ file: 'a', - args: ['b', 'c'], + args: ['b', 'c'] }); }); - it('parses explicit command array', () => { - const config = parseConfig(JSON.stringify({command: ['a', 'b', 'c']})); + const config = (0, _shellConfig().parseConfig)(JSON.stringify({ + command: ['a', 'b', 'c'] + })); expect(config.command).toEqual({ file: 'a', - args: ['b', 'c'], + args: ['b', 'c'] }); }); - it('throws on non-string argument', () => { - const config = JSON.stringify({command: ['a', 3]}); - expect(() => parseConfig(config)).toThrow(); + const config = JSON.stringify({ + command: ['a', 3] + }); + expect(() => (0, _shellConfig().parseConfig)(config)).toThrow(); }); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/sink-test.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/sink-test.js index 977c6fca..fabc73c7 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/sink-test.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/__tests__/sink-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _sink() { + const data = require("../lib/sink"); + + _sink = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,30 +18,23 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import {removePrefixSink, patternCounterSink} from '../lib/sink'; - -import type {Sink} from '../lib/sink'; - describe('sink', () => { describe('removePrefixSink', () => { const prefix = '\x1bprefix'; - let callCount: number; - let output: string; - let sink: Sink; - + let callCount; + let output; + let sink; beforeEach(() => { callCount = 0; output = ''; - sink = removePrefixSink(prefix, data => { + sink = (0, _sink().removePrefixSink)(prefix, data => { output += data; callCount++; }); }); - it('removes prefix when sent a single chunk', () => { sink(prefix + 'suffix'); expect(output).toEqual('suffix'); @@ -39,6 +44,7 @@ describe('sink', () => { sink(prefix.charAt(i)); expect(output).toEqual(''); } + expect(callCount).toEqual(0); sink('_'); expect(output).toEqual('_'); @@ -53,17 +59,17 @@ describe('sink', () => { expect(output).toEqual(prefix); }); }); - describe('patternCounterSink', () => { - let enabled: boolean; - let counter: number; - let output: string; + let enabled; + let counter; + let output; - function notify(): boolean { + function notify() { counter++; return enabled; } - function next(data: string): void { + + function next(data) { output += data; } @@ -72,36 +78,35 @@ describe('sink', () => { counter = 0; output = ''; }); - it('does not match a non-match', () => { - const sink = patternCounterSink('needle', notify, next); + const sink = (0, _sink().patternCounterSink)('needle', notify, next); sink('haystack'); expect(counter).toEqual(0); expect(output).toEqual('haystack'); }); it('matches a unique pattern exactly', () => { - const sink = patternCounterSink('abc', notify, next); + const sink = (0, _sink().patternCounterSink)('abc', notify, next); sink('abc'); expect(counter).toEqual(1); expect(output).toEqual('abc'); }); it('matches multiple occurrences of a pattern', () => { - const sink = patternCounterSink('abc', notify, next); + const sink = (0, _sink().patternCounterSink)('abc', notify, next); sink('abc abc abc'); expect(counter).toEqual(3); }); it('matches overlapping occurrences of a pattern', () => { - const sink = patternCounterSink('aba', notify, next); + const sink = (0, _sink().patternCounterSink)('aba', notify, next); sink('ababababa'); expect(counter).toEqual(4); }); it('matches odd characters', () => { - const sink = patternCounterSink('\x1b\n ', notify, next); + const sink = (0, _sink().patternCounterSink)('\x1b\n ', notify, next); sink(' \x1b\n '); expect(counter).toEqual(1); }); it('matches small chunks', () => { - const sink = patternCounterSink('aaa', notify, next); + const sink = (0, _sink().patternCounterSink)('aaa', notify, next); sink('b'); expect(counter).toEqual(0); sink('a'); @@ -124,7 +129,7 @@ describe('sink', () => { expect(counter).toEqual(4); }); it('Returning enabled=false has no subsequent notifications', () => { - const sink = patternCounterSink('a', notify, next); + const sink = (0, _sink().patternCounterSink)('a', notify, next); sink('a'); expect(counter).toEqual(1); enabled = false; @@ -136,4 +141,4 @@ describe('sink', () => { expect(counter).toEqual(2); }); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/AtomServiceContainer.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/AtomServiceContainer.js index 234323f1..94dce18f 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/AtomServiceContainer.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/AtomServiceContainer.js @@ -1,3 +1,55 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.setRpcService = setRpcService; +exports.getPtyServiceByNuclideUri = getPtyServiceByNuclideUri; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function PtyServiceLocal() { + const data = _interopRequireWildcard(require("./pty-service/PtyService")); + + PtyServiceLocal = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,35 +58,24 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +let _rpcService = null; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import typeof * as PtyService from './pty-service/PtyService'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as PtyServiceLocal from './pty-service/PtyService'; -import nullthrows from 'nullthrows'; - -let _rpcService: ?nuclide$RpcService = null; - -export function setRpcService(rpcService: nuclide$RpcService): IDisposable { +function setRpcService(rpcService) { _rpcService = rpcService; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { _rpcService = null; }); } -export function getPtyServiceByNuclideUri(uri: ?NuclideUri): PtyService { +function getPtyServiceByNuclideUri(uri) { const serviceUri = uri || ''; - if (_rpcService == null && !nuclideUri.isRemote(serviceUri)) { - return PtyServiceLocal; + + if (_rpcService == null && !_nuclideUri().default.isRemote(serviceUri)) { + return PtyServiceLocal(); } - return nullthrows(_rpcService).getServiceByNuclideUri( - 'PtyService', - serviceUri, - ); -} + return (0, _nullthrows().default)(_rpcService).getServiceByNuclideUri('PtyService', serviceUri); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/FocusManager.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/FocusManager.js index cb289ff9..a033e35e 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/FocusManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/FocusManager.js @@ -1,3 +1,40 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.FocusManager = exports.LastItemManager = void 0; + +function _terminalView() { + const data = require("./terminal-view.js"); + + _terminalView = function () { + return data; + }; + + return data; +} + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,25 +43,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {TerminalView} from './terminal-view.js'; -import {getLogger} from 'log4js'; -import {track} from 'nuclide-commons/analytics'; - -const logger = getLogger('terminal-focus-manager'); - -// These could be individual fields of LastItemManager, but by grouping them, +const logger = (0, _log4js().getLogger)('terminal-focus-manager'); // These could be individual fields of LastItemManager, but by grouping them, // we ensure they are always assigned together. -type LastItem = { - item: T, - onWillRemoveSubscription: IDisposable, -}; -export class LastItemManager { - _last: ?LastItem = null; +class LastItemManager { + constructor() { + this._last = null; + } dispose() { if (this._last != null) { @@ -32,65 +60,73 @@ export class LastItemManager { } } - get item(): ?T { + get item() { return this._last != null ? this._last.item : null; } - onActiveItem(item: T) { + onActiveItem(item) { const pane = atom.workspace.paneForItem(item); + if (pane == null) { logger.error(`Suspicious: no pane for item: ${String(item)}`); return; } const last = this._last; + if (last != null) { if (item === last.item) { return; } - last.onWillRemoveSubscription.dispose(); - } - // Subscribe to the destruction of the item. If it is destroyed, then + last.onWillRemoveSubscription.dispose(); + } // Subscribe to the destruction of the item. If it is destroyed, then // it should no longer be considered the last item focused. + + const onWillRemoveSubscription = pane.onWillRemoveItem(event => { if (event.item === item) { onWillRemoveSubscription.dispose(); + if (this._last != null && this._last.item === item) { this._last = null; } } }); - this._last = {item, onWillRemoveSubscription}; + this._last = { + item, + onWillRemoveSubscription + }; } + } -export class FocusManager { - _observeActivePaneItemSubscription: IDisposable; - _lastTerminal: LastItemManager = new LastItemManager(); - _observeActiveTextEditorSubscription: IDisposable; - _lastEditor: LastItemManager = new LastItemManager(); +exports.LastItemManager = LastItemManager; +class FocusManager { constructor() { - this._observeActivePaneItemSubscription = atom.workspace.observeActivePaneItem( - this._onActivePaneItem.bind(this), - ); - this._observeActiveTextEditorSubscription = atom.workspace.observeActiveTextEditor( - this._onActiveTextEditor.bind(this), - ); + this._lastTerminal = new LastItemManager(); + this._lastEditor = new LastItemManager(); + this._observeActivePaneItemSubscription = atom.workspace.observeActivePaneItem(this._onActivePaneItem.bind(this)); + this._observeActiveTextEditorSubscription = atom.workspace.observeActiveTextEditor(this._onActiveTextEditor.bind(this)); } dispose() { this._observeActivePaneItemSubscription.dispose(); + this._lastTerminal.dispose(); + this._observeActiveTextEditorSubscription.dispose(); + this._lastEditor.dispose(); } toggleFocus() { - track('toggle-terminal-focus'); - if (atom.workspace.getActivePaneItem() instanceof TerminalView) { + (0, _analytics().track)('toggle-terminal-focus'); + + if (atom.workspace.getActivePaneItem() instanceof _terminalView().TerminalView) { const editor = this._lastEditor.item; + if (editor != null) { focus(editor); } @@ -99,13 +135,13 @@ export class FocusManager { } } - _onActivePaneItem(item: ?mixed) { - if (item instanceof TerminalView) { + _onActivePaneItem(item) { + if (item instanceof _terminalView().TerminalView) { this._lastTerminal.onActiveItem(item); } } - _onActiveTextEditor(editor: TextEditor) { + _onActiveTextEditor(editor) { // Apparently `editor` can be null on startup. if (editor != null && editor !== this._lastEditor.item) { this._lastEditor.onActiveItem(editor); @@ -114,47 +150,56 @@ export class FocusManager { focusTerminal() { const lastTerminal = this._lastTerminal.item; + if (lastTerminal != null) { focus(lastTerminal); return; } const someTerminal = findTerminal(); + if (someTerminal != null) { focus(someTerminal); return; - } - - // TODO(mbolin): Decide whether to open a local or remote terminal. + } // TODO(mbolin): Decide whether to open a local or remote terminal. // Base it on the path of the active text editor? + + atom.notifications.addInfo('No terminal found.'); } -} +} /** Focus the specified Terminal. */ -function focus(item: Object) { + + +exports.FocusManager = FocusManager; + +function focus(item) { const pane = atom.workspace.paneForItem(item); + if (pane == null) { return; - } + } // First we must activate the item within the pane, then we focus it. + - // First we must activate the item within the pane, then we focus it. pane.activateItem(item); const view = atom.views.getView(item); view.focus(); } - /** * Traverses the panes and pane items and returns the first TerminalView it * finds, if any. */ -function findTerminal(): ?TerminalView { + + +function findTerminal() { for (const pane of atom.workspace.getPanes()) { for (const item of pane.getItems()) { - if (item instanceof TerminalView) { + if (item instanceof _terminalView().TerminalView) { return item; } } } + return null; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/config.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/config.js index a7c9c6e9..1ffa1a69 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/config.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/config.js @@ -1,3 +1,23 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getFontSize = getFontSize; +exports.FONT_SCALE_CONFIG = exports.FONT_FAMILY_CONFIG = exports.RENDERER_TYPE_CONFIG = exports.CHAR_ATLAS_CONFIG = exports.TRANSPARENCY_CONFIG = exports.OPTION_IS_META_CONFIG = exports.ADD_ESCAPE_COMMAND = exports.DOCUMENTATION_MESSAGE_CONFIG = exports.LINE_HEIGHT_CONFIG = exports.CURSOR_BLINK_CONFIG = exports.CURSOR_STYLE_CONFIG = exports.SCROLLBACK_CONFIG = exports.PRESERVED_COMMANDS_CONFIG = exports.COLOR_CONFIGS = void 0; + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,13 +26,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import featureConfig from 'nuclide-commons-atom/feature-config'; - -export const COLOR_CONFIGS = Object.freeze({ +const COLOR_CONFIGS = Object.freeze({ // dark black: 'atom-ide-terminal.black', red: 'atom-ide-terminal.red', @@ -30,27 +47,36 @@ export const COLOR_CONFIGS = Object.freeze({ brightYellow: 'atom-ide-terminal.brightYellow', brightCyan: 'atom-ide-terminal.brightCyan', brightMagenta: 'atom-ide-terminal.brightMagenta', - brightWhite: 'atom-ide-terminal.brightWhite', + brightWhite: 'atom-ide-terminal.brightWhite' }); +exports.COLOR_CONFIGS = COLOR_CONFIGS; +const PRESERVED_COMMANDS_CONFIG = 'atom-ide-terminal.preservedCommands'; +exports.PRESERVED_COMMANDS_CONFIG = PRESERVED_COMMANDS_CONFIG; +const SCROLLBACK_CONFIG = 'atom-ide-terminal.scrollback'; +exports.SCROLLBACK_CONFIG = SCROLLBACK_CONFIG; +const CURSOR_STYLE_CONFIG = 'atom-ide-terminal.cursorStyle'; +exports.CURSOR_STYLE_CONFIG = CURSOR_STYLE_CONFIG; +const CURSOR_BLINK_CONFIG = 'atom-ide-terminal.cursorBlink'; +exports.CURSOR_BLINK_CONFIG = CURSOR_BLINK_CONFIG; +const LINE_HEIGHT_CONFIG = 'atom-ide-terminal.lineHeight'; +exports.LINE_HEIGHT_CONFIG = LINE_HEIGHT_CONFIG; +const DOCUMENTATION_MESSAGE_CONFIG = 'atom-ide-terminal.documentationMessage'; +exports.DOCUMENTATION_MESSAGE_CONFIG = DOCUMENTATION_MESSAGE_CONFIG; +const ADD_ESCAPE_COMMAND = 'atom-ide-terminal:add-escape-prefix'; +exports.ADD_ESCAPE_COMMAND = ADD_ESCAPE_COMMAND; +const OPTION_IS_META_CONFIG = 'atom-ide-terminal.optionIsMeta'; +exports.OPTION_IS_META_CONFIG = OPTION_IS_META_CONFIG; +const TRANSPARENCY_CONFIG = 'atom-ide-terminal.allowTransparency'; +exports.TRANSPARENCY_CONFIG = TRANSPARENCY_CONFIG; +const CHAR_ATLAS_CONFIG = 'atom-ide-terminal.charAtlas'; +exports.CHAR_ATLAS_CONFIG = CHAR_ATLAS_CONFIG; +const RENDERER_TYPE_CONFIG = 'atom-ide-terminal.rendererType'; +exports.RENDERER_TYPE_CONFIG = RENDERER_TYPE_CONFIG; +const FONT_FAMILY_CONFIG = 'atom-ide-terminal.fontFamily'; +exports.FONT_FAMILY_CONFIG = FONT_FAMILY_CONFIG; +const FONT_SCALE_CONFIG = 'atom-ide-terminal.fontScale'; +exports.FONT_SCALE_CONFIG = FONT_SCALE_CONFIG; -export const PRESERVED_COMMANDS_CONFIG = 'atom-ide-terminal.preservedCommands'; -export const SCROLLBACK_CONFIG = 'atom-ide-terminal.scrollback'; -export const CURSOR_STYLE_CONFIG = 'atom-ide-terminal.cursorStyle'; -export const CURSOR_BLINK_CONFIG = 'atom-ide-terminal.cursorBlink'; -export const LINE_HEIGHT_CONFIG = 'atom-ide-terminal.lineHeight'; -export const DOCUMENTATION_MESSAGE_CONFIG = - 'atom-ide-terminal.documentationMessage'; -export const ADD_ESCAPE_COMMAND = 'atom-ide-terminal:add-escape-prefix'; -export const OPTION_IS_META_CONFIG = 'atom-ide-terminal.optionIsMeta'; -export const TRANSPARENCY_CONFIG = 'atom-ide-terminal.allowTransparency'; -export const CHAR_ATLAS_CONFIG = 'atom-ide-terminal.charAtlas'; -export const RENDERER_TYPE_CONFIG = 'atom-ide-terminal.rendererType'; -export const FONT_FAMILY_CONFIG = 'atom-ide-terminal.fontFamily'; -export const FONT_SCALE_CONFIG = 'atom-ide-terminal.fontScale'; - -export function getFontSize(): number { - return ( - parseFloat(featureConfig.get(FONT_SCALE_CONFIG)) * - parseFloat(atom.config.get('editor.fontSize')) - ); -} +function getFontSize() { + return parseFloat(_featureConfig().default.get(FONT_SCALE_CONFIG)) * parseFloat(atom.config.get('editor.fontSize')); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/createTerminal.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/createTerminal.js index cc458214..7a04de8b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/createTerminal.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/createTerminal.js @@ -1,3 +1,74 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.createTerminal = createTerminal; + +function _decoders() { + const data = require("decoders"); + + _decoders = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _xterm() { + const data = require("xterm"); + + _xterm = function () { + return data; + }; + + return data; +} + +function Fit() { + const data = _interopRequireWildcard(require("xterm/lib/addons/fit/fit")); + + Fit = function () { + return data; + }; + + return data; +} + +function WebLinks() { + const data = _interopRequireWildcard(require("xterm/lib/addons/webLinks/webLinks")); + + WebLinks = function () { + return data; + }; + + return data; +} + +function _config() { + const data = require("./config"); + + _config = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,116 +77,64 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const assertTerminalOptionsInFeatureConfig = (0, _decoders().guard)((0, _decoders().object)({ + cursorBlink: _decoders().boolean, + cursorStyle: (0, _decoders().either3)((0, _decoders().constant)('block'), (0, _decoders().constant)('underline'), (0, _decoders().constant)('bar')), + scrollback: _decoders().number, + fontFamily: _decoders().string, + fontSize: _decoders().number, + lineHeight: _decoders().number, + macOptionIsMeta: _decoders().boolean, + allowTransparency: _decoders().boolean, + experimentalCharAtlas: (0, _decoders().either3)((0, _decoders().constant)('none'), (0, _decoders().constant)('static'), (0, _decoders().constant)('dynamic')), + rendererType: (0, _decoders().either)((0, _decoders().constant)('canvas'), (0, _decoders().constant)('dom')) +})); -import type {TerminalOptions} from 'xterm'; - -import { - boolean, - constant, - either, - either3, - object, - number, - string, - guard, -} from 'decoders'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {Terminal as XTerminal} from 'xterm'; -import * as Fit from 'xterm/lib/addons/fit/fit'; -import * as WebLinks from 'xterm/lib/addons/webLinks/webLinks'; -import { - CHAR_ATLAS_CONFIG, - CURSOR_BLINK_CONFIG, - CURSOR_STYLE_CONFIG, - FONT_FAMILY_CONFIG, - LINE_HEIGHT_CONFIG, - OPTION_IS_META_CONFIG, - SCROLLBACK_CONFIG, - TRANSPARENCY_CONFIG, - getFontSize, - RENDERER_TYPE_CONFIG, -} from './config'; - -export type Terminal = TerminalClass; -declare class TerminalClass extends XTerminal { - proposeGeometry: () => {rows: number, cols: number}; - fit: () => void; - webLinksInit: (handler?: (event: Event, link: string) => void) => void; - - // TODO: Update xterm types? - linkifier: any; - buffer: any; - selectionManager: any; - dispose: () => void; -} - -const assertTerminalOptionsInFeatureConfig = guard( - object({ - cursorBlink: boolean, - cursorStyle: either3( - constant('block'), - constant('underline'), - constant('bar'), - ), - scrollback: number, - fontFamily: string, - fontSize: number, - lineHeight: number, - macOptionIsMeta: boolean, - allowTransparency: boolean, - experimentalCharAtlas: either3( - constant('none'), - constant('static'), - constant('dynamic'), - ), - rendererType: either(constant('canvas'), constant('dom')), - }), -); - -export function createTerminal(options: TerminalOptions = {}): Terminal { +function createTerminal(options = {}) { // Load the addons on-demand the first time we create a terminal. // $FlowIgnore - if (XTerminal.fit == null) { + if (_xterm().Terminal.fit == null) { // The 'fit' add-on resizes the terminal based on the container size // and the font size such that the terminal fills the container. - XTerminal.applyAddon(Fit); - } - // $FlowIgnore - if (XTerminal.webLinksInit == null) { + _xterm().Terminal.applyAddon(Fit()); + } // $FlowIgnore + + + if (_xterm().Terminal.webLinksInit == null) { // The 'webLinks' add-on linkifies http URL strings. - XTerminal.applyAddon(WebLinks); + _xterm().Terminal.applyAddon(WebLinks()); } - const rendererType = featureConfig.get(RENDERER_TYPE_CONFIG); - // $FlowIgnore We know that TerminalClass is XTerminal + addons - const terminal = new XTerminal( - // $FlowIssue: xterms type needs to be updated to include experimentalCharAtlas - assertTerminalOptionsInFeatureConfig({ - cursorBlink: featureConfig.get(CURSOR_BLINK_CONFIG), - cursorStyle: featureConfig.get(CURSOR_STYLE_CONFIG), - scrollback: featureConfig.get(SCROLLBACK_CONFIG), - fontFamily: featureConfig.get(FONT_FAMILY_CONFIG), - fontSize: getFontSize(), - lineHeight: featureConfig.get(LINE_HEIGHT_CONFIG), - macOptionIsMeta: featureConfig.get(OPTION_IS_META_CONFIG), - allowTransparency: featureConfig.get(TRANSPARENCY_CONFIG), - experimentalCharAtlas: featureConfig.get(CHAR_ATLAS_CONFIG), - rendererType: rendererType === 'auto' ? 'canvas' : rendererType, - ...options, - }), - ); - // Patch into xterm Linkifier to catch errors on isWrapped property. + + const rendererType = _featureConfig().default.get(_config().RENDERER_TYPE_CONFIG); // $FlowIgnore We know that TerminalClass is XTerminal + addons + + + const terminal = new (_xterm().Terminal)( // $FlowIssue: xterms type needs to be updated to include experimentalCharAtlas + assertTerminalOptionsInFeatureConfig(Object.assign({ + cursorBlink: _featureConfig().default.get(_config().CURSOR_BLINK_CONFIG), + cursorStyle: _featureConfig().default.get(_config().CURSOR_STYLE_CONFIG), + scrollback: _featureConfig().default.get(_config().SCROLLBACK_CONFIG), + fontFamily: _featureConfig().default.get(_config().FONT_FAMILY_CONFIG), + fontSize: (0, _config().getFontSize)(), + lineHeight: _featureConfig().default.get(_config().LINE_HEIGHT_CONFIG), + macOptionIsMeta: _featureConfig().default.get(_config().OPTION_IS_META_CONFIG), + allowTransparency: _featureConfig().default.get(_config().TRANSPARENCY_CONFIG), + experimentalCharAtlas: _featureConfig().default.get(_config().CHAR_ATLAS_CONFIG), + rendererType: rendererType === 'auto' ? 'canvas' : rendererType + }, options))); // Patch into xterm Linkifier to catch errors on isWrapped property. // Track issue at https://github.com/xtermjs/xterm.js/issues/1509 + const linkifier = terminal._core.linkifier; const linkifyRow = linkifier._linkifyRow; + linkifier._linkifyRow = row => { try { linkifyRow.call(linkifier, row); - } catch (e) { - // swallow errors to avoid red box because the linkifier runs on a timer. + } catch (e) {// swallow errors to avoid red box because the linkifier runs on a timer. } }; + return terminal; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/main.js index 568191ca..7262d779 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/main.js @@ -1,3 +1,119 @@ +"use strict"; + +function _destroyItemWhere() { + const data = require("../../../../nuclide-commons-atom/destroyItemWhere"); + + _destroyItemWhere = function () { + return data; + }; + + return data; +} + +var _os = _interopRequireDefault(require("os")); + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _getElementFilePath() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/getElementFilePath")); + + _getElementFilePath = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _AtomServiceContainer() { + const data = require("./AtomServiceContainer"); + + _AtomServiceContainer = function () { + return data; + }; + + return data; +} + +function _terminalView() { + const data = require("./terminal-view"); + + _terminalView = function () { + return data; + }; + + return data; +} + +function _nuclideTerminalUri() { + const data = require("./nuclide-terminal-uri"); + + _nuclideTerminalUri = function () { + return data; + }; + + return data; +} + +function _FocusManager() { + const data = require("./FocusManager"); + + _FocusManager = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,88 +122,55 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {destroyItemWhere} from 'nuclide-commons-atom/destroyItemWhere'; // for homedir -import os from 'os'; -import nullthrows from 'nullthrows'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import getElementFilePath from 'nuclide-commons-atom/getElementFilePath'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -import {setRpcService} from './AtomServiceContainer'; -import {deserializeTerminalView, TerminalView} from './terminal-view'; -import {infoFromUri, uriFromInfo, URI_PREFIX} from './nuclide-terminal-uri'; -import {FocusManager} from './FocusManager'; - -import type {CreatePasteFunction} from 'atom-ide-ui/pkg/atom-ide-console/lib/types'; -import type {TerminalApi, TerminalInfo, TerminalInstance} from './types'; - class Activation { - _subscriptions: UniversalDisposable; - _cwd: ?nuclide$CwdApi; - constructor() { - const focusManager = new FocusManager(); - this._subscriptions = new UniversalDisposable( - focusManager, - atom.workspace.addOpener(uri => { - if (uri.startsWith(URI_PREFIX)) { - return new TerminalView(uri); - } - }), - atom.commands.add( - 'atom-workspace', - 'atom-ide-terminal:new-terminal', - event => { - const cwd = this._getPathOrCwd(event); - const uri = cwd != null ? uriFromInfo({cwd}) : uriFromInfo({}); - goToLocation(uri); - }, - ), - atom.commands.add( - 'atom-workspace', - 'atom-ide-terminal:new-local-terminal', - event => { - const uri = uriFromInfo({cwd: os.homedir()}); - goToLocation(uri); - }, - ), - atom.commands.add( - 'atom-workspace', - 'atom-ide-terminal:toggle-terminal-focus', - () => focusManager.toggleFocus(), - ), - ); + const focusManager = new (_FocusManager().FocusManager)(); + this._subscriptions = new (_UniversalDisposable().default)(focusManager, atom.workspace.addOpener(uri => { + if (uri.startsWith(_nuclideTerminalUri().URI_PREFIX)) { + return new (_terminalView().TerminalView)(uri); + } + }), atom.commands.add('atom-workspace', 'atom-ide-terminal:new-terminal', event => { + const cwd = this._getPathOrCwd(event); + + const uri = cwd != null ? (0, _nuclideTerminalUri().uriFromInfo)({ + cwd + }) : (0, _nuclideTerminalUri().uriFromInfo)({}); + (0, _goToLocation().goToLocation)(uri); + }), atom.commands.add('atom-workspace', 'atom-ide-terminal:new-local-terminal', event => { + const uri = (0, _nuclideTerminalUri().uriFromInfo)({ + cwd: _os.default.homedir() + }); + (0, _goToLocation().goToLocation)(uri); + }), atom.commands.add('atom-workspace', 'atom-ide-terminal:toggle-terminal-focus', () => focusManager.toggleFocus())); } - provideTerminal(): TerminalApi { + provideTerminal() { return { - open: (info: TerminalInfo): Promise => { - const terminalView: any = goToLocation(uriFromInfo(info)); + open: info => { + const terminalView = (0, _goToLocation().goToLocation)((0, _nuclideTerminalUri().uriFromInfo)(info)); return terminalView; }, - close: (key: string) => { - destroyItemWhere(item => { + close: key => { + (0, _destroyItemWhere().destroyItemWhere)(item => { if (item.getURI == null || item.getURI() == null) { return false; } - const uri = nullthrows(item.getURI()); + const uri = (0, _nullthrows().default)(item.getURI()); + try { // Only close terminal tabs with the same unique key. - const otherInfo = infoFromUri(uri); + const otherInfo = (0, _nuclideTerminalUri().infoFromUri)(uri); return otherInfo.key === key; } catch (e) {} + return false; }); - }, + } }; } @@ -95,91 +178,86 @@ class Activation { this._subscriptions.dispose(); } - consumeToolBar(getToolBar: toolbar$GetToolbar): IDisposable { + consumeToolBar(getToolBar) { const toolBar = getToolBar('nuclide-terminal'); toolBar.addButton({ icon: 'terminal', callback: 'atom-ide-terminal:new-terminal', tooltip: 'New Terminal', - priority: 700, + priority: 700 }); - - const disposable = new UniversalDisposable(() => { + const disposable = new (_UniversalDisposable().default)(() => { toolBar.removeItems(); }); + this._subscriptions.add(disposable); + return disposable; } - consumePasteProvider(provider: any): IDisposable { - const createPaste: CreatePasteFunction = provider.createPaste; - const disposable = new UniversalDisposable( - atom.commands.add( - '.terminal-pane', - 'atom-ide-terminal:create-paste', - async event => { + consumePasteProvider(provider) { + const createPaste = provider.createPaste; + const disposable = new (_UniversalDisposable().default)(atom.commands.add('.terminal-pane', 'atom-ide-terminal:create-paste', async event => { + const { + currentTarget: { + terminal + } + } = event; + const uri = await createPaste(terminal.getSelection(), { + title: 'Paste from Atom IDE Terminal' + }, 'terminal paste'); + atom.notifications.addSuccess(`Created paste at ${uri}`); + }), atom.contextMenu.add({ + '.terminal-pane': [{ + label: 'Create Paste', + command: 'atom-ide-terminal:create-paste', + shouldDisplay: event => { + const div = event.target.closest('.terminal-pane'); + + if (div == null) { + return false; + } + const { - currentTarget: {terminal}, - } = (event: any); - const uri = await createPaste( - terminal.getSelection(), - { - title: 'Paste from Atom IDE Terminal', - }, - 'terminal paste', - ); - atom.notifications.addSuccess(`Created paste at ${uri}`); - }, - ), - atom.contextMenu.add({ - '.terminal-pane': [ - { - label: 'Create Paste', - command: 'atom-ide-terminal:create-paste', - shouldDisplay: event => { - const div = event.target.closest('.terminal-pane'); - if (div == null) { - return false; - } - const {terminal} = (div: any); - if (terminal == null) { - return false; - } - return terminal.hasSelection(); - }, - }, - {type: 'separator'}, - ], - }), - ); + terminal + } = div; + + if (terminal == null) { + return false; + } + + return terminal.hasSelection(); + } + }, { + type: 'separator' + }] + })); + this._subscriptions.add(disposable); - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { disposable.dispose(); + this._subscriptions.remove(disposable); }); } - initializeCwdApi(cwd: nuclide$CwdApi): IDisposable { + initializeCwdApi(cwd) { this._cwd = cwd; - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { this._cwd = null; }); } - consumeRpcService(rpcService: nuclide$RpcService): IDisposable { - return setRpcService(rpcService); + consumeRpcService(rpcService) { + return (0, _AtomServiceContainer().setRpcService)(rpcService); } - _getPathOrCwd(event: Event): ?string { - const editorPath = getElementFilePath( - ((event.target: any): HTMLElement), - true, - ); + _getPathOrCwd(event) { + const editorPath = (0, _getElementFilePath().default)(event.target, true); if (editorPath != null) { - return nuclideUri.endsWithSeparator(editorPath) - ? editorPath - : nuclideUri.dirname(editorPath); + return _nuclideUri().default.endsWithSeparator(editorPath) ? editorPath : _nuclideUri().default.dirname(editorPath); } if (this._cwd != null) { @@ -188,12 +266,12 @@ class Activation { return null; } -} -// eslint-disable-next-line nuclide-internal/no-commonjs +} // eslint-disable-next-line nuclide-internal/no-commonjs + + module.exports = { // exported for package.json entry - deserializeTerminalView, + deserializeTerminalView: _terminalView().deserializeTerminalView }; - -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/measure-performance.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/measure-performance.js index 905e41ef..617f82ca 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/measure-performance.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/measure-performance.js @@ -1,3 +1,72 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = measurePerformance; + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _performanceNow() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/performanceNow")); + + _performanceNow = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _xterm() { + const data = require("xterm"); + + _xterm = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _config() { + const data = require("./config"); + + _config = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,96 +75,86 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import featureConfig from 'nuclide-commons-atom/feature-config'; -import performanceNow from 'nuclide-commons/performanceNow'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Terminal as XTerminal} from 'xterm'; -import {track} from 'nuclide-commons/analytics'; -import {RENDERER_TYPE_CONFIG} from './config'; - // Tune the parameters for performance measurement. const SLOW_CANVAS_RENDER_THRESHOLD = 66; // in ms -const NUMBER_OF_FRAMES_TO_MEASURE = 20; +const NUMBER_OF_FRAMES_TO_MEASURE = 20; /** * Track the performance of the canvas terminal renderer and offer switching to * the DOM-based fallback if we detect slow rendering. */ -export default function measurePerformance(terminal: XTerminal): IDisposable { + +function measurePerformance(terminal) { // If the terminal isn't using the canvas renderer, do nothing. if (terminal.getOption('rendererType') !== 'canvas') { - return new UniversalDisposable(); - } - // Similar to https://github.com/Microsoft/vscode/commit/84eb4778f18215d00608ccf8fb7649e6f2cd428a + return new (_UniversalDisposable().default)(); + } // Similar to https://github.com/Microsoft/vscode/commit/84eb4778f18215d00608ccf8fb7649e6f2cd428a + + let frameTimes = []; - let evaluated = false; - // $FlowIgnore: using unofficial _core interface defined in https://github.com/Microsoft/vscode/blob/master/src/typings/vscode-xterm.d.ts#L682-L706 + let evaluated = false; // $FlowIgnore: using unofficial _core interface defined in https://github.com/Microsoft/vscode/blob/master/src/typings/vscode-xterm.d.ts#L682-L706 + const textRenderLayer = terminal._core.renderer._renderLayers[0]; const originalOnGridChanged = textRenderLayer.onGridChanged; const evaluateCanvasRenderer = () => { - evaluated = true; - // Discard first frame time as it's normal to take longer - frameTimes.shift(); + evaluated = true; // Discard first frame time as it's normal to take longer + frameTimes.shift(); const averageTime = frameTimes.reduce((p, c) => p + c) / frameTimes.length; - track('nuclide-terminal.render-performance', {averageTime}); + (0, _analytics().track)('nuclide-terminal.render-performance', { + averageTime + }); + if (averageTime > SLOW_CANVAS_RENDER_THRESHOLD) { - const notification = atom.notifications.addWarning( - `The terminal GPU-based rendering appears to be slow on your computer (average frame render time was ${averageTime.toFixed( - 2, - )}ms), do you want to use the fallback non-GPU renderer?`, - { - dismissable: true, - buttons: [ - { - text: 'Yes', - onDidClick: () => { - notification.dismiss(); - featureConfig.set(RENDERER_TYPE_CONFIG, 'dom'); - atom.notifications.addSuccess( - 'All new terminals launched will use the non-GPU renderer', - ); - }, - }, - {text: 'No', onDidClick: () => notification.dismiss()}, - { - text: "No, don't show again", - onDidClick: () => { - featureConfig.set(RENDERER_TYPE_CONFIG, 'canvas'); - notification.dismiss(); - }, - }, - ], - }, - ); + const notification = atom.notifications.addWarning(`The terminal GPU-based rendering appears to be slow on your computer (average frame render time was ${averageTime.toFixed(2)}ms), do you want to use the fallback non-GPU renderer?`, { + dismissable: true, + buttons: [{ + text: 'Yes', + onDidClick: () => { + notification.dismiss(); + + _featureConfig().default.set(_config().RENDERER_TYPE_CONFIG, 'dom'); + + atom.notifications.addSuccess('All new terminals launched will use the non-GPU renderer'); + } + }, { + text: 'No', + onDidClick: () => notification.dismiss() + }, { + text: "No, don't show again", + onDidClick: () => { + _featureConfig().default.set(_config().RENDERER_TYPE_CONFIG, 'canvas'); + + notification.dismiss(); + } + }] + }); } }; - textRenderLayer.onGridChanged = ( - term: XTerminal, - firstRow: number, - lastRow: number, - ) => { - const startTime = performanceNow(); + textRenderLayer.onGridChanged = (term, firstRow, lastRow) => { + const startTime = (0, _performanceNow().default)(); originalOnGridChanged.call(textRenderLayer, term, firstRow, lastRow); - frameTimes.push(performanceNow() - startTime); + frameTimes.push((0, _performanceNow().default)() - startTime); + if (frameTimes.length === NUMBER_OF_FRAMES_TO_MEASURE) { - evaluateCanvasRenderer(); - // Restore original function + evaluateCanvasRenderer(); // Restore original function + textRenderLayer.onGridChanged = originalOnGridChanged; } }; - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { if (!evaluated) { // Restore original function if we haven't done that already. textRenderLayer.onGridChanged = originalOnGridChanged; - } - // Clear frame times because it won't be used again. + } // Clear frame times because it won't be used again. + + frameTimes = []; }); -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/nuclide-terminal-uri.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/nuclide-terminal-uri.js index e5a61c6e..0ce5ad0f 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/nuclide-terminal-uri.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/nuclide-terminal-uri.js @@ -1,3 +1,28 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.uriFromInfo = uriFromInfo; +exports.infoFromUri = infoFromUri; +exports.TERMINAL_DEFAULT_INFO = exports.TERMINAL_DEFAULT_ICON = exports.TERMINAL_DEFAULT_LOCATION = exports.URI_PREFIX = void 0; + +var _crypto = _interopRequireDefault(require("crypto")); + +var _url = _interopRequireDefault(require("url")); + +function _uuid() { + const data = _interopRequireDefault(require("uuid")); + + _uuid = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,45 +31,25 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import type {Command} from './pty-service/rpc-types'; -import type {TerminalInfo} from './types'; - -import crypto from 'crypto'; -import invariant from 'assert'; -import url from 'url'; -import uuid from 'uuid'; - // Generate a unique random token that is included in every URI we generate. // We use this to check that URIs containing shell commands and similarly // sensitive data were generated by this instance of Nuclide. The goal is // to prevent externally generated URIs from ever resulting in command // execution. -const trustToken = crypto.randomBytes(256).toString('hex'); - -// The external interface TerminalInfo leaves everything optional. +const trustToken = _crypto.default.randomBytes(256).toString('hex'); // The external interface TerminalInfo leaves everything optional. // When we open a terminal we will instantiate missing fields with defaults. -export type InstantiatedTerminalInfo = { - title: string, - key: string, - remainOnCleanExit: boolean, - defaultLocation: atom$PaneLocation | 'pane', - icon: string, - trustToken: string, - command?: Command, - cwd: string, - environmentVariables?: Map, - preservedCommands: Array, - initialInput: string, -}; -export const URI_PREFIX = 'atom://nuclide-terminal-view'; -export const TERMINAL_DEFAULT_LOCATION = 'pane'; -export const TERMINAL_DEFAULT_ICON = 'terminal'; -export const TERMINAL_DEFAULT_INFO = { + +const URI_PREFIX = 'atom://nuclide-terminal-view'; +exports.URI_PREFIX = URI_PREFIX; +const TERMINAL_DEFAULT_LOCATION = 'pane'; +exports.TERMINAL_DEFAULT_LOCATION = TERMINAL_DEFAULT_LOCATION; +const TERMINAL_DEFAULT_ICON = 'terminal'; +exports.TERMINAL_DEFAULT_ICON = TERMINAL_DEFAULT_ICON; +const TERMINAL_DEFAULT_INFO = { remainOnCleanExit: false, defaultLocation: TERMINAL_DEFAULT_LOCATION, icon: TERMINAL_DEFAULT_ICON, @@ -52,13 +57,12 @@ export const TERMINAL_DEFAULT_INFO = { title: '', cwd: '', preservedCommands: [], - trustToken, + trustToken }; +exports.TERMINAL_DEFAULT_INFO = TERMINAL_DEFAULT_INFO; -export function uriFromInfo( - info: TerminalInfo | InstantiatedTerminalInfo, -): string { - const uri = url.format({ +function uriFromInfo(info) { + const uri = _url.default.format({ protocol: 'atom', host: 'nuclide-terminal-view', slashes: true, @@ -66,80 +70,69 @@ export function uriFromInfo( cwd: info.cwd == null ? '' : info.cwd, command: info.command == null ? '' : JSON.stringify(info.command), title: info.title == null ? '' : info.title, - key: info.key != null && info.key !== '' ? info.key : uuid.v4(), + key: info.key != null && info.key !== '' ? info.key : _uuid().default.v4(), remainOnCleanExit: info.remainOnCleanExit, defaultLocation: info.defaultLocation, icon: info.icon, - environmentVariables: - info.environmentVariables != null - ? JSON.stringify([...info.environmentVariables]) - : '', + environmentVariables: info.environmentVariables != null ? JSON.stringify([...info.environmentVariables]) : '', preservedCommands: JSON.stringify(info.preservedCommands || []), initialInput: info.initialInput != null ? info.initialInput : '', - trustToken, - }, + trustToken + } }); - invariant(uri.startsWith(URI_PREFIX)); + + if (!uri.startsWith(URI_PREFIX)) { + throw new Error("Invariant violation: \"uri.startsWith(URI_PREFIX)\""); + } + return uri; } -export function infoFromUri( - paneUri: string, - uriFromTrustedSource: boolean = false, -): InstantiatedTerminalInfo { - const {query} = url.parse(paneUri, true); +function infoFromUri(paneUri, uriFromTrustedSource = false) { + const { + query + } = _url.default.parse(paneUri, true); if (query == null) { - return {...TERMINAL_DEFAULT_INFO, key: uuid.v4()}; + return Object.assign({}, TERMINAL_DEFAULT_INFO, { + key: _uuid().default.v4() + }); } else { - const cwd = query.cwd === '' ? {} : {cwd: query.cwd}; - const command = - query.command !== '' ? {command: JSON.parse(query.command)} : {}; - const title = query.title === '' ? {} : {title: query.title}; + const cwd = query.cwd === '' ? {} : { + cwd: query.cwd + }; + const command = query.command !== '' ? { + command: JSON.parse(query.command) + } : {}; + const title = query.title === '' ? {} : { + title: query.title + }; const remainOnCleanExit = query.remainOnCleanExit === 'true'; const key = query.key; - const defaultLocation = - query.defaultLocation != null && query.defaultLocation !== '' - ? query.defaultLocation - : TERMINAL_DEFAULT_LOCATION; - const icon = - query.icon != null && query.icon !== '' - ? query.icon - : TERMINAL_DEFAULT_ICON; - const environmentVariables = - query.environmentVariables != null && query.environmentVariables !== '' - ? new Map(JSON.parse(query.environmentVariables)) - : new Map(); + const defaultLocation = query.defaultLocation != null && query.defaultLocation !== '' ? query.defaultLocation : TERMINAL_DEFAULT_LOCATION; + const icon = query.icon != null && query.icon !== '' ? query.icon : TERMINAL_DEFAULT_ICON; + const environmentVariables = query.environmentVariables != null && query.environmentVariables !== '' ? new Map(JSON.parse(query.environmentVariables)) : new Map(); const preservedCommands = JSON.parse(query.preservedCommands || '[]'); - const initialInput = query.initialInput != null ? query.initialInput : ''; - - // Information that can affect the commands executed by the terminal, + const initialInput = query.initialInput != null ? query.initialInput : ''; // Information that can affect the commands executed by the terminal, // and that therefore must come from a trusted source. // // If we detect that the URL did not come from this instance of Nuclide, // we just omit these fields so the user gets a default shell. - const trustedFields = { - ...cwd, - ...command, + + const trustedFields = Object.assign({}, cwd, command, { environmentVariables, preservedCommands, - initialInput, - }; - - // Everything here is cosmetic information that does not affect + initialInput + }); // Everything here is cosmetic information that does not affect // processes running in the resulting terminal. - const untrustedFields = { - ...title, + + const untrustedFields = Object.assign({}, title, { remainOnCleanExit, defaultLocation, icon, - key, - }; - + key + }); const isTrusted = uriFromTrustedSource || query.trustToken === trustToken; - return { - ...untrustedFields, - ...(isTrusted ? trustedFields : TERMINAL_DEFAULT_INFO), - }; + return Object.assign({}, untrustedFields, isTrusted ? trustedFields : TERMINAL_DEFAULT_INFO); } -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/PtyService.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/PtyService.js index c0a018ca..16e401c8 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/PtyService.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/PtyService.js @@ -1,3 +1,110 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.spawn = spawn; +exports.useTitleAsPath = useTitleAsPath; +exports.PtyImplementation = void 0; + +var _fs = _interopRequireDefault(require("fs")); + +function _fsPromise() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function ptyFactory() { + const data = _interopRequireWildcard(require("nuclide-prebuilt-libs/pty")); + + ptyFactory = function () { + return data; + }; + + return data; +} + +var _os = _interopRequireDefault(require("os")); + +function _process() { + const data = require("../../../../../nuclide-commons/process"); + + _process = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _performanceNow() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/performanceNow")); + + _performanceNow = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _shellConfig() { + const data = require("./shellConfig"); + + _shellConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +113,17 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import fs from 'fs'; -import fsPromise from 'nuclide-commons/fsPromise'; -import * as ptyFactory from 'nuclide-prebuilt-libs/pty'; - -import os from 'os'; -import {getOriginalEnvironment} from 'nuclide-commons/process'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {objectFromMap} from 'nuclide-commons/collection'; -import performanceNow from 'nuclide-commons/performanceNow'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {track} from 'nuclide-commons/analytics'; -import {runCommand} from 'nuclide-commons/process'; - -import {readConfig} from './shellConfig'; - -import type {Command, Pty, PtyInfo, PtyClient} from './rpc-types'; - -export async function spawn(info: PtyInfo, client: PtyClient): Promise { - return new PtyImplementation( - info, - client, - await getCommand(info, client), - await getEnvironment(info), - ); +async function spawn(info, client) { + return new PtyImplementation(info, client, (await getCommand(info, client)), (await getEnvironment(info))); } -export async function useTitleAsPath(client: PtyClient): Promise { +async function useTitleAsPath(client) { try { - const config = await readConfig(); - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + const config = await (0, _shellConfig().readConfig)(); // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + if (config != null && config.useTitleAsPath != null) { return config.useTitleAsPath; } @@ -50,17 +134,18 @@ export async function useTitleAsPath(client: PtyClient): Promise { return false; } -async function getCommand(info: PtyInfo, client: PtyClient): Promise { +async function getCommand(info, client) { // Client-specified command is highest precedence. if (info.command != null) { return info.command; - } - - // If no command, fall back to shell command specified in local + } // If no command, fall back to shell command specified in local // (server) config file. This cannot be Atom config/preference, // since the default shell path varies between client and server. + + try { - const config = await readConfig(); + const config = await (0, _shellConfig().readConfig)(); + if (config != null && config.command != null) { return config.command; } @@ -70,162 +155,157 @@ async function getCommand(info: PtyInfo, client: PtyClient): Promise { try { const defaultShellCommand = await getDefaultShellCommand(); + if (defaultShellCommand != null) { return defaultShellCommand; } } catch (error) { client.onOutput(`Error getting default shell:\r\n${error}\r\n`); - } + } // If no command and no local settings, default to /bin/bash + - // If no command and no local settings, default to /bin/bash return { file: '/bin/bash', - args: ['-l'], + args: ['-l'] }; } -async function getDefaultShellCommand(): Promise { +async function getDefaultShellCommand() { if (process.platform === 'win32') { return { file: 'cmd.exe', - args: [], + args: [] }; } - const userInfo = os.userInfo(); + const userInfo = _os.default.userInfo(); + const username = userInfo.username; let defaultShell = null; + if (process.platform === 'darwin') { const homedir = userInfo.homedir; - const output = await runCommand('dscl', [ - '.', - '-read', - homedir, - 'UserShell', - ]).toPromise(); - // Expected output looks like: + const output = await (0, _process().runCommand)('dscl', ['.', '-read', homedir, 'UserShell']).toPromise(); // Expected output looks like: // UserShell: /bin/bash + const prefix = 'UserShell: '; + if (output != null && output.startsWith(prefix)) { defaultShell = output.substring(prefix.length).trim(); } } else if (process.platform === 'linux') { - const output = await runCommand('getent', ['passwd', username]).toPromise(); - // Expected output looks like: + const output = await (0, _process().runCommand)('getent', ['passwd', username]).toPromise(); // Expected output looks like: // userid:*:1000:1000:Full Name:/home/userid:/bin/bash + defaultShell = output.substring(output.lastIndexOf(':') + 1).trim(); } + if (defaultShell == null || defaultShell === '') { return null; - } + } // Sanity check that the file exists and is executable + - // Sanity check that the file exists and is executable - const stat = await fsPromise.stat(defaultShell); - // eslint-disable-next-line no-bitwise - if ((stat.mode & fs.constants.S_IXOTH) === 0) { + const stat = await _fsPromise().default.stat(defaultShell); // eslint-disable-next-line no-bitwise + + if ((stat.mode & _fs.default.constants.S_IXOTH) === 0) { return null; } return { file: defaultShell, - args: ['-l'], + args: ['-l'] }; -} - -// variable defined in the original atom environment we want +} // variable defined in the original atom environment we want // erased. + + const filteredVariables = ['NODE_ENV', 'NODE_PATH']; -async function getEnvironment(info: PtyInfo): Promise { - const newEnv = {...(await getOriginalEnvironment())}; +async function getEnvironment(info) { + const newEnv = Object.assign({}, (await (0, _process().getOriginalEnvironment)())); + for (const x of filteredVariables) { delete newEnv[x]; } - return { - ...newEnv, - ...(info.environment != null ? objectFromMap(info.environment) : {}), - TERM_PROGRAM: 'nuclide', - }; + + return Object.assign({}, newEnv, info.environment != null ? (0, _collection().objectFromMap)(info.environment) : {}, { + TERM_PROGRAM: 'nuclide' + }); } -export class PtyImplementation implements Pty { - _subscriptions: UniversalDisposable; - _pty: Object; - _client: PtyClient; - _initialization: {command: string, cwd: string}; - _startTime: number; - _bytesIn: number; - _bytesOut: number; - - constructor(info: PtyInfo, client: PtyClient, command: Command, env: Object) { - this._startTime = performanceNow(); +class PtyImplementation { + constructor(info, client, command, env) { + this._startTime = (0, _performanceNow().default)(); this._bytesIn = 0; this._bytesOut = 0; this._initialization = { command: [command.file, ...command.args].join(' '), - cwd: info.cwd != null ? info.cwd : '', + cwd: info.cwd != null ? info.cwd : '' }; - track('nuclide-pty-rpc.spawn', this._initialization); - - const subscriptions = (this._subscriptions = new UniversalDisposable()); - const pty = (this._pty = ptyFactory.spawn(command.file, command.args, { + (0, _analytics().track)('nuclide-pty-rpc.spawn', this._initialization); + const subscriptions = this._subscriptions = new (_UniversalDisposable().default)(); + const pty = this._pty = ptyFactory().spawn(command.file, command.args, { name: info.terminalType, - cwd: - info.cwd != null - ? nuclideUri.expandHomeDir(info.cwd) - : nuclideUri.expandHomeDir('~'), - env, - })); - subscriptions.add(() => pty.destroy()); - // We need to dispose PtyClient here so that the client can GC the client. + cwd: info.cwd != null ? _nuclideUri().default.expandHomeDir(info.cwd) : _nuclideUri().default.expandHomeDir('~'), + env + }); + subscriptions.add(() => pty.destroy()); // We need to dispose PtyClient here so that the client can GC the client. // (Otherwise, Nuclide's RPC framework will keep it around forever). // This is a bit of a weird flow where // 1) PtyClient gets disposed, which triggers this.dispose // 2) this.dispose triggers PtyClient.dispose // so make sure that double-disposing PtyClient is OK. + subscriptions.add(client); this._client = client; const onOutput = this._onOutput.bind(this); + pty.addListener('data', onOutput); subscriptions.add(() => pty.removeListener('data', onOutput)); const onExit = this._onExit.bind(this); + pty.addListener('exit', onExit); subscriptions.add(() => pty.removeListener('exit', onExit)); } - _onOutput(data: string): void { + _onOutput(data) { this._bytesOut += data.length; + this._client.onOutput(data); } - _onExit(code: number, signal: number): void { - track('nuclide-pty-rpc.on-exit', { - ...this._initialization, + _onExit(code, signal) { + (0, _analytics().track)('nuclide-pty-rpc.on-exit', Object.assign({}, this._initialization, { bytesIn: String(this._bytesIn), bytesOut: String(this._bytesOut), - duration: String((performanceNow() - this._startTime) / 1000), + duration: String(((0, _performanceNow().default)() - this._startTime) / 1000), exitCode: String(code), - signal: String(code), - }); + signal: String(code) + })); + this._client.onExit(code, signal); } - dispose(): void { + dispose() { this._subscriptions.dispose(); } - resize(columns: number, rows: number): void { + resize(columns, rows) { if (this._pty.writable) { this._pty.resize(columns, rows); } } - writeInput(data: string): void { + writeInput(data) { if (this._pty.writable) { this._bytesIn += data.length; + this._pty.write(data); } } + } + +exports.PtyImplementation = PtyImplementation; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/rpc-types.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/rpc-types.js index 6cd58bbd..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/rpc-types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/rpc-types.js @@ -1,35 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict - * @format - */ - -export type PtyInfo = { - terminalType: string, - environment?: Map, - cwd?: string, - command?: Command, -}; - -export interface PtyClient { - onOutput(data: string): void; - onExit(code: number, signal: number): void; - dispose(): void; -} - -export interface Pty { - resize(columns: number, rows: number): void; - writeInput(data: string): void; - dispose(): void; -} - -export type Command = { - file: string, - args: Array, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/shellConfig.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/shellConfig.js index afca5185..ce4c7f85 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/shellConfig.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/pty-service/shellConfig.js @@ -1,3 +1,45 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.readConfig = readConfig; +exports.parseConfig = parseConfig; + +var _os = _interopRequireDefault(require("os")); + +function _fsPromise() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _string() { + const data = require("../../../../../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,38 +48,24 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import os from 'os'; - -import fsPromise from 'nuclide-commons/fsPromise'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {shellParse} from 'nuclide-commons/string'; - -import type {Command} from './rpc-types'; - const CONFIG_BASENAME = '.nuclide-terminal.json'; -export type Config = { - command?: Command, -}; +async function readConfig() { + let configContents = null; -export async function readConfig(): Promise { - let configContents = (null: ?string); try { - const configFile = nuclideUri.expandHomeDir(`~/${CONFIG_BASENAME}`); - configContents = await fsPromise.readFile(configFile, 'utf-8'); + const configFile = _nuclideUri().default.expandHomeDir(`~/${CONFIG_BASENAME}`); + + configContents = await _fsPromise().default.readFile(configFile, 'utf-8'); } catch (error) { if (error.code === 'ENOENT') { // If the user has no config file, that is still success, just with empty result. return Promise.resolve(null); } else { - return Promise.reject( - new Error(`code='${error.code}', error='${error}'`), - ); + return Promise.reject(new Error(`code='${error.code}', error='${error}'`)); } } @@ -48,17 +76,13 @@ export async function readConfig(): Promise { } } -export function parseConfig(configContents: string): Config { - function throwError(message: string): Error { - throw new Error( - `(${os.hostname()}) error parsing ~/${CONFIG_BASENAME}:\n` + - ` ${message}.\n` + - 'Contents:\n' + - configContents, - ); +function parseConfig(configContents) { + function throwError(message) { + throw new Error(`(${_os.default.hostname()}) error parsing ~/${CONFIG_BASENAME}:\n` + ` ${message}.\n` + 'Contents:\n' + configContents); } let rawConfig = null; + try { rawConfig = JSON.parse(configContents); } catch (e) { @@ -68,18 +92,23 @@ export function parseConfig(configContents: string): Config { if (typeof rawConfig !== 'object') { throw throwError('Expected top-level to be an object.'); } - invariant(rawConfig != null); + + if (!(rawConfig != null)) { + throw new Error("Invariant violation: \"rawConfig != null\""); + } let argv = null; const command = rawConfig.command; + if (typeof command === 'string') { - argv = shellParse(command); + argv = (0, _string().shellParse)(command); } else if (Array.isArray(command)) { for (const arg of command) { if (typeof arg !== 'string') { throwError(`'args' must be strings, got ${arg}`); } } + argv = command; } else { throw throwError('"command" must be a string or string array'); @@ -88,7 +117,7 @@ export function parseConfig(configContents: string): Config { return { command: { file: argv[0], - args: argv.slice(1), - }, + args: argv.slice(1) + } }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/sink.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/sink.js index 69a5c407..63429994 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/sink.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/sink.js @@ -1,3 +1,11 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.removePrefixSink = removePrefixSink; +exports.patternCounterSink = patternCounterSink; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +14,26 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import invariant from 'assert'; - -// A Sink is a simplfiication of a writable stream. This facilitates -// setting up a pipeline of transformations on a string stream in cases -// where there is no need to consider errors, buffering, encoding, or EOF. -export type Sink = string => void; - // Creates a pass-through Sink that skips over a literal prefix if present. // // Parameters: // prefix - literal prefix to match. // next - next Sink in the data processing chain. -export function removePrefixSink(prefix: string, next: Sink): Sink { +function removePrefixSink(prefix, next) { let doneMatching = false; let matched = 0; return data => { if (doneMatching) { next(data); return; - } - // At this point we are still in the prefix. + } // At this point we are still in the prefix. + + const limit = Math.min(prefix.length - matched, data.length); + for (let i = 0; i < limit; i++) { if (data.charAt(i) !== prefix.charAt(matched + i)) { // Found a non-match. Forward any partial match plus new data. @@ -39,42 +41,46 @@ export function removePrefixSink(prefix: string, next: Sink): Sink { next(prefix.slice(0, matched) + data); return; } - } - // At this point everything we have seen so far has matched. + } // At this point everything we have seen so far has matched. + + matched += limit; - invariant(matched <= prefix.length); + + if (!(matched <= prefix.length)) { + throw new Error("Invariant violation: \"matched <= prefix.length\""); + } + if (matched === prefix.length) { // Matched the whole prefix. Remove prefix and forward remainder (if any). doneMatching = true; + if (limit < data.length) { next(data.slice(limit)); } } }; -} - -// Creates a pass-through Sink that calls a callback with the count of +} // Creates a pass-through Sink that calls a callback with the count of // (possibly overlapping) matches of a pattern. // // Parameters: // pattern - sequence of characters to match. // notify - called each time a match occurs. This can return false to disable future notifications. // next - next Sink in the data processing chain. -export function patternCounterSink( - pattern: string, - notify: () => boolean, - next: Sink, -): Sink { + + +function patternCounterSink(pattern, notify, next) { let enabled = true; - let partial: Array = []; - let nextPartial: Array = []; + let partial = []; + let nextPartial = []; return data => { for (let i = 0; enabled && i < data.length; i++) { const dataCh = data.charAt(i); partial.push(0); + while (enabled && partial.length > 0) { let patternIndex = partial.pop(); const patternCh = pattern.charAt(patternIndex++); + if (patternCh === dataCh) { if (patternIndex < pattern.length) { nextPartial.push(patternIndex); @@ -83,8 +89,10 @@ export function patternCounterSink( } } } + [partial, nextPartial] = [nextPartial, partial]; } + next(data); }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/terminal-view.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/terminal-view.js index 886baada..eed7f7e9 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/terminal-view.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/terminal-view.js @@ -1,3 +1,192 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.deserializeTerminalView = deserializeTerminalView; +exports.getSafeInitialInput = getSafeInitialInput; +exports.TerminalView = exports.URI_PREFIX = void 0; + +var _atom = require("atom"); + +var _electron = require("electron"); + +function _observePaneItemVisibility() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/observePaneItemVisibility")); + + _observePaneItemVisibility = function () { + return data; + }; + + return data; +} + +function _projects() { + const data = require("../../../../nuclide-commons-atom/projects"); + + _projects = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../../../../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../../../../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +var _url = _interopRequireDefault(require("url")); + +function _AtomServiceContainer() { + const data = require("./AtomServiceContainer"); + + _AtomServiceContainer = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _observableDom() { + const data = require("../../../../nuclide-commons-ui/observable-dom"); + + _observableDom = function () { + return data; + }; + + return data; +} + +function _performanceNow() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/performanceNow")); + + _performanceNow = function () { + return data; + }; + + return data; +} + +function _createTerminal() { + const data = require("./createTerminal"); + + _createTerminal = function () { + return data; + }; + + return data; +} + +function _measurePerformance() { + const data = _interopRequireDefault(require("./measure-performance")); + + _measurePerformance = function () { + return data; + }; + + return data; +} + +function _nuclideTerminalUri() { + const data = require("./nuclide-terminal-uri"); + + _nuclideTerminalUri = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../../../../nuclide-commons-atom/go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _config() { + const data = require("./config"); + + _config = function () { + return data; + }; + + return data; +} + +function _sink() { + const data = require("./sink"); + + _sink = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,474 +195,311 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-env browser */ +const TMUX_CONTROLCONTROL_PREFIX = '\x1BP1000p'; +const URI_PREFIX = 'atom://nuclide-terminal-view'; +exports.URI_PREFIX = URI_PREFIX; -import invariant from 'assert'; -import {Emitter} from 'atom'; -import {shell, clipboard} from 'electron'; -import observePaneItemVisibility from 'nuclide-commons-atom/observePaneItemVisibility'; -import { - observeAddedHostnames, - observeRemovedHostnames, -} from 'nuclide-commons-atom/projects'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {fastDebounce} from 'nuclide-commons/observable'; -import {Observable} from 'rxjs'; -import url from 'url'; - -import {getPtyServiceByNuclideUri} from './AtomServiceContainer'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {ResizeObservable} from 'nuclide-commons-ui/observable-dom'; -import performanceNow from 'nuclide-commons/performanceNow'; -import {createTerminal} from './createTerminal'; -import measurePerformance from './measure-performance'; -import {infoFromUri, uriFromInfo} from './nuclide-terminal-uri'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {track} from 'nuclide-commons/analytics'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; - -import { - ADD_ESCAPE_COMMAND, - COLOR_CONFIGS, - CURSOR_BLINK_CONFIG, - CURSOR_STYLE_CONFIG, - DOCUMENTATION_MESSAGE_CONFIG, - FONT_FAMILY_CONFIG, - FONT_SCALE_CONFIG, - LINE_HEIGHT_CONFIG, - OPTION_IS_META_CONFIG, - PRESERVED_COMMANDS_CONFIG, - SCROLLBACK_CONFIG, - getFontSize, - RENDERER_TYPE_CONFIG, -} from './config'; -import {removePrefixSink, patternCounterSink} from './sink'; - -import type {Terminal} from './createTerminal'; -import type {TerminalInstance} from './types'; -import type {IconName} from 'nuclide-commons-ui/Icon'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {Command, Pty, PtyClient, PtyInfo} from './pty-service/rpc-types'; -import type {InstantiatedTerminalInfo} from './nuclide-terminal-uri'; - -import type {Sink} from './sink'; +class TerminalView { + constructor(paneUri) { + this._syncFontAndFit = () => { + this._setTerminalOption('fontSize', (0, _config().getFontSize)()); -const TMUX_CONTROLCONTROL_PREFIX = '\x1BP1000p'; -export const URI_PREFIX = 'atom://nuclide-terminal-view'; + this._setTerminalOption('lineHeight', _featureConfig().default.get(_config().LINE_HEIGHT_CONFIG)); -export interface TerminalViewState { - deserializer: 'TerminalView'; - paneUri: string; -} + this._setTerminalOption('fontFamily', _featureConfig().default.get(_config().FONT_FAMILY_CONFIG)); + + this._fitAndResize(); + }; -type ProcessExitCallback = () => mixed; - -export class TerminalView implements PtyClient, TerminalInstance { - _paneUri: string; - _cwd: ?NuclideUri; - _path: ?NuclideUri; - _command: ?Command; - _useTitleAsPath: boolean; - _title: string; - _subscriptions: UniversalDisposable; - _emitter: Emitter; - _preservedCommands: Set; - _div: HTMLDivElement; - _terminal: Terminal; - _pty: ?Pty; - _processOutput: Sink; - _startTime: number; - _bytesIn: number; - _bytesOut: number; - _focusStart: ?number; - _focusDuration: number; - _terminalInfo: InstantiatedTerminalInfo; - _processExitCallback: ProcessExitCallback; - _isFirstOutput: boolean; - _initialInput: string; - - constructor(paneUri: string) { this._paneUri = paneUri; - const info = infoFromUri(paneUri); + const info = (0, _nuclideTerminalUri().infoFromUri)(paneUri); this._terminalInfo = info; - const cwd = (this._cwd = info.cwd == null ? null : info.cwd); + const cwd = this._cwd = info.cwd == null ? null : info.cwd; this._command = info.command == null ? null : info.command; this._title = info.title == null ? 'terminal' : info.title; this._path = cwd; - this._initialInput = - info.initialInput == null ? '' : getSafeInitialInput(info.initialInput); + this._initialInput = info.initialInput == null ? '' : getSafeInitialInput(info.initialInput); + this._processExitCallback = () => {}; - this._useTitleAsPath = false; - this._startTime = performanceNow(); + this._useTitleAsPath = false; + this._startTime = (0, _performanceNow().default)(); this._bytesIn = 0; this._bytesOut = 0; this._focusStart = null; this._focusDuration = 0; this._isFirstOutput = true; - - const subscriptions = (this._subscriptions = new UniversalDisposable()); + const subscriptions = this._subscriptions = new (_UniversalDisposable().default)(); this._processOutput = this._createOutputSink(); - - this._emitter = new Emitter(); + this._emitter = new _atom.Emitter(); subscriptions.add(this._emitter); - - subscriptions.add( - featureConfig - .observeAsStream(PRESERVED_COMMANDS_CONFIG) - .subscribe((preserved: any) => { - this._preservedCommands = new Set([ - ...(preserved || []), - ...(info.preservedCommands || []), - ]); - }), - atom.config.onDidChange('core.themes', this._syncAtomTheme.bind(this)), - atom.themes.onDidChangeActiveThemes(this._syncAtomTheme.bind(this)), - ); - - subscriptions.add( - // Skip the first value because the observe callback triggers once when - // we begin observing, duplicating work in the constructor. - ...Object.keys(COLOR_CONFIGS).map(color => - featureConfig - .observeAsStream(COLOR_CONFIGS[color]) - .skip(1) - .subscribe(this._syncAtomTheme.bind(this)), - ), - ); - - const div = (this._div = document.createElement('div')); + subscriptions.add(_featureConfig().default.observeAsStream(_config().PRESERVED_COMMANDS_CONFIG).subscribe(preserved => { + this._preservedCommands = new Set([...(preserved || []), ...(info.preservedCommands || [])]); + }), atom.config.onDidChange('core.themes', this._syncAtomTheme.bind(this)), atom.themes.onDidChangeActiveThemes(this._syncAtomTheme.bind(this))); + subscriptions.add( // Skip the first value because the observe callback triggers once when + // we begin observing, duplicating work in the constructor. + ...Object.keys(_config().COLOR_CONFIGS).map(color => _featureConfig().default.observeAsStream(_config().COLOR_CONFIGS[color]).skip(1).subscribe(this._syncAtomTheme.bind(this)))); + const div = this._div = document.createElement('div'); div.classList.add('terminal-pane'); subscriptions.add(() => div.remove()); + const terminal = this._terminal = (0, _createTerminal().createTerminal)(); + terminal.attachCustomKeyEventHandler(this._checkIfKeyBoundOrDivertToXTerm.bind(this)); - const terminal = (this._terminal = createTerminal()); - terminal.attachCustomKeyEventHandler( - this._checkIfKeyBoundOrDivertToXTerm.bind(this), - ); this._subscriptions.add(() => terminal.dispose()); + terminal.webLinksInit(openLink); registerLinkHandlers(terminal, this._cwd); - this._subscriptions.add( - atom.commands.add(div, 'core:copy', () => { - document.execCommand('copy'); - }), - atom.commands.add(div, 'core:paste', () => { - document.execCommand('paste'); - }), - atom.commands.add( - div, - ADD_ESCAPE_COMMAND, - this._addEscapePrefix.bind(this), - ), - atom.commands.add(div, 'atom-ide-terminal:clear', this._clear.bind(this)), - ); + this._subscriptions.add(atom.commands.add(div, 'core:copy', () => { + document.execCommand('copy'); + }), atom.commands.add(div, 'core:paste', () => { + document.execCommand('paste'); + }), atom.commands.add(div, _config().ADD_ESCAPE_COMMAND, this._addEscapePrefix.bind(this)), atom.commands.add(div, 'atom-ide-terminal:clear', this._clear.bind(this))); if (process.platform === 'win32') { // On Windows, add Putty-style highlight and right click to copy, right click to paste. - this._subscriptions.add( - Observable.fromEvent(div, 'contextmenu').subscribe(e => { - // Note: Manipulating the clipboard directly because atom's core:copy and core:paste - // commands are not working correctly with terminal selection. - if (terminal.hasSelection()) { - // $FlowFixMe: add types for clipboard - clipboard.writeText(terminal.getSelection()); - } else { - document.execCommand('paste'); - } - terminal.clearSelection(); - terminal.focus(); - e.stopPropagation(); - }), - ); - } + this._subscriptions.add(_RxMin.Observable.fromEvent(div, 'contextmenu').subscribe(e => { + // Note: Manipulating the clipboard directly because atom's core:copy and core:paste + // commands are not working correctly with terminal selection. + if (terminal.hasSelection()) { + // $FlowFixMe: add types for clipboard + _electron.clipboard.writeText(terminal.getSelection()); + } else { + document.execCommand('paste'); + } - if (cwd != null && nuclideUri.isRemote(cwd)) { - this._subscriptions.add( - observeRemovedHostnames().subscribe(hostname => { - if (nuclideUri.getHostname(cwd) === hostname) { - this._closeTab(); - } - }), - ); + terminal.clearSelection(); + terminal.focus(); + e.stopPropagation(); + })); } - // div items don't support a 'focus' event, and we need to forward. - (this._div: any).focus = () => terminal.focus(); - (this._div: any).blur = () => terminal.blur(); + if (cwd != null && _nuclideUri().default.isRemote(cwd)) { + this._subscriptions.add((0, _projects().observeRemovedHostnames)().subscribe(hostname => { + if (_nuclideUri().default.getHostname(cwd) === hostname) { + this._closeTab(); + } + })); + } // div items don't support a 'focus' event, and we need to forward. - // Terminal.open only works after its div has been attached to the DOM, + + this._div.focus = () => terminal.focus(); + + this._div.blur = () => terminal.blur(); // Terminal.open only works after its div has been attached to the DOM, // which happens in getElement, not in this constructor. Therefore delay // open and spawn until the div is visible, which means it is in the DOM. - this._subscriptions.add( - observePaneItemVisibility(this) - .filter(Boolean) - .first() - .subscribe(() => { - terminal.open(this._div); - (div: any).terminal = terminal; - if (featureConfig.get(DOCUMENTATION_MESSAGE_CONFIG)) { - const docsUrl = 'https://nuclide.io/docs/features/terminal'; - terminal.writeln(`For more info check out the docs: ${docsUrl}`); - } - terminal.focus(); - this._subscriptions.add(this._subscribeFitEvents()); - this._spawn(cwd) - .then(pty => this._onPtyFulfill(pty)) - .catch(error => this._onPtyFail(error)); - }), - ); - } - - _subscribeFitEvents(): UniversalDisposable { - return new UniversalDisposable( - featureConfig - .observeAsStream(OPTION_IS_META_CONFIG) - .skip(1) - .subscribe(optionIsMeta => { - this._setTerminalOption('macOptionIsMeta', optionIsMeta); - }), - featureConfig - .observeAsStream(CURSOR_STYLE_CONFIG) - .skip(1) - .subscribe(cursorStyle => - this._setTerminalOption('cursorStyle', cursorStyle), - ), - featureConfig - .observeAsStream(CURSOR_BLINK_CONFIG) - .skip(1) - .subscribe(cursorBlink => - this._setTerminalOption('cursorBlink', cursorBlink), - ), - featureConfig - .observeAsStream(SCROLLBACK_CONFIG) - .skip(1) - .subscribe(scrollback => - this._setTerminalOption('scrollback', scrollback), - ), - Observable.combineLatest( - observePaneItemVisibility(this), - Observable.merge( - observableFromSubscribeFunction(cb => - atom.config.onDidChange('editor.fontSize', cb), - ), - featureConfig.observeAsStream(FONT_SCALE_CONFIG).skip(1), - featureConfig.observeAsStream(FONT_FAMILY_CONFIG).skip(1), - featureConfig.observeAsStream(LINE_HEIGHT_CONFIG).skip(1), - Observable.fromEvent(this._terminal, 'focus'), - // Debounce resize observables to reduce lag. - Observable.merge( - Observable.fromEvent(window, 'resize'), - new ResizeObservable(this._div), - ).let(fastDebounce(100)), - ), - ) - // Don't emit syncs if the pane is not visible. - .filter(([visible]) => visible) - .subscribe(this._syncFontAndFit), - ); - } - - _spawn(cwd: ?NuclideUri): Promise { + + + this._subscriptions.add((0, _observePaneItemVisibility().default)(this).filter(Boolean).first().subscribe(() => { + terminal.open(this._div); + div.terminal = terminal; + + if (_featureConfig().default.get(_config().DOCUMENTATION_MESSAGE_CONFIG)) { + const docsUrl = 'https://nuclide.io/docs/features/terminal'; + terminal.writeln(`For more info check out the docs: ${docsUrl}`); + } + + terminal.focus(); + + this._subscriptions.add(this._subscribeFitEvents()); + + this._spawn(cwd).then(pty => this._onPtyFulfill(pty)).catch(error => this._onPtyFail(error)); + })); + } + + _subscribeFitEvents() { + return new (_UniversalDisposable().default)(_featureConfig().default.observeAsStream(_config().OPTION_IS_META_CONFIG).skip(1).subscribe(optionIsMeta => { + this._setTerminalOption('macOptionIsMeta', optionIsMeta); + }), _featureConfig().default.observeAsStream(_config().CURSOR_STYLE_CONFIG).skip(1).subscribe(cursorStyle => this._setTerminalOption('cursorStyle', cursorStyle)), _featureConfig().default.observeAsStream(_config().CURSOR_BLINK_CONFIG).skip(1).subscribe(cursorBlink => this._setTerminalOption('cursorBlink', cursorBlink)), _featureConfig().default.observeAsStream(_config().SCROLLBACK_CONFIG).skip(1).subscribe(scrollback => this._setTerminalOption('scrollback', scrollback)), _RxMin.Observable.combineLatest((0, _observePaneItemVisibility().default)(this), _RxMin.Observable.merge((0, _event().observableFromSubscribeFunction)(cb => atom.config.onDidChange('editor.fontSize', cb)), _featureConfig().default.observeAsStream(_config().FONT_SCALE_CONFIG).skip(1), _featureConfig().default.observeAsStream(_config().FONT_FAMILY_CONFIG).skip(1), _featureConfig().default.observeAsStream(_config().LINE_HEIGHT_CONFIG).skip(1), _RxMin.Observable.fromEvent(this._terminal, 'focus'), // Debounce resize observables to reduce lag. + _RxMin.Observable.merge(_RxMin.Observable.fromEvent(window, 'resize'), new (_observableDom().ResizeObservable)(this._div)).let((0, _observable().fastDebounce)(100)))) // Don't emit syncs if the pane is not visible. + .filter(([visible]) => visible).subscribe(this._syncFontAndFit)); + } + + _spawn(cwd) { const command = this._command; - const info: PtyInfo = { + const info = Object.assign({ terminalType: 'xterm-256color', - environment: this._terminalInfo.environmentVariables, - ...(command == null ? {} : {command}), - }; + environment: this._terminalInfo.environmentVariables + }, command == null ? {} : { + command + }); + const performSpawn = () => { this._setUseTitleAsPath(cwd); - return getPtyServiceByNuclideUri(cwd).spawn( - cwd != null ? {...info, cwd: nuclideUri.getPath(cwd)} : info, - this, - ); + + return (0, _AtomServiceContainer().getPtyServiceByNuclideUri)(cwd).spawn(cwd != null ? Object.assign({}, info, { + cwd: _nuclideUri().default.getPath(cwd) + }) : info, this); }; - if (cwd == null || nuclideUri.isLocal(cwd)) { + + if (cwd == null || _nuclideUri().default.isLocal(cwd)) { return performSpawn(); } else { - const cwdHostname = nuclideUri.getHostname(cwd); - // Wait for the remote connection to be added before spawning. - const hostnameAddedPromise = observeAddedHostnames() - .filter(hostname => hostname === cwdHostname) - .take(1) - .toPromise(); + const cwdHostname = _nuclideUri().default.getHostname(cwd); // Wait for the remote connection to be added before spawning. + + + const hostnameAddedPromise = (0, _projects().observeAddedHostnames)().filter(hostname => hostname === cwdHostname).take(1).toPromise(); return hostnameAddedPromise.then(performSpawn); } } - _setUseTitleAsPath(cwd: ?NuclideUri): void { - getPtyServiceByNuclideUri(cwd) - .useTitleAsPath(this) - .then(value => (this._useTitleAsPath = value)); + _setUseTitleAsPath(cwd) { + (0, _AtomServiceContainer().getPtyServiceByNuclideUri)(cwd).useTitleAsPath(this).then(value => this._useTitleAsPath = value); } - _onPtyFulfill(pty: Pty): void { - invariant(this._pty == null); - this._pty = pty; + _onPtyFulfill(pty) { + if (!(this._pty == null)) { + throw new Error("Invariant violation: \"this._pty == null\""); + } - const now = performanceNow(); + this._pty = pty; + const now = (0, _performanceNow().default)(); this._focusStart = now; - track('nuclide-terminal.started', { + (0, _analytics().track)('nuclide-terminal.started', { pane: this._paneUri, uri: this._cwd, - startDelay: Math.round(now - this._startTime), + startDelay: Math.round(now - this._startTime) }); - this._subscriptions.add( - this.dispose.bind(this), - Observable.fromEvent(this._terminal, 'data').subscribe( - this._onInput.bind(this), - ), - Observable.fromEvent(this._terminal, 'title').subscribe(title => { - this._setTitle(title); - if (this._useTitleAsPath) { - this._setPath(title); - } - }), - Observable.interval(60 * 60 * 1000).subscribe(() => - track('nuclide-terminal.hourly', this._statistics()), - ), - Observable.fromEvent(this._terminal, 'focus').subscribe( - this._focused.bind(this), - ), - Observable.fromEvent(this._terminal, 'blur').subscribe( - this._blurred.bind(this), - ), - ); + this._subscriptions.add(this.dispose.bind(this), _RxMin.Observable.fromEvent(this._terminal, 'data').subscribe(this._onInput.bind(this)), _RxMin.Observable.fromEvent(this._terminal, 'title').subscribe(title => { + this._setTitle(title); + + if (this._useTitleAsPath) { + this._setPath(title); + } + }), _RxMin.Observable.interval(60 * 60 * 1000).subscribe(() => (0, _analytics().track)('nuclide-terminal.hourly', this._statistics())), _RxMin.Observable.fromEvent(this._terminal, 'focus').subscribe(this._focused.bind(this)), _RxMin.Observable.fromEvent(this._terminal, 'blur').subscribe(this._blurred.bind(this))); + this._syncFontAndFit(); - const performanceDisposable = measurePerformance(this._terminal); - // Stop observing performance if the renderer type is no longer auto. - this._subscriptions.add( - featureConfig - .observeAsStream(RENDERER_TYPE_CONFIG) - .filter(value => value !== 'auto') - .take(1) - .subscribe(() => performanceDisposable.dispose()), - ); + + const performanceDisposable = (0, _measurePerformance().default)(this._terminal); // Stop observing performance if the renderer type is no longer auto. + + this._subscriptions.add(_featureConfig().default.observeAsStream(_config().RENDERER_TYPE_CONFIG).filter(value => value !== 'auto').take(1).subscribe(() => performanceDisposable.dispose())); + this._subscriptions.add(performanceDisposable); } - _focused(): void { + _focused() { if (this._focusStart == null) { - this._focusStart = performanceNow(); + this._focusStart = (0, _performanceNow().default)(); } } - _blurred(): void { + _blurred() { const focusStart = this._focusStart; + if (focusStart != null) { this._focusStart = null; - this._focusDuration += performanceNow() - focusStart; + this._focusDuration += (0, _performanceNow().default)() - focusStart; } } - _statistics(): Object { - const now = performanceNow(); + _statistics() { + const now = (0, _performanceNow().default)(); const focusStart = this._focusStart; - const focusDuration = - this._focusDuration + (focusStart == null ? 0 : now - focusStart); - const {query} = url.parse(this._paneUri, true); - const id = query == null ? null : query.unique; + const focusDuration = this._focusDuration + (focusStart == null ? 0 : now - focusStart); + const { + query + } = _url.default.parse(this._paneUri, true); + + const id = query == null ? null : query.unique; return { id, uri: this._cwd, focusDuration: Math.round(focusDuration), duration: Math.round(now - this._startTime), bytesIn: this._bytesIn, - bytesOut: this._bytesOut, + bytesOut: this._bytesOut }; } - _onPtyFail(error: Error) { + _onPtyFail(error) { this._terminal.writeln('Error starting process:'); + for (const line of String(error).split('\n')) { this._terminal.writeln(line); } - track('nuclide-terminal.failed', { + + (0, _analytics().track)('nuclide-terminal.failed', { pane: this._paneUri, uri: this._cwd, - startDelay: Math.round(performanceNow() - this._startTime), - error: String(error), + startDelay: Math.round((0, _performanceNow().default)() - this._startTime), + error: String(error) }); - } - - // Since changing the font settings may resize the contents, we have to + } // Since changing the font settings may resize the contents, we have to // trigger a re-fit when updating font settings. - _syncFontAndFit = (): void => { - this._setTerminalOption('fontSize', getFontSize()); - this._setTerminalOption( - 'lineHeight', - featureConfig.get(LINE_HEIGHT_CONFIG), - ); - this._setTerminalOption( - 'fontFamily', - featureConfig.get(FONT_FAMILY_CONFIG), - ); - this._fitAndResize(); - }; - _setTerminalOption(optionName: string, value: mixed): void { + + _setTerminalOption(optionName, value) { if (this._terminal.getOption(optionName) !== value) { this._terminal.setOption(optionName, value); } } - _fitAndResize(): void { + _fitAndResize() { // Force character measure before 'fit' runs. this._terminal.resize(this._terminal.cols, this._terminal.rows); + this._terminal.fit(); + if (this._pty != null) { this._pty.resize(this._terminal.cols, this._terminal.rows); } - this._syncAtomTheme(); - // documented workaround for https://github.com/xtermjs/xterm.js/issues/291 + + this._syncAtomTheme(); // documented workaround for https://github.com/xtermjs/xterm.js/issues/291 // see https://github.com/Microsoft/vscode/commit/134cbec22f81d5558909040491286d72b547bee6 // $FlowIgnore: using unofficial _core interface defined in https://github.com/Microsoft/vscode/blob/master/src/typings/vscode-xterm.d.ts#L682-L706 + + this._terminal.emit('scroll', this._terminal._core.buffer.ydisp); } - _syncAtomTheme(): void { + _syncAtomTheme() { const div = this._div; + this._setTerminalOption('theme', getTerminalTheme(div)); } - _clear(): void { + _clear() { this._terminal.clear(); } - _onInput(data: string): void { + _onInput(data) { if (this._pty != null) { this._bytesIn += data.length; + this._pty.writeInput(data); } } - _setTitle(title: string) { + _setTitle(title) { this._title = title; + this._emitter.emit('did-change-title', title); } - _setPath(path: string) { + _setPath(path) { this._path = path; + this._emitter.emit('did-change-path', path); } - _addEscapePrefix(event: Object): void { + _addEscapePrefix(event) { if (typeof event.originalEvent === 'object') { - const keyEvent: Object = event.originalEvent; + const keyEvent = event.originalEvent; + if (typeof keyEvent.key === 'string') { this._onInput(`\x1B${keyEvent.key}`); } } } - _checkIfKeyBoundOrDivertToXTerm(event: Event): boolean { + _checkIfKeyBoundOrDivertToXTerm(event) { // Only allow input if we have somewhere to send it. if (this._pty == null) { return false; @@ -482,113 +508,93 @@ export class TerminalView implements PtyClient, TerminalInstance { const keystroke = atom.keymaps.keystrokeForKeyboardEvent(event); const bindings = atom.keymaps.findKeyBindings({ keystrokes: keystroke, - target: this._div, + target: this._div }); const preserved = this._preservedCommands; - if ( - preserved.has(ADD_ESCAPE_COMMAND) && - bindings.some(b => b.command === ADD_ESCAPE_COMMAND) - ) { + if (preserved.has(_config().ADD_ESCAPE_COMMAND) && bindings.some(b => b.command === _config().ADD_ESCAPE_COMMAND)) { // Intercept the add escape binding and send escape directly, then // divert to xterm (to handle keys like Backspace). this._onInput('\x1B'); + return true; } - const result = !bindings.some(b => preserved.has(b.command)); - // This facilitates debugging keystroke issues. You can set a breakpoint + const result = !bindings.some(b => preserved.has(b.command)); // This facilitates debugging keystroke issues. You can set a breakpoint // in the else block without stopping on modifier keys. - if ( - keystroke === 'alt' || - keystroke === 'shift' || - keystroke === 'ctrl' || - keystroke === 'cmd' - ) { + + if (keystroke === 'alt' || keystroke === 'shift' || keystroke === 'ctrl' || keystroke === 'cmd') { return result; } else { return result; } } - _createOutputSink(): Sink { + _createOutputSink() { let tmuxLines = 0; let lines = 0; - let firstChar: ?string = null; + let firstChar = null; let warned = false; - return removePrefixSink( - TMUX_CONTROLCONTROL_PREFIX, - patternCounterSink( - '\n%', - n => ++tmuxLines < 2, - patternCounterSink( - '\n', - n => ++lines < 2, - data => { - if (firstChar == null && data.length > 0) { - firstChar = data.charAt(0); - } - if ( - firstChar === '%' && - tmuxLines === lines && - tmuxLines >= 2 && - !warned - ) { - warned = true; - atom.notifications.addWarning('Tmux control protocol detected', { - detail: - 'The terminal output looks like you might be using tmux with -C or -CC. ' + - 'Nuclide terminal can be used with tmux, but not with the -C or -CC options. ' + - 'In your ~/.bashrc or similar, you can avoid invocations of tmux -C (or -CC) ' + - 'in Nuclide terminal by checking:\n' + - ' if [ "$TERM_PROGRAM" != nuclide ]; then\n' + - ' tmux -C ...\n' + - ' fi', - dismissable: true, - }); - } - this._terminal.write(data); - }, - ), - ), - ); - } - - _closeTab(): void { + return (0, _sink().removePrefixSink)(TMUX_CONTROLCONTROL_PREFIX, (0, _sink().patternCounterSink)('\n%', n => ++tmuxLines < 2, (0, _sink().patternCounterSink)('\n', n => ++lines < 2, data => { + if (firstChar == null && data.length > 0) { + firstChar = data.charAt(0); + } + + if (firstChar === '%' && tmuxLines === lines && tmuxLines >= 2 && !warned) { + warned = true; + atom.notifications.addWarning('Tmux control protocol detected', { + detail: 'The terminal output looks like you might be using tmux with -C or -CC. ' + 'Nuclide terminal can be used with tmux, but not with the -C or -CC options. ' + 'In your ~/.bashrc or similar, you can avoid invocations of tmux -C (or -CC) ' + 'in Nuclide terminal by checking:\n' + ' if [ "$TERM_PROGRAM" != nuclide ]; then\n' + ' tmux -C ...\n' + ' fi', + dismissable: true + }); + } + + this._terminal.write(data); + }))); + } + + _closeTab() { const pane = atom.workspace.paneForItem(this); + if (pane != null) { pane.destroyItem(this); } } - onOutput(data: string): void { + onOutput(data) { this._bytesOut += data.length; + this._processOutput(data); if (this._isFirstOutput) { this._isFirstOutput = false; + this._onInput(this._initialInput); } } - onExit(code: number, signal: number): void { + onExit(code, signal) { const terminal = this._terminal; - track('nuclide-terminal.exit', {...this._statistics(), code, signal}); + (0, _analytics().track)('nuclide-terminal.exit', Object.assign({}, this._statistics(), { + code, + signal + })); if (code === 0 && !this._terminalInfo.remainOnCleanExit) { this._closeTab(); + return; } terminal.writeln(''); terminal.writeln(''); const command = this._terminalInfo.command; - const process = - command == null ? 'Process' : `${command.file} ${command.args.join(' ')}`; + const process = command == null ? 'Process' : `${command.file} ${command.args.join(' ')}`; terminal.writeln(`${process} exited with error code '${code}'.`); + if (signal !== 0) { terminal.writeln(` killed by signal '${signal}'.`); } + terminal.writeln(''); this._disableTerminal(); @@ -596,223 +602,227 @@ export class TerminalView implements PtyClient, TerminalInstance { _disableTerminal() { this.dispose(); - this._terminal.blur(); - // Disable terminal's ability to capture input once in error state. - (this._div: any).focus = () => {}; - (this._div: any).blur = () => {}; + this._terminal.blur(); // Disable terminal's ability to capture input once in error state. + + + this._div.focus = () => {}; + + this._div.blur = () => {}; } - setProcessExitCallback(callback: ProcessExitCallback): void { + setProcessExitCallback(callback) { this._processExitCallback = callback; } - terminateProcess(): void { + terminateProcess() { if (this._pty != null) { this._disableTerminal(); + this._terminal.writeln(''); + this._terminal.writeln('Process terminated.'); + this._terminal.writeln(''); } } - copy(): TerminalView { - const paneUri = uriFromInfo(this._terminalInfo); + copy() { + const paneUri = (0, _nuclideTerminalUri().uriFromInfo)(this._terminalInfo); return new TerminalView(paneUri); - } - - // Remote connection is closing--note the window remains open to show error + } // Remote connection is closing--note the window remains open to show error // output if the process exit code was not 0. - dispose(): void { + + + dispose() { if (this._pty != null) { this._pty.dispose(); + this._pty = null; } this._processExitCallback(); + this._processExitCallback = () => {}; - } + } // Window is closing, so close everything. + - // Window is closing, so close everything. - destroy(): void { + destroy() { this._subscriptions.dispose(); } - getTitle(): string { + getTitle() { return this._title; } - getIconName(): IconName { - return (this._terminalInfo.icon: any); + getIconName() { + return this._terminalInfo.icon; } - getURI(): string { + getURI() { return this._paneUri; } - getDefaultLocation(): string { + getDefaultLocation() { return this._terminalInfo.defaultLocation; } - getElement(): any { + getElement() { return this._div; } - getPath(): ?NuclideUri { + getPath() { return this._path; } - onDidChangePath(callback: (v: ?string) => mixed): IDisposable { + onDidChangePath(callback) { return this.on('did-change-path', callback); } - onDidChangeTitle(callback: (v: ?string) => mixed): IDisposable { + onDidChangeTitle(callback) { return this.on('did-change-title', callback); } - on(name: string, callback: (v: any) => mixed): IDisposable { + on(name, callback) { return this._emitter.on(name, callback); } - serialize(): TerminalViewState { + serialize() { return { deserializer: 'TerminalView', - paneUri: this._paneUri, + paneUri: this._paneUri }; } + } -export function deserializeTerminalView( - state: TerminalViewState, -): TerminalView { +exports.TerminalView = TerminalView; + +function deserializeTerminalView(state) { // Convert from/to uri to generate a new unique id. - const info = infoFromUri(state.paneUri, true); - const paneUri = uriFromInfo(info); + const info = (0, _nuclideTerminalUri().infoFromUri)(state.paneUri, true); + const paneUri = (0, _nuclideTerminalUri().uriFromInfo)(info); return new TerminalView(paneUri); } -function registerLinkHandlers(terminal: Terminal, cwd: ?NuclideUri): void { - const diffPattern = toString( - featureConfig.get('atom-ide-console.diffUrlPattern'), - ); - const taskPattern = toString( - featureConfig.get('atom-ide-console.taskUrlPattern'), - ); - const bindings = [ - { - // Diff (e.g. 'D1234') with word boundary on either side. - regex: /\bD[1-9][0-9]{3,}\b/, - matchIndex: 0, - urlPattern: diffPattern, - }, - { - // Paste (e.g. 'P1234') with word boundary on either side. - regex: /\bP[1-9][0-9]{3,}\b/, - matchIndex: 0, - urlPattern: diffPattern, - }, - { - // Task (e.g. 't1234' or 'T1234') with word boundary on either side. - // Note the [tT] is not included in the resulting URL. - regex: /\b[tT]([1-9][0-9]{3,})\b/, - matchIndex: 1, - urlPattern: taskPattern, - }, - { - // Task (e.g. '#1234') preceded by beginning-of-line or whitespace and followed - // by word boundary. Unfortunately, since '#' is punctuation, the point before - // it is not normally a word boundary, so this has to be registered separately. - regex: /(^|\s)#([1-9][0-9]{3,})\b/, - matchIndex: 2, - urlPattern: taskPattern, - }, - { - // An absolute file path - regex: /(^|\s)(\/[^<>:"\\|?*[\]\s]+)/, - matchIndex: 2, - urlPattern: 'open-file-object://%s', - }, - ]; - - for (const {regex, matchIndex, urlPattern} of bindings) { - terminal.registerLinkMatcher( +function registerLinkHandlers(terminal, cwd) { + const diffPattern = toString(_featureConfig().default.get('atom-ide-console.diffUrlPattern')); + const taskPattern = toString(_featureConfig().default.get('atom-ide-console.taskUrlPattern')); + const bindings = [{ + // Diff (e.g. 'D1234') with word boundary on either side. + regex: /\bD[1-9][0-9]{3,}\b/, + matchIndex: 0, + urlPattern: diffPattern + }, { + // Paste (e.g. 'P1234') with word boundary on either side. + regex: /\bP[1-9][0-9]{3,}\b/, + matchIndex: 0, + urlPattern: diffPattern + }, { + // Task (e.g. 't1234' or 'T1234') with word boundary on either side. + // Note the [tT] is not included in the resulting URL. + regex: /\b[tT]([1-9][0-9]{3,})\b/, + matchIndex: 1, + urlPattern: taskPattern + }, { + // Task (e.g. '#1234') preceded by beginning-of-line or whitespace and followed + // by word boundary. Unfortunately, since '#' is punctuation, the point before + // it is not normally a word boundary, so this has to be registered separately. + regex: /(^|\s)#([1-9][0-9]{3,})\b/, + matchIndex: 2, + urlPattern: taskPattern + }, { + // An absolute file path + regex: /(^|\s)(\/[^<>:"\\|?*[\]\s]+)/, + matchIndex: 2, + urlPattern: 'open-file-object://%s' + }]; + + for (const _ref of bindings) { + const { regex, - (event, match) => { - const replacedUrl = urlPattern.replace('%s', match); - if (replacedUrl !== '') { - const commandClicked = - process.platform === 'win32' ? event.ctrlKey : event.metaKey; - if (commandClicked && tryOpenInAtom(replacedUrl, cwd)) { - return; - } - shell.openExternal(replacedUrl); + matchIndex, + urlPattern + } = _ref; + terminal.registerLinkMatcher(regex, (event, match) => { + const replacedUrl = urlPattern.replace('%s', match); + + if (replacedUrl !== '') { + const commandClicked = process.platform === 'win32' ? event.ctrlKey : event.metaKey; + + if (commandClicked && tryOpenInAtom(replacedUrl, cwd)) { + return; } - }, - {matchIndex}, - ); + + _electron.shell.openExternal(replacedUrl); + } + }, { + matchIndex + }); } } -function tryOpenInAtom(link: string, cwd: ?NuclideUri): boolean { - const parsed = url.parse(link); +function tryOpenInAtom(link, cwd) { + const parsed = _url.default.parse(link); if (parsed.protocol === 'open-file-object:') { let path = parsed.path; + if (path != null) { - if (cwd != null && nuclideUri.isRemote(cwd)) { - const terminalLocation = nuclideUri.parseRemoteUri(cwd); - path = nuclideUri.createRemoteUri(terminalLocation.hostname, path); + if (cwd != null && _nuclideUri().default.isRemote(cwd)) { + const terminalLocation = _nuclideUri().default.parseRemoteUri(cwd); + + path = _nuclideUri().default.createRemoteUri(terminalLocation.hostname, path); } - goToLocation(path); + (0, _goToLocation().goToLocation)(path); } + return true; } return false; } -function openLink(event: Event, link: string): void { - shell.openExternal(trimTrailingDot(link)); +function openLink(event, link) { + _electron.shell.openExternal(trimTrailingDot(link)); } -function trimTrailingDot(s: string): string { +function trimTrailingDot(s) { return s.endsWith('.') ? s.substring(0, s.length - 1) : s; } -function toString(value: mixed): string { +function toString(value) { return typeof value === 'string' ? value : ''; -} +} // As a precaution, we should not let any undisplayable or potentially unsafe characters through + -// As a precaution, we should not let any undisplayable or potentially unsafe characters through -export function getSafeInitialInput(initialInput: string): string { +function getSafeInitialInput(initialInput) { for (let i = 0; i < initialInput.length; i++) { - const code = initialInput.charCodeAt(i); - // ASCII codes under 32 and 127 are control characters (potentially dangerous) + const code = initialInput.charCodeAt(i); // ASCII codes under 32 and 127 are control characters (potentially dangerous) // ASCII codes 128-165 are extended ASCII characters that have uses in other languages + if (code < 32 || code === 127 || code > 165) { - atom.notifications.addWarning( - 'Initial input for terminal unable to be prefilled', - { - detail: `Potentially malicious characters were found in the prefill command: ${initialInput}`, - dismissable: true, - }, - ); + atom.notifications.addWarning('Initial input for terminal unable to be prefilled', { + detail: `Potentially malicious characters were found in the prefill command: ${initialInput}`, + dismissable: true + }); return ''; } } + return initialInput; } -function getTerminalColors(): {[$Keys]: string} { +function getTerminalColors() { const colorsMap = {}; - for (const color of Object.keys(COLOR_CONFIGS)) { - const configValue: ?(string | atom$Color) = (featureConfig.get( - COLOR_CONFIGS[color], - ): any); - // config value may be string when Atom deserializes the terminal package + + for (const color of Object.keys(_config().COLOR_CONFIGS)) { + const configValue = _featureConfig().default.get(_config().COLOR_CONFIGS[color]); // config value may be string when Atom deserializes the terminal package // on startup, and it may be undefined if this is the first time the package // is being deserialized after the config was added. + + if (configValue != null) { if (typeof configValue === 'string') { colorsMap[color] = configValue; @@ -821,18 +831,18 @@ function getTerminalColors(): {[$Keys]: string} { } } } + return colorsMap; } -function getTerminalTheme(div: HTMLDivElement): any { +function getTerminalTheme(div) { const style = window.getComputedStyle(div); const foreground = style.getPropertyValue('color'); - const background = style.getPropertyValue('background-color'); - // return type: https://git.io/vxooH - return { + const background = style.getPropertyValue('background-color'); // return type: https://git.io/vxooH + + return Object.assign({ foreground, background, - cursor: foreground, - ...getTerminalColors(), - }; -} + cursor: foreground + }, getTerminalColors()); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/types.js index 4a50d3e7..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-terminal/lib/types.js @@ -1,45 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict - * @format - */ - -import type {Command} from './pty-service/rpc-types'; - -// Fields that are legal from untrusted sources. -type TerminalInfoUntrustedFields = { - title?: string, - key?: string, - remainOnCleanExit?: boolean, - defaultLocation?: atom$PaneLocation | 'pane', - icon?: string, - trustToken?: string, -}; - -// Fields that are only legal from trusted sources. -type TerminalInfoTrustedFields = { - command?: Command, - cwd?: string, - environmentVariables?: Map, - preservedCommands?: Array, - initialInput?: string, -}; - -export type TerminalInfo = TerminalInfoUntrustedFields & - TerminalInfoTrustedFields; - -export interface TerminalInstance { - setProcessExitCallback(callback: () => mixed): void; - terminateProcess(): void; -} - -export type TerminalApi = { - open(info: TerminalInfo): Promise, - close(key: string): void, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/hyperclick/lib/Hyperclick.js b/modules/atom-ide-ui/pkg/hyperclick/lib/Hyperclick.js index 1dc9eb50..ba818f72 100644 --- a/modules/atom-ide-ui/pkg/hyperclick/lib/Hyperclick.js +++ b/modules/atom-ide-ui/pkg/hyperclick/lib/Hyperclick.js @@ -1,3 +1,62 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _HyperclickForTextEditor() { + const data = _interopRequireDefault(require("./HyperclickForTextEditor")); + + _HyperclickForTextEditor = function () { + return data; + }; + + return data; +} + +function _SuggestionList() { + const data = _interopRequireDefault(require("./SuggestionList")); + + _SuggestionList = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _range() { + const data = require("../../../../nuclide-commons-atom/range"); + + _range = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,116 +65,96 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {HyperclickSuggestion, HyperclickProvider} from './types'; - -import HyperclickForTextEditor from './HyperclickForTextEditor'; -import SuggestionList from './SuggestionList'; - -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {wordAtPosition} from 'nuclide-commons-atom/range'; - /** * Construct this object to enable Hyperclick in the Atom workspace. * Call `dispose` to disable the feature. */ -export default class Hyperclick { - _providers: ProviderRegistry; - _suggestionList: SuggestionList; - _hyperclickForTextEditors: Set; - _textEditorSubscription: IDisposable; - +class Hyperclick { constructor() { - this._providers = new ProviderRegistry(); - - this._suggestionList = new SuggestionList(); + this._providers = new (_ProviderRegistry().default)(); + this._suggestionList = new (_SuggestionList().default)(); this._hyperclickForTextEditors = new Set(); - this._textEditorSubscription = atom.workspace.observeTextEditors( - this.observeTextEditor.bind(this), - ); + this._textEditorSubscription = atom.workspace.observeTextEditors(this.observeTextEditor.bind(this)); } - observeTextEditor(textEditor: TextEditor): IDisposable { - const hyperclickForTextEditor = new HyperclickForTextEditor( - textEditor, - this, - ); + observeTextEditor(textEditor) { + const hyperclickForTextEditor = new (_HyperclickForTextEditor().default)(textEditor, this); + this._hyperclickForTextEditors.add(hyperclickForTextEditor); - const disposable = new UniversalDisposable(() => { + + const disposable = new (_UniversalDisposable().default)(() => { hyperclickForTextEditor.dispose(); + this._hyperclickForTextEditors.delete(hyperclickForTextEditor); }); - return new UniversalDisposable( - textEditor.onDidDestroy(() => disposable.dispose()), - disposable, - ); + return new (_UniversalDisposable().default)(textEditor.onDidDestroy(() => disposable.dispose()), disposable); } dispose() { this._suggestionList.hide(); + this._textEditorSubscription.dispose(); + this._hyperclickForTextEditors.forEach(hyperclick => hyperclick.dispose()); + this._hyperclickForTextEditors.clear(); } - addProvider( - provider: HyperclickProvider | Array, - ): IDisposable { + addProvider(provider) { if (Array.isArray(provider)) { - return new UniversalDisposable( - ...provider.map(p => this._providers.addProvider(p)), - ); + return new (_UniversalDisposable().default)(...provider.map(p => this._providers.addProvider(p))); } + return this._providers.addProvider(provider); } - /** * Returns the first suggestion from the consumed providers. */ - async getSuggestion( - textEditor: TextEditor, - position: atom$Point, - ): Promise { - for (const provider of this._providers.getAllProvidersForEditor( - textEditor, - )) { + + + async getSuggestion(textEditor, position) { + for (const provider of this._providers.getAllProvidersForEditor(textEditor)) { let result; + if (provider.getSuggestion) { // eslint-disable-next-line no-await-in-loop result = await provider.getSuggestion(textEditor, position); } else if (provider.getSuggestionForWord) { - const match = wordAtPosition(textEditor, position, provider.wordRegExp); + const match = (0, _range().wordAtPosition)(textEditor, position, provider.wordRegExp); + if (match == null) { continue; } - const {wordMatch, range} = match; - invariant(provider.getSuggestionForWord); - // eslint-disable-next-line no-await-in-loop - result = await provider.getSuggestionForWord( - textEditor, - wordMatch[0], - range, - ); + + const { + wordMatch, + range + } = match; + + if (!provider.getSuggestionForWord) { + throw new Error("Invariant violation: \"provider.getSuggestionForWord\""); + } // eslint-disable-next-line no-await-in-loop + + + result = await provider.getSuggestionForWord(textEditor, wordMatch[0], range); } else { - throw new Error( - 'Hyperclick must have either `getSuggestion` or `getSuggestionForWord`', - ); + throw new Error('Hyperclick must have either `getSuggestion` or `getSuggestionForWord`'); } + if (result != null) { return result; } } } - showSuggestionList( - textEditor: TextEditor, - suggestion: HyperclickSuggestion, - ): void { + showSuggestionList(textEditor, suggestion) { this._suggestionList.show(textEditor, suggestion); } + } + +exports.default = Hyperclick; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/hyperclick/lib/HyperclickForTextEditor.js b/modules/atom-ide-ui/pkg/hyperclick/lib/HyperclickForTextEditor.js index 26ace4b0..24b3f5b3 100644 --- a/modules/atom-ide-ui/pkg/hyperclick/lib/HyperclickForTextEditor.js +++ b/modules/atom-ide-ui/pkg/hyperclick/lib/HyperclickForTextEditor.js @@ -1,3 +1,76 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _atom = require("atom"); + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _range() { + const data = require("../../../../nuclide-commons-atom/range"); + + _range = function () { + return data; + }; + + return data; +} + +function _range2() { + const data = require("../../../../nuclide-commons/range"); + + _range2 = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _showTriggerConflictWarning() { + const data = _interopRequireDefault(require("./showTriggerConflictWarning")); + + _showTriggerConflictWarning = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,171 +79,131 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* global localStorage */ - -import type {HyperclickSuggestion} from './types'; -import type Hyperclick from './Hyperclick'; - -import {Point} from 'atom'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {wordAtPosition} from 'nuclide-commons-atom/range'; -import {isPositionInRange} from 'nuclide-commons/range'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import showTriggerConflictWarning from './showTriggerConflictWarning'; -import invariant from 'assert'; -import {Subject, Observable} from 'rxjs'; - -import {getLogger} from 'log4js'; - const WARN_ABOUT_TRIGGER_CONFLICT_KEY = 'hyperclick.warnAboutTriggerConflict'; const LOADING_DELAY = 250; - /** * Construct this object to enable Hyperclick in a text editor. * Call `dispose` to disable the feature. */ -export default class HyperclickForTextEditor { - _textEditor: atom$TextEditor; - _textEditorView: atom$TextEditorElement; - _hyperclick: Hyperclick; - _lastMouseEvent: ?MouseEvent; - _lastSuggestionAtMouse: ?HyperclickSuggestion; - _navigationMarkers: ?Array; - _lastWordRange: ?atom$Range; - _onMouseMove: (event: Event) => void; - _onMouseDown: (event: Event) => void; - _onKeyDown: (event: Event) => void; - _onKeyUp: (event: Event) => void; - _subscriptions: UniversalDisposable; - _isDestroyed: boolean; - _loadingTimer: ?number; - _triggerKeys: Set<'shiftKey' | 'ctrlKey' | 'altKey' | 'metaKey'>; +class HyperclickForTextEditor { // A central "event bus" for all fetch events. // TODO: Rx-ify all incoming events to avoid using a subject. - _fetchStream: Subject; // Stored for testing. - _suggestionStream: Observable; - - constructor(textEditor: atom$TextEditor, hyperclick: Hyperclick) { + constructor(textEditor, hyperclick) { this._textEditor = textEditor; this._textEditorView = atom.views.getView(textEditor); - this._hyperclick = hyperclick; + this._lastMouseEvent = null; // Cache the most recent suggestion so we can avoid unnecessary fetching. - this._lastMouseEvent = null; - // Cache the most recent suggestion so we can avoid unnecessary fetching. this._lastSuggestionAtMouse = null; this._navigationMarkers = null; - this._lastWordRange = null; - this._subscriptions = new UniversalDisposable(); - + this._subscriptions = new (_UniversalDisposable().default)(); this._onMouseMove = this._onMouseMove.bind(this); this._onMouseDown = this._onMouseDown.bind(this); + this._setupMouseListeners(); this._onKeyDown = this._onKeyDown.bind(this); + this._textEditorView.addEventListener('keydown', this._onKeyDown); + this._onKeyUp = this._onKeyUp.bind(this); + this._textEditorView.addEventListener('keyup', this._onKeyUp); - (this: any)._onContextMenu = this._onContextMenu.bind(this); + + this._onContextMenu = this._onContextMenu.bind(this); + this._textEditorView.addEventListener('contextmenu', this._onContextMenu); - this._subscriptions.add( - atom.commands.add(this._textEditorView, { - 'hyperclick:confirm-cursor': () => this._confirmSuggestionAtCursor(), - }), - ); + this._subscriptions.add(atom.commands.add(this._textEditorView, { + 'hyperclick:confirm-cursor': () => this._confirmSuggestionAtCursor() + })); this._isDestroyed = false; - this._fetchStream = new Subject(); + this._fetchStream = new _RxMin.Subject(); this._suggestionStream = this._observeSuggestions().share(); - this._subscriptions.add( - featureConfig.observe( - process.platform === 'darwin' - ? 'hyperclick.darwinTriggerKeys' - : process.platform === 'win32' - ? 'hyperclick.win32TriggerKeys' - : 'hyperclick.linuxTriggerKeys', - newValue_ => { - const newValue = ((newValue_: any): string); - // For all Flow knows, newValue.split could return any old strings - this._triggerKeys = (new Set(newValue.split(',')): Set); - }, - ), - this._suggestionStream.subscribe(suggestion => - this._updateSuggestion(suggestion), - ), - ); + this._subscriptions.add(_featureConfig().default.observe(process.platform === 'darwin' ? 'hyperclick.darwinTriggerKeys' : process.platform === 'win32' ? 'hyperclick.win32TriggerKeys' : 'hyperclick.linuxTriggerKeys', newValue_ => { + const newValue = newValue_; // For all Flow knows, newValue.split could return any old strings + + this._triggerKeys = new Set(newValue.split(',')); + }), this._suggestionStream.subscribe(suggestion => this._updateSuggestion(suggestion))); } - _setupMouseListeners(): void { + _setupMouseListeners() { const addMouseListeners = () => { - const {component} = this._textEditorView; - invariant(component); + const { + component + } = this._textEditorView; + + if (!component) { + throw new Error("Invariant violation: \"component\""); + } + const linesDomNode = component.refs.lineTiles; + if (linesDomNode == null) { return; } + linesDomNode.addEventListener('mousedown', this._onMouseDown); linesDomNode.addEventListener('mousemove', this._onMouseMove); - const removalDisposable = new UniversalDisposable(() => { + const removalDisposable = new (_UniversalDisposable().default)(() => { linesDomNode.removeEventListener('mousedown', this._onMouseDown); linesDomNode.removeEventListener('mousemove', this._onMouseMove); }); + this._subscriptions.add(removalDisposable); - this._subscriptions.add( - this._textEditorView.onDidDetach(() => removalDisposable.dispose()), - ); + + this._subscriptions.add(this._textEditorView.onDidDetach(() => removalDisposable.dispose())); }; - if ( - this._textEditorView.component && - this._textEditorView.parentNode != null - ) { + + if (this._textEditorView.component && this._textEditorView.parentNode != null) { addMouseListeners(); } else { - this._subscriptions.add( - this._textEditorView.onDidAttach(addMouseListeners), - ); + this._subscriptions.add(this._textEditorView.onDidAttach(addMouseListeners)); } } - _confirmSuggestion(suggestion: HyperclickSuggestion): void { + _confirmSuggestion(suggestion) { if (Array.isArray(suggestion.callback) && suggestion.callback.length > 0) { this._hyperclick.showSuggestionList(this._textEditor, suggestion); } else { - invariant(typeof suggestion.callback === 'function'); + if (!(typeof suggestion.callback === 'function')) { + throw new Error("Invariant violation: \"typeof suggestion.callback === 'function'\""); + } + suggestion.callback(); } } - _onContextMenu(event: Event): void { - const mouseEvent: MouseEvent = (event: any); - // If the key trigger happens to cause the context menu to show up, then + _onContextMenu(event) { + const mouseEvent = event; // If the key trigger happens to cause the context menu to show up, then // cancel it. By this point, it's too late to know if you're at a suggestion // position to be more fine grained. So if your trigger keys are "ctrl+cmd", // then you can't use that combination to bring up the context menu. + if (this._isHyperclickEvent(mouseEvent)) { event.stopPropagation(); } } - _onMouseMove(event: Event): void { - const mouseEvent: MouseEvent = (event: any); - - // We save the last `MouseEvent` so the user can trigger Hyperclick by + _onMouseMove(event) { + const mouseEvent = event; // We save the last `MouseEvent` so the user can trigger Hyperclick by // pressing the key without moving the mouse again. We only save the // relevant properties to prevent retaining a reference to the event. - this._lastMouseEvent = ({ + + this._lastMouseEvent = { clientX: mouseEvent.clientX, - clientY: mouseEvent.clientY, - }: any); + clientY: mouseEvent.clientY + }; if (this._isHyperclickEvent(mouseEvent)) { this._fetchSuggestion(mouseEvent); @@ -179,146 +212,134 @@ export default class HyperclickForTextEditor { } } - _onMouseDown(event: Event): void { - const mouseEvent: MouseEvent = (event: any); + _onMouseDown(event) { + const mouseEvent = event; + if (!this._isHyperclickEvent(mouseEvent)) { return; - } + } // If hyperclick and multicursor are using the same trigger, prevent multicursor. + - // If hyperclick and multicursor are using the same trigger, prevent multicursor. if (isMulticursorEvent(mouseEvent)) { mouseEvent.stopPropagation(); + if (localStorage.getItem(WARN_ABOUT_TRIGGER_CONFLICT_KEY) !== 'false') { localStorage.setItem(WARN_ABOUT_TRIGGER_CONFLICT_KEY, 'false'); - showTriggerConflictWarning(); + (0, _showTriggerConflictWarning().default)(); } } if (this._lastMouseEvent == null) { return; } - const lastPosition = this._getMousePositionAsBufferPosition( - this._lastMouseEvent, - ); + + const lastPosition = this._getMousePositionAsBufferPosition(this._lastMouseEvent); + if (lastPosition == null || !this._isInLastSuggestion(lastPosition)) { return; } if (this._lastSuggestionAtMouse) { - const lastSuggestionAtMouse = this._lastSuggestionAtMouse; - // Move the cursor to the click location to force a navigation-stack push. + const lastSuggestionAtMouse = this._lastSuggestionAtMouse; // Move the cursor to the click location to force a navigation-stack push. + this._textEditor.setCursorBufferPosition(lastPosition); - this._confirmSuggestion(lastSuggestionAtMouse); - // Prevent the event from adding another cursor. + this._confirmSuggestion(lastSuggestionAtMouse); // Prevent the event from adding another cursor. + + event.stopPropagation(); } this._clearSuggestion(); } - _onKeyDown(event: Event): void { - const mouseEvent: MouseEvent = (event: any); - // Show the suggestion at the last known mouse position. + _onKeyDown(event) { + const mouseEvent = event; // Show the suggestion at the last known mouse position. + if (this._isHyperclickEvent(mouseEvent) && this._lastMouseEvent != null) { this._fetchSuggestion(this._lastMouseEvent); } } - _onKeyUp(event: Event): void { - const mouseEvent: MouseEvent = (event: any); + _onKeyUp(event) { + const mouseEvent = event; + if (!this._isHyperclickEvent(mouseEvent)) { this._clearSuggestion(); } } - /** * Returns a `Promise` that's resolved when the latest suggestion's available. * (Exposed for testing.) */ - getSuggestionAtMouse(): Promise { + + + getSuggestionAtMouse() { return this._suggestionStream.take(1).toPromise(); } - _fetchSuggestion(mouseEvent: MouseEvent): void { + _fetchSuggestion(mouseEvent) { this._fetchStream.next(mouseEvent); } - _observeSuggestions(): Observable { - return this._fetchStream - .map(mouseEvent => { - if (mouseEvent == null) { - return null; - } - return this._getMousePositionAsBufferPosition(mouseEvent); - }) - .distinctUntilChanged((x, y) => { - if (x == null || y == null) { - return (x == null) === (y == null); - } - return x.compare(y) === 0; - }) - .filter(position => { - if (position == null) { - return true; - } - - // Don't fetch suggestions if the mouse is still in the same 'word', where - // 'word' is defined by the wordRegExp at the current position. - // - // If the last suggestion had multiple ranges, we have no choice but to - // fetch suggestions because the new word might be between those ranges. - // This should be ok because it will reuse that last suggestion until the - // mouse moves off of it. - if ( - (this._lastSuggestionAtMouse == null || - !Array.isArray(this._lastSuggestionAtMouse.range)) && - this._isInLastWordRange(position) - ) { - return false; - } - - // Don't refetch if we're already inside the previously emitted suggestion. - if (this._isInLastSuggestion(position)) { - return false; - } + _observeSuggestions() { + return this._fetchStream.map(mouseEvent => { + if (mouseEvent == null) { + return null; + } + + return this._getMousePositionAsBufferPosition(mouseEvent); + }).distinctUntilChanged((x, y) => { + if (x == null || y == null) { + return x == null === (y == null); + } + return x.compare(y) === 0; + }).filter(position => { + if (position == null) { return true; - }) - .do(position => { - if (position == null) { - this._lastWordRange = null; - } else { - const match = wordAtPosition(this._textEditor, position); - this._lastWordRange = match != null ? match.range : null; - } - }) - .switchMap(position => { - if (position == null) { - return Observable.of(null); - } - - return Observable.using( - () => this._showLoading(), - () => - Observable.defer(() => - this._hyperclick.getSuggestion(this._textEditor, position), - ) - .startWith(null) // Clear the previous suggestion immediately. - .catch(e => { - getLogger('hyperclick').error( - 'Error getting Hyperclick suggestion:', - e, - ); - return Observable.of(null); - }), - ); - }) - .distinctUntilChanged(); + } // Don't fetch suggestions if the mouse is still in the same 'word', where + // 'word' is defined by the wordRegExp at the current position. + // + // If the last suggestion had multiple ranges, we have no choice but to + // fetch suggestions because the new word might be between those ranges. + // This should be ok because it will reuse that last suggestion until the + // mouse moves off of it. + + + if ((this._lastSuggestionAtMouse == null || !Array.isArray(this._lastSuggestionAtMouse.range)) && this._isInLastWordRange(position)) { + return false; + } // Don't refetch if we're already inside the previously emitted suggestion. + + + if (this._isInLastSuggestion(position)) { + return false; + } + + return true; + }).do(position => { + if (position == null) { + this._lastWordRange = null; + } else { + const match = (0, _range().wordAtPosition)(this._textEditor, position); + this._lastWordRange = match != null ? match.range : null; + } + }).switchMap(position => { + if (position == null) { + return _RxMin.Observable.of(null); + } + + return _RxMin.Observable.using(() => this._showLoading(), () => _RxMin.Observable.defer(() => this._hyperclick.getSuggestion(this._textEditor, position)).startWith(null) // Clear the previous suggestion immediately. + .catch(e => { + (0, _log4js().getLogger)('hyperclick').error('Error getting Hyperclick suggestion:', e); + return _RxMin.Observable.of(null); + })); + }).distinctUntilChanged(); } - _updateSuggestion(suggestion: ?HyperclickSuggestion): void { + _updateSuggestion(suggestion) { this._lastSuggestionAtMouse = suggestion; + if (suggestion != null) { // Add the hyperclick markers if there's a new suggestion and it's under the mouse. this._updateNavigationMarkers(suggestion.range); @@ -328,17 +349,24 @@ export default class HyperclickForTextEditor { } } - _getMousePositionAsBufferPosition(mouseEvent: MouseEvent): ?atom$Point { - const {component} = this._textEditorView; - invariant(component); + _getMousePositionAsBufferPosition(mouseEvent) { + const { + component + } = this._textEditorView; + + if (!component) { + throw new Error("Invariant violation: \"component\""); + } + const screenPosition = component.screenPositionForMouseEvent(mouseEvent); - const screenLine = this._textEditor.lineTextForScreenRow( - screenPosition.row, - ); + + const screenLine = this._textEditor.lineTextForScreenRow(screenPosition.row); + if (screenPosition.column >= screenLine.length) { // We shouldn't try to fetch suggestions for trailing whitespace. return null; } + try { return this._textEditor.bufferPositionForScreenPosition(screenPosition); } catch (error) { @@ -346,130 +374,140 @@ export default class HyperclickForTextEditor { // When navigating Atom workspace with `CMD/CTRL` down, // it triggers TextEditorElement's `mousemove` with invalid screen position. // This falls back to returning the start of the editor. - getLogger('hyperclick').error( - 'Hyperclick: Error getting buffer position for screen position:', - error, - ); - return new Point(0, 0); + (0, _log4js().getLogger)('hyperclick').error('Hyperclick: Error getting buffer position for screen position:', error); + return new _atom.Point(0, 0); } } - _isInLastSuggestion(position: atom$Point): boolean { + _isInLastSuggestion(position) { if (!this._lastSuggestionAtMouse) { return false; } - const {range} = this._lastSuggestionAtMouse; - return isPositionInRange(position, range); + + const { + range + } = this._lastSuggestionAtMouse; + return (0, _range2().isPositionInRange)(position, range); } - _isInLastWordRange(position: atom$Point): boolean { + _isInLastWordRange(position) { const lastWordRange = this._lastWordRange; + if (lastWordRange == null) { return false; } - return isPositionInRange(position, lastWordRange); + + return (0, _range2().isPositionInRange)(position, lastWordRange); } - _clearSuggestion(): void { + _clearSuggestion() { this._fetchStream.next(null); } - async _confirmSuggestionAtCursor(): Promise { - const suggestion = await this._hyperclick.getSuggestion( - this._textEditor, - this._textEditor.getCursorBufferPosition(), - ); + async _confirmSuggestionAtCursor() { + const suggestion = await this._hyperclick.getSuggestion(this._textEditor, this._textEditor.getCursorBufferPosition()); + if (suggestion) { this._confirmSuggestion(suggestion); } } - /** * Add markers for the given range(s), or clears them if `ranges` is null. */ - _updateNavigationMarkers(range: ?(atom$Range | Array)): void { + + + _updateNavigationMarkers(range) { if (this._navigationMarkers) { this._navigationMarkers.forEach(marker => marker.destroy()); + this._navigationMarkers = null; - } + } // Only change the cursor to a pointer if there is a suggestion ready. + - // Only change the cursor to a pointer if there is a suggestion ready. if (range == null) { this._textEditorView.classList.remove('hyperclick'); + return; } this._textEditorView.classList.add('hyperclick'); + const ranges = Array.isArray(range) ? range : [range]; this._navigationMarkers = ranges.map(markerRange => { const marker = this._textEditor.markBufferRange(markerRange, { - invalidate: 'never', + invalidate: 'never' }); + this._textEditor.decorateMarker(marker, { type: 'highlight', - class: 'hyperclick', + class: 'hyperclick' }); + return marker; }); } - /** * Returns whether an event should be handled by hyperclick or not. */ - _isHyperclickEvent(event: SyntheticKeyboardEvent<> | MouseEvent): boolean { - return ( - event.shiftKey === this._triggerKeys.has('shiftKey') && - event.ctrlKey === this._triggerKeys.has('ctrlKey') && - event.altKey === this._triggerKeys.has('altKey') && - event.metaKey === this._triggerKeys.has('metaKey') - ); - } - // A subscription that encapsulates the cursor loading spinner. + + _isHyperclickEvent(event) { + return event.shiftKey === this._triggerKeys.has('shiftKey') && event.ctrlKey === this._triggerKeys.has('ctrlKey') && event.altKey === this._triggerKeys.has('altKey') && event.metaKey === this._triggerKeys.has('metaKey'); + } // A subscription that encapsulates the cursor loading spinner. // There should only be one subscription active at a given time! - _showLoading(): rxjs$Subscription { - return Observable.timer(LOADING_DELAY) - .switchMap(() => - Observable.create(() => { - this._textEditorView.classList.add('hyperclick-loading'); - return () => { - this._textEditorView.classList.remove('hyperclick-loading'); - }; - }), - ) - .subscribe(); + + + _showLoading() { + return _RxMin.Observable.timer(LOADING_DELAY).switchMap(() => _RxMin.Observable.create(() => { + this._textEditorView.classList.add('hyperclick-loading'); + + return () => { + this._textEditorView.classList.remove('hyperclick-loading'); + }; + })).subscribe(); } dispose() { this._isDestroyed = true; + this._clearSuggestion(); + this._textEditorView.removeEventListener('keydown', this._onKeyDown); + this._textEditorView.removeEventListener('keyup', this._onKeyUp); - this._textEditorView.removeEventListener( - 'contextmenu', - this._onContextMenu, - ); + + this._textEditorView.removeEventListener('contextmenu', this._onContextMenu); + this._subscriptions.dispose(); } -} +} /** * Determine whether the specified event will trigger Atom's multiple cursors. This is based on (and * must be the same as!) [Atom's * logic](https://github.com/atom/atom/blob/v1.14.2/src/text-editor-component.coffee#L527). */ -function isMulticursorEvent(event: MouseEvent): boolean { - const {platform} = process; - const isLeftButton = - event.button === 0 || (event.button === 1 && platform === 'linux'); - const {metaKey, ctrlKey} = event; + + +exports.default = HyperclickForTextEditor; + +function isMulticursorEvent(event) { + const { + platform + } = process; + const isLeftButton = event.button === 0 || event.button === 1 && platform === 'linux'; + const { + metaKey, + ctrlKey + } = event; if (!isLeftButton) { return false; } + if (ctrlKey && platform === 'darwin') { return false; } - return metaKey || (ctrlKey && platform !== 'darwin'); -} + return metaKey || ctrlKey && platform !== 'darwin'; +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/hyperclick/lib/SuggestionList.js b/modules/atom-ide-ui/pkg/hyperclick/lib/SuggestionList.js index b465153f..65f302dc 100644 --- a/modules/atom-ide-ui/pkg/hyperclick/lib/SuggestionList.js +++ b/modules/atom-ide-ui/pkg/hyperclick/lib/SuggestionList.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _SuggestionListElement() { + const data = _interopRequireDefault(require("./SuggestionListElement")); + + _SuggestionListElement = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,71 +25,69 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {HyperclickSuggestion} from './types'; - -import invariant from 'assert'; -import SuggestionListElement from './SuggestionListElement'; - -export default class SuggestionList { - _element: ?SuggestionListElement; - _textEditor: atom$TextEditor; - _suggestion: HyperclickSuggestion; - _suggestionMarker: ?atom$Marker; - _overlayDecoration: ?atom$Decoration; - - getElement(): SuggestionListElement { +class SuggestionList { + getElement() { if (this._element == null) { - this._element = new SuggestionListElement().initialize(this); + this._element = new (_SuggestionListElement().default)().initialize(this); } + return this._element; } - show(textEditor: atom$TextEditor, suggestion: HyperclickSuggestion): void { + show(textEditor, suggestion) { if (!textEditor || !suggestion) { return; } this._textEditor = textEditor; this._suggestion = suggestion; - this.hide(); + const { + range + } = suggestion; + + if (!range) { + throw new Error("Invariant violation: \"range\""); + } - const {range} = suggestion; - invariant(range); - const {start: position} = Array.isArray(range) ? range[0] : range; + const { + start: position + } = Array.isArray(range) ? range[0] : range; this._suggestionMarker = textEditor.markBufferPosition(position); + if (this._suggestionMarker) { - this._overlayDecoration = textEditor.decorateMarker( - this._suggestionMarker, - { - type: 'overlay', - item: this, - }, - ); + this._overlayDecoration = textEditor.decorateMarker(this._suggestionMarker, { + type: 'overlay', + item: this + }); } } hide() { // $FlowFixMe method override not working with `this`. atom.views.getView(this).dispose(); + if (this._suggestionMarker) { this._suggestionMarker.destroy(); } else if (this._overlayDecoration) { this._overlayDecoration.destroy(); } + this._suggestionMarker = undefined; this._overlayDecoration = undefined; } - getTextEditor(): ?TextEditor { + getTextEditor() { return this._textEditor; } - getSuggestion(): ?HyperclickSuggestion { + getSuggestion() { return this._suggestion; } + } + +exports.default = SuggestionList; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/hyperclick/lib/SuggestionListElement.js b/modules/atom-ide-ui/pkg/hyperclick/lib/SuggestionListElement.js index 7e575fc8..0260fed4 100644 --- a/modules/atom-ide-ui/pkg/hyperclick/lib/SuggestionListElement.js +++ b/modules/atom-ide-ui/pkg/hyperclick/lib/SuggestionListElement.js @@ -1,3 +1,38 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _scrollIntoView() { + const data = require("../../../../nuclide-commons-ui/scrollIntoView"); + + _scrollIntoView = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,38 +41,30 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* global HTMLElement */ -import type SuggestionListType from './SuggestionList'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import invariant from 'assert'; -import {scrollIntoViewIfNeeded} from 'nuclide-commons-ui/scrollIntoView'; - /** * We need to create this custom HTML element so we can hook into the view * registry. The overlay decoration only works through the view registry. */ class SuggestionListElement extends HTMLElement { - _model: SuggestionListType; - - initialize(model: SuggestionListType) { + initialize(model) { this._model = model; return this; } - attachedCallback(): mixed { - ReactDOM.render(, this); + attachedCallback() { + _reactDom.default.render(React.createElement(SuggestionList, { + suggestionList: this._model + }), this); } - detachedCallback(): mixed { - ReactDOM.unmountComponentAtNode(this); + detachedCallback() { + _reactDom.default.unmountComponentAtNode(this); } dispose() { @@ -45,125 +72,121 @@ class SuggestionListElement extends HTMLElement { this.parentNode.removeChild(this); } } -} - -type Props = { - suggestionList: SuggestionListType, -}; -type State = { - selectedIndex: number, -}; - -class SuggestionList extends React.Component { - _items: Array<{rightLabel?: string, title: string, callback: () => mixed}>; - _textEditor: ?atom$TextEditor; - _subscriptions: UniversalDisposable; - _boundConfirm: () => void; - - _scroller: ?HTMLElement; - _selectionList: ?HTMLElement; +} - constructor(props: Props) { +class SuggestionList extends React.Component { + constructor(props) { super(props); this.state = { - selectedIndex: 0, + selectedIndex: 0 }; - this._subscriptions = new UniversalDisposable(); + this._subscriptions = new (_UniversalDisposable().default)(); this._boundConfirm = this._confirm.bind(this); } UNSAFE_componentWillMount() { - const {suggestionList} = this.props; - const suggestion = suggestionList.getSuggestion(); - // TODO(nmote): This is assuming `suggestion.callback` is always an Array, which is not true + const { + suggestionList + } = this.props; + const suggestion = suggestionList.getSuggestion(); // TODO(nmote): This is assuming `suggestion.callback` is always an Array, which is not true // according to hyperclick/lib/types. It can also be a function. - invariant(suggestion != null && Array.isArray(suggestion.callback)); + + if (!(suggestion != null && Array.isArray(suggestion.callback))) { + throw new Error("Invariant violation: \"suggestion != null && Array.isArray(suggestion.callback)\""); + } + this._items = suggestion.callback; this._textEditor = suggestionList.getTextEditor(); } componentDidMount() { const textEditor = this._textEditor; - invariant(textEditor); + + if (!textEditor) { + throw new Error("Invariant violation: \"textEditor\""); + } + const textEditorView = atom.views.getView(textEditor); + const boundClose = this._close.bind(this); - this._subscriptions.add( - atom.commands.add(textEditorView, { - 'core:move-up': this._moveSelectionUp.bind(this), - 'core:move-down': this._moveSelectionDown.bind(this), - 'core:move-to-top': this._moveSelectionToTop.bind(this), - 'core:move-to-bottom': this._moveSelectionToBottom.bind(this), - 'core:cancel': boundClose, - 'editor:newline': this._boundConfirm, - }), - ); + + this._subscriptions.add(atom.commands.add(textEditorView, { + 'core:move-up': this._moveSelectionUp.bind(this), + 'core:move-down': this._moveSelectionDown.bind(this), + 'core:move-to-top': this._moveSelectionToTop.bind(this), + 'core:move-to-bottom': this._moveSelectionToBottom.bind(this), + 'core:cancel': boundClose, + 'editor:newline': this._boundConfirm + })); this._subscriptions.add(textEditor.getBuffer().onDidChangeText(boundClose)); - this._subscriptions.add(textEditor.onDidChangeCursorPosition(boundClose)); - // Prevent scrolling the editor when scrolling the suggestion list. + this._subscriptions.add(textEditor.onDidChangeCursorPosition(boundClose)); // Prevent scrolling the editor when scrolling the suggestion list. + + const stopPropagation = event => event.stopPropagation(); + const scroller = this._scroller; - invariant(scroller != null); + + if (!(scroller != null)) { + throw new Error("Invariant violation: \"scroller != null\""); + } + scroller.addEventListener('mousewheel', stopPropagation); - this._subscriptions.add( - new UniversalDisposable(() => { - scroller.removeEventListener('mousewheel', stopPropagation); - }), - ); - const keydown = (event: KeyboardEvent) => { + this._subscriptions.add(new (_UniversalDisposable().default)(() => { + scroller.removeEventListener('mousewheel', stopPropagation); + })); + + const keydown = event => { // If the user presses the enter key, confirm the selection. if (event.keyCode === 13) { event.stopImmediatePropagation(); + this._confirm(); } }; + textEditorView.addEventListener('keydown', keydown); - this._subscriptions.add( - new UniversalDisposable(() => { - textEditorView.removeEventListener('keydown', keydown); - }), - ); + + this._subscriptions.add(new (_UniversalDisposable().default)(() => { + textEditorView.removeEventListener('keydown', keydown); + })); } render() { const itemComponents = this._items.map((item, index) => { let className = 'hyperclick-result-item'; + if (index === this.state.selectedIndex) { className += ' selected'; } - return ( -
  • - {item.title} - {item.rightLabel} -
  • - ); + + return React.createElement("li", { + className: className, + key: index, + onMouseDown: this._boundConfirm, + onMouseEnter: this._setSelectedIndex.bind(this, index) + }, item.title, React.createElement("span", { + className: "right-label" + }, item.rightLabel)); }); - return ( -
    { - this._scroller = el; - }}> -
      { - this._selectionList = el; - }}> - {itemComponents} -
    -
    - ); - } - - componentDidUpdate(prevProps: Object, prevState: Object) { + return React.createElement("div", { + className: "popover-list select-list hyperclick-suggestion-list-scroller", + ref: el => { + this._scroller = el; + } + }, React.createElement("ol", { + className: "list-group", + ref: el => { + this._selectionList = el; + } + }, itemComponents)); + } + + componentDidUpdate(prevProps, prevState) { if (prevState.selectedIndex !== this.state.selectedIndex) { this._updateScrollPosition(); } @@ -175,7 +198,9 @@ class SuggestionList extends React.Component { _confirm(event) { this._items[this.state.selectedIndex].callback(); + this._close(); + if (event) { event.stopImmediatePropagation(); } @@ -185,9 +210,9 @@ class SuggestionList extends React.Component { this.props.suggestionList.hide(); } - _setSelectedIndex(index: number) { + _setSelectedIndex(index) { this.setState({ - selectedIndex: index, + selectedIndex: index }); } @@ -196,10 +221,13 @@ class SuggestionList extends React.Component { // TODO: (wbinnssmith) T30771435 this setState depends on current state // and should use an updater function rather than an object // eslint-disable-next-line react/no-access-state-in-setstate - this.setState({selectedIndex: this.state.selectedIndex + 1}); + this.setState({ + selectedIndex: this.state.selectedIndex + 1 + }); } else { this._moveSelectionToTop(); } + if (event) { event.stopImmediatePropagation(); } @@ -210,24 +238,33 @@ class SuggestionList extends React.Component { // TODO: (wbinnssmith) T30771435 this setState depends on current state // and should use an updater function rather than an object // eslint-disable-next-line react/no-access-state-in-setstate - this.setState({selectedIndex: this.state.selectedIndex - 1}); + this.setState({ + selectedIndex: this.state.selectedIndex - 1 + }); } else { this._moveSelectionToBottom(); } + if (event) { event.stopImmediatePropagation(); } } _moveSelectionToBottom(event) { - this.setState({selectedIndex: Math.max(this._items.length - 1, 0)}); + this.setState({ + selectedIndex: Math.max(this._items.length - 1, 0) + }); + if (event) { event.stopImmediatePropagation(); } } _moveSelectionToTop(event) { - this.setState({selectedIndex: 0}); + this.setState({ + selectedIndex: 0 + }); + if (event) { event.stopImmediatePropagation(); } @@ -235,12 +272,19 @@ class SuggestionList extends React.Component { _updateScrollPosition() { const listNode = this._selectionList; - invariant(listNode != null); + + if (!(listNode != null)) { + throw new Error("Invariant violation: \"listNode != null\""); + } + const selectedNode = listNode.getElementsByClassName('selected')[0]; - scrollIntoViewIfNeeded(selectedNode, false); + (0, _scrollIntoView().scrollIntoViewIfNeeded)(selectedNode, false); } + } -export default document.registerElement('hyperclick-suggestion-list', { - prototype: SuggestionListElement.prototype, +var _default = document.registerElement('hyperclick-suggestion-list', { + prototype: SuggestionListElement.prototype }); + +exports.default = _default; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/hyperclick/lib/main.js b/modules/atom-ide-ui/pkg/hyperclick/lib/main.js index f8278c26..703ebcf3 100644 --- a/modules/atom-ide-ui/pkg/hyperclick/lib/main.js +++ b/modules/atom-ide-ui/pkg/hyperclick/lib/main.js @@ -1,3 +1,37 @@ +"use strict"; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _createPackage() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _Hyperclick() { + const data = _interopRequireDefault(require("./Hyperclick")); + + _Hyperclick = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,65 +40,51 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {HyperclickProvider} from './types'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import Hyperclick from './Hyperclick'; - // Legacy providers have a default priority of 0. -function fixLegacyProvider(provider: HyperclickProvider) { +function fixLegacyProvider(provider) { if (provider.priority == null) { provider.priority = 0; } + return provider; } class Activation { - _hyperclick: Hyperclick; - _disposables: UniversalDisposable; - constructor() { - this._hyperclick = new Hyperclick(); - this._disposables = new UniversalDisposable(this._hyperclick); + this._hyperclick = new (_Hyperclick().default)(); + this._disposables = new (_UniversalDisposable().default)(this._hyperclick); } dispose() { this._disposables.dispose(); - } + } // Legacy providers have a default priority of 0. + - // Legacy providers have a default priority of 0. - addLegacyProvider( - provider: HyperclickProvider | Array, - ): IDisposable { - return this.addProvider( - Array.isArray(provider) - ? provider.map(fixLegacyProvider) - : fixLegacyProvider(provider), - ); + addLegacyProvider(provider) { + return this.addProvider(Array.isArray(provider) ? provider.map(fixLegacyProvider) : fixLegacyProvider(provider)); } - addProvider( - provider: HyperclickProvider | Array, - ): IDisposable { + addProvider(provider) { const disposable = this._hyperclick.addProvider(provider); + this._disposables.add(disposable); + return disposable; } - /** * A TextEditor whose creation is announced via atom.workspace.observeTextEditors() will be * observed by default by hyperclick. However, if a TextEditor is created via some other means, * (such as a building block for a piece of UI), then it must be observed explicitly. */ - observeTextEditor(): (textEditor: atom$TextEditor) => IDisposable { - return (textEditor: atom$TextEditor) => - this._hyperclick.observeTextEditor(textEditor); + + + observeTextEditor() { + return textEditor => this._hyperclick.observeTextEditor(textEditor); } + } -createPackage(module.exports, Activation); +(0, _createPackage().default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/hyperclick/lib/showTriggerConflictWarning.js b/modules/atom-ide-ui/pkg/hyperclick/lib/showTriggerConflictWarning.js index 56688038..7eba3e7f 100644 --- a/modules/atom-ide-ui/pkg/hyperclick/lib/showTriggerConflictWarning.js +++ b/modules/atom-ide-ui/pkg/hyperclick/lib/showTriggerConflictWarning.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = showTriggerConflictWarning; + +function _featureConfig() { + const data = _interopRequireDefault(require("../../../../nuclide-commons-atom/feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,47 +25,47 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +function showTriggerConflictWarning() { + const triggerKeys = _featureConfig().default.get(`hyperclick.${process.platform}TriggerKeys`); -import invariant from 'assert'; -import featureConfig from 'nuclide-commons-atom/feature-config'; + if (!(typeof triggerKeys === 'string')) { + throw new Error("Invariant violation: \"typeof triggerKeys === 'string'\""); + } -export default function showTriggerConflictWarning(): atom$Notification { - const triggerKeys = featureConfig.get( - `hyperclick.${process.platform}TriggerKeys`, - ); - invariant(typeof triggerKeys === 'string'); const triggerKeyDescription = getTriggerDescription(triggerKeys); - const {platform} = process; + const { + platform + } = process; const commandOrMeta = platform === 'darwin' ? 'command' : 'meta'; const optionOrAlt = platform === 'darwin' ? 'option' : 'alt'; - const alternative = - triggerKeys === 'altKey,metaKey' - ? commandOrMeta - : `${commandOrMeta} + ${optionOrAlt}`; - return atom.notifications.addInfo( - `Hyperclick (jump to definition) is using ${triggerKeyDescription}`, - { - description: - `If you want to use ${triggerKeyDescription} for multiple cursors instead,` + - ' change the Hyperclick "Trigger Keys" setting.

    ' + - `(You can still use ${alternative} + click for multiple cursors.)`, - dismissable: true, - }, - ); + const alternative = triggerKeys === 'altKey,metaKey' ? commandOrMeta : `${commandOrMeta} + ${optionOrAlt}`; + return atom.notifications.addInfo(`Hyperclick (jump to definition) is using ${triggerKeyDescription}`, { + description: `If you want to use ${triggerKeyDescription} for multiple cursors instead,` + ' change the Hyperclick "Trigger Keys" setting.

    ' + `(You can still use ${alternative} + click for multiple cursors.)`, + dismissable: true + }); } -function getTriggerDescription(trigger: string): string { - const schema = featureConfig.getSchema( - `hyperclick.${process.platform}TriggerKeys`, - ); - invariant(schema != null && schema.enum != null); +function getTriggerDescription(trigger) { + const schema = _featureConfig().default.getSchema(`hyperclick.${process.platform}TriggerKeys`); + + if (!(schema != null && schema.enum != null)) { + throw new Error("Invariant violation: \"schema != null && schema.enum != null\""); + } + const match = schema.enum.find(option => { - invariant(option != null && typeof option.value === 'string'); + if (!(option != null && typeof option.value === 'string')) { + throw new Error("Invariant violation: \"option != null && typeof option.value === 'string'\""); + } + return option.value === trigger; }); - invariant(match != null && typeof match.description === 'string'); + + if (!(match != null && typeof match.description === 'string')) { + throw new Error("Invariant violation: \"match != null && typeof match.description === 'string'\""); + } + return match.description; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/hyperclick/lib/types.js b/modules/atom-ide-ui/pkg/hyperclick/lib/types.js index 9fce523c..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/hyperclick/lib/types.js +++ b/modules/atom-ide-ui/pkg/hyperclick/lib/types.js @@ -1,49 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict - * @format - */ - -export type HyperclickProvider = { - // Use this to provide a suggestion for single-word matches. - // Optionally set `wordRegExp` to adjust word-matching. - getSuggestionForWord?: ( - textEditor: atom$TextEditor, - text: string, - range: atom$Range, - ) => Promise, - - wordRegExp?: RegExp, - - // Use this to provide a suggestion if it can have non-contiguous ranges. - // A primary use-case for this is Objective-C methods. - getSuggestion?: ( - textEditor: atom$TextEditor, - position: atom$Point, - ) => Promise, - - // Must be unique. Used for analytics. - providerName?: string, - - // The higher this is, the more precedence the provider gets. - priority: number, - - // Optionally, limit the set of grammar scopes the provider should apply to. - grammarScopes?: Array, -}; - -export type HyperclickSuggestion = { - // The range(s) to underline to provide as a visual cue for clicking. - range: atom$Range | Array, - - // The function to call when the underlined text is clicked. - callback: - | (() => mixed) - | Array<{rightLabel?: string, title: string, callback: () => mixed}>, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/hyperclick/spec/Hyperclick-spec.js b/modules/atom-ide-ui/pkg/hyperclick/spec/Hyperclick-spec.js index 01e025c7..c850e7dd 100644 --- a/modules/atom-ide-ui/pkg/hyperclick/spec/Hyperclick-spec.js +++ b/modules/atom-ide-ui/pkg/hyperclick/spec/Hyperclick-spec.js @@ -1,3 +1,59 @@ +"use strict"; + +var _atom = require("atom"); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _testHelpers() { + const data = require("../../../../nuclide-commons-atom/test-helpers"); + + _testHelpers = function () { + return data; + }; + + return data; +} + +function _Hyperclick() { + const data = _interopRequireDefault(require("../lib/Hyperclick")); + + _Hyperclick = function () { + return data; + }; + + return data; +} + +function _showTriggerConflictWarning() { + const data = _interopRequireDefault(require("../lib/showTriggerConflictWarning")); + + _showTriggerConflictWarning = function () { + return data; + }; + + return data; +} + +function _package() { + const data = require("../package.json"); + + _package = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,97 +62,80 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {HyperclickProvider} from '../lib/types'; -import type HyperclickForTextEditor from '../lib/HyperclickForTextEditor'; - -import {Point, Range} from 'atom'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {jasmineAttachWorkspace} from 'nuclide-commons-atom/test-helpers'; -import Hyperclick from '../lib/Hyperclick'; -import showTriggerConflictWarning from '../lib/showTriggerConflictWarning'; -import invariant from 'assert'; -import {atomConfig} from '../package.json'; - beforeEach(() => { - Object.keys(atomConfig).forEach(key => { - const config = atomConfig[key]; - atom.config.setSchema(`hyperclick.${key}`, { - ...config, + Object.keys(_package().atomConfig).forEach(key => { + const config = _package().atomConfig[key]; + + atom.config.setSchema(`hyperclick.${key}`, Object.assign({}, config, { // To make testing easier, use metaKey on all platforms. - default: 'metaKey', - }); + default: 'metaKey' + })); }); }); - describe('Hyperclick', () => { - let textEditor: atom$TextEditor = (null: any); - let textEditorView: atom$TextEditorElement = (null: any); - let hyperclick: Hyperclick = (null: any); - let hyperclickForTextEditor: HyperclickForTextEditor = (null: any); + let textEditor = null; + let textEditorView = null; + let hyperclick = null; + let hyperclickForTextEditor = null; async function setup() { - jasmineAttachWorkspace(); - + (0, _testHelpers().jasmineAttachWorkspace)(); atom.packages.activatePackage('hyperclick'); - - textEditor = await atom.workspace.open( - nuclideUri.join(__dirname, 'fixtures', 'hyperclick.txt'), - ); + textEditor = await atom.workspace.open(_nuclideUri().default.join(__dirname, 'fixtures', 'hyperclick.txt')); textEditorView = atom.views.getView(textEditor); - - hyperclick = new Hyperclick(); - hyperclickForTextEditor = Array.from( - hyperclick._hyperclickForTextEditors, - )[0]; + hyperclick = new (_Hyperclick().default)(); + hyperclickForTextEditor = Array.from(hyperclick._hyperclickForTextEditors)[0]; } - /** * Returns the pixel position in the DOM of the text editor's screen position. * This is used for dispatching mouse events in the text editor. * * Adapted from https://github.com/atom/atom/blob/5272584d2910e5b3f2b0f309aab4775eb0f779a6/spec/text-editor-component-spec.coffee#L2845 */ - function clientCoordinatesForScreenPosition( - screenPosition: atom$Point, - ): {clientX: number, clientY: number} { - const positionOffset = textEditorView.pixelPositionForScreenPosition( - screenPosition, - ); + + + function clientCoordinatesForScreenPosition(screenPosition) { + const positionOffset = textEditorView.pixelPositionForScreenPosition(screenPosition); const scrollViewElement = textEditorView.querySelector('.scroll-view'); - invariant(scrollViewElement != null); + + if (!(scrollViewElement != null)) { + throw new Error("Invariant violation: \"scrollViewElement != null\""); + } + const scrollViewClientRect = scrollViewElement.getBoundingClientRect(); - const clientX = - scrollViewClientRect.left + - positionOffset.left - - textEditorView.getScrollLeft(); - const clientY = - scrollViewClientRect.top + - positionOffset.top - - textEditorView.getScrollTop(); - return {clientX, clientY}; + const clientX = scrollViewClientRect.left + positionOffset.left - textEditorView.getScrollLeft(); + const clientY = scrollViewClientRect.top + positionOffset.top - textEditorView.getScrollTop(); + return { + clientX, + clientY + }; } - function dispatch( - eventClass: typeof KeyboardEvent | typeof MouseEvent, - type: string, - position: atom$Point, - properties?: {clientX?: number, clientY?: number, metaKey?: boolean}, - ): void { - const {clientX, clientY} = clientCoordinatesForScreenPosition(position); + function dispatch(eventClass, type, position, properties) { + const { + clientX, + clientY + } = clientCoordinatesForScreenPosition(position); const mouseEventInit = { clientX, clientY, - metaKey: properties != null ? properties.metaKey : undefined, + metaKey: properties != null ? properties.metaKey : undefined }; const event = new eventClass(type, mouseEventInit); let domNode = null; + if (eventClass === MouseEvent) { - const {component} = textEditorView; - invariant(component); + const { + component + } = textEditorView; + + if (!component) { + throw new Error("Invariant violation: \"component\""); + } + if (component.refs != null) { domNode = component.refs.lineTiles; } else { @@ -105,6 +144,7 @@ describe('Hyperclick', () => { } else { domNode = textEditorView; } + domNode.dispatchEvent(event); } @@ -114,23 +154,25 @@ describe('Hyperclick', () => { await setup(); }); }); - afterEach(() => { hyperclick.dispose(); }); - describe('simple case', () => { - let provider: HyperclickProvider = (null: any); - const position = new Point(0, 1); - + let provider = null; + const position = new _atom.Point(0, 1); let disposable; beforeEach(() => { provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: () => {}}; + return { + range, + callback: () => {} + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); disposable = hyperclick.addProvider(provider); @@ -149,767 +191,817 @@ describe('Hyperclick', () => { }); }); }); - describe(' + ', () => { it('consumes single-word providers without wordRegExp', () => { waitsForPromise(async () => { const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); hyperclick.addProvider(provider); - - const position = new Point(0, 1); + const position = new _atom.Point(0, 1); const expectedText = 'word1'; - const expectedRange = Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); - await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText, - expectedRange, - ); + const expectedRange = _atom.Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); + await hyperclickForTextEditor.getSuggestionAtMouse(); + expect(provider.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText, expectedRange); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); expect(callback.callCount).toBe(1); }); }); - it('consumes single-word providers with wordRegExp', () => { waitsForPromise(async () => { const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, + wordRegExp: /word/g, - priority: 0, + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); hyperclick.addProvider(provider); - - const position = new Point(0, 8); + const position = new _atom.Point(0, 8); const expectedText = 'word'; - const expectedRange = Range.fromObject([[0, 6], [0, 10]]); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); - await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText, - expectedRange, - ); + const expectedRange = _atom.Range.fromObject([[0, 6], [0, 10]]); - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); + await hyperclickForTextEditor.getSuggestionAtMouse(); + expect(provider.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText, expectedRange); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); expect(callback.callCount).toBe(1); }); }); - it('consumes multi-range providers', () => { waitsForPromise(async () => { const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', - async getSuggestion( - sourceTextEditor: TextEditor, - sourcePosition: Point, - ) { - const range = [ - new Range(sourcePosition, sourcePosition.translate([0, 1])), - new Range( - sourcePosition.translate([0, 2]), - sourcePosition.translate([0, 3]), - ), - ]; - return {range, callback}; + + async getSuggestion(sourceTextEditor, sourcePosition) { + const range = [new _atom.Range(sourcePosition, sourcePosition.translate([0, 1])), new _atom.Range(sourcePosition.translate([0, 2]), sourcePosition.translate([0, 3]))]; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestion').andCallThrough(); hyperclick.addProvider(provider); - - const position = new Point(0, 8); - - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); + const position = new _atom.Point(0, 8); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestion).toHaveBeenCalledWith( - textEditor, - position, - ); - - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); + expect(provider.getSuggestion).toHaveBeenCalledWith(textEditor, position); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); expect(callback.callCount).toBe(1); }); }); - it('consumes multiple providers from different sources', () => { waitsForPromise(async () => { const callback1 = jasmine.createSpy('callback'); const provider1 = { providerName: 'test', + // Do not return a suggestion, so we can fall through to provider2. async getSuggestionForWord(sourceTextEditor, text, range) {}, - priority: 0, + + priority: 0 }; spyOn(provider1, 'getSuggestionForWord').andCallThrough(); - const callback2 = jasmine.createSpy('callback'); const provider2 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback2}; + return { + range, + callback: callback2 + }; }, - priority: 0, + + priority: 0 }; spyOn(provider2, 'getSuggestionForWord').andCallThrough(); - hyperclick.addProvider(provider1); hyperclick.addProvider(provider2); - - const position = new Point(0, 1); + const position = new _atom.Point(0, 1); const expectedText = 'word1'; - const expectedRange = Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); - await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider2.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText, - expectedRange, - ); + const expectedRange = _atom.Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); + await hyperclickForTextEditor.getSuggestionAtMouse(); + expect(provider2.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText, expectedRange); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); expect(callback1.callCount).toBe(0); expect(callback2.callCount).toBe(1); }); }); - it('consumes multiple providers from the same source', () => { waitsForPromise(async () => { const callback1 = jasmine.createSpy('callback'); const provider1 = { providerName: 'test', + // Do not return a suggestion, so we can fall through to provider2. async getSuggestionForWord(sourceTextEditor, text, range) {}, - priority: 0, + + priority: 0 }; spyOn(provider1, 'getSuggestionForWord').andCallThrough(); - const callback2 = jasmine.createSpy('callback'); const provider2 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback2}; + return { + range, + callback: callback2 + }; }, - priority: 0, + + priority: 0 }; spyOn(provider2, 'getSuggestionForWord').andCallThrough(); - hyperclick.addProvider([provider1, provider2]); - - const position = new Point(0, 1); + const position = new _atom.Point(0, 1); const expectedText = 'word1'; - const expectedRange = Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); - await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider2.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText, - expectedRange, - ); + const expectedRange = _atom.Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); + await hyperclickForTextEditor.getSuggestionAtMouse(); + expect(provider2.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText, expectedRange); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); expect(callback1.callCount).toBe(0); expect(callback2.callCount).toBe(1); }); }); }); - describe('avoids excessive calls', () => { it('ignores in the same word as the last position', () => { waitsForPromise(async () => { const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { // Never resolve this, so we know that no suggestion is set. return new Promise(() => {}); }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); hyperclick.addProvider(provider); - - const position = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); + const position = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); dispatch(MouseEvent, 'mousemove', position.translate([0, 1]), { - metaKey: true, + metaKey: true }); dispatch(MouseEvent, 'mousemove', position.translate([0, 2]), { - metaKey: true, + metaKey: true }); - expect(provider.getSuggestionForWord.callCount).toBe(1); }); }); - it('ignores in the same single-range as the last suggestion', () => { waitsForPromise(async () => { const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); hyperclick.addProvider(provider); - - const position = new Point(0, 1); + const position = new _atom.Point(0, 1); const expectedText = 'word1'; - const expectedRange = Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); - await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText, - expectedRange, - ); + const expectedRange = _atom.Range.fromObject([[0, 0], [0, 5]]); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); + await hyperclickForTextEditor.getSuggestionAtMouse(); + expect(provider.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText, expectedRange); dispatch(MouseEvent, 'mousemove', position.translate([0, 1]), { - metaKey: true, + metaKey: true }); expect(provider.getSuggestionForWord.callCount).toBe(1); - - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); expect(callback.callCount).toBe(1); }); }); - it('handles in a different single-range as the last suggestion', () => { waitsForPromise(async () => { const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); hyperclick.addProvider(provider); - - const position1 = new Point(0, 1); + const position1 = new _atom.Point(0, 1); const expectedText1 = 'word1'; - const expectedRange1 = Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousemove', position1, {metaKey: true}); - await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText1, - expectedRange1, - ); + const expectedRange1 = _atom.Range.fromObject([[0, 0], [0, 5]]); - const position2 = new Point(0, 8); - const expectedText2 = 'word2'; - const expectedRange2 = Range.fromObject([[0, 6], [0, 11]]); - dispatch(MouseEvent, 'mousemove', position2, {metaKey: true}); + dispatch(MouseEvent, 'mousemove', position1, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText2, - expectedRange2, - ); + expect(provider.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText1, expectedRange1); + const position2 = new _atom.Point(0, 8); + const expectedText2 = 'word2'; - expect(provider.getSuggestionForWord.callCount).toBe(2); + const expectedRange2 = _atom.Range.fromObject([[0, 6], [0, 11]]); - dispatch(MouseEvent, 'mousedown', position2, {metaKey: true}); + dispatch(MouseEvent, 'mousemove', position2, { + metaKey: true + }); + await hyperclickForTextEditor.getSuggestionAtMouse(); + expect(provider.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText2, expectedRange2); + expect(provider.getSuggestionForWord.callCount).toBe(2); + dispatch(MouseEvent, 'mousedown', position2, { + metaKey: true + }); expect(callback.callCount).toBe(1); }); }); - it('ignores in the same multi-range as the last suggestion', () => { waitsForPromise(async () => { - const range = [ - new Range(new Point(0, 1), new Point(0, 2)), - new Range(new Point(0, 4), new Point(0, 5)), - ]; + const range = [new _atom.Range(new _atom.Point(0, 1), new _atom.Point(0, 2)), new _atom.Range(new _atom.Point(0, 4), new _atom.Point(0, 5))]; const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', + async getSuggestion(sourceTextEditor, sourcePosition) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestion').andCallThrough(); hyperclick.addProvider(provider); - - const position = new Point(0, 1); - - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); + const position = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestion).toHaveBeenCalledWith( - textEditor, - position, - ); - - dispatch(MouseEvent, 'mousemove', new Point(0, 4), {metaKey: true}); + expect(provider.getSuggestion).toHaveBeenCalledWith(textEditor, position); + dispatch(MouseEvent, 'mousemove', new _atom.Point(0, 4), { + metaKey: true + }); expect(provider.getSuggestion.callCount).toBe(1); - - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); expect(callback.callCount).toBe(1); }); }); - it('ignores when out of result range', () => { waitsForPromise(async () => { const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); hyperclick.addProvider(provider); - - const inRangePosition = new Point(0, 1); - const outOfRangePosition = new Point(1, 0); + const inRangePosition = new _atom.Point(0, 1); + const outOfRangePosition = new _atom.Point(1, 0); const expectedText = 'word1'; - const expectedRange = Range.fromObject([[0, 0], [0, 5]]); - dispatch(MouseEvent, 'mousemove', inRangePosition, {metaKey: true}); - await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText, - expectedRange, - ); + const expectedRange = _atom.Range.fromObject([[0, 0], [0, 5]]); + dispatch(MouseEvent, 'mousemove', inRangePosition, { + metaKey: true + }); + await hyperclickForTextEditor.getSuggestionAtMouse(); + expect(provider.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText, expectedRange); dispatch(MouseEvent, 'mousemove', outOfRangePosition, { - metaKey: true, + metaKey: true }); dispatch(MouseEvent, 'mousedown', outOfRangePosition, { - metaKey: true, + metaKey: true }); expect(callback.callCount).toBe(0); }); }); - it('ignores when past the end of the line', () => { waitsForPromise(async () => { const provider = { providerName: 'test', + async getSuggestion(sourceTextEditor, text) { - return {range: [], callback() {}}; + return { + range: [], + + callback() {} + + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestion').andCallThrough(); hyperclick.addProvider(provider); - - const outOfRangePosition = new Point(0, 20); + const outOfRangePosition = new _atom.Point(0, 20); dispatch(MouseEvent, 'mousemove', outOfRangePosition, { - metaKey: true, + metaKey: true }); expect(provider.getSuggestion).not.toHaveBeenCalled(); }); }); }); - describe('adds the `hyperclick` CSS class', () => { const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback() {}}; + return { + range, + + callback() {} + + }; }, - priority: 0, - }; + priority: 0 + }; beforeEach(() => { hyperclick.addProvider(provider); }); - it('adds on , removes on ', () => { waitsForPromise(async () => { - const position = new Point(0, 1); - + const position = new _atom.Point(0, 1); expect(textEditorView.classList.contains('hyperclick')).toBe(false); - - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); expect(textEditorView.classList.contains('hyperclick')).toBe(true); - - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); expect(textEditorView.classList.contains('hyperclick')).toBe(false); }); }); - it('adds on , removes on ', () => { waitsForPromise(async () => { - const position = new Point(0, 1); + const position = new _atom.Point(0, 1); // We need to move the mouse once, so Hyperclick knows where it is. - // We need to move the mouse once, so Hyperclick knows where it is. dispatch(MouseEvent, 'mousemove', position); expect(textEditorView.classList.contains('hyperclick')).toBe(false); - - dispatch(KeyboardEvent, 'keydown', position, {metaKey: true}); + dispatch(KeyboardEvent, 'keydown', position, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); expect(textEditorView.classList.contains('hyperclick')).toBe(true); - dispatch(KeyboardEvent, 'keyup', position); expect(textEditorView.classList.contains('hyperclick')).toBe(false); }); }); }); - describe('hyperclick:confirm-cursor', () => { it('confirms the suggestion at the cursor even if the mouse moved', () => { waitsForPromise(async () => { const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); hyperclick.addProvider(provider); - - const mousePosition = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', mousePosition, {metaKey: true}); + const mousePosition = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', mousePosition, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - - textEditor.setCursorBufferPosition(new Point(0, 8)); + textEditor.setCursorBufferPosition(new _atom.Point(0, 8)); atom.commands.dispatch(textEditorView, 'hyperclick:confirm-cursor'); - expect(provider.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - 'word2', - Range.fromObject([[0, 6], [0, 11]]), - ); + expect(provider.getSuggestionForWord).toHaveBeenCalledWith(textEditor, 'word2', _atom.Range.fromObject([[0, 6], [0, 11]])); waitsFor(() => callback.callCount === 1); }); }); }); - describe('priority', () => { it('confirms higher priority provider when it is consumed first', () => { waitsForPromise(async () => { const callback1 = jasmine.createSpy('callback'); const provider1 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback1}; + return { + range, + callback: callback1 + }; }, - priority: 5, + + priority: 5 }; hyperclick.addProvider(provider1); - const callback2 = jasmine.createSpy('callback'); const provider2 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback1}; + return { + range, + callback: callback1 + }; }, - priority: 3, + + priority: 3 }; hyperclick.addProvider(provider2); - - const mousePosition = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', mousePosition, {metaKey: true}); + const mousePosition = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', mousePosition, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - dispatch(MouseEvent, 'mousedown', mousePosition, {metaKey: true}); - + dispatch(MouseEvent, 'mousedown', mousePosition, { + metaKey: true + }); expect(callback1.callCount).toBe(1); expect(callback2.callCount).toBe(0); }); }); - it('confirms higher priority provider when it is consumed last', () => { waitsForPromise(async () => { const callback1 = jasmine.createSpy('callback'); const provider1 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback1}; + return { + range, + callback: callback1 + }; }, - priority: 3, + + priority: 3 }; hyperclick.addProvider(provider1); - const callback2 = jasmine.createSpy('callback'); const provider2 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback2}; + return { + range, + callback: callback2 + }; }, - priority: 5, + + priority: 5 }; hyperclick.addProvider(provider2); - - const mousePosition = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', mousePosition, {metaKey: true}); + const mousePosition = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', mousePosition, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - dispatch(MouseEvent, 'mousedown', mousePosition, {metaKey: true}); - + dispatch(MouseEvent, 'mousedown', mousePosition, { + metaKey: true + }); expect(callback1.callCount).toBe(0); expect(callback2.callCount).toBe(1); }); }); - it('confirms same-priority in the order they are consumed', () => { waitsForPromise(async () => { const callback1 = jasmine.createSpy('callback'); const provider1 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback1}; + return { + range, + callback: callback1 + }; }, - priority: 0, + + priority: 0 }; hyperclick.addProvider(provider1); - const callback2 = jasmine.createSpy('callback'); const provider2 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback2}; + return { + range, + callback: callback2 + }; }, - priority: 0, + + priority: 0 }; hyperclick.addProvider(provider2); - - const mousePosition = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', mousePosition, {metaKey: true}); + const mousePosition = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', mousePosition, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - dispatch(MouseEvent, 'mousedown', mousePosition, {metaKey: true}); - + dispatch(MouseEvent, 'mousedown', mousePosition, { + metaKey: true + }); expect(callback1.callCount).toBe(1); expect(callback2.callCount).toBe(0); }); }); - it('confirms highest priority provider when multiple are consumed at a time', () => { waitsForPromise(async () => { const callback1 = jasmine.createSpy('callback'); const provider1 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback1}; + return { + range, + callback: callback1 + }; }, - priority: 1, + + priority: 1 }; const callback2 = jasmine.createSpy('callback'); const provider2 = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback: callback2}; + return { + range, + callback: callback2 + }; }, - priority: 2, - }; + priority: 2 + }; hyperclick.addProvider([provider1, provider2]); - - const mousePosition = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', mousePosition, {metaKey: true}); + const mousePosition = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', mousePosition, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - dispatch(MouseEvent, 'mousedown', mousePosition, {metaKey: true}); - + dispatch(MouseEvent, 'mousedown', mousePosition, { + metaKey: true + }); expect(callback1.callCount).toBe(0); expect(callback2.callCount).toBe(1); }); }); }); - describe('multiple suggestions', () => { it('confirms the first suggestion', () => { waitsForPromise(async () => { - const callback = [ - { - title: 'callback1', - callback: jasmine.createSpy('callback1'), - }, - { - title: 'callback2', - callback: jasmine.createSpy('callback1'), - }, - ]; + const callback = [{ + title: 'callback1', + callback: jasmine.createSpy('callback1') + }, { + title: 'callback2', + callback: jasmine.createSpy('callback1') + }]; const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; hyperclick.addProvider(provider); - - const position = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); + const position = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); - - const suggestionListEl = textEditorView.querySelector( - 'hyperclick-suggestion-list', - ); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); + const suggestionListEl = textEditorView.querySelector('hyperclick-suggestion-list'); expect(suggestionListEl).toExist(); - atom.commands.dispatch(textEditorView, 'editor:newline'); - expect(callback[0].callback.callCount).toBe(1); expect(callback[1].callback.callCount).toBe(0); - expect( - textEditorView.querySelector('hyperclick-suggestion-list'), - ).not.toExist(); + expect(textEditorView.querySelector('hyperclick-suggestion-list')).not.toExist(); }); }); - it('confirms the second suggestion', () => { waitsForPromise(async () => { - const callback = [ - { - title: 'callback1', - callback: jasmine.createSpy('callback1'), - }, - { - title: 'callback2', - callback: jasmine.createSpy('callback1'), - }, - ]; + const callback = [{ + title: 'callback1', + callback: jasmine.createSpy('callback1') + }, { + title: 'callback2', + callback: jasmine.createSpy('callback1') + }]; const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; hyperclick.addProvider(provider); - - const position = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); + const position = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); - - const suggestionListEl = textEditorView.querySelector( - 'hyperclick-suggestion-list', - ); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); + const suggestionListEl = textEditorView.querySelector('hyperclick-suggestion-list'); expect(suggestionListEl).toExist(); - atom.commands.dispatch(textEditorView, 'core:move-down'); atom.commands.dispatch(textEditorView, 'editor:newline'); - expect(callback[0].callback.callCount).toBe(0); expect(callback[1].callback.callCount).toBe(1); - expect( - textEditorView.querySelector('hyperclick-suggestion-list'), - ).not.toExist(); + expect(textEditorView.querySelector('hyperclick-suggestion-list')).not.toExist(); }); }); - it('is cancelable', () => { waitsForPromise(async () => { - const callback = [ - { - title: 'callback1', - callback: jasmine.createSpy('callback1'), - }, - { - title: 'callback2', - callback: jasmine.createSpy('callback1'), - }, - ]; + const callback = [{ + title: 'callback1', + callback: jasmine.createSpy('callback1') + }, { + title: 'callback2', + callback: jasmine.createSpy('callback1') + }]; const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; hyperclick.addProvider(provider); - - const position = new Point(0, 1); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); + const position = new _atom.Point(0, 1); + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - dispatch(MouseEvent, 'mousedown', position, {metaKey: true}); - - const suggestionListEl = textEditorView.querySelector( - 'hyperclick-suggestion-list', - ); + dispatch(MouseEvent, 'mousedown', position, { + metaKey: true + }); + const suggestionListEl = textEditorView.querySelector('hyperclick-suggestion-list'); expect(suggestionListEl).toExist(); - atom.commands.dispatch(textEditorView, 'core:cancel'); - expect(callback[0].callback.callCount).toBe(0); expect(callback[1].callback.callCount).toBe(0); - expect( - textEditorView.querySelector('hyperclick-suggestion-list'), - ).not.toExist(); + expect(textEditorView.querySelector('hyperclick-suggestion-list')).not.toExist(); }); }); }); }); - describe('with line wrapping', () => { beforeEach(() => { waitsForPromise(async () => { atom.config.set('editor.softWrap', true); atom.config.set('editor.softWrapAtPreferredLineLength', true); atom.config.set('editor.preferredLineLength', 6); // This wraps each word onto its own line. + await setup(); }); }); - afterEach(() => { - hyperclick.dispose(); - // Bug: Atom 1.19+ hangs on teardown if we don't manually detach the DOM. + hyperclick.dispose(); // Bug: Atom 1.19+ hangs on teardown if we don't manually detach the DOM. + atom.workspace.getElement().remove(); }); - describe('when the editor has soft-wrapped lines', () => { it('Hyperclick correctly detects the word being moused over.', () => { waitsForPromise(async () => { const callback = jasmine.createSpy('callback'); const provider = { providerName: 'test', + async getSuggestionForWord(sourceTextEditor, text, range) { - return {range, callback}; + return { + range, + callback + }; }, - priority: 0, + + priority: 0 }; spyOn(provider, 'getSuggestionForWord').andCallThrough(); hyperclick.addProvider(provider); - - const position = new Point(8, 0); + const position = new _atom.Point(8, 0); const expectedText = 'word9'; - const expectedBufferRange = Range.fromObject([[2, 12], [2, 17]]); - dispatch(MouseEvent, 'mousemove', position, {metaKey: true}); + + const expectedBufferRange = _atom.Range.fromObject([[2, 12], [2, 17]]); + + dispatch(MouseEvent, 'mousemove', position, { + metaKey: true + }); await hyperclickForTextEditor.getSuggestionAtMouse(); - expect(provider.getSuggestionForWord).toHaveBeenCalledWith( - textEditor, - expectedText, - expectedBufferRange, - ); + expect(provider.getSuggestionForWord).toHaveBeenCalledWith(textEditor, expectedText, expectedBufferRange); expect(provider.getSuggestionForWord.callCount).toBe(1); }); }); }); }); }); - describe('showTriggerConflictWarning', () => { it('formats the message without erroring', () => { - showTriggerConflictWarning().dismiss(); + (0, _showTriggerConflictWarning().default)().dismiss(); }); -}); +}); \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-1/file.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-1/file.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-1/file.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-1/file.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-1/index.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-1/index.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-1/index.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-1/index.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-2/file.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-2/file.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-2/file.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-2/file.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-2/index.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-2/index.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-2/index.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-atom-package-2/index.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-1/file.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-1/file.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-1/file.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-1/file.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-1/index.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-1/index.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-1/index.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-1/index.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-2/file.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-2/file.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-2/file.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-2/file.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-2/index.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-2/index.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-2/index.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-apm-package-2/index.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-1/file.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-1/file.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-1/file.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-1/file.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-1/index.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-1/index.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-1/index.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-1/index.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-2/file.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-2/file.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-2/file.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-2/file.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-2/index.js b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-2/index.js index b9475d21..b0bef253 100644 --- a/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-2/index.js +++ b/modules/eslint-plugin-nuclide-internal/__mocks__/nuclide-fake-node-npm-package-2/index.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +"use strict"; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/AtomTestWorker.js b/modules/jest-atom-runner/src/AtomTestWorker.js index e7776f5e..441a6dd1 100644 --- a/modules/jest-atom-runner/src/AtomTestWorker.js +++ b/modules/jest-atom-runner/src/AtomTestWorker.js @@ -1,3 +1,40 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _child_process = require("child_process"); + +function _mkdirp() { + const data = _interopRequireDefault(require("mkdirp")); + + _mkdirp = function () { + return data; + }; + + return data; +} + +var _path = _interopRequireDefault(require("path")); + +var _fs = _interopRequireDefault(require("fs")); + +var _os = _interopRequireDefault(require("os")); + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +43,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ @@ -16,28 +53,8 @@ /* eslint-disable nuclide-internal/prefer-nuclide-uri */ /* eslint-disable-next-line nuclide-internal/consistent-import-name */ -import type {IPCServer, Socket} from './ipc-server'; -import type {ServerID, WorkerID, MessageType} from './utils'; -import type {Test, GlobalConfig, TestResult} from './types'; - // eslint-disable-next-line nuclide-internal/consistent-import-name -import {spawn} from 'child_process'; -import mkdirp from 'mkdirp'; -import path from 'path'; -import fs from 'fs'; -import os from 'os'; -import { - makeUniqWorkerId, - mergeIPCIDs, - parseMessage, - makeMessage, - MESSAGE_TYPES, - parseJSON, -} from './utils'; - -const TMP_DIR = path.resolve(os.tmpdir(), 'jest-atom-runner'); - -// Atom resolves to its testing framework based on what's specified +const TMP_DIR = _path.default.resolve(_os.default.tmpdir(), 'jest-atom-runner'); // Atom resolves to its testing framework based on what's specified // under the "atomTestRunner" key in the package.json in the parent directory // of the first passed path. // so if we run `atom -t /some_dir/__tests__/1-test.js` @@ -46,87 +63,82 @@ const TMP_DIR = path.resolve(os.tmpdir(), 'jest-atom-runner'); // To work around (or rather make atom execute arbitrary code) we // will create a dummy `/tmp/packages.json` with `atomTestRunner` pointing // to the file that we want to inject into atom's runtime. + + const createDummyPackageJson = () => { - mkdirp.sync(path.resolve(TMP_DIR)); - const packageJsonPath = path.resolve(TMP_DIR, 'package.json'); - fs.writeFileSync( - packageJsonPath, - JSON.stringify({atomTestRunner: require.resolve('./atomTestRunner')}), - ); -}; + _mkdirp().default.sync(_path.default.resolve(TMP_DIR)); -type OnMessageCallback = (MessageType, data?: string) => void; -type TestRunResolver = {resolve: TestResult => void, reject: Error => void}; + const packageJsonPath = _path.default.resolve(TMP_DIR, 'package.json'); -class AtomTestWorker { - _childProcess: child_process$ChildProcess; - _ipcServer: IPCServer; - _serverID: ServerID; - _workerID: WorkerID; - _alive: boolean; // whether the worker is up and running - _socket: ?Socket; - _onMessageCallbacks: Array; - _globalConfig: GlobalConfig; - _runningTests: Map; + _fs.default.writeFileSync(packageJsonPath, JSON.stringify({ + atomTestRunner: require.resolve("./atomTestRunner") + })); +}; +class AtomTestWorker { + // whether the worker is up and running constructor({ ipcServer, serverID, - globalConfig, - }: { - ipcServer: IPCServer, - serverID: ServerID, - globalConfig: GlobalConfig, + globalConfig }) { this._ipcServer = ipcServer; this._serverID = serverID; this._alive = false; this._onMessageCallbacks = []; - this._workerID = makeUniqWorkerId(); + this._workerID = (0, _utils().makeUniqWorkerId)(); this._globalConfig = globalConfig; this._runningTests = new Map(); } async start() { - const {_serverID: serverID, _ipcServer: ipcServer} = this; + const { + _serverID: serverID, + _ipcServer: ipcServer + } = this; return new Promise(resolve => { createDummyPackageJson(); const workerID = this._workerID; - const atomPathArg = path.resolve( - TMP_DIR, - mergeIPCIDs({serverID, workerID}), - ); + + const atomPathArg = _path.default.resolve(TMP_DIR, (0, _utils().mergeIPCIDs)({ + serverID, + workerID + })); let firstMessage = false; ipcServer.on(workerID, (message, socket) => { - const {messageType, data} = parseMessage(message); + const { + messageType, + data + } = (0, _utils().parseMessage)(message); + if (!firstMessage) { firstMessage = true; this._alive = true; this._socket = socket; resolve(); } else { - this._onMessage((messageType: MessageType), data); + this._onMessage(messageType, data); } }); - - this._childProcess = spawn('atom', ['-t', atomPathArg], { - stdio: [ - 'inherit', - // redirect child process' stdout to parent process stderr, so it - // doesn't break any tools that depend on stdout (like the ones - // that consume a generated JSON report from jest's stdout) - process.stderr, - 'inherit', - ], + this._childProcess = (0, _child_process.spawn)('atom', ['-t', atomPathArg], { + stdio: ['inherit', // redirect child process' stdout to parent process stderr, so it + // doesn't break any tools that depend on stdout (like the ones + // that consume a generated JSON report from jest's stdout) + process.stderr, 'inherit'] }); const crash = error => { - for (const {reject} of this._runningTests.values()) { + for (const _ref of this._runningTests.values()) { + const { + reject + } = _ref; reject(error); } }; + this._childProcess.on('error', crash); + this._childProcess.on('close', code => { crash(new Error(`child process exited with code: ${code}`)); }); @@ -134,49 +146,54 @@ class AtomTestWorker { } async stop() { - this.send(makeMessage({messageType: MESSAGE_TYPES.SHUT_DOWN})); + this.send((0, _utils().makeMessage)({ + messageType: _utils().MESSAGE_TYPES.SHUT_DOWN + })); + this._childProcess.kill('SIGTERM'); } - send(message: string) { + send(message) { if (!this._socket || !this._alive || !this._workerID) { throw new Error("Can't interact with the worker before it comes alive"); } + this._ipcServer.emit(this._socket, this._workerID, message); } - _onMessage(messageType: MessageType, data: string) { + _onMessage(messageType, data) { switch (messageType) { - case MESSAGE_TYPES.TEST_RESULT: { - const testResult: TestResult = parseJSON(data); - const {testFilePath} = testResult; - const runningTest = this._runningTests.get(testFilePath); - if (!runningTest) { - throw new Error(` + case _utils().MESSAGE_TYPES.TEST_RESULT: + { + const testResult = (0, _utils().parseJSON)(data); + const { + testFilePath + } = testResult; + + const runningTest = this._runningTests.get(testFilePath); + + if (!runningTest) { + throw new Error(` Can't find any references to the test result that returned from the worker. returned test path: ${testFilePath} list of tests that we know has been running in the worker: - ${Array.from(this._runningTests) - .map(([key, _]) => key) - .join(', ')} + ${Array.from(this._runningTests).map(([key, _]) => key).join(', ')} `); - } + } - testResult.testExecError != null - ? // $FlowFixMe jest expects it to be rejected with an object - runningTest.reject(testResult.testExecError) - : runningTest.resolve(testResult); - this._runningTests.delete(testFilePath); - } + testResult.testExecError != null ? // $FlowFixMe jest expects it to be rejected with an object + runningTest.reject(testResult.testExecError) : runningTest.resolve(testResult); + + this._runningTests.delete(testFilePath); + } } } - runTest(test: Test): Promise { + runTest(test) { if (this._runningTests.has(test.path)) { - throw new Error( - "Can't run the same test in the same worker at the same time", - ); + throw new Error("Can't run the same test in the same worker at the same time"); } + return new Promise((resolve, reject) => { // Ideally we don't want to pass all thing info with every test // because it never changes. We should try to initialize it @@ -186,26 +203,28 @@ class AtomTestWorker { const rawModuleMap = test.context.moduleMap.getRawModuleMap(); const config = test.context.config; const globalConfig = this._globalConfig; - - this.send( - makeMessage({ - messageType: MESSAGE_TYPES.RUN_TEST, - data: JSON.stringify({ - rawModuleMap, - config, - globalConfig, - path: test.path, - }), - }), - ); - - this._runningTests.set(test.path, {resolve, reject}); + this.send((0, _utils().makeMessage)({ + messageType: _utils().MESSAGE_TYPES.RUN_TEST, + data: JSON.stringify({ + rawModuleMap, + config, + globalConfig, + path: test.path + }) + })); + + this._runningTests.set(test.path, { + resolve, + reject + }); }); } isBusy() { return this._runningTests.size > 0; } + } -export default AtomTestWorker; +var _default = AtomTestWorker; +exports.default = _default; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/AtomTestWorkerFarm.js b/modules/jest-atom-runner/src/AtomTestWorkerFarm.js index 17579ab4..11815709 100644 --- a/modules/jest-atom-runner/src/AtomTestWorkerFarm.js +++ b/modules/jest-atom-runner/src/AtomTestWorkerFarm.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _AtomTestWorker() { + const data = _interopRequireDefault(require("./AtomTestWorker")); + + _AtomTestWorker = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,57 +25,40 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ /* An abstroction that acts as a sempaphore for Atom workers. */ /* eslint-disable nuclide-internal/no-commonjs */ - -import type {ServerID} from './utils'; -import type {IPCServer} from './ipc-server'; -import type {Test, GlobalConfig, TestResult} from './types'; - -import AtomTestWorker from './AtomTestWorker'; - class AtomTestWorkerFarm { - _workers: Array; - _queue: Array<{ - test: Test, - onStart: Test => void, - resolve: TestResult => void, - reject: Error => void, - }>; - constructor({ ipcServer, serverID, globalConfig, - concurrency, - }: { - ipcServer: IPCServer, - serverID: ServerID, - globalConfig: GlobalConfig, - concurrency: number, + concurrency }) { if (concurrency < 1) { - throw new Error( - `concurrency has to be greater than 1, given: ${concurrency}`, - ); + throw new Error(`concurrency has to be greater than 1, given: ${concurrency}`); } + this._workers = []; this._queue = []; + for (let i = 0; i < concurrency; i++) { - const worker = new AtomTestWorker({ipcServer, serverID, globalConfig}); + const worker = new (_AtomTestWorker().default)({ + ipcServer, + serverID, + globalConfig + }); + this._workers.push(worker); } } async start() { - await Promise.all(this._workers.map(w => w.start())).then(results => - results.forEach(() => this._processNext()), - ); + await Promise.all(this._workers.map(w => w.start())).then(results => results.forEach(() => this._processNext())); } async stop() { @@ -65,30 +67,39 @@ class AtomTestWorkerFarm { _processNext() { const availableWorker = this._workers.find(w => !w.isBusy()); + if (availableWorker) { const nextInQueue = this._queue.shift(); + if (nextInQueue) { nextInQueue.onStart(nextInQueue.test); - availableWorker - .runTest(nextInQueue.test) - .then(testResult => { - nextInQueue.resolve(testResult); - this._processNext(); - }) - .catch(error => { - nextInQueue.reject(error); - this._processNext(); - }); + availableWorker.runTest(nextInQueue.test).then(testResult => { + nextInQueue.resolve(testResult); + + this._processNext(); + }).catch(error => { + nextInQueue.reject(error); + + this._processNext(); + }); } } } - runTest(test: Test, onStart: Test => void): Promise { + runTest(test, onStart) { return new Promise((resolve, reject) => { - this._queue.push({test, resolve, reject, onStart}); + this._queue.push({ + test, + resolve, + reject, + onStart + }); + this._processNext(); }); } + } -export default AtomTestWorkerFarm; +var _default = AtomTestWorkerFarm; +exports.default = _default; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/atomTestRunner.js b/modules/jest-atom-runner/src/atomTestRunner.js index 8482ab75..c6682954 100644 --- a/modules/jest-atom-runner/src/atomTestRunner.js +++ b/modules/jest-atom-runner/src/atomTestRunner.js @@ -1,3 +1,69 @@ +"use strict"; + +var _os = _interopRequireDefault(require("os")); + +function _run_test() { + const data = _interopRequireDefault(require("jest-runner/build/run_test")); + + _run_test = function () { + return data; + }; + + return data; +} + +function _jestRuntime() { + const data = _interopRequireDefault(require("jest-runtime")); + + _jestRuntime = function () { + return data; + }; + + return data; +} + +function _jestHasteMap() { + const data = _interopRequireDefault(require("jest-haste-map")); + + _jestHasteMap = function () { + return data; + }; + + return data; +} + +function _patchAtomConsole() { + const data = _interopRequireDefault(require("../../nuclide-commons/patch-atom-console")); + + _patchAtomConsole = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _ipcClient() { + const data = require("./ipc-client"); + + _ipcClient = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,11 +72,12 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-disable no-console */ + /* eslint-disable nuclide-internal/no-commonjs */ /* @@ -18,99 +85,68 @@ * client, find the parant Jest IPC server that spawned this process and say * "hey! i'm up and ready to run tests, send them over!" */ -import type {IPCWorker} from './ipc-client'; - -import os from 'os'; -import runTest from 'jest-runner/build/run_test'; -import Runtime from 'jest-runtime'; -import HasteMap from 'jest-haste-map'; -import patchAtomConsole from 'nuclide-commons/patch-atom-console'; - -import { - parseMessage, - MESSAGE_TYPES, - parseJSON, - makeMessage, - buildFailureTestResult, -} from './utils'; -import {connectToIPCServer} from './ipc-client'; -import {extractIPCIDsFromFilePath} from './utils'; - const ATOM_BUILTIN_MODULES = new Set(['atom', 'electron']); - process.on('uncaughtException', err => { console.error(err.stack); process.exit(1); }); -export type AtomParams = { - testPaths: Array, - buildAtomEnvironment: any, - buildDefaultApplicationDelegate: any, -}; - -module.exports = async function(params: AtomParams) { - patchAtomConsole(); - const firstTestPath = params.testPaths[0]; - - // We pass server and worker IDs via a basename of non-existing file. +module.exports = async function (params) { + (0, _patchAtomConsole().default)(); + const firstTestPath = params.testPaths[0]; // We pass server and worker IDs via a basename of non-existing file. // Yeah.. it's weird, i know! :( - const {serverID, workerID} = extractIPCIDsFromFilePath(firstTestPath); - const connection: IPCWorker = await connectToIPCServer({ + + const { + serverID, + workerID + } = (0, _utils().extractIPCIDsFromFilePath)(firstTestPath); + const connection = await (0, _ipcClient().connectToIPCServer)({ serverID, - workerID, + workerID }); - global.__buildAtomGlobal = () => - params.buildAtomEnvironment({ - applicationDelegate: params.buildDefaultApplicationDelegate(), - window, - document: window.document, - configDirPath: os.tmpdir(), - enablePersistence: true, - }); - - // We need to delete whatever is in `prepareStackTrace` to make source map work. + global.__buildAtomGlobal = () => params.buildAtomEnvironment({ + applicationDelegate: params.buildDefaultApplicationDelegate(), + window, + document: window.document, + configDirPath: _os.default.tmpdir(), + enablePersistence: true + }); // We need to delete whatever is in `prepareStackTrace` to make source map work. // Right now Atom is overwriting it without an ability to reassign, so the only // option is to delete the whole thing. (https://fburl.com/cqp7mj01) - delete Error.prepareStackTrace; + + delete Error.prepareStackTrace; return new Promise((resolve, reject) => { connection.onMessage(message => { try { - const {messageType, data} = parseMessage(message); + const { + messageType, + data + } = (0, _utils().parseMessage)(message); switch (messageType) { - case MESSAGE_TYPES.RUN_TEST: { - const testData = parseJSON(data); - runTest( - testData.path, - testData.globalConfig, - testData.config, - getResolver(testData.config, testData.rawModuleMap), - ) - .catch(error => { - const testResult = buildFailureTestResult( - testData.path, - error, - testData.config, - testData.globalConfig, - ); + case _utils().MESSAGE_TYPES.RUN_TEST: + { + const testData = (0, _utils().parseJSON)(data); + (0, _run_test().default)(testData.path, testData.globalConfig, testData.config, getResolver(testData.config, testData.rawModuleMap)).catch(error => { + const testResult = (0, _utils().buildFailureTestResult)(testData.path, error, testData.config, testData.globalConfig); return testResult; - }) - .then(result => { - const msg = makeMessage({ - messageType: MESSAGE_TYPES.TEST_RESULT, - data: JSON.stringify(result), + }).then(result => { + const msg = (0, _utils().makeMessage)({ + messageType: _utils().MESSAGE_TYPES.TEST_RESULT, + data: JSON.stringify(result) }); connection.send(msg); }); - break; - } - case MESSAGE_TYPES.SHUT_DOWN: { - resolve(); - break; - } + break; + } + + case _utils().MESSAGE_TYPES.SHUT_DOWN: + { + resolve(); + break; + } } } catch (e) { console.error(e); @@ -120,11 +156,11 @@ module.exports = async function(params: AtomParams) { console.error(e); throw e; }); -}; - -// Atom has builtin modules that can't go through jest transforme/cache +}; // Atom has builtin modules that can't go through jest transforme/cache // pipeline. There's no easy way to add custom modules to jest, so we'll wrap // jest Resolver object and make it bypass atom's modules. + + const wrapResolver = resolver => { const isCoreModule = resolver.isCoreModule; const resolveModule = resolver.resolveModule; @@ -149,25 +185,21 @@ const wrapResolver = resolver => { }; const resolvers = Object.create(null); + const getResolver = (config, rawModuleMap) => { // In watch mode, the raw module map with all haste modules is passed from // the test runner to the watch command. This is because jest-haste-map's // watch mode does not persist the haste map on disk after every file change. // To make this fast and consistent, we pass it from the TestRunner. if (rawModuleMap) { - return wrapResolver( - Runtime.createResolver(config, new HasteMap.ModuleMap(rawModuleMap)), - ); + return wrapResolver(_jestRuntime().default.createResolver(config, new (_jestHasteMap().default.ModuleMap)(rawModuleMap))); } else { const name = config.name; + if (!resolvers[name]) { - resolvers[name] = wrapResolver( - Runtime.createResolver( - config, - Runtime.createHasteMap(config).readModuleMap(), - ), - ); + resolvers[name] = wrapResolver(_jestRuntime().default.createResolver(config, _jestRuntime().default.createHasteMap(config).readModuleMap())); } + return resolvers[name]; } -}; +}; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/environment.js b/modules/jest-atom-runner/src/environment.js index 631fb085..a2e5bc38 100644 --- a/modules/jest-atom-runner/src/environment.js +++ b/modules/jest-atom-runner/src/environment.js @@ -1,3 +1,17 @@ +"use strict"; + +function _jestMock() { + const data = _interopRequireDefault(require("jest-mock")); + + _jestMock = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,31 +20,23 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-disable nuclide-internal/no-commonjs */ - -import type {ProjectConfig} from './types'; - -import mock from 'jest-mock'; - class Atom { - global: Object; - moduleMocker: Object; - fakeTimers: Object; - - constructor(config: ProjectConfig) { - this.global = global; - // __buildAtomGlobal should be set at the atom entry point. It depends + constructor(config) { + this.global = global; // __buildAtomGlobal should be set at the atom entry point. It depends // on the data Atom test runner provides. + global.atom = global.__buildAtomGlobal(); - this.moduleMocker = new mock.ModuleMocker(global); + this.moduleMocker = new (_jestMock().default.ModuleMocker)(global); this.fakeTimers = { useFakeTimers() { throw new Error('fakeTimers are not supproted in atom environment'); - }, + } + }; } @@ -41,13 +47,14 @@ class Atom { async teardown() {} - runScript(script: any): ?any { + runScript(script) { // unfortunately electron crashes if we try to access anything // on global from within a vm content. The only workaround i found // is to lose sandboxing and run everything in a single context. // We should look into using iframes/webviews in the future. return script.runInThisContext(); } + } -module.exports = Atom; +module.exports = Atom; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/index.js b/modules/jest-atom-runner/src/index.js index 31299bc3..0d4d46a1 100644 --- a/modules/jest-atom-runner/src/index.js +++ b/modules/jest-atom-runner/src/index.js @@ -1,3 +1,37 @@ +"use strict"; + +function _ipcServer() { + const data = require("./ipc-server"); + + _ipcServer = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _AtomTestWorkerFarm() { + const data = _interopRequireDefault(require("./AtomTestWorkerFarm")); + + _AtomTestWorkerFarm = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +40,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ @@ -15,16 +49,6 @@ */ /* eslint-disable nuclide-internal/no-commonjs */ - -import type {IPCServer} from './ipc-server'; -import type {GlobalConfig, Test, TestResult, Watcher} from './types'; -import type {ServerID} from './utils'; - -import {startServer} from './ipc-server'; -import {makeUniqServerId} from './utils'; - -import AtomTestWorkerFarm from './AtomTestWorkerFarm'; - // Share ipc server and farm between multiple runs, so we don't restart // the whole thing in watch mode every time. (it steals window focus when // atom launches) @@ -34,40 +58,27 @@ let farm; let cleanupRegistered = false; class TestRunner { - _globalConfig: GlobalConfig; - _serverID: ServerID; - _ipcServerPromise: Promise; - - constructor(globalConfig: GlobalConfig) { + constructor(globalConfig) { this._globalConfig = globalConfig; - serverID = serverID || (serverID = makeUniqServerId()); - ipcServerPromise || - (ipcServerPromise = startServer({ - serverID: this._serverID, - })); + serverID = serverID || (serverID = (0, _utils().makeUniqServerId)()); + ipcServerPromise || (ipcServerPromise = (0, _ipcServer().startServer)({ + serverID: this._serverID + })); } - async runTests( - tests: Array, - watcher: Watcher, - onStart: Test => void, - onResult: (Test, TestResult) => void, - onFailure: (Test, Error) => void, - options: {}, - ) { + async runTests(tests, watcher, onStart, onResult, onFailure, options) { const isWatch = this._globalConfig.watch || this._globalConfig.watchAll; - const concurrency = isWatch - ? // spawning multiple atoms in watch mode is weird, - // they all try to steal focus from the current window - 1 - : Math.min(tests.length, this._globalConfig.maxWorkers); + const concurrency = isWatch ? // spawning multiple atoms in watch mode is weird, + // they all try to steal focus from the current window + 1 : Math.min(tests.length, this._globalConfig.maxWorkers); const ipcServer = await ipcServerPromise; + if (!farm) { - farm = new AtomTestWorkerFarm({ + farm = new (_AtomTestWorkerFarm().default)({ serverID: this._serverID, ipcServer: await ipcServer, globalConfig: this._globalConfig, - concurrency, + concurrency }); await farm.start(); } @@ -86,19 +97,15 @@ class TestRunner { process.on('uncaughtException', cleanup); } - await Promise.all( - tests.map(test => { - return farm - .runTest(test, onStart) - .then(testResult => onResult(test, testResult)) - .catch(error => onFailure(test, error)); - }), - ); + await Promise.all(tests.map(test => { + return farm.runTest(test, onStart).then(testResult => onResult(test, testResult)).catch(error => onFailure(test, error)); + })); if (!isWatch) { cleanup(); } } + } -module.exports = TestRunner; +module.exports = TestRunner; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/ipc-client.js b/modules/jest-atom-runner/src/ipc-client.js index fbca958c..3b072f5d 100644 --- a/modules/jest-atom-runner/src/ipc-client.js +++ b/modules/jest-atom-runner/src/ipc-client.js @@ -1,3 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.connectToIPCServer = void 0; + +function _nodeIpc() { + const data = _interopRequireDefault(require("node-ipc")); + + _nodeIpc = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,58 +35,46 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {ServerID, WorkerID} from './utils'; - -export type IPCWorker = { - onMessage((message: string) => void): void, - send(message: string): void, -}; - -import ipc from 'node-ipc'; -import {makeMessage, MESSAGE_TYPES} from './utils'; - let connected = false; -export const connectToIPCServer = ({ + +const connectToIPCServer = ({ serverID, - workerID, -}: { - serverID: ServerID, - workerID: WorkerID, -}): Promise => { + workerID +}) => { if (connected) { - throw new Error( - "can't connect to IPC server more than once from one worker", - ); + throw new Error("can't connect to IPC server more than once from one worker"); } - connected = true; - - ipc.config.id = serverID; - ipc.config.retry = 1500; + connected = true; + _nodeIpc().default.config.id = serverID; + _nodeIpc().default.config.retry = 1500; return new Promise(resolve => { const onMessageCallbacks = []; - ipc.connectTo(serverID, () => { - ipc.of[serverID].on('connect', () => { - const initMessage = makeMessage({ - messageType: MESSAGE_TYPES.INITIALIZE, + + _nodeIpc().default.connectTo(serverID, () => { + _nodeIpc().default.of[serverID].on('connect', () => { + const initMessage = (0, _utils().makeMessage)({ + messageType: _utils().MESSAGE_TYPES.INITIALIZE }); - ipc.of[serverID].emit(workerID, initMessage); + + _nodeIpc().default.of[serverID].emit(workerID, initMessage); }); - ipc.of[serverID].on(workerID, data => { + _nodeIpc().default.of[serverID].on(workerID, data => { onMessageCallbacks.forEach(cb => cb(data)); }); resolve({ - send: message => ipc.of[serverID].emit(workerID, message), + send: message => _nodeIpc().default.of[serverID].emit(workerID, message), onMessage: fn => { onMessageCallbacks.push(fn); - }, + } }); }); }); }; + +exports.connectToIPCServer = connectToIPCServer; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/ipc-server.js b/modules/jest-atom-runner/src/ipc-server.js index 34c025d0..41aa9ef5 100644 --- a/modules/jest-atom-runner/src/ipc-server.js +++ b/modules/jest-atom-runner/src/ipc-server.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.startServer = void 0; + +function _nodeIpc() { + const data = _interopRequireDefault(require("node-ipc")); + + _nodeIpc = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,43 +25,30 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {ServerID, WorkerID} from './utils'; - -export opaque type Socket = any; - -export type IPCServer = { - start: () => void, - stop: () => void, - on: (WorkerID, (message: string, Socket) => void) => void, - emit: (Socket, WorkerID, message: string) => void, -}; - -import ipc from 'node-ipc'; - let started = false; -export const startServer = ({ - serverID, -}: { - serverID: ServerID, -}): Promise => { +const startServer = ({ + serverID +}) => { if (started) { throw new Error('IPC server can only be started once'); } + return new Promise(resolve => { started = true; - ipc.config.id = serverID; - ipc.config.retry = 1500; - ipc.config.silent = true; + _nodeIpc().default.config.id = serverID; + _nodeIpc().default.config.retry = 1500; + _nodeIpc().default.config.silent = true; - ipc.serve(() => { - resolve(ipc.server); + _nodeIpc().default.serve(() => { + resolve(_nodeIpc().default.server); }); - ipc.server.start(); + _nodeIpc().default.server.start(); }); }; + +exports.startServer = startServer; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/types.js b/modules/jest-atom-runner/src/types.js index 0c7bc3cd..9a390c31 100644 --- a/modules/jest-atom-runner/src/types.js +++ b/modules/jest-atom-runner/src/types.js @@ -1,67 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -export type GlobalConfig = { - maxWorkers: number, - watch: boolean, - watchAll: boolean, -}; -export type ProjectConfig = {}; -export type Resolver = {}; -export type RawModuleMap = {}; - -export type Context = { - moduleMap: { - getRawModuleMap(): RawModuleMap, - }, - config: ProjectConfig, -}; -export type Test = { - context: Context, - path: string, -}; - -export type Watcher = any; - -export type TestResult = { - console: ?Array, - failureMessage: ?string, - numFailingTests: number, - numPassingTests: number, - numPendingTests: number, - perfStats: {end: number, start: number}, - snapshot: { - added: number, - fileDeleted: boolean, - matched: number, - unchecked: number, - unmatched: number, - updated: number, - uncheckedKeys: Array, - }, - testFilePath: string, - testResults: Array<{ - ancestorTitles: Array, - duration: number, - failureMessages: Array, - fullName: string, - location: any, - numPassingAsserts: number, - status: 'passed' | 'failed' | 'skipped', - title: string, - }>, - sourceMaps: {}, - skipped: boolean, - displayName: string, - leaks: boolean, - testExecError: ?string, -}; +"use strict"; \ No newline at end of file diff --git a/modules/jest-atom-runner/src/utils.js b/modules/jest-atom-runner/src/utils.js index 20cd91e1..62e41ab6 100644 --- a/modules/jest-atom-runner/src/utils.js +++ b/modules/jest-atom-runner/src/utils.js @@ -1,3 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.buildFailureTestResult = exports.parseMessage = exports.makeMessage = exports.parseJSON = exports.MESSAGE_TYPES = exports.extractIPCIDsFromFilePath = exports.parseIPCIDs = exports.mergeIPCIDs = exports.makeUniqWorkerId = exports.makeUniqServerId = exports.rand = void 0; + +var _path = _interopRequireDefault(require("path")); + +function _jestMessageUtil() { + const data = require("jest-message-util"); + + _jestMessageUtil = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,69 +27,75 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-disable nuclide-internal/prefer-nuclide-uri */ +// server id and worker id merged into one string +const IPC_IDS_SEPARATOR = '_'; -import type {TestResult, ProjectConfig, GlobalConfig} from './types'; -export opaque type IPCID = string; // server id and worker id merged into one string -export opaque type WorkerID = string; -export opaque type ServerID = string; +const rand = () => Math.floor(Math.random() * 10000000); -import path from 'path'; -import {formatExecError} from 'jest-message-util'; +exports.rand = rand; -const IPC_IDS_SEPARATOR = '_'; +const makeUniqServerId = () => `jest-atom-runner-ipc-server-${Date.now() + rand()}`; -export const rand = () => Math.floor(Math.random() * 10000000); +exports.makeUniqServerId = makeUniqServerId; -export const makeUniqServerId = (): ServerID => - `jest-atom-runner-ipc-server-${Date.now() + rand()}`; +const makeUniqWorkerId = () => `jest-atom-runner-ipc-worker-${Date.now() + rand()}`; -export const makeUniqWorkerId = (): WorkerID => - `jest-atom-runner-ipc-worker-${Date.now() + rand()}`; +exports.makeUniqWorkerId = makeUniqWorkerId; -export const mergeIPCIDs = ({ +const mergeIPCIDs = ({ serverID, - workerID, -}: { - serverID: ServerID, - workerID: WorkerID, + workerID }) => `${serverID}${IPC_IDS_SEPARATOR}${workerID}`; -export const parseIPCIDs = (mergedIDs: IPCID) => { - const [serverID, workerID] = mergedIDs.split(IPC_IDS_SEPARATOR); - return {serverID, workerID}; -}; +exports.mergeIPCIDs = mergeIPCIDs; -// The only way atom allows us to pass data to it is in the form of a file path. +const parseIPCIDs = mergedIDs => { + const [serverID, workerID] = mergedIDs.split(IPC_IDS_SEPARATOR); + return { + serverID, + workerID + }; +}; // The only way atom allows us to pass data to it is in the form of a file path. // So we pass a non-existing file path to it that encodes server and worker IDs, // that we later parse and use to communicate back with the parent process. -export const extractIPCIDsFromFilePath = ( - passedFilePath: string, -): {serverID: ServerID, workerID: WorkerID} => { - const {serverID, workerID} = parseIPCIDs(path.basename(passedFilePath)); - return {serverID, workerID}; + + +exports.parseIPCIDs = parseIPCIDs; + +const extractIPCIDsFromFilePath = passedFilePath => { + const { + serverID, + workerID + } = parseIPCIDs(_path.default.basename(passedFilePath)); + return { + serverID, + workerID + }; }; -export const MESSAGE_TYPES = Object.freeze({ +exports.extractIPCIDsFromFilePath = extractIPCIDsFromFilePath; +const MESSAGE_TYPES = Object.freeze({ INITIALIZE: 'INITIALIZE', DATA: 'DATA', RUN_TEST: 'RUN_TEST', TEST_RESULT: 'TEST_RESULT', TEST_FAILURE: 'TEST_FAILURE', - SHUT_DOWN: 'SHUT_DOWN', + SHUT_DOWN: 'SHUT_DOWN' }); +exports.MESSAGE_TYPES = MESSAGE_TYPES; -export type MessageType = $Values; - -export const parseJSON = (str: ?string): Object => { +const parseJSON = str => { if (str == null) { throw new Error('String needs to be passed when parsing JSON'); } + let data; + try { data = JSON.parse(str); } catch (error) { @@ -78,33 +105,33 @@ export const parseJSON = (str: ?string): Object => { return data; }; -export const makeMessage = ({ +exports.parseJSON = parseJSON; + +const makeMessage = ({ messageType, - data, -}: { - messageType: MessageType, - data?: string, + data }) => `${messageType}-${data || ''}`; -export const parseMessage = (message: string) => { - const messageType: messageType = Object.values(MESSAGE_TYPES).find(msgType => - message.startsWith((msgType: any)), - ); +exports.makeMessage = makeMessage; + +const parseMessage = message => { + const messageType = Object.values(MESSAGE_TYPES).find(msgType => message.startsWith(msgType)); + if (!messageType) { throw new Error(`IPC message of unknown type. Message must start from one of the following strings representing types followed by "-'. known types: ${JSON.stringify(MESSAGE_TYPES)}`); } - return {messageType, data: message.slice(messageType.length + 1)}; + return { + messageType, + data: message.slice(messageType.length + 1) + }; }; -export const buildFailureTestResult = ( - testPath: string, - err: Error, - config: ProjectConfig, - globalConfig: GlobalConfig, -): TestResult => { - const failureMessage = formatExecError(err, config, globalConfig); +exports.parseMessage = parseMessage; + +const buildFailureTestResult = (testPath, err, config, globalConfig) => { + const failureMessage = (0, _jestMessageUtil().formatExecError)(err, config, globalConfig); return { console: null, displayName: '', @@ -115,7 +142,7 @@ export const buildFailureTestResult = ( numPendingTests: 0, perfStats: { end: 0, - start: 0, + start: 0 }, skipped: false, snapshot: { @@ -125,16 +152,17 @@ export const buildFailureTestResult = ( unchecked: 0, uncheckedKeys: [], unmatched: 0, - updated: 0, + updated: 0 }, sourceMaps: {}, testExecError: failureMessage, testFilePath: testPath, - testResults: [], + testResults: [] }; }; -export default { +exports.buildFailureTestResult = buildFailureTestResult; +var _default = { rand, makeUniqServerId, makeUniqWorkerId, @@ -144,5 +172,6 @@ export default { makeMessage, MESSAGE_TYPES, parseJSON, - buildFailureTestResult, + buildFailureTestResult }; +exports.default = _default; \ No newline at end of file diff --git a/modules/nuclide-adb/__tests__/parsePsOutput-test.js b/modules/nuclide-adb/__tests__/parsePsOutput-test.js index 07f7f331..a9cba7d8 100644 --- a/modules/nuclide-adb/__tests__/parsePsOutput-test.js +++ b/modules/nuclide-adb/__tests__/parsePsOutput-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _ps() { + const data = require("../lib/common/ps"); + + _ps = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,72 +18,45 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {parsePsTableOutput} from '../lib/common/ps'; - describe('parsePsOutput', () => { it('splits the output by message', () => { - const fakePsOutput = - 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + - 'u0_a2 2326 1257 1113392 38148 SyS_epoll_ 00000000 S com.android.providers.calendar\n' + - 'u0_a16 2359 1257 1109708 33484 SyS_epoll_ 00000000 S com.android.managedprovisioning\n' + - 'u0_a48 2386 1257 1215388 56116 SyS_epoll_ 00000000 S com.google.android.apps.maps\n'; - - let parsed = parsePsTableOutput(fakePsOutput, ['user', 'pid', 'name']); + const fakePsOutput = 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'u0_a2 2326 1257 1113392 38148 SyS_epoll_ 00000000 S com.android.providers.calendar\n' + 'u0_a16 2359 1257 1109708 33484 SyS_epoll_ 00000000 S com.android.managedprovisioning\n' + 'u0_a48 2386 1257 1215388 56116 SyS_epoll_ 00000000 S com.google.android.apps.maps\n'; + let parsed = (0, _ps().parsePsTableOutput)(fakePsOutput, ['user', 'pid', 'name']); expect(parsed.length).toBe(3); - expect(parsed[0].user).toEqual('u0_a2'); expect(parsed[0].pid).toEqual('2326'); expect(parsed[0].name).toEqual('com.android.providers.calendar'); - expect(parsed[1].user).toEqual('u0_a16'); expect(parsed[1].pid).toEqual('2359'); expect(parsed[1].name).toEqual('com.android.managedprovisioning'); - expect(parsed[2].user).toEqual('u0_a48'); expect(parsed[2].pid).toEqual('2386'); - expect(parsed[2].name).toEqual('com.google.android.apps.maps'); - - // Try another output with different column ordering to confirm column mapping is working. - const fakePsOutput2 = - 'PID USER PPID VSIZE RSS WCHAN PC NAME\n' + - '2326 u0_a2 1257 1113392 38148 SyS_epoll_ 00000000 S com.android.providers.calendar\n' + - '2359 u0_a16 1257 1109708 33484 SyS_epoll_ 00000000 S com.android.managedprovisioning\n' + - '2386 u0_a48 1257 1215388 56116 SyS_epoll_ 00000000 S com.google.android.apps.maps\n'; + expect(parsed[2].name).toEqual('com.google.android.apps.maps'); // Try another output with different column ordering to confirm column mapping is working. - parsed = parsePsTableOutput(fakePsOutput2, ['user', 'pid']); + const fakePsOutput2 = 'PID USER PPID VSIZE RSS WCHAN PC NAME\n' + '2326 u0_a2 1257 1113392 38148 SyS_epoll_ 00000000 S com.android.providers.calendar\n' + '2359 u0_a16 1257 1109708 33484 SyS_epoll_ 00000000 S com.android.managedprovisioning\n' + '2386 u0_a48 1257 1215388 56116 SyS_epoll_ 00000000 S com.google.android.apps.maps\n'; + parsed = (0, _ps().parsePsTableOutput)(fakePsOutput2, ['user', 'pid']); expect(parsed.length).toBe(3); - expect(parsed[0].user).toEqual('u0_a2'); expect(parsed[0].pid).toEqual('2326'); expect(parsed[0].name).toBeUndefined(); - expect(parsed[1].user).toEqual('u0_a16'); expect(parsed[1].pid).toEqual('2359'); expect(parsed[1].name).toBeUndefined(); - expect(parsed[2].user).toEqual('u0_a48'); expect(parsed[2].pid).toEqual('2386'); - expect(parsed[2].name).toBeUndefined(); - - // Test Android 8.0 format - const newPsOutput = - 'USER PID PPID VSZ RSS WCHAN ADDR S NAME\n' + - 'root 1 0 10612 3528 SyS_epoll_wait 0 S init\n' + - 'root 2 0 0 0 kthreadd 0 S [kthreadd]\n'; + expect(parsed[2].name).toBeUndefined(); // Test Android 8.0 format - parsed = parsePsTableOutput(newPsOutput, ['user', 'pid', 'name']); + const newPsOutput = 'USER PID PPID VSZ RSS WCHAN ADDR S NAME\n' + 'root 1 0 10612 3528 SyS_epoll_wait 0 S init\n' + 'root 2 0 0 0 kthreadd 0 S [kthreadd]\n'; + parsed = (0, _ps().parsePsTableOutput)(newPsOutput, ['user', 'pid', 'name']); expect(parsed.length).toBe(2); - expect(parsed[0].user).toEqual('root'); expect(parsed[0].pid).toEqual('1'); expect(parsed[0].name).toEqual('init'); - expect(parsed[1].user).toEqual('root'); expect(parsed[1].pid).toEqual('2'); expect(parsed[1].name).toEqual('[kthreadd]'); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-adb/lib/Adb.js b/modules/nuclide-adb/lib/Adb.js index 626f5711..3719f2ff 100644 --- a/modules/nuclide-adb/lib/Adb.js +++ b/modules/nuclide-adb/lib/Adb.js @@ -1,3 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Adb = void 0; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _ps() { + const data = require("./common/ps"); + + _ps = function () { + return data; + }; + + return data; +} + +function _process() { + const data = require("../../nuclide-commons/process"); + + _process = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,203 +47,124 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {AndroidJavaProcess, SimpleProcess, AdbDevice} from './types'; -import type {LegacyProcessMessage} from 'nuclide-commons/process'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import invariant from 'assert'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {Observable} from 'rxjs'; -import {parsePsTableOutput} from './common/ps'; -import {runCommand, observeProcess} from 'nuclide-commons/process'; - const ADB_TIMEOUT = 5000; -export class Adb { - _serial: string; - - constructor(serial: string) { +class Adb { + constructor(serial) { this._serial = serial; } - getAndroidProp(key: string): Observable { + getAndroidProp(key) { return this.runShortCommand('shell', 'getprop', key).map(s => s.trim()); } - getDeviceArchitecture(): Observable { + getDeviceArchitecture() { return this.getAndroidProp('ro.product.cpu.abi'); } - async getInstalledPackages(): Promise> { + async getInstalledPackages() { const prefix = 'package:'; - const stdout = await this.runShortCommand( - 'shell', - 'pm', - 'list', - 'packages', - ).toPromise(); - return stdout - .trim() - .split(/\s+/) - .map(s => s.substring(prefix.length)); - } - - async isPackageInstalled(pkg: string): Promise { + const stdout = await this.runShortCommand('shell', 'pm', 'list', 'packages').toPromise(); + return stdout.trim().split(/\s+/).map(s => s.substring(prefix.length)); + } + + async isPackageInstalled(pkg) { const packages = await this.getInstalledPackages(); return packages.includes(pkg); } - getDeviceModel(): Observable { - return this.getAndroidProp('ro.product.model').map( - s => (s === 'sdk' ? 'emulator' : s), - ); + getDeviceModel() { + return this.getAndroidProp('ro.product.model').map(s => s === 'sdk' ? 'emulator' : s); } - getAPIVersion(): Observable { + getAPIVersion() { return this.getAndroidProp('ro.build.version.sdk'); } - getBrand(): Observable { + getBrand() { return this.getAndroidProp('ro.product.brand'); } - getManufacturer(): Observable { + getManufacturer() { return this.getAndroidProp('ro.product.manufacturer'); } - getDeviceInfo(): Observable> { - const unknownCB = () => Observable.of(''); - return Observable.forkJoin( - this.getDeviceArchitecture().catch(unknownCB), - this.getAPIVersion().catch(unknownCB), - this.getDeviceModel().catch(unknownCB), - this.getOSVersion().catch(unknownCB), - this.getManufacturer().catch(unknownCB), - this.getBrand().catch(unknownCB), - this.getWifiIp().catch(unknownCB), - ).map( - ([ - architecture, - apiVersion, - model, - android_version, - manufacturer, - brand, - wifi_ip, - ]) => { - return new Map([ - ['name', this._serial], - ['adb_port', '5037'], - ['architecture', architecture], - ['api_version', apiVersion], - ['model', model], - ['android_version', android_version], - ['manufacturer', manufacturer], - ['brand', brand], - ['wifi_ip', wifi_ip], - ]); - }, - ); - } - - getWifiIp(): Observable { - return this.runShortCommand('shell', 'ip', 'addr', 'show', 'wlan0').map( - lines => { - const line = lines.split(/\n/).filter(l => l.includes('inet'))[0]; - if (line == null) { - return ''; - } - const rawIp = line.trim().split(/\s+/)[1]; - return rawIp.substring(0, rawIp.indexOf('/')); - }, - ); + getDeviceInfo() { + const unknownCB = () => _RxMin.Observable.of(''); + + return _RxMin.Observable.forkJoin(this.getDeviceArchitecture().catch(unknownCB), this.getAPIVersion().catch(unknownCB), this.getDeviceModel().catch(unknownCB), this.getOSVersion().catch(unknownCB), this.getManufacturer().catch(unknownCB), this.getBrand().catch(unknownCB), this.getWifiIp().catch(unknownCB)).map(([architecture, apiVersion, model, android_version, manufacturer, brand, wifi_ip]) => { + return new Map([['name', this._serial], ['adb_port', '5037'], ['architecture', architecture], ['api_version', apiVersion], ['model', model], ['android_version', android_version], ['manufacturer', manufacturer], ['brand', brand], ['wifi_ip', wifi_ip]]); + }); } - // In some android devices, we have to kill the package, not the process. + getWifiIp() { + return this.runShortCommand('shell', 'ip', 'addr', 'show', 'wlan0').map(lines => { + const line = lines.split(/\n/).filter(l => l.includes('inet'))[0]; + + if (line == null) { + return ''; + } + + const rawIp = line.trim().split(/\s+/)[1]; + return rawIp.substring(0, rawIp.indexOf('/')); + }); + } // In some android devices, we have to kill the package, not the process. // http://stackoverflow.com/questions/17154961/adb-shell-operation-not-permitted - async stopProcess(packageName: string, pid: number): Promise { - await Promise.all([ - this.runShortCommand( - 'shell', - 'am', - 'force-stop', - packageName, - ).toPromise(), - this.runShortCommand('shell', 'kill', '-9', `${pid}`).toPromise(), - this.runShortCommand( - 'shell', - 'run-as', - packageName, - 'kill', - '-9', - `${pid}`, - ).toPromise(), - ]); - } - - getOSVersion(): Observable { + + + async stopProcess(packageName, pid) { + await Promise.all([this.runShortCommand('shell', 'am', 'force-stop', packageName).toPromise(), this.runShortCommand('shell', 'kill', '-9', `${pid}`).toPromise(), this.runShortCommand('shell', 'run-as', packageName, 'kill', '-9', `${pid}`).toPromise()]); + } + + getOSVersion() { return this.getAndroidProp('ro.build.version.release'); } - installPackage(packagePath: NuclideUri): Observable { + installPackage(packagePath) { // TODO(T17463635) - invariant(!nuclideUri.isRemote(packagePath)); - // The -d option allows downgrades, which happen frequently during development. - return this.getAPIVersion() - .map(version => parseInt(version, 10) >= 17) - .catch(() => Observable.of(false)) - .switchMap(canUseDowngradeOption => - this.runLongCommand( - ...[ - 'install', - '-r', - ...(canUseDowngradeOption ? ['-d'] : []), - packagePath, - ], - ), - ); - } - - uninstallPackage(packageName: string): Observable { + if (!!_nuclideUri().default.isRemote(packagePath)) { + throw new Error("Invariant violation: \"!nuclideUri.isRemote(packagePath)\""); + } // The -d option allows downgrades, which happen frequently during development. + + + return this.getAPIVersion().map(version => parseInt(version, 10) >= 17).catch(() => _RxMin.Observable.of(false)).switchMap(canUseDowngradeOption => this.runLongCommand(...['install', '-r', ...(canUseDowngradeOption ? ['-d'] : []), packagePath])); + } + + uninstallPackage(packageName) { // TODO(T17463635) return this.runLongCommand('uninstall', packageName); } - async getForwardSpec(pid: number): Promise { - const specLines = await this.runShortCommand( - 'forward', - '--list', - ).toPromise(); + async getForwardSpec(pid) { + const specLines = await this.runShortCommand('forward', '--list').toPromise(); const specs = specLines.split(/\n/).map(line => { const cols = line.split(/\s+/); return { spec: cols[1], - target: cols[2], + target: cols[2] }; }); const matchingSpec = specs.find(spec => spec.target === `jdwp:${pid}`); + if (matchingSpec != null) { return matchingSpec.spec; } + return null; } - async forwardJdwpPortToPid(tcpPort: number, pid: number): Promise { - await this.runShortCommand( - 'forward', - `tcp:${tcpPort}`, - `jdwp:${pid}`, - ).toPromise(); + async forwardJdwpPortToPid(tcpPort, pid) { + await this.runShortCommand('forward', `tcp:${tcpPort}`, `jdwp:${pid}`).toPromise(); return this.getForwardSpec(pid); } - async removeJdwpForwardSpec(spec: ?string): Promise { + async removeJdwpForwardSpec(spec) { let output; let result = ''; + if (spec != null) { output = this.runLongCommand('forward', '--remove', spec); } else { @@ -222,13 +184,7 @@ export class Adb { return result; } - async launchActivity( - packageName: string, - activity: string, - debug: boolean, - action: ?string, - parameters: ?Map, - ): Promise { + async launchActivity(packageName, activity, debug, action, parameters) { if (debug) { // Enable "wait for debugger" semantics for the next launch of // the specified package. @@ -236,267 +192,245 @@ export class Adb { } const args = ['shell', 'am', 'start']; + if (action != null) { args.push('-a', action); } + if (parameters != null) { for (const [key, parameter] of parameters) { args.push('-e', key, parameter); } } + args.push('-W', '-n'); args.push(`${packageName}/${activity}`); return this.runShortCommand(...args).toPromise(); } - async launchMainActivity( - packageName: string, - debug: boolean, - ): Promise { + async launchMainActivity(packageName, debug) { if (debug) { // Enable "wait for debugger" semantics for the next launch of // the specified package. await this.setDebugApp(packageName, false); } - const args = [ - 'shell', - 'monkey', - '-p', - `${packageName}`, - '-c', - 'android.intent.category.LAUNCHER', - '1', - ]; + const args = ['shell', 'monkey', '-p', `${packageName}`, '-c', 'android.intent.category.LAUNCHER', '1']; return this.runShortCommand(...args).toPromise(); } - async launchService( - packageName: string, - serviceName: string, - debug: boolean, - ): Promise { + async launchService(packageName, serviceName, debug) { if (debug) { // Enable "wait for debugger" semantics for the next launch of // the specified package. await this.setDebugApp(packageName, false); } - const args = [ - 'shell', - 'am', - 'startservice', - `${packageName}/${serviceName}`, - ]; + const args = ['shell', 'am', 'startservice', `${packageName}/${serviceName}`]; return this.runShortCommand(...args).toPromise(); } - setDebugApp(packageName: string, persist: boolean): Promise { + setDebugApp(packageName, persist) { const args = ['shell', 'am', 'set-debug-app', '-w']; if (persist) { args.push('--persistent'); } + args.push(`${packageName}`); return this.runShortCommand(...args).toPromise(); } - _dumpsysPackage(): Observable { + _dumpsysPackage() { return this.runShortCommand('shell', 'dumpsys', 'package'); } - activityExists(packageName: string, activity: string): Promise { + activityExists(packageName, activity) { const packageActivityString = `${packageName}/${activity}`; - return this._dumpsysPackage() - .map(stdout => stdout.includes(packageActivityString)) - .toPromise(); + return this._dumpsysPackage().map(stdout => stdout.includes(packageActivityString)).toPromise(); } - getAllAvailablePackages(): Promise> { - return this._dumpsysPackage() - .map(stdout => stdout.split('\n').map(line => line.trim())) - .toPromise(); + getAllAvailablePackages() { + return this._dumpsysPackage().map(stdout => stdout.split('\n').map(line => line.trim())).toPromise(); } - touchFile(path: string): Promise { + touchFile(path) { return this.runShortCommand('shell', 'touch', path).toPromise(); } - removeFile(path: string): Promise { + removeFile(path) { return this.runShortCommand('shell', 'rm', path).toPromise(); } - getDebuggableProcesses(): Observable> { + getDebuggableProcesses() { return this.getJavaProcesses(); } - getJavaProcesses(): Observable> { + getJavaProcesses() { const jdwpProcesses = new Set(); - return this.runShortCommand('shell', 'ps') - .map(stdout => { - const psOutput = stdout.trim(); - return parsePsTableOutput(psOutput, ['user', 'pid', 'name']); - }) - .switchMap(allProcesses => { - const map = new Map(); - allProcesses - .filter(row => row != null) - .forEach(proc => map.set(proc.pid, proc)); - return Promise.resolve(map); - }) - .switchMap(allProcessesMap => { - return this.runLongCommand('jdwp').map(output => { - if (output.kind === 'stdout') { - const block: string = output.data; - block.split(/\s+/).forEach(pid => { - const proc = allProcessesMap.get(pid); - if (proc != null) { - jdwpProcesses.add(proc); - } - }); - } - }); - }) - .timeout(1000) - .catch(error => Observable.of([])) - .switchMap(() => { - return Promise.resolve(Array.from(jdwpProcesses)); + return this.runShortCommand('shell', 'ps').map(stdout => { + const psOutput = stdout.trim(); + return (0, _ps().parsePsTableOutput)(psOutput, ['user', 'pid', 'name']); + }).switchMap(allProcesses => { + const map = new Map(); + allProcesses.filter(row => row != null).forEach(proc => map.set(proc.pid, proc)); + return Promise.resolve(map); + }).switchMap(allProcessesMap => { + return this.runLongCommand('jdwp').map(output => { + if (output.kind === 'stdout') { + const block = output.data; + block.split(/\s+/).forEach(pid => { + const proc = allProcessesMap.get(pid); + + if (proc != null) { + jdwpProcesses.add(proc); + } + }); + } }); + }).timeout(1000).catch(error => _RxMin.Observable.of([])).switchMap(() => { + return Promise.resolve(Array.from(jdwpProcesses)); + }); } - async dumpsysPackage(pkg: string): Promise { + async dumpsysPackage(pkg) { if (!(await this.isPackageInstalled(pkg))) { return null; } + return this.runShortCommand('shell', 'dumpsys', 'package', pkg).toPromise(); } - getDeviceArgs(): Array { + getDeviceArgs() { return this._serial !== '' ? ['-s', this._serial] : []; } - getProcesses(): Observable> { - return this.runShortCommand('shell', 'ps').map(stdout => - stdout.split(/\n/).map(line => { - const info = line.trim().split(/\s+/); - return {user: info[0], pid: info[1], name: info[info.length - 1]}; - }), - ); + getProcesses() { + return this.runShortCommand('shell', 'ps').map(stdout => stdout.split(/\n/).map(line => { + const info = line.trim().split(/\s+/); + return { + user: info[0], + pid: info[1], + name: info[info.length - 1] + }; + })); } - runShortCommand(...command: string[]): Observable { - return runCommand('adb', this.getDeviceArgs().concat(command)); + runShortCommand(...command) { + return (0, _process().runCommand)('adb', this.getDeviceArgs().concat(command)); } - runLongCommand(...command: string[]): Observable { + runLongCommand(...command) { // TODO(T17463635) - return observeProcess('adb', this.getDeviceArgs().concat(command), { + return (0, _process().observeProcess)('adb', this.getDeviceArgs().concat(command), { killTreeWhenDone: true, - /* TODO(T17353599) */ isExitError: () => false, - }).catch(error => Observable.of({kind: 'error', error})); // TODO(T17463635) + + /* TODO(T17353599) */ + isExitError: () => false + }).catch(error => _RxMin.Observable.of({ + kind: 'error', + error + })); // TODO(T17463635) } - static _parseDevicesCommandOutput(stdout: string): Array { + static _parseDevicesCommandOutput(stdout) { const nameFrequency = new Map(); - - return stdout - .split(/\n+/g) - .slice(1) - .filter(s => s.length > 0 && !s.trim().startsWith('*')) - .map(s => s.split(/\s+/g)) - .filter(a => a[0] !== '') - .map(a => { - const serial = a[0]; - const props = a.slice(2); - let product; - let model; - let device; - let usb; - let transportId; - for (const prop of props) { - const pair = prop.split(':'); - if (pair.length !== 2) { - continue; - } - switch (pair[0]) { - case 'product': - product = pair[1]; - break; - case 'model': - model = pair[1]; - break; - case 'device': - device = pair[1]; - break; - case 'usb': - usb = pair[1]; - break; - case 'transport_id': - transportId = pair[1]; - break; - default: - break; - } - } - const displayName = - serial.startsWith('emulator') || - serial.startsWith('localhost:') || - model == null - ? serial - : model; - - const count = nameFrequency.get(displayName); - if (count == null) { - nameFrequency.set(displayName, 1); - } else { - nameFrequency.set(displayName, count + 1); + return stdout.split(/\n+/g).slice(1).filter(s => s.length > 0 && !s.trim().startsWith('*')).map(s => s.split(/\s+/g)).filter(a => a[0] !== '').map(a => { + const serial = a[0]; + const props = a.slice(2); + let product; + let model; + let device; + let usb; + let transportId; + + for (const prop of props) { + const pair = prop.split(':'); + + if (pair.length !== 2) { + continue; } - return { - serial, - displayName, - product, - model, - device, - usb, - transportId, - }; - }) - .map(device => { - const {displayName, serial} = device; - if (displayName === serial || nameFrequency.get(displayName === 1)) { - return device; - } else { - return { - ...device, - displayName: `${displayName} - ${serial}`, - }; + switch (pair[0]) { + case 'product': + product = pair[1]; + break; + + case 'model': + model = pair[1]; + break; + + case 'device': + device = pair[1]; + break; + + case 'usb': + usb = pair[1]; + break; + + case 'transport_id': + transportId = pair[1]; + break; + + default: + break; } - }); + } + + const displayName = serial.startsWith('emulator') || serial.startsWith('localhost:') || model == null ? serial : model; + const count = nameFrequency.get(displayName); + + if (count == null) { + nameFrequency.set(displayName, 1); + } else { + nameFrequency.set(displayName, count + 1); + } + + return { + serial, + displayName, + product, + model, + device, + usb, + transportId + }; + }).map(device => { + const { + displayName, + serial + } = device; + + if (displayName === serial || nameFrequency.get(displayName === 1)) { + return device; + } else { + return Object.assign({}, device, { + displayName: `${displayName} - ${serial}` + }); + } + }); } - static getDevices(): Promise> { - return runCommand('adb', ['devices', '-l']) - .map(stdout => this._parseDevicesCommandOutput(stdout)) - .timeout(ADB_TIMEOUT) - .toPromise(); + static getDevices() { + return (0, _process().runCommand)('adb', ['devices', '-l']).map(stdout => this._parseDevicesCommandOutput(stdout)).timeout(ADB_TIMEOUT).toPromise(); } - static killServer(): Promise { - return runCommand('adb', ['kill-server']) - .mapTo(undefined) - .toPromise(); + static killServer() { + return (0, _process().runCommand)('adb', ['kill-server']).mapTo(undefined).toPromise(); } - static getVersion(): Promise { - return runCommand('adb', ['version']) - .map(versionString => { - const version = versionString.match(/version (\d+.\d+.\d+)/); - if (version) { - return version[1]; - } - throw new Error(`No version found with "${versionString}"`); - }) - .toPromise(); + static getVersion() { + return (0, _process().runCommand)('adb', ['version']).map(versionString => { + const version = versionString.match(/version (\d+.\d+.\d+)/); + + if (version) { + return version[1]; + } + + throw new Error(`No version found with "${versionString}"`); + }).toPromise(); } + } + +exports.Adb = Adb; \ No newline at end of file diff --git a/modules/nuclide-adb/lib/AdbDevicePoller.js b/modules/nuclide-adb/lib/AdbDevicePoller.js index 7bf459ee..aac2b14a 100644 --- a/modules/nuclide-adb/lib/AdbDevicePoller.js +++ b/modules/nuclide-adb/lib/AdbDevicePoller.js @@ -1,3 +1,95 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.observeAndroidDevices = observeAndroidDevices; +exports.adbDeviceForIdentifier = adbDeviceForIdentifier; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _SimpleCache() { + const data = require("../../nuclide-commons/SimpleCache"); + + _SimpleCache = function () { + return data; + }; + + return data; +} + +function _shallowequal() { + const data = _interopRequireDefault(require("shallowequal")); + + _shallowequal = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _expected() { + const data = require("../../nuclide-commons/expected"); + + _expected = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,90 +98,43 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {Expected} from 'nuclide-commons/expected'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {AdbDevice} from './types'; - -import {getLogger} from 'log4js'; -import {arrayEqual} from 'nuclide-commons/collection'; -import {SimpleCache} from 'nuclide-commons/SimpleCache'; // $FlowIgnore untyped import -import shallowEqual from 'shallowequal'; -import {Observable} from 'rxjs'; -import {Expect, expectedEqual} from 'nuclide-commons/expected'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {track} from 'nuclide-commons/analytics'; -import {getAdbServiceByNuclideUri} from './utils'; - -export function observeAndroidDevices( - host: NuclideUri, -): Observable>> { - const serviceUri = nuclideUri.isRemote(host) - ? nuclideUri.createRemoteUri(nuclideUri.getHostname(host), '/') - : ''; +function observeAndroidDevices(host) { + const serviceUri = _nuclideUri().default.isRemote(host) ? _nuclideUri().default.createRemoteUri(_nuclideUri().default.getHostname(host), '/') : ''; return pollersForUris.getOrCreate(serviceUri, () => { - return Observable.interval(2000) - .startWith(0) - .exhaustMap(() => { - const service = getAdbServiceByNuclideUri(serviceUri); - if (service == null) { - // Gracefully handle a lost remote connection - return Observable.of(Expect.pending()); - } - return Observable.fromPromise(service.getDeviceList()) - .map(devices => Expect.value(devices)) - .catch(error => { - const message = - error.code !== 'ENOENT' - ? error.message - : "'adb' not found in $PATH."; - return Observable.of( - Expect.error( - new Error("Can't fetch Android devices. " + message), - ), - ); - }); - }) - .distinctUntilChanged((a, b) => - expectedEqual( - a, - b, - (v1, v2) => arrayEqual(v1, v2, shallowEqual), - (e1, e2) => e1.message === e2.message, - ), - ) - .do(value => { - if (value.isError) { - const logger = getLogger('nuclide-adb'); - logger.warn(value.error.message); - track('nuclide-adb:device-poller:error', { - error: value.error, - host: serviceUri, - }); - } - }) - .publishReplay(1) - .refCount(); - }); -} + return _RxMin.Observable.interval(2000).startWith(0).exhaustMap(() => { + const service = (0, _utils().getAdbServiceByNuclideUri)(serviceUri); + + if (service == null) { + // Gracefully handle a lost remote connection + return _RxMin.Observable.of(_expected().Expect.pending()); + } -// This is a convenient way for any device panel plugins of type Android to get from Device to + return _RxMin.Observable.fromPromise(service.getDeviceList()).map(devices => _expected().Expect.value(devices)).catch(error => { + const message = error.code !== 'ENOENT' ? error.message : "'adb' not found in $PATH."; + return _RxMin.Observable.of(_expected().Expect.error(new Error("Can't fetch Android devices. " + message))); + }); + }).distinctUntilChanged((a, b) => (0, _expected().expectedEqual)(a, b, (v1, v2) => (0, _collection().arrayEqual)(v1, v2, _shallowequal().default), (e1, e2) => e1.message === e2.message)).do(value => { + if (value.isError) { + const logger = (0, _log4js().getLogger)('nuclide-adb'); + logger.warn(value.error.message); + (0, _analytics().track)('nuclide-adb:device-poller:error', { + error: value.error, + host: serviceUri + }); + } + }).publishReplay(1).refCount(); + }); +} // This is a convenient way for any device panel plugins of type Android to get from Device to // to the strongly typed AdbDevice. -export async function adbDeviceForIdentifier( - host: NuclideUri, - identifier: string, -): Promise { - const devices = await observeAndroidDevices(host) - .take(1) - .toPromise(); + + +async function adbDeviceForIdentifier(host, identifier) { + const devices = await observeAndroidDevices(host).take(1).toPromise(); return devices.getOrDefault([]).find(d => d.serial === identifier); } -const pollersForUris: SimpleCache< - string, - Observable>>, -> = new SimpleCache(); +const pollersForUris = new (_SimpleCache().SimpleCache)(); \ No newline at end of file diff --git a/modules/nuclide-adb/lib/AdbService.js b/modules/nuclide-adb/lib/AdbService.js index 4ec2603b..61e1c519 100644 --- a/modules/nuclide-adb/lib/AdbService.js +++ b/modules/nuclide-adb/lib/AdbService.js @@ -1,3 +1,90 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getDeviceInfo = getDeviceInfo; +exports.getProcesses = getProcesses; +exports.stopProcess = stopProcess; +exports.getDeviceList = getDeviceList; +exports.getPidFromPackageName = getPidFromPackageName; +exports.installPackage = installPackage; +exports.uninstallPackage = uninstallPackage; +exports.forwardJdwpPortToPid = forwardJdwpPortToPid; +exports.removeJdwpForwardSpec = removeJdwpForwardSpec; +exports.launchActivity = launchActivity; +exports.launchMainActivity = launchMainActivity; +exports.launchService = launchService; +exports.activityExists = activityExists; +exports.getAllAvailablePackages = getAllAvailablePackages; +exports.getJavaProcesses = getJavaProcesses; +exports.dumpsysPackage = dumpsysPackage; +exports.touchFile = touchFile; +exports.removeFile = removeFile; +exports.getAPIVersion = getAPIVersion; +exports.getDeviceArchitecture = getDeviceArchitecture; +exports.getInstalledPackages = getInstalledPackages; +exports.killServer = killServer; +exports.getApkManifest = getApkManifest; +exports.getVersion = getVersion; +exports.checkMuxStatus = checkMuxStatus; +exports.checkInMuxPort = checkInMuxPort; +exports.checkOutMuxPort = checkOutMuxPort; + +function _fsPromise() { + const data = _interopRequireDefault(require("../../nuclide-commons/fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _Adb() { + const data = require("./Adb"); + + _Adb = function () { + return data; + }; + + return data; +} + +function _Processes() { + const data = require("./common/Processes"); + + _Processes = function () { + return data; + }; + + return data; +} + +function _process() { + const data = require("../../nuclide-commons/process"); + + _process = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,188 +93,110 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {LegacyProcessMessage} from 'nuclide-commons/process'; -import type {AdbDevice, AndroidJavaProcess, Process} from './types'; - -import fsPromise from 'nuclide-commons/fsPromise'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {ConnectableObservable} from 'rxjs'; -import {Adb} from './Adb'; -import {Processes} from './common/Processes'; -import {runCommand} from 'nuclide-commons/process'; - -export function getDeviceInfo( - serial: string, -): ConnectableObservable> { - return new Adb(serial).getDeviceInfo().publish(); +function getDeviceInfo(serial) { + return new (_Adb().Adb)(serial).getDeviceInfo().publish(); } -export function getProcesses( - serial: string, - timeout: number, -): ConnectableObservable> { - return new Processes(new Adb(serial)).fetch(timeout).publish(); +function getProcesses(serial, timeout) { + return new (_Processes().Processes)(new (_Adb().Adb)(serial)).fetch(timeout).publish(); } -export async function stopProcess( - serial: string, - packageName: string, - pid: number, -): Promise { - return new Adb(serial).stopProcess(packageName, pid); +async function stopProcess(serial, packageName, pid) { + return new (_Adb().Adb)(serial).stopProcess(packageName, pid); } -export function getDeviceList(): Promise> { - return Adb.getDevices(); +function getDeviceList() { + return _Adb().Adb.getDevices(); } -export async function getPidFromPackageName( - serial: string, - packageName: string, -): Promise { - return new Processes(new Adb(serial)).getPidFromPackageName(packageName); +async function getPidFromPackageName(serial, packageName) { + return new (_Processes().Processes)(new (_Adb().Adb)(serial)).getPidFromPackageName(packageName); } -export function installPackage( - serial: string, - packagePath: NuclideUri, -): ConnectableObservable { +function installPackage(serial, packagePath) { // TODO(T17463635) - return new Adb(serial).installPackage(packagePath).publish(); + return new (_Adb().Adb)(serial).installPackage(packagePath).publish(); } -export function uninstallPackage( - serial: string, - packageName: string, -): ConnectableObservable { +function uninstallPackage(serial, packageName) { // TODO(T17463635) - return new Adb(serial).uninstallPackage(packageName).publish(); + return new (_Adb().Adb)(serial).uninstallPackage(packageName).publish(); } -export async function forwardJdwpPortToPid( - serial: string, - tcpPort: number, - pid: number, -): Promise { - return new Adb(serial).forwardJdwpPortToPid(tcpPort, pid); +async function forwardJdwpPortToPid(serial, tcpPort, pid) { + return new (_Adb().Adb)(serial).forwardJdwpPortToPid(tcpPort, pid); } -export async function removeJdwpForwardSpec( - serial: string, - spec: ?string, -): Promise { - return new Adb(serial).removeJdwpForwardSpec(spec); +async function removeJdwpForwardSpec(serial, spec) { + return new (_Adb().Adb)(serial).removeJdwpForwardSpec(spec); } -export async function launchActivity( - serial: string, - packageName: string, - activity: string, - debug: boolean, - action: ?string, - parameters: ?Map, -): Promise { - return new Adb(serial).launchActivity( - packageName, - activity, - debug, - action, - parameters, - ); +async function launchActivity(serial, packageName, activity, debug, action, parameters) { + return new (_Adb().Adb)(serial).launchActivity(packageName, activity, debug, action, parameters); } -export async function launchMainActivity( - serial: string, - packageName: string, - debug: boolean, -): Promise { - return new Adb(serial).launchMainActivity(packageName, debug); +async function launchMainActivity(serial, packageName, debug) { + return new (_Adb().Adb)(serial).launchMainActivity(packageName, debug); } -export async function launchService( - serial: string, - packageName: string, - serviceName: string, - debug: boolean, -): Promise { - return new Adb(serial).launchService(packageName, serviceName, debug); +async function launchService(serial, packageName, serviceName, debug) { + return new (_Adb().Adb)(serial).launchService(packageName, serviceName, debug); } -export async function activityExists( - serial: string, - packageName: string, - activity: string, -): Promise { - return new Adb(serial).activityExists(packageName, activity); +async function activityExists(serial, packageName, activity) { + return new (_Adb().Adb)(serial).activityExists(packageName, activity); } -export async function getAllAvailablePackages( - serial: string, -): Promise> { - return new Adb(serial).getAllAvailablePackages(); +async function getAllAvailablePackages(serial) { + return new (_Adb().Adb)(serial).getAllAvailablePackages(); } -export function getJavaProcesses( - serial: string, -): ConnectableObservable> { - return new Adb(serial).getJavaProcesses().publish(); +function getJavaProcesses(serial) { + return new (_Adb().Adb)(serial).getJavaProcesses().publish(); } -export async function dumpsysPackage( - serial: string, - identifier: string, -): Promise { - return new Adb(serial).dumpsysPackage(identifier); +async function dumpsysPackage(serial, identifier) { + return new (_Adb().Adb)(serial).dumpsysPackage(identifier); } -export async function touchFile(serial: string, path: string): Promise { - return new Adb(serial).touchFile(path); +async function touchFile(serial, path) { + return new (_Adb().Adb)(serial).touchFile(path); } -export async function removeFile( - serial: string, - path: string, -): Promise { - return new Adb(serial).removeFile(path); +async function removeFile(serial, path) { + return new (_Adb().Adb)(serial).removeFile(path); } -export async function getAPIVersion(serial: string): Promise { - return new Adb(serial).getAPIVersion().toPromise(); +async function getAPIVersion(serial) { + return new (_Adb().Adb)(serial).getAPIVersion().toPromise(); } -export async function getDeviceArchitecture(serial: string): Promise { - return new Adb(serial).getDeviceArchitecture().toPromise(); +async function getDeviceArchitecture(serial) { + return new (_Adb().Adb)(serial).getDeviceArchitecture().toPromise(); } -export async function getInstalledPackages( - serial: string, -): Promise> { - return new Adb(serial).getInstalledPackages(); +async function getInstalledPackages(serial) { + return new (_Adb().Adb)(serial).getInstalledPackages(); } -export async function killServer(): Promise { - return Adb.killServer(); +async function killServer() { + return _Adb().Adb.killServer(); } -async function getAaptBinary(buildToolsVersion: ?string): Promise { +async function getAaptBinary(buildToolsVersion) { if (process.env.ANDROID_SDK == null || buildToolsVersion == null) { return 'aapt'; } else { - const allBuildToolsPath = nuclideUri.join( - process.env.ANDROID_SDK, - 'build-tools', - ); - const exactBuildToolPath = nuclideUri.join( - allBuildToolsPath, - buildToolsVersion, - ); - const aaptPath = nuclideUri.join(exactBuildToolPath, 'aapt'); - if (await fsPromise.exists(aaptPath)) { + const allBuildToolsPath = _nuclideUri().default.join(process.env.ANDROID_SDK, 'build-tools'); + + const exactBuildToolPath = _nuclideUri().default.join(allBuildToolsPath, buildToolsVersion); + + const aaptPath = _nuclideUri().default.join(exactBuildToolPath, 'aapt'); + + if (await _fsPromise().default.exists(aaptPath)) { return aaptPath; } else { return 'aapt'; @@ -195,37 +204,29 @@ async function getAaptBinary(buildToolsVersion: ?string): Promise { } } -export async function getApkManifest( - apkPath: string, - buildToolsVersion: ?string, -): Promise { +async function getApkManifest(apkPath, buildToolsVersion) { const aaptBinary = await getAaptBinary(buildToolsVersion); - return runCommand(aaptBinary, ['dump', 'badging', apkPath]).toPromise(); + return (0, _process().runCommand)(aaptBinary, ['dump', 'badging', apkPath]).toPromise(); } -export async function getVersion(): Promise { - return Adb.getVersion(); +async function getVersion() { + return _Adb().Adb.getVersion(); } -export async function checkMuxStatus(): Promise { +async function checkMuxStatus() { try { - await runCommand('adbmux', ['status']) - .ignoreElements() - .toPromise(); + await (0, _process().runCommand)('adbmux', ['status']).ignoreElements().toPromise(); } catch (_) { return false; } + return true; } -export function checkInMuxPort(port: number): Promise { - return runCommand('adbmux', ['checkin', `${port}`]) - .ignoreElements() - .toPromise(); +function checkInMuxPort(port) { + return (0, _process().runCommand)('adbmux', ['checkin', `${port}`]).ignoreElements().toPromise(); } -export function checkOutMuxPort(port: number): Promise { - return runCommand('adbmux', ['checkout', `${port}`]) - .ignoreElements() - .toPromise(); -} +function checkOutMuxPort(port) { + return (0, _process().runCommand)('adbmux', ['checkout', `${port}`]).ignoreElements().toPromise(); +} \ No newline at end of file diff --git a/modules/nuclide-adb/lib/Tunneling.js b/modules/nuclide-adb/lib/Tunneling.js index 44e276e2..a27c67f5 100644 --- a/modules/nuclide-adb/lib/Tunneling.js +++ b/modules/nuclide-adb/lib/Tunneling.js @@ -1,3 +1,76 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.startTunnelingAdb = startTunnelingAdb; +exports.stopTunnelingAdb = stopTunnelingAdb; +exports.isAdbTunneled = isAdbTunneled; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _SimpleCache() { + const data = require("../../nuclide-commons/SimpleCache"); + + _SimpleCache = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _consumeFirstProvider() { + const data = _interopRequireDefault(require("../../nuclide-commons-atom/consumeFirstProvider")); + + _consumeFirstProvider = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _analytics() { + const data = require("../../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,184 +79,186 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {SshTunnelService} from 'nuclide-adb/lib/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {Subscription} from 'rxjs'; - -import invariant from 'assert'; -import {getLogger} from 'log4js'; -import {SimpleCache} from 'nuclide-commons/SimpleCache'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {Observable, Subject} from 'rxjs'; -import consumeFirstProvider from 'nuclide-commons-atom/consumeFirstProvider'; -import {getAdbServiceByNuclideUri} from './utils'; -import {track} from 'nuclide-commons/analytics'; - -export type AdbTunnelingOptions = { - adbMismatchErrorMessage?: string, -}; - -export function startTunnelingAdb( - uri: NuclideUri, - options?: AdbTunnelingOptions = {}, -): Observable<'ready'> { - if (!nuclideUri.isRemote(uri)) { - return Observable.of('ready').concat(Observable.never()); +function startTunnelingAdb(uri, options = {}) { + if (!_nuclideUri().default.isRemote(uri)) { + return _RxMin.Observable.of('ready').concat(_RxMin.Observable.never()); } - const {tunnels} = activeTunnels.getOrCreate(uri, (_, serviceUri) => { - invariant(typeof serviceUri === 'string'); - const adbService = getAdbServiceByNuclideUri(serviceUri); - const localAdbService = getAdbServiceByNuclideUri(''); - - const observable = Observable.defer(async () => { - const [adbVersion, localAdbVersion] = await Promise.all([ - adbService.getVersion(), - localAdbService.getVersion(), - ]); + + const { + tunnels + } = activeTunnels.getOrCreate(uri, (_, serviceUri) => { + if (!(typeof serviceUri === 'string')) { + throw new Error("Invariant violation: \"typeof serviceUri === 'string'\""); + } + + const adbService = (0, _utils().getAdbServiceByNuclideUri)(serviceUri); + const localAdbService = (0, _utils().getAdbServiceByNuclideUri)(''); + + const observable = _RxMin.Observable.defer(async () => { + const [adbVersion, localAdbVersion] = await Promise.all([adbService.getVersion(), localAdbService.getVersion()]); + if (adbVersion !== localAdbVersion) { - throw new Error( - `Your remote adb version differs from the local one: ${adbVersion} (remote) != ${localAdbVersion} (local).\n\n${options.adbMismatchErrorMessage || - ''}`, - ); + throw new Error(`Your remote adb version differs from the local one: ${adbVersion} (remote) != ${localAdbVersion} (local).\n\n${options.adbMismatchErrorMessage || ''}`); } + return adbService.checkMuxStatus(); - }) - .switchMap( - useAdbmux => - useAdbmux - ? checkInToAdbmux(serviceUri) - : openTunnelsManually(serviceUri), - ) - .catch(e => { - getLogger('nuclide-adb').error(e); - track('nuclide-adb:tunneling:error', {host: uri, error: e}); - throw e; - }) - .publishReplay(1); + }).switchMap(useAdbmux => useAdbmux ? checkInToAdbmux(serviceUri) : openTunnelsManually(serviceUri)).catch(e => { + (0, _log4js().getLogger)('nuclide-adb').error(e); + (0, _analytics().track)('nuclide-adb:tunneling:error', { + host: uri, + error: e + }); + throw e; + }).publishReplay(1); let adbmuxPort; - const subscription = observable - .subscribe(port => (adbmuxPort = port)) - .add(() => { - if (adbmuxPort != null) { - adbService.checkOutMuxPort(adbmuxPort); - adbmuxPort = null; - } - stopTunnelingAdb(uri); - }) - // Start everything! - .add(observable.connect()); + const subscription = observable.subscribe(port => adbmuxPort = port).add(() => { + if (adbmuxPort != null) { + adbService.checkOutMuxPort(adbmuxPort); + adbmuxPort = null; + } + stopTunnelingAdb(uri); + }) // Start everything! + .add(observable.connect()); return { subscription, - tunnels: observable, + tunnels: observable }; }); changes.next(); - return tunnels.mapTo('ready'); } -export function stopTunnelingAdb(uri: NuclideUri) { +function stopTunnelingAdb(uri) { activeTunnels.delete(uri); changes.next(); } -export function isAdbTunneled(uri: NuclideUri): Observable { - return changes - .startWith(undefined) - .map(() => activeTunnels.get(uri) != null) - .distinctUntilChanged(); +function isAdbTunneled(uri) { + return changes.startWith(undefined).map(() => activeTunnels.get(uri) != null).distinctUntilChanged(); } -const activeTunnels: SimpleCache< - NuclideUri, - {tunnels: Observable, subscription: Subscription}, -> = new SimpleCache({ - keyFactory: uri => - nuclideUri.createRemoteUri(nuclideUri.getHostname(uri), '/'), - dispose: value => value.subscription.unsubscribe(), +const activeTunnels = new (_SimpleCache().SimpleCache)({ + keyFactory: uri => _nuclideUri().default.createRemoteUri(_nuclideUri().default.getHostname(uri), '/'), + dispose: value => value.subscription.unsubscribe() }); -const changes: Subject = new Subject(); - -function checkInToAdbmux(host: NuclideUri): Observable { - return Observable.defer(async () => { - const service: SshTunnelService = await consumeFirstProvider( - 'nuclide.ssh-tunnel', - ); - invariant(service); +const changes = new _RxMin.Subject(); + +function checkInToAdbmux(host) { + return _RxMin.Observable.defer(async () => { + const service = await (0, _consumeFirstProvider().default)('nuclide.ssh-tunnel'); + + if (!service) { + throw new Error("Invariant violation: \"service\""); + } + const port = await service.getAvailableServerPort(host); - return {service, port}; - }) - .switchMap(({service, port}) => - service - .openTunnels([ - { - description: 'adbmux', - from: {host, port, family: 4}, - to: {host: 'localhost', port: 5037, family: 4}, - }, - { - description: 'exopackage', - from: {host, port: 2829, family: 4}, - to: {host: 'localhost', port: 2829, family: 4}, - }, - ]) - .mapTo(port), - ) - .switchMap(async port => { - const service = getAdbServiceByNuclideUri(host); - await service.checkInMuxPort(port); - return port; - }); + return { + service, + port + }; + }).switchMap(({ + service, + port + }) => service.openTunnels([{ + description: 'adbmux', + from: { + host, + port, + family: 4 + }, + to: { + host: 'localhost', + port: 5037, + family: 4 + } + }, { + description: 'exopackage', + from: { + host, + port: 2829, + family: 4 + }, + to: { + host: 'localhost', + port: 2829, + family: 4 + } + }]).mapTo(port)).switchMap(async port => { + const service = (0, _utils().getAdbServiceByNuclideUri)(host); + await service.checkInMuxPort(port); + return port; + }); } -function openTunnelsManually(host: NuclideUri): Observable { +function openTunnelsManually(host) { let retries = 3; - return Observable.defer(async () => { - await getAdbServiceByNuclideUri(host).killServer(); + return _RxMin.Observable.defer(async () => { + await (0, _utils().getAdbServiceByNuclideUri)(host).killServer(); + const service = await (0, _consumeFirstProvider().default)('nuclide.ssh-tunnel'); + + if (!service) { + throw new Error("Invariant violation: \"service\""); + } - const service: SshTunnelService = await consumeFirstProvider( - 'nuclide.ssh-tunnel', - ); - invariant(service); return service; - }) - .timeout(5000) - .switchMap(service => - service.openTunnels([ - { - description: 'adb', - from: {host, port: 5037, family: 4}, - to: {host: 'localhost', port: 5037, family: 4}, - }, - { - description: 'emulator console', - from: {host, port: 5554, family: 4}, - to: {host: 'localhost', port: 5554, family: 4}, - }, - { - description: 'emulator adb', - from: {host, port: 5555, family: 4}, - to: {host: 'localhost', port: 5555, family: 4}, - }, - { - description: 'exopackage', - from: {host, port: 2829, family: 4}, - to: {host: 'localhost', port: 2829, family: 4}, - }, - ]), - ) - .retryWhen(errors => { - return errors.do(error => { - if (retries-- <= 0) { - throw error; - } - }); - }) - .mapTo(null); -} + }).timeout(5000).switchMap(service => service.openTunnels([{ + description: 'adb', + from: { + host, + port: 5037, + family: 4 + }, + to: { + host: 'localhost', + port: 5037, + family: 4 + } + }, { + description: 'emulator console', + from: { + host, + port: 5554, + family: 4 + }, + to: { + host: 'localhost', + port: 5554, + family: 4 + } + }, { + description: 'emulator adb', + from: { + host, + port: 5555, + family: 4 + }, + to: { + host: 'localhost', + port: 5555, + family: 4 + } + }, { + description: 'exopackage', + from: { + host, + port: 2829, + family: 4 + }, + to: { + host: 'localhost', + port: 2829, + family: 4 + } + }])).retryWhen(errors => { + return errors.do(error => { + if (retries-- <= 0) { + throw error; + } + }); + }).mapTo(null); +} \ No newline at end of file diff --git a/modules/nuclide-adb/lib/common/Processes.js b/modules/nuclide-adb/lib/common/Processes.js index c543ff8c..6764c8e2 100644 --- a/modules/nuclide-adb/lib/common/Processes.js +++ b/modules/nuclide-adb/lib/common/Processes.js @@ -1,3 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Processes = void 0; + +function _collection() { + const data = require("../../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +var _os = _interopRequireDefault(require("os")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,173 +29,132 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {Adb} from '../Adb'; -import type {Process} from '../types'; - -import {arrayCompact} from 'nuclide-commons/collection'; -import {Observable} from 'rxjs'; -import os from 'os'; - -type CPU_MEM = [number, number]; - const VALID_PROCESS_REGEX = new RegExp(/\d+\s()/); -export class Processes { - _adb: Adb; - - constructor(adb: Adb) { +class Processes { + constructor(adb) { this._adb = adb; } - _getGlobalProcessStat(): Observable { - return this._adb - .runShortCommand('shell', 'cat', '/proc/stat') - .map(stdout => stdout.split(/\n/)[0].trim()); + _getGlobalProcessStat() { + return this._adb.runShortCommand('shell', 'cat', '/proc/stat').map(stdout => stdout.split(/\n/)[0].trim()); } - _getProcStats(): Observable> { - return this._adb - .runShortCommand( - 'shell', - 'for file in /proc/[0-9]*/stat; do cat "$file" 2>/dev/null || true; done', - ) - .map(stdout => { - return stdout - .split(/\n/) - .filter(line => VALID_PROCESS_REGEX.test(line)); - }); + _getProcStats() { + return this._adb.runShortCommand('shell', 'for file in /proc/[0-9]*/stat; do cat "$file" 2>/dev/null || true; done').map(stdout => { + return stdout.split(/\n/).filter(line => VALID_PROCESS_REGEX.test(line)); + }); } - fetch(timeout: number): Observable { - const internalTimeout = (timeout * 2) / 3; - return Observable.forkJoin( - this._adb - .getProcesses() - .timeout(internalTimeout) - .catch(() => Observable.of([])), - this._adb - .getDebuggableProcesses() - .timeout(internalTimeout) - .catch(() => Observable.of([])), - this._getProcessAndMemoryUsage() - .timeout(internalTimeout) - .catch(() => Observable.of(new Map())), - ).map(([processes, javaProcesses, cpuAndMemUsage]) => { - const javaPids = new Set( - javaProcesses.map(javaProc => Number(javaProc.pid)), - ); - return arrayCompact( - processes.map(simpleProcess => { - const pid = parseInt(simpleProcess.pid, 10); - if (!Number.isInteger(pid)) { - return null; - } - const cpuAndMem = cpuAndMemUsage.get(pid); - let cpu = null; - let mem = null; - if (cpuAndMem != null) { - cpu = parseFloat(cpuAndMem[0]); - mem = parseFloat(cpuAndMem[1]); - } - const isJava = javaPids.has(pid); - return { - user: simpleProcess.user, - pid, - name: simpleProcess.name, - cpuUsage: cpu, - memUsage: mem, - isJava, // TODO(wallace) rename this to debuggable or make this a list of possible debugger types - }; - }), - ); + fetch(timeout) { + const internalTimeout = timeout * 2 / 3; + return _RxMin.Observable.forkJoin(this._adb.getProcesses().timeout(internalTimeout).catch(() => _RxMin.Observable.of([])), this._adb.getDebuggableProcesses().timeout(internalTimeout).catch(() => _RxMin.Observable.of([])), this._getProcessAndMemoryUsage().timeout(internalTimeout).catch(() => _RxMin.Observable.of(new Map()))).map(([processes, javaProcesses, cpuAndMemUsage]) => { + const javaPids = new Set(javaProcesses.map(javaProc => Number(javaProc.pid))); + return (0, _collection().arrayCompact)(processes.map(simpleProcess => { + const pid = parseInt(simpleProcess.pid, 10); + + if (!Number.isInteger(pid)) { + return null; + } + + const cpuAndMem = cpuAndMemUsage.get(pid); + let cpu = null; + let mem = null; + + if (cpuAndMem != null) { + cpu = parseFloat(cpuAndMem[0]); + mem = parseFloat(cpuAndMem[1]); + } + + const isJava = javaPids.has(pid); + return { + user: simpleProcess.user, + pid, + name: simpleProcess.name, + cpuUsage: cpu, + memUsage: mem, + isJava // TODO(wallace) rename this to debuggable or make this a list of possible debugger types + + }; + })); }); } - _getProcessAndMemoryUsage(): Observable> { - return Observable.interval(2000) - .startWith(0) - .switchMap(() => { - return Observable.forkJoin( - this._getProcessesTime(), - this._getGlobalCPUTime(), - ); - }) - .take(2) - .toArray() - .map(times => { - const [procTimePrev, cpuTimePrev] = times[0]; - const [procTime, cpuTime] = times[1]; - // pid => cpuUsage, memory usage - const cpuAndMemUsage = new Map(); - const deltaCpu = cpuTime - cpuTimePrev; - procTime.forEach((p1, pid) => { - if (!procTimePrev.has(pid)) { - return; - } - const p0 = procTimePrev.get(pid); - if (p0 != null) { - const deltaProc = p1[0] - p0[0]; - const memUsage = p1[1]; - cpuAndMemUsage.set(pid, [(deltaProc / deltaCpu) * 100, memUsage]); - } - }); - return cpuAndMemUsage; + _getProcessAndMemoryUsage() { + return _RxMin.Observable.interval(2000).startWith(0).switchMap(() => { + return _RxMin.Observable.forkJoin(this._getProcessesTime(), this._getGlobalCPUTime()); + }).take(2).toArray().map(times => { + const [procTimePrev, cpuTimePrev] = times[0]; + const [procTime, cpuTime] = times[1]; // pid => cpuUsage, memory usage + + const cpuAndMemUsage = new Map(); + const deltaCpu = cpuTime - cpuTimePrev; + procTime.forEach((p1, pid) => { + if (!procTimePrev.has(pid)) { + return; + } + + const p0 = procTimePrev.get(pid); + + if (p0 != null) { + const deltaProc = p1[0] - p0[0]; + const memUsage = p1[1]; + cpuAndMemUsage.set(pid, [deltaProc / deltaCpu * 100, memUsage]); + } }); + return cpuAndMemUsage; + }); } - /** * Returns a map: pid => utime + stime */ - _getProcessesTime(): Observable> { + + + _getProcessesTime() { // We look for the all the /proc/PID/stat files failing silently if the process dies as the // command runs. return this._getProcStats().map(lines => { - return new Map( - lines.map(line => { - const info = line.trim().split(/\s/); - return [ - parseInt(info[0], 10), - [ - parseInt(info[12], 10) + parseInt(info[13], 10), // stime + utime - parseInt(info[23], 10), // RSS - ], - ]; - }), - ); + return new Map(lines.map(line => { + const info = line.trim().split(/\s/); + return [parseInt(info[0], 10), [parseInt(info[12], 10) + parseInt(info[13], 10), // stime + utime + parseInt(info[23], 10)]]; + } // RSS + )); }); } - _getGlobalCPUTime(): Observable { + _getGlobalCPUTime() { return this._getGlobalProcessStat().map(stdout => { - return stdout - .split(/\s+/) - .slice(1, -2) - .reduce((acc, current) => { - return acc + parseInt(current, 10); - }, 0); + return stdout.split(/\s+/).slice(1, -2).reduce((acc, current) => { + return acc + parseInt(current, 10); + }, 0); }); } - async getPidFromPackageName(packageName: string): Promise { - let pidLines: string; + async getPidFromPackageName(packageName) { + let pidLines; + try { - pidLines = await this._adb - .runShortCommand('shell', 'ps', '|', 'grep', '-i', packageName) - .toPromise(); + pidLines = await this._adb.runShortCommand('shell', 'ps', '|', 'grep', '-i', packageName).toPromise(); } catch (e) { pidLines = ''; } - const pidLine = pidLines.split(os.EOL)[0]; + + const pidLine = pidLines.split(_os.default.EOL)[0]; + if (pidLine == null) { - throw new Error( - `Can not find a running process with package name: ${packageName}`, - ); - } - // First column is 'USER', second is 'PID'. - return parseInt(pidLine.trim().split(/\s+/)[1], /* radix */ 10); + throw new Error(`Can not find a running process with package name: ${packageName}`); + } // First column is 'USER', second is 'PID'. + + + return parseInt(pidLine.trim().split(/\s+/)[1], + /* radix */ + 10); } + } + +exports.Processes = Processes; \ No newline at end of file diff --git a/modules/nuclide-adb/lib/common/ps.js b/modules/nuclide-adb/lib/common/ps.js index b9256df6..a159d621 100644 --- a/modules/nuclide-adb/lib/common/ps.js +++ b/modules/nuclide-adb/lib/common/ps.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.parsePsTableOutput = parsePsTableOutput; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,14 +13,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -export function parsePsTableOutput( - output: string, - desiredFields: Array, -): Array { +function parsePsTableOutput(output, desiredFields) { const lines = output.split(/\n/); const header = lines[0]; const cols = header.split(/\s+/); @@ -21,6 +24,7 @@ export function parsePsTableOutput( for (let i = 0; i < cols.length; i++) { const columnName = cols[i].toLowerCase(); + if (desiredFields.includes(columnName)) { colMapping[i] = columnName; } @@ -32,8 +36,10 @@ export function parsePsTableOutput( data.filter(row => row.trim() !== '').forEach(row => { const rowData = row.split(/\s+/); const rowObj = {}; + for (let i = 0; i < rowData.length; i++) { const effectiveColumn = i; + if (ignoreSColumn) { // Android's ps output has an extra column "S" (versions prior to API 26) // in the data that doesn't appear in the header. Skip that column's value. @@ -49,6 +55,5 @@ export function parsePsTableOutput( formattedData.push(rowObj); }); - return formattedData; -} +} \ No newline at end of file diff --git a/modules/nuclide-adb/lib/main.js b/modules/nuclide-adb/lib/main.js index d50a7b02..d83f62f7 100644 --- a/modules/nuclide-adb/lib/main.js +++ b/modules/nuclide-adb/lib/main.js @@ -1,14 +1,43 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -export {getAdbServiceByNuclideUri} from './utils'; -export {adbDeviceForIdentifier, observeAndroidDevices} from './AdbDevicePoller'; +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "getAdbServiceByNuclideUri", { + enumerable: true, + get: function () { + return _utils().getAdbServiceByNuclideUri; + } +}); +Object.defineProperty(exports, "adbDeviceForIdentifier", { + enumerable: true, + get: function () { + return _AdbDevicePoller().adbDeviceForIdentifier; + } +}); +Object.defineProperty(exports, "observeAndroidDevices", { + enumerable: true, + get: function () { + return _AdbDevicePoller().observeAndroidDevices; + } +}); + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _AdbDevicePoller() { + const data = require("./AdbDevicePoller"); + + _AdbDevicePoller = function () { + return data; + }; + + return data; +} \ No newline at end of file diff --git a/modules/nuclide-adb/lib/types.js b/modules/nuclide-adb/lib/types.js index d91ec2e9..9a390c31 100644 --- a/modules/nuclide-adb/lib/types.js +++ b/modules/nuclide-adb/lib/types.js @@ -1,74 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {Observable} from 'rxjs'; - -export type SimpleProcess = { - user: string, - pid: string, - name: string, -}; - -export type AndroidJavaProcess = SimpleProcess; - -export type AdbDevice = {| - serial: string, - displayName: string, - usb: ?string, - product: ?string, - model: ?string, - device: ?string, - transportId: ?string, -|}; - -export type Process = { - user: string, - pid: number, - name: string, - cpuUsage: ?number, - memUsage: ?number, - isJava: boolean, -}; - -// -// nuclide-ssh-tunnel types and nuclide-socket-rpc types -// - -export type TunnelHost = { - host: string, - port: number, - family: 4 | 6, -}; - -export type ResolvedTunnel = { - from: TunnelHost, - to: TunnelHost, -}; - -export type Host = { - host: 'localhost' | NuclideUri, - port: number, - family?: 4 | 6, -}; - -export type Tunnel = { - description: string, - from: Host, - to: Host, -}; - -export type SshTunnelService = { - openTunnels(tunnels: Array): Observable<'ready'>, - getOpenTunnels(): Set, - getAvailableServerPort(uri: NuclideUri): Promise, -}; +"use strict"; \ No newline at end of file diff --git a/modules/nuclide-adb/lib/utils.js b/modules/nuclide-adb/lib/utils.js index 562ef871..b602eb18 100644 --- a/modules/nuclide-adb/lib/utils.js +++ b/modules/nuclide-adb/lib/utils.js @@ -1,3 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getAdbServiceByNuclideUri = getAdbServiceByNuclideUri; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function AdbServiceLocal() { + const data = _interopRequireWildcard(require("./AdbService")); + + AdbServiceLocal = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,28 +47,20 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import nuclideUri from 'nuclide-commons/nuclideUri'; -import typeof * as AdbService from './AdbService'; - -import * as AdbServiceLocal from './AdbService'; -import nullthrows from 'nullthrows'; - -let rpcService: ?nuclide$RpcService; +let rpcService; atom.packages.serviceHub.consume('nuclide-rpc-services', '0.0.0', provider => { rpcService = provider; }); -export function getAdbServiceByNuclideUri(uri: NuclideUri): AdbService { - if (rpcService == null && !nuclideUri.isRemote(uri)) { - return AdbServiceLocal; - } - // nuclide-rpc-services should be available at this point. +function getAdbServiceByNuclideUri(uri) { + if (rpcService == null && !_nuclideUri().default.isRemote(uri)) { + return AdbServiceLocal(); + } // nuclide-rpc-services should be available at this point. // If it isn't, throw an error. - return nullthrows(rpcService).getServiceByNuclideUri('AdbService', uri); -} + + + return (0, _nullthrows().default)(rpcService).getServiceByNuclideUri('AdbService', uri); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/ActiveEditorRegistry.js b/modules/nuclide-commons-atom/ActiveEditorRegistry.js index 6f9e39d2..bedcce86 100644 --- a/modules/nuclide-commons-atom/ActiveEditorRegistry.js +++ b/modules/nuclide-commons-atom/ActiveEditorRegistry.js @@ -1,3 +1,84 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _debounced() { + const data = require("./debounced"); + + _debounced = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _paneItem() { + const data = require("./pane-item"); + + _paneItem = function () { + return data; + }; + + return data; +} + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("./ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +87,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ @@ -14,248 +95,127 @@ * ActiveEditorRegistry provides abstractions for creating services that operate * on text editor contents. */ - -import {Observable, Subject} from 'rxjs'; - -import { - observeActiveEditorsDebounced, - editorChangesDebounced, -} from './debounced'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {cacheWhileSubscribed} from 'nuclide-commons/observable'; - -import {getLogger} from 'log4js'; - -import {isPending, observePendingStateEnd} from './pane-item'; -import ProviderRegistry from './ProviderRegistry'; - -export type Provider = { - priority: number, - grammarScopes: Array, - // This overrides the updateOnEdit setting in ActiveEditorRegistry's config. - updateOnEdit?: boolean, -}; - -export type Result = - | { - kind: 'not-text-editor', - } - | { - kind: 'no-provider', - grammar: atom$Grammar, - } - | { - kind: 'provider-error', - provider: T, - } - | { - // Since providers can be slow, the pane-change and edit events are emitted immediately in case - // the UI needs to clear outdated results. - kind: 'pane-change', - editor: atom$TextEditor, - } - | { - kind: 'edit', - editor: atom$TextEditor, - } - | { - kind: 'save', - editor: atom$TextEditor, - } - | { - kind: 'result', - result: V, - // The editor that the result was computed from - editor: atom$TextEditor, - // The provider that computed the result - // TODO Use a type parameter for this type - provider: T, - }; - -export type ResultFunction = ( - provider: T, - editor: atom$TextEditor, -) => Promise; - -type PartialEventSources = { - +activeEditors?: Observable, - +changesForEditor?: (editor: atom$TextEditor) => Observable, - +savesForEditor?: (editor: atom$TextEditor) => Observable, -}; - -export type EventSources = { - activeEditors: Observable, - changesForEditor: (editor: atom$TextEditor) => Observable, - savesForEditor: (editor: atom$TextEditor) => Observable, +const DEFAULT_CONFIG = { + updateOnEdit: true }; -export type Config = { - /** - * If true, we will query providers for updates whenever the text in the editor is changed. - * Otherwise, we will query only when there is a save event. - */ - updateOnEdit?: boolean, -}; - -type ConcreteConfig = { - updateOnEdit: boolean, -}; - -const DEFAULT_CONFIG: ConcreteConfig = { - updateOnEdit: true, -}; - -function getConcreteConfig(config: Config): ConcreteConfig { - return { - ...DEFAULT_CONFIG, - ...config, - }; +function getConcreteConfig(config) { + return Object.assign({}, DEFAULT_CONFIG, config); } -export default class ActiveEditorRegistry { - _resultFunction: ResultFunction; - _providerRegistry: ProviderRegistry; - _newProviderEvents: Subject; - _resultsStream: Observable>; - _config: ConcreteConfig; - - constructor( - resultFunction: ResultFunction, - config: Config = {}, - eventSources: PartialEventSources = {}, - ) { +class ActiveEditorRegistry { + constructor(resultFunction, config = {}, eventSources = {}) { this._config = getConcreteConfig(config); this._resultFunction = resultFunction; - this._providerRegistry = new ProviderRegistry(); - this._newProviderEvents = new Subject(); + this._providerRegistry = new (_ProviderRegistry().default)(); + this._newProviderEvents = new _RxMin.Subject(); this._resultsStream = this._createResultsStream({ - activeEditors: - eventSources.activeEditors || observeActiveEditorsDebounced(), - changesForEditor: - eventSources.changesForEditor || - (editor => editorChangesDebounced(editor)), - savesForEditor: - eventSources.savesForEditor || - (editor => { - return observableFromSubscribeFunction(callback => - editor.onDidSave(callback), - ).mapTo(undefined); - }), + activeEditors: eventSources.activeEditors || (0, _debounced().observeActiveEditorsDebounced)(), + changesForEditor: eventSources.changesForEditor || (editor => (0, _debounced().editorChangesDebounced)(editor)), + savesForEditor: eventSources.savesForEditor || (editor => { + return (0, _event().observableFromSubscribeFunction)(callback => editor.onDidSave(callback)).mapTo(undefined); + }) }); } - consumeProvider(provider: T): IDisposable { + consumeProvider(provider) { this._providerRegistry.addProvider(provider); + this._newProviderEvents.next(); - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { this._providerRegistry.removeProvider(provider); }); } - getResultsStream(): Observable> { + getResultsStream() { return this._resultsStream; } - _createResultsStream(eventSources: EventSources): Observable> { + _createResultsStream(eventSources) { const repeatedEditors = eventSources.activeEditors.switchMap(editor => { if (editor == null) { - return Observable.of(editor); + return _RxMin.Observable.of(editor); } - return Observable.concat( - Observable.of(editor), - this._newProviderEvents.mapTo(editor), - ); + + return _RxMin.Observable.concat(_RxMin.Observable.of(editor), this._newProviderEvents.mapTo(editor)); }); const results = repeatedEditors.switchMap(editorArg => { // Necessary so the type refinement holds in the callback later const editor = editorArg; + if (editor == null) { - return Observable.of({kind: 'not-text-editor'}); + return _RxMin.Observable.of({ + kind: 'not-text-editor' + }); } - return Observable.concat( - // Emit a pane change event first, so that clients can do something while waiting for a - // provider to give a result. - Observable.of({ - kind: 'pane-change', - editor, - }), - // wait for pending panes to no longer be pending, or if they're not, - // get the result right away. - (isPending(editor) - ? observePendingStateEnd(editor).take(1) - : Observable.of(null) - ).ignoreElements(), - Observable.fromPromise( - this._getResultForEditor(this._getProviderForEditor(editor), editor), - ), - this._resultsForEditor(editor, eventSources), - ); + return _RxMin.Observable.concat( // Emit a pane change event first, so that clients can do something while waiting for a + // provider to give a result. + _RxMin.Observable.of({ + kind: 'pane-change', + editor + }), // wait for pending panes to no longer be pending, or if they're not, + // get the result right away. + ((0, _paneItem().isPending)(editor) ? (0, _paneItem().observePendingStateEnd)(editor).take(1) : _RxMin.Observable.of(null)).ignoreElements(), _RxMin.Observable.fromPromise(this._getResultForEditor(this._getProviderForEditor(editor), editor)), this._resultsForEditor(editor, eventSources)); }); - return cacheWhileSubscribed(results); + return (0, _observable().cacheWhileSubscribed)(results); } - _resultsForEditor( - editor: atom$TextEditor, - eventSources: EventSources, - ): Observable> { + _resultsForEditor(editor, eventSources) { // It's possible that the active provider for an editor changes over time. // Thus, we have to subscribe to both edits and saves. - return Observable.merge( - eventSources.changesForEditor(editor).map(() => 'edit'), - eventSources.savesForEditor(editor).map(() => 'save'), - ).flatMap(event => { + return _RxMin.Observable.merge(eventSources.changesForEditor(editor).map(() => 'edit'), eventSources.savesForEditor(editor).map(() => 'save')).flatMap(event => { const provider = this._getProviderForEditor(editor); + if (provider != null) { - let updateOnEdit = provider.updateOnEdit; - // Fall back to the config's updateOnEdit if not provided. + let updateOnEdit = provider.updateOnEdit; // Fall back to the config's updateOnEdit if not provided. + if (updateOnEdit == null) { updateOnEdit = this._config.updateOnEdit; } + if (updateOnEdit !== (event === 'edit')) { - return Observable.empty(); + return _RxMin.Observable.empty(); } } - return Observable.concat( - // $FlowIssue: {kind: edit | save} <=> {kind: edit} | {kind: save} - Observable.of({kind: event, editor}), - Observable.fromPromise(this._getResultForEditor(provider, editor)), - ); + + return _RxMin.Observable.concat( // $FlowIssue: {kind: edit | save} <=> {kind: edit} | {kind: save} + _RxMin.Observable.of({ + kind: event, + editor + }), _RxMin.Observable.fromPromise(this._getResultForEditor(provider, editor))); }); } - _getProviderForEditor(editor: atom$TextEditor): ?T { + _getProviderForEditor(editor) { return this._providerRegistry.getProviderForEditor(editor); } - async _getResultForEditor( - provider: ?T, - editor: atom$TextEditor, - ): Promise> { + async _getResultForEditor(provider, editor) { if (provider == null) { return { kind: 'no-provider', - grammar: editor.getGrammar(), + grammar: editor.getGrammar() }; } + try { return { kind: 'result', result: await this._resultFunction(provider, editor), provider, - editor, + editor }; } catch (e) { - getLogger(this.constructor.name).error( - `Error from provider for ${editor.getGrammar().scopeName}`, - e, - ); + (0, _log4js().getLogger)(this.constructor.name).error(`Error from provider for ${editor.getGrammar().scopeName}`, e); return { provider, - kind: 'provider-error', + kind: 'provider-error' }; } } + } + +exports.default = ActiveEditorRegistry; \ No newline at end of file diff --git a/modules/nuclide-commons-atom/ContextMenu.js b/modules/nuclide-commons-atom/ContextMenu.js index 450fbfb0..39f25287 100644 --- a/modules/nuclide-commons-atom/ContextMenu.js +++ b/modules/nuclide-commons-atom/ContextMenu.js @@ -1,3 +1,25 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.showMenuForEvent = showMenuForEvent; +exports.default = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _electron = require("electron"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,42 +28,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import invariant from 'assert'; -import {remote} from 'electron'; - -type Item = { - type: 'item', - item: atom$ContextMenuItem, - priority: number, -}; - -type Menu = { - type: 'menu', - menu: ContextMenu, - priority: number, -}; - -type InternalItem = Item | Menu; - -type RootMenuOptions = { - type: 'root', - cssSelector: string, -}; - -type SubmenuOptions = { - type: 'submenu', - label: string, - parent: ContextMenu, - shouldDisplay?: (e: MouseEvent) => boolean, -}; - -type MenuOptions = RootMenuOptions | SubmenuOptions; - /** * This class represents a collection of context menu items that have been registered with Atom's * ContextMenuManager under a single CSS selector. These items are ordered based on the specified @@ -57,18 +47,12 @@ type MenuOptions = RootMenuOptions | SubmenuOptions; * Note that this class also provides support for submenu items. This requires Atom 1.6 or later * because it relies on this fix: https://github.com/atom/atom/pull/10486. */ -export default class ContextMenu { - _menuOptions: MenuOptions; - +class ContextMenu { /** * List of items that have been added to this context menu in the order they were added. * Note that this list does not get sorted: only a filtered version of it does. * Further, this list is mutated heavily, but it is never reassigned. */ - _items: Array; - - _needsSort: boolean; - _sort: Function; /** * This is the Disposable that represents adding all of this object's menu items to Atom's own @@ -76,24 +60,22 @@ export default class ContextMenu { * that we previously added to the ContextMenuManager and then re-add them based on the new * ordering of priorities that results from the new item. */ - _disposable: ?IDisposable; - - constructor(menuOptions: MenuOptions) { + constructor(menuOptions) { this._menuOptions = menuOptions; this._items = []; this._needsSort = false; this._sort = this._sort.bind(this); this._disposable = null; } - /** * @return true if this menu does not contain any items; otherwise, returns false. Note this will * return true if it contains only empty submenu items. */ - isEmpty(): boolean { + + + isEmpty() { return this._items.length === 0; } - /** * Adds the specified item to this contenxt menu. * @@ -102,11 +84,16 @@ export default class ContextMenu { * * @return object whose dispose() method can be used to remove the menu item from this object. */ - addItem(item: atom$ContextMenuItem, priority: number): IDisposable { - const value = {type: 'item', item, priority}; + + + addItem(item, priority) { + const value = { + type: 'item', + item, + priority + }; return this._addItemToList(value); } - /** * Adds the specified submenu to this contenxt menu. * @@ -115,34 +102,43 @@ export default class ContextMenu { * * @return object whose dispose() method can be used to remove the submenu from this object. */ - addSubmenu(contextMenu: ContextMenu, priority: number): IDisposable { - const value = {type: 'menu', menu: contextMenu, priority}; + + + addSubmenu(contextMenu, priority) { + const value = { + type: 'menu', + menu: contextMenu, + priority + }; return this._addItemToList(value); } - _addItemToList(value: InternalItem): IDisposable { + _addItemToList(value) { this._items.push(value); - this._needsSort = true; - process.nextTick(this._sort); - // TODO(mbolin): Ideally, this Disposable should be garbage-collected if this ContextMenu is + this._needsSort = true; + process.nextTick(this._sort); // TODO(mbolin): Ideally, this Disposable should be garbage-collected if this ContextMenu is // disposed. - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { const index = this._items.indexOf(value); - this._items.splice(index, 1); - // We need to invoke _sort for the management of this._disposable and atom.contextMenu.add. + this._items.splice(index, 1); // We need to invoke _sort for the management of this._disposable and atom.contextMenu.add. + + this._needsSort = true; + this._sort(); }); } - /** * This method must be invoked after this._items has been modified. If necessary, it will remove * all items that this object previously registered with Atom's ContextMenuManager. Then it will * re-register everything in this._items once it has been sorted. */ - _sort(): void { + + + _sort() { if (!this._needsSort) { return; } @@ -154,45 +150,51 @@ export default class ContextMenu { } const menuOptions = this._menuOptions; + if (menuOptions.type === 'root') { const items = this._sortAndFilterItems(); + this._disposable = atom.contextMenu.add({ - [menuOptions.cssSelector]: items.map( - this._contextMenuItemForInternalItem, - this, - ), + [menuOptions.cssSelector]: items.map(this._contextMenuItemForInternalItem, this) }); } else if (menuOptions.type === 'submenu') { // Tell the parent menu to sort itself. menuOptions.parent._needsSort = true; + menuOptions.parent._sort(); } } - /** Translates this object's internal representation of a menu item to Atom's representation. */ - _contextMenuItemForInternalItem( - internalItem: InternalItem, - ): atom$ContextMenuItem { + + + _contextMenuItemForInternalItem(internalItem) { if (internalItem.type === 'item') { return internalItem.item; } else if (internalItem.type === 'menu') { // Note that due to our own strict renaming rules, this must be a private method instead of a // static function because of the access to _menuOptions and _items. const menuOptions = internalItem.menu._menuOptions; - invariant(menuOptions.type === 'submenu'); + + if (!(menuOptions.type === 'submenu')) { + throw new Error("Invariant violation: \"menuOptions.type === 'submenu'\""); + } + const items = internalItem.menu._sortAndFilterItems(); + return { label: menuOptions.label, submenu: items.map(this._contextMenuItemForInternalItem, this), - shouldDisplay: menuOptions.shouldDisplay, + shouldDisplay: menuOptions.shouldDisplay }; } else { - invariant(false); + if (!false) { + throw new Error("Invariant violation: \"false\""); + } } } - _sortAndFilterItems(): Array { - const items = this._items.filter((item: InternalItem) => { + _sortAndFilterItems() { + const items = this._items.filter(item => { if (item.type === 'item') { return true; } else if (item.type === 'menu') { @@ -200,37 +202,41 @@ export default class ContextMenu { return !contextMenu.isEmpty(); } }); + items.sort(compareInternalItems); return items; } - /** Removes all items this object has added to Atom's ContextMenuManager. */ + + dispose() { this._needsSort = false; + if (this._disposable != null) { this._disposable.dispose(); } + this._items.length = 0; } - static isEventFromContextMenu(event: Event) { + static isEventFromContextMenu(event) { // Context menu commands contain a specific `detail` parameter: // https://github.com/atom/atom/blob/v1.15.0/src/main-process/context-menu.coffee#L17 - return ( - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) - Array.isArray(event.detail) && - // flowlint-next-line sketchy-null-mixed:off - event.detail[0] && - (event.detail[0]: any).contextCommand + return (// $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + Array.isArray(event.detail) && // flowlint-next-line sketchy-null-mixed:off + event.detail[0] && event.detail[0].contextCommand ); } -} +} /** Comparator used to sort menu items by priority: lower priorities appear earlier. */ -function compareInternalItems(a: InternalItem, b: InternalItem): number { + + +exports.default = ContextMenu; + +function compareInternalItems(a, b) { return a.priority - b.priority; } - /** * Shows the provided menu template. This will result in [an extra call to `templateForEvent()`][1], * but it means that we still go through `showMenuForEvent()`, maintaining its behavior wrt @@ -238,24 +244,31 @@ function compareInternalItems(a: InternalItem, b: InternalItem): number { * * [1]: https://github.com/atom/atom/blob/v1.13.0/src/context-menu-manager.coffee#L200 */ -export function showMenuForEvent( - event: MouseEvent, - menuTemplate: Array, -): UniversalDisposable { - invariant(remote != null); - const win = (remote.getCurrentWindow(): any); + + +function showMenuForEvent(event, menuTemplate) { + if (!(_electron.remote != null)) { + throw new Error("Invariant violation: \"remote != null\""); + } + + const win = _electron.remote.getCurrentWindow(); + const originalEmit = win.emit; + const restore = () => { win.emit = originalEmit; }; + win.emit = (eventType, ...args) => { if (eventType !== 'context-menu') { return originalEmit(eventType, ...args); } + const result = originalEmit('context-menu', menuTemplate); restore(); return result; }; + atom.contextMenu.showForEvent(event); - return new UniversalDisposable(restore); -} + return new (_UniversalDisposable().default)(restore); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/FeatureLoader.js b/modules/nuclide-commons-atom/FeatureLoader.js index 88a925b0..7740449e 100644 --- a/modules/nuclide-commons-atom/FeatureLoader.js +++ b/modules/nuclide-commons-atom/FeatureLoader.js @@ -1,3 +1,76 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.INITIAL_FEATURE_GROUP = exports.REQUIRED_FEATURE_GROUP = void 0; + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _activatePackages() { + const data = _interopRequireDefault(require("./experimental-packages/activatePackages")); + + _activatePackages = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("./feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +var _path2 = _interopRequireDefault(require("path")); + +function _collection() { + const data = require("../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,94 +79,45 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* global localStorage */ - -import invariant from 'assert'; -import idx from 'idx'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import nullthrows from 'nullthrows'; -import activateExperimentalPackages from './experimental-packages/activatePackages'; -import featureConfig from './feature-config'; -import path from 'path'; // eslint-disable-line nuclide-internal/prefer-nuclide-uri -import {MultiMap, setIntersect, setUnion} from 'nuclide-commons/collection'; -import {Observable} from 'rxjs'; - -type FeaturePkg = { - name: string, - atomConfig?: Object, - consumedServices?: Object, - description?: string, - displayName?: string, - nuclide?: { - config?: Object, - }, - providedServices?: Object, - featureGroups?: Array, -}; - -export type Feature = { - path: string, - pkg: FeaturePkg, -}; - -type FeatureLoaderParams = { - path: string, - features: Array, - featureGroups?: { - [string]: Array, - }, -}; - -type UseFeatureRules = { - [name: string]: 'always' | 'never' | 'default', -}; - +// eslint-disable-line nuclide-internal/prefer-nuclide-uri const ALWAYS_ENABLED = 'always'; const NEVER_ENABLED = 'never'; const DEFAULT = 'default'; - -const {devMode} = atom.getLoadSettings(); - -export const REQUIRED_FEATURE_GROUP = 'nuclide-required'; -export const INITIAL_FEATURE_GROUP = 'nuclide-core'; - -export default class FeatureLoader { - _activationDisposable: ?UniversalDisposable; - _loadDisposable: UniversalDisposable; - - _config: ?Object; - _features: Array; - _featureBeingActivated: ?Feature; - _featureBeingDeactivated: ?Feature; - _featureGroups: MultiMap; - _deferringFeatureActivation: boolean = true; - _pkgName: string; - _path: string; - _currentlyActiveFeatures: Set = new Set(); - - constructor({features, path: _path, featureGroups}: FeatureLoaderParams) { +const { + devMode +} = atom.getLoadSettings(); +const REQUIRED_FEATURE_GROUP = 'nuclide-required'; +exports.REQUIRED_FEATURE_GROUP = REQUIRED_FEATURE_GROUP; +const INITIAL_FEATURE_GROUP = 'nuclide-core'; +exports.INITIAL_FEATURE_GROUP = INITIAL_FEATURE_GROUP; + +class FeatureLoader { + constructor({ + features, + path: _path, + featureGroups + }) { + this._deferringFeatureActivation = true; + this._currentlyActiveFeatures = new Set(); this._path = _path; this._features = reorderFeatures(features); - this._loadDisposable = new UniversalDisposable(); + this._loadDisposable = new (_UniversalDisposable().default)(); this._pkgName = packageNameFromPath(this._path); - this._featureGroups = groupFeatures( - this._features, - featureGroups == null ? {} : featureGroups, - ); - } + this._featureGroups = groupFeatures(this._features, featureGroups == null ? {} : featureGroups); + } // Build the config. Should occur with root package's load - // Build the config. Should occur with root package's load - load(): void { - invariant(!this._loadDisposable.disposed); - patchPackageManager(); + load() { + if (!!this._loadDisposable.disposed) { + throw new Error("Invariant violation: \"!this._loadDisposable.disposed\""); + } - // Add a dummy deserializer. This forces Atom to load Nuclide's main module + patchPackageManager(); // Add a dummy deserializer. This forces Atom to load Nuclide's main module // (this file) when the package is loaded, which is super important because // this module loads all of the Nuclide features. We could accomplish the same // thing by unsetting [the local storage value][1] that Atom uses to indicate @@ -103,16 +127,15 @@ export default class FeatureLoader { // features would never load again! // // [1] https://github.com/atom/atom/blob/v1.9.8/src/package.coffee#L442 - this._loadDisposable.add( - atom.deserializers.add({ - name: `${this._pkgName}.ForceMainModuleLoad`, - deserialize() {}, - }), - ); - featureConfig.setPackageName(this._pkgName); + this._loadDisposable.add(atom.deserializers.add({ + name: `${this._pkgName}.ForceMainModuleLoad`, - // + deserialize() {} + + })); + + _featureConfig().default.setPackageName(this._pkgName); // // Build the "config" object. This determines the config defaults and // it's what is shown by the Settings view. It includes: // (1) An entry to enable/disable each feature - called "${pkgName}.use.*". @@ -120,139 +143,107 @@ export default class FeatureLoader { // // https://atom.io/docs/api/latest/Config // - this._config = buildConfig(this._features); - // Load enabled features. This needs to be done during Atom's load phase to + + this._config = buildConfig(this._features); // Load enabled features. This needs to be done during Atom's load phase to // make sure that deserializers are registered, etc. // https://github.com/atom/atom/blob/v1.1.0/src/atom-environment.coffee#L625-L631 // https://atom.io/docs/api/latest/PackageManager + const featuresToLoad = this.getEnabledFeatures(); - this._loadDisposable.add( - // Nesting loads within loads leads to reverse activation order- that is, if - // the root package loads feature packages, then the feature package activations will - // happen before the root package's. So we wait until the root package is done loading, - // but before it activates, to load the features. - didLoadPackage(this._pkgName).subscribe(() => { - // Load "regular" feature packages. - featuresToLoad.forEach(feature => { - atom.packages.loadPackage(feature.path); - }); - }), - // Load "experimental" format packages. - didLoadPackage(this._pkgName) - .switchMap(() => - Observable.create( - () => - new UniversalDisposable( - activateExperimentalPackages([...featuresToLoad]), - ), - ), - ) - .subscribe(), - ); - - const featureNames = new Set( - this._features.map(feature => feature.pkg.name), - ); - - // Ensure that the root package is initialized before all of its features. This is important + + this._loadDisposable.add( // Nesting loads within loads leads to reverse activation order- that is, if + // the root package loads feature packages, then the feature package activations will + // happen before the root package's. So we wait until the root package is done loading, + // but before it activates, to load the features. + didLoadPackage(this._pkgName).subscribe(() => { + // Load "regular" feature packages. + featuresToLoad.forEach(feature => { + atom.packages.loadPackage(feature.path); + }); + }), // Load "experimental" format packages. + didLoadPackage(this._pkgName).switchMap(() => _RxMin.Observable.create(() => new (_UniversalDisposable().default)((0, _activatePackages().default)([...featuresToLoad])))).subscribe()); + + const featureNames = new Set(this._features.map(feature => feature.pkg.name)); // Ensure that the root package is initialized before all of its features. This is important // because the root package defines the config for all managed features and we need to make // sure that it's present before they're initialized (i.e. before their deserializers are // called). // $FlowIssue: Need to upstream this. - const onWillInitializePackageDisposable = atom.packages.onWillInitializePackage( - pack => { - if (featureNames.has(pack.name)) { - onWillInitializePackageDisposable.dispose(); - const rootPackage = atom.packages.getLoadedPackage(this._pkgName); - nullthrows(rootPackage).initializeIfNeeded(); - } - }, - ); - this._loadDisposable.add(onWillInitializePackageDisposable); - - // Clean up when the package is unloaded. - this._loadDisposable.add( - atom.packages.onDidUnloadPackage(pack => { - if (pack.name === this._pkgName) { - this._loadDisposable.dispose(); - } - }), - ); + + const onWillInitializePackageDisposable = atom.packages.onWillInitializePackage(pack => { + if (featureNames.has(pack.name)) { + onWillInitializePackageDisposable.dispose(); + const rootPackage = atom.packages.getLoadedPackage(this._pkgName); + (0, _nullthrows().default)(rootPackage).initializeIfNeeded(); + } + }); + + this._loadDisposable.add(onWillInitializePackageDisposable); // Clean up when the package is unloaded. + + + this._loadDisposable.add(atom.packages.onDidUnloadPackage(pack => { + if (pack.name === this._pkgName) { + this._loadDisposable.dispose(); + } + })); } - activate(): void { - invariant(this._activationDisposable == null); + activate() { + if (!(this._activationDisposable == null)) { + throw new Error("Invariant violation: \"this._activationDisposable == null\""); + } + const rootPackage = atom.packages.getLoadedPackage(this._pkgName); - invariant(rootPackage != null); - // This is a failsafe in case the `.ForceMainModuleLoad` deserializer + if (!(rootPackage != null)) { + throw new Error("Invariant violation: \"rootPackage != null\""); + } // This is a failsafe in case the `.ForceMainModuleLoad` deserializer // defined above does not register in time, or if the defer key has been set // w/o our knowledge. This can happen during OSS upgrades. - localStorage.removeItem( - rootPackage.getCanDeferMainModuleRequireStorageKey(), - ); - this.updateActiveFeatures(); - // Watch things that should trigger reevaluation of active features. Note that we do this + localStorage.removeItem(rootPackage.getCanDeferMainModuleRequireStorageKey()); + this.updateActiveFeatures(); // Watch things that should trigger reevaluation of active features. Note that we do this // *after* the initial `updateActiveFeatures()` call because that could trigger one of these // events. - this._activationDisposable = new UniversalDisposable( - atom.config.onDidChange(this.getUseKeyPath(), () => { - this.updateActiveFeatures(); - }), - atom.config.onDidChange(this.getEnabledFeatureGroupsKeyPath(), () => { - this.updateActiveFeatures(); - }), - Observable.merge(didAddFirstPath, didAddFirstTextEditor) - .take(1) - .subscribe(() => { - // Hopefully we've opened a project so we don't have to load all the features. - this._deferringFeatureActivation = false; - this.updateActiveFeatures(); - }), - ); + + this._activationDisposable = new (_UniversalDisposable().default)(atom.config.onDidChange(this.getUseKeyPath(), () => { + this.updateActiveFeatures(); + }), atom.config.onDidChange(this.getEnabledFeatureGroupsKeyPath(), () => { + this.updateActiveFeatures(); + }), _RxMin.Observable.merge(didAddFirstPath, didAddFirstTextEditor).take(1).subscribe(() => { + // Hopefully we've opened a project so we don't have to load all the features. + this._deferringFeatureActivation = false; + this.updateActiveFeatures(); + })); } updateActiveFeatures() { // `updateActiveFeatures()` can't be called recursively. If it is, just warn and bail. if (this._featureBeingActivated != null) { // eslint-disable-next-line no-console - console.warn( - `Activating ${this._featureBeingActivated.pkg.name} caused a` + - ' reevaluation of active features.', - ); + console.warn(`Activating ${this._featureBeingActivated.pkg.name} caused a` + ' reevaluation of active features.'); return; } + if (this._featureBeingDeactivated != null) { // eslint-disable-next-line no-console - console.warn( - `Deactivating ${this._featureBeingDeactivated.pkg.name} caused a` + - ' reevaluation of active features.', - ); + console.warn(`Deactivating ${this._featureBeingDeactivated.pkg.name} caused a` + ' reevaluation of active features.'); return; } + this.updateActiveFeaturesNow(); } - /** * Enable and disable the correct features according to the current configuration. */ + + updateActiveFeaturesNow() { const enabledFeatures = this.getEnabledFeatures(); - const featuresToActivate = setUnion( - this._featureGroups.get(REQUIRED_FEATURE_GROUP), - this._deferringFeatureActivation - ? setIntersect( - enabledFeatures, - this._featureGroups.get(INITIAL_FEATURE_GROUP), - ) - : enabledFeatures, - ); - - // Enable all packages in featuresToActivate but not in currentState. + const featuresToActivate = (0, _collection().setUnion)(this._featureGroups.get(REQUIRED_FEATURE_GROUP), this._deferringFeatureActivation ? (0, _collection().setIntersect)(enabledFeatures, this._featureGroups.get(INITIAL_FEATURE_GROUP)) : enabledFeatures); // Enable all packages in featuresToActivate but not in currentState. // Disable all packages not in featuresToActivate but in currentState. + for (const feature of featuresToActivate) { if (!this._currentlyActiveFeatures.has(feature)) { this._featureBeingActivated = feature; @@ -272,74 +263,63 @@ export default class FeatureLoader { this._currentlyActiveFeatures = featuresToActivate; } - deactivate(): void { - invariant( - this._activationDisposable && !this._activationDisposable.disposed, - ); + deactivate() { + if (!(this._activationDisposable && !this._activationDisposable.disposed)) { + throw new Error("Invariant violation: \"this._activationDisposable && !this._activationDisposable.disposed\""); + } this._currentlyActiveFeatures.forEach(feature => { // Deactivate the package, but don't serialize. That needs to be done in a separate phase so that // we don't end up disconnecting a service and then serializing the disconnected state. safeDeactivate(feature, true); }); + this._currentlyActiveFeatures = new Set(); - invariant(this._activationDisposable); // reasserting for flow + if (!this._activationDisposable) { + throw new Error("Invariant violation: \"this._activationDisposable\""); + } // reasserting for flow + + this._activationDisposable.dispose(); + this._activationDisposable = null; } - /** * Determine which features are enabled based on the current state of the configuration. This set * is then used to load and activate the features. */ - getEnabledFeatures(): Set { + + + getEnabledFeatures() { // we know enabledFeatureGroups must be ?Array, and useFeatureRules must be ?UseFeatureRules, // since it's in our schema. However, flow thinks it's a mixed type, since it doesn't know about - // the schema enforcements. - const useFeatureRules: ?UseFeatureRules = (atom.config.get( - this.getUseKeyPath(), - ): any); - const enabledFeatureGroups: ?Array = (atom.config.get( - this.getEnabledFeatureGroupsKeyPath(), - ): any); - - const featuresInEnabledGroups = - enabledFeatureGroups == null - ? new Set(this._features) // If featuregroups is undefined, assume all features should be enabled. - : setUnion( - ...enabledFeatureGroups.map(featureGroup => - this._featureGroups.get(featureGroup), - ), - ); - - const requiredFeatures = - this._featureGroups.get(REQUIRED_FEATURE_GROUP) || new Set(); - - // If a feature is "always enabled", it should be on whether or not a feature-group includes it. + const useFeatureRules = atom.config.get(this.getUseKeyPath()); + const enabledFeatureGroups = atom.config.get(this.getEnabledFeatureGroupsKeyPath()); + const featuresInEnabledGroups = enabledFeatureGroups == null ? new Set(this._features) // If featuregroups is undefined, assume all features should be enabled. + : (0, _collection().setUnion)(...enabledFeatureGroups.map(featureGroup => this._featureGroups.get(featureGroup))); + const requiredFeatures = this._featureGroups.get(REQUIRED_FEATURE_GROUP) || new Set(); // If a feature is "always enabled", it should be on whether or not a feature-group includes it. // If a feature is "default", it should be on if and only if a feature-group includes it. - return new Set( - this._features.filter(feature => { - const featureName = packageNameFromPath(feature.path); - const rawRule = idx(useFeatureRules, _ => _[featureName]); - const rule = - rawRule == null ? getFeatureDefaultValue(feature) : rawRule; - return ( - rule === ALWAYS_ENABLED || - rule === true || - (featuresInEnabledGroups.has(feature) && rule === DEFAULT) || - requiredFeatures.has(feature) - ); - }), - ); + + return new Set(this._features.filter(feature => { + var _ref; + + const featureName = packageNameFromPath(feature.path); + const rawRule = (_ref = useFeatureRules) != null ? _ref[featureName] : _ref; + const rule = rawRule == null ? getFeatureDefaultValue(feature) : rawRule; + return rule === ALWAYS_ENABLED || rule === true || featuresInEnabledGroups.has(feature) && rule === DEFAULT || requiredFeatures.has(feature); + })); } - getConfig(): Object { - invariant(this._config != null); + getConfig() { + if (!(this._config != null)) { + throw new Error("Invariant violation: \"this._config != null\""); + } + return this._config; } - serialize(): void { + serialize() { // When the root package is serialized, all of its features need to be serialized. This is an abuse of // `serialize()` since we're using it to do side effects instead of returning the serialization, // but it ensures that serialization of the Atom packages happens at the right point in the @@ -348,22 +328,24 @@ export default class FeatureLoader { this._features.forEach(safeSerialize); } - getUseKeyPath(): string { + getUseKeyPath() { return `${this._pkgName}.use`; } - getEnabledFeatureGroupsKeyPath(): string { + getEnabledFeatureGroupsKeyPath() { return `${this._pkgName}.enabledFeatureGroups`; } + } -function safeDeactivate( - feature: Feature, - suppressSerialization: boolean = false, -) { +exports.default = FeatureLoader; + +function safeDeactivate(feature, suppressSerialization = false) { const name = packageNameFromPath(feature.path); + try { const pack = atom.packages.getLoadedPackage(name); + if (pack != null) { atom.packages.deactivatePackage(name, suppressSerialization); } @@ -373,17 +355,17 @@ function safeDeactivate( } } -function getFeatureDefaultValue(feature: Feature): string { +function getFeatureDefaultValue(feature) { const name = packageNameFromPath(feature.path); - return name.startsWith('sample-') || name.startsWith('fb-sample-') - ? NEVER_ENABLED - : DEFAULT; + return name.startsWith('sample-') || name.startsWith('fb-sample-') ? NEVER_ENABLED : DEFAULT; } -function safeSerialize(feature: Feature) { +function safeSerialize(feature) { const name = packageNameFromPath(feature.path); + try { const pack = atom.packages.getActivePackage(name); + if (pack != null) { // Serialize the package atom.packages.serializePackage(pack); @@ -392,48 +374,48 @@ function safeSerialize(feature: Feature) { // eslint-disable-next-line no-console console.error(`Error serializing "${name}": ${err.message}`); } -} - -// this could be inlined into its use above, but this makes the intent more +} // this could be inlined into its use above, but this makes the intent more // explicit, and unifies it in the case this ever needs to change. -function packageNameFromPath(pkgPath: string): string { - return path.basename(pkgPath); + + +function packageNameFromPath(pkgPath) { + return _path2.default.basename(pkgPath); } -function packageIsRepositoryProvider(pkg: FeaturePkg): boolean { - return Boolean(idx(pkg, _ => _.providedServices['atom.repository-provider'])); +function packageIsRepositoryProvider(pkg) { + var _ref2; + + return Boolean((_ref2 = pkg) != null ? (_ref2 = _ref2.providedServices) != null ? _ref2['atom.repository-provider'] : _ref2 : _ref2); } -function buildConfig(features: Array): Object { +function buildConfig(features) { const config = { use: { title: 'Enabled Features', description: 'Enable and disable individual features', type: 'object', collapsed: true, - properties: {}, - }, + properties: {} + } }; features.forEach(feature => { const featurePkg = feature.pkg; const name = packageNameFromPath(feature.path); - const setting = { - title: - featurePkg.displayName == null - ? `Enable the "${name}" feature` - : `Enable ${featurePkg.displayName}`, + title: featurePkg.displayName == null ? `Enable the "${name}" feature` : `Enable ${featurePkg.displayName}`, description: featurePkg.description || '', type: 'string', - enum: [ - {value: ALWAYS_ENABLED, description: 'Always enabled'}, - {value: NEVER_ENABLED, description: 'Never enabled'}, - { - value: DEFAULT, - description: 'Only when in an enabled package group', - }, - ], - default: getFeatureDefaultValue(feature), + enum: [{ + value: ALWAYS_ENABLED, + description: 'Always enabled' + }, { + value: NEVER_ENABLED, + description: 'Never enabled' + }, { + value: DEFAULT, + description: 'Only when in an enabled package group' + }], + default: getFeatureDefaultValue(feature) }; if (devMode) { @@ -441,18 +423,16 @@ function buildConfig(features: Array): Object { const provides = Object.keys(featurePkg.providedServices).join(', '); setting.description += `
    **Provides:** _${provides}_`; } + if (featurePkg.consumedServices) { const consumes = Object.keys(featurePkg.consumedServices).join(', '); setting.description += `
    **Consumes:** _${consumes}_`; } } - config.use.properties[name] = setting; + config.use.properties[name] = setting; // Merge in the feature's config - // Merge in the feature's config - const featurePkgConfig = - featurePkg.atomConfig || - (featurePkg.nuclide && featurePkg.nuclide.config); + const featurePkgConfig = featurePkg.atomConfig || featurePkg.nuclide && featurePkg.nuclide.config; if (featurePkgConfig) { config[name] = { @@ -460,19 +440,17 @@ function buildConfig(features: Array): Object { title: featurePkg.displayName, description: featurePkg.description, collapsed: true, - properties: {}, + properties: {} }; Object.keys(featurePkgConfig).forEach(key => { - config[name].properties[key] = { - ...featurePkgConfig[key], - title: featurePkgConfig[key].title || key, - }; + config[name].properties[key] = Object.assign({}, featurePkgConfig[key], { + title: featurePkgConfig[key].title || key + }); }); } }); return config; } - /** * Hack time!! Atom's repository APIs are synchronous. Any package that tries to use them before * we've had a chance to provide our implementation are going to get wrong answers. The correct @@ -481,128 +459,120 @@ function buildConfig(features: Array): Object { * codepaths that make that difficult. As a temporary (I hope) workaround, we prioritize * activation of the features that provide this service. */ -function reorderFeatures(features_: Array): Array { + + +function reorderFeatures(features_) { const features = features_.slice(); const originalOrder = new Map(features.map((feature, i) => [feature, i])); features.sort((a, b) => { const aIsRepoProvider = packageIsRepositoryProvider(a.pkg); const bIsRepoProvider = packageIsRepositoryProvider(b.pkg); + if (aIsRepoProvider !== bIsRepoProvider) { return aIsRepoProvider ? -1 : 1; } - const aIndex = nullthrows(originalOrder.get(a)); - const bIndex = nullthrows(originalOrder.get(b)); + + const aIndex = (0, _nullthrows().default)(originalOrder.get(a)); + const bIndex = (0, _nullthrows().default)(originalOrder.get(b)); return aIndex - bIndex; }); return features; } - /** * Construct a map whose keys are feature group names and values are sets of features belonging to * the group. */ -function groupFeatures( - features: Array, - rawFeatureGroups: { - [string]: Array, - }, -): MultiMap { + + +function groupFeatures(features, rawFeatureGroups) { const namesToFeatures = new Map(); features.forEach(feature => { - namesToFeatures.set(path.basename(feature.path), feature); + namesToFeatures.set(_path2.default.basename(feature.path), feature); }); + const featureGroups = new (_collection().MultiMap)(); - const featureGroups = new MultiMap(); for (const key of Object.keys(rawFeatureGroups)) { if (Array.isArray(rawFeatureGroups[key])) { - const featuresForKey = rawFeatureGroups[key] - .map(featureName => namesToFeatures.get(featureName)) - .filter(Boolean); + const featuresForKey = rawFeatureGroups[key].map(featureName => namesToFeatures.get(featureName)).filter(Boolean); + if (featuresForKey != null) { featureGroups.set(key, featuresForKey); } } } + return featureGroups; } - /** * Patch the package manager and packages to (1) implement `onWillInitializePackage` and (2) call * `registerConfigSchemaFromMainModule()` when a package is initialized (to guarantee its config * schema is ready when its deserializers are called). This should be removed once these changes * are upstreamed. */ -function patchPackageManager(): void { - if ( - (atom.packages: any).onWillInitializePackage == null && - !(atom.packages: any).__onWillInitializePackagePatched - ) { - (atom.packages: any).onWillInitializePackage = function(callback) { - (atom.packages: any).__onWillInitializePackagePatched = true; + + +function patchPackageManager() { + if (atom.packages.onWillInitializePackage == null && !atom.packages.__onWillInitializePackagePatched) { + atom.packages.onWillInitializePackage = function (callback) { + atom.packages.__onWillInitializePackagePatched = true; return this.emitter.on('will-initialize-package', callback); }; } - if (!(atom.packages: any).__packageLookupPatched) { - (atom.packages: any).__packageLookupPatched = true; + if (!atom.packages.__packageLookupPatched) { + atom.packages.__packageLookupPatched = true; const loadPackage = atom.packages.loadPackage; - (atom.packages: any).loadPackage = function(nameOrPath, ...args) { + + atom.packages.loadPackage = function (nameOrPath, ...args) { const pack = loadPackage.call(this, nameOrPath, ...args); + if (pack == null) { return null; } + patchPackage(pack); return pack; }; const getLoadedPackage = atom.packages.getLoadedPackage; - (atom.packages: any).getLoadedPackage = function(name, ...args) { + + atom.packages.getLoadedPackage = function (name, ...args) { const pack = getLoadedPackage.call(this, name, ...args); + if (pack == null) { return null; } + patchPackage(pack); return pack; }; } } -function patchPackage(pack): void { - if ((pack: any).__initializeIfNeededPatched) { +function patchPackage(pack) { + if (pack.__initializeIfNeededPatched) { return; } - (pack: any).__initializeIfNeededPatched = true; - const initializeIfNeeded = (pack: any).initializeIfNeeded; - (pack: any).initializeIfNeeded = function() { + pack.__initializeIfNeededPatched = true; + const initializeIfNeeded = pack.initializeIfNeeded; + + pack.initializeIfNeeded = function () { if (this.mainInitialized) { return; } - if ((atom.packages: any).__onWillInitializePackagePatched) { + + if (atom.packages.__onWillInitializePackagePatched) { // If we didn't apply our patch for this, Atom is already dispatching the event. atom.packages.emitter.emit('will-initialize-package', pack); } + this.registerConfigSchemaFromMainModule(); return initializeIfNeeded.call(this); }; } -const didLoadPackage = (pkgName: string) => - observableFromSubscribeFunction(cb => atom.packages.onDidLoadPackage(cb)) - .startWith(null) - .filter(() => atom.packages.getLoadedPackage(pkgName) != null) - .take(1); - -const didAddFirstPath = observableFromSubscribeFunction(cb => - atom.project.onDidChangePaths(cb), -) - .startWith(null) - .filter(() => atom.project.getDirectories().length > 0) - .take(1); - -const didAddFirstTextEditor = observableFromSubscribeFunction(cb => - atom.workspace.getCenter().onDidAddTextEditor(cb), -) - .startWith(null) - .filter(() => atom.workspace.getCenter().getTextEditors().length > 0) - .take(1); +const didLoadPackage = pkgName => (0, _event().observableFromSubscribeFunction)(cb => atom.packages.onDidLoadPackage(cb)).startWith(null).filter(() => atom.packages.getLoadedPackage(pkgName) != null).take(1); + +const didAddFirstPath = (0, _event().observableFromSubscribeFunction)(cb => atom.project.onDidChangePaths(cb)).startWith(null).filter(() => atom.project.getDirectories().length > 0).take(1); +const didAddFirstTextEditor = (0, _event().observableFromSubscribeFunction)(cb => atom.workspace.getCenter().onDidAddTextEditor(cb)).startWith(null).filter(() => atom.workspace.getCenter().getTextEditors().length > 0).take(1); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/FocusBoomerang.js b/modules/nuclide-commons-atom/FocusBoomerang.js index 169a6f8c..7720ccc3 100644 --- a/modules/nuclide-commons-atom/FocusBoomerang.js +++ b/modules/nuclide-commons-atom/FocusBoomerang.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +13,41 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -type Focus = { - node: ?HTMLElement, - pane: ?atom$Pane, -}; - -export default class FocusBoomerang { - _focus: ?Focus; - - recordFocus(): void { +class FocusBoomerang { + recordFocus() { if (this._focus != null) { return; } this._focus = { node: document.activeElement, - pane: atom.workspace.getActivePane(), + pane: atom.workspace.getActivePane() }; } - returnFocus(): void { + returnFocus() { if (this._focus == null) { return; } - const {node, pane} = this._focus; + + const { + node, + pane + } = this._focus; + if (node != null && document.body != null && document.body.contains(node)) { node.focus(); return; } + if (pane != null && !pane.isDestroyed()) { pane.activate(); } } + } + +exports.default = FocusBoomerang; \ No newline at end of file diff --git a/modules/nuclide-commons-atom/ProjectManager.js b/modules/nuclide-commons-atom/ProjectManager.js index c325eddc..ddc6cebe 100644 --- a/modules/nuclide-commons-atom/ProjectManager.js +++ b/modules/nuclide-commons-atom/ProjectManager.js @@ -1,125 +1,173 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import AsyncStorage from 'idb-keyval'; -import LRUCache from 'lru-cache'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {BehaviorSubject, Observable, Subject} from 'rxjs'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {without} from 'lodash'; -import toml from 'toml'; -import season from 'season'; -import fsPromise from 'nuclide-commons/fsPromise'; +"use strict"; -const RECENT_PROJECTS_KEY = 'nuclide_recent_projects'; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports._validateProjectSpec = _validateProjectSpec; +exports.default = void 0; + +function _without2() { + const data = _interopRequireDefault(require("lodash/without")); + + _without2 = function () { + return data; + }; + + return data; +} + +function _idbKeyval() { + const data = _interopRequireDefault(require("idb-keyval")); + + _idbKeyval = function () { + return data; + }; + + return data; +} + +function _lruCache() { + const data = _interopRequireDefault(require("lru-cache")); + + _lruCache = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _toml() { + const data = _interopRequireDefault(require("toml")); + + _toml = function () { + return data; + }; + + return data; +} + +function _season() { + const data = _interopRequireDefault(require("season")); + + _season = function () { + return data; + }; -type ProjectFile = {| - repo: string, - path: string, - originPath?: string, -|}; + return data; +} + +function _fsPromise() { + const data = _interopRequireDefault(require("../nuclide-commons/fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} -type ProjectSession = {| - projectFile: ProjectFile, - host: string, -|}; +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -export type ProjectSessions = {| - projectFile: ProjectFile, - hosts: Array, - lastAccessed: number, -|}; +const RECENT_PROJECTS_KEY = 'nuclide_recent_projects'; class ProjectManager { - _projects: BehaviorSubject = new BehaviorSubject(); - _recentProjects: Subject> = new Subject(); - - getProjects(): Observable> { - return Observable.concat( - Observable.defer(() => this.getRecentProjects()), - this._recentProjects.asObservable(), - ); + constructor() { + this._projects = new _RxMin.BehaviorSubject(); + this._recentProjects = new _RxMin.Subject(); + } + + getProjects() { + return _RxMin.Observable.concat(_RxMin.Observable.defer(() => this.getRecentProjects()), this._recentProjects.asObservable()); } - getActiveProject(): ?ProjectSession { + getActiveProject() { return this._projects.getValue(); } - observeActiveProjectSpec( - cb: (spec: ?atom$ProjectSpecification) => mixed, - ): IDisposable { + observeActiveProjectSpec(cb) { // TODO: Remove after `atom.project.getSpecification()` is upstreamed. - if ( - typeof atom.project.onDidReplace !== 'function' || - // $FlowFixMe: Add this to the typedef after we've upstreamed. - typeof atom.project.getSpecification !== 'function' - ) { + if (typeof atom.project.onDidReplace !== 'function' || // $FlowFixMe: Add this to the typedef after we've upstreamed. + typeof atom.project.getSpecification !== 'function') { cb(null); - return new UniversalDisposable(); + return new (_UniversalDisposable().default)(); } - return new UniversalDisposable( - observableFromSubscribeFunction(callback => - atom.project.onDidReplace(callback), - ) - // $FlowFixMe: Add this to the typedef after we've upstreamed. - .startWith(atom.project.getSpecification()) - .subscribe(cb), - ); + return new (_UniversalDisposable().default)((0, _event().observableFromSubscribeFunction)(callback => atom.project.onDidReplace(callback)) // $FlowFixMe: Add this to the typedef after we've upstreamed. + .startWith(atom.project.getSpecification()).subscribe(cb)); } - /** * Open a project. While this can handle both local and remote URIs, there must already be a * connection to the host of the remote project file or it will error. */ - async open(uri: NuclideUri): Promise { + + + async open(uri) { let fsApi; - let expandedUri: ?NuclideUri; - let realPath: ?NuclideUri; + let expandedUri; + let realPath; - if (nuclideUri.isRemote(uri)) { + if (_nuclideUri().default.isRemote(uri)) { fsApi = await getFsServiceFor(uri); + if (fsApi == null) { throw new Error('Tried to load a remote project without a connection.'); } - realPath = await fsApi.resolveRealPath(nuclideUri.getPath(uri)); - expandedUri = nuclideUri.resolve(uri, realPath); + + realPath = await fsApi.resolveRealPath(_nuclideUri().default.getPath(uri)); + expandedUri = _nuclideUri().default.resolve(uri, realPath); } else { - fsApi = fsPromise; - expandedUri = nuclideUri.expandHomeDir(uri); + fsApi = _fsPromise().default; + expandedUri = _nuclideUri().default.expandHomeDir(uri); realPath = expandedUri; } let rawContents; + try { rawContents = parseProject((await fsApi.readFile(realPath)).toString()); } catch (e) { - throw new Error( - `Unable to find or parse atomproject at ${expandedUri}, make sure that the root repo` + - ' (ie: fbsource, www) is in the correct location.', - ); + throw new Error(`Unable to find or parse atomproject at ${expandedUri}, make sure that the root repo` + ' (ie: fbsource, www) is in the correct location.'); } - const spec = _validateProjectSpec({ - ...rawContents, - originPath: expandedUri, - }); - - // Even though the project may list a bunch of directories, we actually mount the repository + const spec = _validateProjectSpec(Object.assign({}, rawContents, { + originPath: expandedUri + })); // Even though the project may list a bunch of directories, we actually mount the repository // root and then filter. This is a change from the normal Atom handling so we need to // account for that. + + const repoRoots = await getRepoRoots(spec.paths, fsApi); if (repoRoots.length !== 0) { @@ -128,58 +176,59 @@ class ProjectManager { // tree. spec._paths = spec.paths; spec.paths = repoRoots; - } + } // $FlowFixMe: Add typedef + - // $FlowFixMe: Add typedef - atom.project.replace({...spec}); + atom.project.replace(Object.assign({}, spec)); } - async addRecentProject( - projectFile: ProjectFile, - host: string, - ): Promise { + async addRecentProject(projectFile, host) { const recentProjects = await loadRecentProjects(); const key = projectFileToKey(projectFile); const project = recentProjects.get(key) || { lastAccessed: 0, projectFile, - hosts: [], + hosts: [] }; - project.hosts = without(project.hosts, host); + project.hosts = (0, _without2().default)(project.hosts, host); project.hosts.unshift(host); project.lastAccessed = Date.now(); recentProjects.set(key, project); await saveRecentProjects(recentProjects); - this._projects.next({projectFile, host}); + + this._projects.next({ + projectFile, + host + }); + this._recentProjects.next(projectsToList(recentProjects)); } - async getRecentProjects(): Promise> { - const recents = projectsToList(await loadRecentProjects()); + async getRecentProjects() { + const recents = projectsToList((await loadRecentProjects())); return recents; } + } -export default new ProjectManager(); +var _default = new ProjectManager(); + +exports.default = _default; -function projectsToList( - projects: LRUCache, -): Array { - return projects - .dump() - .map(pair => pair.v) - .sort((a, b) => b.lastAccessed - a.lastAccessed); +function projectsToList(projects) { + return projects.dump().map(pair => pair.v).sort((a, b) => b.lastAccessed - a.lastAccessed); } -function projectFileToKey(projectFile: ProjectFile): string { +function projectFileToKey(projectFile) { return [projectFile.repo, projectFile.path].join('#'); } -async function loadRecentProjects(): Promise< - LRUCache, -> { - const recentProjectsEntries = await AsyncStorage.get(RECENT_PROJECTS_KEY); - const recentProjects = new LRUCache({max: 100}); +async function loadRecentProjects() { + const recentProjectsEntries = await _idbKeyval().default.get(RECENT_PROJECTS_KEY); + const recentProjects = new (_lruCache().default)({ + max: 100 + }); + if (recentProjectsEntries) { recentProjects.load(recentProjectsEntries); } @@ -187,61 +236,44 @@ async function loadRecentProjects(): Promise< return recentProjects; } -async function saveRecentProjects( - recentProjects: LRUCache, -): Promise { - await AsyncStorage.set(RECENT_PROJECTS_KEY, recentProjects.dump()); +async function saveRecentProjects(recentProjects) { + await _idbKeyval().default.set(RECENT_PROJECTS_KEY, recentProjects.dump()); } -function parseProject(raw: string): any { +function parseProject(raw) { try { - return toml.parse(raw); + return _toml().default.parse(raw); } catch (err) { if (err.name === 'SyntaxError') { - return season.parse(raw); + return _season().default.parse(raw); } + throw err; } } -export function _validateProjectSpec( - raw: atom$ProjectSpecification, -): { - originPath: NuclideUri, - paths: Array, - _paths?: ?Array, -} { - const spec = {...raw}; - const baseDir = nuclideUri.dirname(nuclideUri.getPath(spec.originPath)); +function _validateProjectSpec(raw) { + const spec = Object.assign({}, raw); + + const baseDir = _nuclideUri().default.dirname(_nuclideUri().default.getPath(spec.originPath)); + const originalPaths = raw.paths; - spec.paths = - originalPaths != null && Array.isArray(originalPaths) - ? originalPaths.map(p => nuclideUri.resolve(baseDir, p)) - : [baseDir]; + spec.paths = originalPaths != null && Array.isArray(originalPaths) ? originalPaths.map(p => _nuclideUri().default.resolve(baseDir, p)) : [baseDir]; return spec; } -async function getRepoRoots( - paths: Array, - fsApi, -): Promise> { - const repoRootUris = await Promise.all( - paths.map(async path => { - const hgRoot = await fsApi.findNearestAncestorNamed('.hg', path); - return hgRoot == null - ? fsApi.findNearestAncestorNamed('.git', path) - : hgRoot; - }), - ); - const repoRoots = repoRootUris - .filter(Boolean) - .map(uri => nuclideUri.getPath(nuclideUri.dirname(uri))); +async function getRepoRoots(paths, fsApi) { + const repoRootUris = await Promise.all(paths.map(async path => { + const hgRoot = await fsApi.findNearestAncestorNamed('.hg', path); + return hgRoot == null ? fsApi.findNearestAncestorNamed('.git', path) : hgRoot; + })); + const repoRoots = repoRootUris.filter(Boolean).map(uri => _nuclideUri().default.getPath(_nuclideUri().default.dirname(uri))); return Array.from(new Set(repoRoots)); } -async function getFsServiceFor(uri: NuclideUri) { - const rpcService: nuclide$RpcService = await new Promise(resolve => { +async function getFsServiceFor(uri) { + const rpcService = await new Promise(resolve => { atom.packages.serviceHub.consume('nuclide-rpc-services', '0.0.0', resolve); }); return rpcService.getServiceByNuclideUri('FileSystemService', uri); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/ProjectUtils.js b/modules/nuclide-commons-atom/ProjectUtils.js index f2f77c4f..bdd1beed 100644 --- a/modules/nuclide-commons-atom/ProjectUtils.js +++ b/modules/nuclide-commons-atom/ProjectUtils.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getLabelFromPath = getLabelFromPath; +exports.getLocalPathsForProjectRepo = getLocalPathsForProjectRepo; +exports.getRemotePathsForProjectRepo = getRemotePathsForProjectRepo; +exports.setLocalPathsForProjectRepo = setLocalPathsForProjectRepo; +exports.setRemotePathsForProjectRepo = setRemotePathsForProjectRepo; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("./feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,108 +39,95 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +function getLabelFromPath(path) { + const basename = _nuclideUri().default.basename(path); -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import nuclideUri from 'nuclide-commons/nuclideUri'; -import featureConfig from './feature-config'; -import invariant from 'assert'; - -export function getLabelFromPath(path: string): string { - const basename = nuclideUri.basename(path); const parts = basename.split('.'); return humanizeProjectName(parts[0] || basename); } -function formatProjectNameWord(word: string): string { +function formatProjectNameWord(word) { switch (word) { case 'www': return 'WWW'; + case 'ios': return 'iOS'; + default: return word[0].toUpperCase() + word.slice(1); } } -function humanizeProjectName(name: string): string { +function humanizeProjectName(name) { const hasCapitalLetters = /[A-Z]/.test(name); + const id = x => x; - return name - .split(/[-_]+/) - .map(hasCapitalLetters ? id : formatProjectNameWord) - .join(' '); -} + return name.split(/[-_]+/).map(hasCapitalLetters ? id : formatProjectNameWord).join(' '); +} /** * Gets the array of paths which can be tried on the local machine to find * the location of `repo`. For example, if repo := fbsource, then we are getting * the paths to fbsource on the user's local machine. */ -export function getLocalPathsForProjectRepo(repo: string): Array { + + +function getLocalPathsForProjectRepo(repo) { return getPathsForProjectRepoFromLocation(repo, 'localPaths'); } - /** * Gets the array of paths which can be tried on a remote machine to find * the location of `repo`. For example, if repo := fbsource, then we are getting * the paths to fbsource on the user's remote machine. */ -export function getRemotePathsForProjectRepo(repo: string): Array { + + +function getRemotePathsForProjectRepo(repo) { return getPathsForProjectRepoFromLocation(repo, 'remotePaths'); } -function getPathsForProjectRepoFromLocation( - repo: string, - featureConfigLocation: string, -): Array { +function getPathsForProjectRepoFromLocation(repo, featureConfigLocation) { if (repo == null) { return []; } - const localPaths = featureConfig.get( - `fb-atomprojects.${featureConfigLocation}`, - ); - invariant(Array.isArray(localPaths)); - const repoPaths = localPaths - // $FlowIgnore - .filter(obj => obj.repo === repo) - // $FlowIgnore - .map(obj => obj.path); + const localPaths = _featureConfig().default.get(`fb-atomprojects.${featureConfigLocation}`); + + if (!Array.isArray(localPaths)) { + throw new Error("Invariant violation: \"Array.isArray(localPaths)\""); + } + + const repoPaths = localPaths // $FlowIgnore + .filter(obj => obj.repo === repo) // $FlowIgnore + .map(obj => obj.path); if (repoPaths.length === 0) { repoPaths.push(`~/${repo}`); } + return repoPaths; } - /** * Sets an array of paths which can be tried on the local machine to find * the location of . For example, if repo := fbsource, then we are setting * the paths to fbsource on the user's local machine. */ -export function setLocalPathsForProjectRepo( - paths: Array<{ - path: NuclideUri, - repo: string, - }>, -): void { - featureConfig.set('fb-atomprojects.localPaths', paths); -} + +function setLocalPathsForProjectRepo(paths) { + _featureConfig().default.set('fb-atomprojects.localPaths', paths); +} /** * Sets an array of paths which can be tried on a remote machine to find * the location of . For example, if repo := fbsource, then we are setting * the paths to fbsource on the user's remote machine. */ -export function setRemotePathsForProjectRepo( - paths: Array<{ - path: NuclideUri, - repo: string, - }>, -): void { - featureConfig.set('fb-atomprojects.remotePaths', paths); -} + + +function setRemotePathsForProjectRepo(paths) { + _featureConfig().default.set('fb-atomprojects.remotePaths', paths); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/ProviderRegistry.js b/modules/nuclide-commons-atom/ProviderRegistry.js index 7bec1b4a..d0b4ce97 100644 --- a/modules/nuclide-commons-atom/ProviderRegistry.js +++ b/modules/nuclide-commons-atom/ProviderRegistry.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,75 +25,66 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -export type Provider = { - // Providers with higher priorities will be preferred over lower ones. - priority: number, - // Omitting grammarScopes implies that the provider applies to all grammars. - +grammarScopes?: Array, -}; - -export default class ProviderRegistry { - _providers: Array; - +class ProviderRegistry { constructor() { this._providers = []; } - addProvider(provider: T): IDisposable { - const index = this._providers.findIndex( - p => provider.priority > p.priority, - ); + addProvider(provider) { + const index = this._providers.findIndex(p => provider.priority > p.priority); + if (index === -1) { this._providers.push(provider); } else { this._providers.splice(index, 0, provider); } - return new UniversalDisposable(() => { + + return new (_UniversalDisposable().default)(() => { this.removeProvider(provider); }); } - removeProvider(provider: T): void { + removeProvider(provider) { const index = this._providers.indexOf(provider); + if (index !== -1) { this._providers.splice(index, 1); } } - getProviderForEditor(editor: atom$TextEditor): ?T { + getProviderForEditor(editor) { const grammar = editor.getGrammar().scopeName; return this.findProvider(grammar); } - getAllProvidersForEditor(editor: atom$TextEditor): Iterable { + getAllProvidersForEditor(editor) { const grammar = editor.getGrammar().scopeName; return this.findAllProviders(grammar); } - findProvider(grammar: string): ?T { + findProvider(grammar) { for (const provider of this.findAllProviders(grammar)) { return provider; } + return null; } - /** * Iterates over all providers matching the grammar, in priority order. */ - *findAllProviders(grammar: string): Iterable { + + + *findAllProviders(grammar) { for (const provider of this._providers) { - if ( - provider.grammarScopes == null || - provider.grammarScopes.indexOf(grammar) !== -1 - ) { + if (provider.grammarScopes == null || provider.grammarScopes.indexOf(grammar) !== -1) { yield provider; } } } + } + +exports.default = ProviderRegistry; \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/ActiveEditorRegistry-test.js b/modules/nuclide-commons-atom/__atom_tests__/ActiveEditorRegistry-test.js index 732eb933..86ed5083 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/ActiveEditorRegistry-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/ActiveEditorRegistry-test.js @@ -1,3 +1,29 @@ +"use strict"; + +function _testHelpers() { + const data = require("../../nuclide-commons/test-helpers"); + + _testHelpers = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _ActiveEditorRegistry() { + const data = _interopRequireDefault(require("../ActiveEditorRegistry")); + + _ActiveEditorRegistry = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,70 +32,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type { - Config, - EventSources, - ResultFunction, - Result, -} from '../ActiveEditorRegistry'; - -import type {Observable} from 'rxjs'; - -import {expectObservableToStartWith} from 'nuclide-commons/test-helpers'; -import {Subject} from 'rxjs'; - -import ActiveEditorRegistry from '../ActiveEditorRegistry'; - -type TestProvider = { - priority: number, - grammarScopes: Array, - updateOnEdit?: boolean, -}; - describe('ActiveEditorRegistry', () => { - let activeEditorRegistry: ActiveEditorRegistry< - TestProvider, - void, - > = (null: any); - - let activeEditors: Subject = (null: any); - let editorChanges: Subject = (null: any); - let editorSaves: Subject = (null: any); - - let resultFunction: ResultFunction = (null: any); - let config: Config = (null: any); - let eventSources: EventSources = (null: any); - - let editor1: atom$TextEditor = (null: any); - let editor2: atom$TextEditor = (null: any); - - let events: Observable> = (null: any); - let eventNames: Observable = (null: any); - - let shouldProviderError: boolean = (null: any); + let activeEditorRegistry = null; + let activeEditors = null; + let editorChanges = null; + let editorSaves = null; + let resultFunction = null; + let config = null; + let eventSources = null; + let editor1 = null; + let editor2 = null; + let events = null; + let eventNames = null; + let shouldProviderError = null; function initializeService() { - activeEditorRegistry = new ActiveEditorRegistry( - resultFunction, - config, - eventSources, - ); - + activeEditorRegistry = new (_ActiveEditorRegistry().default)(resultFunction, config, eventSources); events = activeEditorRegistry.getResultsStream().publishReplay(); eventNames = events.map(event => event.kind); events.connect(); } beforeEach(async () => { - activeEditors = new Subject(); - editorChanges = new Subject(); - editorSaves = new Subject(); + activeEditors = new _RxMin.Subject(); + editorChanges = new _RxMin.Subject(); + editorSaves = new _RxMin.Subject(); shouldProviderError = false; - resultFunction = jest.fn().mockImplementation(async () => { if (shouldProviderError) { throw new Error('baaaaad'); @@ -79,217 +70,143 @@ describe('ActiveEditorRegistry', () => { eventSources = { activeEditors, changesForEditor: () => editorChanges, - savesForEditor: () => editorSaves, + savesForEditor: () => editorSaves }; - initializeService(); - editor1 = await atom.workspace.open(); editor2 = await atom.workspace.open(); }); - describe('when there is a provider', () => { - let provider: TestProvider = (null: any); + let provider = null; beforeEach(() => { provider = { priority: 10, - grammarScopes: ['text.plain.null-grammar'], + grammarScopes: ['text.plain.null-grammar'] }; activeEditorRegistry.consumeProvider(provider); }); - it('should create correct event stream during normal use', async () => { activeEditors.next(null); await waitForNextTick(); - activeEditors.next(editor1); await waitForNextTick(); - editorChanges.next(undefined); await waitForNextTick(); - activeEditors.next(editor2); - - await expectObservableToStartWith(eventNames, [ - 'not-text-editor', - 'pane-change', - 'result', - 'edit', - 'result', - 'pane-change', - 'result', - ]); - - const fullEvents = await events - .take(4) - .toArray() - .toPromise(); + await (0, _testHelpers().expectObservableToStartWith)(eventNames, ['not-text-editor', 'pane-change', 'result', 'edit', 'result', 'pane-change', 'result']); + const fullEvents = await events.take(4).toArray().toPromise(); expect(fullEvents[1]).toEqual({ kind: 'pane-change', - editor: editor1, + editor: editor1 }); expect(fullEvents[2]).toEqual({ kind: 'result', editor: editor1, provider, - result: undefined, + result: undefined }); expect(fullEvents[3]).toEqual({ kind: 'edit', - editor: editor1, + editor: editor1 }); }); - it('should not emit save events when it is configured to respond to edit events', async () => { activeEditors.next(editor1); await waitForNextTick(); - editorChanges.next(undefined); await waitForNextTick(); - editorSaves.next(undefined); await waitForNextTick(); - - await expectObservableToStartWith(eventNames, [ - 'pane-change', - 'result', - 'edit', - 'result', - ]); + await (0, _testHelpers().expectObservableToStartWith)(eventNames, ['pane-change', 'result', 'edit', 'result']); }); - describe('when configured to respond to save events', () => { beforeEach(() => { config.updateOnEdit = false; - initializeService(); - // Have to re-add this since the re-initialization kills it + initializeService(); // Have to re-add this since the re-initialization kills it + activeEditorRegistry.consumeProvider({ priority: 10, - grammarScopes: ['text.plain.null-grammar'], + grammarScopes: ['text.plain.null-grammar'] }); }); - it('should generate and respond to save events', async () => { activeEditors.next(editor1); await waitForNextTick(); - editorChanges.next(undefined); await waitForNextTick(); - editorSaves.next(undefined); await waitForNextTick(); - - await expectObservableToStartWith(eventNames, [ - 'pane-change', - 'result', - 'save', - 'result', - ]); - - const fullEvents = await events - .take(3) - .toArray() - .toPromise(); + await (0, _testHelpers().expectObservableToStartWith)(eventNames, ['pane-change', 'result', 'save', 'result']); + const fullEvents = await events.take(3).toArray().toPromise(); expect(fullEvents[2]).toEqual({ kind: 'save', - editor: editor1, + editor: editor1 }); }); }); - describe('when given providers with different updateOnEdit settings', () => { beforeEach(() => { - initializeService(); - // Have to re-add this since the re-initialization kills it + initializeService(); // Have to re-add this since the re-initialization kills it + activeEditorRegistry.consumeProvider({ priority: 10, - grammarScopes: ['text.plain.null-grammar'], + grammarScopes: ['text.plain.null-grammar'] }); activeEditorRegistry.consumeProvider({ priority: 10, grammarScopes: ['source.cpp'], - updateOnEdit: false, + updateOnEdit: false }); jest.spyOn(editor2, 'getGrammar').mockReturnValue({ - scopeName: 'source.cpp', + scopeName: 'source.cpp' }); }); - it('should generate and respond to the appropriate event', async () => { activeEditors.next(editor1); await waitForNextTick(); - editorChanges.next(undefined); await waitForNextTick(); - editorSaves.next(undefined); await waitForNextTick(); - activeEditors.next(editor2); await waitForNextTick(); - editorChanges.next(undefined); await waitForNextTick(); - editorSaves.next(undefined); await waitForNextTick(); - - await expectObservableToStartWith(eventNames, [ - 'pane-change', - 'result', - 'edit', - 'result', - 'pane-change', - 'result', - 'save', - 'result', - ]); + await (0, _testHelpers().expectObservableToStartWith)(eventNames, ['pane-change', 'result', 'edit', 'result', 'pane-change', 'result', 'save', 'result']); }); }); - it("should produce the 'provider-error' event when a provider errors", async () => { shouldProviderError = true; - activeEditors.next(editor1); await waitForNextTick(); - - await expectObservableToStartWith(eventNames, [ - 'pane-change', - 'provider-error', - ]); - - expect(await events.elementAt(1).toPromise()).toEqual({ + await (0, _testHelpers().expectObservableToStartWith)(eventNames, ['pane-change', 'provider-error']); + expect((await events.elementAt(1).toPromise())).toEqual({ kind: 'provider-error', - provider, + provider }); }); - it('should immediately query a better provider', () => { const betterProvider = { priority: 20, - grammarScopes: ['text.plain.null-grammar'], + grammarScopes: ['text.plain.null-grammar'] }; - activeEditors.next(editor1); expect(resultFunction).toHaveBeenCalledWith(provider, editor1); activeEditorRegistry.consumeProvider(betterProvider); expect(resultFunction).toHaveBeenCalledWith(betterProvider, editor1); }); }); - describe('when there is no provider', () => { it("should produce the 'no-provider' result when there is no provider", async () => { activeEditors.next(editor1); await waitForNextTick(); - - await expectObservableToStartWith(eventNames, [ - 'pane-change', - 'no-provider', - ]); + await (0, _testHelpers().expectObservableToStartWith)(eventNames, ['pane-change', 'no-provider']); }); }); }); -function waitForNextTick(): Promise { +function waitForNextTick() { return new Promise(resolve => process.nextTick(resolve)); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/ContextMenu-test.js b/modules/nuclide-commons-atom/__atom_tests__/ContextMenu-test.js index fea7faa7..7921a95e 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/ContextMenu-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/ContextMenu-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _ContextMenu() { + const data = _interopRequireDefault(require("../ContextMenu")); + + _ContextMenu = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,306 +20,390 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import invariant from 'assert'; - -import ContextMenu from '../ContextMenu'; - describe('ContextMenu', () => { const cssSelector = '.nuclide-context-menu-unit-test'; - let div: HTMLDivElement; + let div; let menu; - beforeEach(() => { div = document.createElement('div'); div.className = cssSelector.substring(1); atom.views.getView(atom.workspace).appendChild(div); }); - afterEach(() => { if (menu != null) { menu.dispose(); } + if (div != null) { - invariant(div.parentNode != null); + if (!(div.parentNode != null)) { + throw new Error("Invariant violation: \"div.parentNode != null\""); + } + div.parentNode.removeChild(div); } }); - it('initializes an empty ContextMenu properly', () => { const options = { type: 'root', - cssSelector, + cssSelector }; - menu = new ContextMenu(options); + menu = new (_ContextMenu().default)(options); expect(menu.isEmpty()).toBe(true); }); - it('items added to a context menu appear in priority order', async () => { const options = { type: 'root', - cssSelector, + cssSelector }; - menu = new ContextMenu(options); - - menu.addItem({label: 'second'}, 20); - menu.addItem({label: 'first', command: 'nuclide-do-something'}, 10); - menu.addItem({label: 'fourth'}, 40); - menu.addItem({label: 'third'}, 30); - + menu = new (_ContextMenu().default)(options); + menu.addItem({ + label: 'second' + }, 20); + menu.addItem({ + label: 'first', + command: 'nuclide-do-something' + }, 10); + menu.addItem({ + label: 'fourth' + }, 40); + menu.addItem({ + label: 'third' + }, 30); await waitForNextTick(); - - expect(getTemplateForContextMenu()).toEqual([ - {label: 'first', command: 'nuclide-do-something'}, - {label: 'second'}, - {label: 'third'}, - {label: 'fourth'}, - ]); + expect(getTemplateForContextMenu()).toEqual([{ + label: 'first', + command: 'nuclide-do-something' + }, { + label: 'second' + }, { + label: 'third' + }, { + label: 'fourth' + }]); }); - it('can handle a mix of menu and submenu items', async () => { const options = { type: 'root', - cssSelector, + cssSelector }; - menu = new ContextMenu(options); - - menu.addItem({label: 'two'}, 20); - menu.addItem({label: 'one'}, 10); - menu.addItem({label: 'four'}, 40); - menu.addItem({label: 'three'}, 30); - - const submenu = new ContextMenu({ + menu = new (_ContextMenu().default)(options); + menu.addItem({ + label: 'two' + }, 20); + menu.addItem({ + label: 'one' + }, 10); + menu.addItem({ + label: 'four' + }, 40); + menu.addItem({ + label: 'three' + }, 30); + const submenu = new (_ContextMenu().default)({ type: 'submenu', label: 'sub', - parent: menu, + parent: menu }); menu.addSubmenu(submenu, 25); - submenu.addItem({label: 'B'}, 2); - submenu.addItem({label: 'A'}, 1); - submenu.addItem({label: 'C'}, 3); - + submenu.addItem({ + label: 'B' + }, 2); + submenu.addItem({ + label: 'A' + }, 1); + submenu.addItem({ + label: 'C' + }, 3); await waitForNextTick(); - - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - {label: 'two'}, - { - label: 'sub', - submenu: [{label: 'A'}, {label: 'B'}, {label: 'C'}], - }, - {label: 'three'}, - {label: 'four'}, - ]); + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'two' + }, { + label: 'sub', + submenu: [{ + label: 'A' + }, { + label: 'B' + }, { + label: 'C' + }] + }, { + label: 'three' + }, { + label: 'four' + }]); }); - it('dispose() returned for an item can be used to remove a menu item', async () => { const options = { type: 'root', - cssSelector, + cssSelector }; - menu = new ContextMenu(options); - - const disposableForItem = menu.addItem({label: 'two'}, 20); - menu.addItem({label: 'one'}, 10); - menu.addItem({label: 'four'}, 40); - menu.addItem({label: 'three'}, 30); - - const submenu = new ContextMenu({ + menu = new (_ContextMenu().default)(options); + const disposableForItem = menu.addItem({ + label: 'two' + }, 20); + menu.addItem({ + label: 'one' + }, 10); + menu.addItem({ + label: 'four' + }, 40); + menu.addItem({ + label: 'three' + }, 30); + const submenu = new (_ContextMenu().default)({ type: 'submenu', label: 'sub', - parent: menu, + parent: menu }); const disposableForSubmenu = menu.addSubmenu(submenu, 25); - submenu.addItem({label: 'B'}, 2); - const disposableForSubmenuItem = submenu.addItem({label: 'A'}, 1); - submenu.addItem({label: 'C'}, 3); - + submenu.addItem({ + label: 'B' + }, 2); + const disposableForSubmenuItem = submenu.addItem({ + label: 'A' + }, 1); + submenu.addItem({ + label: 'C' + }, 3); await waitForNextTick(); - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - {label: 'two'}, - { - label: 'sub', - submenu: [{label: 'A'}, {label: 'B'}, {label: 'C'}], - }, - {label: 'three'}, - {label: 'four'}, - ]); - - // Note that unlike addItem() or addSubmenu(), invoking dispose() is synchronous. + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'two' + }, { + label: 'sub', + submenu: [{ + label: 'A' + }, { + label: 'B' + }, { + label: 'C' + }] + }, { + label: 'three' + }, { + label: 'four' + }]); // Note that unlike addItem() or addSubmenu(), invoking dispose() is synchronous. disposableForItem.dispose(); - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - { - label: 'sub', - submenu: [{label: 'A'}, {label: 'B'}, {label: 'C'}], - }, - {label: 'three'}, - {label: 'four'}, - ]); - + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'sub', + submenu: [{ + label: 'A' + }, { + label: 'B' + }, { + label: 'C' + }] + }, { + label: 'three' + }, { + label: 'four' + }]); disposableForSubmenuItem.dispose(); - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - { - label: 'sub', - submenu: [{label: 'B'}, {label: 'C'}], - }, - {label: 'three'}, - {label: 'four'}, - ]); - + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'sub', + submenu: [{ + label: 'B' + }, { + label: 'C' + }] + }, { + label: 'three' + }, { + label: 'four' + }]); disposableForSubmenu.dispose(); - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - {label: 'three'}, - {label: 'four'}, - ]); + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'three' + }, { + label: 'four' + }]); }); - it('removing all submenu items should result in it being filtered from view', async () => { const options = { type: 'root', - cssSelector, + cssSelector }; - menu = new ContextMenu(options); - - menu.addItem({label: 'two'}, 20); - menu.addItem({label: 'one'}, 10); - menu.addItem({label: 'four'}, 40); - menu.addItem({label: 'three'}, 30); - - const submenu = new ContextMenu({ + menu = new (_ContextMenu().default)(options); + menu.addItem({ + label: 'two' + }, 20); + menu.addItem({ + label: 'one' + }, 10); + menu.addItem({ + label: 'four' + }, 40); + menu.addItem({ + label: 'three' + }, 30); + const submenu = new (_ContextMenu().default)({ type: 'submenu', label: 'sub', - parent: menu, + parent: menu }); menu.addSubmenu(submenu, 25); - const disposableForSubmenuItem1 = submenu.addItem({label: 'A'}, 1); - const disposableForSubmenuItem2 = submenu.addItem({label: 'B'}, 2); - const disposableForSubmenuItem3 = submenu.addItem({label: 'C'}, 3); - + const disposableForSubmenuItem1 = submenu.addItem({ + label: 'A' + }, 1); + const disposableForSubmenuItem2 = submenu.addItem({ + label: 'B' + }, 2); + const disposableForSubmenuItem3 = submenu.addItem({ + label: 'C' + }, 3); await waitForNextTick(); - - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - {label: 'two'}, - { - label: 'sub', - submenu: [{label: 'A'}, {label: 'B'}, {label: 'C'}], - }, - {label: 'three'}, - {label: 'four'}, - ]); - + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'two' + }, { + label: 'sub', + submenu: [{ + label: 'A' + }, { + label: 'B' + }, { + label: 'C' + }] + }, { + label: 'three' + }, { + label: 'four' + }]); disposableForSubmenuItem1.dispose(); disposableForSubmenuItem2.dispose(); disposableForSubmenuItem3.dispose(); - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - {label: 'two'}, - {label: 'three'}, - {label: 'four'}, - ]); - - // It should still be possible to add items to the submenu after it has been cleared out. - submenu.addItem({label: 'D'}, 4); - + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'two' + }, { + label: 'three' + }, { + label: 'four' + }]); // It should still be possible to add items to the submenu after it has been cleared out. + + submenu.addItem({ + label: 'D' + }, 4); await waitForNextTick(); - - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - {label: 'two'}, - { - label: 'sub', - submenu: [{label: 'D'}], - }, - {label: 'three'}, - {label: 'four'}, - ]); + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'two' + }, { + label: 'sub', + submenu: [{ + label: 'D' + }] + }, { + label: 'three' + }, { + label: 'four' + }]); }); - it('.isEmpty()', () => { const options = { type: 'root', - cssSelector, + cssSelector }; - menu = new ContextMenu(options); + menu = new (_ContextMenu().default)(options); expect(menu.isEmpty()).toBe(true); - - const submenu = new ContextMenu({ + const submenu = new (_ContextMenu().default)({ type: 'submenu', label: 'sub', - parent: menu, + parent: menu }); const disposableForSubmenu = menu.addSubmenu(submenu, 20); expect(menu.isEmpty()).toBe(false); - disposableForSubmenu.dispose(); expect(menu.isEmpty()).toBe(true); }); - it('.dispose() removes all items', async () => { const options = { type: 'root', - cssSelector, + cssSelector }; - menu = new ContextMenu(options); - - menu.addItem({label: 'two'}, 20); - menu.addItem({label: 'one'}, 10); - menu.addItem({label: 'four'}, 40); - menu.addItem({label: 'three'}, 30); - - const submenu = new ContextMenu({ + menu = new (_ContextMenu().default)(options); + menu.addItem({ + label: 'two' + }, 20); + menu.addItem({ + label: 'one' + }, 10); + menu.addItem({ + label: 'four' + }, 40); + menu.addItem({ + label: 'three' + }, 30); + const submenu = new (_ContextMenu().default)({ type: 'submenu', label: 'sub', - parent: menu, + parent: menu }); menu.addSubmenu(submenu, 25); - submenu.addItem({label: 'B'}, 2); - submenu.addItem({label: 'A'}, 1); - submenu.addItem({label: 'C'}, 3); - + submenu.addItem({ + label: 'B' + }, 2); + submenu.addItem({ + label: 'A' + }, 1); + submenu.addItem({ + label: 'C' + }, 3); await waitForNextTick(); - - expect(getTemplateForContextMenu()).toEqual([ - {label: 'one'}, - {label: 'two'}, - { - label: 'sub', - submenu: [{label: 'A'}, {label: 'B'}, {label: 'C'}], - }, - {label: 'three'}, - {label: 'four'}, - ]); - + expect(getTemplateForContextMenu()).toEqual([{ + label: 'one' + }, { + label: 'two' + }, { + label: 'sub', + submenu: [{ + label: 'A' + }, { + label: 'B' + }, { + label: 'C' + }] + }, { + label: 'three' + }, { + label: 'four' + }]); expect(menu.isEmpty()).toBe(false); menu.dispose(); expect(menu.isEmpty()).toBe(true); expect(getTemplateForContextMenu()).toEqual([]); }); - function getTemplateForContextMenu(): Array { - const template: Array = - // $FlowIgnore: This relies on an non-public API of Atom's ContextMenuManager. - atom.contextMenu.templateForElement(div); - const lastItem = template[template.length - 1]; - // Unfortunately, Atom does not give us a way to exclude the 'Inspect Element' item from + function getTemplateForContextMenu() { + const template = // $FlowIgnore: This relies on an non-public API of Atom's ContextMenuManager. + atom.contextMenu.templateForElement(div); + const lastItem = template[template.length - 1]; // Unfortunately, Atom does not give us a way to exclude the 'Inspect Element' item from // a custom context menu. For now, we exclude it from the template to reduce noise in our // unit tests. + if (lastItem.label === 'Inspect Element') { template.pop(); } + return template; } }); - /** * Calls to ContextMenu.addItem() and ContextMenu.addSubmenu() on the same turn of the event loop * batch up their internal _sort() calls to run at the end of the current event loop. This function @@ -313,6 +411,7 @@ describe('ContextMenu', () => { * * @return Promise that resolves on process.nextTick(). */ -function waitForNextTick(): Promise { + +function waitForNextTick() { return new Promise(resolve => process.nextTick(resolve)); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/FeatureLoader-test.js b/modules/nuclide-commons-atom/__atom_tests__/FeatureLoader-test.js index 3b35aaea..a78f111d 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/FeatureLoader-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/FeatureLoader-test.js @@ -1,3 +1,21 @@ +"use strict"; + +var _fs = _interopRequireDefault(require("fs")); + +var _path = _interopRequireDefault(require("path")); + +function _FeatureLoader() { + const data = _interopRequireDefault(require("../FeatureLoader")); + + _FeatureLoader = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,129 +24,86 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import fs from 'fs'; // eslint-disable-next-line nuclide-internal/prefer-nuclide-uri -import path from 'path'; - -import FeatureLoader from '../FeatureLoader'; - -const FEATURE_PACKAGE_PATH = path.join( - __dirname, - '../__mocks__/fixtures', - 'feature-package', -); +const FEATURE_PACKAGE_PATH = _path.default.join(__dirname, '../__mocks__/fixtures', 'feature-package'); const ALWAYS_ENABLED = 'always'; +const featurePkg = JSON.parse(_fs.default.readFileSync(_path.default.join(FEATURE_PACKAGE_PATH, 'package.json')).toString()); -const featurePkg = JSON.parse( - fs.readFileSync(path.join(FEATURE_PACKAGE_PATH, 'package.json')).toString(), -); -const featureName = path.basename(FEATURE_PACKAGE_PATH); +const featureName = _path.default.basename(FEATURE_PACKAGE_PATH); const ROOT_PACKAGE_DIRNAME = 'root-package'; -const ROOT_PACKAGE_PATH = path.join( - __dirname, - '../__mocks__/fixtures', - ROOT_PACKAGE_DIRNAME, -); -const rootName = path.basename(ROOT_PACKAGE_PATH); + +const ROOT_PACKAGE_PATH = _path.default.join(__dirname, '../__mocks__/fixtures', ROOT_PACKAGE_DIRNAME); + +const rootName = _path.default.basename(ROOT_PACKAGE_PATH); describe('FeatureLoader', () => { let loader; beforeEach(() => { - loader = new FeatureLoader({ + loader = new (_FeatureLoader().default)({ path: ROOT_PACKAGE_PATH, pkgName: rootName, - features: [ - { - path: FEATURE_PACKAGE_PATH, - pkg: featurePkg, - }, - ], + features: [{ + path: FEATURE_PACKAGE_PATH, + pkg: featurePkg + }] }); atom.packages.loadPackage(ROOT_PACKAGE_PATH); }); - describe('load', () => { beforeEach(() => { jest.spyOn(atom.packages, 'loadPackage').mockImplementation(() => {}); atom.config.set(`${rootName}.use.${featureName}`, ALWAYS_ENABLED); loader.load(); atom.packages.emitter.emit('did-load-package', { - name: ROOT_PACKAGE_DIRNAME, + name: ROOT_PACKAGE_DIRNAME }); }); - it('sets a description including provided and consumed services', () => { - expect( - loader.getConfig()?.use?.properties?.[featureName]?.description, - ).toEqual( - 'Hyperclick UI
    **Provides:** _hyperclick.observeTextEditor_
    **Consumes:** _hyperclick_', - ); - }); + var _loader$getConfig, _loader$getConfig$use, _loader$getConfig$use2, _loader$getConfig$use3; - it("merges the feature config into the passed config's feature properties", () => { - expect(loader.getConfig()?.[featureName]?.properties).toEqual( - featurePkg.atomConfig, - ); + expect((_loader$getConfig = loader.getConfig()) === null || _loader$getConfig === void 0 ? void 0 : (_loader$getConfig$use = _loader$getConfig.use) === null || _loader$getConfig$use === void 0 ? void 0 : (_loader$getConfig$use2 = _loader$getConfig$use.properties) === null || _loader$getConfig$use2 === void 0 ? void 0 : (_loader$getConfig$use3 = _loader$getConfig$use2[featureName]) === null || _loader$getConfig$use3 === void 0 ? void 0 : _loader$getConfig$use3.description).toEqual('Hyperclick UI
    **Provides:** _hyperclick.observeTextEditor_
    **Consumes:** _hyperclick_'); }); + it("merges the feature config into the passed config's feature properties", () => { + var _loader$getConfig2, _loader$getConfig2$fe; + expect((_loader$getConfig2 = loader.getConfig()) === null || _loader$getConfig2 === void 0 ? void 0 : (_loader$getConfig2$fe = _loader$getConfig2[featureName]) === null || _loader$getConfig2$fe === void 0 ? void 0 : _loader$getConfig2$fe.properties).toEqual(featurePkg.atomConfig); + }); it.skip('loads the feature package when the root package loads', () => { - expect(atom.packages.loadPackage).toHaveBeenCalledWith( - FEATURE_PACKAGE_PATH, - ); + expect(atom.packages.loadPackage).toHaveBeenCalledWith(FEATURE_PACKAGE_PATH); }); }); - describe('activate', () => { it.skip('activates the feature package right away if enabled', () => { jest.spyOn(atom.packages, 'activatePackage').mockImplementation(() => {}); - loader.load(); atom.config.set(`${rootName}.use.${featureName}`, ALWAYS_ENABLED); atom.packages.emitter.emit('did-load-package', { - name: ROOT_PACKAGE_DIRNAME, + name: ROOT_PACKAGE_DIRNAME }); loader.activate(); - - expect(atom.packages.activatePackage).toHaveBeenCalledWith( - FEATURE_PACKAGE_PATH, - ); + expect(atom.packages.activatePackage).toHaveBeenCalledWith(FEATURE_PACKAGE_PATH); }); }); - describe('activating, deactivating, then activating again', () => { it.skip('actives, deactivates, then activates feature packages', () => { jest.spyOn(atom.packages, 'activatePackage').mockImplementation(() => {}); - jest - .spyOn(atom.packages, 'deactivatePackage') - .mockImplementation(() => {}); - + jest.spyOn(atom.packages, 'deactivatePackage').mockImplementation(() => {}); loader.load(); atom.config.set(`${rootName}.use.${featureName}`, ALWAYS_ENABLED); atom.packages.emitter.emit('did-load-package', { - name: ROOT_PACKAGE_DIRNAME, + name: ROOT_PACKAGE_DIRNAME }); loader.activate(); - - expect(atom.packages.activatePackage).toHaveBeenCalledWith( - FEATURE_PACKAGE_PATH, - ); - + expect(atom.packages.activatePackage).toHaveBeenCalledWith(FEATURE_PACKAGE_PATH); loader.deactivate(); - expect(atom.packages.deactivatePackage).toHaveBeenCalledWith( - featureName, - true, - ); - + expect(atom.packages.deactivatePackage).toHaveBeenCalledWith(featureName, true); loader.activate(); - expect(atom.packages.activatePackage).toHaveBeenCalledWith( - FEATURE_PACKAGE_PATH, - ); + expect(atom.packages.activatePackage).toHaveBeenCalledWith(FEATURE_PACKAGE_PATH); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/debounced-test.js b/modules/nuclide-commons-atom/__atom_tests__/debounced-test.js index f4266657..4c6f258b 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/debounced-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/debounced-test.js @@ -1,3 +1,39 @@ +"use strict"; + +var _atom = require("atom"); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _debounced() { + const data = require("../debounced"); + + _debounced = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("../../nuclide-commons/promise"); + + _promise = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,81 +42,50 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {Point} from 'atom'; -import {Observable} from 'rxjs'; -import { - editorScrollTopDebounced, - observeActivePaneItemDebounced, - observeActiveEditorsDebounced, - editorChangesDebounced, - observeTextEditorsPositions, -} from '../debounced'; -import {goToLocationInEditor} from '../go-to-location'; - -import {sleep} from 'nuclide-commons/promise'; - // Shorter than the default so the tests don't run long. -const DEBOUNCE_INTERVAL = 10; -// Longer than DEBOUNCE_INTERVAL so when we wait for this amount of time, a debounced event will be +const DEBOUNCE_INTERVAL = 10; // Longer than DEBOUNCE_INTERVAL so when we wait for this amount of time, a debounced event will be // emitted. -const SLEEP_INTERVAL = 15; -// Sleep interval for double the debounce interval. -const SLEEP_INTERVAL_2 = 25; -// eslint-disable-next-line jasmine/no-disabled-tests +const SLEEP_INTERVAL = 15; // Sleep interval for double the debounce interval. + +const SLEEP_INTERVAL_2 = 25; // eslint-disable-next-line jasmine/no-disabled-tests + xdescribe('editorScrollTopDebounced', () => { it('debounces scroll event', async () => { const LINES = 1000; - const mockText = Array(LINES) - .fill('MOCK LINE\n') - .reduce((a, b) => a.concat(b)); - + const mockText = Array(LINES).fill('MOCK LINE\n').reduce((a, b) => a.concat(b)); const editor = await atom.workspace.open(); editor.setText(mockText); - - const editorScroll = editorScrollTopDebounced(editor, DEBOUNCE_INTERVAL); - - const eventsPromise = editorScroll - .takeUntil(Observable.of(null).delay(500)) - .toArray() - .toPromise(); - - editor.scrollToBufferPosition(new Point(LINES / 2, 0)); - editor.scrollToBufferPosition(new Point(0, 0)); - editor.scrollToBufferPosition(new Point(LINES - 1, 0)); - editor.scrollToBufferPosition(new Point(LINES / 4, 0)); - + const editorScroll = (0, _debounced().editorScrollTopDebounced)(editor, DEBOUNCE_INTERVAL); + const eventsPromise = editorScroll.takeUntil(_RxMin.Observable.of(null).delay(500)).toArray().toPromise(); + editor.scrollToBufferPosition(new _atom.Point(LINES / 2, 0)); + editor.scrollToBufferPosition(new _atom.Point(0, 0)); + editor.scrollToBufferPosition(new _atom.Point(LINES - 1, 0)); + editor.scrollToBufferPosition(new _atom.Point(LINES / 4, 0)); const events = await eventsPromise; - expect(events.length).toBe(1); - editor.destroy(); }); -}); +}); // eslint-disable-next-line jasmine/no-disabled-tests -// eslint-disable-next-line jasmine/no-disabled-tests xdescribe('pane item change events', () => { - let pane: atom$Pane = (null: any); - let editor1: atom$TextEditor = (null: any); - let editor2: atom$TextEditor = (null: any); - let editor3: atom$TextEditor = (null: any); - let nonEditor: Object = (null: any); - let activePaneItems: Observable = (null: any); - + let pane = null; + let editor1 = null; + let editor2 = null; + let editor3 = null; + let nonEditor = null; + let activePaneItems = null; beforeEach(async () => { await (async () => { // Since RX manages to dodge the built-in clock mocking we'll use the real clock for these // tests :( jasmine.useRealClock(); - editor3 = await atom.workspace.open(); editor2 = await atom.workspace.open(); editor1 = await atom.workspace.open(); - pane = atom.workspace.getActivePane(); nonEditor = { // Ordinarily we would have to provide an element or register a view, but since we are just @@ -88,134 +93,101 @@ xdescribe('pane item change events', () => { // these tests start failing because Atom can't find a view, look here. getTitle() { return 'foo'; - }, + } + }; pane.addItem(nonEditor); pane.activateItem(editor1); })(); }); - describe('observeActivePaneItemDebounced', () => { beforeEach(() => { - activePaneItems = observeActivePaneItemDebounced(DEBOUNCE_INTERVAL); + activePaneItems = (0, _debounced().observeActivePaneItemDebounced)(DEBOUNCE_INTERVAL); }); - it('should issue an initial item', async () => { - expect( - await activePaneItems - .first() - // Split out an empty observable after waiting 20 ms. - .race(Observable.empty().delay(20)) - .toArray() - .toPromise(), - ).toEqual([editor1]); + expect((await activePaneItems.first() // Split out an empty observable after waiting 20 ms. + .race(_RxMin.Observable.empty().delay(20)).toArray().toPromise())).toEqual([editor1]); }); - it('should debounce', async () => { - const itemsPromise = activePaneItems - .take(2) - .toArray() - .toPromise(); - - await sleep(SLEEP_INTERVAL); - + const itemsPromise = activePaneItems.take(2).toArray().toPromise(); + await (0, _promise().sleep)(SLEEP_INTERVAL); pane.activateItem(editor2); pane.activateItem(editor3); - - expect(await itemsPromise).toEqual([editor1, editor3]); + expect((await itemsPromise)).toEqual([editor1, editor3]); }); }); - describe('observeActiveEditorsDebounced', () => { - let activeEditors: Observable = (null: any); + let activeEditors = null; beforeEach(() => { - activeEditors = observeActiveEditorsDebounced(DEBOUNCE_INTERVAL); + activeEditors = (0, _debounced().observeActiveEditorsDebounced)(DEBOUNCE_INTERVAL); }); - it('should return null if the item is not an editor', async () => { - const itemsPromise = activeEditors - .take(3) - .toArray() - .toPromise(); - - await sleep(SLEEP_INTERVAL); + const itemsPromise = activeEditors.take(3).toArray().toPromise(); + await (0, _promise().sleep)(SLEEP_INTERVAL); pane.activateItem(nonEditor); - - await sleep(SLEEP_INTERVAL); + await (0, _promise().sleep)(SLEEP_INTERVAL); pane.activateItem(editor2); - - expect(await itemsPromise).toEqual([editor1, null, editor2]); + expect((await itemsPromise)).toEqual([editor1, null, editor2]); }); }); - describe('observeTextEditorsPositions', () => { it('cursor moves and non-editors', async () => { - const itemsPromise = observeTextEditorsPositions( - DEBOUNCE_INTERVAL, - DEBOUNCE_INTERVAL, - ) - .take(5) - .toArray() - .toPromise(); - await sleep(SLEEP_INTERVAL_2); - goToLocationInEditor(editor1, {line: 3, column: 4}); - await sleep(SLEEP_INTERVAL_2); + const itemsPromise = (0, _debounced().observeTextEditorsPositions)(DEBOUNCE_INTERVAL, DEBOUNCE_INTERVAL).take(5).toArray().toPromise(); + await (0, _promise().sleep)(SLEEP_INTERVAL_2); + (0, _goToLocation().goToLocationInEditor)(editor1, { + line: 3, + column: 4 + }); + await (0, _promise().sleep)(SLEEP_INTERVAL_2); pane.activateItem(nonEditor); - await sleep(SLEEP_INTERVAL_2); - goToLocationInEditor(editor1, {line: 0, column: 0}); - await sleep(SLEEP_INTERVAL_2); + await (0, _promise().sleep)(SLEEP_INTERVAL_2); + (0, _goToLocation().goToLocationInEditor)(editor1, { + line: 0, + column: 0 + }); + await (0, _promise().sleep)(SLEEP_INTERVAL_2); pane.activateItem(editor2); - await sleep(SLEEP_INTERVAL_2); - goToLocationInEditor(editor1, {line: 3, column: 4}); - await sleep(SLEEP_INTERVAL_2); - goToLocationInEditor(editor2, {line: 1, column: 1}); - await sleep(SLEEP_INTERVAL_2); - - expect(await itemsPromise).toEqual([ - { - editor: editor1, - position: new Point(4, 0), - }, - { - editor: editor1, - position: new Point(3, 4), - }, - null, - { - editor: editor2, - position: new Point(3, 0), - }, - { - editor: editor2, - position: new Point(1, 1), - }, - ]); + await (0, _promise().sleep)(SLEEP_INTERVAL_2); + (0, _goToLocation().goToLocationInEditor)(editor1, { + line: 3, + column: 4 + }); + await (0, _promise().sleep)(SLEEP_INTERVAL_2); + (0, _goToLocation().goToLocationInEditor)(editor2, { + line: 1, + column: 1 + }); + await (0, _promise().sleep)(SLEEP_INTERVAL_2); + expect((await itemsPromise)).toEqual([{ + editor: editor1, + position: new _atom.Point(4, 0) + }, { + editor: editor1, + position: new _atom.Point(3, 4) + }, null, { + editor: editor2, + position: new _atom.Point(3, 0) + }, { + editor: editor2, + position: new _atom.Point(1, 1) + }]); }); }); -}); +}); // eslint-disable-next-line jasmine/no-disabled-tests -// eslint-disable-next-line jasmine/no-disabled-tests xdescribe('editorChangesDebounced', () => { - let editor: atom$TextEditor = (null: any); - let editorChanges: Observable = (null: any); - + let editor = null; + let editorChanges = null; beforeEach(async () => { jasmine.useRealClock(); editor = await atom.workspace.open(); - editorChanges = editorChangesDebounced(editor, DEBOUNCE_INTERVAL); + editorChanges = (0, _debounced().editorChangesDebounced)(editor, DEBOUNCE_INTERVAL); }); - it('debounces changes', async () => { - const eventsPromise = editorChanges - .takeUntil(Observable.of(null).delay(50)) - .toArray() - .toPromise(); - - await sleep(SLEEP_INTERVAL); - + const eventsPromise = editorChanges.takeUntil(_RxMin.Observable.of(null).delay(50)).toArray().toPromise(); + await (0, _promise().sleep)(SLEEP_INTERVAL); editor.insertNewline(); editor.insertNewline(); - expect((await eventsPromise).length).toBe(1); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/feature-config-test.js b/modules/nuclide-commons-atom/__atom_tests__/feature-config-test.js index c9bf7a16..9648b2c7 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/feature-config-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/feature-config-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _featureConfig() { + const data = _interopRequireDefault(require("../feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,56 +20,70 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import featureConfig from '../feature-config'; - describe('main', () => { it('returns numbers when numbers are set', () => { - featureConfig.set('foobar', 5); - expect(featureConfig.get('foobar')).toEqual(5); - }); + _featureConfig().default.set('foobar', 5); - it('returns booleans when numbers are set', () => { - featureConfig.set('cat', true); - expect(featureConfig.get('cat')).toEqual(true); + expect(_featureConfig().default.get('foobar')).toEqual(5); }); + it('returns booleans when numbers are set', () => { + _featureConfig().default.set('cat', true); + expect(_featureConfig().default.get('cat')).toEqual(true); + }); it('passes values to observers on change', () => { const spy = jest.fn(); - featureConfig.observe('animal', spy); - featureConfig.set('animal', 'yup'); + + _featureConfig().default.observe('animal', spy); + + _featureConfig().default.set('animal', 'yup'); + expect(spy).toHaveBeenCalled(); }); - it('can observeAsStream', () => { const spy = jest.fn(); - featureConfig.observeAsStream('animal').subscribe(spy); - featureConfig.set('animal', 'yup'); + + _featureConfig().default.observeAsStream('animal').subscribe(spy); + + _featureConfig().default.set('animal', 'yup'); + expect(spy).toHaveBeenCalled(); }); - it('passes options to observe', () => { const spy = jest.fn(); - featureConfig.observe('animal', {scope: []}, spy); - featureConfig.set('animal', 'yup'); + + _featureConfig().default.observe('animal', { + scope: [] + }, spy); + + _featureConfig().default.set('animal', 'yup'); + expect(spy).toHaveBeenCalled(); }); - it('calls callbacks passed to `onDidChange`', () => { const spy = jest.fn(); - featureConfig.onDidChange('mars.attacks', spy); - featureConfig.set('mars.attacks', 42); + + _featureConfig().default.onDidChange('mars.attacks', spy); + + _featureConfig().default.set('mars.attacks', 42); + expect(spy).toHaveBeenCalled(); }); - it('resets to defaults with "unset"', () => { - featureConfig.setSchema('purple.pants', {type: 'number', default: 15}); - featureConfig.set('purple.pants', 25); - expect(featureConfig.get('purple.pants')).toEqual(25); - featureConfig.unset('purple.pants'); - expect(featureConfig.get('purple.pants')).toEqual(15); + _featureConfig().default.setSchema('purple.pants', { + type: 'number', + default: 15 + }); + + _featureConfig().default.set('purple.pants', 25); + + expect(_featureConfig().default.get('purple.pants')).toEqual(25); + + _featureConfig().default.unset('purple.pants'); + + expect(_featureConfig().default.get('purple.pants')).toEqual(15); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/go-to-location-test.js b/modules/nuclide-commons-atom/__atom_tests__/go-to-location-test.js index 949147b0..010eccfb 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/go-to-location-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/go-to-location-test.js @@ -1,3 +1,39 @@ +"use strict"; + +var _atom = require("atom"); + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("../go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + +function _testHelpers() { + const data = require("../test-helpers"); + + _testHelpers = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,133 +42,132 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const FILE1_PATH = _nuclideUri().default.join(__dirname, '../__mocks__/fixtures/file1.txt'); -import {Point} from 'atom'; - -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {goToLocation, observeNavigatingEditors} from '../go-to-location'; -import {attachWorkspace} from '../test-helpers'; - -const FILE1_PATH = nuclideUri.join( - __dirname, - '../__mocks__/fixtures/file1.txt', -); -const FILE2_PATH = nuclideUri.join( - __dirname, - '../__mocks__/fixtures/file2.txt', -); +const FILE2_PATH = _nuclideUri().default.join(__dirname, '../__mocks__/fixtures/file2.txt'); describe('goToLocation', () => { beforeEach(() => { - attachWorkspace(); + (0, _testHelpers().attachWorkspace)(); atom.workspace.getTextEditors().forEach(editor => { editor.destroy(); }); }); - it('should work with nothing open', async () => { - const editor = await goToLocation(FILE1_PATH); + const editor = await (0, _goToLocation().goToLocation)(FILE1_PATH); expect(editor.getPath()).toBe(FILE1_PATH); expect(atom.workspace.getActiveTextEditor()).toBe(editor); }); - it('should open the current file successfully', async () => { - const editor1 = await goToLocation(FILE1_PATH); - const editor2 = await goToLocation(FILE1_PATH); + const editor1 = await (0, _goToLocation().goToLocation)(FILE1_PATH); + const editor2 = await (0, _goToLocation().goToLocation)(FILE1_PATH); expect(editor1).toBe(editor2); expect(editor1.getPath()).toBe(FILE1_PATH); expect(atom.workspace.getActiveTextEditor()).toBe(editor1); }); - it('should re-use the editor for an already-open file', async () => { - const editor1 = await goToLocation(FILE1_PATH); - const editor2 = await goToLocation(FILE2_PATH); + const editor1 = await (0, _goToLocation().goToLocation)(FILE1_PATH); + const editor2 = await (0, _goToLocation().goToLocation)(FILE2_PATH); expect(atom.workspace.getActiveTextEditor()).toBe(editor2); - - const editor3 = await goToLocation(FILE1_PATH); + const editor3 = await (0, _goToLocation().goToLocation)(FILE1_PATH); expect(editor1).toBe(editor3); expect(atom.workspace.getActiveTextEditor()).toBe(editor1); }); - it('should search other panes for an editor for this file', async () => { const editor1 = await atom.workspace.open(FILE1_PATH); - const editor2 = await atom.workspace.open(FILE2_PATH, {split: 'right'}); + const editor2 = await atom.workspace.open(FILE2_PATH, { + split: 'right' + }); expect(atom.workspace.getActiveTextEditor()).toBe(editor2); - expect(atom.workspace.paneForItem(editor1)).not.toBe( - atom.workspace.paneForItem(editor2), - ); - - const editor3 = await goToLocation(FILE1_PATH); + expect(atom.workspace.paneForItem(editor1)).not.toBe(atom.workspace.paneForItem(editor2)); + const editor3 = await (0, _goToLocation().goToLocation)(FILE1_PATH); expect(atom.workspace.getActiveTextEditor()).toBe(editor3); expect(editor3).toBe(editor1); }); - it('should correctly set the cursor position when opening a file', async () => { - const editor = await goToLocation(FILE1_PATH, {line: 1, column: 3}); - expect(editor.getCursorBufferPosition()).toEqual(new Point(1, 3)); + const editor = await (0, _goToLocation().goToLocation)(FILE1_PATH, { + line: 1, + column: 3 + }); + expect(editor.getCursorBufferPosition()).toEqual(new _atom.Point(1, 3)); }); - it('should correctly set the cursor position when opening an already-open file', async () => { const editor1 = await atom.workspace.open(FILE1_PATH); - expect(editor1.getCursorBufferPosition()).toEqual(new Point(0, 0)); - - const editor2 = await goToLocation(FILE1_PATH, {line: 1, column: 3}); + expect(editor1.getCursorBufferPosition()).toEqual(new _atom.Point(0, 0)); + const editor2 = await (0, _goToLocation().goToLocation)(FILE1_PATH, { + line: 1, + column: 3 + }); expect(editor2).toBe(editor1); - expect(editor1.getCursorBufferPosition()).toEqual(new Point(1, 3)); + expect(editor1.getCursorBufferPosition()).toEqual(new _atom.Point(1, 3)); }); - it('focuses the editor', async () => { const editor1 = await atom.workspace.open(FILE1_PATH); const dock = atom.workspace.getLeftDock(); dock.activate(); expect(dock.getElement().contains(document.activeElement)).toBe(true); - const editor2 = await goToLocation(FILE1_PATH, {line: 0, column: 0}); + const editor2 = await (0, _goToLocation().goToLocation)(FILE1_PATH, { + line: 0, + column: 0 + }); expect(editor2).toBe(editor1); expect(editor1.getElement().contains(document.activeElement)).toBe(true); }); - describe('its effect on observeNavigatingEditors', () => { - let navigatingEditorsArray: Array = (null: any); - let subscription: rxjs$ISubscription = (null: any); - + let navigatingEditorsArray = null; + let subscription = null; beforeEach(() => { navigatingEditorsArray = []; - subscription = observeNavigatingEditors().subscribe(editor => { + subscription = (0, _goToLocation().observeNavigatingEditors)().subscribe(editor => { navigatingEditorsArray.push(editor); }); }); afterEach(() => { subscription.unsubscribe(); }); - it('should not publish when opening a new file', async () => { - await goToLocation(FILE1_PATH, {line: 1, column: 2}); + await (0, _goToLocation().goToLocation)(FILE1_PATH, { + line: 1, + column: 2 + }); expect(navigatingEditorsArray).toEqual([]); }); - it('should not publish when opening the current editor with no position', async () => { - await goToLocation(FILE1_PATH); - await goToLocation(FILE1_PATH); + await (0, _goToLocation().goToLocation)(FILE1_PATH); + await (0, _goToLocation().goToLocation)(FILE1_PATH); expect(navigatingEditorsArray).toEqual([]); }); - it('should publish when opening the current file with a position', async () => { - const editor1 = await goToLocation(FILE1_PATH, {line: 1, column: 2}); + const editor1 = await (0, _goToLocation().goToLocation)(FILE1_PATH, { + line: 1, + column: 2 + }); expect(navigatingEditorsArray).toEqual([]); - const editor2 = await goToLocation(FILE1_PATH, {line: 1, column: 2}); + const editor2 = await (0, _goToLocation().goToLocation)(FILE1_PATH, { + line: 1, + column: 2 + }); expect(editor2).toBe(editor1); expect(navigatingEditorsArray).toEqual([editor1]); - expect(editor1.getCursorBufferPosition()).toEqual(new Point(1, 2)); + expect(editor1.getCursorBufferPosition()).toEqual(new _atom.Point(1, 2)); }); - it('should not publish when opening a file that is already open but not focused', async () => { - await goToLocation(FILE1_PATH, {line: 1, column: 2}); - await goToLocation(FILE2_PATH, {line: 1, column: 2}); - await goToLocation(FILE1_PATH, {line: 1, column: 2}); + await (0, _goToLocation().goToLocation)(FILE1_PATH, { + line: 1, + column: 2 + }); + await (0, _goToLocation().goToLocation)(FILE2_PATH, { + line: 1, + column: 2 + }); + await (0, _goToLocation().goToLocation)(FILE1_PATH, { + line: 1, + column: 2 + }); expect(navigatingEditorsArray).toEqual([]); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/observe-grammar-for-text-editors-test.js b/modules/nuclide-commons-atom/__atom_tests__/observe-grammar-for-text-editors-test.js index 6ab94f43..3bf29a5f 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/observe-grammar-for-text-editors-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/observe-grammar-for-text-editors-test.js @@ -1,3 +1,47 @@ +"use strict"; + +function _fsPromise() { + const data = _interopRequireDefault(require("../../nuclide-commons/fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _observeGrammarForTextEditors() { + const data = _interopRequireDefault(require("../observe-grammar-for-text-editors")); + + _observeGrammarForTextEditors = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,106 +50,78 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import fsPromise from 'nuclide-commons/fsPromise'; -import nullthrows from 'nullthrows'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import observeGrammarForTextEditors from '../observe-grammar-for-text-editors'; - describe('observeGrammarForTextEditors', () => { - let objcGrammar: atom$Grammar = (null: any); - let jsGrammar: atom$Grammar = (null: any); - let tempFilename: string = (null: any); - + let objcGrammar = null; + let jsGrammar = null; + let tempFilename = null; beforeEach(async () => { - observeGrammarForTextEditors.__reset__(); - // The grammar registry is cleared automatically after Atom 1.3.0+ + _observeGrammarForTextEditors().default.__reset__(); // The grammar registry is cleared automatically after Atom 1.3.0+ + + atom.grammars.clear(); - atom.grammars.loadGrammarSync( - nuclideUri.join(__dirname, '../__mocks__/grammars/objective-c.cson'), - ); - objcGrammar = nullthrows(atom.grammars.grammarForScopeName('source.objc')); - atom.grammars.loadGrammarSync( - nuclideUri.join(__dirname, '../__mocks__/grammars/javascript.cson'), - ); - jsGrammar = nullthrows(atom.grammars.grammarForScopeName('source.js')); - tempFilename = `${await fsPromise.tempfile()}.m`; + atom.grammars.loadGrammarSync(_nuclideUri().default.join(__dirname, '../__mocks__/grammars/objective-c.cson')); + objcGrammar = (0, _nullthrows().default)(atom.grammars.grammarForScopeName('source.objc')); + atom.grammars.loadGrammarSync(_nuclideUri().default.join(__dirname, '../__mocks__/grammars/javascript.cson')); + jsGrammar = (0, _nullthrows().default)(atom.grammars.grammarForScopeName('source.js')); + tempFilename = `${await _fsPromise().default.tempfile()}.m`; }); - it('calls for existing text editors', async () => { const textEditor = await atom.workspace.open(tempFilename); - - const fn: any = jest.fn(); - const subscription = observeGrammarForTextEditors(fn); + const fn = jest.fn(); + const subscription = (0, _observeGrammarForTextEditors().default)(fn); expect(fn).toHaveBeenCalledWith(textEditor, objcGrammar); expect(fn.mock.calls.length).toBe(1); - subscription.dispose(); textEditor.destroy(); }); - it('calls for new text editors', async () => { - const fn: any = jest.fn(); - const subscription = observeGrammarForTextEditors(fn); + const fn = jest.fn(); + const subscription = (0, _observeGrammarForTextEditors().default)(fn); const textEditor = await atom.workspace.open(tempFilename); - expect(fn).toHaveBeenCalledWith(textEditor, objcGrammar); expect(fn.mock.calls.length).toBe(1); - subscription.dispose(); textEditor.destroy(); }); - it('calls when a text editor changes grammars', async () => { - const fn: any = jest.fn(); - const subscription = observeGrammarForTextEditors(fn); + const fn = jest.fn(); + const subscription = (0, _observeGrammarForTextEditors().default)(fn); const textEditor = await atom.workspace.open(tempFilename); textEditor.setGrammar(jsGrammar); - expect(fn).toHaveBeenCalledWith(textEditor, objcGrammar); expect(fn).toHaveBeenCalledWith(textEditor, jsGrammar); expect(fn.mock.calls.length).toBe(2); - subscription.dispose(); textEditor.destroy(); }); - it('does not call after the return value is disposed', async () => { - const fn: any = jest.fn(); - const subscription = observeGrammarForTextEditors(fn); + const fn = jest.fn(); + const subscription = (0, _observeGrammarForTextEditors().default)(fn); const textEditor = await atom.workspace.open(tempFilename); - subscription.dispose(); textEditor.setGrammar(jsGrammar); - expect(fn).toHaveBeenCalledWith(textEditor, objcGrammar); expect(fn.mock.calls.length).toBe(1); - subscription.dispose(); textEditor.destroy(); }); - it('calls for other clients after another listener is disposed', async () => { - const fn: any = jest.fn(); - const subscription = observeGrammarForTextEditors(fn); - const fn2: any = jest.fn(); - const subscription2 = observeGrammarForTextEditors(fn2); + const fn = jest.fn(); + const subscription = (0, _observeGrammarForTextEditors().default)(fn); + const fn2 = jest.fn(); + const subscription2 = (0, _observeGrammarForTextEditors().default)(fn2); const textEditor = await atom.workspace.open(tempFilename); - subscription.dispose(); - textEditor.setGrammar(jsGrammar); - expect(fn).toHaveBeenCalledWith(textEditor, objcGrammar); expect(fn.mock.calls.length).toBe(1); expect(fn2).toHaveBeenCalledWith(textEditor, objcGrammar); expect(fn2).toHaveBeenCalledWith(textEditor, jsGrammar); expect(fn2.mock.calls.length).toBe(2); - subscription2.dispose(); textEditor.destroy(); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/projects-test.js b/modules/nuclide-commons-atom/__atom_tests__/projects-test.js index 405870ce..978936c9 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/projects-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/projects-test.js @@ -1,3 +1,37 @@ +"use strict"; + +function _temp() { + const data = _interopRequireDefault(require("temp")); + + _temp = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _projects() { + const data = require("../projects"); + + _projects = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,102 +40,95 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import temp from 'temp'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import { - getFileForPath, - observeProjectPaths, - onDidAddProjectPath, - onDidRemoveProjectPath, - observeProjectPathsAll, -} from '../projects'; - describe('projects', () => { - let firstProjectPath: string = (null: any); - let otherProjectPath: string = (null: any); - + let firstProjectPath = null; + let otherProjectPath = null; beforeEach(() => { - temp.track(); - // `atom.project.addPath` only works for paths that actually exist. - firstProjectPath = temp.mkdirSync('firstProjectPath'); - otherProjectPath = temp.mkdirSync('otherProjectPath'); - }); + _temp().default.track(); // `atom.project.addPath` only works for paths that actually exist. + + firstProjectPath = _temp().default.mkdirSync('firstProjectPath'); + otherProjectPath = _temp().default.mkdirSync('otherProjectPath'); + }); describe('getFileForPath', () => { it('works for paths both above and below the project path', () => { atom.project.setPaths([firstProjectPath]); - const path1 = nuclideUri.join(firstProjectPath, '../test'); - const file1 = getFileForPath(path1); - invariant(file1 != null); + + const path1 = _nuclideUri().default.join(firstProjectPath, '../test'); + + const file1 = (0, _projects().getFileForPath)(path1); + + if (!(file1 != null)) { + throw new Error("Invariant violation: \"file1 != null\""); + } + expect(file1.getPath()).toBe(path1); - const path2 = nuclideUri.join(firstProjectPath, 'child/path'); - const file2 = getFileForPath(path2); - invariant(file2 != null); + const path2 = _nuclideUri().default.join(firstProjectPath, 'child/path'); + + const file2 = (0, _projects().getFileForPath)(path2); + + if (!(file2 != null)) { + throw new Error("Invariant violation: \"file2 != null\""); + } + expect(file2.getPath()).toBe(path2); }); }); - describe('observeProjectPaths()', () => { it('observes existing projects and future added projects', () => { - const projectPaths: Array = []; - observeProjectPaths(projectPath => projectPaths.push(projectPath)); + const projectPaths = []; + (0, _projects().observeProjectPaths)(projectPath => projectPaths.push(projectPath)); atom.project.setPaths([firstProjectPath]); expect(projectPaths).toEqual([firstProjectPath]); atom.project.addPath(otherProjectPath); expect(projectPaths).toEqual([firstProjectPath, otherProjectPath]); }); }); - describe('observeProjectPathsAll()', () => { it('observes all existing projects and future added projects', () => { - let projectPaths: Array = []; - observeProjectPathsAll(newPaths => (projectPaths = newPaths)); + let projectPaths = []; + (0, _projects().observeProjectPathsAll)(newPaths => projectPaths = newPaths); atom.project.setPaths([firstProjectPath]); expect(projectPaths).toEqual([firstProjectPath]); atom.project.addPath(otherProjectPath); expect(projectPaths).toEqual([firstProjectPath, otherProjectPath]); }); }); - describe('onDidAddProjectPath()', () => { it('listens only to newly added project paths', () => { - const addedProjectPaths: Array = []; + const addedProjectPaths = []; atom.project.setPaths([firstProjectPath]); - onDidAddProjectPath(projectPath => { + (0, _projects().onDidAddProjectPath)(projectPath => { addedProjectPaths.push(projectPath); }); expect(addedProjectPaths.length).toBe(0); atom.project.addPath(otherProjectPath); expect(addedProjectPaths).toEqual([otherProjectPath]); }); - it('throws when doing updates within updates', () => { expect(() => { - onDidAddProjectPath(projectPath => { + (0, _projects().onDidAddProjectPath)(projectPath => { atom.project.addPath(otherProjectPath); }); atom.project.setPaths([firstProjectPath]); }).toThrow('Cannot update projects in the middle of an update'); expect(() => { - onDidAddProjectPath(projectPath => { + (0, _projects().onDidAddProjectPath)(projectPath => { atom.project.removePath(firstProjectPath); }); atom.project.setPaths([firstProjectPath]); }).toThrow('Cannot update projects in the middle of an update'); }); }); - describe('onDidRemoveProjectPath()', () => { it('listens to removed project paths', () => { - const removedProjectPaths: Array = []; + const removedProjectPaths = []; atom.project.setPaths([firstProjectPath]); - onDidRemoveProjectPath(projectPath => { + (0, _projects().onDidRemoveProjectPath)(projectPath => { removedProjectPaths.push(projectPath); }); expect(removedProjectPaths.length).toBe(0); @@ -109,4 +136,4 @@ describe('projects', () => { expect(removedProjectPaths).toEqual([firstProjectPath]); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/range-test.js b/modules/nuclide-commons-atom/__atom_tests__/range-test.js index d1c38f76..248a8e14 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/range-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/range-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _range() { + const data = require("../range"); + + _range = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,36 +18,28 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {getWordFromCursorOrSelection} from '../range'; - describe('getWordFromCursorOrSelection', () => { it('matches a word in an editor from a selection before cursor', async () => { const editor = await atom.workspace.open(); editor.setText('Darmok and Jalad\nat Tanagra.'); - editor.addCursorAtBufferPosition([1, 4]); editor.setSelectedBufferRange([[0, 7], [0, 10]]); - const word = getWordFromCursorOrSelection(editor); + const word = (0, _range().getWordFromCursorOrSelection)(editor); expect(word).toBe('and'); }); - it('matches a word in an editor from a cursor position', async () => { const editor = await atom.workspace.open(); editor.setText('Darmok and Jalad\nat Tanagra.'); - editor.addCursorAtBufferPosition([1, 4]); - const word = getWordFromCursorOrSelection(editor); + const word = (0, _range().getWordFromCursorOrSelection)(editor); expect(word).toBe('Tanagra'); }); - it('does not match a word without a cursor or selection', async () => { const editor = await atom.workspace.open(); - - const word = getWordFromCursorOrSelection(editor); + const word = (0, _range().getWordFromCursorOrSelection)(editor); expect(word).toBeNull(); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/text-edit-diff-test.js b/modules/nuclide-commons-atom/__atom_tests__/text-edit-diff-test.js index 414d882e..bb26c4bf 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/text-edit-diff-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/text-edit-diff-test.js @@ -1,3 +1,21 @@ +"use strict"; + +var _fs = _interopRequireDefault(require("fs")); + +function _textEditDiff() { + const data = require("../text-edit-diff"); + + _textEditDiff = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,52 +24,38 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import fs from 'fs'; -import {toUnifiedDiff} from '../text-edit-diff'; -import {Range, TextBuffer} from 'atom'; - -const fixturePath = require.resolve( - '../__mocks__/fixtures/text-edit-diff-file.txt', -); +const fixturePath = require.resolve("../__mocks__/fixtures/text-edit-diff-file.txt"); describe('toUnifiedDiff', () => { - let buffer: atom$TextBuffer = (null: any); - + let buffer = null; beforeEach(() => { - const text = fs.readFileSync(fixturePath, 'utf8'); - buffer = new TextBuffer(text); - }); + const text = _fs.default.readFileSync(fixturePath, 'utf8'); + buffer = new _atom.TextBuffer(text); + }); it('should handle empty text edits', () => { - const diff = toUnifiedDiff('foo', buffer, []); + const diff = (0, _textEditDiff().toUnifiedDiff)('foo', buffer, []); expect(diff).toEqual('--- foo\n+++ foo'); }); - it('should handle a single text edit with 0 context lines', () => { - const diff = toUnifiedDiff( - 'foo', - buffer, - [{oldRange: new Range([4, 29], [4, 32]), newText: '12345'}], - 0, - ); + const diff = (0, _textEditDiff().toUnifiedDiff)('foo', buffer, [{ + oldRange: new _atom.Range([4, 29], [4, 32]), + newText: '12345' + }], 0); expect(diff).toEqual(`--- foo +++ foo @@ -5,1 +5,1 @@ - return fibonacci(fibonacci(100)); + return fibonacci(fibonacci(12345));`); }); - it('should handle a single text edits with 1 context line', () => { - const diff = toUnifiedDiff( - 'foo', - buffer, - [{oldRange: new Range([4, 29], [4, 32]), newText: '12345'}], - 1, - ); + const diff = (0, _textEditDiff().toUnifiedDiff)('foo', buffer, [{ + oldRange: new _atom.Range([4, 29], [4, 32]), + newText: '12345' + }], 1); expect(diff).toEqual(`--- foo +++ foo @@ -4,3 +4,3 @@ @@ -60,14 +64,11 @@ describe('toUnifiedDiff', () => { + return fibonacci(fibonacci(12345)); }`); }); - it('should handle truncated context lines', () => { - const diff = toUnifiedDiff( - 'foo', - buffer, - [{oldRange: new Range([4, 29], [4, 32]), newText: '12345'}], - 10, - ); + const diff = (0, _textEditDiff().toUnifiedDiff)('foo', buffer, [{ + oldRange: new _atom.Range([4, 29], [4, 32]), + newText: '12345' + }], 10); expect(diff).toEqual(`--- foo +++ foo @@ -1,8 +1,8 @@ @@ -81,20 +82,23 @@ describe('toUnifiedDiff', () => { console.log(foo()); `); }); - it('should handle multiple text edits', () => { - const diff = toUnifiedDiff( - 'foo', - buffer, - [ - {oldRange: new Range([0, 9], [0, 18]), newText: 'fib'}, - {oldRange: new Range([1, 21], [1, 30]), newText: 'fib'}, - {oldRange: new Range([1, 40], [1, 49]), newText: 'fib'}, - {oldRange: new Range([4, 9], [4, 18]), newText: 'fib'}, - {oldRange: new Range([4, 19], [4, 28]), newText: 'fib'}, - ], - 0, - ); + const diff = (0, _textEditDiff().toUnifiedDiff)('foo', buffer, [{ + oldRange: new _atom.Range([0, 9], [0, 18]), + newText: 'fib' + }, { + oldRange: new _atom.Range([1, 21], [1, 30]), + newText: 'fib' + }, { + oldRange: new _atom.Range([1, 40], [1, 49]), + newText: 'fib' + }, { + oldRange: new _atom.Range([4, 9], [4, 18]), + newText: 'fib' + }, { + oldRange: new _atom.Range([4, 19], [4, 28]), + newText: 'fib' + }], 0); expect(diff).toEqual(`--- foo +++ foo @@ -1,1 +1,1 @@ @@ -107,20 +111,23 @@ describe('toUnifiedDiff', () => { - return fibonacci(fibonacci(100)); + return fib(fib(100));`); }); - it('should merge text edits based on context', () => { - const diff = toUnifiedDiff( - 'foo', - buffer, - [ - {oldRange: new Range([0, 9], [0, 18]), newText: 'fib'}, - {oldRange: new Range([1, 21], [1, 30]), newText: 'fib'}, - {oldRange: new Range([1, 40], [1, 49]), newText: 'fib'}, - {oldRange: new Range([4, 9], [4, 18]), newText: 'fib'}, - {oldRange: new Range([4, 19], [4, 28]), newText: 'fib'}, - ], - 1, - ); + const diff = (0, _textEditDiff().toUnifiedDiff)('foo', buffer, [{ + oldRange: new _atom.Range([0, 9], [0, 18]), + newText: 'fib' + }, { + oldRange: new _atom.Range([1, 21], [1, 30]), + newText: 'fib' + }, { + oldRange: new _atom.Range([1, 40], [1, 49]), + newText: 'fib' + }, { + oldRange: new _atom.Range([4, 9], [4, 18]), + newText: 'fib' + }, { + oldRange: new _atom.Range([4, 19], [4, 28]), + newText: 'fib' + }], 1); expect(diff).toEqual(`--- foo +++ foo @@ -1,3 +1,3 @@ @@ -135,23 +142,14 @@ describe('toUnifiedDiff', () => { + return fib(fib(100)); }`); }); - it('should handle multiline text edits', () => { - const diff = toUnifiedDiff( - 'foo', - buffer, - [ - { - oldRange: new Range([4, 19], [4, 35]), - newText: '\n fibonacci(100)\n );', - }, - { - oldRange: new Range([6, 12], [6, 19]), - newText: '\n foo()\n);', - }, - ], - 0, - ); + const diff = (0, _textEditDiff().toUnifiedDiff)('foo', buffer, [{ + oldRange: new _atom.Range([4, 19], [4, 35]), + newText: '\n fibonacci(100)\n );' + }, { + oldRange: new _atom.Range([6, 12], [6, 19]), + newText: '\n foo()\n);' + }], 0); expect(diff).toEqual(`--- foo +++ foo @@ -5,1 +5,3 @@ @@ -165,4 +163,4 @@ describe('toUnifiedDiff', () => { + foo() +);`); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/text-edit-test.js b/modules/nuclide-commons-atom/__atom_tests__/text-edit-test.js index 7704e5ab..da82765a 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/text-edit-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/text-edit-test.js @@ -1,3 +1,17 @@ +"use strict"; + +var _atom = require("atom"); + +function _textEdit() { + const data = require("../text-edit"); + + _textEdit = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,138 +20,106 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {Range} from 'atom'; - -import {applyTextEdits} from '../text-edit'; - const fakeFile = '/tmp/file.txt'; - describe('applyTextEdits', () => { - let editor: atom$TextEditor = (null: any); - + let editor = null; beforeEach(async () => { editor = await atom.workspace.open(fakeFile); editor.setText('foo\nbar\nbaz\n'); }); - it('should apply a patch', () => { const textedit = { - oldRange: new Range([1, 0], [1, 2]), - newText: 'BAR', + oldRange: new _atom.Range([1, 0], [1, 2]), + newText: 'BAR' }; - - expect(applyTextEdits(fakeFile, textedit)).toBeTruthy(); + expect((0, _textEdit().applyTextEdits)(fakeFile, textedit)).toBeTruthy(); expect(editor.getText()).toEqual('foo\nBARr\nbaz\n'); }); - it('should apply whole-file patches by diffing', () => { const textedit = { oldRange: editor.getBuffer().getRange(), - newText: 'BAR', + newText: 'BAR' }; - jest.spyOn(editor.getBuffer(), 'setTextViaDiff'); - - expect(applyTextEdits(fakeFile, textedit)).toBeTruthy(); + expect((0, _textEdit().applyTextEdits)(fakeFile, textedit)).toBeTruthy(); expect(editor.getText()).toEqual('BAR'); expect(editor.getBuffer().setTextViaDiff).toHaveBeenCalled(); }); - it('should accept a patch with the right old text', () => { const textedit = { - oldRange: new Range([1, 0], [1, 2]), + oldRange: new _atom.Range([1, 0], [1, 2]), newText: 'BAR', - oldText: 'ba', + oldText: 'ba' }; - - expect(applyTextEdits(fakeFile, textedit)).toBeTruthy(); + expect((0, _textEdit().applyTextEdits)(fakeFile, textedit)).toBeTruthy(); expect(editor.getText()).toEqual('foo\nBARr\nbaz\n'); }); - it('should reject a patch with the wrong old text', () => { const textedit = { - oldRange: new Range([1, 0], [1, 2]), + oldRange: new _atom.Range([1, 0], [1, 2]), newText: 'BAR', - oldText: 'b', + oldText: 'b' }; - - expect(applyTextEdits(fakeFile, textedit)).toBeFalsy(); + expect((0, _textEdit().applyTextEdits)(fakeFile, textedit)).toBeFalsy(); expect(editor.getText()).toEqual('foo\nbar\nbaz\n'); }); - it('should reject a patch with an invalid old range', () => { const textedit = { - oldRange: new Range([1, 4], [1, 4]), + oldRange: new _atom.Range([1, 4], [1, 4]), newText: 'foo', - oldText: '', + oldText: '' }; - - expect(applyTextEdits(fakeFile, textedit)).toBeFalsy(); + expect((0, _textEdit().applyTextEdits)(fakeFile, textedit)).toBeFalsy(); }); - it('should accept a patch that appends to a line', () => { const textedit = { - oldRange: new Range([1, 3], [1, 3]), + oldRange: new _atom.Range([1, 3], [1, 3]), newText: ';', - oldText: '', + oldText: '' }; - - expect(applyTextEdits(fakeFile, textedit)).toBeTruthy(); + expect((0, _textEdit().applyTextEdits)(fakeFile, textedit)).toBeTruthy(); expect(editor.getText()).toEqual('foo\nbar;\nbaz\n'); }); - it('should correctly apply edits on the same line', () => { - const edits = [ - { - oldRange: new Range([0, 0], [0, 1]), - oldText: 'f', - newText: 'FFF', - }, - { - oldRange: new Range([0, 2], [0, 3]), - oldText: 'o', - newText: 'OOO', - }, - ]; - expect(applyTextEdits(fakeFile, ...edits)).toBeTruthy(); + const edits = [{ + oldRange: new _atom.Range([0, 0], [0, 1]), + oldText: 'f', + newText: 'FFF' + }, { + oldRange: new _atom.Range([0, 2], [0, 3]), + oldText: 'o', + newText: 'OOO' + }]; + expect((0, _textEdit().applyTextEdits)(fakeFile, ...edits)).toBeTruthy(); expect(editor.getText()).toEqual('FFFoOOO\nbar\nbaz\n'); }); - it('should correctly apply an insert edit followed by a remove edit with the same start position', () => { - const edits = [ - { - oldRange: new Range([0, 0], [0, 0]), - oldText: '', - newText: 'Hello World', - }, - { - oldRange: new Range([0, 0], [3, 3]), - oldText: 'foo\nbar\nbaz\n', - newText: '', - }, - ]; - expect(applyTextEdits(fakeFile, ...edits)).toBeTruthy(); + const edits = [{ + oldRange: new _atom.Range([0, 0], [0, 0]), + oldText: '', + newText: 'Hello World' + }, { + oldRange: new _atom.Range([0, 0], [3, 3]), + oldText: 'foo\nbar\nbaz\n', + newText: '' + }]; + expect((0, _textEdit().applyTextEdits)(fakeFile, ...edits)).toBeTruthy(); expect(editor.getText()).toEqual('Hello World'); }); - it('should correctly apply a remove edit followed by an insert edit with the same start position', () => { - const edits = [ - { - oldRange: new Range([0, 0], [3, 3]), - oldText: 'foo\nbar\nbaz\n', - newText: '', - }, - { - oldRange: new Range([0, 0], [0, 0]), - oldText: '', - newText: 'Hello World', - }, - ]; - expect(applyTextEdits(fakeFile, ...edits)).toBeTruthy(); + const edits = [{ + oldRange: new _atom.Range([0, 0], [3, 3]), + oldText: 'foo\nbar\nbaz\n', + newText: '' + }, { + oldRange: new _atom.Range([0, 0], [0, 0]), + oldText: '', + newText: 'Hello World' + }]; + expect((0, _textEdit().applyTextEdits)(fakeFile, ...edits)).toBeTruthy(); expect(editor.getText()).toEqual('Hello World'); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/text-editor-test.js b/modules/nuclide-commons-atom/__atom_tests__/text-editor-test.js index f36fbf55..68ab9e46 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/text-editor-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/text-editor-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _textEditor() { + const data = require("../text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + +var _atom = require("atom"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,52 +20,39 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {enforceReadOnlyEditor, existingEditorForUri} from '../text-editor'; - -import {Point, Range} from 'atom'; - describe('existingEditorForUri', () => { const file1 = '/tmp/file1.txt'; const file2 = '/tmp/file2.txt'; const file3 = '/tmp/file3.txt'; - - let file1Editor: atom$TextEditor = (null: any); - let file2Editor: atom$TextEditor = (null: any); - let secondFile2Editor: atom$TextEditor = (null: any); - + let file1Editor = null; + let file2Editor = null; + let secondFile2Editor = null; beforeEach(async () => { file1Editor = await atom.workspace.open(file1); file2Editor = await atom.workspace.open(file2); secondFile2Editor = await atom.workspace.open(file2); }); - it('should find the one editor for a file', () => { - expect(existingEditorForUri(file1)).toBe(file1Editor); + expect((0, _textEditor().existingEditorForUri)(file1)).toBe(file1Editor); }); - it('should find one of the editors for a file', () => { - const editor = existingEditorForUri(file2); + const editor = (0, _textEditor().existingEditorForUri)(file2); expect(editor === file2Editor || editor === secondFile2Editor).toBeTruthy(); }); - it('should return null if no editor exists', () => { - expect(existingEditorForUri(file3)).toBeNull(); + expect((0, _textEditor().existingEditorForUri)(file3)).toBeNull(); }); }); -function ensureReadOnlyOperations( - buffer: atom$TextBuffer, - operations: Array<() => mixed>, - expectedReadOnly: boolean, -): void { +function ensureReadOnlyOperations(buffer, operations, expectedReadOnly) { const initialText = buffer.getText(); operations.forEach(operation => { operation(); expect(initialText === buffer.getText()).toBe(expectedReadOnly); + if (!expectedReadOnly) { buffer.setText(initialText); } @@ -60,79 +61,51 @@ function ensureReadOnlyOperations( describe('enforceReadOnlyEditor', () => { function getOperations(editor) { - return [ - () => editor.insertText('xyz'), - () => editor.backspace(), - () => editor.duplicateLines(), - () => editor.insertNewline(), - ]; + return [() => editor.insertText('xyz'), () => editor.backspace(), () => editor.duplicateLines(), () => editor.insertNewline()]; } it('should not be able to write to the text editor', async () => { const editor = await atom.workspace.open(''); editor.setText('ABC\nDEF'); const buffer = editor.getBuffer(); - const operations = getOperations(editor); - ensureReadOnlyOperations(buffer, operations, false); + (0, _textEditor().enforceReadOnlyEditor)(editor); + ensureReadOnlyOperations(buffer, operations, true); // Underlying buffer's `setText` and `append` are an exception by default. - enforceReadOnlyEditor(editor); - ensureReadOnlyOperations(buffer, operations, true); - // Underlying buffer's `setText` and `append` are an exception by default. - ensureReadOnlyOperations( - buffer, - [() => buffer.setText('lol'), () => buffer.append('lol')], - false, - ); + ensureReadOnlyOperations(buffer, [() => buffer.setText('lol'), () => buffer.append('lol')], false); }); - it('should be able to write to the text editor after cancelling', async () => { const editor = await atom.workspace.open(''); editor.setText('ABC\nDEF'); const buffer = editor.getBuffer(); - const operations = getOperations(editor); - - const enforceReadOnlyEditorDisposable = enforceReadOnlyEditor(editor, []); + const enforceReadOnlyEditorDisposable = (0, _textEditor().enforceReadOnlyEditor)(editor, []); enforceReadOnlyEditorDisposable.dispose(); ensureReadOnlyOperations(buffer, operations, false); }); }); - describe('enforceReadOnlyBuffer', () => { function getOperations(buffer) { - return [ - () => buffer.append('xyz'), - () => buffer.deleteRows(0, 1), - () => buffer.delete(new Range([0, 0], [1, 0])), - () => buffer.insert(new Point(0, 0), 'lol'), - () => buffer.undo(), - () => buffer.setText('lol'), - ]; + return [() => buffer.append('xyz'), () => buffer.deleteRows(0, 1), () => buffer.delete(new _atom.Range([0, 0], [1, 0])), () => buffer.insert(new _atom.Point(0, 0), 'lol'), () => buffer.undo(), () => buffer.setText('lol')]; } it('should not be able to write to the text buffer', async () => { const editor = await atom.workspace.open(''); const buffer = editor.getBuffer(); buffer.setText('ABC\nDEF'); - const operations = getOperations(buffer); - ensureReadOnlyOperations(buffer, operations, false); - enforceReadOnlyEditor(editor, []); + (0, _textEditor().enforceReadOnlyEditor)(editor, []); ensureReadOnlyOperations(buffer, operations, true); }); - it('should be able to write to the text buffer after cancelling', async () => { const editor = await atom.workspace.open(''); const buffer = editor.getBuffer(); buffer.setText('ABC\nDEF'); - const operations = getOperations(buffer); - - const enforceReadOnlyEditorDisposable = enforceReadOnlyEditor(editor, []); + const enforceReadOnlyEditorDisposable = (0, _textEditor().enforceReadOnlyEditor)(editor, []); enforceReadOnlyEditorDisposable.dispose(); ensureReadOnlyOperations(buffer, operations, false); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__atom_tests__/text-event-test.js b/modules/nuclide-commons-atom/__atom_tests__/text-event-test.js index 17d97c79..642be53f 100644 --- a/modules/nuclide-commons-atom/__atom_tests__/text-event-test.js +++ b/modules/nuclide-commons-atom/__atom_tests__/text-event-test.js @@ -1,3 +1,37 @@ +"use strict"; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("../../nuclide-commons/promise"); + + _promise = function () { + return data; + }; + + return data; +} + +function _textEvent() { + const data = require("../text-event"); + + _textEvent = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,28 +40,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {sleep} from 'nuclide-commons/promise'; - -import { - TextEventDispatcher, - observeTextEditorEvents, - __TEST__, -} from '../text-event'; - const grammar = 'testgrammar'; - describe('TextCallbackContainer', () => { - let textCallbackContainer: any; - let callback: any; - + let textCallbackContainer; + let callback; beforeEach(() => { jest.restoreAllMocks(); - textCallbackContainer = new __TEST__.TextCallbackContainer(); + textCallbackContainer = new (_textEvent().__TEST__.TextCallbackContainer)(); callback = jest.fn(); }); @@ -39,6 +61,7 @@ describe('TextCallbackContainer', () => { expect(callbackSet.size).not.toBe(0); }); }); + textCallbackContainer._allGrammarCallbacks.forEach(callbackSet => { expect(callbackSet.size).not.toBe(0); }); @@ -50,77 +73,78 @@ describe('TextCallbackContainer', () => { expect(callbacks).toEqual(new Set([callback])); checkInvariant(); }); - it('should always return callbacks for all', () => { textCallbackContainer.addCallback('all', ['did-save'], callback); const callbacks = textCallbackContainer.getCallbacks('asdf', 'did-save'); expect(callbacks).toEqual(new Set([callback])); checkInvariant(); }); - it('should properly remove a callback', () => { textCallbackContainer.addCallback([grammar], ['did-change'], callback); - expect(textCallbackContainer.getCallbacks(grammar, 'did-change')).toEqual( - new Set([callback]), - ); + expect(textCallbackContainer.getCallbacks(grammar, 'did-change')).toEqual(new Set([callback])); checkInvariant(); textCallbackContainer.removeCallback([grammar], ['did-change'], callback); - expect(textCallbackContainer.getCallbacks(grammar, 'did-change')).toEqual( - new Set(), - ); + expect(textCallbackContainer.getCallbacks(grammar, 'did-change')).toEqual(new Set()); }); }); - describe('TextEventDispatcher', () => { - let textEventDispatcher: any; - let fakeTextEditor: any; - let fakeTextEditor2: any; - let activeEditor: any; - // Stores callbacks that have subscribed to Atom text events. Can be called to simulate - let textEventCallbacks: any; - let paneSwitchCallbacks: any; + let textEventDispatcher; + let fakeTextEditor; + let fakeTextEditor2; + let activeEditor; // Stores callbacks that have subscribed to Atom text events. Can be called to simulate + + let textEventCallbacks; + let paneSwitchCallbacks; function fakeObserveEditors(callback) { callback(fakeTextEditor); callback(fakeTextEditor2); - return new UniversalDisposable(); + return new (_UniversalDisposable().default)(); } - function makeFakeEditor(path?: string = '') { + function makeFakeEditor(path = '') { // Register a callback for this fake editor. const registerCallback = callback => { let set = textEventCallbacks.get(editor); + if (!set) { set = new Set(); textEventCallbacks.set(editor, set); } + set.add(callback); - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { set.delete(callback); }); }; + const buffer = { onDidStopChanging: registerCallback, onDidSave: registerCallback, - onDidReload: registerCallback, + onDidReload: registerCallback }; const editor = { getBuffer() { return buffer; }, + getGrammar() { return { - scopeName: grammar, + scopeName: grammar }; }, + // getPath is nice for debugging tests getPath() { return path; }, + destroy() {}, + onDidDestroy(callback) { - return new UniversalDisposable(); - }, + return new (_UniversalDisposable().default)(); + } + }; return editor; } @@ -134,58 +158,43 @@ describe('TextEventDispatcher', () => { beforeEach(() => { textEventCallbacks = new Map(); paneSwitchCallbacks = new Set(); - fakeTextEditor = makeFakeEditor('foo'); fakeTextEditor2 = makeFakeEditor('bar'); activeEditor = fakeTextEditor; jest.spyOn(atom.workspace, 'isTextEditor').mockReturnValue(true); - jest - .spyOn(atom.workspace, 'observeTextEditors') - .mockImplementation(fakeObserveEditors); - jest - .spyOn(atom.workspace, 'getActiveTextEditor') - .mockImplementation(() => activeEditor); - jest - .spyOn(atom.workspace, 'getTextEditors') - .mockReturnValue([fakeTextEditor, fakeTextEditor2]); - jest - .spyOn(atom.workspace, 'onDidChangeActivePaneItem') - .mockImplementation(callback => { - paneSwitchCallbacks.add(callback); - return new UniversalDisposable(() => {}); - }); - textEventDispatcher = new TextEventDispatcher(); + jest.spyOn(atom.workspace, 'observeTextEditors').mockImplementation(fakeObserveEditors); + jest.spyOn(atom.workspace, 'getActiveTextEditor').mockImplementation(() => activeEditor); + jest.spyOn(atom.workspace, 'getTextEditors').mockReturnValue([fakeTextEditor, fakeTextEditor2]); + jest.spyOn(atom.workspace, 'onDidChangeActivePaneItem').mockImplementation(callback => { + paneSwitchCallbacks.add(callback); + return new (_UniversalDisposable().default)(() => {}); + }); + textEventDispatcher = new (_textEvent().TextEventDispatcher)(); }); - it('should fire events', () => { const callback = jest.fn(); textEventDispatcher.onFileChange([grammar], callback); triggerAtomEvent(fakeTextEditor); expect(callback).toHaveBeenCalled(); }); - it('should work with observeTextEditorEvents', () => { const spy = jest.fn(); - observeTextEditorEvents([grammar], 'changes').subscribe(editor => - spy(editor), - ); + (0, _textEvent().observeTextEditorEvents)([grammar], 'changes').subscribe(editor => spy(editor)); triggerAtomEvent(fakeTextEditor); expect(spy).toHaveBeenCalledWith(fakeTextEditor); }); - it('should debounce events', () => { const callback = jest.fn(); - textEventDispatcher.onFileChange([grammar], callback); - // This test hinges on these two calls happening within 50 ms of each other. + textEventDispatcher.onFileChange([grammar], callback); // This test hinges on these two calls happening within 50 ms of each other. // An initial attempt to mock the clock was unsuccessful, probably because // of problems clearing the require cache thoroughly enough that the // debounce function picks up the mocked clock. If this causes problems, // figure out how to mock the clock properly. + triggerAtomEvent(fakeTextEditor); triggerAtomEvent(fakeTextEditor); expect(callback.mock.calls.length).toBe(1); }); - it('should dispatch pending events on a tab switch', () => { const callback = jest.fn(); textEventDispatcher.onFileChange([grammar], callback); @@ -195,36 +204,28 @@ describe('TextEventDispatcher', () => { paneSwitchCallbacks.forEach(f => f()); expect(callback).toHaveBeenCalledWith(fakeTextEditor2); }); - it('should register simultaneous open events as pending', async () => { - const callback = jest.fn(); + const callback = jest.fn(); // Initially, both fakeTextEditor/fakeTextEditor2 are opened. - // Initially, both fakeTextEditor/fakeTextEditor2 are opened. - textEventDispatcher.onFileChange([grammar], callback); + textEventDispatcher.onFileChange([grammar], callback); // Open events need a tick to process. - // Open events need a tick to process. - await sleep(0); + await (0, _promise().sleep)(0); // Only fakeTextEditor should have opened; the other one should be pending. - // Only fakeTextEditor should have opened; the other one should be pending. expect(callback).toHaveBeenCalledWith(fakeTextEditor); - expect(callback).not.toHaveBeenCalledWith(fakeTextEditor2); + expect(callback).not.toHaveBeenCalledWith(fakeTextEditor2); // Prevent the next open event from being debounced. - // Prevent the next open event from being debounced. - await sleep(100); + await (0, _promise().sleep)(100); // Switching to fakeTextEditor2 should now trigger its pending open event. - // Switching to fakeTextEditor2 should now trigger its pending open event. activeEditor = fakeTextEditor2; paneSwitchCallbacks.forEach(f => f()); expect(callback).toHaveBeenCalledWith(fakeTextEditor2); }); - it('should always dispatch to clients that request all changes', () => { const callback = jest.fn(); textEventDispatcher.onAnyFileChange(callback); triggerAtomEvent(fakeTextEditor); expect(callback).toHaveBeenCalled(); }); - it('should deregister from text editor events when it has no subscribers', () => { expect(textEventCallbacks.get(fakeTextEditor)).toBe(undefined); const callback = jest.fn(); @@ -233,4 +234,4 @@ describe('TextEventDispatcher', () => { disposable.dispose(); expect(textEventCallbacks.get(fakeTextEditor).size).toBe(0); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__tests__/ProjectManager-test.js b/modules/nuclide-commons-atom/__tests__/ProjectManager-test.js index a7a955d6..2aeb91bd 100644 --- a/modules/nuclide-commons-atom/__tests__/ProjectManager-test.js +++ b/modules/nuclide-commons-atom/__tests__/ProjectManager-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _ProjectManager() { + const data = require("../ProjectManager"); + + _ProjectManager = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,50 +18,38 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {_validateProjectSpec} from '../ProjectManager'; - describe('_validateProjectSpec', () => { test('local', () => { - expect( - _validateProjectSpec({ - originPath: '/a/b/c/my.project.toml', - paths: ['d/e/f', '/x/y/z'], - }), - ).toEqual({ + expect((0, _ProjectManager()._validateProjectSpec)({ + originPath: '/a/b/c/my.project.toml', + paths: ['d/e/f', '/x/y/z'] + })).toEqual({ originPath: '/a/b/c/my.project.toml', - paths: ['/a/b/c/d/e/f', '/x/y/z'], + paths: ['/a/b/c/d/e/f', '/x/y/z'] }); - expect( - _validateProjectSpec({ - originPath: '/a/b/c/my.project.toml', - }), - ).toEqual({ + expect((0, _ProjectManager()._validateProjectSpec)({ + originPath: '/a/b/c/my.project.toml' + })).toEqual({ originPath: '/a/b/c/my.project.toml', - paths: ['/a/b/c'], + paths: ['/a/b/c'] }); }); - test('remote', () => { - expect( - _validateProjectSpec({ - originPath: 'nuclide://a.com/b/c/my.project.toml', - paths: ['d/e/f', '/x/y/z'], - }), - ).toEqual({ + expect((0, _ProjectManager()._validateProjectSpec)({ + originPath: 'nuclide://a.com/b/c/my.project.toml', + paths: ['d/e/f', '/x/y/z'] + })).toEqual({ originPath: 'nuclide://a.com/b/c/my.project.toml', - paths: ['/b/c/d/e/f', '/x/y/z'], + paths: ['/b/c/d/e/f', '/x/y/z'] }); - expect( - _validateProjectSpec({ - originPath: 'nuclide://a.com/b/c/my.project.toml', - }), - ).toEqual({ + expect((0, _ProjectManager()._validateProjectSpec)({ + originPath: 'nuclide://a.com/b/c/my.project.toml' + })).toEqual({ originPath: 'nuclide://a.com/b/c/my.project.toml', - paths: ['/b/c'], + paths: ['/b/c'] }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__tests__/ProjectUtils-test.js b/modules/nuclide-commons-atom/__tests__/ProjectUtils-test.js index e4ac3c67..5c8787fa 100644 --- a/modules/nuclide-commons-atom/__tests__/ProjectUtils-test.js +++ b/modules/nuclide-commons-atom/__tests__/ProjectUtils-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function ProjectUtils() { + const data = _interopRequireWildcard(require("../ProjectUtils")); + + ProjectUtils = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,22 +20,14 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import * as ProjectUtils from '../ProjectUtils'; - describe('getLabelFromPath', () => { it('extracts a pretty label', () => { - const pathsToExpectedLabels = new Map([ - ['nuclide://x.com/abc/def/my_project.project.toml', 'My Project'], - ['nuclide://x.com/abc/def/my_project', 'My Project'], - ['nuclide://x.com/abc/def/My_iOS_Project.project.toml', 'My iOS Project'], - ]); - + const pathsToExpectedLabels = new Map([['nuclide://x.com/abc/def/my_project.project.toml', 'My Project'], ['nuclide://x.com/abc/def/my_project', 'My Project'], ['nuclide://x.com/abc/def/My_iOS_Project.project.toml', 'My iOS Project']]); pathsToExpectedLabels.forEach((label, path) => { - expect(ProjectUtils.getLabelFromPath(path)).toBe(label); + expect(ProjectUtils().getLabelFromPath(path)).toBe(label); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__tests__/ProviderRegistry-test.js b/modules/nuclide-commons-atom/__tests__/ProviderRegistry-test.js index e3e4c6bb..5e6391e4 100644 --- a/modules/nuclide-commons-atom/__tests__/ProviderRegistry-test.js +++ b/modules/nuclide-commons-atom/__tests__/ProviderRegistry-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _ProviderRegistry() { + const data = _interopRequireDefault(require("../ProviderRegistry")); + + _ProviderRegistry = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,78 +20,66 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {Provider} from '../ProviderRegistry'; - -import ProviderRegistry from '../ProviderRegistry'; - describe('ProviderRegistry', () => { - let providerRegistry: ProviderRegistry = (null: any); - let provider1: Provider; - let provider2: Provider; - + let providerRegistry = null; + let provider1; + let provider2; beforeEach(() => { - providerRegistry = new ProviderRegistry(); + providerRegistry = new (_ProviderRegistry().default)(); provider1 = { priority: 10, - grammarScopes: ['foo', 'bar'], + grammarScopes: ['foo', 'bar'] }; provider2 = { priority: 9, - grammarScopes: ['bar', 'baz'], + grammarScopes: ['bar', 'baz'] }; providerRegistry.addProvider(provider1); providerRegistry.addProvider(provider2); }); - it('should return the highest-priority provider', () => { expect(providerRegistry.findProvider('foo')).toBe(provider1); expect(providerRegistry.findProvider('bar')).toBe(provider1); expect(providerRegistry.findProvider('baz')).toBe(provider2); }); - it('should return the provider for an editor', () => { - const editor: any = { + const editor = { getGrammar() { return { - scopeName: 'foo', + scopeName: 'foo' }; - }, + } + }; expect(providerRegistry.getProviderForEditor(editor)).toBe(provider1); }); - it('should treat null grammarScopes as all-inclusive', () => { const provider3 = { - priority: 0, + priority: 0 }; providerRegistry.addProvider(provider3); expect(providerRegistry.findProvider('asdf')).toBe(provider3); }); - it('can return all providers for an editor', () => { - const editor: any = { + const editor = { getGrammar() { return { - scopeName: 'bar', + scopeName: 'bar' }; - }, + } + }; - expect( - Array.from(providerRegistry.getAllProvidersForEditor(editor)), - ).toEqual([provider1, provider2]); + expect(Array.from(providerRegistry.getAllProvidersForEditor(editor))).toEqual([provider1, provider2]); }); - it('should return null if there is no provider', () => { expect(providerRegistry.findProvider('42')).toBeNull(); }); - it('should correctly remove a provider', () => { providerRegistry.removeProvider(provider1); expect(providerRegistry.findProvider('foo')).toBe(null); expect(providerRegistry.findProvider('bar')).toBe(provider2); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__tests__/createPackage-test.js b/modules/nuclide-commons-atom/__tests__/createPackage-test.js index bac69c9b..6fa85397 100644 --- a/modules/nuclide-commons-atom/__tests__/createPackage-test.js +++ b/modules/nuclide-commons-atom/__tests__/createPackage-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _createPackage() { + const data = _interopRequireDefault(require("../createPackage")); + + _createPackage = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,84 +20,87 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import createPackage from '../createPackage'; - describe('createPackage', () => { it('throws when the activation class contains an `initialize()`', () => { class Activation { initialize() {} + } - expect(() => createPackage({}, Activation)).toThrow( - 'Your activation class contains an "initialize" method, but that work should be done in the' + - ' constructor.', - ); - }); + expect(() => (0, _createPackage().default)({}, Activation)).toThrow('Your activation class contains an "initialize" method, but that work should be done in the' + ' constructor.'); + }); it('throws when the activation class contains a `deactivate()`', () => { class Activation { deactivate() {} + } - expect(() => createPackage({}, Activation)).toThrow( - 'Your activation class contains an "deactivate" method. Please use "dispose" instead.', - ); - }); + expect(() => (0, _createPackage().default)({}, Activation)).toThrow('Your activation class contains an "deactivate" method. Please use "dispose" instead.'); + }); it("calls the activation's `dispose()` when deactivated", () => { let called = false; + class Activation { dispose() { called = true; } + } + const pkg = {}; - createPackage(pkg, Activation); + (0, _createPackage().default)(pkg, Activation); pkg.initialize(); expect(called).toBe(false); pkg.deactivate(); expect(called).toBe(true); }); - it('proxies methods to the activation instance', () => { let called = false; + class Activation { doSomething() { called = true; } + } + const pkg = {}; - createPackage(pkg, Activation); + (0, _createPackage().default)(pkg, Activation); pkg.initialize(); pkg.doSomething(); expect(called).toBe(true); }); - it('proxies activate()', () => { let state; + class Activation { activate(serializedState) { state = serializedState; } + } + const pkg = {}; - createPackage(pkg, Activation); + (0, _createPackage().default)(pkg, Activation); pkg.initialize(); pkg.activate(1); expect(state).toBe(1); }); - it("throws if methods are called when the package isn't initialized", () => { let called = false; + class Activation { doSomething() { called = true; } + } + const pkg = {}; - createPackage(pkg, Activation); + (0, _createPackage().default)(pkg, Activation); pkg.initialize(); pkg.deactivate(); expect(() => { @@ -91,14 +108,16 @@ describe('createPackage', () => { }).toThrow('Package not initialized'); expect(called).toBe(false); }); - it('contains methods inherited by the activation class', () => { class A { inheritedMethod() {} + } + class B extends A {} + const pkg = {}; - createPackage(pkg, B); + (0, _createPackage().default)(pkg, B); expect('inheritedMethod' in pkg).toBe(true); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/__tests__/humanizePath-test.js b/modules/nuclide-commons-atom/__tests__/humanizePath-test.js index de42db74..9a6c8a9e 100644 --- a/modules/nuclide-commons-atom/__tests__/humanizePath-test.js +++ b/modules/nuclide-commons-atom/__tests__/humanizePath-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _humanizePath() { + const data = _interopRequireDefault(require("../humanizePath")); + + _humanizePath = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,46 +20,53 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import humanizePath from '../humanizePath'; - describe('humanizePath', () => { it("includes the root name if there's more than one", () => { - expect(humanizePath('/a/b/c', {rootPaths: ['/a', '/d']})).toBe('a/b/c'); + expect((0, _humanizePath().default)('/a/b/c', { + rootPaths: ['/a', '/d'] + })).toBe('a/b/c'); }); - it("doesn't care if the rootPaths have trailing slashes", () => { // Atom hasn't always been consistent about this. - expect(humanizePath('/a/b/c', {rootPaths: ['/a/', '/d/']})).toBe('a/b/c'); + expect((0, _humanizePath().default)('/a/b/c', { + rootPaths: ['/a/', '/d/'] + })).toBe('a/b/c'); }); - it("doesn't include the root name if there's only one", () => { - expect(humanizePath('/a/b/c', {rootPaths: ['/a']})).toBe('b/c'); + expect((0, _humanizePath().default)('/a/b/c', { + rootPaths: ['/a'] + })).toBe('b/c'); }); - it("returns the root name if it's a root path", () => { - expect(humanizePath('/a/', {rootPaths: ['/a', '/d']})).toBe('a/'); + expect((0, _humanizePath().default)('/a/', { + rootPaths: ['/a', '/d'] + })).toBe('a/'); }); - it("returns the absolute path if the file isn't in an open project root", () => { - expect(humanizePath('/a/b/c', {rootPaths: []})).toBe('/a/b/c'); - expect(humanizePath('/a/b/c', {rootPaths: ['/d']})).toBe('/a/b/c'); + expect((0, _humanizePath().default)('/a/b/c', { + rootPaths: [] + })).toBe('/a/b/c'); + expect((0, _humanizePath().default)('/a/b/c', { + rootPaths: ['/d'] + })).toBe('/a/b/c'); }); - it('doesn\'t use ".." if the file is above the root', () => { - expect(humanizePath('/a/b/c', {rootPaths: ['/a/b/c/d']})).toBe('/a/b/c'); + expect((0, _humanizePath().default)('/a/b/c', { + rootPaths: ['/a/b/c/d'] + })).toBe('/a/b/c'); }); - it("includes a trailing slash if you say it's a directory", () => { - expect(humanizePath('/a/b/c', {isDirectory: true, rootPaths: []})).toBe( - '/a/b/c/', - ); + expect((0, _humanizePath().default)('/a/b/c', { + isDirectory: true, + rootPaths: [] + })).toBe('/a/b/c/'); }); - it('normalizes', () => { - expect(humanizePath('/a/b//c//', {rootPaths: []})).toBe('/a/b/c/'); + expect((0, _humanizePath().default)('/a/b//c//', { + rootPaths: [] + })).toBe('/a/b/c/'); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/consumeFirstProvider.js b/modules/nuclide-commons-atom/consumeFirstProvider.js index 52020e5f..e1adf69f 100644 --- a/modules/nuclide-commons-atom/consumeFirstProvider.js +++ b/modules/nuclide-commons-atom/consumeFirstProvider.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = consumeFirstProvider; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +13,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -14,21 +21,13 @@ * The module formerly known as "service-hub-plus". Provides a workaround for * https://github.com/atom/service-hub/issues/6 */ - -export default function consumeFirstProvider( - keyPath: string, - version: string = '0.0.0', -): Promise { +function consumeFirstProvider(keyPath, version = '0.0.0') { return new Promise((resolve, reject) => { - const subscription = atom.packages.serviceHub.consume( - keyPath, - version, - provider => { - process.nextTick(() => { - resolve(provider); - subscription.dispose(); - }); - }, - ); + const subscription = atom.packages.serviceHub.consume(keyPath, version, provider => { + process.nextTick(() => { + resolve(provider); + subscription.dispose(); + }); + }); }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/create-pane-container.js b/modules/nuclide-commons-atom/create-pane-container.js index 82552ca5..21d7d9ab 100644 --- a/modules/nuclide-commons-atom/create-pane-container.js +++ b/modules/nuclide-commons-atom/create-pane-container.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createPaneContainer; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,21 +13,17 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -export default function createPaneContainer(): Object { - const instance = - typeof atom.workspace.getCenter === 'function' - ? atom.workspace.getCenter().paneContainer - : (atom.workspace: any).paneContainer; +function createPaneContainer() { + const instance = typeof atom.workspace.getCenter === 'function' ? atom.workspace.getCenter().paneContainer : atom.workspace.paneContainer; const PaneContainer = instance.constructor; return new PaneContainer({ viewRegistry: atom.views, config: atom.config, applicationDelegate: atom.applicationDelegate, notificationManager: atom.notifications, - deserializerManager: atom.deserializers, + deserializerManager: atom.deserializers }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/createPackage.js b/modules/nuclide-commons-atom/createPackage.js index c985c745..70c4c557 100644 --- a/modules/nuclide-commons-atom/createPackage.js +++ b/modules/nuclide-commons-atom/createPackage.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createPackage; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,12 +13,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import invariant from 'assert'; - /** * Create an Atom package from an Activation constructor. * @@ -31,81 +36,93 @@ import invariant from 'assert'; * `module.exports = createPackage(Activation)`, to avoid code style misunderstandings wrt * CommonJS vs ES Modules. */ -export default function createPackage( - moduleExports: Object, - Activation: Class, -): void { - let activation = null; +function createPackage(moduleExports, Activation) { + let activation = null; // Proxy method calls on the package to the activation object. - // Proxy method calls on the package to the activation object. for (const property of getPropertyList(Activation.prototype)) { if (typeof Activation.prototype[property] !== 'function') { continue; } + if (property === 'constructor') { continue; } + if (property === 'initialize') { - throw new Error( - 'Your activation class contains an "initialize" method, but that work should be done in the' + - ' constructor.', - ); + throw new Error('Your activation class contains an "initialize" method, but that work should be done in the' + ' constructor.'); } + if (property === 'deactivate') { - throw new Error( - 'Your activation class contains an "deactivate" method. Please use "dispose" instead.', - ); + throw new Error('Your activation class contains an "deactivate" method. Please use "dispose" instead.'); } - moduleExports[property] = function(...args) { - invariant(activation != null, 'Package not initialized'); + moduleExports[property] = function (...args) { + if (!(activation != null)) { + throw new Error('Package not initialized'); + } + return activation[property](...args); }; } - /** * Calling `initialize()` creates a new instance. */ - moduleExports.initialize = (initialState: ?Object): void => { - invariant(activation == null, 'Package already initialized'); + + + moduleExports.initialize = initialState => { + if (!(activation == null)) { + throw new Error('Package already initialized'); + } + activation = new Activation(initialState); }; - /** * The `deactivate()` method is special-cased to null our activation instance reference. */ - moduleExports.deactivate = (): void => { - invariant(activation != null, 'Package not initialized'); + + + moduleExports.deactivate = () => { + if (!(activation != null)) { + throw new Error('Package not initialized'); + } + if (typeof activation.dispose === 'function') { activation.dispose(); } + activation = null; }; } -function getPrototypeChain(prototype: Class): Array> { +function getPrototypeChain(prototype) { let currentPrototype = prototype; const prototypes = []; + while (currentPrototype != null) { prototypes.push(currentPrototype); currentPrototype = Object.getPrototypeOf(currentPrototype); } + return prototypes; } - /** * List the properties (including inherited ones) of the provided prototype, excluding the ones * inherited from `Object`. */ -function getPropertyList(prototype: Class): Array { + + +function getPropertyList(prototype) { const properties = []; + for (const proto of getPrototypeChain(prototype)) { - if (proto === (Object: any).prototype) { + if (proto === Object.prototype) { break; } + for (const property of Object.getOwnPropertyNames(proto)) { properties.push(property); } } + return properties; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/debounced.js b/modules/nuclide-commons-atom/debounced.js index f8187011..d0091a82 100644 --- a/modules/nuclide-commons-atom/debounced.js +++ b/modules/nuclide-commons-atom/debounced.js @@ -1,3 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.observeActivePaneItemDebounced = observeActivePaneItemDebounced; +exports.observeActiveEditorsDebounced = observeActiveEditorsDebounced; +exports.editorChangesDebounced = editorChangesDebounced; +exports.editorScrollTopDebounced = editorScrollTopDebounced; +exports.observeTextEditorsPositions = observeTextEditorsPositions; + +function _observable() { + const data = require("../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _textEditor() { + const data = require("./text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +49,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -18,81 +61,49 @@ * stopped changing. * This file provides methods to do this. */ - -import {fastDebounce} from 'nuclide-commons/observable'; -import {Observable} from 'rxjs'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {getCursorPositions, isValidTextEditor} from './text-editor'; -import invariant from 'assert'; - const DEFAULT_PANE_DEBOUNCE_INTERVAL_MS = 100; const DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS = 300; const DEFAULT_POSITION_DEBOUNCE_INTERVAL_MS = 300; -export function observeActivePaneItemDebounced( - debounceInterval: number = DEFAULT_PANE_DEBOUNCE_INTERVAL_MS, -): Observable { - return observableFromSubscribeFunction(callback => { +function observeActivePaneItemDebounced(debounceInterval = DEFAULT_PANE_DEBOUNCE_INTERVAL_MS) { + return (0, _event().observableFromSubscribeFunction)(callback => { if (atom.workspace.getCenter != null) { return atom.workspace.getCenter().observeActivePaneItem(callback); } + return atom.workspace.observeActivePaneItem(callback); - }).let(fastDebounce(debounceInterval)); + }).let((0, _observable().fastDebounce)(debounceInterval)); } -export function observeActiveEditorsDebounced( - debounceInterval: number = DEFAULT_PANE_DEBOUNCE_INTERVAL_MS, -): Observable { +function observeActiveEditorsDebounced(debounceInterval = DEFAULT_PANE_DEBOUNCE_INTERVAL_MS) { return observeActivePaneItemDebounced(debounceInterval).map(paneItem => { - return isValidTextEditor(paneItem) ? paneItem : null; + return (0, _textEditor().isValidTextEditor)(paneItem) ? paneItem : null; }); } -export function editorChangesDebounced( - editor: atom$TextEditor, - debounceInterval: number = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS, -): Observable { - return ( - observableFromSubscribeFunction(callback => - editor.getBuffer().onDidChangeText(() => callback()), - ) - // Debounce manually rather than using editor.onDidStopChanging so that the debounce time is - // configurable. - .let(fastDebounce(debounceInterval)) - ); +function editorChangesDebounced(editor, debounceInterval = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS) { + return (0, _event().observableFromSubscribeFunction)(callback => editor.getBuffer().onDidChangeText(() => callback())) // Debounce manually rather than using editor.onDidStopChanging so that the debounce time is + // configurable. + .let((0, _observable().fastDebounce)(debounceInterval)); } -export function editorScrollTopDebounced( - editor: atom$TextEditor, - debounceInterval: number = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS, -): Observable { - return observableFromSubscribeFunction(callback => - atom.views.getView(editor).onDidChangeScrollTop(callback), - ).let(fastDebounce(debounceInterval)); +function editorScrollTopDebounced(editor, debounceInterval = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS) { + return (0, _event().observableFromSubscribeFunction)(callback => atom.views.getView(editor).onDidChangeScrollTop(callback)).let((0, _observable().fastDebounce)(debounceInterval)); } -export type EditorPosition = { - editor: atom$TextEditor, - position: atom$Point, -}; - // Yields null when the current pane is not an editor, // otherwise yields events on each move of the primary cursor within any Editor. -export function observeTextEditorsPositions( - editorDebounceInterval: number = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS, - positionDebounceInterval: number = DEFAULT_POSITION_DEBOUNCE_INTERVAL_MS, -): Observable { - return observeActiveEditorsDebounced(editorDebounceInterval).switchMap( - editor => { - return editor == null - ? Observable.of(null) - : getCursorPositions(editor) - .let(fastDebounce(positionDebounceInterval)) - .map(position => { - invariant(editor != null); - return {editor, position}; - }); - }, - ); -} +function observeTextEditorsPositions(editorDebounceInterval = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS, positionDebounceInterval = DEFAULT_POSITION_DEBOUNCE_INTERVAL_MS) { + return observeActiveEditorsDebounced(editorDebounceInterval).switchMap(editor => { + return editor == null ? _RxMin.Observable.of(null) : (0, _textEditor().getCursorPositions)(editor).let((0, _observable().fastDebounce)(positionDebounceInterval)).map(position => { + if (!(editor != null)) { + throw new Error("Invariant violation: \"editor != null\""); + } + + return { + editor, + position + }; + }); + }); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/debugger.js b/modules/nuclide-commons-atom/debugger.js index ff163f9d..a9715db6 100644 --- a/modules/nuclide-commons-atom/debugger.js +++ b/modules/nuclide-commons-atom/debugger.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getDebuggerService = getDebuggerService; + +function _consumeFirstProvider() { + const data = _interopRequireDefault(require("./consumeFirstProvider")); + + _consumeFirstProvider = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,14 +25,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {DebuggerService} from 'atom-ide-ui'; - -import consumeFirstProvider from './consumeFirstProvider'; - -export function getDebuggerService(): Promise { - return consumeFirstProvider('debugger.remote'); -} +function getDebuggerService() { + return (0, _consumeFirstProvider().default)('debugger.remote'); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/destroyItemWhere.js b/modules/nuclide-commons-atom/destroyItemWhere.js index bffc81a4..a22b6536 100644 --- a/modules/nuclide-commons-atom/destroyItemWhere.js +++ b/modules/nuclide-commons-atom/destroyItemWhere.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.destroyItemWhere = destroyItemWhere; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,13 +13,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -export function destroyItemWhere( - predicate: (item: atom$PaneItem) => boolean, -): void { +function destroyItemWhere(predicate) { atom.workspace.getPanes().forEach(pane => { pane.getItems().forEach(item => { if (predicate(item)) { @@ -20,4 +24,4 @@ export function destroyItemWhere( } }); }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/dock-for-location.js b/modules/nuclide-commons-atom/dock-for-location.js index 2d3fa9e3..fdc562ee 100644 --- a/modules/nuclide-commons-atom/dock-for-location.js +++ b/modules/nuclide-commons-atom/dock-for-location.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = dockForLocation; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,19 +13,21 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - // Return the dock for a pane if the pane is in a dock (i.e. not in the center.) -export default function dockForLocation(location: string): ?atom$Dock { +function dockForLocation(location) { switch (location) { case 'bottom': return atom.workspace.getBottomDock(); + case 'left': return atom.workspace.getLeftDock(); + case 'right': return atom.workspace.getRightDock(); } + return null; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/experimental-packages/MessageRouter.js b/modules/nuclide-commons-atom/experimental-packages/MessageRouter.js index 6523d614..8d7956d1 100644 --- a/modules/nuclide-commons-atom/experimental-packages/MessageRouter.js +++ b/modules/nuclide-commons-atom/experimental-packages/MessageRouter.js @@ -1,3 +1,64 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function jsonrpc() { + const data = _interopRequireWildcard(require("vscode-jsonrpc")); + + jsonrpc = function () { + return data; + }; + + return data; +} + +function _messageReader() { + const data = require("vscode-jsonrpc/lib/messageReader"); + + _messageReader = function () { + return data; + }; + + return data; +} + +function _messageWriter() { + const data = require("vscode-jsonrpc/lib/messageWriter"); + + _messageWriter = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,55 +67,42 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {PipedMessage, ServiceConnection} from './types'; - -import {getLogger} from 'log4js'; -import {DefaultMap} from 'nuclide-commons/collection'; -import {Observable, Subject} from 'rxjs'; -import * as jsonrpc from 'vscode-jsonrpc'; -import {AbstractMessageReader} from 'vscode-jsonrpc/lib/messageReader'; -import {AbstractMessageWriter} from 'vscode-jsonrpc/lib/messageWriter'; - -// We'll represent sockets in pairs (numbers and their negatives). -// After writing to a socket, the message may be read through its negative. -// eslint-disable-next-line -export opaque type Socket = number; - -type DataCallback = (data: PipedMessage) => mixed; - /** * In the new package model, communication between packages will be modeled as sockets. * For each producer <-> consumer pair, we will create a socket: * the consumer gets one end of the socket, while the producer gets the other end. */ -export default class MessageRouter { - _curSocketID = 1; - _sockets: Map> = new Map(); - - // If messages are sent to a socket before a listener gets attached, - // buffer it up here. The buffer will be cleared after the first getMessages call. - _buffer: DefaultMap> = new DefaultMap(Array); +class MessageRouter { + constructor() { + this._curSocketID = 1; + this._sockets = new Map(); + this._buffer = new (_collection().DefaultMap)(Array); + } /** * Returns a pair of sockets. */ - getSocket(): [Socket, Socket] { + getSocket() { const socket = [this._curSocketID, -this._curSocketID]; this._curSocketID++; return socket; } - reverseSocket(socket: Socket): Socket { + reverseSocket(socket) { return -socket; } - send(message: PipedMessage): void { - const {socket} = message; + send(message) { + const { + socket + } = message; + const subject = this._sockets.get(socket); + if (subject == null) { this._buffer.get(socket).push(message); } else { @@ -62,56 +110,59 @@ export default class MessageRouter { } } - getMessages(socket: Socket): Observable { + getMessages(socket) { let subject = this._sockets.get(socket); + if (subject == null) { - subject = new Subject(); + subject = new _RxMin.Subject(); + this._sockets.set(socket, subject); + const buffered = this._buffer.get(socket); + this._buffer.delete(socket); - return Observable.from(buffered).concat(subject); + + return _RxMin.Observable.from(buffered).concat(subject); } + return subject; } - createConnection(socket: Socket, config: ?Object): ServiceConnection { - const connection: ServiceConnection = (jsonrpc.createMessageConnection( - // Messages intended for socket actually come through -socket. - new SimpleReader(cb => - this.getMessages(this.reverseSocket(socket)).subscribe(cb), - ), - // Tag each message with the socket it originated from. - new SimpleWriter(msg => this.send({...msg, socket})), - getLogger('ExperimentalMessageRouter-jsonrpc'), - ): any); + createConnection(socket, config) { + const connection = jsonrpc().createMessageConnection( // Messages intended for socket actually come through -socket. + new SimpleReader(cb => this.getMessages(this.reverseSocket(socket)).subscribe(cb)), // Tag each message with the socket it originated from. + new SimpleWriter(msg => this.send(Object.assign({}, msg, { + socket + }))), (0, _log4js().getLogger)('ExperimentalMessageRouter-jsonrpc')); connection.config = config || {}; connection.listen(); return connection; } + } -class SimpleReader extends AbstractMessageReader { - _subscribe: (callback: DataCallback) => mixed; +exports.default = MessageRouter; - constructor(subscribe: (callback: DataCallback) => mixed): void { +class SimpleReader extends _messageReader().AbstractMessageReader { + constructor(subscribe) { super(); this._subscribe = subscribe; } - listen(callback: (data: PipedMessage) => mixed): void { + listen(callback) { this._subscribe(callback); } -} -class SimpleWriter extends AbstractMessageWriter { - _write: (message: PipedMessage) => mixed; +} - constructor(write: (message: PipedMessage) => mixed) { +class SimpleWriter extends _messageWriter().AbstractMessageWriter { + constructor(write) { super(); this._write = write; } - write(message: PipedMessage): void { + write(message) { this._write(message); } -} + +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/experimental-packages/PackageRunners.js b/modules/nuclide-commons-atom/experimental-packages/PackageRunners.js index 9a68636c..dfac1a80 100644 --- a/modules/nuclide-commons-atom/experimental-packages/PackageRunners.js +++ b/modules/nuclide-commons-atom/experimental-packages/PackageRunners.js @@ -1,3 +1,54 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.AtomPackageRunner = exports.ProcessPackageRunner = void 0; + +function _process() { + const data = require("../../nuclide-commons/process"); + + _process = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _MessageRouter() { + const data = _interopRequireDefault(require("./MessageRouter")); + + _MessageRouter = function () { + return data; + }; + + return data; +} + +function _activatePackage() { + const data = _interopRequireDefault(require("./activatePackage")); + + _activatePackage = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,138 +57,103 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class ProcessPackageRunner { + constructor(packages, messageRouter) { + this._disposed = new _RxMin.ReplaySubject(1); + this._processStream = (0, _process().fork)(require.resolve("./run-package-entry.js"), [], { + silent: true + }).takeUntil(this._disposed).do(proc => { + proc.on('message', msg => { + messageRouter.send(msg); + }); + const exposedSockets = getExposedSockets(packages, messageRouter); + proc.send({ + packages, + exposedSockets + }); + exposedSockets.forEach(socket => { + // Intercept incoming messages for each exposed socket. + messageRouter.getMessages(messageRouter.reverseSocket(socket)).takeUntil(this._disposed).subscribe(msg => proc.send(msg)); + }); + }) // TODO: Error on early completion. + .share().publishReplay(1); // Note: this won't start emitting anything activate() gets called. -import type {ProcessMessage} from 'nuclide-commons/process'; -import type {ConnectableObservable} from 'rxjs'; -import type { - InitializeMessage, - PackageParams, - PackageRunner, - Socket, -} from './types'; - -import {fork, getOutputStream} from 'nuclide-commons/process'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {ReplaySubject} from 'rxjs'; -import MessageRouter from './MessageRouter'; -import activatePackage from './activatePackage'; - -export class ProcessPackageRunner implements PackageRunner { - _processStream: ConnectableObservable; - _outputStream: ConnectableObservable; - _disposed = new ReplaySubject(1); - - constructor( - packages: Array, - messageRouter: MessageRouter, - ): void { - this._processStream = fork(require.resolve('./run-package-entry.js'), [], { - silent: true, - }) - .takeUntil(this._disposed) - .do(proc => { - proc.on('message', msg => { - messageRouter.send(msg); - }); - const exposedSockets = getExposedSockets(packages, messageRouter); - proc.send(({packages, exposedSockets}: InitializeMessage)); - exposedSockets.forEach(socket => { - // Intercept incoming messages for each exposed socket. - messageRouter - .getMessages(messageRouter.reverseSocket(socket)) - .takeUntil(this._disposed) - .subscribe(msg => proc.send(msg)); - }); - }) - // TODO: Error on early completion. - .share() - .publishReplay(1); - - // Note: this won't start emitting anything activate() gets called. - this._outputStream = this._processStream - .switchMap(proc => getOutputStream(proc)) - .publish(); + this._outputStream = this._processStream.switchMap(proc => (0, _process().getOutputStream)(proc)).publish(); } - activate(): void { + activate() { this._processStream.connect(); } - onDidError(callback: (error: Error) => mixed): IDisposable { - return new UniversalDisposable( - this._outputStream.refCount().subscribe({ - error: err => { - callback(err); - }, - }), - ); + onDidError(callback) { + return new (_UniversalDisposable().default)(this._outputStream.refCount().subscribe({ + error: err => { + callback(err); + } + })); } - dispose(): void { + dispose() { this._disposed.next(); } -} -// Atom packages have to run in the same process. -export class AtomPackageRunner implements PackageRunner { - _packages: Array; - _messageRouter: MessageRouter; - _disposables: UniversalDisposable; +} // Atom packages have to run in the same process. + + +exports.ProcessPackageRunner = ProcessPackageRunner; - constructor( - packages: Array, - messageRouter: MessageRouter, - ): void { +class AtomPackageRunner { + constructor(packages, messageRouter) { this._packages = packages; this._messageRouter = messageRouter; - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable().default)(); } - activate(): void { - this._disposables.add( - ...this._packages.map(params => { - const pkg = activatePackage(params, this._messageRouter); - return () => { - if (pkg.dispose != null) { - pkg.dispose(); - } - }; - }), - ); + activate() { + this._disposables.add(...this._packages.map(params => { + const pkg = (0, _activatePackage().default)(params, this._messageRouter); + return () => { + if (pkg.dispose != null) { + pkg.dispose(); + } + }; + })); } dispose() { this._disposables.dispose(); } - onDidError(callback: (error: Error) => mixed): IDisposable { - return new UniversalDisposable(); + onDidError(callback) { + return new (_UniversalDisposable().default)(); } + } -function getExposedSockets( - packages: Array, - messageRouter: MessageRouter, -): Array { +exports.AtomPackageRunner = AtomPackageRunner; + +function getExposedSockets(packages, messageRouter) { // Exposed sockets are those that are either: // 1) provided here but not consumed // 2) consumed here but not provided. const allSockets = new Set(); packages.forEach(pkg => { Object.keys(pkg.consumedServices).forEach(key => { - const {socket} = pkg.consumedServices[key]; + const { + socket + } = pkg.consumedServices[key]; allSockets.add(socket); }); Object.keys(pkg.providedServices).forEach(key => { - pkg.providedServices[key].rawConnections.forEach(({socket}) => { + pkg.providedServices[key].rawConnections.forEach(({ + socket + }) => { allSockets.add(socket); }); }); }); - return Array.from(allSockets).filter( - socket => !allSockets.has(messageRouter.reverseSocket(socket)), - ); -} + return Array.from(allSockets).filter(socket => !allSockets.has(messageRouter.reverseSocket(socket))); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/experimental-packages/activatePackage.js b/modules/nuclide-commons-atom/experimental-packages/activatePackage.js index cd444529..ae8e2bcc 100644 --- a/modules/nuclide-commons-atom/experimental-packages/activatePackage.js +++ b/modules/nuclide-commons-atom/experimental-packages/activatePackage.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = activatePackage; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,35 +13,38 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {PackageParams} from './types'; -import type MessageRouter from './MessageRouter'; - -export default function activatePackage( - {main, providedServices, consumedServices}: PackageParams, - messageRouter: MessageRouter, -): IDisposable { +function activatePackage({ + main, + providedServices, + consumedServices +}, messageRouter) { const connections = Object.create(null); Object.keys(providedServices).forEach(key => { - const {rawConnections} = providedServices[key]; - connections[key] = rawConnections.map(({socket, config}) => - messageRouter.createConnection(socket, config), - ); - }); + const { + rawConnections + } = providedServices[key]; + connections[key] = rawConnections.map(({ + socket, + config + }) => messageRouter.createConnection(socket, config)); + }); // Create clients for each consumed service. - // Create clients for each consumed service. const clients = Object.create(null); Object.keys(consumedServices).forEach(key => { - const {socket, client} = consumedServices[key]; - // $FlowIgnore + const { + socket, + client + } = consumedServices[key]; // $FlowIgnore + const clientClass = require(client).default; + clients[key] = new clientClass(messageRouter.createConnection(socket)); - }); + }); // $FlowIgnore - // $FlowIgnore const packageClass = require(main).default; + return new packageClass(clients, connections); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/experimental-packages/activatePackages.js b/modules/nuclide-commons-atom/experimental-packages/activatePackages.js index 394dac97..787ce499 100644 --- a/modules/nuclide-commons-atom/experimental-packages/activatePackages.js +++ b/modules/nuclide-commons-atom/experimental-packages/activatePackages.js @@ -1,3 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = activateExperimentalPackages; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _path = _interopRequireDefault(require("path")); + +function _PackageRunners() { + const data = require("./PackageRunners"); + + _PackageRunners = function () { + return data; + }; + + return data; +} + +function _MessageRouter() { + const data = _interopRequireDefault(require("./MessageRouter")); + + _MessageRouter = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,89 +47,72 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {Feature} from '../FeatureLoader'; -import type { - ExperimentalPackageDefinition, - PackageParams, - Socket, -} from './types'; - -import idx from 'idx'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import path from 'path'; // eslint-disable-line nuclide-internal/prefer-nuclide-uri -import {AtomPackageRunner, ProcessPackageRunner} from './PackageRunners'; -import MessageRouter from './MessageRouter'; - -type ExperimentalServiceTable = { - [serviceName: string]: { - [version: string]: {| - client: string, - rawConsumerConnections: Array<{|socket: Socket, config: Object|}>, - |}, - }, -}; - -export default function activateExperimentalPackages( - features: Array, -): IDisposable { - const messageRouter = new MessageRouter(); +// eslint-disable-line nuclide-internal/prefer-nuclide-uri +function activateExperimentalPackages(features) { + const messageRouter = new (_MessageRouter().default)(); const experimentalFeatures = getExperimentalFeatures(features); const availableServices = aggregateExperimentalServices(experimentalFeatures); - const packages = []; - const disposables = new UniversalDisposable(); + const disposables = new (_UniversalDisposable().default)(); + const atomPackages = []; // TODO: split into multiple processes? - const atomPackages = []; - // TODO: split into multiple processes? const processPackages = []; - experimentalFeatures.forEach(feature => { - const experimentalSection: ExperimentalPackageDefinition = (feature.pkg: any) - .experimental; - const main = path.join(feature.path, experimentalSection.main); - const pkgParams: PackageParams = { + const experimentalSection = feature.pkg.experimental; + + const main = _path.default.join(feature.path, experimentalSection.main); + + const pkgParams = { main, consumedServices: createObject(), - providedServices: createObject(), + providedServices: createObject() }; - const consumedServicesRaw = experimentalSection.consumedServices; - const providedServicesRaw = experimentalSection.providedServices; + const providedServicesRaw = experimentalSection.providedServices; // Build a map of services consumed by this package. - // Build a map of services consumed by this package. if (consumedServicesRaw != null) { Object.keys(consumedServicesRaw).forEach(key => { - const {name, version, config} = consumedServicesRaw[key]; - const availableVersion = idx(availableServices, _ => _[name][version]); - // TODO: Handle missing required services. + var _ref; + + const { + name, + version, + config + } = consumedServicesRaw[key]; + const availableVersion = (_ref = availableServices) != null ? (_ref = _ref[name]) != null ? _ref[version] : _ref : _ref; // TODO: Handle missing required services. + if (availableVersion != null) { const [inSocket, outSocket] = messageRouter.getSocket(); pkgParams.consumedServices[key] = { socket: inSocket, - client: availableVersion.client, + client: availableVersion.client }; availableVersion.rawConsumerConnections.push({ socket: outSocket, - config: config || {}, + config: config || {} }); } }); - } + } // Build a map of services provided by this package. + - // Build a map of services provided by this package. if (providedServicesRaw != null) { Object.keys(providedServicesRaw).forEach(key => { - const {name, version} = providedServicesRaw[key]; - const availableVersion = idx(availableServices, _ => _[name][version]); - // TODO: Handle missing required services. + var _ref2; + + const { + name, + version + } = providedServicesRaw[key]; + const availableVersion = (_ref2 = availableServices) != null ? (_ref2 = _ref2[name]) != null ? _ref2[version] : _ref2 : _ref2; // TODO: Handle missing required services. + if (availableVersion != null) { pkgParams.providedServices[key] = { // NOTE: This only becomes complete after checking all packages. - rawConnections: availableVersion.rawConsumerConnections, + rawConnections: availableVersion.rawConsumerConnections }; } }); @@ -102,68 +126,75 @@ export default function activateExperimentalPackages( }); if (atomPackages.length > 0) { - packages.push(new AtomPackageRunner(atomPackages, messageRouter)); + packages.push(new (_PackageRunners().AtomPackageRunner)(atomPackages, messageRouter)); } if (processPackages.length > 0) { - packages.push(new ProcessPackageRunner(processPackages, messageRouter)); - } + packages.push(new (_PackageRunners().ProcessPackageRunner)(processPackages, messageRouter)); + } // Activate all the packages. + - // Activate all the packages. packages.forEach(pkg => { - disposables.add( - pkg, - pkg.onDidError(err => { - atom.notifications.addError('Feature Process Crashed', { - description: 'Please restart Atom to continue.', - detail: String(err), - buttons: [ - { - className: 'icon icon-zap', - text: 'Reload Atom', - onDidClick() { - atom.reload(); - }, - }, - ], - }); - }), - ); + disposables.add(pkg, pkg.onDidError(err => { + atom.notifications.addError('Feature Process Crashed', { + description: 'Please restart Atom to continue.', + detail: String(err), + buttons: [{ + className: 'icon icon-zap', + text: 'Reload Atom', + + onDidClick() { + atom.reload(); + } + + }] + }); + })); pkg.activate(); }); - return disposables; } -function getExperimentalFeatures(features: Array): Array { - return features.filter( - // $FlowIgnore - feature => idx(feature.pkg, _ => _.experimental.main) != null, - ); +function getExperimentalFeatures(features) { + return features.filter( // $FlowIgnore + feature => { + var _ref3; + + return ((_ref3 = feature.pkg) != null ? (_ref3 = _ref3.experimental) != null ? _ref3.main : _ref3 : _ref3) != null; + }); } -function aggregateExperimentalServices( - features: Array, -): ExperimentalServiceTable { +function aggregateExperimentalServices(features) { // Build a table of provided services. - const table: ExperimentalServiceTable = createObject(); + const table = createObject(); features.forEach(feature => { - const experimentalSection: ExperimentalPackageDefinition = (feature.pkg: any) - .experimental; - const {providedServices} = experimentalSection; + const experimentalSection = feature.pkg.experimental; + const { + providedServices + } = experimentalSection; + if (providedServices != null) { Object.keys(providedServices).forEach(alias => { - const {client, name, version} = providedServices[alias]; + const { + client, + name, + version + } = providedServices[alias]; const row = table[name] || (table[name] = {}); - const clientPath = path.join(feature.path, client); - row[version] = {client: clientPath, rawConsumerConnections: []}; + + const clientPath = _path.default.join(feature.path, client); + + row[version] = { + client: clientPath, + rawConsumerConnections: [] + }; }); } }); return table; -} +} // An object that may safely be used as a map. -// An object that may safely be used as a map. -function createObject(): Object { + +function createObject() { return Object.create(null); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/experimental-packages/run-package.js b/modules/nuclide-commons-atom/experimental-packages/run-package.js index 009f0d3d..0a3aec84 100644 --- a/modules/nuclide-commons-atom/experimental-packages/run-package.js +++ b/modules/nuclide-commons-atom/experimental-packages/run-package.js @@ -1,3 +1,37 @@ +"use strict"; + +function _log4js() { + const data = _interopRequireDefault(require("log4js")); + + _log4js = function () { + return data; + }; + + return data; +} + +function _MessageRouter() { + const data = _interopRequireDefault(require("./MessageRouter")); + + _MessageRouter = function () { + return data; + }; + + return data; +} + +function _activatePackage() { + const data = _interopRequireDefault(require("./activatePackage")); + + _activatePackage = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,66 +40,55 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {InitializeMessage, PipedMessage} from './types'; - -import invariant from 'assert'; -import log4js from 'log4js'; -import MessageRouter from './MessageRouter'; -import activatePackage from './activatePackage'; - // Send log4js errors to stderr for visibility from the main process. -log4js.configure({ - appenders: [{type: 'stderr'}], +_log4js().default.configure({ + appenders: [{ + type: 'stderr' + }] }); -const logger = log4js.getLogger('experimental-run-package'); +const logger = _log4js().default.getLogger('experimental-run-package'); + process.on('uncaughtException', err => { logger.fatal('Uncaught exception:', err); - log4js.shutdown(() => process.abort()); -}); + _log4js().default.shutdown(() => process.abort()); +}); process.on('unhandledRejection', err => { logger.warn('Unhandled rejection', err); -}); +}); // Properly terminate if the parent server crashes. -// Properly terminate if the parent server crashes. process.on('disconnect', () => { process.exit(); }); +process.once('message', ({ + packages, + exposedSockets +}) => { + const messageRouter = new (_MessageRouter().default)(); // Route incoming IPC messages into the message router. -process.once('message', ({packages, exposedSockets}: InitializeMessage) => { - const messageRouter = new MessageRouter(); - - // Route incoming IPC messages into the message router. - process.on('message', (message: PipedMessage) => { + process.on('message', message => { messageRouter.send(message); - }); + }); // Messages to external sockets need to go over IPC. - // Messages to external sockets need to go over IPC. exposedSockets.forEach(socket => { - messageRouter - .getMessages(socket) - .mergeMap( - message => - new Promise(resolve => { - invariant(process.send != null); - process.send(message, resolve); - }), - /* Set concurrency to 1 to avoid blocking IPC. */ 1, - ) - .subscribe(); - }); + messageRouter.getMessages(socket).mergeMap(message => new Promise(resolve => { + if (!(process.send != null)) { + throw new Error("Invariant violation: \"process.send != null\""); + } + + process.send(message, resolve); + }), + /* Set concurrency to 1 to avoid blocking IPC. */ + 1).subscribe(); + }); // Create connections for each provided service. - // Create connections for each provided service. const activatedPackages = packages.map(pkg => { - return activatePackage(pkg, messageRouter); - // ??? Maybe there should be an explicit signal or shutdown message via IPC. + return (0, _activatePackage().default)(pkg, messageRouter); // ??? Maybe there should be an explicit signal or shutdown message via IPC. }); - process.on('exit', () => { activatedPackages.forEach(pkg => { if (pkg.dispose != null) { @@ -73,4 +96,4 @@ process.once('message', ({packages, exposedSockets}: InitializeMessage) => { } }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons-atom/experimental-packages/types.js b/modules/nuclide-commons-atom/experimental-packages/types.js index 601812fa..9a390c31 100644 --- a/modules/nuclide-commons-atom/experimental-packages/types.js +++ b/modules/nuclide-commons-atom/experimental-packages/types.js @@ -1,84 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {MessageConnection} from 'vscode-jsonrpc'; -import type {Socket as Socket_} from './MessageRouter'; // eslint-disable-line nuclide-internal/import-type-style - -export type Socket = Socket_; - -export type ExperimentalPackageDefinition = { - main: string, - // Allows the package to run in the Atom renderer process. - // Should only be used when absolutely necessary! - runInAtom_UNSAFE?: boolean, - consumedServices?: { - [alias: string]: {| - name: string, - version: string, - config?: Object, - |}, - }, - providedServices?: { - [alias: string]: {| - name: string, - version: string, - // The `client` specifies a path to a module that exports a single function which accepts a - // JsonRpcConnection and returns the object that's provided to packages that consume the - // service. - client: string, - // In the future, we may allow for a server to be specified here as well. If it is, it will be - // used to wrap the array of connections passed to the package and that will be passed - // instead. There isn't a *huge* benefit to this, but it would provide a typed interface and - // could be generated. - |}, - }, -}; - -type ProvidedServices = { - [key: string]: {| - rawConnections: Array<{|socket: Socket, config: Object|}>, - |}, -}; - -type ConsumedServices = { - [key: string]: {| - socket: Socket, - // Path to JS module that creates the client object. - client: string, - |}, -}; - -export type PackageParams = {| - main: string, - providedServices: ProvidedServices, - consumedServices: ConsumedServices, -|}; - -export interface PackageRunner { - activate(): void; - dispose(): void; - - onDidError(callback: (error: Error) => mixed): IDisposable; -} - -export type ServiceConnection = MessageConnection & {config: Object}; - -// Each JSON-RPC message comes from a socket. -// Include the socket as a field in the message. -export type PipedMessage = { - socket: Socket, -}; - -export type InitializeMessage = {| - packages: Array, - exposedSockets: Array, -|}; +"use strict"; \ No newline at end of file diff --git a/modules/nuclide-commons-atom/feature-config.js b/modules/nuclide-commons-atom/feature-config.js index e454ad66..b500482f 100644 --- a/modules/nuclide-commons-atom/feature-config.js +++ b/modules/nuclide-commons-atom/feature-config.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +15,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -14,32 +23,31 @@ * A wrapper over Atom's config functions for use with FeatureLoader. * Each individual loaded package's config is a subconfig of the root package. */ - -import invariant from 'assert'; -import {Observable} from 'rxjs'; - let packageName = null; - /** * Sets the root package name. * This gets automatically called from FeatureLoader. */ -function setPackageName(name: string): void { + +function setPackageName(name) { packageName = name; } -function getPackageName(): string { - invariant(packageName != null, 'No package name available'); +function getPackageName() { + if (!(packageName != null)) { + throw new Error('No package name available'); + } + return packageName; } -function formatKeyPath(keyPath: string): string { +function formatKeyPath(keyPath) { if (packageName == null) { return keyPath; } + return `${packageName}.${keyPath}`; } - /* * Returns the value of a setting for a Nuclide feature key. Takes and returns the same types as * `atom.config.get` exception `keyPath` is not optional. To get the entire config object, use @@ -52,153 +60,100 @@ function formatKeyPath(keyPath: string): string { * Example: * const config: MyConfigType = (featureConfig.get('config-name'): any); */ -function get( - keyPath: string, - options?: { - excludeSources?: Array, - sources?: Array, - scope?: atom$ScopeDescriptorLike, - }, -): mixed { + + +function get(keyPath, options) { // atom.config.get will crash if the second arg is present and undefined. // It does not crash if the second arg is missing. - return atom.config.get( - formatKeyPath(keyPath), - ...(options == null ? [] : [options]), - ); + return atom.config.get(formatKeyPath(keyPath), ...(options == null ? [] : [options])); } -function getWithDefaults( - keyPath: string, - defaults: T, - options?: { - excludeSources?: Array, - sources?: Array, - scope?: atom$ScopeDescriptorLike, - }, -): T { - const current: any = get(keyPath, options); +function getWithDefaults(keyPath, defaults, options) { + const current = get(keyPath, options); return current == null ? defaults : current; } - /* * Gets the schema of a setting for a Nuclide feature key. Takes and returns the same types as * `atom.config.getSchema`. */ -function getSchema(keyPath: string): atom$ConfigSchema { + + +function getSchema(keyPath) { return atom.config.getSchema(formatKeyPath(keyPath)); } - /* * Similar to `atom.config.observe` except arguments are required, and options cannot be given. * * To observe changes on the entire config, use `atom.config.observe`. */ -function observe( - keyPath: string, - optionsOrCallback?: - | {scope?: atom$ScopeDescriptorLike} - | ((value: any) => mixed), - callback?: (value: any) => mixed, -): IDisposable { - return atom.config.observe( - formatKeyPath(keyPath), - ...Array.prototype.slice.call(arguments, 1), - ); -} + +function observe(keyPath, optionsOrCallback, callback) { + return atom.config.observe(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); +} /* * Behaves similarly to the `observe` function, but returns a stream of values, rather * then receiving a callback. */ -function observeAsStream( - keyPath: string, - options?: {scope?: atom$ScopeDescriptorLike} = {}, -): Observable { - return Observable.create(observer => { + + +function observeAsStream(keyPath, options = {}) { + return _RxMin.Observable.create(observer => { const disposable = observe(keyPath, options, observer.next.bind(observer)); return disposable.dispose.bind(disposable); }); } - /* * Takes and returns the same types as `atom.config.onDidChange` except `keyPath` is not optional. * To listen to changes on all key paths, use `atom.config.onDidChange`. */ -function onDidChange( - keyPath: string, - optionsOrCallback?: - | {scope?: atom$ScopeDescriptorLike} - | ((event: {oldValue: mixed, newValue: mixed}) => mixed), - callback?: (event: {oldValue: mixed, newValue: mixed}) => mixed, -): IDisposable { - return atom.config.onDidChange( - formatKeyPath(keyPath), - ...Array.prototype.slice.call(arguments, 1), - ); -} + +function onDidChange(keyPath, optionsOrCallback, callback) { + return atom.config.onDidChange(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); +} /* * Sets the value of a setting for a Nuclide feature key. Takes and returns the same types as * `atom.config.set`. */ -function set( - keyPath: string, - value: ?mixed, - options?: { - scopeSelector?: string, - source?: string, - }, -): boolean { - return atom.config.set( - formatKeyPath(keyPath), - ...Array.prototype.slice.call(arguments, 1), - ); -} + +function set(keyPath, value, options) { + return atom.config.set(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); +} /* * Sets the schema of a setting for a Nuclide feature key. Takes and returns the same types as * `atom.config.setSchema`. */ -function setSchema(keyPath: string, schema: Object): void { - return atom.config.setSchema( - formatKeyPath(keyPath), - ...Array.prototype.slice.call(arguments, 1), - ); -} + +function setSchema(keyPath, schema) { + return atom.config.setSchema(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); +} /* * Restores a setting for a Nuclide feature key to its default value. Takes and returns the same * types as `atom.config.set`. */ -function unset( - keyPath: string, - options?: { - scopeSelector?: string, - source?: string, - }, -): void { - return atom.config.unset( - formatKeyPath(keyPath), - ...Array.prototype.slice.call(arguments, 1), - ); -} + +function unset(keyPath, options) { + return atom.config.unset(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); +} /** * Returns `true` if the feature with the given name is disabled either directly or because the * container package itself is disabled. */ -function isFeatureDisabled(name: string): boolean { + + +function isFeatureDisabled(name) { if (packageName == null) { return atom.packages.isPackageDisabled(name); } - return ( - atom.packages.isPackageDisabled(packageName) || - !atom.config.get(`${packageName}.use.${name}`) - ); + + return atom.packages.isPackageDisabled(packageName) || !atom.config.get(`${packageName}.use.${name}`); } -export default { +var _default = { formatKeyPath, setPackageName, getPackageName, @@ -211,5 +166,6 @@ export default { set, setSchema, unset, - isFeatureDisabled, + isFeatureDisabled }; +exports.default = _default; \ No newline at end of file diff --git a/modules/nuclide-commons-atom/findKeyBindingsForCommand.js b/modules/nuclide-commons-atom/findKeyBindingsForCommand.js index 49b93d01..677fdd1b 100644 --- a/modules/nuclide-commons-atom/findKeyBindingsForCommand.js +++ b/modules/nuclide-commons-atom/findKeyBindingsForCommand.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = findKeyBindingsForCommand; + +function _humanizeKeystroke() { + const data = _interopRequireDefault(require("../nuclide-commons/humanizeKeystroke")); + + _humanizeKeystroke = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,22 +25,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ -import humanizeKeystroke from 'nuclide-commons/humanizeKeystroke'; - /** * Determine what the applicable shortcut for a given command is within this component's context. * For example, this will return different keybindings on windows vs linux. */ -export default function findKeyBindingsForCommand( - command: string, - target?: HTMLElement = atom.views.getView(atom.workspace), -): string { - const matchingKeyBindings = atom.keymaps.findKeyBindings({command, target}); - const keystroke = - (matchingKeyBindings.length && matchingKeyBindings[0].keystrokes) || ''; - return humanizeKeystroke(keystroke, process.platform); -} +function findKeyBindingsForCommand(command, target = atom.views.getView(atom.workspace)) { + const matchingKeyBindings = atom.keymaps.findKeyBindings({ + command, + target + }); + const keystroke = matchingKeyBindings.length && matchingKeyBindings[0].keystrokes || ''; + return (0, _humanizeKeystroke().default)(keystroke, process.platform); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/getElementFilePath.js b/modules/nuclide-commons-atom/getElementFilePath.js index de10682d..d6c80ab6 100644 --- a/modules/nuclide-commons-atom/getElementFilePath.js +++ b/modules/nuclide-commons-atom/getElementFilePath.js @@ -1,3 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = getElementFilePath; + +function _textEditor() { + const data = require("./text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +23,40 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import {isValidTextEditor} from './text-editor'; - -export default function getElementFilePath( - element: ?HTMLElement, - fallbackToActiveTextEditor: boolean = false, -): ?NuclideUri { +function getElementFilePath(element, fallbackToActiveTextEditor = false) { let el = element; + while (el != null) { if (el.dataset != null && el.dataset.path != null) { - return (el.dataset: any).path; - } - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + return el.dataset.path; + } // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + + if (typeof el.getModel === 'function') { const model = el.getModel(); - if (isValidTextEditor(model)) { + + if ((0, _textEditor().isValidTextEditor)(model)) { const path = model.getPath(); + if (path != null) { return path; } } } + el = el.parentElement; } + if (fallbackToActiveTextEditor) { const editor = atom.workspace.getActiveTextEditor(); + if (editor != null) { return editor.getPath(); } } + return null; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/getFragmentGrammar.js b/modules/nuclide-commons-atom/getFragmentGrammar.js index 0b24a38b..dcf5f7b0 100644 --- a/modules/nuclide-commons-atom/getFragmentGrammar.js +++ b/modules/nuclide-commons-atom/getFragmentGrammar.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = getFragmentGrammar; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +13,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ @@ -14,18 +21,16 @@ * Some grammars should use a specialized grammar for code fragments * (namely PHP, since it's wildly different depending on the presence of a { - const center_ = idx(options, _ => _.center); +async function goToLocation(file, options) { + var _ref, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7; + + const center_ = (_ref = options) != null ? _ref.center : _ref; const center = center_ == null ? true : center_; - const moveCursor_ = idx(options, _ => _.moveCursor); + const moveCursor_ = (_ref2 = options) != null ? _ref2.moveCursor : _ref2; const moveCursor = moveCursor_ == null ? true : moveCursor_; - const activatePane_ = idx(options, _ => _.activatePane); + const activatePane_ = (_ref3 = options) != null ? _ref3.activatePane : _ref3; const activatePane = activatePane_ == null ? true : activatePane_; - const activateItem = idx(options, _ => _.activateItem); - const line = idx(options, _ => _.line); - const column = idx(options, _ => _.column); - const pending = idx(options, _ => _.pending); + const activateItem = (_ref4 = options) != null ? _ref4.activateItem : _ref4; + const line = (_ref5 = options) != null ? _ref5.line : _ref5; + const column = (_ref6 = options) != null ? _ref6.column : _ref6; + const pending = (_ref7 = options) != null ? _ref7.pending : _ref7; // Prefer going to the current editor rather than the leftmost editor. - // Prefer going to the current editor rather than the leftmost editor. const currentEditor = atom.workspace.getActiveTextEditor(); + if (currentEditor != null && currentEditor.getPath() === file) { const paneContainer = atom.workspace.paneContainerForItem(currentEditor); - invariant(paneContainer != null); + + if (!(paneContainer != null)) { + throw new Error("Invariant violation: \"paneContainer != null\""); + } + if (activatePane) { paneContainer.activate(); } + if (line != null) { goToLocationInEditor(currentEditor, { line, column: column == null ? 0 : column, center, - moveCursor, + moveCursor }); } else { - invariant(column == null, 'goToLocation: Cannot specify just column'); + if (!(column == null)) { + throw new Error('goToLocation: Cannot specify just column'); + } } + return currentEditor; } else { // Obviously, calling goToLocation isn't a viable alternative here :P @@ -94,54 +105,53 @@ export async function goToLocation( searchAllPanes: true, activatePane, activateItem, - pending, - }); - // TODO(T28305560) Investigate offenders for this error + pending + }); // TODO(T28305560) Investigate offenders for this error + if (editor == null) { const tmp = {}; Error.captureStackTrace(tmp); const error = Error(`atom.workspace.open returned null on ${file}`); - getLogger('goToLocation').error(error); + (0, _log4js().getLogger)('goToLocation').error(error); throw error; } if (center && line != null) { - editor.scrollToBufferPosition([line, column], {center: true}); + editor.scrollToBufferPosition([line, column], { + center: true + }); } + return editor; } } -const goToLocationSubject = new Subject(); - -type GotoLocationInEditorOptions = {| - line: number, - column: number, - center?: boolean, - moveCursor?: boolean, -|}; +const goToLocationSubject = new _RxMin.Subject(); // Scrolls to the given line/column at the given editor // broadcasts the editor instance on an observable (subject) available // through the getGoToLocation -export function goToLocationInEditor( - editor: atom$TextEditor, - options: GotoLocationInEditorOptions, -): void { +function goToLocationInEditor(editor, options) { const center = options.center == null ? true : options.center; const moveCursor = options.moveCursor == null ? true : options.moveCursor; - const {line, column} = options; + const { + line, + column + } = options; if (moveCursor) { editor.setCursorBufferPosition([line, column]); } + if (center) { - editor.scrollToBufferPosition([line, column], {center: true}); + editor.scrollToBufferPosition([line, column], { + center: true + }); } goToLocationSubject.next(editor); } -export function observeNavigatingEditors(): Observable { +function observeNavigatingEditors() { return goToLocationSubject; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/humanizePath.js b/modules/nuclide-commons-atom/humanizePath.js index a02e4da5..1ebfc425 100644 --- a/modules/nuclide-commons-atom/humanizePath.js +++ b/modules/nuclide-commons-atom/humanizePath.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = humanizePath; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,15 +25,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import idx from 'idx'; -import nuclideUri from 'nuclide-commons/nuclideUri'; - /** * Format a path for display. After the path is humanized, it should no longer be treated like a * parsable, navigable path; it's just for display. @@ -23,44 +37,41 @@ import nuclideUri from 'nuclide-commons/nuclideUri'; * Atom. If you have multiple directories open, the result will be prefixed with one of their names. * If you only have one, it won't. */ -export default function humanizePath( - path: NuclideUri, - options: ?{ - isDirectory?: boolean, - rootPaths?: Array, - }, -): string { - const isDirectory = idx(options, _ => _.isDirectory); - const rootPaths = - idx(options, _ => _.rootPaths) || - atom.project.getDirectories().map(dir => dir.getPath()); +function humanizePath(path, options) { + var _ref, _ref2; + + const isDirectory = (_ref = options) != null ? _ref.isDirectory : _ref; + const rootPaths = ((_ref2 = options) != null ? _ref2.rootPaths : _ref2) || atom.project.getDirectories().map(dir => dir.getPath()); const normalized = normalizePath(path, isDirectory); let resolved; + for (const rootPath of rootPaths) { - const normalizedDir = nuclideUri.normalizeDir(rootPath); - if (nuclideUri.contains(normalizedDir, normalized)) { + const normalizedDir = _nuclideUri().default.normalizeDir(rootPath); + + if (_nuclideUri().default.contains(normalizedDir, normalized)) { resolved = normalized.substr(normalizedDir.length); - const rootName = nuclideUri.basename(normalizedDir); - // If the path is a root or there's more than one root, include the root's name. + + const rootName = _nuclideUri().default.basename(normalizedDir); // If the path is a root or there's more than one root, include the root's name. + + if (normalized === normalizedDir) { - return nuclideUri.normalizeDir(rootName); + return _nuclideUri().default.normalizeDir(rootName); } + if (rootPaths.length > 1) { - return nuclideUri.join(rootName, resolved); + return _nuclideUri().default.join(rootName, resolved); } + return resolved; } - } - - // It's not in one of the project directories so return the full (normalized) + } // It's not in one of the project directories so return the full (normalized) // path run through nuclideUriToDisplayString to remove nuclide:// etc. - return nuclideUri.nuclideUriToDisplayString(normalized); -} -function normalizePath(path: NuclideUri, isDirectory_: ?boolean): NuclideUri { - const isDirectory = - isDirectory_ == null ? nuclideUri.endsWithSeparator(path) : isDirectory_; - return isDirectory - ? nuclideUri.normalizeDir(path) - : nuclideUri.normalize(path); + + return _nuclideUri().default.nuclideUriToDisplayString(normalized); } + +function normalizePath(path, isDirectory_) { + const isDirectory = isDirectory_ == null ? _nuclideUri().default.endsWithSeparator(path) : isDirectory_; + return isDirectory ? _nuclideUri().default.normalizeDir(path) : _nuclideUri().default.normalize(path); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/mouse-to-position.js b/modules/nuclide-commons-atom/mouse-to-position.js index ec40fe32..0a383955 100644 --- a/modules/nuclide-commons-atom/mouse-to-position.js +++ b/modules/nuclide-commons-atom/mouse-to-position.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.bufferPositionForMouseEvent = bufferPositionForMouseEvent; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,22 +13,24 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +function bufferPositionForMouseEvent(event, editor = null) { + const _editor = editor || atom.workspace.getActiveTextEditor(); -import invariant from 'assert'; + if (!(_editor != null)) { + throw new Error("Invariant violation: \"_editor != null\""); + } -export function bufferPositionForMouseEvent( - event: MouseEvent, - editor: ?atom$TextEditor = null, -): atom$Point { - const _editor = editor || atom.workspace.getActiveTextEditor(); - invariant(_editor != null); const view = atom.views.getView(_editor); const component = view.component; - invariant(component != null); - // Beware, screenPositionForMouseEvent is not a public api and may change in future versions. + + if (!(component != null)) { + throw new Error("Invariant violation: \"component != null\""); + } // Beware, screenPositionForMouseEvent is not a public api and may change in future versions. + + const screenPosition = component.screenPositionForMouseEvent(event); return _editor.bufferPositionForScreenPosition(screenPosition); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/observe-grammar-for-text-editors.js b/modules/nuclide-commons-atom/observe-grammar-for-text-editors.js index 98d655a5..a32cfc41 100644 --- a/modules/nuclide-commons-atom/observe-grammar-for-text-editors.js +++ b/modules/nuclide-commons-atom/observe-grammar-for-text-editors.js @@ -1,3 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = observeGrammarForTextEditors; + +var _atom = require("atom"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,78 +27,64 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {Emitter} from 'atom'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - const GRAMMAR_CHANGE_EVENT = 'grammar-change'; - /** * A singleton that listens to grammar changes in all text editors. */ -class GrammarForTextEditorsListener { - _emitter: Emitter; - _subscriptions: UniversalDisposable; +class GrammarForTextEditorsListener { constructor() { - this._emitter = new Emitter(); - this._subscriptions = new UniversalDisposable(); - this._subscriptions.add( - this._emitter, - atom.workspace.observeTextEditors(textEditor => { - this._subscriptions.addUntilDestroyed( - textEditor, - textEditor.observeGrammar(grammar => { - this._emitter.emit(GRAMMAR_CHANGE_EVENT, textEditor); - }), - ); - }), - ); + this._emitter = new _atom.Emitter(); + this._subscriptions = new (_UniversalDisposable().default)(); + + this._subscriptions.add(this._emitter, atom.workspace.observeTextEditors(textEditor => { + this._subscriptions.addUntilDestroyed(textEditor, textEditor.observeGrammar(grammar => { + this._emitter.emit(GRAMMAR_CHANGE_EVENT, textEditor); + })); + })); } - observeGrammarForTextEditors( - fn: (textEditor: TextEditor, grammar: atom$Grammar) => void, - ): IDisposable { + observeGrammarForTextEditors(fn) { function fnWithGrammar(textEditor) { fn(textEditor, textEditor.getGrammar()); - } - - // The event was already handled before `fn` was added to the emitter, so + } // The event was already handled before `fn` was added to the emitter, so // we need to call it on all the existing editors. + + atom.workspace.getTextEditors().forEach(fnWithGrammar); return this._emitter.on(GRAMMAR_CHANGE_EVENT, fnWithGrammar); } - dispose(): void { + dispose() { this._subscriptions.dispose(); } -} -let grammarForTextEditorsListener: ?GrammarForTextEditorsListener; +} +let grammarForTextEditorsListener; /** * Use this to perform an action on every text editor with its latest grammar. * * @param fn This is called once for every text editor, and then again every * time it changes to a grammar. */ -export default function observeGrammarForTextEditors( - fn: (textEditor: TextEditor, grammar: atom$Grammar) => void, -): IDisposable { + +function observeGrammarForTextEditors(fn) { if (!grammarForTextEditorsListener) { grammarForTextEditorsListener = new GrammarForTextEditorsListener(); } + return grammarForTextEditorsListener.observeGrammarForTextEditors(fn); } if (atom.inSpecMode()) { - observeGrammarForTextEditors.__reset__ = function() { + observeGrammarForTextEditors.__reset__ = function () { if (grammarForTextEditorsListener) { grammarForTextEditorsListener.dispose(); grammarForTextEditorsListener = null; } }; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/observePaneItemVisibility.js b/modules/nuclide-commons-atom/observePaneItemVisibility.js index 4f2953a2..c767ebc5 100644 --- a/modules/nuclide-commons-atom/observePaneItemVisibility.js +++ b/modules/nuclide-commons-atom/observePaneItemVisibility.js @@ -1,3 +1,55 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = observePaneItemVisibility; +exports.observeVisibleItems = observeVisibleItems; + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _memoizeUntilChanged() { + const data = _interopRequireDefault(require("../nuclide-commons/memoizeUntilChanged")); + + _memoizeUntilChanged = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _shallowequal() { + const data = _interopRequireDefault(require("shallowequal")); + + _shallowequal = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,55 +58,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import memoizeUntilChanged from 'nuclide-commons/memoizeUntilChanged'; -import {setFilter} from 'nuclide-commons/collection'; -import {Observable, Scheduler, Subject} from 'rxjs'; -import shallowEqual from 'shallowequal'; - // TODO(T17495608): Currently, docks don't have a way of observing their visibility so this will // have some false positives when an item is its pane's active item but its dock is hidden. -export default function observePaneItemVisibility( - item: Object, -): Observable { +function observePaneItemVisibility(item) { patchDocks(); - const workspaceEl = atom.workspace.getElement(); - return Observable.combineLatest( - // atom.workspace.reset() (in tests) resets all the panes. - // Pass in the workspace dom element to act as a cache-breaker. - observeActiveItems(workspaceEl), - observePaneContainerVisibilities(workspaceEl), - ) - .map(([activeItems, locationVisibilities]) => { - // If it's not active, it's not visible. - if (!activeItems.has(item)) { - return false; - } - // If it's active, it's only visible if its container is. - const paneContainer = atom.workspace.paneContainerForItem(item); - return paneContainer == null - ? false - : locationVisibilities[paneContainer.getLocation()]; - }) - .distinctUntilChanged(); + return _RxMin.Observable.combineLatest( // atom.workspace.reset() (in tests) resets all the panes. + // Pass in the workspace dom element to act as a cache-breaker. + observeActiveItems(workspaceEl), observePaneContainerVisibilities(workspaceEl)).map(([activeItems, locationVisibilities]) => { + // If it's not active, it's not visible. + if (!activeItems.has(item)) { + return false; + } // If it's active, it's only visible if its container is. + + + const paneContainer = atom.workspace.paneContainerForItem(item); + return paneContainer == null ? false : locationVisibilities[paneContainer.getLocation()]; + }).distinctUntilChanged(); } -export function observeVisibleItems() { +function observeVisibleItems() { patchDocks(); - const workspaceEl = atom.workspace.getElement(); - return Observable.combineLatest( - observeActiveItems(workspaceEl), - observePaneContainerVisibilities(workspaceEl), - ).map(([activeItems, locationVisibilities]) => { + return _RxMin.Observable.combineLatest(observeActiveItems(workspaceEl), observePaneContainerVisibilities(workspaceEl)).map(([activeItems, locationVisibilities]) => { // If it's not active, it's not visible. // If it's active, it's only visible if its container is. - return setFilter(activeItems, item => { + return (0, _collection().setFilter)(activeItems, item => { const paneContainer = atom.workspace.paneContainerForItem(item); const location = paneContainer && paneContainer.getLocation(); return location ? Boolean(locationVisibilities[location]) : false; @@ -62,110 +94,100 @@ export function observeVisibleItems() { }); } -const observeActiveItems = memoizeUntilChanged(_cacheKey => { +const observeActiveItems = (0, _memoizeUntilChanged().default)(_cacheKey => { // An observable that emits `{pane, item}` whenever the active item of a pane changes. - const itemActivations = Observable.merge( - ...atom.workspace.getPaneContainers().map(paneContainer => { - const observePanes = paneContainer.observePanes.bind(paneContainer); - return observableFromSubscribeFunction(observePanes).flatMap(pane => { - const paneDestroyed = observableFromSubscribeFunction( - pane.onDidDestroy.bind(pane), - ); - const activeItems = observableFromSubscribeFunction( - pane.observeActiveItem.bind(pane), - ).takeUntil(paneDestroyed); - return Observable.concat( - activeItems.map(item => ({pane, item})), - Observable.of({pane, item: null}), - ); - }); - }), - ); - - // Create a map of panes to their active items. We could look this up by examining the workspace + const itemActivations = _RxMin.Observable.merge(...atom.workspace.getPaneContainers().map(paneContainer => { + const observePanes = paneContainer.observePanes.bind(paneContainer); + return (0, _event().observableFromSubscribeFunction)(observePanes).flatMap(pane => { + const paneDestroyed = (0, _event().observableFromSubscribeFunction)(pane.onDidDestroy.bind(pane)); + const activeItems = (0, _event().observableFromSubscribeFunction)(pane.observeActiveItem.bind(pane)).takeUntil(paneDestroyed); + return _RxMin.Observable.concat(activeItems.map(item => ({ + pane, + item + })), _RxMin.Observable.of({ + pane, + item: null + })); + }); + })); // Create a map of panes to their active items. We could look this up by examining the workspace // every time; this is an optimization. - const panesToActiveItem = itemActivations.scan((acc, {pane, item}) => { + + + const panesToActiveItem = itemActivations.scan((acc, { + pane, + item + }) => { if (item == null) { acc.delete(pane); } else { acc.set(pane, item); } + return acc; }, new Map()); - - return ( - panesToActiveItem - // When dragging items between panes, they'll be quickly deactivated and activated again. To - // avoid doing extra work, we debounce and use the rAF scheduler. - .debounceTime(0, Scheduler.animationFrame) - .map(map => new Set(map.values())) - // $FlowIgnore: this is just not listed in the flow-typed defs - .shareReplay(1) - ); -}); - -// Create an observable that contains the current visibility state of each dock, but where the + return panesToActiveItem // When dragging items between panes, they'll be quickly deactivated and activated again. To + // avoid doing extra work, we debounce and use the rAF scheduler. + .debounceTime(0, _RxMin.Scheduler.animationFrame).map(map => new Set(map.values())) // $FlowIgnore: this is just not listed in the flow-typed defs + .shareReplay(1); +}); // Create an observable that contains the current visibility state of each dock, but where the // "false" values are delayed to account for the time it takes to animate the dock closed. -const observePaneContainerVisibilities = memoizeUntilChanged(_cacheKey => { - const visibilitiesByDock = ['left', 'right', 'bottom'].map(loc => - dockStateChanges - .filter(({location}) => location === loc) - .switchMap( - ({location, visible}) => - // Delay the "false" values so they don't occur while the dock is being animated closed. - visible - ? Observable.of({location, visible}) - : Observable.of({location, visible}).delay(300), - ) - .distinctUntilKeyChanged('visible'), - ); +const observePaneContainerVisibilities = (0, _memoizeUntilChanged().default)(_cacheKey => { + const visibilitiesByDock = ['left', 'right', 'bottom'].map(loc => dockStateChanges.filter(({ + location + }) => location === loc).switchMap(({ + location, + visible + }) => // Delay the "false" values so they don't occur while the dock is being animated closed. + visible ? _RxMin.Observable.of({ + location, + visible + }) : _RxMin.Observable.of({ + location, + visible + }).delay(300)).distinctUntilKeyChanged('visible')); const initialVisibilities = { // The center is always visible. center: true, left: atom.workspace.getLeftDock().isVisible(), right: atom.workspace.getRightDock().isVisible(), - bottom: atom.workspace.getBottomDock().isVisible(), - }; + bottom: atom.workspace.getBottomDock().isVisible() + }; // Accumulate the dock visibilities. - // Accumulate the dock visibilities. - const visibilityStates = Observable.merge(...visibilitiesByDock) - .scan( - (acc, {location, visible}) => ({ - ...acc, - [location]: visible, - }), - initialVisibilities, - ) - .startWith(initialVisibilities) - .distinctUntilChanged(shallowEqual) - .publishReplay(1); - visibilityStates.connect(); + const visibilityStates = _RxMin.Observable.merge(...visibilitiesByDock).scan((acc, { + location, + visible + }) => Object.assign({}, acc, { + [location]: visible + }), initialVisibilities).startWith(initialVisibilities).distinctUntilChanged(_shallowequal().default).publishReplay(1); + visibilityStates.connect(); return visibilityStates; -}); - -// HACK: Monkey-patch Docks in order to observe visibility toggling. +}); // HACK: Monkey-patch Docks in order to observe visibility toggling. // TODO: Use `Dock::observeVisibility` once atom/atom#14736 is in our lowest-supported version + let docksPatched = false; -const dockStateChanges = new Subject(); +const dockStateChanges = new _RxMin.Subject(); + function patchDocks() { if (docksPatched || typeof atom.workspace.getLeftDock !== 'function') { return; } + docksPatched = true; const ctor = atom.workspace.getLeftDock().constructor; - const proto = ctor.prototype; - // $FlowIgnore - const originalSetState = proto.setState; - // $FlowIgnore - proto.setState = function(newState, ...args) { + const proto = ctor.prototype; // $FlowIgnore + + const originalSetState = proto.setState; // $FlowIgnore + + proto.setState = function (newState, ...args) { originalSetState.call(this, newState, ...args); + if (newState.hasOwnProperty('visible')) { dockStateChanges.next({ location: this.getLocation(), - visible: newState.visible, + visible: newState.visible }); } }; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/pane-item.js b/modules/nuclide-commons-atom/pane-item.js index 5fb37675..5b49194d 100644 --- a/modules/nuclide-commons-atom/pane-item.js +++ b/modules/nuclide-commons-atom/pane-item.js @@ -1,3 +1,21 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isPending = isPending; +exports.observePendingStateEnd = observePendingStateEnd; + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,25 +24,18 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; - -export function isPending(paneItem: atom$PaneItem) { +function isPending(paneItem) { const pane = atom.workspace.paneForItem(paneItem); return pane && pane.getPendingItem() === paneItem; } -export function observePendingStateEnd(paneItem: atom$PaneItem) { - invariant( - typeof paneItem.onDidTerminatePendingState === 'function', - 'paneItem must implement onDidTerminatePendingState method', - ); +function observePendingStateEnd(paneItem) { + if (!(typeof paneItem.onDidTerminatePendingState === 'function')) { + throw new Error('paneItem must implement onDidTerminatePendingState method'); + } - return observableFromSubscribeFunction( - paneItem.onDidTerminatePendingState.bind(paneItem), - ); -} + return (0, _event().observableFromSubscribeFunction)(paneItem.onDidTerminatePendingState.bind(paneItem)); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/parseOpenable.js b/modules/nuclide-commons-atom/parseOpenable.js index f459ad15..47c73945 100644 --- a/modules/nuclide-commons-atom/parseOpenable.js +++ b/modules/nuclide-commons-atom/parseOpenable.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = parseOpenable; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,28 +13,18 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -type ParsedOpenable = {| - +uri: NuclideUri, - +line?: number, - +column?: number, -|}; - // From the nuclide-fuzzy-filename-provider module // TODO: Remove that module when Dash and openables replace it -export default function parseOpenable(query: string): ParsedOpenable { +function parseOpenable(query) { const [uri, line, column] = query.split(/:+/); const lineNumber = parseInt(line, 10); const columnNumber = parseInt(column, 10); - return { uri, line: !Number.isNaN(lineNumber) ? lineNumber - 1 : undefined, - column: !Number.isNaN(columnNumber) ? columnNumber - 1 : undefined, + column: !Number.isNaN(columnNumber) ? columnNumber - 1 : undefined }; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/projects.js b/modules/nuclide-commons-atom/projects.js index 0ab29ff8..8e6cb9e2 100644 --- a/modules/nuclide-commons-atom/projects.js +++ b/modules/nuclide-commons-atom/projects.js @@ -1,3 +1,58 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getValidProjectPaths = getValidProjectPaths; +exports.getAtomProjectRelativePath = getAtomProjectRelativePath; +exports.getAtomProjectRootPath = getAtomProjectRootPath; +exports.relativizePathWithDirectory = relativizePathWithDirectory; +exports.getDirectoryForPath = getDirectoryForPath; +exports.getFileForPath = getFileForPath; +exports.observeProjectPaths = observeProjectPaths; +exports.observeProjectPathsAll = observeProjectPathsAll; +exports.onDidChangeProjectPath = onDidChangeProjectPath; +exports.onDidAddProjectPath = onDidAddProjectPath; +exports.onDidRemoveProjectPath = onDidRemoveProjectPath; +exports.observeRemovedHostnames = observeRemovedHostnames; +exports.observeAddedHostnames = observeAddedHostnames; + +var _atom = require("atom"); + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,48 +61,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +function getValidProjectPaths() { + return atom.project.getDirectories().filter(directory => { + // If a remote directory path is a local `Directory` instance, the project path + // isn't yet ready for consumption. + if (_nuclideUri().default.isRemote(directory.getPath()) && directory instanceof _atom.Directory) { + return false; + } -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import {File, Directory} from 'atom'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {diffSets} from 'nuclide-commons/observable'; -import {Observable} from 'rxjs'; - -export function getValidProjectPaths(): Array { - return atom.project - .getDirectories() - .filter(directory => { - // If a remote directory path is a local `Directory` instance, the project path - // isn't yet ready for consumption. - if ( - nuclideUri.isRemote(directory.getPath()) && - directory instanceof Directory - ) { - return false; - } - return true; - }) - .map(directory => directory.getPath()); + return true; + }).map(directory => directory.getPath()); } -export function getAtomProjectRelativePath(path: NuclideUri): ?string { +function getAtomProjectRelativePath(path) { const [projectPath, relativePath] = atom.project.relativizePath(path); + if (!projectPath) { return null; } + return relativePath; } -export function getAtomProjectRootPath(path: NuclideUri): ?string { +function getAtomProjectRootPath(path) { const [projectPath] = atom.project.relativizePath(path); return projectPath; } - /** * Like `atom.project.relativizePath`, except it returns the `Directory` rather than the path. * It also works for non-children, i.e. this can return `../../x`. @@ -55,46 +97,47 @@ export function getAtomProjectRootPath(path: NuclideUri): ?string { * This is intended to be used as a way to get a File object for any path * without worrying about remote vs. local paths. */ -export function relativizePathWithDirectory( - path: NuclideUri, -): [?Directory, NuclideUri] { + + +function relativizePathWithDirectory(path) { for (const directory of atom.project.getDirectories()) { try { - const relativePath = nuclideUri.relative(directory.getPath(), path); + const relativePath = _nuclideUri().default.relative(directory.getPath(), path); + return [directory, relativePath]; - } catch (e) { - // We have a remote-local mismatch or hostname mismatch. + } catch (e) {// We have a remote-local mismatch or hostname mismatch. } } + return [null, path]; } -export function getDirectoryForPath(path: NuclideUri): ?Directory { +function getDirectoryForPath(path) { const [directory, relativePath] = relativizePathWithDirectory(path); + if (directory == null) { return null; } + return directory.getSubdirectory(relativePath); } -export function getFileForPath(path: NuclideUri): ?File { +function getFileForPath(path) { const [directory, relativePath] = relativizePathWithDirectory(path); + if (directory == null) { return null; } + return directory.getFile(relativePath); } -export function observeProjectPaths( - callback: (projectPath: string, added: boolean) => any, -): IDisposable { +function observeProjectPaths(callback) { getValidProjectPaths().forEach(existingPath => callback(existingPath, true)); return onDidChangeProjectPath(callback); } -export function observeProjectPathsAll( - callback: (projectPaths: Array) => any, -): IDisposable { +function observeProjectPathsAll(callback) { let projectPaths = getValidProjectPaths(); let changing = false; callback(projectPaths); @@ -102,6 +145,7 @@ export function observeProjectPathsAll( if (changing) { throw new Error('Cannot update projects in the middle of an update'); } + changing = true; projectPaths = getValidProjectPaths(); callback(projectPaths); @@ -109,36 +153,35 @@ export function observeProjectPathsAll( }); } -export function onDidChangeProjectPath( - callback: (projectPath: string, added: boolean) => void, -): IDisposable { +function onDidChangeProjectPath(callback) { let projectPaths = getValidProjectPaths(); let changing = false; return observeProjectPathsAll(newProjectPaths => { if (changing) { throw new Error('Cannot update projects in the middle of an update'); } - changing = true; - // Check to see if the change was the addition of a project. + + changing = true; // Check to see if the change was the addition of a project. + for (const newProjectPath of newProjectPaths) { if (!projectPaths.includes(newProjectPath)) { callback(newProjectPath, true); } - } - // Check to see if the change was the deletion of a project. + } // Check to see if the change was the deletion of a project. + + for (const projectPath of projectPaths) { if (!newProjectPaths.includes(projectPath)) { callback(projectPath, false); } } + changing = false; projectPaths = newProjectPaths; }); } -export function onDidAddProjectPath( - callback: (projectPath: string) => void, -): IDisposable { +function onDidAddProjectPath(callback) { return onDidChangeProjectPath((projectPath, added) => { if (added) { callback(projectPath); @@ -146,9 +189,7 @@ export function onDidAddProjectPath( }); } -export function onDidRemoveProjectPath( - callback: (projectPath: string) => void, -): IDisposable { +function onDidRemoveProjectPath(callback) { return onDidChangeProjectPath((projectPath, added) => { if (!added) { callback(projectPath); @@ -157,33 +198,13 @@ export function onDidRemoveProjectPath( } function observeHostnames() { - return (atom.packages.initialPackagesActivated - ? Observable.of(null) - : observableFromSubscribeFunction( - atom.packages.onDidActivateInitialPackages.bind(atom.packages), - ) - ).switchMap(() => - observableFromSubscribeFunction( - atom.project.onDidChangePaths.bind(atom.project), - ) - .startWith(null) - .map( - () => - new Set( - atom.project - .getPaths() - .filter(nuclideUri.isRemote) - .map(nuclideUri.getHostname), - ), - ) - .let(diffSets()), - ); -} - -export function observeRemovedHostnames(): Observable { - return observeHostnames().flatMap(diff => Observable.from(diff.removed)); -} - -export function observeAddedHostnames(): Observable { - return observeHostnames().flatMap(diff => Observable.from(diff.added)); + return (atom.packages.initialPackagesActivated ? _RxMin.Observable.of(null) : (0, _event().observableFromSubscribeFunction)(atom.packages.onDidActivateInitialPackages.bind(atom.packages))).switchMap(() => (0, _event().observableFromSubscribeFunction)(atom.project.onDidChangePaths.bind(atom.project)).startWith(null).map(() => new Set(atom.project.getPaths().filter(_nuclideUri().default.isRemote).map(_nuclideUri().default.getHostname))).let((0, _observable().diffSets)())); +} + +function observeRemovedHostnames() { + return observeHostnames().flatMap(diff => _RxMin.Observable.from(diff.removed)); } + +function observeAddedHostnames() { + return observeHostnames().flatMap(diff => _RxMin.Observable.from(diff.added)); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/range.js b/modules/nuclide-commons-atom/range.js index 8ba820ac..d9fe3d63 100644 --- a/modules/nuclide-commons-atom/range.js +++ b/modules/nuclide-commons-atom/range.js @@ -1,3 +1,25 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.wordAtPosition = wordAtPosition; +exports.trimRange = trimRange; +exports.getWordFromMouseEvent = getWordFromMouseEvent; +exports.getWordFromCursorOrSelection = getWordFromCursorOrSelection; + +var _atom = require("atom"); + +function _range() { + const data = require("../nuclide-commons/range"); + + _range = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,26 +28,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import {Range} from 'atom'; -import invariant from 'assert'; -import {wordAtPositionFromBuffer} from 'nuclide-commons/range'; - /** * Finds the word at the position. You can either provide a word regex yourself, * or have Atom use the word regex in force at the scopes at that position, * in which case it uses the optional includeNonWordCharacters, default true. * (I know that's a weird default but it follows Atom's convention...) */ -export function wordAtPosition( - editor: atom$TextEditor, - position: atom$PointObject, - wordRegex?: RegExp | {includeNonWordCharacters: boolean}, -): ?{wordMatch: Array, range: atom$Range} { +function wordAtPosition(editor, position, wordRegex) { let wordRegex_; + if (wordRegex instanceof RegExp) { wordRegex_ = wordRegex; } else { @@ -34,20 +49,22 @@ export function wordAtPosition( // with the editor's current cursor while we want the regex associated with // the specific position. So we re-implement it ourselves... const nonWordChars = editor.getNonWordCharacters(position); - const escaped = nonWordChars.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); - // We copied this escaping regex from atom$Cursor.wordRegexp, rather than + const escaped = nonWordChars.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); // We copied this escaping regex from atom$Cursor.wordRegexp, rather than // using the library function 'escapeStringRegExp'. That's because the // library function doesn't escape the hyphen character and so is // unsuitable for use inside a range. + let r = `^[\t ]*$|[^\\s${escaped}]+`; + if (wordRegex == null || wordRegex.includeNonWordCharacters) { r += `|[${escaped}]+`; } + wordRegex_ = new RegExp(r, 'g'); } - return wordAtPositionFromBuffer(editor.getBuffer(), position, wordRegex_); -} + return (0, _range().wordAtPositionFromBuffer)(editor.getBuffer(), position, wordRegex_); +} /** * Gets the trimmed range from a given range, i.e. moves the start and end points * to the first and last non-whitespace characters (or specified regex) @@ -59,37 +76,40 @@ export function wordAtPosition( * defaults to first non-whitespace character * @return atom$Range the trimmed range */ -export function trimRange( - editor: atom$TextEditor, - rangeToTrim: atom$Range, - stopRegex: RegExp = /\S/, -): atom$Range { + + +function trimRange(editor, rangeToTrim, stopRegex = /\S/) { const buffer = editor.getBuffer(); - let {start, end} = rangeToTrim; - buffer.scanInRange(stopRegex, rangeToTrim, ({range, stop}) => { + let { + start, + end + } = rangeToTrim; + buffer.scanInRange(stopRegex, rangeToTrim, ({ + range, + stop + }) => { start = range.start; stop(); }); - buffer.backwardsScanInRange(stopRegex, rangeToTrim, ({range, stop}) => { + buffer.backwardsScanInRange(stopRegex, rangeToTrim, ({ + range, + stop + }) => { end = range.end; stop(); }); - return new Range(start, end); + return new _atom.Range(start, end); } -function getSingleWordAtPosition( - editor: atom$TextEditor, - position: atom$Point, -): ?string { - const match = wordAtPosition(editor, position); - // We should only receive a single identifier from a single point. +function getSingleWordAtPosition(editor, position) { + const match = wordAtPosition(editor, position); // We should only receive a single identifier from a single point. + if (match == null || match.wordMatch.length !== 1) { return null; } return match.wordMatch[0]; } - /** * Gets the word being right-clicked on in a MouseEvent. A good use case for * this is performing an action on a word from a context menu. @@ -98,21 +118,23 @@ function getSingleWordAtPosition( * from * @param event the MouseEvent containing the screen position of the click */ -export function getWordFromMouseEvent( - editor: atom$TextEditor, - event: MouseEvent, -): ?string { + + +function getWordFromMouseEvent(editor, event) { // We can't immediately get the identifier right-clicked on from // the MouseEvent. Using its target element content would work in // some cases but wouldn't work if there was additional content // in the same element, such as in a comment. const component = editor.getElement().component; - invariant(component); - // This solution doesn't feel ideal but it is the way hyperclick does it. + + if (!component) { + throw new Error("Invariant violation: \"component\""); + } // This solution doesn't feel ideal but it is the way hyperclick does it. + + const point = component.screenPositionForMouseEvent(event); return getSingleWordAtPosition(editor, point); } - /** * Attempts to get a word from the last selection or cursor. A good use case for * this is performing an action on an 'active' word after a command is triggered @@ -121,13 +143,16 @@ export function getWordFromMouseEvent( * @param editor the editor containing the 'active' word when the keybinding is * triggered */ -export function getWordFromCursorOrSelection(editor: atom$TextEditor): ?string { + + +function getWordFromCursorOrSelection(editor) { const selection = editor.getSelectedText(); + if (selection && selection.length > 0) { return selection; - } + } // There was no selection so we can go ahead and try the cursor position. + - // There was no selection so we can go ahead and try the cursor position. const point = editor.getCursorScreenPosition(); return getSingleWordAtPosition(editor, point); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/reduxLoggerMiddleware.js b/modules/nuclide-commons-atom/reduxLoggerMiddleware.js index 511a66e0..acc26e2a 100644 --- a/modules/nuclide-commons-atom/reduxLoggerMiddleware.js +++ b/modules/nuclide-commons-atom/reduxLoggerMiddleware.js @@ -1,3 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createLoggerMiddleware; + +function _reduxLogger() { + const data = require("redux-logger"); + + _reduxLogger = function () { + return data; + }; + + return data; +} + +function _featureConfig() { + const data = _interopRequireDefault(require("./feature-config")); + + _featureConfig = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,51 +35,17 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const enabledLoggers = _featureConfig().default.getWithDefaults('redux-debug-loggers', []); -import {createLogger} from 'redux-logger'; -import featureConfig from './feature-config'; - -/* -To turn on debug console logging for the feature you are debugging, add to your config.cson: - -"*": - "nuclide": - "redux-debug-loggers": [ - "" - ] -*/ - -type Store = { - getState: () => mixed, -}; - -type Action = { - type: string, -}; +const noopMiddleware = store => next => action => next(action); -type Dispatch = Action => Action; - -// More options can be found here if you wish to enable them: -// https://github.com/evgenyrodionov/redux-logger#options -type LoggerConfig = { - diff: boolean, -}; - -const enabledLoggers = featureConfig.getWithDefaults('redux-debug-loggers', []); - -const noopMiddleware = (store: Store) => (next: Dispatch) => (action: Action) => - next(action); - -export default function createLoggerMiddleware( - appName: string, - loggerConfig: ?LoggerConfig, -) { +function createLoggerMiddleware(appName, loggerConfig) { if (!enabledLoggers.includes(appName)) { return noopMiddleware; } - return createLogger(loggerConfig); -} + return (0, _reduxLogger().createLogger)(loggerConfig); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/test-helpers.js b/modules/nuclide-commons-atom/test-helpers.js index 85d9a7df..aa972226 100644 --- a/modules/nuclide-commons-atom/test-helpers.js +++ b/modules/nuclide-commons-atom/test-helpers.js @@ -1,3 +1,11 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.jasmineAttachWorkspace = jasmineAttachWorkspace; +exports.attachWorkspace = attachWorkspace; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,56 +14,63 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import invariant from 'assert'; - // Attach the Atom workspace to the DOM, and give it a reasonable size. // This is important for tests that touch the text editor in 1.19+, as they'll have a height of 0 // unless properly attached with a valid viewport. // NOTE: for Jest tests use `attachWorkspace()` function from this file -export function jasmineAttachWorkspace(): void { - jasmine.attachToDOM(atom.views.getView(atom.workspace)); +function jasmineAttachWorkspace() { + jasmine.attachToDOM(atom.views.getView(atom.workspace)); // Set the testing window dimensions (smallish, yet realistic). - // Set the testing window dimensions (smallish, yet realistic). const styleCSS = ` height: 600px; width: 1000px; `; const content = document.querySelector('#jasmine-content'); - invariant(content != null); - content.setAttribute('style', styleCSS); - // Unset the 'top' attribute of the spec reporter to make the full window visible. + if (!(content != null)) { + throw new Error("Invariant violation: \"content != null\""); + } + + content.setAttribute('style', styleCSS); // Unset the 'top' attribute of the spec reporter to make the full window visible. // This is purely for developer convenience when running specs in a visible window. + const specReporter = document.querySelector('.spec-reporter-container'); + if (specReporter != null) { specReporter.setAttribute('style', 'top: inherit'); } } -export function attachWorkspace(): void { +function attachWorkspace() { const container = document.createElement('div'); container.id = 'test-container'; - invariant(document.body); + + if (!document.body) { + throw new Error("Invariant violation: \"document.body\""); + } + document.body.appendChild(container); - container.appendChild(atom.views.getView(atom.workspace)); + container.appendChild(atom.views.getView(atom.workspace)); // Set the testing window dimensions (smallish, yet realistic). - // Set the testing window dimensions (smallish, yet realistic). const styleCSS = ` height: 600px; width: 1000px; `; const content = document.querySelector('#test-container'); - invariant(content != null); - content.setAttribute('style', styleCSS); - // Unset the 'top' attribute of the spec reporter to make the full window visible. + if (!(content != null)) { + throw new Error("Invariant violation: \"content != null\""); + } + + content.setAttribute('style', styleCSS); // Unset the 'top' attribute of the spec reporter to make the full window visible. // This is purely for developer convenience when running specs in a visible window. + const specReporter = document.querySelector('.spec-reporter-container'); + if (specReporter != null) { specReporter.setAttribute('style', 'top: inherit'); } -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/text-edit-diff.js b/modules/nuclide-commons-atom/text-edit-diff.js index 95d9a7e3..41d216c4 100644 --- a/modules/nuclide-commons-atom/text-edit-diff.js +++ b/modules/nuclide-commons-atom/text-edit-diff.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.toUnifiedDiff = toUnifiedDiff; + +var _atom = require("atom"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,108 +15,79 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {TextEdit} from './text-edit'; +function toUnifiedDiff(filename, buffer, edits, contextRows = 1) { + const hunks = getHunks(buffer, edits, contextRows); + return [`--- ${filename}`, `+++ ${filename}`].concat(mapHunkToString(buffer, hunks, contextRows)).join('\n'); +} -import invariant from 'assert'; -import {Range} from 'atom'; +function getHunks(buffer, edits, contextRows) { + return edits.sort((e1, e2) => e1.oldRange.compare(e2.oldRange)).reduce((mergedEdits, nextEdit) => { + const edit = mergedEdits[mergedEdits.length - 1]; -type Hunk = { - rows: Array, - newLines: Array, -}; + if (edit && nextEdit.oldRange.start.row <= edit.oldRange.end.row + contextRows) { + mergedEdits[mergedEdits.length - 1] = mergeEdit(buffer, edit, nextEdit); + } else { + mergedEdits.push(nextEdit); + } -export function toUnifiedDiff( - filename: string, - buffer: atom$TextBuffer, - edits: Array, - contextRows: number = 1, -): string { - const hunks: Array = getHunks(buffer, edits, contextRows); - return [`--- ${filename}`, `+++ ${filename}`] - .concat(mapHunkToString(buffer, hunks, contextRows)) - .join('\n'); + return mergedEdits; + }, []).map(edit => { + const oldRange = edit.oldRange; + const rows = oldRange.getRows(); + const newText = buffer.lineForRow(rows[0]).substring(0, oldRange.start.column) + edit.newText + buffer.lineForRow(rows[rows.length - 1]).substring(oldRange.end.column); + const newLines = newText.split(/\r\n|\r|\n/); + return { + rows, + newLines + }; + }); } -function getHunks( - buffer: atom$TextBuffer, - edits: Array, - contextRows: number, -): Array { - return edits - .sort((e1, e2) => e1.oldRange.compare(e2.oldRange)) - .reduce((mergedEdits: Array, nextEdit: TextEdit) => { - const edit = mergedEdits[mergedEdits.length - 1]; - if ( - edit && - nextEdit.oldRange.start.row <= edit.oldRange.end.row + contextRows - ) { - mergedEdits[mergedEdits.length - 1] = mergeEdit(buffer, edit, nextEdit); - } else { - mergedEdits.push(nextEdit); - } - return mergedEdits; - }, []) - .map((edit: TextEdit) => { - const oldRange = edit.oldRange; - const rows = oldRange.getRows(); - const newText = - buffer.lineForRow(rows[0]).substring(0, oldRange.start.column) + - edit.newText + - buffer.lineForRow(rows[rows.length - 1]).substring(oldRange.end.column); - const newLines = newText.split(/\r\n|\r|\n/); - return {rows, newLines}; - }); -} +function mergeEdit(buffer, e1, e2) { + if (!e1.oldRange.end.isLessThanOrEqual(e2.oldRange.start)) { + throw new Error("Invariant violation: \"e1.oldRange.end.isLessThanOrEqual(e2.oldRange.start)\""); + } -function mergeEdit( - buffer: atom$TextBuffer, - e1: TextEdit, - e2: TextEdit, -): TextEdit { - invariant(e1.oldRange.end.isLessThanOrEqual(e2.oldRange.start)); const mergedEdit = {}; - mergedEdit.newText = - e1.newText + - buffer.getTextInRange(new Range(e1.oldRange.end, e2.oldRange.start)) + - e2.newText; + mergedEdit.newText = e1.newText + buffer.getTextInRange(new _atom.Range(e1.oldRange.end, e2.oldRange.start)) + e2.newText; mergedEdit.oldRange = e1.oldRange.union(e2.oldRange); return mergedEdit; } -function mapHunkToString( - buffer: atom$TextBuffer, - hunks: Array, - contextRows: number, -): Array { +function mapHunkToString(buffer, hunks, contextRows) { // This requires storing some state across the map() to compute the row // numbers correctly. let newRowOffset = 0; - return hunks.map((hunk: Hunk) => { - const {rows, newLines} = hunk; + return hunks.map(hunk => { + const { + rows, + newLines + } = hunk; const beforeRows = []; const afterRows = []; + for (let i = 1; i <= contextRows; i++) { const beforeRow = rows[0] - i; const afterRow = rows[rows.length - 1] + i; + if (beforeRow >= 0) { beforeRows.unshift(beforeRow); } + if (afterRow <= buffer.getLastRow()) { afterRows.push(afterRow); } } + const oldBeginRow = rows[0] - beforeRows.length + 1; const oldRowLength = rows.length + beforeRows.length + afterRows.length; const newBeginRow = oldBeginRow + newRowOffset; const newRowLength = newLines.length + beforeRows.length + afterRows.length; - const parts = []; - parts.push( - `@@ -${oldBeginRow},${oldRowLength} +${newBeginRow},${newRowLength} @@`, - ); + parts.push(`@@ -${oldBeginRow},${oldRowLength} +${newBeginRow},${newRowLength} @@`); beforeRows.forEach(row => { parts.push(' ' + buffer.lineForRow(row)); }); @@ -123,4 +103,4 @@ function mapHunkToString( newRowOffset += newLines.length - rows.length; return parts.join('\n'); }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/text-edit.js b/modules/nuclide-commons-atom/text-edit.js index e6cfa7a8..c27becd9 100644 --- a/modules/nuclide-commons-atom/text-edit.js +++ b/modules/nuclide-commons-atom/text-edit.js @@ -1,3 +1,42 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.applyTextEditsForMultipleFiles = applyTextEditsForMultipleFiles; +exports.applyTextEdits = applyTextEdits; +exports.applyTextEditsToBuffer = applyTextEditsToBuffer; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _textEditor() { + const data = require("./text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + +function _goToLocation() { + const data = require("./go-to-location"); + + _goToLocation = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,25 +45,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import invariant from 'assert'; -import {getLogger} from 'log4js'; - -import {existingEditorForUri} from './text-editor'; -import {goToLocation} from './go-to-location'; - -export type TextEdit = { - oldRange: atom$Range, - newText: string, - // If included, this will be used to verify that the edit still applies cleanly. - oldText?: string, -}; - /** * Attempts to apply the given patches for multiple files. Accepts a Map as input * with file paths as keys and a corresponding array of TextEdits as values. @@ -42,19 +66,17 @@ export type TextEdit = { * Returns true if the application was successful, otherwise false. If any of * the changes fail, for ANY file, then none of the changes are applied. */ -export async function applyTextEditsForMultipleFiles( - changes: Map>, -): Promise { - const paths = Array.from(changes.keys()); - - // NOTE: There is a race here. If the file contents change while the +async function applyTextEditsForMultipleFiles(changes) { + const paths = Array.from(changes.keys()); // NOTE: There is a race here. If the file contents change while the // editors are being opened, then the ranges of the TextEdits will be off. // However, currently this is only used to applyEdits to open files. - const editors = await Promise.all( - paths.map(async path => goToLocation(path)), - ); + + const editors = await Promise.all(paths.map(async path => (0, _goToLocation().goToLocation)(path))); const checkpoints = editors.map(editor => { - invariant(editor != null); + if (!(editor != null)) { + throw new Error("Invariant violation: \"editor != null\""); + } + const buffer = editor.getBuffer(); return [buffer, buffer.createCheckpoint()]; }); @@ -62,15 +84,16 @@ export async function applyTextEditsForMultipleFiles( const edits = changes.get(path); return successSoFar && edits != null && applyTextEdits(path, ...edits); }, true); + if (!allOkay) { checkpoints.forEach(([buffer, checkPoint]) => { buffer.revertToCheckpoint(checkPoint); return false; }); } + return allOkay; } - /** * Attempts to apply the given patches to the given file. * @@ -83,51 +106,48 @@ export async function applyTextEditsForMultipleFiles( * Returns true if the application was successful, otherwise false (e.g. if the oldText did not * match). */ -export function applyTextEdits( - path: NuclideUri, - ...edits: Array -): boolean { + + +function applyTextEdits(path, ...edits) { const sortedEdits = sortEdits(edits); - const editor = existingEditorForUri(path); - invariant(editor != null); + const editor = (0, _textEditor().existingEditorForUri)(path); + + if (!(editor != null)) { + throw new Error("Invariant violation: \"editor != null\""); + } + return applySortedTextEditsToBuffer(editor.getBuffer(), sortedEdits); } -export function applyTextEditsToBuffer( - buffer: atom$TextBuffer, - edits: Array, -): boolean { +function applyTextEditsToBuffer(buffer, edits) { return applySortedTextEditsToBuffer(buffer, sortEdits(edits)); } -function applySortedTextEditsToBuffer( - buffer: atom$TextBuffer, - edits: Array, -): boolean { +function applySortedTextEditsToBuffer(buffer, edits) { // For every edit, the start of its range will be after the end of the // previous edit's range. if (editsOverlap(edits)) { - getLogger('text-edit').warn( - 'applyTextEdits was called with overlapping edits.', - ); + (0, _log4js().getLogger)('text-edit').warn('applyTextEdits was called with overlapping edits.'); return false; - } - // Special-case whole-buffer changes to minimize disruption. + } // Special-case whole-buffer changes to minimize disruption. + + if (edits.length === 1 && edits[0].oldRange.isEqual(buffer.getRange())) { if (edits[0].oldText != null && edits[0].oldText !== buffer.getText()) { return false; } + buffer.setTextViaDiff(edits[0].newText); return true; } - const checkpoint = buffer.createCheckpoint(); - - // Iterate through in reverse order. Edits earlier in the file can move around text later in the + const checkpoint = buffer.createCheckpoint(); // Iterate through in reverse order. Edits earlier in the file can move around text later in the // file, so to avoid conflicts edits should be applied last first. + for (let i = edits.length - 1; i >= 0; i--) { const edit = edits[i]; const success = applyToBuffer(buffer, edit); + if (!success) { buffer.revertToCheckpoint(checkpoint); return false; @@ -138,49 +158,42 @@ function applySortedTextEditsToBuffer( return true; } -function applyToBuffer(buffer: atom$TextBuffer, edit: TextEdit): boolean { +function applyToBuffer(buffer, edit) { if (edit.oldRange.start.row === edit.oldRange.end.row) { // A little extra validation when the old range spans only one line. In particular, this helps // when the old range is empty so there is no old text for us to compare against. We can at // least abort if the line isn't long enough. const lineLength = buffer.lineLengthForRow(edit.oldRange.start.row); + if (edit.oldRange.end.column > lineLength) { return false; } } + if (edit.oldText != null) { const currentText = buffer.getTextInRange(edit.oldRange); + if (currentText !== edit.oldText) { return false; } } + buffer.setTextInRange(edit.oldRange, edit.newText); return true; -} +} // Returns whether an array of sorted TextEdits contain an overlapping range. + -// Returns whether an array of sorted TextEdits contain an overlapping range. -function editsOverlap(sortedEdits: Array): boolean { +function editsOverlap(sortedEdits) { for (let i = 0; i < sortedEdits.length - 1; i++) { - if ( - sortedEdits[i].oldRange.end.isGreaterThan( - sortedEdits[i + 1].oldRange.start, - ) - ) { + if (sortedEdits[i].oldRange.end.isGreaterThan(sortedEdits[i + 1].oldRange.start)) { return true; } } + return false; } -function sortEdits(edits: Array): Array { +function sortEdits(edits) { // stable sort (preserve order of edits starting in the same location) - return edits - .map((edit, i) => [edit, i]) - .sort( - ([e1, i1], [e2, i2]) => - e1.oldRange.start.compare(e2.oldRange.start) || - e1.oldRange.end.compare(e2.oldRange.end) || - i1 - i2, - ) - .map(([edit]) => edit); -} + return edits.map((edit, i) => [edit, i]).sort(([e1, i1], [e2, i2]) => e1.oldRange.start.compare(e2.oldRange.start) || e1.oldRange.end.compare(e2.oldRange.end) || i1 - i2).map(([edit]) => edit); +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/text-editor.js b/modules/nuclide-commons-atom/text-editor.js index 9b4a5357..d36dd123 100644 --- a/modules/nuclide-commons-atom/text-editor.js +++ b/modules/nuclide-commons-atom/text-editor.js @@ -1,3 +1,47 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.existingEditorForUri = existingEditorForUri; +exports.existingEditorForBuffer = existingEditorForBuffer; +exports.getViewOfEditor = getViewOfEditor; +exports.getScrollTop = getScrollTop; +exports.setScrollTop = setScrollTop; +exports.setPositionAndScroll = setPositionAndScroll; +exports.getCursorPositions = getCursorPositions; +exports.observeEditorDestroy = observeEditorDestroy; +exports.enforceReadOnlyEditor = enforceReadOnlyEditor; +exports.enforceSoftWrap = enforceSoftWrap; +exports.isValidTextEditor = isValidTextEditor; +exports.centerScrollToBufferLine = centerScrollToBufferLine; + +var _atom = require("atom"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,24 +50,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import invariant from 'assert'; -import {TextEditor} from 'atom'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Observable} from 'rxjs'; - -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; - /** * Returns a text editor that has the given path open, or null if none exists. If there are multiple * text editors for this path, one is chosen arbitrarily. */ -export function existingEditorForUri(path: NuclideUri): ?atom$TextEditor { +function existingEditorForUri(path) { // This isn't ideal but realistically iterating through even a few hundred editors shouldn't be a // real problem. And if you have more than a few hundred you probably have bigger problems. for (const editor of atom.workspace.getTextEditors()) { @@ -34,14 +69,13 @@ export function existingEditorForUri(path: NuclideUri): ?atom$TextEditor { return null; } - /** * Returns a text editor that has the given buffer open, or null if none exists. If there are * multiple text editors for this buffer, one is chosen arbitrarily. */ -export function existingEditorForBuffer( - buffer: atom$TextBuffer, -): ?atom$TextEditor { + + +function existingEditorForBuffer(buffer) { // This isn't ideal but realistically iterating through even a few hundred editors shouldn't be a // real problem. And if you have more than a few hundred you probably have bigger problems. for (const editor of atom.workspace.getTextEditors()) { @@ -53,20 +87,17 @@ export function existingEditorForBuffer( return null; } -export function getViewOfEditor( - editor: atom$TextEditor, -): atom$TextEditorElement { +function getViewOfEditor(editor) { return atom.views.getView(editor); } -export function getScrollTop(editor: atom$TextEditor): number { +function getScrollTop(editor) { return getViewOfEditor(editor).getScrollTop(); } -export function setScrollTop(editor: atom$TextEditor, scrollTop: number): void { +function setScrollTop(editor, scrollTop) { getViewOfEditor(editor).setScrollTop(scrollTop); } - /** * Does a best effort to set an editor pane to a given cursor position & scroll. * Does not ensure that the current cursor position is visible. @@ -74,79 +105,62 @@ export function setScrollTop(editor: atom$TextEditor, scrollTop: number): void { * Can be used with editor.getCursorBufferPosition() & getScrollTop() to restore * an editors cursor and scroll. */ -export function setPositionAndScroll( - editor: atom$TextEditor, - position: atom$Point, - scrollTop: number, -): void { - editor.setCursorBufferPosition(position, {autoscroll: false}); + + +function setPositionAndScroll(editor, position, scrollTop) { + editor.setCursorBufferPosition(position, { + autoscroll: false + }); setScrollTop(editor, scrollTop); } -export function getCursorPositions( - editor: atom$TextEditor, -): Observable { - return Observable.defer(() => { +function getCursorPositions(editor) { + return _RxMin.Observable.defer(() => { // This will behave strangely in the face of multiple cursors. Consider supporting multiple // cursors in the future. const cursor = editor.getCursors()[0]; - invariant(cursor != null); - return Observable.merge( - Observable.of(cursor.getBufferPosition()), - observableFromSubscribeFunction( - cursor.onDidChangePosition.bind(cursor), - ).map(event => event.newBufferPosition), - ); - }); -} -export function observeEditorDestroy( - editor: atom$TextEditor, -): Observable { - return observableFromSubscribeFunction(editor.onDidDestroy.bind(editor)) - .map(event => editor) - .take(1); + if (!(cursor != null)) { + throw new Error("Invariant violation: \"cursor != null\""); + } + + return _RxMin.Observable.merge(_RxMin.Observable.of(cursor.getBufferPosition()), (0, _event().observableFromSubscribeFunction)(cursor.onDidChangePosition.bind(cursor)).map(event => event.newBufferPosition)); + }); } -// As of the introduction of atom.workspace.buildTextEditor(), it is no longer possible to +function observeEditorDestroy(editor) { + return (0, _event().observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor)).map(event => editor).take(1); +} // As of the introduction of atom.workspace.buildTextEditor(), it is no longer possible to // subclass TextEditor to create a ReadOnlyTextEditor. Instead, the way to achieve this effect // is to create an ordinary TextEditor and then override any methods that would allow it to // change its contents. // TODO: https://github.com/atom/atom/issues/9237. -export function enforceReadOnlyEditor( - textEditor: atom$TextEditor, - readOnlyExceptions?: Array = ['append', 'setText'], -): IDisposable { + + +function enforceReadOnlyEditor(textEditor, readOnlyExceptions = ['append', 'setText']) { // Cancel insert events to prevent typing in the text editor and disallow editing (read-only). const willInsertTextDisposable = textEditor.onWillInsertText(event => { event.cancel(); }); - - return new UniversalDisposable( - willInsertTextDisposable, - // `setText` & `append` are the only exceptions that's used to set the read-only text. - enforceReadOnlyBuffer(textEditor.getBuffer(), readOnlyExceptions), - ); + return new (_UniversalDisposable().default)(willInsertTextDisposable, // `setText` & `append` are the only exceptions that's used to set the read-only text. + enforceReadOnlyBuffer(textEditor.getBuffer(), readOnlyExceptions)); } -function enforceReadOnlyBuffer( - textBuffer: atom$TextBuffer, - readOnlyExceptions?: Array = [], -): IDisposable { - const noop = () => {}; - // All user edits use `transact` - so, mocking this will effectively make the editor read-only. +function enforceReadOnlyBuffer(textBuffer, readOnlyExceptions = []) { + const noop = () => {}; // All user edits use `transact` - so, mocking this will effectively make the editor read-only. + + const originalApplyChange = textBuffer.applyChange; const originalReadOnlyExceptionFunctions = {}; textBuffer.applyChange = noop; - readOnlyExceptions.forEach(passReadOnlyException); - function passReadOnlyException(functionName: string) { - const buffer: any = textBuffer; + function passReadOnlyException(functionName) { + const buffer = textBuffer; const originalFunction = buffer[functionName]; originalReadOnlyExceptionFunctions[functionName] = originalFunction; - buffer[functionName] = function() { + buffer[functionName] = function () { textBuffer.applyChange = originalApplyChange; const result = originalFunction.apply(textBuffer, arguments); textBuffer.applyChange = noop; @@ -154,25 +168,17 @@ function enforceReadOnlyBuffer( }; } - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { textBuffer.applyChange = originalApplyChange; - - const buffer: any = textBuffer; - readOnlyExceptions.forEach( - functionName => - (buffer[functionName] = - originalReadOnlyExceptionFunctions[functionName]), - ); + const buffer = textBuffer; + readOnlyExceptions.forEach(functionName => buffer[functionName] = originalReadOnlyExceptionFunctions[functionName]); }); -} - -// Turn off soft wrap setting for these editors so diffs properly align. +} // Turn off soft wrap setting for these editors so diffs properly align. // Some text editor register sometimes override the set soft wrapping // after mounting an editor to the workspace - here, that's watched and reset to `false`. -export function enforceSoftWrap( - editor: atom$TextEditor, - enforcedSoftWrap: boolean, -): IDisposable { + + +function enforceSoftWrap(editor, enforcedSoftWrap) { editor.setSoftWrapped(enforcedSoftWrap); return editor.onDidChangeSoftWrapped(softWrapped => { if (softWrapped !== enforcedSoftWrap) { @@ -185,37 +191,27 @@ export function enforceSoftWrap( } }); } - /** * Checks if an object (typically an Atom pane) is a TextEditor. * Could be replaced with atom.workspace.isValidTextEditor, * but Flow doesn't support %checks in methods yet. */ -export function isValidTextEditor(item: mixed): boolean %checks { - return item instanceof TextEditor; + + +function isValidTextEditor(item) { + return item instanceof _atom.TextEditor; } -export function centerScrollToBufferLine( - textEditorElement: atom$TextEditorElement, - bufferLineNumber: number, -): void { +function centerScrollToBufferLine(textEditorElement, bufferLineNumber) { const textEditor = textEditorElement.getModel(); - const pixelPositionTop = textEditorElement.pixelPositionForBufferPosition([ - bufferLineNumber, - 0, - ]).top; - // Manually calculate the scroll location, instead of using + const pixelPositionTop = textEditorElement.pixelPositionForBufferPosition([bufferLineNumber, 0]).top; // Manually calculate the scroll location, instead of using // `textEditor.scrollToBufferPosition([lineNumber, 0], {center: true})` // because that API to wouldn't center the line if it was in the visible screen range. - const scrollTop = - pixelPositionTop + - textEditor.getLineHeightInPixels() / 2 - - textEditorElement.clientHeight / 2; - textEditorElement.setScrollTop(Math.max(scrollTop, 1)); + const scrollTop = pixelPositionTop + textEditor.getLineHeightInPixels() / 2 - textEditorElement.clientHeight / 2; + textEditorElement.setScrollTop(Math.max(scrollTop, 1)); textEditorElement.focus(); - textEditor.setCursorBufferPosition([bufferLineNumber, 0], { - autoscroll: false, + autoscroll: false }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-atom/text-event.js b/modules/nuclide-commons-atom/text-event.js index b2405a88..53056aa7 100644 --- a/modules/nuclide-commons-atom/text-event.js +++ b/modules/nuclide-commons-atom/text-event.js @@ -1,3 +1,45 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.observeTextEditorEvents = observeTextEditorEvents; +exports.__TEST__ = exports.TextEventDispatcher = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _debounce() { + const data = _interopRequireDefault(require("../nuclide-commons/debounce")); + + _debounce = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("../nuclide-commons/event"); + + _event = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,116 +48,99 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import invariant from 'assert'; -import {Observable} from 'rxjs'; -import debounce from 'nuclide-commons/debounce'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -type EventCallback = (editor: TextEditor) => mixed; - -type Event = 'did-reload' | 'did-change' | 'did-save' | 'did-open'; - // A reload changes the text in the buffer, so it should trigger a refresh. -const FILE_CHANGE_EVENTS = ['did-change', 'did-reload', 'did-open']; - -// A reload basically indicates that an external program saved the file, so +const FILE_CHANGE_EVENTS = ['did-change', 'did-reload', 'did-open']; // A reload basically indicates that an external program saved the file, so // it should trigger a refresh. -const FILE_SAVE_EVENTS = ['did-save', 'did-reload', 'did-open']; +const FILE_SAVE_EVENTS = ['did-save', 'did-reload', 'did-open']; /** * Stores callbacks keyed on grammar and event, to allow for easy retrieval when * we need to dispatch to all callbacks registered for a given (grammar, event) * pair. */ -class TextCallbackContainer { + +class TextCallbackContainer { // grammar -> event -> callback // invariant: no empty maps or sets (they should be removed instead) - _callbacks: Map mixed>>>; - // event -> callback // invariant: no keys mapping to empty sets (they should be removed instead) - _allGrammarCallbacks: Map mixed>>; - constructor() { this._callbacks = new Map(); this._allGrammarCallbacks = new Map(); } - getCallbacks( - grammar: string, - event: Event, - ): Set<(arg: CallbackArg) => mixed> { + getCallbacks(grammar, event) { const eventMap = this._callbacks.get(grammar); + const callbacksForGrammar = this._getCallbacksFromEventMap(eventMap, event); - const callbacksForAll = this._getCallbacksFromEventMap( - this._allGrammarCallbacks, - event, - ); + + const callbacksForAll = this._getCallbacksFromEventMap(this._allGrammarCallbacks, event); + const resultSet = new Set(); + const add = callback => { resultSet.add(callback); }; + callbacksForGrammar.forEach(add); callbacksForAll.forEach(add); return resultSet; } - isEmpty(): boolean { + isEmpty() { return this._callbacks.size === 0 && this._allGrammarCallbacks.size === 0; } - _getCallbacksFromEventMap( - eventMap: ?Map mixed>>, - event: Event, - ): Set<(arg: CallbackArg) => mixed> { + _getCallbacksFromEventMap(eventMap, event) { if (!eventMap) { return new Set(); } + const callbackSet = eventMap.get(event); + if (!callbackSet) { return new Set(); } + return callbackSet; } - addCallback( - grammarScopes: Iterable | 'all', - events: Iterable, - callback: (arg: CallbackArg) => mixed, - ): void { + addCallback(grammarScopes, events, callback) { if (grammarScopes === 'all') { this._addToEventMap(this._allGrammarCallbacks, events, callback); } else { for (const grammarScope of grammarScopes) { let eventMap = this._callbacks.get(grammarScope); + if (!eventMap) { eventMap = new Map(); + this._callbacks.set(grammarScope, eventMap); } + this._addToEventMap(eventMap, events, callback); } } - } - - // remove the callbacks, maintaining the invariant that there should be no + } // remove the callbacks, maintaining the invariant that there should be no // empty maps or sets in this._callbacks - removeCallback( - grammarScopes: Iterable | 'all', - events: Iterable, - callback: (arg: CallbackArg) => mixed, - ): void { + + + removeCallback(grammarScopes, events, callback) { if (grammarScopes === 'all') { this._removeFromEventMap(this._allGrammarCallbacks, events, callback); } else { for (const grammarScope of grammarScopes) { const eventMap = this._callbacks.get(grammarScope); - invariant(eventMap); + + if (!eventMap) { + throw new Error("Invariant violation: \"eventMap\""); + } + this._removeFromEventMap(eventMap, events, callback); + if (eventMap.size === 0) { this._callbacks.delete(grammarScope); } @@ -123,37 +148,36 @@ class TextCallbackContainer { } } - _addToEventMap( - eventMap: Map mixed>>, - events: Iterable, - callback: (arg: CallbackArg) => mixed, - ): void { + _addToEventMap(eventMap, events, callback) { for (const event of events) { let callbackSet = eventMap.get(event); + if (!callbackSet) { callbackSet = new Set(); eventMap.set(event, callbackSet); } + callbackSet.add(callback); } } - _removeFromEventMap( - eventMap: Map mixed>>, - events: Iterable, - callback: (arg: CallbackArg) => mixed, - ): void { + _removeFromEventMap(eventMap, events, callback) { for (const event of events) { const callbackSet = eventMap.get(event); - invariant(callbackSet); + + if (!callbackSet) { + throw new Error("Invariant violation: \"callbackSet\""); + } + callbackSet.delete(callback); + if (callbackSet.size === 0) { eventMap.delete(event); } } } -} +} /** * Meant to make it simple and easy for a DiagnosticProvider to subscribe to * relevant events. Currently provides two methods, onFileChange and onFileSave. @@ -171,41 +195,29 @@ class TextCallbackContainer { * from Atom's text events. * */ -export class TextEventDispatcher { - _callbackContainer: TextCallbackContainer; - _editorListenerDisposable: ?UniversalDisposable; - - _pendingEvents: WeakMap>; +class TextEventDispatcher { constructor() { this._callbackContainer = new TextCallbackContainer(); this._editorListenerDisposable = null; this._pendingEvents = new WeakMap(); } - _onEvents( - grammarScopes: Iterable | 'all', - events: Iterable, - callback: EventCallback, - ) { + _onEvents(grammarScopes, events, callback) { if (this._callbackContainer.isEmpty()) { this._registerEditorListeners(); - } - // Sometimes these events get triggered several times in succession + } // Sometimes these events get triggered several times in succession // (particularly on startup). - const debouncedCallback = debounce(callback, 50, true); - this._callbackContainer.addCallback( - grammarScopes, - events, - debouncedCallback, - ); - const disposables = new UniversalDisposable(() => { - this._callbackContainer.removeCallback( - grammarScopes, - events, - debouncedCallback, - ); + + + const debouncedCallback = (0, _debounce().default)(callback, 50, true); + + this._callbackContainer.addCallback(grammarScopes, events, debouncedCallback); + + const disposables = new (_UniversalDisposable().default)(() => { + this._callbackContainer.removeCallback(grammarScopes, events, debouncedCallback); + if (this._callbackContainer.isEmpty()) { this._deregisterEditorListeners(); } @@ -213,147 +225,135 @@ export class TextEventDispatcher { return disposables; } - onFileChange( - grammarScopes: Iterable, - callback: EventCallback, - ): IDisposable { + onFileChange(grammarScopes, callback) { return this._onEvents(grammarScopes, FILE_CHANGE_EVENTS, callback); } - onAnyFileChange(callback: EventCallback): IDisposable { + onAnyFileChange(callback) { return this._onEvents('all', FILE_CHANGE_EVENTS, callback); } - onFileSave( - grammarScopes: Iterable, - callback: EventCallback, - ): IDisposable { + onFileSave(grammarScopes, callback) { return this._onEvents(grammarScopes, FILE_SAVE_EVENTS, callback); } - onAnyFileSave(callback: EventCallback): IDisposable { + onAnyFileSave(callback) { return this._onEvents('all', FILE_SAVE_EVENTS, callback); } - _registerEditorListeners(): void { + _registerEditorListeners() { if (!this._editorListenerDisposable) { - this._editorListenerDisposable = new UniversalDisposable(); - } - - // Whenever the active pane item changes, we check to see if there are any + this._editorListenerDisposable = new (_UniversalDisposable().default)(); + } // Whenever the active pane item changes, we check to see if there are any // pending events for the newly-focused TextEditor. - this._getEditorListenerDisposable().add( - atom.workspace.onDidChangeActivePaneItem(() => { - const currentEditor = atom.workspace.getActiveTextEditor(); - if (currentEditor) { - const pendingEvents = this._pendingEvents.get( - currentEditor.getBuffer(), - ); - if (pendingEvents) { - for (const event of pendingEvents) { - this._dispatchEvents(currentEditor, event); - } - this._pendingEvents.delete(currentEditor.getBuffer()); + + + this._getEditorListenerDisposable().add(atom.workspace.onDidChangeActivePaneItem(() => { + const currentEditor = atom.workspace.getActiveTextEditor(); + + if (currentEditor) { + const pendingEvents = this._pendingEvents.get(currentEditor.getBuffer()); + + if (pendingEvents) { + for (const event of pendingEvents) { + this._dispatchEvents(currentEditor, event); } + + this._pendingEvents.delete(currentEditor.getBuffer()); } - }), - ); - - this._getEditorListenerDisposable().add( - atom.workspace.observeTextEditors(editor => { - const buffer = editor.getBuffer(); - const makeDispatch = (event: Event) => { - return () => { - this._dispatchEvents(editor, event); - }; + } + })); + + this._getEditorListenerDisposable().add(atom.workspace.observeTextEditors(editor => { + const buffer = editor.getBuffer(); + + const makeDispatch = event => { + return () => { + this._dispatchEvents(editor, event); }; - this._getEditorListenerDisposable().addUntilDestroyed( - editor, - buffer.onDidStopChanging(makeDispatch('did-change')), - buffer.onDidSave(makeDispatch('did-save')), - buffer.onDidReload(makeDispatch('did-reload')), - ); - // During reload, many text editors are opened simultaneously. - // Due to the debounce on the event callback, this means that many editors never receive - // a 'did-open' event. To work around this, defer editor open events so that simultaneous - // open events are properly registered as pending. - setImmediate(() => this._dispatchEvents(editor, 'did-open')); - }), - ); + }; + + this._getEditorListenerDisposable().addUntilDestroyed(editor, buffer.onDidStopChanging(makeDispatch('did-change')), buffer.onDidSave(makeDispatch('did-save')), buffer.onDidReload(makeDispatch('did-reload'))); // During reload, many text editors are opened simultaneously. + // Due to the debounce on the event callback, this means that many editors never receive + // a 'did-open' event. To work around this, defer editor open events so that simultaneous + // open events are properly registered as pending. + + + setImmediate(() => this._dispatchEvents(editor, 'did-open')); + })); } _deregisterEditorListeners() { if (this._editorListenerDisposable) { this._getEditorListenerDisposable().dispose(); + this._editorListenerDisposable = null; } } - _dispatchEvents(editor: TextEditor, event: Event): void { + _dispatchEvents(editor, event) { const currentEditor = atom.workspace.getActiveTextEditor(); + if (currentEditor && editor === currentEditor) { - const callbacks = this._callbackContainer.getCallbacks( - editor.getGrammar().scopeName, - event, - ); + const callbacks = this._callbackContainer.getCallbacks(editor.getGrammar().scopeName, event); + for (const callback of callbacks) { callback(editor); - } - // We want to avoid storing pending events if this event was generated by + } // We want to avoid storing pending events if this event was generated by // the same buffer as the current editor, to avoid duplicating events when // multiple panes have the same file open. - } else if ( - !currentEditor || - editor.getBuffer() !== currentEditor.getBuffer() - ) { + + } else if (!currentEditor || editor.getBuffer() !== currentEditor.getBuffer()) { // Trigger this event next time we switch to an editor with this buffer. const buffer = editor.getBuffer(); + let events = this._pendingEvents.get(buffer); + if (!events) { events = new Set(); + this._pendingEvents.set(buffer, events); } + events.add(event); } } - _getEditorListenerDisposable(): UniversalDisposable { + _getEditorListenerDisposable() { const disposable = this._editorListenerDisposable; - invariant(disposable, 'TextEventDispatcher disposable is not initialized'); + + if (!disposable) { + throw new Error('TextEventDispatcher disposable is not initialized'); + } + return disposable; } + } -export function observeTextEditorEvents( - grammarScopes: Iterable | 'all', - events: 'changes' | 'saves', -): Observable { - return Observable.defer(() => { +exports.TextEventDispatcher = TextEventDispatcher; + +function observeTextEditorEvents(grammarScopes, events) { + return _RxMin.Observable.defer(() => { const dispatcher = new TextEventDispatcher(); + if (events === 'changes') { if (grammarScopes === 'all') { - return observableFromSubscribeFunction(cb => - dispatcher.onAnyFileChange(cb), - ); + return (0, _event().observableFromSubscribeFunction)(cb => dispatcher.onAnyFileChange(cb)); } else { - return observableFromSubscribeFunction(cb => - dispatcher.onFileChange(grammarScopes, cb), - ); + return (0, _event().observableFromSubscribeFunction)(cb => dispatcher.onFileChange(grammarScopes, cb)); } } else { if (grammarScopes === 'all') { - return observableFromSubscribeFunction(cb => - dispatcher.onAnyFileSave(cb), - ); + return (0, _event().observableFromSubscribeFunction)(cb => dispatcher.onAnyFileSave(cb)); } else { - return observableFromSubscribeFunction(cb => - dispatcher.onFileSave(grammarScopes, cb), - ); + return (0, _event().observableFromSubscribeFunction)(cb => dispatcher.onFileSave(grammarScopes, cb)); } } }); } -export const __TEST__ = { - TextCallbackContainer, +const __TEST__ = { + TextCallbackContainer }; +exports.__TEST__ = __TEST__; \ No newline at end of file diff --git a/modules/nuclide-commons-atom/types.js b/modules/nuclide-commons-atom/types.js index 9705b7e8..9a390c31 100644 --- a/modules/nuclide-commons-atom/types.js +++ b/modules/nuclide-commons-atom/types.js @@ -1,15 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict - * @format - */ - -export type GatekeeperService = { - passesGK: (name: string, timeout?: number) => Promise, -}; +"use strict"; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Ansi.js b/modules/nuclide-commons-ui/Ansi.js index 9de9a590..7dcf569d 100644 --- a/modules/nuclide-commons-ui/Ansi.js +++ b/modules/nuclide-commons-ui/Ansi.js @@ -1,37 +1,59 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import * as React from 'react'; -import Anser from 'anser'; -import escapeCarriageReturn from 'escape-carriage'; +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _anser() { + const data = _interopRequireDefault(require("anser")); + + _anser = function () { + return data; + }; + + return data; +} + +function _escapeCarriage() { + const data = _interopRequireDefault(require("escape-carriage")); + + _escapeCarriage = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function ansiToJSON(input) { - return Anser.ansiToJson(escapeCarriageReturn(input), { + return _anser().default.ansiToJson((0, _escapeCarriage().default)(input), { json: true, - remove_empty: true, + remove_empty: true }); } function ansiJSONtoStyleBundle(ansiBundle) { const style = {}; + if (ansiBundle.bg) { style.backgroundColor = `rgb(${ansiBundle.bg})`; } + if (ansiBundle.fg) { style.color = `rgb(${ansiBundle.fg})`; } + return { content: ansiBundle.content, - style, + style }; } @@ -39,34 +61,36 @@ function ansiToInlineStyle(text) { return ansiToJSON(text).map(ansiJSONtoStyleBundle); } -type Props = { - children?: string, - renderSegment?: RenderSegmentProps => React.Node, -}; - -export type RenderSegmentProps = {key: string, style: Object, content: string}; - -function defaultRenderSegment({key, style, content}: RenderSegmentProps) { - return ( - - {content} - - ); +function defaultRenderSegment({ + key, + style, + content +}) { + return React.createElement("span", { + key: key, + style: style + }, content); } -export default class Ansi extends React.PureComponent { +class Ansi extends React.PureComponent { render() { - const { + const _this$props = this.props, + { children, - renderSegment = defaultRenderSegment, - ...passThroughProps - } = this.props; - return ( - - {ansiToInlineStyle(children).map(({style, content}, key) => - renderSegment({key: String(key), style, content}), - )} - - ); + renderSegment = defaultRenderSegment + } = _this$props, + passThroughProps = _objectWithoutProperties(_this$props, ["children", "renderSegment"]); + + return React.createElement("code", passThroughProps, ansiToInlineStyle(children).map(({ + style, + content + }, key) => renderSegment({ + key: String(key), + style, + content + }))); } + } + +exports.default = Ansi; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/AtomInput.js b/modules/nuclide-commons-ui/AtomInput.js index 34468550..c50fe717 100644 --- a/modules/nuclide-commons-ui/AtomInput.js +++ b/modules/nuclide-commons-ui/AtomInput.js @@ -1,3 +1,66 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.AtomInput = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _string() { + const data = require("../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("../nuclide-commons/observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _debounce() { + const data = _interopRequireDefault(require("../nuclide-commons/debounce")); + + _debounce = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,207 +69,139 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import * as React from 'react'; -import classNames from 'classnames'; -import invariant from 'assert'; - -import {maybeToString} from 'nuclide-commons/string'; -import {microtask} from 'nuclide-commons/observable'; -import debounce from 'nuclide-commons/debounce'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -type DefaultProps = { - disabled: boolean, - autofocus: boolean, - startSelected: boolean, - initialValue: string, - tabIndex: string, - onClick: (event: SyntheticMouseEvent<>) => mixed, - onDidChange: (text: string) => mixed, - onFocus: () => mixed, - onBlur: (blurEvent: Event) => mixed, - unstyled: boolean, - style: ?Object, -}; - -type Props = { - className?: string, - disabled: boolean, - autofocus: boolean, - startSelected: boolean, - startSelectedRange?: ?[number, number], - initialValue: string, - invalid?: boolean, - placeholderText?: string, - tabIndex: string, - onFocus: () => mixed, - onClick: (event: SyntheticMouseEvent<>) => mixed, - onDidChange: (text: string) => mixed, - onDidChangeSelectionRange?: (event: atom$ChangeSelectionRangeEvent) => mixed, - onConfirm?: (event?: atom$CustomEvent) => mixed, - onCancel?: (event?: atom$CustomEvent) => mixed, - onBlur: (blurEvent: Event) => mixed, - size?: 'xs' | 'sm' | 'lg', - unstyled: boolean, - width?: ?number, - // If the `value` prop is specified, then the component's displayed text is controlled by this - // prop. Otherwise its displayed text must be imperatively set on the instance. - value?: string, - style: ?Object, -}; - -type State = { - value: string, -}; - const BLUR_FOCUS_DEBOUNCE_DELAY = 100; - /** * An input field rendered as an . */ -export class AtomInput extends React.Component { - _disposables: ?UniversalDisposable; - _rootNode: ?HTMLElement; - _debouncedEditorBlur: (blurEvent: Event) => void; - _debouncedEditorFocus: () => void; - _isFocused: boolean; - - static defaultProps: DefaultProps = { - disabled: false, - autofocus: false, - startSelected: false, - initialValue: '', - tabIndex: '0', // Default to all components being in tab order - onClick: event => {}, - onDidChange: text => {}, - onFocus: () => {}, - onBlur: () => {}, - unstyled: false, - style: null, - }; - constructor(props: Props) { +class AtomInput extends React.Component { + constructor(props) { super(props); + + this._onEditorFocus = () => { + if (this.isFocused() && !this._isFocused) { + this._isFocused = true; + this.props.onFocus && this.props.onFocus(); + } + }; + + this._onEditorBlur = blurEvent => { + if (!this.isFocused() && this._isFocused) { + this._isFocused = false; + this.props.onBlur && this.props.onBlur(blurEvent); + } + }; + const value = props.value == null ? props.initialValue : props.value; this.state = { - value, + value }; - this._debouncedEditorFocus = debounce( - this._onEditorFocus, - BLUR_FOCUS_DEBOUNCE_DELAY, - ); - this._debouncedEditorBlur = debounce( - this._onEditorBlur, - BLUR_FOCUS_DEBOUNCE_DELAY, - ); + this._debouncedEditorFocus = (0, _debounce().default)(this._onEditorFocus, BLUR_FOCUS_DEBOUNCE_DELAY); + this._debouncedEditorBlur = (0, _debounce().default)(this._onEditorBlur, BLUR_FOCUS_DEBOUNCE_DELAY); } - componentDidMount(): void { - const disposables = (this._disposables = new UniversalDisposable()); - - // There does not appear to be any sort of infinite loop where calling + componentDidMount() { + const disposables = this._disposables = new (_UniversalDisposable().default)(); // There does not appear to be any sort of infinite loop where calling // setState({value}) in response to onDidChange() causes another change // event. + const textEditor = this.getTextEditor(); const textEditorElement = this.getTextEditorElement(); + if (this.props.autofocus) { this.focus(); } - invariant( - !(this.props.startSelected && this.props.startSelectedRange != null), - 'cannot have both startSelected (all) and startSelectedRange', - ); + if (!!(this.props.startSelected && this.props.startSelectedRange != null)) { + throw new Error('cannot have both startSelected (all) and startSelectedRange'); + } if (this.props.startSelected) { // For some reason, selectAll() has no effect if called right now. - disposables.add( - microtask.subscribe(() => { - if (!textEditor.isDestroyed()) { - textEditor.selectAll(); - } - }), - ); + disposables.add(_observable().microtask.subscribe(() => { + if (!textEditor.isDestroyed()) { + textEditor.selectAll(); + } + })); } const startSelectedRange = this.props.startSelectedRange; + if (startSelectedRange != null) { // For some reason, selectAll() has no effect if called right now. - disposables.add( - microtask.subscribe(() => { - if (!textEditor.isDestroyed()) { - textEditor.setSelectedBufferRange([ - [0, startSelectedRange[0]], - [0, startSelectedRange[1]], - ]); - } - }), - ); + disposables.add(_observable().microtask.subscribe(() => { + if (!textEditor.isDestroyed()) { + textEditor.setSelectedBufferRange([[0, startSelectedRange[0]], [0, startSelectedRange[1]]]); + } + })); } - disposables.add( - atom.commands.add(textEditorElement, { - 'core:confirm': event => { - if (this.props.onConfirm != null) { - this.props.onConfirm(event); - } - }, - 'core:cancel': event => { - if (this.props.onCancel != null) { - this.props.onCancel(event); - } - }, - }), - ); + disposables.add(atom.commands.add(textEditorElement, { + 'core:confirm': event => { + if (this.props.onConfirm != null) { + this.props.onConfirm(event); + } + }, + 'core:cancel': event => { + if (this.props.onCancel != null) { + this.props.onCancel(event); + } + } + })); const placeholderText = this.props.placeholderText; + if (placeholderText != null) { textEditor.setPlaceholderText(placeholderText); } + this.getTextEditorElement().setAttribute('tabindex', this.props.tabIndex); + if (this.props.disabled) { this._updateDisabledState(true); - } - - // Set the text editor's initial value and keep the cursor at the beginning of the line. Cursor + } // Set the text editor's initial value and keep the cursor at the beginning of the line. Cursor // position was documented in a test and is retained here after changes to how text is set in // the text editor. (see focus-related spec in AtomInput-spec.js) + + this.setText(this.state.value); - this.getTextEditor().moveToBeginningOfLine(); - - // Begin listening for changes only after initial value is set. - disposables.add( - textEditor.onDidChange(() => { - this.setState({value: textEditor.getText()}); - this.props.onDidChange.call(null, textEditor.getText()); - }), - ); + this.getTextEditor().moveToBeginningOfLine(); // Begin listening for changes only after initial value is set. + + disposables.add(textEditor.onDidChange(() => { + this.setState({ + value: textEditor.getText() + }); + this.props.onDidChange.call(null, textEditor.getText()); + })); + if (this.props.onDidChangeSelectionRange != null) { - disposables.add( - textEditor.onDidChangeSelectionRange( - this.props.onDidChangeSelectionRange, - ), - ); + disposables.add(textEditor.onDidChangeSelectionRange(this.props.onDidChangeSelectionRange)); } this._updateWidth(); } - UNSAFE_componentWillReceiveProps(nextProps: Props): void { + UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.disabled !== this.props.disabled) { this._updateDisabledState(nextProps.disabled); } - const {value, placeholderText} = nextProps; + + const { + value, + placeholderText + } = nextProps; + if (typeof value === 'string' && value !== this.props.value) { // If the `value` prop is specified, then we must update the input area when there is new // text, and this includes maintaining the correct cursor position. - this.setState({value}); - const editor = this.getTextEditor(); - // Calling setText if the value did not change will redundantly call any + this.setState({ + value + }); + const editor = this.getTextEditor(); // Calling setText if the value did not change will redundantly call any // onDidChange listeners with the same input. + if (editor.getText() !== value) { const cursorPosition = editor.getCursorBufferPosition(); this.setText(value); @@ -219,22 +214,23 @@ export class AtomInput extends React.Component { } } - componentDidUpdate(prevProps: Object, prevState: Object): void { + componentDidUpdate(prevProps, prevState) { this._updateWidth(prevProps.width); } - componentWillUnmount(): void { + componentWillUnmount() { // Note that destroy() is not part of TextEditor's public API. const editor = this.getTextEditor(); process.nextTick(() => editor.destroy()); if (this._disposables) { this._disposables.dispose(); + this._disposables = null; } } - _updateDisabledState(isDisabled: boolean): void { + _updateDisabledState(isDisabled) { // Hack to set TextEditor to read-only mode, per https://github.com/atom/atom/issues/6880 if (isDisabled) { this.getTextEditorElement().removeAttribute('tabindex'); @@ -243,84 +239,83 @@ export class AtomInput extends React.Component { } } - _onEditorFocus = (): void => { - if (this.isFocused() && !this._isFocused) { - this._isFocused = true; - this.props.onFocus && this.props.onFocus(); - } - }; - - _onEditorBlur = (blurEvent: Event): void => { - if (!this.isFocused() && this._isFocused) { - this._isFocused = false; - this.props.onBlur && this.props.onBlur(blurEvent); - } - }; - - isFocused(): boolean { - return ( - this._rootNode != null && this._rootNode.contains(document.activeElement) - ); + isFocused() { + return this._rootNode != null && this._rootNode.contains(document.activeElement); } - render(): React.Node { - const className = classNames(this.props.className, { + render() { + const className = (0, _classnames().default)(this.props.className, { 'atom-text-editor-unstyled': this.props.unstyled, - [`atom-text-editor-${maybeToString(this.props.size)}`]: - this.props.size != null, - 'atom-text-editor-invalid': this.props.invalid, + [`atom-text-editor-${(0, _string().maybeToString)(this.props.size)}`]: this.props.size != null, + 'atom-text-editor-invalid': this.props.invalid }); - - return ( - // Because the contents of `` elements are managed by its custom web + return (// Because the contents of `` elements are managed by its custom web // component class when "Use Shadow DOM" is disabled, this element should never have children. // If an element has no children, React guarantees it will never re-render the element (which // would wipe out the web component's work in this case). - (this._rootNode = rootNode)} - onClick={this.props.onClick} - onFocus={this._debouncedEditorFocus} - onBlur={this._debouncedEditorBlur} - style={this.props.style} - /> + React.createElement("atom-text-editor", { + "class": className, + mini: true, + ref: rootNode => this._rootNode = rootNode, + onClick: this.props.onClick, + onFocus: this._debouncedEditorFocus, + onBlur: this._debouncedEditorBlur, + style: this.props.style + }) ); } - getText(): string { + getText() { return this.state.value; } - setText(text: string) { + setText(text) { this.getTextEditor().setText(text); } - getTextEditor(): TextEditor { + getTextEditor() { return this.getTextEditorElement().getModel(); } - onDidChange(callback: () => any): IDisposable { - return this.getTextEditor() - .getBuffer() - .onDidChangeText(callback); + onDidChange(callback) { + return this.getTextEditor().getBuffer().onDidChangeText(callback); } - getTextEditorElement(): atom$TextEditorElement { - invariant(this._rootNode != null); - // $FlowFixMe + getTextEditorElement() { + if (!(this._rootNode != null)) { + throw new Error("Invariant violation: \"this._rootNode != null\""); + } // $FlowFixMe + + return this._rootNode; } - _updateWidth(prevWidth?: number): void { + _updateWidth(prevWidth) { if (this.props.width !== prevWidth) { const width = this.props.width == null ? undefined : this.props.width; this.getTextEditorElement().setWidth(width); } } - focus(): void { + focus() { this.getTextEditor().moveToEndOfLine(); this.getTextEditorElement().focus(); } + } + +exports.AtomInput = AtomInput; +AtomInput.defaultProps = { + disabled: false, + autofocus: false, + startSelected: false, + initialValue: '', + tabIndex: '0', + // Default to all components being in tab order + onClick: event => {}, + onDidChange: text => {}, + onFocus: () => {}, + onBlur: () => {}, + unstyled: false, + style: null +}; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/AtomTextEditor.js b/modules/nuclide-commons-ui/AtomTextEditor.js index 2214f016..09102a4a 100644 --- a/modules/nuclide-commons-ui/AtomTextEditor.js +++ b/modules/nuclide-commons-ui/AtomTextEditor.js @@ -1,3 +1,48 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.AtomTextEditor = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _atom = require("atom"); + +function _textEditor() { + const data = require("../nuclide-commons-atom/text-editor"); + + _textEditor = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,36 +51,21 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import classnames from 'classnames'; -import * as React from 'react'; -import {TextBuffer} from 'atom'; -import { - enforceReadOnlyEditor, - enforceSoftWrap, -} from 'nuclide-commons-atom/text-editor'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - const doNothing = () => {}; -type TextEditorSetup = { - disposables: IDisposable, - textEditor: atom$TextEditor, -}; +function setupTextEditor(props) { + const textBuffer = props.textBuffer || new _atom.TextBuffer(); // flowlint-next-line sketchy-null-string:off -function setupTextEditor(props: Props): TextEditorSetup { - const textBuffer = props.textBuffer || new TextBuffer(); - // flowlint-next-line sketchy-null-string:off if (props.path) { // $FlowIgnore textBuffer.setPath(props.path); } - const disposables = new UniversalDisposable(); + const disposables = new (_UniversalDisposable().default)(); + if (props.onDidTextBufferChange != null) { disposables.add(textBuffer.onDidChangeText(props.onDidTextBufferChange)); } @@ -43,150 +73,103 @@ function setupTextEditor(props: Props): TextEditorSetup { const textEditorParams = { buffer: textBuffer, lineNumberGutterVisible: !props.gutterHidden, - autoHeight: props.autoGrow, + autoHeight: props.autoGrow }; - const textEditor: atom$TextEditor = atom.workspace.buildTextEditor( - textEditorParams, - ); + const textEditor = atom.workspace.buildTextEditor(textEditorParams); disposables.add(() => textEditor.destroy()); + if (props.grammar != null) { textEditor.setGrammar(props.grammar); } else { atom.grammars.autoAssignLanguageMode(textBuffer); } - disposables.add(enforceSoftWrap(textEditor, props.softWrapped)); - // flowlint-next-line sketchy-null-string:off + disposables.add((0, _textEditor().enforceSoftWrap)(textEditor, props.softWrapped)); // flowlint-next-line sketchy-null-string:off + if (props.placeholderText) { textEditor.setPlaceholderText(props.placeholderText); } if (props.readOnly) { - enforceReadOnlyEditor(textEditor); + (0, _textEditor().enforceReadOnlyEditor)(textEditor); // Remove the cursor line decorations because that's distracting in read-only mode. - // Remove the cursor line decorations because that's distracting in read-only mode. - textEditor.getDecorations({class: 'cursor-line'}).forEach(decoration => { + textEditor.getDecorations({ + class: 'cursor-line' + }).forEach(decoration => { decoration.destroy(); }); } + return { disposables, - textEditor, + textEditor }; } -type DefaultProps = { - autoGrow: boolean, - correctContainerWidth: boolean, - disabled: boolean, - gutterHidden: boolean, - lineNumberGutterVisible: boolean, - readOnly: boolean, - syncTextContents: boolean, - tabIndex: string, - softWrapped: boolean, -}; - -type Props = { - autoGrow: boolean, - className?: string, - correctContainerWidth: boolean, - disabled: boolean, - gutterHidden: boolean, - grammar?: ?Object, - // these are processed in setupTextEditor below - /* eslint-disable react/no-unused-prop-types */ - onDidTextBufferChange?: (event: atom$AggregatedTextEditEvent) => mixed, - path?: string, - placeholderText?: string, - syncTextContents: boolean, - /* eslint-enable react/no-unused-prop-types */ - readOnly: boolean, - textBuffer?: TextBuffer, - tabIndex: string, - softWrapped: boolean, - onConfirm?: () => mixed, - // Called with text editor as input after initializing and attaching to DOM. - onInitialized?: atom$TextEditor => IDisposable, -}; - -export class AtomTextEditor extends React.Component { - static defaultProps: DefaultProps = { - correctContainerWidth: true, - disabled: false, - gutterHidden: false, - lineNumberGutterVisible: true, - readOnly: false, - autoGrow: false, - syncTextContents: true, - tabIndex: '0', // Keep in line with other input elements. - softWrapped: false, - }; - - _rootElement: ?HTMLDivElement; - _textEditorElement: ?atom$TextEditorElement; - _editorDisposables: UniversalDisposable; +class AtomTextEditor extends React.Component { + componentDidMount() { + this._editorDisposables = new (_UniversalDisposable().default)(); - componentDidMount(): void { - this._editorDisposables = new UniversalDisposable(); this._updateTextEditor(setupTextEditor(this.props)); + this._onDidUpdateTextEditorElement(this.props); + if (this.props.disabled) { this._updateDisabledState(true); } } - _updateTextEditor(setup: TextEditorSetup): void { + _updateTextEditor(setup) { const container = this._rootElement; + if (container == null) { return; } this._editorDisposables.dispose(); - const {textEditor, disposables} = setup; - - this._editorDisposables = new UniversalDisposable(disposables); - const textEditorElement: atom$TextEditorElement = (this._textEditorElement = (document.createElement( - 'atom-text-editor', - ): any)); + const { + textEditor, + disposables + } = setup; + this._editorDisposables = new (_UniversalDisposable().default)(disposables); + const textEditorElement = this._textEditorElement = document.createElement('atom-text-editor'); textEditorElement.setModel(textEditor); - textEditorElement.setAttribute('tabindex', this.props.tabIndex); - // HACK! This is a workaround for the ViewRegistry where Atom has a default view provider for + textEditorElement.setAttribute('tabindex', this.props.tabIndex); // HACK! This is a workaround for the ViewRegistry where Atom has a default view provider for // TextEditor (that we cannot override), which is responsible for creating the view associated // with the TextEditor that we create and adding a mapping for it in its private views map. // To workaround this, we reach into the internals of the ViewRegistry and update the entry in // the map manually. Filed as https://github.com/atom/atom/issues/7954. // $FlowFixMe + atom.views.views.set(textEditor, textEditorElement); if (this.props.correctContainerWidth) { - this._editorDisposables.add( - textEditorElement.onDidAttach(() => { - const correctlySizedElement = textEditorElement.querySelector( - '.lines > :first-child', - ); - if (correctlySizedElement == null) { - return; - } - container.style.width = correctlySizedElement.style.width; - }), - ); - } + this._editorDisposables.add(textEditorElement.onDidAttach(() => { + const correctlySizedElement = textEditorElement.querySelector('.lines > :first-child'); + + if (correctlySizedElement == null) { + return; + } + + container.style.width = correctlySizedElement.style.width; + })); + } // Attach to DOM. + - // Attach to DOM. container.innerHTML = ''; container.appendChild(textEditorElement); if (this.props.onConfirm != null) { - this._editorDisposables.add( - atom.commands.add(textEditorElement, { - 'core:confirm': () => { - invariant(this.props.onConfirm != null); - this.props.onConfirm(); - }, - }), - ); + this._editorDisposables.add(atom.commands.add(textEditorElement, { + 'core:confirm': () => { + if (!(this.props.onConfirm != null)) { + throw new Error("Invariant violation: \"this.props.onConfirm != null\""); + } + + this.props.onConfirm(); + } + })); } if (this.props.onInitialized != null) { @@ -194,71 +177,83 @@ export class AtomTextEditor extends React.Component { } } - UNSAFE_componentWillReceiveProps(nextProps: Props): void { - if ( - nextProps.textBuffer !== this.props.textBuffer || - nextProps.readOnly !== this.props.readOnly - ) { + UNSAFE_componentWillReceiveProps(nextProps) { + if (nextProps.textBuffer !== this.props.textBuffer || nextProps.readOnly !== this.props.readOnly) { const previousTextContents = this.getTextBuffer().getText(); - const nextTextContents = - nextProps.textBuffer == null - ? nextProps.textBuffer - : nextProps.textBuffer.getText(); + const nextTextContents = nextProps.textBuffer == null ? nextProps.textBuffer : nextProps.textBuffer.getText(); + if (nextTextContents !== previousTextContents) { const textEditorSetup = setupTextEditor(nextProps); if (nextProps.syncTextContents) { textEditorSetup.textEditor.setText(previousTextContents); } + this._updateTextEditor(textEditorSetup); + this._onDidUpdateTextEditorElement(nextProps); } } + if (nextProps.path !== this.props.path) { // $FlowIgnore this.getTextBuffer().setPath(nextProps.path || ''); } + if (nextProps.gutterHidden !== this.props.gutterHidden) { this.getModel().setLineNumberGutterVisible(nextProps.gutterHidden); } + if (nextProps.grammar !== this.props.grammar) { this.getModel().setGrammar(nextProps.grammar); } + if (nextProps.softWrapped !== this.props.softWrapped) { this.getModel().setSoftWrapped(nextProps.softWrapped); } + if (nextProps.disabled !== this.props.disabled) { this._updateDisabledState(nextProps.disabled); } + if (nextProps.placeholderText !== this.props.placeholderText) { this.getModel().setPlaceholderText(nextProps.placeholderText || ''); this.getModel().scheduleComponentUpdate(); } } - _onDidUpdateTextEditorElement(props: Props): void { + _onDidUpdateTextEditorElement(props) { if (!props.readOnly) { return; - } - // TODO(most): t9929679 Remove this hack when Atom has a blinking cursor configuration API. - const {component} = this.getElement(); + } // TODO(most): t9929679 Remove this hack when Atom has a blinking cursor configuration API. + + + const { + component + } = this.getElement(); + if (component == null) { return; } + if (component.startCursorBlinking) { component.startCursorBlinking = doNothing; component.stopCursorBlinking(); } else { - const {presenter} = component; + const { + presenter + } = component; + if (presenter == null) { return; } + presenter.startBlinkingCursors = doNothing; presenter.stopBlinkingCursors(false); } } - _updateDisabledState(isDisabled: boolean): void { + _updateDisabledState(isDisabled) { // Hack to set TextEditor to read-only mode, per https://github.com/atom/atom/issues/6880 if (isDisabled) { this.getElement().removeAttribute('tabindex'); @@ -267,42 +262,54 @@ export class AtomTextEditor extends React.Component { } } - getTextBuffer(): atom$TextBuffer { + getTextBuffer() { return this.getModel().getBuffer(); } - getModel(): atom$TextEditor { + getModel() { return this.getElement().getModel(); } - getElement(): atom$TextEditorElement { - invariant(this._textEditorElement); - return this._textEditorElement; - } + getElement() { + if (!this._textEditorElement) { + throw new Error("Invariant violation: \"this._textEditorElement\""); + } - render(): React.Node { - const className = classnames( - this.props.className, - 'nuclide-text-editor-container', - { - 'no-auto-grow': !this.props.autoGrow, - }, - ); - return ( -
    (this._rootElement = rootElement)} - /> - ); + return this._textEditorElement; } - // This component wraps the imperative API of ``, and so React's rendering + render() { + const className = (0, _classnames().default)(this.props.className, 'nuclide-text-editor-container', { + 'no-auto-grow': !this.props.autoGrow + }); + return React.createElement("div", { + className: className, + ref: rootElement => this._rootElement = rootElement + }); + } // This component wraps the imperative API of ``, and so React's rendering // should always pass because this subtree won't change. - shouldComponentUpdate(nextProps: Object, nextState: void): boolean { + + + shouldComponentUpdate(nextProps, nextState) { return false; } - componentWillUnmount(): void { + componentWillUnmount() { process.nextTick(() => this._editorDisposables.dispose()); } + } + +exports.AtomTextEditor = AtomTextEditor; +AtomTextEditor.defaultProps = { + correctContainerWidth: true, + disabled: false, + gutterHidden: false, + lineNumberGutterVisible: true, + readOnly: false, + autoGrow: false, + syncTextContents: true, + tabIndex: '0', + // Keep in line with other input elements. + softWrapped: false +}; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Atomicon.js b/modules/nuclide-commons-ui/Atomicon.js index 8c36a320..0439639d 100644 --- a/modules/nuclide-commons-ui/Atomicon.js +++ b/modules/nuclide-commons-ui/Atomicon.js @@ -1,3 +1,47 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = Atomicon; +exports.getTypeFromIconName = getTypeFromIconName; + +function _invert2() { + const data = _interopRequireDefault(require("lodash/invert")); + + _invert2 = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _string() { + const data = require("../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,15 +50,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import * as React from 'react'; -import {capitalize} from 'nuclide-commons/string'; -import classnames from 'classnames'; -import {invert} from 'lodash'; - const TYPE_TO_ICON_NAME = { array: 'type-array', boolean: 'type-boolean', @@ -33,24 +71,21 @@ const TYPE_TO_ICON_NAME = { package: 'type-package', property: 'type-property', string: 'type-string', - variable: 'type-variable', + variable: 'type-variable' }; +const ICON_NAME_TO_TYPE = (0, _invert2().default)(TYPE_TO_ICON_NAME); -const ICON_NAME_TO_TYPE = invert(TYPE_TO_ICON_NAME); - -type AtomiconType = $Keys; - -export default function Atomicon({type}: {type: AtomiconType}) { - const displayName = capitalize(type); - return ( - - ); +function Atomicon({ + type +}) { + const displayName = (0, _string().capitalize)(type); + return React.createElement("span", { + className: (0, _classnames().default)('icon', 'icon-' + TYPE_TO_ICON_NAME[type]), + role: "presentation", + title: displayName + }); } -export function getTypeFromIconName(iconName: string): ?AtomiconType { +function getTypeFromIconName(iconName) { return ICON_NAME_TO_TYPE[iconName]; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Block.js b/modules/nuclide-commons-ui/Block.js index 91dda48d..7e16ce3d 100644 --- a/modules/nuclide-commons-ui/Block.js +++ b/modules/nuclide-commons-ui/Block.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Block = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,18 +17,14 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ -import * as React from 'react'; - -type Props = { - children?: mixed, -}; - /** A Block. */ -export const Block = (props: Props) => ( - // $FlowFixMe(>=0.53.0) Flow suppress -
    {props.children}
    -); +const Block = props => // $FlowFixMe(>=0.53.0) Flow suppress +React.createElement("div", { + className: "block" +}, props.children); + +exports.Block = Block; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/BoundSettingsControl.js b/modules/nuclide-commons-ui/BoundSettingsControl.js index 96f8e55b..5d17be2c 100644 --- a/modules/nuclide-commons-ui/BoundSettingsControl.js +++ b/modules/nuclide-commons-ui/BoundSettingsControl.js @@ -1,3 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _SettingsControl() { + const data = _interopRequireDefault(require("./SettingsControl")); + + _SettingsControl = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,76 +29,66 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class BoundSettingsControl extends React.Component { + constructor(props) { + super(props); -import SettingsControl from './SettingsControl'; -import * as React from 'react'; - -type Props = {| - keyPath: string, -|}; - -type State = {| - value: any, -|}; - -export default class BoundSettingsControl extends React.Component< - Props, - State, -> { - _observeConfigDisposable: ?IDisposable; + this._onChange = value => { + atom.config.set(this.props.keyPath, value); + }; - constructor(props: Props) { - super(props); this.state = { - value: atom.config.get(props.keyPath), + value: atom.config.get(props.keyPath) }; } - _updateSubscription(): void { + _updateSubscription() { if (this._observeConfigDisposable != null) { this._observeConfigDisposable.dispose(); } - this._observeConfigDisposable = atom.config.onDidChange( - this.props.keyPath, - ({newValue}) => { - this.setState({value: newValue}); - }, - ); + + this._observeConfigDisposable = atom.config.onDidChange(this.props.keyPath, ({ + newValue + }) => { + this.setState({ + value: newValue + }); + }); } - componentDidMount(): void { + componentDidMount() { this._updateSubscription(); } - componentDidUpdate(prevProps: Props): void { + componentDidUpdate(prevProps) { if (prevProps.keyPath !== this.props.keyPath) { - this.setState({value: atom.config.get(this.props.keyPath)}); + this.setState({ + value: atom.config.get(this.props.keyPath) + }); + this._updateSubscription(); } } - componentWillUnmount(): void { + componentWillUnmount() { if (this._observeConfigDisposable != null) { this._observeConfigDisposable.dispose(); } } - render(): React.Element { + render() { const schema = atom.config.getSchema(this.props.keyPath); - return ( - - ); + return React.createElement(_SettingsControl().default, { + keyPath: this.props.keyPath, + value: this.state.value, + onChange: this._onChange, + schema: schema + }); } - _onChange = (value: any): void => { - atom.config.set(this.props.keyPath, value); - }; } + +exports.default = BoundSettingsControl; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Button.example.js b/modules/nuclide-commons-ui/Button.example.js index fd51a13a..60516bd2 100644 --- a/modules/nuclide-commons-ui/Button.example.js +++ b/modules/nuclide-commons-ui/Button.example.js @@ -1,3 +1,54 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ButtonExamples = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Button() { + const data = require("./Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _ButtonGroup() { + const data = require("./ButtonGroup"); + + _ButtonGroup = function () { + return data; + }; + + return data; +} + +function _ButtonToolbar() { + const data = require("./ButtonToolbar"); + + _ButtonToolbar = function () { + return data; + }; + + return data; +} + +function _Block() { + const data = require("./Block"); + + _Block = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,163 +57,109 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const ButtonSizeExample = () => React.createElement(_Block().Block, null, React.createElement(_Button().Button, { + className: "inline-block", + size: "EXTRA_SMALL" +}, "extra_small"), React.createElement(_Button().Button, { + className: "inline-block", + size: "SMALL" +}, "small"), React.createElement(_Button().Button, { + className: "inline-block" +}, "regular"), React.createElement(_Button().Button, { + className: "inline-block", + size: "LARGE" +}, "large")); + +const ButtonDisabledExample = () => React.createElement(_Block().Block, null, React.createElement(_Button().Button, { + className: "inline-block" +}, "enabled"), React.createElement(_Button().Button, { + className: "inline-block", + disabled: true +}, "disabled")); + +const ButtonColorExample = () => React.createElement("div", null, React.createElement(_Block().Block, null, React.createElement(_ButtonGroup().ButtonGroup, null, React.createElement(_Button().Button, { + buttonType: "PRIMARY" +}, "primary"), React.createElement(_Button().Button, { + buttonType: "INFO" +}, "info"), React.createElement(_Button().Button, { + buttonType: "SUCCESS" +}, "success"), React.createElement(_Button().Button, { + buttonType: "WARNING" +}, "warning"), React.createElement(_Button().Button, { + buttonType: "ERROR" +}, "error"))), React.createElement(_Block().Block, null, React.createElement("p", null, "selected:"), React.createElement(_ButtonGroup().ButtonGroup, null, React.createElement(_Button().Button, { + selected: true, + buttonType: "PRIMARY" +}, "primary"), React.createElement(_Button().Button, { + selected: true, + buttonType: "INFO" +}, "info"), React.createElement(_Button().Button, { + selected: true, + buttonType: "SUCCESS" +}, "success"), React.createElement(_Button().Button, { + selected: true, + buttonType: "WARNING" +}, "warning"), React.createElement(_Button().Button, { + selected: true, + buttonType: "ERROR" +}, "error")))); + +const ButtonIconExample = () => React.createElement(_Block().Block, null, React.createElement(_ButtonGroup().ButtonGroup, null, React.createElement(_Button().Button, { + icon: "gear" +}), React.createElement(_Button().Button, { + icon: "cloud-download" +}), React.createElement(_Button().Button, { + icon: "code" +}), React.createElement(_Button().Button, { + icon: "check" +}), React.createElement(_Button().Button, { + icon: "device-mobile" +}), React.createElement(_Button().Button, { + icon: "alert" +}))); + +const ButtonGroupExample = () => React.createElement("div", null, React.createElement(_Block().Block, null, React.createElement(_ButtonGroup().ButtonGroup, { + size: "EXTRA_SMALL" +}, React.createElement(_Button().Button, { + buttonType: "SUCCESS" +}, "extra small"), React.createElement(_Button().Button, null, "button"), React.createElement(_Button().Button, null, "group"))), React.createElement(_Block().Block, null, React.createElement(_ButtonGroup().ButtonGroup, { + size: "SMALL" +}, React.createElement(_Button().Button, { + buttonType: "SUCCESS" +}, "small"), React.createElement(_Button().Button, null, "button"), React.createElement(_Button().Button, null, "group"))), React.createElement(_Block().Block, null, React.createElement(_ButtonGroup().ButtonGroup, null, React.createElement(_Button().Button, { + buttonType: "SUCCESS" +}, "regular"), React.createElement(_Button().Button, null, "button"), React.createElement(_Button().Button, null, "group"))), React.createElement(_Block().Block, null, React.createElement(_ButtonGroup().ButtonGroup, { + size: "LARGE" +}, React.createElement(_Button().Button, { + buttonType: "SUCCESS" +}, "large"), React.createElement(_Button().Button, null, "button"), React.createElement(_Button().Button, null, "group")))); + +const ButtonToolbarExample = () => React.createElement("div", null, React.createElement(_Block().Block, null, React.createElement(_ButtonToolbar().ButtonToolbar, null, React.createElement(_ButtonGroup().ButtonGroup, null, React.createElement(_Button().Button, null, "ButtonGroup"), React.createElement(_Button().Button, null, "in a"), React.createElement(_Button().Button, null, "toolbar")), React.createElement(_Button().Button, null, "single buttons"), React.createElement(_Button().Button, null, "in toolbar")))); -import * as React from 'react'; -import {Button} from './Button'; -import {ButtonGroup} from './ButtonGroup'; -import {ButtonToolbar} from './ButtonToolbar'; -import {Block} from './Block'; - -const ButtonSizeExample = (): React.Element => ( - - - - - - -); - -const ButtonDisabledExample = (): React.Element => ( - - - - -); - -const ButtonColorExample = (): React.Element => ( -
    - - - - - - - - - - -

    selected:

    - - - - - - - -
    -
    -); - -const ButtonIconExample = (): React.Element => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -); - -const ButtonToolbarExample = (): React.Element => ( -
    - - - - - - - - - - - -
    -); - -export const ButtonExamples = { +const ButtonExamples = { sectionName: 'Buttons', description: 'For clicking things.', - examples: [ - { - title: 'Button sizes', - component: ButtonSizeExample, - }, - { - title: 'Disabled/enabled', - component: ButtonDisabledExample, - }, - { - title: 'Button colors', - component: ButtonColorExample, - }, - { - title: 'Buttons with icons', - component: ButtonIconExample, - }, - { - title: 'Button Group', - component: ButtonGroupExample, - }, - { - title: 'Button Toolbar', - component: ButtonToolbarExample, - }, - ], + examples: [{ + title: 'Button sizes', + component: ButtonSizeExample + }, { + title: 'Disabled/enabled', + component: ButtonDisabledExample + }, { + title: 'Button colors', + component: ButtonColorExample + }, { + title: 'Buttons with icons', + component: ButtonIconExample + }, { + title: 'Button Group', + component: ButtonGroupExample + }, { + title: 'Button Toolbar', + component: ButtonToolbarExample + }] }; +exports.ButtonExamples = ButtonExamples; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Button.js b/modules/nuclide-commons-ui/Button.js index 1b9f3682..c4d83294 100644 --- a/modules/nuclide-commons-ui/Button.js +++ b/modules/nuclide-commons-ui/Button.js @@ -1,87 +1,95 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Button = exports.ButtonTypes = exports.ButtonSizes = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _string() { + const data = require("../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _addTooltip() { + const data = _interopRequireDefault(require("./addTooltip")); + + _addTooltip = function () { + return data; + }; -import type {IconName} from './Icon'; - -import classnames from 'classnames'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import {maybeToString} from 'nuclide-commons/string'; -import addTooltip from './addTooltip'; - -export type ButtonType = 'PRIMARY' | 'INFO' | 'SUCCESS' | 'WARNING' | 'ERROR'; -export type ButtonSize = 'EXTRA_SMALL' | 'SMALL' | 'LARGE'; -type ButtonNodeName = 'button' | 'a'; - -type Props = { - /** Icon name, without the `icon-` prefix. E.g. `'arrow-up'` */ - icon?: IconName, - /** Optional specifier for special buttons, e.g. primary, info, success or error buttons. */ - buttonType?: ?ButtonType, - selected?: boolean, - /** */ - size?: ButtonSize, - className?: string, - /** The button's content; generally a string. */ - children?: mixed, - /** Allows specifying an element other than `button` to be used as the wrapper node. */ - wrapperElement?: ButtonNodeName, - tooltip?: atom$TooltipsAddOptions, - disabled?: boolean, -}; - -export const ButtonSizes = Object.freeze({ + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +const ButtonSizes = Object.freeze({ EXTRA_SMALL: 'EXTRA_SMALL', SMALL: 'SMALL', - LARGE: 'LARGE', + LARGE: 'LARGE' }); - -export const ButtonTypes = Object.freeze({ +exports.ButtonSizes = ButtonSizes; +const ButtonTypes = Object.freeze({ PRIMARY: 'PRIMARY', INFO: 'INFO', SUCCESS: 'SUCCESS', WARNING: 'WARNING', - ERROR: 'ERROR', + ERROR: 'ERROR' }); - +exports.ButtonTypes = ButtonTypes; const ButtonSizeClassnames = Object.freeze({ EXTRA_SMALL: 'btn-xs', SMALL: 'btn-sm', - LARGE: 'btn-lg', + LARGE: 'btn-lg' }); - const ButtonTypeClassnames = Object.freeze({ PRIMARY: 'btn-primary', INFO: 'btn-info', SUCCESS: 'btn-success', WARNING: 'btn-warning', - ERROR: 'btn-error', + ERROR: 'btn-error' }); - /** * Generic Button wrapper. */ -export class Button extends React.Component { - focus(): void { - const node = ReactDOM.findDOMNode(this); + +class Button extends React.Component { + focus() { + const node = _reactDom.default.findDOMNode(this); + if (node == null) { return; - } - // $FlowFixMe + } // $FlowFixMe + + node.focus(); } - render(): React.Node { - const { + render() { + const _this$props = this.props, + { icon, buttonType, selected, @@ -89,31 +97,32 @@ export class Button extends React.Component { children, className, wrapperElement, - tooltip, - ...remainingProps - } = this.props; + tooltip + } = _this$props, + remainingProps = _objectWithoutProperties(_this$props, ["icon", "buttonType", "selected", "size", "children", "className", "wrapperElement", "tooltip"]); + const sizeClassname = size == null ? '' : ButtonSizeClassnames[size] || ''; - const buttonTypeClassname = - buttonType == null ? '' : ButtonTypeClassnames[buttonType] || ''; - const ref = tooltip && !this.props.disabled ? addTooltip(tooltip) : null; + const buttonTypeClassname = buttonType == null ? '' : ButtonTypeClassnames[buttonType] || ''; + const ref = tooltip && !this.props.disabled ? (0, _addTooltip().default)(tooltip) : null; const titleToolTip = tooltip && this.props.disabled ? tooltip.title : null; - const newClassName = classnames(className, 'btn', { - [`icon icon-${maybeToString(icon)}`]: icon != null, + const newClassName = (0, _classnames().default)(className, 'btn', { + [`icon icon-${(0, _string().maybeToString)(icon)}`]: icon != null, [sizeClassname]: size != null, selected, - [buttonTypeClassname]: buttonType != null, + [buttonTypeClassname]: buttonType != null }); const Wrapper = wrapperElement == null ? 'button' : wrapperElement; - return ( - // $FlowFixMe(>=0.53.0) Flow suppress - - {children} - + return (// $FlowFixMe(>=0.53.0) Flow suppress + React.createElement(Wrapper, Object.assign({ + className: newClassName // eslint-disable-next-line nuclide-internal/jsx-simple-callback-refs + , + ref: ref + }, remainingProps, { + title: titleToolTip + }), children) ); } + } + +exports.Button = Button; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/ButtonGroup.js b/modules/nuclide-commons-ui/ButtonGroup.js index 0dae1821..5b2969e1 100644 --- a/modules/nuclide-commons-ui/ButtonGroup.js +++ b/modules/nuclide-commons-ui/ButtonGroup.js @@ -1,3 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ButtonGroup = exports.ButtonGroupSizes = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,47 +29,39 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import classnames from 'classnames'; -import * as React from 'react'; - -type ButtonGroupSize = 'EXTRA_SMALL' | 'SMALL' | 'LARGE'; - -type Props = { - /** The size of the buttons within the group. Overrides any `size` props on child buttons. */ - size?: ButtonGroupSize, - /** The contents of the ButtonGroup; Generally, an instance of `Button`. */ - children?: mixed, - className?: string, -}; - -export const ButtonGroupSizes = Object.freeze({ +const ButtonGroupSizes = Object.freeze({ EXTRA_SMALL: 'EXTRA_SMALL', SMALL: 'SMALL', - LARGE: 'LARGE', + LARGE: 'LARGE' }); - +exports.ButtonGroupSizes = ButtonGroupSizes; const ButtonGroupSizeClassnames = Object.freeze({ EXTRA_SMALL: 'btn-group-xs', SMALL: 'btn-group-sm', - LARGE: 'btn-group-lg', + LARGE: 'btn-group-lg' }); - /** * Visually groups Buttons passed in as children. */ -export const ButtonGroup = (props: Props) => { - const {size, children, className} = props; - const sizeClassName = - size == null ? '' : ButtonGroupSizeClassnames[size] || ''; - const newClassName = classnames(className, 'btn-group', 'nuclide-btn-group', { - [sizeClassName]: size != null, + +const ButtonGroup = props => { + const { + size, + children, + className + } = props; + const sizeClassName = size == null ? '' : ButtonGroupSizeClassnames[size] || ''; + const newClassName = (0, _classnames().default)(className, 'btn-group', 'nuclide-btn-group', { + [sizeClassName]: size != null }); - return ( - // $FlowFixMe(>=0.53.0) Flow suppress -
    {children}
    + return (// $FlowFixMe(>=0.53.0) Flow suppress + React.createElement("div", { + className: newClassName + }, children) ); }; + +exports.ButtonGroup = ButtonGroup; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/ButtonToolbar.js b/modules/nuclide-commons-ui/ButtonToolbar.js index 3bbc1d68..cdc40256 100644 --- a/modules/nuclide-commons-ui/ButtonToolbar.js +++ b/modules/nuclide-commons-ui/ButtonToolbar.js @@ -1,3 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ButtonToolbar = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,25 +29,23 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ -import classnames from 'classnames'; -import * as React from 'react'; - -type Props = { - className?: string, - children?: mixed, -}; - /** * Visually groups Buttons passed in as children. */ -export const ButtonToolbar = (props: Props) => { - const {children, className} = props; - return ( - // $FlowFixMe(>=0.53.0) Flow suppress -
    {children}
    +const ButtonToolbar = props => { + const { + children, + className + } = props; + return (// $FlowFixMe(>=0.53.0) Flow suppress + React.createElement("div", { + className: (0, _classnames().default)('btn-toolbar', className) + }, children) ); }; + +exports.ButtonToolbar = ButtonToolbar; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Checkbox.example.js b/modules/nuclide-commons-ui/Checkbox.example.js index 3a26e060..5164c2b9 100644 --- a/modules/nuclide-commons-ui/Checkbox.example.js +++ b/modules/nuclide-commons-ui/Checkbox.example.js @@ -1,3 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.CheckboxExamples = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Block() { + const data = require("./Block"); + + _Block = function () { + return data; + }; + + return data; +} + +function _Checkbox() { + const data = require("./Checkbox"); + + _Checkbox = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,71 +37,47 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import * as React from 'react'; -import {Block} from './Block'; -import {Checkbox} from './Checkbox'; - const NOOP = () => {}; -const CheckboxExample = (): React.Element => ( -
    - - - - - - - - - - - - - - - -
    -); +const CheckboxExample = () => React.createElement("div", null, React.createElement(_Block().Block, null, React.createElement(_Checkbox().Checkbox, { + checked: false, + onClick: NOOP, + onChange: NOOP, + label: "A Checkbox." +})), React.createElement(_Block().Block, null, React.createElement(_Checkbox().Checkbox, { + onClick: NOOP, + onChange: NOOP, + checked: true, + label: "A checked Checkbox." +})), React.createElement(_Block().Block, null, React.createElement(_Checkbox().Checkbox, { + onClick: NOOP, + onChange: NOOP, + disabled: true, + checked: false, + label: "A disabled Checkbox." +})), React.createElement(_Block().Block, null, React.createElement(_Checkbox().Checkbox, { + onClick: NOOP, + onChange: NOOP, + checked: true, + disabled: true, + label: "A disabled, checked Checkbox." +})), React.createElement(_Block().Block, null, React.createElement(_Checkbox().Checkbox, { + onClick: NOOP, + onChange: NOOP, + indeterminate: true, + checked: false, + label: "An indeterminate Checkbox." +}))); -export const CheckboxExamples = { +const CheckboxExamples = { sectionName: 'Checkbox', description: '', - examples: [ - { - title: '', - component: CheckboxExample, - }, - ], + examples: [{ + title: '', + component: CheckboxExample + }] }; +exports.CheckboxExamples = CheckboxExamples; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Checkbox.js b/modules/nuclide-commons-ui/Checkbox.js index 89822ca9..c28313d4 100644 --- a/modules/nuclide-commons-ui/Checkbox.js +++ b/modules/nuclide-commons-ui/Checkbox.js @@ -1,3 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Checkbox = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _addTooltip() { + const data = _interopRequireDefault(require("./addTooltip")); + + _addTooltip = function () { + return data; + }; + + return data; +} + +function _ignoreTextSelectionEvents() { + const data = _interopRequireDefault(require("./ignoreTextSelectionEvents")); + + _ignoreTextSelectionEvents = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,55 +49,18 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import * as React from 'react'; -import classnames from 'classnames'; -import addTooltip from './addTooltip'; - -import ignoreTextSelectionEvents from './ignoreTextSelectionEvents'; - -type DefaultProps = { - disabled: boolean, - indeterminate: boolean, - label: string, - onClick: (event: SyntheticMouseEvent<>) => mixed, - onMouseDown: (event: SyntheticMouseEvent<>) => mixed, -}; - -type Props = { - className?: string, - checked: boolean, - disabled: boolean, - indeterminate: boolean, - label: string, - onChange: (isChecked: boolean) => mixed, - onClick: (event: SyntheticMouseEvent<>) => mixed, - tooltip?: atom$TooltipsAddOptions, - title?: ?string, - onMouseDown: (event: SyntheticMouseEvent<>) => mixed, -}; - /** * A checkbox component with an input checkbox and a label. We restrict the label to a string * to ensure this component is pure. */ -export class Checkbox extends React.PureComponent { - _input: ?HTMLInputElement; - - static defaultProps: DefaultProps = { - disabled: false, - indeterminate: false, - label: '', - onClick(event) {}, - onMouseDown(event) {}, - }; - - constructor(props: Props) { +class Checkbox extends React.PureComponent { + constructor(props) { super(props); - (this: any)._onChange = this._onChange.bind(this); + this._onChange = this._onChange.bind(this); } componentDidMount() { @@ -65,65 +71,75 @@ export class Checkbox extends React.PureComponent { this._setIndeterminate(); } - _onChange(event: SyntheticEvent<>) { - const isChecked = ((event.target: any): HTMLInputElement).checked; + _onChange(event) { + const isChecked = event.target.checked; this.props.onChange.call(null, isChecked); } - /* * Syncs the `indeterminate` prop to the underlying ``. `indeterminate` is intentionally * not settable via HTML; it must be done on the `HTMLInputElement` instance in script. * * @see https://www.w3.org/TR/html5/forms.html#the-input-element */ - _setIndeterminate(): void { + + + _setIndeterminate() { if (this._input == null) { return; } + this._input.indeterminate = this.props.indeterminate; } - render(): React.Node { + render() { const { checked, className, disabled, // eslint-disable-next-line no-unused-vars - indeterminate, // exclude `indeterminate` from `remainingProps` + indeterminate, + // exclude `indeterminate` from `remainingProps` label, onClick, tooltip, title, - onMouseDown, + onMouseDown } = this.props; - - const ref = tooltip ? addTooltip(tooltip) : null; - const text = - label === '' ? null : ( - {label} - ); - return ( - - ); + const ref = tooltip ? (0, _addTooltip().default)(tooltip) : null; + const text = label === '' ? null : React.createElement("span", { + className: "nuclide-ui-checkbox-label-text" + }, " ", label); + return React.createElement("label", { + className: (0, _classnames().default)(className, 'nuclide-ui-checkbox-label', { + 'nuclide-ui-checkbox-disabled': disabled + }) // eslint-disable-next-line nuclide-internal/jsx-simple-callback-refs + , + ref: ref, + onClick: onClick && (0, _ignoreTextSelectionEvents().default)(onClick), + title: title + }, React.createElement("input", { + checked: checked, + className: "input-checkbox nuclide-ui-checkbox", + disabled: disabled, + onChange: this._onChange, + onMouseDown: onMouseDown, + ref: el => { + this._input = el; + }, + type: "checkbox" + }), text); } + } + +exports.Checkbox = Checkbox; +Checkbox.defaultProps = { + disabled: false, + indeterminate: false, + label: '', + + onClick(event) {}, + + onMouseDown(event) {} + +}; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/ClickOutsideBoundary.js b/modules/nuclide-commons-ui/ClickOutsideBoundary.js index 770e8a12..3a60b76d 100644 --- a/modules/nuclide-commons-ui/ClickOutsideBoundary.js +++ b/modules/nuclide-commons-ui/ClickOutsideBoundary.js @@ -1,75 +1,80 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import * as React from 'react'; -import {findDOMNode} from 'react-dom'; - -type Props = { - onClickOutside: ?() => mixed, - children: ?React.Element, -}; - -export default class ClickOutsideBoundary extends React.Component { - _lastInternalEvent: ?MouseEvent; - _node: null | Element | Text; - - constructor(props: Props) { +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = require("react-dom"); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +class ClickOutsideBoundary extends React.Component { + constructor(props) { super(props); + + this._handleDocumentClick = e => { + // A more straight-forward approach would be to use + // `this._node.contains(e.target)`, however that fails in the edge case were + // some other event handler causes the target to be removed from the DOM + // before the event reaches the document root. So instead, we use this + // reference comparison approach which works for all cases where an event + // passed trough the boundary node, and makes it all the way to the document + // root. + if (e !== this._lastInternalEvent) { + if (this.props.onClickOutside != null) { + this.props.onClickOutside(); + } + } + + this._lastInternalEvent = null; + }; + + this._handleInternalClick = e => { + this._lastInternalEvent = e; + }; + this._lastInternalEvent = null; this._node = null; } componentDidMount() { - const node = (this._node = findDOMNode(this)); + const node = this._node = (0, _reactDom.findDOMNode)(this); + if (node == null) { return; } - window.document.addEventListener('click', this._handleDocumentClick); - // We use an actual DOM node (via refs) because React does not gaurnetee + + window.document.addEventListener('click', this._handleDocumentClick); // We use an actual DOM node (via refs) because React does not gaurnetee // any particular event ordering between synthentic events and native // events, and we require that the internal event fire before the global event. // https://discuss.reactjs.org/t/ordering-of-native-and-react-events/829/2 + node.addEventListener('click', this._handleInternalClick); } - _handleDocumentClick = (e: MouseEvent) => { - // A more straight-forward approach would be to use - // `this._node.contains(e.target)`, however that fails in the edge case were - // some other event handler causes the target to be removed from the DOM - // before the event reaches the document root. So instead, we use this - // reference comparison approach which works for all cases where an event - // passed trough the boundary node, and makes it all the way to the document - // root. - if (e !== this._lastInternalEvent) { - if (this.props.onClickOutside != null) { - this.props.onClickOutside(); - } - } - this._lastInternalEvent = null; - }; - - _handleInternalClick = (e: MouseEvent) => { - this._lastInternalEvent = e; - }; - componentWillUnmount() { window.document.removeEventListener('click', this._handleDocumentClick); + if (this._node != null) { this._node.removeEventListener('click', this._handleInternalClick); } } render() { - const {onClickOutside, ...passThroughProps} = this.props; - return
    ; + const _this$props = this.props, + { + onClickOutside + } = _this$props, + passThroughProps = _objectWithoutProperties(_this$props, ["onClickOutside"]); + + return React.createElement("div", passThroughProps); } + } + +exports.default = ClickOutsideBoundary; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/CodeSnippet.js b/modules/nuclide-commons-ui/CodeSnippet.js index d5e4614d..eccb1060 100644 --- a/modules/nuclide-commons-ui/CodeSnippet.js +++ b/modules/nuclide-commons-ui/CodeSnippet.js @@ -1,3 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.CodeSnippet = void 0; + +function _AtomInput() { + const data = require("./AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +27,22 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {AtomInput} from './AtomInput'; -import invariant from 'assert'; -import * as React from 'react'; - -type Props = { - text: string, - grammar?: atom$Grammar, - highlights?: Array, - startLine: number, - endLine: number, - onClick: (event: SyntheticMouseEvent<>) => mixed, - onLineClick: (event: SyntheticMouseEvent<>, line: number) => mixed, -}; - -export class CodeSnippet extends React.Component { - _editor: ?AtomInput; - _ongoingSelection: ?atom$Selection; - +class CodeSnippet extends React.Component { componentDidMount() { - invariant(this._editor != null); + if (!(this._editor != null)) { + throw new Error("Invariant violation: \"this._editor != null\""); + } + const editor = this._editor.getTextEditor(); - const {grammar, highlights, startLine} = this.props; + + const { + grammar, + highlights, + startLine + } = this.props; if (grammar) { editor.setGrammar(grammar); @@ -39,69 +50,68 @@ export class CodeSnippet extends React.Component { if (highlights != null) { highlights.forEach(range => { - const marker = editor.markBufferRange([ - [range.start.row - startLine, range.start.column], - [range.end.row - startLine, range.end.column], - ]); + const marker = editor.markBufferRange([[range.start.row - startLine, range.start.column], [range.end.row - startLine, range.end.column]]); editor.decorateMarker(marker, { type: 'highlight', - class: 'code-snippet-highlight', + class: 'code-snippet-highlight' }); - }); + }); // Make sure at least one highlight is visible. - // Make sure at least one highlight is visible. if (highlights.length > 0) { - editor.scrollToBufferPosition([ - highlights[0].end.row - startLine + 1, - highlights[0].end.column, - ]); + editor.scrollToBufferPosition([highlights[0].end.row - startLine + 1, highlights[0].end.column]); } } } - render(): React.Node { + render() { const lineNumbers = []; + for (let i = this.props.startLine; i <= this.props.endLine; i++) { - lineNumbers.push( -
    this.props.onLineClick(evt, i)}> - {i + 1} -
    , - ); + lineNumbers.push(React.createElement("div", { + key: i, + className: "nuclide-ui-code-snippet-line-number", + onClick: evt => this.props.onLineClick(evt, i) + }, i + 1)); } - return ( -
    -
    - {lineNumbers} -
    - { - this._editor = input; - }} - initialValue={this.props.text} - onMouseDown={e => { - this._ongoingSelection = null; - }} - onDidChangeSelectionRange={e => { - this._ongoingSelection = e.selection; - }} - onClick={e => { - // If the user selected a range, cancel the `onClick` behavior - // to enable copying the selection. - let shouldCancel = false; - if (this._ongoingSelection != null) { - const {start, end} = this._ongoingSelection.getBufferRange(); - shouldCancel = start.compare(end) !== 0; - } - if (!shouldCancel) { - this.props.onClick(e); - } - this._ongoingSelection = null; - }} - /> -
    - ); + + return React.createElement("div", { + className: "nuclide-ui-code-snippet" + }, React.createElement("div", { + className: "nuclide-ui-code-snippet-line-number-column" + }, lineNumbers), React.createElement(_AtomInput().AtomInput, { + ref: input => { + this._editor = input; + }, + initialValue: this.props.text, + onMouseDown: e => { + this._ongoingSelection = null; + }, + onDidChangeSelectionRange: e => { + this._ongoingSelection = e.selection; + }, + onClick: e => { + // If the user selected a range, cancel the `onClick` behavior + // to enable copying the selection. + let shouldCancel = false; + + if (this._ongoingSelection != null) { + const { + start, + end + } = this._ongoingSelection.getBufferRange(); + + shouldCancel = start.compare(end) !== 0; + } + + if (!shouldCancel) { + this.props.onClick(e); + } + + this._ongoingSelection = null; + } + })); } + } + +exports.CodeSnippet = CodeSnippet; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/DragResizeContainer.js b/modules/nuclide-commons-ui/DragResizeContainer.js index 349bea90..72f10a5f 100644 --- a/modules/nuclide-commons-ui/DragResizeContainer.js +++ b/modules/nuclide-commons-ui/DragResizeContainer.js @@ -1,3 +1,38 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DragResizeContainer = void 0; + +var React = _interopRequireWildcard(require("react")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,118 +41,97 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import * as React from 'react'; -import {Observable, Subject} from 'rxjs'; -import nullthrows from 'nullthrows'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -type Props = { - children?: React.Node, -}; - -type State = { - height: ?number, - isDragging: boolean, - lastMouseDown: number, -}; - -export class DragResizeContainer extends React.Component { - _disposables: UniversalDisposable; - _resizeStarts: Subject>; - _node: ?HTMLDivElement; - - constructor(props: Props) { +class DragResizeContainer extends React.Component { + constructor(props) { super(props); - this._resizeStarts = new Subject(); + this._resizeStarts = new _RxMin.Subject(); this.state = { height: null, isDragging: false, - lastMouseDown: 0, + lastMouseDown: 0 }; } - componentDidMount(): void { - const el = nullthrows(this._node); - - this._disposables = new UniversalDisposable( - this._resizeStarts - .switchMap(startEvent => { - // Only fire on primary mouse button - if (startEvent.button !== 0) { - return Observable.empty(); - } - - // Abort everything if double click - const now = Date.now(); - if (now - this.state.lastMouseDown < 500) { - this.setState({ - height: null, - isDragging: false, - lastMouseDown: now, - }); - return Observable.empty(); - } - - this.setState({isDragging: true, lastMouseDown: now}); - const startY = startEvent.pageY; - const startHeight = el.getBoundingClientRect().height; - return Observable.fromEvent(document, 'mousemove') - .takeUntil(Observable.fromEvent(document, 'mouseup')) - .map(event => { - const change = event.pageY - startY; - return startHeight + change; - }) - .do({ - complete: () => this.setState({isDragging: false}), - }); + componentDidMount() { + const el = (0, _nullthrows().default)(this._node); + this._disposables = new (_UniversalDisposable().default)(this._resizeStarts.switchMap(startEvent => { + // Only fire on primary mouse button + if (startEvent.button !== 0) { + return _RxMin.Observable.empty(); + } // Abort everything if double click + + + const now = Date.now(); + + if (now - this.state.lastMouseDown < 500) { + this.setState({ + height: null, + isDragging: false, + lastMouseDown: now + }); + return _RxMin.Observable.empty(); + } + + this.setState({ + isDragging: true, + lastMouseDown: now + }); + const startY = startEvent.pageY; + const startHeight = el.getBoundingClientRect().height; + return _RxMin.Observable.fromEvent(document, 'mousemove').takeUntil(_RxMin.Observable.fromEvent(document, 'mouseup')).map(event => { + const change = event.pageY - startY; + return startHeight + change; + }).do({ + complete: () => this.setState({ + isDragging: false }) - .subscribe(height => this.setState({height})), - atom.commands.add(el, 'resize-container:reset-height', () => - this.setState({height: null}), - ), - atom.contextMenu.add({ - '.nuclide-ui-drag-resize-container': [ - { - label: 'Reset Height', - command: 'resize-container:reset-height', - }, - ], - }), - ); + }); + }).subscribe(height => this.setState({ + height + })), atom.commands.add(el, 'resize-container:reset-height', () => this.setState({ + height: null + })), atom.contextMenu.add({ + '.nuclide-ui-drag-resize-container': [{ + label: 'Reset Height', + command: 'resize-container:reset-height' + }] + })); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - render(): React.Node { - const {height, isDragging} = this.state; + render() { + const { + height, + isDragging + } = this.state; const style = {}; + if (height == null) { style.maxHeight = '20vh'; } else { style.height = height; } - return ( -
    (this._node = node)}> - {this.props.children} -
    this._resizeStarts.next(event)}> -
    - {isDragging ? ( -
    - ) : null} -
    -
    - ); + return React.createElement("div", { + className: "nuclide-ui-drag-resize-container", + style: style, + ref: node => this._node = node + }, this.props.children, React.createElement("div", { + className: "nuclide-ui-drag-resize-container-handle", + onMouseDown: event => this._resizeStarts.next(event) + }, React.createElement("div", { + className: "nuclide-ui-drag-resize-container-handle-line" + }), isDragging ? React.createElement("div", { + className: "nuclide-ui-drag-resize-container-handle-overlay" + }) : null)); } + } + +exports.DragResizeContainer = DragResizeContainer; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Dropdown.example.js b/modules/nuclide-commons-ui/Dropdown.example.js index 4f39c874..74a133d3 100644 --- a/modules/nuclide-commons-ui/Dropdown.example.js +++ b/modules/nuclide-commons-ui/Dropdown.example.js @@ -1,3 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DropdownExamples = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Dropdown() { + const data = require("./Dropdown"); + + _Dropdown = function () { + return data; + }; + + return data; +} + +function _SplitButtonDropdown() { + const data = require("./SplitButtonDropdown"); + + _SplitButtonDropdown = function () { + return data; + }; + + return data; +} + +function _ModalMultiSelect() { + const data = require("./ModalMultiSelect"); + + _ModalMultiSelect = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,97 +47,112 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* global alert */ - -import * as React from 'react'; -import {Dropdown} from './Dropdown'; -import {SplitButtonDropdown} from './SplitButtonDropdown'; -import {ModalMultiSelect} from './ModalMultiSelect'; - const DropdownExample = (() => { - const options = [ - {value: 1, label: 'One'}, - {value: 2, label: 'Two'}, - {value: 3, label: 'Three'}, - {value: 4, label: 'Four'}, - ]; - return (): React.Element => ( -
    - -
    - ); + const options = [{ + value: 1, + label: 'One' + }, { + value: 2, + label: 'Two' + }, { + value: 3, + label: 'Three' + }, { + value: 4, + label: 'Four' + }]; + return () => React.createElement("div", null, React.createElement(_Dropdown().Dropdown, { + options: options, + value: 2 + })); })(); const SplitButtonDropdownExample = (() => { - const options = [ - {value: 1, label: 'Build', icon: 'tools'}, - {value: 2, label: 'Run', icon: 'triangle-right', selectedLabel: 'Run It!'}, - {value: 3, label: 'Rocket', icon: 'rocket'}, - {type: 'separator'}, - {value: 4, label: 'Squirrel', icon: 'squirrel'}, - {value: 5, label: 'Beaker', icon: 'telescope', disabled: true}, - ]; - return (): React.Element => ( -
    - alert(`You selected ${x}!`) - } - /> -
    - ); + const options = [{ + value: 1, + label: 'Build', + icon: 'tools' + }, { + value: 2, + label: 'Run', + icon: 'triangle-right', + selectedLabel: 'Run It!' + }, { + value: 3, + label: 'Rocket', + icon: 'rocket' + }, { + type: 'separator' + }, { + value: 4, + label: 'Squirrel', + icon: 'squirrel' + }, { + value: 5, + label: 'Beaker', + icon: 'telescope', + disabled: true + }]; + return () => React.createElement("div", null, React.createElement(_SplitButtonDropdown().SplitButtonDropdown, { + options: options, + value: 2, + onConfirm: // eslint-disable-next-line no-alert + x => alert(`You selected ${x}!`) + })); })(); -class ModalMultiSelectExample extends React.Component< - void, - {value: Array}, -> { - constructor(props: void) { +class ModalMultiSelectExample extends React.Component { + constructor(props) { super(props); - this.state = {value: [2]}; + this.state = { + value: [2] + }; } - render(): React.Node { - const options = [ - {value: 1, label: 'One'}, - {value: 2, label: 'Two'}, - {value: 3, label: 'Three'}, - {value: 4, label: 'Four'}, - ]; - return ( - { - this.setState({value}); - }} - value={this.state.value} - /> - ); + render() { + const options = [{ + value: 1, + label: 'One' + }, { + value: 2, + label: 'Two' + }, { + value: 3, + label: 'Three' + }, { + value: 4, + label: 'Four' + }]; + return React.createElement(_ModalMultiSelect().ModalMultiSelect, { + options: options, + onChange: value => { + this.setState({ + value + }); + }, + value: this.state.value + }); } + } -export const DropdownExamples = { +const DropdownExamples = { sectionName: 'Dropdowns', description: 'For selecting things.', - examples: [ - { - title: 'Dropdown', - component: DropdownExample, - }, - { - title: 'Split Button Dropdown', - component: SplitButtonDropdownExample, - }, - { - title: 'Modal Multi-Select', - component: ModalMultiSelectExample, - }, - ], + examples: [{ + title: 'Dropdown', + component: DropdownExample + }, { + title: 'Split Button Dropdown', + component: SplitButtonDropdownExample + }, { + title: 'Modal Multi-Select', + component: ModalMultiSelectExample + }] }; +exports.DropdownExamples = DropdownExamples; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Dropdown.js b/modules/nuclide-commons-ui/Dropdown.js index 737fe238..3bc9ea0a 100644 --- a/modules/nuclide-commons-ui/Dropdown.js +++ b/modules/nuclide-commons-ui/Dropdown.js @@ -1,3 +1,55 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DropdownButton = DropdownButton; +Object.defineProperty(exports, "ButtonSizes", { + enumerable: true, + get: function () { + return _Button().ButtonSizes; + } +}); +exports.Dropdown = void 0; + +function _Button() { + const data = require("./Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _Icon() { + const data = require("./Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var _electron = _interopRequireDefault(require("electron")); + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,90 +58,34 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const { + remote +} = _electron.default; -import type {IconName} from './Icon'; -import type {ButtonType} from './Button'; - -import {Button, ButtonSizes} from './Button'; -import {Icon} from './Icon'; -import classnames from 'classnames'; -import invariant from 'assert'; -import electron from 'electron'; -import * as React from 'react'; - -const {remote} = electron; -invariant(remote != null); - -// For backwards compat, we have to do some conversion here. -type ShortButtonSize = 'xs' | 'sm' | 'lg'; -type ButtonSize = 'EXTRA_SMALL' | 'SMALL' | 'LARGE'; - -type Separator = { - type: 'separator', -}; +if (!(remote != null)) { + throw new Error("Invariant violation: \"remote != null\""); +} // For backwards compat, we have to do some conversion here. -export type MenuItem = { - type?: void, - value: any, - label: string, - selectedLabel?: string, - submenu?: void, - icon?: IconName, - iconset?: string, - disabled?: boolean, -}; -type SubMenuItem = { - type: 'submenu', - label: string, - submenu: Array
    - - ); -}; - -class SortableTableExample extends React.Component< - mixed, - { - rows: Array, - sortDescending: boolean, - sortedColumn: ?string, - }, -> { - constructor(props: mixed) { - super(props); - const rows = [ - { - data: { - first: 1, - second: 3, - third: 300, - }, - }, - { - data: { - first: 2, - second: 5, - third: 200, - }, - }, - { - className: 'nuclide-ui-custom-classname-example', - data: { - first: 3, - second: 4, - third: 100, - }, - }, - ]; + first: 3, + second: 4, + third: 100 + } + }]; this.state = { sortDescending: false, sortedColumn: null, - rows, + rows }; - (this: any)._handleSort = this._handleSort.bind(this); + this._handleSort = this._handleSort.bind(this); } - _handleSort(sortedColumn: ?string, sortDescending: boolean): void { + _handleSort(sortedColumn, sortDescending) { // TODO: (wbinnssmith) T30771435 this setState depends on current state // and should use an updater function rather than an object // eslint-disable-next-line react/no-access-state-in-setstate @@ -132,81 +139,64 @@ class SortableTableExample extends React.Component< this.setState({ rows: sortedRows, sortedColumn, - sortDescending, + sortDescending }); } - render(): React.Node { - const columns = [ - { - title: 'first', - key: 'first', - }, - { - title: 'second', - key: 'second', - }, - { - title: 'third', - key: 'third', - }, - ]; - return ( - -
    ( -
    An optional, custom "empty message" component.
    - )} - columns={columns} - rows={this.state.rows} - sortable={true} - onSort={this._handleSort} - sortedColumn={this.state.sortedColumn} - sortDescending={this.state.sortDescending} - /> - - ); + render() { + const columns = [{ + title: 'first', + key: 'first' + }, { + title: 'second', + key: 'second' + }, { + title: 'third', + key: 'third' + }]; + return React.createElement(_Block().Block, null, React.createElement(_Table().Table, { + emptyComponent: () => React.createElement("div", null, "An optional, custom \"empty message\" component."), + columns: columns, + rows: this.state.rows, + sortable: true, + onSort: this._handleSort, + sortedColumn: this.state.sortedColumn, + sortDescending: this.state.sortDescending + })); } + } -const EmptyTableExample = (): React.Element => { - const columns = [ - { - title: 'first column', - key: 'first', - }, - { - title: 'second column', - key: 'second', - }, - { - title: 'third column', - key: 'third', - }, - ]; +const EmptyTableExample = () => { + const columns = [{ + title: 'first column', + key: 'first' + }, { + title: 'second column', + key: 'second' + }, { + title: 'third column', + key: 'third' + }]; const rows = []; - return ( - -
    - - ); + return React.createElement(_Block().Block, null, React.createElement(_Table().Table, { + columns: columns, + rows: rows + })); }; -export const TableExamples = { +const TableExamples = { sectionName: 'Table', description: '', - examples: [ - { - title: 'Simple Table', - component: TableExample, - }, - { - title: 'Sortable Table', - component: SortableTableExample, - }, - { - title: 'Empty Table', - component: EmptyTableExample, - }, - ], + examples: [{ + title: 'Simple Table', + component: TableExample + }, { + title: 'Sortable Table', + component: SortableTableExample + }, { + title: 'Empty Table', + component: EmptyTableExample + }] }; +exports.TableExamples = TableExamples; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Table.js b/modules/nuclide-commons-ui/Table.js index c8ecf942..247a03b9 100644 --- a/modules/nuclide-commons-ui/Table.js +++ b/modules/nuclide-commons-ui/Table.js @@ -1,3 +1,100 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports._calculateColumnWidths = _calculateColumnWidths; +exports._calculatePreferredColumnWidths = _calculatePreferredColumnWidths; +exports.Table = void 0; + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _shallowequal() { + const data = _interopRequireDefault(require("shallowequal")); + + _shallowequal = function () { + return data; + }; + + return data; +} + +function _Icon() { + const data = require("./Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _observableDom() { + const data = require("./observable-dom"); + + _observableDom = function () { + return data; + }; + + return data; +} + +function _scrollIntoView() { + const data = require("./scrollIntoView"); + + _scrollIntoView = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,183 +103,14 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import nullthrows from 'nullthrows'; -import classnames from 'classnames'; -import * as React from 'react'; -import {Observable, Subject} from 'rxjs'; -import shallowEqual from 'shallowequal'; -import {Icon} from './Icon'; -import { - areSetsEqual, - objectMapValues, - objectFromPairs, - arrayEqual, -} from 'nuclide-commons/collection'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {ResizeObservable} from './observable-dom'; -import {scrollIntoViewIfNeeded} from './scrollIntoView'; - -type SelectionEvent = SyntheticEvent<*> | Event; - const DEFAULT_MIN_COLUMN_WIDTH = 40; -const DefaultEmptyComponent = () => ( -
    Empty table
    -); - -export type Column = { - title: string, - key: $Keys, - // Percentage. The `width`s of all columns must add up to 1. - width?: number, - // Optional React component for rendering cell contents. - // The component receives the cell value via `props.data`. - component?: React.ComponentType, - shouldRightAlign?: boolean, - // A class to add to the cell. This will be added to both the header and body; you can - // differentiate between them with `.nuclide-ui-table-header-cell` and - // `.nuclide-ui-table-body-cell`. - cellClassName?: string, - // A minimum width (in pixels) for the column. - minWidth?: number, -}; - -export type Row = { - className?: string, - data: T, - rowAttributes?: Object, -}; - -type PercentageWidthMap = {[key: $Keys]: number}; -// Same shape; the separate type is just used for documentation--Flow doesn't recognize a -// difference. -type PixelWidthMap = {[key: $Keys]: number}; - -type Props = { - /** - * Optional classname for the entire table. - */ - className?: string, - /** - * Optional max-height for the body container. - * Useful for making the table scrollable while keeping the header fixed. - */ - maxBodyHeight?: string, - columns: Array>, - rows: Array>, - /** - * Whether to shade even and odd items differently. Default behavior is `true`. - */ - alternateBackground?: number, - /** - * Whether column widths can be resized interactively via drag&drop. Default behavior is `true`. - */ - resizable?: boolean, - children?: React.Element, - /** - * Whether columns can be sorted. - * If specified, `onSort`, `sortedColumn`, and `sortDescending` must also be specified. - */ - sortable?: boolean, - onSort?: (sortedBy: $Keys, sortDescending: boolean) => void, - sortedColumn?: ?$Keys, - sortDescending?: boolean, - /** - * Whether items can be selected. - * If specified, `onSelect` must also be specified. - */ - selectable?: boolean | ((row: T) => boolean), - selectedIndex?: ?number, - /** - * Handler to be called upon selection. Called iff `selectable` is `true`. We pass along the event - * because some consumers may want to take different action depending on it. For example, if you - * click on a row in the diagnostics table, we know you want to go to that diagnostic; if you - * select it with the keyboard, you may just be doing so incidentally while moving the selection - * to another row. - */ - onSelect?: ( - selectedItem: any, - selectedIndex: number, - event: SelectionEvent, - ) => mixed, - /** - * Callback to be invoked before calling onSelect. Called iff `selectable` is `true`. - * If this callback returns false, row selection is canceled. - */ - onWillSelect?: ( - selectedItem: any, - selectedIndex: number, - event: SelectionEvent, - ) => boolean, - - /** - * Called when a row selection is confirmed. Currently, this is done either by triggering - * "core:confirm" while an item is selected or by single clicking (which selects and confirms). - * In the future, we may consider moving single click to select-only and requiring double click - * for confirmation. - */ - onConfirm?: (selectedItem: any, selectedIndex: number) => mixed, - - onBodyBlur?: (event: SyntheticEvent<*>) => mixed, - onBodyFocus?: (event: SyntheticEvent<*>) => mixed, - - /** - * Optional React Component to override the default message when zero rows are provided. - * Useful for showing loading spinners and custom messages. - */ - emptyComponent?: React.ComponentType, - /** - * Whether a table row will be collapsed if its content is too large - */ - collapsable?: boolean, - /** - * Whether there's a header title spanning all cells instead of the column titles. - * It disables the 'sortable' prop. - */ - headerTitle?: string, - /** - * Optional HTMLElement to render a custom table header. Takes precedence over - * headerTitle. - */ - headerElement?: React.Node, - - /** - * Should keyboard navigation be enabled? This option exists for historical purposes. Ideally it - * would always be enabled, however, some locations require the "native-key-bindings" class-- - * usually to enable copying to the clipboard--which prevents Atom commands from firing. - * TODO: Find an alternative means of enabling copying in those locations, always enable keyboard - * navigation, and remove this prop. - */ - enableKeyboardNavigation?: ?boolean, -}; - -type ResizeOffset = {| - deltaPx: number, // In pixels - resizerLocation: number, // The column after which the resizer is located. -|}; - -type State = {| - tableWidth: number, - - // The user's preferred column distributions. These do not take into account the minimum widths. - preferredColumnWidths: PercentageWidthMap, - - // During a drag operation, specifies the change the user desires in the column size (and to which - // column). This is kept as a separate piece of state from the calculated widths and only combined - // with them in `render()` so that we can recalculate widths if the table changes size. We could - // also support cancelation of the resize (though we don't yet). - resizeOffset: ?ResizeOffset, - - // It's awkward to have hover styling when you're using keyboard navigation and the mouse just - // happens to be over a row. Therefore, we'll keep track of when you use keyboard navigation and - // will disable the hover state until you move the mouse again. - usingKeyboard: boolean, -|}; +const DefaultEmptyComponent = () => React.createElement("div", { + className: "nuclide-ui-table-empty-message" +}, "Empty table"); /** * Design concerns: @@ -211,265 +139,274 @@ type State = {| * our table may behave a little strangely when the available area is less than the sum of the * minimum widths of the columns. (Ideally, the table would scroll horizontally in this case.) */ -export class Table extends React.Component, State> { - _mouseMoveDisposable: ?IDisposable; - _rootNode: ?HTMLDivElement; - _disposables: UniversalDisposable; - _tableBody: ?HTMLElement; - - _resizeStarts: Subject<{ - event: SyntheticMouseEvent<*>, - resizerLocation: number, - }>; - - constructor(props: Props) { +class Table extends React.Component { + constructor(props) { super(props); - this._resizeStarts = new Subject(); + this._resizeStarts = new _RxMin.Subject(); this.state = { preferredColumnWidths: getInitialPreferredColumnWidths(props.columns), resizeOffset: null, tableWidth: 0, - usingKeyboard: false, + usingKeyboard: false }; } - shouldComponentUpdate(nextProps: Props, nextState: State): boolean { + shouldComponentUpdate(nextProps, nextState) { // If the state changed, we need to re-render. - if (!shallowEqual(nextState, this.state)) { + if (!(0, _shallowequal().default)(nextState, this.state)) { return true; } - if (!shallowEqual(nextProps, this.props, compareCheapProps)) { + if (!(0, _shallowequal().default)(nextProps, this.props, compareCheapProps)) { return true; } - if (!arrayEqual(nextProps.columns, this.props.columns, shallowEqual)) { + if (!(0, _collection().arrayEqual)(nextProps.columns, this.props.columns, _shallowequal().default)) { return true; } - if (!arrayEqual(nextProps.rows, this.props.rows)) { + if (!(0, _collection().arrayEqual)(nextProps.rows, this.props.rows)) { return true; } return false; } - componentDidMount(): void { - const el = nullthrows(this._rootNode); - - this._disposables = new UniversalDisposable( - // Update the column widths when the table is resized. - new ResizeObservable(el) - .startWith((null: any)) - .map(() => el.offsetWidth) - .filter(tableWidth => tableWidth > 0) - .subscribe(tableWidth => { - this.setState({tableWidth}); - }), - this._resizeStarts - .switchMap(({event: startEvent, resizerLocation}) => { - const startX = startEvent.pageX; - return Observable.fromEvent(document, 'mousemove') - .takeUntil(Observable.fromEvent(document, 'mouseup')) - .map(event => ({ - deltaPx: event.pageX - startX, - resizerLocation, - })) - .concat(Observable.of(null)); - }) - .subscribe(resizeOffset => { - if (resizeOffset == null) { - // Finalize the resize by updating the user's preferred column widths to account for - // their action. Note that these preferences are only updated when columns are resized - // (NOT when the table is). This is important so that, if the user resizes the table - // such that a column is at its minimum width and then resizes the table back to its - // orignal size, their original column widths are restored. - const preferredColumnWidths = _calculatePreferredColumnWidths({ - currentWidths: this._calculateColumnWidths(), - // TODO: (wbinnssmith) T30771435 this setState depends on current state - // and should use an updater function rather than an object - // eslint-disable-next-line react/no-access-state-in-setstate - tableWidth: this.state.tableWidth, - minWidths: getMinWidths(this.props.columns), - }); + componentDidMount() { + const el = (0, _nullthrows().default)(this._rootNode); + this._disposables = new (_UniversalDisposable().default)( // Update the column widths when the table is resized. + new (_observableDom().ResizeObservable)(el).startWith(null).map(() => el.offsetWidth).filter(tableWidth => tableWidth > 0).subscribe(tableWidth => { + this.setState({ + tableWidth + }); + }), this._resizeStarts.switchMap(({ + event: startEvent, + resizerLocation + }) => { + const startX = startEvent.pageX; + return _RxMin.Observable.fromEvent(document, 'mousemove').takeUntil(_RxMin.Observable.fromEvent(document, 'mouseup')).map(event => ({ + deltaPx: event.pageX - startX, + resizerLocation + })).concat(_RxMin.Observable.of(null)); + }).subscribe(resizeOffset => { + if (resizeOffset == null) { + // Finalize the resize by updating the user's preferred column widths to account for + // their action. Note that these preferences are only updated when columns are resized + // (NOT when the table is). This is important so that, if the user resizes the table + // such that a column is at its minimum width and then resizes the table back to its + // orignal size, their original column widths are restored. + const preferredColumnWidths = _calculatePreferredColumnWidths({ + currentWidths: this._calculateColumnWidths(), + // TODO: (wbinnssmith) T30771435 this setState depends on current state + // and should use an updater function rather than an object + // eslint-disable-next-line react/no-access-state-in-setstate + tableWidth: this.state.tableWidth, + minWidths: getMinWidths(this.props.columns) + }); // Update the preferred distributions and end the resize. + + + this.setState({ + preferredColumnWidths, + resizeOffset: null + }); + } else { + this.setState({ + resizeOffset + }); + } + }), atom.commands.add(el, { + 'core:move-up': event => { + this.setState({ + usingKeyboard: true + }); - // Update the preferred distributions and end the resize. - this.setState({preferredColumnWidths, resizeOffset: null}); - } else { - this.setState({resizeOffset}); - } - }), - atom.commands.add(el, { - 'core:move-up': event => { - this.setState({usingKeyboard: true}); - this._moveSelection(-1, event); - }, - 'core:move-down': event => { - this.setState({usingKeyboard: true}); - this._moveSelection(1, event); - }, - 'core:confirm': event => { - this.setState({usingKeyboard: true}); - const {rows, selectedIndex, onConfirm} = this.props; - if (onConfirm == null || selectedIndex == null) { - return; - } - const selectedRow = rows[selectedIndex]; - const selectedItem = selectedRow && selectedRow.data; - if (selectedItem != null) { - onConfirm(selectedItem, selectedIndex); - } - }, - }), - () => { - if (this._mouseMoveDisposable != null) { - this._mouseMoveDisposable.dispose(); - } + this._moveSelection(-1, event); + }, + 'core:move-down': event => { + this.setState({ + usingKeyboard: true + }); + + this._moveSelection(1, event); }, - ); + 'core:confirm': event => { + this.setState({ + usingKeyboard: true + }); + const { + rows, + selectedIndex, + onConfirm + } = this.props; + + if (onConfirm == null || selectedIndex == null) { + return; + } + + const selectedRow = rows[selectedIndex]; + const selectedItem = selectedRow && selectedRow.data; + + if (selectedItem != null) { + onConfirm(selectedItem, selectedIndex); + } + } + }), () => { + if (this._mouseMoveDisposable != null) { + this._mouseMoveDisposable.dispose(); + } + }); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - componentDidUpdate(prevProps: Props, prevState: State): void { - if ( - this._tableBody != null && - this.props.selectedIndex != null && - this.props.selectedIndex !== prevProps.selectedIndex - ) { + componentDidUpdate(prevProps, prevState) { + if (this._tableBody != null && this.props.selectedIndex != null && this.props.selectedIndex !== prevProps.selectedIndex) { const selectedRow = this._tableBody.children[this.props.selectedIndex]; + if (selectedRow != null) { - scrollIntoViewIfNeeded(selectedRow); + (0, _scrollIntoView().scrollIntoViewIfNeeded)(selectedRow); } } + if (this.state.usingKeyboard !== prevState.usingKeyboard) { if (this._mouseMoveDisposable != null) { this._mouseMoveDisposable.dispose(); } + if (this.state.usingKeyboard) { - this._mouseMoveDisposable = new UniversalDisposable( - Observable.fromEvent(document, 'mousemove') - .take(1) - .subscribe(() => { - this.setState({usingKeyboard: false}); - }), - ); + this._mouseMoveDisposable = new (_UniversalDisposable().default)(_RxMin.Observable.fromEvent(document, 'mousemove').take(1).subscribe(() => { + this.setState({ + usingKeyboard: false + }); + })); } } } - focus(): void { + focus() { if (this._tableBody == null) { return; } + let el = document.activeElement; + while (el != null) { if (el === this._tableBody) { // Already focused! return; } + el = el.parentNode; } + this._tableBody.focus(); } - UNSAFE_componentWillReceiveProps(nextProps: Props): void { + UNSAFE_componentWillReceiveProps(nextProps) { // Did the columns change? If so, we need to recalculate the widths. const currentColumns = this.props.columns; const nextColumns = nextProps.columns; - if ( - nextColumns.length !== currentColumns.length || - // If the columns just changed order, we want to keep their widths. - !areSetsEqual( - new Set(currentColumns.map(column => column.key)), - new Set(nextColumns.map(column => column.key)), - ) - ) { + + if (nextColumns.length !== currentColumns.length || // If the columns just changed order, we want to keep their widths. + !(0, _collection().areSetsEqual)(new Set(currentColumns.map(column => column.key)), new Set(nextColumns.map(column => column.key)))) { this.setState({ - preferredColumnWidths: getInitialPreferredColumnWidths(nextColumns), + preferredColumnWidths: getInitialPreferredColumnWidths(nextColumns) }); } } - _moveSelection(offset: -1 | 1, event: SelectionEvent): void { - const {selectedIndex} = this.props; + _moveSelection(offset, event) { + const { + selectedIndex + } = this.props; + if (selectedIndex == null) { return; } - const nextSelectedIndex = Math.max( - 0, - Math.min(this.props.rows.length - 1, selectedIndex + offset), - ); + + const nextSelectedIndex = Math.max(0, Math.min(this.props.rows.length - 1, selectedIndex + offset)); + if (nextSelectedIndex === selectedIndex) { return; } - this._selectRow({index: nextSelectedIndex, event}); + + this._selectRow({ + index: nextSelectedIndex, + event + }); } - _selectRow(options: {| - index: number, - event: SelectionEvent, - confirm?: boolean, - |}): void { - const {index: selectedIndex, event, confirm} = options; - const {onSelect, onWillSelect, rows} = this.props; + _selectRow(options) { + const { + index: selectedIndex, + event, + confirm + } = options; + const { + onSelect, + onWillSelect, + rows + } = this.props; + if (onSelect == null) { return; } + const selectedRow = rows[selectedIndex]; const selectedItem = selectedRow.data; + if (onWillSelect != null) { if (onWillSelect(selectedItem, selectedIndex, event) === false) { return; } } + onSelect(selectedItem, selectedIndex, event); + if (confirm && this.props.onConfirm != null) { this.props.onConfirm(selectedItem, selectedIndex); } } - _handleSortByColumn(sortedBy: $Keys): void { - const {onSort, sortDescending, sortedColumn} = this.props; + _handleSortByColumn(sortedBy) { + const { + onSort, + sortDescending, + sortedColumn + } = this.props; + if (onSort == null) { return; } - onSort( - sortedBy, - sortDescending == null || sortedBy !== sortedColumn - ? false - : !sortDescending, - ); - } - // Just a bound version of the `_calculateColumnWidths` function for convenience. - _calculateColumnWidths(): PercentageWidthMap { + onSort(sortedBy, sortDescending == null || sortedBy !== sortedColumn ? false : !sortDescending); + } // Just a bound version of the `_calculateColumnWidths` function for convenience. + + + _calculateColumnWidths() { return _calculateColumnWidths({ preferredWidths: this.state.preferredColumnWidths, minWidths: getMinWidths(this.props.columns), tableWidth: this.state.tableWidth, columnOrder: this.props.columns.map(column => column.key), - resizeOffset: this.state.resizeOffset, + resizeOffset: this.state.resizeOffset }); } - _renderEmptyCellContent(): React.Element { - return
    ; + _renderEmptyCellContent() { + return React.createElement("div", null); } - render(): React.Node { - return ( -
    (this._rootNode = rootNode)}> - {this._renderContents()} -
    - ); + render() { + return React.createElement("div", { + className: this.props.className, + ref: rootNode => this._rootNode = rootNode + }, this._renderContents()); } - _renderContents(): React.Node { + _renderContents() { if (this.state.tableWidth === 0) { // We don't have the table width yet so we can't render the columns. return null; @@ -486,214 +423,227 @@ export class Table extends React.Component, State> { selectedIndex, sortable, sortedColumn, - sortDescending, + sortDescending } = this.props; const columnWidths = this._calculateColumnWidths(); - const header = - headerElement != null || headerTitle != null ? ( -
    - {headerElement != null ? headerElement : headerTitle} -
    - ) : ( - columns.map((column, i) => { - const {title, key, shouldRightAlign, cellClassName} = column; - let resizer; - if (i < columns.length - 1) { - resizer = ( -
    { - this._resizeStarts.next({event, resizerLocation: i}); - }} - onClick={(e: SyntheticMouseEvent<>) => { - // Prevent sortable column header click event from firing. - e.stopPropagation(); - }} - /> - ); - } - const width = columnWidths[key]; - const optionalHeaderCellProps = {}; - if (width != null) { - optionalHeaderCellProps.style = {width: `${width * 100}%`}; - } - let sortIndicator; - let titleOverlay = title; - if (sortable) { - optionalHeaderCellProps.onClick = () => { - this._handleSortByColumn(key); - }; - titleOverlay += ' – click to sort'; - if (sortedColumn === key) { - sortIndicator = ( - - - - ); - } + const header = headerElement != null || headerTitle != null ? React.createElement("div", { + className: "nuclide-ui-table-header-cell nuclide-ui-table-full-header" + }, headerElement != null ? headerElement : headerTitle) : columns.map((column, i) => { + const { + title, + key, + shouldRightAlign, + cellClassName + } = column; + let resizer; + + if (i < columns.length - 1) { + resizer = React.createElement("div", { + className: "nuclide-ui-table-header-resize-handle", + onMouseDown: event => { + this._resizeStarts.next({ + event, + resizerLocation: i + }); + }, + onClick: e => { + // Prevent sortable column header click event from firing. + e.stopPropagation(); } - return ( -
    - {title} - {sortIndicator} - {resizer} -
    - ); - }) - ); + }); + } + + const width = columnWidths[key]; + const optionalHeaderCellProps = {}; + + if (width != null) { + optionalHeaderCellProps.style = { + width: `${width * 100}%` + }; + } + + let sortIndicator; + let titleOverlay = title; + + if (sortable) { + optionalHeaderCellProps.onClick = () => { + this._handleSortByColumn(key); + }; + + titleOverlay += ' – click to sort'; + + if (sortedColumn === key) { + sortIndicator = React.createElement("span", { + className: "nuclide-ui-table-sort-indicator" + }, React.createElement(_Icon().Icon, { + icon: sortDescending ? 'triangle-down' : 'triangle-up' + })); + } + } + + return React.createElement("div", Object.assign({ + className: (0, _classnames().default)(cellClassName, { + 'nuclide-ui-table-cell-text-align-right': shouldRightAlign, + 'nuclide-ui-table-header-cell': true, + 'nuclide-ui-table-header-cell-sortable': sortable + }), + title: titleOverlay, + key: key + }, optionalHeaderCellProps), title, sortIndicator, resizer); + }); let body = rows.map((row, i) => { - const {className: rowClassName, data, rowAttributes} = row; + const { + className: rowClassName, + data, + rowAttributes + } = row; const renderedRow = columns.map((column, j) => { const { key, cellClassName, component: Component, - shouldRightAlign, + shouldRightAlign } = column; let datum = data[key]; + if (Component != null) { - datum = ; + datum = React.createElement(Component, { + data: datum + }); } else if (datum == null) { datum = this._renderEmptyCellContent(); } + const cellStyle = {}; const width = columnWidths[key]; + if (width != null) { cellStyle.width = `${width * 100}%`; } - return ( -
    - {datum} -
    - ); + + return React.createElement("div", Object.assign({ + className: (0, _classnames().default)(cellClassName, { + 'nuclide-ui-table-body-cell': true, + 'nuclide-ui-table-cell-text-align-right': shouldRightAlign + }), + key: j, + style: cellStyle, + title: typeof datum !== 'object' ? String(datum) : null + }, rowAttributes), datum); }); - const selectableRow = - typeof selectable === 'function' ? selectable(row.data) : selectable; - const rowProps = selectableRow - ? { - onClick: event => { - switch (event.detail) { - // This (`event.detail === 0`) shouldn't happen normally but does when the click is - // triggered by the integration test. - case 0: - case 1: - this._selectRow({index: i, event}); - return; - case 2: - // We need to check `event.detail` (instead of using `onDoubleClick`) because - // (for some reason) `onDoubleClick` is only firing sporadically. - // TODO: Figure out why. Repros in the diagnostic table with React 16.0.0 and - // Atom 1.22.0-beta1 (Chrome 56.0.2924.87). This may be because we're swapping out - // the component on the click so a different one is receiving the second? - this._selectRow({index: i, event, confirm: true}); - return; - } - }, + const selectableRow = typeof selectable === 'function' ? selectable(row.data) : selectable; + const rowProps = selectableRow ? { + onClick: event => { + switch (event.detail) { + // This (`event.detail === 0`) shouldn't happen normally but does when the click is + // triggered by the integration test. + case 0: + case 1: + this._selectRow({ + index: i, + event + }); + + return; + + case 2: + // We need to check `event.detail` (instead of using `onDoubleClick`) because + // (for some reason) `onDoubleClick` is only firing sporadically. + // TODO: Figure out why. Repros in the diagnostic table with React 16.0.0 and + // Atom 1.22.0-beta1 (Chrome 56.0.2924.87). This may be because we're swapping out + // the component on the click so a different one is receiving the second? + this._selectRow({ + index: i, + event, + confirm: true + }); + + return; } - : {}; + } + } : {}; const isSelectedRow = selectedIndex != null && i === selectedIndex; - return ( -
    - {renderedRow} -
    - ); + return React.createElement("div", Object.assign({ + className: (0, _classnames().default)(rowClassName, { + 'nuclide-ui-table-row': true, + 'nuclide-ui-table-row-selectable': selectableRow, + 'nuclide-ui-table-row-disabled': typeof selectable === 'function' && !selectableRow, + 'nuclide-ui-table-row-using-keyboard-nav': this.state.usingKeyboard, + 'nuclide-ui-table-row-selected': isSelectedRow, + 'nuclide-ui-table-row-alternate': alternateBackground !== false && i % 2 === 1, + 'nuclide-ui-table-collapsed-row': this.props.collapsable && !isSelectedRow + }), + "data-row-index": i, + key: i + }, rowProps), renderedRow); }); + if (rows.length === 0) { const EmptyComponent = this.props.emptyComponent || DefaultEmptyComponent; - body = ; + body = React.createElement(EmptyComponent, null); } + const scrollableBodyStyle = {}; + if (maxBodyHeight != null) { scrollableBodyStyle.maxHeight = maxBodyHeight; scrollableBodyStyle.overflowY = 'auto'; } - const bodyClassNames = classnames( - 'nuclide-ui-table', - 'nuclide-ui-table-body', - { - // Using native-key-bindings prevents the up and down arrows from being captured. - 'native-key-bindings': !this.props.enableKeyboardNavigation, - // Only enable text selection if the rows aren't selectable as these two things conflict. - // TODO: Add the ability to copy text that doesn't involve text selection within selections. - 'nuclide-ui-table-body-selectable-text': !this.props.selectable, + + const bodyClassNames = (0, _classnames().default)('nuclide-ui-table', 'nuclide-ui-table-body', { + // Using native-key-bindings prevents the up and down arrows from being captured. + 'native-key-bindings': !this.props.enableKeyboardNavigation, + // Only enable text selection if the rows aren't selectable as these two things conflict. + // TODO: Add the ability to copy text that doesn't involve text selection within selections. + 'nuclide-ui-table-body-selectable-text': !this.props.selectable + }); + return [React.createElement("div", { + key: "header", + className: "nuclide-ui-table" + }, React.createElement("div", { + className: "nuclide-ui-table-header" + }, header)), React.createElement("div", { + key: "body", + style: scrollableBodyStyle, + onFocus: event => { + if (this.props.onBodyFocus != null) { + this.props.onBodyFocus(event); + } }, - ); - return [ -
    -
    {header}
    -
    , -
    { - if (this.props.onBodyFocus != null) { - this.props.onBodyFocus(event); - } - }} - onBlur={event => { - if (this.props.onBodyBlur != null) { - this.props.onBodyBlur(event); - } - }}> -
    { - this._tableBody = el; - }} - className={bodyClassNames} - tabIndex="-1"> - {body} -
    -
    , - ]; + onBlur: event => { + if (this.props.onBodyBlur != null) { + this.props.onBodyBlur(event); + } + } + }, React.createElement("div", { + ref: el => { + this._tableBody = el; + }, + className: bodyClassNames, + tabIndex: "-1" + }, body))]; } -} +} /** * Get the initial size of each column as a percentage of the total. */ -function getInitialPreferredColumnWidths( - columns: Array>, -): PercentageWidthMap { + + +exports.Table = Table; + +function getInitialPreferredColumnWidths(columns) { const columnWidthRatios = {}; let assignedWidth = 0; const unresolvedColumns = []; columns.forEach(column => { - const {key, width} = column; + const { + key, + width + } = column; + if (width != null) { columnWidthRatios[key] = width; assignedWidth += width; @@ -708,91 +658,81 @@ function getInitialPreferredColumnWidths( return columnWidthRatios; } -function getMinWidths(columns: Array>): PixelWidthMap { +function getMinWidths(columns) { const minWidths = {}; columns.forEach(column => { - minWidths[column.key] = - column.minWidth == null ? DEFAULT_MIN_COLUMN_WIDTH : column.minWidth; + minWidths[column.key] = column.minWidth == null ? DEFAULT_MIN_COLUMN_WIDTH : column.minWidth; }); return minWidths; } - /** * Calculate widths, taking into account the preferred and minimum widths. Exported for testing * only. */ -export function _calculateColumnWidths(options: { - preferredWidths: PercentageWidthMap, - minWidths: PixelWidthMap, - tableWidth: number, - columnOrder: Array<$Keys>, - resizeOffset: ?ResizeOffset, -}): PercentageWidthMap { + + +function _calculateColumnWidths(options) { const { preferredWidths, minWidths: minWidthsPx, tableWidth, columnOrder, - resizeOffset: resizeOffset_, + resizeOffset: resizeOffset_ } = options; - const resizeOffset = resizeOffset_ || {deltaPx: 0, resizerLocation: 0}; - const widthsPx = {}; - - // Calculate the pixel widths of each column given its desired percentage width and minimum pixel + const resizeOffset = resizeOffset_ || { + deltaPx: 0, + resizerLocation: 0 + }; + const widthsPx = {}; // Calculate the pixel widths of each column given its desired percentage width and minimum pixel // width. + { // Figure out how many pixels each column wants, given the current available width. let widthToAllocate = tableWidth; let columnsToAllocate = columnOrder; + while (columnsToAllocate.length > 0 && widthToAllocate > 0) { - const remainingPct = columnsToAllocate - .map(columnName => preferredWidths[columnName]) - .reduce((a, b) => a + b, 0); - const desiredWidthsPx = objectFromPairs( - columnsToAllocate.map(columnName => { - const desiredPct = preferredWidths[columnName] / remainingPct; - const desiredPx = Math.round(desiredPct * widthToAllocate); - return [columnName, desiredPx]; - }), - ); + const remainingPct = columnsToAllocate.map(columnName => preferredWidths[columnName]).reduce((a, b) => a + b, 0); + const desiredWidthsPx = (0, _collection().objectFromPairs)(columnsToAllocate.map(columnName => { + const desiredPct = preferredWidths[columnName] / remainingPct; + const desiredPx = Math.round(desiredPct * widthToAllocate); + return [columnName, desiredPx]; + })); // Allocate widths for the columns who want less than their minimum width. - // Allocate widths for the columns who want less than their minimum width. let remainingPx = widthToAllocate; let remainingColumns = []; columnsToAllocate.forEach(columnName => { const desiredPx = desiredWidthsPx[columnName]; const minPx = minWidthsPx[columnName]; + if (minPx >= desiredPx) { widthsPx[columnName] = Math.min(minPx, remainingPx); remainingPx -= widthsPx[columnName]; } else { remainingColumns.push(columnName); } - }); + }); // If we didn't need to truncate any of the columns, give them all their desired width. - // If we didn't need to truncate any of the columns, give them all their desired width. if (columnsToAllocate.length === remainingColumns.length) { Object.assign(widthsPx, desiredWidthsPx); remainingColumns = []; - } - - // If we had to truncate any of the columns, that changes the calculations for how big the + } // If we had to truncate any of the columns, that changes the calculations for how big the // remaining columns want to be, so make another pass. + + widthToAllocate = remainingPx; columnsToAllocate = remainingColumns; } } - { // Adjust the column widths according to the resized column. - const {deltaPx, resizerLocation} = resizeOffset; + const { + deltaPx, + resizerLocation + } = resizeOffset; const leftColumns = columnOrder.slice(0, resizerLocation + 1); const rightColumns = columnOrder.slice(resizerLocation + 1); - - const [shrinkingColumns, growingColumn] = - deltaPx < 0 - ? [leftColumns.reverse(), rightColumns[0]] - : [rightColumns, leftColumns[leftColumns.length - 1]]; + const [shrinkingColumns, growingColumn] = deltaPx < 0 ? [leftColumns.reverse(), rightColumns[0]] : [rightColumns, leftColumns[leftColumns.length - 1]]; const targetChange = Math.abs(deltaPx); let cumulativeChange = 0; @@ -804,20 +744,21 @@ export function _calculateColumnWidths(options: { const change = Math.abs(startWidth - newWidth); cumulativeChange += change; widthsPx[columnName] = newWidth; + if (cumulativeChange >= targetChange) { break; } } widthsPx[growingColumn] += cumulativeChange; - } + } // Convert all the widths from pixels to percentages. - // Convert all the widths from pixels to percentages. const widths = {}; { let remainingWidth = 1; columnOrder.forEach((columnName, i) => { const isLastColumn = i === columnOrder.length - 1; + if (isLastColumn) { // Give the last column all the remaining to account for rounding issues. widths[columnName] = remainingWidth; @@ -827,34 +768,37 @@ export function _calculateColumnWidths(options: { } }); } - return widths; } - /** * Given the current (percentage) widths of each column, determines what user-preferred distribution * this represents. Exported for testing only. */ -export function _calculatePreferredColumnWidths(options: { - currentWidths: PercentageWidthMap, - tableWidth: number, - minWidths: PixelWidthMap, -}): PercentageWidthMap { - const {currentWidths, tableWidth, minWidths: minWidthsPx} = options; - const currentWidthsPx = objectMapValues(currentWidths, w => w * tableWidth); - - // If any column is at its minimum width, we take that to mean that the user wants the column + + +function _calculatePreferredColumnWidths(options) { + const { + currentWidths, + tableWidth, + minWidths: minWidthsPx + } = options; + const currentWidthsPx = (0, _collection().objectMapValues)(currentWidths, w => w * tableWidth); // If any column is at its minimum width, we take that to mean that the user wants the column // remain at its minimum if the table is resized (as opposed to maintaining the same percentage). // Accordingly, we make that column's preferred width 0. - const preferredColumnWidths = {}; + const preferredColumnWidths = {}; // Figure out which columns are at their minimum widths. - // Figure out which columns are at their minimum widths. let remainingPx = 0; // The width that isn't accounted for after minWidth. + const columnsNotAtMinimum = []; + for (const [columnName, widthPx] of Object.entries(currentWidthsPx)) { - invariant(typeof widthPx === 'number'); + if (!(typeof widthPx === 'number')) { + throw new Error("Invariant violation: \"typeof widthPx === 'number'\""); + } + const minWidthPx = minWidthsPx[columnName]; + if (Math.floor(widthPx) <= minWidthPx) { // Keep it at its min-width. preferredColumnWidths[columnName] = 0; @@ -862,12 +806,13 @@ export function _calculatePreferredColumnWidths(options: { remainingPx += widthPx; columnsNotAtMinimum.push([columnName, widthPx]); } - } + } // Now distribute the widths of the other columns. + - // Now distribute the widths of the other columns. let remainingPct = 1; columnsNotAtMinimum.forEach(([columnName, width], index) => { const isLastColumn = index === columnsNotAtMinimum.length - 1; + if (isLastColumn) { // We give the last column the remaining width just to be certain they all add up to 1. preferredColumnWidths[columnName] = remainingPct; @@ -876,26 +821,28 @@ export function _calculatePreferredColumnWidths(options: { remainingPct -= preferredColumnWidths[columnName]; } }); - return preferredColumnWidths; } - /** * An equality check for comparing Props using `shallowEqual()`. This only performs the cheap * checks and assumes that the rows and columns are equal. (They can be checked separatedly iff * necessary.) */ -function compareCheapProps(a: mixed, b: mixed, key: ?string): ?boolean { + + +function compareCheapProps(a, b, key) { switch (key) { case undefined: // This is a magic way of telling `shallowEqual()` to use the default comparison for the // props objects (inspect its members). return undefined; + case 'rows': case 'columns': // We'll check these later iff we need to since they're more expensive. return true; + default: return a === b; } -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Tabs.example.js b/modules/nuclide-commons-ui/Tabs.example.js index 8d47ef8e..257186aa 100644 --- a/modules/nuclide-commons-ui/Tabs.example.js +++ b/modules/nuclide-commons-ui/Tabs.example.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.TabExamples = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Block() { + const data = require("./Block"); + + _Block = function () { + return data; + }; + + return data; +} + +function _Tabs() { + const data = _interopRequireDefault(require("./Tabs")); + + _Tabs = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,79 +39,65 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const tabs = [{ + name: 'one', + tabContent: React.createElement("div", null, "One") +}, { + name: 'two', + tabContent: React.createElement("div", null, "Two") +}, { + name: 'three', + tabContent: React.createElement("div", null, "Three") +}, { + name: 'four', + tabContent: React.createElement("div", null, "Four") +}, { + name: 'five', + tabContent: React.createElement("div", null, "Five") +}]; -import * as React from 'react'; -import {Block} from './Block'; -import Tabs from './Tabs'; - -const tabs = [ - { - name: 'one', - tabContent:
    One
    , - }, - { - name: 'two', - tabContent:
    Two
    , - }, - { - name: 'three', - tabContent:
    Three
    , - }, - { - name: 'four', - tabContent:
    Four
    , - }, - { - name: 'five', - tabContent:
    Five
    , - }, -]; - -class TabExample extends React.Component { - constructor(props: any) { +class TabExample extends React.Component { + constructor(props) { super(props); + + this.handleTabChange = newTabName => { + this.setState({ + activeTabName: newTabName.name + }); + }; + this.state = { - activeTabName: 'one', + activeTabName: 'one' }; } - handleTabChange = (newTabName: { - name: string, - tabContent: React.Element, - }): void => { - this.setState({ - activeTabName: newTabName.name, - }); - }; - - render(): React.Node { - const {activeTabName} = this.state; - return ( - - -
    - Showing content for tab "{activeTabName}". -
    -
    - ); + render() { + const { + activeTabName + } = this.state; + return React.createElement(_Block().Block, null, React.createElement(_Tabs().default, { + tabs: tabs, + activeTabName: activeTabName, + triggeringEvent: "onClick", + onActiveTabChange: this.handleTabChange + }), React.createElement("div", { + style: { + padding: '2em 0 2em 0' + } + }, "Showing content for tab \"", activeTabName, "\".")); } + } -export const TabExamples = { +const TabExamples = { sectionName: 'Tabs', description: '', - examples: [ - { - title: '', - component: TabExample, - }, - ], + examples: [{ + title: '', + component: TabExample + }] }; +exports.TabExamples = TabExamples; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Tabs.js b/modules/nuclide-commons-ui/Tabs.js index a8a736fd..91d398de 100644 --- a/modules/nuclide-commons-ui/Tabs.js +++ b/modules/nuclide-commons-ui/Tabs.js @@ -1,3 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _Icon() { + const data = require("./Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,81 +49,57 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class Tabs extends React.Component { + constructor(...args) { + var _temp; -import type {IconName} from './Icon'; - -import {Icon} from './Icon'; -import * as React from 'react'; -import classnames from 'classnames'; -import nullthrows from 'nullthrows'; - -export type Tab = { - name: string, - icon?: IconName, - tabContent: React.Element, -}; - -type Props = { - tabs: Array, - activeTabName: ?string, - closeable: boolean, - onActiveTabChange: (tab: Tab) => void, - onClose?: () => void, - triggeringEvent: string, - growable?: boolean, -}; - -export default class Tabs extends React.Component { - static defaultProps = { - closeable: false, - triggeringEvent: 'onClick', - growable: false, - }; - - _handleTabChange = (selectedTabName: string) => { - if (typeof this.props.onActiveTabChange === 'function') { - this.props.onActiveTabChange( - nullthrows(this.props.tabs.find(tab => tab.name === selectedTabName)), - ); - } - }; - - _renderTabMenu = (): React.Element => { - const closeButton = this.props.closeable ? ( -
    - ) : null; - const tabs = this.props.tabs.map(tab => { - const icon = tab.icon == null ? null : ; - const handler = {}; - handler[this.props.triggeringEvent] = this._handleTabChange.bind( - this, - tab.name, - ); - return ( -
  • { + if (typeof this.props.onActiveTabChange === 'function') { + this.props.onActiveTabChange((0, _nullthrows().default)(this.props.tabs.find(tab => tab.name === selectedTabName))); + } + }, this._renderTabMenu = () => { + const closeButton = this.props.closeable ? React.createElement("div", { + className: "close-icon", + onClick: this.props.onClose + }) : null; + const tabs = this.props.tabs.map(tab => { + const icon = tab.icon == null ? null : React.createElement(_Icon().Icon, { + icon: tab.icon + }); + const handler = {}; + handler[this.props.triggeringEvent] = this._handleTabChange.bind(this, tab.name); + return React.createElement("li", Object.assign({ + className: (0, _classnames().default)({ tab: true, active: this.props.activeTabName === tab.name, - growable: this.props.growable, - })} - key={tab.name} - title={tab.name} - {...handler}> -
    - {icon} - {tab.tabContent} -
    - {closeButton} -
  • - ); - }); - return
      {tabs}
    ; - }; + growable: this.props.growable + }), + key: tab.name, + title: tab.name + }, handler), React.createElement("div", { + className: "title" + }, icon, tab.tabContent), closeButton); + }); + return React.createElement("ul", { + className: "tab-bar list-inline inset-panel" + }, tabs); + }, _temp; + } - render(): React.Node { - return
    {this._renderTabMenu()}
    ; + render() { + return React.createElement("div", { + className: "nuclide-tabs" + }, this._renderTabMenu()); } + } + +exports.default = Tabs; +Tabs.defaultProps = { + closeable: false, + triggeringEvent: 'onClick', + growable: false +}; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/TextEditorBanner.js b/modules/nuclide-commons-ui/TextEditorBanner.js index 6386021a..e7c0e5d8 100644 --- a/modules/nuclide-commons-ui/TextEditorBanner.js +++ b/modules/nuclide-commons-ui/TextEditorBanner.js @@ -1,3 +1,38 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Notice = exports.TextEditorBanner = void 0; + +function _Message() { + const data = require("./Message"); + + _Message = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,127 +41,101 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class TextEditorBanner { + constructor(editor) { + this.render = reactElement => { + this.renderUnstyled(React.createElement("div", { + className: "nuclide-ui-text-editor-banner-element" + }, reactElement)); + }; + + this.renderUnstyled = reactElement => { + _reactDom.default.render(React.createElement("div", { + className: "nuclide-ui-text-editor-banner" + }, reactElement, React.createElement("div", { + // eslint-disable-next-line nuclide-internal/jsx-simple-callback-refs + ref: ref => this._updateTextEditorElement(ref), + className: "nuclide-ui-text-editor-banner-editor" + })), this._element); + }; -import type {AtomTextEditor} from './AtomTextEditor'; -import type {MessageType} from './Message'; -import {Message} from './Message'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import invariant from 'assert'; - -export class TextEditorBanner { - _disposables: UniversalDisposable; - _editor: atom$TextEditor | AtomTextEditor; - _element: HTMLElement; - _editorElement: HTMLElement; - _marker: ?atom$Marker; - - constructor(editor: atom$TextEditor | AtomTextEditor) { this._editor = editor; const editorElement = editor.getElement().firstChild; this._element = document.createElement('div'); this._element.className = 'nuclide-ui-text-editor-banner-container'; - invariant( - editorElement instanceof HTMLElement && editorElement.parentNode != null, - ); + if (!(editorElement instanceof HTMLElement && editorElement.parentNode != null)) { + throw new Error("Invariant violation: \"editorElement instanceof HTMLElement && editorElement.parentNode != null\""); + } editorElement.parentNode.insertBefore(this._element, editorElement); this._editorElement = editorElement; - - this._disposables = new UniversalDisposable( - () => { - ReactDOM.unmountComponentAtNode(this._element); - this._element.replaceWith(editorElement); - }, - atom.workspace.observeActiveTextEditor(activeEditor => { - if (activeEditor == null) { - return; - } - if (activeEditor.getElement().contains(editor.getElement())) { - // This is needed for situations where the editor was rendered while - // display: none so _updateTextEditorElement wasn't able to properly - // measure at that time. - editor.getElement().measureDimensions(); - } - }), - ); + this._disposables = new (_UniversalDisposable().default)(() => { + _reactDom.default.unmountComponentAtNode(this._element); + + this._element.replaceWith(editorElement); + }, atom.workspace.observeActiveTextEditor(activeEditor => { + if (activeEditor == null) { + return; + } + + if (activeEditor.getElement().contains(editor.getElement())) { + // This is needed for situations where the editor was rendered while + // display: none so _updateTextEditorElement wasn't able to properly + // measure at that time. + editor.getElement().measureDimensions(); + } + })); } - dispose(): void { + dispose() { this._disposables.dispose(); } - _updateTextEditorElement(editorContainerRef: ?React.ElementRef<'div'>) { - const editorContainerNode = ReactDOM.findDOMNode(editorContainerRef); + _updateTextEditorElement(editorContainerRef) { + const editorContainerNode = _reactDom.default.findDOMNode(editorContainerRef); + if (editorContainerNode == null) { return; - } + } // Clear the previous child + - // Clear the previous child while (editorContainerNode.lastChild) { editorContainerNode.removeChild(editorContainerNode.lastChild); - } + } // And insert the new one instead + - // And insert the new one instead editorContainerNode.appendChild(this._editorElement); - this._editor.getElement().measureDimensions(); - // Fix for Hyperclicking a read-only file. + this._editor.getElement().measureDimensions(); // Fix for Hyperclicking a read-only file. // Restore the scroll position in the editor. - this._editor - .getElement() - .getModel() - .scrollToCursorPosition(); - } - render = (reactElement: React.Element): void => { - this.renderUnstyled( -
    - {reactElement} -
    , - ); - }; - renderUnstyled = (reactElement: React.Element): void => { - ReactDOM.render( -
    - {reactElement} -
    this._updateTextEditorElement(ref)} - className="nuclide-ui-text-editor-banner-editor" - /> -
    , - this._element, - ); - }; + this._editor.getElement().getModel().scrollToCursorPosition(); + } hide() { this.dispose(); } + } -type NoticeProps = { - messageType: MessageType, - children: React.Node, -}; +exports.TextEditorBanner = TextEditorBanner; -export class Notice extends React.Component { +class Notice extends React.Component { render() { - return ( -
    - -
    - {this.props.children} -
    -
    -
    - ); + return React.createElement("div", { + className: "nuclide-ui-text-editor-banner-notice" + }, React.createElement(_Message().Message, { + type: this.props.messageType + }, React.createElement("div", { + className: "nuclide-ui-text-editor-banner-notice-content" + }, this.props.children))); } + } + +exports.Notice = Notice; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/TextInputs.example.js b/modules/nuclide-commons-ui/TextInputs.example.js index 466153f3..3e119b77 100644 --- a/modules/nuclide-commons-ui/TextInputs.example.js +++ b/modules/nuclide-commons-ui/TextInputs.example.js @@ -1,3 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.TextInputExamples = void 0; + +var _atom = require("atom"); + +var React = _interopRequireWildcard(require("react")); + +function _Block() { + const data = require("./Block"); + + _Block = function () { + return data; + }; + + return data; +} + +function _AtomInput() { + const data = require("./AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +function _AtomTextEditor() { + const data = require("./AtomTextEditor"); + + _AtomTextEditor = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,120 +49,83 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const AtomInputExample = () => React.createElement("div", null, React.createElement(_Block().Block, null, React.createElement(_AtomInput().AtomInput, { + disabled: false, + initialValue: "atom input", + placeholderText: "placeholder text" +})), React.createElement(_Block().Block, null, React.createElement(_AtomInput().AtomInput, { + disabled: true, + initialValue: "disabled atom input", + placeholderText: "placeholder text" +})), React.createElement(_Block().Block, null, React.createElement(_AtomInput().AtomInput, { + initialValue: "xs atom input", + placeholderText: "placeholder text", + size: "xs" +})), React.createElement(_Block().Block, null, React.createElement(_AtomInput().AtomInput, { + initialValue: "sm atom input", + placeholderText: "placeholder text", + size: "sm" +})), React.createElement(_Block().Block, null, React.createElement(_AtomInput().AtomInput, { + initialValue: "lg atom input", + placeholderText: "placeholder text", + size: "lg" +})), React.createElement(_Block().Block, null, React.createElement(_AtomInput().AtomInput, { + initialValue: "unstyled atom input", + placeholderText: "placeholder text", + unstyled: true +})), React.createElement(_Block().Block, null, React.createElement(_AtomInput().AtomInput, { + initialValue: "atom input with custom width", + placeholderText: "placeholder text", + width: 200 +}))); -import {TextBuffer} from 'atom'; -import * as React from 'react'; -import {Block} from './Block'; -import {AtomInput} from './AtomInput'; -import {AtomTextEditor} from './AtomTextEditor'; - -const AtomInputExample = (): React.Element => ( -
    - - - - - - - - - - - - - - - - - - - - - -
    -); - -const buffer1 = new TextBuffer({ - text: '/**\n * Hi!\n */\n\n// I am a TextBuffer.\nconst a = 42;', +const buffer1 = new _atom.TextBuffer({ + text: '/**\n * Hi!\n */\n\n// I am a TextBuffer.\nconst a = 42;' }); -const buffer2 = new TextBuffer({ - text: - '/**\n * Hi!\n */\n\n// I am a read-only, gutter-less TextBuffer.\nconst a = 42;', +const buffer2 = new _atom.TextBuffer({ + text: '/**\n * Hi!\n */\n\n// I am a read-only, gutter-less TextBuffer.\nconst a = 42;' }); const editorWrapperStyle = { display: 'flex', flexGrow: 1, height: '12em', - boxShadow: '0 0 20px 0 rgba(0, 0, 0, 0.3)', + boxShadow: '0 0 20px 0 rgba(0, 0, 0, 0.3)' }; -const AtomTextEditorExample = (): React.Element => ( - -
    - -
    -
    - -
    -
    -); - -export const TextInputExamples = { +const AtomTextEditorExample = () => React.createElement(_Block().Block, null, React.createElement("div", { + style: editorWrapperStyle +}, React.createElement(_AtomTextEditor().AtomTextEditor, { + gutterHidden: false, + readOnly: false, + syncTextContents: false, + autoGrow: false, + path: "aJavaScriptFile.js", + textBuffer: buffer1 +})), React.createElement("div", { + style: Object.assign({}, editorWrapperStyle, { + marginTop: '2em' + }) +}, React.createElement(_AtomTextEditor().AtomTextEditor, { + gutterHidden: true, + readOnly: true, + syncTextContents: false, + autoGrow: false, + path: "aJavaScriptFile.js", + textBuffer: buffer2 +}))); + +const TextInputExamples = { sectionName: 'Text Inputs', description: '', - examples: [ - { - title: 'AtomInput', - component: AtomInputExample, - }, - { - title: 'AtomTextEditor', - component: AtomTextEditorExample, - }, - ], + examples: [{ + title: 'AtomInput', + component: AtomInputExample + }, { + title: 'AtomTextEditor', + component: AtomTextEditorExample + }] }; +exports.TextInputExamples = TextInputExamples; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/TextRenderer.js b/modules/nuclide-commons-ui/TextRenderer.js index 2a33a4ca..1f6aae4d 100644 --- a/modules/nuclide-commons-ui/TextRenderer.js +++ b/modules/nuclide-commons-ui/TextRenderer.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.TextRenderer = TextRenderer; + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,30 +17,18 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +function TextRenderer(evaluationResult) { + const { + type, + value + } = evaluationResult; -import * as React from 'react'; - -/* Evaluation & values */ -export type EvaluationResult = { - type: string, - // Either: - value?: string, - // Or: - description?: string, - objectId?: string, - subtype?: string, -}; - -export function TextRenderer( - evaluationResult: EvaluationResult, -): ?React.Element { - const {type, value} = evaluationResult; if (type === 'text') { - return {value}; + return React.createElement("span", null, value); } else { return null; } -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Toggle.js b/modules/nuclide-commons-ui/Toggle.js index c95516af..de506ed0 100644 --- a/modules/nuclide-commons-ui/Toggle.js +++ b/modules/nuclide-commons-ui/Toggle.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Toggle = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _ignoreTextSelectionEvents() { + const data = _interopRequireDefault(require("./ignoreTextSelectionEvents")); + + _ignoreTextSelectionEvents = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,65 +39,55 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import * as React from 'react'; -import classnames from 'classnames'; - -import ignoreTextSelectionEvents from './ignoreTextSelectionEvents'; - -type DefaultProps = { - disabled: boolean, - onClick: (event: SyntheticEvent<>) => mixed, -}; - -type Props = { - className?: string, - toggled: boolean, - disabled: boolean, - label: ?string, - onChange: (isToggled: boolean) => mixed, - onClick: (event: SyntheticEvent<>) => mixed, -}; - /** * A toggle component with an input toggle and a label. We restrict the label to a string * to ensure this component is pure. */ -export class Toggle extends React.Component { - static defaultProps: DefaultProps = { - disabled: false, - onClick(event) {}, - }; +class Toggle extends React.Component { + constructor(...args) { + var _temp; - _onChange = (event: SyntheticEvent<>) => { - const isToggled = ((event.target: any): HTMLInputElement).checked; - this.props.onChange.call(null, isToggled); - }; + return _temp = super(...args), this._onChange = event => { + const isToggled = event.target.checked; + this.props.onChange.call(null, isToggled); + }, _temp; + } - render(): React.Node { - const {className, disabled, label, onClick, toggled} = this.props; - const text = - label === '' ? null : ( - {label} - ); - return ( - - ); + render() { + const { + className, + disabled, + label, + onClick, + toggled + } = this.props; + const text = label === '' ? null : React.createElement("span", { + className: "nuclide-ui-toggle-label-text" + }, " ", label); + return React.createElement("label", { + className: (0, _classnames().default)(className, 'nuclide-ui-toggle-label', { + 'nuclide-ui-toggle-disabled': disabled + }), + onClick: onClick && (0, _ignoreTextSelectionEvents().default)(onClick) + }, React.createElement("input", { + checked: toggled, + className: "input-toggle", + disabled: disabled, + onChange: this._onChange, + type: "checkbox" + }), text); } + } + +exports.Toggle = Toggle; +Toggle.defaultProps = { + disabled: false, + + onClick(event) {} + +}; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Toolbar.example.js b/modules/nuclide-commons-ui/Toolbar.example.js index f8093874..b9cc4389 100644 --- a/modules/nuclide-commons-ui/Toolbar.example.js +++ b/modules/nuclide-commons-ui/Toolbar.example.js @@ -1,3 +1,74 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ToolbarExamples = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Block() { + const data = require("./Block"); + + _Block = function () { + return data; + }; + + return data; +} + +function _Toolbar() { + const data = require("./Toolbar"); + + _Toolbar = function () { + return data; + }; + + return data; +} + +function _ToolbarCenter() { + const data = require("./ToolbarCenter"); + + _ToolbarCenter = function () { + return data; + }; + + return data; +} + +function _ToolbarLeft() { + const data = require("./ToolbarLeft"); + + _ToolbarLeft = function () { + return data; + }; + + return data; +} + +function _ToolbarRight() { + const data = require("./ToolbarRight"); + + _ToolbarRight = function () { + return data; + }; + + return data; +} + +function _Button() { + const data = require("./Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,92 +77,40 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const ToolbarExampleLeft = () => React.createElement("div", null, React.createElement(_Block().Block, null, React.createElement(_Toolbar().Toolbar, { + location: "top" +}, React.createElement(_ToolbarLeft().ToolbarLeft, null, React.createElement("div", null, "a toolbar can have multiple children,"), React.createElement(_Button().Button, null, "such as this button.")))), React.createElement(_Block().Block, null, React.createElement("div", null, "Be sure to use ", ', , and ', " as children."))); + +const ToolbarExampleCenter = () => React.createElement(_Block().Block, null, React.createElement(_Toolbar().Toolbar, { + location: "top" +}, React.createElement(_ToolbarCenter().ToolbarCenter, null, React.createElement("div", null, "Example of ", '', ".")))); + +const ToolbarExampleRight = () => React.createElement(_Block().Block, null, React.createElement(_Toolbar().Toolbar, { + location: "top" +}, React.createElement(_ToolbarRight().ToolbarRight, null, React.createElement("div", null, "Example of ", '')))); + +const ToolbarExampleMultiple = () => React.createElement(_Block().Block, null, React.createElement(_Toolbar().Toolbar, { + location: "top" +}, React.createElement(_ToolbarLeft().ToolbarLeft, null, React.createElement("div", null, "You can combine")), React.createElement(_ToolbarCenter().ToolbarCenter, null, React.createElement("div", null, "the various kinds")), React.createElement(_ToolbarRight().ToolbarRight, null, React.createElement("div", null, "of aligners.")))); -import * as React from 'react'; -import {Block} from './Block'; -import {Toolbar} from './Toolbar'; -import {ToolbarCenter} from './ToolbarCenter'; -import {ToolbarLeft} from './ToolbarLeft'; -import {ToolbarRight} from './ToolbarRight'; -import {Button} from './Button'; - -const ToolbarExampleLeft = (): React.Element => ( -
    - - - -
    a toolbar can have multiple children,
    - -
    -
    -
    - -
    - Be sure to use {', , and '} as - children. -
    -
    -
    -); - -const ToolbarExampleCenter = (): React.Element => ( - - - -
    Example of {''}.
    -
    -
    -
    -); - -const ToolbarExampleRight = (): React.Element => ( - - - -
    Example of {''}
    -
    -
    -
    -); - -const ToolbarExampleMultiple = (): React.Element => ( - - - -
    You can combine
    -
    - -
    the various kinds
    -
    - -
    of aligners.
    -
    -
    -
    -); - -export const ToolbarExamples = { +const ToolbarExamples = { sectionName: 'Toolbar', description: '', - examples: [ - { - title: 'Left Toolbar', - component: ToolbarExampleLeft, - }, - { - title: 'Center Toolbar', - component: ToolbarExampleCenter, - }, - { - title: 'Right Toolbar', - component: ToolbarExampleRight, - }, - { - title: 'Combining Toolbar aligners', - component: ToolbarExampleMultiple, - }, - ], + examples: [{ + title: 'Left Toolbar', + component: ToolbarExampleLeft + }, { + title: 'Center Toolbar', + component: ToolbarExampleCenter + }, { + title: 'Right Toolbar', + component: ToolbarExampleRight + }, { + title: 'Combining Toolbar aligners', + component: ToolbarExampleMultiple + }] }; +exports.ToolbarExamples = ToolbarExamples; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Toolbar.js b/modules/nuclide-commons-ui/Toolbar.js index 8c1579c3..e7967349 100644 --- a/modules/nuclide-commons-ui/Toolbar.js +++ b/modules/nuclide-commons-ui/Toolbar.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Toolbar = void 0; + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _string() { + const data = require("../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +39,18 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import classnames from 'classnames'; -import * as React from 'react'; -import {maybeToString} from 'nuclide-commons/string'; - -type Props = { - children?: mixed, - className?: string, - location?: 'top' | 'bottom', -}; - -export const Toolbar = (props: Props) => { - const className = classnames( - 'nuclide-ui-toolbar', - { - [`nuclide-ui-toolbar--${maybeToString(props.location)}`]: - props.location != null, - }, - props.className, - ); - - return ( - // $FlowFixMe(>=0.53.0) Flow suppress -
    {props.children}
    +const Toolbar = props => { + const className = (0, _classnames().default)('nuclide-ui-toolbar', { + [`nuclide-ui-toolbar--${(0, _string().maybeToString)(props.location)}`]: props.location != null + }, props.className); + return (// $FlowFixMe(>=0.53.0) Flow suppress + React.createElement("div", { + className: className + }, props.children) ); }; + +exports.Toolbar = Toolbar; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/ToolbarCenter.js b/modules/nuclide-commons-ui/ToolbarCenter.js index 1b99e08a..4a8c9bc3 100644 --- a/modules/nuclide-commons-ui/ToolbarCenter.js +++ b/modules/nuclide-commons-ui/ToolbarCenter.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ToolbarCenter = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,19 +17,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import * as React from 'react'; - -type Props = { - children?: mixed, -}; - -export const ToolbarCenter = (props: Props) => { - return ( - // $FlowFixMe(>=0.53.0) Flow suppress -
    {props.children}
    +const ToolbarCenter = props => { + return (// $FlowFixMe(>=0.53.0) Flow suppress + React.createElement("div", { + className: "nuclide-ui-toolbar__center" + }, props.children) ); }; + +exports.ToolbarCenter = ToolbarCenter; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/ToolbarLeft.js b/modules/nuclide-commons-ui/ToolbarLeft.js index f85c1060..e71a9f9d 100644 --- a/modules/nuclide-commons-ui/ToolbarLeft.js +++ b/modules/nuclide-commons-ui/ToolbarLeft.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ToolbarLeft = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,19 +17,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import * as React from 'react'; - -type Props = { - children?: mixed, -}; - -export const ToolbarLeft = (props: Props) => { - return ( - // $FlowFixMe(>=0.53.0) Flow suppress -
    {props.children}
    +const ToolbarLeft = props => { + return (// $FlowFixMe(>=0.53.0) Flow suppress + React.createElement("div", { + className: "nuclide-ui-toolbar__left" + }, props.children) ); }; + +exports.ToolbarLeft = ToolbarLeft; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/ToolbarRight.js b/modules/nuclide-commons-ui/ToolbarRight.js index 33f2c0c6..820e6e05 100644 --- a/modules/nuclide-commons-ui/ToolbarRight.js +++ b/modules/nuclide-commons-ui/ToolbarRight.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ToolbarRight = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,19 +17,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import * as React from 'react'; - -type Props = { - children?: mixed, -}; - -export const ToolbarRight = (props: Props) => { - return ( - // $FlowFixMe(>=0.53.0) Flow suppress -
    {props.children}
    +const ToolbarRight = props => { + return (// $FlowFixMe(>=0.53.0) Flow suppress + React.createElement("div", { + className: "nuclide-ui-toolbar__right" + }, props.children) ); }; + +exports.ToolbarRight = ToolbarRight; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Tree.example.js b/modules/nuclide-commons-ui/Tree.example.js index 88a41a26..2fe464a7 100644 --- a/modules/nuclide-commons-ui/Tree.example.js +++ b/modules/nuclide-commons-ui/Tree.example.js @@ -1,3 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.TreeExamples = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _Block() { + const data = require("./Block"); + + _Block = function () { + return data; + }; + + return data; +} + +function _Icon() { + const data = require("./Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _Tree() { + const data = require("./Tree"); + + _Tree = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,96 +47,73 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const BasicTreeExample = () => React.createElement("div", null, "Trees", React.createElement(_Block().Block, null, React.createElement(_Tree().TreeList, null, React.createElement(_Tree().TreeItem, null, "TreeItem 1"), React.createElement(_Tree().TreeItem, null, "TreeItem 2"), React.createElement(_Tree().NestedTreeItem, { + title: React.createElement("span", null, "NestedTreeItem 1 -- click me!"), + onSelect: handleSelect, + onConfirm: handleConfirm, + onTripleClick: handleTripleClick, + selected: true +}, React.createElement(_Tree().TreeItem, null, "TreeItem 3"), React.createElement(_Tree().TreeItem, null, "TreeItem 4")), React.createElement(_Tree().NestedTreeItem, { + title: React.createElement("span", null, "NestedTreeItem 2"), + collapsed: true +})))); + +const AtomStyleguideTreeExample = () => React.createElement(_Block().Block, null, React.createElement(_Tree().TreeList, { + showArrows: true +}, React.createElement(_Tree().NestedTreeItem, { + title: React.createElement(_Icon().Icon, { + icon: "file-directory" + }, "A Directory") +}, React.createElement(_Tree().NestedTreeItem, { + collapsed: false, + title: React.createElement(_Icon().Icon, { + icon: "file-directory" + }, "Nested Directory") +}, React.createElement(_Tree().TreeItem, null, React.createElement(_Icon().Icon, { + icon: "file-text" +}, "File one"))), React.createElement(_Tree().NestedTreeItem, { + collapsed: true, + title: React.createElement(_Icon().Icon, { + icon: "file-directory" + }, "Collapsed Nested Directory") +}, React.createElement(_Tree().TreeItem, null, React.createElement(_Icon().Icon, { + icon: "file-text" +}, "File one"))), React.createElement(_Tree().TreeItem, null, React.createElement(_Icon().Icon, { + icon: "file-text" +}, "File one")), React.createElement(_Tree().TreeItem, { + selected: true +}, React.createElement(_Icon().Icon, { + icon: "file-text" +}, "File three .selected!"))), React.createElement(_Tree().TreeItem, null, React.createElement(_Icon().Icon, { + icon: "file-text" +}, ".icon-file-text")), React.createElement(_Tree().TreeItem, null, React.createElement(_Icon().Icon, { + icon: "file-symlink-file" +}, ".icon-file-symlink-file")))); -import * as React from 'react'; -import {Block} from './Block'; -import {Icon} from './Icon'; -import {TreeList, TreeItem, NestedTreeItem} from './Tree'; - -const BasicTreeExample = (): React.Element => ( -
    - Trees - - - TreeItem 1 - TreeItem 2 - NestedTreeItem 1 -- click me!} - onSelect={handleSelect} - onConfirm={handleConfirm} - onTripleClick={handleTripleClick} - selected={true}> - TreeItem 3 - TreeItem 4 - - NestedTreeItem 2} - collapsed={true} - /> - - -
    -); - -const AtomStyleguideTreeExample = (): React.Element => ( - - - A Directory}> - Nested Directory}> - - File one - - - Collapsed Nested Directory}> - - File one - - - - File one - - - File three .selected! - - - - .icon-file-text - - - .icon-file-symlink-file - - - -); - -export const TreeExamples = { +const TreeExamples = { sectionName: 'Trees', description: 'Expandable, hierarchical lists.', - examples: [ - { - title: 'Basic Tree', - component: BasicTreeExample, - }, - { - title: 'Reproducing the Atom style guide example:', - component: AtomStyleguideTreeExample, - }, - ], + examples: [{ + title: 'Basic Tree', + component: BasicTreeExample + }, { + title: 'Reproducing the Atom style guide example:', + component: AtomStyleguideTreeExample + }] }; +exports.TreeExamples = TreeExamples; function handleSelect() { atom.notifications.addInfo('selected!'); } + function handleConfirm() { atom.notifications.addInfo('confirmed!'); } + function handleTripleClick() { atom.notifications.addInfo('triple clicked!'); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-ui/Tree.js b/modules/nuclide-commons-ui/Tree.js index a120ed60..828179fc 100644 --- a/modules/nuclide-commons-ui/Tree.js +++ b/modules/nuclide-commons-ui/Tree.js @@ -1,58 +1,65 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -/* eslint-env browser */ - -import * as React from 'react'; -import classnames from 'classnames'; -import invariant from 'assert'; -import {scrollIntoView} from './scrollIntoView'; - -export function Tree({className, style, ...props}: Object) { - return ( -
      - ); +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Tree = Tree; +exports.TreeList = exports.NestedTreeItem = exports.TreeItem = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _scrollIntoView() { + const data = require("./scrollIntoView"); + + _scrollIntoView = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +function Tree(_ref) { + let { + className, + style + } = _ref, + props = _objectWithoutProperties(_ref, ["className", "style"]); + + return React.createElement("ol", Object.assign({ + className: (0, _classnames().default)('list-tree', className), + role: "tree", + style: Object.assign({ + position: 'relative' + }, style) + }, props)); } -type TreeItemProps = {| - children?: React.Node, - className?: string, - // handled below in `handleClick` - /* eslint-disable react/no-unused-prop-types */ - onSelect?: (e: SyntheticMouseEvent<>) => mixed, - onConfirm?: (e: SyntheticMouseEvent<>) => mixed, - onTripleClick?: (e: SyntheticMouseEvent<>) => mixed, - /* eslint-enable react/no-unused-prop-types */ - selected?: boolean, - onMouseDown?: (e: SyntheticMouseEvent<>) => mixed, - onMouseEnter?: (e: SyntheticMouseEvent<>) => mixed, - onMouseLeave?: (e: SyntheticMouseEvent<>) => mixed, - path?: string, - name?: string, - title?: string, -|}; - -export class TreeItem extends React.Component { - _liNode: ?HTMLLIElement; - _handleClick = handleClick.bind(this); +class TreeItem extends React.Component { + constructor(...args) { + var _temp; + + return _temp = super(...args), this._handleClick = handleClick.bind(this), _temp; + } scrollIntoView() { if (this._liNode != null) { - scrollIntoView(this._liNode); + (0, _scrollIntoView().scrollIntoView)(this._liNode); } } @@ -66,69 +73,52 @@ export class TreeItem extends React.Component { onMouseLeave, path, name, - title, + title } = this.props; - - return ( -
      -
    1. (this._liNode = liNode)} - role="treeitem" - tabIndex={selected ? '0' : '-1'}> - {selected && typeof children === 'string' ? ( - // String children must be wrapped to receive correct styles when selected. - {children} - ) : ( - children - )} -
    2. -
      - ); + return React.createElement("div", { + title: title + }, React.createElement("li", { + "aria-selected": selected, + className: (0, _classnames().default)(className, { + selected + }, 'list-item'), + onMouseDown: onMouseDown, + onMouseEnter: onMouseEnter, + onMouseLeave: onMouseLeave, + "data-path": path, + "data-name": name, + onClick: this._handleClick, + ref: liNode => this._liNode = liNode, + role: "treeitem", + tabIndex: selected ? '0' : '-1' + }, selected && typeof children === 'string' ? // String children must be wrapped to receive correct styles when selected. + React.createElement("span", null, children) : children)); } + } -type NestedTreeItemProps = {| - title?: React.Node, - children?: mixed, - className?: string, - hasFlatChildren?: boolean, // passthrough to inner TreeList - selected?: boolean, - collapsed?: boolean, - // handled below in `handleClick` - /* eslint-disable react/no-unused-prop-types */ - onSelect?: (e: SyntheticMouseEvent<>) => mixed, - onConfirm?: (e: SyntheticMouseEvent<>) => mixed, - onTripleClick?: (e: SyntheticMouseEvent<>) => mixed, - /* eslint-disable react/no-unused-prop-types */ -|}; - -export class NestedTreeItem extends React.Component { - _itemNode: ?HTMLDivElement; - _handleClick = (e: SyntheticMouseEvent<>) => { - const itemNode = this._itemNode; - if (itemNode == null) { - return; - } +exports.TreeItem = TreeItem; - invariant(e.target instanceof Element); - if (e.target.closest('.list-item') === itemNode) { - handleClick.call(this, e); - } - }; +class NestedTreeItem extends React.Component { + constructor(...args) { + var _temp2; + + return _temp2 = super(...args), this._handleClick = e => { + const itemNode = this._itemNode; + + if (itemNode == null) { + return; + } + + if (!(e.target instanceof Element)) { + throw new Error("Invariant violation: \"e.target instanceof Element\""); + } + + if (e.target.closest('.list-item') === itemNode) { + handleClick.call(this, e); + } + }, _temp2; + } render() { const { @@ -137,76 +127,64 @@ export class NestedTreeItem extends React.Component { selected, collapsed, title, - children, + children } = this.props; - - return ( -
    3. - {title == null ? null : ( -
      (this._itemNode = node)}> - {title} -
      - )} - {children} -
    4. - ); + return React.createElement("li", { + "aria-selected": selected, + "aria-expanded": !collapsed, + className: (0, _classnames().default)(className, { + selected, + collapsed + }, 'list-nested-item'), + onClick: this._handleClick, + role: "treeitem", + tabIndex: selected ? '0' : '-1' + }, title == null ? null : React.createElement("div", { + tabIndex: -1, + className: "native-key-bindings list-item", + ref: node => this._itemNode = node + }, title), React.createElement(TreeList, { + hasFlatChildren: hasFlatChildren + }, children)); } -} -type TreeListProps = { - className?: string, - /* typically, instances of TreeItem or NestedTreeItem. */ - children?: mixed, - showArrows?: boolean, - hasFlatChildren?: boolean, -}; -export const TreeList = (props: TreeListProps) => ( - // $FlowFixMe(>=0.53.0) Flow suppress -
        - {props.children} -
      -); - -function handleClick(e: SyntheticMouseEvent<>): void { - const {onSelect, onConfirm, onTripleClick} = this.props; +} +exports.NestedTreeItem = NestedTreeItem; + +const TreeList = props => // $FlowFixMe(>=0.53.0) Flow suppress +React.createElement("ul", { + className: (0, _classnames().default)(props.className, { + 'has-collapsable-children': props.showArrows, + 'has-flat-children': props.hasFlatChildren + }, 'list-tree'), + role: "group" +}, props.children); + +exports.TreeList = TreeList; + +function handleClick(e) { + const { + onSelect, + onConfirm, + onTripleClick + } = this.props; const numberOfClicks = e.detail; + switch (numberOfClicks) { case 1: onSelect && onSelect(e); break; + case 2: onConfirm && onConfirm(e); break; + case 3: onTripleClick && onTripleClick(e); break; + default: break; } -} +} \ No newline at end of file diff --git a/modules/nuclide-commons-ui/TruncatedButton.js b/modules/nuclide-commons-ui/TruncatedButton.js index 3731eef2..628019c7 100644 --- a/modules/nuclide-commons-ui/TruncatedButton.js +++ b/modules/nuclide-commons-ui/TruncatedButton.js @@ -1,40 +1,54 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local - * @format - */ - -import {Button} from './Button'; -import classnames from 'classnames'; -import * as React from 'react'; - -type Props = { - className?: string, - // $FlowFixMe(>=0.53.0) Flow suppress - children?: React.Children, - label?: string, -}; - -export default class TruncatedButton extends React.Component { - render(): React.Node { - const {children, className, label, ...props} = this.props; - return ( - - ); +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _Button() { + const data = require("./Button"); + + _Button = function () { + return data; + }; + + return data; +} + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +class TruncatedButton extends React.Component { + render() { + const _this$props = this.props, + { + children, + className, + label + } = _this$props, + props = _objectWithoutProperties(_this$props, ["children", "className", "label"]); + + return React.createElement(_Button().Button, Object.assign({ + className: (0, _classnames().default)('btn-block', 'nuclide-ui-truncated-button', className), + title: label + }, props), children || label); } + } + +exports.default = TruncatedButton; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/UnstyledButton.js b/modules/nuclide-commons-ui/UnstyledButton.js index ee10f216..d63c7b6e 100644 --- a/modules/nuclide-commons-ui/UnstyledButton.js +++ b/modules/nuclide-commons-ui/UnstyledButton.js @@ -1,37 +1,62 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import React from 'react'; -import classnames from 'classnames'; -import nullthrows from 'nullthrows'; - -type Props = { - className?: string, -}; - -export default class UnstyledButton extends React.Component { - props: Props; - _node: ?HTMLButtonElement; - - focus(): void { - nullthrows(this._node).focus(); +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _react = _interopRequireDefault(require("react")); + +function _classnames() { + const data = _interopRequireDefault(require("classnames")); + + _classnames = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +class UnstyledButton extends _react.default.Component { + constructor(...args) { + var _temp; + + return _temp = super(...args), this._setRef = node => this._node = node, _temp; } - _setRef = (node: ?HTMLButtonElement) => (this._node = node); + focus() { + (0, _nullthrows().default)(this._node).focus(); + } - render(): React$Element { - const {className, ...props} = this.props; - const classes = classnames('nuclide-ui-unstyled-button', className); - // eslint-disable-next-line nuclide-internal/use-nuclide-ui-components - return ; + return React.createElement(_Button().Button, { + onClick: showExampleModal + }, "Show Modal"); } function showExampleModal() { - showModal(({dismiss}) => { - return ( -
      -
      - I'm a modal. You can add any content you like. I have all the standard - behavior, like obeying the "core:cancel" command! -
      - -
      - ); + (0, _showModal().default)(({ + dismiss + }) => { + return React.createElement("div", null, React.createElement("div", null, "I'm a modal. You can add any content you like. I have all the standard behavior, like obeying the \"core:cancel\" command!"), React.createElement(_Button().Button, { + onClick: dismiss + }, "Hide Modal")); }); } -export const ModalExamples = { +const ModalExamples = { sectionName: 'Modal', description: 'Overlays that cover the entire screen. ', - examples: [ - { - title: 'Click the button to toggle a modal:', - component: ModalButton, - }, - ], + examples: [{ + title: 'Click the button to toggle a modal:', + component: ModalButton + }] }; +exports.ModalExamples = ModalExamples; \ No newline at end of file diff --git a/modules/nuclide-commons-ui/showModal.js b/modules/nuclide-commons-ui/showModal.js index 26b42bd0..b0244933 100644 --- a/modules/nuclide-commons-ui/showModal.js +++ b/modules/nuclide-commons-ui/showModal.js @@ -1,3 +1,40 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = showModal; + +var React = _interopRequireWildcard(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _TabbableContainer() { + const data = _interopRequireDefault(require("./TabbableContainer")); + + _TabbableContainer = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,50 +43,13 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* global Node */ -/* global HTMLElement */ - -import invariant from 'assert'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import {Observable} from 'rxjs'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -import TabbableContainer from './TabbableContainer'; -/** - * Given a function to dismiss the modal, return a React element for the content. - * Call the function when e.g. the user clicks a Cancel or Submit button. - */ -type ContentFactory = ({ - dismiss(): void, - element: Element, -}) => React.Node; - -/** Wrap options in an object so we can add new ones later without an explosion of params */ -type Options = {| - /** Called when the modal is dismissed (just before it is destroyed). */ - onDismiss?: () => mixed, - onOpen?: () => mixed, - /** - * Called when the user clicks outside the modal, return false to prevent dismissal. - * If unspecified the modal will be dismissed if the user clicks outside the modal. - */ - shouldDismissOnClickOutsideModal?: () => boolean, - /** - * Called when the user presses the escape key, return false to prevent dismissal. - * If unspecified the modal will be dismissed if the user presses escape. - */ - shouldDismissOnPressEscape?: () => boolean, - /** Passed to atom's underlying addModalPanel function. */ - priority?: number, - /** Passed to atom's underlying addModalPanel function. */ - className?: string, -|}; +/* global HTMLElement */ /** * Shows a modal dialog that renders a React element as its content. @@ -58,109 +58,101 @@ type Options = {| * you may not hide the panel and then re-show it later. * Returns a disposable that you may use to hide and destroy the modal. */ -export default function showModal( - contentFactory: ContentFactory, - options: Options = defaults, -): IDisposable { +function showModal(contentFactory, options = defaults) { const hostElement = document.createElement('div'); const atomPanel = atom.workspace.addModalPanel({ item: hostElement, priority: options.priority, - className: options.className, + className: options.className }); - const shouldDismissOnClickOutsideModal = - options.shouldDismissOnClickOutsideModal || (() => true); - const shouldDismissOnPressEscape = - options.shouldDismissOnPressEscape || (() => true); + + const shouldDismissOnClickOutsideModal = options.shouldDismissOnClickOutsideModal || (() => true); + + const shouldDismissOnPressEscape = options.shouldDismissOnPressEscape || (() => true); const element = atomPanel.getElement(); const previouslyFocusedElement = document.activeElement; - const disposable = new UniversalDisposable( - Observable.fromEvent(document, 'mousedown').subscribe(({target}) => { - if (!shouldDismissOnClickOutsideModal()) { - return; - } - invariant(target instanceof Node); - if ( - !atomPanel.getItem().contains(target) && - // don't count clicks on notifications or tooltips as clicks 'outside' - target.closest('atom-notifications, .tooltip') == null - ) { - atomPanel.hide(); - } - }), - atomPanel.onDidChangeVisible(visible => { - if (!visible) { - disposable.dispose(); - } - }), - atom.commands.add('atom-workspace', 'core:cancel', () => { - if (shouldDismissOnPressEscape()) { - disposable.dispose(); - } - }), - () => { - // Call onDismiss before unmounting the component and destroying the panel: - if (options.onDismiss) { - options.onDismiss(); - } - ReactDOM.unmountComponentAtNode(hostElement); - atomPanel.destroy(); - if ( - document.activeElement === document.body && - previouslyFocusedElement != null - ) { - previouslyFocusedElement.focus(); - } - }, - ); - - ReactDOM.render( - - {contentFactory({dismiss: disposable.dispose.bind(disposable), element})} - , - hostElement, - () => { - if (options.onOpen) { - options.onOpen(); - } - }, - ); + const disposable = new (_UniversalDisposable().default)(_RxMin.Observable.fromEvent(document, 'mousedown').subscribe(({ + target + }) => { + if (!shouldDismissOnClickOutsideModal()) { + return; + } + + if (!(target instanceof Node)) { + throw new Error("Invariant violation: \"target instanceof Node\""); + } + + if (!atomPanel.getItem().contains(target) && // don't count clicks on notifications or tooltips as clicks 'outside' + target.closest('atom-notifications, .tooltip') == null) { + atomPanel.hide(); + } + }), atomPanel.onDidChangeVisible(visible => { + if (!visible) { + disposable.dispose(); + } + }), atom.commands.add('atom-workspace', 'core:cancel', () => { + if (shouldDismissOnPressEscape()) { + disposable.dispose(); + } + }), () => { + // Call onDismiss before unmounting the component and destroying the panel: + if (options.onDismiss) { + options.onDismiss(); + } + + _reactDom.default.unmountComponentAtNode(hostElement); + + atomPanel.destroy(); + + if (document.activeElement === document.body && previouslyFocusedElement != null) { + previouslyFocusedElement.focus(); + } + }); + + _reactDom.default.render(React.createElement(ModalContainer, null, contentFactory({ + dismiss: disposable.dispose.bind(disposable), + element + })), hostElement, () => { + if (options.onOpen) { + options.onOpen(); + } + }); return disposable; } - /** Flow makes {} an unsealed object (eyeroll) */ -const defaults: Options = Object.freeze({}); -type Props = { - children?: any, -}; + +const defaults = Object.freeze({}); /** * Just exists to provide a div that we can focus on mount. This ensures we steal focus from any * editors or other panes while the modal is present. */ -class ModalContainer extends React.Component { - render(): React.Node { - return ( -
      - - {this.props.children} - -
      - ); +class ModalContainer extends React.Component { + render() { + return React.createElement("div", { + tabIndex: "-1" + }, React.createElement(_TabbableContainer().default, { + contained: true + }, this.props.children)); } - componentDidMount(): void { - const node = ReactDOM.findDOMNode(this); - invariant(node instanceof HTMLElement); - // Steal the focus away from any active editor or pane, setting it on the modal; + componentDidMount() { + const node = _reactDom.default.findDOMNode(this); + + if (!(node instanceof HTMLElement)) { + throw new Error("Invariant violation: \"node instanceof HTMLElement\""); + } // Steal the focus away from any active editor or pane, setting it on the modal; // but don't steal focus away from a descendant. This can happen if a React element focuses // during its componentDidMount. For example, does this since the underlying // does not support the autofocus attribute. + + if (!node.contains(document.activeElement)) { node.focus(); } } -} + +} \ No newline at end of file diff --git a/modules/nuclide-commons/AbortController.js b/modules/nuclide-commons/AbortController.js index 09437905..46ae5bd8 100644 --- a/modules/nuclide-commons/AbortController.js +++ b/modules/nuclide-commons/AbortController.js @@ -1,3 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.AbortSignal = void 0; + +function _eventTargetShim() { + const data = require("event-target-shim"); + + _eventTargetShim = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +23,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -15,45 +32,51 @@ * from the whatwg spec: https://dom.spec.whatwg.org/#aborting-ongoing-activities * These will become available in Chrome 66. */ - // Shim of EventTarget usable in Node. // Note that even in Chrome, EventTarget also isn't instantiable until version 64. -import { - EventTarget as EventTargetShim, - defineEventAttribute, -} from 'event-target-shim'; +class AbortSignal extends _eventTargetShim().EventTarget { + constructor(...args) { + var _temp; -export class AbortSignal extends (EventTargetShim: typeof EventTarget) { - aborted: boolean = false; - // Defined via defineEventAttribute below. - onabort: ?(event: Event) => mixed; + return _temp = super(...args), this.aborted = false, _temp; + } // $FlowIssue: Computed properties are not supported get [Symbol.toStringTag]() { return 'AbortSignal'; } + } -defineEventAttribute(AbortSignal.prototype, 'abort'); +exports.AbortSignal = AbortSignal; +(0, _eventTargetShim().defineEventAttribute)(AbortSignal.prototype, 'abort'); -export default class AbortController { - signal = new AbortSignal(); +class AbortController { + constructor() { + this.signal = new AbortSignal(); + } abort() { // From whatwg spec, section 3.2: // If signal’s aborted flag is set, then return. if (this.signal.aborted) { return; - } - // Set signal’s aborted flag. - this.signal.aborted = true; - // Fire an event named abort at signal. + } // Set signal’s aborted flag. + + + this.signal.aborted = true; // Fire an event named abort at signal. // Note: event-target-shim converts objects to Events. - this.signal.dispatchEvent(({type: 'abort'}: any)); - } - // $FlowIssue: Computed properties are not supported + this.signal.dispatchEvent({ + type: 'abort' + }); + } // $FlowIssue: Computed properties are not supported + + get [Symbol.toStringTag]() { return 'AbortController'; } + } + +exports.default = AbortController; \ No newline at end of file diff --git a/modules/nuclide-commons/BatchProcessedQueue.js b/modules/nuclide-commons/BatchProcessedQueue.js index 8840d433..3a4ab79e 100644 --- a/modules/nuclide-commons/BatchProcessedQueue.js +++ b/modules/nuclide-commons/BatchProcessedQueue.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,30 +13,23 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -export type BatchHandler = (batch: Array) => void; - // A Queue which will process elements at intervals, only if the // queue contains any elements. -export default class BatchProcessedQueue { - _batchPeriod: number; - _handler: BatchHandler; - _timeoutId: ?TimeoutID; - _items: Array; - - constructor(batchPeriod: number, handler: BatchHandler) { +class BatchProcessedQueue { + constructor(batchPeriod, handler) { this._batchPeriod = batchPeriod; this._handler = handler; this._timeoutId = null; this._items = []; } - add(item: T): void { - this._items.push(item); - // eslint-disable-next-line eqeqeq + add(item) { + this._items.push(item); // eslint-disable-next-line eqeqeq + + if (this._timeoutId === null) { this._timeoutId = setTimeout(() => { this._handleBatch(); @@ -41,14 +41,19 @@ export default class BatchProcessedQueue { this._timeoutId = null; const batch = this._items; this._items = []; + this._handler(batch); } - dispose(): void { + dispose() { // eslint-disable-next-line eqeqeq if (this._timeoutId !== null) { clearTimeout(this._timeoutId); + this._handleBatch(); } } + } + +exports.default = BatchProcessedQueue; \ No newline at end of file diff --git a/modules/nuclide-commons/ConfigCache.js b/modules/nuclide-commons/ConfigCache.js index 251c3b51..5d9382a2 100644 --- a/modules/nuclide-commons/ConfigCache.js +++ b/modules/nuclide-commons/ConfigCache.js @@ -1,3 +1,52 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ConfigCache = void 0; + +function _lruCache() { + const data = _interopRequireDefault(require("lru-cache")); + + _lruCache = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("./collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _fsPromise() { + const data = _interopRequireDefault(require("./fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("./nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,73 +55,53 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {LRUCache} from 'lru-cache'; -import type {NuclideUri} from './nuclideUri'; - -import LRU from 'lru-cache'; -import {findSubArrayIndex} from './collection'; -import fsPromise from './fsPromise'; -import nuclideUri from './nuclideUri'; - -export type SearchStrategy = 'nearest' | 'eclipse' | 'ocaml' | 'thrift'; - -export class ConfigCache { - _configPatterns: Array; - _searchStrategy: SearchStrategy; - _configCache: LRUCache>; - - constructor( - configPatterns: Array, - searchStrategy?: SearchStrategy = 'nearest', - ) { +class ConfigCache { + constructor(configPatterns, searchStrategy = 'nearest') { this._configPatterns = configPatterns; this._searchStrategy = searchStrategy; - this._configCache = LRU({ - max: 200, // Want this to exceed the maximum expected number of open files + dirs. - maxAge: 1000 * 30, // 30 seconds + this._configCache = (0, _lruCache().default)({ + max: 200, + // Want this to exceed the maximum expected number of open files + dirs. + maxAge: 1000 * 30 // 30 seconds + }); } - getConfigDir(path: NuclideUri): Promise { + getConfigDir(path) { let result = this._configCache.get(path); + if (result == null) { result = this._findConfigDir(path); + this._configCache.set(path, result); } + return result; } - async _findConfigDir(path: NuclideUri): Promise { + async _findConfigDir(path) { if (this._searchStrategy === 'eclipse') { - const configDirs = await Promise.all( - this._configPatterns.map(configFile => - fsPromise.findFurthestFile(configFile, path), - ), - ); + const configDirs = await Promise.all(this._configPatterns.map(configFile => _fsPromise().default.findFurthestFile(configFile, path))); return configDirs.filter(Boolean).reduce((previous, configDir) => { if (previous == null || configDir.length < previous.length) { return configDir; } + return previous; }, null); } else if (this._searchStrategy === 'thrift') { // Find the first occurrence of a config segment in the path. - const pathSplit = nuclideUri.split(path); - return this._configPatterns - .map(configPattern => { - const configSplit = nuclideUri.split(configPattern); - const foundIndex = findSubArrayIndex(pathSplit, configSplit); - return foundIndex !== -1 - ? nuclideUri.join( - ...pathSplit.slice(0, foundIndex + configSplit.length), - ) - : null; - }) - .find(Boolean); + const pathSplit = _nuclideUri().default.split(path); + + return this._configPatterns.map(configPattern => { + const configSplit = _nuclideUri().default.split(configPattern); + + const foundIndex = (0, _collection().findSubArrayIndex)(pathSplit, configSplit); + return foundIndex !== -1 ? _nuclideUri().default.join(...pathSplit.slice(0, foundIndex + configSplit.length)) : null; + }).find(Boolean); } else if (this._searchStrategy === 'ocaml') { // ocaml-language-server (the LSP server) is the same single LSP server binary // for all ocaml projects and for all versions of merlin. @@ -89,44 +118,38 @@ export class ConfigCache { // Therefore: to find project root for a given file, we'll either use the nearest // containing parent such that directory parent/node_modules/.cache/_esy/build/bin exists, // or "/" otherwise. + let dir = _nuclideUri().default.dirname(path); - let dir = nuclideUri.dirname(path); while (true) { - const wrapper = nuclideUri.join( - dir, - 'node_modules', - '.cache', - '_esy', - 'build', - 'bin', - ); - // eslint-disable-next-line no-await-in-loop - if (await fsPromise.exists(wrapper)) { + const wrapper = _nuclideUri().default.join(dir, 'node_modules', '.cache', '_esy', 'build', 'bin'); // eslint-disable-next-line no-await-in-loop + + + if (await _fsPromise().default.exists(wrapper)) { return dir; - } else if (nuclideUri.isRoot(dir)) { + } else if (_nuclideUri().default.isRoot(dir)) { return dir; } else { - dir = nuclideUri.dirname(dir); + dir = _nuclideUri().default.dirname(dir); } } } else { - (this._searchStrategy: 'nearest'); - // Find the result with the greatest length (the closest match). - const configDirs = await Promise.all( - this._configPatterns.map(configFile => - fsPromise.findNearestFile(configFile, path), - ), - ); + this._searchStrategy; // Find the result with the greatest length (the closest match). + + const configDirs = await Promise.all(this._configPatterns.map(configFile => _fsPromise().default.findNearestFile(configFile, path))); return configDirs.filter(Boolean).reduce((previous, configDir) => { if (previous == null || configDir.length > previous.length) { return configDir; } + return previous; }, null); } } - dispose(): void { + dispose() { this._configCache.reset(); } + } + +exports.ConfigCache = ConfigCache; \ No newline at end of file diff --git a/modules/nuclide-commons/Hasher.js b/modules/nuclide-commons/Hasher.js index bc32d517..27799042 100644 --- a/modules/nuclide-commons/Hasher.js +++ b/modules/nuclide-commons/Hasher.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +13,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ @@ -29,43 +36,51 @@ * } * } */ -export default class Hasher { - _hashes: WeakMap; - _objectCount: number; - +class Hasher { constructor() { this._hashes = new WeakMap(); this._objectCount = 0; } - getHash(item: K): string | number { + getHash(item) { // eslint-disable-next-line eqeqeq if (item === null) { return 'null'; } + const type = typeof item; + switch (typeof item) { - case 'object': { - let hash = this._hashes.get(item); - if (hash == null) { - hash = `${type}:${this._objectCount}`; - this._hashes.set(item, hash); - this._objectCount = - this._objectCount + 1 === Number.MAX_SAFE_INTEGER - ? Number.MIN_SAFE_INTEGER - : this._objectCount + 1; + case 'object': + { + let hash = this._hashes.get(item); + + if (hash == null) { + hash = `${type}:${this._objectCount}`; + + this._hashes.set(item, hash); + + this._objectCount = this._objectCount + 1 === Number.MAX_SAFE_INTEGER ? Number.MIN_SAFE_INTEGER : this._objectCount + 1; + } + + return hash; } - return hash; - } + case 'undefined': return 'undefined'; + case 'string': case 'boolean': return `${type}:${item.toString()}`; + case 'number': return item; + default: throw new Error('Unhashable object'); } } + } + +exports.default = Hasher; \ No newline at end of file diff --git a/modules/nuclide-commons/Model.js b/modules/nuclide-commons/Model.js index ff3a12e7..e7d8c0a4 100644 --- a/modules/nuclide-commons/Model.js +++ b/modules/nuclide-commons/Model.js @@ -1,3 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("./UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,15 +27,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {Observable} from 'rxjs'; - -import {BehaviorSubject} from 'rxjs'; -import UniversalDisposable from './UniversalDisposable'; - /** * Exposes a simple API for a stateful model. This is similar to React's `state`/`setState()` API * except achieved via composition and easily convertible to observables so you can do awesome @@ -52,27 +68,31 @@ import UniversalDisposable from './UniversalDisposable'; * action creators. That's awesome! It means that, should the state grow and require new * capabilities, we can always switch to full-blown Redux without having to refactor a ton of stuff. */ -export default class Model { - _states: BehaviorSubject; - - constructor(initialState: State) { - this._states = new BehaviorSubject(initialState); +class Model { + constructor(initialState) { + this._states = new _RxMin.BehaviorSubject(initialState); } - setState(newState: $Shape): void { - const nextState = {...this.state, ...newState}; + setState(newState) { + const nextState = Object.assign({}, this.state, newState); + this._states.next(nextState); } - get state(): State { + get state() { return this._states.getValue(); } - subscribe(cb: (state: State) => mixed): IDisposable { - return new UniversalDisposable(this.toObservable().subscribe({next: cb})); + subscribe(cb) { + return new (_UniversalDisposable().default)(this.toObservable().subscribe({ + next: cb + })); } - toObservable(): Observable { + toObservable() { return this._states.distinctUntilChanged(); } + } + +exports.default = Model; \ No newline at end of file diff --git a/modules/nuclide-commons/ObservablePool.js b/modules/nuclide-commons/ObservablePool.js index fa99ad20..8619f774 100644 --- a/modules/nuclide-commons/ObservablePool.js +++ b/modules/nuclide-commons/ObservablePool.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,21 +15,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ -import {Observable, Subject} from 'rxjs'; - -type Executor = Observable | (() => rxjs$ObservableInput); - -type Request = {tag: mixed, executor: Executor}; - -type Response = { - observer: rxjs$Observer, - unsubscribed: Subject, -}; - /** * ObservablePool allows you to execute Observables or functions that return * Observable inputs (i.e. Observables, Promises, or Iterables) @@ -52,81 +50,94 @@ type Response = { * The output here is 1, 2, then 3. Despite the fact that the third observable * finishes more quickly, its execution is postponed until the first two finish. */ -export default class ObservablePool { - _requests: Subject>; - _responseListeners: Map>; - _subscription: rxjs$ISubscription; - - constructor(concurrency: number) { - this._requests = new Subject(); +class ObservablePool { + constructor(concurrency) { + this._requests = new _RxMin.Subject(); this._responseListeners = new Map(); this._subscription = this._handleEvents(concurrency); } - schedule(executor: Executor): Observable { - return Observable.create(observer => { - const unsubscribed = new Subject(); + schedule(executor) { + return _RxMin.Observable.create(observer => { + const unsubscribed = new _RxMin.Subject(); const tag = {}; // Just a unique object. - this._responseListeners.set(tag, {observer, unsubscribed}); - this._requests.next({tag, executor}); + + this._responseListeners.set(tag, { + observer, + unsubscribed + }); + + this._requests.next({ + tag, + executor + }); + return () => { this._responseListeners.delete(tag); + unsubscribed.next(); }; }); } - /** * Warning: calling dispose() will error all executing requests. */ + + dispose() { - this._responseListeners.forEach(({observer}) => { + this._responseListeners.forEach(({ + observer + }) => { observer.error(Error('ObservablePool was disposed')); }); + this._subscription.unsubscribe(); } - _handleEvents(concurrency: number): rxjs$ISubscription { - return this._requests - .mergeMap(event => { - const {executor, tag} = event; - const listener = this._responseListeners.get(tag); - // unsubscribed before we could even get to it! - if (listener == null) { - return Observable.empty(); - } - const {observer, unsubscribed} = listener; - let result; - if (executor instanceof Observable) { - result = executor; - } else { - try { - result = executor(); - } catch (err) { - // Catch errors from executor(). - observer.error(err); - return Observable.empty(); - } - } - if (result instanceof Observable) { - // We can safely forward unsubscriptions! - return ( - result - .takeUntil(unsubscribed) - // $FlowFixMe: Flow doesn't like this. - .do(observer) - .catch(() => Observable.empty()) - ); - } else { - // In the absence of cancellation, assume the worst. - return ( - Observable.from(result) - // $FlowFixMe: Flow doesn't like this. - .do(observer) - .catch(() => Observable.empty()) - ); + _handleEvents(concurrency) { + return this._requests.mergeMap(event => { + const { + executor, + tag + } = event; + + const listener = this._responseListeners.get(tag); // unsubscribed before we could even get to it! + + + if (listener == null) { + return _RxMin.Observable.empty(); + } + + const { + observer, + unsubscribed + } = listener; + let result; + + if (executor instanceof _RxMin.Observable) { + result = executor; + } else { + try { + result = executor(); + } catch (err) { + // Catch errors from executor(). + observer.error(err); + return _RxMin.Observable.empty(); } - }, concurrency) - .subscribe(); + } + + if (result instanceof _RxMin.Observable) { + // We can safely forward unsubscriptions! + return result.takeUntil(unsubscribed) // $FlowFixMe: Flow doesn't like this. + .do(observer).catch(() => _RxMin.Observable.empty()); + } else { + // In the absence of cancellation, assume the worst. + return _RxMin.Observable.from(result) // $FlowFixMe: Flow doesn't like this. + .do(observer).catch(() => _RxMin.Observable.empty()); + } + }, concurrency).subscribe(); } + } + +exports.default = ObservablePool; \ No newline at end of file diff --git a/modules/nuclide-commons/SafeStreamMessageReader.js b/modules/nuclide-commons/SafeStreamMessageReader.js index 0e5ba69a..050c71b2 100644 --- a/modules/nuclide-commons/SafeStreamMessageReader.js +++ b/modules/nuclide-commons/SafeStreamMessageReader.js @@ -1,3 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _vscodeJsonrpc() { + const data = require("vscode-jsonrpc"); + + _vscodeJsonrpc = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,12 +23,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ -import {StreamMessageReader} from 'vscode-jsonrpc'; - /** * vscode-jsonrpc's StreamMessageReader has a fatal flaw of throwing exceptions! * It's hard to catch asynchronous exceptions, and it crashes the Nuclide server. @@ -19,16 +34,19 @@ import {StreamMessageReader} from 'vscode-jsonrpc'; * * https://github.com/Microsoft/vscode-languageserver-node/issues/270 */ -export default class SafeStreamMessageReader extends StreamMessageReader { - onData(data: Buffer | string) { +class SafeStreamMessageReader extends _vscodeJsonrpc().StreamMessageReader { + onData(data) { try { super.onData(data); } catch (err) { - this.fireError(err); - // Stop handling events, as stream errors are unrecoverable. + this.fireError(err); // Stop handling events, as stream errors are unrecoverable. // The owner of the reader should tear itself down as well. + this.dispose(); this.readable.removeAllListeners(); } } + } + +exports.default = SafeStreamMessageReader; \ No newline at end of file diff --git a/modules/nuclide-commons/SimpleCache.js b/modules/nuclide-commons/SimpleCache.js index a4bda051..b3e1e87c 100644 --- a/modules/nuclide-commons/SimpleCache.js +++ b/modules/nuclide-commons/SimpleCache.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SimpleCache = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,10 +13,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - // TODO: Merge this class with nuclide-commons/cache.js because they probably do // very similar things @@ -17,78 +23,73 @@ * Tiny class that is useful to cache simple values. * It's quite useful for promises with a SimpleCache> which allows reusing the same promise. */ +class SimpleCache { + constructor(config = {}) { + this.store = new Map(); -type DisposeCallback = (value: T) => void; -type KeyFactory = (args: KeyArgs) => mixed; - -type CacheConfig = { - keyFactory?: KeyFactory, - dispose?: DisposeCallback, -}; - -export class SimpleCache { - store: Map = new Map(); - _dispose: ?DisposeCallback; - _keyFactory: KeyFactory; - - constructor(config: CacheConfig = {}) { if (config.dispose != null) { this._dispose = config.dispose; } - this._keyFactory = - config.keyFactory != null - ? config.keyFactory - : (keyArgs: KeyArgs) => keyArgs; + + this._keyFactory = config.keyFactory != null ? config.keyFactory : keyArgs => keyArgs; } - _getUnsafe(key: mixed): T { - return ((this.store.get(key): any): T); + _getUnsafe(key) { + return this.store.get(key); } - getOrCreate(keyArgs: KeyArgs, factory: (KeyArgs, mixed) => T): T { + getOrCreate(keyArgs, factory) { const key = this._keyFactory(keyArgs); + if (this.store.has(key)) { return this._getUnsafe(key); } + const value = factory(keyArgs, key); this.store.set(key, value); return value; } - delete(keyArgs: KeyArgs): void { + delete(keyArgs) { const key = this._keyFactory(keyArgs); + if (this._dispose != null) { this._ifHas(key, this._dispose); } + this.store.delete(key); } - clear(): void { + clear() { if (this._dispose != null) { this.store.forEach(this._dispose); } + this.store.clear(); } - get(keyArgs: KeyArgs): ?T { + get(keyArgs) { return this.store.get(this._keyFactory(keyArgs)); } - set(keyArgs: KeyArgs, value: T): void { + set(keyArgs, value) { this.store.set(this._keyFactory(keyArgs), value); } - ifHas(keyArgs: KeyArgs, callback: (value: T) => void) { + ifHas(keyArgs, callback) { this._ifHas(this._keyFactory(keyArgs), callback); } - _ifHas(key: mixed, callback: (value: T) => void) { + _ifHas(key, callback) { if (this.store.has(key)) { callback(this._getUnsafe(key)); } } - keyForArgs(keyArgs: KeyArgs): mixed { + keyForArgs(keyArgs) { return this._keyFactory(keyArgs); } + } + +exports.SimpleCache = SimpleCache; \ No newline at end of file diff --git a/modules/nuclide-commons/UniversalDisposable.js b/modules/nuclide-commons/UniversalDisposable.js index f09f4ab8..426cb0b2 100644 --- a/modules/nuclide-commons/UniversalDisposable.js +++ b/modules/nuclide-commons/UniversalDisposable.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,47 +13,34 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -export type IDestructible = { - destroy(): void, - onDidDestroy(callback: () => mixed): IDisposable, -}; - -export type AnyTeardown = - | (() => mixed) - | rxjs$ISubscription - | IDisposable - | IDestructible; - /** * Like a CompositeDisposable, but in addition to Disposable instances it can * also accept plain functions and Rx subscriptions. */ -export default class UniversalDisposable { - disposed: boolean; - teardowns: Set; - - constructor(...teardowns: Array) { +class UniversalDisposable { + constructor(...teardowns) { this.teardowns = new Set(); this.disposed = false; + if (teardowns.length) { this.add(...teardowns); } } - add(...teardowns: Array): void { + add(...teardowns) { if (this.disposed) { throw new Error('Cannot add to an already disposed UniversalDisposable!'); } + for (let i = 0; i < teardowns.length; i++) { assertTeardown(teardowns[i]); this.teardowns.add(teardowns[i]); } } - /** * Adds a list of teardowns but also ties them to the lifetime of `destructible`. * When `destructible` is destroyed (or `this.dispose()` gets called, whichever comes first), @@ -56,30 +50,27 @@ export default class UniversalDisposable { * - we need to make sure that all teardowns are also removed on destroy * - we also need to ensure that we don't leak the onDidDestroy disposable */ - addUntilDestroyed( - destructible: IDestructible, - ...teardowns: Array - ) { + + + addUntilDestroyed(destructible, ...teardowns) { if (this.disposed) { throw new Error('Cannot add to an already disposed UniversalDisposable!'); } - const destroyDisposable = new UniversalDisposable( - ...teardowns, - destructible.onDidDestroy(() => { - destroyDisposable.dispose(); - this.remove(destroyDisposable); - }), - ); + + const destroyDisposable = new UniversalDisposable(...teardowns, destructible.onDidDestroy(() => { + destroyDisposable.dispose(); + this.remove(destroyDisposable); + })); this.add(destroyDisposable); } - remove(teardown: AnyTeardown): void { + remove(teardown) { if (!this.disposed) { this.teardowns.delete(teardown); } } - dispose(): void { + dispose() { if (!this.disposed) { this.disposed = true; this.teardowns.forEach(teardown => { @@ -93,31 +84,28 @@ export default class UniversalDisposable { teardown(); } }); - this.teardowns = (null: any); + this.teardowns = null; } } - unsubscribe(): void { + unsubscribe() { this.dispose(); } - clear(): void { + clear() { if (!this.disposed) { this.teardowns.clear(); } } + } -function assertTeardown(teardown: AnyTeardown): void { - if ( - typeof teardown.dispose === 'function' || - typeof teardown.unsubscribe === 'function' || - typeof teardown.destroy === 'function' || - typeof teardown === 'function' - ) { +exports.default = UniversalDisposable; + +function assertTeardown(teardown) { + if (typeof teardown.dispose === 'function' || typeof teardown.unsubscribe === 'function' || typeof teardown.destroy === 'function' || typeof teardown === 'function') { return; } - throw new TypeError( - 'Arguments to UniversalDisposable.add must be disposable', - ); -} + + throw new TypeError('Arguments to UniversalDisposable.add must be disposable'); +} \ No newline at end of file diff --git a/modules/nuclide-commons/__mocks__/fixtures/symbol-definition-preview-sample.js b/modules/nuclide-commons/__mocks__/fixtures/symbol-definition-preview-sample.js index b2edab68..f60bb6bc 100644 --- a/modules/nuclide-commons/__mocks__/fixtures/symbol-definition-preview-sample.js +++ b/modules/nuclide-commons/__mocks__/fixtures/symbol-definition-preview-sample.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.aSingleLineFunctionSignature = aSingleLineFunctionSignature; +exports.aMultiLineFunctionSignature = aMultiLineFunctionSignature; +exports.aPoorlyIndentedFunction = aPoorlyIndentedFunction; + /** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. @@ -5,47 +14,30 @@ * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. * - * @flow strict + * strict */ // license header above without @format // eslint-disable-next-line const A_CONSTANT = 42; -const SOME_OTHER_CONSTANT = 24; +const SOME_OTHER_CONSTANT = 24; // eslint-disable-next-line -// eslint-disable-next-line const A_MULTILINE_CONST = ` hey look I span multiple lines `; -type Something = { - name: string, - age?: number, -}; - -export function aSingleLineFunctionSignature() { +function aSingleLineFunctionSignature() { return A_CONSTANT + SOME_OTHER_CONSTANT; } -export function aMultiLineFunctionSignature( - aReallyReallyLongArgumentNameThatWouldRequireThisToBreakAcrossMultipleLines: Something, -): number { +function aMultiLineFunctionSignature(aReallyReallyLongArgumentNameThatWouldRequireThisToBreakAcrossMultipleLines) { return 97; } - export function aPoorlyIndentedFunction( -aReallyReallyLongArgumentNameThatWouldRequireThisToBreakAcrossMultipleLines: Something, -): number { +function aPoorlyIndentedFunction(aReallyReallyLongArgumentNameThatWouldRequireThisToBreakAcrossMultipleLines) { return 97; } -type SomethingComplex = { - properties: { - name: string, - age?: number, - }, -}; - -const foo: ?SomethingComplex = null; -foo; +const foo = null; +foo; \ No newline at end of file diff --git a/modules/nuclide-commons/__mocks__/fixtures/throw.js b/modules/nuclide-commons/__mocks__/fixtures/throw.js index 23330ab5..bf74f08b 100644 --- a/modules/nuclide-commons/__mocks__/fixtures/throw.js +++ b/modules/nuclide-commons/__mocks__/fixtures/throw.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,8 +8,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -throw new Error('Errored!'); +throw new Error('Errored!'); \ No newline at end of file diff --git a/modules/nuclide-commons/__mocks__/fixtures/toBeMocked.js b/modules/nuclide-commons/__mocks__/fixtures/toBeMocked.js index 009f7899..d3734297 100644 --- a/modules/nuclide-commons/__mocks__/fixtures/toBeMocked.js +++ b/modules/nuclide-commons/__mocks__/fixtures/toBeMocked.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.importedFunction = importedFunction; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,10 +13,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -export function importedFunction(arg: any): any { +function importedFunction(arg) { return 0; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/__mocks__/fixtures/toBeTested.js b/modules/nuclide-commons/__mocks__/fixtures/toBeTested.js index 8eda9295..ccbaa987 100644 --- a/modules/nuclide-commons/__mocks__/fixtures/toBeTested.js +++ b/modules/nuclide-commons/__mocks__/fixtures/toBeTested.js @@ -1,3 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.functionToTest = functionToTest; + +function _toBeMocked() { + const data = require("./toBeMocked"); + + _toBeMocked = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,12 +23,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {importedFunction} from './toBeMocked'; - -export function functionToTest(): any { - return importedFunction(42); -} +function functionToTest() { + return (0, _toBeMocked().importedFunction)(42); +} \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/AbortController-test.js b/modules/nuclide-commons/__tests__/AbortController-test.js index 105bd527..0a3d0c41 100644 --- a/modules/nuclide-commons/__tests__/AbortController-test.js +++ b/modules/nuclide-commons/__tests__/AbortController-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _AbortController() { + const data = _interopRequireDefault(require("../AbortController")); + + _AbortController = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,38 +20,28 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import AbortController from '../AbortController'; - describe('AbortController', () => { it('dispatches abort() events', () => { - const controller = new AbortController(); + const controller = new (_AbortController().default)(); expect(controller.signal.aborted).toBe(false); - const spy = jest.fn(); controller.signal.onabort = spy; - controller.abort(); - expect(controller.signal.aborted).toBe(true); - expect(spy).toHaveBeenCalled(); + expect(spy).toHaveBeenCalled(); // Ensure that we don't double-abort. - // Ensure that we don't double-abort. controller.abort(); expect(spy.mock.calls.length).toBe(1); }); - it('dispatches abort() events via addEventListener', () => { - const controller = new AbortController(); + const controller = new (_AbortController().default)(); const spy = jest.fn(); controller.signal.addEventListener('abort', spy); - controller.abort(); - expect(controller.signal.aborted).toBe(true); expect(spy).toHaveBeenCalled(); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/BatchProcessedQueue-test.js b/modules/nuclide-commons/__tests__/BatchProcessedQueue-test.js index cbf6c70e..a61ecd8d 100644 --- a/modules/nuclide-commons/__tests__/BatchProcessedQueue-test.js +++ b/modules/nuclide-commons/__tests__/BatchProcessedQueue-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _BatchProcessedQueue() { + const data = _interopRequireDefault(require("../BatchProcessedQueue")); + + _BatchProcessedQueue = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,33 +20,26 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import BatchProcessedQueue from '../BatchProcessedQueue'; - jest.useFakeTimers(); - describe('analytics - BatchProcessedQueue', () => { it('regular operation', () => { const handler = jasmine.createSpy('handler'); - const queue = new BatchProcessedQueue(5000, handler); - + const queue = new (_BatchProcessedQueue().default)(5000, handler); queue.add(1); queue.add(2); queue.add(3); queue.add(4); queue.add(5); expect(handler).not.toHaveBeenCalled(); - jest.advanceTimersByTime(4999); expect(handler).not.toHaveBeenCalled(); jest.advanceTimersByTime(1); expect(handler).toHaveBeenCalledWith([1, 2, 3, 4, 5]); - queue.add(42); jest.advanceTimersByTime(10000); expect(handler).toHaveBeenCalledWith([42]); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/ConfigCache-test.js b/modules/nuclide-commons/__tests__/ConfigCache-test.js index af176d4c..3c4fa975 100644 --- a/modules/nuclide-commons/__tests__/ConfigCache-test.js +++ b/modules/nuclide-commons/__tests__/ConfigCache-test.js @@ -1,3 +1,27 @@ +"use strict"; + +function _ConfigCache() { + const data = require("../ConfigCache"); + + _ConfigCache = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,80 +30,49 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {ConfigCache} from '../ConfigCache'; -import nuclideUri from '../nuclideUri'; - const CONFIG_FILE_NAME = '.test_nuclide_config_file'; const CONFIG_FILE_NAME_2 = '.test_nuclide_config_file_2'; - describe('ConfigCache', () => { - const noConfigFolder = nuclideUri.join(__dirname, '../__mocks__/fixtures'); - const rootFolder = nuclideUri.join( - __dirname, - '../__mocks__/fixtures/ConfigCache', - ); - const rootFile = nuclideUri.join( - __dirname, - '../__mocks__/fixtures/ConfigCache/file', - ); - const nestedFolder = nuclideUri.join( - __dirname, - '../__mocks__/fixtures/ConfigCache/testFolder', - ); - const nestedFolder2 = nuclideUri.join( - __dirname, - '../__mocks__/fixtures/ConfigCache/testFolder2', - ); + const noConfigFolder = _nuclideUri().default.join(__dirname, '../__mocks__/fixtures'); - it('finds the right config dir', async () => { - const cache = new ConfigCache([CONFIG_FILE_NAME]); + const rootFolder = _nuclideUri().default.join(__dirname, '../__mocks__/fixtures/ConfigCache'); - expect(await cache.getConfigDir(noConfigFolder)).toBe(null); - expect(await cache.getConfigDir(rootFolder)).toBe(rootFolder); - expect(await cache.getConfigDir(rootFile)).toBe(rootFolder); - }); + const rootFile = _nuclideUri().default.join(__dirname, '../__mocks__/fixtures/ConfigCache/file'); - it('prefers closer matches with multiple config files', async () => { - const cache = new ConfigCache([CONFIG_FILE_NAME, CONFIG_FILE_NAME_2]); + const nestedFolder = _nuclideUri().default.join(__dirname, '../__mocks__/fixtures/ConfigCache/testFolder'); - expect(await cache.getConfigDir(rootFolder)).toBe(rootFolder); - expect(await cache.getConfigDir(nestedFolder2)).toBe(nestedFolder2); - }); + const nestedFolder2 = _nuclideUri().default.join(__dirname, '../__mocks__/fixtures/ConfigCache/testFolder2'); + it('finds the right config dir', async () => { + const cache = new (_ConfigCache().ConfigCache)([CONFIG_FILE_NAME]); + expect((await cache.getConfigDir(noConfigFolder))).toBe(null); + expect((await cache.getConfigDir(rootFolder))).toBe(rootFolder); + expect((await cache.getConfigDir(rootFile))).toBe(rootFolder); + }); + it('prefers closer matches with multiple config files', async () => { + const cache = new (_ConfigCache().ConfigCache)([CONFIG_FILE_NAME, CONFIG_FILE_NAME_2]); + expect((await cache.getConfigDir(rootFolder))).toBe(rootFolder); + expect((await cache.getConfigDir(nestedFolder2))).toBe(nestedFolder2); + }); it('prefers further matches when the search strategy is "eclipse"', async () => { - const cache = new ConfigCache( - [CONFIG_FILE_NAME, CONFIG_FILE_NAME_2], - 'eclipse', - ); - - expect(await cache.getConfigDir(rootFolder)).toBe(rootFolder); - expect(await cache.getConfigDir(nestedFolder)).toBe(rootFolder); - expect(await cache.getConfigDir(nestedFolder2)).toBe(rootFolder); + const cache = new (_ConfigCache().ConfigCache)([CONFIG_FILE_NAME, CONFIG_FILE_NAME_2], 'eclipse'); + expect((await cache.getConfigDir(rootFolder))).toBe(rootFolder); + expect((await cache.getConfigDir(nestedFolder))).toBe(rootFolder); + expect((await cache.getConfigDir(nestedFolder2))).toBe(rootFolder); }); - it('prefers priority matches when the search strategy is "ocaml"', async () => { - const cache = new ConfigCache( - [CONFIG_FILE_NAME, CONFIG_FILE_NAME_2], - 'ocaml', - ); - - expect(await cache.getConfigDir(rootFolder)).toBe('/'); - expect(await cache.getConfigDir(nestedFolder2)).toBe('/'); + const cache = new (_ConfigCache().ConfigCache)([CONFIG_FILE_NAME, CONFIG_FILE_NAME_2], 'ocaml'); + expect((await cache.getConfigDir(rootFolder))).toBe('/'); + expect((await cache.getConfigDir(nestedFolder2))).toBe('/'); }); - it('matches first path segment when the search strategy is "thrift"', async () => { - const cache = new ConfigCache( - ['ConfigCache/testFolder', 'ConfigCache'], - 'thrift', - ); + const cache = new (_ConfigCache().ConfigCache)(['ConfigCache/testFolder', 'ConfigCache'], 'thrift'); // matches both patterns, tie-breaks with first one + + expect((await cache.getConfigDir(nestedFolder))).toBe(nestedFolder); // matches second pattern - // matches both patterns, tie-breaks with first one - expect(await cache.getConfigDir(nestedFolder)).toBe(nestedFolder); - // matches second pattern - expect(await cache.getConfigDir(nestedFolder2)).toBe(rootFolder); + expect((await cache.getConfigDir(nestedFolder2))).toBe(rootFolder); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/Hasher-test.js b/modules/nuclide-commons/__tests__/Hasher-test.js index 1c242b19..43c6f1a9 100644 --- a/modules/nuclide-commons/__tests__/Hasher-test.js +++ b/modules/nuclide-commons/__tests__/Hasher-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _Hasher() { + const data = _interopRequireDefault(require("../Hasher")); + + _Hasher = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,41 +20,34 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import Hasher from '../Hasher'; - describe('Hasher', () => { it('creates a new hash for each object', () => { const a = {}; const b = {}; - const hasher = new Hasher(); + const hasher = new (_Hasher().default)(); expect(hasher.getHash(a)).not.toBe(hasher.getHash(b)); }); - it('returns the same hash for the same object', () => { const a = {}; - const hasher = new Hasher(); + const hasher = new (_Hasher().default)(); expect(hasher.getHash(a)).toBe(hasher.getHash(a)); }); - it('works for numbers', () => { - const hasher = new Hasher(); + const hasher = new (_Hasher().default)(); expect(hasher.getHash(1)).toBe(hasher.getHash(1)); expect(hasher.getHash(1)).not.toBe(hasher.getHash(2)); }); - it('works for booleans', () => { - const hasher = new Hasher(); + const hasher = new (_Hasher().default)(); expect(hasher.getHash(true)).toBe(hasher.getHash(true)); expect(hasher.getHash(true)).not.toBe(hasher.getHash(false)); }); - it('works for strings', () => { - const hasher = new Hasher(); + const hasher = new (_Hasher().default)(); expect(hasher.getHash('a')).toBe(hasher.getHash('a')); expect(hasher.getHash('a')).not.toBe(hasher.getHash('b')); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/Model-test.js b/modules/nuclide-commons/__tests__/Model-test.js index 366e80c5..a8d1f9bd 100644 --- a/modules/nuclide-commons/__tests__/Model-test.js +++ b/modules/nuclide-commons/__tests__/Model-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _Model() { + const data = _interopRequireDefault(require("../Model")); + + _Model = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,36 +20,45 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import Model from '../Model'; - describe('Model', () => { it('setStates state when setState is called', () => { - const model = new Model({count: 0, other: true}); - model.setState({count: 5}); + const model = new (_Model().default)({ + count: 0, + other: true + }); + model.setState({ + count: 5 + }); expect(model.state.count).toBe(5); }); - it('only changes the provided values when setState is called', () => { - const model = new Model({count: 0, other: true}); - model.setState({count: 5}); + const model = new (_Model().default)({ + count: 0, + other: true + }); + model.setState({ + count: 5 + }); expect(model.state.other).toBe(true); }); - it('can be converted to an observable', async () => { - const model = new Model({count: 0, other: true}); - const states = model - .toObservable() - .take(2) - .toArray() - .toPromise(); - model.setState({count: 5}); - expect(await states).toEqual([ - {count: 0, other: true}, - {count: 5, other: true}, - ]); + const model = new (_Model().default)({ + count: 0, + other: true + }); + const states = model.toObservable().take(2).toArray().toPromise(); + model.setState({ + count: 5 + }); + expect((await states)).toEqual([{ + count: 0, + other: true + }, { + count: 5, + other: true + }]); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/ObservablePool-test.js b/modules/nuclide-commons/__tests__/ObservablePool-test.js index 38990e89..356bbce9 100644 --- a/modules/nuclide-commons/__tests__/ObservablePool-test.js +++ b/modules/nuclide-commons/__tests__/ObservablePool-test.js @@ -1,3 +1,29 @@ +"use strict"; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _ObservablePool() { + const data = _interopRequireDefault(require("../ObservablePool")); + + _ObservablePool = function () { + return data; + }; + + return data; +} + +function _waits_for() { + const data = _interopRequireDefault(require("../../../jest/waits_for")); + + _waits_for = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,133 +32,107 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import {Observable, Subject} from 'rxjs'; -import ObservablePool from '../ObservablePool'; -import waitsFor from '../../../jest/waits_for'; - describe('ObservablePool', () => { it('limits the concurrency of observable values with cancellation', () => { - const pool = new ObservablePool(2); - - const subject1 = new Subject(); + const pool = new (_ObservablePool().default)(2); + const subject1 = new _RxMin.Subject(); const spy1 = jest.fn().mockReturnValue(subject1); const req1 = pool.schedule(spy1); - - const subject2 = new Subject(); + const subject2 = new _RxMin.Subject(); const spy2 = jest.fn().mockReturnValue(subject2); const req2 = pool.schedule(spy2); - - const subject3 = new Subject(); + const subject3 = new _RxMin.Subject(); const spy3 = jest.fn().mockReturnValue(subject3); - const req3 = pool.schedule(Observable.defer(spy3)); + const req3 = pool.schedule(_RxMin.Observable.defer(spy3)); // Nothing should happen until subscription. - // Nothing should happen until subscription. expect(spy1).not.toHaveBeenCalled(); expect(spy2).not.toHaveBeenCalled(); expect(spy3).not.toHaveBeenCalled(); - const subscription = req1.subscribe(); req2.subscribe(); const nextSpy = jest.fn(); const errorSpy = jest.fn(); - req3.subscribe(nextSpy, errorSpy); + req3.subscribe(nextSpy, errorSpy); // Only the first two should be subscribed to. - // Only the first two should be subscribed to. expect(spy1).toHaveBeenCalled(); expect(spy2).toHaveBeenCalled(); - expect(spy3).not.toHaveBeenCalled(); + expect(spy3).not.toHaveBeenCalled(); // Once we cancel the first, the third goes through. - // Once we cancel the first, the third goes through. subscription.unsubscribe(); expect(spy3).toHaveBeenCalled(); - subject2.complete(); expect(pool._responseListeners.size).toBe(1); - subject3.next('test'); subject3.error('error'); expect(pool._responseListeners.size).toBe(0); expect(nextSpy).toHaveBeenCalledWith('test'); expect(errorSpy).toHaveBeenCalledWith('error'); }); - it('waits for promises, even on unsubscribe', async () => { - const pool = new ObservablePool(1); - let resolve: ?Function; - let reject: ?Function; - const spy1 = jest.fn().mockReturnValue( - new Promise(r => { - resolve = r; - }), - ); - const spy2 = jest.fn().mockReturnValue( - new Promise((_, r) => { - reject = r; - }), - ); + const pool = new (_ObservablePool().default)(1); + let resolve; + let reject; + const spy1 = jest.fn().mockReturnValue(new Promise(r => { + resolve = r; + })); + const spy2 = jest.fn().mockReturnValue(new Promise((_, r) => { + reject = r; + })); const errorSpy = jest.fn(); const sub1 = pool.schedule(spy1).subscribe(); - pool.schedule(spy2).subscribe(() => {}, errorSpy); - - // Immediately subscribe & unsubscribe - + pool.schedule(spy2).subscribe(() => {}, errorSpy); // Immediately subscribe & unsubscribe - // the request should never be scheduled. - const spy3 = jest.fn().mockReturnValue(Promise.resolve()); - pool - .schedule(spy3) - .subscribe() - .unsubscribe(); + const spy3 = jest.fn().mockReturnValue(Promise.resolve()); + pool.schedule(spy3).subscribe().unsubscribe(); expect(spy1).toHaveBeenCalled(); expect(spy2).not.toHaveBeenCalled(); + sub1.unsubscribe(); // Remove the request, but remain blocked until the promise actually resolves. - sub1.unsubscribe(); - // Remove the request, but remain blocked until the promise actually resolves. expect(pool._responseListeners.size).toEqual(1); expect(spy2).not.toHaveBeenCalled(); - invariant(resolve != null, 'spy1 should have been scheduled'); - resolve(); - // Promise resolution is always async... - await waitsFor(() => spy2.mock.calls.length > 0, 'spy2 should be called'); + if (!(resolve != null)) { + throw new Error('spy1 should have been scheduled'); + } - invariant(reject != null, 'spy2 was called'); - reject('test'); + resolve(); // Promise resolution is always async... + + await (0, _waits_for().default)(() => spy2.mock.calls.length > 0, 'spy2 should be called'); - await waitsFor( - () => errorSpy.mock.calls.length > 0, - 'errorSpy should be called', - ); + if (!(reject != null)) { + throw new Error('spy2 was called'); + } + reject('test'); + await (0, _waits_for().default)(() => errorSpy.mock.calls.length > 0, 'errorSpy should be called'); expect(errorSpy).toHaveBeenCalledWith('test'); expect(pool._responseListeners.size).toBe(0); expect(spy3).not.toHaveBeenCalled(); }); - it('catches executor errors', () => { - const pool = new ObservablePool(1); + const pool = new (_ObservablePool().default)(1); let error; - pool - .schedule(() => { - throw new Error('test'); - }) - .subscribe({ - error(err) { - error = err; - }, - }); + pool.schedule(() => { + throw new Error('test'); + }).subscribe({ + error(err) { + error = err; + } + + }); expect(error).toEqual(Error('test')); }); - it('errors on disposal', () => { - const pool = new ObservablePool(1); + const pool = new (_ObservablePool().default)(1); const errorSpy = jest.fn(); - pool.schedule(() => Promise.resolve()).subscribe({error: errorSpy}); + pool.schedule(() => Promise.resolve()).subscribe({ + error: errorSpy + }); pool.dispose(); expect(errorSpy).toHaveBeenCalled(); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/SafeStreamMessageReader-test.js b/modules/nuclide-commons/__tests__/SafeStreamMessageReader-test.js index d8b2a08f..32d5c677 100644 --- a/modules/nuclide-commons/__tests__/SafeStreamMessageReader-test.js +++ b/modules/nuclide-commons/__tests__/SafeStreamMessageReader-test.js @@ -1,3 +1,29 @@ +"use strict"; + +var _stream = _interopRequireDefault(require("stream")); + +function _SafeStreamMessageReader() { + const data = _interopRequireDefault(require("../SafeStreamMessageReader")); + + _SafeStreamMessageReader = function () { + return data; + }; + + return data; +} + +function _waits_for() { + const data = _interopRequireDefault(require("../../../jest/waits_for")); + + _waits_for = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,48 +32,42 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import Stream from 'stream'; -import SafeStreamMessageReader from '../SafeStreamMessageReader'; -import waitsFor from '../../../jest/waits_for'; - describe('SafeStreamMessageReader', () => { it('reads valid messages', async () => { - const readable = new Stream.Readable({ + const readable = new _stream.default.Readable({ read() { this.push('Content-Length: 7\r\n\r\n{"a":1}'); this.push(null); - }, + } + }); - const reader = new SafeStreamMessageReader(readable); + const reader = new (_SafeStreamMessageReader().default)(readable); const listenSpy = jest.fn(); reader.listen(listenSpy); - - await waitsFor(() => listenSpy.mock.calls.length > 0); - - expect(listenSpy.mock.calls[0]).toEqual([{a: 1}]); + await (0, _waits_for().default)(() => listenSpy.mock.calls.length > 0); + expect(listenSpy.mock.calls[0]).toEqual([{ + a: 1 + }]); }); - it('emits an error for an invalid header', async () => { - const readable = new Stream.Readable({ + const readable = new _stream.default.Readable({ read() { this.push('Invalid-Header: test\r\n\r\n'); this.push('Content-Length: 2\r\n\r\n{}'); this.push(null); - }, + } + }); - const reader = new SafeStreamMessageReader(readable); + const reader = new (_SafeStreamMessageReader().default)(readable); const listenSpy = jest.fn(); const errorSpy = jest.fn(); reader.listen(listenSpy); reader.onError(errorSpy); - - await waitsFor(() => errorSpy.mock.calls.length > 0); - + await (0, _waits_for().default)(() => errorSpy.mock.calls.length > 0); expect(errorSpy.mock.calls[0][0].name).toBe('Error'); expect(listenSpy).not.toHaveBeenCalled(); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/UniversalDisposable-test.js b/modules/nuclide-commons/__tests__/UniversalDisposable-test.js index 08f2af04..3fdcfafc 100644 --- a/modules/nuclide-commons/__tests__/UniversalDisposable-test.js +++ b/modules/nuclide-commons/__tests__/UniversalDisposable-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,54 +20,49 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import UniversalDisposable from '../UniversalDisposable'; - describe('UniversalDisposable', () => { it('disposes of the Disposable arguments', () => { const dispose = jest.fn(); - const universal = new UniversalDisposable({dispose}); - + const universal = new (_UniversalDisposable().default)({ + dispose + }); expect(dispose.mock.calls.length > 0).toBe(false); universal.dispose(); expect(dispose.mock.calls.length).toBe(1); }); - it('throws if you add after disposing', () => { - const universal = new UniversalDisposable(); + const universal = new (_UniversalDisposable().default)(); universal.dispose(); expect(() => { universal.add(() => {}); }).toThrow('Cannot add to an already disposed UniversalDisposable!'); }); - it('calls function arguments', () => { const foo = jest.fn(); - const universal = new UniversalDisposable(foo); - + const universal = new (_UniversalDisposable().default)(foo); expect(foo.mock.calls.length > 0).toBe(false); universal.dispose(); expect(foo.mock.calls.length).toBe(1); }); - it('calls unsubscribe arguments', () => { const unsubscribe = jest.fn(); - const universal = new UniversalDisposable(unsubscribe); - + const universal = new (_UniversalDisposable().default)(unsubscribe); expect(unsubscribe.mock.calls.length > 0).toBe(false); universal.dispose(); expect(unsubscribe.mock.calls.length).toBe(1); }); - it('supports creation with mixed teardowns', () => { const dispose = jest.fn(); const unsubscribe = jest.fn(); const foo = jest.fn(); - const universal = new UniversalDisposable({dispose}, {unsubscribe}, foo); - + const universal = new (_UniversalDisposable().default)({ + dispose + }, { + unsubscribe + }, foo); expect(dispose.mock.calls.length > 0).toBe(false); expect(unsubscribe.mock.calls.length > 0).toBe(false); expect(foo.mock.calls.length > 0).toBe(false); @@ -62,16 +71,21 @@ describe('UniversalDisposable', () => { expect(unsubscribe.mock.calls.length).toBe(1); expect(foo.mock.calls.length).toBe(1); }); - it('supports adding mixed teardowns', () => { const dispose = jest.fn(); const unsubscribe = jest.fn(); const destroy = jest.fn(); - const destroyable = {destroy, onDidDestroy: jest.fn()}; + const destroyable = { + destroy, + onDidDestroy: jest.fn() + }; const foo = jest.fn(); - const universal = new UniversalDisposable(); - universal.add({dispose}, {unsubscribe}, destroyable, foo); - + const universal = new (_UniversalDisposable().default)(); + universal.add({ + dispose + }, { + unsubscribe + }, destroyable, foo); expect(dispose.mock.calls.length > 0).toBe(false); expect(unsubscribe.mock.calls.length > 0).toBe(false); expect(destroy.mock.calls.length > 0).toBe(false); @@ -82,13 +96,15 @@ describe('UniversalDisposable', () => { expect(destroy.mock.calls.length).toBe(1); expect(foo.mock.calls.length).toBe(1); }); - it('supports unsubscribe as well', () => { const dispose = jest.fn(); const unsubscribe = jest.fn(); const foo = jest.fn(); - const universal = new UniversalDisposable({dispose}, {unsubscribe}, foo); - + const universal = new (_UniversalDisposable().default)({ + dispose + }, { + unsubscribe + }, foo); expect(dispose.mock.calls.length > 0).toBe(false); expect(unsubscribe.mock.calls.length > 0).toBe(false); expect(foo.mock.calls.length > 0).toBe(false); @@ -97,13 +113,15 @@ describe('UniversalDisposable', () => { expect(unsubscribe.mock.calls.length).toBe(1); expect(foo.mock.calls.length).toBe(1); }); - it('multiple dispose/unsubscribe calls have no effect', () => { const dispose = jest.fn(); const unsubscribe = jest.fn(); const foo = jest.fn(); - const universal = new UniversalDisposable({dispose}, {unsubscribe}, foo); - + const universal = new (_UniversalDisposable().default)({ + dispose + }, { + unsubscribe + }, foo); expect(dispose.mock.calls.length > 0).toBe(false); expect(unsubscribe.mock.calls.length > 0).toBe(false); expect(foo.mock.calls.length > 0).toBe(false); @@ -115,133 +133,123 @@ describe('UniversalDisposable', () => { expect(unsubscribe.mock.calls.length).toBe(1); expect(foo.mock.calls.length).toBe(1); }); - it('supports removal of the teardowns', () => { - const dispose = {dispose: jest.fn()}; - const unsubscribe = {unsubscribe: jest.fn()}; + const dispose = { + dispose: jest.fn() + }; + const unsubscribe = { + unsubscribe: jest.fn() + }; const foo = jest.fn(); - const universal = new UniversalDisposable(dispose, unsubscribe, foo); - + const universal = new (_UniversalDisposable().default)(dispose, unsubscribe, foo); universal.remove(unsubscribe); universal.remove(dispose); universal.remove(foo); - universal.dispose(); - expect(dispose.dispose.mock.calls.length > 0).toBe(false); expect(unsubscribe.unsubscribe.mock.calls.length > 0).toBe(false); expect(foo.mock.calls.length > 0).toBe(false); }); - it('can clear all of the teardowns', () => { - const dispose = {dispose: jest.fn()}; - const unsubscribe = {unsubscribe: jest.fn()}; + const dispose = { + dispose: jest.fn() + }; + const unsubscribe = { + unsubscribe: jest.fn() + }; const foo = jest.fn(); - const universal = new UniversalDisposable(dispose, unsubscribe, foo); - + const universal = new (_UniversalDisposable().default)(dispose, unsubscribe, foo); universal.clear(); - universal.dispose(); - expect(dispose.dispose.mock.calls.length > 0).toBe(false); expect(unsubscribe.unsubscribe.mock.calls.length > 0).toBe(false); expect(foo.mock.calls.length > 0).toBe(false); }); - it('maintains implicit order of the teardowns', () => { const ids = []; const foo1 = () => ids.push(1); + const foo2 = () => ids.push(2); + const foo3 = () => ids.push(3); + const foo4 = () => ids.push(4); - const universal = new UniversalDisposable(foo1, foo3); + const universal = new (_UniversalDisposable().default)(foo1, foo3); universal.add(foo4, foo2); - universal.dispose(); - expect(ids).toEqual([1, 3, 4, 2]); }); - describe('teardown priority', () => { it('calls dispose()', () => { - const foo: Function = jest.fn(); + const foo = jest.fn(); foo.dispose = jest.fn(); foo.unsubscribe = jest.fn(); - - const universal = new UniversalDisposable(foo); + const universal = new (_UniversalDisposable().default)(foo); universal.dispose(); - expect(foo.dispose.mock.calls.length > 0).toBe(true); expect(foo.unsubscribe.mock.calls.length > 0).toBe(false); expect(foo.mock.calls.length > 0).toBe(false); }); - it('calls unsubscribe()', () => { - const foo: Function = jest.fn(); + const foo = jest.fn(); foo.dispose = null; foo.unsubscribe = jest.fn(); - - const universal = new UniversalDisposable(foo); + const universal = new (_UniversalDisposable().default)(foo); universal.dispose(); - expect(foo.unsubscribe.mock.calls.length > 0).toBe(true); expect(foo.mock.calls.length > 0).toBe(false); }); - it('calls the function', () => { - const foo: Function = jest.fn(); + const foo = jest.fn(); foo.dispose = null; foo.unsubscribe = null; - - const universal = new UniversalDisposable(foo); + const universal = new (_UniversalDisposable().default)(foo); universal.dispose(); - expect(foo.mock.calls.length > 0).toBe(true); }); }); - describe('addUntilDestroyed', () => { class MockDestructible { - destroyCallbacks = new Set(); + constructor() { + this.destroyCallbacks = new Set(); + } + destroy() { this.destroyCallbacks.forEach(x => x()); } + onDidDestroy(callback) { this.destroyCallbacks.add(callback); - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { this.destroyCallbacks.delete(callback); }); } + } it('cleans everything up on destroy', () => { - const universal = new UniversalDisposable(); + const universal = new (_UniversalDisposable().default)(); const mockDestructible = new MockDestructible(); - const disposable1 = new UniversalDisposable(); - const disposable2 = new UniversalDisposable(); - + const disposable1 = new (_UniversalDisposable().default)(); + const disposable2 = new (_UniversalDisposable().default)(); universal.addUntilDestroyed(mockDestructible, disposable1, disposable2); expect(universal.teardowns.size).toBe(1); mockDestructible.destroy(); - expect(universal.teardowns.size).toBe(0); expect(disposable1.disposed).toBe(true); expect(disposable2.disposed).toBe(true); }); - it('cleans up destroy handlers on dispose', () => { - const universal = new UniversalDisposable(); + const universal = new (_UniversalDisposable().default)(); const mockDestructible = new MockDestructible(); - const disposable1 = new UniversalDisposable(); - + const disposable1 = new (_UniversalDisposable().default)(); universal.addUntilDestroyed(mockDestructible, disposable1); expect(universal.teardowns.size).toBe(1); universal.dispose(); - expect(disposable1.disposed).toBe(true); expect(mockDestructible.destroyCallbacks.size).toBe(0); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/cache-test.js b/modules/nuclide-commons/__tests__/cache-test.js index ea59cac1..c9ff9791 100644 --- a/modules/nuclide-commons/__tests__/cache-test.js +++ b/modules/nuclide-commons/__tests__/cache-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _cache() { + const data = require("../cache"); + + _cache = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,17 +18,13 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {Cache} from '../cache'; - describe('Cache', () => { const key1 = 'key1'; const key2 = 'key2'; const value = 'value'; - it('creates values on demand', () => { let callCount = 0; const factory = jest.fn().mockImplementation(key => { @@ -24,8 +32,7 @@ describe('Cache', () => { expect(key).toEqual(key1); return value; }); - const cache: Cache = new Cache(factory); - + const cache = new (_cache().Cache)(factory); expect(factory).not.toHaveBeenCalled(); expect(cache.has(key1)).toEqual(false); expect(cache.get(key1)).toEqual(value); @@ -33,77 +40,58 @@ describe('Cache', () => { expect(cache.has(key1)).toEqual(true); expect(factory).toHaveBeenCalledWith(key1); expect(Array.from(cache.values())).toEqual([value]); - expect(cache.get(key1)).toEqual(value); expect(callCount).toEqual(1); }); - it('delete', () => { const factory = jest.fn().mockReturnValue(value); - const cache: Cache = new Cache(factory); - + const cache = new (_cache().Cache)(factory); expect(cache.delete(key1)).toEqual(false); cache.get(key1); expect(cache.has(key1)).toEqual(true); expect(cache.delete(key1)).toEqual(true); expect(cache.has(key1)).toEqual(false); }); - it('delete disposes values', () => { const factory = jest.fn().mockReturnValue(value); const dispose = jest.fn(); - const cache: Cache = new Cache(factory, dispose); - + const cache = new (_cache().Cache)(factory, dispose); cache.get(key1); cache.delete(key1); expect(dispose).toHaveBeenCalledWith(value); }); - it('clear disposes values', () => { const factory = jest.fn().mockReturnValue(value); const dispose = jest.fn(); - const cache: Cache = new Cache(factory, dispose); - + const cache = new (_cache().Cache)(factory, dispose); cache.get(key1); cache.clear(); expect(dispose).toHaveBeenCalledWith(value); }); - it('dispose disposes values', () => { const factory = jest.fn().mockReturnValue(value); const dispose = jest.fn(); - const cache: Cache = new Cache(factory, dispose); - + const cache = new (_cache().Cache)(factory, dispose); cache.get(key1); cache.dispose(); expect(dispose).toHaveBeenCalledWith(value); }); - it('observeValues sees existing and new values', async () => { const factory = jest.fn().mockImplementation(key => key); - const cache: Cache = new Cache(factory); - + const cache = new (_cache().Cache)(factory); cache.get(key1); - const values = cache - .observeValues() - .toArray() - .toPromise(); + const values = cache.observeValues().toArray().toPromise(); cache.get(key2); cache.dispose(); - expect(await values).toEqual([key1, key2]); + expect((await values)).toEqual([key1, key2]); }); - it('observeKeys sees existing and new keys', async () => { const factory = jest.fn().mockImplementation(key => value); - const cache: Cache = new Cache(factory); - + const cache = new (_cache().Cache)(factory); cache.get(key1); - const values = cache - .observeKeys() - .toArray() - .toPromise(); + const values = cache.observeKeys().toArray().toPromise(); cache.get(key2); cache.dispose(); - expect(await values).toEqual([key1, key2]); + expect((await values)).toEqual([key1, key2]); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/collection-test.js b/modules/nuclide-commons/__tests__/collection-test.js index c0315810..aa08f3e8 100644 --- a/modules/nuclide-commons/__tests__/collection-test.js +++ b/modules/nuclide-commons/__tests__/collection-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _collection() { + const data = require("../collection"); + + _collection = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,732 +18,574 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import { - ensureArray, - arrayRemove, - arrayEqual, - arrayCompact, - arrayFindLastIndex, - mapUnion, - insideOut, - isEmpty, - isIterable, - keyMirror, - setFilter, - setIntersect, - setUnion, - collect, - DefaultMap, - MultiMap, - objectEntries, - objectFromPairs, - objectFromMap, - objectMapValues, - objectValues, - concatIterators, - areSetsEqual, - someOfIterable, - findInIterable, - filterIterable, - mapEqual, - mapIterable, - mapGetWithDefault, - count, - range, - mapFromObject, - distinct, - takeIterable, -} from '../collection'; - describe('ensureArray', () => { it('works on arrays', () => { - expect(ensureArray([1])).toEqual([1]); - expect(ensureArray(['test'])).toEqual(['test']); + expect((0, _collection().ensureArray)([1])).toEqual([1]); + expect((0, _collection().ensureArray)(['test'])).toEqual(['test']); }); - it('works on non-arrays', () => { - expect(ensureArray(1)).toEqual([1]); - expect(ensureArray('test')).toEqual(['test']); + expect((0, _collection().ensureArray)(1)).toEqual([1]); + expect((0, _collection().ensureArray)('test')).toEqual(['test']); }); }); - describe('arrayRemove', () => { - let a: any; - let empty: any; - let single: any; - + let a; + let empty; + let single; beforeEach(() => { a = ['a', 'b', 'c']; empty = []; single = ['x']; }); - it('removes an element properly', () => { - arrayRemove(a, 'b'); + (0, _collection().arrayRemove)(a, 'b'); expect(a).toEqual(['a', 'c']); }); - it('removes the first element properly', () => { - arrayRemove(a, 'a'); + (0, _collection().arrayRemove)(a, 'a'); expect(a).toEqual(['b', 'c']); }); - it('removes the last element properly', () => { - arrayRemove(a, 'c'); + (0, _collection().arrayRemove)(a, 'c'); expect(a).toEqual(['a', 'b']); }); - it('does nothing if the element is not found', () => { - arrayRemove(a, 'd'); + (0, _collection().arrayRemove)(a, 'd'); expect(a).toEqual(['a', 'b', 'c']); }); - it('does nothing to an empty array', () => { - arrayRemove(empty, 'a'); + (0, _collection().arrayRemove)(empty, 'a'); expect(empty).toEqual([]); }); - it('works when there is a single element', () => { - arrayRemove(single, 'x'); + (0, _collection().arrayRemove)(single, 'x'); expect(single).toEqual([]); }); }); - describe('arrayEqual', () => { it('checks boolean elements', () => { - expect(arrayEqual([true, false, true], [true, false, true])).toBe(true); - expect(arrayEqual([true], [false])).toBe(false); + expect((0, _collection().arrayEqual)([true, false, true], [true, false, true])).toBe(true); + expect((0, _collection().arrayEqual)([true], [false])).toBe(false); }); - it('checks number elements', () => { - expect(arrayEqual([1, 2, 3], [1, 2, 3])).toBe(true); - expect(arrayEqual([1, 5, 3], [1, 2, 3])).toBe(false); + expect((0, _collection().arrayEqual)([1, 2, 3], [1, 2, 3])).toBe(true); + expect((0, _collection().arrayEqual)([1, 5, 3], [1, 2, 3])).toBe(false); }); - it('checks object elements', () => { - expect(arrayEqual([{}], [{}])).toBe(false); - expect( - arrayEqual([{x: 1}, {x: 2}], [{x: 1}, {x: 2}], (a, b) => a.x === b.x), - ).toBe(true); + expect((0, _collection().arrayEqual)([{}], [{}])).toBe(false); + expect((0, _collection().arrayEqual)([{ + x: 1 + }, { + x: 2 + }], [{ + x: 1 + }, { + x: 2 + }], (a, b) => a.x === b.x)).toBe(true); }); - it('works with arrays of different lengths', () => { - expect(arrayEqual([1, 2], [1, 2, 3])).toBe(false); - expect(arrayEqual([1, 2, 3], [1, 2])).toBe(false); + expect((0, _collection().arrayEqual)([1, 2], [1, 2, 3])).toBe(false); + expect((0, _collection().arrayEqual)([1, 2, 3], [1, 2])).toBe(false); }); - it("doesn't call the compare function if the same array is used", () => { const compare = jest.fn().mockReturnValue(true); const a = [1, 2, 3]; - arrayEqual(a, a, compare); + (0, _collection().arrayEqual)(a, a, compare); expect(compare).not.toHaveBeenCalled(); }); }); - describe('arrayCompact', () => { it('filters out null and undefined elements', () => { - expect(arrayCompact([0, false, '', [], null, undefined])).toEqual([ - 0, - false, - '', - [], - ]); + expect((0, _collection().arrayCompact)([0, false, '', [], null, undefined])).toEqual([0, false, '', []]); }); }); - describe('arrayFindLastIndex', () => { it('returns the last matching index', () => { - expect(arrayFindLastIndex([1, 1, 2], x => x === 1)).toBe(1); + expect((0, _collection().arrayFindLastIndex)([1, 1, 2], x => x === 1)).toBe(1); }); - it('returns -1 if no match is found', () => { - expect(arrayFindLastIndex([1, 1, 2], x => x === 0)).toBe(-1); + expect((0, _collection().arrayFindLastIndex)([1, 1, 2], x => x === 0)).toBe(-1); }); }); - describe('mapUnion', () => { it('merges two unique maps', () => { const map1 = new Map([['key1', 'value1'], ['key2', 'value2']]); const map2 = new Map([['key3', 'value3'], ['key4', 'value4']]); - const result = mapUnion(map1, map2); - + const result = (0, _collection().mapUnion)(map1, map2); expect(result.size).toBe(4); expect(result.get('key1')).toBe('value1'); expect(result.get('key2')).toBe('value2'); expect(result.get('key3')).toBe('value3'); expect(result.get('key4')).toBe('value4'); }); - it('overrodes with the values of the latest maps', () => { const map1 = new Map([['commonKey', 'value1'], ['key2', 'value2']]); const map2 = new Map([['commonKey', 'value3'], ['key4', 'value4']]); - const result = mapUnion(...[map1, map2]); - + const result = (0, _collection().mapUnion)(...[map1, map2]); expect(result.size).toBe(3); expect(result.get('commonKey')).toBe('value3'); expect(result.get('key2')).toBe('value2'); expect(result.get('key4')).toBe('value4'); }); }); - describe('isEmpty', () => { it('correctly identifies empty Objects', () => { - expect(isEmpty({})).toEqual(true); + expect((0, _collection().isEmpty)({})).toEqual(true); }); - it('correctly identifies non-empty Objects', () => { - const proto = {a: 1, b: 2, c: 3}; - const objWithOwnProperties = Object.create(proto, {foo: {value: 'bar'}}); + const proto = { + a: 1, + b: 2, + c: 3 + }; + const objWithOwnProperties = Object.create(proto, { + foo: { + value: 'bar' + } + }); const objWithoutOwnProperties = Object.create(proto); - - expect(isEmpty({a: 1})).toEqual(false); - expect(isEmpty(objWithOwnProperties)).toEqual(false); - expect(isEmpty(objWithoutOwnProperties)).toEqual(false); + expect((0, _collection().isEmpty)({ + a: 1 + })).toEqual(false); + expect((0, _collection().isEmpty)(objWithOwnProperties)).toEqual(false); + expect((0, _collection().isEmpty)(objWithoutOwnProperties)).toEqual(false); }); }); - describe('isIterable', () => { it('detects arrays are iterable', () => { - expect(isIterable(['foo', 'bar'])).toBe(true); + expect((0, _collection().isIterable)(['foo', 'bar'])).toBe(true); }); - it('detects strings are iterable', () => { - expect(isIterable('foo')).toBe(true); + expect((0, _collection().isIterable)('foo')).toBe(true); }); - it('detects Sets are iterable', () => { - expect(isIterable(new Set(['foo', 'bar']))).toBe(true); + expect((0, _collection().isIterable)(new Set(['foo', 'bar']))).toBe(true); }); - it('detects iterable objects are iterable', () => { const anIterable = { *[Symbol.iterator]() { yield 1; yield 42; - }, - }; + } - expect(isIterable(anIterable)).toBe(true); + }; + expect((0, _collection().isIterable)(anIterable)).toBe(true); }); - it('detects plain objects are not iterable', () => { - expect(isIterable({foo: 'bar', baz: 42})).toBe(false); + expect((0, _collection().isIterable)({ + foo: 'bar', + baz: 42 + })).toBe(false); }); - it('detects numbers are not iterable', () => { - expect(isIterable(42)).toBe(false); + expect((0, _collection().isIterable)(42)).toBe(false); }); }); - describe('keyMirror', () => { it('correctly mirrors objects', () => { - expect(keyMirror({a: null, b: null})).toEqual({a: 'a', b: 'b'}); + expect((0, _collection().keyMirror)({ + a: null, + b: null + })).toEqual({ + a: 'a', + b: 'b' + }); }); }); - describe('setFilter', () => { it('filters', () => { const set = new Set(['foo', 'bar', 'baz']); - const filtered = setFilter(set, x => x.startsWith('b')); - + const filtered = (0, _collection().setFilter)(set, x => x.startsWith('b')); expect(filtered.size).toBe(2); expect(filtered.has('bar')).toBe(true); expect(filtered.has('baz')).toBe(true); expect(filtered.has('foo')).toBe(false); }); }); - describe('setIntersect', () => { it('intersects', () => { const set1 = new Set(['foo', 'bar', 'baz']); const set2 = new Set(['fool', 'bar', 'bazl']); - const result = setIntersect(set1, set2); - + const result = (0, _collection().setIntersect)(set1, set2); expect(result.size).toBe(1); expect(result.has('bar')).toBe(true); }); }); - describe('setUnion', () => { it('unions', () => { const set1 = new Set(['foo', 'bar', 'baz']); const set2 = new Set(['fool', 'bar', 'bazl']); - const result = setUnion(set1, set2); - + const result = (0, _collection().setUnion)(set1, set2); const expected = new Set(['foo', 'bar', 'baz', 'fool', 'bazl']); - expect(areSetsEqual(result, expected)).toBe(true); + expect((0, _collection().areSetsEqual)(result, expected)).toBe(true); }); }); - describe('collect', () => { it('collects key-value pairs into a Map of arrays', () => { - const pairs = [ - ['neither', 1], - ['neither', 2], - ['fizz', 3], - ['neither', 4], - ['buzz', 5], - ['fizz', 6], - ['neither', 7], - ['neither', 8], - ['fizz', 9], - ]; - const result = collect(pairs); - + const pairs = [['neither', 1], ['neither', 2], ['fizz', 3], ['neither', 4], ['buzz', 5], ['fizz', 6], ['neither', 7], ['neither', 8], ['fizz', 9]]; + const result = (0, _collection().collect)(pairs); expect(result.size).toBe(3); expect(result.get('fizz')).toEqual([3, 6, 9]); expect(result.get('buzz')).toEqual([5]); expect(result.get('neither')).toEqual([1, 2, 4, 7, 8]); }); }); - describe('DefaultMap', () => { it('calls the factory each time you get a nonexistant key', () => { const spy = jest.fn().mockReturnValue('default'); - const map = new DefaultMap(spy); + const map = new (_collection().DefaultMap)(spy); expect(map.size).toBe(0); expect(map.get('a')).toBe('default'); expect(map.get('b')).toBe('default'); expect(map.size).toBe(2); expect(spy.mock.calls).toHaveLength(2); }); - it('can be cleared', () => { - const map = new DefaultMap(() => 'default'); + const map = new (_collection().DefaultMap)(() => 'default'); map.get('a'); map.get('b'); map.clear(); expect(map.size).toBe(0); }); - it('can update default values', () => { - const map = new DefaultMap(() => 'default'); + const map = new (_collection().DefaultMap)(() => 'default'); expect(map.get('a')).toBe('default'); map.set('a', 'custom'); expect(map.get('a')).toBe('custom'); }); - it('can be iterated', () => { - const map = new DefaultMap(() => 'default'); + const map = new (_collection().DefaultMap)(() => 'default'); map.get('a'); map.get('b'); expect([...map.keys()]).toEqual(['a', 'b']); expect([...map.entries()]).toEqual([['a', 'default'], ['b', 'default']]); }); - it('takes initial values', () => { - const map = new DefaultMap(() => 0, [['a', 1], ['b', 2]]); + const map = new (_collection().DefaultMap)(() => 0, [['a', 1], ['b', 2]]); map.get('c'); expect([...map.entries()]).toEqual([['a', 1], ['b', 2], ['c', 0]]); expect(map.size).toBe(3); }); }); - describe('MultiMap', () => { - let multimap: MultiMap = (null: any); - + let multimap = null; beforeEach(() => { - multimap = new MultiMap(); + multimap = new (_collection().MultiMap)(); }); - afterEach(() => { // check representation invariants let size = 0; + for (const [, set] of multimap._map) { expect(set.size).toBeGreaterThan(0); size += set.size; } + expect(multimap.size).toEqual(size); }); - it("returns an empty set when a binding doesn't exist", () => { expect(multimap.get(4)).toEqual(new Set()); }); - it('returns itself from add', () => { expect(multimap.add(1, 2)).toBe(multimap); }); - it('properly adds a single binding', () => { multimap.add(1, 2); expect(multimap.size).toEqual(1); expect(multimap.get(1)).toEqual(new Set([2])); }); - it('properly adds multiple bindings', () => { - multimap - .add(1, 2) - .add(1, 3) - .add(10, 11); + multimap.add(1, 2).add(1, 3).add(10, 11); expect(multimap.size).toEqual(3); expect(multimap.get(1)).toEqual(new Set([2, 3])); expect(multimap.get(10)).toEqual(new Set([11])); }); - it('returns false from delete when nothing was deleted', () => { multimap.add(1, 2); expect(multimap.delete(1, 3)).toBe(false); expect(multimap.delete(2, 3)).toBe(false); }); - it('properly deletes a single binding', () => { - multimap - .add(1, 2) - .add(1, 3) - .add(10, 11); + multimap.add(1, 2).add(1, 3).add(10, 11); expect(multimap.delete(1, 2)).toBe(true); expect(multimap.get(1)).toEqual(new Set([3])); expect(multimap.get(10)).toEqual(new Set([11])); expect(multimap.size).toEqual(2); }); - it('returns false from deleteAll when nothing was deleted', () => { expect(multimap.deleteAll(5)).toBe(false); }); - it('properly deletes all bindings for a given key', () => { multimap.add(1, 2).add(1, 3); expect(multimap.deleteAll(1)).toBe(true); expect(multimap.size).toEqual(0); expect(multimap.get(1)).toEqual(new Set()); }); - it('properly clears', () => { - multimap - .add(1, 2) - .add(1, 3) - .add(10, 11); + multimap.add(1, 2).add(1, 3).add(10, 11); multimap.clear(); expect(multimap.size).toEqual(0); expect(multimap.get(1)).toEqual(new Set()); expect(multimap.get(10)).toEqual(new Set()); }); - it('checks membership with has', () => { multimap.add(1, 2); expect(multimap.has(5, 6)).toBe(false); expect(multimap.has(1, 2)).toBe(true); expect(multimap.has(1, 3)).toBe(false); }); - it('checks membership with hasAny', () => { multimap.add(1, 2); expect(multimap.hasAny(1)).toBe(true); expect(multimap.hasAny(2)).toBe(false); }); }); - describe('objectValues', () => { it('returns the values of an object', () => { - expect( - objectValues({ - a: 1, - b: 2, - c: 4, - }), - ).toEqual([1, 2, 4]); + expect((0, _collection().objectValues)({ + a: 1, + b: 2, + c: 4 + })).toEqual([1, 2, 4]); }); - it('throws for null', () => { - expect(() => objectEntries(null)).toThrow(); + expect(() => (0, _collection().objectEntries)(null)).toThrow(); }); - it('throws for undefined', () => { - expect(() => objectEntries(undefined)).toThrow(); + expect(() => (0, _collection().objectEntries)(undefined)).toThrow(); }); }); - describe('objectEntries', () => { it('gets the entries of an object', () => { - expect(objectEntries({a: 1, b: 2})).toEqual([['a', 1], ['b', 2]]); + expect((0, _collection().objectEntries)({ + a: 1, + b: 2 + })).toEqual([['a', 1], ['b', 2]]); }); - it('errors for null', () => { - expect(() => objectEntries((null: any))).toThrow(); + expect(() => (0, _collection().objectEntries)(null)).toThrow(); }); - it('errors for undefined', () => { - expect(() => objectEntries((null: any))).toThrow(); + expect(() => (0, _collection().objectEntries)(null)).toThrow(); }); - it('only includes own properties', () => { - const a = {a: 1}; - const b = {b: 2}; + const a = { + a: 1 + }; + const b = { + b: 2 + }; Object.setPrototypeOf(b, a); - expect(objectEntries(b)).toEqual([['b', 2]]); + expect((0, _collection().objectEntries)(b)).toEqual([['b', 2]]); }); }); - describe('objectFromMap', () => { it('converts a map to an object', () => { - expect(objectFromMap(new Map([['a', 1], ['b', 2]]))).toEqual({a: 1, b: 2}); + expect((0, _collection().objectFromMap)(new Map([['a', 1], ['b', 2]]))).toEqual({ + a: 1, + b: 2 + }); }); }); - describe('concatIterators', () => { it('concatenates different iterable stuff to a single iterator', () => { - expect( - Array.from( - concatIterators( - new Set([1, 2, 3]), - [4, 5, 6], - new Set([7, 8, 9]).values(), - ), - ), - ).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); + expect(Array.from((0, _collection().concatIterators)(new Set([1, 2, 3]), [4, 5, 6], new Set([7, 8, 9]).values()))).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); }); - describe('areSetsEqual', () => { it('correctly compares empty sets', () => { - expect(areSetsEqual(new Set(), new Set())).toBe(true); + expect((0, _collection().areSetsEqual)(new Set(), new Set())).toBe(true); }); - it('correctly compares sets with the same properties', () => { - expect(areSetsEqual(new Set(['foo']), new Set(['foo']))).toBe(true); + expect((0, _collection().areSetsEqual)(new Set(['foo']), new Set(['foo']))).toBe(true); }); - it('returns false when properties are not equal', () => { - expect(areSetsEqual(new Set(['foo']), new Set(['bar']))).toBe(false); + expect((0, _collection().areSetsEqual)(new Set(['foo']), new Set(['bar']))).toBe(false); }); - it('returns false when an item exists in one set but not the other', () => { - expect(areSetsEqual(new Set(['foo']), new Set())).toBe(false); - expect(areSetsEqual(new Set(), new Set(['foo']))).toBe(false); + expect((0, _collection().areSetsEqual)(new Set(['foo']), new Set())).toBe(false); + expect((0, _collection().areSetsEqual)(new Set(), new Set(['foo']))).toBe(false); }); }); - describe('someOfIterable', () => { it('lazily returns whether any element of an iterable fulfills a given predicate', () => { - expect( - someOfIterable(new Set([1, 2, 3, 4, 5]), element => element % 2 === 0), - ).toEqual(true); - expect( - someOfIterable(new Set([1, 2, 3, 4, 5]), element => element % 5 === 0), - ).toEqual(true); - expect( - someOfIterable(new Set([1, 2, 3, 4, 5]), element => element % 6 === 0), - ).toEqual(false); - expect(someOfIterable([], element => true)).toEqual(false); + expect((0, _collection().someOfIterable)(new Set([1, 2, 3, 4, 5]), element => element % 2 === 0)).toEqual(true); + expect((0, _collection().someOfIterable)(new Set([1, 2, 3, 4, 5]), element => element % 5 === 0)).toEqual(true); + expect((0, _collection().someOfIterable)(new Set([1, 2, 3, 4, 5]), element => element % 6 === 0)).toEqual(false); + expect((0, _collection().someOfIterable)([], element => true)).toEqual(false); }); }); - describe('findInIterable', () => { it('return the first element of an iterable which fulfills a given predicate', () => { - expect( - findInIterable(new Set([1, 2, 3, 4, 5]), element => element % 2 === 0), - ).toEqual(2); - expect( - findInIterable(new Set([1, 2, 3, 4, 5]), element => element % 5 === 0), - ).toEqual(5); - expect( - findInIterable(new Set([1, 2, 3, 4, 5]), element => element % 6 === 0), - ).toEqual(null); - expect(findInIterable([], element => true)).toEqual(null); + expect((0, _collection().findInIterable)(new Set([1, 2, 3, 4, 5]), element => element % 2 === 0)).toEqual(2); + expect((0, _collection().findInIterable)(new Set([1, 2, 3, 4, 5]), element => element % 5 === 0)).toEqual(5); + expect((0, _collection().findInIterable)(new Set([1, 2, 3, 4, 5]), element => element % 6 === 0)).toEqual(null); + expect((0, _collection().findInIterable)([], element => true)).toEqual(null); }); }); - describe('filterIterable', () => { it('returns a (lazy) iterable containing all elements which fulfill the given predicate', () => { - expect( - Array.from( - filterIterable(new Set([1, 2, 3, 4, 5]), element => element % 2 === 0), - ), - ).toEqual([2, 4]); - expect( - Array.from(filterIterable(new Set([1, 2, 3, 4, 5]), element => true)), - ).toEqual([1, 2, 3, 4, 5]); - expect( - Array.from(filterIterable(new Set([1, 2, 3, 4, 5]), element => false)), - ).toEqual([]); - expect(Array.from(filterIterable([], element => true))).toEqual([]); + expect(Array.from((0, _collection().filterIterable)(new Set([1, 2, 3, 4, 5]), element => element % 2 === 0))).toEqual([2, 4]); + expect(Array.from((0, _collection().filterIterable)(new Set([1, 2, 3, 4, 5]), element => true))).toEqual([1, 2, 3, 4, 5]); + expect(Array.from((0, _collection().filterIterable)(new Set([1, 2, 3, 4, 5]), element => false))).toEqual([]); + expect(Array.from((0, _collection().filterIterable)([], element => true))).toEqual([]); }); }); - describe('takeIterable', () => { it('returns a (lazy) iterable containing the first n elements', () => { - expect( - Array.from(takeIterable(new Set([1, 2, 3, 4, 5]), 3)).length, - ).toEqual(3); - expect(Array.from(takeIterable([1, 2, 3], 0)).length).toEqual(0); - expect(Array.from(takeIterable([1, 2, 3], 1)).length).toEqual(1); - expect(Array.from(takeIterable([1, 2, 3], 2)).length).toEqual(2); - expect(Array.from(takeIterable([1, 2, 3], 3)).length).toEqual(3); - expect(Array.from(takeIterable([1, 2, 3], 4)).length).toEqual(3); + expect(Array.from((0, _collection().takeIterable)(new Set([1, 2, 3, 4, 5]), 3)).length).toEqual(3); + expect(Array.from((0, _collection().takeIterable)([1, 2, 3], 0)).length).toEqual(0); + expect(Array.from((0, _collection().takeIterable)([1, 2, 3], 1)).length).toEqual(1); + expect(Array.from((0, _collection().takeIterable)([1, 2, 3], 2)).length).toEqual(2); + expect(Array.from((0, _collection().takeIterable)([1, 2, 3], 3)).length).toEqual(3); + expect(Array.from((0, _collection().takeIterable)([1, 2, 3], 4)).length).toEqual(3); }); }); - describe('mapEqual', () => { it('checks primary elements', () => { - expect( - mapEqual( - new Map([[1, true], [2, false], [5, true]]), - new Map([[1, true], [2, false], [5, true]]), - ), - ).toBe(true); - expect(mapEqual(new Map([[1, true]]), new Map([[1, false]]))).toBe(false); - expect(mapEqual(new Map([[1, true]]), new Map([]))).toBe(false); - expect(mapEqual(new Map([[1, true]]), new Map([[2, false]]))).toBe(false); + expect((0, _collection().mapEqual)(new Map([[1, true], [2, false], [5, true]]), new Map([[1, true], [2, false], [5, true]]))).toBe(true); + expect((0, _collection().mapEqual)(new Map([[1, true]]), new Map([[1, false]]))).toBe(false); + expect((0, _collection().mapEqual)(new Map([[1, true]]), new Map([]))).toBe(false); + expect((0, _collection().mapEqual)(new Map([[1, true]]), new Map([[2, false]]))).toBe(false); }); - it('checks object value elements', () => { - expect(mapEqual(new Map([[1, {x: 1}]]), new Map([[1, {x: 1}]]))).toBe( - false, - ); - expect( - mapEqual( - new Map([[1, {x: 1}]]), - new Map([[1, {x: 1}]]), - (v1, v2) => v1.x === v2.x, - ), - ).toBe(true); + expect((0, _collection().mapEqual)(new Map([[1, { + x: 1 + }]]), new Map([[1, { + x: 1 + }]]))).toBe(false); + expect((0, _collection().mapEqual)(new Map([[1, { + x: 1 + }]]), new Map([[1, { + x: 1 + }]]), (v1, v2) => v1.x === v2.x)).toBe(true); }); }); - describe('mapIterable', () => { it('projects each element of an iterable into a new iterable', () => { - expect(Array.from(mapIterable(new Set(), element => true))).toEqual([]); - expect( - Array.from( - mapIterable(new Set([1, 2, 3, 4, 5]), element => element * element), - ), - ).toEqual([1, 4, 9, 16, 25]); + expect(Array.from((0, _collection().mapIterable)(new Set(), element => true))).toEqual([]); + expect(Array.from((0, _collection().mapIterable)(new Set([1, 2, 3, 4, 5]), element => element * element))).toEqual([1, 4, 9, 16, 25]); }); }); - describe('mapGetWithDefault', () => { it('normally returns whatever is in the map', () => { - expect(mapGetWithDefault(new Map([[1, 2]]), 1, 3)).toBe(2); + expect((0, _collection().mapGetWithDefault)(new Map([[1, 2]]), 1, 3)).toBe(2); }); - it('returns the default if the key is not in the map', () => { - expect(mapGetWithDefault(new Map([[1, 2]]), 5, 3)).toBe(3); + expect((0, _collection().mapGetWithDefault)(new Map([[1, 2]]), 5, 3)).toBe(3); }); - it('returns `null` or `undefined` if they are values in the map', () => { - expect(mapGetWithDefault(new Map([[1, null]]), 1, 3)).toBeNull(); - expect(mapGetWithDefault(new Map([[1, undefined]]), 1, 3)).toBeUndefined(); + expect((0, _collection().mapGetWithDefault)(new Map([[1, null]]), 1, 3)).toBeNull(); + expect((0, _collection().mapGetWithDefault)(new Map([[1, undefined]]), 1, 3)).toBeUndefined(); }); }); - describe('count', () => { it('returns how many values are in an iterable', () => { - expect(count([1, 2])).toBe(2); + expect((0, _collection().count)([1, 2])).toBe(2); }); }); - describe('insideOut', () => { it('traverses correctly', () => { - expect([...insideOut([])]).toEqual([]); - expect([...insideOut(['a'])]).toEqual([['a', 0]]); - expect([...insideOut(['a', 'b'])]).toEqual([['b', 1], ['a', 0]]); - expect([...insideOut(['a', 'b', 'c'])]).toEqual([ - ['b', 1], - ['a', 0], - ['c', 2], - ]); - expect([...insideOut(['a', 'b', 'c', 'd'])]).toEqual([ - ['c', 2], - ['b', 1], - ['d', 3], - ['a', 0], - ]); + expect([...(0, _collection().insideOut)([])]).toEqual([]); + expect([...(0, _collection().insideOut)(['a'])]).toEqual([['a', 0]]); + expect([...(0, _collection().insideOut)(['a', 'b'])]).toEqual([['b', 1], ['a', 0]]); + expect([...(0, _collection().insideOut)(['a', 'b', 'c'])]).toEqual([['b', 1], ['a', 0], ['c', 2]]); + expect([...(0, _collection().insideOut)(['a', 'b', 'c', 'd'])]).toEqual([['c', 2], ['b', 1], ['d', 3], ['a', 0]]); }); - it('traverses correctly with an index', () => { - expect([...insideOut([], 99)]).toEqual([]); - expect([...insideOut(['a'], 99)]).toEqual([['a', 0]]); - expect([...insideOut(['a', 'b'], 99)]).toEqual([['b', 1], ['a', 0]]); - expect([...insideOut(['a', 'b', 'c'], 99)]).toEqual([ - ['c', 2], - ['b', 1], - ['a', 0], - ]); - - expect([...insideOut([], -99)]).toEqual([]); - expect([...insideOut(['a'], -99)]).toEqual([['a', 0]]); - expect([...insideOut(['a', 'b'], -99)]).toEqual([['a', 0], ['b', 1]]); - expect([...insideOut(['a', 'b', 'c'], -99)]).toEqual([ - ['a', 0], - ['b', 1], - ['c', 2], - ]); - - expect([...insideOut(['a', 'b'], 1)]).toEqual([['b', 1], ['a', 0]]); - expect([...insideOut(['a', 'b'], 0)]).toEqual([['a', 0], ['b', 1]]); - - expect([...insideOut(['a', 'b', 'c'], 1)]).toEqual([ - ['b', 1], - ['a', 0], - ['c', 2], - ]); - expect([...insideOut(['a', 'b', 'c', 'd'], 1)]).toEqual([ - ['b', 1], - ['a', 0], - ['c', 2], - ['d', 3], - ]); - expect([...insideOut(['a', 'b', 'c', 'd'], 2)]).toEqual([ - ['c', 2], - ['b', 1], - ['d', 3], - ['a', 0], - ]); + expect([...(0, _collection().insideOut)([], 99)]).toEqual([]); + expect([...(0, _collection().insideOut)(['a'], 99)]).toEqual([['a', 0]]); + expect([...(0, _collection().insideOut)(['a', 'b'], 99)]).toEqual([['b', 1], ['a', 0]]); + expect([...(0, _collection().insideOut)(['a', 'b', 'c'], 99)]).toEqual([['c', 2], ['b', 1], ['a', 0]]); + expect([...(0, _collection().insideOut)([], -99)]).toEqual([]); + expect([...(0, _collection().insideOut)(['a'], -99)]).toEqual([['a', 0]]); + expect([...(0, _collection().insideOut)(['a', 'b'], -99)]).toEqual([['a', 0], ['b', 1]]); + expect([...(0, _collection().insideOut)(['a', 'b', 'c'], -99)]).toEqual([['a', 0], ['b', 1], ['c', 2]]); + expect([...(0, _collection().insideOut)(['a', 'b'], 1)]).toEqual([['b', 1], ['a', 0]]); + expect([...(0, _collection().insideOut)(['a', 'b'], 0)]).toEqual([['a', 0], ['b', 1]]); + expect([...(0, _collection().insideOut)(['a', 'b', 'c'], 1)]).toEqual([['b', 1], ['a', 0], ['c', 2]]); + expect([...(0, _collection().insideOut)(['a', 'b', 'c', 'd'], 1)]).toEqual([['b', 1], ['a', 0], ['c', 2], ['d', 3]]); + expect([...(0, _collection().insideOut)(['a', 'b', 'c', 'd'], 2)]).toEqual([['c', 2], ['b', 1], ['d', 3], ['a', 0]]); }); }); - describe('range', () => { it('includes the start value, but not the stop value', () => { - expect([...range(1, 4)]).toEqual([1, 2, 3]); + expect([...(0, _collection().range)(1, 4)]).toEqual([1, 2, 3]); }); - it('is empty if stop is less than, or equal to, start', () => { - expect([...range(2, 1)]).toEqual([]); - expect([...range(1, 1)]).toEqual([]); + expect([...(0, _collection().range)(2, 1)]).toEqual([]); + expect([...(0, _collection().range)(1, 1)]).toEqual([]); }); }); - describe('objectFromPairs', () => { it('creates an object with the keys and values of the iterable', () => { function* gen() { yield ['a', 1]; yield ['b', 2]; } - expect(objectFromPairs(gen())).toEqual({a: 1, b: 2}); + + expect((0, _collection().objectFromPairs)(gen())).toEqual({ + a: 1, + b: 2 + }); }); }); - describe('objectMapValues', () => { it('maps values', () => { - const mapped = objectMapValues({a: 1, b: 2}, v => v + 1); - expect(mapped).toEqual({a: 2, b: 3}); + const mapped = (0, _collection().objectMapValues)({ + a: 1, + b: 2 + }, v => v + 1); + expect(mapped).toEqual({ + a: 2, + b: 3 + }); }); - it('can project keys too', () => { - const mapped = objectMapValues({a: 1, b: 2}, (v, k) => k); - expect(mapped).toEqual({a: 'a', b: 'b'}); + const mapped = (0, _collection().objectMapValues)({ + a: 1, + b: 2 + }, (v, k) => k); + expect(mapped).toEqual({ + a: 'a', + b: 'b' + }); }); }); - describe('mapFromObject', () => { it('converts a object to an map', () => { - expect( - mapEqual(mapFromObject({a: 1, b: 2}), new Map([['a', 1], ['b', 2]])), - ).toEqual(true); + expect((0, _collection().mapEqual)((0, _collection().mapFromObject)({ + a: 1, + b: 2 + }), new Map([['a', 1], ['b', 2]]))).toEqual(true); }); }); - describe('distinct', () => { it('returns the unique values mapped (or keys)', () => { - expect(distinct([1, 2, 3])).toEqual([1, 2, 3]); - expect(distinct(['1', '2', '3'])).toEqual(['1', '2', '3']); - expect(distinct([1, 2, 3, 4], x => String(x % 2 === 0))).toEqual([1, 2]); - expect( - distinct([{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 4}], o => String(o.x)), - ).toEqual([{x: 1}, {x: 2}, {x: 3}, {x: 4}]); - }); -}); + expect((0, _collection().distinct)([1, 2, 3])).toEqual([1, 2, 3]); + expect((0, _collection().distinct)(['1', '2', '3'])).toEqual(['1', '2', '3']); + expect((0, _collection().distinct)([1, 2, 3, 4], x => String(x % 2 === 0))).toEqual([1, 2]); + expect((0, _collection().distinct)([{ + x: 1 + }, { + x: 2 + }, { + x: 3 + }, { + x: 4 + }, { + x: 4 + }], o => String(o.x))).toEqual([{ + x: 1 + }, { + x: 2 + }, { + x: 3 + }, { + x: 4 + }]); + }); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/debounce-test.js b/modules/nuclide-commons/__tests__/debounce-test.js index 1bf40574..ebe635a9 100644 --- a/modules/nuclide-commons/__tests__/debounce-test.js +++ b/modules/nuclide-commons/__tests__/debounce-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _debounce() { + const data = _interopRequireDefault(require("../debounce")); + + _debounce = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,64 +20,54 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import debounce from '../debounce'; - const sleep = n => new Promise(resolve => setTimeout(resolve, n)); describe('debounce()', () => { it('only calls function once after time advances', async () => { jest.useRealTimers(); - const timerCallback: any = jasmine.createSpy('timerCallback'); - const debouncedFunc = debounce(timerCallback, 10, false); - + const timerCallback = jasmine.createSpy('timerCallback'); + const debouncedFunc = (0, _debounce().default)(timerCallback, 10, false); debouncedFunc(); expect(timerCallback).not.toHaveBeenCalled(); - await sleep(50); expect(timerCallback).toHaveBeenCalled(); }); - it('disposes', () => { jest.useFakeTimers(); - const timerCallback: any = jasmine.createSpy('timerCallback'); - const debouncedFunc = debounce(timerCallback, 100, false); - + const timerCallback = jasmine.createSpy('timerCallback'); + const debouncedFunc = (0, _debounce().default)(timerCallback, 100, false); debouncedFunc(); expect(timerCallback).not.toHaveBeenCalled(); - debouncedFunc.dispose(); - jest.advanceTimersByTime(101); expect(timerCallback).not.toHaveBeenCalled(); }); - it('does not swallow flow types', () => { jest.useFakeTimers(); - const func = (a: string): number => 1; - const debounced = debounce(func, 0); - const ret = debounced('bar'); - // $FlowIgnore: func's first param should be a string. - debounced(1); + const func = a => 1; + + const debounced = (0, _debounce().default)(func, 0); + const ret = debounced('bar'); // $FlowIgnore: func's first param should be a string. + debounced(1); expect(() => { // $FlowIgnore: debounce's return type is "maybe func's return" type. - (ret: number); - // This is false because we haven't waited for the timer. - invariant(ret != null); - (ret: number); - }).toThrow(); + ret; // This is false because we haven't waited for the timer. - debounced.dispose(); + if (!(ret != null)) { + throw new Error("Invariant violation: \"ret != null\""); + } + ret; + }).toThrow(); + debounced.dispose(); expect(() => { // $FlowIgnore: debounced has no "bar" property. debounced.bar(); }).toThrow(); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/event-test.js b/modules/nuclide-commons/__tests__/event-test.js index 0b9c323f..0bea0232 100644 --- a/modules/nuclide-commons/__tests__/event-test.js +++ b/modules/nuclide-commons/__tests__/event-test.js @@ -1,3 +1,19 @@ +"use strict"; + +var _events = _interopRequireDefault(require("events")); + +function _event() { + const data = require("../event"); + + _event = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,21 +22,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import invariant from 'assert'; -import EventEmitter from 'events'; -import {attachEvent, observableFromSubscribeFunction} from '../event'; - describe('attachEvent', () => { describe('the returned disposable', () => { it("doesn't remove other listeners when disposed multiple times", () => { const foo = jasmine.createSpy('foo'); - const emitter = new EventEmitter(); - const d1 = attachEvent(emitter, 'event', foo); - attachEvent(emitter, 'event', foo); + const emitter = new _events.default(); + const d1 = (0, _event().attachEvent)(emitter, 'event', foo); + (0, _event().attachEvent)(emitter, 'event', foo); d1.dispose(); d1.dispose(); emitter.emit('event'); @@ -28,19 +39,18 @@ describe('attachEvent', () => { }); }); }); - describe('observableFromSubscribeFunction', () => { - let callback: ?(item: number) => mixed; - let disposable: ?IDisposable; - - // The subscribe function will put the given callback and the returned disposable in the variables + let callback; + let disposable; // The subscribe function will put the given callback and the returned disposable in the variables // above for inspection. + const subscribeFunction = fn => { callback = fn; disposable = { dispose() { callback = null; - }, + } + }; jest.spyOn(disposable, 'dispose'); return disposable; @@ -50,43 +60,40 @@ describe('observableFromSubscribeFunction', () => { callback = null; disposable = null; }); - it('should not call the subscription function until the Observable is subscribed to', () => { - const observable = observableFromSubscribeFunction(subscribeFunction); + const observable = (0, _event().observableFromSubscribeFunction)(subscribeFunction); expect(callback).toBeNull(); observable.subscribe(() => {}); expect(callback).not.toBeNull(); }); - it('should send events to the observable stream', async () => { - const result = observableFromSubscribeFunction(subscribeFunction) - .take(2) - .toArray() - .toPromise(); - invariant(callback != null); + const result = (0, _event().observableFromSubscribeFunction)(subscribeFunction).take(2).toArray().toPromise(); + + if (!(callback != null)) { + throw new Error("Invariant violation: \"callback != null\""); + } + callback(1); callback(2); - expect(await result).toEqual([1, 2]); + expect((await result)).toEqual([1, 2]); }); - it('should properly unsubscribe and resubscribe', () => { - const observable = observableFromSubscribeFunction(subscribeFunction); + const observable = (0, _event().observableFromSubscribeFunction)(subscribeFunction); let subscription = observable.subscribe(() => {}); expect(callback).not.toBeNull(); - invariant(disposable != null); + if (!(disposable != null)) { + throw new Error("Invariant violation: \"disposable != null\""); + } + expect(disposable.dispose).not.toHaveBeenCalled(); subscription.unsubscribe(); expect(disposable.dispose).toHaveBeenCalled(); - expect(callback).toBeNull(); - subscription = observable.subscribe(() => {}); - expect(callback).not.toBeNull(); - expect(disposable.dispose).not.toHaveBeenCalled(); subscription.unsubscribe(); expect(disposable.dispose).toHaveBeenCalled(); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/fsPromise-test.js b/modules/nuclide-commons/__tests__/fsPromise-test.js index 8e3c1ddb..5ee07c4a 100644 --- a/modules/nuclide-commons/__tests__/fsPromise-test.js +++ b/modules/nuclide-commons/__tests__/fsPromise-test.js @@ -1,3 +1,49 @@ +"use strict"; + +var _fs = _interopRequireDefault(require("fs")); + +function _temp() { + const data = _interopRequireDefault(require("temp")); + + _temp = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _fsPromise() { + const data = _interopRequireDefault(require("../fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _testHelpers() { + const data = require("../test-helpers"); + + _testHelpers = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,252 +52,205 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import fs from 'fs'; -import temp from 'temp'; -import nuclideUri from '../nuclideUri'; -import fsPromise from '../fsPromise'; -import {generateFixture} from '../test-helpers'; - -temp.track(); +_temp().default.track(); describe('fsPromise test suite', () => { describe('findNearestFile()', () => { - let dirPath: string = (null: any); - + let dirPath = null; beforeEach(async () => { - dirPath = await generateFixture( - 'nearest_test', - new Map([ - ['.some_file', 'just some file'], - ['nested_dir/.another_file', 'just another file'], - ]), - ); + dirPath = await (0, _testHelpers().generateFixture)('nearest_test', new Map([['.some_file', 'just some file'], ['nested_dir/.another_file', 'just another file']])); }); - it('find the file if given the exact directory', async () => { - const foundPath = await fsPromise.findNearestFile('.some_file', dirPath); + const foundPath = await _fsPromise().default.findNearestFile('.some_file', dirPath); expect(foundPath).toBe(dirPath); }); - it('find the file if given a nested directory', async () => { - const foundPath = await fsPromise.findNearestFile( - '.some_file', - nuclideUri.join(dirPath, 'nested_dir'), - ); + const foundPath = await _fsPromise().default.findNearestFile('.some_file', _nuclideUri().default.join(dirPath, 'nested_dir')); expect(foundPath).toBe(dirPath); }); - it('does not find the file if not existing', async () => { - const foundPath = await fsPromise.findNearestFile( - 'non-existent.txt', - nuclideUri.join(dirPath, 'nested_dir'), - ); + const foundPath = await _fsPromise().default.findNearestFile('non-existent.txt', _nuclideUri().default.join(dirPath, 'nested_dir')); expect(foundPath).toBe(null); }); }); describe('mv', () => { let dirPath; beforeEach(async () => { - dirPath = await generateFixture( - 'move', - new Map([['foo/bar', 'bar content'], ['baz/biz', 'bar content']]), - ); + dirPath = await (0, _testHelpers().generateFixture)('move', new Map([['foo/bar', 'bar content'], ['baz/biz', 'bar content']])); }); test('Dest dir exists and clobber is false', async () => { - const source = nuclideUri.join(dirPath, 'foo'); - const dest = nuclideUri.join(dirPath, 'baz'); + const source = _nuclideUri().default.join(dirPath, 'foo'); + + const dest = _nuclideUri().default.join(dirPath, 'baz'); - await expect( - fsPromise.mv(source, dest, {mkdirp: true, clobber: false}), - ).rejects.toMatchObject({ + await expect(_fsPromise().default.mv(source, dest, { + mkdirp: true, + clobber: false + })).rejects.toMatchObject({ message: 'Destination file exists', path: dest, - code: 'EEXIST', + code: 'EEXIST' }); }); test('Dest dir exists and clobber is true', async () => { - const source = nuclideUri.join(dirPath, 'foo'); - const dest = nuclideUri.join(dirPath, 'baz'); + const source = _nuclideUri().default.join(dirPath, 'foo'); - expect( - fsPromise.mv(source, dest, {mkdirp: true, clobber: true}), - ).rejects.toMatchObject({ - message: expect.stringMatching(/directory not empty/), + const dest = _nuclideUri().default.join(dirPath, 'baz'); + + expect(_fsPromise().default.mv(source, dest, { + mkdirp: true, + clobber: true + })).rejects.toMatchObject({ + message: expect.stringMatching(/directory not empty/) }); }); - test('Dest dir is the same as source and clobber is true', async () => { - const source = nuclideUri.join(dirPath, 'foo'); - const dest = nuclideUri.join(dirPath, 'foo'); + const source = _nuclideUri().default.join(dirPath, 'foo'); - await fsPromise.mv(source, dest, {mkdirp: true, clobber: true}); - expect(fs.existsSync(nuclideUri.join(dirPath, 'foo/bar'))).toBe(true); + const dest = _nuclideUri().default.join(dirPath, 'foo'); + + await _fsPromise().default.mv(source, dest, { + mkdirp: true, + clobber: true + }); + expect(_fs.default.existsSync(_nuclideUri().default.join(dirPath, 'foo/bar'))).toBe(true); }); test('Dest dir is the same as source and clobber is false', async () => { - const source = nuclideUri.join(dirPath, 'foo'); - const dest = nuclideUri.join(dirPath, 'foo'); + const source = _nuclideUri().default.join(dirPath, 'foo'); - await expect( - fsPromise.mv(source, dest, {mkdirp: true, clobber: false}), - ).rejects.toMatchObject({ + const dest = _nuclideUri().default.join(dirPath, 'foo'); + + await expect(_fsPromise().default.mv(source, dest, { + mkdirp: true, + clobber: false + })).rejects.toMatchObject({ message: 'Destination file exists', path: dest, - code: 'EEXIST', + code: 'EEXIST' }); }); if (process.platform === 'darwin' || process.platform === 'win32') { test('Dest dir is case insenstively the same as source and clobber is true', async () => { - const source = nuclideUri.join(dirPath, 'foo'); - const dest = nuclideUri.join(dirPath, 'Foo'); + const source = _nuclideUri().default.join(dirPath, 'foo'); + + const dest = _nuclideUri().default.join(dirPath, 'Foo'); - await fsPromise.mv(source, dest, {mkdirp: true, clobber: true}); - expect(fs.existsSync(nuclideUri.join(dirPath, 'foo/bar'))).toBe(true); - expect(fs.existsSync(nuclideUri.join(dirPath, 'Foo/bar'))).toBe(true); + await _fsPromise().default.mv(source, dest, { + mkdirp: true, + clobber: true + }); + expect(_fs.default.existsSync(_nuclideUri().default.join(dirPath, 'foo/bar'))).toBe(true); + expect(_fs.default.existsSync(_nuclideUri().default.join(dirPath, 'Foo/bar'))).toBe(true); }); test('Dest dir is case insenstively the same as source and clobber is false', async () => { - const source = nuclideUri.join(dirPath, 'foo'); - const dest = nuclideUri.join(dirPath, 'Foo'); + const source = _nuclideUri().default.join(dirPath, 'foo'); + + const dest = _nuclideUri().default.join(dirPath, 'Foo'); - await expect( - fsPromise.mv(source, dest, {mkdirp: true, clobber: false}), - ).rejects.toMatchObject({ + await expect(_fsPromise().default.mv(source, dest, { + mkdirp: true, + clobber: false + })).rejects.toMatchObject({ message: 'Destination file exists', path: dest, - code: 'EEXIST', + code: 'EEXIST' }); }); } }); - describe('findFurthestFile()', () => { - let dirPath: string = (null: any); - + let dirPath = null; beforeEach(async () => { - dirPath = await generateFixture( - 'furthest_test', - new Map([ - ['0/.some_file', 'just a file'], - ['0/1/.some_file', 'just b file'], - // Skip one file to test consecutive vs non-consecutive. - // ['0/1/2', 'just c file'], - ['0/1/2/3/.some_file', 'just d file'], - ['0/1/2/3/4/.some_file', 'just f file'], - ]), - ); + dirPath = await (0, _testHelpers().generateFixture)('furthest_test', new Map([['0/.some_file', 'just a file'], ['0/1/.some_file', 'just b file'], // Skip one file to test consecutive vs non-consecutive. + // ['0/1/2', 'just c file'], + ['0/1/2/3/.some_file', 'just d file'], ['0/1/2/3/4/.some_file', 'just f file']])); }); - it('find the file if given the exact directory', async () => { - const expectedPath = nuclideUri.join(dirPath, '0'); - const foundPath = await fsPromise.findFurthestFile( - '.some_file', - expectedPath, - ); + const expectedPath = _nuclideUri().default.join(dirPath, '0'); + + const foundPath = await _fsPromise().default.findFurthestFile('.some_file', expectedPath); expect(foundPath).toBe(expectedPath); }); - it('finds the furthest file if given a nested directory', async () => { - const expectedPath = nuclideUri.join(dirPath, '0'); - const startPath = nuclideUri.join(dirPath, '0/1/2/3/4'); - const foundPath = await fsPromise.findFurthestFile( - '.some_file', - startPath, - ); + const expectedPath = _nuclideUri().default.join(dirPath, '0'); + + const startPath = _nuclideUri().default.join(dirPath, '0/1/2/3/4'); + + const foundPath = await _fsPromise().default.findFurthestFile('.some_file', startPath); expect(foundPath).toBe(expectedPath); }); - it('terminates search as soon as file is not found if given the stopOnMissing flag', async () => { - const expectedPath = nuclideUri.join(dirPath, '0/1/2/3'); - const startPath = nuclideUri.join(dirPath, '0/1/2/3/4'); - const foundPath = await fsPromise.findFurthestFile( - '.some_file', - startPath, - true /* stopOnMissing */, + const expectedPath = _nuclideUri().default.join(dirPath, '0/1/2/3'); + + const startPath = _nuclideUri().default.join(dirPath, '0/1/2/3/4'); + + const foundPath = await _fsPromise().default.findFurthestFile('.some_file', startPath, true + /* stopOnMissing */ ); expect(foundPath).toBe(expectedPath); }); - it('does not find the file if not existing', async () => { - const startPath = nuclideUri.join(dirPath, '0/1/2/3/4'); - const foundPath = await fsPromise.findFurthestFile( - 'non-existent.txt', - startPath, - ); + const startPath = _nuclideUri().default.join(dirPath, '0/1/2/3/4'); + + const foundPath = await _fsPromise().default.findFurthestFile('non-existent.txt', startPath); expect(foundPath).toBe(null); }); }); - describe('getCommonAncestorDirectory', () => { it('gets the parent directory', () => { - expect( - fsPromise.getCommonAncestorDirectory([ - '/foo/bar.txt', - '/foo/baz/lol.txt', - ]), - ).toBe('/foo'); - expect( - fsPromise.getCommonAncestorDirectory([ - '/foo/bar/abc/def/abc.txt', - '/foo/bar/lol.txt', - ]), - ).toBe('/foo/bar'); + expect(_fsPromise().default.getCommonAncestorDirectory(['/foo/bar.txt', '/foo/baz/lol.txt'])).toBe('/foo'); + expect(_fsPromise().default.getCommonAncestorDirectory(['/foo/bar/abc/def/abc.txt', '/foo/bar/lol.txt'])).toBe('/foo/bar'); }); }); - describe('writeFileAtomic', () => { - let pathToWriteFile: string; + let pathToWriteFile; beforeEach(() => { - const tempDir = temp.mkdirSync(); - pathToWriteFile = nuclideUri.join(tempDir, 'test'); - }); + const tempDir = _temp().default.mkdirSync(); + pathToWriteFile = _nuclideUri().default.join(tempDir, 'test'); + }); it('can write to a file', async () => { - await fsPromise.writeFileAtomic( - pathToWriteFile, - "I'm a little teapot.\n", - ); - expect(fs.readFileSync(pathToWriteFile).toString()).toEqual( - "I'm a little teapot.\n", - ); - // eslint-disable-next-line no-bitwise - expect(fs.statSync(pathToWriteFile).mode & 0o777).toEqual( - 0o666 & ~process.umask(), // eslint-disable-line no-bitwise + await _fsPromise().default.writeFileAtomic(pathToWriteFile, "I'm a little teapot.\n"); + expect(_fs.default.readFileSync(pathToWriteFile).toString()).toEqual("I'm a little teapot.\n"); // eslint-disable-next-line no-bitwise + + expect(_fs.default.statSync(pathToWriteFile).mode & 0o777).toEqual(0o666 & ~process.umask() // eslint-disable-line no-bitwise ); }); - it('calls mkdirp', async () => { - const subPath = nuclideUri.join(pathToWriteFile, 'test'); - await fsPromise.writeFileAtomic(subPath, 'test1234\n'); - expect(fs.readFileSync(subPath).toString()).toEqual('test1234\n'); - }); + const subPath = _nuclideUri().default.join(pathToWriteFile, 'test'); + await _fsPromise().default.writeFileAtomic(subPath, 'test1234\n'); + expect(_fs.default.readFileSync(subPath).toString()).toEqual('test1234\n'); + }); it('preserves permissions on files', async () => { - fs.writeFileSync(pathToWriteFile, 'test'); - fs.chmodSync(pathToWriteFile, 0o700); + _fs.default.writeFileSync(pathToWriteFile, 'test'); + + _fs.default.chmodSync(pathToWriteFile, 0o700); + + await _fsPromise().default.writeFileAtomic(pathToWriteFile, 'test2'); + expect(_fs.default.readFileSync(pathToWriteFile).toString()).toEqual('test2'); + + const stat = _fs.default.statSync(pathToWriteFile); // eslint-disable-next-line no-bitwise + - await fsPromise.writeFileAtomic(pathToWriteFile, 'test2'); - expect(fs.readFileSync(pathToWriteFile).toString()).toEqual('test2'); - const stat = fs.statSync(pathToWriteFile); - // eslint-disable-next-line no-bitwise expect(stat.mode & 0o777).toEqual(0o700); }); - it('errors if file cannot be written', async () => { let err; + try { - await fsPromise.writeFileAtomic( - pathToWriteFile + '/that/is/missing/', - 'something', - ); + await _fsPromise().default.writeFileAtomic(pathToWriteFile + '/that/is/missing/', 'something'); } catch (e) { err = e; } - invariant(err != null, 'Expected an error'); + + if (!(err != null)) { + throw new Error('Expected an error'); + } }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/humanizeKeystroke-test.js b/modules/nuclide-commons/__tests__/humanizeKeystroke-test.js index bfcac4a4..9ed9acec 100644 --- a/modules/nuclide-commons/__tests__/humanizeKeystroke-test.js +++ b/modules/nuclide-commons/__tests__/humanizeKeystroke-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _humanizeKeystroke() { + const data = _interopRequireDefault(require("../humanizeKeystroke")); + + _humanizeKeystroke = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,91 +20,58 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import humanizeKeystroke from '../humanizeKeystroke'; - describe('nuclide-keystroke-label', () => { // adapted from https://github.com/atom/underscore-plus/blob/master/spec/underscore-plus-spec.coffee describe('humanizeKeystroke', () => { it('replaces single keystroke', () => { - expect(humanizeKeystroke('cmd-O', 'darwin')).toEqual('⌘⇧O'); - expect(humanizeKeystroke('cmd-O', 'linux')).toEqual('Cmd+Shift+O'); - - expect(humanizeKeystroke('cmd-shift-up', 'darwin')).toEqual('⌘⇧↑'); - expect(humanizeKeystroke('cmd-shift-up', 'linux')).toEqual( - 'Cmd+Shift+Up', - ); - - expect(humanizeKeystroke('cmd-option-down', 'darwin')).toEqual('⌘⌥↓'); - expect(humanizeKeystroke('cmd-option-down', 'linux')).toEqual( - 'Cmd+Alt+Down', - ); - - expect(humanizeKeystroke('cmd-option-left', 'darwin')).toEqual('⌘⌥←'); - expect(humanizeKeystroke('cmd-option-left', 'linux')).toEqual( - 'Cmd+Alt+Left', - ); - - expect(humanizeKeystroke('cmd-option-right', 'darwin')).toEqual('⌘⌥→'); - expect(humanizeKeystroke('cmd-option-right', 'linux')).toEqual( - 'Cmd+Alt+Right', - ); - - expect(humanizeKeystroke('cmd-o', 'darwin')).toEqual('⌘O'); - expect(humanizeKeystroke('cmd-o', 'linux')).toEqual('Cmd+O'); - - expect(humanizeKeystroke('ctrl-2', 'darwin')).toEqual('⌃2'); - expect(humanizeKeystroke('ctrl-2', 'linux')).toEqual('Ctrl+2'); - - expect(humanizeKeystroke('cmd-space', 'darwin')).toEqual('⌘space'); - expect(humanizeKeystroke('cmd-space', 'linux')).toEqual('Cmd+Space'); - - expect(humanizeKeystroke('cmd-|', 'darwin')).toEqual('⌘⇧\\'); - expect(humanizeKeystroke('cmd-|', 'linux')).toEqual('Cmd+Shift+\\'); - - expect(humanizeKeystroke('cmd-}', 'darwin')).toEqual('⌘⇧]'); - expect(humanizeKeystroke('cmd-}', 'linux')).toEqual('Cmd+Shift+]'); - - expect(humanizeKeystroke('cmd--', 'darwin')).toEqual('⌘-'); - expect(humanizeKeystroke('cmd--', 'linux')).toEqual('Cmd+-'); + expect((0, _humanizeKeystroke().default)('cmd-O', 'darwin')).toEqual('⌘⇧O'); + expect((0, _humanizeKeystroke().default)('cmd-O', 'linux')).toEqual('Cmd+Shift+O'); + expect((0, _humanizeKeystroke().default)('cmd-shift-up', 'darwin')).toEqual('⌘⇧↑'); + expect((0, _humanizeKeystroke().default)('cmd-shift-up', 'linux')).toEqual('Cmd+Shift+Up'); + expect((0, _humanizeKeystroke().default)('cmd-option-down', 'darwin')).toEqual('⌘⌥↓'); + expect((0, _humanizeKeystroke().default)('cmd-option-down', 'linux')).toEqual('Cmd+Alt+Down'); + expect((0, _humanizeKeystroke().default)('cmd-option-left', 'darwin')).toEqual('⌘⌥←'); + expect((0, _humanizeKeystroke().default)('cmd-option-left', 'linux')).toEqual('Cmd+Alt+Left'); + expect((0, _humanizeKeystroke().default)('cmd-option-right', 'darwin')).toEqual('⌘⌥→'); + expect((0, _humanizeKeystroke().default)('cmd-option-right', 'linux')).toEqual('Cmd+Alt+Right'); + expect((0, _humanizeKeystroke().default)('cmd-o', 'darwin')).toEqual('⌘O'); + expect((0, _humanizeKeystroke().default)('cmd-o', 'linux')).toEqual('Cmd+O'); + expect((0, _humanizeKeystroke().default)('ctrl-2', 'darwin')).toEqual('⌃2'); + expect((0, _humanizeKeystroke().default)('ctrl-2', 'linux')).toEqual('Ctrl+2'); + expect((0, _humanizeKeystroke().default)('cmd-space', 'darwin')).toEqual('⌘space'); + expect((0, _humanizeKeystroke().default)('cmd-space', 'linux')).toEqual('Cmd+Space'); + expect((0, _humanizeKeystroke().default)('cmd-|', 'darwin')).toEqual('⌘⇧\\'); + expect((0, _humanizeKeystroke().default)('cmd-|', 'linux')).toEqual('Cmd+Shift+\\'); + expect((0, _humanizeKeystroke().default)('cmd-}', 'darwin')).toEqual('⌘⇧]'); + expect((0, _humanizeKeystroke().default)('cmd-}', 'linux')).toEqual('Cmd+Shift+]'); + expect((0, _humanizeKeystroke().default)('cmd--', 'darwin')).toEqual('⌘-'); + expect((0, _humanizeKeystroke().default)('cmd--', 'linux')).toEqual('Cmd+-'); }); - it('correctly replaces keystrokes with shift and capital letter', () => { - expect(humanizeKeystroke('cmd-shift-P', 'darwin')).toEqual('⌘⇧P'); - expect(humanizeKeystroke('cmd-shift-P', 'linux')).toEqual('Cmd+Shift+P'); + expect((0, _humanizeKeystroke().default)('cmd-shift-P', 'darwin')).toEqual('⌘⇧P'); + expect((0, _humanizeKeystroke().default)('cmd-shift-P', 'linux')).toEqual('Cmd+Shift+P'); }); - it('replaces multiple keystrokes', () => { - expect(humanizeKeystroke('cmd-O cmd-n', 'darwin')).toEqual('⌘⇧O ⌘N'); - expect(humanizeKeystroke('cmd-O cmd-n', 'linux')).toEqual( - 'Cmd+Shift+O Cmd+N', - ); - - expect(humanizeKeystroke('cmd-shift-- cmd-n', 'darwin')).toEqual( - '⌘⇧- ⌘N', - ); - expect(humanizeKeystroke('cmd-shift-- cmd-n', 'linux')).toEqual( - 'Cmd+Shift+- Cmd+N', - ); - - expect(humanizeKeystroke('cmd-k right', 'darwin')).toEqual('⌘K →'); - expect(humanizeKeystroke('cmd-k right', 'linux')).toEqual('Cmd+K Right'); + expect((0, _humanizeKeystroke().default)('cmd-O cmd-n', 'darwin')).toEqual('⌘⇧O ⌘N'); + expect((0, _humanizeKeystroke().default)('cmd-O cmd-n', 'linux')).toEqual('Cmd+Shift+O Cmd+N'); + expect((0, _humanizeKeystroke().default)('cmd-shift-- cmd-n', 'darwin')).toEqual('⌘⇧- ⌘N'); + expect((0, _humanizeKeystroke().default)('cmd-shift-- cmd-n', 'linux')).toEqual('Cmd+Shift+- Cmd+N'); + expect((0, _humanizeKeystroke().default)('cmd-k right', 'darwin')).toEqual('⌘K →'); + expect((0, _humanizeKeystroke().default)('cmd-k right', 'linux')).toEqual('Cmd+K Right'); }); - it('formats function keys', () => { - expect(humanizeKeystroke('cmd-f2', 'darwin')).toEqual('⌘F2'); - expect(humanizeKeystroke('cmd-f2', 'linux')).toEqual('Cmd+F2'); + expect((0, _humanizeKeystroke().default)('cmd-f2', 'darwin')).toEqual('⌘F2'); + expect((0, _humanizeKeystroke().default)('cmd-f2', 'linux')).toEqual('Cmd+F2'); }); - it('handles junk input', () => { // $FlowFixMe: Deliberately testing invalid input. - expect(humanizeKeystroke()).toEqual(undefined); - // $FlowFixMe: Deliberately testing invalid input. - expect(humanizeKeystroke(null)).toEqual(null); - expect(humanizeKeystroke('')).toEqual(''); + expect((0, _humanizeKeystroke().default)()).toEqual(undefined); // $FlowFixMe: Deliberately testing invalid input. + + expect((0, _humanizeKeystroke().default)(null)).toEqual(null); + expect((0, _humanizeKeystroke().default)('')).toEqual(''); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/matchIndexesToRanges-test.js b/modules/nuclide-commons/__tests__/matchIndexesToRanges-test.js index 30e3e2ac..de3a693b 100644 --- a/modules/nuclide-commons/__tests__/matchIndexesToRanges-test.js +++ b/modules/nuclide-commons/__tests__/matchIndexesToRanges-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _matchIndexesToRanges() { + const data = _interopRequireDefault(require("../matchIndexesToRanges")); + + _matchIndexesToRanges = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,26 +20,14 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import matchIndexesToRanges from '../matchIndexesToRanges'; - describe('matchIndexesToRanges', () => { it('makes single character ranges for nonconsecutive values at consecutive indexes', () => { - expect(matchIndexesToRanges([1, 3, 5, 7, 9])).toEqual([ - [1, 2], - [3, 4], - [5, 6], - [7, 8], - [9, 10], - ]); + expect((0, _matchIndexesToRanges().default)([1, 3, 5, 7, 9])).toEqual([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]); }); - it('collapses consecutive values into ranges', () => { - expect( - matchIndexesToRanges([0, 1, 2, 3, 10, 11, 12, 20, 21, 22, 23, 24, 25]), - ).toEqual([[0, 4], [10, 13], [20, 26]]); + expect((0, _matchIndexesToRanges().default)([0, 1, 2, 3, 10, 11, 12, 20, 21, 22, 23, 24, 25])).toEqual([[0, 4], [10, 13], [20, 26]]); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/memoizeUntilChanged-test.js b/modules/nuclide-commons/__tests__/memoizeUntilChanged-test.js index 56af101e..1249b70d 100644 --- a/modules/nuclide-commons/__tests__/memoizeUntilChanged-test.js +++ b/modules/nuclide-commons/__tests__/memoizeUntilChanged-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _memoizeUntilChanged() { + const data = _interopRequireDefault(require("../memoizeUntilChanged")); + + _memoizeUntilChanged = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,62 +20,51 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import memoizeUntilChanged from '../memoizeUntilChanged'; - const sum = (a, b) => a + b; describe('memoizeUntilChanged', () => { it('memoizes', () => { const spy = jest.fn().mockImplementation(sum); - const f = memoizeUntilChanged(spy); + const f = (0, _memoizeUntilChanged().default)(spy); f(1, 2); const result = f(1, 2); expect(result).toBe(3); expect(spy.mock.calls.length).toBe(1); }); - it('resets when args change', () => { const spy = jest.fn().mockImplementation(sum); - const f = memoizeUntilChanged(spy); + const f = (0, _memoizeUntilChanged().default)(spy); f(1, 2); const result = f(1, 3); expect(result).toBe(4); expect(spy.mock.calls.length).toBe(2); }); - it('preserves context', () => { let that; const obj = {}; - const f = memoizeUntilChanged(function f() { + const f = (0, _memoizeUntilChanged().default)(function f() { that = this; }); f.call(obj); expect(that).toBe(obj); }); - it('uses all args when memoizing by default', () => { const spy = jest.fn().mockImplementation(sum); - const f = memoizeUntilChanged(spy); + const f = (0, _memoizeUntilChanged().default)(spy); f(1, 2); const result = f(1, 3); expect(result).toBe(4); expect(spy.mock.calls.length).toBe(2); }); - it('uses the key selector and comparator', () => { const spy = jest.fn().mockImplementation(sum); - const f = memoizeUntilChanged( - spy, - // A pretty poor keyselector that uses the sum of the arguments as the key. Lots of collisions - // here! - (x, y) => x + y, - // Compare numbers. - (a, b) => a === b, - ); + const f = (0, _memoizeUntilChanged().default)(spy, // A pretty poor keyselector that uses the sum of the arguments as the key. Lots of collisions + // here! + (x, y) => x + y, // Compare numbers. + (a, b) => a === b); f(1, 2); f(2, 1); f(0, 3); @@ -69,4 +72,4 @@ describe('memoizeUntilChanged', () => { f(0, 4); expect(spy.mock.calls.length).toBe(2); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/nice-test.js b/modules/nuclide-commons/__tests__/nice-test.js index 48395426..80a01de1 100644 --- a/modules/nuclide-commons/__tests__/nice-test.js +++ b/modules/nuclide-commons/__tests__/nice-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _testHelpers() { + const data = require("../test-helpers"); + + _testHelpers = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,59 +20,40 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import typeof {niceSafeSpawn as niceSafeSpawnType} from '../nice'; - -import {uncachedRequire} from '../test-helpers'; -import {Observable} from 'rxjs'; - describe('nice', () => { - let niceSafeSpawn: niceSafeSpawnType = (null: any); - - let whichSpy = (null: any); - let spawnSpy = (null: any); - let shouldFindNiceCommand: boolean = (null: any); - let shouldFindIoniceCommand: boolean = (null: any); - // All we need here is a unique value to make sure that `nice` returns whatever `safeSpawn` + let niceSafeSpawn = null; + let whichSpy = null; + let spawnSpy = null; + let shouldFindNiceCommand = null; + let shouldFindIoniceCommand = null; // All we need here is a unique value to make sure that `nice` returns whatever `safeSpawn` // returns - const fakeSafeSpawnReturn: child_process$ChildProcess = ({}: any); + const fakeSafeSpawnReturn = {}; beforeEach(() => { jest.resetModules(); shouldFindNiceCommand = true; shouldFindIoniceCommand = true; - whichSpy = jest - .spyOn(require('../which'), 'default') - .mockImplementation(async command => { - if ( - (shouldFindNiceCommand && command === 'nice') || - (shouldFindIoniceCommand && command === 'ionice') - ) { - return command; - } else { - return null; - } - }); - spawnSpy = jest - .spyOn(require('../process'), 'spawn') - .mockReturnValue(Observable.of(fakeSafeSpawnReturn)); - ({niceSafeSpawn} = (uncachedRequire(require, '../nice'): any)); + whichSpy = jest.spyOn(require("../which"), 'default').mockImplementation(async command => { + if (shouldFindNiceCommand && command === 'nice' || shouldFindIoniceCommand && command === 'ionice') { + return command; + } else { + return null; + } + }); + spawnSpy = jest.spyOn(require("../process"), 'spawn').mockReturnValue(_RxMin.Observable.of(fakeSafeSpawnReturn)); + ({ + niceSafeSpawn + } = (0, _testHelpers().uncachedRequire)(require, '../nice')); }); - it('should spawn `nice` and return whatever spawn returns', async () => { const execOptions = {}; const result = await niceSafeSpawn('echo', ['hi'], execOptions); - expect(spawnSpy).toHaveBeenCalledWith( - 'ionice', - ['-n', '7', 'nice', 'echo', 'hi'], - execOptions, - ); + expect(spawnSpy).toHaveBeenCalledWith('ionice', ['-n', '7', 'nice', 'echo', 'hi'], execOptions); expect(result).toBe(fakeSafeSpawnReturn); }); - it('should spawn the command normally if nice and ionice cannot be found', async () => { shouldFindNiceCommand = false; shouldFindIoniceCommand = false; @@ -67,7 +62,6 @@ describe('nice', () => { expect(spawnSpy).toHaveBeenCalledWith('echo', ['hi'], execOptions); expect(result).toBe(fakeSafeSpawnReturn); }); - it('should spawn with only nice if ionice cannot be found', async () => { shouldFindIoniceCommand = false; const execOptions = {}; @@ -75,20 +69,14 @@ describe('nice', () => { expect(spawnSpy).toHaveBeenCalledWith('nice', ['echo', 'hi'], execOptions); expect(result).toBe(fakeSafeSpawnReturn); }); - it('should spawn with only ionice if nice cannot be found', async () => { // I don't know when we would have ionice but not nice, but we may as well support this case. shouldFindNiceCommand = false; const execOptions = {}; const result = await niceSafeSpawn('echo', ['hi'], execOptions); - expect(spawnSpy).toHaveBeenCalledWith( - 'ionice', - ['-n', '7', 'echo', 'hi'], - execOptions, - ); + expect(spawnSpy).toHaveBeenCalledWith('ionice', ['-n', '7', 'echo', 'hi'], execOptions); expect(result).toBe(fakeSafeSpawnReturn); }); - it('should call which only once per command and cache the result', async () => { await niceSafeSpawn('echo', []); await niceSafeSpawn('echo', []); @@ -96,4 +84,4 @@ describe('nice', () => { expect(whichSpy).toHaveBeenCalledWith('ionice'); expect(whichSpy.mock.calls.length).toBe(2); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/nuclideUri-test.js b/modules/nuclide-commons/__tests__/nuclideUri-test.js index 1848f4b8..37dd1f17 100644 --- a/modules/nuclide-commons/__tests__/nuclideUri-test.js +++ b/modules/nuclide-commons/__tests__/nuclideUri-test.js @@ -1,3 +1,21 @@ +"use strict"; + +function _nuclideUri() { + const data = _interopRequireWildcard(require("../nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +var _path = _interopRequireDefault(require("path")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,762 +24,496 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import nuclideUri, {__TEST__} from '../nuclideUri'; // eslint-disable-next-line nuclide-internal/prefer-nuclide-uri -import path from 'path'; - describe('nuclide-uri', () => { const localUri = '/usr/local/file'; const badRemoteUriNoPath = 'nuclide://fb.com'; const atomUri = 'atom://bla/bla'; - const remoteUri = nuclideUri.createRemoteUri('fb.com', '/usr/local'); - const remoteUriWithSpaces = nuclideUri.createRemoteUri('fb.com', '/a b/c d'); - const remoteUriWithHashes = nuclideUri.createRemoteUri( - 'fb.co.uk', - '/ab/#c.d #', - ); + + const remoteUri = _nuclideUri().default.createRemoteUri('fb.com', '/usr/local'); + + const remoteUriWithSpaces = _nuclideUri().default.createRemoteUri('fb.com', '/a b/c d'); + + const remoteUriWithHashes = _nuclideUri().default.createRemoteUri('fb.co.uk', '/ab/#c.d #'); + const localArchiveUri = '/etc/file.zip!a'; - const remoteArchiveUri = nuclideUri.createRemoteUri( - 'fb.com', - '/file.zip!a.txt', - ); + + const remoteArchiveUri = _nuclideUri().default.createRemoteUri('fb.com', '/file.zip!a.txt'); + const endsWithExclamationUri = '/module/!! WARNING !!'; const archiveSuffixUri = '/etc/file.zip!'; - it('isRemote', () => { - expect(nuclideUri.isRemote('/')).toBe(false); - expect(nuclideUri.isRemote(remoteUri)).toBe(true); - expect(nuclideUri.isRemote(atomUri)).toBe(false); - expect(nuclideUri.isRemote(localArchiveUri)).toBe(false); - expect(nuclideUri.isRemote(remoteArchiveUri)).toBe(true); - expect(nuclideUri.isRemote(endsWithExclamationUri)).toBe(false); + expect(_nuclideUri().default.isRemote('/')).toBe(false); + expect(_nuclideUri().default.isRemote(remoteUri)).toBe(true); + expect(_nuclideUri().default.isRemote(atomUri)).toBe(false); + expect(_nuclideUri().default.isRemote(localArchiveUri)).toBe(false); + expect(_nuclideUri().default.isRemote(remoteArchiveUri)).toBe(true); + expect(_nuclideUri().default.isRemote(endsWithExclamationUri)).toBe(false); }); - it('isLocal', () => { - expect(nuclideUri.isLocal('/')).toBe(true); - expect(nuclideUri.isLocal(remoteUri)).toBe(false); - expect(nuclideUri.isLocal('C:\\abc')).toBe(true); - expect(nuclideUri.isLocal(atomUri)).toBe(false); - expect(nuclideUri.isLocal(localArchiveUri)).toBe(true); - expect(nuclideUri.isLocal(remoteArchiveUri)).toBe(false); - expect(nuclideUri.isLocal(endsWithExclamationUri)).toBe(true); + expect(_nuclideUri().default.isLocal('/')).toBe(true); + expect(_nuclideUri().default.isLocal(remoteUri)).toBe(false); + expect(_nuclideUri().default.isLocal('C:\\abc')).toBe(true); + expect(_nuclideUri().default.isLocal(atomUri)).toBe(false); + expect(_nuclideUri().default.isLocal(localArchiveUri)).toBe(true); + expect(_nuclideUri().default.isLocal(remoteArchiveUri)).toBe(false); + expect(_nuclideUri().default.isLocal(endsWithExclamationUri)).toBe(true); }); - it('createRemoteUri', () => { expect(remoteUri).toBe('nuclide://fb.com/usr/local'); expect(remoteUriWithSpaces).toBe('nuclide://fb.com/a b/c d'); expect(remoteArchiveUri).toBe('nuclide://fb.com/file.zip!a.txt'); }); - it('join', () => { - expect(nuclideUri.join.bind(null, badRemoteUriNoPath, '../foo')).toThrow(); - expect(nuclideUri.join('/usr/local', 'bin')).toBe('/usr/local/bin'); - expect(nuclideUri.join(remoteUri, 'bin')).toBe( - 'nuclide://fb.com/usr/local/bin', - ); - expect(nuclideUri.join(localArchiveUri, 'b.txt')).toBe( - '/etc/file.zip!a/b.txt', - ); - expect(nuclideUri.join(endsWithExclamationUri, 'b.txt')).toBe( - '/module/!! WARNING !!/b.txt', - ); - expect(nuclideUri.join('/usr/local', '..')).toBe('/usr'); - expect(nuclideUri.join(remoteUri, '..')).toBe('nuclide://fb.com/usr'); - expect(() => nuclideUri.join(archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.join.bind(null, badRemoteUriNoPath, '../foo')).toThrow(); + expect(_nuclideUri().default.join('/usr/local', 'bin')).toBe('/usr/local/bin'); + expect(_nuclideUri().default.join(remoteUri, 'bin')).toBe('nuclide://fb.com/usr/local/bin'); + expect(_nuclideUri().default.join(localArchiveUri, 'b.txt')).toBe('/etc/file.zip!a/b.txt'); + expect(_nuclideUri().default.join(endsWithExclamationUri, 'b.txt')).toBe('/module/!! WARNING !!/b.txt'); + expect(_nuclideUri().default.join('/usr/local', '..')).toBe('/usr'); + expect(_nuclideUri().default.join(remoteUri, '..')).toBe('nuclide://fb.com/usr'); + expect(() => _nuclideUri().default.join(archiveSuffixUri)).toThrow(); }); - it('archiveJoin', () => { - expect(nuclideUri.archiveJoin('/file.zip', 'a.txt')).toBe( - '/file.zip!a.txt', - ); - expect(() => nuclideUri.archiveJoin(archiveSuffixUri, 'a.txt')).toThrow(); - expect(() => nuclideUri.archiveJoin('/dir', 'a.txt')).toThrow(); + expect(_nuclideUri().default.archiveJoin('/file.zip', 'a.txt')).toBe('/file.zip!a.txt'); + expect(() => _nuclideUri().default.archiveJoin(archiveSuffixUri, 'a.txt')).toThrow(); + expect(() => _nuclideUri().default.archiveJoin('/dir', 'a.txt')).toThrow(); }); - describe('parsing remote', () => { it('handles simple paths', () => { - expect(nuclideUri.getHostname(remoteUri)).toBe('fb.com'); - expect(nuclideUri.getPath(remoteUri)).toBe('/usr/local'); + expect(_nuclideUri().default.getHostname(remoteUri)).toBe('fb.com'); + expect(_nuclideUri().default.getPath(remoteUri)).toBe('/usr/local'); }); - it('does not encode space characters', () => { - expect(nuclideUri.getHostname(remoteUriWithSpaces)).toBe('fb.com'); - expect(nuclideUri.getPath(remoteUriWithSpaces)).toBe('/a b/c d'); + expect(_nuclideUri().default.getHostname(remoteUriWithSpaces)).toBe('fb.com'); + expect(_nuclideUri().default.getPath(remoteUriWithSpaces)).toBe('/a b/c d'); }); - it('treats hash symbols as literals, part of the path', () => { - const parsedUri = nuclideUri.parse(remoteUriWithHashes); + const parsedUri = _nuclideUri().default.parse(remoteUriWithHashes); + expect(parsedUri.hostname).toBe('fb.co.uk'); expect(parsedUri.path).toBe('/ab/#c.d #'); }); - it('throws when given an illegal URI', () => { - expect(() => nuclideUri.getHostname(archiveSuffixUri)).toThrow(); - expect(() => nuclideUri.getPath(archiveSuffixUri)).toThrow(); - expect(() => nuclideUri.parse(archiveSuffixUri)).toThrow(); + expect(() => _nuclideUri().default.getHostname(archiveSuffixUri)).toThrow(); + expect(() => _nuclideUri().default.getPath(archiveSuffixUri)).toThrow(); + expect(() => _nuclideUri().default.parse(archiveSuffixUri)).toThrow(); }); }); - it('parsing local', () => { - expect(() => nuclideUri.getHostname(localUri)).toThrow(); - expect(() => nuclideUri.getHostname(localArchiveUri)).toThrow(); - expect(nuclideUri.getPath(localUri)).toBe(localUri); - expect(nuclideUri.getPath(localArchiveUri)).toBe(localArchiveUri); - expect(nuclideUri.getPath(remoteArchiveUri)).toBe('/file.zip!a.txt'); - expect(nuclideUri.getPath(endsWithExclamationUri)).toBe( - endsWithExclamationUri, - ); - expect(() => nuclideUri.getPath('nuclide://host/archive.zip!')).toThrow(); - expect(() => nuclideUri.parseRemoteUri(localUri)).toThrow(); + expect(() => _nuclideUri().default.getHostname(localUri)).toThrow(); + expect(() => _nuclideUri().default.getHostname(localArchiveUri)).toThrow(); + expect(_nuclideUri().default.getPath(localUri)).toBe(localUri); + expect(_nuclideUri().default.getPath(localArchiveUri)).toBe(localArchiveUri); + expect(_nuclideUri().default.getPath(remoteArchiveUri)).toBe('/file.zip!a.txt'); + expect(_nuclideUri().default.getPath(endsWithExclamationUri)).toBe(endsWithExclamationUri); + expect(() => _nuclideUri().default.getPath('nuclide://host/archive.zip!')).toThrow(); + expect(() => _nuclideUri().default.parseRemoteUri(localUri)).toThrow(); }); - it('basename', () => { - expect(nuclideUri.basename('/')).toBe(''); - expect(nuclideUri.basename('/abc')).toBe('abc'); - expect(nuclideUri.basename('/abc/')).toBe('abc'); - expect(nuclideUri.basename('/abc/def')).toBe('def'); - expect(nuclideUri.basename('/abc/def/')).toBe('def'); - - expect(nuclideUri.basename('nuclide://host/')).toBe(''); - expect(nuclideUri.basename('nuclide://host/abc')).toBe('abc'); - expect(nuclideUri.basename('nuclide://host/abc/')).toBe('abc'); - expect(nuclideUri.basename('nuclide://host/abc/def')).toBe('def'); - expect(nuclideUri.basename('nuclide://host/abc/def/')).toBe('def'); - expect(nuclideUri.basename('nuclide://host/a c/d f')).toBe('d f'); - - expect(nuclideUri.basename('/z.zip!abc')).toBe('abc'); - expect(nuclideUri.basename('/z.zip!abc/')).toBe('abc'); - expect(nuclideUri.basename('/z.zip!abc/def')).toBe('def'); - expect(nuclideUri.basename('/abc/def!ghi')).toBe('def!ghi'); - expect(nuclideUri.basename('/abc/def.txt!ghi')).toBe('def.txt!ghi'); - - expect(nuclideUri.basename('nuclide://host/z.zip!abc')).toBe('abc'); - expect(nuclideUri.basename('nuclide://host/z.zip!abc/')).toBe('abc'); - expect(nuclideUri.basename('nuclide://host/z.zip!abc/def')).toBe('def'); - - expect(nuclideUri.basename('C:\\')).toBe(''); - expect(nuclideUri.basename('C:\\abc')).toBe('abc'); - expect(nuclideUri.basename('C:\\abc\\')).toBe('abc'); - expect(nuclideUri.basename('C:\\abc\\def')).toBe('def'); - expect(nuclideUri.basename('C:\\abc\\def\\')).toBe('def'); - expect(nuclideUri.basename('\\abc\\def')).toBe('def'); - expect(nuclideUri.basename('\\abc\\def\\')).toBe('def'); - - expect(nuclideUri.basename('C:\\z.zip!abc')).toBe('abc'); - expect(nuclideUri.basename('C:\\z.zip!abc\\')).toBe('abc'); - expect(nuclideUri.basename('C:\\z.zip!abc\\def')).toBe('def'); - expect(nuclideUri.basename('C:\\abc\\def!ghi')).toBe('def!ghi'); - expect(nuclideUri.basename('C:\\abc\\def.txt!ghi')).toBe('def.txt!ghi'); - expect(nuclideUri.basename('\\z.zip!abc')).toBe('abc'); - expect(nuclideUri.basename('\\z.zip!abc\\')).toBe('abc'); - expect(nuclideUri.basename('\\z.zip!abc\\def')).toBe('def'); - expect(nuclideUri.basename('\\abc\\def!ghi')).toBe('def!ghi'); - expect(nuclideUri.basename('\\abc\\def.txt!ghi')).toBe('def.txt!ghi'); - - expect(() => nuclideUri.basename(archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.basename('/')).toBe(''); + expect(_nuclideUri().default.basename('/abc')).toBe('abc'); + expect(_nuclideUri().default.basename('/abc/')).toBe('abc'); + expect(_nuclideUri().default.basename('/abc/def')).toBe('def'); + expect(_nuclideUri().default.basename('/abc/def/')).toBe('def'); + expect(_nuclideUri().default.basename('nuclide://host/')).toBe(''); + expect(_nuclideUri().default.basename('nuclide://host/abc')).toBe('abc'); + expect(_nuclideUri().default.basename('nuclide://host/abc/')).toBe('abc'); + expect(_nuclideUri().default.basename('nuclide://host/abc/def')).toBe('def'); + expect(_nuclideUri().default.basename('nuclide://host/abc/def/')).toBe('def'); + expect(_nuclideUri().default.basename('nuclide://host/a c/d f')).toBe('d f'); + expect(_nuclideUri().default.basename('/z.zip!abc')).toBe('abc'); + expect(_nuclideUri().default.basename('/z.zip!abc/')).toBe('abc'); + expect(_nuclideUri().default.basename('/z.zip!abc/def')).toBe('def'); + expect(_nuclideUri().default.basename('/abc/def!ghi')).toBe('def!ghi'); + expect(_nuclideUri().default.basename('/abc/def.txt!ghi')).toBe('def.txt!ghi'); + expect(_nuclideUri().default.basename('nuclide://host/z.zip!abc')).toBe('abc'); + expect(_nuclideUri().default.basename('nuclide://host/z.zip!abc/')).toBe('abc'); + expect(_nuclideUri().default.basename('nuclide://host/z.zip!abc/def')).toBe('def'); + expect(_nuclideUri().default.basename('C:\\')).toBe(''); + expect(_nuclideUri().default.basename('C:\\abc')).toBe('abc'); + expect(_nuclideUri().default.basename('C:\\abc\\')).toBe('abc'); + expect(_nuclideUri().default.basename('C:\\abc\\def')).toBe('def'); + expect(_nuclideUri().default.basename('C:\\abc\\def\\')).toBe('def'); + expect(_nuclideUri().default.basename('\\abc\\def')).toBe('def'); + expect(_nuclideUri().default.basename('\\abc\\def\\')).toBe('def'); + expect(_nuclideUri().default.basename('C:\\z.zip!abc')).toBe('abc'); + expect(_nuclideUri().default.basename('C:\\z.zip!abc\\')).toBe('abc'); + expect(_nuclideUri().default.basename('C:\\z.zip!abc\\def')).toBe('def'); + expect(_nuclideUri().default.basename('C:\\abc\\def!ghi')).toBe('def!ghi'); + expect(_nuclideUri().default.basename('C:\\abc\\def.txt!ghi')).toBe('def.txt!ghi'); + expect(_nuclideUri().default.basename('\\z.zip!abc')).toBe('abc'); + expect(_nuclideUri().default.basename('\\z.zip!abc\\')).toBe('abc'); + expect(_nuclideUri().default.basename('\\z.zip!abc\\def')).toBe('def'); + expect(_nuclideUri().default.basename('\\abc\\def!ghi')).toBe('def!ghi'); + expect(_nuclideUri().default.basename('\\abc\\def.txt!ghi')).toBe('def.txt!ghi'); + expect(() => _nuclideUri().default.basename(archiveSuffixUri)).toThrow(); }); - it('dirname', () => { - expect(nuclideUri.dirname('/')).toBe('/'); - expect(nuclideUri.dirname('/abc')).toBe('/'); - expect(nuclideUri.dirname('/abc/')).toBe('/'); - expect(nuclideUri.dirname('/abc/def')).toBe('/abc'); - expect(nuclideUri.dirname('/abc/def/')).toBe('/abc'); - - expect(nuclideUri.dirname('nuclide://host/')).toBe('nuclide://host/'); - expect(nuclideUri.dirname('nuclide://host/abc')).toBe('nuclide://host/'); - expect(nuclideUri.dirname('nuclide://host/abc/')).toBe('nuclide://host/'); - expect(nuclideUri.dirname('nuclide://host/abc/def')).toBe( - 'nuclide://host/abc', - ); - expect(nuclideUri.dirname('nuclide://host/abc/def/')).toBe( - 'nuclide://host/abc', - ); - expect(nuclideUri.dirname('nuclide://host/a c/d f')).toBe( - 'nuclide://host/a c', - ); - - expect(nuclideUri.dirname('/z.zip!abc')).toBe('/z.zip'); - expect(nuclideUri.dirname('/z.zip!abc/')).toBe('/z.zip'); - expect(nuclideUri.dirname('/z.zip!abc/def')).toBe('/z.zip!abc'); - expect(nuclideUri.dirname('/abc/def!ghi')).toBe('/abc'); - expect(nuclideUri.dirname('/abc/def.txt!ghi')).toBe('/abc'); - - expect(nuclideUri.dirname('nuclide://host/z.zip!abc')).toBe( - 'nuclide://host/z.zip', - ); - expect(nuclideUri.dirname('nuclide://host/z.zip!abc/')).toBe( - 'nuclide://host/z.zip', - ); - expect(nuclideUri.dirname('nuclide://host/z.zip!abc/def')).toBe( - 'nuclide://host/z.zip!abc', - ); - - expect(nuclideUri.dirname('C:\\')).toBe('C:\\'); - expect(nuclideUri.dirname('C:\\abc')).toBe('C:\\'); - expect(nuclideUri.dirname('C:\\abc\\')).toBe('C:\\'); - expect(nuclideUri.dirname('C:\\abc\\def')).toBe('C:\\abc'); - expect(nuclideUri.dirname('C:\\abc\\def\\')).toBe('C:\\abc'); - expect(nuclideUri.dirname('\\abc\\def')).toBe('\\abc'); - expect(nuclideUri.dirname('\\abc\\def\\')).toBe('\\abc'); - - expect(nuclideUri.dirname('C:\\z.zip!abc')).toBe('C:\\z.zip'); - expect(nuclideUri.dirname('C:\\z.zip!abc/')).toBe('C:\\z.zip'); - expect(nuclideUri.dirname('C:\\z.zip!abc/def')).toBe('C:\\z.zip!abc'); - expect(nuclideUri.dirname('C:\\abc\\def!ghi')).toBe('C:\\abc'); - expect(nuclideUri.dirname('C:\\abc\\def.txt!ghi')).toBe('C:\\abc'); - expect(nuclideUri.dirname('\\z.zip!abc')).toBe('\\z.zip'); - expect(nuclideUri.dirname('\\z.zip!abc/')).toBe('\\z.zip'); - expect(nuclideUri.dirname('\\z.zip!abc/def')).toBe('\\z.zip!abc'); - expect(nuclideUri.dirname('\\abc\\def!ghi')).toBe('\\abc'); - expect(nuclideUri.dirname('\\abc\\def.txt!ghi')).toBe('\\abc'); - - expect(() => nuclideUri.dirname(archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.dirname('/')).toBe('/'); + expect(_nuclideUri().default.dirname('/abc')).toBe('/'); + expect(_nuclideUri().default.dirname('/abc/')).toBe('/'); + expect(_nuclideUri().default.dirname('/abc/def')).toBe('/abc'); + expect(_nuclideUri().default.dirname('/abc/def/')).toBe('/abc'); + expect(_nuclideUri().default.dirname('nuclide://host/')).toBe('nuclide://host/'); + expect(_nuclideUri().default.dirname('nuclide://host/abc')).toBe('nuclide://host/'); + expect(_nuclideUri().default.dirname('nuclide://host/abc/')).toBe('nuclide://host/'); + expect(_nuclideUri().default.dirname('nuclide://host/abc/def')).toBe('nuclide://host/abc'); + expect(_nuclideUri().default.dirname('nuclide://host/abc/def/')).toBe('nuclide://host/abc'); + expect(_nuclideUri().default.dirname('nuclide://host/a c/d f')).toBe('nuclide://host/a c'); + expect(_nuclideUri().default.dirname('/z.zip!abc')).toBe('/z.zip'); + expect(_nuclideUri().default.dirname('/z.zip!abc/')).toBe('/z.zip'); + expect(_nuclideUri().default.dirname('/z.zip!abc/def')).toBe('/z.zip!abc'); + expect(_nuclideUri().default.dirname('/abc/def!ghi')).toBe('/abc'); + expect(_nuclideUri().default.dirname('/abc/def.txt!ghi')).toBe('/abc'); + expect(_nuclideUri().default.dirname('nuclide://host/z.zip!abc')).toBe('nuclide://host/z.zip'); + expect(_nuclideUri().default.dirname('nuclide://host/z.zip!abc/')).toBe('nuclide://host/z.zip'); + expect(_nuclideUri().default.dirname('nuclide://host/z.zip!abc/def')).toBe('nuclide://host/z.zip!abc'); + expect(_nuclideUri().default.dirname('C:\\')).toBe('C:\\'); + expect(_nuclideUri().default.dirname('C:\\abc')).toBe('C:\\'); + expect(_nuclideUri().default.dirname('C:\\abc\\')).toBe('C:\\'); + expect(_nuclideUri().default.dirname('C:\\abc\\def')).toBe('C:\\abc'); + expect(_nuclideUri().default.dirname('C:\\abc\\def\\')).toBe('C:\\abc'); + expect(_nuclideUri().default.dirname('\\abc\\def')).toBe('\\abc'); + expect(_nuclideUri().default.dirname('\\abc\\def\\')).toBe('\\abc'); + expect(_nuclideUri().default.dirname('C:\\z.zip!abc')).toBe('C:\\z.zip'); + expect(_nuclideUri().default.dirname('C:\\z.zip!abc/')).toBe('C:\\z.zip'); + expect(_nuclideUri().default.dirname('C:\\z.zip!abc/def')).toBe('C:\\z.zip!abc'); + expect(_nuclideUri().default.dirname('C:\\abc\\def!ghi')).toBe('C:\\abc'); + expect(_nuclideUri().default.dirname('C:\\abc\\def.txt!ghi')).toBe('C:\\abc'); + expect(_nuclideUri().default.dirname('\\z.zip!abc')).toBe('\\z.zip'); + expect(_nuclideUri().default.dirname('\\z.zip!abc/')).toBe('\\z.zip'); + expect(_nuclideUri().default.dirname('\\z.zip!abc/def')).toBe('\\z.zip!abc'); + expect(_nuclideUri().default.dirname('\\abc\\def!ghi')).toBe('\\abc'); + expect(_nuclideUri().default.dirname('\\abc\\def.txt!ghi')).toBe('\\abc'); + expect(() => _nuclideUri().default.dirname(archiveSuffixUri)).toThrow(); }); - it('extname', () => { - expect(nuclideUri.extname('/abc')).toBe(''); - expect(nuclideUri.extname('/abc.')).toBe('.'); - expect(nuclideUri.extname('/abc.txt')).toBe('.txt'); - expect(nuclideUri.extname('/abc/def.html')).toBe('.html'); - expect(nuclideUri.extname('/abc/def/')).toBe(''); - expect(nuclideUri.extname('/abc/def.dir/')).toBe('.dir'); - - expect(nuclideUri.extname('nuclide://host/')).toBe(''); - expect(nuclideUri.extname('nuclide://host/abc')).toBe(''); - expect(nuclideUri.extname('nuclide://host/abc.txt')).toBe('.txt'); - expect(nuclideUri.extname('nuclide://host/abc.')).toBe('.'); - expect(nuclideUri.extname('nuclide://host/abc/')).toBe(''); - expect(nuclideUri.extname('nuclide://host/abc/def')).toBe(''); - expect(nuclideUri.extname('nuclide://host/abc/def.js')).toBe('.js'); - - expect(nuclideUri.extname('/z.zip!abc')).toBe(''); - expect(nuclideUri.extname('/z.zip!abc.zip')).toBe('.zip'); - expect(nuclideUri.extname('/abc.txt!def')).toBe('.txt!def'); - - expect(nuclideUri.extname('C:\\')).toBe(''); - expect(nuclideUri.extname('C:\\abc')).toBe(''); - expect(nuclideUri.extname('C:\\abc\\')).toBe(''); - expect(nuclideUri.extname('C:\\abc.')).toBe('.'); - expect(nuclideUri.extname('C:\\abc.js')).toBe('.js'); - expect(nuclideUri.extname('C:\\abc\\def')).toBe(''); - expect(nuclideUri.extname('C:\\abc\\def\\')).toBe(''); - expect(nuclideUri.extname('C:\\abc\\def.')).toBe('.'); - expect(nuclideUri.extname('C:\\abc\\def.html')).toBe('.html'); - expect(nuclideUri.extname('\\abc\\def')).toBe(''); - expect(nuclideUri.extname('\\abc\\def.dir\\')).toBe('.dir'); - expect(nuclideUri.extname('\\abc\\def.')).toBe('.'); - expect(nuclideUri.extname('\\abc\\def.xml')).toBe('.xml'); - - expect(nuclideUri.extname('C:\\z.zip!abc')).toBe(''); - expect(nuclideUri.extname('C:\\z.zip!abc.zip')).toBe('.zip'); - expect(nuclideUri.extname('C:\\abc.txt!def')).toBe('.txt!def'); - expect(nuclideUri.extname('\\z.zip!abc')).toBe(''); - expect(nuclideUri.extname('\\z.zip!abc.zip')).toBe('.zip'); - expect(nuclideUri.extname('\\abc.txt!def')).toBe('.txt!def'); - - expect(() => nuclideUri.extname(archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.extname('/abc')).toBe(''); + expect(_nuclideUri().default.extname('/abc.')).toBe('.'); + expect(_nuclideUri().default.extname('/abc.txt')).toBe('.txt'); + expect(_nuclideUri().default.extname('/abc/def.html')).toBe('.html'); + expect(_nuclideUri().default.extname('/abc/def/')).toBe(''); + expect(_nuclideUri().default.extname('/abc/def.dir/')).toBe('.dir'); + expect(_nuclideUri().default.extname('nuclide://host/')).toBe(''); + expect(_nuclideUri().default.extname('nuclide://host/abc')).toBe(''); + expect(_nuclideUri().default.extname('nuclide://host/abc.txt')).toBe('.txt'); + expect(_nuclideUri().default.extname('nuclide://host/abc.')).toBe('.'); + expect(_nuclideUri().default.extname('nuclide://host/abc/')).toBe(''); + expect(_nuclideUri().default.extname('nuclide://host/abc/def')).toBe(''); + expect(_nuclideUri().default.extname('nuclide://host/abc/def.js')).toBe('.js'); + expect(_nuclideUri().default.extname('/z.zip!abc')).toBe(''); + expect(_nuclideUri().default.extname('/z.zip!abc.zip')).toBe('.zip'); + expect(_nuclideUri().default.extname('/abc.txt!def')).toBe('.txt!def'); + expect(_nuclideUri().default.extname('C:\\')).toBe(''); + expect(_nuclideUri().default.extname('C:\\abc')).toBe(''); + expect(_nuclideUri().default.extname('C:\\abc\\')).toBe(''); + expect(_nuclideUri().default.extname('C:\\abc.')).toBe('.'); + expect(_nuclideUri().default.extname('C:\\abc.js')).toBe('.js'); + expect(_nuclideUri().default.extname('C:\\abc\\def')).toBe(''); + expect(_nuclideUri().default.extname('C:\\abc\\def\\')).toBe(''); + expect(_nuclideUri().default.extname('C:\\abc\\def.')).toBe('.'); + expect(_nuclideUri().default.extname('C:\\abc\\def.html')).toBe('.html'); + expect(_nuclideUri().default.extname('\\abc\\def')).toBe(''); + expect(_nuclideUri().default.extname('\\abc\\def.dir\\')).toBe('.dir'); + expect(_nuclideUri().default.extname('\\abc\\def.')).toBe('.'); + expect(_nuclideUri().default.extname('\\abc\\def.xml')).toBe('.xml'); + expect(_nuclideUri().default.extname('C:\\z.zip!abc')).toBe(''); + expect(_nuclideUri().default.extname('C:\\z.zip!abc.zip')).toBe('.zip'); + expect(_nuclideUri().default.extname('C:\\abc.txt!def')).toBe('.txt!def'); + expect(_nuclideUri().default.extname('\\z.zip!abc')).toBe(''); + expect(_nuclideUri().default.extname('\\z.zip!abc.zip')).toBe('.zip'); + expect(_nuclideUri().default.extname('\\abc.txt!def')).toBe('.txt!def'); + expect(() => _nuclideUri().default.extname(archiveSuffixUri)).toThrow(); }); - it('getParent', () => { - expect(nuclideUri.getParent(localUri)).toBe('/usr/local'); - expect(nuclideUri.getParent(remoteUri)).toBe('nuclide://fb.com/usr'); - expect(nuclideUri.getParent('/etc/file.zip!a')).toBe('/etc/file.zip'); - expect(nuclideUri.getParent(localArchiveUri)).toBe('/etc/file.zip'); - expect(nuclideUri.getParent(remoteArchiveUri)).toBe( - 'nuclide://fb.com/file.zip', - ); - expect(nuclideUri.getParent(endsWithExclamationUri)).toBe('/module'); - expect(nuclideUri.getParent('/abc/def!ghi')).toBe('/abc'); - expect(() => nuclideUri.getParent(archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.getParent(localUri)).toBe('/usr/local'); + expect(_nuclideUri().default.getParent(remoteUri)).toBe('nuclide://fb.com/usr'); + expect(_nuclideUri().default.getParent('/etc/file.zip!a')).toBe('/etc/file.zip'); + expect(_nuclideUri().default.getParent(localArchiveUri)).toBe('/etc/file.zip'); + expect(_nuclideUri().default.getParent(remoteArchiveUri)).toBe('nuclide://fb.com/file.zip'); + expect(_nuclideUri().default.getParent(endsWithExclamationUri)).toBe('/module'); + expect(_nuclideUri().default.getParent('/abc/def!ghi')).toBe('/abc'); + expect(() => _nuclideUri().default.getParent(archiveSuffixUri)).toThrow(); }); - it('contains', () => { - expect(nuclideUri.contains('/usr/local', localUri)).toBe(true); - expect(nuclideUri.contains('nuclide://fb.com/usr', remoteUri)).toBe(true); - expect(nuclideUri.contains('/foo/bar/', '/foo/bar/abc.txt')).toBe(true); - expect(nuclideUri.contains('/foo/bar', '/foo/bar/')).toBe(true); - expect(nuclideUri.contains('/foo/bar/', '/foo/bar/')).toBe(true); - expect(nuclideUri.contains('/foo/bar/', '/foo/bar')).toBe(true); - - expect(nuclideUri.contains('/z.zip', '/z.zip!abc')).toBe(true); - expect( - nuclideUri.contains( - 'nuclide://fb.com/z.zip', - 'nuclide://fb.com/z.zip!abc', - ), - ).toBe(true); - expect(nuclideUri.contains('/z.zip!abc', '/z.zip!abc/def')).toBe(true); - expect( - nuclideUri.contains( - 'nuclide://fb.com/z.zip!abc', - 'nuclide://fb.com/z.zip!abc/def', - ), - ).toBe(true); - expect(nuclideUri.contains('/abc', '/abc!def')).toBe(false); - - expect(() => nuclideUri.contains(archiveSuffixUri, '/foo/bar')).toThrow(); - expect(() => nuclideUri.contains('/foo/bar', archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.contains('/usr/local', localUri)).toBe(true); + expect(_nuclideUri().default.contains('nuclide://fb.com/usr', remoteUri)).toBe(true); + expect(_nuclideUri().default.contains('/foo/bar/', '/foo/bar/abc.txt')).toBe(true); + expect(_nuclideUri().default.contains('/foo/bar', '/foo/bar/')).toBe(true); + expect(_nuclideUri().default.contains('/foo/bar/', '/foo/bar/')).toBe(true); + expect(_nuclideUri().default.contains('/foo/bar/', '/foo/bar')).toBe(true); + expect(_nuclideUri().default.contains('/z.zip', '/z.zip!abc')).toBe(true); + expect(_nuclideUri().default.contains('nuclide://fb.com/z.zip', 'nuclide://fb.com/z.zip!abc')).toBe(true); + expect(_nuclideUri().default.contains('/z.zip!abc', '/z.zip!abc/def')).toBe(true); + expect(_nuclideUri().default.contains('nuclide://fb.com/z.zip!abc', 'nuclide://fb.com/z.zip!abc/def')).toBe(true); + expect(_nuclideUri().default.contains('/abc', '/abc!def')).toBe(false); + expect(() => _nuclideUri().default.contains(archiveSuffixUri, '/foo/bar')).toThrow(); + expect(() => _nuclideUri().default.contains('/foo/bar', archiveSuffixUri)).toThrow(); }); - it('collapse', () => { - expect(nuclideUri.collapse(['/a', '/b'])).toEqual(['/a', '/b']); - expect(nuclideUri.collapse(['/a/b/c/d', '/a', '/a/b'])).toEqual(['/a']); - expect(nuclideUri.collapse(['/a', '/a/b', '/a/b/c'])).toEqual(['/a']); - expect( - nuclideUri.collapse(['/a/b.zip', '/a/b.zip!c', '/a/b.zip!c/d']), - ).toEqual(['/a/b.zip']); - expect( - nuclideUri.collapse(['/a/b.zip!c', '/a/b.zip!c/d', '/a/b.zip!c/d/e']), - ).toEqual(['/a/b.zip!c']); - expect( - nuclideUri.collapse(['/a/c', '/a/c/d', '/a/b', '/a/b/c/d/e']), - ).toEqual(['/a/c', '/a/b']); - expect(nuclideUri.collapse(['/a/be', '/a/b'])).toEqual(['/a/be', '/a/b']); - expect( - nuclideUri.collapse([ - 'nuclide://fb.com/usr/local', - 'nuclide://fb.com/usr/local/test', - 'nuclide://facebook.com/usr/local/test', - ]), - ).toEqual([ - 'nuclide://fb.com/usr/local', - 'nuclide://facebook.com/usr/local/test', - ]); + expect(_nuclideUri().default.collapse(['/a', '/b'])).toEqual(['/a', '/b']); + expect(_nuclideUri().default.collapse(['/a/b/c/d', '/a', '/a/b'])).toEqual(['/a']); + expect(_nuclideUri().default.collapse(['/a', '/a/b', '/a/b/c'])).toEqual(['/a']); + expect(_nuclideUri().default.collapse(['/a/b.zip', '/a/b.zip!c', '/a/b.zip!c/d'])).toEqual(['/a/b.zip']); + expect(_nuclideUri().default.collapse(['/a/b.zip!c', '/a/b.zip!c/d', '/a/b.zip!c/d/e'])).toEqual(['/a/b.zip!c']); + expect(_nuclideUri().default.collapse(['/a/c', '/a/c/d', '/a/b', '/a/b/c/d/e'])).toEqual(['/a/c', '/a/b']); + expect(_nuclideUri().default.collapse(['/a/be', '/a/b'])).toEqual(['/a/be', '/a/b']); + expect(_nuclideUri().default.collapse(['nuclide://fb.com/usr/local', 'nuclide://fb.com/usr/local/test', 'nuclide://facebook.com/usr/local/test'])).toEqual(['nuclide://fb.com/usr/local', 'nuclide://facebook.com/usr/local/test']); }); - it('normalize', () => { - expect(nuclideUri.normalize(localUri)).toBe(localUri); - expect(nuclideUri.normalize(remoteUri)).toBe(remoteUri); - expect(nuclideUri.normalize.bind(null, badRemoteUriNoPath)).toThrow(); - expect(nuclideUri.normalize('/usr/local/..')).toBe('/usr'); - expect(nuclideUri.normalize('nuclide://fb.com/usr/local/..')).toBe( - 'nuclide://fb.com/usr', - ); - expect(nuclideUri.normalize('/a b/c d/..')).toBe('/a b'); - expect(nuclideUri.normalize('/a/b.zip!c/..')).toBe('/a/b.zip'); - expect(nuclideUri.normalize('/a/b.zip!c/d/../../..')).toBe('/a'); - expect(nuclideUri.normalize('/a/b!c/..')).toBe('/a'); - expect(() => nuclideUri.normalize(archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.normalize(localUri)).toBe(localUri); + expect(_nuclideUri().default.normalize(remoteUri)).toBe(remoteUri); + expect(_nuclideUri().default.normalize.bind(null, badRemoteUriNoPath)).toThrow(); + expect(_nuclideUri().default.normalize('/usr/local/..')).toBe('/usr'); + expect(_nuclideUri().default.normalize('nuclide://fb.com/usr/local/..')).toBe('nuclide://fb.com/usr'); + expect(_nuclideUri().default.normalize('/a b/c d/..')).toBe('/a b'); + expect(_nuclideUri().default.normalize('/a/b.zip!c/..')).toBe('/a/b.zip'); + expect(_nuclideUri().default.normalize('/a/b.zip!c/d/../../..')).toBe('/a'); + expect(_nuclideUri().default.normalize('/a/b!c/..')).toBe('/a'); + expect(() => _nuclideUri().default.normalize(archiveSuffixUri)).toThrow(); }); - it('relative', () => { - expect(() => nuclideUri.relative(localUri, remoteUri)).toThrow(); - expect(nuclideUri.relative(nuclideUri.dirname(remoteUri), remoteUri)).toBe( - 'local', - ); - expect(nuclideUri.relative(remoteUri, nuclideUri.dirname(remoteUri))).toBe( - '..', - ); - expect( - nuclideUri.relative( - nuclideUri.dirname(remoteUriWithSpaces), - remoteUriWithSpaces, - ), - ).toBe('c d'); - expect( - nuclideUri.relative( - remoteUriWithSpaces, - nuclideUri.dirname(remoteUriWithSpaces), - ), - ).toBe('..'); - expect(nuclideUri.relative(nuclideUri.dirname(localUri), localUri)).toBe( - 'file', - ); - expect(nuclideUri.relative(localUri, nuclideUri.dirname(localUri))).toBe( - '..', - ); - expect( - nuclideUri.relative(nuclideUri.dirname(localArchiveUri), localArchiveUri), - ).toBe('a'); - expect( - nuclideUri.relative(localArchiveUri, nuclideUri.dirname(localArchiveUri)), - ).toBe('..'); - expect( - nuclideUri.relative( - nuclideUri.dirname(endsWithExclamationUri), - endsWithExclamationUri, - ), - ).toBe('!! WARNING !!'); - expect( - nuclideUri.relative( - endsWithExclamationUri, - nuclideUri.dirname(endsWithExclamationUri), - ), - ).toBe('..'); - expect(nuclideUri.relative('/a/b.zip!c', '/a/b.zip!d')).toBe('../d'); - expect(nuclideUri.relative('/a/b!c', '/a/b!d')).toBe('../b!d'); - expect(() => nuclideUri.relative(archiveSuffixUri, 'foo')).toThrow(); + expect(() => _nuclideUri().default.relative(localUri, remoteUri)).toThrow(); + expect(_nuclideUri().default.relative(_nuclideUri().default.dirname(remoteUri), remoteUri)).toBe('local'); + expect(_nuclideUri().default.relative(remoteUri, _nuclideUri().default.dirname(remoteUri))).toBe('..'); + expect(_nuclideUri().default.relative(_nuclideUri().default.dirname(remoteUriWithSpaces), remoteUriWithSpaces)).toBe('c d'); + expect(_nuclideUri().default.relative(remoteUriWithSpaces, _nuclideUri().default.dirname(remoteUriWithSpaces))).toBe('..'); + expect(_nuclideUri().default.relative(_nuclideUri().default.dirname(localUri), localUri)).toBe('file'); + expect(_nuclideUri().default.relative(localUri, _nuclideUri().default.dirname(localUri))).toBe('..'); + expect(_nuclideUri().default.relative(_nuclideUri().default.dirname(localArchiveUri), localArchiveUri)).toBe('a'); + expect(_nuclideUri().default.relative(localArchiveUri, _nuclideUri().default.dirname(localArchiveUri))).toBe('..'); + expect(_nuclideUri().default.relative(_nuclideUri().default.dirname(endsWithExclamationUri), endsWithExclamationUri)).toBe('!! WARNING !!'); + expect(_nuclideUri().default.relative(endsWithExclamationUri, _nuclideUri().default.dirname(endsWithExclamationUri))).toBe('..'); + expect(_nuclideUri().default.relative('/a/b.zip!c', '/a/b.zip!d')).toBe('../d'); + expect(_nuclideUri().default.relative('/a/b!c', '/a/b!d')).toBe('../b!d'); + expect(() => _nuclideUri().default.relative(archiveSuffixUri, 'foo')).toThrow(); }); - it('nuclideUriToDisplayString', () => { - expect(nuclideUri.nuclideUriToDisplayString(localUri)).toBe(localUri); - expect(nuclideUri.nuclideUriToDisplayString(remoteUri)).toBe( - 'fb.com:/usr/local', - ); - expect(nuclideUri.nuclideUriToDisplayString(localArchiveUri)).toBe( - '/etc/file.zip!a', - ); - expect(nuclideUri.nuclideUriToDisplayString(remoteArchiveUri)).toBe( - 'fb.com:/file.zip!a.txt', - ); - expect(nuclideUri.nuclideUriToDisplayString(endsWithExclamationUri)).toBe( - '/module/!! WARNING !!', - ); - expect(() => - nuclideUri.nuclideUriToDisplayString(archiveSuffixUri), - ).toThrow(); + expect(_nuclideUri().default.nuclideUriToDisplayString(localUri)).toBe(localUri); + expect(_nuclideUri().default.nuclideUriToDisplayString(remoteUri)).toBe('fb.com:/usr/local'); + expect(_nuclideUri().default.nuclideUriToDisplayString(localArchiveUri)).toBe('/etc/file.zip!a'); + expect(_nuclideUri().default.nuclideUriToDisplayString(remoteArchiveUri)).toBe('fb.com:/file.zip!a.txt'); + expect(_nuclideUri().default.nuclideUriToDisplayString(endsWithExclamationUri)).toBe('/module/!! WARNING !!'); + expect(() => _nuclideUri().default.nuclideUriToDisplayString(archiveSuffixUri)).toThrow(); }); - describe('isRoot', () => { - it('plain posix root', () => expect(nuclideUri.isRoot('/')).toBe(true)); - it('double root', () => expect(nuclideUri.isRoot('//')).toBe(false)); - it('/abc', () => expect(nuclideUri.isRoot('/abc')).toBe(false)); - it('abc', () => expect(nuclideUri.isRoot('abc')).toBe(false)); - it('abc/def', () => expect(nuclideUri.isRoot('abc/def')).toBe(false)); - it('/file.zip!', () => - expect(() => nuclideUri.isRoot('/file.zip!')).toThrow()); - it('/file.zip!abc', () => - expect(nuclideUri.isRoot('/file.zip!abc')).toBe(false)); - it('remote root', () => - expect(nuclideUri.isRoot('nuclide://host/')).toBe(true)); - it('remote root with port', () => - expect(nuclideUri.isRoot('nuclide://host/')).toBe(true)); - it('remote non-root', () => - expect(nuclideUri.isRoot('nuclide://host/abc')).toBe(false)); + it('plain posix root', () => expect(_nuclideUri().default.isRoot('/')).toBe(true)); + it('double root', () => expect(_nuclideUri().default.isRoot('//')).toBe(false)); + it('/abc', () => expect(_nuclideUri().default.isRoot('/abc')).toBe(false)); + it('abc', () => expect(_nuclideUri().default.isRoot('abc')).toBe(false)); + it('abc/def', () => expect(_nuclideUri().default.isRoot('abc/def')).toBe(false)); + it('/file.zip!', () => expect(() => _nuclideUri().default.isRoot('/file.zip!')).toThrow()); + it('/file.zip!abc', () => expect(_nuclideUri().default.isRoot('/file.zip!abc')).toBe(false)); + it('remote root', () => expect(_nuclideUri().default.isRoot('nuclide://host/')).toBe(true)); + it('remote root with port', () => expect(_nuclideUri().default.isRoot('nuclide://host/')).toBe(true)); + it('remote non-root', () => expect(_nuclideUri().default.isRoot('nuclide://host/abc')).toBe(false)); it('remote non-root no port', () => { - expect(nuclideUri.isRoot('nuclide://host/abc')).toBe(false); + expect(_nuclideUri().default.isRoot('nuclide://host/abc')).toBe(false); }); - it('win diskless root', () => expect(nuclideUri.isRoot('\\')).toBe(true)); - it('win diskless double root', () => - expect(nuclideUri.isRoot('\\\\')).toBe(false)); - it('win diskless non-root', () => - expect(nuclideUri.isRoot('\\abc')).toBe(false)); - it('win diskful root', () => expect(nuclideUri.isRoot('C:\\')).toBe(true)); - it('win diskful double root', () => - expect(nuclideUri.isRoot('C:\\\\')).toBe(false)); - it('win diskful non-root', () => - expect(nuclideUri.isRoot('C:\\abc')).toBe(false)); - - it('win relative', () => expect(nuclideUri.isRoot('abc\\def')).toBe(false)); - + it('win diskless root', () => expect(_nuclideUri().default.isRoot('\\')).toBe(true)); + it('win diskless double root', () => expect(_nuclideUri().default.isRoot('\\\\')).toBe(false)); + it('win diskless non-root', () => expect(_nuclideUri().default.isRoot('\\abc')).toBe(false)); + it('win diskful root', () => expect(_nuclideUri().default.isRoot('C:\\')).toBe(true)); + it('win diskful double root', () => expect(_nuclideUri().default.isRoot('C:\\\\')).toBe(false)); + it('win diskful non-root', () => expect(_nuclideUri().default.isRoot('C:\\abc')).toBe(false)); + it('win relative', () => expect(_nuclideUri().default.isRoot('abc\\def')).toBe(false)); it('throws on illegal URIs', () => { - expect(() => nuclideUri.basename(archiveSuffixUri)).toThrow(); + expect(() => _nuclideUri().default.basename(archiveSuffixUri)).toThrow(); }); }); - it('adds a proper suffix when needed', () => { - expect(nuclideUri.ensureTrailingSeparator('/')).toBe('/'); - expect(nuclideUri.ensureTrailingSeparator('/abc')).toBe('/abc/'); - expect(nuclideUri.ensureTrailingSeparator('/abc/')).toBe('/abc/'); - expect(nuclideUri.ensureTrailingSeparator('/abc/def')).toBe('/abc/def/'); - expect(nuclideUri.ensureTrailingSeparator('/abc/def/')).toBe('/abc/def/'); - expect(nuclideUri.ensureTrailingSeparator('nuclide://host')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.ensureTrailingSeparator('nuclide://host/')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.ensureTrailingSeparator('nuclide://host/abc')).toBe( - 'nuclide://host/abc/', - ); - expect(nuclideUri.ensureTrailingSeparator('nuclide://host/abc/def')).toBe( - 'nuclide://host/abc/def/', - ); - expect(nuclideUri.ensureTrailingSeparator('nuclide://host/abc/def/')).toBe( - 'nuclide://host/abc/def/', - ); - expect(nuclideUri.ensureTrailingSeparator('C:\\')).toBe('C:\\'); - expect(nuclideUri.ensureTrailingSeparator('C:\\abc')).toBe('C:\\abc\\'); - expect(nuclideUri.ensureTrailingSeparator('C:\\abc\\')).toBe('C:\\abc\\'); - expect(nuclideUri.ensureTrailingSeparator('C:\\abc\\def')).toBe( - 'C:\\abc\\def\\', - ); - expect(nuclideUri.ensureTrailingSeparator('C:\\abc\\def\\')).toBe( - 'C:\\abc\\def\\', - ); - expect(nuclideUri.ensureTrailingSeparator('\\abc\\def')).toBe( - '\\abc\\def\\', - ); - expect(nuclideUri.ensureTrailingSeparator('\\abc\\def\\')).toBe( - '\\abc\\def\\', - ); - expect(() => - nuclideUri.ensureTrailingSeparator(archiveSuffixUri), - ).toThrow(); + expect(_nuclideUri().default.ensureTrailingSeparator('/')).toBe('/'); + expect(_nuclideUri().default.ensureTrailingSeparator('/abc')).toBe('/abc/'); + expect(_nuclideUri().default.ensureTrailingSeparator('/abc/')).toBe('/abc/'); + expect(_nuclideUri().default.ensureTrailingSeparator('/abc/def')).toBe('/abc/def/'); + expect(_nuclideUri().default.ensureTrailingSeparator('/abc/def/')).toBe('/abc/def/'); + expect(_nuclideUri().default.ensureTrailingSeparator('nuclide://host')).toBe('nuclide://host/'); + expect(_nuclideUri().default.ensureTrailingSeparator('nuclide://host/')).toBe('nuclide://host/'); + expect(_nuclideUri().default.ensureTrailingSeparator('nuclide://host/abc')).toBe('nuclide://host/abc/'); + expect(_nuclideUri().default.ensureTrailingSeparator('nuclide://host/abc/def')).toBe('nuclide://host/abc/def/'); + expect(_nuclideUri().default.ensureTrailingSeparator('nuclide://host/abc/def/')).toBe('nuclide://host/abc/def/'); + expect(_nuclideUri().default.ensureTrailingSeparator('C:\\')).toBe('C:\\'); + expect(_nuclideUri().default.ensureTrailingSeparator('C:\\abc')).toBe('C:\\abc\\'); + expect(_nuclideUri().default.ensureTrailingSeparator('C:\\abc\\')).toBe('C:\\abc\\'); + expect(_nuclideUri().default.ensureTrailingSeparator('C:\\abc\\def')).toBe('C:\\abc\\def\\'); + expect(_nuclideUri().default.ensureTrailingSeparator('C:\\abc\\def\\')).toBe('C:\\abc\\def\\'); + expect(_nuclideUri().default.ensureTrailingSeparator('\\abc\\def')).toBe('\\abc\\def\\'); + expect(_nuclideUri().default.ensureTrailingSeparator('\\abc\\def\\')).toBe('\\abc\\def\\'); + expect(() => _nuclideUri().default.ensureTrailingSeparator(archiveSuffixUri)).toThrow(); }); - it('properly removes suffix when needed', () => { - expect(nuclideUri.trimTrailingSeparator('/')).toBe('/'); - expect(nuclideUri.trimTrailingSeparator('//')).toBe('/'); - expect(nuclideUri.trimTrailingSeparator('/abc')).toBe('/abc'); - expect(nuclideUri.trimTrailingSeparator('/abc/')).toBe('/abc'); - expect(nuclideUri.trimTrailingSeparator('/abc/def')).toBe('/abc/def'); - expect(nuclideUri.trimTrailingSeparator('/abc/def/')).toBe('/abc/def'); - expect(nuclideUri.trimTrailingSeparator('nuclide://host/')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.trimTrailingSeparator('nuclide://host//')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.trimTrailingSeparator('nuclide://host/')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.trimTrailingSeparator('nuclide://host//')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.trimTrailingSeparator('nuclide://host/abc')).toBe( - 'nuclide://host/abc', - ); - expect(nuclideUri.trimTrailingSeparator('nuclide://host/abc/')).toBe( - 'nuclide://host/abc', - ); - expect(nuclideUri.trimTrailingSeparator('nuclide://host/abc/def')).toBe( - 'nuclide://host/abc/def', - ); - expect(nuclideUri.trimTrailingSeparator('nuclide://host/abc/def/')).toBe( - 'nuclide://host/abc/def', - ); - expect(nuclideUri.trimTrailingSeparator('C:\\')).toBe('C:\\'); - expect(nuclideUri.trimTrailingSeparator('C:\\\\')).toBe('C:\\'); - expect(nuclideUri.trimTrailingSeparator('C:\\abc')).toBe('C:\\abc'); - expect(nuclideUri.trimTrailingSeparator('C:\\abc\\')).toBe('C:\\abc'); - expect(nuclideUri.trimTrailingSeparator('C:\\abc\\def')).toBe( - 'C:\\abc\\def', - ); - expect(nuclideUri.trimTrailingSeparator('C:\\abc\\def\\')).toBe( - 'C:\\abc\\def', - ); - expect(nuclideUri.trimTrailingSeparator('\\')).toBe('\\'); - expect(nuclideUri.trimTrailingSeparator('\\\\')).toBe('\\'); - expect(nuclideUri.trimTrailingSeparator('\\abc\\def')).toBe('\\abc\\def'); - expect(nuclideUri.trimTrailingSeparator('\\abc\\def\\')).toBe('\\abc\\def'); - expect(() => nuclideUri.trimTrailingSeparator(archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.trimTrailingSeparator('/')).toBe('/'); + expect(_nuclideUri().default.trimTrailingSeparator('//')).toBe('/'); + expect(_nuclideUri().default.trimTrailingSeparator('/abc')).toBe('/abc'); + expect(_nuclideUri().default.trimTrailingSeparator('/abc/')).toBe('/abc'); + expect(_nuclideUri().default.trimTrailingSeparator('/abc/def')).toBe('/abc/def'); + expect(_nuclideUri().default.trimTrailingSeparator('/abc/def/')).toBe('/abc/def'); + expect(_nuclideUri().default.trimTrailingSeparator('nuclide://host/')).toBe('nuclide://host/'); + expect(_nuclideUri().default.trimTrailingSeparator('nuclide://host//')).toBe('nuclide://host/'); + expect(_nuclideUri().default.trimTrailingSeparator('nuclide://host/')).toBe('nuclide://host/'); + expect(_nuclideUri().default.trimTrailingSeparator('nuclide://host//')).toBe('nuclide://host/'); + expect(_nuclideUri().default.trimTrailingSeparator('nuclide://host/abc')).toBe('nuclide://host/abc'); + expect(_nuclideUri().default.trimTrailingSeparator('nuclide://host/abc/')).toBe('nuclide://host/abc'); + expect(_nuclideUri().default.trimTrailingSeparator('nuclide://host/abc/def')).toBe('nuclide://host/abc/def'); + expect(_nuclideUri().default.trimTrailingSeparator('nuclide://host/abc/def/')).toBe('nuclide://host/abc/def'); + expect(_nuclideUri().default.trimTrailingSeparator('C:\\')).toBe('C:\\'); + expect(_nuclideUri().default.trimTrailingSeparator('C:\\\\')).toBe('C:\\'); + expect(_nuclideUri().default.trimTrailingSeparator('C:\\abc')).toBe('C:\\abc'); + expect(_nuclideUri().default.trimTrailingSeparator('C:\\abc\\')).toBe('C:\\abc'); + expect(_nuclideUri().default.trimTrailingSeparator('C:\\abc\\def')).toBe('C:\\abc\\def'); + expect(_nuclideUri().default.trimTrailingSeparator('C:\\abc\\def\\')).toBe('C:\\abc\\def'); + expect(_nuclideUri().default.trimTrailingSeparator('\\')).toBe('\\'); + expect(_nuclideUri().default.trimTrailingSeparator('\\\\')).toBe('\\'); + expect(_nuclideUri().default.trimTrailingSeparator('\\abc\\def')).toBe('\\abc\\def'); + expect(_nuclideUri().default.trimTrailingSeparator('\\abc\\def\\')).toBe('\\abc\\def'); + expect(() => _nuclideUri().default.trimTrailingSeparator(archiveSuffixUri)).toThrow(); }); - it('isAbsolute', () => { - expect(nuclideUri.isAbsolute('/abc')).toBe(true); - expect(nuclideUri.isAbsolute('/abc/def')).toBe(true); - expect(nuclideUri.isAbsolute('nuclide://host/')).toBe(true); - expect(nuclideUri.isAbsolute('nuclide://host/abc')).toBe(true); - expect(nuclideUri.isAbsolute('nuclide://host/abc/def')).toBe(true); - - expect(nuclideUri.isAbsolute('C:\\abc')).toBe(true); - expect(nuclideUri.isAbsolute('C:\\abc\\def')).toBe(true); - expect(nuclideUri.isAbsolute('\\abc')).toBe(true); - expect(nuclideUri.isAbsolute('\\abc\\def')).toBe(true); - - expect(nuclideUri.isAbsolute('abc')).toBe(false); - expect(nuclideUri.isAbsolute('abc/def')).toBe(false); - - expect(nuclideUri.isAbsolute('abc\\def')).toBe(false); - expect(() => nuclideUri.isAbsolute(archiveSuffixUri)).toThrow(); + expect(_nuclideUri().default.isAbsolute('/abc')).toBe(true); + expect(_nuclideUri().default.isAbsolute('/abc/def')).toBe(true); + expect(_nuclideUri().default.isAbsolute('nuclide://host/')).toBe(true); + expect(_nuclideUri().default.isAbsolute('nuclide://host/abc')).toBe(true); + expect(_nuclideUri().default.isAbsolute('nuclide://host/abc/def')).toBe(true); + expect(_nuclideUri().default.isAbsolute('C:\\abc')).toBe(true); + expect(_nuclideUri().default.isAbsolute('C:\\abc\\def')).toBe(true); + expect(_nuclideUri().default.isAbsolute('\\abc')).toBe(true); + expect(_nuclideUri().default.isAbsolute('\\abc\\def')).toBe(true); + expect(_nuclideUri().default.isAbsolute('abc')).toBe(false); + expect(_nuclideUri().default.isAbsolute('abc/def')).toBe(false); + expect(_nuclideUri().default.isAbsolute('abc\\def')).toBe(false); + expect(() => _nuclideUri().default.isAbsolute(archiveSuffixUri)).toThrow(); }); - it('resolve', () => { - expect(nuclideUri.resolve('/abc')).toBe('/abc'); - expect(nuclideUri.resolve('/abc', '..')).toBe('/'); - expect(nuclideUri.resolve('/abc', '..', '..')).toBe('/'); - expect(nuclideUri.resolve('/abc', '../..')).toBe('/'); - - expect(nuclideUri.resolve('/abc/def')).toBe('/abc/def'); - expect(nuclideUri.resolve('/abc/def', 'ghi')).toBe('/abc/def/ghi'); - expect(nuclideUri.resolve('/abc/def', '..', 'ghi')).toBe('/abc/ghi'); - expect(nuclideUri.resolve('/abc/def', '../ghi')).toBe('/abc/ghi'); - expect(nuclideUri.resolve('/abc/def', '/ghi')).toBe('/ghi'); - - expect(nuclideUri.resolve('/z.zip!abc')).toBe('/z.zip!abc'); - expect(nuclideUri.resolve('/z.zip!abc', '..')).toBe('/z.zip'); - expect(nuclideUri.resolve('/z.zip!abc', '..', '..')).toBe('/'); - expect(nuclideUri.resolve('/z.zip!abc', '../..')).toBe('/'); - - expect(nuclideUri.resolve('/z.zip!abc', 'def')).toBe('/z.zip!abc/def'); - expect(nuclideUri.resolve('/z.zip!abc', '..', 'def')).toBe('/z.zip!def'); - expect(nuclideUri.resolve('/z.zip!abc', '../def')).toBe('/z.zip!def'); - expect(nuclideUri.resolve('/z.zip!abc', '/def')).toBe('/def'); - - expect(nuclideUri.resolve('/dir/file!abc')).toBe('/dir/file!abc'); - expect(nuclideUri.resolve('/dir/file!abc', '..')).toBe('/dir'); - expect(nuclideUri.resolve('/dir/file!abc', '..', '..')).toBe('/'); - expect(nuclideUri.resolve('/dir/file!abc', '../..')).toBe('/'); - - expect(nuclideUri.resolve('/dir/file!abc', 'def')).toBe( - '/dir/file!abc/def', - ); - expect(nuclideUri.resolve('/dir/file!abc', '..', 'def')).toBe('/dir/def'); - expect(nuclideUri.resolve('/dir/file!abc', '../def')).toBe('/dir/def'); - expect(nuclideUri.resolve('/dir/file!abc', '/def')).toBe('/def'); - - expect(nuclideUri.resolve('nuclide://host/')).toBe('nuclide://host/'); - expect(nuclideUri.resolve('nuclide://host/', '..')).toBe('nuclide://host/'); - expect(nuclideUri.resolve('nuclide://host/abc')).toBe('nuclide://host/abc'); - expect(nuclideUri.resolve('nuclide://host/abc', '..')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.resolve('nuclide://host/abc', '..', '..')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.resolve('nuclide://host/abc', '../..')).toBe( - 'nuclide://host/', - ); - expect(nuclideUri.resolve('nuclide://host/abc/def', 'ghi')).toBe( - 'nuclide://host/abc/def/ghi', - ); - expect(nuclideUri.resolve('nuclide://host/abc/def', '../ghi')).toBe( - 'nuclide://host/abc/ghi', - ); - expect(nuclideUri.resolve('nuclide://host/abc/def', '..', 'ghi')).toBe( - 'nuclide://host/abc/ghi', - ); - expect(nuclideUri.resolve('nuclide://host/abc/def', '/ghi')).toBe( - 'nuclide://host/ghi', - ); - - expect(nuclideUri.resolve('C:\\abc')).toBe('C:\\abc'); - expect(nuclideUri.resolve('C:\\abc', '..')).toBe('C:\\'); - expect(nuclideUri.resolve('C:\\abc', '..', '..')).toBe('C:\\'); - expect(nuclideUri.resolve('C:\\abc', '..\\..')).toBe('C:\\'); - expect(nuclideUri.resolve('C:\\abc', 'def')).toBe('C:\\abc\\def'); - expect(nuclideUri.resolve('C:\\abc', '..\\def')).toBe('C:\\def'); - expect(nuclideUri.resolve('C:\\abc', '..', 'def')).toBe('C:\\def'); - - expect(nuclideUri.resolve('\\abc', 'def')).toBe('\\abc\\def'); - expect(nuclideUri.resolve('\\abc', '..\\def')).toBe('\\def'); - expect(nuclideUri.resolve('\\abc', '..', 'def')).toBe('\\def'); + expect(_nuclideUri().default.resolve('/abc')).toBe('/abc'); + expect(_nuclideUri().default.resolve('/abc', '..')).toBe('/'); + expect(_nuclideUri().default.resolve('/abc', '..', '..')).toBe('/'); + expect(_nuclideUri().default.resolve('/abc', '../..')).toBe('/'); + expect(_nuclideUri().default.resolve('/abc/def')).toBe('/abc/def'); + expect(_nuclideUri().default.resolve('/abc/def', 'ghi')).toBe('/abc/def/ghi'); + expect(_nuclideUri().default.resolve('/abc/def', '..', 'ghi')).toBe('/abc/ghi'); + expect(_nuclideUri().default.resolve('/abc/def', '../ghi')).toBe('/abc/ghi'); + expect(_nuclideUri().default.resolve('/abc/def', '/ghi')).toBe('/ghi'); + expect(_nuclideUri().default.resolve('/z.zip!abc')).toBe('/z.zip!abc'); + expect(_nuclideUri().default.resolve('/z.zip!abc', '..')).toBe('/z.zip'); + expect(_nuclideUri().default.resolve('/z.zip!abc', '..', '..')).toBe('/'); + expect(_nuclideUri().default.resolve('/z.zip!abc', '../..')).toBe('/'); + expect(_nuclideUri().default.resolve('/z.zip!abc', 'def')).toBe('/z.zip!abc/def'); + expect(_nuclideUri().default.resolve('/z.zip!abc', '..', 'def')).toBe('/z.zip!def'); + expect(_nuclideUri().default.resolve('/z.zip!abc', '../def')).toBe('/z.zip!def'); + expect(_nuclideUri().default.resolve('/z.zip!abc', '/def')).toBe('/def'); + expect(_nuclideUri().default.resolve('/dir/file!abc')).toBe('/dir/file!abc'); + expect(_nuclideUri().default.resolve('/dir/file!abc', '..')).toBe('/dir'); + expect(_nuclideUri().default.resolve('/dir/file!abc', '..', '..')).toBe('/'); + expect(_nuclideUri().default.resolve('/dir/file!abc', '../..')).toBe('/'); + expect(_nuclideUri().default.resolve('/dir/file!abc', 'def')).toBe('/dir/file!abc/def'); + expect(_nuclideUri().default.resolve('/dir/file!abc', '..', 'def')).toBe('/dir/def'); + expect(_nuclideUri().default.resolve('/dir/file!abc', '../def')).toBe('/dir/def'); + expect(_nuclideUri().default.resolve('/dir/file!abc', '/def')).toBe('/def'); + expect(_nuclideUri().default.resolve('nuclide://host/')).toBe('nuclide://host/'); + expect(_nuclideUri().default.resolve('nuclide://host/', '..')).toBe('nuclide://host/'); + expect(_nuclideUri().default.resolve('nuclide://host/abc')).toBe('nuclide://host/abc'); + expect(_nuclideUri().default.resolve('nuclide://host/abc', '..')).toBe('nuclide://host/'); + expect(_nuclideUri().default.resolve('nuclide://host/abc', '..', '..')).toBe('nuclide://host/'); + expect(_nuclideUri().default.resolve('nuclide://host/abc', '../..')).toBe('nuclide://host/'); + expect(_nuclideUri().default.resolve('nuclide://host/abc/def', 'ghi')).toBe('nuclide://host/abc/def/ghi'); + expect(_nuclideUri().default.resolve('nuclide://host/abc/def', '../ghi')).toBe('nuclide://host/abc/ghi'); + expect(_nuclideUri().default.resolve('nuclide://host/abc/def', '..', 'ghi')).toBe('nuclide://host/abc/ghi'); + expect(_nuclideUri().default.resolve('nuclide://host/abc/def', '/ghi')).toBe('nuclide://host/ghi'); + expect(_nuclideUri().default.resolve('C:\\abc')).toBe('C:\\abc'); + expect(_nuclideUri().default.resolve('C:\\abc', '..')).toBe('C:\\'); + expect(_nuclideUri().default.resolve('C:\\abc', '..', '..')).toBe('C:\\'); + expect(_nuclideUri().default.resolve('C:\\abc', '..\\..')).toBe('C:\\'); + expect(_nuclideUri().default.resolve('C:\\abc', 'def')).toBe('C:\\abc\\def'); + expect(_nuclideUri().default.resolve('C:\\abc', '..\\def')).toBe('C:\\def'); + expect(_nuclideUri().default.resolve('C:\\abc', '..', 'def')).toBe('C:\\def'); + expect(_nuclideUri().default.resolve('\\abc', 'def')).toBe('\\abc\\def'); + expect(_nuclideUri().default.resolve('\\abc', '..\\def')).toBe('\\def'); + expect(_nuclideUri().default.resolve('\\abc', '..', 'def')).toBe('\\def'); }); - describe('expandHomeDir()', () => { it('expands ~ to HOME', () => { - expect(nuclideUri.expandHomeDir('~')).toBe(process.env.HOME); + expect(_nuclideUri().default.expandHomeDir('~')).toBe(process.env.HOME); }); - it('expands ~/ to HOME', () => { const HOME = process.env.HOME; expect(HOME).not.toBeNull(); - expect(nuclideUri.expandHomeDir('~/abc')).toBe( - path.posix.join(HOME, 'abc'), - ); + expect(_nuclideUri().default.expandHomeDir('~/abc')).toBe(_path.default.posix.join(HOME, 'abc')); }); - it('keeps ~def to ~def', () => { - expect(nuclideUri.expandHomeDir('~def')).toBe('~def'); + expect(_nuclideUri().default.expandHomeDir('~def')).toBe('~def'); }); - it('throws on illegal URIs', () => { - expect(() => nuclideUri.expandHomeDir(archiveSuffixUri)).toThrow(); + expect(() => _nuclideUri().default.expandHomeDir(archiveSuffixUri)).toThrow(); }); }); - it('detects Windows and Posix paths properly', () => { - expect(__TEST__._pathModuleFor('/')).toEqual(path.posix); - expect(__TEST__._pathModuleFor('/abc')).toEqual(path.posix); - expect(__TEST__._pathModuleFor('/abc/def')).toEqual(path.posix); - expect(__TEST__._pathModuleFor('/abc.txt')).toEqual(path.posix); - expect(__TEST__._pathModuleFor('nuclide://host')).toEqual(path.posix); - expect(__TEST__._pathModuleFor('nuclide://host/')).toEqual(path.posix); - expect(__TEST__._pathModuleFor('nuclide://host/abc')).toEqual(path.posix); - expect(__TEST__._pathModuleFor('nuclide://host/abc/def')).toEqual( - path.posix, - ); - expect(__TEST__._pathModuleFor('nuclide://host/abc/def.txt')).toEqual( - path.posix, - ); - expect(__TEST__._pathModuleFor('C:\\')).toEqual(path.win32); - expect(__TEST__._pathModuleFor('C:\\abc')).toEqual(path.win32); - expect(__TEST__._pathModuleFor('C:\\abc\\def')).toEqual(path.win32); - expect(__TEST__._pathModuleFor('C:\\abc\\def.txt')).toEqual(path.win32); - expect(__TEST__._pathModuleFor('D:\\abc\\aaa bbb')).toEqual(path.win32); - expect(__TEST__._pathModuleFor('\\abc\\def')).toEqual(path.win32); - - // Default to Posix - expect(__TEST__._pathModuleFor('abcdef')).toEqual(path.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('/')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('/abc')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('/abc/def')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('/abc.txt')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('nuclide://host')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('nuclide://host/')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('nuclide://host/abc')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('nuclide://host/abc/def')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('nuclide://host/abc/def.txt')).toEqual(_path.default.posix); + expect(_nuclideUri().__TEST__._pathModuleFor('C:\\')).toEqual(_path.default.win32); + expect(_nuclideUri().__TEST__._pathModuleFor('C:\\abc')).toEqual(_path.default.win32); + expect(_nuclideUri().__TEST__._pathModuleFor('C:\\abc\\def')).toEqual(_path.default.win32); + expect(_nuclideUri().__TEST__._pathModuleFor('C:\\abc\\def.txt')).toEqual(_path.default.win32); + expect(_nuclideUri().__TEST__._pathModuleFor('D:\\abc\\aaa bbb')).toEqual(_path.default.win32); + expect(_nuclideUri().__TEST__._pathModuleFor('\\abc\\def')).toEqual(_path.default.win32); // Default to Posix + + expect(_nuclideUri().__TEST__._pathModuleFor('abcdef')).toEqual(_path.default.posix); }); - it('properly converts file URIs to local paths', () => { - expect(nuclideUri.uriToNuclideUri('\\abc\\def')).toEqual(null); - expect(nuclideUri.uriToNuclideUri('file://somehost/file/path')).toEqual( - '/file/path', - ); - expect( - nuclideUri.uriToNuclideUri( - 'file:///foo/bar/buck-out/flavor%231%232%233%2Cabc', - ), - ).toEqual('/foo/bar/buck-out/flavor#1#2#3,abc'); - expect( - nuclideUri.uriToNuclideUri('file:///file/path/file_%25.ext'), - ).toEqual('/file/path/file_%.ext'); - expect(nuclideUri.uriToNuclideUri('file://C:\\some\\file\\path')).toEqual( - 'C:\\some\\file\\path', - ); + expect(_nuclideUri().default.uriToNuclideUri('\\abc\\def')).toEqual(null); + expect(_nuclideUri().default.uriToNuclideUri('file://somehost/file/path')).toEqual('/file/path'); + expect(_nuclideUri().default.uriToNuclideUri('file:///foo/bar/buck-out/flavor%231%232%233%2Cabc')).toEqual('/foo/bar/buck-out/flavor#1#2#3,abc'); + expect(_nuclideUri().default.uriToNuclideUri('file:///file/path/file_%25.ext')).toEqual('/file/path/file_%.ext'); + expect(_nuclideUri().default.uriToNuclideUri('file://C:\\some\\file\\path')).toEqual('C:\\some\\file\\path'); }); - it('properly converts local paths to file URIs', () => { - expect(nuclideUri.nuclideUriToUri('/foo/bar/file.ext')).toEqual( - 'file:///foo/bar/file.ext', - ); - expect( - nuclideUri.nuclideUriToUri('/foo/bar/buck-out/flavor#1#2#3,abc'), - ).toEqual('file:///foo/bar/buck-out/flavor%231%232%233%2Cabc'); - expect(nuclideUri.nuclideUriToUri('/file/path/file_%.ext')).toEqual( - 'file:///file/path/file_%25.ext', - ); + expect(_nuclideUri().default.nuclideUriToUri('/foo/bar/file.ext')).toEqual('file:///foo/bar/file.ext'); + expect(_nuclideUri().default.nuclideUriToUri('/foo/bar/buck-out/flavor#1#2#3,abc')).toEqual('file:///foo/bar/buck-out/flavor%231%232%233%2Cabc'); + expect(_nuclideUri().default.nuclideUriToUri('/file/path/file_%.ext')).toEqual('file:///file/path/file_%25.ext'); }); - it('properly handles backslash-containing remote URIs', () => { - expect(nuclideUri.getPath('nuclide://host/aaa\\bbb.txt')).toBe( - '/aaa\\bbb.txt', - ); - expect(nuclideUri.getPath('nuclide://host/dir/aaa\\bbb.txt')).toBe( - '/dir/aaa\\bbb.txt', - ); - expect(nuclideUri.getPath('nuclide://host/one\\two\\file.txt')).toBe( - '/one\\two\\file.txt', - ); + expect(_nuclideUri().default.getPath('nuclide://host/aaa\\bbb.txt')).toBe('/aaa\\bbb.txt'); + expect(_nuclideUri().default.getPath('nuclide://host/dir/aaa\\bbb.txt')).toBe('/dir/aaa\\bbb.txt'); + expect(_nuclideUri().default.getPath('nuclide://host/one\\two\\file.txt')).toBe('/one\\two\\file.txt'); }); - it('correctly distinguishes paths that refer to files in an archive', () => { - expect(nuclideUri.isInArchive('abc')).toBe(false); - expect(nuclideUri.isInArchive('/abc')).toBe(false); - expect(nuclideUri.isInArchive('nuclide://host/abc')).toBe(false); - expect(nuclideUri.isInArchive('abc.zip')).toBe(false); - expect(nuclideUri.isInArchive('abc.jar')).toBe(false); - expect(nuclideUri.isInArchive('abc.zip!def')).toBe(true); - expect(nuclideUri.isInArchive('abc.jar!def')).toBe(true); - expect(nuclideUri.isInArchive('/abc.zip!def')).toBe(true); - expect(nuclideUri.isInArchive('/abc.jar!def')).toBe(true); - expect(nuclideUri.isInArchive('C:\\abc.zip!def')).toBe(true); - expect(nuclideUri.isInArchive('C:\\abc.jar!def')).toBe(true); - expect(nuclideUri.isInArchive('nuclide://host/abc.zip!def')).toBe(true); - expect(nuclideUri.isInArchive('nuclide://host/abc.jar!def')).toBe(true); + expect(_nuclideUri().default.isInArchive('abc')).toBe(false); + expect(_nuclideUri().default.isInArchive('/abc')).toBe(false); + expect(_nuclideUri().default.isInArchive('nuclide://host/abc')).toBe(false); + expect(_nuclideUri().default.isInArchive('abc.zip')).toBe(false); + expect(_nuclideUri().default.isInArchive('abc.jar')).toBe(false); + expect(_nuclideUri().default.isInArchive('abc.zip!def')).toBe(true); + expect(_nuclideUri().default.isInArchive('abc.jar!def')).toBe(true); + expect(_nuclideUri().default.isInArchive('/abc.zip!def')).toBe(true); + expect(_nuclideUri().default.isInArchive('/abc.jar!def')).toBe(true); + expect(_nuclideUri().default.isInArchive('C:\\abc.zip!def')).toBe(true); + expect(_nuclideUri().default.isInArchive('C:\\abc.jar!def')).toBe(true); + expect(_nuclideUri().default.isInArchive('nuclide://host/abc.zip!def')).toBe(true); + expect(_nuclideUri().default.isInArchive('nuclide://host/abc.jar!def')).toBe(true); }); - it('reports first ancestor outside archive', () => { - expect(nuclideUri.ancestorOutsideArchive('abc')).toBe('abc'); - expect(nuclideUri.ancestorOutsideArchive('/abc')).toBe('/abc'); - expect(nuclideUri.ancestorOutsideArchive('nuclide://host/abc')).toBe( - 'nuclide://host/abc', - ); - expect(nuclideUri.ancestorOutsideArchive('abc.zip')).toBe('abc.zip'); - expect(nuclideUri.ancestorOutsideArchive('abc.jar')).toBe('abc.jar'); - expect(nuclideUri.ancestorOutsideArchive('abc.zip!def')).toBe('abc.zip'); - expect(nuclideUri.ancestorOutsideArchive('abc.jar!def')).toBe('abc.jar'); - expect(nuclideUri.ancestorOutsideArchive('/abc.zip!def')).toBe('/abc.zip'); - expect(nuclideUri.ancestorOutsideArchive('/abc.jar!def')).toBe('/abc.jar'); - expect(nuclideUri.ancestorOutsideArchive('C:\\abc.zip!def')).toBe( - 'C:\\abc.zip', - ); - expect(nuclideUri.ancestorOutsideArchive('C:\\abc.jar!def')).toBe( - 'C:\\abc.jar', - ); - expect( - nuclideUri.ancestorOutsideArchive('nuclide://host/abc.zip!def'), - ).toBe('nuclide://host/abc.zip'); - expect( - nuclideUri.ancestorOutsideArchive('nuclide://host/abc.jar!def'), - ).toBe('nuclide://host/abc.jar'); + expect(_nuclideUri().default.ancestorOutsideArchive('abc')).toBe('abc'); + expect(_nuclideUri().default.ancestorOutsideArchive('/abc')).toBe('/abc'); + expect(_nuclideUri().default.ancestorOutsideArchive('nuclide://host/abc')).toBe('nuclide://host/abc'); + expect(_nuclideUri().default.ancestorOutsideArchive('abc.zip')).toBe('abc.zip'); + expect(_nuclideUri().default.ancestorOutsideArchive('abc.jar')).toBe('abc.jar'); + expect(_nuclideUri().default.ancestorOutsideArchive('abc.zip!def')).toBe('abc.zip'); + expect(_nuclideUri().default.ancestorOutsideArchive('abc.jar!def')).toBe('abc.jar'); + expect(_nuclideUri().default.ancestorOutsideArchive('/abc.zip!def')).toBe('/abc.zip'); + expect(_nuclideUri().default.ancestorOutsideArchive('/abc.jar!def')).toBe('/abc.jar'); + expect(_nuclideUri().default.ancestorOutsideArchive('C:\\abc.zip!def')).toBe('C:\\abc.zip'); + expect(_nuclideUri().default.ancestorOutsideArchive('C:\\abc.jar!def')).toBe('C:\\abc.jar'); + expect(_nuclideUri().default.ancestorOutsideArchive('nuclide://host/abc.zip!def')).toBe('nuclide://host/abc.zip'); + expect(_nuclideUri().default.ancestorOutsideArchive('nuclide://host/abc.jar!def')).toBe('nuclide://host/abc.jar'); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/observable-test.js b/modules/nuclide-commons/__tests__/observable-test.js index 8a2d1f43..800b6b5e 100644 --- a/modules/nuclide-commons/__tests__/observable-test.js +++ b/modules/nuclide-commons/__tests__/observable-test.js @@ -1,3 +1,49 @@ +"use strict"; + +function _observable() { + const data = require("../observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _AbortController() { + const data = _interopRequireDefault(require("../AbortController")); + + _AbortController = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,45 +52,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +const setsAreEqual = (a, b) => a.size === b.size && Array.from(a).every(b.has.bind(b)); + +const diffsAreEqual = (a, b) => setsAreEqual(a.added, b.added) && setsAreEqual(a.removed, b.removed); -import type {AbortSignal} from '../AbortController'; - -import { - bufferUntil, - cacheWhileSubscribed, - completingSwitchMap, - mergeUntilAnyComplete, - concatLatest, - diffSets, - fastDebounce, - fromAbortablePromise, - macrotask, - microtask, - nextAnimationFrame, - poll, - reconcileSetDiffs, - SingletonExecutor, - splitStream, - takeUntilAbort, - takeWhileInclusive, - throttle, - toAbortablePromise, - toggle, -} from '../observable'; -import nullthrows from 'nullthrows'; -import AbortController from '../AbortController'; -import UniversalDisposable from '../UniversalDisposable'; -import {Observable, Subject} from 'rxjs'; - -const setsAreEqual = (a, b) => - a.size === b.size && Array.from(a).every(b.has.bind(b)); -const diffsAreEqual = (a, b) => - setsAreEqual(a.added, b.added) && setsAreEqual(a.removed, b.removed); const createDisposable = () => { - const disposable = new UniversalDisposable(); + const disposable = new (_UniversalDisposable().default)(); jest.spyOn(disposable, 'dispose'); return disposable; }; @@ -53,28 +69,25 @@ describe('nuclide-commons/observable', () => { describe('splitStream', () => { it('splits streams', async () => { const input = ['foo\nbar', '\n', '\nba', 'z', '\nblar']; - const output = await splitStream(Observable.from(input)) - .toArray() - .toPromise(); + const output = await (0, _observable().splitStream)(_RxMin.Observable.from(input)).toArray().toPromise(); expect(output).toEqual(['foo\n', 'bar\n', '\n', 'baz\n', 'blar']); }); - it('splits streams without the newline', async () => { const input = ['foo\nbar', '\n', '\nba', 'z', '\nblar']; - const output = await splitStream(Observable.from(input), false) - .toArray() - .toPromise(); + const output = await (0, _observable().splitStream)(_RxMin.Observable.from(input), false).toArray().toPromise(); expect(output).toEqual(['foo', 'bar', '', 'baz', 'blar']); }); }); - describe('takeWhileInclusive', () => { it('completes the stream when something matches the predicate', () => { - const source = new Subject(); - const result = source.let(takeWhileInclusive(x => x !== 2)); - const next: (n: number) => mixed = jest.fn(); - const complete: () => mixed = jest.fn(); - result.subscribe({next, complete}); + const source = new _RxMin.Subject(); + const result = source.let((0, _observable().takeWhileInclusive)(x => x !== 2)); + const next = jest.fn(); + const complete = jest.fn(); + result.subscribe({ + next, + complete + }); source.next(1); source.next(2); source.next(3); @@ -82,297 +95,283 @@ describe('nuclide-commons/observable', () => { expect(next.mock.calls.map(call => call[0])).toEqual([1, 2]); }); }); - describe('cacheWhileSubscribed', () => { - let input: Subject = (null: any); - let output: Observable = (null: any); + let input = null; + let output = null; - function subscribeArray(arr: Array): rxjs$ISubscription { + function subscribeArray(arr) { return output.subscribe(x => arr.push(x)); } + beforeEach(() => { - input = new Subject(); - output = cacheWhileSubscribed(input); + input = new _RxMin.Subject(); + output = (0, _observable().cacheWhileSubscribed)(input); }); - it('should provide cached values to late subscribers', () => { const arr1 = []; const arr2 = []; - input.next(0); const sub1 = subscribeArray(arr1); input.next(1); input.next(2); const sub2 = subscribeArray(arr2); - sub1.unsubscribe(); sub2.unsubscribe(); expect(arr1).toEqual([1, 2]); expect(arr2).toEqual([2]); }); - it('should not store stale events when everyone is unsubscribed', () => { const arr1 = []; const arr2 = []; - input.next(0); const sub1 = subscribeArray(arr1); input.next(1); sub1.unsubscribe(); - input.next(2); - const sub2 = subscribeArray(arr2); input.next(3); sub2.unsubscribe(); - expect(arr1).toEqual([1]); expect(arr2).toEqual([3]); }); }); - describe('diffSets', () => { it('emits a diff for the first item', async () => { - const source = new Subject(); - const diffsPromise = source - .let(diffSets()) - .toArray() - .toPromise(); + const source = new _RxMin.Subject(); + const diffsPromise = source.let((0, _observable().diffSets)()).toArray().toPromise(); source.next(new Set([1, 2, 3])); source.complete(); const diffs = await diffsPromise; expect(diffs.length).toBe(1); - expect( - diffsAreEqual(diffs[0], { - added: new Set([1, 2, 3]), - removed: new Set(), - }), - ).toBe(true); + expect(diffsAreEqual(diffs[0], { + added: new Set([1, 2, 3]), + removed: new Set() + })).toBe(true); }); - it('correctly identifies removed items', async () => { - const source = new Subject(); - const diffsPromise = source - .let(diffSets()) - .toArray() - .toPromise(); + const source = new _RxMin.Subject(); + const diffsPromise = source.let((0, _observable().diffSets)()).toArray().toPromise(); source.next(new Set([1, 2, 3])); source.next(new Set([1, 2])); source.complete(); const diffs = await diffsPromise; expect(setsAreEqual(diffs[1].removed, new Set([3]))).toBe(true); }); - it('correctly identifies removed items when a hash function is used', async () => { - const source = new Subject(); - const diffsPromise = source - .let(diffSets(x => x.key)) - .toArray() - .toPromise(); - const firstItems = [{key: 1}, {key: 2}, {key: 3}]; - const secondItems = [{key: 1}, {key: 2}]; + const source = new _RxMin.Subject(); + const diffsPromise = source.let((0, _observable().diffSets)(x => x.key)).toArray().toPromise(); + const firstItems = [{ + key: 1 + }, { + key: 2 + }, { + key: 3 + }]; + const secondItems = [{ + key: 1 + }, { + key: 2 + }]; source.next(new Set(firstItems)); source.next(new Set(secondItems)); source.complete(); const diffs = await diffsPromise; - expect(setsAreEqual(diffs[1].removed, new Set([firstItems[2]]))).toBe( - true, - ); + expect(setsAreEqual(diffs[1].removed, new Set([firstItems[2]]))).toBe(true); }); - it('correctly identifies added items', async () => { - const source = new Subject(); - const diffsPromise = source - .let(diffSets()) - .toArray() - .toPromise(); + const source = new _RxMin.Subject(); + const diffsPromise = source.let((0, _observable().diffSets)()).toArray().toPromise(); source.next(new Set([1, 2])); source.next(new Set([1, 2, 3])); source.complete(); const diffs = await diffsPromise; expect(setsAreEqual(diffs[1].added, new Set([3]))).toBe(true); }); - it('correctly identifies added items when a hash function is used', async () => { - const source = new Subject(); - const diffsPromise = source - .let(diffSets(x => x.key)) - .toArray() - .toPromise(); - const firstItems = [{key: 1}, {key: 2}]; - const secondItems = [{key: 1}, {key: 2}, {key: 3}]; + const source = new _RxMin.Subject(); + const diffsPromise = source.let((0, _observable().diffSets)(x => x.key)).toArray().toPromise(); + const firstItems = [{ + key: 1 + }, { + key: 2 + }]; + const secondItems = [{ + key: 1 + }, { + key: 2 + }, { + key: 3 + }]; source.next(new Set(firstItems)); source.next(new Set(secondItems)); source.complete(); const diffs = await diffsPromise; - expect(setsAreEqual(diffs[1].added, new Set([secondItems[2]]))).toBe( - true, - ); + expect(setsAreEqual(diffs[1].added, new Set([secondItems[2]]))).toBe(true); }); - it("doesn't emit a diff when nothing changes", async () => { - const source = new Subject(); - const diffsPromise = source - .let(diffSets()) - .toArray() - .toPromise(); + const source = new _RxMin.Subject(); + const diffsPromise = source.let((0, _observable().diffSets)()).toArray().toPromise(); source.next(new Set([1, 2, 3])); source.next(new Set([1, 2, 3])); source.complete(); - const diffs = await diffsPromise; - // Make sure we only get one diff (from the implicit initial empty set). + const diffs = await diffsPromise; // Make sure we only get one diff (from the implicit initial empty set). + expect(diffs.length).toBe(1); }); - it("doesn't emit a diff when nothing changes and a hash function is used", async () => { - const source = new Subject(); - const diffsPromise = source - .let(diffSets(x => x.key)) - .toArray() - .toPromise(); - const firstItems = [{key: 1}, {key: 2}, {key: 3}]; - const secondItems = [{key: 1}, {key: 2}, {key: 3}]; + const source = new _RxMin.Subject(); + const diffsPromise = source.let((0, _observable().diffSets)(x => x.key)).toArray().toPromise(); + const firstItems = [{ + key: 1 + }, { + key: 2 + }, { + key: 3 + }]; + const secondItems = [{ + key: 1 + }, { + key: 2 + }, { + key: 3 + }]; source.next(new Set(firstItems)); source.next(new Set(secondItems)); source.complete(); - const diffs = await diffsPromise; - // Make sure we only get one diff (from the implicit initial empty set). + const diffs = await diffsPromise; // Make sure we only get one diff (from the implicit initial empty set). + expect(diffs.length).toBe(1); }); }); - describe('reconcileSetDiffs', () => { it("calls the add action for each item that's added", () => { - const diffs = new Subject(); - const addAction = jest.fn().mockReturnValue(new UniversalDisposable()); - reconcileSetDiffs(diffs, addAction); + const diffs = new _RxMin.Subject(); + const addAction = jest.fn().mockReturnValue(new (_UniversalDisposable().default)()); + (0, _observable().reconcileSetDiffs)(diffs, addAction); diffs.next({ added: new Set(['a', 'b']), - removed: new Set(), + removed: new Set() }); expect(addAction.mock.calls.map(call => call[0])).toEqual(['a', 'b']); }); - it("disposes for each item that's removed", () => { - const diffs = new Subject(); + const diffs = new _RxMin.Subject(); const disposables = { a: createDisposable(), - b: createDisposable(), + b: createDisposable() }; + const addAction = item => disposables[item]; - reconcileSetDiffs(diffs, addAction); + + (0, _observable().reconcileSetDiffs)(diffs, addAction); diffs.next({ added: new Set(['a', 'b']), - removed: new Set(), + removed: new Set() }); diffs.next({ added: new Set(), - removed: new Set(['a', 'b']), + removed: new Set(['a', 'b']) }); expect(disposables.a.dispose).toHaveBeenCalled(); expect(disposables.b.dispose).toHaveBeenCalled(); }); - it('disposes for all items when disposed', () => { - const diffs = new Subject(); + const diffs = new _RxMin.Subject(); const disposables = { a: createDisposable(), - b: createDisposable(), + b: createDisposable() }; + const addAction = item => disposables[item]; - const reconciliationDisposable = reconcileSetDiffs(diffs, addAction); + + const reconciliationDisposable = (0, _observable().reconcileSetDiffs)(diffs, addAction); diffs.next({ added: new Set(['a', 'b']), - removed: new Set(), + removed: new Set() }); reconciliationDisposable.dispose(); expect(disposables.a.dispose).toHaveBeenCalled(); expect(disposables.b.dispose).toHaveBeenCalled(); }); - it("disposes for each item that's removed when a hash function is used", () => { - const diffs = new Subject(); + const diffs = new _RxMin.Subject(); const disposables = { a: createDisposable(), - b: createDisposable(), + b: createDisposable() }; + const addAction = item => disposables[item.key]; - reconcileSetDiffs(diffs, addAction, x => x.key); + + (0, _observable().reconcileSetDiffs)(diffs, addAction, x => x.key); diffs.next({ - added: new Set([{key: 'a'}, {key: 'b'}]), - removed: new Set(), + added: new Set([{ + key: 'a' + }, { + key: 'b' + }]), + removed: new Set() }); diffs.next({ added: new Set(), - removed: new Set([{key: 'a'}, {key: 'b'}]), + removed: new Set([{ + key: 'a' + }, { + key: 'b' + }]) }); expect(disposables.a.dispose).toHaveBeenCalled(); expect(disposables.b.dispose).toHaveBeenCalled(); }); }); - describe('toggle', () => { - let toggler: Subject = (null: any); - let source: Observable = (null: any); - let output: Observable = (null: any); - let outputArray: Array = (null: any); - + let toggler = null; + let source = null; + let output = null; + let outputArray = null; beforeEach(() => { - toggler = new Subject(); - // Deferred so individual 'it' blocks can set the source on the fly. - output = Observable.defer(() => source).let(toggle(toggler)); - }); + toggler = new _RxMin.Subject(); // Deferred so individual 'it' blocks can set the source on the fly. + output = _RxMin.Observable.defer(() => source).let((0, _observable().toggle)(toggler)); + }); describe('with a standard source', () => { - let realSource: Subject = (null: any); - + let realSource = null; beforeEach(() => { - source = realSource = new Subject(); + source = realSource = new _RxMin.Subject(); outputArray = []; output.subscribe(x => outputArray.push(x)); }); - it("should not emit anything before the toggler is set to 'true'", () => { realSource.next(5); expect(outputArray).toEqual([]); }); - it("should start emitting events when the toggler is set to 'true'", () => { toggler.next(true); realSource.next(5); expect(outputArray).toEqual([5]); }); - it("should stop emitting events when the toggler is set to 'false'", () => { toggler.next(true); toggler.next(false); realSource.next(4); expect(outputArray).toEqual([]); }); - }); - - // These ones are set apart from the rest because we want a cold observable to explicitly test + }); // These ones are set apart from the rest because we want a cold observable to explicitly test // that toggling off unsubscribes and then resubscribes. + describe('subscription behavior', () => { beforeEach(() => { - source = Observable.of(1, 2, 3); + source = _RxMin.Observable.of(1, 2, 3); outputArray = []; output.subscribe(x => outputArray.push(x)); }); - it('should unsubscribe and resusbscribe when toggled off and back on', () => { expect(outputArray).toEqual([]); - toggler.next(true); - expect(outputArray).toEqual([1, 2, 3]); - toggler.next(false); toggler.next(true); - expect(outputArray).toEqual([1, 2, 3, 1, 2, 3]); }); - it('should not re-subscribe on duplicate toggler values', () => { toggler.next(true); toggler.next(true); @@ -380,48 +379,39 @@ describe('nuclide-commons/observable', () => { }); }); }); - describe('concatLatest', () => { it('should work with empty input', async () => { - const output = await concatLatest() - .toArray() - .toPromise(); + const output = await (0, _observable().concatLatest)().toArray().toPromise(); expect(output).toEqual([]); }); - it('should work with several observables', async () => { - const output = await concatLatest( - Observable.of([], [1]), - Observable.of([2]), - Observable.of([3], [3, 4]), - ) - .toArray() - .toPromise(); + const output = await (0, _observable().concatLatest)(_RxMin.Observable.of([], [1]), _RxMin.Observable.of([2]), _RxMin.Observable.of([3], [3, 4])).toArray().toPromise(); expect(output).toEqual([[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]); }); }); - describe('throttle', () => { it('emits the leading item immeditately by default', () => { - const source = Observable.of(1, 2).merge(Observable.never()); + const source = _RxMin.Observable.of(1, 2).merge(_RxMin.Observable.never()); + const spy = jest.fn(); - source.let(throttle(Observable.never())).subscribe(spy); + source.let((0, _observable().throttle)(_RxMin.Observable.never())).subscribe(spy); expect(spy).toHaveBeenCalledWith(1); }); - it("doesn't emit the leading item twice", () => { - const source = Observable.of(1).merge(Observable.never()); - const notifier = Observable.of(null); // emits immediately on subscription. + const source = _RxMin.Observable.of(1).merge(_RxMin.Observable.never()); + + const notifier = _RxMin.Observable.of(null); // emits immediately on subscription. + + const spy = jest.fn(); - source.let(throttle(notifier)).subscribe(spy); + source.let((0, _observable().throttle)(notifier)).subscribe(spy); expect(spy.mock.calls.length).toBe(1); }); - it('throttles', () => { - const source = new Subject(); - const notifier = new Subject(); + const source = new _RxMin.Subject(); + const notifier = new _RxMin.Subject(); const spy = jest.fn(); - source.let(throttle(notifier)).subscribe(spy); + source.let((0, _observable().throttle)(notifier)).subscribe(spy); source.next(1); spy.mockClear(); source.next(2); @@ -437,15 +427,15 @@ describe('nuclide-commons/observable', () => { expect(spy).toHaveBeenCalledWith(4); expect(spy.mock.calls.length).toBe(1); }); - it('subscribes to the source once per subscription', () => { const spy = jest.fn(); - const source = Observable.create(spy); - source.let(throttle(Observable.of(null))).subscribe(); + + const source = _RxMin.Observable.create(spy); + + source.let((0, _observable().throttle)(_RxMin.Observable.of(null))).subscribe(); expect(spy.mock.calls.length).toBe(1); }); }); - describe('nextAnimationFrame', () => { let oldRequestAnimationFrame; let oldCancelAnimationFrame; @@ -455,141 +445,110 @@ describe('nuclide-commons/observable', () => { window.requestAnimationFrame = jest.fn(); window.cancelAnimationFrame = jest.fn(); }); - afterEach(() => { window.requestAnimationFrame = oldRequestAnimationFrame; window.cancelAnimationFrame = oldCancelAnimationFrame; }); - it('schedules next using requestAnimationFrame', () => { - const sub = nextAnimationFrame.subscribe(); + const sub = _observable().nextAnimationFrame.subscribe(); + expect(window.requestAnimationFrame).toHaveBeenCalled(); sub.unsubscribe(); }); - it('uses cancelAnimationFrame when unsubscribed', () => { - const sub = nextAnimationFrame.subscribe(); + const sub = _observable().nextAnimationFrame.subscribe(); + expect(window.cancelAnimationFrame).not.toHaveBeenCalled(); sub.unsubscribe(); expect(window.cancelAnimationFrame).toHaveBeenCalled(); }); }); - describe('bufferUntil', () => { it('buffers based on the predicate', async () => { - const chunks = await Observable.of(1, 2, 3, 4) - .let(bufferUntil(x => x % 2 === 0)) - .toArray() - .toPromise(); + const chunks = await _RxMin.Observable.of(1, 2, 3, 4).let((0, _observable().bufferUntil)(x => x % 2 === 0)).toArray().toPromise(); expect(chunks).toEqual([[1, 2], [3, 4]]); }); - it('provides the current buffer', async () => { - const chunks = await Observable.of(1, 2, 3, 4) - .let(bufferUntil((x, buffer) => buffer.length === 2)) - .toArray() - .toPromise(); + const chunks = await _RxMin.Observable.of(1, 2, 3, 4).let((0, _observable().bufferUntil)((x, buffer) => buffer.length === 2)).toArray().toPromise(); expect(chunks).toEqual([[1, 2], [3, 4]]); }); }); - describe('completingSwitchMap', () => { it('propagates completions to the inner observable', async () => { await (async () => { - const results = await Observable.of(1, 2) - .let( - completingSwitchMap(x => { - return Observable.concat( - Observable.of(x + 1), - Observable.never(), - ); - }), - ) - .toArray() - .toPromise(); + const results = await _RxMin.Observable.of(1, 2).let((0, _observable().completingSwitchMap)(x => { + return _RxMin.Observable.concat(_RxMin.Observable.of(x + 1), _RxMin.Observable.never()); + })).toArray().toPromise(); expect(results).toEqual([2, 3]); })(); }); }); - describe('mergeUntilAnyComplete', () => { it('completes when the first inner observable completes', async () => { const aDone = jest.fn(); const bDone = jest.fn(); - const a = Observable.timer(0, 10) - .mapTo('A') - .take(100) - .finally(aDone); - const b = Observable.timer(5, 10) - .mapTo('B') - .take(3) - .finally(bDone); - const results = await mergeUntilAnyComplete(a, b) - .toArray() - .toPromise(); + + const a = _RxMin.Observable.timer(0, 10).mapTo('A').take(100).finally(aDone); + + const b = _RxMin.Observable.timer(5, 10).mapTo('B').take(3).finally(bDone); + + const results = await (0, _observable().mergeUntilAnyComplete)(a, b).toArray().toPromise(); expect(results).toEqual(['A', 'B', 'A', 'B', 'A', 'B']); expect(aDone).toHaveBeenCalled(); expect(bDone).toHaveBeenCalled(); }); }); - describe('fastDebounce', () => { it('debounces events', async () => { let nextSpy; - const originalCreate = Observable.create.bind(Observable); - // Spy on the created observer's next to ensure that we always cancel + + const originalCreate = _RxMin.Observable.create.bind(_RxMin.Observable); // Spy on the created observer's next to ensure that we always cancel // the last debounced timer on unsubscribe. - jest.spyOn(Observable, 'create').mockImplementation(callback => { + + + jest.spyOn(_RxMin.Observable, 'create').mockImplementation(callback => { return originalCreate(observer => { nextSpy = jest.spyOn(observer, 'next'); return callback(observer); }); }); - - const subject = new Subject(); - const promise = subject - .let(fastDebounce(10)) - .toArray() - .toPromise(); - + const subject = new _RxMin.Subject(); + const promise = subject.let((0, _observable().fastDebounce)(10)).toArray().toPromise(); subject.next(1); subject.next(2); await sleep(20); - subject.next(3); await sleep(5); - subject.next(4); await sleep(15); - subject.next(5); subject.complete(); await sleep(20); - - expect(await promise).toEqual([2, 4]); - expect(nullthrows(nextSpy).mock.calls.length).toBe(2); + expect((await promise)).toEqual([2, 4]); + expect((0, _nullthrows().default)(nextSpy).mock.calls.length).toBe(2); }); - it('passes errors through immediately', () => { let caught = false; - Observable.throw(1) - .let(fastDebounce(10)) - .subscribe({ - error() { - caught = true; - }, - }); + + _RxMin.Observable.throw(1).let((0, _observable().fastDebounce)(10)).subscribe({ + error() { + caught = true; + } + + }); + expect(caught).toBe(true); }); }); - describe('microtask', () => { it('is cancelable', async () => { await (async () => { const spy = jest.fn(); - const sub = microtask.subscribe(spy); + + const sub = _observable().microtask.subscribe(spy); + let resolve; - const promise = new Promise(r => (resolve = r)); + const promise = new Promise(r => resolve = r); sub.unsubscribe(); process.nextTick(() => { expect(spy).not.toHaveBeenCalled(); @@ -599,228 +558,201 @@ describe('nuclide-commons/observable', () => { })(); }); }); - describe('macrotask', () => { it('is cancelable', () => { jest.spyOn(global, 'clearImmediate'); - const sub = macrotask.subscribe(() => {}); + + const sub = _observable().macrotask.subscribe(() => {}); + sub.unsubscribe(); expect(clearImmediate).toHaveBeenCalled(); }); }); - describe('fromAbortablePromise', () => { it('is able to cancel a promise after unsubscription', () => { const spy = jest.fn(); - function f(signal: AbortSignal) { + + function f(signal) { expect(signal.aborted).toBe(false); signal.onabort = spy; return new Promise(resolve => {}); } - const subscription = fromAbortablePromise(f).subscribe(); + + const subscription = (0, _observable().fromAbortablePromise)(f).subscribe(); subscription.unsubscribe(); expect(spy).toHaveBeenCalled(); }); - it('does not trigger an abort after normal completion', async () => { await (async () => { const spy = jest.fn(); - function f(signal: AbortSignal) { + + function f(signal) { signal.onabort = spy; return Promise.resolve(1); } - const result = await fromAbortablePromise(f).toPromise(); + + const result = await (0, _observable().fromAbortablePromise)(f).toPromise(); expect(result).toBe(1); expect(spy).not.toHaveBeenCalled(); })(); }); }); - describe('toAbortablePromise', () => { it('rejects with a DOMException on abort', async () => { - const controller = new AbortController(); + const controller = new (_AbortController().default)(); const spy = jest.fn(); - const promise = toAbortablePromise( - Observable.never(), - controller.signal, - ).catch(spy); + const promise = (0, _observable().toAbortablePromise)(_RxMin.Observable.never(), controller.signal).catch(spy); controller.abort(); await promise; - expect(spy).toHaveBeenCalled(); - const exception: any = spy.mock.calls[0][0]; + const exception = spy.mock.calls[0][0]; expect(exception.constructor.name).toBe('DOMException'); expect(exception.name).toBe('AbortError'); expect(exception.message).toBe('Aborted'); }); - describe('takeUntilAbort', () => { it('completes on abort', () => { - const controller = new AbortController(); - + const controller = new (_AbortController().default)(); const spy = jest.fn(); - Observable.never() - .let(obs => takeUntilAbort(obs, controller.signal)) - .subscribe({complete: spy}); + + _RxMin.Observable.never().let(obs => (0, _observable().takeUntilAbort)(obs, controller.signal)).subscribe({ + complete: spy + }); expect(spy).not.toHaveBeenCalled(); controller.abort(); expect(spy).toHaveBeenCalled(); }); - it('completes when already aborted', () => { - const controller = new AbortController(); + const controller = new (_AbortController().default)(); controller.abort(); - const spy = jest.fn(); - Observable.never() - .let(obs => takeUntilAbort(obs, controller.signal)) - .subscribe({complete: spy}); + + _RxMin.Observable.never().let(obs => (0, _observable().takeUntilAbort)(obs, controller.signal)).subscribe({ + complete: spy + }); expect(spy).toHaveBeenCalled(); }); }); - it('works with no signal', async () => { - const promise = toAbortablePromise(Observable.of(1)); - expect(await promise).toBe(1); + const promise = (0, _observable().toAbortablePromise)(_RxMin.Observable.of(1)); + expect((await promise)).toBe(1); }); }); - describe('SingletonExecutor', () => { it('isExecuting()', () => { - const executor = new SingletonExecutor(); + const executor = new (_observable().SingletonExecutor)(); expect(executor.isExecuting()).toBe(false); - - const source = new Subject(); + const source = new _RxMin.Subject(); const result = executor.execute(source); result.catch(() => 'silence unhandled promise rejection warning'); expect(executor.isExecuting()).toBe(true); - executor.cancel(); expect(executor.isExecuting()).toBe(false); }); - it('completing task normally', async () => { - const executor = new SingletonExecutor(); - const source = new Subject(); - + const executor = new (_observable().SingletonExecutor)(); + const source = new _RxMin.Subject(); const result = executor.execute(source); expect(executor.isExecuting()).toBe(true); - source.next(42); source.complete(); - expect(await result).toBe(42); + expect((await result)).toBe(42); expect(executor.isExecuting()).toBe(false); }); - it('completing task by error', async () => { - const executor = new SingletonExecutor(); - const source = new Subject(); - + const executor = new (_observable().SingletonExecutor)(); + const source = new _RxMin.Subject(); const result = executor.execute(source); expect(executor.isExecuting()).toBe(true); - source.error(42); let thrown = false; + try { await result; } catch (e) { expect(e).toBe(42); thrown = true; } + expect(executor.isExecuting()).toBe(false); expect(thrown).toBe(true); }); - it('scheduling second task while first is in flight', async () => { - const executor = new SingletonExecutor(); - - const source1 = new Subject(); + const executor = new (_observable().SingletonExecutor)(); + const source1 = new _RxMin.Subject(); const result1 = executor.execute(source1); expect(executor.isExecuting()).toBe(true); - - const source2 = new Subject(); + const source2 = new _RxMin.Subject(); const result2 = executor.execute(source2); expect(executor.isExecuting()).toBe(true); - let thrown = false; + try { await result1; } catch (e) { expect(e.name).toBe('AbortError'); thrown = true; } + expect(executor.isExecuting()).toBe(true); expect(thrown).toBe(true); - source2.next(42); source2.complete(); - - expect(await result2).toBe(42); + expect((await result2)).toBe(42); expect(executor.isExecuting()).toBe(false); }); }); - describe('poll', () => { beforeEach(() => {}); - it('subscribes to the observable synchronously', () => { - const source = Observable.never(); + const source = _RxMin.Observable.never(); + const spy = jest.spyOn(source, 'subscribe'); - const sub = source.let(poll(10)).subscribe(); + const sub = source.let((0, _observable().poll)(10)).subscribe(); expect(spy.mock.calls.length).toBe(1); sub.unsubscribe(); }); - it('resubscribes when complete', async () => { let mostRecentObserver; - const source = Observable.create(observer => { + + const source = _RxMin.Observable.create(observer => { mostRecentObserver = observer; }); + const spy = jest.spyOn(source, 'subscribe'); - const sub = source.let(poll(10)).subscribe(); + const sub = source.let((0, _observable().poll)(10)).subscribe(); expect(spy.mock.calls.length).toBe(1); - (mostRecentObserver: any).next(); - - // Even though we're waiting longer than the delay, it hasn't completed yet so we shouldn't + mostRecentObserver.next(); // Even though we're waiting longer than the delay, it hasn't completed yet so we shouldn't // resubscribe. + await sleep(30); expect(spy.mock.calls.length).toBe(1); - (mostRecentObserver: any).complete(); - expect(spy.mock.calls.length).toBe(1); + mostRecentObserver.complete(); + expect(spy.mock.calls.length).toBe(1); // Now that the source has completed, we should subscribe again. - // Now that the source has completed, we should subscribe again. await sleep(30); expect(spy.mock.calls.length).toBe(2); sub.unsubscribe(); }); - it("doesn't resubscribe to the source when you unsubscribe", async () => { - const source = new Subject(); + const source = new _RxMin.Subject(); const spy = jest.spyOn(source, 'subscribe'); - source - .let(poll(10)) - .take(1) // This will unsubscribe after the first element. - .subscribe(); + source.let((0, _observable().poll)(10)).take(1) // This will unsubscribe after the first element. + .subscribe(); expect(spy.mock.calls.length).toBe(1); source.next(); await sleep(30); expect(spy.mock.calls.length).toBe(1); }); - it('polls synchronously completing observables', async () => { - const result = await Observable.of('hi') - .let(poll(10)) - .take(2) - .toArray() - .toPromise(); + const result = await _RxMin.Observable.of('hi').let((0, _observable().poll)(10)).take(2).toArray().toPromise(); expect(result).toEqual(['hi', 'hi']); }); }); }); -const sleep = n => - new Promise(resolve => { - setTimeout(resolve, n); - }); +const sleep = n => new Promise(resolve => { + setTimeout(resolve, n); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/process-test.js b/modules/nuclide-commons/__tests__/process-test.js index 751a16b8..1abc910f 100644 --- a/modules/nuclide-commons/__tests__/process-test.js +++ b/modules/nuclide-commons/__tests__/process-test.js @@ -1,3 +1,43 @@ +"use strict"; + +var _events = _interopRequireDefault(require("events")); + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("../promise"); + + _promise = function () { + return data; + }; + + return data; +} + +var _child_process = _interopRequireDefault(require("child_process")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _process() { + const data = require("../process"); + + _process = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,412 +46,351 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {ProcessExitMessage} from '../process'; - -import EventEmitter from 'events'; -import {getLogger} from 'log4js'; -import {sleep} from '../promise'; -import child_process from 'child_process'; -import invariant from 'assert'; -import {Observable, Scheduler, Subject} from 'rxjs'; - -import { - spawn, - getOutputStream, - killProcess, - killUnixProcessTree, - logStreamErrors, - observeProcess, - observeProcessRaw, - parsePsOutput, - preventStreamsFromThrowing, - ProcessSystemError, - runCommand, - runCommandDetailed, - scriptifyCommand, - exitEventToMessage, - LOG_CATEGORY, -} from '../process'; - -jest.mock('../performanceNow'); - +jest.mock("../performanceNow"); beforeEach(() => { jest.clearAllMocks(); }); - describe('commons-node/process', () => { let origPlatform; - beforeEach(() => { - origPlatform = process.platform; - // Use a fake platform so the platform's PATH is not used in case the test is run on a platform + origPlatform = process.platform; // Use a fake platform so the platform's PATH is not used in case the test is run on a platform // that requires special handling (like OS X). - Object.defineProperty(process, 'platform', {value: 'MockMock'}); - }); + Object.defineProperty(process, 'platform', { + value: 'MockMock' + }); + }); afterEach(() => { - Object.defineProperty(process, 'platform', {value: origPlatform}); + Object.defineProperty(process, 'platform', { + value: origPlatform + }); }); - describe('process.killProcess', () => { it('should only kill the process when `killTree` is false', async () => { const proc = { - kill: jasmine.createSpy(), + kill: jasmine.createSpy() }; jest.spyOn(console, 'log'); // suppress log printing - await killProcess((proc: any), false); + + await (0, _process().killProcess)(proc, false); expect(proc.kill).toHaveBeenCalled(); }); - it('should kill the process tree when `killTree` is true', async () => { // Create a tree that's more than level child deep. - const proc = child_process.spawn('bash', [ - '-c', - '( (sleep 1000)& sleep 1000 )& wait', - ]); + const proc = _child_process.default.spawn('bash', ['-c', '( (sleep 1000)& sleep 1000 )& wait']); + jest.spyOn(console, 'log'); // suppress log printing + jest.spyOn(process, 'kill'); - await sleep(250); // Give some time for the processes to spawn. - await killUnixProcessTree(proc); + await (0, _promise().sleep)(250); // Give some time for the processes to spawn. + + await (0, _process().killUnixProcessTree)(proc); expect(process.kill.mock.calls.length).toBeGreaterThan(2); }); - it('should kill the process tree on windows when `killTree` is true', async () => { const proc = { - pid: 123, + pid: 123 }; jest.spyOn(console, 'log'); // suppress log printing - Object.defineProperty(process, 'platform', {value: 'win32'}); - jest.spyOn(child_process, 'exec'); - await killProcess((proc: any), true); - expect(child_process.exec.mock.calls).toHaveLength(1); - expect(child_process.exec.mock.calls[0][0]).toBe( - `taskkill /pid ${proc.pid} /T /F`, - ); + + Object.defineProperty(process, 'platform', { + value: 'win32' + }); + jest.spyOn(_child_process.default, 'exec'); + await (0, _process().killProcess)(proc, true); + expect(_child_process.default.exec.mock.calls).toHaveLength(1); + expect(_child_process.default.exec.mock.calls[0][0]).toBe(`taskkill /pid ${proc.pid} /T /F`); }); }); - describe('process.parsePsOutput', () => { it('parse `ps` unix output', () => { - const unixPsOut = - ' PPID PID COMM\n' + - ' 0 1 /sbin/launchd\n' + - ' 1 42 command with spaces'; - const processList = parsePsOutput(unixPsOut); - expect(processList).toEqual([ - { - command: '/sbin/launchd', - pid: 1, - parentPid: 0, - commandWithArgs: '/sbin/launchd', - }, - { - command: 'command with spaces', - pid: 42, - parentPid: 1, - commandWithArgs: 'command with spaces', - }, - ]); + const unixPsOut = ' PPID PID COMM\n' + ' 0 1 /sbin/launchd\n' + ' 1 42 command with spaces'; + const processList = (0, _process().parsePsOutput)(unixPsOut); + expect(processList).toEqual([{ + command: '/sbin/launchd', + pid: 1, + parentPid: 0, + commandWithArgs: '/sbin/launchd' + }, { + command: 'command with spaces', + pid: 42, + parentPid: 1, + commandWithArgs: 'command with spaces' + }]); }); - it('parse `ps` unix output with command arguments', () => { - const unixPsOut = - ' PPID PID COMM\n' + - ' 0 1 /sbin/launchd\n' + - ' 1 42 command with spaces'; - - const unixPsOutWithArgs = - ' PID ARGS\n' + - ' 1 /sbin/launchd\n' + - ' 42 command with spaces and some more arguments'; - - const processList = parsePsOutput(unixPsOut, unixPsOutWithArgs); - expect(processList).toEqual([ - { - command: '/sbin/launchd', - pid: 1, - parentPid: 0, - commandWithArgs: '/sbin/launchd', - }, - { - command: 'command with spaces', - pid: 42, - parentPid: 1, - commandWithArgs: 'command with spaces and some more arguments', - }, - ]); + const unixPsOut = ' PPID PID COMM\n' + ' 0 1 /sbin/launchd\n' + ' 1 42 command with spaces'; + const unixPsOutWithArgs = ' PID ARGS\n' + ' 1 /sbin/launchd\n' + ' 42 command with spaces and some more arguments'; + const processList = (0, _process().parsePsOutput)(unixPsOut, unixPsOutWithArgs); + expect(processList).toEqual([{ + command: '/sbin/launchd', + pid: 1, + parentPid: 0, + commandWithArgs: '/sbin/launchd' + }, { + command: 'command with spaces', + pid: 42, + parentPid: 1, + commandWithArgs: 'command with spaces and some more arguments' + }]); }); - it('parse `ps` windows output', () => { - const windowsProcessOut = - 'ParentProcessId ProcessId Name\r\n' + - ' 0 4 System Process\r\n' + - ' 4 228 smss.exe'; - - const processList = parsePsOutput(windowsProcessOut); - expect(processList).toEqual([ - { - command: 'System Process', - pid: 4, - parentPid: 0, - commandWithArgs: 'System Process', - }, - { - command: 'smss.exe', - pid: 228, - parentPid: 4, - commandWithArgs: 'smss.exe', - }, - ]); + const windowsProcessOut = 'ParentProcessId ProcessId Name\r\n' + ' 0 4 System Process\r\n' + ' 4 228 smss.exe'; + const processList = (0, _process().parsePsOutput)(windowsProcessOut); + expect(processList).toEqual([{ + command: 'System Process', + pid: 4, + parentPid: 0, + commandWithArgs: 'System Process' + }, { + command: 'smss.exe', + pid: 228, + parentPid: 4, + commandWithArgs: 'smss.exe' + }]); }); }); - describe('getOutputStream', () => { it('captures stdout, stderr and exitCode', async () => { - const child = child_process.spawn(process.execPath, [ - '-e', - 'console.error("stderr"); console.log("std out"); process.exit(0);', - ]); - const results = await getOutputStream(child) - .toArray() - .toPromise(); - expect(results).toEqual([ - {kind: 'stderr', data: 'stderr\n'}, - {kind: 'stdout', data: 'std out\n'}, - {kind: 'exit', exitCode: 0, signal: null}, - ]); + const child = _child_process.default.spawn(process.execPath, ['-e', 'console.error("stderr"); console.log("std out"); process.exit(0);']); + + const results = await (0, _process().getOutputStream)(child).toArray().toPromise(); + expect(results).toEqual([{ + kind: 'stderr', + data: 'stderr\n' + }, { + kind: 'stdout', + data: 'std out\n' + }, { + kind: 'exit', + exitCode: 0, + signal: null + }]); }); - it('errors on nonzero exit codes by default', async () => { - const child = child_process.spawn(process.execPath, [ - '-e', - 'console.error("stderr"); console.log("std out"); process.exit(42);', - ]); - const results = await getOutputStream(child) - .materialize() - .toArray() - .toPromise(); - expect(results.map(notification => notification.kind)).toEqual([ - 'N', - 'N', - 'E', - ]); - const {error} = results[2]; + const child = _child_process.default.spawn(process.execPath, ['-e', 'console.error("stderr"); console.log("std out"); process.exit(42);']); + + const results = await (0, _process().getOutputStream)(child).materialize().toArray().toPromise(); + expect(results.map(notification => notification.kind)).toEqual(['N', 'N', 'E']); + const { + error + } = results[2]; expect(error.name).toBe('ProcessExitError'); expect(error.exitCode).toBe(42); expect(error.stderr).toBe('stderr\n'); }); - it('accumulates the first `exitErrorBufferSize` bytes of stderr for the exit error', async () => { let error; - const child = child_process.spawn(process.execPath, [ - '-e', - 'console.error("stderr"); process.exit(42);', - ]); + + const child = _child_process.default.spawn(process.execPath, ['-e', 'console.error("stderr"); process.exit(42);']); + try { - await getOutputStream(child, { + await (0, _process().getOutputStream)(child, { exitErrorBufferSize: 2, - isExitError: () => true, - }) - .toArray() - .toPromise(); + isExitError: () => true + }).toArray().toPromise(); } catch (err) { error = err; } + expect(error).toBeDefined(); - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.stderr).toBe('st'); }); }); - describe('spawn', () => { it('errors when the process does', async () => { jest.spyOn(console, 'log'); // suppress log printing - const processStream = spawn('fakeCommand', undefined, { - dontLogInNuclide: true, + + const processStream = (0, _process().spawn)('fakeCommand', undefined, { + dontLogInNuclide: true }); let error; + try { await processStream.toPromise(); } catch (err) { error = err; } + expect(error).toBeDefined(); - invariant(error); + + if (!error) { + throw new Error("Invariant violation: \"error\""); + } + expect(error.code).toBe('ENOENT'); expect(error.message).toBe('spawn fakeCommand ENOENT'); - }); - - // Node delays the emission of the error until after the process is returned so that you have a + }); // Node delays the emission of the error until after the process is returned so that you have a // chance to subscribe to the error event. Observables aren't bound by the same limitations as // event-emitter APIs, so we can do better and not emit the process if there was an error // spawning it. + it('errors before emitting the process', async () => { jest.spyOn(console, 'log'); // suppress log printing + let proc; - await spawn('fakeCommand', undefined, {dontLogInNuclide: true}) - .do(p => { - proc = p; - }) - .catch(err => { - expect(proc).toBeUndefined(); - expect(err.code).toBe('ENOENT'); - expect(err.message).toBe('spawn fakeCommand ENOENT'); - return Observable.empty(); - }) - .toPromise(); + await (0, _process().spawn)('fakeCommand', undefined, { + dontLogInNuclide: true + }).do(p => { + proc = p; + }).catch(err => { + expect(proc).toBeUndefined(); + expect(err.code).toBe('ENOENT'); + expect(err.message).toBe('spawn fakeCommand ENOENT'); + return _RxMin.Observable.empty(); + }).toPromise(); }); - it('leaves an error handler when you unsubscribe', async () => { jest.spyOn(console, 'log'); // suppress log printing + let resolve; const promise = new Promise(r => { resolve = r; }); - const sub = spawn('cat', undefined, {dontLogInNuclide: true}) - // If we subscribe synchronously, and it emits synchronously, `sub` won't have been - // assigned yet in our `subscribe()` callback, so we use the async scheduler. - .subscribeOn(Scheduler.async) - .subscribe(proc => { - // As soon as we have a process, unsubscribe. This will happen before the error is - // thrown. - sub.unsubscribe(); - - // Make sure that the error handler is still registered. If it isn't, and the process - // errors, node will consider the error unhandled and we'll get a redbox. - expect(proc.listenerCount('error')).toBe(1); - - resolve(); - }); + const sub = (0, _process().spawn)('cat', undefined, { + dontLogInNuclide: true + }) // If we subscribe synchronously, and it emits synchronously, `sub` won't have been + // assigned yet in our `subscribe()` callback, so we use the async scheduler. + .subscribeOn(_RxMin.Scheduler.async).subscribe(proc => { + // As soon as we have a process, unsubscribe. This will happen before the error is + // thrown. + sub.unsubscribe(); // Make sure that the error handler is still registered. If it isn't, and the process + // errors, node will consider the error unhandled and we'll get a redbox. + + expect(proc.listenerCount('error')).toBe(1); + resolve(); + }); await promise; }); - it('can be retried', async () => { jest.spyOn(console, 'log'); // suppress log printing - jest.spyOn(child_process, 'spawn'); + + jest.spyOn(_child_process.default, 'spawn'); + try { - await spawn('fakeCommand', undefined, {dontLogInNuclide: true}) - .retryWhen(errors => - errors.scan((errorCount, err) => { - // If this is the third time the process has errored (i.e. the have already been - // two errors before), stop retrying. (We try 3 times because because Rx 3 and 4 - // have bugs with retrying shared observables that would give false negatives for - // this test if we only tried twice.) - if (errorCount === 2) { - throw err; - } - return errorCount + 1; - }, 0), - ) - .toPromise(); + await (0, _process().spawn)('fakeCommand', undefined, { + dontLogInNuclide: true + }).retryWhen(errors => errors.scan((errorCount, err) => { + // If this is the third time the process has errored (i.e. the have already been + // two errors before), stop retrying. (We try 3 times because because Rx 3 and 4 + // have bugs with retrying shared observables that would give false negatives for + // this test if we only tried twice.) + if (errorCount === 2) { + throw err; + } + + return errorCount + 1; + }, 0)).toPromise(); } catch (err) {} - expect(child_process.spawn.mock.calls).toHaveLength(3); - }); + expect(_child_process.default.spawn.mock.calls).toHaveLength(3); + }); it('can be timed out', async () => { let error; let proc; + try { - await spawn('sleep', ['10000'], {timeout: 1}) - .do(p => { - proc = p; - jest.spyOn(proc, 'kill'); - }) - .toPromise(); + await (0, _process().spawn)('sleep', ['10000'], { + timeout: 1 + }).do(p => { + proc = p; + jest.spyOn(proc, 'kill'); + }).toPromise(); } catch (err) { error = err; } - invariant(proc != null); - invariant(error != null); + + if (!(proc != null)) { + throw new Error("Invariant violation: \"proc != null\""); + } + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.name).toBe('ProcessTimeoutError'); expect(proc.kill).toHaveBeenCalled(); }); }); - describe('observeProcess', () => { it('errors when the process does', async () => { jest.spyOn(console, 'log'); // suppress log printing - const processStream = observeProcess('fakeCommand', []); + + const processStream = (0, _process().observeProcess)('fakeCommand', []); let error; + try { await processStream.toPromise(); } catch (err) { error = err; } + expect(error).toBeDefined(); - invariant(error); + + if (!error) { + throw new Error("Invariant violation: \"error\""); + } + expect(error.code).toBe('ENOENT'); expect(error.message).toBe('spawn fakeCommand ENOENT'); }); - it('errors on nonzero exit codes by default', async () => { - const results = await observeProcess(process.execPath, [ - '-e', - 'console.error("stderr"); console.log("std out"); process.exit(42);', - ]) - .materialize() - .toArray() - .toPromise(); - expect(results.map(notification => notification.kind)).toEqual([ - 'N', - 'N', - 'E', - ]); - const {error} = results[2]; + const results = await (0, _process().observeProcess)(process.execPath, ['-e', 'console.error("stderr"); console.log("std out"); process.exit(42);']).materialize().toArray().toPromise(); + expect(results.map(notification => notification.kind)).toEqual(['N', 'N', 'E']); + const { + error + } = results[2]; expect(error.name).toBe('ProcessExitError'); expect(error.exitCode).toBe(42); expect(error.stderr).toBe('stderr\n'); }); - it("doesn't get an exit message when there's an exit error", async () => { - const results = await observeProcess(process.execPath, [ - '-e', - 'process.exit(42);', - ]) - .materialize() - .toArray() - .toPromise(); + const results = await (0, _process().observeProcess)(process.execPath, ['-e', 'process.exit(42);']).materialize().toArray().toPromise(); expect(results.length).toBe(1); expect(results[0].kind).toBe('E'); }); - it('accumulates the first `exitErrorBufferSize` bytes of stderr for the exit error', async () => { let error; + try { - await observeProcess( - process.execPath, - ['-e', 'console.error("stderr"); process.exit(42);'], - {exitErrorBufferSize: 2, isExitError: () => true}, - ) - .toArray() - .toPromise(); + await (0, _process().observeProcess)(process.execPath, ['-e', 'console.error("stderr"); process.exit(42);'], { + exitErrorBufferSize: 2, + isExitError: () => true + }).toArray().toPromise(); } catch (err) { error = err; } + expect(error).toBeDefined(); - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.stderr).toBe('st'); }); }); - describe('observeProcessRaw', () => { it("doesn't split on line breaks", async () => { jest.spyOn(console, 'log'); // suppress log printing - const event = await observeProcessRaw(process.execPath, [ - '-e', - 'process.stdout.write("stdout1\\nstdout2\\n"); process.exit(1)', - ]) - .take(1) - .toPromise(); - invariant(event.kind === 'stdout'); + + const event = await (0, _process().observeProcessRaw)(process.execPath, ['-e', 'process.stdout.write("stdout1\\nstdout2\\n"); process.exit(1)']).take(1).toPromise(); + + if (!(event.kind === 'stdout')) { + throw new Error("Invariant violation: \"event.kind === 'stdout'\""); + } + expect(event.data).toBe('stdout1\nstdout2\n'); }); }); - describe('runCommand', () => { beforeEach(() => { // Suppress console spew. @@ -423,117 +402,118 @@ describe('commons-node/process', () => { } it('sends the stdin to the process', async () => { - const output = await runCommand('cat', [], { - input: 'hello', + const output = await (0, _process().runCommand)('cat', [], { + input: 'hello' }).toPromise(); expect(output).toBe('hello'); }); - it('sends a stream of stdin to the process', async () => { - const input = new Subject(); - const outputPromise = runCommand('cat', [], { - input, + const input = new _RxMin.Subject(); + const outputPromise = (0, _process().runCommand)('cat', [], { + input }).toPromise(); input.next('hello'); input.next(' '); input.next('world'); input.complete(); - expect(await outputPromise).toBe('hello world'); + expect((await outputPromise)).toBe('hello world'); }); - it('enforces maxBuffer', async () => { let error; + try { - await runCommand('yes', [], {maxBuffer: 100}).toPromise(); + await (0, _process().runCommand)('yes', [], { + maxBuffer: 100 + }).toPromise(); } catch (err) { error = err; } - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.message).toContain('maxBuffer'); }); - it('returns stdout of the running process', async () => { - const val = await runCommand('echo', ['-n', 'foo'], { - env: process.env, + const val = await (0, _process().runCommand)('echo', ['-n', 'foo'], { + env: process.env }).toPromise(); expect(val).toEqual('foo'); }); - it("throws an error if the process can't be spawned", async () => { let error; + try { - await runCommand('fakeCommand').toPromise(); + await (0, _process().runCommand)('fakeCommand').toPromise(); } catch (err) { error = err; } - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.code).toBe('ENOENT'); expect(error.message).toBe('spawn fakeCommand ENOENT'); }); - it('throws an error if the exit code !== 0', async () => { - const cmd = runCommand(process.execPath, ['-e', 'process.exit(1)']); + const cmd = (0, _process().runCommand)(process.execPath, ['-e', 'process.exit(1)']); await expect(cmd.toPromise()).rejects.toThrow('failed with exit code 1'); }); - it('includes stdout and stderr in ProcessExitErrors', async () => { let error; + try { - await runCommand(process.execPath, [ - '-e', - 'process.stderr.write("oopsy"); process.stdout.write("daisy"); process.exit(1)', - ]).toPromise(); + await (0, _process().runCommand)(process.execPath, ['-e', 'process.stderr.write("oopsy"); process.stdout.write("daisy"); process.exit(1)']).toPromise(); } catch (err) { error = err; } - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.name).toBe('ProcessExitError'); expect(error.stderr).toBe('oopsy'); expect(error.stdout).toBe('daisy'); }); - it('accumulates the stderr if the process exits with a non-zero code', async () => { let error; + try { - await runCommand(process.execPath, [ - '-e', - 'process.stderr.write("oopsy"); process.exit(1)', - ]).toPromise(); + await (0, _process().runCommand)(process.execPath, ['-e', 'process.stderr.write("oopsy"); process.exit(1)']).toPromise(); } catch (err) { error = err; } - invariant(error != null); - expect(error.stderr).toBe('oopsy'); - }); - // Previously we had a bug where we mutated the seed and subsequent subscriptions would use the + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + + expect(error.stderr).toBe('oopsy'); + }); // Previously we had a bug where we mutated the seed and subsequent subscriptions would use the // mutated value. + it("doesn't share a mutable seed (regression test)", async () => { - const observable = runCommand(process.execPath, [ - '-e', - 'process.stdout.write("hello"); process.exit(0)', - ]); + const observable = (0, _process().runCommand)(process.execPath, ['-e', 'process.stdout.write("hello"); process.exit(0)']); await observable.toPromise(); - expect(await observable.toPromise()).toBe('hello'); + expect((await observable.toPromise())).toBe('hello'); }); - describe('checkOutput compatibility', () => { if (origPlatform !== 'win32') { it('returns stdout of the running process', async () => { - const val = await runCommand('echo', ['-n', 'foo'], { - env: process.env, + const val = await (0, _process().runCommand)('echo', ['-n', 'foo'], { + env: process.env }).toPromise(); expect(val).toEqual('foo'); }); it('throws an error if the exit code !== 0', async () => { - await expect( - runCommand(process.execPath, ['-e', 'process.exit(1)']).toPromise(), - ).rejects.toThrow('failed with exit code 1'); + await expect((0, _process().runCommand)(process.execPath, ['-e', 'process.exit(1)']).toPromise()).rejects.toThrow('failed with exit code 1'); }); } }); }); - describe('runCommandDetailed', () => { beforeEach(() => { // Suppress console spew. @@ -545,93 +525,104 @@ describe('commons-node/process', () => { } it('sends the stdin to the process', async () => { - const output = await runCommandDetailed('cat', [], { - input: 'hello', + const output = await (0, _process().runCommandDetailed)('cat', [], { + input: 'hello' }).toPromise(); expect(output.stdout).toBe('hello'); }); - it('enforces maxBuffer', async () => { let error; + try { - await runCommandDetailed('yes', [], {maxBuffer: 100}).toPromise(); + await (0, _process().runCommandDetailed)('yes', [], { + maxBuffer: 100 + }).toPromise(); } catch (err) { error = err; } - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.message).toContain('maxBuffer'); }); - it('returns stdout, stderr, and the exit code of the running process', async () => { - const val = await runCommandDetailed(process.execPath, [ - '-e', - 'process.stdout.write("out"); process.stderr.write("err"); process.exit(0)', - ]).toPromise(); - expect(val).toEqual({stdout: 'out', stderr: 'err', exitCode: 0}); + const val = await (0, _process().runCommandDetailed)(process.execPath, ['-e', 'process.stdout.write("out"); process.stderr.write("err"); process.exit(0)']).toPromise(); + expect(val).toEqual({ + stdout: 'out', + stderr: 'err', + exitCode: 0 + }); }); - it("throws an error if the process can't be spawned", async () => { let error; + try { - await runCommandDetailed('fakeCommand').toPromise(); + await (0, _process().runCommandDetailed)('fakeCommand').toPromise(); } catch (err) { error = err; } - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.code).toBe('ENOENT'); expect(error.message).toBe('spawn fakeCommand ENOENT'); }); - it('throws an error if the exit code !== 0', async () => { let error; + try { - await runCommandDetailed(process.execPath, [ - '-e', - 'process.exit(1)', - ]).toPromise(); + await (0, _process().runCommandDetailed)(process.execPath, ['-e', 'process.exit(1)']).toPromise(); } catch (err) { error = err; } - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.name).toBe('ProcessExitError'); expect(error.exitCode).toBe(1); }); - it('accumulates the stderr if the process exits with a non-zero code', async () => { let error; + try { - await runCommandDetailed(process.execPath, [ - '-e', - 'process.stderr.write("oopsy"); process.exit(1)', - ]).toPromise(); + await (0, _process().runCommandDetailed)(process.execPath, ['-e', 'process.stderr.write("oopsy"); process.exit(1)']).toPromise(); } catch (err) { error = err; } - invariant(error != null); + + if (!(error != null)) { + throw new Error("Invariant violation: \"error != null\""); + } + expect(error.stderr).toBe('oopsy'); }); }); - describe('exitEventToMessage', () => { it('exitCode', () => { - expect(exitEventToMessage(makeExitMessage(1))).toBe('exit code 1'); + expect((0, _process().exitEventToMessage)(makeExitMessage(1))).toBe('exit code 1'); }); - it('signal', () => { - expect( - exitEventToMessage({kind: 'exit', exitCode: null, signal: 'SIGTERM'}), - ).toBe('signal SIGTERM'); + expect((0, _process().exitEventToMessage)({ + kind: 'exit', + exitCode: null, + signal: 'SIGTERM' + })).toBe('signal SIGTERM'); }); }); - describe('preventStreamsFromThrowing', () => { - let proc: child_process$ChildProcess; + let proc; beforeEach(() => { - proc = ({ - stdin: new EventEmitter(), - stdout: new EventEmitter(), - stderr: new EventEmitter(), - }: any); + proc = { + stdin: new _events.default(), + stdout: new _events.default(), + stderr: new _events.default() + }; jest.spyOn(proc.stdin, 'addListener'); jest.spyOn(proc.stdout, 'addListener'); jest.spyOn(proc.stderr, 'addListener'); @@ -639,81 +630,56 @@ describe('commons-node/process', () => { jest.spyOn(proc.stdout, 'removeListener'); jest.spyOn(proc.stderr, 'removeListener'); }); - it('adds listeners', () => { - preventStreamsFromThrowing(proc); - expect(proc.stdin.addListener).toHaveBeenCalledWith( - 'error', - jasmine.any(Function), - ); - expect(proc.stdout.addListener).toHaveBeenCalledWith( - 'error', - jasmine.any(Function), - ); - expect(proc.stderr.addListener).toHaveBeenCalledWith( - 'error', - jasmine.any(Function), - ); + (0, _process().preventStreamsFromThrowing)(proc); + expect(proc.stdin.addListener).toHaveBeenCalledWith('error', jasmine.any(Function)); + expect(proc.stdout.addListener).toHaveBeenCalledWith('error', jasmine.any(Function)); + expect(proc.stderr.addListener).toHaveBeenCalledWith('error', jasmine.any(Function)); }); - it('removes listeners when disposed', () => { - const disposable = preventStreamsFromThrowing(proc); + const disposable = (0, _process().preventStreamsFromThrowing)(proc); disposable.dispose(); - expect(proc.stdin.removeListener).toHaveBeenCalledWith( - 'error', - jasmine.any(Function), - ); - expect(proc.stdout.removeListener).toHaveBeenCalledWith( - 'error', - jasmine.any(Function), - ); - expect(proc.stderr.removeListener).toHaveBeenCalledWith( - 'error', - jasmine.any(Function), - ); + expect(proc.stdin.removeListener).toHaveBeenCalledWith('error', jasmine.any(Function)); + expect(proc.stdout.removeListener).toHaveBeenCalledWith('error', jasmine.any(Function)); + expect(proc.stderr.removeListener).toHaveBeenCalledWith('error', jasmine.any(Function)); }); }); - describe('logStreamErrors', () => { - const logger = getLogger(LOG_CATEGORY); - let proc: child_process$ChildProcess; + const logger = (0, _log4js().getLogger)(_process().LOG_CATEGORY); + let proc; beforeEach(() => { - proc = ({ - stdin: new EventEmitter(), - stdout: new EventEmitter(), - stderr: new EventEmitter(), - }: any); + proc = { + stdin: new _events.default(), + stdout: new _events.default(), + stderr: new _events.default() + }; // Add a no-op listener so the error events aren't thrown. - // Add a no-op listener so the error events aren't thrown. proc.stdin.on('error', () => {}); proc.stdout.on('error', () => {}); proc.stderr.on('error', () => {}); }); - it('logs errors', () => { - logStreamErrors(proc, 'test', [], {}); + (0, _process().logStreamErrors)(proc, 'test', [], {}); proc.stderr.emit('error', new Error('Test error')); expect(logger.error).toHaveBeenCalled(); }); - it("doesn't log when disposed", () => { - const disposable = logStreamErrors(proc, 'test', [], {}); + const disposable = (0, _process().logStreamErrors)(proc, 'test', [], {}); disposable.dispose(); proc.stderr.emit('error', new Error('Test error')); expect(logger.error).not.toHaveBeenCalled(); }); }); - describe('ProcessSystemError', () => { it('contains the correct properties', () => { - const proc = (({}: any): child_process$ChildProcess); + const proc = {}; const originalError = { errno: 2, code: 'ETEST', path: 'path value', - syscall: 'syscall value', + syscall: 'syscall value' }; - const err = new ProcessSystemError(originalError, proc); + const err = new (_process().ProcessSystemError)(originalError, proc); expect(err.errno).toBe(2); expect(err.code).toBe('ETEST'); expect(err.path).toBe('path value'); @@ -721,28 +687,20 @@ describe('commons-node/process', () => { expect(err.process).toBe(proc); }); }); - describe('scriptifyCommand', () => { if (process.platform === 'linux') { it('escapes correctly on linux', async () => { - const output = await runCommand( - ...scriptifyCommand('echo', [ - 'a\\b c\\\\d e\\\\\\f g\\\\\\\\h "dubs" \'singles\'', - 'one two', - ]), - ).toPromise(); - expect(output.trim()).toBe( - 'a\\b c\\\\d e\\\\\\f g\\\\\\\\h "dubs" \'singles\' one two', - ); + const output = await (0, _process().runCommand)(...(0, _process().scriptifyCommand)('echo', ['a\\b c\\\\d e\\\\\\f g\\\\\\\\h "dubs" \'singles\'', 'one two'])).toPromise(); + expect(output.trim()).toBe('a\\b c\\\\d e\\\\\\f g\\\\\\\\h "dubs" \'singles\' one two'); }); } }); }); -function makeExitMessage(exitCode: number): ProcessExitMessage { +function makeExitMessage(exitCode) { return { kind: 'exit', exitCode, - signal: null, + signal: null }; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/promise-test.js b/modules/nuclide-commons/__tests__/promise-test.js index 4a295f5f..4d0c2855 100644 --- a/modules/nuclide-commons/__tests__/promise-test.js +++ b/modules/nuclide-commons/__tests__/promise-test.js @@ -1,3 +1,37 @@ +"use strict"; + +function _promise() { + const data = require("../promise"); + + _promise = function () { + return data; + }; + + return data; +} + +function _testHelpers() { + const data = require("../test-helpers"); + + _testHelpers = function () { + return data; + }; + + return data; +} + +function _waits_for() { + const data = _interopRequireDefault(require("../../../jest/waits_for")); + + _waits_for = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +40,12 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-disable prefer-promise-reject-errors */ - -import { - asyncFind, - denodeify, - serializeAsyncCall, - asyncLimit, - asyncFilter, - asyncObjFilter, - asyncSome, - lastly, - retryLimit, - RequestSerializer, - TimedOutError, - timeoutPromise, -} from '../promise'; -import invariant from 'assert'; -import {expectAsyncFailure} from '../test-helpers'; -import waitsFor from '../../../jest/waits_for'; - jest.useFakeTimers(); - describe('promises::asyncFind()', () => { beforeEach(() => { jest.useRealTimers(); @@ -42,37 +56,32 @@ describe('promises::asyncFind()', () => { let observedResult; let isRejected = false; let observedError; - const args = []; + const test = value => { throw new Error('Should not be called.'); }; - asyncFind(args, test) - .then(result => { - observedResult = result; - isResolved = true; - }) - .catch(error => { - observedError = error; - isRejected = true; - }); - - await waitsFor(() => isResolved || isRejected); - + (0, _promise().asyncFind)(args, test).then(result => { + observedResult = result; + isResolved = true; + }).catch(error => { + observedError = error; + isRejected = true; + }); + await (0, _waits_for().default)(() => isResolved || isRejected); expect(isResolved).toBe(true); expect(observedResult).toBe(null); expect(isRejected).toBe(false); expect(observedError).toBe(undefined); }); - it('Last item in list resolves.', async () => { let isResolved = false; let observedResult; let isRejected = false; let observedError; - const args = ['foo', 'bar', 'baz']; + const test = value => { if (value === 'foo') { return null; @@ -83,25 +92,20 @@ describe('promises::asyncFind()', () => { } }; - asyncFind(args, test) - .then(result => { - observedResult = result; - isResolved = true; - }) - .catch(error => { - observedError = error; - isRejected = true; - }); - - await waitsFor(() => isResolved || isRejected); - + (0, _promise().asyncFind)(args, test).then(result => { + observedResult = result; + isResolved = true; + }).catch(error => { + observedError = error; + isRejected = true; + }); + await (0, _waits_for().default)(() => isResolved || isRejected); expect(isResolved).toBe(true); expect(observedResult).toBe('win'); expect(isRejected).toBe(false); expect(observedError).toBe(undefined); }); }); - describe('promises::denodeify()', () => { /** * Vararg function that assumes that all elements except the last are @@ -115,7 +119,7 @@ describe('promises::denodeify()', () => { * rather than at the end. The type signature of this function cannot be * expressed in Flow. */ - function asyncProduct(...factors): void { + function asyncProduct(...factors) { const callback = factors.pop(); const product = factors.reduce((previousValue, currentValue) => { return previousValue * currentValue; @@ -129,22 +133,17 @@ describe('promises::denodeify()', () => { } it('resolves Promise when callback succeeds', async () => { - const denodeifiedAsyncProduct = denodeify(asyncProduct); + const denodeifiedAsyncProduct = (0, _promise().denodeify)(asyncProduct); const trivialProduct = await denodeifiedAsyncProduct(); expect(trivialProduct).toBe(1); - const product = await denodeifiedAsyncProduct(1, 2, 3, 4, 5); expect(product).toBe(120); }); - it('rejects Promise when callback fails', async () => { - const denodeifiedAsyncProduct = denodeify(asyncProduct); - await expectAsyncFailure( - denodeifiedAsyncProduct('a', 'b'), - (error: Error) => { - expect(error.message).toBe('product was NaN'); - }, - ); + const denodeifiedAsyncProduct = (0, _promise().denodeify)(asyncProduct); + await (0, _testHelpers().expectAsyncFailure)(denodeifiedAsyncProduct('a', 'b'), error => { + expect(error.message).toBe('product was NaN'); + }); }); function checksReceiver(expectedReceiver, callback) { @@ -156,448 +155,393 @@ describe('promises::denodeify()', () => { } it('result of denodeify propagates receiver as expected', async () => { - const denodeifiedChecksReceiver = denodeify(checksReceiver); - - const receiver = {denodeifiedChecksReceiver}; + const denodeifiedChecksReceiver = (0, _promise().denodeify)(checksReceiver); + const receiver = { + denodeifiedChecksReceiver + }; const result = await receiver.denodeifiedChecksReceiver(receiver); expect(result).toBe('winner'); { - const receiver2 = {denodeifiedChecksReceiver}; - await expectAsyncFailure( - receiver2.denodeifiedChecksReceiver(null), - (error: Error) => { - expect(error.message).toBe('unexpected receiver'); - }, - ); + const receiver2 = { + denodeifiedChecksReceiver + }; + await (0, _testHelpers().expectAsyncFailure)(receiver2.denodeifiedChecksReceiver(null), error => { + expect(error.message).toBe('unexpected receiver'); + }); } }); }); - describe('promises::serializeAsyncCall()', () => { it('Returns the same result when called after scheduled', async () => { jest.useRealTimers(); let i = 0; const asyncFunSpy = jasmine.createSpy('async'); - const oneAsyncCallAtATime = serializeAsyncCall(() => { + const oneAsyncCallAtATime = (0, _promise().serializeAsyncCall)(() => { i++; const resultPromise = waitPromise(10, i); asyncFunSpy(); return resultPromise; - }); - // Start an async, and resolve to 1 in 10 ms. - const result1Promise = oneAsyncCallAtATime(); - // Schedule the next async, and resolve to 2 in 20 ms. - const result2Promise = oneAsyncCallAtATime(); - // Reuse scheduled promise and resolve to 2 in 20 ms. + }); // Start an async, and resolve to 1 in 10 ms. + + const result1Promise = oneAsyncCallAtATime(); // Schedule the next async, and resolve to 2 in 20 ms. + + const result2Promise = oneAsyncCallAtATime(); // Reuse scheduled promise and resolve to 2 in 20 ms. + const result3Promise = oneAsyncCallAtATime(); - await waitPromise(11); - // Wait for the promise to call the next chain + await waitPromise(11); // Wait for the promise to call the next chain // That isn't synchrnously guranteed because it happens on `process.nextTick`. - waitsFor(() => asyncFunSpy.callCount === 2); + + (0, _waits_for().default)(() => asyncFunSpy.callCount === 2); await waitPromise(11); - const results = await Promise.all([ - result1Promise, - result2Promise, - result3Promise, - ]); + const results = await Promise.all([result1Promise, result2Promise, result3Promise]); expect(results).toEqual([1, 2, 2]); }); - it('Calls and returns (even if errors) the same number of times if serially called', async () => { jest.useFakeTimers(); let i = 0; - const oneAsyncCallAtATime = serializeAsyncCall(() => { + const oneAsyncCallAtATime = (0, _promise().serializeAsyncCall)(() => { i++; + if (i === 4) { return Promise.reject('ERROR'); } + return waitPromise(10, i); }); const result1Promise = oneAsyncCallAtATime(); jest.advanceTimersByTime(11); const result1 = await result1Promise; - const result2Promise = oneAsyncCallAtATime(); jest.advanceTimersByTime(11); const result2 = await result2Promise; - const result3Promise = oneAsyncCallAtATime(); jest.advanceTimersByTime(11); const result3 = await result3Promise; - const errorPromoise = oneAsyncCallAtATime(); jest.advanceTimersByTime(11); - await expectAsyncFailure(errorPromoise, error => { + await (0, _testHelpers().expectAsyncFailure)(errorPromoise, error => { expect(error).toBe('ERROR'); }); - const result5Promise = oneAsyncCallAtATime(); jest.advanceTimersByTime(11); const result5 = await result5Promise; expect([result1, result2, result3, result5]).toEqual([1, 2, 3, 5]); }); }); - describe('promises::asyncLimit()', () => { beforeEach(() => { jest.useRealTimers(); }); - it('runs in series if limit is 1', async () => { - const {result, parallelismHistory} = await captureParallelismHistory( - asyncLimit, - [[1, 2, 3], 1, item => waitPromise(10, item + 1)], - ); + const { + result, + parallelismHistory + } = await captureParallelismHistory(_promise().asyncLimit, [[1, 2, 3], 1, item => waitPromise(10, item + 1)]); expect(parallelismHistory).toEqual([1, 1, 1]); expect(result).toEqual([2, 3, 4]); }); - it('runs with the specified limit, until finishing', async () => { - const {result, parallelismHistory} = await captureParallelismHistory( - asyncLimit, - [ - [1, 2, 3, 4, 5, 6, 7, 8, 9], - 3, - item => waitPromise(10 + item, item - 1), - ], - ); + const { + result, + parallelismHistory + } = await captureParallelismHistory(_promise().asyncLimit, [[1, 2, 3, 4, 5, 6, 7, 8, 9], 3, item => waitPromise(10 + item, item - 1)]); expect(result).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]); expect(parallelismHistory).toEqual([1, 2, 3, 3, 3, 3, 3, 3, 3]); }); - it('works when the limit is bigger than the array length', async () => { - const result = await asyncLimit([1, 2, 3], 10, item => - waitPromise(10, item * 2), - ); + const result = await (0, _promise().asyncLimit)([1, 2, 3], 10, item => waitPromise(10, item * 2)); expect(result).toEqual([2, 4, 6]); }); - it('a rejected promise rejects the whole call with the error', async () => { - await expectAsyncFailure( - asyncLimit([1], 1, async item => { - throw new Error('rejected iterator promise'); - }), - (error: Error) => { - expect(error.message).toBe('rejected iterator promise'); - }, - ); + await (0, _testHelpers().expectAsyncFailure)((0, _promise().asyncLimit)([1], 1, async item => { + throw new Error('rejected iterator promise'); + }), error => { + expect(error.message).toBe('rejected iterator promise'); + }); }); - it('works when the array is empty', async () => { - const result = await asyncLimit([], 1, () => Promise.resolve()); + const result = await (0, _promise().asyncLimit)([], 1, () => Promise.resolve()); expect(result).toEqual([]); }); }); - describe('promises::asyncFilter()', () => { beforeEach(() => { jest.useRealTimers(); - }); + }); // eslint-disable-next-line max-len - // eslint-disable-next-line max-len it('filters an array with an async iterator and maximum parallelization when no limit is specified', async () => { const { result: filtered, - parallelismHistory, - } = await captureParallelismHistory(asyncFilter, [ - [1, 2, 3, 4, 5], - item => waitPromise(10 + item, item > 2), - ]); + parallelismHistory + } = await captureParallelismHistory(_promise().asyncFilter, [[1, 2, 3, 4, 5], item => waitPromise(10 + item, item > 2)]); expect(filtered).toEqual([3, 4, 5]); expect(parallelismHistory).toEqual([1, 2, 3, 4, 5]); }); - it('filters an array with a limit on parallelization', async () => { const { result: filtered, - parallelismHistory, - } = await captureParallelismHistory(asyncFilter, [ - [1, 2, 3, 4, 5], - item => waitPromise(10 + item, item > 2), - 3, - ]); - expect(filtered).toEqual([3, 4, 5]); - // Increasing promise resolve time will gurantee maximum parallelization. + parallelismHistory + } = await captureParallelismHistory(_promise().asyncFilter, [[1, 2, 3, 4, 5], item => waitPromise(10 + item, item > 2), 3]); + expect(filtered).toEqual([3, 4, 5]); // Increasing promise resolve time will gurantee maximum parallelization. + expect(parallelismHistory).toEqual([1, 2, 3, 3, 3]); }); }); - describe('promises::asyncObjFilter()', () => { beforeEach(() => { jest.useRealTimers(); - }); + }); // eslint-disable-next-line max-len - // eslint-disable-next-line max-len it('filters an object with an async iterator and maximum parallelization when no limit is specified', async () => { const { result: filtered, - parallelismHistory, - } = await captureParallelismHistory(asyncObjFilter, [ - {a: 1, b: 2, c: 3, d: 4, e: 5}, - (value, key) => waitPromise(5 + value, value > 2), - ]); - expect(filtered).toEqual({c: 3, d: 4, e: 5}); + parallelismHistory + } = await captureParallelismHistory(_promise().asyncObjFilter, [{ + a: 1, + b: 2, + c: 3, + d: 4, + e: 5 + }, (value, key) => waitPromise(5 + value, value > 2)]); + expect(filtered).toEqual({ + c: 3, + d: 4, + e: 5 + }); expect(parallelismHistory).toEqual([1, 2, 3, 4, 5]); }); - it('filters an array with a limit on parallelization', async () => { const { result: filtered, - parallelismHistory, - } = await captureParallelismHistory(asyncObjFilter, [ - {a: 1, b: 2, c: 3, d: 4, e: 5}, - (value, key) => waitPromise(5 + value, value > 2), - 3, - ]); - expect(filtered).toEqual({c: 3, d: 4, e: 5}); - // Increasing promise resolve time will gurantee maximum parallelization. + parallelismHistory + } = await captureParallelismHistory(_promise().asyncObjFilter, [{ + a: 1, + b: 2, + c: 3, + d: 4, + e: 5 + }, (value, key) => waitPromise(5 + value, value > 2), 3]); + expect(filtered).toEqual({ + c: 3, + d: 4, + e: 5 + }); // Increasing promise resolve time will gurantee maximum parallelization. + expect(parallelismHistory).toEqual([1, 2, 3, 3, 3]); }); }); - describe('promises::asyncSome()', () => { beforeEach(() => { jest.useRealTimers(); - }); + }); // eslint-disable-next-line max-len - // eslint-disable-next-line max-len it('some an array with an async iterator and maximum parallelization when no limit is specified', async () => { - const {result, parallelismHistory} = await captureParallelismHistory( - asyncSome, - [[1, 2, 3, 4, 5], item => waitPromise(10, item === 6)], - ); + const { + result, + parallelismHistory + } = await captureParallelismHistory(_promise().asyncSome, [[1, 2, 3, 4, 5], item => waitPromise(10, item === 6)]); expect(result).toEqual(false); expect(parallelismHistory).toEqual([1, 2, 3, 4, 5]); }); - it('some an array with a limit on parallelization', async () => { - const {result, parallelismHistory} = await captureParallelismHistory( - asyncSome, - [[1, 2, 3, 4, 5], item => waitPromise(10 + item, item === 5), 3], - ); + const { + result, + parallelismHistory + } = await captureParallelismHistory(_promise().asyncSome, [[1, 2, 3, 4, 5], item => waitPromise(10 + item, item === 5), 3]); expect(result).toEqual(true); expect(parallelismHistory).toEqual([1, 2, 3, 3, 3]); }); }); - describe('promises::lastly', () => { it('executes after a resolved promise', async () => { const spy = jasmine.createSpy('spy'); - const result = await lastly(Promise.resolve(1), spy); + const result = await (0, _promise().lastly)(Promise.resolve(1), spy); expect(result).toBe(1); expect(spy).toHaveBeenCalled(); }); - it('executes after a rejected promise', async () => { const spy = jasmine.createSpy('spy'); - await expectAsyncFailure(lastly(Promise.reject(2), spy), err => { + await (0, _testHelpers().expectAsyncFailure)((0, _promise().lastly)(Promise.reject(2), spy), err => { expect(err).toBe(2); }); expect(spy).toHaveBeenCalled(); }); - it('works for async functions', async () => { const spy = jasmine.createSpy('spy'); - const result = await lastly(Promise.resolve(1), async () => { + const result = await (0, _promise().lastly)(Promise.resolve(1), async () => { spy(); }); expect(result).toBe(1); expect(spy).toHaveBeenCalled(); }); }); - describe('promises::retryLimit()', () => { beforeEach(() => { jest.useRealTimers(); }); - it('retries and fails 2 times before resolving to an acceptable result where limit = 5', async () => { await (async () => { let succeedAfter = 2; let calls = 0; let validationCalls = 0; - const retrialsResult = await retryLimit( - () => { - return new Promise((resolve, reject) => { - calls++; - if (succeedAfter-- === 0) { - resolve('RESULT'); - } else { - reject('ERROR'); - } - }); - }, - result => { - validationCalls++; - return result === 'RESULT'; - }, - 5, - ); + const retrialsResult = await (0, _promise().retryLimit)(() => { + return new Promise((resolve, reject) => { + calls++; + + if (succeedAfter-- === 0) { + resolve('RESULT'); + } else { + reject('ERROR'); + } + }); + }, result => { + validationCalls++; + return result === 'RESULT'; + }, 5); expect(calls).toBe(3); expect(validationCalls).toBe(1); expect(retrialsResult).toBe('RESULT'); })(); }); - it('retries and fails consistently', async () => { await (async () => { let calls = 0; let validationCalls = 0; - const failRetriesPromise = retryLimit( - () => { - calls++; - return Promise.reject('ERROR'); - }, - result => { - validationCalls++; - return result != null; - }, - 2, - ); - await expectAsyncFailure(failRetriesPromise, error => { + const failRetriesPromise = (0, _promise().retryLimit)(() => { + calls++; + return Promise.reject('ERROR'); + }, result => { + validationCalls++; + return result != null; + }, 2); + await (0, _testHelpers().expectAsyncFailure)(failRetriesPromise, error => { expect(error).toBe('ERROR'); }); expect(calls).toBe(2); expect(validationCalls).toBe(0); })(); }); - it('accepts a null response', async () => { await (async () => { let succeedAfter = 2; let calls = 0; let validationCalls = 0; - const retryResult = await retryLimit( - () => { - calls++; - if (succeedAfter-- === 0) { - return Promise.resolve(null); - } else { - return Promise.resolve('NOT_GOOD'); - } - }, - result => { - validationCalls++; - return result == null; - }, - 5, - ); + const retryResult = await (0, _promise().retryLimit)(() => { + calls++; + + if (succeedAfter-- === 0) { + return Promise.resolve(null); + } else { + return Promise.resolve('NOT_GOOD'); + } + }, result => { + validationCalls++; + return result == null; + }, 5); expect(retryResult).toBe(null); expect(calls).toBe(3); expect(validationCalls).toBe(3); })(); }); - it('no valid response is ever got', async () => { await (async () => { - const nonValidRetriesPromise = retryLimit( - () => { - return Promise.resolve('A'); - }, - result => { - return result === 'B'; - }, - 2, - ); - await expectAsyncFailure(nonValidRetriesPromise, error => { + const nonValidRetriesPromise = (0, _promise().retryLimit)(() => { + return Promise.resolve('A'); + }, result => { + return result === 'B'; + }, 2); + await (0, _testHelpers().expectAsyncFailure)(nonValidRetriesPromise, error => { expect(error.message).toBe('No valid response found!'); }); })(); }); }); - describe('promises::RequestSerializer()', () => { - let requestSerializer: RequestSerializer = (null: any); - + let requestSerializer = null; beforeEach(() => { jest.useRealTimers(); - requestSerializer = new RequestSerializer(); + requestSerializer = new (_promise().RequestSerializer)(); }); - it('gets outdated result for old promises resolving after newer calls', async () => { const oldPromise = requestSerializer.run(waitPromise(10, 'OLD')); const newPromise = requestSerializer.run(waitPromise(5, 'NEW')); - const {status: oldStatus} = await oldPromise; + const { + status: oldStatus + } = await oldPromise; expect(oldStatus).toBe('outdated'); const newResult = await newPromise; - invariant(newResult.status === 'success'); + + if (!(newResult.status === 'success')) { + throw new Error("Invariant violation: \"newResult.status === 'success'\""); + } + expect(newResult.result).toBe('NEW'); }); - it('waitForLatestResult: waits for the latest result', async () => { requestSerializer.run(waitPromise(5, 'OLD')); requestSerializer.run(waitPromise(10, 'NEW')); const latestResult = await requestSerializer.waitForLatestResult(); expect(latestResult).toBe('NEW'); }); - it('waitForLatestResult: waits even if the first run did not kick off', async () => { const latestResultPromise = requestSerializer.waitForLatestResult(); requestSerializer.run(waitPromise(10, 'RESULT')); const latestResult = await latestResultPromise; expect(latestResult).toBe('RESULT'); }); - it('waitForLatestResult: does not wait for the first, if the second resolves faster', async () => { requestSerializer.run(waitPromise(1000000, 'OLD')); // This will never resolve. + requestSerializer.run(waitPromise(10, 'NEW')); const latestResult = await requestSerializer.waitForLatestResult(); expect(latestResult).toBe('NEW'); }); }); - describe('timeoutPromise', () => { it('should resolve normally if within the timeout', async () => { const inputPromise = new Promise(resolve => resolve('foo')); - const outputPromise = timeoutPromise(inputPromise, 1000); - expect(await outputPromise).toBe('foo'); + const outputPromise = (0, _promise().timeoutPromise)(inputPromise, 1000); + expect((await outputPromise)).toBe('foo'); }); - it('should reject if the given promise rejects', async () => { const inputPromise = new Promise((resolve, reject) => reject('foo')); - const outputPromise = timeoutPromise(inputPromise, 1000).catch( - value => `rejected with ${value}`, - ); - expect(await outputPromise).toBe('rejected with foo'); + const outputPromise = (0, _promise().timeoutPromise)(inputPromise, 1000).catch(value => `rejected with ${value}`); + expect((await outputPromise)).toBe('rejected with foo'); }); - it('should reject if the given promise takes too long', async () => { jest.useFakeTimers(); const inputPromise = new Promise(resolve => setTimeout(resolve, 2000)); - const outputPromise = timeoutPromise(inputPromise, 1000).catch( - value => value, - ); + const outputPromise = (0, _promise().timeoutPromise)(inputPromise, 1000).catch(value => value); jest.advanceTimersByTime(1500); - expect(await outputPromise).toEqual(new TimedOutError(1000)); + expect((await outputPromise)).toEqual(new (_promise().TimedOutError)(1000)); }); }); -async function captureParallelismHistory( - asyncFunction: (...args: Array) => Promise, - args: Array, -): Promise<{result: mixed, parallelismHistory: Array}> { +async function captureParallelismHistory(asyncFunction, args) { const parallelismHistory = []; let parralelism = 0; - const result = await asyncFunction( - ...args.map(arg => { - if (typeof arg !== 'function') { - return arg; - } - const func = arg; - return async item => { - ++parralelism; - parallelismHistory.push(parralelism); - const value = await func(item); - --parralelism; - return value; - }; - }), - ); - return {result, parallelismHistory}; + const result = await asyncFunction(...args.map(arg => { + if (typeof arg !== 'function') { + return arg; + } + + const func = arg; + return async item => { + ++parralelism; + parallelismHistory.push(parralelism); + const value = await func(item); + --parralelism; + return value; + }; + })); + return { + result, + parallelismHistory + }; } -function waitPromise(timeoutMs: number, value: any): Promise { +function waitPromise(timeoutMs, value) { return new Promise((resolve, reject) => { setTimeout(() => resolve(value), timeoutMs); }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/range-test.js b/modules/nuclide-commons/__tests__/range-test.js index 95e188a0..0366adc0 100644 --- a/modules/nuclide-commons/__tests__/range-test.js +++ b/modules/nuclide-commons/__tests__/range-test.js @@ -1,3 +1,27 @@ +"use strict"; + +function _simpleTextBuffer() { + const data = _interopRequireWildcard(require("simple-text-buffer")); + + _simpleTextBuffer = function () { + return data; + }; + + return data; +} + +function _range() { + const data = require("../range"); + + _range = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,28 +30,32 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import invariant from 'assert'; -import {default as TextBuffer, Range} from 'simple-text-buffer'; -import {wordAtPositionFromBuffer} from '../range'; - describe('wordAtPositionFromBuffer', () => { it('matches a word in a buffer', () => { - const buffer = new TextBuffer('word1 word2 word3\n'); - const match = wordAtPositionFromBuffer(buffer, {row: 0, column: 6}, /\S+/g); + const buffer = new (_simpleTextBuffer().default)('word1 word2 word3\n'); + const match = (0, _range().wordAtPositionFromBuffer)(buffer, { + row: 0, + column: 6 + }, /\S+/g); expect(match).not.toBeNull(); - invariant(match != null); + + if (!(match != null)) { + throw new Error("Invariant violation: \"match != null\""); + } + expect(match.wordMatch.length).toBe(1); expect(match.wordMatch[0]).toBe('word2'); - expect(match.range).toEqual(new Range([0, 6], [0, 11])); + expect(match.range).toEqual(new (_simpleTextBuffer().Range)([0, 6], [0, 11])); }); - it('should not include endpoints', () => { - const buffer = new TextBuffer('word1 word2 word3\n'); - const match = wordAtPositionFromBuffer(buffer, {row: 0, column: 5}, /\S+/g); + const buffer = new (_simpleTextBuffer().default)('word1 word2 word3\n'); + const match = (0, _range().wordAtPositionFromBuffer)(buffer, { + row: 0, + column: 5 + }, /\S+/g); expect(match).toBeNull(); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/sanitizeHtml-test.js b/modules/nuclide-commons/__tests__/sanitizeHtml-test.js index bc21b1c3..31a97bb6 100644 --- a/modules/nuclide-commons/__tests__/sanitizeHtml-test.js +++ b/modules/nuclide-commons/__tests__/sanitizeHtml-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _sanitizeHtml() { + const data = _interopRequireDefault(require("../sanitizeHtml")); + + _sanitizeHtml = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +20,28 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import sanitize from '../sanitizeHtml'; - it('removes html', () => { - expect(sanitize('

      a lowly h4

      ')).toBe('a lowly h4'); + expect((0, _sanitizeHtml().default)('

      a lowly h4

      ')).toBe('a lowly h4'); }); - it('removes leading and trailing whitespace', () => { - expect(sanitize(' a\nb ', {condenseWhitespaces: false})).toBe('a\nb'); + expect((0, _sanitizeHtml().default)(' a\nb ', { + condenseWhitespaces: false + })).toBe('a\nb'); }); - it('compresses whitespace with option', () => { - expect( - sanitize(' a\n \n b ', {condenseWhitespaces: true}), - ).toBe('a\nb'); - expect( - sanitize(' a\n \n b ', {condenseWhitespaces: false}), - ).toBe('a\n \n b'); + expect((0, _sanitizeHtml().default)(' a\n \n b ', { + condenseWhitespaces: true + })).toBe('a\nb'); + expect((0, _sanitizeHtml().default)(' a\n \n b ', { + condenseWhitespaces: false + })).toBe('a\n \n b'); }); - it('adds line breaks for

      and
      tags', () => { - expect(sanitize('a
      b

      c

      d

      ')).toBe('a\nb\nc\nd'); - expect(sanitize('a
      b')).toBe('a\nb'); - expect(sanitize('a
      b')).toBe('a\nb'); - expect(sanitize('a
      b')).toBe('a\nb'); -}); + expect((0, _sanitizeHtml().default)('a
      b

      c

      d

      ')).toBe('a\nb\nc\nd'); + expect((0, _sanitizeHtml().default)('a
      b')).toBe('a\nb'); + expect((0, _sanitizeHtml().default)('a
      b')).toBe('a\nb'); + expect((0, _sanitizeHtml().default)('a
      b')).toBe('a\nb'); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/shell-quote-test.js b/modules/nuclide-commons/__tests__/shell-quote-test.js index f542088c..20bc4817 100644 --- a/modules/nuclide-commons/__tests__/shell-quote-test.js +++ b/modules/nuclide-commons/__tests__/shell-quote-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _shellQuote() { + const data = require("../_shell-quote"); + + _shellQuote = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,37 +18,42 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import {parse, quote} from '../_shell-quote'; - /** * The rest of shell-quote has been verified to work correctly. * We just need to test the comment parsing. */ - describe('shell-quote', () => { describe('parse', () => { it('parses comments correctly', () => { - expect(parse('beep#boop')).toEqual(['beep#boop']); - expect(parse('beep #boop')).toEqual(['beep', {comment: 'boop'}]); - expect(parse('beep # boop')).toEqual(['beep', {comment: 'boop'}]); - expect(parse('beep # > boop')).toEqual(['beep', {comment: '> boop'}]); - expect(parse('beep # "> boop"')).toEqual(['beep', {comment: '"> boop"'}]); - expect(parse('beep "#"')).toEqual(['beep', '#']); - expect(parse('beep #"#"#')).toEqual(['beep', {comment: '"#"#'}]); - expect(parse('beep > boop # > foo')).toEqual([ - 'beep', - {op: '>'}, - 'boop', - {comment: '> foo'}, - ]); + expect((0, _shellQuote().parse)('beep#boop')).toEqual(['beep#boop']); + expect((0, _shellQuote().parse)('beep #boop')).toEqual(['beep', { + comment: 'boop' + }]); + expect((0, _shellQuote().parse)('beep # boop')).toEqual(['beep', { + comment: 'boop' + }]); + expect((0, _shellQuote().parse)('beep # > boop')).toEqual(['beep', { + comment: '> boop' + }]); + expect((0, _shellQuote().parse)('beep # "> boop"')).toEqual(['beep', { + comment: '"> boop"' + }]); + expect((0, _shellQuote().parse)('beep "#"')).toEqual(['beep', '#']); + expect((0, _shellQuote().parse)('beep #"#"#')).toEqual(['beep', { + comment: '"#"#' + }]); + expect((0, _shellQuote().parse)('beep > boop # > foo')).toEqual(['beep', { + op: '>' + }, 'boop', { + comment: '> foo' + }]); }); }); - describe('quote', () => { - expect(quote(['X#(){}*|][!'])).toBe('X\\#\\(\\)\\{\\}\\*\\|\\]\\[\\!'); + expect((0, _shellQuote().quote)(['X#(){}*|][!'])).toBe('X\\#\\(\\)\\{\\}\\*\\|\\]\\[\\!'); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/stream-test.js b/modules/nuclide-commons/__tests__/stream-test.js index 24252743..377c1dc2 100644 --- a/modules/nuclide-commons/__tests__/stream-test.js +++ b/modules/nuclide-commons/__tests__/stream-test.js @@ -1,3 +1,33 @@ +"use strict"; + +function _stream() { + const data = require("../stream"); + + _stream = function () { + return data; + }; + + return data; +} + +function _fsPromise() { + const data = _interopRequireDefault(require("../fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +var _stream2 = _interopRequireDefault(require("stream")); + +var _fs = _interopRequireDefault(require("fs")); + +var _path = _interopRequireDefault(require("path")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,23 +36,14 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {observeStream, observeRawStream, writeToStream} from '../stream'; -import fsPromise from '../fsPromise'; -import Stream from 'stream'; -import fs from 'fs'; -import path from 'path'; - describe('commons-node/stream', () => { it('observeStream', async () => { const input = ['foo\nbar', '\n', '\nba', 'z', '\nblar']; - const stream = new Stream.PassThrough(); - const promise = observeStream(stream) - .toArray() - .toPromise(); + const stream = new _stream2.default.PassThrough(); + const promise = (0, _stream().observeStream)(stream).toArray().toPromise(); input.forEach(value => { stream.write(value, 'utf8'); }); @@ -30,43 +51,38 @@ describe('commons-node/stream', () => { const output = await promise; expect(output.join('')).toEqual(input.join('')); }); - it('observeStream - error', async () => { - const stream = new Stream.PassThrough(); + const stream = new _stream2.default.PassThrough(); const input = ['foo\nbar', '\n', '\nba', 'z', '\nblar']; const output = []; const promise = new Promise((resolve, reject) => { - observeStream(stream).subscribe( - v => output.push(v), - e => resolve(e), - () => {}, - ); + (0, _stream().observeStream)(stream).subscribe(v => output.push(v), e => resolve(e), () => {}); }); const error = new Error('Had an error'); - input.forEach(value => { stream.write(value, 'utf8'); }); stream.emit('error', error); - const result = await promise; expect(output).toEqual(input); expect(result).toBe(error); }); - it('writeToStream', async () => { - const tempPath = await fsPromise.tempfile(); - const fixturePath = path.resolve(__dirname, '../__mocks__/fixtures/lyrics'); - const stream = fs.createWriteStream(tempPath, {highWaterMark: 10}); - // Read faster than we write to test buffering - const observable = observeRawStream( - fs.createReadStream(fixturePath, {highWaterMark: 100}), - ); + const tempPath = await _fsPromise().default.tempfile(); + + const fixturePath = _path.default.resolve(__dirname, '../__mocks__/fixtures/lyrics'); + + const stream = _fs.default.createWriteStream(tempPath, { + highWaterMark: 10 + }); // Read faster than we write to test buffering - await writeToStream(observable, stream).toPromise(); - const writtenFile = await fsPromise.readFile(tempPath); - const fixtureFile = await fsPromise.readFile(fixturePath); + const observable = (0, _stream().observeRawStream)(_fs.default.createReadStream(fixturePath, { + highWaterMark: 100 + })); + await (0, _stream().writeToStream)(observable, stream).toPromise(); + const writtenFile = await _fsPromise().default.readFile(tempPath); + const fixtureFile = await _fsPromise().default.readFile(fixturePath); expect(writtenFile).toEqual(fixtureFile); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/string-test.js b/modules/nuclide-commons/__tests__/string-test.js index 3ee54911..c8c1cabb 100644 --- a/modules/nuclide-commons/__tests__/string-test.js +++ b/modules/nuclide-commons/__tests__/string-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _string() { + const data = require("../string"); + + _string = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,25 +18,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import { - capitalize, - countOccurrences, - getMatchRanges, - indent, - maybeToString, - pluralize, - relativeDate, - removeCommonPrefix, - removeCommonSuffix, - shellParse, - shorten, - splitOnce, -} from '../string'; - describe('relativeDate', () => { it('works', () => { const SECOND = 1000; @@ -34,272 +30,220 @@ describe('relativeDate', () => { const WEEK = 7 * DAY; const YEAR = DAY * 365; const MONTH = YEAR / 12; - const reference = 157765000000; // 01.01.1975 00:00 - const now = new Date().getTime(); - // test long format - expect(relativeDate(0)).toEqual(Math.round(now / YEAR) + ' years ago'); - expect(relativeDate(reference * SECOND, reference)).toEqual('just now'); - expect(relativeDate(reference - 41 * SECOND, reference)).toEqual( - 'just now', - ); - expect(relativeDate(reference - 42 * SECOND, reference)).toEqual( - 'a minute ago', - ); - expect(relativeDate(reference - MINUTE, reference)).toEqual('a minute ago'); - expect(relativeDate(reference - MINUTE * 1.5, reference)).toEqual( - '2 minutes ago', - ); - expect(relativeDate(reference - MINUTE * 59, reference)).toEqual( - '59 minutes ago', - ); - expect(relativeDate(reference - HOUR, reference)).toEqual('an hour ago'); - expect(relativeDate(reference - HOUR * 1.5, reference)).toEqual( - '2 hours ago', - ); - expect(relativeDate(reference - HOUR * 16, reference)).toEqual( - '16 hours ago', - ); - expect(relativeDate(reference - HOUR * 23, reference)).toEqual( - '23 hours ago', - ); - expect(relativeDate(reference - DAY * 1.8, reference)).toEqual('yesterday'); - expect(relativeDate(reference - DAY * 3, reference)).toEqual('3 days ago'); - expect(relativeDate(reference - DAY * 6, reference)).toEqual('6 days ago'); - expect(relativeDate(reference - WEEK, reference)).toEqual('a week ago'); - expect(relativeDate(reference - WEEK * 2, reference)).toEqual( - '2 weeks ago', - ); - expect(relativeDate(reference - WEEK * 4, reference)).toEqual( - '4 weeks ago', - ); - expect(relativeDate(reference - MONTH * 1.2, reference)).toEqual( - 'a month ago', - ); - expect(relativeDate(reference - YEAR + HOUR, reference)).toEqual( - '12 months ago', - ); - expect(relativeDate(reference - YEAR, reference)).toEqual('a year ago'); - expect(relativeDate(reference - YEAR * 2, reference)).toEqual( - '2 years ago', - ); - expect(relativeDate(0, reference)).toEqual('5 years ago'); - - // test short format - expect(relativeDate(0, undefined, /* short */ true)).toEqual( - Math.round(now / YEAR) + 'y', - ); - expect( - relativeDate(reference * SECOND, reference, /* short */ true), - ).toEqual('now'); - expect( - relativeDate(reference - 41 * SECOND, reference, /* short */ true), - ).toEqual('now'); - expect( - relativeDate(reference - 42 * SECOND, reference, /* short */ true), - ).toEqual('1m'); - expect( - relativeDate(reference - MINUTE, reference, /* short */ true), - ).toEqual('1m'); - expect( - relativeDate(reference - MINUTE * 1.5, reference, /* short */ true), - ).toEqual('2m'); - expect( - relativeDate(reference - MINUTE * 59, reference, /* short */ true), - ).toEqual('59m'); - expect(relativeDate(reference - HOUR, reference, /* short */ true)).toEqual( - '1h', - ); - expect( - relativeDate(reference - HOUR * 1.5, reference, /* short */ true), - ).toEqual('2h'); - expect( - relativeDate(reference - HOUR * 16, reference, /* short */ true), - ).toEqual('16h'); - expect( - relativeDate(reference - HOUR * 23, reference, /* short */ true), - ).toEqual('23h'); - expect( - relativeDate(reference - DAY * 1.8, reference, /* short */ true), - ).toEqual('1d'); - expect( - relativeDate(reference - DAY * 3, reference, /* short */ true), - ).toEqual('3d'); - expect( - relativeDate(reference - DAY * 6, reference, /* short */ true), - ).toEqual('6d'); - expect(relativeDate(reference - WEEK, reference, /* short */ true)).toEqual( - '1w', - ); - expect( - relativeDate(reference - WEEK * 2, reference, /* short */ true), - ).toEqual('2w'); - expect( - relativeDate(reference - WEEK * 4, reference, /* short */ true), - ).toEqual('4w'); - expect( - relativeDate(reference - MONTH * 1.2, reference, /* short */ true), - ).toEqual('1mo'); - expect( - relativeDate(reference - YEAR + HOUR, reference, /* short */ true), - ).toEqual('12mo'); - expect(relativeDate(reference - YEAR, reference, /* short */ true)).toEqual( - '1y', - ); - expect( - relativeDate(reference - YEAR * 2, reference, /* short */ true), - ).toEqual('2y'); - expect(relativeDate(0, reference, /* short */ true)).toEqual('5y'); + const now = new Date().getTime(); // test long format + + expect((0, _string().relativeDate)(0)).toEqual(Math.round(now / YEAR) + ' years ago'); + expect((0, _string().relativeDate)(reference * SECOND, reference)).toEqual('just now'); + expect((0, _string().relativeDate)(reference - 41 * SECOND, reference)).toEqual('just now'); + expect((0, _string().relativeDate)(reference - 42 * SECOND, reference)).toEqual('a minute ago'); + expect((0, _string().relativeDate)(reference - MINUTE, reference)).toEqual('a minute ago'); + expect((0, _string().relativeDate)(reference - MINUTE * 1.5, reference)).toEqual('2 minutes ago'); + expect((0, _string().relativeDate)(reference - MINUTE * 59, reference)).toEqual('59 minutes ago'); + expect((0, _string().relativeDate)(reference - HOUR, reference)).toEqual('an hour ago'); + expect((0, _string().relativeDate)(reference - HOUR * 1.5, reference)).toEqual('2 hours ago'); + expect((0, _string().relativeDate)(reference - HOUR * 16, reference)).toEqual('16 hours ago'); + expect((0, _string().relativeDate)(reference - HOUR * 23, reference)).toEqual('23 hours ago'); + expect((0, _string().relativeDate)(reference - DAY * 1.8, reference)).toEqual('yesterday'); + expect((0, _string().relativeDate)(reference - DAY * 3, reference)).toEqual('3 days ago'); + expect((0, _string().relativeDate)(reference - DAY * 6, reference)).toEqual('6 days ago'); + expect((0, _string().relativeDate)(reference - WEEK, reference)).toEqual('a week ago'); + expect((0, _string().relativeDate)(reference - WEEK * 2, reference)).toEqual('2 weeks ago'); + expect((0, _string().relativeDate)(reference - WEEK * 4, reference)).toEqual('4 weeks ago'); + expect((0, _string().relativeDate)(reference - MONTH * 1.2, reference)).toEqual('a month ago'); + expect((0, _string().relativeDate)(reference - YEAR + HOUR, reference)).toEqual('12 months ago'); + expect((0, _string().relativeDate)(reference - YEAR, reference)).toEqual('a year ago'); + expect((0, _string().relativeDate)(reference - YEAR * 2, reference)).toEqual('2 years ago'); + expect((0, _string().relativeDate)(0, reference)).toEqual('5 years ago'); // test short format + + expect((0, _string().relativeDate)(0, undefined, + /* short */ + true)).toEqual(Math.round(now / YEAR) + 'y'); + expect((0, _string().relativeDate)(reference * SECOND, reference, + /* short */ + true)).toEqual('now'); + expect((0, _string().relativeDate)(reference - 41 * SECOND, reference, + /* short */ + true)).toEqual('now'); + expect((0, _string().relativeDate)(reference - 42 * SECOND, reference, + /* short */ + true)).toEqual('1m'); + expect((0, _string().relativeDate)(reference - MINUTE, reference, + /* short */ + true)).toEqual('1m'); + expect((0, _string().relativeDate)(reference - MINUTE * 1.5, reference, + /* short */ + true)).toEqual('2m'); + expect((0, _string().relativeDate)(reference - MINUTE * 59, reference, + /* short */ + true)).toEqual('59m'); + expect((0, _string().relativeDate)(reference - HOUR, reference, + /* short */ + true)).toEqual('1h'); + expect((0, _string().relativeDate)(reference - HOUR * 1.5, reference, + /* short */ + true)).toEqual('2h'); + expect((0, _string().relativeDate)(reference - HOUR * 16, reference, + /* short */ + true)).toEqual('16h'); + expect((0, _string().relativeDate)(reference - HOUR * 23, reference, + /* short */ + true)).toEqual('23h'); + expect((0, _string().relativeDate)(reference - DAY * 1.8, reference, + /* short */ + true)).toEqual('1d'); + expect((0, _string().relativeDate)(reference - DAY * 3, reference, + /* short */ + true)).toEqual('3d'); + expect((0, _string().relativeDate)(reference - DAY * 6, reference, + /* short */ + true)).toEqual('6d'); + expect((0, _string().relativeDate)(reference - WEEK, reference, + /* short */ + true)).toEqual('1w'); + expect((0, _string().relativeDate)(reference - WEEK * 2, reference, + /* short */ + true)).toEqual('2w'); + expect((0, _string().relativeDate)(reference - WEEK * 4, reference, + /* short */ + true)).toEqual('4w'); + expect((0, _string().relativeDate)(reference - MONTH * 1.2, reference, + /* short */ + true)).toEqual('1mo'); + expect((0, _string().relativeDate)(reference - YEAR + HOUR, reference, + /* short */ + true)).toEqual('12mo'); + expect((0, _string().relativeDate)(reference - YEAR, reference, + /* short */ + true)).toEqual('1y'); + expect((0, _string().relativeDate)(reference - YEAR * 2, reference, + /* short */ + true)).toEqual('2y'); + expect((0, _string().relativeDate)(0, reference, + /* short */ + true)).toEqual('5y'); }); }); - describe('maybeToString', () => { it("returns 'undefined'", () => { - expect(maybeToString(undefined)).toEqual('undefined'); + expect((0, _string().maybeToString)(undefined)).toEqual('undefined'); }); - it("returns 'null'", () => { - expect(maybeToString(null)).toEqual('null'); + expect((0, _string().maybeToString)(null)).toEqual('null'); }); - it('returns an ordinary string', () => { - expect(maybeToString('foo')).toEqual('foo'); + expect((0, _string().maybeToString)('foo')).toEqual('foo'); }); }); - describe('countOccurrences', () => { it('counts the number of characters', () => { - expect(countOccurrences('abcaaa', 'a')).toBe(4); + expect((0, _string().countOccurrences)('abcaaa', 'a')).toBe(4); }); - it('throws for non-length-1 searches', () => { expect(() => { - countOccurrences('abc', 'abc'); + (0, _string().countOccurrences)('abc', 'abc'); }).toThrow(); }); }); - describe('shellParse', () => { it('parses a list of arguments', () => { - expect(shellParse('1 2 3 "a b c"')).toEqual(['1', '2', '3', 'a b c']); + expect((0, _string().shellParse)('1 2 3 "a b c"')).toEqual(['1', '2', '3', 'a b c']); }); - it('throws if operators are given', () => { expect(() => { - shellParse('a | b'); + (0, _string().shellParse)('a | b'); }).toThrow(Error('Unexpected operator "|" provided to shellParse')); expect(() => { - shellParse('a > b'); + (0, _string().shellParse)('a > b'); }).toThrow(Error('Unexpected operator ">" provided to shellParse')); }); }); - describe('removeCommonPrefix', () => { it('does nothing if there is no common prefix', () => { - expect(removeCommonPrefix('foo', 'bar')).toEqual(['foo', 'bar']); + expect((0, _string().removeCommonPrefix)('foo', 'bar')).toEqual(['foo', 'bar']); }); - it('removes a common prefix', () => { - expect(removeCommonPrefix('foo', 'fbar')).toEqual(['oo', 'bar']); - expect(removeCommonPrefix('asdffoo', 'asdfbar')).toEqual(['foo', 'bar']); + expect((0, _string().removeCommonPrefix)('foo', 'fbar')).toEqual(['oo', 'bar']); + expect((0, _string().removeCommonPrefix)('asdffoo', 'asdfbar')).toEqual(['foo', 'bar']); }); - it('works with the empty string', () => { - expect(removeCommonPrefix('', 'bar')).toEqual(['', 'bar']); - expect(removeCommonPrefix('foo', '')).toEqual(['foo', '']); - expect(removeCommonPrefix('', '')).toEqual(['', '']); + expect((0, _string().removeCommonPrefix)('', 'bar')).toEqual(['', 'bar']); + expect((0, _string().removeCommonPrefix)('foo', '')).toEqual(['foo', '']); + expect((0, _string().removeCommonPrefix)('', '')).toEqual(['', '']); }); - it('returns empty strings for identical strings', () => { - expect(removeCommonPrefix('foo', 'foo')).toEqual(['', '']); + expect((0, _string().removeCommonPrefix)('foo', 'foo')).toEqual(['', '']); }); }); - describe('removeCommonSuffix', () => { it('does nothing if there is no common suffix', () => { - expect(removeCommonSuffix('foo', 'bar')).toEqual(['foo', 'bar']); + expect((0, _string().removeCommonSuffix)('foo', 'bar')).toEqual(['foo', 'bar']); }); - it('removes a common suffix', () => { - expect(removeCommonSuffix('foo', 'baro')).toEqual(['fo', 'bar']); - expect(removeCommonSuffix('fooasdf', 'baroasdf')).toEqual(['fo', 'bar']); + expect((0, _string().removeCommonSuffix)('foo', 'baro')).toEqual(['fo', 'bar']); + expect((0, _string().removeCommonSuffix)('fooasdf', 'baroasdf')).toEqual(['fo', 'bar']); }); - it('works with the empty string', () => { - expect(removeCommonSuffix('', 'bar')).toEqual(['', 'bar']); - expect(removeCommonSuffix('foo', '')).toEqual(['foo', '']); - expect(removeCommonSuffix('', '')).toEqual(['', '']); + expect((0, _string().removeCommonSuffix)('', 'bar')).toEqual(['', 'bar']); + expect((0, _string().removeCommonSuffix)('foo', '')).toEqual(['foo', '']); + expect((0, _string().removeCommonSuffix)('', '')).toEqual(['', '']); }); - it('returns empty strings for identical strings', () => { - expect(removeCommonSuffix('foo', 'foo')).toEqual(['', '']); + expect((0, _string().removeCommonSuffix)('foo', 'foo')).toEqual(['', '']); }); }); - describe('shorten', () => { it('works', () => { - expect(shorten('', 1)).toEqual(''); - expect(shorten('test', 3)).toEqual('tes'); - expect(shorten('test', 100)).toEqual('test'); - expect(shorten('test', 1, '...')).toEqual('t...'); + expect((0, _string().shorten)('', 1)).toEqual(''); + expect((0, _string().shorten)('test', 3)).toEqual('tes'); + expect((0, _string().shorten)('test', 100)).toEqual('test'); + expect((0, _string().shorten)('test', 1, '...')).toEqual('t...'); }); }); - describe('splitOnce', () => { it('splits once', () => { - expect(splitOnce('ab-cd-ef', '-')).toEqual(['ab', 'cd-ef']); + expect((0, _string().splitOnce)('ab-cd-ef', '-')).toEqual(['ab', 'cd-ef']); }); it("handles when there's no match", () => { - expect(splitOnce('ab-cd-ef', '_')).toEqual(['ab-cd-ef', null]); + expect((0, _string().splitOnce)('ab-cd-ef', '_')).toEqual(['ab-cd-ef', null]); }); }); - describe('indent', () => { it('indents lines', () => { - expect(indent('a\nb')).toBe(' a\n b'); + expect((0, _string().indent)('a\nb')).toBe(' a\n b'); }); - it("doesn't indent empty lines", () => { - expect(indent('a\n\nb')).toBe(' a\n\n b'); + expect((0, _string().indent)('a\n\nb')).toBe(' a\n\n b'); }); - it('uses the provided level', () => { - expect(indent('a\n\nb', 4)).toBe(' a\n\n b'); + expect((0, _string().indent)('a\n\nb', 4)).toBe(' a\n\n b'); }); - it('uses the provided character', () => { - expect(indent('a\n\nb', 1, '\t')).toBe('\ta\n\n\tb'); + expect((0, _string().indent)('a\n\nb', 1, '\t')).toBe('\ta\n\n\tb'); }); }); - describe('pluralize', () => { it('works', () => { - expect(pluralize('test', 0)).toEqual('tests'); - expect(pluralize('test', 1)).toEqual('test'); - expect(pluralize('test', 2)).toEqual('tests'); - expect(pluralize('test', 123)).toEqual('tests'); + expect((0, _string().pluralize)('test', 0)).toEqual('tests'); + expect((0, _string().pluralize)('test', 1)).toEqual('test'); + expect((0, _string().pluralize)('test', 2)).toEqual('tests'); + expect((0, _string().pluralize)('test', 123)).toEqual('tests'); }); }); - describe('capitalize', () => { it('works', () => { - expect(capitalize('')).toEqual(''); - expect(capitalize('t')).toEqual('T'); - expect(capitalize('te')).toEqual('Te'); - expect(capitalize('test')).toEqual('Test'); + expect((0, _string().capitalize)('')).toEqual(''); + expect((0, _string().capitalize)('t')).toEqual('T'); + expect((0, _string().capitalize)('te')).toEqual('Te'); + expect((0, _string().capitalize)('test')).toEqual('Test'); }); }); - describe('getMatchRanges', () => { it('works', () => { - expect(getMatchRanges('test1test2test3', 'test')).toEqual([ - [0, 4], - [5, 9], - [10, 14], - ]); - expect(getMatchRanges('ttttttt', 'ttt')).toEqual([[0, 6]]); - expect(getMatchRanges('test1test2test3', 'none')).toEqual([]); - expect(getMatchRanges('test1test2test3', '')).toEqual([]); + expect((0, _string().getMatchRanges)('test1test2test3', 'test')).toEqual([[0, 4], [5, 9], [10, 14]]); + expect((0, _string().getMatchRanges)('ttttttt', 'ttt')).toEqual([[0, 6]]); + expect((0, _string().getMatchRanges)('test1test2test3', 'none')).toEqual([]); + expect((0, _string().getMatchRanges)('test1test2test3', '')).toEqual([]); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/symbol-definition-preview-test.js b/modules/nuclide-commons/__tests__/symbol-definition-preview-test.js index 07807fa7..236f35c4 100644 --- a/modules/nuclide-commons/__tests__/symbol-definition-preview-test.js +++ b/modules/nuclide-commons/__tests__/symbol-definition-preview-test.js @@ -1,3 +1,47 @@ +"use strict"; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _dedent() { + const data = _interopRequireDefault(require("dedent")); + + _dedent = function () { + return data; + }; + + return data; +} + +function _simpleTextBuffer() { + const data = require("simple-text-buffer"); + + _simpleTextBuffer = function () { + return data; + }; + + return data; +} + +function _symbolDefinitionPreview() { + const data = require("../symbol-definition-preview"); + + _symbolDefinitionPreview = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,180 +50,150 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import nuclideUri from '../nuclideUri'; -import dedent from 'dedent'; -import {Point} from 'simple-text-buffer'; -import {getDefinitionPreview} from '../symbol-definition-preview'; -import invariant from 'assert'; - -function javascriptFixtureDefinitionWithPoint(point: Point) { +function javascriptFixtureDefinitionWithPoint(point) { return { - path: nuclideUri.join( - __dirname, - '../__mocks__', - 'fixtures', - 'symbol-definition-preview-sample.js', - ), + path: _nuclideUri().default.join(__dirname, '../__mocks__', 'fixtures', 'symbol-definition-preview-sample.js'), language: 'javascript', - position: point, + position: point }; } -function pythonFixtureDefinitionWithPoint(point: Point) { +function pythonFixtureDefinitionWithPoint(point) { return { - path: nuclideUri.join( - __dirname, - '../__mocks__', - 'fixtures', - 'symbol-definition-preview-sample.py', - ), + path: _nuclideUri().default.join(__dirname, '../__mocks__', 'fixtures', 'symbol-definition-preview-sample.py'), language: 'python', - position: point, + position: point }; } describe('getDefinitionPreview', () => { describe('Constant symbols', () => { it('returns the only line of a one-line symbol', async () => { - const preview = await getDefinitionPreview( - javascriptFixtureDefinitionWithPoint(new Point(11, 6)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(javascriptFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(11, 6))); expect(preview).not.toBeNull(); - invariant(preview != null); + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + expect(preview.contents).toEqual('const A_CONSTANT = 42;'); }); - it('returns the entire multi-line symbol', async () => { - const preview = await getDefinitionPreview( - javascriptFixtureDefinitionWithPoint(new Point(15, 6)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(javascriptFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(15, 6))); expect(preview).not.toBeNull(); - invariant(preview != null); - expect(preview.contents).toEqual( - dedent`const A_MULTILINE_CONST = \` + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + + expect(preview.contents).toEqual(_dedent().default`const A_MULTILINE_CONST = \` hey look I span multiple lines - \`;`, - ); + \`;`); }); }); - describe('Type symbols', () => { it('returns an entire multi-line type', async () => { - const preview = await getDefinitionPreview( - javascriptFixtureDefinitionWithPoint(new Point(21, 5)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(javascriptFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(21, 5))); expect(preview).not.toBeNull(); - invariant(preview != null); - expect(preview.contents).toEqual( - dedent`type Something = { + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + + expect(preview.contents).toEqual(_dedent().default`type Something = { name: string, age?: number, - };`, - ); + };`); }); - it('returns only the property from within a type', async () => { - const preview = await getDefinitionPreview( - javascriptFixtureDefinitionWithPoint(new Point(44, 4)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(javascriptFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(44, 4))); expect(preview).not.toBeNull(); - invariant(preview != null); + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + expect(preview.contents).toEqual('name: string,'); }); - it('returns property and value of a complex type within a type', async () => { - const preview = await getDefinitionPreview( - javascriptFixtureDefinitionWithPoint(new Point(43, 2)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(javascriptFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(43, 2))); expect(preview).not.toBeNull(); - invariant(preview != null); - expect(preview.contents).toEqual( - dedent`properties: { + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + + expect(preview.contents).toEqual(_dedent().default`properties: { name: string, age?: number, - },`, - ); + },`); }); }); - describe('Function symbols', () => { it('returns just one line if parens are balanced on the first line', async () => { - const preview = await getDefinitionPreview( - javascriptFixtureDefinitionWithPoint(new Point(26, 16)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(javascriptFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(26, 16))); expect(preview).not.toBeNull(); - invariant(preview != null); - expect(preview.contents).toEqual( - 'export function aSingleLineFunctionSignature() {', - ); - }); - it('works without parentheses as with python', async () => { - const preview = await getDefinitionPreview( - pythonFixtureDefinitionWithPoint(new Point(7, 4)), - ); + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + expect(preview.contents).toEqual('export function aSingleLineFunctionSignature() {'); + }); + it('works without parentheses as with python', async () => { + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(pythonFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(7, 4))); expect(preview).not.toBeNull(); - invariant(preview != null); + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + expect(preview.contents).toEqual('def foo(bar=27):'); }); - it('works without parentheses but with braces as with python', async () => { - const preview = await getDefinitionPreview( - pythonFixtureDefinitionWithPoint(new Point(11, 4)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(pythonFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(11, 4))); expect(preview).not.toBeNull(); - invariant(preview != null); - expect(preview.contents).toEqual( - dedent`def baz(test={ + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + + expect(preview.contents).toEqual(_dedent().default`def baz(test={ 'one': 'two' - }):`, - ); + }):`); }); - it("doesn't dedent beyond the current lines indentation level", async () => { - const preview = await getDefinitionPreview( - javascriptFixtureDefinitionWithPoint(new Point(36, 18)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(javascriptFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(36, 18))); expect(preview).not.toBeNull(); - invariant(preview != null); - expect(preview.contents).toEqual( - dedent` + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + + expect(preview.contents).toEqual(_dedent().default` export function aPoorlyIndentedFunction( aReallyReallyLongArgumentNameThatWouldRequireThisToBreakAcrossMultipleLines: Something, ): number { - `, - ); + `); }); - it('reads until the indentation returns to initial and parens are balanced', async () => { - const preview = await getDefinitionPreview( - javascriptFixtureDefinitionWithPoint(new Point(30, 16)), - ); - + const preview = await (0, _symbolDefinitionPreview().getDefinitionPreview)(javascriptFixtureDefinitionWithPoint(new (_simpleTextBuffer().Point)(30, 16))); expect(preview).not.toBeNull(); - invariant(preview != null); - expect(preview.contents).toEqual( - dedent` + + if (!(preview != null)) { + throw new Error("Invariant violation: \"preview != null\""); + } + + expect(preview.contents).toEqual(_dedent().default` export function aMultiLineFunctionSignature( aReallyReallyLongArgumentNameThatWouldRequireThisToBreakAcrossMultipleLines: Something, ): number { - `, - ); + `); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/test-helpers-test.js b/modules/nuclide-commons/__tests__/test-helpers-test.js index cfafefca..080a421b 100644 --- a/modules/nuclide-commons/__tests__/test-helpers-test.js +++ b/modules/nuclide-commons/__tests__/test-helpers-test.js @@ -1,3 +1,39 @@ +"use strict"; + +var _fs = _interopRequireDefault(require("fs")); + +function _glob() { + const data = _interopRequireDefault(require("glob")); + + _glob = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _testHelpers() { + const data = require("../test-helpers"); + + _testHelpers = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,172 +42,149 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import typeof * as TestModuleType from '../__mocks__/fixtures/toBeTested'; - -import fs from 'fs'; -import glob from 'glob'; -import nuclideUri from '../nuclideUri'; -import { - arePropertiesEqual, - clearRequireCache, - expectAsyncFailure, - generateFixture, - uncachedRequire, -} from '../test-helpers'; - describe('arePropertiesEqual', () => { it('correctly compares empty objects', () => { - expect(arePropertiesEqual({}, {})).toBe(true); + expect((0, _testHelpers().arePropertiesEqual)({}, {})).toBe(true); }); - it('correctly compares objects with the same properties', () => { - expect(arePropertiesEqual({foo: 5}, {foo: 5})).toBe(true); + expect((0, _testHelpers().arePropertiesEqual)({ + foo: 5 + }, { + foo: 5 + })).toBe(true); }); - it('allows one property to be undefined while another does not exist at all', () => { - expect(arePropertiesEqual({foo: undefined}, {})).toBe(true); + expect((0, _testHelpers().arePropertiesEqual)({ + foo: undefined + }, {})).toBe(true); }); - it('returns false when properties are not equal', () => { - expect(arePropertiesEqual({foo: 5}, {foo: 4})).toBe(false); + expect((0, _testHelpers().arePropertiesEqual)({ + foo: 5 + }, { + foo: 4 + })).toBe(false); }); - it('returns false when one property is undefined and another is defined', () => { - expect(arePropertiesEqual({foo: 5}, {foo: undefined})).toBe(false); - expect(arePropertiesEqual({foo: undefined}, {foo: 5})).toBe(false); + expect((0, _testHelpers().arePropertiesEqual)({ + foo: 5 + }, { + foo: undefined + })).toBe(false); + expect((0, _testHelpers().arePropertiesEqual)({ + foo: undefined + }, { + foo: 5 + })).toBe(false); }); - it('returns false when one property exists but the other does not', () => { - expect(arePropertiesEqual({foo: 5}, {})).toBe(false); - expect(arePropertiesEqual({}, {foo: 5})).toBe(false); + expect((0, _testHelpers().arePropertiesEqual)({ + foo: 5 + }, {})).toBe(false); + expect((0, _testHelpers().arePropertiesEqual)({}, { + foo: 5 + })).toBe(false); }); }); - describe('expectAsyncFailure', () => { it('fails when provided Promise succeeds', async () => { - const verify: any = jest.fn(); - await expect( - expectAsyncFailure(Promise.resolve('resolved, not rejected!'), verify), - ).rejects.toThrow(/but did not/); + const verify = jest.fn(); + await expect((0, _testHelpers().expectAsyncFailure)(Promise.resolve('resolved, not rejected!'), verify)).rejects.toThrow(/but did not/); expect(verify.mock.calls).toHaveLength(0); }); - it('fails when provided Promise fails but with wrong error message', async () => { let callCount = 0; + function verify(error) { ++callCount; const expectedMessage = 'I failed badly.'; + if (error.message !== expectedMessage) { - throw new Error( - `Expected '${expectedMessage}', but was ${error.message}.`, - ); + throw new Error(`Expected '${expectedMessage}', but was ${error.message}.`); } } - await expect( - expectAsyncFailure(Promise.reject(Error('I failed.')), verify), - ).rejects.toThrow(/I failed/); + await expect((0, _testHelpers().expectAsyncFailure)(Promise.reject(Error('I failed.')), verify)).rejects.toThrow(/I failed/); expect(callCount).toBe(1); }); - it('succeeds when provided Promise fails in the expected way', async () => { let callCount = 0; + function verify(error) { ++callCount; const expectedMessage = 'I failed badly.'; + if (error.message !== expectedMessage) { - throw new Error( - `Expected '${expectedMessage}', but was ${error.message}.`, - ); + throw new Error(`Expected '${expectedMessage}', but was ${error.message}.`); } } - await expectAsyncFailure(Promise.reject(Error('I failed badly.')), verify); + await (0, _testHelpers().expectAsyncFailure)(Promise.reject(Error('I failed badly.')), verify); expect(callCount).toBe(1); }); }); - describe('generateFixture', () => { it('should create the directory hierarchy', async () => { - const fixturePath = await generateFixture( - 'fixture-to-generate', - new Map([['foo.js', undefined], ['bar/baz.txt', 'some text']]), - ); + const fixturePath = await (0, _testHelpers().generateFixture)('fixture-to-generate', new Map([['foo.js', undefined], ['bar/baz.txt', 'some text']])); + expect(_nuclideUri().default.isAbsolute(fixturePath)).toBe(true); + expect(_fs.default.statSync(fixturePath).isDirectory()).toBe(true); - expect(nuclideUri.isAbsolute(fixturePath)).toBe(true); - expect(fs.statSync(fixturePath).isDirectory()).toBe(true); + const fooPath = _nuclideUri().default.join(fixturePath, 'foo.js'); - const fooPath = nuclideUri.join(fixturePath, 'foo.js'); - const bazPath = nuclideUri.join(fixturePath, 'bar/baz.txt'); + const bazPath = _nuclideUri().default.join(fixturePath, 'bar/baz.txt'); - expect(fs.statSync(fooPath).isFile()).toBe(true); - expect(fs.statSync(bazPath).isFile()).toBe(true); - - expect(fs.readFileSync(fooPath, 'utf8')).toBe(''); - expect(fs.readFileSync(bazPath, 'utf8')).toBe('some text'); + expect(_fs.default.statSync(fooPath).isFile()).toBe(true); + expect(_fs.default.statSync(bazPath).isFile()).toBe(true); + expect(_fs.default.readFileSync(fooPath, 'utf8')).toBe(''); + expect(_fs.default.readFileSync(bazPath, 'utf8')).toBe('some text'); }); + it('should work with lots of files', async () => { + const files = new Map(); - it( - 'should work with lots of files', - async () => { - const files = new Map(); - for (let i = 0; i < 10; i++) { - for (let j = 0; j < 300; j++) { - files.set(`dir_${i}/file_${j}.txt`, `${i} + ${j} = ${i + j}`); - } + for (let i = 0; i < 10; i++) { + for (let j = 0; j < 300; j++) { + files.set(`dir_${i}/file_${j}.txt`, `${i} + ${j} = ${i + j}`); } - const fixturePath = await generateFixture('lots-of-files', files); - const fixtureFiles = glob.sync( - nuclideUri.join(fixturePath, 'dir_*/file_*.txt'), - ); - expect(fixtureFiles.length).toBe(3000); - }, - 20000, - ); + } + const fixturePath = await (0, _testHelpers().generateFixture)('lots-of-files', files); + + const fixtureFiles = _glob().default.sync(_nuclideUri().default.join(fixturePath, 'dir_*/file_*.txt')); + + expect(fixtureFiles.length).toBe(3000); + }, 20000); it('should work with no files', async () => { - const fixturePath = await generateFixture('fixture-empty', new Map()); - expect(nuclideUri.isAbsolute(fixturePath)).toBe(true); - expect(fs.statSync(fixturePath).isDirectory()).toBe(true); - expect(fs.readdirSync(fixturePath)).toEqual([]); + const fixturePath = await (0, _testHelpers().generateFixture)('fixture-empty', new Map()); + expect(_nuclideUri().default.isAbsolute(fixturePath)).toBe(true); + expect(_fs.default.statSync(fixturePath).isDirectory()).toBe(true); + expect(_fs.default.readdirSync(fixturePath)).toEqual([]); }); - it('works with no files arg', async () => { - const fixturePath = await generateFixture('fixture-empty'); - expect(nuclideUri.isAbsolute(fixturePath)).toBe(true); - expect(fs.statSync(fixturePath).isDirectory()).toBe(true); - expect(fs.readdirSync(fixturePath)).toEqual([]); + const fixturePath = await (0, _testHelpers().generateFixture)('fixture-empty'); + expect(_nuclideUri().default.isAbsolute(fixturePath)).toBe(true); + expect(_fs.default.statSync(fixturePath).isDirectory()).toBe(true); + expect(_fs.default.readdirSync(fixturePath)).toEqual([]); }); }); - describe('Mocking Imports test suite', () => { // Tests ToBeTested.functionToTest while mocking imported function toBeMocked. it('Mocking imported dependencies', () => { // 1 - First mock all functions imported by the module under test - const mock = jest - .spyOn(require('../__mocks__/fixtures/toBeMocked'), 'importedFunction') - .mockReturnValue(45); - - // 2 - Do an uncachedRequire of the module to test + const mock = jest.spyOn(require("../__mocks__/fixtures/toBeMocked"), 'importedFunction').mockReturnValue(45); // 2 - Do an uncachedRequire of the module to test // Note the 'import typeof * as ... ' above to get type checking // for the functions to be tested. // You may want to put steps 1 & 2 in your beforeEach. - const moduleToTest: TestModuleType = (uncachedRequire( - require, - '../__mocks__/fixtures/toBeTested', - ): any); - // 3 - Perform your test + const moduleToTest = (0, _testHelpers().uncachedRequire)(require, '../__mocks__/fixtures/toBeTested'); // 3 - Perform your test + const result = moduleToTest.functionToTest(); expect(mock).toHaveBeenCalledWith(42); - expect(result).toEqual(45); - - // 4 - Reset the require cache so your mocks don't get used for other tests. + expect(result).toEqual(45); // 4 - Reset the require cache so your mocks don't get used for other tests. // You may want to put this in your afterEach. - clearRequireCache(require, '../__mocks__/fixtures/toBeTested'); + + (0, _testHelpers().clearRequireCache)(require, '../__mocks__/fixtures/toBeTested'); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/__tests__/which-test.js b/modules/nuclide-commons/__tests__/which-test.js index 5fb47fc3..b7e30de9 100644 --- a/modules/nuclide-commons/__tests__/which-test.js +++ b/modules/nuclide-commons/__tests__/which-test.js @@ -1,3 +1,19 @@ +"use strict"; + +function _which() { + const data = _interopRequireDefault(require("../which")); + + _which = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,80 +22,78 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import which from '../which'; -import {Observable} from 'rxjs'; - describe('which', () => { let runCommand; let runCommandReturn = ''; - beforeEach(() => { runCommandReturn = ''; - runCommand = jest - .spyOn(require('../process'), 'runCommand') - .mockImplementation(() => Observable.of(runCommandReturn)); + runCommand = jest.spyOn(require("../process"), 'runCommand').mockImplementation(() => _RxMin.Observable.of(runCommandReturn)); }); - afterEach(() => { // $FlowFixMe - require('../process').runCommand.mockRestore(); + require("../process").runCommand.mockRestore(); }); - describe('on windows', () => { - const real_platform: string = process.platform; + const real_platform = process.platform; const eol = '\r\n'; + const os = require('os'); + const real_eol = os.EOL; beforeEach(() => { - Object.defineProperty(process, 'platform', {value: 'win32'}); + Object.defineProperty(process, 'platform', { + value: 'win32' + }); os.EOL = eol; }); afterEach(() => { - Object.defineProperty(process, 'platform', {value: real_platform}); + Object.defineProperty(process, 'platform', { + value: real_platform + }); os.EOL = real_eol; }); - it('calls where on Windows', () => { - const param: string = ''; - which(param); + const param = ''; + (0, _which().default)(param); expect(runCommand).toHaveBeenCalledWith('where', [''], {}); }); - it('returns the first match', async () => { runCommandReturn = 'hello' + os.EOL + 'hello.exe' + os.EOL; - const ret = await which('bla'); + const ret = await (0, _which().default)('bla'); expect(ret).toEqual('hello'); }); }); - describe('on linux', () => { - const real_platform: string = process.platform; + const real_platform = process.platform; const eol = '\n'; + const os = require('os'); + const real_eol = os.EOL; beforeEach(() => { - Object.defineProperty(process, 'platform', {value: 'linux'}); + Object.defineProperty(process, 'platform', { + value: 'linux' + }); os.EOL = eol; }); afterEach(() => { - Object.defineProperty(process, 'platform', {value: real_platform}); + Object.defineProperty(process, 'platform', { + value: real_platform + }); os.EOL = real_eol; }); - it('calls which', () => { - const param: string = ''; - which(param); + const param = ''; + (0, _which().default)(param); expect(runCommand).toHaveBeenCalledWith('which', [param], {}); }); - it('returns the first match', async () => { runCommandReturn = 'hello' + os.EOL + '/bin/hello' + os.EOL; - const ret = await which('bla'); + const ret = await (0, _which().default)('bla'); expect(ret).toEqual('hello'); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-commons/analytics.js b/modules/nuclide-commons/analytics.js index 253b7bfe..e76d36c0 100644 --- a/modules/nuclide-commons/analytics.js +++ b/modules/nuclide-commons/analytics.js @@ -1,3 +1,52 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.track = track; +exports.isTrackSupported = isTrackSupported; +exports.trackImmediate = trackImmediate; +exports.trackEvent = trackEvent; +exports.trackEvents = trackEvents; +exports.trackSampled = trackSampled; +exports.startTracking = startTracking; +exports.trackTiming = trackTiming; +exports.trackTimingSampled = trackTimingSampled; +exports.setRawAnalyticsService = setRawAnalyticsService; +exports.default = exports.TimingTracker = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("./UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("./promise"); + + _promise = function () { + return data; + }; + + return data; +} + +function _performanceNow() { + const data = _interopRequireDefault(require("./performanceNow")); + + _performanceNow = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,38 +55,13 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +let rawAnalyticsService = { + track() {}, -import type {Observable} from 'rxjs'; - -import UniversalDisposable from './UniversalDisposable'; -import {isPromise} from './promise'; -import performanceNow from './performanceNow'; - -export type RawAnalyticsService = { - track( - eventName: string, - values?: {[key: string]: mixed}, - immediate?: boolean, - ): ?Promise, - isTrackSupported: () => boolean, -}; - -let rawAnalyticsService: RawAnalyticsService = { - track(): ?Promise {}, - isTrackSupported: () => false, -}; - -export type TrackingEvent = { - type: string, - data?: Object, -}; - -export type TrackEvent = { - key: string, - values: {[key: string]: mixed}, + isTrackSupported: () => false }; /** @@ -47,121 +71,107 @@ export type TrackEvent = { * @param eventName Name of the event to be tracked. * @param values The object containing the data to track. */ -export function track( - eventName: string, - values?: {[key: string]: mixed}, -): void { +function track(eventName, values) { rawAnalyticsService.track(eventName, values || {}); } -export function isTrackSupported(): boolean { +function isTrackSupported() { return rawAnalyticsService.isTrackSupported(); } - /** * Same as `track`, except this is guaranteed to send immediately. * The returned promise will resolve when the request completes (or reject on failure). */ -export function trackImmediate( - eventName: string, - values?: {[key: string]: mixed}, -): Promise { - return ( - rawAnalyticsService.track(eventName, values || {}, true) || - Promise.resolve() - ); -} + +function trackImmediate(eventName, values) { + return rawAnalyticsService.track(eventName, values || {}, true) || Promise.resolve(); +} /** * An alternative interface for `track` that accepts a single event object. This is particularly * useful when dealing with streams (Observables). */ -export function trackEvent(event: TrackingEvent): void { + + +function trackEvent(event) { track(event.type, event.data); } - /** * Track each event in a stream of TrackingEvents. */ -export function trackEvents(events: Observable): IDisposable { - return new UniversalDisposable(events.subscribe(trackEvent)); -} + +function trackEvents(events) { + return new (_UniversalDisposable().default)(events.subscribe(trackEvent)); +} /** * A sampled version of track that only tracks every 1/sampleRate calls. */ -export function trackSampled( - eventName: string, - sampleRate: number, - values?: {[key: string]: mixed}, -): void { + + +function trackSampled(eventName, sampleRate, values) { if (Math.random() * sampleRate <= 1) { - rawAnalyticsService.track(eventName, { - ...values, - sample_rate: sampleRate, - }); + rawAnalyticsService.track(eventName, Object.assign({}, values, { + sample_rate: sampleRate + })); } } const PERFORMANCE_EVENT = 'performance'; const canMeasure = typeof performance !== 'undefined'; -export class TimingTracker { - static eventCount = 0; - _eventName: string; - _startTime: number; - _startMark: string; - _values: {[key: string]: mixed}; - - constructor(eventName: string, values: {[key: string]: mixed}) { +class TimingTracker { + constructor(eventName, values) { this._eventName = eventName; this._startMark = `${this._eventName}_${TimingTracker.eventCount++}_start`; - this._startTime = performanceNow(); + this._startTime = (0, _performanceNow().default)(); this._values = values; + if (canMeasure) { // eslint-disable-next-line no-undef performance.mark(this._startMark); } } - onError(error: Error): void { + onError(error) { this._trackTimingEvent(error); } - onSuccess(): void { - this._trackTimingEvent(/* error */ null); + onSuccess() { + this._trackTimingEvent( + /* error */ + null); } - _trackTimingEvent(exception: ?Error): void { + _trackTimingEvent(exception) { if (canMeasure) { /* eslint-disable no-undef */ // call measure to add this information to the devtools timeline in the // case the profiler is running. - performance.measure(this._eventName, this._startMark); - // then clear all the marks and measurements to avoid growing the + performance.measure(this._eventName, this._startMark); // then clear all the marks and measurements to avoid growing the // performance entry buffer + performance.clearMarks(this._startMark); performance.clearMeasures(this._eventName); /* eslint-enable no-undef */ } - track(PERFORMANCE_EVENT, { - ...this._values, - duration: Math.round(performanceNow() - this._startTime).toString(), + track(PERFORMANCE_EVENT, Object.assign({}, this._values, { + duration: Math.round((0, _performanceNow().default)() - this._startTime).toString(), eventName: this._eventName, error: exception ? '1' : '0', - exception: exception ? exception.toString() : '', - }); + exception: exception ? exception.toString() : '' + })); } + } -export function startTracking( - eventName: string, - values?: {[key: string]: any} = {}, -): TimingTracker { +exports.TimingTracker = TimingTracker; +TimingTracker.eventCount = 0; + +function startTracking(eventName, values = {}) { return new TimingTracker(eventName, values); } - /** * Reports analytics including timing for a single operation. * @@ -171,31 +181,25 @@ export function startTracking( * * Returns (or throws) the result of the operation. */ -export function trackTiming( - eventName: string, - operation: () => T, - values?: {[key: string]: any} = {}, -): T { + + +function trackTiming(eventName, operation, values = {}) { const tracker = startTracking(eventName, values); try { const result = operation(); - if (isPromise(result)) { + if ((0, _promise().isPromise)(result)) { // Atom uses a different Promise implementation than Nuclide, so the following is not true: // invariant(result instanceof Promise); - // For the method returning a Promise, track the time after the promise is resolved/rejected. - return (result: any).then( - value => { - tracker.onSuccess(); - return value; - }, - reason => { - tracker.onError(reason instanceof Error ? reason : new Error(reason)); - return Promise.reject(reason); - }, - ); + return result.then(value => { + tracker.onSuccess(); + return value; + }, reason => { + tracker.onError(reason instanceof Error ? reason : new Error(reason)); + return Promise.reject(reason); + }); } else { tracker.onSuccess(); return result; @@ -205,37 +209,32 @@ export function trackTiming( throw error; } } - /** * A sampled version of trackTiming that only tracks every 1/sampleRate calls. */ -export function trackTimingSampled( - eventName: string, - operation: () => T, - sampleRate: number, - values?: {[key: string]: any} = {}, -): T { + + +function trackTimingSampled(eventName, operation, sampleRate, values = {}) { if (Math.random() * sampleRate <= 1) { - return trackTiming(eventName, operation, { - ...values, - sample_rate: sampleRate, - }); + return trackTiming(eventName, operation, Object.assign({}, values, { + sample_rate: sampleRate + })); } + return operation(); } -export function setRawAnalyticsService( - analyticsService: RawAnalyticsService, -): void { +function setRawAnalyticsService(analyticsService) { rawAnalyticsService = analyticsService; } -export default { +var _default = { track, trackSampled, trackEvent, trackTiming, trackTimingSampled, startTracking, - TimingTracker, + TimingTracker }; +exports.default = _default; \ No newline at end of file diff --git a/modules/nuclide-commons/cache.js b/modules/nuclide-commons/cache.js index 9a182061..f139251b 100644 --- a/modules/nuclide-commons/cache.js +++ b/modules/nuclide-commons/cache.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DISPOSE_VALUE = exports.Cache = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,49 +15,41 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import {Observable, Subject} from 'rxjs'; - // A Cache mapping keys to values which creates entries as they are requested. -export class Cache { - _values: Map; - _factory: (key: KeyType) => ValueType; - _disposeValue: (value: ValueType) => mixed; - _entriesSubject: Subject<[KeyType, ValueType]>; - - constructor( - factory: (key: KeyType) => ValueType, - disposeValue: (value: ValueType) => mixed = value => {}, - ) { +class Cache { + constructor(factory, disposeValue = value => {}) { this._values = new Map(); this._factory = factory; this._disposeValue = disposeValue; - this._entriesSubject = new Subject(); + this._entriesSubject = new _RxMin.Subject(); } - has(key: KeyType): boolean { + has(key) { return this._values.has(key); } - get(key: KeyType): ValueType { + get(key) { if (!this._values.has(key)) { const newValue = this._factory(key); + this._values.set(key, newValue); + this._entriesSubject.next([key, newValue]); + return newValue; } else { // Cannot use invariant as ValueType may include null/undefined. - return (this._values.get(key): any); + return this._values.get(key); } - } - - // After this method this._values.keys() === newKeys. + } // After this method this._values.keys() === newKeys. // deletes all keys not in newKeys // gets all keys in newKeys - setKeys(newKeys: Set): void { + + + setKeys(newKeys) { for (const existingKey of this._values.keys()) { if (!newKeys.has(existingKey)) { this.delete(existingKey); @@ -60,60 +61,67 @@ export class Cache { } } - entries(): Iterator<[KeyType, ValueType]> { + entries() { return this._values.entries(); } - keys(): Iterator { + keys() { return this._values.keys(); } - values(): Iterator { + values() { return this._values.values(); } - observeValues(): Observable { + observeValues() { return this.observeEntries().map(entry => entry[1]); } - observeEntries(): Observable<[KeyType, ValueType]> { - return Observable.concat( - Observable.from(this._values.entries()), - this._entriesSubject, - ); + observeEntries() { + return _RxMin.Observable.concat(_RxMin.Observable.from(this._values.entries()), this._entriesSubject); } - observeKeys(): Observable { + observeKeys() { return this.observeEntries().map(entry => entry[0]); } - delete(key: KeyType): boolean { + delete(key) { if (this.has(key)) { const value = this.get(key); + this._values.delete(key); + this._disposeValue(value); + return true; } else { return false; } } - clear(): void { + clear() { // Defend against a dispose call removing elements from the Cache. const values = this._values; this._values = new Map(); + for (const value of values.values()) { this._disposeValue(value); } } - dispose(): void { + dispose() { this.clear(); + this._entriesSubject.complete(); } -} -// Useful for optional second parameter to Cache constructor. -export const DISPOSE_VALUE = (value: IDisposable) => { +} // Useful for optional second parameter to Cache constructor. + + +exports.Cache = Cache; + +const DISPOSE_VALUE = value => { value.dispose(); }; + +exports.DISPOSE_VALUE = DISPOSE_VALUE; \ No newline at end of file diff --git a/modules/nuclide-commons/collection.js b/modules/nuclide-commons/collection.js index 8af526a6..099c132b 100644 --- a/modules/nuclide-commons/collection.js +++ b/modules/nuclide-commons/collection.js @@ -1,3 +1,54 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ensureArray = ensureArray; +exports.arrayRemove = arrayRemove; +exports.arrayEqual = arrayEqual; +exports.arrayCompact = arrayCompact; +exports.arrayFlatten = arrayFlatten; +exports.arrayUnique = arrayUnique; +exports.arrayFindLastIndex = arrayFindLastIndex; +exports.findSubArrayIndex = findSubArrayIndex; +exports.mapUnion = mapUnion; +exports.mapCompact = mapCompact; +exports.mapFilter = mapFilter; +exports.mapTransform = mapTransform; +exports.mapEqual = mapEqual; +exports.mapGetWithDefault = mapGetWithDefault; +exports.areSetsEqual = areSetsEqual; +exports.every = every; +exports.setIntersect = setIntersect; +exports.setUnion = setUnion; +exports.setDifference = setDifference; +exports.setFilter = setFilter; +exports.isEmpty = isEmpty; +exports.keyMirror = keyMirror; +exports.collect = collect; +exports.objectFromPairs = objectFromPairs; +exports.objectMapValues = objectMapValues; +exports.objectValues = objectValues; +exports.objectEntries = objectEntries; +exports.objectFromMap = objectFromMap; +exports.concatIterators = concatIterators; +exports.someOfIterable = someOfIterable; +exports.findInIterable = findInIterable; +exports.filterIterable = filterIterable; +exports.mapIterable = mapIterable; +exports.takeIterable = takeIterable; +exports.range = range; +exports.firstOfIterable = firstOfIterable; +exports.iterableIsEmpty = iterableIsEmpty; +exports.iterableContains = iterableContains; +exports.count = count; +exports.isIterable = isIterable; +exports.insideOut = insideOut; +exports.mapFromObject = mapFromObject; +exports.lastFromArray = lastFromArray; +exports.distinct = distinct; +exports.DefaultMap = exports.MultiMap = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,202 +57,198 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -export function ensureArray(x: Array | T): Array { +function ensureArray(x) { return Array.isArray(x) ? x : [x]; } -export function arrayRemove(array: Array, element: T): void { +function arrayRemove(array, element) { const index = array.indexOf(element); + if (index >= 0) { array.splice(index, 1); } } -export function arrayEqual( - array1: Array, - array2: Array, - equalComparator?: (a: T, b: T) => boolean, -): boolean { +function arrayEqual(array1, array2, equalComparator) { if (array1 === array2) { return true; } + if (array1.length !== array2.length) { return false; } - const equalFunction = equalComparator || ((a: T, b: T) => a === b); + + const equalFunction = equalComparator || ((a, b) => a === b); + return array1.every((item1, i) => equalFunction(item1, array2[i])); } - /** * Returns a copy of the input Array with all `null` and `undefined` values filtered out. * Allows Flow to typecheck the common `filter(x => x != null)` pattern. */ -export function arrayCompact(array: Array): Array { + + +function arrayCompact(array) { const result = []; + for (const elem of array) { if (elem != null) { result.push(elem); } } + return result; } - /** * Flattens an Array> into just an Array */ -export function arrayFlatten(array: Array>): Array { + + +function arrayFlatten(array) { const result = []; + for (const subArray of array) { result.push(...subArray); } + return result; } - /** * Removes duplicates from Array. * Uses SameValueZero for equality purposes, which is like '===' except it deems * two NaNs equal. http://www.ecma-international.org/ecma-262/6.0/#sec-samevaluezero */ -export function arrayUnique(array: Array): Array { + + +function arrayUnique(array) { return Array.from(new Set(array)); } - /** * Returns the last index in the input array that matches the predicate. * Returns -1 if no match is found. */ -export function arrayFindLastIndex( - array: Array, - predicate: (elem: T, index: number, array: Array) => boolean, - thisArg?: any, -): number { + + +function arrayFindLastIndex(array, predicate, thisArg) { for (let i = array.length - 1; i >= 0; i--) { if (predicate.call(thisArg, array[i], i, array)) { return i; } } + return -1; } - /** * Return the first index in array where subarray is equal to the next * subarray-sized slice of array. Return -1 if no match is found. */ -export function findSubArrayIndex( - array: Array, - subarr: Array, -): number { - return array.findIndex((_, offset) => - arrayEqual(array.slice(offset, offset + subarr.length), subarr), - ); -} + +function findSubArrayIndex(array, subarr) { + return array.findIndex((_, offset) => arrayEqual(array.slice(offset, offset + subarr.length), subarr)); +} /** * Merges a given arguments of maps into one Map, with the latest maps * overriding the values of the prior maps. */ -export function mapUnion(...maps: Array>): Map { + + +function mapUnion(...maps) { const unionMap = new Map(); + for (const map of maps) { for (const [key, value] of map) { unionMap.set(key, value); } } + return unionMap; } -export function mapCompact(map: Map): Map { +function mapCompact(map) { const selected = new Map(); + for (const [key, value] of map) { if (value != null) { selected.set(key, value); } } + return selected; } -export function mapFilter( - map: Map, - selector: (key: T, value: X) => boolean, -): Map { +function mapFilter(map, selector) { const selected = new Map(); + for (const [key, value] of map) { if (selector(key, value)) { selected.set(key, value); } } + return selected; } -export function mapTransform( - src: Map, - transform: (value: V1, key: T) => V2, -): Map { +function mapTransform(src, transform) { const result = new Map(); + for (const [key, value] of src) { result.set(key, transform(value, key)); } + return result; } -export function mapEqual( - map1: Map, - map2: Map, - equalComparator?: (val1: X, val2: X, key1?: T, key2?: T) => boolean, -) { +function mapEqual(map1, map2, equalComparator) { if (map1.size !== map2.size) { return false; } - const equalFunction = equalComparator || ((a: X, b: X) => a === b); + + const equalFunction = equalComparator || ((a, b) => a === b); + for (const [key1, value1] of map1) { - if (!map2.has(key1) || !equalFunction(value1, (map2.get(key1): any))) { + if (!map2.has(key1) || !equalFunction(value1, map2.get(key1))) { return false; } } + return true; } -export function mapGetWithDefault( - map: Map, - key: K, - default_: V, -): V { +function mapGetWithDefault(map, key, default_) { if (map.has(key)) { // Cast through `any` since map.get's return is a maybe type. We can't just get the value and // check it against `null`, since null/undefined may inhabit V. We know this is safe since we // just checked that the map has the key. - return (map.get(key): any); + return map.get(key); } else { return default_; } } -export function areSetsEqual(a: Set, b: Set): boolean { +function areSetsEqual(a, b) { return a.size === b.size && every(a, element => b.has(element)); -} +} // Array.every but for any iterable. + -// Array.every but for any iterable. -export function every( - values: Iterable, - predicate: (element: T) => boolean, -): boolean { +function every(values, predicate) { for (const element of values) { if (!predicate(element)) { return false; } } + return true; } -export function setIntersect(a: Set, b: Set): Set { +function setIntersect(a, b) { return setFilter(a, e => b.has(e)); } -function setUnionTwo(a: Set, b: Set): Set { +function setUnionTwo(a, b) { // Avoids the extra Array allocations that `new Set([...a, ...b])` would incur. Some quick tests // indicate it would be about 60% slower. const result = new Set(a); @@ -211,30 +258,29 @@ function setUnionTwo(a: Set, b: Set): Set { return result; } -export function setUnion(...sets: Array>): Set { +function setUnion(...sets) { if (sets.length < 1) { return new Set(); } - const setReducer = (accumulator: Set, current: Set): Set => { + const setReducer = (accumulator, current) => { return setUnionTwo(accumulator, current); }; return sets.reduce(setReducer); } -export function setDifference( - a: Set, - b: Set, - hash_?: (v: T) => any, -): Set { +function setDifference(a, b, hash_) { if (a.size === 0) { return new Set(); } else if (b.size === 0) { return new Set(a); } + const result = new Set(); + const hash = hash_ || (x => x); + const bHashes = hash_ == null ? b : new Set(Array.from(b.values()).map(hash)); a.forEach(value => { if (!bHashes.has(hash(value))) { @@ -244,11 +290,9 @@ export function setDifference( return result; } -export function setFilter( - set: Set, - predicate: (value: T) => boolean, -): Set { +function setFilter(set, predicate) { const out = new Set(); + for (const item of set) { if (predicate(item)) { out.add(item); @@ -257,198 +301,218 @@ export function setFilter( return out; } - /** * O(1)-check if a given object is empty (has no properties, inherited or not) */ -export function isEmpty(obj: Object): boolean { + + +function isEmpty(obj) { for (const key in obj) { return false; } + return true; } - /** * Constructs an enumeration with keys equal to their value. * e.g. keyMirror({a: null, b: null}) => {a: 'a', b: 'b'} * * Based off the equivalent function in www. */ -export function keyMirror(obj: T): $ObjMapi(k: K) => K> { + + +function keyMirror(obj) { const ret = {}; Object.keys(obj).forEach(key => { ret[key] = key; }); return ret; } - /** * Given an array of [key, value] pairs, construct a map where the values for * each key are collected into an array of values, in order. */ -export function collect(pairs: Array<[K, V]>): Map> { + + +function collect(pairs) { const result = new Map(); + for (const pair of pairs) { const [k, v] = pair; let list = result.get(k); + if (list == null) { list = []; result.set(k, list); } + list.push(v); } + return result; } -export function objectFromPairs( - iterable: Iterable<[T, U]>, -): {[T]: U} { +function objectFromPairs(iterable) { const result = {}; + for (const [key, value] of iterable) { result[key] = value; } + return result; } -export function objectMapValues( - object: {[T: string]: U}, - project: (value: U, key: T) => V, -): {[T]: V} { +function objectMapValues(object, project) { const result = {}; Object.keys(object).forEach(key => { - result[key] = project(object[key], ((key: any): T)); + result[key] = project(object[key], key); }); return result; } -export class MultiMap { +class MultiMap { // Invariant: no empty sets. They should be removed instead. - _map: Map>; - // TODO may be worth defining a getter but no setter, to mimic Map. But please just behave and // don't mutate this from outside this class. // // Invariant: equal to the sum of the sizes of all the sets contained in this._map - /* The total number of key-value bindings contained */ - size: number; + /* The total number of key-value bindings contained */ constructor() { this._map = new Map(); this.size = 0; } - /* * Returns the set of values associated with the given key. Do not mutate the given set. Copy it * if you need to store it past the next operation on this MultiMap. */ - get(key: K): Set { + + + get(key) { const set = this._map.get(key); + if (set == null) { return new Set(); } + return set; } - /* * Mimics the Map.prototype.set interface. Deliberately did not choose "set" as the name since the * implication is that it removes the previous binding. */ - add(key: K, value: V): MultiMap { + + + add(key, value) { let set = this._map.get(key); + if (set == null) { set = new Set(); + this._map.set(key, set); } + if (!set.has(value)) { set.add(value); this.size++; } + return this; } - /* * Mimics the Map.prototype.set interface. Replaces the previous binding with new values. */ - set(key: K, values: Iterable): void { + + + set(key, values) { this.deleteAll(key); const newSet = new Set(values); + if (newSet.size !== 0) { this._map.set(key, newSet); + this.size += newSet.size; } } - /* * Deletes a single binding. Returns true iff the binding existed. */ - delete(key: K, value: V): boolean { + + + delete(key, value) { const set = this.get(key); const didRemove = set.delete(value); + if (set.size === 0) { this._map.delete(key); } + if (didRemove) { this.size--; } + return didRemove; } - /* * Deletes all bindings associated with the given key. Returns true iff any bindings were deleted. */ - deleteAll(key: K): boolean { + + + deleteAll(key) { const set = this.get(key); this.size -= set.size; return this._map.delete(key); } - clear(): void { + clear() { this._map.clear(); + this.size = 0; } - has(key: K, value: V): boolean { + has(key, value) { return this.get(key).has(value); } - hasAny(key: K): boolean { + hasAny(key) { return this._map.has(key); } - *values(): Iterable { + *values() { for (const set of this._map.values()) { yield* set; } } - forEach(callback: (value: V, key: K, obj: MultiMap) => void): void { - this._map.forEach((values, key) => - values.forEach(value => callback(value, key, this)), - ); + forEach(callback) { + this._map.forEach((values, key) => values.forEach(value => callback(value, key, this))); } + } -export function objectValues(obj: {[key: string]: T}): Array { +exports.MultiMap = MultiMap; + +function objectValues(obj) { return Object.keys(obj).map(key => obj[key]); } -export function objectEntries(obj: ?{[key: string]: T}): Array<[string, T]> { +function objectEntries(obj) { if (obj == null) { throw new TypeError(); } + const entries = []; + for (const key in obj) { - if ( - obj.hasOwnProperty(key) && - Object.prototype.propertyIsEnumerable.call(obj, key) - ) { + if (obj.hasOwnProperty(key) && Object.prototype.propertyIsEnumerable.call(obj, key)) { entries.push([key, obj[key]]); } } + return entries; } -export function objectFromMap(map: Map): {[key: string]: T} { +function objectFromMap(map) { const obj = {}; map.forEach((v, k) => { obj[k] = v; @@ -456,9 +520,7 @@ export function objectFromMap(map: Map): {[key: string]: T} { return obj; } -export function* concatIterators( - ...iterators: Array> -): Iterator { +function* concatIterators(...iterators) { for (const iterator of iterators) { for (const element of iterator) { yield element; @@ -466,34 +528,27 @@ export function* concatIterators( } } -export function someOfIterable( - iterable: Iterable, - predicate: (element: T) => boolean, -): boolean { +function someOfIterable(iterable, predicate) { for (const element of iterable) { if (predicate(element)) { return true; } } + return false; } -export function findInIterable( - iterable: Iterable, - predicate: (element: T) => boolean, -): ?T { +function findInIterable(iterable, predicate) { for (const element of iterable) { if (predicate(element)) { return element; } } + return null; } -export function* filterIterable( - iterable: Iterable, - predicate: (element: T) => boolean, -): Iterable { +function* filterIterable(iterable, predicate) { for (const element of iterable) { if (predicate(element)) { yield element; @@ -501,83 +556,69 @@ export function* filterIterable( } } -export function* mapIterable( - iterable: Iterable, - projectorFn: (element: T) => M, -): Iterable { +function* mapIterable(iterable, projectorFn) { for (const element of iterable) { yield projectorFn(element); } } -export function* takeIterable( - iterable: Iterable, - limit: number, -): Iterable { +function* takeIterable(iterable, limit) { let i = 0; + for (const element of iterable) { if (++i > limit) { break; } + yield element; } -} +} // Return an iterable of the numbers start (inclusive) through stop (exclusive) + -// Return an iterable of the numbers start (inclusive) through stop (exclusive) -export function* range( - start: number, - stop: number, - step?: number = 1, -): Iterable { +function* range(start, stop, step = 1) { for (let i = start; i < stop; i += step) { yield i; } } -export function firstOfIterable(iterable: Iterable): ?T { +function firstOfIterable(iterable) { return findInIterable(iterable, () => true); } -export function iterableIsEmpty(iterable: Iterable): boolean { +function iterableIsEmpty(iterable) { // eslint-disable-next-line no-unused-vars for (const element of iterable) { return false; } + return true; } -export function iterableContains(iterable: Iterable, value: T): boolean { - return !iterableIsEmpty( - filterIterable(iterable, element => element === value), - ); +function iterableContains(iterable, value) { + return !iterableIsEmpty(filterIterable(iterable, element => element === value)); } -export function count(iterable: Iterable): number { - let size = 0; - // eslint-disable-next-line no-unused-vars +function count(iterable) { + let size = 0; // eslint-disable-next-line no-unused-vars + for (const element of iterable) { size++; } + return size; } -export function isIterable(obj: any): boolean { +function isIterable(obj) { return typeof obj[Symbol.iterator] === 'function'; -} +} // Traverse an array from the inside out, starting at the specified index. -// Traverse an array from the inside out, starting at the specified index. -export function* insideOut( - arr: Array, - startingIndex?: number, -): Iterable<[T, number]> { + +function* insideOut(arr, startingIndex) { if (arr.length === 0) { return; } - let i = - startingIndex == null - ? Math.floor(arr.length / 2) - : Math.min(arr.length, Math.max(0, startingIndex)); + let i = startingIndex == null ? Math.floor(arr.length / 2) : Math.min(arr.length, Math.max(0, startingIndex)); let j = i - 1; while (i < arr.length || j >= 0) { @@ -585,6 +626,7 @@ export function* insideOut( yield [arr[i], i]; i++; } + if (j >= 0) { yield [arr[j], j]; j--; @@ -592,15 +634,15 @@ export function* insideOut( } } -export function mapFromObject(obj: {[key: string]: T}): Map { +function mapFromObject(obj) { return new Map(objectEntries(obj)); } -export function lastFromArray(arr: Array): T { +function lastFromArray(arr) { return arr[arr.length - 1]; } -export function distinct(array: T[], keyFn?: (t: T) => string): T[] { +function distinct(array, keyFn) { if (keyFn == null) { return Array.from(new Set(array)); } @@ -608,29 +650,34 @@ export function distinct(array: T[], keyFn?: (t: T) => string): T[] { const seenKeys = new Set(); return array.filter(elem => { const key = keyFn(elem); + if (seenKeys.has(key)) { return false; } + seenKeys.add(key); return true; }); } -export class DefaultMap extends Map { - _factory: () => V; - - constructor(factory: () => V, iterable: ?Iterable<[K, V]>) { +class DefaultMap extends Map { + constructor(factory, iterable) { super(iterable); this._factory = factory; } - get(key: K): V { + get(key) { if (!this.has(key)) { const value = this._factory(); + this.set(key, value); return value; - } - // If the key is present we must have a value of type V. - return (super.get(key): any); + } // If the key is present we must have a value of type V. + + + return super.get(key); } + } + +exports.DefaultMap = DefaultMap; \ No newline at end of file diff --git a/modules/nuclide-commons/debounce.js b/modules/nuclide-commons/debounce.js index 54c6172a..4fc2a54d 100644 --- a/modules/nuclide-commons/debounce.js +++ b/modules/nuclide-commons/debounce.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = debounce; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,42 +13,32 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; - -export default function debounce< - T, - TArgs: Array, - TReturn, - TFunc: (...TArgs) => TReturn, // eslint-disable-line space-before-function-paren ->( - func: TFunc, - wait: number, - immediate?: boolean = false, -): { - (...TArgs): TReturn | void, - dispose(): void, -} { +function debounce(func, wait, immediate = false) { // Taken from: https://github.com/jashkenas/underscore/blob/b10b2e6d72/underscore.js#L815. - let timeout: ?TimeoutID; - let args: ?TArgs; - let context: any; + let timeout; + let args; + let context; let timestamp = 0; - let result: TReturn | void; + let result; - const later = function() { + const later = function () { const last = Date.now() - timestamp; if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; + if (!immediate) { - invariant(args != null); + if (!(args != null)) { + throw new Error("Invariant violation: \"args != null\""); + } + result = func.apply(context, args); + if (!timeout) { context = args = null; } @@ -49,14 +46,16 @@ export default function debounce< } }; - const debounced = function(...args_: TArgs): TReturn | void { + const debounced = function (...args_) { context = this; args = args_; timestamp = Date.now(); const callNow = immediate && !timeout; + if (!timeout) { timeout = setTimeout(later, wait); } + if (callNow) { result = func.apply(context, args); context = args = null; @@ -73,4 +72,4 @@ export default function debounce< }; return debounced; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/event.js b/modules/nuclide-commons/event.js index 085e1032..6bde7390 100644 --- a/modules/nuclide-commons/event.js +++ b/modules/nuclide-commons/event.js @@ -1,3 +1,25 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.attachEvent = attachEvent; +exports.observableFromSubscribeFunction = observableFromSubscribeFunction; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("./UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,39 +28,27 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import UniversalDisposable from './UniversalDisposable'; -import {Observable} from 'rxjs'; - /** * Add an event listener an return a disposable for removing it. Note that this function assumes * node EventEmitter semantics: namely, that adding the same combination of eventName and callback * adds a second listener. */ -export function attachEvent( - emitter: events$EventEmitter, - eventName: string, - callback: Function, -): IDisposable { +function attachEvent(emitter, eventName, callback) { emitter.addListener(eventName, callback); - return new UniversalDisposable(() => { + return new (_UniversalDisposable().default)(() => { emitter.removeListener(eventName, callback); }); } -type SubscribeCallback = (item: T) => any; -type SubscribeFunction = (callback: SubscribeCallback) => IDisposable; - -export function observableFromSubscribeFunction( - fn: SubscribeFunction, -): Observable { - return Observable.create(observer => { +function observableFromSubscribeFunction(fn) { + return _RxMin.Observable.create(observer => { const disposable = fn(observer.next.bind(observer)); return () => { disposable.dispose(); }; }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/expected.js b/modules/nuclide-commons/expected.js index 78de8737..17d14fa6 100644 --- a/modules/nuclide-commons/expected.js +++ b/modules/nuclide-commons/expected.js @@ -1,3 +1,11 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.expectedEqual = expectedEqual; +exports.Expect = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +14,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ @@ -15,89 +23,65 @@ * and later switch back to regular values if they recover. Normally, a source finishes after * passing an uncaught error. */ -export type Expected = - | ExpectedError - | ExpectedValue - | ExpectedPending; - -type ExpectedError = {| - isError: true, - isPending: false, - isValue: false, - error: Error, - getOrDefault: (def: T) => T, - map(fn: (T) => U): Expected, -|}; - -type ExpectedValue = {| - isError: false, - isPending: false, - isValue: true, - value: T, - getOrDefault: (def: T) => T, - map(fn: (T) => U): Expected, -|}; - -type ExpectedPending = {| - isError: false, - isPending: true, - isValue: false, - getOrDefault: (def: T) => T, - map(fn: (T) => U): Expected, -|}; - -export class Expect { - static error(error: Error): ExpectedError { +class Expect { + static error(error) { return { isError: true, isPending: false, isValue: false, error, - getOrDefault(def: T): T { + + getOrDefault(def) { return def; }, - map(fn: T => U): Expected { + + map(fn) { return Expect.error(error); - }, + } + }; } - static value(value: T): ExpectedValue { + static value(value) { return { isError: false, isPending: false, isValue: true, value, - getOrDefault(def: T): T { + + getOrDefault(def) { return this.value; }, - map(fn: T => U): Expected { + + map(fn) { return Expect.value(fn(value)); - }, + } + }; } - static pending(): ExpectedPending { + static pending() { return { isError: false, isPending: true, isValue: false, - getOrDefault(def: T): T { + + getOrDefault(def) { return def; }, - map(fn: T => U): Expected { + + map(fn) { return Expect.pending(); - }, + } + }; } + } -export function expectedEqual( - a: Expected, - b: Expected, - valueEqual: (valueA: T, valueB: T) => boolean, - errorEqual: (errorA: Error, errorB: Error) => boolean, -): boolean { +exports.Expect = Expect; + +function expectedEqual(a, b, valueEqual, errorEqual) { if (a.isValue && b.isValue) { return valueEqual(a.value, b.value); } else if (a.isError && b.isError) { @@ -107,4 +91,4 @@ export function expectedEqual( } else { return false; } -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/fsPromise.js b/modules/nuclide-commons/fsPromise.js index 3f1c9e66..b52babab 100644 --- a/modules/nuclide-commons/fsPromise.js +++ b/modules/nuclide-commons/fsPromise.js @@ -1,3 +1,94 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _fs = _interopRequireDefault(require("fs")); + +function _fsPlus() { + const data = _interopRequireDefault(require("fs-plus")); + + _fsPlus = function () { + return data; + }; + + return data; +} + +function _glob() { + const data = _interopRequireDefault(require("glob")); + + _glob = function () { + return data; + }; + + return data; +} + +function _mkdirp() { + const data = _interopRequireDefault(require("mkdirp")); + + _mkdirp = function () { + return data; + }; + + return data; +} + +function _mv() { + const data = _interopRequireDefault(require("mv")); + + _mv = function () { + return data; + }; + + return data; +} + +function _rimraf() { + const data = _interopRequireDefault(require("rimraf")); + + _rimraf = function () { + return data; + }; + + return data; +} + +function _temp() { + const data = _interopRequireDefault(require("temp")); + + _temp = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("./nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _process() { + const data = require("./process"); + + _process = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,30 +97,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import fs from 'fs'; -import fsPlus from 'fs-plus'; -import globLib from 'glob'; -import mkdirpLib from 'mkdirp'; -import mvLib from 'mv'; -import rimraf from 'rimraf'; -import temp from 'temp'; - -import nuclideUri from './nuclideUri'; -import {runCommand} from './process'; - /** * Create a temp directory with given prefix. The caller is responsible for cleaning up the * drectory. * @param prefix optinal prefix for the temp directory name. * @return path to a temporary directory. */ -function tempdir(prefix: string = ''): Promise { +function tempdir(prefix = '') { return new Promise((resolve, reject) => { - temp.mkdir(prefix, (err, result) => { + _temp().default.mkdir(prefix, (err, result) => { if (err == null) { resolve(result); } else { @@ -38,18 +118,19 @@ function tempdir(prefix: string = ''): Promise { }); }); } - /** * @return path to a temporary file. The caller is responsible for cleaning up * the file. */ -function tempfile(options: any): Promise { + + +function tempfile(options) { return new Promise((resolve, reject) => { - temp.open(options, (err, info) => { + _temp().default.open(options, (err, info) => { if (err) { reject(err); } else { - fs.close(info.fd, closeErr => { + _fs.default.close(info.fd, closeErr => { if (closeErr) { reject(closeErr); } else { @@ -60,7 +141,6 @@ function tempfile(options: any): Promise { }); }); } - /** * Searches upward through the filesystem from pathToDirectory to find a file with * fileName. @@ -69,45 +149,46 @@ function tempfile(options: any): Promise { * not a file. * @return directory that contains the nearest file or null. */ -async function findNearestFile( - fileName: string, - pathToDirectory: string, -): Promise { + + +async function findNearestFile(fileName, pathToDirectory) { // TODO(5586355): If this becomes a bottleneck, we should consider memoizing // this function. The downside would be that if someone added a closer file // with fileName to pathToFile (or deleted the one that was cached), then we // would have a bug. This would probably be pretty rare, though. - let currentPath = nuclideUri.resolve(pathToDirectory); + let currentPath = _nuclideUri().default.resolve(pathToDirectory); + for (;;) { - const fileToFind = nuclideUri.join(currentPath, fileName); - // eslint-disable-next-line no-await-in-loop + const fileToFind = _nuclideUri().default.join(currentPath, fileName); // eslint-disable-next-line no-await-in-loop + + const hasFile = await exists(fileToFind); + if (hasFile) { return currentPath; } - if (nuclideUri.isRoot(currentPath)) { + + if (_nuclideUri().default.isRoot(currentPath)) { return null; } - currentPath = nuclideUri.dirname(currentPath); + + currentPath = _nuclideUri().default.dirname(currentPath); } } -async function findNearestAncestorNamed( - fileName: string, - pathToDirectory: string, -): Promise { +async function findNearestAncestorNamed(fileName, pathToDirectory) { const directory = await findNearestFile(fileName, pathToDirectory); + if (directory != null) { - return nuclideUri.join(directory, fileName); + return _nuclideUri().default.join(directory, fileName); } else { return null; } } -function resolveRealPath(path: string): Promise { - return realpath(nuclideUri.expandHomeDir(path)); +function resolveRealPath(path) { + return realpath(_nuclideUri().default.expandHomeDir(path)); } - /** * Searches upward through the filesystem from pathToDirectory to find the furthest * file with fileName. @@ -117,42 +198,44 @@ function resolveRealPath(path: string): Promise { * @param stopOnMissing Stop searching when we reach a directory without fileName. * @return directory that contains the furthest file or null. */ -async function findFurthestFile( - fileName: string, - pathToDirectory: string, - stopOnMissing: boolean = false, -): Promise { - let currentPath = nuclideUri.resolve(pathToDirectory); + + +async function findFurthestFile(fileName, pathToDirectory, stopOnMissing = false) { + let currentPath = _nuclideUri().default.resolve(pathToDirectory); + let result = null; + for (;;) { - const fileToFind = nuclideUri.join(currentPath, fileName); - // eslint-disable-next-line no-await-in-loop + const fileToFind = _nuclideUri().default.join(currentPath, fileName); // eslint-disable-next-line no-await-in-loop + + const hasFile = await exists(fileToFind); - if ((!hasFile && stopOnMissing) || nuclideUri.isRoot(currentPath)) { + + if (!hasFile && stopOnMissing || _nuclideUri().default.isRoot(currentPath)) { return result; } else if (hasFile) { result = currentPath; } - currentPath = nuclideUri.dirname(currentPath); + + currentPath = _nuclideUri().default.dirname(currentPath); } } -function getCommonAncestorDirectory(filePaths: Array): string { - let commonDirectoryPath = nuclideUri.dirname(filePaths[0]); - while ( - filePaths.some(filePath => !filePath.startsWith(commonDirectoryPath)) - ) { - commonDirectoryPath = nuclideUri.dirname(commonDirectoryPath); +function getCommonAncestorDirectory(filePaths) { + let commonDirectoryPath = _nuclideUri().default.dirname(filePaths[0]); + + while (filePaths.some(filePath => !filePath.startsWith(commonDirectoryPath))) { + commonDirectoryPath = _nuclideUri().default.dirname(commonDirectoryPath); } + return commonDirectoryPath; } -function exists(filePath: string): Promise { +function exists(filePath) { return new Promise((resolve, reject) => { - fs.exists(filePath, resolve); + _fs.default.exists(filePath, resolve); }); } - /** * Runs the equivalent of `mkdir -p` with the given path. * @@ -160,13 +243,16 @@ function exists(filePath: string): Promise { * directories were created for some prefix of the given path. * @return true if the path was created; false if it already existed. */ -async function mkdirp(filePath: string): Promise { + + +async function mkdirp(filePath) { const isExistingDirectory = await exists(filePath); + if (isExistingDirectory) { return false; } else { return new Promise((resolve, reject) => { - mkdirpLib(filePath, err => { + (0, _mkdirp().default)(filePath, err => { if (err) { reject(err); } else { @@ -176,13 +262,14 @@ async function mkdirp(filePath: string): Promise { }); } } - /** * Removes directories even if they are non-empty. Does not fail if the directory doesn't exist. */ -function rimrafWrapper(filePath: string): Promise { + + +function rimrafWrapper(filePath) { return new Promise((resolve, reject) => { - rimraf(filePath, (err, result) => { + (0, _rimraf().default)(filePath, (err, result) => { if (err == null) { resolve(result); } else { @@ -192,16 +279,10 @@ function rimrafWrapper(filePath: string): Promise { }); } -async function getFileSystemType(entityPath: string): Promise { +async function getFileSystemType(entityPath) { if (process.platform === 'linux' || process.platform === 'darwin') { try { - const stdout = await runCommand('stat', [ - '-f', - '-L', - '-c', - '%T', - entityPath, - ]).toPromise(); + const stdout = await (0, _process().runCommand)('stat', ['-f', '-L', '-c', '%T', entityPath]).toPromise(); return stdout.trim(); } catch (err) { return null; @@ -211,22 +292,24 @@ async function getFileSystemType(entityPath: string): Promise { return null; } } - /** @return true only if we are sure entityPath is on NFS. */ -async function isNfs(entityPath: string): Promise { + + +async function isNfs(entityPath) { return (await getFileSystemType(entityPath)) === 'nfs'; } - /** @return true only if we are sure entityPath is on a Fuse filesystem like dewey or gvfs. */ -async function isFuse(entityPath: string): Promise { + + +async function isFuse(entityPath) { return (await getFileSystemType(entityPath)) === 'fuseblk'; } -function glob(pattern: string, options?: Object): Promise> { +function glob(pattern, options) { return new Promise((resolve, reject) => { - globLib(pattern, options, (err, result) => { + (0, _glob().default)(pattern, options, (err, result) => { if (err == null) { resolve(result); } else { @@ -236,9 +319,10 @@ function glob(pattern: string, options?: Object): Promise> { }); } -async function isNonNfsDirectory(directoryPath: string): Promise { +async function isNonNfsDirectory(directoryPath) { try { const stats = await stat(directoryPath); + if (stats.isDirectory()) { return !(await isNfs(directoryPath)); } else { @@ -251,14 +335,14 @@ async function isNonNfsDirectory(directoryPath: string): Promise { return false; } } - /** * Promisified wrappers around fs-plus functions. */ -function copy(source: string, dest: string): Promise { + +function copy(source, dest) { return new Promise((resolve, reject) => { - fsPlus.copy(source, dest, (err, result) => { + _fsPlus().default.copy(source, dest, (err, result) => { if (err == null) { resolve(result); } else { @@ -268,41 +352,35 @@ function copy(source: string, dest: string): Promise { }); } -async function copyFilePermissions( - sourcePath: string, - destinationPath: string, -): Promise { +async function copyFilePermissions(sourcePath, destinationPath) { try { - const {mode, uid, gid} = await stat(sourcePath); - await Promise.all([ - // The user may not have permissions to use the uid/gid. - chown(destinationPath, uid, gid).catch(() => {}), - chmod(destinationPath, mode), - ]); + const { + mode, + uid, + gid + } = await stat(sourcePath); + await Promise.all([// The user may not have permissions to use the uid/gid. + chown(destinationPath, uid, gid).catch(() => {}), chmod(destinationPath, mode)]); } catch (e) { // If the file does not exist, then ENOENT will be thrown. if (e.code !== 'ENOENT') { throw e; - } - // For new files, use the default process file creation mask. - await chmod( - destinationPath, - 0o666 & ~process.umask(), // eslint-disable-line no-bitwise + } // For new files, use the default process file creation mask. + + + await chmod(destinationPath, 0o666 & ~process.umask() // eslint-disable-line no-bitwise ); } } - /** * TODO: the fs-plus `writeFile` implementation runs `mkdirp` first. * We should use `fs.writeFile` and have callsites explicitly opt-in to this behaviour. */ -function writeFile( - filename: string, - data: Buffer | string, - options?: Object | string, -): Promise { + + +function writeFile(filename, data, options) { return new Promise((resolve, reject) => { - fsPlus.writeFile(filename, data, options, (err, result) => { + _fsPlus().default.writeFile(filename, data, options, (err, result) => { if (err == null) { resolve(result); } else { @@ -312,48 +390,45 @@ function writeFile( }); } -async function writeFileAtomic( - path: string, - data: Buffer | string, - options?: Object | string, -): Promise { +async function writeFileAtomic(path, data, options) { const tempFilePath = await tempfile('nuclide'); + try { - await writeFile(tempFilePath, data, options); + await writeFile(tempFilePath, data, options); // Expand the target path in case it contains symlinks. - // Expand the target path in case it contains symlinks. let realPath = path; + try { realPath = await realpath(path); - } catch (e) { - // Fallback to using the specified path if it cannot be expanded. - // Note: this is expected in cases where the remote file does not - // actually exist. - } - + } catch (e) {} // Fallback to using the specified path if it cannot be expanded. + // Note: this is expected in cases where the remote file does not + // actually exist. // Ensure file still has original permissions: // https://github.com/facebook/nuclide/issues/157 // We update the mode of the temp file rather than the destination file because // if we did the mv() then the chmod(), there would be a brief period between // those two operations where the destination file might have the wrong permissions. - await copyFilePermissions(realPath, tempFilePath); - // TODO: put renames into a queue so we don't write older save over new save. + + await copyFilePermissions(realPath, tempFilePath); // TODO: put renames into a queue so we don't write older save over new save. // Use mv as fs.rename doesn't work across partitions. - await mv(tempFilePath, realPath, {mkdirp: true}); + + await mv(tempFilePath, realPath, { + mkdirp: true + }); } catch (err) { await unlink(tempFilePath); throw err; } } - /** * Promisified wrappers around fs functions. */ -function chmod(path: string, mode: number | string): Promise { + +function chmod(path, mode) { return new Promise((resolve, reject) => { - fs.chmod(path, mode, (err, result) => { + _fs.default.chmod(path, mode, (err, result) => { if (err == null) { resolve(result); } else { @@ -363,9 +438,9 @@ function chmod(path: string, mode: number | string): Promise { }); } -function chown(path: string, uid: number, gid: number): Promise { +function chown(path, uid, gid) { return new Promise((resolve, reject) => { - fs.chown(path, uid, gid, (err, result) => { + _fs.default.chown(path, uid, gid, (err, result) => { if (err == null) { resolve(result); } else { @@ -375,9 +450,9 @@ function chown(path: string, uid: number, gid: number): Promise { }); } -function close(fd: number): Promise { +function close(fd) { return new Promise((resolve, reject) => { - fs.close(fd, err => { + _fs.default.close(fd, err => { if (err == null) { resolve(); } else { @@ -387,9 +462,9 @@ function close(fd: number): Promise { }); } -function lstat(path: string): Promise { +function lstat(path) { return new Promise((resolve, reject) => { - fs.lstat(path, (err, result) => { + _fs.default.lstat(path, (err, result) => { if (err == null) { resolve(result); } else { @@ -399,9 +474,9 @@ function lstat(path: string): Promise { }); } -function mkdir(path: string, mode?: number): Promise { +function mkdir(path, mode) { return new Promise((resolve, reject) => { - fs.mkdir(path, mode, (err, result) => { + _fs.default.mkdir(path, mode, (err, result) => { if (err == null) { resolve(result); } else { @@ -411,36 +486,24 @@ function mkdir(path: string, mode?: number): Promise { }); } -export type MvOptions = { - // Run mkdirp for the directory first. Defaults to false. - mkdirp?: boolean, - // Overwrite the file if it exists. Defaults to true. - clobber?: boolean, - // Optional: the concurrency limit when moving a directory. - limit?: number, -}; - /** * The key difference between 'mv' and 'rename' is that 'mv' works across devices. * It's not uncommon to have temporary files in a different disk, for instance. */ -async function mv( - sourcePath: string, - destinationPath: string, - options?: MvOptions = {}, -): Promise { +async function mv(sourcePath, destinationPath, options = {}) { // mv-node fails to account for the case where a destination directory exists // and `clobber` is false. This can result in the source directory getting // deleted but the destination not getting written. // https://github.com/andrewrk/node-mv/issues/30 if (options.clobber === false && (await exists(destinationPath))) { - const err: ErrnoError = new Error('Destination file exists'); + const err = new Error('Destination file exists'); err.code = 'EEXIST'; err.path = destinationPath; throw err; } + return new Promise((resolve, reject) => { - mvLib(sourcePath, destinationPath, options, error => { + (0, _mv().default)(sourcePath, destinationPath, options, error => { if (error) { reject(error); } else { @@ -450,13 +513,9 @@ async function mv( }); } -function open( - path: string | Buffer | URL, - flags: string | number, - mode: number = 0o666, -): Promise { +function open(path, flags, mode = 0o666) { return new Promise((resolve, reject) => { - fs.open(path, flags, mode, (err, fd) => { + _fs.default.open(path, flags, mode, (err, fd) => { if (err == null) { resolve(fd); } else { @@ -466,15 +525,9 @@ function open( }); } -function read( - fd: number, - buffer: Buffer, - offset: number, - length: number, - position: number | null, -): Promise { +function read(fd, buffer, offset, length, position) { return new Promise((resolve, reject) => { - fs.read(fd, buffer, offset, length, position, (err, bytesRead) => { + _fs.default.read(fd, buffer, offset, length, position, (err, bytesRead) => { if (err == null) { resolve(bytesRead); } else { @@ -482,21 +535,14 @@ function read( } }); }); -} - -// `fs.readFile` returns a Buffer unless an encoding is specified. +} // `fs.readFile` returns a Buffer unless an encoding is specified. // This workaround is adapted from the Flow declarations. -type ReadFileType = ((filename: string, encoding: string) => Promise) & - (( - filename: string, - options: {encoding: string, flag?: string}, - ) => Promise) & - ((filename: string, options?: {flag?: string}) => Promise); - -const readFile: ReadFileType = (function(...args: Array) { + + +const readFile = function (...args) { return new Promise((resolve, reject) => { // $FlowIssue: spread operator doesn't preserve any-type - fs.readFile(...args, (err, result) => { + _fs.default.readFile(...args, (err, result) => { if (err == null) { resolve(result); } else { @@ -504,11 +550,11 @@ const readFile: ReadFileType = (function(...args: Array) { } }); }); -}: any); +}; -function readdir(path: string): Promise> { +function readdir(path) { return new Promise((resolve, reject) => { - fs.readdir(path, (err, result) => { + _fs.default.readdir(path, (err, result) => { if (err == null) { resolve(result); } else { @@ -518,9 +564,9 @@ function readdir(path: string): Promise> { }); } -function readlink(path: string): Promise { +function readlink(path) { return new Promise((resolve, reject) => { - fs.readlink(path, (err, result) => { + _fs.default.readlink(path, (err, result) => { if (err == null) { resolve(result); } else { @@ -530,9 +576,9 @@ function readlink(path: string): Promise { }); } -function realpath(path: string, cache?: Object): Promise { +function realpath(path, cache) { return new Promise((resolve, reject) => { - fs.realpath(path, cache, (err, result) => { + _fs.default.realpath(path, cache, (err, result) => { if (err == null) { resolve(result); } else { @@ -542,9 +588,9 @@ function realpath(path: string, cache?: Object): Promise { }); } -function access(path: string, mode: number): Promise { +function access(path, mode) { return new Promise((resolve, reject) => { - fs.access(path, mode, err => { + _fs.default.access(path, mode, err => { if (err == null) { resolve(true); } else { @@ -554,9 +600,9 @@ function access(path: string, mode: number): Promise { }); } -function stat(path: string): Promise { +function stat(path) { return new Promise((resolve, reject) => { - fs.stat(path, (err, result) => { + _fs.default.stat(path, (err, result) => { if (err == null) { resolve(result); } else { @@ -566,9 +612,9 @@ function stat(path: string): Promise { }); } -function symlink(source: string, dest: string, type?: string): Promise { +function symlink(source, dest, type) { return new Promise((resolve, reject) => { - fs.symlink(source, dest, type, (err, result) => { + _fs.default.symlink(source, dest, type, (err, result) => { if (err == null) { resolve(result); } else { @@ -577,50 +623,48 @@ function symlink(source: string, dest: string, type?: string): Promise { }); }); } - /** * A utility function to grab the last N bytes from a file. Attempts to do so * without reading the entire file. */ -async function tailBytes(file: string, maxBytes: number): Promise { + + +async function tailBytes(file, maxBytes) { if (maxBytes <= 0) { throw new Error('tailbytes expects maxBytes > 0'); - } + } // Figure out the size so we know what strategy to use - // Figure out the size so we know what strategy to use - const {size: file_size} = await stat(file); + + const { + size: file_size + } = await stat(file); if (file_size > maxBytes) { const fd = await open(file, 'r'); const buffer = Buffer.alloc(maxBytes); - const bytesRead = await read( - fd, - buffer, - 0, // buffer offset - maxBytes, // length to read - file_size - maxBytes, // file offset + const bytesRead = await read(fd, buffer, 0, // buffer offset + maxBytes, // length to read + file_size - maxBytes // file offset ); await close(fd); - /* If we meant to read the last 100 bytes but only read 50 bytes, then we've * failed to read the last 100 bytes. So throw. In the future, someone * could update this code to keep calling `read` until we read maxBytes. */ + if (bytesRead !== maxBytes) { - throw new Error( - `Failed to tail file. Intended to read ${maxBytes} bytes but ` + - `only read ${bytesRead} bytes`, - ); + throw new Error(`Failed to tail file. Intended to read ${maxBytes} bytes but ` + `only read ${bytesRead} bytes`); } + return buffer; } else { return readFile(file); } } -function unlink(path: string): Promise { +function unlink(path) { return new Promise((resolve, reject) => { - fs.unlink(path, (err, result) => { + _fs.default.unlink(path, (err, result) => { if (err == null) { resolve(result); } else { @@ -630,13 +674,9 @@ function unlink(path: string): Promise { }); } -function utimes( - path: string, - atime: number | Date, - mtime: number | Date, -): Promise { +function utimes(path, atime, mtime) { return new Promise((resolve, reject) => { - fs.utimes(path, atime, mtime, err => { + _fs.default.utimes(path, atime, mtime, err => { if (err == null) { resolve(); } else { @@ -646,9 +686,9 @@ function utimes( }); } -function rmdir(path: string): Promise { +function rmdir(path) { return new Promise((resolve, reject) => { - fs.rmdir(path, err => { + _fs.default.rmdir(path, err => { if (err == null) { resolve(); } else { @@ -658,7 +698,7 @@ function rmdir(path: string): Promise { }); } -export default { +var _default = { tempdir, tempfile, findNearestFile, @@ -671,12 +711,10 @@ export default { isFuse, glob, isNonNfsDirectory, - copy, copyFilePermissions, writeFile, writeFileAtomic, - chmod, chown, close, @@ -696,7 +734,7 @@ export default { utimes, rmdir, access, - findNearestAncestorNamed, - resolveRealPath, + resolveRealPath }; +exports.default = _default; \ No newline at end of file diff --git a/modules/nuclide-commons/humanizeKeystroke.js b/modules/nuclide-commons/humanizeKeystroke.js index 05ab877e..3109b3f1 100644 --- a/modules/nuclide-commons/humanizeKeystroke.js +++ b/modules/nuclide-commons/humanizeKeystroke.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = humanizeKeystroke; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,14 +13,13 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ /* * adapted from https://github.com/atom/underscore-plus/blob/master/src/underscore-plus.coffee */ - const MAC_MODIFIER_KEYMAP = { alt: '\u2325', cmd: '\u2318', @@ -24,9 +30,8 @@ const MAC_MODIFIER_KEYMAP = { option: '\u2325', right: '\u2192', shift: '\u21e7', - up: '\u2191', + up: '\u2191' }; - const NON_MAC_MODIFIER_KEYMAP = { alt: 'Alt', cmd: 'Cmd', @@ -37,11 +42,10 @@ const NON_MAC_MODIFIER_KEYMAP = { option: 'Alt', right: 'Right', shift: 'Shift', - up: 'Up', -}; - -// Human key combos should always explicitly state the shift key. This map is a disambiguator. + up: 'Up' +}; // Human key combos should always explicitly state the shift key. This map is a disambiguator. // 'shift-version': 'no-shift-version' + const SHIFT_KEYMAP = { _: '-', ':': ';', @@ -53,14 +57,13 @@ const SHIFT_KEYMAP = { '<': ',', '>': '.', '|': '\\', - '~': '`', + '~': '`' }; +const FN_KEY_RE = /f[0-9]{1,2}/; // $FlowIssue -const FN_KEY_RE = /f[0-9]{1,2}/; - -// $FlowIssue -function flatten(arr: Array>): Array { +function flatten(arr) { let flattened = []; + for (const el of arr) { if (Array.isArray(el)) { flattened = flattened.concat(flatten(el)); @@ -68,40 +71,47 @@ function flatten(arr: Array>): Array { flattened.push(el); } } + return flattened; } -function capitalize(word: string): string { +function capitalize(word) { const first = word[0] || ''; const rest = word.slice(1); return first.toUpperCase() + rest; } -function humanizeKey(key: string, platform: ?string): string | Array { +function humanizeKey(key, platform) { if (!key) { return key; } - const modifierKeyMap = - platform === 'darwin' ? MAC_MODIFIER_KEYMAP : NON_MAC_MODIFIER_KEYMAP; + + const modifierKeyMap = platform === 'darwin' ? MAC_MODIFIER_KEYMAP : NON_MAC_MODIFIER_KEYMAP; + if (modifierKeyMap[key]) { return modifierKeyMap[key]; } + if (key.length === 1) { if (SHIFT_KEYMAP[key]) { return [modifierKeyMap.shift, SHIFT_KEYMAP[key]]; } + const uppercase = key.toUpperCase(); + if (key === uppercase && uppercase !== key.toLowerCase()) { return [modifierKeyMap.shift, uppercase]; } + return uppercase; } + if (FN_KEY_RE.test(key)) { return key.toUpperCase(); } + return platform === 'darwin' ? key : capitalize(key); } - /** * Humanize the keystroke according to platform conventions. This method * attempts to mirror the text the given keystroke would have if displayed in @@ -111,15 +121,16 @@ function humanizeKey(key: string, platform: ?string): string | Array { * @param platform An optional String platform to humanize for (default: `process.platform`). * @return a humanized representation of the keystroke. */ -export default function humanizeKeystroke( - keystroke: string, - platform_: ?string, -): string { + + +function humanizeKeystroke(keystroke, platform_) { let platform = platform_; + if (!keystroke) { return keystroke; - } - // flowlint-next-line sketchy-null-string:off + } // flowlint-next-line sketchy-null-string:off + + platform = platform || process.platform; const separator = platform === 'darwin' ? '' : '+'; let key; @@ -127,21 +138,27 @@ export default function humanizeKeystroke( let splitKeystroke; const keystrokes = keystroke.split(' '); const humanizedKeystrokes = []; + for (let i = 0; i < keystrokes.length; i++) { const currentKeystroke = keystrokes[i]; splitKeystroke = currentKeystroke.split('-'); keys = []; + for (let index = 0; index < splitKeystroke.length; index++) { key = splitKeystroke[index]; + if (key === '' && splitKeystroke[index - 1] === '') { key = '-'; } + if (key) { keys.push(humanizeKey(key, platform)); } } + keys = Array.from(new Set(flatten(keys))); humanizedKeystrokes.push(keys.join(separator)); } + return humanizedKeystrokes.join(' '); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/matchIndexesToRanges.js b/modules/nuclide-commons/matchIndexesToRanges.js index 7e1bec7b..cbe2adf9 100644 --- a/modules/nuclide-commons/matchIndexesToRanges.js +++ b/modules/nuclide-commons/matchIndexesToRanges.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = matchIndexesToRanges; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,21 +13,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -type MatchRange = [/* start */ number, /* end */ number]; - -export default function matchIndexesToRanges( - matchIndexes: Array, -): Array { +function matchIndexesToRanges(matchIndexes) { let streakOngoing = false; let start = 0; - const ranges = []; - - // Collapse consecutive values for consecutive indexes into range pairs. + const ranges = []; // Collapse consecutive values for consecutive indexes into range pairs. // Do this in O(n) where n is the number of matchIndexes (ie. less than the length of the path). + matchIndexes.forEach((i, n) => { if (matchIndexes[n + 1] === i + 1) { if (!streakOngoing) { @@ -34,8 +35,9 @@ export default function matchIndexesToRanges( } else { ranges.push([i, i + 1]); } + start = i + 1; } }); return ranges; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/memoizeUntilChanged.js b/modules/nuclide-commons/memoizeUntilChanged.js index 93088431..158d57a0 100644 --- a/modules/nuclide-commons/memoizeUntilChanged.js +++ b/modules/nuclide-commons/memoizeUntilChanged.js @@ -1,3 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _collection() { + const data = require("./collection"); + + _collection = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +23,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import invariant from 'assert'; -import {arrayEqual} from './collection'; - -type memoizeUntilChanged = (( - func: (A, B, C, D) => R, - keySelector_?: (A, B, C, D) => U, - compareKeys_?: (U, U) => boolean, -) => (A, B, C, D) => R) & - (( - func: (A, B, C) => R, - keySelector_?: (A, B, C) => U, - compareKeys_?: (U, U) => boolean, - ) => (A, B, C) => R) & - (( - func: (A, B) => R, - keySelector_?: (A, B) => U, - compareKeys_?: (U, U) => boolean, - ) => (A, B) => R) & - (( - func: (A) => R, - keySelector_?: (A) => U, - compareKeys_?: (U, U) => boolean, - ) => A => R) & - ((func: () => R) => () => R) & - (( - func: (...any: $ReadOnlyArray) => R, - (...any: $ReadOnlyArray) => U, - compareKeys_?: (U, U) => boolean, - ) => (...any: $ReadOnlyArray) => R); - /** * Create a memoized version of the provided function that caches only the latest result. This is * especially useful for optimizing React component methods without having to worry about @@ -77,25 +64,33 @@ type memoizeUntilChanged = (( * } * } */ -export default ((func, keySelector_?, compareKeys_?) => { - invariant( - !(keySelector_ == null && compareKeys_ != null), - "You can't provide a compare function without also providing a key selector.", - ); +var _default = (func, keySelector_, compareKeys_) => { + if (!!(keySelector_ == null && compareKeys_ != null)) { + throw new Error("You can't provide a compare function without also providing a key selector."); + } let prevKey = null; let prevResult; const keySelector = keySelector_ || DEFAULT_KEY_SELECTOR; - const compareKeys = compareKeys_ || arrayEqual; - return function(...args) { - const key = (keySelector: Function)(...args); - invariant(key != null, 'Key cannot be null'); + + const compareKeys = compareKeys_ || _collection().arrayEqual; + + return function (...args) { + const key = keySelector(...args); + + if (!(key != null)) { + throw new Error('Key cannot be null'); + } + if (prevKey == null || !compareKeys(key, prevKey)) { prevKey = key; - prevResult = (func: Function).apply(this, args); + prevResult = func.apply(this, args); } + return prevResult; }; -}: memoizeUntilChanged); +}; + +exports.default = _default; -const DEFAULT_KEY_SELECTOR = (...args) => args; +const DEFAULT_KEY_SELECTOR = (...args) => args; \ No newline at end of file diff --git a/modules/nuclide-commons/menuUtils.js b/modules/nuclide-commons/menuUtils.js index 0c41e405..6b924eb0 100644 --- a/modules/nuclide-commons/menuUtils.js +++ b/modules/nuclide-commons/menuUtils.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sortLabelValue = sortLabelValue; +exports.sortSubmenuGroup = sortSubmenuGroup; +exports.sortMenuGroups = sortMenuGroups; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,23 +15,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -export function sortLabelValue(label: ?string) { +function sortLabelValue(label) { // Ignore the Windows accelerator key hint when sorting, the & doesn't // actually appear in the UX so it shouldn't affect the sort. return String(label).replace('&', ''); } -export function sortSubmenuGroup( - menuItems: Array<{ - label: string, - }>, - startIndex: number, - itemCount: number, -) { +function sortSubmenuGroup(menuItems, startIndex, itemCount) { // Sort a subset of the items in the menu of length itemCount beginning // at startIndex. const itemsToSort = menuItems.splice(startIndex, itemCount); @@ -34,41 +36,36 @@ export function sortSubmenuGroup( return sortLabelValue(a.label).localeCompare(sortLabelValue(b.label)); } }); - menuItems.splice(startIndex, 0, ...itemsToSort); } -export function sortMenuGroups(menuNames: Array) { +function sortMenuGroups(menuNames) { for (const menuName of menuNames) { // Sorts the items in a menu alphabetically. If the menu contains one or more // separators, then the items within each separator subgroup will be sorted // with respect to each other, but items will remain in the same groups, and // the separators will not be moved. - const menu = atom.menu.template.find( - m => sortLabelValue(m.label) === menuName, - ); + const menu = atom.menu.template.find(m => sortLabelValue(m.label) === menuName); + if (menu == null) { continue; - } + } // Sort each group of items (separated by a separator) individually. + - // Sort each group of items (separated by a separator) individually. let sortStart = 0; + for (let i = 0; i < menu.submenu.length; i++) { if (menu.submenu[i].type === 'separator') { sortSubmenuGroup(menu.submenu, sortStart, i - sortStart); sortStart = i + 1; } - } + } // Sort any remaining items after the last separator. + - // Sort any remaining items after the last separator. if (sortStart < menu.submenu.length) { - sortSubmenuGroup( - menu.submenu, - sortStart, - menu.submenu.length - sortStart, - ); + sortSubmenuGroup(menu.submenu, sortStart, menu.submenu.length - sortStart); } } atom.menu.update(); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/nice.js b/modules/nuclide-commons/nice.js index 61115d1a..c3f692b0 100644 --- a/modules/nuclide-commons/nice.js +++ b/modules/nuclide-commons/nice.js @@ -1,3 +1,45 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.niceSafeSpawn = niceSafeSpawn; +exports.niceObserveProcess = niceObserveProcess; + +function _lruCache() { + const data = _interopRequireDefault(require("lru-cache")); + + _lruCache = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _which() { + const data = _interopRequireDefault(require("./which")); + + _which = function () { + return data; + }; + + return data; +} + +function _process() { + const data = require("./process"); + + _process = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,34 +48,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {LRUCache} from 'lru-cache'; -import type {ObserveProcessOptions, ProcessMessage} from './process'; - -import LRU from 'lru-cache'; -import {Observable} from 'rxjs'; - -import which from './which'; -import {spawn, observeProcess} from './process'; - const NICE_COMMAND = 'nice'; const IONICE_COMMAND = 'ionice'; -export async function niceSafeSpawn( - command: string, - args: Array, - execOptions?: Object, -): Promise { +async function niceSafeSpawn(command, args, execOptions) { const nicified = await nicifyCommand(command, args, execOptions); - const processStream = spawn(...nicified).publish(); + const processStream = (0, _process().spawn)(...nicified).publish(); const processPromise = processStream.take(1).toPromise(); processStream.connect(); return processPromise; } - /** * Takes the arguments that you would normally pass to `spawn()` and returns an array of new * arguments to use to run the command under `nice`. @@ -46,15 +73,15 @@ export async function niceSafeSpawn( * * See also `scriptifyCommand()` which does a similar thing but for `script`. */ -async function nicifyCommand( - command: string, - args?: Array, - options: T, -): Promise<[string, Array, T]> { + + +async function nicifyCommand(command, args, options) { const fullArgs = [command, ...(args || [])]; + if (await hasNiceCommand()) { fullArgs.unshift(NICE_COMMAND); } + if (await hasIoniceCommand()) { // Leave the process in the Best Effort class (default), but set it to the lowest priority for // that class. Priorities range from 0-7 with 4 as the default and lower numbers representing @@ -68,40 +95,38 @@ async function nicifyCommand( // think we can assume that it uses this interface if it exists. fullArgs.unshift(IONICE_COMMAND, '-n', '7'); } + return [fullArgs[0], fullArgs.slice(1), options]; } -const commandAvailabilityCache: LRUCache> = LRU({ +const commandAvailabilityCache = (0, _lruCache().default)({ max: 10, // Realistically this will not change very often so we can cache for long periods of time. We // probably could just check at startup and get away with it, but maybe someone will install // `ionice` and it would be nice to pick that up. - maxAge: 1000 * 60 * 5, // 5 minutes + maxAge: 1000 * 60 * 5 // 5 minutes + }); -function hasNiceCommand(): Promise { +function hasNiceCommand() { return hasCommand(NICE_COMMAND); } -function hasIoniceCommand(): Promise { +function hasIoniceCommand() { return hasCommand(IONICE_COMMAND); } -function hasCommand(command: string): Promise { - let result: ?Promise = commandAvailabilityCache.get(command); +function hasCommand(command) { + let result = commandAvailabilityCache.get(command); + if (result == null) { - result = which(command).then(x => x != null); + result = (0, _which().default)(command).then(x => x != null); commandAvailabilityCache.set(command, result); } + return result; } -export function niceObserveProcess( - command: string, - args?: Array, - options?: ObserveProcessOptions, -): Observable { - return Observable.defer(() => - nicifyCommand(command, args, options), - ).switchMap(spawnArgs => observeProcess(...spawnArgs)); -} +function niceObserveProcess(command, args, options) { + return _RxMin.Observable.defer(() => nicifyCommand(command, args, options)).switchMap(spawnArgs => (0, _process().observeProcess)(...spawnArgs)); +} \ No newline at end of file diff --git a/modules/nuclide-commons/nuclideUri.js b/modules/nuclide-commons/nuclideUri.js index 3f60c4f9..31ca0fef 100644 --- a/modules/nuclide-commons/nuclideUri.js +++ b/modules/nuclide-commons/nuclideUri.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.__TEST__ = exports.default = void 0; + +function _vscodeUri() { + const data = _interopRequireDefault(require("vscode-uri")); + + _vscodeUri = function () { + return data; + }; + + return data; +} + +var _path = _interopRequireDefault(require("path")); + +var _os = _interopRequireDefault(require("os")); + +function _string() { + const data = require("./string"); + + _string = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,106 +39,69 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - // NuclideUri's are either a local file path, or a URI // of the form nuclide:// // // This package creates, queries and decomposes NuclideUris. - -import LspUri from 'vscode-uri'; - -export type NuclideUri = string; - -type ParsedUrl = { - hostname: ?string, - path: string, -}; - -type ParsedRemoteUrl = { - hostname: string, - path: string, -}; - -type ParsedPath = { - root: string, - dir: string, - base: string, - ext: string, - name: string, -}; - -import invariant from 'assert'; // eslint-disable-next-line nuclide-internal/prefer-nuclide-uri -import pathModule from 'path'; - -import os from 'os'; -import {maybeToString} from './string'; - const ARCHIVE_SEPARATOR = '!'; const KNOWN_ARCHIVE_EXTENSIONS = ['.jar', '.zip']; - -const REMOTE_PATH_URI_PREFIX = 'nuclide://'; -// TODO(ljw): following regex is incorrect. A URI scheme must start with +const REMOTE_PATH_URI_PREFIX = 'nuclide://'; // TODO(ljw): following regex is incorrect. A URI scheme must start with // [A-Za-z] not [0-9_-]. Also, not all schemes require // after them. + const URI_PREFIX_REGEX = /^[A-Za-z0-9_-]+:\/\/.*/; -function isRemote(uri: NuclideUri): boolean { +function isRemote(uri) { return uri.startsWith(REMOTE_PATH_URI_PREFIX); -} +} // Atom often puts its URIs in places where we'd expect to see Nuclide URIs (or plain paths) -// Atom often puts its URIs in places where we'd expect to see Nuclide URIs (or plain paths) -function isAtomUri(uri: NuclideUri): boolean { + +function isAtomUri(uri) { return uri.startsWith('atom://'); } -function isUri(uri: string): boolean { +function isUri(uri) { return URI_PREFIX_REGEX.test(uri); } -function isLocal(uri: NuclideUri): boolean { +function isLocal(uri) { return !isRemote(uri) && !isUri(uri) && !isAtomUri(uri); } -function createRemoteUri(hostname: string, remotePath: string): string { - invariant( - remotePath != null && remotePath !== '', - 'NuclideUri must include a path.', - ); +function createRemoteUri(hostname, remotePath) { + if (!(remotePath != null && remotePath !== '')) { + throw new Error('NuclideUri must include a path.'); + } + return `nuclide://${hostname}${remotePath}`; } -function isInArchive(uri: NuclideUri): boolean { +function isInArchive(uri) { if (isAtomUri(uri) || uri.indexOf(ARCHIVE_SEPARATOR) < 0) { return false; } - for ( - let i = uri.indexOf(ARCHIVE_SEPARATOR); - i >= 0; - i = uri.indexOf(ARCHIVE_SEPARATOR, i + 1) - ) { + + for (let i = uri.indexOf(ARCHIVE_SEPARATOR); i >= 0; i = uri.indexOf(ARCHIVE_SEPARATOR, i + 1)) { if (_isArchiveSeparator(uri, i)) { return true; } } + return false; } -function ancestorOutsideArchive(uri: NuclideUri): NuclideUri { - for ( - let i = uri.indexOf(ARCHIVE_SEPARATOR); - i >= 0; - i = uri.indexOf(ARCHIVE_SEPARATOR, i + 1) - ) { +function ancestorOutsideArchive(uri) { + for (let i = uri.indexOf(ARCHIVE_SEPARATOR); i >= 0; i = uri.indexOf(ARCHIVE_SEPARATOR, i + 1)) { if (_isArchiveSeparator(uri, i)) { return uri.substring(0, i); } } + return uri; } - /** * Parses valid Nuclide URIs into the hostname and path components. * Throws an Error on invalid URIs. Invalid URIs are: @@ -115,67 +111,74 @@ function ancestorOutsideArchive(uri: NuclideUri): NuclideUri { * Everything that does not contain a '://' is assumed to be a local path. Both POSIX and Windows * paths are legal */ -function parse(uri: NuclideUri): ParsedUrl { + + +function parse(uri) { if (uri.startsWith(REMOTE_PATH_URI_PREFIX)) { const hostAndPath = uri.substr(REMOTE_PATH_URI_PREFIX.length); const hostSep = hostAndPath.indexOf('/'); - invariant( - hostSep !== -1, - `Remote URIs must contain a hostname and a path. Failed to parse ${uri}`, - ); + if (!(hostSep !== -1)) { + throw new Error(`Remote URIs must contain a hostname and a path. Failed to parse ${uri}`); + } const hostname = hostAndPath.substr(0, hostSep); - invariant( - hostname !== '', - `Remote URIs must contain a hostname. Failed to parse ${uri}`, - ); + + if (!(hostname !== '')) { + throw new Error(`Remote URIs must contain a hostname. Failed to parse ${uri}`); + } const path = hostAndPath.substr(hostSep); - invariant( - !_endsWithArchiveSeparator(uri), - `Path cannot end with archive separator. Failed to parse ${uri}`, - ); - return {hostname, path}; + + if (!!_endsWithArchiveSeparator(uri)) { + throw new Error(`Path cannot end with archive separator. Failed to parse ${uri}`); + } + + return { + hostname, + path + }; } - invariant( - !_endsWithArchiveSeparator(uri), - `Path cannot end with archive separator. Failed to parse ${uri}`, - ); - return {hostname: null, path: uri}; + if (!!_endsWithArchiveSeparator(uri)) { + throw new Error(`Path cannot end with archive separator. Failed to parse ${uri}`); + } + + return { + hostname: null, + path: uri + }; } -function parseRemoteUri(remoteUri: NuclideUri): ParsedRemoteUrl { +function parseRemoteUri(remoteUri) { if (!isRemote(remoteUri)) { throw new Error('Expected remote uri. Got ' + remoteUri); } + const parsedUri = parse(remoteUri); - invariant( - // flowlint-next-line sketchy-null-string:off - parsedUri.hostname, - `Remote Nuclide URIs must contain hostnames, '${maybeToString( - parsedUri.hostname, - )}' found while parsing '${remoteUri}'`, - ); - - // Explicitly copying object properties appeases Flow's "maybe" type handling. Using the `...` + + if (! // flowlint-next-line sketchy-null-string:off + parsedUri.hostname) { + throw new Error(`Remote Nuclide URIs must contain hostnames, '${(0, _string().maybeToString)(parsedUri.hostname)}' found while parsing '${remoteUri}'`); + } // Explicitly copying object properties appeases Flow's "maybe" type handling. Using the `...` // operator causes null/undefined errors, and `Object.assign` bypasses type checking. + + return { hostname: parsedUri.hostname, - path: parsedUri.path, + path: parsedUri.path }; } -function getPath(uri: NuclideUri): string { +function getPath(uri) { return parse(uri).path; } -function getHostname(remoteUri: NuclideUri): string { +function getHostname(remoteUri) { return parseRemoteUri(remoteUri).hostname; } -function getHostnameOpt(remoteUri: ?NuclideUri): ?string { +function getHostnameOpt(remoteUri) { if (remoteUri == null || !isRemote(remoteUri)) { return null; } @@ -183,133 +186,127 @@ function getHostnameOpt(remoteUri: ?NuclideUri): ?string { return getHostname(remoteUri); } -function join(uri: NuclideUri, ...relativePath: Array): NuclideUri { +function join(uri, ...relativePath) { return joinArray(uri, relativePath); } -function joinArray(uri: NuclideUri, relativePath: Array): NuclideUri { +function joinArray(uri, relativePath) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + if (isRemote(uri)) { - const {hostname, path} = parseRemoteUri(uri); + const { + hostname, + path + } = parseRemoteUri(uri); relativePath.splice(0, 0, path); + _archiveEncodeArrayInPlace(uriPathModule, relativePath); - return _archiveDecode( - uriPathModule, - createRemoteUri(hostname, uriPathModule.join.apply(null, relativePath)), - ); + + return _archiveDecode(uriPathModule, createRemoteUri(hostname, uriPathModule.join.apply(null, relativePath))); } else { relativePath.splice(0, 0, uri); + _archiveEncodeArrayInPlace(uriPathModule, relativePath); - return _archiveDecode( - uriPathModule, - uriPathModule.join.apply(null, relativePath), - ); + + return _archiveDecode(uriPathModule, uriPathModule.join.apply(null, relativePath)); } } -function archiveJoin(uri: NuclideUri, path: string): NuclideUri { +function archiveJoin(uri, path) { _testForIllegalUri(uri); + if (!KNOWN_ARCHIVE_EXTENSIONS.some(ext => uri.endsWith(ext))) { throw new Error(`Cannot archiveJoin with non-archive ${uri} and ${path}`); } + return uri + ARCHIVE_SEPARATOR + path; } -function normalize(uri: NuclideUri): NuclideUri { +function normalize(uri) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + if (isRemote(uri)) { - const {hostname, path} = parseRemoteUri(uri); - const normal = _archiveDecode( - uriPathModule, - uriPathModule.normalize(_archiveEncode(uriPathModule, path)), - ); + const { + hostname, + path + } = parseRemoteUri(uri); + + const normal = _archiveDecode(uriPathModule, uriPathModule.normalize(_archiveEncode(uriPathModule, path))); + return createRemoteUri(hostname, normal); } else { - return _archiveDecode( - uriPathModule, - uriPathModule.normalize(_archiveEncode(uriPathModule, uri)), - ); + return _archiveDecode(uriPathModule, uriPathModule.normalize(_archiveEncode(uriPathModule, uri))); } } -function normalizeDir(uri: NuclideUri): NuclideUri { +function normalizeDir(uri) { return ensureTrailingSeparator(normalize(uri)); } -function getParent(uri: NuclideUri): NuclideUri { +function getParent(uri) { // TODO: Is this different than dirname? return normalize(join(uri, '..')); } -function relative(uri: NuclideUri, other: NuclideUri): string { +function relative(uri, other) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + const remote = isRemote(uri); - if ( - remote !== isRemote(other) || - (remote && getHostname(uri) !== getHostname(other)) - ) { - throw new Error( - `Cannot relative urls on different hosts: ${uri} and ${other}`, - ); + + if (remote !== isRemote(other) || remote && getHostname(uri) !== getHostname(other)) { + throw new Error(`Cannot relative urls on different hosts: ${uri} and ${other}`); } + const uriEncode = _archiveEncode(uriPathModule, remote ? getPath(uri) : uri); - const otherEncode = _archiveEncode( - uriPathModule, - remote ? getPath(other) : other, - ); - return _archiveDecode( - uriPathModule, - uriPathModule.relative( - _matchTrailingArchive(uriEncode, otherEncode), - _matchTrailingArchive(otherEncode, uriEncode), - ), - ); -} - -function basename(uri: NuclideUri, ext: string = ''): string { + + const otherEncode = _archiveEncode(uriPathModule, remote ? getPath(other) : other); + + return _archiveDecode(uriPathModule, uriPathModule.relative(_matchTrailingArchive(uriEncode, otherEncode), _matchTrailingArchive(otherEncode, uriEncode))); +} + +function basename(uri, ext = '') { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); - return _archiveDecode( - uriPathModule, - uriPathModule.basename(_archiveEncode(uriPathModule, getPath(uri)), ext), - ); + + return _archiveDecode(uriPathModule, uriPathModule.basename(_archiveEncode(uriPathModule, getPath(uri)), ext)); } -function dirname(uri: NuclideUri): NuclideUri { +function dirname(uri) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + if (isRemote(uri)) { - const {hostname, path} = parseRemoteUri(uri); - return createRemoteUri( + const { hostname, - _archiveDecode( - uriPathModule, - uriPathModule.dirname(_archiveEncode(uriPathModule, path)), - ), - ); + path + } = parseRemoteUri(uri); + return createRemoteUri(hostname, _archiveDecode(uriPathModule, uriPathModule.dirname(_archiveEncode(uriPathModule, path)))); } else { - return _archiveDecode( - uriPathModule, - uriPathModule.dirname(_archiveEncode(uriPathModule, uri)), - ); + return _archiveDecode(uriPathModule, uriPathModule.dirname(_archiveEncode(uriPathModule, uri))); } } -function extname(uri: NuclideUri): string { +function extname(uri) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); - return _archiveDecode( - uriPathModule, - uriPathModule.extname(_archiveEncode(uriPathModule, getPath(uri))), - ); + + return _archiveDecode(uriPathModule, uriPathModule.extname(_archiveEncode(uriPathModule, getPath(uri)))); } -function stripExtension(uri: NuclideUri): NuclideUri { +function stripExtension(uri) { _testForIllegalUri(uri); + const ext = extname(uri); + if (ext.length === 0) { return uri; } @@ -317,12 +314,13 @@ function stripExtension(uri: NuclideUri): NuclideUri { return uri.slice(0, -1 * ext.length); } -function _isWindowsPath(path: string): boolean { - return _pathModuleFor(path) === pathModule.win32; +function _isWindowsPath(path) { + return _pathModuleFor(path) === _path.default.win32; } -function _getWindowsPathFromWindowsFileUri(uri: string): ?string { +function _getWindowsPathFromWindowsFileUri(uri) { const prefix = 'file://'; + if (!uri.startsWith(prefix)) { return null; } @@ -330,19 +328,21 @@ function _getWindowsPathFromWindowsFileUri(uri: string): ?string { const path = uri.substr(prefix.length); return _isWindowsPath(path) ? path : null; } - /** * uri is either a file: uri, or a nuclide: uri. * must convert file: uri's to just a path for atom. * * Returns null if not a valid file: URI. */ -function uriToNuclideUri(uri: string): ?string { + + +function uriToNuclideUri(uri) { // TODO(ljw): the following check is incorrect. It's designed to support // two-slash file URLs of the form "file://c:\path". But those are invalid // file URLs, and indeed it fails to %-escape "file://c:\My%20Documents". - const windowsPathFromUri = _getWindowsPathFromWindowsFileUri(uri); - // flowlint-next-line sketchy-null-string:off + const windowsPathFromUri = _getWindowsPathFromWindowsFileUri(uri); // flowlint-next-line sketchy-null-string:off + + if (windowsPathFromUri) { // If the specified URI is a local file:// URI to a Windows path, // handle specially first. url.parse() gets confused by the "X:" @@ -351,7 +351,7 @@ function uriToNuclideUri(uri: string): ?string { return windowsPathFromUri; } - const lspUri = LspUri.parse(uri); + const lspUri = _vscodeUri().default.parse(uri); if (lspUri.scheme === 'file' && lspUri.path) { // only handle real files for now. @@ -362,27 +362,29 @@ function uriToNuclideUri(uri: string): ?string { return null; } } - /** * Converts local paths to file: URI's. Leaves remote URI's alone. */ -function nuclideUriToUri(uri: NuclideUri): string { + + +function nuclideUriToUri(uri) { _testForIllegalUri(uri); + if (isRemote(uri)) { return uri; } else { - return LspUri.file(uri).toString(); + return _vscodeUri().default.file(uri).toString(); } } - /** * Returns true if child is equal to, or is a proper descendant of parent. */ -function contains(parent: NuclideUri, child: NuclideUri): boolean { + + +function contains(parent, child) { _testForIllegalUri(parent); - _testForIllegalUri(child); - // Can't just do startsWith here. If this directory is "www" and you + _testForIllegalUri(child); // Can't just do startsWith here. If this directory is "www" and you // are trying to check "www-base", just using startsWith would return // true, even though "www-base" is at the same level as "Www", not // contained in it. @@ -397,6 +399,7 @@ function contains(parent: NuclideUri, child: NuclideUri): boolean { // equal OR if the next character in the path to check is a path // separator, then we know the checked path is in this path. + if (child.length < parent.length) { // A strong indication of false // It could be a matter of a trailing separator, though @@ -405,10 +408,7 @@ function contains(parent: NuclideUri, child: NuclideUri): boolean { return false; } - return ( - parent.startsWith(child) && - (endsWithSeparator(parent) || _isArchiveSeparator(child, parent.length)) - ); + return parent.startsWith(child) && (endsWithSeparator(parent) || _isArchiveSeparator(child, parent.length)); } if (!child.startsWith(parent)) { @@ -421,64 +421,66 @@ function contains(parent: NuclideUri, child: NuclideUri): boolean { const uriPathModule = _pathModuleFor(child); - return ( - _isArchiveSeparator(child, parent.length) || - child.slice(parent.length).startsWith(uriPathModule.sep) - ); + return _isArchiveSeparator(child, parent.length) || child.slice(parent.length).startsWith(uriPathModule.sep); } - /** * Filter an array of paths to contain only the collapsed root paths, e.g. * [a/b/c, a/, c/d/, c/d/e] collapses to [a/, c/d/] */ -function collapse(paths: Array): Array { + + +function collapse(paths) { return paths.filter(p => !paths.some(fp => contains(fp, p) && fp !== p)); } -const hostFormatters = []; - -// A formatter which may shorten hostnames. +const hostFormatters = []; // A formatter which may shorten hostnames. // Returns null if the formatter won't shorten the hostname. -export type HostnameFormatter = (uri: NuclideUri) => ?string; // Registers a host formatter for nuclideUriToDisplayString -function registerHostnameFormatter(formatter: HostnameFormatter): IDisposable { +function registerHostnameFormatter(formatter) { hostFormatters.push(formatter); return { dispose: () => { const index = hostFormatters.indexOf(formatter); + if (index >= 0) { hostFormatters.splice(index, 1); } - }, + } }; } - /** * NuclideUris should never be shown to humans. * This function returns a human usable string. */ -function nuclideUriToDisplayString(uri: NuclideUri): string { + + +function nuclideUriToDisplayString(uri) { _testForIllegalUri(uri); + if (isRemote(uri)) { let hostname = getHostname(uri); + for (const formatter of hostFormatters) { - const formattedHostname = formatter(hostname); - // flowlint-next-line sketchy-null-string:off + const formattedHostname = formatter(hostname); // flowlint-next-line sketchy-null-string:off + if (formattedHostname) { hostname = formattedHostname; break; } } + return `${hostname}:${getPath(uri)}`; } else { return uri; } } -function ensureTrailingSeparator(uri: NuclideUri): NuclideUri { +function ensureTrailingSeparator(uri) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + if (uri.endsWith(uriPathModule.sep)) { return uri; } @@ -486,9 +488,11 @@ function ensureTrailingSeparator(uri: NuclideUri): NuclideUri { return uri + uriPathModule.sep; } -function trimTrailingSeparator(uri: NuclideUri): NuclideUri { +function trimTrailingSeparator(uri) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + let stripped = uri; while (stripped.endsWith(uriPathModule.sep) && !isRoot(stripped)) { @@ -498,123 +502,142 @@ function trimTrailingSeparator(uri: NuclideUri): NuclideUri { return stripped; } -function endsWithSeparator(uri: NuclideUri): boolean { +function endsWithSeparator(uri) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + return uri.endsWith(uriPathModule.sep); } -function isAbsolute(uri: NuclideUri): boolean { +function isAbsolute(uri) { _testForIllegalUri(uri); + if (isRemote(uri)) { return true; } else { const uriPathModule = _pathModuleFor(uri); + return uriPathModule.isAbsolute(uri); } } -function resolve(uri: NuclideUri, ...paths: Array): NuclideUri { +function resolve(uri, ...paths) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + if (isRemote(uri)) { - const {hostname, path} = parseRemoteUri(uri); + const { + hostname, + path + } = parseRemoteUri(uri); paths.splice(0, 0, path); + _archiveEncodeArrayInPlace(uriPathModule, paths); - return createRemoteUri( - hostname, - _archiveDecode(uriPathModule, uriPathModule.resolve.apply(null, paths)), - ); + + return createRemoteUri(hostname, _archiveDecode(uriPathModule, uriPathModule.resolve.apply(null, paths))); } else { paths.splice(0, 0, uri); + _archiveEncodeArrayInPlace(uriPathModule, paths); - return _archiveDecode( - uriPathModule, - uriPathModule.resolve.apply(null, paths), - ); + + return _archiveDecode(uriPathModule, uriPathModule.resolve.apply(null, paths)); } } -function expandHomeDir(uri: NuclideUri): NuclideUri { - _testForIllegalUri(uri); +function expandHomeDir(uri) { + _testForIllegalUri(uri); // Do not expand non home relative uris + - // Do not expand non home relative uris if (!uri.startsWith('~')) { return uri; - } - - // "home" on Windows is %UserProfile%. Note that Windows environment variables + } // "home" on Windows is %UserProfile%. Note that Windows environment variables // are NOT case sensitive, but process.env is a magic object that wraps GetEnvironmentVariableW // on Windows, so asking for any case is expected to work. - const {HOME, UserProfile} = process.env; - const isWindows = !isRemote(uri) && os.platform() === 'win32'; + + const { + HOME, + UserProfile + } = process.env; + const isWindows = !isRemote(uri) && _os.default.platform() === 'win32'; const homePath = isWindows ? UserProfile : HOME; - invariant(homePath != null); + + if (!(homePath != null)) { + throw new Error("Invariant violation: \"homePath != null\""); + } if (uri === '~') { return homePath; - } + } // Uris like ~abc should not be expanded + - // Uris like ~abc should not be expanded if (!uri.startsWith('~/') && (!isWindows || !uri.startsWith('~\\'))) { return uri; } - return pathModule.resolve(homePath, uri.replace('~', '.')); + return _path.default.resolve(homePath, uri.replace('~', '.')); } - /** * Splits a string containing local paths by an OS-specific path delimiter * Useful for splitting env variables such as PATH * * Since remote URI might contain the delimiter, only local paths are allowed. */ -function splitPathList(paths: string): Array { - invariant( - paths.indexOf(REMOTE_PATH_URI_PREFIX) < 0, - 'Splitting remote URIs is not supported', - ); + + +function splitPathList(paths) { + if (!(paths.indexOf(REMOTE_PATH_URI_PREFIX) < 0)) { + throw new Error('Splitting remote URIs is not supported'); + } + const uriPathModule = _pathModuleFor(paths); return paths.split(uriPathModule.delimiter); } - /** * Joins an array of local paths with an OS-specific path delimiter into a single string. * Useful for constructing env variables such as PATH * * Since remote URI might contain the delimiter, only local paths are allowed. */ -function joinPathList(paths: Array): string { + + +function joinPathList(paths) { if (paths.length === 0) { return ''; } - invariant( - paths.every(path => !isRemote(path)), - 'Joining of remote URIs is not supported', - ); + if (!paths.every(path => !isRemote(path))) { + throw new Error('Joining of remote URIs is not supported'); + } const uriPathModule = _pathModuleFor(paths[0]); + return paths.join(uriPathModule.delimiter); } - /** * This function prepends the given relative path with a "current-folder" prefix * which is `./` on *nix and .\ on Windows */ -function ensureLocalPrefix(uri: NuclideUri): NuclideUri { + + +function ensureLocalPrefix(uri) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); - invariant(!isRemote(uri), 'Local prefix can not be added to a remote path'); - invariant( - !isAbsolute(uri), - 'Local prefix can not be added to an absolute path', - ); + if (!!isRemote(uri)) { + throw new Error('Local prefix can not be added to a remote path'); + } + + if (!!isAbsolute(uri)) { + throw new Error('Local prefix can not be added to an absolute path'); + } const localPrefix = `.${uriPathModule.sep}`; + if (uri.startsWith(localPrefix)) { return uri; } @@ -622,42 +645,42 @@ function ensureLocalPrefix(uri: NuclideUri): NuclideUri { return localPrefix + uri; } -function isRoot(uri: NuclideUri): boolean { +function isRoot(uri) { _testForIllegalUri(uri); + return dirname(uri) === uri; } -function parsePath(uri: NuclideUri): ParsedPath { +function parsePath(uri) { _testForIllegalUri(uri); + const uriPathModule = _pathModuleFor(uri); + if (!isInArchive(uri)) { return uriPathModule.parse(getPath(uri)); } else { - const parsed = uriPathModule.parse( - _archiveEncode(uriPathModule, getPath(uri)), - ); + const parsed = uriPathModule.parse(_archiveEncode(uriPathModule, getPath(uri))); return { root: _archiveDecode(uriPathModule, parsed.root), dir: _archiveDecode(uriPathModule, parsed.dir), base: _archiveDecode(uriPathModule, parsed.base), ext: _archiveDecode(uriPathModule, parsed.ext), - name: _archiveDecode(uriPathModule, parsed.name), + name: _archiveDecode(uriPathModule, parsed.name) }; } } -function pathSeparatorFor(uri: NuclideUri): string { +function pathSeparatorFor(uri) { return _pathModuleFor(uri).sep; } -function split(uri: NuclideUri): Array { +function split(uri) { const parts = []; let current = uri; let parent = dirname(current); while (current !== parent) { parts.push(basename(current)); - current = parent; parent = dirname(current); } @@ -665,91 +688,66 @@ function split(uri: NuclideUri): Array { if (isAbsolute(uri)) { parts.push(parent); } + parts.reverse(); return parts; } -function hasKnownArchiveExtension(uri: NuclideUri): boolean { +function hasKnownArchiveExtension(uri) { return KNOWN_ARCHIVE_EXTENSIONS.some(ext => uri.endsWith(ext)); } -function _pathModuleFor(uri: NuclideUri): typeof pathModule { - invariant( - !_endsWithArchiveSeparator(uri), - `Path cannot end with archive separator. Failed to determine path module for ${uri}`, - ); - if (uri.startsWith(pathModule.posix.sep)) { - return pathModule.posix; +function _pathModuleFor(uri) { + if (!!_endsWithArchiveSeparator(uri)) { + throw new Error(`Path cannot end with archive separator. Failed to determine path module for ${uri}`); } - if (uri.indexOf('://') > -1) { - return pathModule.posix; + + if (uri.startsWith(_path.default.posix.sep)) { + return _path.default.posix; } - if (uri[1] === ':' && uri[2] === pathModule.win32.sep) { - return pathModule.win32; + + if (uri.indexOf('://') > -1) { + return _path.default.posix; } - // This little russian roulette here is blocking T29990593. I didn't + if (uri[1] === ':' && uri[2] === _path.default.win32.sep) { + return _path.default.win32; + } // This little russian roulette here is blocking T29990593. I didn't // clean it because we might see posix paths on windows and vice versa. - if ( - uri.split(pathModule.win32.sep).length > - uri.split(pathModule.posix.sep).length - ) { - return pathModule.win32; + + + if (uri.split(_path.default.win32.sep).length > uri.split(_path.default.posix.sep).length) { + return _path.default.win32; } else { - return pathModule.posix; + return _path.default.posix; } -} +} // Runs _archiveEncode in-place on array, and returns argument for convenience. -// Runs _archiveEncode in-place on array, and returns argument for convenience. -function _archiveEncodeArrayInPlace( - uriPathModule: typeof pathModule, - array: Array, -): Array { - array.forEach((uri, i, a) => (a[i] = _archiveEncode(uriPathModule, uri))); - return array; -} -// This adds a native separator after every archive separator +function _archiveEncodeArrayInPlace(uriPathModule, array) { + array.forEach((uri, i, a) => a[i] = _archiveEncode(uriPathModule, uri)); + return array; +} // This adds a native separator after every archive separator // so that the native path handling code sees them. -function _archiveEncode( - uriPathModule: typeof pathModule, - uri: NuclideUri, -): NuclideUri { + + +function _archiveEncode(uriPathModule, uri) { if (uri.indexOf(ARCHIVE_SEPARATOR) < 0) { return uri; } - return KNOWN_ARCHIVE_EXTENSIONS.reduce( - (acc, ext) => - acc.replace( - `${ext}${ARCHIVE_SEPARATOR}`, - `${ext}${ARCHIVE_SEPARATOR}${uriPathModule.sep}`, - ), - uri, - ); -} -// This is the inverse of `encodeArchiveSeparators()` to put things + return KNOWN_ARCHIVE_EXTENSIONS.reduce((acc, ext) => acc.replace(`${ext}${ARCHIVE_SEPARATOR}`, `${ext}${ARCHIVE_SEPARATOR}${uriPathModule.sep}`), uri); +} // This is the inverse of `encodeArchiveSeparators()` to put things // back after the native path handler has run. -function _archiveDecode( - uriPathModule: typeof pathModule, - uri: NuclideUri, -): NuclideUri { + + +function _archiveDecode(uriPathModule, uri) { if (uri.indexOf(ARCHIVE_SEPARATOR) < 0) { return uri; } - return _trimArchiveSuffix( - KNOWN_ARCHIVE_EXTENSIONS.reduce( - (acc, ext) => - acc.replace( - `${ext}${ARCHIVE_SEPARATOR}${uriPathModule.sep}`, - `${ext}${ARCHIVE_SEPARATOR}`, - ), - uri, - ), - ); -} -// When working with encoded uri's, the archive separator is part of the name + return _trimArchiveSuffix(KNOWN_ARCHIVE_EXTENSIONS.reduce((acc, ext) => acc.replace(`${ext}${ARCHIVE_SEPARATOR}${uriPathModule.sep}`, `${ext}${ARCHIVE_SEPARATOR}`), uri)); +} // When working with encoded uri's, the archive separator is part of the name // so we can manipulate paths with uriPathModule. However, in `relative` if // one uri contains the other, we need the names seen by uriPathModule to agree // on whether there is an archive separator or not. E.g. if we have: @@ -760,19 +758,17 @@ function _archiveDecode( // /etc/file.zip!/abc // We need to add a trailing '!' to the first one so uriPathModule can see that // the first contains the second. -function _matchTrailingArchive(uri: string, other: string): string { - if ( - uri.length < other.length && - other.startsWith(uri) && - _isArchiveSeparator(other, uri.length) - ) { + + +function _matchTrailingArchive(uri, other) { + if (uri.length < other.length && other.startsWith(uri) && _isArchiveSeparator(other, uri.length)) { return uri + ARCHIVE_SEPARATOR; } else { return uri; } } -function _trimArchiveSuffix(path: string): string { +function _trimArchiveSuffix(path) { if (_endsWithArchiveSeparator(path)) { return path.substring(0, path.length - ARCHIVE_SEPARATOR.length); } else { @@ -780,71 +776,66 @@ function _trimArchiveSuffix(path: string): string { } } -function _endsWithArchiveSeparator(path: string): boolean { +function _endsWithArchiveSeparator(path) { return _isArchiveSeparator(path, path.length - 1); } -function _isArchiveSeparator(path: string, index: number): boolean { - return ( - path.length > index && - path.charAt(index) === ARCHIVE_SEPARATOR && - KNOWN_ARCHIVE_EXTENSIONS.some(ext => { - const extStart = index - ext.length; - return path.indexOf(ext, extStart) === extStart; - }) - ); +function _isArchiveSeparator(path, index) { + return path.length > index && path.charAt(index) === ARCHIVE_SEPARATOR && KNOWN_ARCHIVE_EXTENSIONS.some(ext => { + const extStart = index - ext.length; + return path.indexOf(ext, extStart) === extStart; + }); } -function _testForIllegalUri(uri: ?NuclideUri): void { +function _testForIllegalUri(uri) { if (uri != null) { if (_endsWithArchiveSeparator(uri)) { - throw new Error( - `Path operation invoked on URI ending with ${ARCHIVE_SEPARATOR}: ${uri}`, - ); + throw new Error(`Path operation invoked on URI ending with ${ARCHIVE_SEPARATOR}: ${uri}`); } } } -const NUCLIDE_URI_TYPE_NAME = 'NuclideUri'; - -// If mustBeRemote is present then remote-ness must match, otherwise remote-ness +const NUCLIDE_URI_TYPE_NAME = 'NuclideUri'; // If mustBeRemote is present then remote-ness must match, otherwise remote-ness // is ignored. -function validate(uri: NuclideUri, mustBeRemote?: boolean): void { + +function validate(uri, mustBeRemote) { // Be a little extra paranoid to catch places where the type system may be weak. - invariant(uri != null, 'Unexpected null NuclideUri'); - invariant( - typeof uri === 'string', - `Unexpected NuclideUri type: ${String(uri)}`, - ); + if (!(uri != null)) { + throw new Error('Unexpected null NuclideUri'); + } + + if (!(typeof uri === 'string')) { + throw new Error(`Unexpected NuclideUri type: ${String(uri)}`); + } if (isRemote(uri)) { parse(uri); - invariant(mustBeRemote !== false, 'Expected remote NuclideUri'); + + if (!(mustBeRemote !== false)) { + throw new Error('Expected remote NuclideUri'); + } } else { - invariant(uri !== '', 'NuclideUri must contain a non-empty path'); - invariant(mustBeRemote !== true, 'Expected local NuclideUri'); + if (!(uri !== '')) { + throw new Error('NuclideUri must contain a non-empty path'); + } + + if (!(mustBeRemote !== true)) { + throw new Error('Expected local NuclideUri'); + } } } -const IMAGE_EXTENSIONS = new Set([ - '.bmp', - '.gif', - '.ico', - '.jpeg', - '.jpg', - '.png', - '.webp', -]); - +const IMAGE_EXTENSIONS = new Set(['.bmp', '.gif', '.ico', '.jpeg', '.jpg', '.png', '.webp']); /** * Returns true if this filename looks like an image that Nuclide can open; otherwise false. */ -function looksLikeImageUri(uri: NuclideUri): boolean { + +function looksLikeImageUri(uri) { const ext = extname(uri).toLowerCase(); return IMAGE_EXTENSIONS.has(ext); } -export default { +var _default = { basename, dirname, extname, @@ -890,9 +881,10 @@ export default { hasKnownArchiveExtension, ARCHIVE_SEPARATOR, KNOWN_ARCHIVE_EXTENSIONS, - NUCLIDE_URI_TYPE_NAME, + NUCLIDE_URI_TYPE_NAME }; - -export const __TEST__ = { - _pathModuleFor, +exports.default = _default; +const __TEST__ = { + _pathModuleFor }; +exports.__TEST__ = __TEST__; \ No newline at end of file diff --git a/modules/nuclide-commons/observable.js b/modules/nuclide-commons/observable.js index f0cecd3a..dcc9dba0 100644 --- a/modules/nuclide-commons/observable.js +++ b/modules/nuclide-commons/observable.js @@ -1,3 +1,82 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.splitStream = splitStream; +exports.bufferUntil = bufferUntil; +exports.cacheWhileSubscribed = cacheWhileSubscribed; +exports.diffSets = diffSets; +exports.reconcileSetDiffs = reconcileSetDiffs; +exports.reconcileSets = reconcileSets; +exports.toggle = toggle; +exports.compact = compact; +exports.takeWhileInclusive = takeWhileInclusive; +exports.concatLatest = concatLatest; +exports.throttle = throttle; +exports.completingSwitchMap = completingSwitchMap; +exports.mergeUntilAnyComplete = mergeUntilAnyComplete; +exports.fastDebounce = fastDebounce; +exports.fromAbortablePromise = fromAbortablePromise; +exports.toAbortablePromise = toAbortablePromise; +exports.takeUntilAbort = takeUntilAbort; +exports.poll = poll; +exports.SingletonExecutor = exports.nextAnimationFrame = exports.macrotask = exports.microtask = void 0; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("./UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _domexception() { + const data = _interopRequireDefault(require("domexception")); + + _domexception = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _AbortController() { + const data = _interopRequireDefault(require("./AbortController")); + + _AbortController = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("./collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _debounce() { + const data = _interopRequireDefault(require("./debounce")); + + _debounce = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,12 +85,11 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* global requestAnimationFrame, cancelAnimationFrame */ - // NOTE: Custom operators that require arguments should be written as higher-order functions. That // is, they should accept the arguments and return a function that accepts only an observable. This // allows a nice ergonomic way of using them with '.let()' (or a potential future pipe operator): @@ -23,17 +101,7 @@ // Observable.of('hey', 'everybody') // .let(makeExciting()) // .subscribe(x => console.log(x)); - -import type {AbortSignal} from './AbortController'; - -import UniversalDisposable from './UniversalDisposable'; -import invariant from 'assert'; // Note: DOMException is usable in Chrome but not in Node. -import DOMException from 'domexception'; -import {Observable, ReplaySubject, Subject} from 'rxjs'; -import AbortController from './AbortController'; -import {setDifference} from './collection'; -import debounce from './debounce'; /** * Splits a stream of strings on newlines. @@ -41,12 +109,9 @@ import debounce from './debounce'; * Sends any non-newline terminated data before closing. * Does not ensure a trailing newline. */ -export function splitStream( - input: Observable, - includeNewlines?: boolean = true, -): Observable { - return Observable.create(observer => { - let current: string = ''; +function splitStream(input, includeNewlines = true) { + return _RxMin.Observable.create(observer => { + let current = ''; function onEnd() { if (current !== '') { @@ -55,29 +120,25 @@ export function splitStream( } } - return input.subscribe( - value => { - const lines = value.split('\n'); - lines[0] = current + lines[0]; - current = lines.pop(); - if (includeNewlines) { - lines.forEach(line => observer.next(line + '\n')); - } else { - lines.forEach(line => observer.next(line)); - } - }, - error => { - onEnd(); - observer.error(error); - }, - () => { - onEnd(); - observer.complete(); - }, - ); + return input.subscribe(value => { + const lines = value.split('\n'); + lines[0] = current + lines[0]; + current = lines.pop(); + + if (includeNewlines) { + lines.forEach(line => observer.next(line + '\n')); + } else { + lines.forEach(line => observer.next(line)); + } + }, error => { + onEnd(); + observer.error(error); + }, () => { + onEnd(); + observer.complete(); + }); }); } - /** * Buffers until the predicate matches an element, then opens a new buffer. * @@ -87,40 +148,38 @@ export function splitStream( * (which includes the element). IMPORTANT: DO NOT MUTATE THE BUFFER. It returns a boolean * specifying whether to complete the buffer (and begin a new one). */ -export function bufferUntil( - condition: (item: T, buffer: Array) => boolean, -): (Observable) => Observable> { - return (stream: Observable) => - Observable.create(observer => { - let buffer = null; - const flush = () => { - if (buffer != null) { - observer.next(buffer); - buffer = null; - } - }; - return stream.subscribe( - x => { - if (buffer == null) { - buffer = []; - } - buffer.push(x); - if (condition(x, buffer)) { - flush(); - } - }, - err => { - flush(); - observer.error(err); - }, - () => { - flush(); - observer.complete(); - }, - ); + + +function bufferUntil(condition) { + return stream => _RxMin.Observable.create(observer => { + let buffer = null; + + const flush = () => { + if (buffer != null) { + observer.next(buffer); + buffer = null; + } + }; + + return stream.subscribe(x => { + if (buffer == null) { + buffer = []; + } + + buffer.push(x); + + if (condition(x, buffer)) { + flush(); + } + }, err => { + flush(); + observer.error(err); + }, () => { + flush(); + observer.complete(); }); + }); } - /** * Caches the latest element as long as there are subscribers. This is useful so that if consumers * unsubscribe and then subscribe much later, they do not get an ancient cached value. @@ -129,52 +188,45 @@ export function bufferUntil( * be just fine because the hot Observable will continue producing values even when there are no * subscribers, so you can be assured that the cached values are up-to-date. */ -export function cacheWhileSubscribed(input: Observable): Observable { - return input.multicast(() => new ReplaySubject(1)).refCount(); -} -type Diff = { - added: Set, - removed: Set, -}; + +function cacheWhileSubscribed(input) { + return input.multicast(() => new _RxMin.ReplaySubject(1)).refCount(); +} /** * Given a stream of sets, return a stream of diffs. * **IMPORTANT:** These sets are assumed to be immutable by convention. Don't mutate them! */ -export function diffSets( - hash?: (v: T) => any, -): (Observable>) => Observable> { - return (sets: Observable>) => - Observable.concat( - Observable.of(new Set()), // Always start with no items with an empty set - sets, - ) - .pairwise() - .map(([previous, next]) => ({ - added: setDifference(next, previous, hash), - removed: setDifference(previous, next, hash), - })) - .filter(diff => diff.added.size > 0 || diff.removed.size > 0); +function diffSets(hash) { + return sets => _RxMin.Observable.concat(_RxMin.Observable.of(new Set()), // Always start with no items with an empty set + sets).pairwise().map(([previous, next]) => ({ + added: (0, _collection().setDifference)(next, previous, hash), + removed: (0, _collection().setDifference)(previous, next, hash) + })).filter(diff => diff.added.size > 0 || diff.removed.size > 0); } - /** * Give a stream of diffs, perform an action for each added item and dispose of the returned * disposable when the item is removed. */ -export function reconcileSetDiffs( - diffs: Observable>, - addAction: (addedItem: T) => IDisposable, - hash_?: (v: T) => any, -): IDisposable { + + +function reconcileSetDiffs(diffs, addAction, hash_) { const hash = hash_ || (x => x); + const itemsToDisposables = new Map(); + const disposeItem = item => { const disposable = itemsToDisposables.get(hash(item)); - invariant(disposable != null); + + if (!(disposable != null)) { + throw new Error("Invariant violation: \"disposable != null\""); + } + disposable.dispose(); itemsToDisposables.delete(item); }; + const disposeAll = () => { itemsToDisposables.forEach(disposable => { disposable.dispose(); @@ -182,20 +234,15 @@ export function reconcileSetDiffs( itemsToDisposables.clear(); }; - return new UniversalDisposable( - diffs.subscribe(diff => { - // For every item that got added, perform the add action. - diff.added.forEach(item => { - itemsToDisposables.set(hash(item), addAction(item)); - }); + return new (_UniversalDisposable().default)(diffs.subscribe(diff => { + // For every item that got added, perform the add action. + diff.added.forEach(item => { + itemsToDisposables.set(hash(item), addAction(item)); + }); // "Undo" the add action for each item that got removed. - // "Undo" the add action for each item that got removed. - diff.removed.forEach(disposeItem); - }), - disposeAll, - ); + diff.removed.forEach(disposeItem); + }), disposeAll); } - /** * Given a stream of sets, perform a side-effect whenever an item is added (i.e. is present in a * set but wasn't in the previous set in the stream), and a corresponding cleanup when it's removed. @@ -224,120 +271,89 @@ export function reconcileSetDiffs( * of the dogs observable, his notification will remain until `disposable.dispose()` is called, at * which point the cleanup for all remaining items will be performed. */ -export function reconcileSets( - sets: Observable>, - addAction: (addedItem: T) => IDisposable, - hash?: (v: T) => any, -): IDisposable { + + +function reconcileSets(sets, addAction, hash) { const diffs = sets.let(diffSets(hash)); return reconcileSetDiffs(diffs, addAction, hash); } -export function toggle( - toggler: Observable, -): (Observable) => Observable { - return (source: Observable) => - toggler - .distinctUntilChanged() - .switchMap(enabled => (enabled ? source : Observable.empty())); +function toggle(toggler) { + return source => toggler.distinctUntilChanged().switchMap(enabled => enabled ? source : _RxMin.Observable.empty()); } -export function compact(source: Observable): Observable { +function compact(source) { // Flow does not understand the semantics of `filter` - return (source.filter(x => x != null): any); + return source.filter(x => x != null); } - /** * Like `takeWhile`, but includes the first item that doesn't match the predicate. */ -export function takeWhileInclusive( - predicate: (value: T) => boolean, -): (Observable) => Observable { - return (source: Observable) => - Observable.create(observer => - source.subscribe( - x => { - observer.next(x); - if (!predicate(x)) { - observer.complete(); - } - }, - err => { - observer.error(err); - }, - () => { - observer.complete(); - }, - ), - ); -} -// Concatenate the latest values from each input observable into one big list. + +function takeWhileInclusive(predicate) { + return source => _RxMin.Observable.create(observer => source.subscribe(x => { + observer.next(x); + + if (!predicate(x)) { + observer.complete(); + } + }, err => { + observer.error(err); + }, () => { + observer.complete(); + })); +} // Concatenate the latest values from each input observable into one big list. // Observables who have not emitted a value yet are treated as empty. -export function concatLatest( - ...observables: Array>> -): Observable> { + + +function concatLatest(...observables) { // First, tag all input observables with their index. - // Flow errors with ambiguity without the explicit annotation. - const tagged: Array, number]>> = observables.map( - (observable, index) => observable.map(list => [list, index]), - ); - return Observable.merge(...tagged) - .scan((accumulator, [list, index]) => { - accumulator[index] = list; - return accumulator; - }, observables.map(x => [])) - .map(accumulator => [].concat(...accumulator)); + const tagged = observables.map((observable, index) => observable.map(list => [list, index])); + return _RxMin.Observable.merge(...tagged).scan((accumulator, [list, index]) => { + accumulator[index] = list; + return accumulator; + }, observables.map(x => [])).map(accumulator => [].concat(...accumulator)); } -type ThrottleOptions = { - // Should the first element be emitted immeditately? Defaults to true. - leading?: boolean, -}; - /** * A more sensible alternative to RxJS's throttle/audit/sample operators. */ -export function throttle( - duration: - | number - | Observable - | ((value: T) => Observable | Promise), - options_: ?ThrottleOptions, -): (Observable) => Observable { - return (source: Observable) => { +function throttle(duration, options_) { + return source => { const options = options_ || {}; const leading = options.leading !== false; let audit; + switch (typeof duration) { case 'number': audit = obs => obs.auditTime(duration); + break; + case 'function': audit = obs => obs.audit(duration); + break; + default: audit = obs => obs.audit(() => duration); + } if (!leading) { return audit(source); } - return Observable.create(observer => { + return _RxMin.Observable.create(observer => { const connectableSource = source.publish(); - const throttled = Observable.merge( - connectableSource.take(1), - audit(connectableSource.skip(1)), - ); - return new UniversalDisposable( - throttled.subscribe(observer), - connectableSource.connect(), - ); + + const throttled = _RxMin.Observable.merge(connectableSource.take(1), audit(connectableSource.skip(1))); + + return new (_UniversalDisposable().default)(throttled.subscribe(observer), connectableSource.connect()); }); }; } - /** * Returns a new function which takes an `observable` and returns * `observable.switchMap(project)`, except that it completes @@ -351,38 +367,32 @@ export function throttle( * ends up returning an Observable that completes immediately. * With a regular switchMap, this would never terminate. */ -export function completingSwitchMap( - project: (input: T, index: number) => rxjs$ObservableInput, -): (Observable) => Observable { + + +function completingSwitchMap(project) { // An alternative implementation is to materialize the input observable, // but this avoids the creation of extra notifier objects. const completedSymbol = Symbol('completed'); - return (observable: Observable) => - Observable.concat( - observable, - Observable.of((completedSymbol: any)), - ).switchMap((input, index) => { - if (input === completedSymbol) { - return Observable.empty(); - } - return project(input, index); - }); -} + return observable => _RxMin.Observable.concat(observable, _RxMin.Observable.of(completedSymbol)).switchMap((input, index) => { + if (input === completedSymbol) { + return _RxMin.Observable.empty(); + } + return project(input, index); + }); +} /** * Returns a new observable consisting of the merged values from the passed * observables and completes when the first inner observable completes. */ -export function mergeUntilAnyComplete( - ...observables: Array> -): Observable { - const notifications = Observable.merge( - ...observables.map(o => o.materialize()), - ); - // $FlowFixMe add dematerialize to rxjs Flow types + + +function mergeUntilAnyComplete(...observables) { + const notifications = _RxMin.Observable.merge(...observables.map(o => o.materialize())); // $FlowFixMe add dematerialize to rxjs Flow types + + return notifications.dematerialize(); } - /** * RxJS's debounceTime is actually fairly inefficient: * on each event, it always clears its interval and [creates a new one][1]. @@ -395,29 +405,26 @@ export function mergeUntilAnyComplete( * * [1]: https://github.com/ReactiveX/rxjs/blob/master/src/operators/debounceTime.ts#L106 */ -export function fastDebounce( - delay: number, -): (Observable) => Observable { - return (observable: Observable) => - Observable.create(observer => { - const debouncedNext = debounce((x: T) => observer.next(x), delay); - const subscription = observable.subscribe( - debouncedNext, - observer.error.bind(observer), - observer.complete.bind(observer), - ); - return new UniversalDisposable(subscription, debouncedNext); - }); + + +function fastDebounce(delay) { + return observable => _RxMin.Observable.create(observer => { + const debouncedNext = (0, _debounce().default)(x => observer.next(x), delay); + const subscription = observable.subscribe(debouncedNext, observer.error.bind(observer), observer.complete.bind(observer)); + return new (_UniversalDisposable().default)(subscription, debouncedNext); + }); } -export const microtask = Observable.create(observer => { +const microtask = _RxMin.Observable.create(observer => { process.nextTick(() => { observer.next(); observer.complete(); }); }); -export const macrotask = Observable.create(observer => { +exports.microtask = microtask; + +const macrotask = _RxMin.Observable.create(observer => { const timerId = setImmediate(() => { observer.next(); observer.complete(); @@ -427,10 +434,13 @@ export const macrotask = Observable.create(observer => { }; }); -export const nextAnimationFrame = Observable.create(observer => { +exports.macrotask = macrotask; + +const nextAnimationFrame = _RxMin.Observable.create(observer => { if (typeof requestAnimationFrame === 'undefined') { throw new Error('This util can only be used in Atom'); } + const id = requestAnimationFrame(() => { observer.next(); observer.complete(); @@ -439,7 +449,6 @@ export const nextAnimationFrame = Observable.create(observer => { cancelAnimationFrame(id); }; }); - /** * Creates an Observable around an abortable promise. * Unsubscriptions are forwarded to the AbortController as an `abort()`. @@ -451,33 +460,30 @@ export const nextAnimationFrame = Observable.create(observer => { * Note that this can take a normal `() => Promise` too * (in which case this acts as just a plain `Observable.defer`). */ -export function fromAbortablePromise( - func: (signal: AbortSignal) => Promise, -): Observable { - return Observable.create(observer => { + + +exports.nextAnimationFrame = nextAnimationFrame; + +function fromAbortablePromise(func) { + return _RxMin.Observable.create(observer => { let completed = false; - const abortController = new AbortController(); - func(abortController.signal).then( - value => { - completed = true; - observer.next(value); - observer.complete(); - }, - error => { - completed = true; - observer.error(error); - }, - ); + const abortController = new (_AbortController().default)(); + func(abortController.signal).then(value => { + completed = true; + observer.next(value); + observer.complete(); + }, error => { + completed = true; + observer.error(error); + }); return () => { if (!completed) { - abortController.abort(); - // If the promise adheres to the spec, it should throw. + abortController.abort(); // If the promise adheres to the spec, it should throw. // The error will be captured above but go into the void. } }; }); } - /** * Converts an observable + AbortSignal into a cancellable Promise, * which rejects with an AbortError DOMException on abort. @@ -498,25 +504,21 @@ export function fromAbortablePromise( * It's currently unclear if this should be usable with let/pipe: * https://github.com/ReactiveX/rxjs/issues/3445 */ -export function toAbortablePromise( - observable: Observable, - signal?: ?AbortSignal, -): Promise { + + +function toAbortablePromise(observable, signal) { if (signal == null) { return observable.toPromise(); } + if (signal.aborted) { - return Promise.reject(DOMException('Aborted', 'AbortError')); + return Promise.reject((0, _domexception().default)('Aborted', 'AbortError')); } - return observable - .race( - Observable.fromEvent(signal, 'abort').map(() => { - throw new DOMException('Aborted', 'AbortError'); - }), - ) - .toPromise(); -} + return observable.race(_RxMin.Observable.fromEvent(signal, 'abort').map(() => { + throw new (_domexception().default)('Aborted', 'AbortError'); + })).toPromise(); +} /** * When using Observables with AbortSignals, be sure to use this - * it's really easy to miss the case when the signal is already aborted! @@ -525,37 +527,37 @@ export function toAbortablePromise( * myObservable * .let(obs => takeUntilAbort(obs, signal)) */ -export function takeUntilAbort( - observable: Observable, - signal: AbortSignal, -): Observable { - return Observable.defer(() => { + + +function takeUntilAbort(observable, signal) { + return _RxMin.Observable.defer(() => { if (signal.aborted) { - return Observable.empty(); + return _RxMin.Observable.empty(); } - return observable.takeUntil(Observable.fromEvent(signal, 'abort')); - }); -} -// Executes tasks. Ensures that at most one task is running at a time. + return observable.takeUntil(_RxMin.Observable.fromEvent(signal, 'abort')); + }); +} // Executes tasks. Ensures that at most one task is running at a time. // This class is handy for expensive tasks like processes, provided // you never want the result of a previous task after a new task has started. -export class SingletonExecutor { - _abortController: ?AbortController = null; + + +class SingletonExecutor { + constructor() { + this._abortController = null; + } // Executes(subscribes to) the task. // Will terminate(unsubscribe) to any previously executing task. // Subsequent executes() will terminate this task if called before // this task completes. - async execute(createTask: Observable): Promise { + async execute(createTask) { // Kill any previously running processes - this.cancel(); + this.cancel(); // Start a new process - // Start a new process - const controller = new AbortController(); - this._abortController = controller; + const controller = new (_AbortController().default)(); + this._abortController = controller; // Wait for the process to complete or be canceled ... - // Wait for the process to complete or be canceled ... try { return await toAbortablePromise(createTask, controller.signal); } finally { @@ -566,19 +568,20 @@ export class SingletonExecutor { } } - isExecuting(): boolean { + isExecuting() { return this._abortController != null; - } + } // Cancels any currently executing tasks. + - // Cancels any currently executing tasks. - cancel(): void { + cancel() { if (this._abortController != null) { this._abortController.abort(); + this._abortController = null; } } -} +} /** * Repeatedly subscribe to an observable every `delay` milliseconds, waiting for the observable to * complete each time. This is preferable to, say, `Observable.interval(d).switchMap(() => source)` @@ -598,21 +601,21 @@ export class SingletonExecutor { * }); * */ -export function poll(delay: number): (Observable) => Observable { - return (source: Observable) => - Observable.defer(() => { - const delays = new Subject(); - return delays - .switchMap(n => Observable.timer(n)) - .merge(Observable.of(null)) - .switchMap(() => { - const subscribedAt = Date.now(); - return source.do({ - complete: () => { - const timeElapsed = Date.now() - subscribedAt; - delays.next(Math.max(0, delay - timeElapsed)); - }, - }); - }); + + +exports.SingletonExecutor = SingletonExecutor; + +function poll(delay) { + return source => _RxMin.Observable.defer(() => { + const delays = new _RxMin.Subject(); + return delays.switchMap(n => _RxMin.Observable.timer(n)).merge(_RxMin.Observable.of(null)).switchMap(() => { + const subscribedAt = Date.now(); + return source.do({ + complete: () => { + const timeElapsed = Date.now() - subscribedAt; + delays.next(Math.max(0, delay - timeElapsed)); + } + }); }); -} + }); +} \ No newline at end of file diff --git a/modules/nuclide-commons/package.js b/modules/nuclide-commons/package.js index 9e5e007a..605e8980 100644 --- a/modules/nuclide-commons/package.js +++ b/modules/nuclide-commons/package.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getPackageMinorVersion = getPackageMinorVersion; + +var _fs = _interopRequireDefault(require("fs")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,24 +17,24 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import fs from 'fs'; -import invariant from 'assert'; - // Use a regex and not the "semver" module so the result here is the same // as from python code. const SEMVERISH_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([a-z0-9.-]+))?$/; -export function getPackageMinorVersion(packageJsonPath: string): string { - const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); +function getPackageMinorVersion(packageJsonPath) { + const pkgJson = JSON.parse(_fs.default.readFileSync(packageJsonPath, 'utf8')); const match = SEMVERISH_RE.exec(pkgJson.version); - invariant(match); - // const majorVersion = match[1]; - const minorVersion = match[2]; - // const patchVersion = match[3]; + + if (!match) { + throw new Error("Invariant violation: \"match\""); + } // const majorVersion = match[1]; + + + const minorVersion = match[2]; // const patchVersion = match[3]; // const prereleaseVersion = match[4]; + return minorVersion; -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/path-uri.js b/modules/nuclide-commons/path-uri.js index afdf9eff..59673c21 100644 --- a/modules/nuclide-commons/path-uri.js +++ b/modules/nuclide-commons/path-uri.js @@ -1,3 +1,15 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.pathToUri = pathToUri; +exports.uriToPath = uriToPath; + +var _url = _interopRequireDefault(require("url")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,13 +18,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import url from 'url'; - -export function pathToUri(path: string): string { +function pathToUri(path) { // TODO(ljw): this is not a valid way of constructing URIs. // The format is "file://server/absolute%20path" where // percent-escaping is to be used inside the path for all unsafe characters. @@ -22,14 +31,16 @@ export function pathToUri(path: string): string { return 'file://' + path; } -export function uriToPath(uri: string): string { +function uriToPath(uri) { // TODO: this will think that "c:\file.txt" uses the protocol "c", // rather than being a local filename. It also fails to recognize the host, // e.g. "file://server/path" vs "file://localhost/path" vs "file:///path". - const components = url.parse(uri); - // Some filename returned from hhvm does not have protocol. + const components = _url.default.parse(uri); // Some filename returned from hhvm does not have protocol. + + if (components.protocol !== 'file:' && components.protocol != null) { throw new Error(`unexpected file protocol. Got: ${components.protocol}`); } + return (components.pathname || '') + (components.hash || ''); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/performanceNow.js b/modules/nuclide-commons/performanceNow.js index bbcd3c1b..f2e6a8d5 100644 --- a/modules/nuclide-commons/performanceNow.js +++ b/modules/nuclide-commons/performanceNow.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +13,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ @@ -21,10 +28,9 @@ * // ... code you want to benchmark ... * const timeItTookInMilliseconds = performanceNow() - now; */ +var _default = typeof performance !== 'undefined' ? () => performance.now() : () => { + const [seconds, nanoseconds] = process.hrtime(); + return seconds * 1000 + nanoseconds / 1000000; +}; -export default (typeof performance !== 'undefined' - ? (): number => performance.now() - : (): number => { - const [seconds, nanoseconds] = process.hrtime(); - return seconds * 1000 + nanoseconds / 1000000; - }); +exports.default = _default; \ No newline at end of file diff --git a/modules/nuclide-commons/process.js b/modules/nuclide-commons/process.js index 50b0aa03..e22d5f1a 100644 --- a/modules/nuclide-commons/process.js +++ b/modules/nuclide-commons/process.js @@ -1,3 +1,128 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.runCommand = runCommand; +exports.observeProcess = observeProcess; +exports.runCommandDetailed = runCommandDetailed; +exports.observeProcessRaw = observeProcessRaw; +exports.spawn = spawn; +exports.fork = fork; +exports.getOutputStream = getOutputStream; +exports.scriptifyCommand = scriptifyCommand; +exports.killProcess = killProcess; +exports.killPid = killPid; +exports.getOriginalEnvironment = getOriginalEnvironment; +exports.exitEventToMessage = exitEventToMessage; +exports.getChildrenOfProcess = getChildrenOfProcess; +exports.psTree = psTree; +exports.parsePsOutput = parsePsOutput; +exports.memoryUsagePerPid = memoryUsagePerPid; +exports.preventStreamsFromThrowing = preventStreamsFromThrowing; +exports.logStreamErrors = logStreamErrors; +exports.getAbsoluteBinaryPathForPid = getAbsoluteBinaryPathForPid; +exports.killUnixProcessTree = killUnixProcessTree; +exports.loggedCalls = exports.ProcessLoggingEvent = exports.ProcessTimeoutError = exports.MaxBufferExceededError = exports.ProcessSystemError = exports.ProcessExitError = exports.LOG_CATEGORY = void 0; + +var _child_process = _interopRequireDefault(require("child_process")); + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +var _util = _interopRequireDefault(require("util")); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("./UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("./nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _performanceNow() { + const data = _interopRequireDefault(require("./performanceNow")); + + _performanceNow = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("./collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("./event"); + + _event = function () { + return data; + }; + + return data; +} + +function _stream() { + const data = require("./stream"); + + _stream = function () { + return data; + }; + + return data; +} + +function _observable() { + const data = require("./observable"); + + _observable = function () { + return data; + }; + + return data; +} + +function _string() { + const data = require("./string"); + + _string = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,10 +131,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - // // __ __ __ __ ___ __ __ __ // |__) |__) / \ / ` |__ /__` /__` | /__` @@ -47,29 +171,10 @@ // operators accidentally. // // [RxJS]: http://reactivex.io/rxjs/ - -import child_process from 'child_process'; -import idx from 'idx'; -import {getLogger} from 'log4js'; -import invariant from 'assert'; -import {Observable} from 'rxjs'; -import util from 'util'; - -import UniversalDisposable from './UniversalDisposable'; -import nuclideUri from './nuclideUri'; -import performanceNow from './performanceNow'; -import {MultiMap} from './collection'; -import {observableFromSubscribeFunction} from './event'; -import {observeStream} from './stream'; -import {splitStream, takeWhileInclusive} from './observable'; -import {shellQuote} from './string'; - -export const LOG_CATEGORY = 'nuclide-commons/process'; - +const LOG_CATEGORY = 'nuclide-commons/process'; +exports.LOG_CATEGORY = LOG_CATEGORY; const NUCLIDE_DO_NOT_LOG = global.NUCLIDE_DO_NOT_LOG; - -const logger = getLogger(LOG_CATEGORY); - +const logger = (0, _log4js().getLogger)(LOG_CATEGORY); /** * Run a command, accumulate the output. Errors are surfaced as stream errors and unsubscribing will * kill the process. In addition to the options accepted by Node's [`child_process.spawn()`][1] @@ -123,15 +228,10 @@ const logger = getLogger(LOG_CATEGORY); * [1]: https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options * [2]: https://nodejs.org/api/errors.html#errors_class_system_error */ -export function runCommand( - command: string, - args?: Array = [], - options?: ObserveProcessOptions = {}, - rest: void, -): Observable { + +function runCommand(command, args = [], options = {}, rest) { return runCommandDetailed(command, args, options).map(event => event.stdout); } - /** * Returns an observable that spawns a process and emits events on stdout, stderr and exit. Output * is buffered by line. Unsubscribing before the observable completes will kill the process. This @@ -158,21 +258,11 @@ export function runCommand( * }); * ``` */ -export function observeProcess( - command: string, - args?: Array, - options?: ObserveProcessOptions, -): Observable { - return spawn(command, args, options).flatMap(proc => - getOutputStream(proc, options), - ); -} -export type DetailedProcessResult = { - stdout: string, - stderr: string, - exitCode: ?number, -}; + +function observeProcess(command, args, options) { + return spawn(command, args, options).flatMap(proc => getOutputStream(proc, options)); +} /** * Identical to `runCommand()`, but instead of only emitting the accumulated stdout, the returned @@ -182,62 +272,65 @@ export type DetailedProcessResult = { * In general, you should prefer `runCommand()`, however, this function is useful for when stderr is * needed even if the process exits successfully. */ -export function runCommandDetailed( - command: string, - args?: Array = [], - options?: ObserveProcessOptions = {}, - rest: void, -): Observable { - const maxBuffer = idx(options, _ => _.maxBuffer) || DEFAULT_MAX_BUFFER; - return observeProcess(command, args, {...options, maxBuffer}) - .catch(error => { - // Catch ProcessExitErrors so that we can add stdout to them. - if (error instanceof ProcessExitError) { - return Observable.of({kind: 'process-exit-error', error}); - } - throw error; - }) - .reduce( - (acc, event) => { - switch (event.kind) { - case 'stdout': - return {...acc, stdout: acc.stdout + event.data}; - case 'stderr': - return {...acc, stderr: acc.stderr + event.data}; - case 'exit': - return {...acc, exitCode: event.exitCode}; - case 'process-exit-error': - const {error} = event; - throw new ProcessExitError( - error.exitCode, - error.signal, - error.process, - acc.stderr, - acc.stdout, - ); - default: - (event.kind: empty); - throw new Error(`Invalid event kind: ${event.kind}`); - } - }, - {stdout: '', stderr: '', exitCode: null}, - ); -} +function runCommandDetailed(command, args = [], options = {}, rest) { + var _ref; + + const maxBuffer = ((_ref = options) != null ? _ref.maxBuffer : _ref) || DEFAULT_MAX_BUFFER; + return observeProcess(command, args, Object.assign({}, options, { + maxBuffer + })).catch(error => { + // Catch ProcessExitErrors so that we can add stdout to them. + if (error instanceof ProcessExitError) { + return _RxMin.Observable.of({ + kind: 'process-exit-error', + error + }); + } + + throw error; + }).reduce((acc, event) => { + switch (event.kind) { + case 'stdout': + return Object.assign({}, acc, { + stdout: acc.stdout + event.data + }); + + case 'stderr': + return Object.assign({}, acc, { + stderr: acc.stderr + event.data + }); + + case 'exit': + return Object.assign({}, acc, { + exitCode: event.exitCode + }); + + case 'process-exit-error': + const { + error + } = event; + throw new ProcessExitError(error.exitCode, error.signal, error.process, acc.stderr, acc.stdout); + default: + event.kind; + throw new Error(`Invalid event kind: ${event.kind}`); + } + }, { + stdout: '', + stderr: '', + exitCode: null + }); +} /** * Identical to `observeProcess()`, but doesn't buffer by line. */ -export function observeProcessRaw( - command: string, - args?: Array, - options?: ObserveProcessOptions, -): Observable { - return spawn(command, args, options).flatMap(proc => - getOutputStream(proc, {...options, splitByLines: false}), - ); -} -// + +function observeProcessRaw(command, args, options) { + return spawn(command, args, options).flatMap(proc => getOutputStream(proc, Object.assign({}, options, { + splitByLines: false + }))); +} // // # Lower-level APIs // // The following functions are used to create the higher-level APIs above. It's rare that you'll @@ -270,25 +363,19 @@ export function observeProcessRaw( * }); * ``` */ -export function spawn( - command: string, - args?: Array, - options?: SpawnProcessOptions, -): Observable { + + +function spawn(command, args, options) { return createProcessStream('spawn', command, args, options); } - /** * Identical to `spawn()` (above), but uses `child_process.fork()` to create the process. */ -export function fork( - modulePath: string, - args?: Array, - options?: ForkProcessOptions, -): Observable { + + +function fork(modulePath, args, options) { return createProcessStream('fork', modulePath, args, options); } - /** * Creates a stream of sensibly-ordered stdout, stdin, and exit messages from a process. Generally, * you shouldn't use this function and should instead use `observeProcess()` (which makes use of @@ -300,80 +387,49 @@ export function fork( * This function intentionally does not close the process when you unsubscribe. It's usually used in * conjunction with `spawn()` which does that already. */ -export function getOutputStream( - proc: child_process$ChildProcess, - options?: GetOutputStreamOptions, - rest: void, -): Observable { - const chunk = - idx(options, _ => _.splitByLines) === false ? x => x : splitStream; - const maxBuffer = idx(options, _ => _.maxBuffer); - const isExitError = idx(options, _ => _.isExitError) || isExitErrorDefault; - const exitErrorBufferSize = idx(options, _ => _.exitErrorBufferSize) || 2000; - return Observable.defer(() => { - const stdoutEvents = chunk( - limitBufferSize(observeStream(proc.stdout), maxBuffer, 'stdout'), - ).map(data => ({kind: 'stdout', data})); - const stderrEvents = chunk( - limitBufferSize(observeStream(proc.stderr), maxBuffer, 'stderr'), - ) - .map(data => ({kind: 'stderr', data})) - .share(); - - // Accumulate the first `exitErrorBufferSize` bytes of stderr so that we can give feedback about + + +function getOutputStream(proc, options, rest) { + var _ref2, _ref3, _ref4, _ref5; + + const chunk = ((_ref2 = options) != null ? _ref2.splitByLines : _ref2) === false ? x => x : _observable().splitStream; + const maxBuffer = (_ref3 = options) != null ? _ref3.maxBuffer : _ref3; + const isExitError = ((_ref4 = options) != null ? _ref4.isExitError : _ref4) || isExitErrorDefault; + const exitErrorBufferSize = ((_ref5 = options) != null ? _ref5.exitErrorBufferSize : _ref5) || 2000; + return _RxMin.Observable.defer(() => { + const stdoutEvents = chunk(limitBufferSize((0, _stream().observeStream)(proc.stdout), maxBuffer, 'stdout')).map(data => ({ + kind: 'stdout', + data + })); + const stderrEvents = chunk(limitBufferSize((0, _stream().observeStream)(proc.stderr), maxBuffer, 'stderr')).map(data => ({ + kind: 'stderr', + data + })).share(); // Accumulate the first `exitErrorBufferSize` bytes of stderr so that we can give feedback about // about exit errors (then stop so we don't fill up memory with it). - const accumulatedStderr = stderrEvents - .scan( - (acc, event) => (acc + event.data).slice(0, exitErrorBufferSize), - '', - ) - .startWith('') - .let(takeWhileInclusive(acc => acc.length < exitErrorBufferSize)); - - // We need to start listening for the exit event immediately, but defer emitting it until the + + const accumulatedStderr = stderrEvents.scan((acc, event) => (acc + event.data).slice(0, exitErrorBufferSize), '').startWith('').let((0, _observable().takeWhileInclusive)(acc => acc.length < exitErrorBufferSize)); // We need to start listening for the exit event immediately, but defer emitting it until the // (buffered) output streams end. - const closeEvents = Observable.fromEvent( - proc, - // We listen to the "close" event instead of "exit" because we want to get all of the stdout - // and stderr. - 'close', - (exitCode: ?number, signal: ?string) => ({ - kind: 'exit', - exitCode, - signal, - }), - ) - .filter(isRealExit) - .take(1) - .withLatestFrom(accumulatedStderr) - .map(([event, stderr]) => { - if (isExitError(event)) { - throw new ProcessExitError( - event.exitCode, - event.signal, - proc, - stderr, - ); - } - return event; - }) - .publishReplay(); - const exitSub = closeEvents.connect(); - return Observable.merge(stdoutEvents, stderrEvents) - .concat(closeEvents) - .let( - takeWhileInclusive( - event => event.kind !== 'error' && event.kind !== 'exit', - ), - ) - .finally(() => { - exitSub.unsubscribe(); - }); - }); -} + const closeEvents = _RxMin.Observable.fromEvent(proc, // We listen to the "close" event instead of "exit" because we want to get all of the stdout + // and stderr. + 'close', (exitCode, signal) => ({ + kind: 'exit', + exitCode, + signal + })).filter(isRealExit).take(1).withLatestFrom(accumulatedStderr).map(([event, stderr]) => { + if (isExitError(event)) { + throw new ProcessExitError(event.exitCode, event.signal, proc, stderr); + } -// + return event; + }).publishReplay(); + + const exitSub = closeEvents.connect(); + return _RxMin.Observable.merge(stdoutEvents, stderrEvents).concat(closeEvents).let((0, _observable().takeWhileInclusive)(event => event.kind !== 'error' && event.kind !== 'exit')).finally(() => { + exitSub.unsubscribe(); + }); + }); +} // // # Miscellaneous Utilities // // The following utilities don't spawn processes or necessarily use observables. Instead, they're @@ -392,52 +448,45 @@ export function getOutputStream( * * See also `nicifyCommand()` which does a similar thing but for `nice`. */ -export function scriptifyCommand( - command: string, - args?: Array = [], - options: T, -): [string, Array, T] { + + +function scriptifyCommand(command, args = [], options) { if (process.platform === 'darwin') { // On OS X, script takes the program to run and its arguments as varargs at the end. return ['script', ['-q', '/dev/null', command].concat(args), options]; } else { // On Linux, script takes the command to run as the -c parameter so we have to combine all of // the arguments into a single string. - const joined = shellQuote([command, ...args]); - // flowlint-next-line sketchy-null-mixed:off - const opts = options || {}; - // flowlint-next-line sketchy-null-mixed:off + const joined = (0, _string().shellQuote)([command, ...args]); // flowlint-next-line sketchy-null-mixed:off + + const opts = options || {}; // flowlint-next-line sketchy-null-mixed:off + const env = opts.env || {}; - return [ - 'script', - ['-q', '/dev/null', '-c', joined], - // `script` will use `SHELL`, but shells have different behaviors with regard to escaping. To - // make sure that out escaping is correct, we need to force a particular shell. - {...opts, env: {...env, SHELL: '/bin/bash'}}, - ]; + return ['script', ['-q', '/dev/null', '-c', joined], // `script` will use `SHELL`, but shells have different behaviors with regard to escaping. To + // make sure that out escaping is correct, we need to force a particular shell. + Object.assign({}, opts, { + env: Object.assign({}, env, { + SHELL: '/bin/bash' + }) + })]; } } - /** * Kills a process and, optionally, its descendants. */ -export function killProcess( - proc: child_process$ChildProcess, - killTree: boolean, - killTreeSignal: ?string, -): void { - _killProcess(proc, killTree, killTreeSignal).then( - () => {}, - error => { - logger.error(`Killing process ${proc.pid} failed`, error); - }, - ); -} + +function killProcess(proc, killTree, killTreeSignal) { + _killProcess(proc, killTree, killTreeSignal).then(() => {}, error => { + logger.error(`Killing process ${proc.pid} failed`, error); + }); +} /** * Kill the process with the provided pid. */ -export function killPid(pid: number): void { + + +function killPid(pid) { try { process.kill(pid); } catch (err) { @@ -445,136 +494,123 @@ export function killPid(pid: number): void { throw err; } } -} - -// If provided, read the original environment from NUCLIDE_ORIGINAL_ENV. +} // If provided, read the original environment from NUCLIDE_ORIGINAL_ENV. // This should contain the base64-encoded output of `env -0`. + + let cachedOriginalEnvironment = null; -export async function getOriginalEnvironment(): Promise { + +async function getOriginalEnvironment() { await new Promise(resolve => { whenShellEnvironmentLoaded(resolve); }); + if (cachedOriginalEnvironment != null) { return cachedOriginalEnvironment; } - const {NUCLIDE_ORIGINAL_ENV} = process.env; + const { + NUCLIDE_ORIGINAL_ENV + } = process.env; + if (NUCLIDE_ORIGINAL_ENV != null && NUCLIDE_ORIGINAL_ENV.trim() !== '') { const envString = new Buffer(NUCLIDE_ORIGINAL_ENV, 'base64').toString(); cachedOriginalEnvironment = {}; + for (const envVar of envString.split('\0')) { // envVar should look like A=value_of_A const equalIndex = envVar.indexOf('='); + if (equalIndex !== -1) { - cachedOriginalEnvironment[ - envVar.substring(0, equalIndex) - ] = envVar.substring(equalIndex + 1); + cachedOriginalEnvironment[envVar.substring(0, equalIndex)] = envVar.substring(equalIndex + 1); } - } - // Guard against invalid original environments. + } // Guard against invalid original environments. + + if (!Object.keys(cachedOriginalEnvironment).length) { cachedOriginalEnvironment = process.env; } } else { cachedOriginalEnvironment = process.env; } + return cachedOriginalEnvironment; } - /** * Returns a string suitable for including in displayed error messages. */ -export function exitEventToMessage(event: { - exitCode: ?number, - signal: ?string, -}): string { + + +function exitEventToMessage(event) { if (event.exitCode != null) { return `exit code ${event.exitCode}`; } else { - invariant(event.signal != null); + if (!(event.signal != null)) { + throw new Error("Invariant violation: \"event.signal != null\""); + } + return `signal ${event.signal}`; } } -export async function getChildrenOfProcess( - processId: number, -): Promise> { +async function getChildrenOfProcess(processId) { const processes = await psTree(); - return processes.filter(processInfo => processInfo.parentPid === processId); } - /** * Get a list of descendants, sorted by increasing depth (including the one with the provided pid). */ -async function getDescendantsOfProcess( - pid: number, -): Promise> { + + +async function getDescendantsOfProcess(pid) { const processes = await psTree(); let rootProcessInfo; - const pidToChildren = new MultiMap(); + const pidToChildren = new (_collection().MultiMap)(); processes.forEach(info => { if (info.pid === pid) { rootProcessInfo = info; } + pidToChildren.add(info.parentPid, info); }); - const descendants = rootProcessInfo == null ? [] : [rootProcessInfo]; - // Walk through the array, adding the children of the current element to the end. This + const descendants = rootProcessInfo == null ? [] : [rootProcessInfo]; // Walk through the array, adding the children of the current element to the end. This // breadth-first traversal means that the elements will be sorted by depth. + for (let i = 0; i < descendants.length; i++) { const info = descendants[i]; const children = pidToChildren.get(info.pid); descendants.push(...Array.from(children)); } + return descendants; } -export async function psTree(): Promise> { +async function psTree() { if (isWindowsPlatform()) { return psTreeWindows(); } - const [commands, withArgs] = await Promise.all([ - runCommand('ps', ['-A', '-o', 'ppid,pid,comm']).toPromise(), - runCommand('ps', ['-A', '-ww', '-o', 'pid,args']).toPromise(), - ]); + const [commands, withArgs] = await Promise.all([runCommand('ps', ['-A', '-o', 'ppid,pid,comm']).toPromise(), runCommand('ps', ['-A', '-ww', '-o', 'pid,args']).toPromise()]); return parsePsOutput(commands, withArgs); } -async function psTreeWindows(): Promise> { - const stdout = await runCommand('wmic.exe', [ - 'PROCESS', - 'GET', - 'ParentProcessId,ProcessId,Name', - ]).toPromise(); +async function psTreeWindows() { + const stdout = await runCommand('wmic.exe', ['PROCESS', 'GET', 'ParentProcessId,ProcessId,Name']).toPromise(); return parsePsOutput(stdout); } -export function parsePsOutput( - psOutput: string, - argsOutput: ?string, -): Array { +function parsePsOutput(psOutput, argsOutput) { // Remove the first header line. - const lines = psOutput - .trim() - .split(/\n|\r\n/) - .slice(1); - + const lines = psOutput.trim().split(/\n|\r\n/).slice(1); let withArgs = new Map(); + if (argsOutput != null) { - withArgs = new Map( - argsOutput - .trim() - .split(/\n|\r\n/) - .slice(1) - .map(line => { - const columns = line.trim().split(/\s+/); - const pid = parseInt(columns[0], 10); - const command = columns.slice(1).join(' '); - return [pid, command]; - }), - ); + withArgs = new Map(argsOutput.trim().split(/\n|\r\n/).slice(1).map(line => { + const columns = line.trim().split(/\s+/); + const pid = parseInt(columns[0], 10); + const command = columns.slice(1).join(' '); + return [pid, command]; + })); } return lines.map(line => { @@ -583,172 +619,60 @@ export function parsePsOutput( const pid = parseInt(pidStr, 10); const command = columns.slice(2).join(' '); const commandWithArgs = withArgs.get(pid); - return { command, parentPid: parseInt(parentPid, 10), pid, - commandWithArgs: commandWithArgs == null ? command : commandWithArgs, + commandWithArgs: commandWithArgs == null ? command : commandWithArgs }; }); -} +} // Use `ps` to get memory usage in kb for an array of process id's as a map. + -// Use `ps` to get memory usage in kb for an array of process id's as a map. -export async function memoryUsagePerPid( - pids: Array, -): Promise> { +async function memoryUsagePerPid(pids) { const usage = new Map(); + if (pids.length >= 1) { try { - const stdout = await runCommand('ps', [ - '-p', - pids.join(','), - '-o', - 'pid=', - '-o', - 'rss=', - ]).toPromise(); + const stdout = await runCommand('ps', ['-p', pids.join(','), '-o', 'pid=', '-o', 'rss=']).toPromise(); stdout.split('\n').forEach(line => { const parts = line.trim().split(/\s+/); + if (parts.length === 2) { const [pid, rss] = parts.map(x => parseInt(x, 10)); usage.set(pid, rss); } }); - } catch (err) { - // Ignore errors. + } catch (err) {// Ignore errors. } } + return usage; } - /** * Add no-op error handlers to the process's streams so that Node doesn't throw them. */ -export function preventStreamsFromThrowing( - proc: child_process$ChildProcess, -): IDisposable { - return new UniversalDisposable(getStreamErrorEvents(proc).subscribe()); -} + +function preventStreamsFromThrowing(proc) { + return new (_UniversalDisposable().default)(getStreamErrorEvents(proc).subscribe()); +} /** * Log errors from a process's streams. This function returns an `rxjs$ISubscription` so that it * can easily be used with `Observable.using()`. */ -export function logStreamErrors( - proc: child_process$ChildProcess, - command: string, - args: Array, - options?: Object, -): IDisposable & rxjs$ISubscription { - return new UniversalDisposable( - getStreamErrorEvents(proc) - .do(([err, streamName]) => { - logger.error( - `stream error on stream ${streamName} with command:`, - command, - args, - options, - 'error:', - err, - ); - }) - .subscribe(), - ); -} -// + +function logStreamErrors(proc, command, args, options) { + return new (_UniversalDisposable().default)(getStreamErrorEvents(proc).do(([err, streamName]) => { + logger.error(`stream error on stream ${streamName} with command:`, command, args, options, 'error:', err); + }).subscribe()); +} // // Types // - // Exactly one of exitCode and signal will be non-null. // Killing a process will result in a null exitCode but a non-null signal. -export type ProcessExitMessage = { - kind: 'exit', - exitCode: ?number, - signal: ?string, -}; - -export type ProcessMessage = - | { - kind: 'stdout', - data: string, - } - | { - kind: 'stderr', - data: string, - } - | ProcessExitMessage; - -// In older versions of process.js, errors were emitted as messages instead of errors. This type -// exists to support the transition, but no new usages should be added. -export type LegacyProcessMessage = - | ProcessMessage - | {kind: 'error', error: Object}; - -export type ProcessInfo = { - parentPid: number, - pid: number, - command: string, - commandWithArgs: string, -}; - -export type Level = 'info' | 'log' | 'warning' | 'error' | 'debug' | 'success'; -export type Message = {text: string, level: Level}; - -export type MessageEvent = { - type: 'message', - message: Message, -}; -export type ProgressEvent = { - type: 'progress', - progress: ?number, -}; - -export type ResultEvent = { - type: 'result', - result: mixed, -}; - -export type StatusEvent = { - type: 'status', - status: ?string, -}; - -export type TaskEvent = - | MessageEvent - | ProgressEvent - | ResultEvent - | StatusEvent; - -type CreateProcessStreamOptions = ( - | child_process$spawnOpts - | child_process$forkOpts -) & { - killTreeWhenDone?: ?boolean, - killTreeSignal?: string, - timeout?: ?number, - input?: ?(string | Observable), - dontLogInNuclide?: ?boolean, -}; - -type GetOutputStreamOptions = { - splitByLines?: ?boolean, - maxBuffer?: ?number, - exitErrorBufferSize?: ?number, - isExitError?: ?(event: ProcessExitMessage) => boolean, -}; - -export type ObserveProcessOptions = SpawnProcessOptions & - GetOutputStreamOptions; - -export type SpawnProcessOptions = child_process$spawnOpts & - CreateProcessStreamOptions; -export type ForkProcessOptions = child_process$forkOpts & - CreateProcessStreamOptions; - -export type ProcessError = ProcessSystemError | ProcessExitError; // // Errors @@ -763,33 +687,18 @@ export type ProcessError = ProcessSystemError | ProcessExitError; * `observeProcess()`, it will be truncated. Similarly, `stdout` will only be populated when the * error is thrown by output-accumulating functions. For others, it will always be `null`. */ -export class ProcessExitError extends Error { - exitCode: ?number; - signal: ?string; - stderr: string; - stdout: ?string; - command: string; - args: Array; - process: child_process$ChildProcess; - - constructor( - exitCode: ?number, - signal: ?string, - proc: child_process$ChildProcess, - stderr: string, - stdout?: string, - ) { +class ProcessExitError extends Error { + constructor(exitCode, signal, proc, stderr, stdout) { // $FlowIssue: This isn't typed in the Flow node type defs - const {spawnargs} = proc; - const argsAndCommand = - spawnargs[0] === process.execPath ? spawnargs.slice(1) : spawnargs; + const { + spawnargs + } = proc; + const argsAndCommand = spawnargs[0] === process.execPath ? spawnargs.slice(1) : spawnargs; const [command, ...args] = argsAndCommand; - super( - `"${command}" failed with ${exitEventToMessage({ - exitCode, - signal, - })}\n\n${stderr}\n\n${argsAndCommand.join(' ')}`, - ); + super(`"${command}" failed with ${exitEventToMessage({ + exitCode, + signal + })}\n\n${stderr}\n\n${argsAndCommand.join(' ')}`); this.name = 'ProcessExitError'; this.exitCode = exitCode; this.signal = signal; @@ -799,20 +708,18 @@ export class ProcessExitError extends Error { this.args = args; this.process = proc; } -} +} /** * Process system errors are just augmented Error objects. We wrap the errors and expose the process * since our utilities throw the errors before returning the process. */ -export class ProcessSystemError extends Error { - errno: number | string; - code: string; - path: ?string; - syscall: ?string; - process: child_process$ChildProcess; - - constructor(err: any, proc: child_process$ChildProcess) { + + +exports.ProcessExitError = ProcessExitError; + +class ProcessSystemError extends Error { + constructor(err, proc) { super(err.message); this.name = 'ProcessSystemError'; this.errno = err.errno; @@ -821,87 +728,92 @@ export class ProcessSystemError extends Error { this.syscall = err.syscall; this.process = proc; } + } -export class MaxBufferExceededError extends Error { - constructor(streamName: string) { +exports.ProcessSystemError = ProcessSystemError; + +class MaxBufferExceededError extends Error { + constructor(streamName) { super(`${streamName} maxBuffer exceeded`); this.name = 'MaxBufferExceededError'; } + } -export class ProcessTimeoutError extends Error { - constructor(timeout: number, proc: child_process$ChildProcess) { +exports.MaxBufferExceededError = MaxBufferExceededError; + +class ProcessTimeoutError extends Error { + constructor(timeout, proc) { // $FlowIssue: This isn't typed in the Flow node type defs - const {spawnargs} = proc; - const commandName = - spawnargs[0] === process.execPath ? spawnargs[1] : spawnargs[0]; + const { + spawnargs + } = proc; + const commandName = spawnargs[0] === process.execPath ? spawnargs[1] : spawnargs[0]; super(`"${commandName}" timed out after ${timeout}ms`); this.name = 'ProcessTimeoutError'; } -} -// +} // // Internal Stuff // // Pay no attention! This is just stuff that's used internally to implement the good stuff. // - // Node crashes if we allow buffers that are too large. -const DEFAULT_MAX_BUFFER = 100 * 1024 * 1024; + +exports.ProcessTimeoutError = ProcessTimeoutError; +const DEFAULT_MAX_BUFFER = 100 * 1024 * 1024; const MAX_LOGGED_CALLS = 100; const NUM_PRESERVED_HISTORY_CALLS = 50; - -const noopDisposable = {dispose: () => {}}; -const whenShellEnvironmentLoaded = - typeof atom !== 'undefined' && !atom.inSpecMode() - ? atom.whenShellEnvironmentLoaded.bind(atom) - : cb => { - cb(); - return noopDisposable; - }; - +const noopDisposable = { + dispose: () => {} +}; +const whenShellEnvironmentLoaded = typeof atom !== 'undefined' && !atom.inSpecMode() ? atom.whenShellEnvironmentLoaded.bind(atom) : cb => { + cb(); + return noopDisposable; +}; /** * Log custom events to log4js so that we can easily hook into process events * using a custom log4js appender (e.g. for analytics purposes). */ -export class ProcessLoggingEvent { - command: string; - duration: number; - constructor(command: string, duration: number) { +class ProcessLoggingEvent { + constructor(command, duration) { this.command = command; - this.duration = duration; - // log4js uses util.inspect to convert log arguments to strings. + this.duration = duration; // log4js uses util.inspect to convert log arguments to strings. // Note: computed property methods aren't supported by Flow yet. - (this: any)[util.inspect.custom] = () => { + + this[_util.default.inspect.custom] = () => { return `${this.duration}ms: ${this.command}`; }; } + } -export const loggedCalls = []; -function logCall(duration: number, command: string, args: Array) { +exports.ProcessLoggingEvent = ProcessLoggingEvent; +const loggedCalls = []; +exports.loggedCalls = loggedCalls; + +function logCall(duration, command, args) { // Trim the history once in a while, to avoid doing expensive array // manipulation all the time after we reached the end of the history if (loggedCalls.length > MAX_LOGGED_CALLS) { loggedCalls.splice(0, loggedCalls.length - NUM_PRESERVED_HISTORY_CALLS, { command: '... history stripped ...', duration: 0, - time: new Date(), + time: new Date() }); } - const fullCommand = shellQuote([command, ...args]); + const fullCommand = (0, _string().shellQuote)([command, ...args]); loggedCalls.push({ command: fullCommand, duration, - time: new Date(), + time: new Date() }); logger.info(new ProcessLoggingEvent(fullCommand, duration)); } - /** * Attempt to get the fully qualified binary name from a process id. This is * surprisingly tricky. 'ps' only reports the path as invoked, and in some cases @@ -912,9 +824,9 @@ function logCall(duration: number, command: string, args: Array) { * an open FD to the executable. This can fail for various reasons (mostly * not having permissions to execute lsof on the pid.) */ -export async function getAbsoluteBinaryPathForPid( - pid: number, -): Promise { + + +async function getAbsoluteBinaryPathForPid(pid) { if (process.platform === 'linux') { return _getLinuxBinaryPathForPid(pid); } @@ -926,33 +838,17 @@ export async function getAbsoluteBinaryPathForPid( return null; } -async function _getLinuxBinaryPathForPid(pid: number): Promise { - const exeLink = `/proc/${pid}/exe`; - // /proc/xxx/exe is a symlink to the real binary in the file system. - return runCommand('/bin/realpath', ['-q', '-e', exeLink]) - .catch(_ => Observable.of(null)) - .toPromise(); -} +async function _getLinuxBinaryPathForPid(pid) { + const exeLink = `/proc/${pid}/exe`; // /proc/xxx/exe is a symlink to the real binary in the file system. -async function _getDarwinBinaryPathForPid(pid: number): Promise { - return runCommand('/usr/sbin/lsof', ['-p', `${pid}`]) - .catch(_ => { - return Observable.of(null); - }) - .map( - stdout => - stdout == null - ? null - : stdout - .split('\n') - .map(line => line.trim().split(/\s+/)) - .filter(line => line[3] === 'txt') - .map(line => line[8])[0], - ) - .take(1) - .toPromise(); + return runCommand('/bin/realpath', ['-q', '-e', exeLink]).catch(_ => _RxMin.Observable.of(null)).toPromise(); } +async function _getDarwinBinaryPathForPid(pid) { + return runCommand('/usr/sbin/lsof', ['-p', `${pid}`]).catch(_ => { + return _RxMin.Observable.of(null); + }).map(stdout => stdout == null ? null : stdout.split('\n').map(line => line.trim().split(/\s+/)).filter(line => line[3] === 'txt').map(line => line[8])[0]).take(1).toPromise(); +} /** * Creates an observable with the following properties: * @@ -965,169 +861,120 @@ async function _getDarwinBinaryPathForPid(pid: number): Promise { * * IMPORTANT: The exit event does NOT mean that all stdout and stderr events have been received. */ -function createProcessStream( - type: 'spawn' | 'fork' = 'spawn', - commandOrModulePath: string, - args?: Array = [], - options?: CreateProcessStreamOptions = {}, -): Observable { + + +function createProcessStream(type = 'spawn', commandOrModulePath, args = [], options = {}) { const inputOption = options.input; let input; + if (inputOption != null) { - input = - typeof inputOption === 'string' - ? Observable.of(inputOption) - : inputOption; + input = typeof inputOption === 'string' ? _RxMin.Observable.of(inputOption) : inputOption; } - return observableFromSubscribeFunction(whenShellEnvironmentLoaded) - .take(1) - .switchMap(() => { - const { - dontLogInNuclide, - killTreeWhenDone, - killTreeSignal, - timeout, - } = options; - // flowlint-next-line sketchy-null-number:off - const enforceTimeout = timeout - ? x => - x.timeoutWith( - timeout, - Observable.throw(new ProcessTimeoutError(timeout, proc)), - ) - : x => x; - const proc = child_process[type]( - nuclideUri.expandHomeDir(commandOrModulePath), - args, - // $FlowFixMe: child_process$spawnOpts and child_process$forkOpts have incompatible stdio types. - {...options}, - ); - - // Don't let Node throw stream errors and crash the process. Note that we never dispose of - // this because stream errors can still occur after the user unsubscribes from our process - // observable. That's okay; when the streams close, the listeners will be removed. - preventStreamsFromThrowing(proc); - - // If we were to connect the error handler as part of the returned observable, unsubscribing - // would cause it to be removed. That would leave no attached error handler, so node would - // throw, triggering Atom's uncaught exception handler. - const errors = Observable.fromEvent(proc, 'error') - .flatMap(Observable.throw) - .publish(); - errors.connect(); - - const exitEvents = Observable.fromEvent( - proc, - 'exit', - (exitCode: ?number, signal: ?string) => ({ - kind: 'exit', - exitCode, - signal, - }), - ) - .filter(isRealExit) - .take(1); - - if (dontLogInNuclide !== true && NUCLIDE_DO_NOT_LOG !== true) { - // Log the completion of the process. Note that we intentionally don't merge this with the - // returned observable because we don't want to cancel the side-effect when the user - // unsubscribes or when the process exits ("close" events come after "exit" events). - const now = performanceNow(); - Observable.fromEvent(proc, 'close') - .do(() => { - logCall( - Math.round(performanceNow() - now), - commandOrModulePath, - args, - ); - }) - .subscribe(); + return (0, _event().observableFromSubscribeFunction)(whenShellEnvironmentLoaded).take(1).switchMap(() => { + const { + dontLogInNuclide, + killTreeWhenDone, + killTreeSignal, + timeout + } = options; // flowlint-next-line sketchy-null-number:off + + const enforceTimeout = timeout ? x => x.timeoutWith(timeout, _RxMin.Observable.throw(new ProcessTimeoutError(timeout, proc))) : x => x; + + const proc = _child_process.default[type](_nuclideUri().default.expandHomeDir(commandOrModulePath), args, // $FlowFixMe: child_process$spawnOpts and child_process$forkOpts have incompatible stdio types. + Object.assign({}, options)); // Don't let Node throw stream errors and crash the process. Note that we never dispose of + // this because stream errors can still occur after the user unsubscribes from our process + // observable. That's okay; when the streams close, the listeners will be removed. + + + preventStreamsFromThrowing(proc); // If we were to connect the error handler as part of the returned observable, unsubscribing + // would cause it to be removed. That would leave no attached error handler, so node would + // throw, triggering Atom's uncaught exception handler. + + const errors = _RxMin.Observable.fromEvent(proc, 'error').flatMap(_RxMin.Observable.throw).publish(); + + errors.connect(); + + const exitEvents = _RxMin.Observable.fromEvent(proc, 'exit', (exitCode, signal) => ({ + kind: 'exit', + exitCode, + signal + })).filter(isRealExit).take(1); + + if (dontLogInNuclide !== true && NUCLIDE_DO_NOT_LOG !== true) { + // Log the completion of the process. Note that we intentionally don't merge this with the + // returned observable because we don't want to cancel the side-effect when the user + // unsubscribes or when the process exits ("close" events come after "exit" events). + const now = (0, _performanceNow().default)(); + + _RxMin.Observable.fromEvent(proc, 'close').do(() => { + logCall(Math.round((0, _performanceNow().default)() - now), commandOrModulePath, args); + }).subscribe(); + } + + let finished = false; + return enforceTimeout(_RxMin.Observable.using( // Log stream errors, but only for as long as you're subscribed to the process observable. + () => logStreamErrors(proc, commandOrModulePath, args, options), () => _RxMin.Observable.merge( // Node [delays the emission of process errors][1] by a tick in order to give + // consumers a chance to subscribe to the error event. This means that our observable + // would normally emit the process and then, a tick later, error. However, it's more + // convenient to never emit the process if there was an error. Although observables + // don't require the error to be delayed at all, the underlying event emitter + // abstraction does, so we'll just roll with that and use `pid == null` as a signal + // that an error is forthcoming. + // + // [1]: https://github.com/nodejs/node/blob/v7.10.0/lib/internal/child_process.js#L301 + proc.pid == null ? _RxMin.Observable.empty() : _RxMin.Observable.of(proc), _RxMin.Observable.never() // Don't complete until we say so! + )).merge( // Write any input to stdin. This is just for the side-effect. We merge it here to + // ensure that writing to the stdin stream happens after our event listeners are added. + input == null ? _RxMin.Observable.empty() : input.do({ + next: str => { + proc.stdin.write(str); + }, + complete: () => { + proc.stdin.end(); + } + }).ignoreElements()).takeUntil(errors).takeUntil(exitEvents).do({ + error: () => { + finished = true; + }, + complete: () => { + finished = true; + } + })).catch(err => { + // Since this utility errors *before* emitting the process, add the process to the error + // so that users can get whatever info they need off of it. + if (err instanceof Error && err.name === 'Error' && 'errno' in err) { + throw new ProcessSystemError(err, proc); } - let finished = false; - return enforceTimeout( - Observable.using( - // Log stream errors, but only for as long as you're subscribed to the process observable. - () => logStreamErrors(proc, commandOrModulePath, args, options), - () => - Observable.merge( - // Node [delays the emission of process errors][1] by a tick in order to give - // consumers a chance to subscribe to the error event. This means that our observable - // would normally emit the process and then, a tick later, error. However, it's more - // convenient to never emit the process if there was an error. Although observables - // don't require the error to be delayed at all, the underlying event emitter - // abstraction does, so we'll just roll with that and use `pid == null` as a signal - // that an error is forthcoming. - // - // [1]: https://github.com/nodejs/node/blob/v7.10.0/lib/internal/child_process.js#L301 - proc.pid == null ? Observable.empty() : Observable.of(proc), - Observable.never(), // Don't complete until we say so! - ), - ) - .merge( - // Write any input to stdin. This is just for the side-effect. We merge it here to - // ensure that writing to the stdin stream happens after our event listeners are added. - input == null - ? Observable.empty() - : input - .do({ - next: str => { - proc.stdin.write(str); - }, - complete: () => { - proc.stdin.end(); - }, - }) - .ignoreElements(), - ) - .takeUntil(errors) - .takeUntil(exitEvents) - .do({ - error: () => { - finished = true; - }, - complete: () => { - finished = true; - }, - }), - ) - .catch(err => { - // Since this utility errors *before* emitting the process, add the process to the error - // so that users can get whatever info they need off of it. - if (err instanceof Error && err.name === 'Error' && 'errno' in err) { - throw new ProcessSystemError(err, proc); - } - throw err; - }) - .finally(() => { - // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) - if (!proc.wasKilled && !finished) { - killProcess(proc, Boolean(killTreeWhenDone), killTreeSignal); - } - }); + throw err; + }).finally(() => { + // $FlowFixMe(>=0.68.0) Flow suppress (T27187857) + if (!proc.wasKilled && !finished) { + killProcess(proc, Boolean(killTreeWhenDone), killTreeSignal); + } }); + }); } -function isRealExit(event: {exitCode: ?number, signal: ?string}): boolean { +function isRealExit(event) { // An exit signal from SIGUSR1 doesn't actually exit the process, so skip that. return event.signal !== 'SIGUSR1'; } -async function _killProcess( - proc: child_process$ChildProcess & {wasKilled?: boolean}, - killTree: boolean, - killTreeSignal: ?string, -): Promise { +async function _killProcess(proc, killTree, killTreeSignal) { proc.wasKilled = true; + if (!killTree) { if (killTreeSignal != null && killTreeSignal !== '') { proc.kill(killTreeSignal); } else { proc.kill(); } + return; } + if (/^win/.test(process.platform)) { await killWindowsProcessTree(proc.pid); } else { @@ -1135,9 +982,9 @@ async function _killProcess( } } -function killWindowsProcessTree(pid: number): Promise { +function killWindowsProcessTree(pid) { return new Promise((resolve, reject) => { - child_process.exec(`taskkill /pid ${pid} /T /F`, error => { + _child_process.default.exec(`taskkill /pid ${pid} /T /F`, error => { if (error == null) { reject(error); } else { @@ -1147,61 +994,45 @@ function killWindowsProcessTree(pid: number): Promise { }); } -export async function killUnixProcessTree( - proc: child_process$ChildProcess, -): Promise { - const descendants = await getDescendantsOfProcess(proc.pid); - // Kill the processes, starting with those of greatest depth. +async function killUnixProcessTree(proc) { + const descendants = await getDescendantsOfProcess(proc.pid); // Kill the processes, starting with those of greatest depth. + for (const info of descendants.reverse()) { killPid(info.pid); } } -function isExitErrorDefault(exit: ProcessExitMessage): boolean { +function isExitErrorDefault(exit) { return exit.exitCode !== 0; } -function isWindowsPlatform(): boolean { +function isWindowsPlatform() { return /^win/.test(process.platform); } -function limitBufferSize( - stream: Observable, - maxBuffer: ?number, - streamName: string, -): Observable { +function limitBufferSize(stream, maxBuffer, streamName) { if (maxBuffer == null) { return stream; } - return Observable.defer(() => { + + return _RxMin.Observable.defer(() => { let totalSize = 0; return stream.do(data => { totalSize += data.length; + if (totalSize > maxBuffer) { throw new MaxBufferExceededError(streamName); } }); }); } - /** * Get an observable of error events for a process's streams. Note that these are represented as * normal elements, not observable errors. */ -function getStreamErrorEvents( - proc: child_process$ChildProcess, -): Observable<[Error, string]> { - const streams = [ - ['stdin', proc.stdin], - ['stdout', proc.stdout], - ['stderr', proc.stderr], - ]; - return Observable.merge( - ...streams.map( - ([name, stream]) => - stream == null - ? Observable.empty() - : Observable.fromEvent(stream, 'error').map(err => [err, name]), - ), - ); -} + + +function getStreamErrorEvents(proc) { + const streams = [['stdin', proc.stdin], ['stdout', proc.stdout], ['stderr', proc.stderr]]; + return _RxMin.Observable.merge(...streams.map(([name, stream]) => stream == null ? _RxMin.Observable.empty() : _RxMin.Observable.fromEvent(stream, 'error').map(err => [err, name]))); +} \ No newline at end of file diff --git a/modules/nuclide-commons/promise.js b/modules/nuclide-commons/promise.js index 264e63ce..556355ec 100644 --- a/modules/nuclide-commons/promise.js +++ b/modules/nuclide-commons/promise.js @@ -1,3 +1,27 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sleep = sleep; +exports.nextTick = nextTick; +exports.triggerAfterWait = triggerAfterWait; +exports.timeoutPromise = timeoutPromise; +exports.createDeadline = createDeadline; +exports.timeoutAfterDeadline = timeoutAfterDeadline; +exports.retryLimit = retryLimit; +exports.serializeAsyncCall = serializeAsyncCall; +exports.asyncFind = asyncFind; +exports.denodeify = denodeify; +exports.asyncLimit = asyncLimit; +exports.asyncFilter = asyncFilter; +exports.asyncObjFilter = asyncObjFilter; +exports.asyncSome = asyncSome; +exports.isPromise = isPromise; +exports.lastly = lastly; +exports.delayTime = delayTime; +exports.PromiseWithState = exports.Deferred = exports.TimedOutError = exports.RequestSerializer = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,21 +30,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import invariant from 'assert'; - -type RunReturn = - | { - status: 'success', - result: T, - } - | { - status: 'outdated', - }; - /** * Allows a caller to ensure that the results it receives from consecutive * promise resolutions are never outdated. Usage: @@ -46,12 +59,7 @@ type RunReturn = * receive a 'success' status. If promise1 later resolved, the first callsite * would receive an 'outdated' status. */ -export class RequestSerializer { - _lastDispatchedOp: number; - _lastFinishedOp: number; - _latestPromise: Promise; - _waitResolve: Function; - +class RequestSerializer { constructor() { this._lastDispatchedOp = 0; this._lastFinishedOp = 0; @@ -60,66 +68,76 @@ export class RequestSerializer { }); } - async run(promise: Promise): Promise> { + async run(promise) { const thisOp = this._lastDispatchedOp + 1; this._lastDispatchedOp = thisOp; this._latestPromise = promise; + this._waitResolve(); + const result = await promise; + if (this._lastFinishedOp < thisOp) { this._lastFinishedOp = thisOp; return { status: 'success', - result, + result }; } else { return { - status: 'outdated', + status: 'outdated' }; } } - /** * Returns a Promise that resolves to the last result of `run`, * as soon as there are no more outstanding `run` calls. */ - async waitForLatestResult(): Promise { + + + async waitForLatestResult() { let lastPromise = null; - let result: any = null; + let result = null; + while (lastPromise !== this._latestPromise) { - lastPromise = this._latestPromise; - // Wait for the current last know promise to resolve, or a next run have started. + lastPromise = this._latestPromise; // Wait for the current last know promise to resolve, or a next run have started. // eslint-disable-next-line no-await-in-loop + result = await new Promise((resolve, reject) => { this._waitResolve = resolve; + this._latestPromise.then(resolve); }); } - return (result: T); + + return result; } - isRunInProgress(): boolean { + isRunInProgress() { return this._lastDispatchedOp > this._lastFinishedOp; } -} +} /* * Returns a promise that will resolve after `milliSeconds` milli seconds. * this can be used to pause execution asynchronously. * e.g. await sleep(1000), pauses the async flow execution for 1 second. */ -export function sleep(milliSeconds: number): Promise { + + +exports.RequestSerializer = RequestSerializer; + +function sleep(milliSeconds) { return new Promise(resolve => { setTimeout(resolve, milliSeconds); }); } -export function nextTick(): Promise { +function nextTick() { return new Promise(resolve => { process.nextTick(resolve); }); } - /** * Executes a provided callback only if a promise takes longer than * `milliSeconds` milliseconds to resolve. @@ -131,67 +149,64 @@ export function nextTick(): Promise { * `milliSeconds` ms to resolve. * @param `cleanupFn` the cleanup function to execute after the promise resolves. */ -export async function triggerAfterWait( - promise: Promise, - milliSeconds: number, - timeoutFn: () => void, - cleanupFn?: () => void, -): Promise { + + +async function triggerAfterWait(promise, milliSeconds, timeoutFn, cleanupFn) { const timeout = setTimeout(timeoutFn, milliSeconds); + try { return await promise; } finally { clearTimeout(timeout); + if (cleanupFn) { cleanupFn(); } } } - /** * Thrown by `timeoutPromise` if the timer fires before the promise resolves/rejects. */ -export class TimedOutError extends Error { - timeout: number; - constructor(milliseconds: number) { + + +class TimedOutError extends Error { + constructor(milliseconds) { super(`Timed out after ${String(milliseconds)} ms`); this.timeout = milliseconds; } -} +} /** * Returns a Promise that resolves to the same value as the given promise, or rejects with * `TimedOutError` if it takes longer than `milliseconds` milliseconds. */ -export function timeoutPromise( - promise: Promise, - milliseconds: number, -): Promise { + + +exports.TimedOutError = TimedOutError; + +function timeoutPromise(promise, milliseconds) { return new Promise((resolve, reject) => { let timeout = setTimeout(() => { timeout = null; - reject(new TimedOutError(milliseconds)); - // This gives useless error.stack results. + reject(new TimedOutError(milliseconds)); // This gives useless error.stack results. // We could capture the stack pre-emptively at the start // of this method if we wanted useful ones. }, milliseconds); - promise - .then(value => { - if (timeout != null) { - clearTimeout(timeout); - } - resolve(value); - }) - .catch(value => { - if (timeout != null) { - clearTimeout(timeout); - } - reject(value); - }); - }); -} + promise.then(value => { + if (timeout != null) { + clearTimeout(timeout); + } -// An DeadlineRequest parameter to an async method is a way of *requesting* that + resolve(value); + }).catch(value => { + if (timeout != null) { + clearTimeout(timeout); + } + + reject(value); + }); + }); +} // An DeadlineRequest parameter to an async method is a way of *requesting* that // method to throw a TimedOutError if it doesn't complete in a certain time. // It's just a request -- the async method will typically honor the request // by passing the parameter on to ALL subsidiary async methods that it awaits, @@ -207,20 +222,16 @@ export function timeoutPromise( // "delay" parameters) and safely remotable (better than "CancellationToken" // parameters) so long as clocks are in sync. In all other respects it's less // versatile than CancellationTokens. -export type DeadlineRequest = number; -export function createDeadline(delay: number): DeadlineRequest { + +function createDeadline(delay) { return Date.now() + delay; } -export function timeoutAfterDeadline( - deadline: DeadlineRequest, - promise: Promise, -): Promise { +function timeoutAfterDeadline(deadline, promise) { const delay = deadline - Date.now(); return timeoutPromise(promise, delay < 0 ? 0 : delay); } - /** * Call an async function repeatedly with a maximum number of trials limit, * until a valid result that's defined by a validation function. @@ -235,20 +246,19 @@ export function timeoutAfterDeadline( * If an exception is encountered on the last trial, the exception is thrown. * If no valid response is found, an exception is thrown. */ -export async function retryLimit( - retryFunction: () => Promise, - validationFunction: (result: T) => boolean, - maximumTries: number, - retryIntervalMs?: number = 0, -): Promise { + + +async function retryLimit(retryFunction, validationFunction, maximumTries, retryIntervalMs = 0) { let result = null; let tries = 0; let lastError = null; + while (tries === 0 || tries < maximumTries) { try { // eslint-disable-next-line no-await-in-loop result = await retryFunction(); lastError = null; + if (validationFunction(result)) { return result; } @@ -262,15 +272,15 @@ export async function retryLimit( await sleep(retryIntervalMs); } } + if (lastError != null) { throw lastError; } else if (tries === maximumTries) { throw new Error('No valid response found!'); } else { - return ((result: any): T); + return result; } } - /** * Limits async function execution parallelism to only one at a time. * Hence, if a call is already running, it will wait for it to finish, @@ -291,30 +301,35 @@ export async function retryLimit( * const result3Promise = oneExecAtATime(); // Reuse scheduled promise and resolve to 2 in 400 ms. * ``` */ -export function serializeAsyncCall( - asyncFun: () => Promise, -): () => Promise { + + +function serializeAsyncCall(asyncFun) { let scheduledCall = null; let pendingCall = null; + const startAsyncCall = () => { const resultPromise = asyncFun(); - pendingCall = resultPromise.then( - () => (pendingCall = null), - () => (pendingCall = null), - ); + pendingCall = resultPromise.then(() => pendingCall = null, () => pendingCall = null); return resultPromise; }; + const callNext = () => { scheduledCall = null; return startAsyncCall(); }; + const scheduleNextCall = () => { if (scheduledCall == null) { - invariant(pendingCall, 'pendingCall must not be null!'); + if (!pendingCall) { + throw new Error('pendingCall must not be null!'); + } + scheduledCall = pendingCall.then(callNext, callNext); } + return scheduledCall; }; + return () => { if (pendingCall == null) { return startAsyncCall(); @@ -323,7 +338,6 @@ export function serializeAsyncCall( } }; } - /** * Provides a promise along with methods to change its state. Our version of the non-standard * `Promise.defer()`. @@ -331,19 +345,17 @@ export function serializeAsyncCall( * IMPORTANT: This should almost never be used!! Instead, use the Promise constructor. See * */ -export class Deferred { - promise: Promise; - resolve: (value: T) => void; - reject: (error: Error) => void; + +class Deferred { constructor() { this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } -} +} /** * Returns a value derived asynchronously from an element in the items array. * The test function is applied sequentially to each element in items until @@ -362,11 +374,11 @@ export class Deferred { * @param thisArg Receiver that will be used when test is called. * @return Promise that resolves to an asynchronously derived value or null. */ -export function asyncFind( - items_: Array, - test: (t: T) => ?Promise, - thisArg?: mixed, -): Promise { + + +exports.Deferred = Deferred; + +function asyncFind(items_, test, thisArg) { let items = items_; return new Promise((resolve, reject) => { // Create a local copy of items to defend against the caller modifying the @@ -374,7 +386,7 @@ export function asyncFind( items = items.slice(); const numItems = items.length; - const next = async function(index) { + const next = async function (index) { if (index === numItems) { resolve(null); return; @@ -382,6 +394,7 @@ export function asyncFind( const item = items[index]; const result = await test.call(thisArg, item); + if (result != null) { resolve(result); } else { @@ -393,10 +406,8 @@ export function asyncFind( }); } -export function denodeify( - f: (...args: Array) => any, -): (...args: Array) => Promise { - return function(...args: Array) { +function denodeify(f) { + return function (...args) { return new Promise((resolve, reject) => { function callback(error, result) { if (error) { @@ -405,11 +416,11 @@ export function denodeify( resolve(result); } } + f.apply(this, args.concat([callback])); }); }; } - /** * A Promise utility that runs a maximum of limit async operations at a time * iterating over an array and returning the result of executions. @@ -426,32 +437,32 @@ export function denodeify( * @param limit the configurable number of parallel async operations. * @param mappingFunction the async Promise function that could return a useful result. */ -export function asyncLimit( - array: Array, - limit: number, - mappingFunction: (item: T) => Promise, -): Promise> { - const result: Array = new Array(array.length); + + +function asyncLimit(array, limit, mappingFunction) { + const result = new Array(array.length); let parallelPromises = 0; let index = 0; - let parallelLimit = Math.min(limit, array.length) || 1; - return new Promise((resolve, reject) => { const runPromise = async () => { if (index === array.length) { if (parallelPromises === 0) { resolve(result); } + return; } + ++parallelPromises; const i = index++; + try { result[i] = await mappingFunction(array[i]); } catch (e) { reject(e); } + --parallelPromises; runPromise(); }; @@ -461,7 +472,6 @@ export function asyncLimit( } }); } - /** * `filter` Promise utility that allows filtering an array with an async Promise function. * It's an alternative to `Array.prototype.filter` that accepts an async function. @@ -483,14 +493,12 @@ export function asyncLimit( * boolean. * @param limit the configurable number of parallel async operations. */ -export async function asyncFilter( - array: Array, - filterFunction: (item: T) => Promise, - limit?: number, -): Promise> { - const filteredList = []; - // flowlint-next-line sketchy-null-number:off - await asyncLimit(array, limit || array.length, async (item: T) => { + + +async function asyncFilter(array, filterFunction, limit) { + const filteredList = []; // flowlint-next-line sketchy-null-number:off + + await asyncLimit(array, limit || array.length, async item => { if (await filterFunction(item)) { filteredList.push(item); } @@ -498,23 +506,19 @@ export async function asyncFilter( return filteredList; } -export async function asyncObjFilter( - obj: {[key: string]: T}, - filterFunction: (item: T, key: string) => Promise, - limit?: number, -): Promise<{[key: string]: T}> { +async function asyncObjFilter(obj, filterFunction, limit) { const keys = Object.keys(obj); - const filteredObj = {}; - // flowlint-next-line sketchy-null-number:off - await asyncLimit(keys, limit || keys.length, async (key: string) => { + const filteredObj = {}; // flowlint-next-line sketchy-null-number:off + + await asyncLimit(keys, limit || keys.length, async key => { const item = obj[key]; + if (await filterFunction(item, key)) { filteredObj[key] = item; } }); return filteredObj; } - /** * `some` Promise utility that allows `some` an array with an async Promise some function. * It's an alternative to `Array.prototype.some` that accepts an async some function. @@ -536,93 +540,85 @@ export async function asyncObjFilter( * boolean. * @param limit the configurable number of parallel async operations. */ -export async function asyncSome( - array: Array, - someFunction: (item: T) => Promise, - limit?: number, -): Promise { - let resolved = false; - // flowlint-next-line sketchy-null-number:off - await asyncLimit(array, limit || array.length, async (item: T) => { + + +async function asyncSome(array, someFunction, limit) { + let resolved = false; // flowlint-next-line sketchy-null-number:off + + await asyncLimit(array, limit || array.length, async item => { if (resolved) { // We don't need to call the someFunction anymore or wait any longer. return; } + if (await someFunction(item)) { resolved = true; } }); return resolved; } - /** * Check if an object is Promise by testing if it has a `then` function property. */ -export function isPromise(object: any): boolean { - return ( - Boolean(object) && - typeof object === 'object' && - typeof object.then === 'function' - ); -} + +function isPromise(object) { + return Boolean(object) && typeof object === 'object' && typeof object.then === 'function'; +} /** * We can't name a function 'finally', so use lastly instead. * fn() will be executed (and completed) after the provided promise resolves/rejects. */ -export function lastly( - promise: Promise, - fn: () => Promise | mixed, -): Promise { - return promise.then( - ret => { - return Promise.resolve(fn()).then(() => ret); - }, - err => { - return Promise.resolve(fn()).then(() => Promise.reject(err)); - }, - ); -} + +function lastly(promise, fn) { + return promise.then(ret => { + return Promise.resolve(fn()).then(() => ret); + }, err => { + return Promise.resolve(fn()).then(() => Promise.reject(err)); + }); +} /** * With a pure promise object, there's no way to tell synchronously * whether or not it has 'settled' (i.e. been fulfilled or rejected). * Here we provide a wrapper that provides that information. */ -export type PromiseState = - | {kind: 'pending'} - | {kind: 'fulfilled', value: T} - | {kind: 'rejected', error: any}; - -export class PromiseWithState { - _promise: Promise; - _state: PromiseState; - - constructor(promise: Promise) { - this._state = {kind: 'pending'}; - this._promise = promise.then( - value => { - this._state = {kind: 'fulfilled', value}; - return value; - }, - error => { - this._state = {kind: 'rejected', error}; - throw error; - }, - ); + + +class PromiseWithState { + constructor(promise) { + this._state = { + kind: 'pending' + }; + this._promise = promise.then(value => { + this._state = { + kind: 'fulfilled', + value + }; + return value; + }, error => { + this._state = { + kind: 'rejected', + error + }; + throw error; + }); } - getPromise(): Promise { + getPromise() { return this._promise; } - getState(): PromiseState { + getState() { return this._state; } + } -export function delayTime(ms: number): Promise { +exports.PromiseWithState = PromiseWithState; + +function delayTime(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/range.js b/modules/nuclide-commons/range.js index c166286f..8aaf2bf0 100644 --- a/modules/nuclide-commons/range.js +++ b/modules/nuclide-commons/range.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.wordAtPositionFromBuffer = wordAtPositionFromBuffer; +exports.matchRegexEndingAt = matchRegexEndingAt; +exports.isPositionInRange = isPositionInRange; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,60 +15,51 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -export function wordAtPositionFromBuffer( - buffer: atom$TextBuffer | simpleTextBuffer$TextBuffer, - position: atom$PointObject, - wordRegex: RegExp, -): ?{wordMatch: Array, range: atom$Range} { - const {row, column} = position; +function wordAtPositionFromBuffer(buffer, position, wordRegex) { + const { + row, + column + } = position; const rowRange = buffer.rangeForRow(row); - let matchData; - // Extract the expression from the row text. + let matchData; // Extract the expression from the row text. + buffer.scanInRange(wordRegex, rowRange, data => { - const {range} = data; - if ( - range.start.isLessThanOrEqual(position) && - range.end.isGreaterThan(position) - ) { + const { + range + } = data; + + if (range.start.isLessThanOrEqual(position) && range.end.isGreaterThan(position)) { matchData = data; - } - // Stop the scan if the scanner has passed our position. + } // Stop the scan if the scanner has passed our position. + + if (range.end.column > column) { data.stop(); } }); + if (matchData) { return { wordMatch: matchData.match, - range: matchData.range, + range: matchData.range }; } else { return null; } -} - -// Matches a regex on the text of the line ending at endPosition. +} // Matches a regex on the text of the line ending at endPosition. // regex should end with a '$'. // Useful for autocomplete. -export function matchRegexEndingAt( - buffer: atom$TextBuffer | simpleTextBuffer$TextBuffer, - endPosition: atom$PointObject, - regex: RegExp, -): ?string { + + +function matchRegexEndingAt(buffer, endPosition, regex) { const line = buffer.getTextInRange([[endPosition.row, 0], endPosition]); const match = regex.exec(line); return match == null ? null : match[0]; } -export function isPositionInRange( - position: atom$Point, - range: atom$Range | Array, -): boolean { - return Array.isArray(range) - ? range.some(r => r.containsPoint(position)) - : range.containsPoint(position); -} +function isPositionInRange(position, range) { + return Array.isArray(range) ? range.some(r => r.containsPoint(position)) : range.containsPoint(position); +} \ No newline at end of file diff --git a/modules/nuclide-commons/redux-observable.js b/modules/nuclide-commons/redux-observable.js index 239fb6ec..9f068a75 100644 --- a/modules/nuclide-commons/redux-observable.js +++ b/modules/nuclide-commons/redux-observable.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.combineEpics = combineEpics; +exports.createEpicMiddleware = createEpicMiddleware; +exports.ActionsObservable = void 0; + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,10 +17,9 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - // Derived from because their version // imports an Rx operator module and we use a bundle. Original license follows: // @@ -34,48 +44,22 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. - -import {Observable, Subject} from 'rxjs'; - -// This should be { type: readonly string } when we get readonly props. Because this is used with -// disjoint unions we can't use `string` here due to mutation concerns. Flow doesn't know that we -// aren't going to mutate the objects with a random string value so it can't allow us to pass a -// specific action type into something of type { type: string } -type Action = {type: any}; -type Store = { - dispatch(action: T): void, - getState(): U, - replaceReducer(reducer: () => mixed): void, -}; -type Next = (action: T) => T; -export type Epic = ( - actions: ActionsObservable, - store: Store, - extra: E, -) => Observable; - -export function combineEpics( - ...epics: Array> -): Epic { - return (actions: ActionsObservable, store: Store, extra: E) => { - const streams: Array> = epics.map(epic => - epic(actions, store, extra), - ); - return Observable.merge(...streams); +function combineEpics(...epics) { + return (actions, store, extra) => { + const streams = epics.map(epic => epic(actions, store, extra)); + return _RxMin.Observable.merge(...streams); }; } -export function createEpicMiddleware( - rootEpic?: Epic, -) { - const actions = new Subject(); +function createEpicMiddleware(rootEpic) { + const actions = new _RxMin.Subject(); const actionsObs = new ActionsObservable(actions); - - return (store: Store) => (next: Next) => { + return store => next => { if (rootEpic != null) { rootEpic(actionsObs, store).subscribe(store.dispatch); } - return (action: T) => { + + return action => { const result = next(action); actions.next(action); return result; @@ -83,23 +67,24 @@ export function createEpicMiddleware( }; } -export class ActionsObservable extends Observable { - operator: any; - - constructor(actionsSubject: Observable) { +class ActionsObservable extends _RxMin.Observable { + constructor(actionsSubject) { super(); this.source = actionsSubject; } - lift(operator: any): Observable { + lift(operator) { const observable = new ActionsObservable(this); observable.operator = operator; return observable; } - ofType(...keys: Array): ActionsObservable { - const result = this.filter(({type}) => { + ofType(...keys) { + const result = this.filter(({ + type + }) => { const len = keys.length; + if (len === 1) { return type === keys[0]; } else { @@ -109,8 +94,12 @@ export class ActionsObservable extends Observable { } } } + return false; }); - return ((result: any): ActionsObservable); + return result; } + } + +exports.ActionsObservable = ActionsObservable; \ No newline at end of file diff --git a/modules/nuclide-commons/sanitizeHtml.js b/modules/nuclide-commons/sanitizeHtml.js index 0cccbf24..d85c404f 100644 --- a/modules/nuclide-commons/sanitizeHtml.js +++ b/modules/nuclide-commons/sanitizeHtml.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sanitizeHtml; + +function _dompurify() { + const data = _interopRequireDefault(require("dompurify")); + + _dompurify = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,44 +25,40 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import domPurify from 'dompurify'; // flowlint-line untyped-import:off - -domPurify.addHook('beforeSanitizeElements', (node: ?Node) => { +// flowlint-line untyped-import:off +_dompurify().default.addHook('beforeSanitizeElements', node => { // Add newlines where we see `

      ` and `
      ` tags. if (node && node.nodeName === 'BR') { const parent = node.parentNode; + if (parent != null) { parent.insertBefore(document.createTextNode('\n'), node); } } + if (node && node.nodeName === 'P') { node.textContent = '\n' + node.textContent; } }); - /** * Sanitize a message for display in a notification. This removes HTML but also tries to be smart * about whitespace. */ -export default function sanitizeHtml( - message: string, - options?: { - condenseWhitespaces?: boolean, - }, -): string { + + +function sanitizeHtml(message, options) { // Remove the HTML. - let withoutTags: string = domPurify.sanitize(message, {ALLOWED_TAGS: []}); + let withoutTags = _dompurify().default.sanitize(message, { + ALLOWED_TAGS: [] + }); // Compress the whitespace. - // Compress the whitespace. - if (options?.condenseWhitespaces) { - withoutTags = withoutTags - .replace(/(?:\s*\n\s*)+/g, '\n') - .replace(/[ \t]+/g, ' '); + + if (options === null || options === void 0 ? void 0 : options.condenseWhitespaces) { + withoutTags = withoutTags.replace(/(?:\s*\n\s*)+/g, '\n').replace(/[ \t]+/g, ' '); } return withoutTags.trim(); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/serverPort.js b/modules/nuclide-commons/serverPort.js index 2f056ccc..de6e3e1c 100644 --- a/modules/nuclide-commons/serverPort.js +++ b/modules/nuclide-commons/serverPort.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getAvailableServerPort = getAvailableServerPort; + +var _net = _interopRequireDefault(require("net")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,22 +17,22 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import net from 'net'; - -export async function getAvailableServerPort(): Promise { +async function getAvailableServerPort() { return new Promise((resolve, reject) => { - const server = net.createServer(); + const server = _net.default.createServer(); + server.unref(); server.on('error', reject); - server.listen({port: 0}, () => { + server.listen({ + port: 0 + }, () => { const port = server.address().port; server.close(() => { resolve(port); }); }); }); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/stream.js b/modules/nuclide-commons/stream.js index 984a021d..945fcac1 100644 --- a/modules/nuclide-commons/stream.js +++ b/modules/nuclide-commons/stream.js @@ -1,3 +1,38 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.observeStream = observeStream; +exports.observeRawStream = observeRawStream; +exports.writeToStream = writeToStream; + +var _stream = _interopRequireDefault(require("stream")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("./UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _event() { + const data = require("./event"); + + _event = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,69 +41,50 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import Stream from 'stream'; -import {Observable} from 'rxjs'; - -import UniversalDisposable from './UniversalDisposable'; -import {attachEvent} from './event'; - /** * Observe a stream like stdout or stderr. */ -export function observeStream(stream: stream$Readable): Observable { +function observeStream(stream) { return observeRawStream(stream).map(data => data.toString()); } -export function observeRawStream(stream: stream$Readable): Observable { - const error = Observable.fromEvent(stream, 'error').flatMap(Observable.throw); - return Observable.fromEvent(stream, 'data') - .merge(error) - .takeUntil(Observable.fromEvent(stream, 'end')); -} +function observeRawStream(stream) { + const error = _RxMin.Observable.fromEvent(stream, 'error').flatMap(_RxMin.Observable.throw); + return _RxMin.Observable.fromEvent(stream, 'data').merge(error).takeUntil(_RxMin.Observable.fromEvent(stream, 'end')); +} /** * Write an observed readable stream into a writable stream. Effectively a pipe() for observables. * Returns an observable accumulating the number of bytes processed. */ -export function writeToStream( - source: Observable, - destStream: stream$Writable, -): Observable { - return Observable.create(observer => { - let byteCount = 0; - const byteCounterStream = new Stream.Transform({ + +function writeToStream(source, destStream) { + return _RxMin.Observable.create(observer => { + let byteCount = 0; + const byteCounterStream = new _stream.default.Transform({ transform(chunk, encoding, cb) { byteCount += chunk.byteLength; observer.next(byteCount); cb(null, chunk); - }, - }); + } + }); byteCounterStream.pipe(destStream); - - return new UniversalDisposable( - attachEvent(destStream, 'error', err => { - observer.error(err); - }), - attachEvent(destStream, 'close', () => { - observer.complete(); - }), - source.subscribe( - buffer => { - byteCounterStream.write(buffer); - }, - err => { - observer.error(err); - }, - () => { - byteCounterStream.end(); - }, - ), - ); + return new (_UniversalDisposable().default)((0, _event().attachEvent)(destStream, 'error', err => { + observer.error(err); + }), (0, _event().attachEvent)(destStream, 'close', () => { + observer.complete(); + }), source.subscribe(buffer => { + byteCounterStream.write(buffer); + }, err => { + observer.error(err); + }, () => { + byteCounterStream.end(); + })); }).share(); -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/string.js b/modules/nuclide-commons/string.js index a37cbaad..afe10f5d 100644 --- a/modules/nuclide-commons/string.js +++ b/modules/nuclide-commons/string.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.stringifyError = stringifyError; +exports.maybeToString = maybeToString; +exports.relativeDate = relativeDate; +exports.countOccurrences = countOccurrences; +exports.shellParse = shellParse; +exports.shellParseWithGlobs = shellParseWithGlobs; +exports.shellQuote = shellQuote; +exports.removeCommonPrefix = removeCommonPrefix; +exports.removeCommonSuffix = removeCommonSuffix; +exports.shorten = shorten; +exports.splitOnce = splitOnce; +exports.indent = indent; +exports.pluralize = pluralize; +exports.capitalize = capitalize; +exports.getMatchRanges = getMatchRanges; +exports.escapeMarkdown = escapeMarkdown; +exports.ZERO_WIDTH_SPACE = exports.ELLIPSIS_CHAR = exports.URL_REGEX = void 0; + +function _shellQuote() { + const data = require("./_shell-quote"); + + _shellQuote = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +39,27 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +function stringifyError(error) { + return `name: ${error.name}, message: ${error.message}, stack: ${error.stack}.`; +} // As of Flow v0.28, Flow does not alllow implicit string coercion of null or undefined. Use this to +// make it explicit. -import invariant from 'assert'; -import {parse, quote} from './_shell-quote'; -export function stringifyError(error: Error): string { - return `name: ${error.name}, message: ${error.message}, stack: ${ - error.stack - }.`; -} - -// As of Flow v0.28, Flow does not alllow implicit string coercion of null or undefined. Use this to -// make it explicit. -export function maybeToString(str: ?string): string { +function maybeToString(str) { // We don't want to encourage the use of this function directly because it coerces anything to a // string. We get stricter typechecking by using maybeToString, so it should generally be // preferred. return String(str); } - /** * Originally adapted from https://github.com/azer/relative-date. * We're including it because of https://github.com/npm/npm/issues/12012 */ + + const SECOND = 1000; const MINUTE = 60 * SECOND; const HOUR = 60 * MINUTE; @@ -39,67 +67,33 @@ const DAY = 24 * HOUR; const WEEK = 7 * DAY; const YEAR = DAY * 365; const MONTH = YEAR / 12; +const shortFormats = [[0.7 * MINUTE, 'now'], [1.5 * MINUTE, '1m'], [60 * MINUTE, 'm', MINUTE], [1.5 * HOUR, '1h'], [DAY, 'h', HOUR], [2 * DAY, '1d'], [7 * DAY, 'd', DAY], [1.5 * WEEK, '1w'], [MONTH, 'w', WEEK], [1.5 * MONTH, '1mo'], [YEAR, 'mo', MONTH], [1.5 * YEAR, '1y'], [Number.MAX_VALUE, 'y', YEAR]]; +const longFormats = [[0.7 * MINUTE, 'just now'], [1.5 * MINUTE, 'a minute ago'], [60 * MINUTE, 'minutes ago', MINUTE], [1.5 * HOUR, 'an hour ago'], [DAY, 'hours ago', HOUR], [2 * DAY, 'yesterday'], [7 * DAY, 'days ago', DAY], [1.5 * WEEK, 'a week ago'], [MONTH, 'weeks ago', WEEK], [1.5 * MONTH, 'a month ago'], [YEAR, 'months ago', MONTH], [1.5 * YEAR, 'a year ago'], [Number.MAX_VALUE, 'years ago', YEAR]]; -const shortFormats = [ - [0.7 * MINUTE, 'now'], - [1.5 * MINUTE, '1m'], - [60 * MINUTE, 'm', MINUTE], - [1.5 * HOUR, '1h'], - [DAY, 'h', HOUR], - [2 * DAY, '1d'], - [7 * DAY, 'd', DAY], - [1.5 * WEEK, '1w'], - [MONTH, 'w', WEEK], - [1.5 * MONTH, '1mo'], - [YEAR, 'mo', MONTH], - [1.5 * YEAR, '1y'], - [Number.MAX_VALUE, 'y', YEAR], -]; - -const longFormats = [ - [0.7 * MINUTE, 'just now'], - [1.5 * MINUTE, 'a minute ago'], - [60 * MINUTE, 'minutes ago', MINUTE], - [1.5 * HOUR, 'an hour ago'], - [DAY, 'hours ago', HOUR], - [2 * DAY, 'yesterday'], - [7 * DAY, 'days ago', DAY], - [1.5 * WEEK, 'a week ago'], - [MONTH, 'weeks ago', WEEK], - [1.5 * MONTH, 'a month ago'], - [YEAR, 'months ago', MONTH], - [1.5 * YEAR, 'a year ago'], - [Number.MAX_VALUE, 'years ago', YEAR], -]; - -export function relativeDate( - input_: number | Date, - reference_?: number | Date, - useShortVariant?: boolean = false, -): string { +function relativeDate(input_, reference_, useShortVariant = false) { let input = input_; let reference = reference_; + if (input instanceof Date) { input = input.getTime(); - } - // flowlint-next-line sketchy-null-number:off + } // flowlint-next-line sketchy-null-number:off + + if (!reference) { reference = new Date().getTime(); } + if (reference instanceof Date) { reference = reference.getTime(); } const delta = reference - input; const formats = useShortVariant ? shortFormats : longFormats; + for (const [limit, relativeFormat, remainder] of formats) { if (delta < limit) { if (typeof remainder === 'number') { - return ( - Math.round(delta / remainder) + - (useShortVariant ? '' : ' ') + - relativeFormat - ); + return Math.round(delta / remainder) + (useShortVariant ? '' : ' ') + relativeFormat; } else { return relativeFormat; } @@ -108,153 +102,137 @@ export function relativeDate( throw new Error('This should never be reached.'); } - /** * Count the number of occurrences of `char` in `str`. * `char` must be a string of length 1. */ -export function countOccurrences(haystack: string, char: string) { - invariant(char.length === 1, 'char must be a string of length 1'); + + +function countOccurrences(haystack, char) { + if (!(char.length === 1)) { + throw new Error('char must be a string of length 1'); + } let count = 0; const code = char.charCodeAt(0); + for (let i = 0; i < haystack.length; i++) { if (haystack.charCodeAt(i) === code) { count++; } } + return count; } - /** * shell-quote's parse allows pipe operators and comments. * Generally users don't care about this, so throw if we encounter any operators. */ -export function shellParse(str: string, env?: Object): Array { - const result = parse(str, env); + + +function shellParse(str, env) { + const result = (0, _shellQuote().parse)(str, env); + for (let i = 0; i < result.length; i++) { if (typeof result[i] !== 'string') { if (result[i].op != null) { - throw new Error( - `Unexpected operator "${result[i].op}" provided to shellParse`, - ); + throw new Error(`Unexpected operator "${result[i].op}" provided to shellParse`); } else { - throw new Error( - `Unexpected comment "${result[i].comment}" provided to shellParse`, - ); + throw new Error(`Unexpected comment "${result[i].comment}" provided to shellParse`); } } } + return result; } - /** * shell-quote's parse allows pipe operators and comments and globs * We treat glob patterns as normal strings. For the other operators, we throw. */ -export function shellParseWithGlobs(str: string, env?: Object): Array { - const result = parse(str, env); + + +function shellParseWithGlobs(str, env) { + const result = (0, _shellQuote().parse)(str, env); + for (let i = 0; i < result.length; i++) { if (typeof result[i] !== 'string') { if (result[i].op === 'glob') { result[i] = result[i].pattern; } else if (result[i].op != null) { - throw new Error( - `Unexpected operator "${result[i].op}" provided to shellParse`, - ); + throw new Error(`Unexpected operator "${result[i].op}" provided to shellParse`); } else { - throw new Error( - `Unexpected comment "${result[i].comment}" provided to shellParse`, - ); + throw new Error(`Unexpected comment "${result[i].comment}" provided to shellParse`); } } } + return result; } - /** * Technically you can pass in { operator: string } here, * but we don't use that in most APIs. */ -export function shellQuote(args: Array): string { - return quote(args); + + +function shellQuote(args) { + return (0, _shellQuote().quote)(args); } -export function removeCommonPrefix(a: string, b: string): [string, string] { +function removeCommonPrefix(a, b) { let i = 0; + while (a[i] === b[i] && i < a.length && i < b.length) { i++; } + return [a.substring(i), b.substring(i)]; } -export function removeCommonSuffix(a: string, b: string): [string, string] { +function removeCommonSuffix(a, b) { let i = 0; - while ( - a[a.length - 1 - i] === b[b.length - 1 - i] && - i < a.length && - i < b.length - ) { + + while (a[a.length - 1 - i] === b[b.length - 1 - i] && i < a.length && i < b.length) { i++; } + return [a.substring(0, a.length - i), b.substring(0, b.length - i)]; } -export function shorten( - str: string, - maxLength: number, - suffix?: string, -): string { - return str.length < maxLength - ? str - : str.slice(0, maxLength) + (suffix || ''); +function shorten(str, maxLength, suffix) { + return str.length < maxLength ? str : str.slice(0, maxLength) + (suffix || ''); } - /** * Like String.split, but only splits once. */ -export function splitOnce(str: string, separator: string): [string, ?string] { + + +function splitOnce(str, separator) { const index = str.indexOf(separator); - return index === -1 - ? [str, null] - : [str.slice(0, index), str.slice(index + separator.length)]; + return index === -1 ? [str, null] : [str.slice(0, index), str.slice(index + separator.length)]; } - /** * Indents each line by the specified number of characters. */ -export function indent( - str: string, - level: number = 2, - char: string = ' ', -): string { + + +function indent(str, level = 2, char = ' ') { return str.replace(/^([^\n])/gm, char.repeat(level) + '$1'); } -export function pluralize(noun: string, count: number) { +function pluralize(noun, count) { return count === 1 ? noun : noun + 's'; } -export function capitalize(str: string): string { - return str.length === 0 - ? str - : str - .charAt(0) - .toUpperCase() - .concat(str.slice(1)); +function capitalize(str) { + return str.length === 0 ? str : str.charAt(0).toUpperCase().concat(str.slice(1)); } -type MatchRange = [/* start */ number, /* end */ number]; - /** * Returns a list of ranges where needle occurs in haystack. * This will *not* return overlapping matches; i.e. the returned list will be disjoint. * This makes it easier to use for e.g. highlighting matches in a UI. */ -export function getMatchRanges( - haystack: string, - needle: string, -): Array { +function getMatchRanges(haystack, needle) { if (needle === '') { // Not really a valid use. return []; @@ -262,36 +240,42 @@ export function getMatchRanges( const ranges = []; let matchIndex = 0; + while ((matchIndex = haystack.indexOf(needle, matchIndex)) !== -1) { const prevRange = ranges[ranges.length - 1]; + if (prevRange != null && prevRange[1] === matchIndex) { prevRange[1] += needle.length; } else { ranges.push([matchIndex, matchIndex + needle.length]); } + matchIndex += needle.length; } + return ranges; } -export function escapeMarkdown(markdown: string): string { +function escapeMarkdown(markdown) { // Which characters can be backslash-escaped? // markdown: ! # ()*+ -. [\] _`{ } https://daringfireball.net/projects/markdown/syntax#backslash // commonMark: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ https://spec.commonmark.org/0.28/#backslash-escapes // We'll only backslash-escape the lowest common denominator. - const slashEscaped = markdown.replace(/[#!()*+\-.[\\\]_`{}]/g, '\\$&'); - // And HTML tags need to be < > escaped. - return slashEscaped.replace(//g, '>'); -} + const slashEscaped = markdown.replace(/[#!()*+\-.[\\\]_`{}]/g, '\\$&'); // And HTML tags need to be < > escaped. -// Originally copied from: + return slashEscaped.replace(//g, '>'); +} // Originally copied from: // http://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url // But adopted to match `www.` urls as well as `https?` urls // and `!` as acceptable url piece. // Then optimized with https://www.npmjs.com/package/regexp-tree. // Added a single matching group for use with String.split. // eslint-disable-next-line max-len -export const URL_REGEX = /(https?:\/\/(?:www\.)?[-\w@:%.+~#=]{2,256}\.[a-z]{2,6}\b[-\w@:%+.~#?&/=!]*|www\.[-\w@:%.+~#=]{2,256}\.[a-z]{2,6}\b[-\w@:%+.~#?&/=!]*)/; -export const ELLIPSIS_CHAR = '\u2026'; -export const ZERO_WIDTH_SPACE = '\u200B'; + +const URL_REGEX = /(https?:\/\/(?:www\.)?[-\w@:%.+~#=]{2,256}\.[a-z]{2,6}\b[-\w@:%+.~#?&/=!]*|www\.[-\w@:%.+~#=]{2,256}\.[a-z]{2,6}\b[-\w@:%+.~#?&/=!]*)/; +exports.URL_REGEX = URL_REGEX; +const ELLIPSIS_CHAR = '\u2026'; +exports.ELLIPSIS_CHAR = ELLIPSIS_CHAR; +const ZERO_WIDTH_SPACE = '\u200B'; +exports.ZERO_WIDTH_SPACE = ZERO_WIDTH_SPACE; \ No newline at end of file diff --git a/modules/nuclide-commons/symbol-definition-preview.js b/modules/nuclide-commons/symbol-definition-preview.js index 4fcac997..3c0cc91d 100644 --- a/modules/nuclide-commons/symbol-definition-preview.js +++ b/modules/nuclide-commons/symbol-definition-preview.js @@ -1,3 +1,52 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getDefinitionPreview = getDefinitionPreview; + +function _mimeTypes() { + const data = _interopRequireDefault(require("mime-types")); + + _mimeTypes = function () { + return data; + }; + + return data; +} + +function _fsPromise() { + const data = _interopRequireDefault(require("./fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _string() { + const data = require("./string"); + + _string = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("./nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,78 +55,55 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {NuclideUri} from './nuclideUri'; -import mimeTypes from 'mime-types'; -import fsPromise from './fsPromise'; -import {countOccurrences} from './string'; -import nuclideUri from './nuclideUri'; - -type Definition = { - path: NuclideUri, - position: atom$Point, -}; - const MAX_PREVIEW_LINES = 10; const MAX_FILESIZE = 100000; const WHITESPACE_REGEX = /^\s*/; -function getIndentLevel(line: string) { + +function getIndentLevel(line) { // $FlowFixMe (>= v0.75.0) - const match: RegExp$matchResult = WHITESPACE_REGEX.exec(line); + const match = WHITESPACE_REGEX.exec(line); return match[0].length; } -export async function getDefinitionPreview( - definition: Definition, -): Promise { +async function getDefinitionPreview(definition) { // ensure filesize not too big before reading in whole file - const stats = await fsPromise.stat(definition.path); + const stats = await _fsPromise().default.stat(definition.path); + if (stats.size > MAX_FILESIZE) { return null; - } + } // if file is image, return base-64 encoded contents - // if file is image, return base-64 encoded contents - const fileBuffer = await fsPromise.readFile(definition.path); - const mime = - mimeTypes.contentType(nuclideUri.extname(definition.path)) || 'text/plain'; + const fileBuffer = await _fsPromise().default.readFile(definition.path); + const mime = _mimeTypes().default.contentType(_nuclideUri().default.extname(definition.path)) || 'text/plain'; + if (mime.startsWith('image/')) { - return {mime, contents: fileBuffer.toString('base64'), encoding: 'base64'}; + return { + mime, + contents: fileBuffer.toString('base64'), + encoding: 'base64' + }; } const contents = fileBuffer.toString('utf8'); const lines = contents.split('\n'); - const start = definition.position.row; const initialIndentLevel = getIndentLevel(lines[start]); - const buffer = []; - for ( - let i = start, - openParenCount = 0, - closedParenCount = 0, - openCurlyCount = 0, - closedCurlyCount = 0; - i < start + MAX_PREVIEW_LINES && i < lines.length; - i++ - ) { + + for (let i = start, openParenCount = 0, closedParenCount = 0, openCurlyCount = 0, closedCurlyCount = 0; i < start + MAX_PREVIEW_LINES && i < lines.length; i++) { const line = lines[i]; const indentLevel = getIndentLevel(line); - openParenCount += countOccurrences(line, '('); - closedParenCount += countOccurrences(line, ')'); - openCurlyCount += countOccurrences(line, '{'); - closedCurlyCount += countOccurrences(line, '}'); - + openParenCount += (0, _string().countOccurrences)(line, '('); + closedParenCount += (0, _string().countOccurrences)(line, ')'); + openCurlyCount += (0, _string().countOccurrences)(line, '{'); + closedCurlyCount += (0, _string().countOccurrences)(line, '}'); buffer.push(line.substr(Math.min(indentLevel, initialIndentLevel))); // dedent - // heuristic for the end of a function signature: + if (indentLevel <= initialIndentLevel) { // we've returned back to the original indentation level if (openParenCount > 0 && openParenCount === closedParenCount) { @@ -86,18 +112,18 @@ export async function getDefinitionPreview( } else if (line.trim().endsWith(';')) { // c-style statement ending break; - } else if ( - // end of a property definition - line.trim().endsWith(',') && - // including complex types as values - openCurlyCount === closedCurlyCount && - // but still not before function signatures are closed - openParenCount === closedParenCount - ) { + } else if ( // end of a property definition + line.trim().endsWith(',') && // including complex types as values + openCurlyCount === closedCurlyCount && // but still not before function signatures are closed + openParenCount === closedParenCount) { break; } } } - return {mime, contents: buffer.join('\n'), encoding: 'utf8'}; -} + return { + mime, + contents: buffer.join('\n'), + encoding: 'utf8' + }; +} \ No newline at end of file diff --git a/modules/nuclide-commons/test-helpers.js b/modules/nuclide-commons/test-helpers.js index 6136555d..2be49de5 100644 --- a/modules/nuclide-commons/test-helpers.js +++ b/modules/nuclide-commons/test-helpers.js @@ -1,3 +1,71 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.expectAsyncFailure = expectAsyncFailure; +exports.clearRequireCache = clearRequireCache; +exports.uncachedRequire = uncachedRequire; +exports.spyOnGetterValue = spyOnGetterValue; +exports.arePropertiesEqual = arePropertiesEqual; +exports.expectObservableToStartWith = expectObservableToStartWith; +exports.generateFixture = generateFixture; +exports.writeCoverage = writeCoverage; + +var _fs = _interopRequireDefault(require("fs")); + +function _temp() { + const data = _interopRequireDefault(require("temp")); + + _temp = function () { + return data; + }; + + return data; +} + +function _uuid() { + const data = _interopRequireDefault(require("uuid")); + + _uuid = function () { + return data; + }; + + return data; +} + +function _fsPromise() { + const data = _interopRequireDefault(require("./fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("./nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _promise() { + const data = require("./promise"); + + _promise = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,26 +74,12 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {Observable} from 'rxjs'; - -import invariant from 'assert'; -import fs from 'fs'; -import temp from 'temp'; -import uuid from 'uuid'; -import fsPromise from './fsPromise'; -import nuclideUri from './nuclideUri'; -import {asyncLimit} from './promise'; - -invariant( - (typeof atom !== 'undefined' && atom.inSpecMode()) || - process.env.NODE_ENV === 'test', - 'Test helpers should only be used in spec mode', -); - +if (!(typeof atom !== 'undefined' && atom.inSpecMode() || process.env.NODE_ENV === 'test')) { + throw new Error('Test helpers should only be used in spec mode'); +} /** * Verifies that a Promise fails with an Error with specific expectations. When * running a test where a Promise is expected to fail, it is important to verify @@ -39,20 +93,16 @@ invariant( * rejection of `promise`. If these expectations are not met, then * `verify()` must throw an exception. */ -export async function expectAsyncFailure( - promise: Promise, - verify: (error: Error) => void, -): Promise { + + +async function expectAsyncFailure(promise, verify) { try { await promise; - return Promise.reject( - new Error('Promise should have failed, but did not.'), - ); + return Promise.reject(new Error('Promise should have failed, but did not.')); } catch (e) { verify(e); } } - /** * This is useful for mocking a module that the module under test requires. * After setting up the mocks, you must invalidate the require cache and then @@ -62,16 +112,17 @@ export async function expectAsyncFailure( * The require parameter is needed because require is bound differently in each * file, and we need to execute this in the caller's context. */ -export function clearRequireCache(require: Object, module: string): void { + + +function clearRequireCache(require, module) { delete require.cache[require.resolve(module)]; } -export function uncachedRequire(require: Object, module: string): mixed { - clearRequireCache(require, module); - // $FlowIgnore +function uncachedRequire(require, module) { + clearRequireCache(require, module); // $FlowIgnore + return require(module); } - /** * Jasmine has trouble spying on properties supplied by getters, so to make it * work we have to get the value, delete the getter, and set the value as a @@ -83,48 +134,49 @@ export function uncachedRequire(require: Object, module: string): mixed { * - The getter returns a function (otherwise, it doesn't make sense to spy on * it) */ -export function spyOnGetterValue(object: Object, f: string): JasmineSpy { + + +function spyOnGetterValue(object, f) { const value = object[f]; delete object[f]; object[f] = value; return spyOn(object, f); } - /** * Checks if the two objects have equal properties. This considers a property * set to undefined to be equivalent to a property that was not set at all. */ -export function arePropertiesEqual(obj1: Object, obj2: Object): boolean { + + +function arePropertiesEqual(obj1, obj2) { const allProps = new Set(); + function addAllProps(obj) { for (const prop of Object.keys(obj)) { allProps.add(prop); } } + [obj1, obj2].forEach(addAllProps); + for (const prop of allProps) { if (obj1[prop] !== obj2[prop]) { return false; } } + return true; } - /** * Warning: Callsites *must* await the resulting promise, or test failures may go unreported or * misattributed. */ -export async function expectObservableToStartWith( - source: Observable, - expected: Array, -): Promise { - const actual: Array = await source - .take(expected.length) - .toArray() - .toPromise(); + + +async function expectObservableToStartWith(source, expected) { + const actual = await source.take(expected.length).toArray().toPromise(); expect(actual).toEqual(expected); } - /** * Takes of Map of file/file-content pairs, and creates a temp dir that matches * the file structure of the Map. Example: @@ -139,67 +191,55 @@ export async function expectObservableToStartWith( * /tmp/myfixture_1/foo.js (empty file) * /tmp/myfixture_1/bar/baz.txt (with 'some text') */ -export async function generateFixture( - fixtureName: string, - files: ?Map, -): Promise { - temp.track(); + + +async function generateFixture(fixtureName, files) { + _temp().default.track(); const MAX_CONCURRENT_FILE_OPS = 100; - const tempDir = await fsPromise.tempdir(fixtureName); + const tempDir = await _fsPromise().default.tempdir(fixtureName); if (files == null) { return tempDir; - } + } // Map -> Array with full paths + - // Map -> Array with full paths const fileTuples = Array.from(files, tuple => { // It's our own array - it's ok to mutate it - tuple[0] = nuclideUri.join(tempDir, tuple[0]); + tuple[0] = _nuclideUri().default.join(tempDir, tuple[0]); return tuple; - }); - - // Dedupe the dirs that we have to make. - const dirsToMake = fileTuples - .map(([filename]) => nuclideUri.dirname(filename)) - .filter((dirname, i, arr) => arr.indexOf(dirname) === i); - - await asyncLimit(dirsToMake, MAX_CONCURRENT_FILE_OPS, dirname => - fsPromise.mkdirp(dirname), - ); - - await asyncLimit( - fileTuples, - MAX_CONCURRENT_FILE_OPS, - ([filename, contents]) => { - // We can't use fsPromise/fs-plus because it does too much extra work. - // They call `mkdirp` before `writeFile`. We know that the target dir - // exists, so we can optimize by going straight to `fs`. When you're - // making 10k files, this adds ~500ms. - return new Promise((resolve, reject) => { - fs.writeFile(filename, contents || '', err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); + }); // Dedupe the dirs that we have to make. + + const dirsToMake = fileTuples.map(([filename]) => _nuclideUri().default.dirname(filename)).filter((dirname, i, arr) => arr.indexOf(dirname) === i); + await (0, _promise().asyncLimit)(dirsToMake, MAX_CONCURRENT_FILE_OPS, dirname => _fsPromise().default.mkdirp(dirname)); + await (0, _promise().asyncLimit)(fileTuples, MAX_CONCURRENT_FILE_OPS, ([filename, contents]) => { + // We can't use fsPromise/fs-plus because it does too much extra work. + // They call `mkdirp` before `writeFile`. We know that the target dir + // exists, so we can optimize by going straight to `fs`. When you're + // making 10k files, this adds ~500ms. + return new Promise((resolve, reject) => { + _fs.default.writeFile(filename, contents || '', err => { + if (err) { + reject(err); + } else { + resolve(); + } }); - }, - ); - + }); + }); return tempDir; } -export function writeCoverage(): void { - const {COVERAGE_DIR} = process.env; +function writeCoverage() { + const { + COVERAGE_DIR + } = process.env; + if (COVERAGE_DIR != null) { const coverage = global.__coverage__; + if (coverage != null) { - fs.writeFileSync( - nuclideUri.join(COVERAGE_DIR, uuid.v4() + '.json'), - JSON.stringify(coverage), - ); + _fs.default.writeFileSync(_nuclideUri().default.join(COVERAGE_DIR, _uuid().default.v4() + '.json'), JSON.stringify(coverage)); } } -} +} \ No newline at end of file diff --git a/modules/nuclide-commons/tokenized-text.js b/modules/nuclide-commons/tokenized-text.js index b4c2c54b..1153ffb3 100644 --- a/modules/nuclide-commons/tokenized-text.js +++ b/modules/nuclide-commons/tokenized-text.js @@ -1,3 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.keyword = keyword; +exports.className = className; +exports.constructor = constructor; +exports.method = method; +exports.param = param; +exports.string = string; +exports.whitespace = whitespace; +exports.plain = plain; +exports.type = type; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,64 +21,48 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -export type TokenKind = - | 'keyword' - | 'class-name' - | 'constructor' - | 'method' - | 'param' - | 'string' - | 'whitespace' - | 'plain' - | 'type'; - -export type TextToken = { - kind: TokenKind, - value: string, -}; - -export type TokenizedText = Array; - -export function keyword(value: string): TextToken { +function keyword(value) { return _buildToken('keyword', value); } -export function className(value: string): TextToken { +function className(value) { return _buildToken('class-name', value); } -export function constructor(value: string): TextToken { +function constructor(value) { return _buildToken('constructor', value); } -export function method(value: string): TextToken { +function method(value) { return _buildToken('method', value); } -export function param(value: string): TextToken { +function param(value) { return _buildToken('param', value); } -export function string(value: string): TextToken { +function string(value) { return _buildToken('string', value); } -export function whitespace(value: string): TextToken { +function whitespace(value) { return _buildToken('whitespace', value); } -export function plain(value: string): TextToken { +function plain(value) { return _buildToken('plain', value); } -export function type(value: string): TextToken { +function type(value) { return _buildToken('type', value); } -function _buildToken(kind: TokenKind, value: string): TextToken { - return {kind, value}; -} +function _buildToken(kind, value) { + return { + kind, + value + }; +} \ No newline at end of file diff --git a/modules/nuclide-commons/which.js b/modules/nuclide-commons/which.js index 4b674064..9bd077c1 100644 --- a/modules/nuclide-commons/which.js +++ b/modules/nuclide-commons/which.js @@ -1,3 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _os = _interopRequireDefault(require("os")); + +function _nuclideUri() { + const data = _interopRequireDefault(require("./nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _process() { + const data = require("./process"); + + _process = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,47 +37,36 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type {ObserveProcessOptions} from './process'; - -import os from 'os'; -import nuclideUri from './nuclideUri'; -import {runCommand} from './process'; - /** * Provides a cross-platform way to check whether a binary is available. * * We ran into problems with the npm `which` package (the nature of which I unfortunately don't * remember) so we can use this for now. */ - -function sanitizePathForWindows(path: string): string { - if (nuclideUri.basename(path) === path) { +function sanitizePathForWindows(path) { + if (_nuclideUri().default.basename(path) === path) { // simple binary in $PATH like `flow` return path; } else { - return `${nuclideUri.dirname(path)}:${nuclideUri.basename(path)}`; + return `${_nuclideUri().default.dirname(path)}:${_nuclideUri().default.basename(path)}`; } } -export default (async function which( - path: string, - options?: ObserveProcessOptions = {}, -): Promise { +var which = async function which(path, options = {}) { const isWindows = process.platform === 'win32'; const whichCommand = isWindows ? 'where' : 'which'; const searchPath = isWindows ? sanitizePathForWindows(path) : path; + try { - const result = await runCommand( - whichCommand, - [searchPath], - options, - ).toPromise(); - return result.split(os.EOL)[0]; + const result = await (0, _process().runCommand)(whichCommand, [searchPath], options).toPromise(); + return result.split(_os.default.EOL)[0]; } catch (e) { return null; } -}); +}; + +exports.default = which; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/AdbDeviceSelector.js b/modules/nuclide-debugger-common/AdbDeviceSelector.js index 2a165f1a..4370bc09 100644 --- a/modules/nuclide-debugger-common/AdbDeviceSelector.js +++ b/modules/nuclide-debugger-common/AdbDeviceSelector.js @@ -1,3 +1,66 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.AdbDeviceSelector = void 0; + +function _nuclideAdb() { + const data = require("../nuclide-adb"); + + _nuclideAdb = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _Dropdown() { + const data = require("../nuclide-commons-ui/Dropdown"); + + _Dropdown = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _expected() { + const data = require("../nuclide-commons/expected"); + + _expected = function () { + return data; + }; + + return data; +} + +function _LoadingSpinner() { + const data = require("../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,77 +69,34 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {AdbDevice} from 'nuclide-adb/lib/types'; -import type {Expected} from 'nuclide-commons/expected'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {MenuItem} from 'nuclide-commons-ui/Dropdown'; - -import {observeAndroidDevices} from 'nuclide-adb'; -import * as React from 'react'; -import {Dropdown} from 'nuclide-commons-ui/Dropdown'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Expect} from 'nuclide-commons/expected'; -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; -import invariant from 'assert'; - const NO_DEVICES_MSG = 'No adb devices attached!'; -type Props = { - targetUri: NuclideUri, - onChange: (value: ?AdbDevice) => void, -}; - -type State = { - deviceList: Expected>, - selectedDevice: ?AdbDevice, -}; - -export class AdbDeviceSelector extends React.Component { - props: Props; - state: State; - _disposables: UniversalDisposable; - - constructor(props: Props) { +class AdbDeviceSelector extends React.Component { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); - - (this: any)._handleDeviceListChange = this._handleDeviceListChange.bind( - this, - ); - (this: any)._handleDeviceDropdownChange = this._handleDeviceDropdownChange.bind( - this, - ); - + this._disposables = new (_UniversalDisposable().default)(); + this._handleDeviceListChange = this._handleDeviceListChange.bind(this); + this._handleDeviceDropdownChange = this._handleDeviceDropdownChange.bind(this); this.state = { - deviceList: Expect.pending(), - selectedDevice: null, + deviceList: _expected().Expect.pending(), + selectedDevice: null }; } - componentDidMount(): void { - this._disposables.add( - observeAndroidDevices(this.props.targetUri) - .startWith(Expect.pending()) - .subscribe(deviceList => this._handleDeviceListChange(deviceList)), - ); + componentDidMount() { + this._disposables.add((0, _nuclideAdb().observeAndroidDevices)(this.props.targetUri).startWith(_expected().Expect.pending()).subscribe(deviceList => this._handleDeviceListChange(deviceList))); } componentWillUnmount() { this._disposables.dispose(); } - _handleDeviceListChange(deviceList: Expected>): void { + _handleDeviceListChange(deviceList) { const previousDevice = this.state.selectedDevice; - let selectedDevice = - previousDevice == null - ? null - : deviceList - .getOrDefault([]) - .find(device => device.serial === previousDevice.serial); + let selectedDevice = previousDevice == null ? null : deviceList.getOrDefault([]).find(device => device.serial === previousDevice.serial); if (selectedDevice == null && deviceList.isValue) { selectedDevice = deviceList.value[0]; @@ -84,50 +104,58 @@ export class AdbDeviceSelector extends React.Component { this.setState({ deviceList, - selectedDevice, + selectedDevice }); this.props.onChange(selectedDevice); } - _getDeviceItems(): Array { - invariant(this.state.deviceList.isValue); + _getDeviceItems() { + if (!this.state.deviceList.isValue) { + throw new Error("Invariant violation: \"this.state.deviceList.isValue\""); + } + if (this.state.deviceList.value.length === 0) { - return [{value: null, label: NO_DEVICES_MSG}]; + return [{ + value: null, + label: NO_DEVICES_MSG + }]; } return this.state.deviceList.value.map(device => ({ value: device, - label: device.displayName, + label: device.displayName })); } - render(): React.Node { + render() { if (this.state.deviceList.isPending) { - return ; + return React.createElement(_LoadingSpinner().LoadingSpinner, { + size: "EXTRA_SMALL" + }); } if (this.state.deviceList.isError) { - return ( -

      - {this.state.deviceList.error.toString()} -
      - ); + return React.createElement("div", { + className: "nuclide-ui-message-error" + }, this.state.deviceList.error.toString()); } const deviceItems = this._getDeviceItems(); - return ( - - ); + + return React.createElement(_Dropdown().Dropdown, { + options: deviceItems, + onChange: this._handleDeviceDropdownChange, + value: this.state.selectedDevice + }); } - _handleDeviceDropdownChange(selectedDevice: ?AdbDevice): void { + _handleDeviceDropdownChange(selectedDevice) { this.setState({ - selectedDevice, + selectedDevice }); this.props.onChange(selectedDevice); } + } + +exports.AdbDeviceSelector = AdbDeviceSelector; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/AutoGenLaunchAttachProvider.js b/modules/nuclide-debugger-common/AutoGenLaunchAttachProvider.js index 7037e24e..537f6263 100644 --- a/modules/nuclide-debugger-common/AutoGenLaunchAttachProvider.js +++ b/modules/nuclide-debugger-common/AutoGenLaunchAttachProvider.js @@ -1,3 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.AutoGenLaunchAttachProvider = void 0; + +function _DebuggerLaunchAttachProvider() { + const data = _interopRequireDefault(require("./DebuggerLaunchAttachProvider")); + + _DebuggerLaunchAttachProvider = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _AutoGenLaunchAttachUiComponent() { + const data = _interopRequireDefault(require("./AutoGenLaunchAttachUiComponent")); + + _AutoGenLaunchAttachUiComponent = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,54 +39,30 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {DebuggerConfigAction} from './types'; -import type {LaunchAttachProviderIsEnabled, AutoGenConfig} from './types'; - -import invariant from 'assert'; -import DebuggerLaunchAttachProvider from './DebuggerLaunchAttachProvider'; -import * as React from 'react'; -import AutoGenLaunchAttachUiComponent from './AutoGenLaunchAttachUiComponent'; - -const LaunchAttachProviderDefaultIsEnabled = ( - action: DebuggerConfigAction, - config: AutoGenConfig, -) => { +const LaunchAttachProviderDefaultIsEnabled = (action, config) => { return Promise.resolve(config[action] != null); }; -export class AutoGenLaunchAttachProvider extends DebuggerLaunchAttachProvider { - _config: AutoGenConfig; - _isEnabled: LaunchAttachProviderIsEnabled; - - constructor( - debuggingTypeName: string, - targetUri: string, - config: AutoGenConfig, - isEnabled?: LaunchAttachProviderIsEnabled = LaunchAttachProviderDefaultIsEnabled, - ) { +class AutoGenLaunchAttachProvider extends _DebuggerLaunchAttachProvider().default { + constructor(debuggingTypeName, targetUri, config, isEnabled = LaunchAttachProviderDefaultIsEnabled) { super(debuggingTypeName, targetUri); this._config = config; this._isEnabled = isEnabled; } - async _resolvePath(project: NuclideUri, filePath: string): Promise { - let rpcService: ?nuclide$RpcService = null; - // Atom's service hub is synchronous. - atom.packages.serviceHub - .consume('nuclide-rpc-services', '0.0.0', provider => { - rpcService = provider; - }) - .dispose(); + async _resolvePath(project, filePath) { + let rpcService = null; // Atom's service hub is synchronous. + + atom.packages.serviceHub.consume('nuclide-rpc-services', '0.0.0', provider => { + rpcService = provider; + }).dispose(); + if (rpcService != null) { - const fsService = rpcService.getServiceByNuclideUri( - 'FileSystemService', - project, - ); + const fsService = rpcService.getServiceByNuclideUri('FileSystemService', project); + if (fsService != null) { try { return fsService.expandHomeDir(filePath); @@ -64,56 +73,48 @@ export class AutoGenLaunchAttachProvider extends DebuggerLaunchAttachProvider { return Promise.resolve(filePath); } - getCallbacksForAction(action: DebuggerConfigAction) { + getCallbacksForAction(action) { return { /** * Whether this provider is enabled or not. */ - isEnabled: async (): Promise => { + isEnabled: async () => { return this._isEnabled(action, this._config); }, /** * Returns the UI component for configuring the specified debugger type and action. */ - getComponent: ( - debuggerTypeName: string, - configIsValidChanged: (valid: boolean) => void, - defaultConfig: ?{[string]: mixed}, - ) => { + getComponent: (debuggerTypeName, configIsValidChanged, defaultConfig) => { const launchOrAttachConfig = this._config[action]; - invariant(launchOrAttachConfig != null); + + if (!(launchOrAttachConfig != null)) { + throw new Error("Invariant violation: \"launchOrAttachConfig != null\""); + } + if (defaultConfig != null) { - launchOrAttachConfig.properties = launchOrAttachConfig.properties.map( - p => ({ - ...p, - defaultValue: - defaultConfig[p.name] == null - ? p.defaultValue - : defaultConfig[p.name], - }), - ); - - // Pass the ignore flag from the properites to the LaunchOrAttachConfigBase + launchOrAttachConfig.properties = launchOrAttachConfig.properties.map(p => Object.assign({}, p, { + defaultValue: defaultConfig[p.name] == null ? p.defaultValue : defaultConfig[p.name] + })); // Pass the ignore flag from the properites to the LaunchOrAttachConfigBase + if (defaultConfig.ignorePreviousParams !== undefined) { - launchOrAttachConfig.ignorePreviousParams = Boolean( - defaultConfig.ignorePreviousParams, - ); + launchOrAttachConfig.ignorePreviousParams = Boolean(defaultConfig.ignorePreviousParams); } else { launchOrAttachConfig.ignorePreviousParams = false; } } - return ( - - ); - }, + return React.createElement(_AutoGenLaunchAttachUiComponent().default, { + targetUri: this.getTargetUri(), + configIsValidChanged: configIsValidChanged, + config: launchOrAttachConfig, + debuggerTypeName: debuggerTypeName, + pathResolver: this._resolvePath + }); + } }; } + } + +exports.AutoGenLaunchAttachProvider = AutoGenLaunchAttachProvider; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/AutoGenLaunchAttachUiComponent.js b/modules/nuclide-debugger-common/AutoGenLaunchAttachUiComponent.js index 40202141..c17c0bd3 100644 --- a/modules/nuclide-debugger-common/AutoGenLaunchAttachUiComponent.js +++ b/modules/nuclide-debugger-common/AutoGenLaunchAttachUiComponent.js @@ -1,3 +1,146 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _Checkbox() { + const data = require("../nuclide-commons-ui/Checkbox"); + + _Checkbox = function () { + return data; + }; + + return data; +} + +function _RadioGroup() { + const data = _interopRequireDefault(require("../nuclide-commons-ui/RadioGroup")); + + _RadioGroup = function () { + return data; + }; + + return data; +} + +function _AtomInput() { + const data = require("../nuclide-commons-ui/AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _string() { + const data = require("../nuclide-commons/string"); + + _string = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +function _debugger() { + const data = require("../nuclide-commons-atom/debugger"); + + _debugger = function () { + return data; + }; + + return data; +} + +function _DebuggerConfigSerializer() { + const data = require("./DebuggerConfigSerializer"); + + _DebuggerConfigSerializer = function () { + return data; + }; + + return data; +} + +function _DeviceAndPackage() { + const data = require("./DeviceAndPackage"); + + _DeviceAndPackage = function () { + return data; + }; + + return data; +} + +function _DeviceAndProcess() { + const data = require("./DeviceAndProcess"); + + _DeviceAndProcess = function () { + return data; + }; + + return data; +} + +function _SelectableFilterableProcessTable() { + const data = _interopRequireDefault(require("./SelectableFilterableProcessTable")); + + _SelectableFilterableProcessTable = function () { + return data; + }; + + return data; +} + +function _SourceSelector() { + const data = require("./SourceSelector"); + + _SourceSelector = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,93 +149,148 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {AndroidJavaProcess, AdbDevice} from 'nuclide-adb/lib/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type { - AutoGenProperty, - AutoGenLaunchOrAttachConfig, - AutoGenPropertyType, - AutoGenPropertyPrimitiveType, -} from './types'; -import * as React from 'react'; - -import idx from 'idx'; -import nullthrows from 'nullthrows'; -import {Checkbox} from 'nuclide-commons-ui/Checkbox'; -import RadioGroup from 'nuclide-commons-ui/RadioGroup'; -import {AtomInput} from 'nuclide-commons-ui/AtomInput'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {capitalize, shellParseWithGlobs} from 'nuclide-commons/string'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {getDebuggerService} from 'nuclide-commons-atom/debugger'; -import { - serializeDebuggerConfig, - deserializeDebuggerConfig, -} from './DebuggerConfigSerializer'; -import {DeviceAndPackage} from './DeviceAndPackage'; -import {DeviceAndProcess} from './DeviceAndProcess'; -import SelectableFilterableProcessTable from './SelectableFilterableProcessTable'; -import {SourceSelector} from './SourceSelector'; - -type StringPair = [string, string]; - -type Props = {| - +targetUri: NuclideUri, - +configIsValidChanged: (valid: boolean) => void, - +config: AutoGenLaunchOrAttachConfig, - +debuggerTypeName: string, - +pathResolver: (project: NuclideUri, filePath: string) => Promise, -|}; - -type DeviceAndPackageType = {| - +device: ?AdbDevice, - +selectedPackage: string, -|}; - -type DeviceAndProcessType = {| - +device: ?AdbDevice, - +selectedProcess: ?AndroidJavaProcess, -|}; - -type State = { - enumValues: Map, - booleanValues: Map, - atomInputValues: Map, - processTableValues: Map, - deviceAndPackageValues: Map, - deviceAndProcessValues: Map, - selectSourcesValues: Map, -}; - // extension must be a string starting with a '.' like '.js' or '.py' -function getActiveScriptPath(extension: string): string { - const center = atom.workspace.getCenter - ? atom.workspace.getCenter() - : atom.workspace; - const activeEditor: ?atom$TextEditor = center.getActiveTextEditor(); - if ( - activeEditor == null || - !activeEditor.getPath() || - !nullthrows(activeEditor.getPath()).endsWith(extension) - ) { +function getActiveScriptPath(extension) { + const center = atom.workspace.getCenter ? atom.workspace.getCenter() : atom.workspace; + const activeEditor = center.getActiveTextEditor(); + + if (activeEditor == null || !activeEditor.getPath() || !(0, _nullthrows().default)(activeEditor.getPath()).endsWith(extension)) { return ''; } - return nuclideUri.getPath(nullthrows(activeEditor.getPath())); -} -export default class AutoGenLaunchAttachUiComponent extends React.Component< - Props, - State, -> { - _disposables: UniversalDisposable; + return _nuclideUri().default.getPath((0, _nullthrows().default)(activeEditor.getPath())); +} - constructor(props: Props) { +class AutoGenLaunchAttachUiComponent extends React.Component { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); + + this._handleDebugButtonClick = async () => { + const { + targetUri, + config + } = this.props; + const { + atomInputValues, + booleanValues, + enumValues, + processTableValues, + deviceAndPackageValues, + deviceAndProcessValues, + selectSourcesValues + } = this.state; + const { + launch, + vsAdapterType, + threads, + getProcessName + } = config; + const stringValues = new Map(); + const stringArrayValues = new Map(); + const objectValues = new Map(); + const numberValues = new Map(); + const jsonValues = new Map(); + await Promise.all(Array.from(this._getConfigurationProperties().filter(property => property.visible && atomInputValues.has(property.name)).map(async property => { + var _ref3; + + const { + name, + type + } = property; + const itemType = (_ref3 = property) != null ? _ref3.itemType : _ref3; + const value = atomInputValues.get(name) || ''; + + if (type === 'path') { + try { + const resolvedPath = this.props.pathResolver == null ? value : await this.props.pathResolver(targetUri, value); + stringValues.set(name, resolvedPath); + } catch (_) { + stringValues.set(name, value); + } + } else if (type === 'string') { + stringValues.set(name, value); + } else if (type === 'array' && itemType === 'string') { + stringArrayValues.set(name, (0, _string().shellParseWithGlobs)(value)); + } else if (type === 'object') { + const objectValue = {}; + (0, _string().shellParseWithGlobs)(value).forEach(variable => { + const [lhs, rhs] = variable.split('='); + objectValue[lhs] = rhs; + }); + objectValues.set(name, objectValue); + } else if (type === 'number') { + numberValues.set(name, Number(value)); + } else if (type === 'json') { + jsonValues.set(name, JSON.parse(value)); + } + + return value; + }))); + const packageValues = new Map(); + + this._getConfigurationProperties().filter(property => property.visible && deviceAndPackageValues.has(property.name)).forEach(property => { + const deviceAndPackage = deviceAndPackageValues.get(property.name); + + if (deviceAndPackage != null) { + packageValues.set(property.name, deviceAndPackage.selectedPackage); + } + }); + + const processValues = new Map(); + + this._getConfigurationProperties().filter(property => property.visible && deviceAndProcessValues.has(property.name)).forEach(property => { + var _ref4; + + const deviceAndProcess = deviceAndProcessValues.get(property.name); + const processName = (_ref4 = deviceAndProcess) != null ? (_ref4 = _ref4.selectedProcess) != null ? _ref4.name : _ref4 : _ref4; + + if (deviceAndProcess != null && processName != null) { + processValues.set(property.name, processName); + } + }); + + const values = {}; + [booleanValues, enumValues, stringValues, stringArrayValues, objectValues, numberValues, processTableValues, jsonValues, deviceAndPackageValues, deviceAndProcessValues, selectSourcesValues].forEach(map => { + map.forEach((value, key) => { + values[key] = value; + }); + }); + + this._getConfigurationProperties().filter(property => !property.visible && !atomInputValues.has(property.name)).forEach(property => { + var _ref5; + + const { + name + } = property; + values[name] = (_ref5 = property) != null ? _ref5.defaultValue : _ref5; + }); + + const debuggerService = await (0, _debugger().getDebuggerService)(); + debuggerService.startVspDebugging({ + targetUri, + debugMode: launch ? 'launch' : 'attach', + adapterType: vsAdapterType, + config: values, + showThreads: threads, + customControlButtons: [], + threadsComponentTitle: 'Threads', + customDisposable: new (_UniversalDisposable().default)(), + processName: getProcessName(values) + }); + (0, _DebuggerConfigSerializer().serializeDebuggerConfig)(...this._getSerializationArgs(this.props), { + atomInputValues: Array.from(atomInputValues), + booleanValues: Array.from(booleanValues), + enumValues: Array.from(enumValues), + packageValues: Array.from(packageValues), + processValues: Array.from(processValues), + selectSourcesValues: Array.from(selectSourcesValues) + }); + }; + + this._disposables = new (_UniversalDisposable().default)(); this.state = { atomInputValues: new Map(), booleanValues: new Map(), @@ -100,192 +298,153 @@ export default class AutoGenLaunchAttachUiComponent extends React.Component< processTableValues: new Map(), deviceAndPackageValues: new Map(), deviceAndProcessValues: new Map(), - selectSourcesValues: new Map(), + selectSourcesValues: new Map() }; } - _atomInputType( - type: AutoGenPropertyType, - itemType: ?AutoGenPropertyPrimitiveType, - ): boolean { - return ( - type === 'string' || - type === 'path' || - (type === 'array' && itemType === 'string') || - type === 'object' || - type === 'number' || - type === 'json' - ); + _atomInputType(type, itemType) { + return type === 'string' || type === 'path' || type === 'array' && itemType === 'string' || type === 'object' || type === 'number' || type === 'json'; } - _getConfigurationProperties(): AutoGenProperty[] { - const {config} = this.props; + _getConfigurationProperties() { + const { + config + } = this.props; return config.properties; } - _populateDefaultValues( - config: AutoGenLaunchOrAttachConfig, - atomInputValues: Map, - booleanValues: Map, - enumValues: Map, - ): void { - const ignorePreviousParams = - config.ignorePreviousParams !== undefined - ? config.ignorePreviousParams - : false; + _populateDefaultValues(config, atomInputValues, booleanValues, enumValues) { + const ignorePreviousParams = config.ignorePreviousParams !== undefined ? config.ignorePreviousParams : false; config.properties.filter(property => property.visible).map(property => { - const {name, type} = property; - const itemType = idx(property, _ => _.itemType); + var _ref; + + const { + name, + type + } = property; + const itemType = (_ref = property) != null ? _ref.itemType : _ref; + if (this._atomInputType(type, itemType)) { const existingValue = atomInputValues.get(name); - if ( - (ignorePreviousParams || existingValue == null) && - typeof property.defaultValue !== 'undefined' - ) { + + if ((ignorePreviousParams || existingValue == null) && typeof property.defaultValue !== 'undefined') { // String(propertyDescription.default) deals with both strings and numbers and arrays // JSON.stringify for JSON // empty string otherwise - const defaultValue = - type === 'string' || type === 'number' || type === 'array' - ? String(property.defaultValue) - : type === 'json' - ? JSON.stringify(property.defaultValue) - : ''; + const defaultValue = type === 'string' || type === 'number' || type === 'array' ? String(property.defaultValue) : type === 'json' ? JSON.stringify(property.defaultValue) : ''; atomInputValues.set(name, defaultValue); } } else if (type === 'boolean') { const existingValue = booleanValues.get(name); - if ( - (ignorePreviousParams || existingValue == null) && - typeof property.defaultValue !== 'undefined' && - property.defaultValue != null && - typeof property.defaultValue === 'boolean' - ) { + + if ((ignorePreviousParams || existingValue == null) && typeof property.defaultValue !== 'undefined' && property.defaultValue != null && typeof property.defaultValue === 'boolean') { booleanValues.set(name, property.defaultValue); } else { booleanValues.set(name, false); } } else if (type === 'enum' && property.enums != null) { const existingValue = enumValues.get(name); - if ( - (ignorePreviousParams || existingValue == null) && - typeof property.defaultValue !== 'undefined' && - property.defaultValue != null && - typeof property.defaultValue === 'string' - ) { + + if ((ignorePreviousParams || existingValue == null) && typeof property.defaultValue !== 'undefined' && property.defaultValue != null && typeof property.defaultValue === 'string') { enumValues.set(name, property.defaultValue); } } }); } - _getSerializationArgs(props: Props) { - const {targetUri, config, debuggerTypeName} = props; - const args = [ - nuclideUri.isRemote(targetUri) - ? nuclideUri.getHostname(targetUri) - : 'local', - config.launch ? 'launch' : 'attach', - debuggerTypeName, - ]; + _getSerializationArgs(props) { + const { + targetUri, + config, + debuggerTypeName + } = props; + const args = [_nuclideUri().default.isRemote(targetUri) ? _nuclideUri().default.getHostname(targetUri) : 'local', config.launch ? 'launch' : 'attach', debuggerTypeName]; return args; } - _deserializeDebuggerConfig(props: Props): void { - deserializeDebuggerConfig( - ...this._getSerializationArgs(props), - (transientSettings, savedSettings) => { - const {config} = props; - const { - cwdPropertyName, - scriptPropertyName, - launch, - scriptExtension, - } = config; - const atomInputValues = new Map(savedSettings.atomInputValues || []); - - const scriptPath = - (scriptPropertyName != null && - atomInputValues.get(scriptPropertyName)) || - (scriptExtension != null && getActiveScriptPath(scriptExtension)) || - ''; - if (cwdPropertyName != null) { - const cwd = - atomInputValues.get(cwdPropertyName) || - (scriptPath !== '' ? nuclideUri.dirname(scriptPath) : ''); - if (cwd !== '') { - atomInputValues.set(cwdPropertyName, cwd); - } + _deserializeDebuggerConfig(props) { + (0, _DebuggerConfigSerializer().deserializeDebuggerConfig)(...this._getSerializationArgs(props), (transientSettings, savedSettings) => { + const { + config + } = props; + const { + cwdPropertyName, + scriptPropertyName, + launch, + scriptExtension + } = config; + const atomInputValues = new Map(savedSettings.atomInputValues || []); + const scriptPath = scriptPropertyName != null && atomInputValues.get(scriptPropertyName) || scriptExtension != null && getActiveScriptPath(scriptExtension) || ''; + + if (cwdPropertyName != null) { + const cwd = atomInputValues.get(cwdPropertyName) || (scriptPath !== '' ? _nuclideUri().default.dirname(scriptPath) : ''); + + if (cwd !== '') { + atomInputValues.set(cwdPropertyName, cwd); } - if (launch) { - if (scriptPath !== '' && scriptPropertyName != null) { - atomInputValues.set(scriptPropertyName, scriptPath); - } + } + + if (launch) { + if (scriptPath !== '' && scriptPropertyName != null) { + atomInputValues.set(scriptPropertyName, scriptPath); } - const booleanValues = new Map(savedSettings.booleanValues || []); - const enumValues = new Map(savedSettings.enumValues || []); - this._populateDefaultValues( - config, - atomInputValues, - booleanValues, - enumValues, - ); - // do not serialize and deserialize these values: - const processTableValues = new Map(); - const deviceAndPackageValues = new Map(); - const deviceAndProcessValues = new Map(); - - this.setState({ - atomInputValues, - booleanValues, - enumValues, - processTableValues, - deviceAndPackageValues, - deviceAndProcessValues, - }); - }, - ); + } + + const booleanValues = new Map(savedSettings.booleanValues || []); + const enumValues = new Map(savedSettings.enumValues || []); + + this._populateDefaultValues(config, atomInputValues, booleanValues, enumValues); // do not serialize and deserialize these values: + + + const processTableValues = new Map(); + const deviceAndPackageValues = new Map(); + const deviceAndProcessValues = new Map(); + this.setState({ + atomInputValues, + booleanValues, + enumValues, + processTableValues, + deviceAndPackageValues, + deviceAndProcessValues + }); + }); } - setState(newState: Object): void { - super.setState(newState, () => - this.props.configIsValidChanged(this._debugButtonShouldEnable()), - ); + setState(newState) { + super.setState(newState, () => this.props.configIsValidChanged(this._debugButtonShouldEnable())); } - UNSAFE_componentWillReceiveProps(nextProps: Props) { + UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.debuggerTypeName !== this.props.debuggerTypeName) { this._deserializeDebuggerConfig(nextProps); } } - UNSAFE_componentWillMount(): void { + UNSAFE_componentWillMount() { this._deserializeDebuggerConfig(this.props); } - componentDidMount(): void { - this._disposables.add( - atom.commands.add('atom-workspace', { - 'core:confirm': async () => { - if (this._debugButtonShouldEnable()) { - await this._handleDebugButtonClick(); - } - }, - }), - ); + componentDidMount() { + this._disposables.add(atom.commands.add('atom-workspace', { + 'core:confirm': async () => { + if (this._debugButtonShouldEnable()) { + await this._handleDebugButtonClick(); + } + } + })); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - _valueExists(property: AutoGenProperty): boolean { - const {name, type} = property; - if ( - type === 'string' || - type === 'path' || - (type === 'array' && property.itemType === 'string') - ) { + _valueExists(property) { + const { + name, + type + } = property; + + if (type === 'string' || type === 'path' || type === 'array' && property.itemType === 'string') { const value = this.state.atomInputValues.get(name); return value != null && value !== ''; } else if (type === 'number') { @@ -302,340 +461,152 @@ export default class AutoGenLaunchAttachUiComponent extends React.Component< return value != null; } else if (type === 'deviceAndPackage') { const deviceAndPackageValue = this.state.deviceAndPackageValues.get(name); - return ( - deviceAndPackageValue != null && - deviceAndPackageValue.device != null && - deviceAndPackageValue.selectedPackage != null - ); + return deviceAndPackageValue != null && deviceAndPackageValue.device != null && deviceAndPackageValue.selectedPackage != null; } else if (type === 'deviceAndProcess') { const deviceAndProcessValue = this.state.deviceAndProcessValues.get(name); - return ( - deviceAndProcessValue != null && - deviceAndProcessValue.device != null && - deviceAndProcessValue.selectedProcess != null - ); + return deviceAndProcessValue != null && deviceAndProcessValue.device != null && deviceAndProcessValue.selectedProcess != null; } else if (type === 'selectSources') { const selectSourcesValue = this.state.selectSourcesValues.get(name); return selectSourcesValue != null; } + return false; } - _debugButtonShouldEnable(): boolean { - return this._getConfigurationProperties() - .filter(p => p.required) - .every(p => this._valueExists(p)); + _debugButtonShouldEnable() { + return this._getConfigurationProperties().filter(p => p.required).every(p => this._valueExists(p)); } - _getComponentForProperty(property: AutoGenProperty): React.Node { - const {name, type, description, required} = property; - const formattedName = - capitalize(name.replace(/([A-Z])/g, ' $1')) + - (required ? ' (Required)' : ''); - const nameLabel = ; - const itemType = idx(property, _ => _.itemType); + _getComponentForProperty(property) { + var _ref2; + + const { + name, + type, + description, + required + } = property; + const formattedName = (0, _string().capitalize)(name.replace(/([A-Z])/g, ' $1')) + (required ? ' (Required)' : ''); + const nameLabel = React.createElement("label", null, formattedName, ":"); + const itemType = (_ref2 = property) != null ? _ref2.itemType : _ref2; + if (this._atomInputType(type, itemType)) { const value = this.state.atomInputValues.get(name) || ''; - return ( -
      - {nameLabel} - { - this.state.atomInputValues.set(name, newValue); - this.props.configIsValidChanged(this._debugButtonShouldEnable()); - }} - /> -
      - ); + return React.createElement("div", null, nameLabel, React.createElement(_AtomInput().AtomInput, { + key: this.props.debuggerTypeName + ':' + name, + placeholderText: description, + value: value, + onDidChange: newValue => { + this.state.atomInputValues.set(name, newValue); + this.props.configIsValidChanged(this._debugButtonShouldEnable()); + } + })); } else if (type === 'boolean') { const checked = this.state.booleanValues.get(name) || false; - return ( -
      -
      {nameLabel}
      - { - this.state.booleanValues.set(name, newValue); - this.props.configIsValidChanged(this._debugButtonShouldEnable()); - }} - /> -
      - ); + return React.createElement("div", null, React.createElement("div", null, nameLabel), React.createElement(_Checkbox().Checkbox, { + checked: checked, + label: description, + onChange: newValue => { + this.state.booleanValues.set(name, newValue); + this.props.configIsValidChanged(this._debugButtonShouldEnable()); + } + })); } else if (type === 'enum' && property.enums != null) { const enums = property.enums; const selectedValue = this.state.enumValues.get(name) || ''; - return ( -
      - {nameLabel} - ( - - ))} - onSelectedChange={index => { - this.state.enumValues.set(name, enums[index]); - this.props.configIsValidChanged(this._debugButtonShouldEnable()); - }} - /> -
      - ); + return React.createElement("div", null, nameLabel, React.createElement(_RadioGroup().default, { + selectedIndex: enums.indexOf(selectedValue), + optionLabels: enums.map((enumValue, i) => React.createElement("label", { + key: i + }, enumValue)), + onSelectedChange: index => { + this.state.enumValues.set(name, enums[index]); + this.props.configIsValidChanged(this._debugButtonShouldEnable()); + } + })); } else if (type === 'process') { - return ( -
      - {nameLabel} - { - if (selectedProcess != null) { - this.state.processTableValues.set(name, selectedProcess.pid); - } else { - this.state.processTableValues.delete(name); - } - this.props.configIsValidChanged(this._debugButtonShouldEnable()); - }} - /> -
      - ); + return React.createElement("div", null, nameLabel, React.createElement(_SelectableFilterableProcessTable().default, { + targetUri: this.props.targetUri, + onSelect: selectedProcess => { + if (selectedProcess != null) { + this.state.processTableValues.set(name, selectedProcess.pid); + } else { + this.state.processTableValues.delete(name); + } + + this.props.configIsValidChanged(this._debugButtonShouldEnable()); + } + })); } else if (type === 'deviceAndPackage') { - return ( - { - let packageValuesArray: Array = []; - deserializeDebuggerConfig( - ...this._getSerializationArgs(this.props), - (transientSettings, savedSettings) => { - packageValuesArray = - (savedSettings.packageValues: Array) || []; - }, - ); - const packageValues = new Map(packageValuesArray); - return packageValues.get(name) || null; - }} - onSelect={(device, javaPackage) => { - this.state.deviceAndPackageValues.set(name, { - device, - selectedPackage: javaPackage, - }); - this.props.configIsValidChanged(this._debugButtonShouldEnable()); - }} - /> - ); + return React.createElement(_DeviceAndPackage().DeviceAndPackage, { + targetUri: this.props.targetUri, + deserialize: () => { + let packageValuesArray = []; + (0, _DebuggerConfigSerializer().deserializeDebuggerConfig)(...this._getSerializationArgs(this.props), (transientSettings, savedSettings) => { + packageValuesArray = savedSettings.packageValues || []; + }); + const packageValues = new Map(packageValuesArray); + return packageValues.get(name) || null; + }, + onSelect: (device, javaPackage) => { + this.state.deviceAndPackageValues.set(name, { + device, + selectedPackage: javaPackage + }); + this.props.configIsValidChanged(this._debugButtonShouldEnable()); + } + }); } else if (type === 'deviceAndProcess') { - return ( - { - let processValuesArray: Array = []; - deserializeDebuggerConfig( - ...this._getSerializationArgs(this.props), - (transientSettings, savedSettings) => { - processValuesArray = - (savedSettings.processValues: Array) || []; - }, - ); - const processValues = new Map(processValuesArray); - return processValues.get(name) || null; - }} - onSelect={(device, javaProcess) => { - this.state.deviceAndProcessValues.set(name, { - device, - selectedProcess: javaProcess, - }); - this.props.configIsValidChanged(this._debugButtonShouldEnable()); - }} - /> - ); + return React.createElement(_DeviceAndProcess().DeviceAndProcess, { + targetUri: this.props.targetUri, + deserialize: () => { + let processValuesArray = []; + (0, _DebuggerConfigSerializer().deserializeDebuggerConfig)(...this._getSerializationArgs(this.props), (transientSettings, savedSettings) => { + processValuesArray = savedSettings.processValues || []; + }); + const processValues = new Map(processValuesArray); + return processValues.get(name) || null; + }, + onSelect: (device, javaProcess) => { + this.state.deviceAndProcessValues.set(name, { + device, + selectedProcess: javaProcess + }); + this.props.configIsValidChanged(this._debugButtonShouldEnable()); + } + }); } else if (type === 'selectSources') { - return ( -
      - {nameLabel} - { - let selectSourcesValuesArray: Array = []; - deserializeDebuggerConfig( - ...this._getSerializationArgs(this.props), - (transientSettings, savedSettings) => { - selectSourcesValuesArray = - (savedSettings.selectSourcesValues: Array) || - []; - }, - ); - const selectSourcesValues = new Map(selectSourcesValuesArray); - return selectSourcesValues.get(name) || null; - }} - onSelect={selectedSource => { - this.state.selectSourcesValues.set(name, selectedSource); - this.props.configIsValidChanged(this._debugButtonShouldEnable()); - }} - /> -
      - ); + return React.createElement("div", null, nameLabel, React.createElement(_SourceSelector().SourceSelector, { + deserialize: () => { + let selectSourcesValuesArray = []; + (0, _DebuggerConfigSerializer().deserializeDebuggerConfig)(...this._getSerializationArgs(this.props), (transientSettings, savedSettings) => { + selectSourcesValuesArray = savedSettings.selectSourcesValues || []; + }); + const selectSourcesValues = new Map(selectSourcesValuesArray); + return selectSourcesValues.get(name) || null; + }, + onSelect: selectedSource => { + this.state.selectSourcesValues.set(name, selectedSource); + this.props.configIsValidChanged(this._debugButtonShouldEnable()); + } + })); } - return ( -
      - -
      -
      - ); - } - render(): React.Node { - const {debuggerTypeName, config} = this.props; - return ( -
      - {config.header} - {this._getConfigurationProperties() - .filter(property => property.visible) - .map(property => ( -
      - {this._getComponentForProperty(property)} -
      - ))} -
      - ); + return React.createElement("div", null, React.createElement("label", null, "NO TRANSLATION YET FOR: ", (0, _string().capitalize)(name)), React.createElement("hr", null)); } - _handleDebugButtonClick = async (): Promise => { - const {targetUri, config} = this.props; + render() { const { - atomInputValues, - booleanValues, - enumValues, - processTableValues, - deviceAndPackageValues, - deviceAndProcessValues, - selectSourcesValues, - } = this.state; - const {launch, vsAdapterType, threads, getProcessName} = config; - - const stringValues = new Map(); - const stringArrayValues = new Map(); - const objectValues = new Map(); - const numberValues = new Map(); - const jsonValues = new Map(); - await Promise.all( - Array.from( - this._getConfigurationProperties() - .filter( - property => property.visible && atomInputValues.has(property.name), - ) - .map(async property => { - const {name, type} = property; - const itemType = idx(property, _ => _.itemType); - const value = atomInputValues.get(name) || ''; - if (type === 'path') { - try { - const resolvedPath = - this.props.pathResolver == null - ? value - : await this.props.pathResolver(targetUri, value); - stringValues.set(name, resolvedPath); - } catch (_) { - stringValues.set(name, value); - } - } else if (type === 'string') { - stringValues.set(name, value); - } else if (type === 'array' && itemType === 'string') { - stringArrayValues.set(name, shellParseWithGlobs(value)); - } else if (type === 'object') { - const objectValue = {}; - shellParseWithGlobs(value).forEach(variable => { - const [lhs, rhs] = variable.split('='); - objectValue[lhs] = rhs; - }); - objectValues.set(name, objectValue); - } else if (type === 'number') { - numberValues.set(name, Number(value)); - } else if (type === 'json') { - jsonValues.set(name, JSON.parse(value)); - } - - return value; - }), - ), - ); - - const packageValues = new Map(); - this._getConfigurationProperties() - .filter( - property => - property.visible && deviceAndPackageValues.has(property.name), - ) - .forEach(property => { - const deviceAndPackage = deviceAndPackageValues.get(property.name); - if (deviceAndPackage != null) { - packageValues.set(property.name, deviceAndPackage.selectedPackage); - } - }); - - const processValues = new Map(); - this._getConfigurationProperties() - .filter( - property => - property.visible && deviceAndProcessValues.has(property.name), - ) - .forEach(property => { - const deviceAndProcess = deviceAndProcessValues.get(property.name); - const processName = idx(deviceAndProcess, _ => _.selectedProcess.name); - if (deviceAndProcess != null && processName != null) { - processValues.set(property.name, processName); - } - }); - - const values = {}; - [ - booleanValues, - enumValues, - stringValues, - stringArrayValues, - objectValues, - numberValues, - processTableValues, - jsonValues, - deviceAndPackageValues, - deviceAndProcessValues, - selectSourcesValues, - ].forEach(map => { - map.forEach((value, key) => { - values[key] = value; - }); - }); - - this._getConfigurationProperties() - .filter( - property => !property.visible && !atomInputValues.has(property.name), - ) - .forEach(property => { - const {name} = property; - values[name] = idx(property, _ => _.defaultValue); - }); - - const debuggerService = await getDebuggerService(); - - debuggerService.startVspDebugging({ - targetUri, - debugMode: launch ? 'launch' : 'attach', - adapterType: vsAdapterType, - config: values, - showThreads: threads, - customControlButtons: [], - threadsComponentTitle: 'Threads', - customDisposable: new UniversalDisposable(), - processName: getProcessName(values), - }); + debuggerTypeName, + config + } = this.props; + return React.createElement("div", { + className: "block" + }, config.header, this._getConfigurationProperties().filter(property => property.visible).map(property => React.createElement("div", { + key: debuggerTypeName + ':' + property.name + }, this._getComponentForProperty(property)))); + } - serializeDebuggerConfig(...this._getSerializationArgs(this.props), { - atomInputValues: Array.from(atomInputValues), - booleanValues: Array.from(booleanValues), - enumValues: Array.from(enumValues), - packageValues: Array.from(packageValues), - processValues: Array.from(processValues), - selectSourcesValues: Array.from(selectSourcesValues), - }); - }; } + +exports.default = AutoGenLaunchAttachUiComponent; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/DebuggerConfigSerializer.js b/modules/nuclide-debugger-common/DebuggerConfigSerializer.js index 954c186b..355c1b78 100644 --- a/modules/nuclide-debugger-common/DebuggerConfigSerializer.js +++ b/modules/nuclide-debugger-common/DebuggerConfigSerializer.js @@ -1,3 +1,11 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.serializeDebuggerConfig = serializeDebuggerConfig; +exports.deserializeDebuggerConfig = deserializeDebuggerConfig; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,37 +14,27 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -/* global localStorage */ - -import type {DebuggerConfigAction} from './types'; +/* global localStorage */ // transientSettings will matinain configuration that should be persisted for the // duration of the current Nunclide session (so preserved across the configuration dialog // closing and re-opening), but not preserved if Nuclide is restarted. const transientSettings = {}; -function _getStorageKey( - host: string, - action: DebuggerConfigAction, - debuggerName: string, -) { +function _getStorageKey(host, action, debuggerName) { return 'NUCLIDE_DEBUGGER_CONFIG_' + host + '_' + action + '_' + debuggerName; } -export function serializeDebuggerConfig( - host: string, - action: DebuggerConfigAction, - debuggerName: string, - persistent: Object, - transient?: Object, -): void { +function serializeDebuggerConfig(host, action, debuggerName, persistent, transient) { if (global.localStorage == null) { throw new Error('localStorage is not available in this runtime'); } + const key = _getStorageKey(host, action, debuggerName); + localStorage.setItem(key, JSON.stringify(persistent)); if (transient == null) { @@ -46,19 +44,17 @@ export function serializeDebuggerConfig( } } -export function deserializeDebuggerConfig( - host: string, - action: DebuggerConfigAction, - debuggerName: string, - callback: (transientSettings: Object, persistentSettings: Object) => void, -): void { +function deserializeDebuggerConfig(host, action, debuggerName, callback) { if (global.localStorage == null) { throw new Error('localStorage is not available in this runtime'); } + const key = _getStorageKey(host, action, debuggerName); + const val = localStorage.getItem(key); + try { - const persistedSettings = val != null ? (JSON.parse(val): any) : {}; + const persistedSettings = val != null ? JSON.parse(val) : {}; callback(transientSettings[key] || {}, persistedSettings); } catch (err) {} -} +} \ No newline at end of file diff --git a/modules/nuclide-debugger-common/DebuggerLaunchAttachProvider.js b/modules/nuclide-debugger-common/DebuggerLaunchAttachProvider.js index 36916dbf..978c9ce4 100644 --- a/modules/nuclide-debugger-common/DebuggerLaunchAttachProvider.js +++ b/modules/nuclide-debugger-common/DebuggerLaunchAttachProvider.js @@ -1,3 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,45 +17,27 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {DebuggerConfigAction} from './types'; -import * as React from 'react'; - let uniqueKeySeed = 0; -export type callbacksForAction = { - isEnabled: () => Promise, - getComponent: ( - debuggerTypeName: string, - configIsValidChanged: (valid: boolean) => void, - defaultConfig: ?{[string]: mixed}, - ) => ?React.Element, -}; - /** * Base class of all launch/attach providers. * It allows each concrete provider to provide customized debugging types, actions and UI. */ -export default class DebuggerLaunchAttachProvider { - _debuggingTypeName: string; - _targetUri: NuclideUri; - _uniqueKey: number; - - constructor(debuggingTypeName: string, targetUri: NuclideUri) { +class DebuggerLaunchAttachProvider { + constructor(debuggingTypeName, targetUri) { this._debuggingTypeName = debuggingTypeName; this._targetUri = targetUri; this._uniqueKey = uniqueKeySeed++; } - getTabName(): string { + getTabName() { return this._debuggingTypeName; } - getCallbacksForAction(action: DebuggerConfigAction): callbacksForAction { + getCallbacksForAction(action) { return { /** * Whether this provider is enabled or not. @@ -56,19 +49,20 @@ export default class DebuggerLaunchAttachProvider { /** * Returns the UI component for configuring the specified debugger type and action. */ - getComponent: ( - debuggerTypeName: string, - configIsValidChanged: (valid: boolean) => void, - ) => { + getComponent: (debuggerTypeName, configIsValidChanged) => { throw new Error('abstract method'); - }, + } }; } - /** * Returns target uri for this provider. */ - getTargetUri(): NuclideUri { + + + getTargetUri() { return this._targetUri; } + } + +exports.default = DebuggerLaunchAttachProvider; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/DeviceAndPackage.js b/modules/nuclide-debugger-common/DeviceAndPackage.js index 72b414e8..e732219d 100644 --- a/modules/nuclide-debugger-common/DeviceAndPackage.js +++ b/modules/nuclide-debugger-common/DeviceAndPackage.js @@ -1,3 +1,64 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DeviceAndPackage = void 0; + +function _nuclideAdb() { + const data = require("../nuclide-adb"); + + _nuclideAdb = function () { + return data; + }; + + return data; +} + +function _Dropdown() { + const data = require("../nuclide-commons-ui/Dropdown"); + + _Dropdown = function () { + return data; + }; + + return data; +} + +function _LoadingSpinner() { + const data = require("../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +function _expected() { + const data = require("../nuclide-commons/expected"); + + _expected = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _AdbDeviceSelector() { + const data = require("./AdbDeviceSelector"); + + _AdbDeviceSelector = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,113 +67,81 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {AdbDevice} from 'nuclide-adb/lib/types'; -import type {Expected} from 'nuclide-commons/expected'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import {getAdbServiceByNuclideUri} from 'nuclide-adb'; -import {Dropdown} from 'nuclide-commons-ui/Dropdown'; -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; -import {Expect} from 'nuclide-commons/expected'; -import * as React from 'react'; -import {AdbDeviceSelector} from './AdbDeviceSelector'; - -type Props = {| - +targetUri: NuclideUri, - +onSelect: (device: ?AdbDevice, javaPackage: string) => void, - +deserialize: () => ?string, -|}; - -type State = { - selectedDevice: ?AdbDevice, - launchPackage: string, - packages: Expected>, -}; - -export class DeviceAndPackage extends React.Component { - constructor(props: Props) { +class DeviceAndPackage extends React.Component { + constructor(props) { super(props); + + this._handleDeviceChange = device => { + const state = { + selectedDevice: device, + packages: device == null ? _expected().Expect.value([]) : _expected().Expect.pending() + }; + const value = this.props.deserialize(); + + if (device != null && (this.state.selectedDevice == null || device.serial !== this.state.selectedDevice.serial) && value != null) { + state.launchPackage = value; + } + + this.setState(state, () => { + this._refreshPackageList(device); + }); + }; + this.state = { selectedDevice: null, launchPackage: '', - packages: Expect.value([]), + packages: _expected().Expect.value([]) }; } - async _refreshPackageList(device: ?AdbDevice) { + async _refreshPackageList(device) { if (device != null) { - const packages = Expect.value( - (await getAdbServiceByNuclideUri( - this.props.targetUri, - ).getInstalledPackages(device.serial)).sort(), - ); + const packages = _expected().Expect.value((await (0, _nuclideAdb().getAdbServiceByNuclideUri)(this.props.targetUri).getInstalledPackages(device.serial)).sort()); + this.setState({ - packages, + packages }); } else { this.setState({ - packages: Expect.value([]), + packages: _expected().Expect.value([]) }); } } - setState(partialState: Object, callback?: () => mixed): void { - const fullState: State = { - ...this.state, - ...partialState, - }; + setState(partialState, callback) { + const fullState = Object.assign({}, this.state, partialState); super.setState(fullState, () => { this.props.onSelect(fullState.selectedDevice, fullState.launchPackage); callback && callback(); }); } - _handleDeviceChange = (device: ?AdbDevice): void => { - const state: $Shape = { - selectedDevice: device, - packages: device == null ? Expect.value([]) : Expect.pending(), - }; - const value = this.props.deserialize(); - if ( - device != null && - (this.state.selectedDevice == null || - device.serial !== this.state.selectedDevice.serial) && - value != null - ) { - state.launchPackage = value; - } - - this.setState(state, () => { - this._refreshPackageList(device); - }); - }; - - render(): React.Node { - return ( -
      - - - - {this.state.packages.isPending ? ( - - ) : ( - { - return {value: packageName, label: packageName}; - })} - onChange={value => this.setState({launchPackage: value})} - value={this.state.launchPackage} - /> - )} -
      - ); + render() { + return React.createElement("div", { + className: "block" + }, React.createElement("label", null, "Device:"), React.createElement(_AdbDeviceSelector().AdbDeviceSelector, { + onChange: this._handleDeviceChange, + targetUri: this.props.targetUri + }), React.createElement("label", null, "Package: "), this.state.packages.isPending ? React.createElement(_LoadingSpinner().LoadingSpinner, { + size: "EXTRA_SMALL" + }) : React.createElement(_Dropdown().Dropdown, { + disabled: this.state.selectedDevice == null, + options: this.state.packages.getOrDefault([]).map(packageName => { + return { + value: packageName, + label: packageName + }; + }), + onChange: value => this.setState({ + launchPackage: value + }), + value: this.state.launchPackage + })); } + } + +exports.DeviceAndPackage = DeviceAndPackage; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/DeviceAndProcess.js b/modules/nuclide-debugger-common/DeviceAndProcess.js index 0983be16..2df07e9b 100644 --- a/modules/nuclide-debugger-common/DeviceAndProcess.js +++ b/modules/nuclide-debugger-common/DeviceAndProcess.js @@ -1,3 +1,88 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DeviceAndProcess = void 0; + +function _nuclideAdb() { + const data = require("../nuclide-adb"); + + _nuclideAdb = function () { + return data; + }; + + return data; +} + +function _LoadingSpinner() { + const data = require("../nuclide-commons-ui/LoadingSpinner"); + + _LoadingSpinner = function () { + return data; + }; + + return data; +} + +function _Table() { + const data = require("../nuclide-commons-ui/Table"); + + _Table = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _expected() { + const data = require("../nuclide-commons/expected"); + + _expected = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _AdbDeviceSelector() { + const data = require("./AdbDeviceSelector"); + + _AdbDeviceSelector = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,53 +91,85 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {AdbDevice, AndroidJavaProcess} from 'nuclide-adb/lib/types'; -import type {Column, Row} from 'nuclide-commons-ui/Table'; -import type {Expected} from 'nuclide-commons/expected'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {Subscription} from 'rxjs'; - -import idx from 'idx'; -import {getAdbServiceByNuclideUri} from 'nuclide-adb'; -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; -import {Table} from 'nuclide-commons-ui/Table'; -import {arrayEqual} from 'nuclide-commons/collection'; -import {Expect} from 'nuclide-commons/expected'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import {Observable} from 'rxjs'; -import {AdbDeviceSelector} from './AdbDeviceSelector'; - -type ColumnName = 'pid' | 'user' | 'name'; - -type Props = {| - +targetUri: NuclideUri, - +onSelect: (device: ?AdbDevice, javaProcess: ?AndroidJavaProcess) => void, - +deserialize: () => ?string, -|}; - -type State = { - selectedDevice: ?AdbDevice, - javaProcesses: Expected>, - selectedProcess: ?AndroidJavaProcess, - selectedProcessName: ?string, - sortedColumn: ?ColumnName, - sortDescending: boolean, -}; - -export class DeviceAndProcess extends React.Component { - _disposables: UniversalDisposable; - _javaProcessSubscription: ?Subscription; - - constructor(props: Props) { +class DeviceAndProcess extends React.Component { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); + this._handleDeviceChange = device => { + const oldDevice = this.state.selectedDevice; + + if (oldDevice != null && device != null && oldDevice.serial === device.serial) { + // Same device selected. + return; + } + + if (this._javaProcessSubscription != null) { + this._javaProcessSubscription.unsubscribe(); + + this._javaProcessSubscription = null; + } + + this.setState({ + selectedDevice: device, + javaProcesses: device == null ? _expected().Expect.value([]) : _expected().Expect.pending(), + selectedProcess: null, + selectedProcessName: this.props.deserialize() + }); + + if (device != null) { + // If a device is selected, observe the Java processes on the device. + const adbService = (0, _nuclideAdb().getAdbServiceByNuclideUri)(this.props.targetUri); + this._javaProcessSubscription = _RxMin.Observable.interval(2000).startWith(0).switchMap(() => adbService.getJavaProcesses(device.serial).refCount()).distinctUntilChanged((a, b) => (0, _collection().arrayEqual)(a, b, (x, y) => { + return x.user === y.user && x.pid === y.pid && x.name === y.name; + })).subscribe(javaProcesses => { + this._javaProcessListChanged(_expected().Expect.value(javaProcesses)); + }); + } + }; + + this._handleSort = (sortedColumn, sortDescending) => { + this.setState({ + sortedColumn, + sortDescending + }); + }; + + this._sortRows = (processes, sortedColumnName, sortDescending) => { + if (sortedColumnName == null) { + return processes; + } // Use a numerical comparison for the pid column, string compare for all the others. + + + const compare = sortedColumnName === 'pid' ? (a, b, isAsc) => { + const cmp = (a || 0) - (b || 0); + return isAsc ? cmp : -cmp; + } : (a, b, isAsc) => { + const cmp = String(a).toLowerCase().localeCompare(String(b).toLowerCase()); + return isAsc ? cmp : -cmp; + }; + + const getter = row => row.data[sortedColumnName]; + + return [...processes].sort((a, b) => { + return compare(getter(a), getter(b), !sortDescending); + }); + }; + + this._handleSelectTableRow = (item, selectedIndex) => { + var _ref; + + this.setState({ + selectedProcess: item, + selectedProcessName: (_ref = item) != null ? _ref.name : _ref + }); + }; + + this._disposables = new (_UniversalDisposable().default)(); this._javaProcessSubscription = null; + this._disposables.add(() => { if (this._javaProcessSubscription != null) { this._javaProcessSubscription.unsubscribe(); @@ -61,11 +178,11 @@ export class DeviceAndProcess extends React.Component { this.state = { selectedDevice: null, - javaProcesses: Expect.value([]), + javaProcesses: _expected().Expect.value([]), selectedProcess: null, selectedProcessName: null, sortedColumn: 'name', - sortDescending: false, + sortDescending: false }; } @@ -73,219 +190,96 @@ export class DeviceAndProcess extends React.Component { this._disposables.dispose(); } - setState(partialState: Object, callback?: () => mixed): void { - const fullState: State = { - ...this.state, - ...partialState, - }; + setState(partialState, callback) { + const fullState = Object.assign({}, this.state, partialState); super.setState(fullState, () => { this.props.onSelect(fullState.selectedDevice, fullState.selectedProcess); callback && callback(); }); } - _handleDeviceChange = (device: ?AdbDevice): void => { - const oldDevice = this.state.selectedDevice; - if ( - oldDevice != null && - device != null && - oldDevice.serial === device.serial - ) { - // Same device selected. - return; - } - - if (this._javaProcessSubscription != null) { - this._javaProcessSubscription.unsubscribe(); - this._javaProcessSubscription = null; - } - - this.setState({ - selectedDevice: device, - javaProcesses: device == null ? Expect.value([]) : Expect.pending(), - selectedProcess: null, - selectedProcessName: this.props.deserialize(), - }); - - if (device != null) { - // If a device is selected, observe the Java processes on the device. - const adbService = getAdbServiceByNuclideUri(this.props.targetUri); - this._javaProcessSubscription = Observable.interval(2000) - .startWith(0) - .switchMap(() => adbService.getJavaProcesses(device.serial).refCount()) - .distinctUntilChanged((a, b) => - arrayEqual(a, b, (x, y) => { - return x.user === y.user && x.pid === y.pid && x.name === y.name; - }), - ) - .subscribe(javaProcesses => { - this._javaProcessListChanged(Expect.value(javaProcesses)); - }); - } - }; - - _javaProcessListChanged(javaProcesses: Expected>) { - const selectedPid = - this.state.selectedProcess == null - ? null - : this.state.selectedProcess.pid; - let selectedProcess = javaProcesses - .getOrDefault([]) - .find(process => process.pid === selectedPid); + _javaProcessListChanged(javaProcesses) { + const selectedPid = this.state.selectedProcess == null ? null : this.state.selectedProcess.pid; + let selectedProcess = javaProcesses.getOrDefault([]).find(process => process.pid === selectedPid); if (this.state.selectedProcessName != null) { - selectedProcess = javaProcesses - .getOrDefault([]) - .find(process => process.name === this.state.selectedProcessName); + selectedProcess = javaProcesses.getOrDefault([]).find(process => process.name === this.state.selectedProcessName); } this.setState({ javaProcesses, selectedProcess, - selectedProcessName: - selectedProcess == null ? null : selectedProcess.name, + selectedProcessName: selectedProcess == null ? null : selectedProcess.name }); } - _getColumns(): Array> { - return [ - { - key: 'pid', - title: 'PID', - width: 0.1, - }, - { - key: 'user', - title: 'User', - width: 0.1, - }, - { - key: 'name', - title: 'Name', - width: 0.8, - }, - ]; + _getColumns() { + return [{ + key: 'pid', + title: 'PID', + width: 0.1 + }, { + key: 'user', + title: 'User', + width: 0.1 + }, { + key: 'name', + title: 'Name', + width: 0.8 + }]; } - _handleSort = (sortedColumn: ?ColumnName, sortDescending: boolean): void => { - this.setState({sortedColumn, sortDescending}); - }; + render() { + const emptyMessage = this.state.selectedDevice == null ? 'No device selected' : 'No debuggable Java processes found!'; - _sortRows = ( - processes: Array>, - sortedColumnName: ?ColumnName, - sortDescending: boolean, - ): Array> => { - if (sortedColumnName == null) { - return processes; - } - - // Use a numerical comparison for the pid column, string compare for all the others. - const compare: any = - sortedColumnName === 'pid' - ? (a: ?number, b: ?number, isAsc: boolean): number => { - const cmp = (a || 0) - (b || 0); - return isAsc ? cmp : -cmp; - } - : (a: ?string, b: ?string, isAsc: boolean): number => { - const cmp = String(a) - .toLowerCase() - .localeCompare(String(b).toLowerCase()); - return isAsc ? cmp : -cmp; - }; - - const getter = row => row.data[sortedColumnName]; - return [...processes].sort((a, b) => { - return compare(getter(a), getter(b), !sortDescending); - }); - }; - - _handleSelectTableRow = ( - item: ?AndroidJavaProcess, - selectedIndex: number, - ): void => { - this.setState({ - selectedProcess: item, - selectedProcessName: idx(item, _ => _.name), - }); - }; - - render(): React.Node { - const emptyMessage: string = - this.state.selectedDevice == null - ? 'No device selected' - : 'No debuggable Java processes found!'; - const emptyComponent = () => ( -
      - {this.state.javaProcesses.isPending ? ( - - ) : ( - emptyMessage - )} -
      - ); + const emptyComponent = () => React.createElement("div", null, this.state.javaProcesses.isPending ? React.createElement(_LoadingSpinner().LoadingSpinner, { + size: "EXTRA_SMALL" + }) : emptyMessage); let shouldHighlightRow = _ => false; + try { // $FlowFB - shouldHighlightRow = require('./fb-isFBProcessName').isFBProcessName; + shouldHighlightRow = require("./fb-isFBProcessName").isFBProcessName; } catch (e) {} - const processListRows = this._sortRows( - this.state.javaProcesses.getOrDefault([]).map(processRow => { - const data = { - pid: processRow.pid, - user: processRow.user, - name: processRow.name, - }; - const highlightRow = shouldHighlightRow(processRow.name); - return { - data, - className: highlightRow - ? 'device-and-process-table-highlight-row' - : undefined, - }; - }), - this.state.sortedColumn, - this.state.sortDescending, - ); - - const selectedRows = - this.state.selectedProcess == null - ? [] - : processListRows.filter( - row => - this.state.selectedProcess == null || - (row.data.pid === this.state.selectedProcess.pid && - row.data.name === this.state.selectedProcess.name), - ); - const selectedRowIndex = - selectedRows.length === 1 ? processListRows.indexOf(selectedRows[0]) : -1; - - return ( -
      - - - -
    - - ); + const processListRows = this._sortRows(this.state.javaProcesses.getOrDefault([]).map(processRow => { + const data = { + pid: processRow.pid, + user: processRow.user, + name: processRow.name + }; + const highlightRow = shouldHighlightRow(processRow.name); + return { + data, + className: highlightRow ? 'device-and-process-table-highlight-row' : undefined + }; + }), this.state.sortedColumn, this.state.sortDescending); + + const selectedRows = this.state.selectedProcess == null ? [] : processListRows.filter(row => this.state.selectedProcess == null || row.data.pid === this.state.selectedProcess.pid && row.data.name === this.state.selectedProcess.name); + const selectedRowIndex = selectedRows.length === 1 ? processListRows.indexOf(selectedRows[0]) : -1; + return React.createElement("div", { + className: "block" + }, React.createElement("label", null, "Device:"), React.createElement(_AdbDeviceSelector().AdbDeviceSelector, { + onChange: this._handleDeviceChange, + targetUri: this.props.targetUri + }), React.createElement("label", null, "Debuggable Java processes: "), React.createElement(_Table().Table, { + collapsable: false, + columns: this._getColumns(), + emptyComponent: emptyComponent, + fixedHeader: true, + maxBodyHeight: "30em", + rows: processListRows, + sortable: true, + onSort: this._handleSort, + sortedColumn: this.state.sortedColumn, + sortDescending: this.state.sortDescending, + selectable: true, + selectedIndex: selectedRowIndex, + onSelect: this._handleSelectTableRow + })); } + } + +exports.DeviceAndProcess = DeviceAndProcess; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/SelectableFilterableProcessTable.js b/modules/nuclide-debugger-common/SelectableFilterableProcessTable.js index 986afb19..fb2632bf 100644 --- a/modules/nuclide-debugger-common/SelectableFilterableProcessTable.js +++ b/modules/nuclide-debugger-common/SelectableFilterableProcessTable.js @@ -1,3 +1,68 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var React = _interopRequireWildcard(require("react")); + +function _debugAdapterService() { + const data = require("./debug-adapter-service"); + + _debugAdapterService = function () { + return data; + }; + + return data; +} + +function _AtomInput() { + const data = require("../nuclide-commons-ui/AtomInput"); + + _AtomInput = function () { + return data; + }; + + return data; +} + +function _Table() { + const data = require("../nuclide-commons-ui/Table"); + + _Table = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,206 +71,159 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {Column} from 'nuclide-commons-ui/Table'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {ProcessInfo} from 'nuclide-commons/process'; -import * as React from 'react'; - -import {getVSCodeDebuggerAdapterServiceByNuclideUri} from './debug-adapter-service'; -import {AtomInput} from 'nuclide-commons-ui/AtomInput'; -import {Table} from 'nuclide-commons-ui/Table'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Observable} from 'rxjs'; - const PROCESS_UPDATES_INTERVAL_MS = 2000; +const COLUMNS = [{ + title: 'Process Binary', + key: 'process', + width: 0.25 +}, { + title: 'PID', + key: 'pid', + width: 0.1 +}, { + title: 'Command', + key: 'command', + width: 0.65 +}]; -const COLUMNS: Array> = [ - { - title: 'Process Binary', - key: 'process', - width: 0.25, - }, - { - title: 'PID', - key: 'pid', - width: 0.1, - }, - { - title: 'Command', - key: 'command', - width: 0.65, - }, -]; - -type ColumnName = 'process' | 'pid' | 'command'; - -type ProcessRow = { - process: string, - pid: number, - command: string, -}; - -type Props = {| - +targetUri: NuclideUri, - +onSelect?: (selectedProcess: ?ProcessRow) => mixed, -|}; - -type State = { - processList: Array, - selectedProcess: ?ProcessRow, - sortDescending: boolean, - sortedColumn: ?ColumnName, - filterText: string, -}; - -function getCompareFunction( - sortedColumn: ?ColumnName, - sortDescending: boolean, -): (a: ProcessRow, b: ProcessRow) => number { +function getCompareFunction(sortedColumn, sortDescending) { switch (sortedColumn) { case 'process': - return (target1: ProcessRow, target2: ProcessRow) => { + return (target1, target2) => { const first = sortDescending ? target2.process : target1.process; const second = sortDescending ? target1.process : target2.process; return first.toLowerCase().localeCompare(second.toLowerCase()); }; + case 'pid': const order = sortDescending ? -1 : 1; - return (target1: ProcessRow, target2: ProcessRow) => - order * (target1.pid - target2.pid); + return (target1, target2) => order * (target1.pid - target2.pid); + case 'command': - return (target1: ProcessRow, target2: ProcessRow) => { + return (target1, target2) => { const first = sortDescending ? target2.command : target1.command; const second = sortDescending ? target1.command : target2.command; return first.toLowerCase().localeCompare(second.toLowerCase()); }; + default: break; } + return () => 0; } -function filterProcesses(processes: Array, filterText: string) { +function filterProcesses(processes, filterText) { // Show all results if invalid regex let filterRegex; + try { filterRegex = new RegExp(filterText, 'i'); } catch (e) { return processes; } - return processes.filter( - item => - filterRegex.test(item.process) || - filterRegex.test(item.pid.toString()) || - filterRegex.test(item.command), - ); -} -export default class SelectableFilterableProcessTable extends React.Component< - Props, - State, -> { - _disposables: UniversalDisposable; + return processes.filter(item => filterRegex.test(item.process) || filterRegex.test(item.pid.toString()) || filterRegex.test(item.command)); +} - constructor(props: Props) { +class SelectableFilterableProcessTable extends React.Component { + constructor(props) { super(props); - this._disposables = new UniversalDisposable(); + + this._updateList = processes => { + // On Linux, process names for which only a name is available + // are denoted as [name] in the commandWithArgs field. These + // names often do not play well with basename (in particular, + // some of the contain literal slashes) so handle them as a special + // case. + const noargsRegex = /^\[(.*)\]$/; + + const commandName = (name, withArgs) => { + const match = withArgs.match(noargsRegex); + + if (match != null) { + return match[1]; + } + + return _nuclideUri().default.basename(name); + }; + + const processList = processes.map(process => { + return { + process: commandName(process.command, process.commandWithArgs), + pid: process.pid, + command: process.commandWithArgs + }; + }); + this.setState({ + processList + }); + }; + + this._handleFilterTextChange = filterText => { + // Check if we've filtered down to one option and select if so + const filteredProcesses = filterProcesses(this.state.processList, filterText); // TODO: (wbinnssmith) T30771435 this setState depends on current state + // and should use an updater function rather than an object + // eslint-disable-next-line react/no-access-state-in-setstate + + let selectedProcess = this.state.selectedProcess; + + if (filteredProcesses.length === 1) { + // Check if we've filtered down to one option and select if so + selectedProcess = filteredProcesses[0]; + } else if (filteredProcesses.findIndex(processRow => selectedProcess != null && selectedProcess.pid === processRow.pid) === -1) { + // If we filter out our current selection, + // set our current selection to null + selectedProcess = null; + } + + this.setState({ + filterText, + selectedProcess + }); + }; + + this._handleSelectTableRow = (selectedProcess, selectedIndex) => { + this.setState({ + selectedProcess + }); + }; + + this._handleSort = (sortedColumn, sortDescending) => { + this.setState({ + sortedColumn, + sortDescending + }); + }; + + this._disposables = new (_UniversalDisposable().default)(); this.state = { processList: [], selectedProcess: null, sortDescending: false, sortedColumn: null, - filterText: '', + filterText: '' }; } - componentDidMount(): void { - this._disposables.add( - Observable.interval(PROCESS_UPDATES_INTERVAL_MS) - .startWith(0) - .flatMap(_ => - getVSCodeDebuggerAdapterServiceByNuclideUri( - this.props.targetUri, - ).getProcessTree(), - ) - .subscribe(this._updateList), - ); + componentDidMount() { + this._disposables.add(_RxMin.Observable.interval(PROCESS_UPDATES_INTERVAL_MS).startWith(0).flatMap(_ => (0, _debugAdapterService().getVSCodeDebuggerAdapterServiceByNuclideUri)(this.props.targetUri).getProcessTree()).subscribe(this._updateList)); } componentWillUnmount() { this._disposables.dispose(); } - _updateList = (processes: Array): void => { - // On Linux, process names for which only a name is available - // are denoted as [name] in the commandWithArgs field. These - // names often do not play well with basename (in particular, - // some of the contain literal slashes) so handle them as a special - // case. - const noargsRegex = /^\[(.*)\]$/; - const commandName = (name, withArgs) => { - const match = withArgs.match(noargsRegex); - if (match != null) { - return match[1]; - } - return nuclideUri.basename(name); - }; - - const processList = processes.map(process => { - return { - process: commandName(process.command, process.commandWithArgs), - pid: process.pid, - command: process.commandWithArgs, - }; - }); - - this.setState({processList}); - }; - - _handleFilterTextChange = (filterText: string): void => { - // Check if we've filtered down to one option and select if so - const filteredProcesses = filterProcesses( - this.state.processList, - filterText, - ); - // TODO: (wbinnssmith) T30771435 this setState depends on current state - // and should use an updater function rather than an object - // eslint-disable-next-line react/no-access-state-in-setstate - let selectedProcess = this.state.selectedProcess; - if (filteredProcesses.length === 1) { - // Check if we've filtered down to one option and select if so - selectedProcess = filteredProcesses[0]; - } else if ( - filteredProcesses.findIndex( - processRow => - selectedProcess != null && selectedProcess.pid === processRow.pid, - ) === -1 - ) { - // If we filter out our current selection, - // set our current selection to null - selectedProcess = null; - } - - this.setState({ - filterText, - selectedProcess, - }); - }; - - setState(newState: Object): void { - const onSelect = - this.props.onSelect != null ? this.props.onSelect : _ => {}; - + setState(newState) { + const onSelect = this.props.onSelect != null ? this.props.onSelect : _ => {}; let changedSelectedProcess = false; + if (newState.selectedProcess != null) { if (this.state.selectedProcess != null) { - changedSelectedProcess = - newState.selectedProcess.pid !== this.state.selectedProcess.pid; + changedSelectedProcess = newState.selectedProcess.pid !== this.state.selectedProcess.pid; } else { changedSelectedProcess = true; } @@ -221,70 +239,51 @@ export default class SelectableFilterableProcessTable extends React.Component< }); } - _handleSelectTableRow = ( - selectedProcess: ProcessRow, - selectedIndex: number, - ): void => { - this.setState({selectedProcess}); - }; - - _handleSort = (sortedColumn: ColumnName, sortDescending: boolean): void => { - this.setState({ - sortedColumn, - sortDescending, - }); - }; - - render(): React.Node { + render() { const { processList, sortedColumn, sortDescending, selectedProcess, - filterText, + filterText } = this.state; const sortFunction = getCompareFunction(sortedColumn, sortDescending); let selectedIndex = null; + const rows = filterProcesses(processList, filterText).sort(sortFunction).map((process, index) => { + const row = { + data: process + }; - const rows = filterProcesses(processList, filterText) - .sort(sortFunction) - .map((process, index) => { - const row = { - data: process, - }; - - if (selectedProcess != null && selectedProcess.pid === process.pid) { - selectedIndex = index; - } - - return row; - }); + if (selectedProcess != null && selectedProcess.pid === process.pid) { + selectedIndex = index; + } - return ( -
    -

    Attach to a running native process

    - -
    - - ); + return row; + }); + return React.createElement("div", { + className: "block" + }, React.createElement("p", null, "Attach to a running native process"), React.createElement(_AtomInput().AtomInput, { + placeholderText: "Search...", + value: this.state.filterText, + onDidChange: this._handleFilterTextChange, + size: "sm", + autofocus: true + }), React.createElement(_Table().Table, { + columns: COLUMNS, + fixedHeader: true, + maxBodyHeight: "30em", + rows: rows, + sortable: true, + onSort: this._handleSort, + sortedColumn: this.state.sortedColumn, + sortDescending: this.state.sortDescending, + selectable: true, + selectedIndex: selectedIndex, + onSelect: this._handleSelectTableRow, + collapsable: true + })); } + } + +exports.default = SelectableFilterableProcessTable; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/SourceSelector.js b/modules/nuclide-debugger-common/SourceSelector.js index 264ac7e3..3af62f35 100644 --- a/modules/nuclide-debugger-common/SourceSelector.js +++ b/modules/nuclide-debugger-common/SourceSelector.js @@ -1,3 +1,56 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SourceSelector = void 0; + +function _Dropdown() { + const data = require("../nuclide-commons-ui/Dropdown"); + + _Dropdown = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var React = _interopRequireWildcard(require("react")); + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,139 +59,106 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +class SourceSelector extends React.Component { + constructor(props) { + super(props); -import type {SuggestedProjectPath} from 'atom-ide-debugger-java/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import idx from 'idx'; -import {Dropdown} from 'nuclide-commons-ui/Dropdown'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import {observeProjectPathsAllFromSourcePathsService} from './utils'; - -type Props = {| - +onSelect: (selectedSource: ?NuclideUri) => void, - +deserialize: () => ?string, -|}; - -type State = { - selectableSources: Array, - selectedSource: ?SuggestedProjectPath, -}; + this._sourceToOption = source => { + const label = this._getLabelFromSource(source); -export class SourceSelector extends React.Component { - _disposables: UniversalDisposable; + return { + value: source, + label, + selectedLabel: label + }; + }; - constructor(props: Props) { - super(props); - this._disposables = new UniversalDisposable(); - this.state = {selectableSources: [], selectedSource: null}; + this._disposables = new (_UniversalDisposable().default)(); + this.state = { + selectableSources: [], + selectedSource: null + }; } - _getNewlySelectedSource( - selectedSource: ?SuggestedProjectPath, - projectPaths: Array, - deserializedProjectPath: ?string, - ): ?SuggestedProjectPath { + _getNewlySelectedSource(selectedSource, projectPaths, deserializedProjectPath) { let newSelectedSource = null; + if (selectedSource != null) { - newSelectedSource = projectPaths.includes(selectedSource) - ? selectedSource - : null; + newSelectedSource = projectPaths.includes(selectedSource) ? selectedSource : null; } + if (newSelectedSource == null && projectPaths.length > 0) { - const matches = projectPaths.filter( - projectPath => projectPath.projectPath === deserializedProjectPath, - ); + const matches = projectPaths.filter(projectPath => projectPath.projectPath === deserializedProjectPath); newSelectedSource = matches.length > 0 ? matches[0] : projectPaths[0]; } + return newSelectedSource; } componentDidMount() { - this._disposables.add( - observeProjectPathsAllFromSourcePathsService( - (projectPaths: Array) => { - const newSelectedSource = this._getNewlySelectedSource( - // TODO: (wbinnssmith) T30771435 this setState depends on current state - // and should use an updater function rather than an object - // eslint-disable-next-line react/no-access-state-in-setstate - this.state.selectedSource, - projectPaths, - this.props.deserialize(), - ); - this.setState({ - selectableSources: projectPaths, - selectedSource: newSelectedSource, - }); - }, - ), - ); + this._disposables.add((0, _utils().observeProjectPathsAllFromSourcePathsService)(projectPaths => { + const newSelectedSource = this._getNewlySelectedSource( // TODO: (wbinnssmith) T30771435 this setState depends on current state + // and should use an updater function rather than an object + // eslint-disable-next-line react/no-access-state-in-setstate + this.state.selectedSource, projectPaths, this.props.deserialize()); + + this.setState({ + selectableSources: projectPaths, + selectedSource: newSelectedSource + }); + })); } componentWillUnmount() { this._disposables.dispose(); } - _getLabelFromSource(source: SuggestedProjectPath) { - const {projectPath, hostLabel} = source; - const basename = nuclideUri.basename(projectPath); + _getLabelFromSource(source) { + const { + projectPath, + hostLabel + } = source; + + const basename = _nuclideUri().default.basename(projectPath); + return hostLabel + ' - ' + basename; } - _sourceToOption = (source: SuggestedProjectPath) => { - const label = this._getLabelFromSource(source); - return { - value: source, - label, - selectedLabel: label, - }; - }; - - setState(partialState: Object, callback?: () => mixed): void { - const fullState: State = { - ...this.state, - ...partialState, - }; + setState(partialState, callback) { + const fullState = Object.assign({}, this.state, partialState); super.setState(fullState, () => { - this.props.onSelect(idx(fullState, _ => _.selectedSource.projectPath)); + var _ref; + + this.props.onSelect((_ref = fullState) != null ? (_ref = _ref.selectedSource) != null ? _ref.projectPath : _ref : _ref); callback && callback(); }); } - render(): React.Node { - const {selectableSources, selectedSource} = this.state; + render() { + const { + selectableSources, + selectedSource + } = this.state; const options = selectableSources.map(this._sourceToOption); + if (options.length === 0) { - return ( -
    - No Projects Found. Please add a project to your file tree so the - debugger can find sources. -
    - ); + return React.createElement("div", null, "No Projects Found. Please add a project to your file tree so the debugger can find sources."); } - const potentiallyWrongSourceLabel = - selectedSource != null && !selectedSource.suggested ? ( - - ) : null; - return ( -
    - this.setState({selectedSource: option})} - placeholder={'Select a source'} - value={selectedSource} - /> - {potentiallyWrongSourceLabel} -
    - ); + + const potentiallyWrongSourceLabel = selectedSource != null && !selectedSource.suggested ? React.createElement("label", null, "Nuclide is not sure that you have selected a project which contains sources the debugger can use. Please double check that your selected source is correct.") : null; + return React.createElement("div", null, React.createElement(_Dropdown().Dropdown, { + options: options, + onChange: option => this.setState({ + selectedSource: option + }), + placeholder: 'Select a source', + value: selectedSource + }), potentiallyWrongSourceLabel); } + } + +exports.SourceSelector = SourceSelector; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/V8Protocol.js b/modules/nuclide-debugger-common/V8Protocol.js index 4cfd95ad..82f40a7a 100644 --- a/modules/nuclide-debugger-common/V8Protocol.js +++ b/modules/nuclide-debugger-common/V8Protocol.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function DebugProtocol() { + const data = _interopRequireWildcard(require("vscode-debugprotocol")); + + DebugProtocol = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,35 +25,16 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {MessageProcessor} from './types'; -import * as DebugProtocol from 'vscode-debugprotocol'; - const TWO_CRLF = '\r\n\r\n'; - /** * JSON-RPC protocol implementation over a read and write buffers. */ -export default class V8Protocol { - _id: string; - _output: (input: string) => mixed; - _sequence: number; - _pendingRequests: Map void>; - _rawData: Buffer; - _contentLength: number; - _logger: log4js$Logger; - _sendPreprocessors: MessageProcessor[]; - _receivePreprocessors: MessageProcessor[]; - - constructor( - id: string, - logger: log4js$Logger, - sendPreprocessors: MessageProcessor[], - receivePreprocessors: MessageProcessor[], - ) { + +class V8Protocol { + constructor(id, logger, sendPreprocessors, receivePreprocessors) { this._id = id; this._logger = logger; this._sendPreprocessors = sendPreprocessors; @@ -45,30 +45,27 @@ export default class V8Protocol { this._rawData = new Buffer(0); } - getId(): string { + getId() { return this._id; } - onServerError(error: Error): void { + onServerError(error) { throw new Error('No implementation found!'); } - onEvent(event: DebugProtocol.Event): void { + onEvent(event) { throw new Error('No implementation found!'); } - async dispatchRequest( - request: DebugProtocol.Request, - response: DebugProtocol.Response, - ): Promise { + async dispatchRequest(request, response) { throw new Error('No implementation found!'); } - setOutput(output: (input: string) => mixed): void { + setOutput(output) { this._output = output; } - send(command: string, args: any): Promise { + send(command, args) { return new Promise((resolve, reject) => { this._doSend(command, args, result => { if (result.success) { @@ -80,26 +77,19 @@ export default class V8Protocol { }); } - sendResponse(response: DebugProtocol.Response): void { + sendResponse(response) { if (response.seq > 0) { - this._logger.error( - `attempt to send more than one response for command ${ - response.command - }`, - ); + this._logger.error(`attempt to send more than one response for command ${response.command}`); } else { this._sendMessage('response', response); } } - _doSend( - command: string, - args: any, - clb: (result: DebugProtocol.Response) => void, - ): void { - const request: any = { - command, + _doSend(command, args, clb) { + const request = { + command }; + if (args && Object.keys(args).length > 0) { request.arguments = args; } @@ -112,42 +102,43 @@ export default class V8Protocol { } } - _sendMessage( - typ: 'request' | 'response' | 'event', - message: DebugProtocol.ProtocolMessage, - ): void { - message.type = (typ: any); + _sendMessage(typ, message) { + message.type = typ; message.seq = this._sequence++; this._sendPreprocessors.forEach(processor => processor(message)); + const json = JSON.stringify(message); const length = Buffer.byteLength(json, 'utf8'); this._output('Content-Length: ' + length.toString() + TWO_CRLF + json); } - handleData(data: Buffer): void { + handleData(data) { this._rawData = Buffer.concat([this._rawData, data]); + while (true) { if (this._contentLength >= 0) { if (this._rawData.length >= this._contentLength) { - const message = this._rawData.toString( - 'utf8', - 0, - this._contentLength, - ); + const message = this._rawData.toString('utf8', 0, this._contentLength); + this._rawData = this._rawData.slice(this._contentLength); this._contentLength = -1; + if (message.length > 0) { this._dispatch(message); } + continue; // there may be more complete messages to process } } else { const s = this._rawData.toString('utf8', 0, this._rawData.length); + const idx = s.indexOf(TWO_CRLF); + if (idx !== -1) { const match = /Content-Length: (\d+)/.exec(s); + if (match && match[1]) { this._contentLength = Number(match[1]); this._rawData = this._rawData.slice(idx + TWO_CRLF.length); @@ -155,35 +146,43 @@ export default class V8Protocol { } } } + break; } } - _dispatch(body: string): void { + _dispatch(body) { try { const rawData = JSON.parse(body); + this._receivePreprocessors.forEach(processor => processor(rawData)); switch (rawData.type) { case 'event': - this.onEvent((rawData: DebugProtocol.Event)); + this.onEvent(rawData); break; + case 'response': - const response: DebugProtocol.Response = rawData; + const response = rawData; + const clb = this._pendingRequests.get(response.request_seq); + if (clb) { this._pendingRequests.delete(response.request_seq); + clb(response); } + break; + case 'request': - const request: DebugProtocol.Request = rawData; - const resp: DebugProtocol.Response = { + const request = rawData; + const resp = { type: 'response', seq: 0, command: request.command, request_seq: request.seq, - success: true, + success: true }; this.dispatchRequest(request, resp); break; @@ -192,4 +191,7 @@ export default class V8Protocol { this.onServerError(new Error(e.message || e)); } } + } + +exports.default = V8Protocol; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/VSCodeDebuggerAdapterService.js b/modules/nuclide-debugger-common/VSCodeDebuggerAdapterService.js index f96eac4b..2be08c02 100644 --- a/modules/nuclide-debugger-common/VSCodeDebuggerAdapterService.js +++ b/modules/nuclide-debugger-common/VSCodeDebuggerAdapterService.js @@ -1,3 +1,68 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.createVsRawAdapterSpawnerService = createVsRawAdapterSpawnerService; +exports.getProcessTree = getProcessTree; +exports.getBuckRootFromUri = getBuckRootFromUri; +exports.getBuckRootFromPid = getBuckRootFromPid; +exports.realpath = realpath; +exports.getAdapterExecutableInfo = getAdapterExecutableInfo; +exports.VsRawAdapterSpawnerService = void 0; + +function _process() { + const data = require("../nuclide-commons/process"); + + _process = function () { + return data; + }; + + return data; +} + +function _VsAdapterSpawner() { + const data = _interopRequireDefault(require("./VsAdapterSpawner")); + + _VsAdapterSpawner = function () { + return data; + }; + + return data; +} + +function _debuggerRegistry() { + const data = require("./debugger-registry"); + + _debuggerRegistry = function () { + return data; + }; + + return data; +} + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _fsPromise() { + const data = _interopRequireDefault(require("../nuclide-commons/fsPromise")); + + _fsPromise = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,60 +71,51 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {ConnectableObservable} from 'rxjs'; -import type {VSAdapterExecutableInfo, VsAdapterType} from './types'; -import type {ProcessInfo, ProcessMessage} from 'nuclide-commons/process'; - -import {psTree} from 'nuclide-commons/process'; -import VsAdapterSpawner from './VsAdapterSpawner'; -import {getAdapterExecutable} from './debugger-registry'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import fsPromise from 'nuclide-commons/fsPromise'; -import {getAbsoluteBinaryPathForPid} from 'nuclide-commons/process'; - -export class VsRawAdapterSpawnerService extends VsAdapterSpawner { - spawnAdapter( - adapter: VSAdapterExecutableInfo, - ): ConnectableObservable { +class VsRawAdapterSpawnerService extends _VsAdapterSpawner().default { + spawnAdapter(adapter) { return super.spawnAdapter(adapter); } - write(input: string): Promise { + write(input) { return super.write(input); } - dispose(): Promise { + dispose() { return super.dispose(); } + } -export async function createVsRawAdapterSpawnerService(): Promise< - VsRawAdapterSpawnerService, -> { +exports.VsRawAdapterSpawnerService = VsRawAdapterSpawnerService; + +async function createVsRawAdapterSpawnerService() { return new VsRawAdapterSpawnerService(); } -export async function getProcessTree(): Promise> { - return psTree(); +async function getProcessTree() { + return (0, _process().psTree)(); } -export async function getBuckRootFromUri(uri: string): Promise { - if (!nuclideUri.isAbsolute(uri)) { +async function getBuckRootFromUri(uri) { + if (!_nuclideUri().default.isAbsolute(uri)) { return null; } + let path = uri; while (true) { - const rootTest = nuclideUri.join(path, '.buckconfig'); - // eslint-disable-next-line no-await-in-loop - if (await fsPromise.exists(rootTest)) { + const rootTest = _nuclideUri().default.join(path, '.buckconfig'); // eslint-disable-next-line no-await-in-loop + + + if (await _fsPromise().default.exists(rootTest)) { return path; } - const newPath = nuclideUri.getParent(path); + + const newPath = _nuclideUri().default.getParent(path); + if (newPath === path) { break; } @@ -70,8 +126,9 @@ export async function getBuckRootFromUri(uri: string): Promise { return null; } -export async function getBuckRootFromPid(pid: number): Promise { - const path = await getAbsoluteBinaryPathForPid(pid); +async function getBuckRootFromPid(pid) { + const path = await (0, _process().getAbsoluteBinaryPathForPid)(pid); + if (path == null) { return null; } @@ -79,12 +136,10 @@ export async function getBuckRootFromPid(pid: number): Promise { return getBuckRootFromUri(path); } -export async function realpath(path: string): Promise { - return fsPromise.realpath(path); +async function realpath(path) { + return _fsPromise().default.realpath(path); } -export async function getAdapterExecutableInfo( - adapterType: VsAdapterType, -): Promise { - return getAdapterExecutable(adapterType); -} +async function getAdapterExecutableInfo(adapterType) { + return (0, _debuggerRegistry().getAdapterExecutable)(adapterType); +} \ No newline at end of file diff --git a/modules/nuclide-debugger-common/VsAdapterSpawner.js b/modules/nuclide-debugger-common/VsAdapterSpawner.js index 82d40d4e..0d593d86 100644 --- a/modules/nuclide-debugger-common/VsAdapterSpawner.js +++ b/modules/nuclide-debugger-common/VsAdapterSpawner.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _process() { + const data = require("../nuclide-commons/process"); + + _process = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,57 +25,46 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +class VsAdapterSpawner { + constructor() { + this._stdin = new _RxMin.Subject(); + } -import type {ConnectableObservable} from 'rxjs'; -import type {ProcessMessage} from 'nuclide-commons/process'; -import type {VSAdapterExecutableInfo, IVsAdapterSpawner} from './types'; + spawnAdapter(adapter) { + const environment = _RxMin.Observable.fromPromise((0, _process().getOriginalEnvironment)()); -import { - observeProcessRaw, - getOriginalEnvironment, -} from 'nuclide-commons/process'; -import {Observable, Subject} from 'rxjs'; + return _RxMin.Observable.forkJoin(this._stdin.buffer(environment), environment).switchMap(([stdinBuffer, env]) => { + const options = { + stdio: ['pipe', // stdin + 'pipe', // stdout + 'pipe'], + env: Object.assign({}, env, { + ELECTRON_RUN_AS_NODE: 1 + }), + input: _RxMin.Observable.from(stdinBuffer).concat(this._stdin), + killTreeWhenDone: true, + killTreeSignal: 'SIGKILL' + }; -export default class VsAdapterSpawner implements IVsAdapterSpawner { - _stdin: Subject; + if (adapter.command === 'node') { + adapter.command = process.execPath; + } - constructor() { - this._stdin = new Subject(); + return (0, _process().observeProcessRaw)(adapter.command, adapter.args, options); + }).publish(); } - spawnAdapter( - adapter: VSAdapterExecutableInfo, - ): ConnectableObservable { - const environment = Observable.fromPromise(getOriginalEnvironment()); - return Observable.forkJoin(this._stdin.buffer(environment), environment) - .switchMap(([stdinBuffer, env]) => { - const options = { - stdio: [ - 'pipe', // stdin - 'pipe', // stdout - 'pipe', // stderr - ], - env: {...env, ELECTRON_RUN_AS_NODE: 1}, - input: Observable.from(stdinBuffer).concat(this._stdin), - killTreeWhenDone: true, - killTreeSignal: 'SIGKILL', - }; - if (adapter.command === 'node') { - adapter.command = process.execPath; - } - return observeProcessRaw(adapter.command, adapter.args, options); - }) - .publish(); - } - - async write(input: string): Promise { + async write(input) { this._stdin.next(input); } - async dispose(): Promise { + async dispose() { this._stdin.complete(); } + } + +exports.default = VsAdapterSpawner; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/VsDebugSession.js b/modules/nuclide-debugger-common/VsDebugSession.js index 5adfa20c..dd6e9e34 100644 --- a/modules/nuclide-debugger-common/VsDebugSession.js +++ b/modules/nuclide-debugger-common/VsDebugSession.js @@ -1,3 +1,66 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function DebugProtocol() { + const data = _interopRequireWildcard(require("vscode-debugprotocol")); + + DebugProtocol = function () { + return data; + }; + + return data; +} + +function _VsAdapterSpawner() { + const data = _interopRequireDefault(require("./VsAdapterSpawner")); + + _VsAdapterSpawner = function () { + return data; + }; + + return data; +} + +function _V8Protocol() { + const data = _interopRequireDefault(require("./V8Protocol")); + + _V8Protocol = function () { + return data; + }; + + return data; +} + +var _RxMin = require("rxjs/bundles/Rx.min.js"); + +function _analytics() { + const data = require("../nuclide-commons/analytics"); + + _analytics = function () { + return data; + }; + + return data; +} + +function _uuid() { + const data = _interopRequireDefault(require("uuid")); + + _uuid = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,266 +69,190 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import * as DebugProtocol from 'vscode-debugprotocol'; -import type { - IVsAdapterSpawner, - MessageProcessor, - VSAdapterExecutableInfo, -} from './types'; -import type {ProcessMessage} from 'nuclide-commons/process'; - -import VsAdapterSpawner from './VsAdapterSpawner'; -import V8Protocol from './V8Protocol'; -import {Observable, Subject} from 'rxjs'; -import idx from 'idx'; -import invariant from 'assert'; -import {track, trackTiming} from 'nuclide-commons/analytics'; -import uuid from 'uuid'; - -export interface AdapterExitedEvent extends DebugProtocol.DebugEvent { - event: 'adapter-exited'; - body: {exitCode: number}; -} - -export type AdapterAnalyticsExtras = { - adapter: string, - host: string, - isRemote: boolean, -}; - -function raiseAdapterExitedEvent(exitCode: number): AdapterExitedEvent { +function raiseAdapterExitedEvent(exitCode) { return { seq: 0, type: 'event', event: 'adapter-exited', - body: {exitCode}, + body: { + exitCode + } }; } -type RunInTerminalHandler = ( - arguments: DebugProtocol.RunInTerminalRequestArguments, -) => Promise; - /** * Use V8 JSON-RPC protocol to send & receive messages * (requests, responses & events) over `stdio` of adapter child processes. */ -export default class VsDebugSession extends V8Protocol { - _readyForBreakpoints: boolean; - _disconnected: boolean; - - _adapterProcessSubscription: ?rxjs$Subscription; - _startTime: number; - - capabilities: DebugProtocol.Capabilities; - _adapterExecutable: VSAdapterExecutableInfo; - _logger: log4js$Logger; - _spawner: IVsAdapterSpawner; - _adapterAnalyticsExtras: AdapterAnalyticsExtras; - _adapterErrorOutput: string; - - _onDidInitialize: Subject; - _onDidStop: Subject; - _onDidContinued: Subject; - _onDidTerminateDebugee: Subject; - _onDidExitDebugee: Subject; - _onDidExitAdapter: Subject; - _onDidThread: Subject; - _onDidOutput: Subject; - _onDidBreakpoint: Subject; - _onDidModule: Subject; - _onDidLoadSource: Subject; - _onDidCustom: Subject; - _onDidEvent: Subject; - _runInTerminalHandler: ?RunInTerminalHandler; - - constructor( - id: string, - logger: log4js$Logger, - adapterExecutable: VSAdapterExecutableInfo, - adapterAnalyticsExtras: ?AdapterAnalyticsExtras, - spawner?: IVsAdapterSpawner, - sendPreprocessors?: MessageProcessor[] = [], - receivePreprocessors?: MessageProcessor[] = [], - runInTerminalHandler?: RunInTerminalHandler, - ) { +class VsDebugSession extends _V8Protocol().default { + constructor(id, logger, adapterExecutable, adapterAnalyticsExtras, spawner, sendPreprocessors = [], receivePreprocessors = [], runInTerminalHandler) { super(id, logger, sendPreprocessors, receivePreprocessors); this._adapterExecutable = adapterExecutable; this._logger = logger; this._readyForBreakpoints = false; - this._spawner = spawner == null ? new VsAdapterSpawner() : spawner; - this._adapterAnalyticsExtras = { - ...adapterAnalyticsExtras, + this._spawner = spawner == null ? new (_VsAdapterSpawner().default)() : spawner; + this._adapterAnalyticsExtras = Object.assign({}, adapterAnalyticsExtras, { // $FlowFixMe flow doesn't consider uuid callable, but it is - debuggerSessionId: uuid(), - }; + debuggerSessionId: (0, _uuid().default)() + }); this._adapterErrorOutput = ''; - - this._onDidInitialize = new Subject(); - this._onDidStop = new Subject(); - this._onDidContinued = new Subject(); - this._onDidTerminateDebugee = new Subject(); - this._onDidExitDebugee = new Subject(); - this._onDidExitAdapter = new Subject(); - this._onDidThread = new Subject(); - this._onDidOutput = new Subject(); - this._onDidBreakpoint = new Subject(); - this._onDidModule = new Subject(); - this._onDidLoadSource = new Subject(); - this._onDidCustom = new Subject(); - this._onDidEvent = new Subject(); + this._onDidInitialize = new _RxMin.Subject(); + this._onDidStop = new _RxMin.Subject(); + this._onDidContinued = new _RxMin.Subject(); + this._onDidTerminateDebugee = new _RxMin.Subject(); + this._onDidExitDebugee = new _RxMin.Subject(); + this._onDidExitAdapter = new _RxMin.Subject(); + this._onDidThread = new _RxMin.Subject(); + this._onDidOutput = new _RxMin.Subject(); + this._onDidBreakpoint = new _RxMin.Subject(); + this._onDidModule = new _RxMin.Subject(); + this._onDidLoadSource = new _RxMin.Subject(); + this._onDidCustom = new _RxMin.Subject(); + this._onDidEvent = new _RxMin.Subject(); this.capabilities = {}; this._runInTerminalHandler = runInTerminalHandler || null; } - observeInitializeEvents(): Observable { + observeInitializeEvents() { return this._onDidInitialize.asObservable(); } - observeStopEvents(): Observable { + observeStopEvents() { return this._onDidStop.asObservable(); } - observeContinuedEvents(): Observable { + observeContinuedEvents() { return this._onDidContinued.asObservable(); } - observeTerminateDebugeeEvents(): Observable { + observeTerminateDebugeeEvents() { return this._onDidTerminateDebugee.asObservable(); } - observeExitedDebugeeEvents(): Observable { + observeExitedDebugeeEvents() { return this._onDidExitDebugee.asObservable(); } - observeAdapterExitedEvents(): Observable { + observeAdapterExitedEvents() { return this._onDidExitAdapter.asObservable(); } - observeThreadEvents(): Observable { + observeThreadEvents() { return this._onDidThread.asObservable(); } - observeOutputEvents(): Observable { + observeOutputEvents() { return this._onDidOutput.asObservable(); } - observeBreakpointEvents(): Observable { + observeBreakpointEvents() { return this._onDidBreakpoint.asObservable(); } - observeModuleEvents(): Observable { + observeModuleEvents() { return this._onDidModule.asObservable(); } - observeSourceLoadedEvents(): Observable { + observeSourceLoadedEvents() { return this._onDidLoadSource.asObservable(); } - observeCustomEvents(): Observable { + observeCustomEvents() { return this._onDidCustom.asObservable(); } - observeAllEvents(): Observable { + observeAllEvents() { return this._onDidEvent.asObservable(); } - _initServer(): void { + _initServer() { if (this._adapterProcessSubscription != null) { return; } this._startServer(); + this._startTime = new Date().getTime(); } - custom(request: string, args: any): Promise { + custom(request, args) { return this.send(request, args); } - send(command: string, args: any): Promise { + send(command, args) { this._logger.info('Send request:', command, args); + this._initServer(); - const operation = (): Promise => { + const operation = () => { // Babel Bug: `super` isn't working with `async` - return super.send(command, args).then( - (response: DebugProtocol.Response) => { - const sanitizedResponse = this._sanitizeResponse(response); - this._logger.info('Received response:', sanitizedResponse); - track('vs-debug-session:transaction', { - ...this._adapterAnalyticsExtras, - request: {command, arguments: args}, - response: sanitizedResponse, - }); - return response; - }, - (errorResponse: DebugProtocol.ErrorResponse) => { - let formattedError = - idx(errorResponse, _ => _.body.error.format) || - idx(errorResponse, _ => _.message); - if (formattedError === '{_stack}') { - formattedError = JSON.stringify(errorResponse.body.error); - } else if (formattedError == null) { - formattedError = [ - `command: ${command}`, - `args: ${JSON.stringify(args)}`, - `response: ${JSON.stringify(errorResponse)}`, - `adapterExecutable: , ${JSON.stringify(this._adapterExecutable)}`, - ].join(', '); - } - track('vs-debug-session:transaction', { - ...this._adapterAnalyticsExtras, - request: {command, arguments: args}, - response: errorResponse, - }); - throw new Error(formattedError); - }, - ); + return super.send(command, args).then(response => { + const sanitizedResponse = this._sanitizeResponse(response); + + this._logger.info('Received response:', sanitizedResponse); + + (0, _analytics().track)('vs-debug-session:transaction', Object.assign({}, this._adapterAnalyticsExtras, { + request: { + command, + arguments: args + }, + response: sanitizedResponse + })); + return response; + }, errorResponse => { + var _ref, _ref2; + + let formattedError = ((_ref = errorResponse) != null ? (_ref = _ref.body) != null ? (_ref = _ref.error) != null ? _ref.format : _ref : _ref : _ref) || ((_ref2 = errorResponse) != null ? _ref2.message : _ref2); + + if (formattedError === '{_stack}') { + formattedError = JSON.stringify(errorResponse.body.error); + } else if (formattedError == null) { + formattedError = [`command: ${command}`, `args: ${JSON.stringify(args)}`, `response: ${JSON.stringify(errorResponse)}`, `adapterExecutable: , ${JSON.stringify(this._adapterExecutable)}`].join(', '); + } + + (0, _analytics().track)('vs-debug-session:transaction', Object.assign({}, this._adapterAnalyticsExtras, { + request: { + command, + arguments: args + }, + response: errorResponse + })); + throw new Error(formattedError); + }); }; - return trackTiming( - `vs-debug-session:${command}`, - operation, - this._adapterAnalyticsExtras, - ); + return (0, _analytics().trackTiming)(`vs-debug-session:${command}`, operation, this._adapterAnalyticsExtras); } - _sanitizeResponse( - response: DebugProtocol.base$Response, - ): DebugProtocol.base$Response { + _sanitizeResponse(response) { try { if (response.command === 'variables') { - const varResponse = ((response: any): DebugProtocol.VariablesResponse); - const sanResponse = { - ...varResponse, - body: { - ...varResponse.body, - variables: varResponse.body.variables.map(v => ({ - ...v, - value: '', - })), - }, - }; - // $FlowFixMe flow isn't recognizing that ...varResponse is filling in needed members + const varResponse = response; + const sanResponse = Object.assign({}, varResponse, { + body: Object.assign({}, varResponse.body, { + variables: varResponse.body.variables.map(v => Object.assign({}, v, { + value: '' + })) + }) + }); // $FlowFixMe flow isn't recognizing that ...varResponse is filling in needed members + return sanResponse; } + if (response.command === 'evaluate') { - const evalResponse = ((response: any): DebugProtocol.EvaluateResponse); - const sanResponse = { - ...evalResponse, - body: { - ...evalResponse.body, - result: '', - }, - }; - // $FlowFixMe flow isn't recognizing that ...evalResponse is filling in needed members + const evalResponse = response; + const sanResponse = Object.assign({}, evalResponse, { + body: Object.assign({}, evalResponse.body, { + result: '' + }) + }); // $FlowFixMe flow isn't recognizing that ...evalResponse is filling in needed members + return sanResponse; } + return response; } catch (e) { // Don't let a malformed response prevent the response from bubbling up @@ -277,278 +264,263 @@ export default class VsDebugSession extends V8Protocol { success: false, command: response.command, error: 'Error sanitizing response.', - message: e.message, + message: e.message }; } } - onEvent(event: DebugProtocol.Event | AdapterExitedEvent): void { + onEvent(event) { if (event.body != null) { // $FlowFixMe `sessionId` isn't in the type def. event.body.sessionId = this.getId(); } else { // $FlowFixMe `event.body` type def. - event.body = {sessionId: this.getId()}; + event.body = { + sessionId: this.getId() + }; } - track('vs-debug-session:transaction', { - ...this._adapterAnalyticsExtras, - event, - }); + (0, _analytics().track)('vs-debug-session:transaction', Object.assign({}, this._adapterAnalyticsExtras, { + event + })); this._onDidEvent.next(event); switch (event.event) { case 'initialized': this._readyForBreakpoints = true; + this._onDidInitialize.next(event); + break; + case 'stopped': this._onDidStop.next(event); + break; + case 'continued': this._onDidContinued.next(event); + break; + case 'thread': this._onDidThread.next(event); + break; + case 'output': this._onDidOutput.next(event); + break; + case 'breakpoint': this._onDidBreakpoint.next(event); + break; + case 'terminated': this._onDidTerminateDebugee.next(event); + break; + case 'exited': this._onDidExitDebugee.next(event); + break; + case 'adapter-exited': this._onDidExitAdapter.next(event); + break; + case 'module': this._onDidModule.next(event); + break; + case 'loadedSource': this._onDidLoadSource.next(event); + break; + default: this._onDidCustom.next(event); + this._logger.info('Custom event type:', event); + break; } } - getCapabilities(): DebugProtocol.Capabilities { + getCapabilities() { return this.capabilities; } - async initialize( - args: DebugProtocol.InitializeRequestArguments, - ): Promise { + async initialize(args) { const response = await this.send('initialize', args); return this._readCapabilities(response); } - _readCapabilities(response: any): any { + _readCapabilities(response) { if (response) { - this.capabilities = { - ...this.capabilities, - ...response.body, - }; + this.capabilities = Object.assign({}, this.capabilities, response.body); } + return response; } - async launch( - args: DebugProtocol.LaunchRequestArguments, - ): Promise { + async launch(args) { const response = await this.send('launch', args); return this._readCapabilities(response); } - async attach( - args: DebugProtocol.AttachRequestArguments, - ): Promise { + async attach(args) { const response = await this.send('attach', args); return this._readCapabilities(response); } - next(args: DebugProtocol.NextArguments): Promise { + next(args) { this._fireFakeContinued(args.threadId); + return this.send('next', args); } - stepIn( - args: DebugProtocol.StepInArguments, - ): Promise { + stepIn(args) { this._fireFakeContinued(args.threadId); + return this.send('stepIn', args); } - stepOut( - args: DebugProtocol.StepOutArguments, - ): Promise { + stepOut(args) { this._fireFakeContinued(args.threadId); + return this.send('stepOut', args); } - continue( - args: DebugProtocol.ContinueArguments, - ): Promise { + continue(args) { this._fireFakeContinued(args.threadId); + return this.send('continue', args); } - pause( - args: DebugProtocol.PauseArguments, - ): Promise { + pause(args) { return this.send('pause', args); } - setVariable( - args: DebugProtocol.SetVariableArguments, - ): Promise { + setVariable(args) { return this.send('setVariable', args); } - restartFrame( - args: DebugProtocol.RestartFrameArguments, - threadId: number, - ): Promise { + restartFrame(args, threadId) { this._fireFakeContinued(threadId); + return this.send('restartFrame', args); } - completions( - args: DebugProtocol.CompletionsArguments, - ): Promise { + completions(args) { return this.send('completions', args); } - async disconnect( - restart?: boolean = false, - force?: boolean = false, - ): Promise { + async disconnect(restart = false, force = false) { if (this._disconnected && force) { this._stopServer(); + return; } if (this._adapterProcessSubscription != null && !this._disconnected) { // point of no return: from now on don't report any errors this._disconnected = true; - await this.send('disconnect', {restart}); + await this.send('disconnect', { + restart + }); + this._stopServer(); } } - setBreakpoints( - args: DebugProtocol.SetBreakpointsArguments, - ): Promise { + setBreakpoints(args) { return this.send('setBreakpoints', args); } - setFunctionBreakpoints( - args: DebugProtocol.SetFunctionBreakpointsArguments, - ): Promise { + setFunctionBreakpoints(args) { return this.send('setFunctionBreakpoints', args); } - setExceptionBreakpoints( - args: DebugProtocol.SetExceptionBreakpointsArguments, - ): Promise { + setExceptionBreakpoints(args) { return this.send('setExceptionBreakpoints', args); } - configurationDone(): Promise { + configurationDone() { return this.send('configurationDone', null); } - stackTrace( - args: DebugProtocol.StackTraceArguments, - ): Promise { + stackTrace(args) { return this.send('stackTrace', args); } - exceptionInfo( - args: DebugProtocol.ExceptionInfoArguments, - ): Promise { + exceptionInfo(args) { return this.send('exceptionInfo', args); } - scopes( - args: DebugProtocol.ScopesArguments, - ): Promise { + scopes(args) { return this.send('scopes', args); } - variables( - args: DebugProtocol.VariablesArguments, - ): Promise { + variables(args) { return this.send('variables', args); } - source( - args: DebugProtocol.SourceArguments, - ): Promise { + source(args) { return this.send('source', args); } - threads(): Promise { + threads() { return this.send('threads', null); } - evaluate( - args: DebugProtocol.EvaluateArguments, - ): Promise { + evaluate(args) { return this.send('evaluate', args); } - stepBack( - args: DebugProtocol.StepBackArguments, - ): Promise { + stepBack(args) { this._fireFakeContinued(args.threadId); + return this.send('stepBack', args); } - reverseContinue( - args: DebugProtocol.ReverseContinueArguments, - ): Promise { + reverseContinue(args) { this._fireFakeContinued(args.threadId); + return this.send('reverseContinue', args); } - nuclide_continueToLocation( - args: DebugProtocol.nuclide_ContinueToLocationArguments, - ): Promise { + nuclide_continueToLocation(args) { return this.custom('nuclide_continueToLocation', args); } - getLengthInSeconds(): number { + getLengthInSeconds() { return (new Date().getTime() - this._startTime) / 1000; } - async dispatchRequest( - request: DebugProtocol.Request, - response: DebugProtocol.Response, - ): Promise { + async dispatchRequest(request, response) { if (request.command === 'runInTerminal') { const runInTerminalHandler = this._runInTerminalHandler; + if (runInTerminalHandler == null) { - this._logger.error( - "'runInTerminal' isn't supported for this debug session", - request, - ); + this._logger.error("'runInTerminal' isn't supported for this debug session", request); + return; } + try { - await runInTerminalHandler((request.arguments: any)); + await runInTerminalHandler(request.arguments); } catch (error) { response.success = false; response.message = error.message; } + this.sendResponse(response); } else if (request.command === 'handshake') { this._logger.error('TODO: handshake', request); @@ -559,119 +531,138 @@ export default class VsDebugSession extends V8Protocol { } } - _fireFakeContinued( - threadId: number, - allThreadsContinued?: boolean = false, - ): void { - const event: DebugProtocol.ContinuedEvent = { + _fireFakeContinued(threadId, allThreadsContinued = false) { + const event = { type: 'event', event: 'continued', body: { threadId, // $FlowFixMe - allThreadsContinued, + allThreadsContinued }, - seq: 0, + seq: 0 }; + this._onDidContinued.next(event); + this._onDidEvent.next(event); } - _startServer(): void { - this._adapterProcessSubscription = this._spawner - .spawnAdapter(this._adapterExecutable) - .refCount() - .subscribe( - (message: ProcessMessage) => { - if (message.kind === 'stdout') { - this.handleData(new Buffer(message.data)); - } else if (message.kind === 'stderr') { - const event: DebugProtocol.OutputEvent = ({ - type: 'event', - event: 'output', - body: { - category: 'stderr', - output: message.data, - }, - seq: 0, - }: any); - this._onDidOutput.next(event); - this._onDidEvent.next(event); - this._logger.error(`adapter stderr: ${message.data}`); - this._adapterErrorOutput = this._adapterErrorOutput + message.data; - } else { - invariant(message.kind === 'exit'); - this.onServerExit(message.exitCode || 0); - } - }, - (err: Error) => { - this.onServerError(err); - }, - ); + _startServer() { + this._adapterProcessSubscription = this._spawner.spawnAdapter(this._adapterExecutable).refCount().subscribe(message => { + if (message.kind === 'stdout') { + this.handleData(new Buffer(message.data)); + } else if (message.kind === 'stderr') { + const event = { + type: 'event', + event: 'output', + body: { + category: 'stderr', + output: message.data + }, + seq: 0 + }; + + this._onDidOutput.next(event); + this._onDidEvent.next(event); + + this._logger.error(`adapter stderr: ${message.data}`); + + this._adapterErrorOutput = this._adapterErrorOutput + message.data; + } else { + if (!(message.kind === 'exit')) { + throw new Error("Invariant violation: \"message.kind === 'exit'\""); + } + + this.onServerExit(message.exitCode || 0); + } + }, err => { + this.onServerError(err); + }); this.setOutput(this._spawner.write.bind(this._spawner)); } - _stopServer(): void { + _stopServer() { this.onEvent(raiseAdapterExitedEvent(0)); + if (this._adapterProcessSubscription == null) { return; } this._disconnected = true; + this._adapterProcessSubscription.unsubscribe(); + this._endHandlers(); } - _endHandlers(): void { + _endHandlers() { this._onDidInitialize.complete(); + this._onDidStop.complete(); + this._onDidContinued.complete(); + this._onDidTerminateDebugee.complete(); + this._onDidExitDebugee.complete(); + this._onDidExitAdapter.complete(); + this._onDidThread.complete(); + this._onDidOutput.complete(); + this._onDidBreakpoint.complete(); + this._onDidModule.complete(); + this._onDidLoadSource.complete(); + this._onDidCustom.complete(); + this._onDidEvent.complete(); } - onServerError(error: Error): void { + onServerError(error) { this._logger.error('Adapter error:', error); + this._stopServer(); } - onServerExit(code: number): void { + onServerExit(code) { if (this._adapterProcessSubscription != null) { this._adapterProcessSubscription.unsubscribe(); + this._adapterProcessSubscription = null; } + if (!this._disconnected) { - this._logger.error( - `Debug adapter process has terminated unexpectedly ${code}`, - ); + this._logger.error(`Debug adapter process has terminated unexpectedly ${code}`); } + this.onEvent(raiseAdapterExitedEvent(code)); } - isReadyForBreakpoints(): boolean { + isReadyForBreakpoints() { return this._readyForBreakpoints; } - isDisconnected(): boolean { + isDisconnected() { return this._disconnected; } - dispose(): void { + dispose() { if (this._adapterErrorOutput) { - track('vs-debug-session:transaction', { - ...this._adapterAnalyticsExtras, - response: this._adapterErrorOutput, - }); + (0, _analytics().track)('vs-debug-session:transaction', Object.assign({}, this._adapterAnalyticsExtras, { + response: this._adapterErrorOutput + })); } + this.disconnect(); } + } + +exports.default = VsDebugSession; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/autogen-utils.js b/modules/nuclide-debugger-common/autogen-utils.js index 860adcb1..8f57065a 100644 --- a/modules/nuclide-debugger-common/autogen-utils.js +++ b/modules/nuclide-debugger-common/autogen-utils.js @@ -1,3 +1,25 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.generatePropertyArray = generatePropertyArray; +exports.getNativeAutoGenConfig = getNativeAutoGenConfig; + +var React = _interopRequireWildcard(require("react")); + +function _constants() { + const data = require("./constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,80 +28,63 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ +function generatePropertyArray(launchOrAttachConfigProperties, required, visible) { + const propertyArray = Object.entries(launchOrAttachConfigProperties).map(property => { + const name = property[0]; + const propertyDetails = property[1]; + const autoGenProperty = { + name, + type: propertyDetails.type, + description: propertyDetails.description, + required: required.includes(name), + visible: visible.includes(name) + }; -import type {AutoGenProperty} from './types'; -import type { - NativeVsAdapterType, - AutoGenLaunchConfig, - AutoGenAttachConfig, - AutoGenConfig, -} from './types'; -import * as React from 'react'; - -import {VsAdapterTypes} from './constants'; - -export function generatePropertyArray( - launchOrAttachConfigProperties: Object, - required: string[], - visible: string[], -): AutoGenProperty[] { - const propertyArray = Object.entries(launchOrAttachConfigProperties) - .map(property => { - const name = property[0]; - const propertyDetails: any = property[1]; - const autoGenProperty: AutoGenProperty = { - name, - type: propertyDetails.type, - description: propertyDetails.description, - required: required.includes(name), - visible: visible.includes(name), - }; - if (typeof propertyDetails.default !== 'undefined') { - autoGenProperty.defaultValue = propertyDetails.default; - } - if ( - propertyDetails.items != null && - typeof propertyDetails.items.type !== 'undefined' - ) { - autoGenProperty.itemType = propertyDetails.items.type; - } - if (typeof propertyDetails.enums !== 'undefined') { - autoGenProperty.enums = propertyDetails.enums; - } - return autoGenProperty; - }) - .sort((p1, p2) => { - // TODO (goom): sort all configs, not just ones generated from the json - if (p1.required && !p2.required) { - return -1; - } - if (p2.required && !p1.required) { - return 1; - } - return 0; - }); + if (typeof propertyDetails.default !== 'undefined') { + autoGenProperty.defaultValue = propertyDetails.default; + } + + if (propertyDetails.items != null && typeof propertyDetails.items.type !== 'undefined') { + autoGenProperty.itemType = propertyDetails.items.type; + } + + if (typeof propertyDetails.enums !== 'undefined') { + autoGenProperty.enums = propertyDetails.enums; + } + + return autoGenProperty; + }).sort((p1, p2) => { + // TODO (goom): sort all configs, not just ones generated from the json + if (p1.required && !p2.required) { + return -1; + } + + if (p2.required && !p1.required) { + return 1; + } + + return 0; + }); return propertyArray; } -export function getNativeAutoGenConfig( - vsAdapterType: NativeVsAdapterType, -): AutoGenConfig { +function getNativeAutoGenConfig(vsAdapterType) { const program = { name: 'program', type: 'path', description: 'Input the program/executable you want to launch', required: true, - visible: true, + visible: true }; const cwd = { name: 'cwd', type: 'path', description: 'Working directory for the launched executable', required: true, - visible: true, + visible: true }; const args = { name: 'args', @@ -88,24 +93,19 @@ export function getNativeAutoGenConfig( description: '(Optional) Arguments to the executable', required: false, defaultValue: '', - visible: true, + visible: true }; const env = { name: 'env', type: 'array', itemType: 'string', - description: - '(Optional) Environment variables (e.g. SHELL=/bin/bash PATH=/bin)', + description: '(Optional) Environment variables (e.g. SHELL=/bin/bash PATH=/bin)', required: false, defaultValue: '', - visible: true, + visible: true }; - - const debugTypeMessage = `using ${ - vsAdapterType === VsAdapterTypes.NATIVE_GDB ? 'gdb' : 'lldb' - }`; - - const autoGenLaunchConfig: AutoGenLaunchConfig = { + const debugTypeMessage = `using ${vsAdapterType === _constants().VsAdapterTypes.NATIVE_GDB ? 'gdb' : 'lldb'}`; + const autoGenLaunchConfig = { launch: true, vsAdapterType, threads: true, @@ -113,36 +113,41 @@ export function getNativeAutoGenConfig( scriptPropertyName: 'program', scriptExtension: '.c', cwdPropertyName: 'working directory', - header:

    Debug native programs {debugTypeMessage}.

    , + header: React.createElement("p", null, "Debug native programs ", debugTypeMessage, "."), + getProcessName(values) { let processName = values.program; const lastSlash = processName.lastIndexOf('/'); + if (lastSlash >= 0) { processName = processName.substring(lastSlash + 1, processName.length); } + return processName; - }, - }; + } + }; const pid = { name: 'pid', type: 'process', description: '', required: true, - visible: true, + visible: true }; - const autoGenAttachConfig: AutoGenAttachConfig = { + const autoGenAttachConfig = { launch: false, vsAdapterType, threads: true, properties: [pid], - header:

    Attach to a running native process {debugTypeMessage}

    , + header: React.createElement("p", null, "Attach to a running native process ", debugTypeMessage), + getProcessName(values) { return 'Pid: ' + values.pid + ' (' + debugTypeMessage + ')'; - }, + } + }; return { launch: autoGenLaunchConfig, - attach: autoGenAttachConfig, + attach: autoGenAttachConfig }; -} +} \ No newline at end of file diff --git a/modules/nuclide-debugger-common/constants.js b/modules/nuclide-debugger-common/constants.js index e9d40ebd..4efe8eb7 100644 --- a/modules/nuclide-debugger-common/constants.js +++ b/modules/nuclide-debugger-common/constants.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.VsAdapterNames = exports.VsAdapterTypes = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,13 +13,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import type {VsAdapterType} from './types'; - -export const VsAdapterTypes = Object.freeze({ +const VsAdapterTypes = Object.freeze({ HHVM: 'hhvm', PYTHON: 'python', REACT_NATIVE: 'react-native', @@ -23,13 +27,12 @@ export const VsAdapterTypes = Object.freeze({ OCAML: 'ocaml', MOBILEJS: 'mobilejs', NATIVE_GDB: 'native_gdb', - NATIVE_LLDB: 'native_lldb', -}); - -// This is to work around flow's missing support of enums. -(VsAdapterTypes: {[key: string]: VsAdapterType}); + NATIVE_LLDB: 'native_lldb' +}); // This is to work around flow's missing support of enums. -export const VsAdapterNames = Object.freeze({ +exports.VsAdapterTypes = VsAdapterTypes; +VsAdapterTypes; +const VsAdapterNames = Object.freeze({ HHVM: 'Hack / PHP', PYTHON: 'Python', REACT_NATIVE: 'React Native', @@ -40,5 +43,6 @@ export const VsAdapterNames = Object.freeze({ OCAML: 'OCaml', MOBILEJS: 'Mobile JS', NATIVE_GDB: 'Native - GDB (C/C++)', - NATIVE_LLDB: 'Native - LLDB (C/C++)', + NATIVE_LLDB: 'Native - LLDB (C/C++)' }); +exports.VsAdapterNames = VsAdapterNames; \ No newline at end of file diff --git a/modules/nuclide-debugger-common/debug-adapter-service.js b/modules/nuclide-debugger-common/debug-adapter-service.js index d9a86d09..783209f3 100644 --- a/modules/nuclide-debugger-common/debug-adapter-service.js +++ b/modules/nuclide-debugger-common/debug-adapter-service.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getVSCodeDebuggerAdapterServiceByNuclideUri = getVSCodeDebuggerAdapterServiceByNuclideUri; + +function VSCodeDebuggerAdapterServiceLocal() { + const data = _interopRequireWildcard(require("./VSCodeDebuggerAdapterService")); + + VSCodeDebuggerAdapterServiceLocal = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,31 +25,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +function getVSCodeDebuggerAdapterServiceByNuclideUri(uri) { + let rpcService = null; // Atom's service hub is synchronous. + + atom.packages.serviceHub.consume('nuclide-rpc-services', '0.0.0', provider => { + rpcService = provider; + }).dispose(); -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import typeof * as VSCodeDebuggerAdapterService from './VSCodeDebuggerAdapterService'; - -import * as VSCodeDebuggerAdapterServiceLocal from './VSCodeDebuggerAdapterService'; - -export function getVSCodeDebuggerAdapterServiceByNuclideUri( - uri: NuclideUri, -): VSCodeDebuggerAdapterService { - let rpcService: ?nuclide$RpcService = null; - // Atom's service hub is synchronous. - atom.packages.serviceHub - .consume('nuclide-rpc-services', '0.0.0', provider => { - rpcService = provider; - }) - .dispose(); if (rpcService != null) { - return rpcService.getServiceByNuclideUri( - 'VSCodeDebuggerAdapterService', - uri, - ); + return rpcService.getServiceByNuclideUri('VSCodeDebuggerAdapterService', uri); } else { - return VSCodeDebuggerAdapterServiceLocal; + return VSCodeDebuggerAdapterServiceLocal(); } -} +} \ No newline at end of file diff --git a/modules/nuclide-debugger-common/debugger-registry.js b/modules/nuclide-debugger-common/debugger-registry.js index 3332651b..4603f84a 100644 --- a/modules/nuclide-debugger-common/debugger-registry.js +++ b/modules/nuclide-debugger-common/debugger-registry.js @@ -1,3 +1,25 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getAdapterExecutable = getAdapterExecutable; +exports.getAdapterPackageRoot = getAdapterPackageRoot; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +var _fs = _interopRequireDefault(require("fs")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,214 +28,111 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +const modulesPath = _nuclideUri().default.dirname(__dirname); -import nuclideUri from 'nuclide-commons/nuclideUri'; -import fs from 'fs'; - -import type {VSAdapterExecutableInfo, VsAdapterType} from './types'; - -type AdapterInfo = { - executable: VSAdapterExecutableInfo, - root: string, -}; +function resolvePackagePath(packageName) { + const bundledPath = _nuclideUri().default.join(modulesPath, packageName); -const modulesPath = nuclideUri.dirname(__dirname); - -function resolvePackagePath(packageName: string): string { - const bundledPath = nuclideUri.join(modulesPath, packageName); - if (fs.existsSync(bundledPath)) { + if (_fs.default.existsSync(bundledPath)) { return bundledPath; } else if (typeof atom !== 'undefined') { const pkg = atom.packages.getActivePackage(packageName); + if (pkg != null) { - return nuclideUri.join(pkg.path, 'node_modules', packageName); + return _nuclideUri().default.join(pkg.path, 'node_modules', packageName); } } + return 'DEBUGGER_RUNTIME_NOT_FOUND'; } -const _adapters: Map = new Map([ - [ - 'node', - { - executable: { - command: 'node', - args: [ - nuclideUri.join( - resolvePackagePath('atom-ide-debugger-node'), - 'VendorLib/vscode-node-debug2/out/src/nodeDebug.js', - ), - ], - }, - root: nuclideUri.join( - resolvePackagePath('atom-ide-debugger-node'), - 'VendorLib/vscode-node-debug2', - ), - }, - ], - [ - 'python', - { - executable: { - command: 'node', - args: [ - nuclideUri.join( - resolvePackagePath('atom-ide-debugger-python'), - 'VendorLib/vs-py-debugger/out/client/debugger/Main.js', - ), - ], - }, - root: nuclideUri.join( - resolvePackagePath('atom-ide-debugger-python'), - 'VendorLib/vs-py-debugger', - ), - }, - ], - [ - 'react-native', - { - executable: { - command: 'node', - args: [ - nuclideUri.join( - resolvePackagePath('atom-ide-debugger-react-native'), - 'VendorLib/vscode-react-native/out/debugger/reactNativeDebugEntryPoint.js', - ), - ], - }, - root: nuclideUri.join( - resolvePackagePath('atom-ide-debugger-react-native'), - 'VendorLib/vscode-react-native', - ), - }, - ], - [ - 'prepack', - { - executable: { - command: 'node', - args: [ - nuclideUri.join( - __dirname, - '../../pkg/nuclide-debugger-prepack/VendorLib/vscode-prepack/adapter/DebugAdapter.js', - ), - ], - }, - root: nuclideUri.join( - __dirname, - '../../pkg/nuclide-debugger-prepack/VendorLib/vscode-prepack', - ), - }, - ], - [ - 'ocaml', - { - executable: { - command: 'node', - args: [ - nuclideUri.join( - resolvePackagePath('atom-ide-debugger-ocaml'), - 'lib/vscode-debugger-entry.js', - ), - ], - }, - root: resolvePackagePath('atom-ide-debugger-ocaml'), - }, - ], - [ - 'native_gdb', - { - executable: { - command: 'node', - args: [ - nuclideUri.join( - resolvePackagePath('atom-ide-debugger-native-gdb'), - 'lib/RunTranspiledServer.js', - ), - ], - }, - root: resolvePackagePath('atom-ide-debugger-native-gdb'), - }, - ], - [ - 'native_lldb', - { - executable: { - command: 'lldb-vscode', - args: [], - }, - root: nuclideUri.join(__dirname, 'fb-native-debugger-lldb-vsp'), - }, - ], - [ - 'java', - { - executable: { - command: 'java', - args: [], - }, - root: resolvePackagePath('atom-ide-debugger-java'), - }, - ], - [ - 'java_android', - { - executable: { - command: 'java', - args: [], - }, - root: resolvePackagePath('atom-ide-debugger-java-android'), - }, - ], - [ - 'hhvm', - { - executable: { - command: 'node', - args: [ - nuclideUri.join( - __dirname, - '../../pkg/nuclide-debugger-hhvm-rpc/lib/hhvmWrapper.js', - ), - ], - }, - root: nuclideUri.join(__dirname, '../../pkg/nuclide-debugger-hhvm-rpc'), - }, - ], - [ - 'mobilejs', - { - executable: { - command: 'node', - args: [ - nuclideUri.join( - __dirname, - '../../pkg/fb-debugger-mobilejs-rpc/lib/vscode/vscode-debugger-entry.js', - ), - ], - }, - root: nuclideUri.join(__dirname, '../../pkg/fb-debugger-mobilejs-rpc'), - }, - ], -]); - -export function getAdapterExecutable( - adapter: VsAdapterType, -): VSAdapterExecutableInfo { +const _adapters = new Map([['node', { + executable: { + command: 'node', + args: [_nuclideUri().default.join(resolvePackagePath('atom-ide-debugger-node'), 'VendorLib/vscode-node-debug2/out/src/nodeDebug.js')] + }, + root: _nuclideUri().default.join(resolvePackagePath('atom-ide-debugger-node'), 'VendorLib/vscode-node-debug2') +}], ['python', { + executable: { + command: 'node', + args: [_nuclideUri().default.join(resolvePackagePath('atom-ide-debugger-python'), 'VendorLib/vs-py-debugger/out/client/debugger/Main.js')] + }, + root: _nuclideUri().default.join(resolvePackagePath('atom-ide-debugger-python'), 'VendorLib/vs-py-debugger') +}], ['react-native', { + executable: { + command: 'node', + args: [_nuclideUri().default.join(resolvePackagePath('atom-ide-debugger-react-native'), 'VendorLib/vscode-react-native/out/debugger/reactNativeDebugEntryPoint.js')] + }, + root: _nuclideUri().default.join(resolvePackagePath('atom-ide-debugger-react-native'), 'VendorLib/vscode-react-native') +}], ['prepack', { + executable: { + command: 'node', + args: [_nuclideUri().default.join(__dirname, '../../pkg/nuclide-debugger-prepack/VendorLib/vscode-prepack/adapter/DebugAdapter.js')] + }, + root: _nuclideUri().default.join(__dirname, '../../pkg/nuclide-debugger-prepack/VendorLib/vscode-prepack') +}], ['ocaml', { + executable: { + command: 'node', + args: [_nuclideUri().default.join(resolvePackagePath('atom-ide-debugger-ocaml'), 'lib/vscode-debugger-entry.js')] + }, + root: resolvePackagePath('atom-ide-debugger-ocaml') +}], ['native_gdb', { + executable: { + command: 'node', + args: [_nuclideUri().default.join(resolvePackagePath('atom-ide-debugger-native-gdb'), 'lib/RunTranspiledServer.js')] + }, + root: resolvePackagePath('atom-ide-debugger-native-gdb') +}], ['native_lldb', { + executable: { + command: 'lldb-vscode', + args: [] + }, + root: _nuclideUri().default.join(__dirname, 'fb-native-debugger-lldb-vsp') +}], ['java', { + executable: { + command: 'java', + args: [] + }, + root: resolvePackagePath('atom-ide-debugger-java') +}], ['java_android', { + executable: { + command: 'java', + args: [] + }, + root: resolvePackagePath('atom-ide-debugger-java-android') +}], ['hhvm', { + executable: { + command: 'node', + args: [_nuclideUri().default.join(__dirname, '../../pkg/nuclide-debugger-hhvm-rpc/lib/hhvmWrapper.js')] + }, + root: _nuclideUri().default.join(__dirname, '../../pkg/nuclide-debugger-hhvm-rpc') +}], ['mobilejs', { + executable: { + command: 'node', + args: [_nuclideUri().default.join(__dirname, '../../pkg/fb-debugger-mobilejs-rpc/lib/vscode/vscode-debugger-entry.js')] + }, + root: _nuclideUri().default.join(__dirname, '../../pkg/fb-debugger-mobilejs-rpc') +}]]); + +function getAdapterExecutable(adapter) { const adapterInfo = _adapters.get(adapter); + if (adapterInfo == null) { throw new Error(`Cannot find VSP for given adapter type ${adapter}`); } + return adapterInfo.executable; } -export function getAdapterPackageRoot(adapter: VsAdapterType): string { +function getAdapterPackageRoot(adapter) { const adapterInfo = _adapters.get(adapter); + if (adapterInfo == null) { throw new Error(`Cannot find VSP for given adapter type ${adapter}`); } + return adapterInfo.root; -} +} \ No newline at end of file diff --git a/modules/nuclide-debugger-common/main.js b/modules/nuclide-debugger-common/main.js index e7451346..685854df 100644 --- a/modules/nuclide-debugger-common/main.js +++ b/modules/nuclide-debugger-common/main.js @@ -1,52 +1,143 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local - * @format - */ - -export type { - AtomNotificationType, - ControlButtonSpecification, - DebuggerConfigAction, - DebuggerConfigurationProvider, - DebuggerSourcePathsService, - Device, - DevicePanelServiceApi, - IProcessConfig, - IVspInstance, - MessageProcessor, - NuclideDebuggerProvider, - VSAdapterExecutableInfo, - VsAdapterType, -} from './types'; - -export { - getVSCodeDebuggerAdapterServiceByNuclideUri, -} from './debug-adapter-service'; - -export { - default as DebuggerLaunchAttachProvider, -} from './DebuggerLaunchAttachProvider'; - -export {default as VsDebugSession} from './VsDebugSession'; - -export {VsAdapterTypes, VsAdapterNames} from './constants'; - -export { - deserializeDebuggerConfig, - serializeDebuggerConfig, -} from './DebuggerConfigSerializer'; - -export { - localToRemoteProcessor, - pathProcessor, - remoteToLocalProcessor, -} from './processors'; - -export {default as VsAdapterSpawner} from './VsAdapterSpawner'; +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "getVSCodeDebuggerAdapterServiceByNuclideUri", { + enumerable: true, + get: function () { + return _debugAdapterService().getVSCodeDebuggerAdapterServiceByNuclideUri; + } +}); +Object.defineProperty(exports, "DebuggerLaunchAttachProvider", { + enumerable: true, + get: function () { + return _DebuggerLaunchAttachProvider().default; + } +}); +Object.defineProperty(exports, "VsDebugSession", { + enumerable: true, + get: function () { + return _VsDebugSession().default; + } +}); +Object.defineProperty(exports, "VsAdapterTypes", { + enumerable: true, + get: function () { + return _constants().VsAdapterTypes; + } +}); +Object.defineProperty(exports, "VsAdapterNames", { + enumerable: true, + get: function () { + return _constants().VsAdapterNames; + } +}); +Object.defineProperty(exports, "deserializeDebuggerConfig", { + enumerable: true, + get: function () { + return _DebuggerConfigSerializer().deserializeDebuggerConfig; + } +}); +Object.defineProperty(exports, "serializeDebuggerConfig", { + enumerable: true, + get: function () { + return _DebuggerConfigSerializer().serializeDebuggerConfig; + } +}); +Object.defineProperty(exports, "localToRemoteProcessor", { + enumerable: true, + get: function () { + return _processors().localToRemoteProcessor; + } +}); +Object.defineProperty(exports, "pathProcessor", { + enumerable: true, + get: function () { + return _processors().pathProcessor; + } +}); +Object.defineProperty(exports, "remoteToLocalProcessor", { + enumerable: true, + get: function () { + return _processors().remoteToLocalProcessor; + } +}); +Object.defineProperty(exports, "VsAdapterSpawner", { + enumerable: true, + get: function () { + return _VsAdapterSpawner().default; + } +}); + +function _debugAdapterService() { + const data = require("./debug-adapter-service"); + + _debugAdapterService = function () { + return data; + }; + + return data; +} + +function _DebuggerLaunchAttachProvider() { + const data = _interopRequireDefault(require("./DebuggerLaunchAttachProvider")); + + _DebuggerLaunchAttachProvider = function () { + return data; + }; + + return data; +} + +function _VsDebugSession() { + const data = _interopRequireDefault(require("./VsDebugSession")); + + _VsDebugSession = function () { + return data; + }; + + return data; +} + +function _constants() { + const data = require("./constants"); + + _constants = function () { + return data; + }; + + return data; +} + +function _DebuggerConfigSerializer() { + const data = require("./DebuggerConfigSerializer"); + + _DebuggerConfigSerializer = function () { + return data; + }; + + return data; +} + +function _processors() { + const data = require("./processors"); + + _processors = function () { + return data; + }; + + return data; +} + +function _VsAdapterSpawner() { + const data = _interopRequireDefault(require("./VsAdapterSpawner")); + + _VsAdapterSpawner = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/modules/nuclide-debugger-common/processors.js b/modules/nuclide-debugger-common/processors.js index e19643d3..4aa9df11 100644 --- a/modules/nuclide-debugger-common/processors.js +++ b/modules/nuclide-debugger-common/processors.js @@ -1,3 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.remoteToLocalProcessor = remoteToLocalProcessor; +exports.localToRemoteProcessor = localToRemoteProcessor; +exports.pathProcessor = pathProcessor; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,29 +27,20 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {MessageProcessor} from './types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import nuclideUri from 'nuclide-commons/nuclideUri'; - -type PathMapper = (path: string) => string; - -export function remoteToLocalProcessor(): MessageProcessor { - return pathProcessor(path => nuclideUri.getPath(path)); +function remoteToLocalProcessor() { + return pathProcessor(path => _nuclideUri().default.getPath(path)); } -export function localToRemoteProcessor( - targetUri: NuclideUri, -): MessageProcessor { - const hostname = nuclideUri.getHostname(targetUri); - return pathProcessor(path => nuclideUri.createRemoteUri(hostname, path)); +function localToRemoteProcessor(targetUri) { + const hostname = _nuclideUri().default.getHostname(targetUri); + + return pathProcessor(path => _nuclideUri().default.createRemoteUri(hostname, path)); } -export function pathProcessor(pathMapper: PathMapper): MessageProcessor { +function pathProcessor(pathMapper) { return message => { processRequestsUris(message, pathMapper); processResponseUris(message, pathMapper); @@ -36,10 +48,11 @@ export function pathProcessor(pathMapper: PathMapper): MessageProcessor { }; } -function processRequestsUris(message: Object, pathMapper: PathMapper): void { +function processRequestsUris(message, pathMapper) { if (message.type !== 'request') { return; } + switch (message.command) { case 'setBreakpoints': case 'source': @@ -48,36 +61,32 @@ function processRequestsUris(message: Object, pathMapper: PathMapper): void { } } -function processResponseUris(message: Object, pathMapper: PathMapper): void { +function processResponseUris(message, pathMapper) { if (message.type !== 'response') { return; } + switch (message.command) { case 'setBreakpoints': case 'setFunctionBreakpoints': - message.body.breakpoints.forEach(bp => - translateField(bp, 'source.path', pathMapper), - ); + message.body.breakpoints.forEach(bp => translateField(bp, 'source.path', pathMapper)); break; + case 'stackTrace': - message.body.stackFrames.forEach(frame => - translateField(frame, 'source.path', pathMapper), - ); + message.body.stackFrames.forEach(frame => translateField(frame, 'source.path', pathMapper)); break; + case 'modules': - message.body.modules.forEach(module => - translateField(module, 'path', pathMapper), - ); + message.body.modules.forEach(module => translateField(module, 'path', pathMapper)); break; + case 'loadedSources': - message.body.sources.forEach(source => - translateField(source, 'path', pathMapper), - ); + message.body.sources.forEach(source => translateField(source, 'path', pathMapper)); break; } } -function processEventsUris(message: Object, pathMapper: PathMapper): void { +function processEventsUris(message, pathMapper) { if (message.type !== 'event') { return; } @@ -87,22 +96,20 @@ function processEventsUris(message: Object, pathMapper: PathMapper): void { case 'loadedSource': translateField(message, 'body.source.path', pathMapper); break; + case 'breakpoint': translateField(message, 'body.breakpoint.source.path', pathMapper); break; + case 'module': translateField(message, 'body.module.path', pathMapper); break; } -} - -// Traverse the source `object` for a deeply nested field, +} // Traverse the source `object` for a deeply nested field, // then apply the `pathMapper` to that field, if existing. -function translateField( - object: Object, - fieldDescriptor: string, - pathMapper: PathMapper, -): void { + + +function translateField(object, fieldDescriptor, pathMapper) { const fields = fieldDescriptor.split('.'); let lastObj = {}; const value = fields.reduce((child, field) => { @@ -113,8 +120,9 @@ function translateField( return child[field]; } }, object); + if (value != null) { const [lastField] = fields.slice(-1); - lastObj[lastField] = pathMapper((value: any)); + lastObj[lastField] = pathMapper(value); } -} +} \ No newline at end of file diff --git a/modules/nuclide-debugger-common/types.js b/modules/nuclide-debugger-common/types.js index 41293b1e..a57f3610 100644 --- a/modules/nuclide-debugger-common/types.js +++ b/modules/nuclide-debugger-common/types.js @@ -1,336 +1,15 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +"use strict"; -import type {SuggestedProjectPath} from 'atom-ide-debugger-java/types'; -import type {ISession} from 'atom-ide-ui/pkg/atom-ide-debugger/lib/types'; -import type UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import type {TaskEvent, ProcessMessage} from 'nuclide-commons/process'; -import type {Expected} from 'nuclide-commons/expected'; -import type DebuggerLaunchAttachProvider from './DebuggerLaunchAttachProvider'; -import type {Observable, ConnectableObservable} from 'rxjs'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {IconName} from 'nuclide-commons-ui/Icon'; -import * as DebugProtocol from 'vscode-debugprotocol'; -import * as React from 'react'; +function DebugProtocol() { + const data = _interopRequireWildcard(require("vscode-debugprotocol")); -export interface IVspInstance { - customRequest( - request: string, - args: any, - ): Promise; - observeCustomEvents(): Observable; - addCustomDisposable(disposable: IDisposable): void; -} - -export type AtomNotificationType = 'info' | 'warning' | 'error' | 'fatalError'; - -export type DebuggerConfigAction = 'launch' | 'attach'; - -export type VSAdapterExecutableInfo = { - command: string, - args: Array, -}; - -export type NativeVsAdapterType = 'native_lldb' | 'native_gdb'; - -export type VsAdapterType = - | 'hhvm' - | 'python' - | 'node' - | 'java' - | 'java_android' - | 'react-native' - | 'prepack' - | 'ocaml' - | 'mobilejs' - | 'native_lldb' - | 'native_gdb'; - -export type NuclideDebuggerProvider = { - type: VsAdapterType, - getLaunchAttachProvider( - connection: NuclideUri, - ): ?DebuggerLaunchAttachProvider, -}; - -export type ControlButtonSpecification = { - icon: IconName, - title?: string, - onClick: () => mixed, -}; - -export type IProcessConfig = {| - +targetUri: NuclideUri, - +debugMode: DebuggerConfigAction, - +adapterType: VsAdapterType, - +adapterExecutable?: ?VSAdapterExecutableInfo, - +config: Object, - +clientPreprocessor?: ?MessageProcessor, - +adapterPreprocessor?: ?MessageProcessor, - +customDisposable?: UniversalDisposable, - +onInitializeCallback?: (session: ISession) => Promise, - +processName?: string, - +customControlButtons?: Array, - +threadsComponentTitle?: string, - +showThreads?: boolean, -|}; - -export interface IVsAdapterSpawner { - spawnAdapter( - adapter: VSAdapterExecutableInfo, - ): ConnectableObservable; - write(input: string): Promise; -} - -export type MessageProcessor = (message: Object) => void; - -export type AutoGenPropertyPrimitiveType = - | 'string' - | 'number' - | 'boolean' - | 'path'; - -export type AutoGenPropertyType = - | AutoGenPropertyPrimitiveType - | 'array' - | 'enum' - | 'object' - | 'json' - | 'deviceAndPackage' - | 'deviceAndProcess' - | 'selectSources' - | 'process'; - -export type AutoGenProperty = { - name: string, - type: AutoGenPropertyType, - itemType?: AutoGenPropertyPrimitiveType, - description: string, - defaultValue?: any, - required: boolean, - visible: boolean, - enums?: string[], - enumsDefaultValue?: string, -}; - -export type ResolveConfig = (config: Object) => Promise; - -type AutoGenLaunchOrAttachConfigBase = { - // General Properties - properties: AutoGenProperty[], - threads: boolean, - vsAdapterType: VsAdapterType, - cwdPropertyName?: ?string, - scriptExtension?: string, - scriptPropertyName?: ?string, - header?: React.Node, - // If you want to overwrite the previously saved parameters, - // set this flag to true and pass in the new values as the defaultValue in the config. - ignorePreviousParams?: ?boolean, - getProcessName: (values: Object) => string, -}; - -export type AutoGenLaunchConfig = AutoGenLaunchOrAttachConfigBase & { - // Disjoint Union Flag - launch: true, - // Launch Specific Properties -}; - -export type AutoGenAttachConfig = AutoGenLaunchOrAttachConfigBase & { - // Disjoint Union Flag - launch: false, - // General Properties - // Attach Specific Properties -}; - -export type AutoGenLaunchOrAttachConfig = - | AutoGenLaunchConfig - | AutoGenAttachConfig; - -export type AutoGenConfig = {| - launch: ?AutoGenLaunchConfig, - attach: ?AutoGenAttachConfig, -|}; - -export type LaunchAttachProviderIsEnabled = ( - action: DebuggerConfigAction, - config: AutoGenConfig, -) => Promise; - -export interface DebuggerConfigurationProvider { - resolveConfiguration(configuration: IProcessConfig): Promise; - adapterType: VsAdapterType; -} - -export interface DebuggerPathResolverProvider { - resolvePath(project: string, filePath: string): Promise; -} - -// -// Device Panel Types -// - -// -// Task interface -// + DebugProtocol = function () { + return data; + }; -export interface Task { - getName(): string; - getTaskEvents(): Observable; - start(): void; - cancel(): void; + return data; } -// -// Api -// +var React = _interopRequireWildcard(require("react")); -export type DevicePanelServiceApi = { - registerListProvider: (provider: DeviceListProvider) => IDisposable, - registerInfoProvider: (provider: DeviceInfoProvider) => IDisposable, - registerProcessesProvider: (provider: DeviceProcessesProvider) => IDisposable, - registerDeviceTaskProvider: (provider: DeviceTaskProvider) => IDisposable, - registerProcessTaskProvider: ( - provider: DeviceProcessTaskProvider, - ) => IDisposable, - registerDeviceTypeTaskProvider: ( - provider: DeviceTypeTaskProvider, - ) => IDisposable, - registerAppInfoProvider: (provider: DeviceAppInfoProvider) => IDisposable, - registerDeviceTypeComponentProvider: ( - provider: DeviceTypeComponentProvider, - ) => IDisposable, -}; - -export interface DeviceListProvider { - observe(host: NuclideUri): Observable>>; - getType(): string; -} - -export interface DeviceInfoProvider { - fetch(host: NuclideUri, device: Device): Observable>; - getType(): string; - getTitle(): string; - getPriority(): number; - isSupported(host: NuclideUri): Observable; -} - -export interface DeviceProcessesProvider { - observe(host: NuclideUri, device: Device): Observable; - getType(): string; -} - -export type DeviceTask = { - getEvents: () => Observable, - getName: () => string, -}; - -export interface DeviceTaskProvider { - getDeviceTasks( - host: NuclideUri, - device: Device, - ): Observable>; - getType(): string; -} - -export interface DeviceTypeTaskProvider { - getDeviceTypeTask(host: NuclideUri): Observable; - getName(): string; - getType(): string; -} - -export interface DeviceProcessTaskProvider { - run(host: NuclideUri, device: Device, proc: Process): Promise; - getTaskType(): ProcessTaskType; - getType(): string; - getSupportedPIDs( - host: NuclideUri, - device: Device, - procs: Process[], - ): Observable>; - getName(): string; -} - -export interface DeviceAppInfoProvider { - observe(host: NuclideUri, device: Device): Observable; - getName(): string; - getType(): string; - getProcessName(): string; - getAppName(): string; - canUpdate(): boolean; - update(value: string): Promise; -} - -export type ComponentPosition = 'host_selector' | 'above_table' | 'below_table'; - -export type DeviceTypeComponent = { - position: ComponentPosition, - type: React$ComponentType, - key: string, -}; - -export interface DeviceTypeComponentProvider { - getType(): string; - observe( - host: NuclideUri, - callback: (?DeviceTypeComponent) => void, - ): IDisposable; -} - -// -// Basic objects -// - -export type Device = {| - // Must be unique within platform, not shown to user - identifier: string, - // Used to display in all UI - displayName: string, - ignoresSelection?: boolean, -|}; - -export type Process = { - user: string, - pid: number, - name: string, - cpuUsage: ?number, - memUsage: ?number, - isJava: boolean, -}; - -export type ProcessTaskType = 'KILL' | 'DEBUG'; - -export type ProcessTask = { - type: ProcessTaskType, - run: (proc: Process) => Promise, - isSupported: (proc: Process) => boolean, - name: string, -}; - -export type AppInfoRow = { - appName: string, - name: string, - value: string, - isError?: boolean, -}; - -export interface DebuggerSourcePathsService { - addKnownJavaSubdirectoryPaths( - remote: boolean, - translatedPath: string, - searchPaths: Array, - ): void; - - observeSuggestedAndroidProjectPaths( - callback: (Array) => void, - ): IDisposable; -} +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } \ No newline at end of file diff --git a/modules/nuclide-debugger-common/utils.js b/modules/nuclide-debugger-common/utils.js index f58f613c..f7abfe53 100644 --- a/modules/nuclide-debugger-common/utils.js +++ b/modules/nuclide-debugger-common/utils.js @@ -1,3 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.observeProjectPathsAllFromSourcePathsService = observeProjectPathsAllFromSourcePathsService; + +function _projects() { + const data = require("../nuclide-commons-atom/projects"); + + _projects = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,37 +23,22 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ +function observeProjectPathsAllFromSourcePathsService(callback) { + let _sourcePathsService; -import type { - SuggestedProjectPath, - DebuggerSourcePathsService, -} from 'atom-ide-debugger-java/types'; - -import {observeProjectPathsAll} from 'nuclide-commons-atom/projects'; - -export function observeProjectPathsAllFromSourcePathsService( - callback: (Array) => void, -) { - let _sourcePathsService: ?DebuggerSourcePathsService; - atom.packages.serviceHub - .consume('debugger.sourcePaths', '0.0.0', provider => { - _sourcePathsService = provider; - }) - .dispose(); - return _sourcePathsService != null - ? _sourcePathsService.observeSuggestedAndroidProjectPaths(callback) - : observeProjectPathsAll(projectPaths => { - callback( - projectPaths.map(projectPath => { - return { - projectPath, - suggested: true, - hostLabel: projectPath, - }; - }), - ); - }); -} + atom.packages.serviceHub.consume('debugger.sourcePaths', '0.0.0', provider => { + _sourcePathsService = provider; + }).dispose(); + return _sourcePathsService != null ? _sourcePathsService.observeSuggestedAndroidProjectPaths(callback) : (0, _projects().observeProjectPathsAll)(projectPaths => { + callback(projectPaths.map(projectPath => { + return { + projectPath, + suggested: true, + hostLabel: projectPath + }; + })); + }); +} \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/__tests__/FallbackMatcher-test.js b/modules/nuclide-fuzzy-native/__tests__/FallbackMatcher-test.js index cfb83d47..28b5d7eb 100644 --- a/modules/nuclide-fuzzy-native/__tests__/FallbackMatcher-test.js +++ b/modules/nuclide-fuzzy-native/__tests__/FallbackMatcher-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _FallbackMatcher() { + const data = require("../lib/FallbackMatcher"); + + _FallbackMatcher = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,44 +18,32 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {Matcher} from '../lib/FallbackMatcher'; - describe('FallbackMatcher', () => { it('should match the native API', () => { - const matcher = new Matcher(['test']); - expect(matcher.match('test')).toEqual([ - { - value: 'test', - score: 0, - matchIndexes: [], - }, - ]); - + const matcher = new (_FallbackMatcher().Matcher)(['test']); + expect(matcher.match('test')).toEqual([{ + value: 'test', + score: 0, + matchIndexes: [] + }]); matcher.addCandidates(['test2']); - expect(matcher.match('test')).toEqual([ - { - value: 'test', - score: 0, - matchIndexes: [], - }, - { - value: 'test2', - score: 5, - matchIndexes: [], - }, - ]); - + expect(matcher.match('test')).toEqual([{ + value: 'test', + score: 0, + matchIndexes: [] + }, { + value: 'test2', + score: 5, + matchIndexes: [] + }]); matcher.removeCandidates(['test']); - expect(matcher.match('test')).toEqual([ - { - value: 'test2', - score: 5, - matchIndexes: [], - }, - ]); + expect(matcher.match('test')).toEqual([{ + value: 'test2', + score: 5, + matchIndexes: [] + }]); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/__tests__/QueryItem-test.js b/modules/nuclide-fuzzy-native/__tests__/QueryItem-test.js index 337f60c7..44fe0ee5 100644 --- a/modules/nuclide-fuzzy-native/__tests__/QueryItem-test.js +++ b/modules/nuclide-fuzzy-native/__tests__/QueryItem-test.js @@ -1,3 +1,27 @@ +"use strict"; + +function _QueryItem() { + const data = _interopRequireWildcard(require("../lib/QueryItem")); + + _QueryItem = function () { + return data; + }; + + return data; +} + +function _collection() { + const data = require("../../nuclide-commons/collection"); + + _collection = function () { + return data; + }; + + return data; +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,144 +30,141 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; -import QueryItem from '../lib/QueryItem'; - describe('QueryItem', () => { describe('"Hello"', () => { - const item = new QueryItem('Hello'); - + const item = new (_QueryItem().default)('Hello'); it('should return 0 for empty queries', () => { expect(item.score('')).toEqual({ score: 0, value: 'Hello', - matchIndexes: [], + matchIndexes: [] }); }); - it('should return null on no match', () => { expect(item.score('z')).toBe(null); }); - it('should return null on non-sequential matches', () => { expect(item.score('lh')).toBe(null); }); - it('should ignore query case', () => { const score1 = item.score('He'); const score2 = item.score('he'); - invariant(score1); - invariant(score2); + + if (!score1) { + throw new Error("Invariant violation: \"score1\""); + } + + if (!score2) { + throw new Error("Invariant violation: \"score2\""); + } + expect(score1.score).toEqual(score2.score); }); - it('should prefer matches where the letters are closer together', () => { const score1 = item.score('he'); const score2 = item.score('hl'); const score3 = item.score('ho'); - invariant(score1); - invariant(score2); - invariant(score3); + + if (!score1) { + throw new Error("Invariant violation: \"score1\""); + } + + if (!score2) { + throw new Error("Invariant violation: \"score2\""); + } + + if (!score3) { + throw new Error("Invariant violation: \"score3\""); + } + expect(score1.score).toBeLessThan(score2.score); expect(score2.score).toBeLessThan(score3.score); }); }); - describe('Path Separator', () => { - const item = new QueryItem('He/y/Hello'); - - // TODO match indices not yet implemented. These are not provided by the FBIDE algorithm. + const item = new (_QueryItem().default)('He/y/Hello'); // TODO match indices not yet implemented. These are not provided by the FBIDE algorithm. // eslint-disable-next-line jasmine/no-disabled-tests + xit('should prefer matches after the last path separator', () => { const score = item.score('h'); - invariant(score); + + if (!score) { + throw new Error("Invariant violation: \"score\""); + } + expect(score.matchIndexes).toEqual([5]); }); - it('should return null if no matches appeared after the last path separator', () => { expect(item.score('hey')).toBe(null); }); - it('should not be able to match characters before the separator', () => { expect(item.score('heyh')).toBe(null); }); }); - describe('Misc', () => { // TODO match indices not yet implemented. These are not provided by the FBIDE algorithm. // eslint-disable-next-line jasmine/no-disabled-tests xit('should prefer matches with an initialism', () => { - const item = new QueryItem('AbBa'); + const item = new (_QueryItem().default)('AbBa'); const score = item.score('ab'); - invariant(score); + + if (!score) { + throw new Error("Invariant violation: \"score\""); + } + expect(score.matchIndexes).toEqual([0, 2]); }); - it('should be able to fall back to substring match when an initialism skip fails', () => { - const item = new QueryItem('AbBa'); + const item = new (_QueryItem().default)('AbBa'); // If the query could initially trigger a skip then fail, still treturn a result. - // If the query could initially trigger a skip then fail, still treturn a result. expect(item.score('bb')).not.toBe(null); }); }); - describe('Extensions', () => { it('should match full filenames', () => { - const item = new QueryItem('Hello.h'); + const item = new (_QueryItem().default)('Hello.h'); expect(item.score('hello.h')).not.toBe(null); expect(item.score('Hello.h')).not.toBe(null); }); }); }); -import {__test__} from '../lib/QueryItem'; const { checkIfMatchesCamelCaseLetters, isLetterImportant, importantCharactersForString, - scoreCommonSubsequence, -} = __test__; + scoreCommonSubsequence +} = _QueryItem().__test__; describe('checkIfMatchesCamelCaseLetters', () => { it('matches when all letters in `needle` are present as uppercase letters in `haystack`', () => { expect(checkIfMatchesCamelCaseLetters('fbide', 'fbide')).toBe(false); expect(checkIfMatchesCamelCaseLetters('fbide', 'FBIDE')).toBe(true); - expect( - checkIfMatchesCamelCaseLetters( - 'fbide', - 'FaceBookIntegratedDevelopmentEnvironment', - ), - ).toBe(true); + expect(checkIfMatchesCamelCaseLetters('fbide', 'FaceBookIntegratedDevelopmentEnvironment')).toBe(true); expect(checkIfMatchesCamelCaseLetters('fb', 'FooBar.js')).toBe(true); expect(checkIfMatchesCamelCaseLetters('fb', 'FooBarBaz.js')).toBe(false); }); - it('is indifferent about the case of only the first characted in `haystack`', () => { expect(checkIfMatchesCamelCaseLetters('fbide', 'fBIDE')).toBe(true); expect(checkIfMatchesCamelCaseLetters('fbide', 'fbIDE')).toBe(false); }); }); - describe('scoreCommonSubsequence', () => { it('returns -1 if there is no common subsequence', () => { expect(scoreCommonSubsequence('nuclide', 'noclide')).toEqual(-1); expect(scoreCommonSubsequence('nuclide', 'nucl')).toEqual(-1); expect(scoreCommonSubsequence('nuclide', '')).toEqual(-1); }); - it('returns a score of 0 for exact matches', () => { expect(scoreCommonSubsequence('nuclide', 'nuclide')).toEqual(0); }); - it('ignores non-alphanumeric characters in `haystack`', () => { expect(scoreCommonSubsequence('nuclide', 'n u c l i d e')).toEqual(0); expect(scoreCommonSubsequence('nuclide', 'n_u-c.l?i!d@e')).toEqual(0); }); - it('returns the correct relevance score for a given needle in a simple haystack', () => { expect(scoreCommonSubsequence('nuclid', 'nuclide')).toEqual(13); expect(scoreCommonSubsequence('nucli', 'nuclide')).toEqual(12); @@ -153,24 +174,21 @@ describe('scoreCommonSubsequence', () => { expect(scoreCommonSubsequence('n', 'nuclide')).toEqual(8); expect(scoreCommonSubsequence('', 'nuclide')).toEqual(7); }); - it('returns the correct relevance score for a given needle in a complex haystack', () => { expect(scoreCommonSubsequence('needle', 'needles')).toEqual(13); expect(scoreCommonSubsequence('needle', 'aneedle')).toEqual(34); - expect(scoreCommonSubsequence('needle', 'aBunchOfNeedles')).toEqual(81); - // TODO this shows that clustering can be improved, as the following scores should be identical: + expect(scoreCommonSubsequence('needle', 'aBunchOfNeedles')).toEqual(81); // TODO this shows that clustering can be improved, as the following scores should be identical: + expect(scoreCommonSubsequence('needle', 'twoneedle')).toEqual(42); expect(scoreCommonSubsequence('needle', 'oneneedle')).toEqual(78); }); }); - describe('isLetterImportant', () => { it('considers the first two letters important', () => { expect(isLetterImportant(0, 'foobar')).toBe(true); expect(isLetterImportant(1, 'foobar')).toBe(true); expect(isLetterImportant(2, 'foobar')).toBe(false); }); - it('considers capital letters important', () => { expect(isLetterImportant(3, 'fooBarBaz')).toBe(true); expect(isLetterImportant(4, 'fooBarBaz')).toBe(false); @@ -178,7 +196,6 @@ describe('isLetterImportant', () => { expect(isLetterImportant(6, 'fooBarBaz')).toBe(true); expect(isLetterImportant(7, 'fooBarBaz')).toBe(false); }); - it('considers letters following delimiting characters important', () => { expect(isLetterImportant(2, 'iam_a-delimited.file')).toBe(false); expect(isLetterImportant(3, 'iam_a-delimited.file')).toBe(false); @@ -188,31 +205,11 @@ describe('isLetterImportant', () => { expect(isLetterImportant(16, 'iam_a-delimited.file')).toBe(true); }); }); - -import {areSetsEqual} from 'nuclide-commons/collection'; - describe('importantCharactersForString', () => { it('returns the set of important characters for a given string', () => { - expect( - areSetsEqual(importantCharactersForString('foobar'), new Set(['f', 'o'])), - ).toBe(true); - - expect( - areSetsEqual( - importantCharactersForString('fooBar'), - new Set(['f', 'o', 'B']), - ), - ).toBe(true); - - expect( - areSetsEqual( - importantCharactersForString('foo.bar'), - new Set(['f', 'o', 'b']), - ), - ).toBe(true); - - expect( - areSetsEqual(importantCharactersForString('foobar'), new Set(['f', 'o'])), - ).toBe(true); + expect((0, _collection().areSetsEqual)(importantCharactersForString('foobar'), new Set(['f', 'o']))).toBe(true); + expect((0, _collection().areSetsEqual)(importantCharactersForString('fooBar'), new Set(['f', 'o', 'B']))).toBe(true); + expect((0, _collection().areSetsEqual)(importantCharactersForString('foo.bar'), new Set(['f', 'o', 'b']))).toBe(true); + expect((0, _collection().areSetsEqual)(importantCharactersForString('foobar'), new Set(['f', 'o']))).toBe(true); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/__tests__/TopScores-test.js b/modules/nuclide-fuzzy-native/__tests__/TopScores-test.js index 8a167370..c8cc35d0 100644 --- a/modules/nuclide-fuzzy-native/__tests__/TopScores-test.js +++ b/modules/nuclide-fuzzy-native/__tests__/TopScores-test.js @@ -1,3 +1,17 @@ +"use strict"; + +function _TopScores() { + const data = _interopRequireDefault(require("../lib/TopScores")); + + _TopScores = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,70 +20,100 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import TopScores from '../lib/TopScores'; - describe('TopScores', () => { it('.getTopScores() returns the top scores', () => { - const topScores = new TopScores(3); - - const one = {score: 1, value: 'one'}; - const two = {score: 2, value: 'two'}; - const three = {score: 3, value: 'three'}; - const four = {score: 4, value: 'four'}; - const five = {score: 5, value: 'five'}; - const six = {score: 6, value: 'six'}; - + const topScores = new (_TopScores().default)(3); + const one = { + score: 1, + value: 'one' + }; + const two = { + score: 2, + value: 'two' + }; + const three = { + score: 3, + value: 'three' + }; + const four = { + score: 4, + value: 'four' + }; + const five = { + score: 5, + value: 'five' + }; + const six = { + score: 6, + value: 'six' + }; topScores.insert(six); topScores.insert(four); topScores.insert(two); topScores.insert(three); topScores.insert(one); topScores.insert(five); - expect(topScores.getTopScores()).toEqual([one, two, three]); }); - it('getTopScores() uses Score.value as a tiebreaker', () => { - const scores = [ - {score: 1, value: 'Cat'}, - {score: 1, value: 'apple'}, - {score: 1, value: 'Apple'}, - {score: 1, value: ''}, - {score: 2, value: 'Z'}, - {score: 3, value: 'ball'}, - {score: 1, value: 'cAt'}, - {score: 1, value: 'cat'}, - {score: 1, value: 'CAT'}, - ]; - const topScores = new TopScores(scores.length); + const scores = [{ + score: 1, + value: 'Cat' + }, { + score: 1, + value: 'apple' + }, { + score: 1, + value: 'Apple' + }, { + score: 1, + value: '' + }, { + score: 2, + value: 'Z' + }, { + score: 3, + value: 'ball' + }, { + score: 1, + value: 'cAt' + }, { + score: 1, + value: 'cat' + }, { + score: 1, + value: 'CAT' + }]; + const topScores = new (_TopScores().default)(scores.length); scores.forEach(score => topScores.insert(score)); - expect(topScores.getTopScores().map(score => score.value)).toEqual([ - 'cat', - 'cAt', - 'Cat', - 'CAT', - 'apple', - 'Apple', - '', - 'Z', - 'ball', - ]); + expect(topScores.getTopScores().map(score => score.value)).toEqual(['cat', 'cAt', 'Cat', 'CAT', 'apple', 'Apple', '', 'Z', 'ball']); }); - it('.getSize() returns the size of the heap', () => { - const topScores = new TopScores(3); + const topScores = new (_TopScores().default)(3); expect(topScores.getSize()).toEqual(0); - - const one = {score: 1, value: 'one'}; - const two = {score: 2, value: 'two'}; - const three = {score: 3, value: 'three'}; - const four = {score: 4, value: 'four'}; - const five = {score: 5, value: 'five'}; - + const one = { + score: 1, + value: 'one' + }; + const two = { + score: 2, + value: 'two' + }; + const three = { + score: 3, + value: 'three' + }; + const four = { + score: 4, + value: 'four' + }; + const five = { + score: 5, + value: 'five' + }; topScores.insert(five); expect(topScores.getSize()).toEqual(1); topScores.insert(four); @@ -81,4 +125,4 @@ describe('TopScores', () => { topScores.insert(one); expect(topScores.getSize()).toEqual(3); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/__tests__/fuzzy-native-test.js b/modules/nuclide-fuzzy-native/__tests__/fuzzy-native-test.js index ab4162b5..e104d7ef 100644 --- a/modules/nuclide-fuzzy-native/__tests__/fuzzy-native-test.js +++ b/modules/nuclide-fuzzy-native/__tests__/fuzzy-native-test.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,16 +8,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - describe('fuzzy-native', () => { it('can be required', () => { const fuzzyNative = require('..'); - const matcher = new fuzzyNative.Matcher(['test']); - // The fallback uses a different scoring mechanism, so this will fail + + const matcher = new fuzzyNative.Matcher(['test']); // The fallback uses a different scoring mechanism, so this will fail // if the native module failed to load. - expect(matcher.match('test')).toEqual([{value: 'test', score: 1}]); + + expect(matcher.match('test')).toEqual([{ + value: 'test', + score: 1 + }]); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/__tests__/utils-test.js b/modules/nuclide-fuzzy-native/__tests__/utils-test.js index e4b7c043..4e6f45d5 100644 --- a/modules/nuclide-fuzzy-native/__tests__/utils-test.js +++ b/modules/nuclide-fuzzy-native/__tests__/utils-test.js @@ -1,3 +1,15 @@ +"use strict"; + +function _utils() { + const data = require("../lib/utils"); + + _utils = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,63 +18,78 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - -import {scoreComparator, valueComparator} from '../lib/utils'; - describe('utils', () => { describe('scoreComparator', () => { it('returns >1 when the first score is greater', () => { - expect( - scoreComparator({score: 2, value: ''}, {score: 1, value: ''}), - ).toBeGreaterThan(0); - expect( - scoreComparator({score: 2, value: 'A'}, {score: 2, value: 'a'}), - ).toBeGreaterThan(0); + expect((0, _utils().scoreComparator)({ + score: 2, + value: '' + }, { + score: 1, + value: '' + })).toBeGreaterThan(0); + expect((0, _utils().scoreComparator)({ + score: 2, + value: 'A' + }, { + score: 2, + value: 'a' + })).toBeGreaterThan(0); }); - it('returns <1 when the second score is greater', () => { - expect( - scoreComparator({score: 1, value: ''}, {score: 2, value: ''}), - ).toBeLessThan(0); - expect( - scoreComparator({score: 2, value: 'a'}, {score: 2, value: 'A'}), - ).toBeLessThan(0); + expect((0, _utils().scoreComparator)({ + score: 1, + value: '' + }, { + score: 2, + value: '' + })).toBeLessThan(0); + expect((0, _utils().scoreComparator)({ + score: 2, + value: 'a' + }, { + score: 2, + value: 'A' + })).toBeLessThan(0); }); - it('returns 0 when the scores are equal', () => { - expect( - scoreComparator({score: 1, value: ''}, {score: 1, value: ''}), - ).toBe(0); - expect( - scoreComparator({score: 2, value: 'A'}, {score: 2, value: 'A'}), - ).toBe(0); + expect((0, _utils().scoreComparator)({ + score: 1, + value: '' + }, { + score: 1, + value: '' + })).toBe(0); + expect((0, _utils().scoreComparator)({ + score: 2, + value: 'A' + }, { + score: 2, + value: 'A' + })).toBe(0); }); }); - describe('valueComparator', () => { it('alpha-sorts and breaks ties with capital letters first', () => { - expect(valueComparator('A', 'A')).toBe(0); - expect(valueComparator('a', 'a')).toBe(0); - expect(valueComparator('A', 'a')).toBeLessThan(0); - expect(valueComparator('a', 'A')).toBeGreaterThan(0); + expect((0, _utils().valueComparator)('A', 'A')).toBe(0); + expect((0, _utils().valueComparator)('a', 'a')).toBe(0); + expect((0, _utils().valueComparator)('A', 'a')).toBeLessThan(0); + expect((0, _utils().valueComparator)('a', 'A')).toBeGreaterThan(0); }); - it('shorter string sorts first', () => { - expect(valueComparator('Bb', 'Bba')).toBeLessThan(0); - expect(valueComparator('Bba', 'Bb')).toBeGreaterThan(0); + expect((0, _utils().valueComparator)('Bb', 'Bba')).toBeLessThan(0); + expect((0, _utils().valueComparator)('Bba', 'Bb')).toBeGreaterThan(0); }); - it('tiebreaker applied only for first non-matching character', () => { - expect(valueComparator('CAT', 'CaT')).toBeLessThan(0); - expect(valueComparator('CaT', 'CAT')).toBeGreaterThan(0); + expect((0, _utils().valueComparator)('CAT', 'CaT')).toBeLessThan(0); + expect((0, _utils().valueComparator)('CaT', 'CAT')).toBeGreaterThan(0); }); - it('behaves like localeCompare', () => { - expect(valueComparator('apple', 'CAT')).toBeLessThan(0); - expect(valueComparator('CAT', 'Cat')).toBeLessThan(0); + expect((0, _utils().valueComparator)('apple', 'CAT')).toBeLessThan(0); + expect((0, _utils().valueComparator)('CAT', 'Cat')).toBeLessThan(0); }); }); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/lib/FallbackMatcher.js b/modules/nuclide-fuzzy-native/lib/FallbackMatcher.js index a130034a..4cfc5a8a 100644 --- a/modules/nuclide-fuzzy-native/lib/FallbackMatcher.js +++ b/modules/nuclide-fuzzy-native/lib/FallbackMatcher.js @@ -1,3 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Matcher = void 0; + +function _QueryItem() { + const data = _interopRequireDefault(require("./QueryItem")); + + _QueryItem = function () { + return data; + }; + + return data; +} + +function _TopScores() { + const data = _interopRequireDefault(require("./TopScores")); + + _TopScores = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,57 +35,54 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ -import type { - MatcherOptions, - MatchResult, -} from 'nuclide-prebuilt-libs/fuzzy-native'; - -import QueryItem from './QueryItem'; -import TopScores from './TopScores'; - /** * Fallback `Matcher` class compatible with the fuzzy-native implementation. * Note that the scores are different: 0 represents the best match while larger numbers are worse. */ -export class Matcher { - _queryItems: Map; - - constructor(candidates: Array) { +class Matcher { + constructor(candidates) { this.setCandidates(candidates); } - /** * Note: caseSensitive, numThreads, and recordMatchIndexes will be ignored. */ - match(query: string, options: MatcherOptions = {}): Array { - const topScores = new TopScores(options.maxResults || 0); + + + match(query, options = {}) { + const topScores = new (_TopScores().default)(options.maxResults || 0); + this._queryItems.forEach(item => { const score = item.score(query); + if (score != null) { topScores.insert(score); } }); + return topScores.getTopScores(); } - addCandidates(candidates: Array): void { + addCandidates(candidates) { candidates.forEach(candidate => { - this._queryItems.set(candidate, new QueryItem(candidate)); + this._queryItems.set(candidate, new (_QueryItem().default)(candidate)); }); } - removeCandidates(candidates: Array): void { + removeCandidates(candidates) { candidates.forEach(candidate => { this._queryItems.delete(candidate); }); } - setCandidates(candidates: Array): void { + setCandidates(candidates) { this._queryItems = new Map(); this.addCandidates(candidates); } + } + +exports.Matcher = Matcher; \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/lib/QueryItem.js b/modules/nuclide-fuzzy-native/lib/QueryItem.js index b9816da3..956ab11b 100644 --- a/modules/nuclide-fuzzy-native/lib/QueryItem.js +++ b/modules/nuclide-fuzzy-native/lib/QueryItem.js @@ -1,3 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.__test__ = void 0; + +function _nuclideUri() { + const data = _interopRequireDefault(require("../../nuclide-commons/nuclideUri")); + + _nuclideUri = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,37 +25,34 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import nuclideUri from 'nuclide-commons/nuclideUri'; - -import type {QueryScore} from './QueryScore'; - const NON_UPPERCASE_CHARS_REGEXP = /[^a-z0-9]/g; -function sanitize(str: string) { +function sanitize(str) { return str.toLowerCase().replace(NON_UPPERCASE_CHARS_REGEXP, ''); } - /** * Returns the score of the common subsequence between `needle` and `haystack` or -1 if there is * no common subsequence. * A lower number means `needle` is more relevant to `haystack`. */ -function scoreCommonSubsequence(needle_: string, haystack_: string): number { + + +function scoreCommonSubsequence(needle_, haystack_) { // Sanitize the needle and haystack. const needle = sanitize(needle_); const haystack = sanitize(haystack_); + if (needle.length === haystack.length) { return needle === haystack ? 0 : -1; } - let needleIndex: number = 0; - let haystackIndex: number = 0; - let score: number = 0; - let inGap: boolean = false; + let needleIndex = 0; + let haystackIndex = 0; + let score = 0; + let inGap = false; while (needleIndex < needle.length && haystackIndex < haystack.length) { if (needle[needleIndex] === haystack[haystackIndex]) { @@ -49,9 +65,11 @@ function scoreCommonSubsequence(needle_: string, haystack_: string): number { inGap = true; } } + if (needleIndex >= needle.length) { return score + haystack.length + haystackIndex; } + return -1; } @@ -61,29 +79,30 @@ const NOT_CAPITAL_LETTERS_REGEXP = /[^A-Z]/g; * `haystack`. E.g. 'fbide' matches 'FaceBookIntegratedDevelopmentEnvironment' and * 'faceBookIntegratedDevelopmentEnvironment'. */ -function checkIfMatchesCamelCaseLetters( - needle: string, - haystack: string, -): boolean { - const uppercase = - haystack.substring(0, 1) + - haystack.substring(1).replace(NOT_CAPITAL_LETTERS_REGEXP, ''); + +function checkIfMatchesCamelCaseLetters(needle, haystack) { + const uppercase = haystack.substring(0, 1) + haystack.substring(1).replace(NOT_CAPITAL_LETTERS_REGEXP, ''); return needle.toLowerCase() === uppercase.toLowerCase(); } const CAPITAL_LETTERS_REGEXP = /[A-Z]/; const IMPORTANT_DELIMITERS_REGEXP = /[_\-.]/; -function isLetterImportant(index: number, name: string): boolean { + +function isLetterImportant(index, name) { if (index <= 1) { return true; } + if (CAPITAL_LETTERS_REGEXP.test(name[index])) { return true; } + const previousCharacter = name[index - 1]; + if (IMPORTANT_DELIMITERS_REGEXP.test(previousCharacter)) { return true; } + return false; } /** @@ -92,37 +111,37 @@ function isLetterImportant(index: number, name: string): boolean { * than relying on the index. Once the index is implemented, consumers of this need to be updated. */ // TODO(jxg): replace with "important characters" index. -function importantCharactersForString(str: string): Set { + + +function importantCharactersForString(str) { const importantCharacters = new Set(); + for (let index = 0; index < str.length; index++) { const char = str[index]; + if (!importantCharacters.has(char) && isLetterImportant(index, str)) { importantCharacters.add(char); } } + return importantCharacters; } -export const __test__ = { +const __test__ = { checkIfMatchesCamelCaseLetters, isLetterImportant, importantCharactersForString, - scoreCommonSubsequence, + scoreCommonSubsequence }; +exports.__test__ = __test__; -export default class QueryItem { - _filepath: string; - _filepathLowercase: string; - _filename: string; - _importantCharacters: Set; - - constructor(filepath: string) { +class QueryItem { + constructor(filepath) { this._filepath = filepath; this._filepathLowercase = filepath.toLowerCase(); - this._filename = nuclideUri.basename(this._filepathLowercase); + this._filename = _nuclideUri().default.basename(this._filepathLowercase); this._importantCharacters = importantCharactersForString(this._filename); } - /** * Scores this object's string against the query given. * @@ -145,33 +164,39 @@ export default class QueryItem { * - The more cases of the characters that match, the more likely it is to be what you want. * f.) Sort the results by the score */ - score(query: string): ?QueryScore { + + + score(query) { const score = this._getScoreFor(query); - return score == null - ? null - : {score, value: this._filepath, matchIndexes: []}; + + return score == null ? null : { + score, + value: this._filepath, + matchIndexes: [] + }; } - _getScoreFor(query: string): ?number { + _getScoreFor(query) { // Everything's an equally decent match for the empty string. if (query.length === 0) { return 0; - } - // Check if this a "possible result". + } // Check if this a "possible result". // TODO consider building a directory-level index from important_character -> QueryItem, // akin to FBIDE's implementation. + + const firstChar = query[0].toLowerCase(); + if (!this._importantCharacters.has(firstChar)) { return null; } - if ( - query.length >= 3 && - checkIfMatchesCamelCaseLetters(query, this._filename) - ) { + + if (query.length >= 3 && checkIfMatchesCamelCaseLetters(query, this._filename)) { // If we match the uppercase characters of the filename, we should be ranked the highest return 0; } else { const sub = this._filepathLowercase.indexOf(query.toLowerCase()); + if (sub !== -1 && query.length < this._filename.length) { /** * We add the length of the term so we can be ranked alongside the @@ -188,11 +213,16 @@ export default class QueryItem { // TODO(jxg): Investigate extending scoreCommonSubsequence to consider subsequences // bidirectionally, or use (some proxy for) edit distance. const score = scoreCommonSubsequence(query, this._filename); + if (score !== -1) { return score; } } } + return null; } + } + +exports.default = QueryItem; \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/lib/QueryScore.js b/modules/nuclide-fuzzy-native/lib/QueryScore.js index 66e0fcc5..9a390c31 100644 --- a/modules/nuclide-fuzzy-native/lib/QueryScore.js +++ b/modules/nuclide-fuzzy-native/lib/QueryScore.js @@ -1,17 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict - * @format - */ - -export type QueryScore = { - value: string, - score: number, - matchIndexes?: Array, -}; +"use strict"; \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/lib/TopScores.js b/modules/nuclide-fuzzy-native/lib/TopScores.js index e48bee93..f01e7063 100644 --- a/modules/nuclide-fuzzy-native/lib/TopScores.js +++ b/modules/nuclide-fuzzy-native/lib/TopScores.js @@ -1,3 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _heap() { + const data = _interopRequireDefault(require("heap")); + + _heap = function () { + return data; + }; + + return data; +} + +function _utils() { + const data = require("./utils"); + + _utils = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,15 +35,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import Heap from 'heap'; - -import type {QueryScore} from './QueryScore'; -import {scoreComparator, inverseScoreComparator} from './utils'; - /** * This data structure is designed to hold the top K scores from a collection of * N scores where scores become available one at a time. The expectation is that @@ -26,22 +50,18 @@ import {scoreComparator, inverseScoreComparator} from './utils'; * Therefore, finding the top K scores from a collection of N elements should be * O(N lg K). */ -export default class TopScores { - _capacity: number; - _full: boolean; - _heap: Heap; - _min: ?QueryScore; - - constructor(capacity: number) { +class TopScores { + constructor(capacity) { this._capacity = capacity; this._full = false; - this._heap = new Heap(inverseScoreComparator); + this._heap = new (_heap().default)(_utils().inverseScoreComparator); this._min = null; } - insert(score: QueryScore) { + insert(score) { if (this._full && this._min) { - const cmp = scoreComparator(score, this._min); + const cmp = (0, _utils().scoreComparator)(score, this._min); + if (cmp < 0) { this._doInsert(score); } @@ -50,26 +70,33 @@ export default class TopScores { } } - _doInsert(score: QueryScore) { + _doInsert(score) { if (this._full) { this._heap.replace(score); } else { this._heap.insert(score); + this._full = this._heap.size() === this._capacity; } + this._min = this._heap.peek(); } - getSize(): number { + getSize() { return this._heap.size(); } - /** * @return an Array where Scores will be sorted in ascending order. */ - getTopScores(): Array { + + + getTopScores() { const array = this._heap.toArray(); - array.sort(scoreComparator); + + array.sort(_utils().scoreComparator); return array; } + } + +exports.default = TopScores; \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/lib/main.js b/modules/nuclide-fuzzy-native/lib/main.js index 05611a73..9c841c78 100644 --- a/modules/nuclide-fuzzy-native/lib/main.js +++ b/modules/nuclide-fuzzy-native/lib/main.js @@ -1,3 +1,15 @@ +"use strict"; + +function _log4js() { + const data = require("log4js"); + + _log4js = function () { + return data; + }; + + return data; +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,24 +18,17 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict-local + * strict-local * @format */ - -import {getLogger} from 'log4js'; - -const logger = getLogger('nuclide-fuzzy-native'); - -// Use the pre-built, native module if available. +const logger = (0, _log4js().getLogger)('nuclide-fuzzy-native'); // Use the pre-built, native module if available. // If not, use the fallback JS implementation. + try { // eslint-disable-next-line nuclide-internal/no-commonjs module.exports = require('nuclide-prebuilt-libs/fuzzy-native'); } catch (e) { - logger.error( - 'Failed to load native fuzzy matching. Falling back to JS implementation', - e, - ); - // eslint-disable-next-line nuclide-internal/no-commonjs - module.exports = require('./FallbackMatcher'); -} + logger.error('Failed to load native fuzzy matching. Falling back to JS implementation', e); // eslint-disable-next-line nuclide-internal/no-commonjs + + module.exports = require("./FallbackMatcher"); +} \ No newline at end of file diff --git a/modules/nuclide-fuzzy-native/lib/utils.js b/modules/nuclide-fuzzy-native/lib/utils.js index 38c41864..bc69572b 100644 --- a/modules/nuclide-fuzzy-native/lib/utils.js +++ b/modules/nuclide-fuzzy-native/lib/utils.js @@ -1,3 +1,12 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.valueComparator = valueComparator; +exports.scoreComparator = scoreComparator; +exports.inverseScoreComparator = inverseScoreComparator; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,12 +15,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ -import type {QueryScore} from './QueryScore'; - /** * String comparator that lists the capitalized version of a string before the lowercase version. * @@ -26,36 +33,40 @@ import type {QueryScore} from './QueryScore'; * * @return <0 if a should appear before b in a list; >0 if b should appear before a in a list */ -export function valueComparator(a: string, b: string): number { +function valueComparator(a, b) { const len = Math.min(a.length, b.length); + for (let i = 0; i < len; i++) { const charA = a.charAt(i); const charB = b.charAt(i); + if (charA === charB) { continue; } const aUpper = charA.toUpperCase(); const bUpper = charB.toUpperCase(); - const caseInsensitiveCompare = aUpper.localeCompare(bUpper); + if (caseInsensitiveCompare !== 0) { return caseInsensitiveCompare; - } - - // If we have reached this point, charA and charB are different, but only one of them is + } // If we have reached this point, charA and charB are different, but only one of them is // uppercase. The uppercase one should be returned first. + + return charA === aUpper ? -1 : 1; } return a.length - b.length; } - /** * @return >0 if a is the greater QueryScore; <0 if b is the greater QueryScore. */ -export function scoreComparator(a: QueryScore, b: QueryScore): number { + + +function scoreComparator(a, b) { const cmp = a.score - b.score; + if (cmp !== 0) { return cmp; } else { @@ -63,6 +74,6 @@ export function scoreComparator(a: QueryScore, b: QueryScore): number { } } -export function inverseScoreComparator(a: QueryScore, b: QueryScore): number { +function inverseScoreComparator(a, b) { return scoreComparator(b, a); -} +} \ No newline at end of file diff --git a/modules/nuclide-jasmine/lib/faketimer.js b/modules/nuclide-jasmine/lib/faketimer.js index 400d4093..b714d358 100644 --- a/modules/nuclide-jasmine/lib/faketimer.js +++ b/modules/nuclide-jasmine/lib/faketimer.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +8,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -17,13 +19,14 @@ */ require('jasmine-node'); // eslint-disable-line nuclide-internal/no-commonjs + let now = 0; let timeoutCount = 0; let intervalCount = 0; let timeouts = []; let intervalTimeouts = {}; -function resetTimeouts(): void { +function resetTimeouts() { now = 0; timeoutCount = 0; intervalCount = 0; @@ -31,34 +34,34 @@ function resetTimeouts(): void { intervalTimeouts = {}; } -function fakeSetTimeout(callback: () => ?any, ms: number): number { +function fakeSetTimeout(callback, ms) { const id = ++timeoutCount; timeouts.push([id, now + ms, callback]); - timeouts.sort( - ([, strikeTime0], [, strikeTime1]) => strikeTime0 - strikeTime1, - ); + timeouts.sort(([, strikeTime0], [, strikeTime1]) => strikeTime0 - strikeTime1); return id; } -function fakeClearTimeout(idToClear: number): void { +function fakeClearTimeout(idToClear) { timeouts = timeouts.filter(([id]) => id !== idToClear); } -function fakeSetInterval(callback: () => ?any, ms: number): number { +function fakeSetInterval(callback, ms) { const id = ++intervalCount; + const action = () => { callback(); intervalTimeouts[id] = fakeSetTimeout(action, ms); }; + intervalTimeouts[id] = fakeSetTimeout(action, ms); return id; } -function fakeClearInterval(idToClear: number): void { +function fakeClearInterval(idToClear) { fakeClearTimeout(intervalTimeouts[idToClear]); } -function advanceClock(deltaMs: number): void { +function advanceClock(deltaMs) { const advanceTo = now + deltaMs; while (timeouts.length !== 0 && timeouts[0][1] <= advanceTo) { @@ -69,26 +72,28 @@ function advanceClock(deltaMs: number): void { now = advanceTo; } - /** * Allows tests to use the non-fake setTimeout and clearTimeout functions. */ -function useRealClock(): void { + + +function useRealClock() { jasmine.unspy(global, 'setTimeout'); jasmine.unspy(global, 'clearTimeout'); jasmine.unspy(Date, 'now'); } - /** * Atom does this half-way mock. * https://github.com/atom/atom/blob/v1.12.7/spec/spec-helper.coffee#L169-L174 */ -function useMockClock(): void { + + +function useMockClock() { spyOn(global, 'setInterval').andCallFake(fakeSetInterval); spyOn(global, 'clearInterval').andCallFake(fakeClearInterval); -} +} // Expose the fake timer utils to global to be used by npm spec tests. + -// Expose the fake timer utils to global to be used by npm spec tests. global.resetTimeouts = resetTimeouts; global.fakeSetTimeout = fakeSetTimeout; global.fakeClearTimeout = fakeClearTimeout; @@ -96,18 +101,20 @@ global.fakeSetInterval = fakeSetInterval; global.fakeClearInterval = fakeClearInterval; global.advanceClock = advanceClock; jasmine.useRealClock = useRealClock; -jasmine.useMockClock = useMockClock; -// $FlowIssue: https://github.com/facebook/flow/issues/285 -Object.defineProperty(global, 'now', {get: () => now}); +jasmine.useMockClock = useMockClock; // $FlowIssue: https://github.com/facebook/flow/issues/285 +Object.defineProperty(global, 'now', { + get: () => now +}); /** * This hook is a the first initialization code that happens before any jasmine test case is * executed. This allows to use the fake timing by default and is a direct port from Atom's * `spec-helper.coffee` */ + beforeEach(() => { resetTimeouts(); spyOn(Date, 'now').andCallFake(() => now); spyOn(global, 'setTimeout').andCallFake(fakeSetTimeout); spyOn(global, 'clearTimeout').andCallFake(fakeClearTimeout); -}); +}); \ No newline at end of file diff --git a/modules/nuclide-jasmine/lib/focused.js b/modules/nuclide-jasmine/lib/focused.js index 19a89cc4..ae3c4258 100644 --- a/modules/nuclide-jasmine/lib/focused.js +++ b/modules/nuclide-jasmine/lib/focused.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +8,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -14,34 +16,20 @@ * A port of Atom's focused specs. * https://github.com/atom/jasmine-focused/blob/c922330/src/jasmine-focused.coffee */ - -import invariant from 'assert'; - // eslint-disable-next-line nuclide-internal/no-commonjs -require('jasmine-node'); - -// These are undocumented APIs. The type of jasmine is redefined here, so that +require('jasmine-node'); // These are undocumented APIs. The type of jasmine is redefined here, so that // we don't pollute the real lib def with this nonsense. -const jasmine: { - getEnv(): { - focusPriority?: number, - specFilter?: (spec: any) => boolean, - }, - Env: { - prototype: { - ddescribe?: () => void, - iit?: () => void, - }, - }, -} = - global.jasmine; + + +const jasmine = global.jasmine; function setGlobalFocusPriority(priority) { - const env = jasmine.getEnv(); - // flowlint-next-line sketchy-null-number:off + const env = jasmine.getEnv(); // flowlint-next-line sketchy-null-number:off + if (!env.focusPriority) { env.focusPriority = 1; } + if (priority > env.focusPriority) { env.focusPriority = priority; } @@ -51,47 +39,61 @@ function fdescribe(description, specDefinitions, priority_) { const priority = priority_ != null ? priority_ : 1; setGlobalFocusPriority(priority); const suite = describe(description, specDefinitions); - invariant(suite != null); + + if (!(suite != null)) { + throw new Error("Invariant violation: \"suite != null\""); + } + suite.focusPriority = priority; return suite; } + global.fdescribe = fdescribe; function ffdescribe(description, specDefinitions) { return fdescribe(description, specDefinitions, 2); } + global.ffdescribe = ffdescribe; function fffdescribe(description, specDefinitions) { return fdescribe(description, specDefinitions, 3); } + global.fffdescribe = fffdescribe; function fit(description, definition, priority_) { const priority = priority_ != null ? priority_ : 1; setGlobalFocusPriority(priority); const spec = it(description, definition); - invariant(spec != null); + + if (!(spec != null)) { + throw new Error("Invariant violation: \"spec != null\""); + } + spec.focusPriority = priority; return spec; } + global.fit = fit; function ffit(description, specDefinitions) { return fit(description, specDefinitions, 2); } + global.ffit = ffit; function fffit(description, specDefinitions) { return fit(description, specDefinitions, 3); } + global.fffit = fffit; -jasmine.getEnv().specFilter = function(spec) { +jasmine.getEnv().specFilter = function (spec) { const env = jasmine.getEnv(); const globalFocusPriority = env.focusPriority; - const parent = spec.parentSuite != null ? spec.parentSuite : spec.suite; - // flowlint-next-line sketchy-null-number:off + const parent = spec.parentSuite != null ? spec.parentSuite : spec.suite; // flowlint-next-line sketchy-null-number:off + if (!globalFocusPriority) { return true; } else if (spec.focusPriority >= globalFocusPriority) { @@ -99,16 +101,19 @@ jasmine.getEnv().specFilter = function(spec) { } else if (!parent) { return false; } else { - invariant(typeof env.specFilter === 'function'); + if (!(typeof env.specFilter === 'function')) { + throw new Error("Invariant violation: \"typeof env.specFilter === 'function'\""); + } + return env.specFilter(parent); } -}; +}; // jasmine-node has ddescribe and iit. Remove them in favor of focus. + -// jasmine-node has ddescribe and iit. Remove them in favor of focus. if (typeof jasmine.Env.prototype.ddescribe === 'function') { delete jasmine.Env.prototype.ddescribe; } if (typeof jasmine.Env.prototype.iit === 'function') { delete jasmine.Env.prototype.iit; -} +} \ No newline at end of file diff --git a/modules/nuclide-jasmine/lib/unspy.js b/modules/nuclide-jasmine/lib/unspy.js index 11ad1c89..8e17f9ee 100644 --- a/modules/nuclide-jasmine/lib/unspy.js +++ b/modules/nuclide-jasmine/lib/unspy.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,20 +8,21 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - // eslint-disable-next-line nuclide-internal/no-commonjs require('jasmine-node'); - /** * unspy is a ported utility from Atom's `spec-helper.coffee` that restores the * jasmine spied function on an object to its original value. */ -jasmine.unspy = function unspy(object: Object, methodName: string) { + + +jasmine.unspy = function unspy(object, methodName) { if (!object[methodName].hasOwnProperty('originalValue')) { throw new Error('Not a spy ' + methodName); } + object[methodName] = object[methodName].originalValue; -}; +}; \ No newline at end of file diff --git a/modules/nuclide-jasmine/lib/waitsForPromise.js b/modules/nuclide-jasmine/lib/waitsForPromise.js index c6dd0b7d..91162a78 100644 --- a/modules/nuclide-jasmine/lib/waitsForPromise.js +++ b/modules/nuclide-jasmine/lib/waitsForPromise.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = waitsForPromise; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,22 +13,13 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import invariant from 'assert'; - -type WaitsForPromiseOptions = { - shouldReject?: boolean, - timeout?: number, -}; - -export default function waitsForPromise( - ...args: Array Promise)> -): void { +function waitsForPromise(...args) { let shouldReject; let timeout; + if (args.length > 1) { shouldReject = args[0].shouldReject; timeout = args[0].timeout; @@ -31,48 +29,31 @@ export default function waitsForPromise( } let finished = false; - runs(() => { const fn = args[args.length - 1]; - invariant(typeof fn === 'function'); + + if (!(typeof fn === 'function')) { + throw new Error("Invariant violation: \"typeof fn === 'function'\""); + } + const promise = fn(); + if (shouldReject) { - promise - .then( - () => { - jasmine - .getEnv() - .currentSpec.fail( - 'Expected promise to be rejected, but it was resolved', - ); - }, - () => { - // Do nothing, it's expected. - }, - ) - .then(() => { - finished = true; - }); + promise.then(() => { + jasmine.getEnv().currentSpec.fail('Expected promise to be rejected, but it was resolved'); + }, () => {// Do nothing, it's expected. + }).then(() => { + finished = true; + }); } else { - promise - .then( - () => { - // Do nothing, it's expected. - }, - error => { - const text = error ? error.stack || error.toString() : 'undefined'; - jasmine - .getEnv() - .currentSpec.fail( - `Expected promise to be resolved, but it was rejected with ${text}`, - ); - }, - ) - .then(() => { - finished = true; - }); + promise.then(() => {// Do nothing, it's expected. + }, error => { + const text = error ? error.stack || error.toString() : 'undefined'; + jasmine.getEnv().currentSpec.fail(`Expected promise to be resolved, but it was rejected with ${text}`); + }).then(() => { + finished = true; + }); } }); - waitsFor(timeout, () => finished); -} +} \ No newline at end of file diff --git a/modules/nuclide-jest/atom-reporter.js b/modules/nuclide-jest/atom-reporter.js index da98540a..dd4f7471 100644 --- a/modules/nuclide-jest/atom-reporter.js +++ b/modules/nuclide-jest/atom-reporter.js @@ -1,3 +1,51 @@ +"use strict"; + +function _UniversalDisposable() { + const data = _interopRequireDefault(require("../nuclide-commons/UniversalDisposable")); + + _UniversalDisposable = function () { + return data; + }; + + return data; +} + +var _react = _interopRequireDefault(require("react")); + +var _reactDom = _interopRequireDefault(require("react-dom")); + +function _nullthrows() { + const data = _interopRequireDefault(require("nullthrows")); + + _nullthrows = function () { + return data; + }; + + return data; +} + +function _Model() { + const data = _interopRequireDefault(require("../nuclide-commons/Model")); + + _Model = function () { + return data; + }; + + return data; +} + +function _Jest() { + const data = _interopRequireDefault(require("./frontend/Jest")); + + _Jest = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,57 +54,39 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ - -import type {TestResult, AggregatedResults} from './types'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import nullthrows from 'nullthrows'; - -import Model from 'nuclide-commons/Model'; -import Jest from './frontend/Jest'; - const div = document.createElement('div'); -nullthrows(document.body).appendChild(div); - -type GlobalConfig = Object; - +(0, _nullthrows().default)(document.body).appendChild(div); // Jest seems to be particular about this being a commonjs export // eslint-disable-next-line nuclide-internal/no-commonjs module.exports = class AtomReporter { - _modelSubscription: UniversalDisposable; - _globalConfig: Object; - _options: Object; - model: Model<{ - results: ?AggregatedResults, - }>; - - constructor(globalConfig: GlobalConfig, options: Object) { + constructor(globalConfig, options) { this._globalConfig = globalConfig; this._options = options; - - this.model = new Model({results: null}); - this._modelSubscription = new UniversalDisposable( - this.model.subscribe(state => { - ReactDOM.render(, div); - }), - ); + this.model = new (_Model().default)({ + results: null + }); + this._modelSubscription = new (_UniversalDisposable().default)(this.model.subscribe(state => { + _reactDom.default.render(_react.default.createElement(_Jest().default, { + results: state.results + }), div); + })); } - onTestResult( - config: GlobalConfig, - result: TestResult, - results: AggregatedResults, - ) { - this.model.setState({results}); + onTestResult(config, result, results) { + this.model.setState({ + results + }); } - onRunComplete(contexts: mixed, results: AggregatedResults) { - this.model.setState({results}); + onRunComplete(contexts, results) { + this.model.setState({ + results + }); + this._modelSubscription.dispose(); } -}; + +}; \ No newline at end of file diff --git a/modules/nuclide-jest/emptyObject.js b/modules/nuclide-jest/emptyObject.js index 0d2152ef..bc9b1fdb 100644 --- a/modules/nuclide-jest/emptyObject.js +++ b/modules/nuclide-jest/emptyObject.js @@ -1,3 +1,5 @@ +"use strict"; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,9 +8,8 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ - // eslint-disable-next-line nuclide-internal/no-commonjs -module.exports = {}; +module.exports = {}; \ No newline at end of file diff --git a/modules/nuclide-jest/frontend/Jest.js b/modules/nuclide-jest/frontend/Jest.js index e378d395..aff3ddbf 100644 --- a/modules/nuclide-jest/frontend/Jest.js +++ b/modules/nuclide-jest/frontend/Jest.js @@ -1,167 +1,137 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local - * @format - */ - -import type {AggregatedResults, TestResult, AssertionResult} from '../types'; - -import React from 'react'; - -import {Icon} from 'nuclide-commons-ui/Icon'; - -type Props = { - results: ?AggregatedResults, -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _react = _interopRequireDefault(require("react")); -export default class Jest extends React.Component { +function _Icon() { + const data = require("../../nuclide-commons-ui/Icon"); + + _Icon = function () { + return data; + }; + + return data; +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } + +class Jest extends _react.default.Component { render() { - const {results} = this.props; + const { + results + } = this.props; + if (results == null) { return null; } const success = results.numFailedTests === 0; - - return ( - -
    - {success ? 'Passed!' : 'Failed :('} -
    - {results.numPassedTests} of {results.numTotalTests} passed -
    -
    -
    - - {results.testResults.map(result => ( - - ))} - -
    - Raw Jest JSON Output (debug) -
    {JSON.stringify(results, null, 2)}
    -
    -
    - - ); + return _react.default.createElement(Body, null, _react.default.createElement(Header, { + success: success + }, _react.default.createElement(HeaderLeft, null, success ? 'Passed!' : 'Failed :('), _react.default.createElement("div", null, results.numPassedTests, " of ", results.numTotalTests, " passed")), _react.default.createElement(Main, null, _react.default.createElement(List, null, results.testResults.map(result => _react.default.createElement(ResultItem, { + key: result.testFilePath, + result: result + }))), _react.default.createElement("details", { + style: { + paddingLeft: 40 + } + }, _react.default.createElement("summary", null, "Raw Jest JSON Output (debug)"), _react.default.createElement("pre", null, JSON.stringify(results, null, 2))))); } + } -type ResultProps = { - result: TestResult, -}; -function ResultItem(props: ResultProps) { - const {result} = props; - return ( -
  • - {result.testFilePath} - {result.failureMessage == null ? null : ( -
    {result.failureMessage}
    - )} -
    - - {result.testResults.map(test => ( - - ))} - -
    -
  • - ); +exports.default = Jest; + +function ResultItem(props) { + const { + result + } = props; + return _react.default.createElement("li", null, _react.default.createElement("span", null, result.testFilePath), result.failureMessage == null ? null : _react.default.createElement("pre", null, result.failureMessage), _react.default.createElement("details", { + open: !result.failureMessage + }, _react.default.createElement(List, null, result.testResults.map(test => _react.default.createElement(SingleTestItem, { + key: test.fullName, + test: test + }))))); } -type SingleTestProps = { - test: AssertionResult, -}; -function SingleTestItem(props: SingleTestProps) { - const {test} = props; - return ( -
  • - - {test.title} - {test.failureMessages.length > 0 ? ( - - {test.failureMessages.map(message => { - return ( -
  • -
    {message}
    -
  • - ); - })} - - ) : null} - - ); +function SingleTestItem(props) { + const { + test + } = props; + return _react.default.createElement("li", null, _react.default.createElement(_Icon().Icon, { + icon: statusToIcon[test.status] + }), _react.default.createElement("span", null, test.title), test.failureMessages.length > 0 ? _react.default.createElement(List, null, test.failureMessages.map(message => { + return _react.default.createElement("li", { + key: message + }, _react.default.createElement("pre", null, message)); + })) : null); } function Body(props) { - return ( -
    - ); + return _react.default.createElement("div", Object.assign({ + style: { + display: 'flex', + flexDirection: 'column', + height: '100vh' + } + }, props)); } function Header(props) { - const {success, ...restProps} = props; - return ( -
    - ); + const { + success + } = props, + restProps = _objectWithoutProperties(props, ["success"]); + + return _react.default.createElement("header", Object.assign({ + style: { + alignItems: 'center', + display: 'flex', + height: 48, + backgroundColor: success ? 'green' : 'red', + color: 'white', + flex: '0 0 48', + padding: 20 + } + }, restProps)); } -const HeaderLeft = function(props) { - return
    ; +const HeaderLeft = function (props) { + return _react.default.createElement("div", Object.assign({ + style: { + flex: 1 + } + }, props)); }; -const Main = function(props) { - return ( -
    - ); +const Main = function (props) { + return _react.default.createElement("div", Object.assign({ + style: { + flex: 1, + overflow: 'auto', + padding: '40px 0' + } + }, props)); }; -const List = function(props) { - return ( -
      - ); +const List = function (props) { + return _react.default.createElement("ol", Object.assign({ + style: { + listStyle: 'none', + paddingLeft: 40 + } + }, props)); }; const statusToIcon = { passed: 'check', failed: 'x', pending: 'dash', - skipped: 'dash', -}; + skipped: 'dash' +}; \ No newline at end of file diff --git a/modules/nuclide-jest/types.js b/modules/nuclide-jest/types.js index 83813c33..9a390c31 100644 --- a/modules/nuclide-jest/types.js +++ b/modules/nuclide-jest/types.js @@ -1,141 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -type ConsoleBuffer = Array; - -type LogMessage = string; -type LogEntry = {| - message: LogMessage, - origin: string, - type: LogType, -|}; - -type LogType = - | 'assert' - | 'count' - | 'debug' - | 'dir' - | 'dirxml' - | 'error' - | 'group' - | 'groupCollapsed' - | 'info' - | 'log' - | 'time' - | 'warn'; - -type SerializableError = {| - code?: mixed, - message: string, - stack: ?string, - type?: string, -|}; - -type RawFileCoverage = {| - path: string, - s: {[statementId: number]: number}, - b: {[branchId: number]: number}, - f: {[functionId: number]: number}, - l: {[lineId: number]: number}, - fnMap: {[functionId: number]: any}, - statementMap: {[statementId: number]: any}, - branchMap: {[branchId: number]: any}, - inputSourceMap?: Object, -|}; - -type RawCoverage = { - [filePath: string]: RawFileCoverage, -}; - -type Bytes = number; -type Milliseconds = number; - -export type TestResult = {| - console: ?ConsoleBuffer, - coverage?: RawCoverage, - displayName: ?string, - failureMessage: ?string, - leaks: boolean, - memoryUsage?: Bytes, - numFailingTests: number, - numPassingTests: number, - numPendingTests: number, - perfStats: {| - end: Milliseconds, - start: Milliseconds, - |}, - skipped: boolean, - snapshot: {| - added: number, - fileDeleted: boolean, - matched: number, - unchecked: number, - uncheckedKeys: Array, - unmatched: number, - updated: number, - |}, - sourceMaps: {[sourcePath: string]: string}, - testExecError?: SerializableError, - testFilePath: string, - testResults: Array, -|}; - -type Callsite = {| - column: number, - line: number, -|}; - -type Status = 'passed' | 'failed' | 'skipped' | 'pending'; - -export type AssertionResult = {| - ancestorTitles: Array, - duration?: ?Milliseconds, - failureMessages: Array, - fullName: string, - location: ?Callsite, - numPassingAsserts: number, - status: Status, - title: string, -|}; - -export type AggregatedResults = {| - numFailedTests: number, - numFailedTestSuites: number, - numPassedTests: number, - numPassedTestSuites: number, - numPendingTests: number, - numPendingTestSuites: number, - numRuntimeErrorTestSuites: number, - numTotalTests: number, - numTotalTestSuites: number, - snapshot: SnapshotSummary, - startTime: number, - success: boolean, - testResults: Array, - wasInterrupted: boolean, -|}; - -type SnapshotSummary = {| - added: number, - didUpdate: boolean, - failure: boolean, - filesAdded: number, - filesRemoved: number, - filesUnmatched: number, - filesUpdated: number, - matched: number, - total: number, - unchecked: number, - uncheckedKeys: Array, - unmatched: number, - updated: number, -|}; +"use strict"; \ No newline at end of file diff --git a/modules/nuclide-node-transpiler/__mocks__/fixtures/modern-syntax.js b/modules/nuclide-node-transpiler/__mocks__/fixtures/modern-syntax.js index cf762377..fd2c302a 100644 --- a/modules/nuclide-node-transpiler/__mocks__/fixtures/modern-syntax.js +++ b/modules/nuclide-node-transpiler/__mocks__/fixtures/modern-syntax.js @@ -1,3 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Foo = void 0; + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,10 +13,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow strict + * strict * @format */ +class Foo {} -export class Foo { - static bar = 'qux'; -} +exports.Foo = Foo; +Foo.bar = 'qux'; \ No newline at end of file diff --git a/package.json b/package.json index 47fd3ba0..1e61453d 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,5 @@ "flow-bin": "0.75.0", "nyc": "11.2.1", "prettier": "1.13.6" - }, - "private": true + } }