// Keep a {TextEditor}'s configuration in sync with Atom's settings. // // * `editor` The editor whose configuration will be maintained. // // Returns a {Disposable} that can be used to stop updating the editor's // configuration. maintainConfig (editor) { if (this.editorsWithMaintainedConfig.has(editor)) { return new Disposable(noop) } this.editorsWithMaintainedConfig.add(editor) this.updateAndMonitorEditorSettings(editor) const languageChangeSubscription = editor.buffer.onDidChangeLanguageMode((newLanguageMode, oldLanguageMode) => { this.updateAndMonitorEditorSettings(editor, oldLanguageMode) }) this.subscriptions.add(languageChangeSubscription) const updateTabTypes = () => { const configOptions = {scope: editor.getRootScopeDescriptor()} editor.setSoftTabs(shouldEditorUseSoftTabs( editor, this.config.get('editor.tabType', configOptions), this.config.get('editor.softTabs', configOptions) )) } updateTabTypes() const tokenizeSubscription = editor.onDidTokenize(updateTabTypes) this.subscriptions.add(tokenizeSubscription) return new Disposable(() => { this.editorsWithMaintainedConfig.delete(editor) tokenizeSubscription.dispose() languageChangeSubscription.dispose() this.subscriptions.remove(languageChangeSubscription) this.subscriptions.remove(tokenizeSubscription) }) }
constructor (path, options = {}) { this.id = nextId++ this.emitter = new Emitter() this.subscriptions = new CompositeDisposable() this.repo = GitUtils.open(path) if (this.repo == null) { throw new Error(`No Git repository found searching path: ${path}`) } this.statusRefreshCount = 0 this.statuses = {} this.upstream = {ahead: 0, behind: 0} for (let submodulePath in this.repo.submodules) { const submoduleRepo = this.repo.submodules[submodulePath] submoduleRepo.upstream = {ahead: 0, behind: 0} } this.project = options.project this.config = options.config if (options.refreshOnWindowFocus || options.refreshOnWindowFocus == null) { const onWindowFocus = () => { this.refreshIndex() this.refreshStatus() } window.addEventListener('focus', onWindowFocus) this.subscriptions.add(new Disposable(() => window.removeEventListener('focus', onWindowFocus))) } if (this.project != null) { this.project.getBuffers().forEach(buffer => this.subscribeToBuffer(buffer)) this.subscriptions.add(this.project.onDidAddBuffer(buffer => this.subscribeToBuffer(buffer))) } }
loadUserStylesheet () { this.unwatchUserStylesheet() const userStylesheetPath = this.styleManager.getUserStyleSheetPath() if (!fs.isFileSync(userStylesheetPath)) { return } try { this.userStylesheetFile = new File(userStylesheetPath) this.userStylesheetSubscriptions = new CompositeDisposable() const reloadStylesheet = () => this.loadUserStylesheet() this.userStylesheetSubscriptions.add(this.userStylesheetFile.onDidChange(reloadStylesheet)) this.userStylesheetSubscriptions.add(this.userStylesheetFile.onDidRename(reloadStylesheet)) this.userStylesheetSubscriptions.add(this.userStylesheetFile.onDidDelete(reloadStylesheet)) } catch (error) { const message = `\ Unable to watch path: \`${path.basename(userStylesheetPath)}\`. Make sure you have permissions to \`${userStylesheetPath}\`. On linux there are currently problems with watch sizes. See [this document][watches] for more info. [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path\ ` this.notificationManager.addError(message, {dismissable: true}) } let userStylesheetContents try { userStylesheetContents = this.loadStylesheet(userStylesheetPath, true) } catch (error) { return } this.userStyleSheetDisposable = this.styleManager.addStyleSheet(userStylesheetContents, {sourcePath: userStylesheetPath, priority: 2}) }
constructor (_path, options = {}) { Git.enableThreadSafety() this.emitter = new Emitter() this.subscriptions = new CompositeDisposable() this.pathStatusCache = {} this.path = null // NB: These needs to happen before the following .openRepository call. this.openedPath = _path this._openExactPath = options.openExactPath || false this.repoPromise = this.openRepository() this.isCaseInsensitive = fs.isCaseInsensitive() this.upstream = {} this.submodules = {} this._refreshingPromise = Promise.resolve() let {refreshOnWindowFocus = true} = options if (refreshOnWindowFocus) { const onWindowFocus = () => this.refreshStatus() window.addEventListener('focus', onWindowFocus) this.subscriptions.add(new Disposable(() => window.removeEventListener('focus', onWindowFocus))) } const {project, subscribeToBuffers} = options this.project = project if (this.project && subscribeToBuffers) { this.project.getBuffers().forEach(buffer => this.subscribeToBuffer(buffer)) this.subscriptions.add(this.project.onDidAddBuffer(buffer => this.subscribeToBuffer(buffer))) } }
// Public: Add one or more command listeners associated with a selector. // // ## Arguments: Registering One Command // // * `target` A {String} containing a CSS selector or a DOM element. If you // pass a selector, the command will be globally associated with all matching // elements. The `,` combinator is not currently supported. If you pass a // DOM element, the command will be associated with just that element. // * `commandName` A {String} containing the name of a command you want to // handle such as `user:insert-date`. // * `listener` A listener which handles the event. Either a {Function} to // call when the given command is invoked on an element matching the // selector, or an {Object} with a `didDispatch` property which is such a // function. // // The function (`listener` itself if it is a function, or the `didDispatch` // method if `listener` is an object) will be called with `this` referencing // the matching DOM node and the following argument: // * `event`: A standard DOM event instance. Call `stopPropagation` or // `stopImmediatePropagation` to terminate bubbling early. // // Additionally, `listener` may have additional properties which are returned // to those who query using `atom.commands.findCommands`, as well as several // meaningful metadata properties: // * `displayName`: Overrides any generated `displayName` that would // otherwise be generated from the event name. // * `description`: Used by consumers to display detailed information about // the command. // * `hiddenInCommandPalette`: If `true`, this command will not appear in // the bundled command palette by default, but can still be shown with. // the `Command Palette: Show Hidden Commands` command. This is a good // option when you need to register large numbers of commands that don't // make sense to be executed from the command palette. Please use this // option conservatively, as it could reduce the discoverability of your // package's commands. // // ## Arguments: Registering Multiple Commands // // * `target` A {String} containing a CSS selector or a DOM element. If you // pass a selector, the commands will be globally associated with all // matching elements. The `,` combinator is not currently supported. // If you pass a DOM element, the command will be associated with just that // element. // * `commands` An {Object} mapping command names like `user:insert-date` to // listener {Function}s. // // Returns a {Disposable} on which `.dispose()` can be called to remove the // added command handler(s). add (target, commandName, listener, throwOnInvalidSelector = true) { if (typeof commandName === 'object') { const commands = commandName throwOnInvalidSelector = listener const disposable = new CompositeDisposable() for (commandName in commands) { listener = commands[commandName] disposable.add(this.add(target, commandName, listener, throwOnInvalidSelector)) } return disposable } if (listener == null) { throw new Error('Cannot register a command with a null listener.') } // type Listener = ((e: CustomEvent) => void) | { // displayName?: string, // description?: string, // didDispatch(e: CustomEvent): void, // } if ((typeof listener !== 'function') && (typeof listener.didDispatch !== 'function')) { throw new Error('Listener must be a callback function or an object with a didDispatch method.') } if (typeof target === 'string') { if (throwOnInvalidSelector) { validateSelector(target) } return this.addSelectorBasedListener(target, commandName, listener) } else { return this.addInlineListener(target, commandName, listener) } }
return new Disposable(() => { this.editorsWithMaintainedConfig.delete(editor) tokenizeSubscription.dispose() languageChangeSubscription.dispose() this.subscriptions.remove(languageChangeSubscription) this.subscriptions.remove(tokenizeSubscription) })
constructor( socket: Socket, onStatusCallback: StatusCallback, onNotificationCallback: NotificationCallback, isDummyConnection: boolean, ) { const dbgpSocket = new DbgpSocket(socket); this._socket = dbgpSocket; this._dataCache = new DataCache(dbgpSocket); this._id = connectionCount++; this._status = ConnectionStatus.Starting; this._isDummyConnection = isDummyConnection; this._isDummyViewable = false; this._disposables = new CompositeDisposable(); this._breakCount = 0; if (onStatusCallback != null) { this._disposables.add( this.onStatus((status, ...args) => onStatusCallback(this, status, ...args), ), ); } if (onNotificationCallback != null) { this._disposables.add( this.onNotification((notifyName, notify) => onNotificationCallback(this, notifyName, notify), ), ); } this._stopReason = null; this._stopBreakpointLocation = null; }
return new Disposable(() => { destroySubscription.dispose() pathChangeSubscription.dispose() this.subscriptions.remove(pathChangeSubscription) this.subscriptions.remove(destroySubscription) this.grammarScoresByBuffer.delete(buffer) this.languageOverridesByBufferId.delete(buffer.id) })
return new Disposable(() => { this.editorsWithMaintainedConfig.delete(editor) editor.setScopedSettingsDelegate(null) tokenizeSubscription.dispose() grammarChangeSubscription.dispose() this.subscriptions.remove(grammarChangeSubscription) this.subscriptions.remove(tokenizeSubscription) })
initialize (model, {views}) { this.model = model this.views = views if (this.views == null) { throw new Error('Must pass a views parameter when initializing PanelContainerElements') } this.subscriptions.add(this.model.onDidAddPanel(this.panelAdded.bind(this))) this.subscriptions.add(this.model.onDidDestroy(this.destroyed.bind(this))) this.classList.add(this.model.getLocation()) return this }
initialize (model, viewRegistry) { this.model = model this.viewRegistry = viewRegistry this.subscriptions.add(this.model.onDidAddPanel(this.panelAdded.bind(this))) this.subscriptions.add(this.model.onDidDestroy(this.destroyed.bind(this))) this.classList.add(this.model.getLocation()) // Add the dock. if (this.model.dock != null) { this.appendChild(this.model.dock.getElement()) } return this }
panelAdded ({panel, index}) { const panelElement = panel.getElement() panelElement.classList.add(this.model.getLocation()) if (this.model.isModal()) { panelElement.classList.add('overlay', 'from-top') } else { panelElement.classList.add('tool-panel', `panel-${this.model.getLocation()}`) } if (index >= this.childNodes.length) { this.appendChild(panelElement) } else { const referenceItem = this.childNodes[index] this.insertBefore(panelElement, referenceItem) } if (this.model.isModal()) { this.hideAllPanelsExcept(panel) this.subscriptions.add(panel.onDidChangeVisible(visible => { if (visible) { this.hideAllPanelsExcept(panel) } })) if (panel.autoFocus) { const focusOptions = { // focus-trap will attempt to give focus to the first tabbable element // on activation. If there aren't any tabbable elements, // give focus to the panel element itself fallbackFocus: panelElement, // closing is handled by core Atom commands and this already deactivates // on visibility changes escapeDeactivates: false } if (panel.autoFocus !== true) { focusOptions.initialFocus = panel.autoFocus } const modalFocusTrap = focusTrap(panelElement, focusOptions) this.subscriptions.add(panel.onDidChangeVisible(visible => { if (visible) { modalFocusTrap.activate() } else { modalFocusTrap.deactivate() } })) } } }
// Public: Add the given item to the pane. // // * `item` The item to add. It can be a model with an associated view or a // view. // * `options` (optional) {Object} // * `index` (optional) {Number} indicating the index at which to add the item. // If omitted, the item is added after the current active item. // * `pending` (optional) {Boolean} indicating that the item should be // added in a pending state. Existing pending items in a pane are replaced with // new pending items when they are opened. // // Returns the added item. addItem (item, options = {}) { // Backward compat with old API: // addItem(item, index=@getActiveItemIndex() + 1) if (typeof options === 'number') { Grim.deprecate(`Pane::addItem(item, ${options}) is deprecated in favor of Pane::addItem(item, {index: ${options}})`) options = {index: options} } const index = options.index != null ? options.index : this.getActiveItemIndex() + 1 const moved = options.moved != null ? options.moved : false const pending = options.pending != null ? options.pending : false if (!item || typeof item !== 'object') { throw new Error(`Pane items must be objects. Attempted to add item ${item}.`) } if (typeof item.isDestroyed === 'function' && item.isDestroyed()) { throw new Error(`Adding a pane item with URI '${typeof item.getURI === 'function' && item.getURI()}' that has already been destroyed`) } if (this.items.includes(item)) return if (typeof item.onDidDestroy === 'function') { const itemSubscriptions = new CompositeDisposable() itemSubscriptions.add(item.onDidDestroy(() => this.removeItem(item, false))) if (typeof item.onDidTerminatePendingState === 'function') { itemSubscriptions.add(item.onDidTerminatePendingState(() => { if (this.getPendingItem() === item) this.clearPendingItem() })) } this.subscriptionsPerItem.set(item, itemSubscriptions) } this.items.splice(index, 0, item) const lastPendingItem = this.getPendingItem() const replacingPendingItem = lastPendingItem != null && !moved if (replacingPendingItem) this.pendingItem = null if (pending) this.setPendingItem(item) this.emitter.emit('did-add-item', {item, index, moved}) if (!moved) { if (this.container) this.container.didAddPaneItem(item, this, index) } if (replacingPendingItem) this.destroyItem(lastPendingItem) if (!this.getActiveItem()) this.setActiveItem(item) return item }
async launch (options) { if (!this.configFilePromise) { this.configFilePromise = this.configFile.watch() this.disposable.add(await this.configFilePromise) this.config.onDidChange('core.titleBar', this.promptForRestart.bind(this)) } const optionsForWindowsToOpen = [] let shouldReopenPreviousWindows = false if (options.test || options.benchmark || options.benchmarkTest) { optionsForWindowsToOpen.push(options) } else if ((options.pathsToOpen && options.pathsToOpen.length > 0) || (options.urlsToOpen && options.urlsToOpen.length > 0)) { optionsForWindowsToOpen.push(options) shouldReopenPreviousWindows = this.config.get('core.restorePreviousWindowsOnStart') === 'always' } else { shouldReopenPreviousWindows = this.config.get('core.restorePreviousWindowsOnStart') !== 'no' } if (shouldReopenPreviousWindows) { for (const previousOptions of await this.loadPreviousWindowOptions()) { optionsForWindowsToOpen.push(Object.assign({}, options, previousOptions)) } } if (optionsForWindowsToOpen.length === 0) { optionsForWindowsToOpen.push(options) } return optionsForWindowsToOpen.map(options => this.openWithOptions(options)) }
dispose () { this.subscriptions.dispose() this.disposeProjectMenu() if (this.reopenProjectListView != null) { this.reopenProjectListView.dispose() } }
// Private: Initialize a native watcher on a path. // // Events will not be produced until {start()} is called. constructor (normalizedPath) { this.normalizedPath = normalizedPath this.emitter = new Emitter() this.subs = new CompositeDisposable() this.backend = null this.state = WATCHER_STATE.STOPPED this.onEvents = this.onEvents.bind(this) this.onError = this.onError.bind(this) this.subs.add(atom.config.onDidChange('core.fileSystemWatcher', async () => { if (this.state === WATCHER_STATE.STARTING) { // Wait for this watcher to finish starting. await new Promise(resolve => { const sub = this.onDidStart(() => { sub.dispose() resolve() }) }) } // Re-read the config setting in case it's changed again while we were waiting for the watcher // to start. const Backend = this.getCurrentBackend() if (this.state === WATCHER_STATE.RUNNING && !(this.backend instanceof Backend)) { await this.stop() await this.start() } })) }
_sendArgumentsToPythonBackend( child: child_process$ChildProcess, args: LaunchAttachArgsType ): void { const ARGUMENT_INPUT_FD = 3; /* $FlowFixMe - update Flow defs for ChildProcess */ const argumentsStream = child.stdio[ARGUMENT_INPUT_FD]; // Make sure the bidirectional communication channel is set up before // sending data. argumentsStream.write('init\n'); this._subscriptions.add(new DisposableSubscription( observeStream(argumentsStream).first().subscribe( text => { if (text.startsWith('ready')) { const args_in_json = JSON.stringify(args); logInfo(`Sending ${args_in_json} to child_process`); argumentsStream.write(`${args_in_json}\n`); } else { logError(`Get unknown initial data: ${text}.`); child.kill(); } }, error => logError(`argumentsStream error: ${JSON.stringify(error)}`) ))); }
destroy () { this.alive = false for (let pane of this.getRoot().getPanes()) { pane.destroy() } this.cancelStoppedChangingActivePaneItemTimeout() this.subscriptions.dispose() this.emitter.dispose() }
addBuffer (buffer, options = {}) { this.buffers.push(buffer) this.subscriptions.add(this.grammarRegistry.maintainLanguageMode(buffer)) this.subscribeToBuffer(buffer) this.emitter.emit('did-add-buffer', buffer) return buffer }
// Extended: set a {TextBuffer}'s language mode based on its path and content, // and continue to update its language mode as grammars are added or updated, or // the buffer's file path changes. // // * `buffer` The {TextBuffer} whose language mode will be maintained. // // Returns a {Disposable} that can be used to stop updating the buffer's // language mode. maintainLanguageMode (buffer) { this.grammarScoresByBuffer.set(buffer, null) const languageOverride = this.languageOverridesByBufferId.get(buffer.id) if (languageOverride) { this.assignLanguageMode(buffer, languageOverride) } else { this.autoAssignLanguageMode(buffer) } const pathChangeSubscription = buffer.onDidChangePath(() => { this.grammarScoresByBuffer.delete(buffer) if (!this.languageOverridesByBufferId.has(buffer.id)) { this.autoAssignLanguageMode(buffer) } }) const destroySubscription = buffer.onDidDestroy(() => { this.grammarScoresByBuffer.delete(buffer) this.languageOverridesByBufferId.delete(buffer.id) this.subscriptions.remove(destroySubscription) this.subscriptions.remove(pathChangeSubscription) }) this.subscriptions.add(pathChangeSubscription, destroySubscription) return new Disposable(() => { destroySubscription.dispose() pathChangeSubscription.dispose() this.subscriptions.remove(pathChangeSubscription) this.subscriptions.remove(destroySubscription) this.grammarScoresByBuffer.delete(buffer) this.languageOverridesByBufferId.delete(buffer.id) }) }
subs.add(term.onExit(function () { if (that.subs) { that.subs.remove(subs); } subs.dispose(); delete that.terms[termID]; }));
// Set a {TextEditor}'s grammar based on its path and content, and continue // to update its grammar as grammars are added or updated, or the editor's // file path changes. // // * `editor` The editor whose grammar will be maintained. // // Returns a {Disposable} that can be used to stop updating the editor's // grammar. maintainGrammar (editor) { if (this.editorsWithMaintainedGrammar.has(editor)) { return new Disposable(noop) } this.editorsWithMaintainedGrammar.add(editor) const buffer = editor.getBuffer() for (let existingEditor of this.editorsWithMaintainedGrammar) { if (existingEditor.getBuffer() === buffer) { const existingOverride = this.editorGrammarOverrides[existingEditor.id] if (existingOverride) { this.editorGrammarOverrides[editor.id] = existingOverride } break } } this.selectGrammarForEditor(editor) const pathChangeSubscription = editor.onDidChangePath(() => { this.editorGrammarScores.delete(editor) this.selectGrammarForEditor(editor) }) this.subscriptions.add(pathChangeSubscription) return new Disposable(() => { delete this.editorGrammarOverrides[editor.id] this.editorsWithMaintainedGrammar.delete(editor) this.subscriptions.remove(pathChangeSubscription) pathChangeSubscription.dispose() }) }
async destroy () { const windowsClosePromises = this.getAllWindows().map(window => { window.close() return window.closedPromise }) await Promise.all(windowsClosePromises) this.disposable.dispose() }
subs.add(term.onExit(function () { Socket.send_delete_term({id: termID}); if (that.subs) { that.subs.remove(subs); } subs.dispose(); delete that.terms[termID]; }));
destroy () { this.subscriptions.dispose() this.paneContainer.destroy() window.removeEventListener('mousemove', this.handleMouseMove) window.removeEventListener('mouseup', this.handleMouseUp) window.removeEventListener('drag', this.handleDrag) window.removeEventListener('dragend', this.handleDragEnd) }
// Extended: Unsubscribe all subscribers from filesystem events. Native resources will be released asynchronously, // but this watcher will stop broadcasting events immediately. dispose () { for (const sub of this.changeCallbacks.values()) { sub.dispose() } this.emitter.dispose() this.subs.dispose() }
// Public: Destroy this {GitRepositoryAsync} object. // // This destroys any tasks and subscriptions and releases the underlying // libgit2 repository handle. This method is idempotent. destroy () { this.repo.destroy() if (this.subscriptions) { this.subscriptions.dispose() this.subscriptions = null } }
// Extended: Create a new text editor. // // Returns a {TextEditor}. buildTextEditor (params) { const editor = this.textEditorRegistry.build(params) const subscriptions = new CompositeDisposable( this.textEditorRegistry.maintainGrammar(editor), this.textEditorRegistry.maintainConfig(editor) ) editor.onDidDestroy(() => { subscriptions.dispose() }) return editor }
activate() { this.createCollection() this.disposables = new CompositeDisposable this.disposables.add(atom.commands.add('atom-workspace', { 'todo-show:toggle': (event) => this.show(undefined, event), 'todo-show:find-in-workspace': () => this.show('workspace'), 'todo-show:find-in-project': () => this.show('project'), 'todo-show:find-in-open-files': () => this.show('open'), 'todo-show:find-in-active-file': () => this.show('active') })) this.disposables.add(atom.workspace.addOpener(uri => { if (uri === this.URI) { return this.deserializeTodoView() } })) }
initialize (model, {views}) { this.model = model this.views = views if (this.views == null) { throw new Error('Must pass a views parameter when initializing PaneContainerElements') } this.subscriptions.add(this.model.observeRoot(this.rootChanged.bind(this))) return this }