| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| /** |
| * @implements {Sources.TabbedEditorContainerDelegate} |
| * @implements {UI.Searchable} |
| * @implements {UI.Replaceable} |
| * @unrestricted |
| */ |
| Sources.SourcesView = class extends UI.VBox { |
| /** |
| * @suppressGlobalPropertiesCheck |
| */ |
| constructor() { |
| super(); |
| this.registerRequiredCSS('sources/sourcesView.css'); |
| this.element.id = 'sources-panel-sources-view'; |
| this.setMinimumAndPreferredSizes(88, 52, 150, 100); |
| |
| const workspace = Workspace.workspace; |
| |
| this._searchableView = new UI.SearchableView(this, 'sourcesViewSearchConfig'); |
| this._searchableView.setMinimalSearchQuerySize(0); |
| this._searchableView.show(this.element); |
| |
| /** @type {!Map.<!Workspace.UISourceCode, !UI.Widget>} */ |
| this._sourceViewByUISourceCode = new Map(); |
| |
| this._editorContainer = new Sources.TabbedEditorContainer( |
| this, Common.settings.createLocalSetting('previouslyViewedFiles', []), this._placeholderElement(), |
| this._focusedPlaceholderElement); |
| this._editorContainer.show(this._searchableView.element); |
| this._editorContainer.addEventListener( |
| Sources.TabbedEditorContainer.Events.EditorSelected, this._editorSelected, this); |
| this._editorContainer.addEventListener(Sources.TabbedEditorContainer.Events.EditorClosed, this._editorClosed, this); |
| |
| this._historyManager = new Sources.EditingLocationHistoryManager(this, this.currentSourceFrame.bind(this)); |
| |
| this._toolbarContainerElement = this.element.createChild('div', 'sources-toolbar'); |
| if (!Root.Runtime.experiments.isEnabled('sourcesPrettyPrint')) { |
| this._toolbarEditorActions = new UI.Toolbar('', this._toolbarContainerElement); |
| self.runtime.allInstances(Sources.SourcesView.EditorAction).then(appendButtonsForExtensions.bind(this)); |
| } |
| /** |
| * @param {!Array.<!Sources.SourcesView.EditorAction>} actions |
| * @this {Sources.SourcesView} |
| */ |
| function appendButtonsForExtensions(actions) { |
| for (let i = 0; i < actions.length; ++i) { |
| this._toolbarEditorActions.appendToolbarItem(actions[i].button(this)); |
| } |
| } |
| this._scriptViewToolbar = new UI.Toolbar('', this._toolbarContainerElement); |
| this._scriptViewToolbar.element.style.flex = 'auto'; |
| this._bottomToolbar = new UI.Toolbar('', this._toolbarContainerElement); |
| |
| /** @type {?Common.EventTarget.EventDescriptor} */ |
| this._toolbarChangedListener = null; |
| |
| UI.startBatchUpdate(); |
| workspace.uiSourceCodes().forEach(this._addUISourceCode.bind(this)); |
| UI.endBatchUpdate(); |
| |
| workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this); |
| workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this); |
| workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved, this._projectRemoved.bind(this), this); |
| |
| /** |
| * @param {!Event} event |
| */ |
| function handleBeforeUnload(event) { |
| if (event.returnValue) { |
| return; |
| } |
| |
| let unsavedSourceCodes = []; |
| const projects = Workspace.workspace.projectsForType(Workspace.projectTypes.FileSystem); |
| for (let i = 0; i < projects.length; ++i) { |
| unsavedSourceCodes = |
| unsavedSourceCodes.concat(projects[i].uiSourceCodes().filter(sourceCode => sourceCode.isDirty())); |
| } |
| |
| if (!unsavedSourceCodes.length) { |
| return; |
| } |
| |
| event.returnValue = Common.UIString('DevTools have unsaved changes that will be permanently lost.'); |
| UI.viewManager.showView('sources'); |
| for (let i = 0; i < unsavedSourceCodes.length; ++i) { |
| Common.Revealer.reveal(unsavedSourceCodes[i]); |
| } |
| } |
| |
| if (!window.opener) { |
| window.addEventListener('beforeunload', handleBeforeUnload, true); |
| } |
| |
| this._shortcuts = {}; |
| this.element.addEventListener('keydown', this._handleKeyDown.bind(this), false); |
| } |
| |
| /** |
| * @return {!Element} |
| */ |
| _placeholderElement() { |
| /** @type {!Array.<{element: !Element, handler: !Function}>} */ |
| this._placeholderOptionArray = []; |
| |
| const shortcuts = [ |
| {actionId: 'quickOpen.show', description: ls`Open file`}, |
| {actionId: 'commandMenu.show', description: ls`Run command`}, |
| {actionId: 'sources.add-folder-to-workspace', description: ls`Drop in a folder to add to workspace`} |
| ]; |
| |
| const element = createElementWithClass('div'); |
| const list = element.createChild('div', 'tabbed-pane-placeholder'); |
| list.addEventListener('keydown', this._placeholderOnKeyDown.bind(this), false); |
| UI.ARIAUtils.markAsList(list); |
| UI.ARIAUtils.setAccessibleName(list, ls`Source View Actions`); |
| |
| for (let i = 0; i < shortcuts.length; i++) { |
| const shortcut = shortcuts[i]; |
| const shortcutKeyText = UI.shortcutRegistry.shortcutTitleForAction(shortcut.actionId); |
| const listItemElement = list.createChild('div'); |
| UI.ARIAUtils.markAsListitem(listItemElement); |
| const row = listItemElement.createChild('div', 'tabbed-pane-placeholder-row'); |
| row.tabIndex = -1; |
| UI.ARIAUtils.markAsButton(row); |
| if (shortcutKeyText) { |
| row.createChild('div', 'tabbed-pane-placeholder-key').textContent = shortcutKeyText; |
| row.createChild('div', 'tabbed-pane-placeholder-value').textContent = shortcut.description; |
| } else { |
| row.createChild('div', 'tabbed-pane-no-shortcut').textContent = shortcut.description; |
| } |
| const action = UI.actionRegistry.action(shortcut.actionId); |
| const actionHandler = action.execute.bind(action); |
| this._placeholderOptionArray.push({element: row, handler: actionHandler}); |
| } |
| |
| const firstElement = this._placeholderOptionArray[0].element; |
| firstElement.tabIndex = 0; |
| this._focusedPlaceholderElement = firstElement; |
| this._selectedIndex = 0; |
| |
| element.appendChild(UI.XLink.create( |
| 'https://developers.google.com/web/tools/chrome-devtools/sources?utm_source=devtools&utm_campaign=2018Q1', |
| 'Learn more')); |
| |
| return element; |
| } |
| |
| /** |
| * @param {!Event} event |
| */ |
| _placeholderOnKeyDown(event) { |
| if (isEnterOrSpaceKey(event)) { |
| this._placeholderOptionArray[this._selectedIndex].handler.call(); |
| return; |
| } |
| |
| let offset = 0; |
| if (event.key === 'ArrowDown') { |
| offset = 1; |
| } else if (event.key === 'ArrowUp') { |
| offset = -1; |
| } |
| |
| const newIndex = Math.max(Math.min(this._placeholderOptionArray.length - 1, this._selectedIndex + offset), 0); |
| const newElement = this._placeholderOptionArray[newIndex].element; |
| const oldElement = this._placeholderOptionArray[this._selectedIndex].element; |
| if (newElement !== oldElement) { |
| oldElement.tabIndex = -1; |
| newElement.tabIndex = 0; |
| UI.ARIAUtils.setSelected(oldElement, false); |
| UI.ARIAUtils.setSelected(newElement, true); |
| this._selectedIndex = newIndex; |
| newElement.focus(); |
| } |
| } |
| |
| _resetPlaceholderState() { |
| this._placeholderOptionArray[this._selectedIndex].element.tabIndex = -1; |
| this._placeholderOptionArray[0].element.tabIndex = 0; |
| this._selectedIndex = 0; |
| } |
| |
| /** |
| * @return {!Map.<!Workspace.UISourceCode, number>} |
| */ |
| static defaultUISourceCodeScores() { |
| /** @type {!Map.<!Workspace.UISourceCode, number>} */ |
| const defaultScores = new Map(); |
| const sourcesView = UI.context.flavor(Sources.SourcesView); |
| if (sourcesView) { |
| const uiSourceCodes = sourcesView._editorContainer.historyUISourceCodes(); |
| for (let i = 1; i < uiSourceCodes.length; ++i) // Skip current element |
| { |
| defaultScores.set(uiSourceCodes[i], uiSourceCodes.length - i); |
| } |
| } |
| return defaultScores; |
| } |
| |
| /** |
| * @return {!UI.Toolbar} |
| */ |
| leftToolbar() { |
| return this._editorContainer.leftToolbar(); |
| } |
| |
| /** |
| * @return {!UI.Toolbar} |
| */ |
| rightToolbar() { |
| return this._editorContainer.rightToolbar(); |
| } |
| |
| /** |
| * @return {!UI.Toolbar} |
| */ |
| bottomToolbar() { |
| return this._bottomToolbar; |
| } |
| |
| /** |
| * @param {!Array.<!UI.KeyboardShortcut.Descriptor>} keys |
| * @param {function(!Event=):boolean} handler |
| */ |
| _registerShortcuts(keys, handler) { |
| for (let i = 0; i < keys.length; ++i) { |
| this._shortcuts[keys[i].key] = handler; |
| } |
| } |
| |
| _handleKeyDown(event) { |
| const shortcutKey = UI.KeyboardShortcut.makeKeyFromEvent(event); |
| const handler = this._shortcuts[shortcutKey]; |
| if (handler && handler()) { |
| event.consume(true); |
| } |
| } |
| |
| /** |
| * @override |
| */ |
| wasShown() { |
| super.wasShown(); |
| UI.context.setFlavor(Sources.SourcesView, this); |
| } |
| |
| /** |
| * @override |
| */ |
| willHide() { |
| UI.context.setFlavor(Sources.SourcesView, null); |
| this._resetPlaceholderState(); |
| super.willHide(); |
| } |
| |
| /** |
| * @return {!Element} |
| */ |
| toolbarContainerElement() { |
| return this._toolbarContainerElement; |
| } |
| |
| /** |
| * @return {!UI.SearchableView} |
| */ |
| searchableView() { |
| return this._searchableView; |
| } |
| |
| /** |
| * @return {?UI.Widget} |
| */ |
| visibleView() { |
| return this._editorContainer.visibleView; |
| } |
| |
| /** |
| * @return {?Sources.UISourceCodeFrame} |
| */ |
| currentSourceFrame() { |
| const view = this.visibleView(); |
| if (!(view instanceof Sources.UISourceCodeFrame)) { |
| return null; |
| } |
| return /** @type {!Sources.UISourceCodeFrame} */ (view); |
| } |
| |
| /** |
| * @return {?Workspace.UISourceCode} |
| */ |
| currentUISourceCode() { |
| return this._editorContainer.currentFile(); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| _onCloseEditorTab() { |
| const uiSourceCode = this._editorContainer.currentFile(); |
| if (!uiSourceCode) { |
| return false; |
| } |
| this._editorContainer.closeFile(uiSourceCode); |
| return true; |
| } |
| |
| _onJumpToPreviousLocation() { |
| this._historyManager.rollback(); |
| } |
| |
| _onJumpToNextLocation() { |
| this._historyManager.rollover(); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _uiSourceCodeAdded(event) { |
| const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data); |
| this._addUISourceCode(uiSourceCode); |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| */ |
| _addUISourceCode(uiSourceCode) { |
| if (uiSourceCode.project().isServiceProject()) { |
| return; |
| } |
| if (uiSourceCode.project().type() === Workspace.projectTypes.FileSystem && |
| Persistence.FileSystemWorkspaceBinding.fileSystemType(uiSourceCode.project()) === 'overrides') { |
| return; |
| } |
| this._editorContainer.addUISourceCode(uiSourceCode); |
| } |
| |
| _uiSourceCodeRemoved(event) { |
| const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data); |
| this._removeUISourceCodes([uiSourceCode]); |
| } |
| |
| /** |
| * @param {!Array.<!Workspace.UISourceCode>} uiSourceCodes |
| */ |
| _removeUISourceCodes(uiSourceCodes) { |
| this._editorContainer.removeUISourceCodes(uiSourceCodes); |
| for (let i = 0; i < uiSourceCodes.length; ++i) { |
| this._removeSourceFrame(uiSourceCodes[i]); |
| this._historyManager.removeHistoryForSourceCode(uiSourceCodes[i]); |
| } |
| } |
| |
| _projectRemoved(event) { |
| const project = event.data; |
| const uiSourceCodes = project.uiSourceCodes(); |
| this._removeUISourceCodes(uiSourceCodes); |
| } |
| |
| _updateScriptViewToolbarItems() { |
| this._scriptViewToolbar.removeToolbarItems(); |
| const view = this.visibleView(); |
| if (view instanceof UI.SimpleView) { |
| for (const item of (/** @type {?UI.SimpleView} */ (view)).syncToolbarItems()) { |
| this._scriptViewToolbar.appendToolbarItem(item); |
| } |
| } |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| * @param {number=} lineNumber 0-based |
| * @param {number=} columnNumber |
| * @param {boolean=} omitFocus |
| * @param {boolean=} omitHighlight |
| */ |
| showSourceLocation(uiSourceCode, lineNumber, columnNumber, omitFocus, omitHighlight) { |
| this._historyManager.updateCurrentState(); |
| this._editorContainer.showFile(uiSourceCode); |
| const currentSourceFrame = this.currentSourceFrame(); |
| if (currentSourceFrame && typeof lineNumber === 'number') { |
| currentSourceFrame.revealPosition(lineNumber, columnNumber, !omitHighlight); |
| } |
| this._historyManager.pushNewState(); |
| if (!omitFocus) { |
| this.visibleView().focus(); |
| } |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| * @return {!UI.Widget} |
| */ |
| _createSourceView(uiSourceCode) { |
| let sourceFrame; |
| let sourceView; |
| const contentType = uiSourceCode.contentType(); |
| |
| if (contentType === Common.resourceTypes.Image) { |
| sourceView = new SourceFrame.ImageView(uiSourceCode.mimeType(), uiSourceCode); |
| } else if (contentType === Common.resourceTypes.Font) { |
| sourceView = new SourceFrame.FontView(uiSourceCode.mimeType(), uiSourceCode); |
| } else { |
| sourceFrame = new Sources.UISourceCodeFrame(uiSourceCode); |
| } |
| |
| if (sourceFrame) { |
| this._historyManager.trackSourceFrameCursorJumps(sourceFrame); |
| } |
| |
| const widget = /** @type {!UI.Widget} */ (sourceFrame || sourceView); |
| this._sourceViewByUISourceCode.set(uiSourceCode, widget); |
| return widget; |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| * @return {!UI.Widget} |
| */ |
| _getOrCreateSourceView(uiSourceCode) { |
| return this._sourceViewByUISourceCode.get(uiSourceCode) || this._createSourceView(uiSourceCode); |
| } |
| |
| /** |
| * @override |
| * @param {!Sources.UISourceCodeFrame} sourceFrame |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| */ |
| recycleUISourceCodeFrame(sourceFrame, uiSourceCode) { |
| this._sourceViewByUISourceCode.delete(sourceFrame.uiSourceCode()); |
| sourceFrame.setUISourceCode(uiSourceCode); |
| this._sourceViewByUISourceCode.set(uiSourceCode, sourceFrame); |
| } |
| |
| /** |
| * @override |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| * @return {!UI.Widget} |
| */ |
| viewForFile(uiSourceCode) { |
| return this._getOrCreateSourceView(uiSourceCode); |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| */ |
| _removeSourceFrame(uiSourceCode) { |
| const sourceView = this._sourceViewByUISourceCode.get(uiSourceCode); |
| this._sourceViewByUISourceCode.remove(uiSourceCode); |
| if (sourceView && sourceView instanceof Sources.UISourceCodeFrame) { |
| /** @type {!Sources.UISourceCodeFrame} */ (sourceView).dispose(); |
| } |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _editorClosed(event) { |
| const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data); |
| this._historyManager.removeHistoryForSourceCode(uiSourceCode); |
| |
| let wasSelected = false; |
| if (!this._editorContainer.currentFile()) { |
| wasSelected = true; |
| } |
| |
| // SourcesNavigator does not need to update on EditorClosed. |
| this._removeToolbarChangedListener(); |
| this._updateScriptViewToolbarItems(); |
| this._searchableView.resetSearch(); |
| |
| const data = {}; |
| data.uiSourceCode = uiSourceCode; |
| data.wasSelected = wasSelected; |
| this.dispatchEventToListeners(Sources.SourcesView.Events.EditorClosed, data); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _editorSelected(event) { |
| const previousSourceFrame = |
| event.data.previousView instanceof Sources.UISourceCodeFrame ? event.data.previousView : null; |
| if (previousSourceFrame) { |
| previousSourceFrame.setSearchableView(null); |
| } |
| const currentSourceFrame = |
| event.data.currentView instanceof Sources.UISourceCodeFrame ? event.data.currentView : null; |
| if (currentSourceFrame) { |
| currentSourceFrame.setSearchableView(this._searchableView); |
| } |
| |
| this._searchableView.setReplaceable(!!currentSourceFrame && currentSourceFrame.canEditSource()); |
| this._searchableView.refreshSearch(); |
| this._updateToolbarChangedListener(); |
| this._updateScriptViewToolbarItems(); |
| |
| this.dispatchEventToListeners(Sources.SourcesView.Events.EditorSelected, this._editorContainer.currentFile()); |
| } |
| |
| _removeToolbarChangedListener() { |
| if (this._toolbarChangedListener) { |
| Common.EventTarget.removeEventListeners([this._toolbarChangedListener]); |
| } |
| this._toolbarChangedListener = null; |
| } |
| |
| _updateToolbarChangedListener() { |
| this._removeToolbarChangedListener(); |
| const sourceFrame = this.currentSourceFrame(); |
| if (!sourceFrame) { |
| return; |
| } |
| this._toolbarChangedListener = sourceFrame.addEventListener( |
| Sources.UISourceCodeFrame.Events.ToolbarItemsChanged, this._updateScriptViewToolbarItems, this); |
| } |
| |
| /** |
| * @override |
| */ |
| searchCanceled() { |
| if (this._searchView) { |
| this._searchView.searchCanceled(); |
| } |
| |
| delete this._searchView; |
| delete this._searchConfig; |
| } |
| |
| /** |
| * @override |
| * @param {!UI.SearchableView.SearchConfig} searchConfig |
| * @param {boolean} shouldJump |
| * @param {boolean=} jumpBackwards |
| */ |
| performSearch(searchConfig, shouldJump, jumpBackwards) { |
| const sourceFrame = this.currentSourceFrame(); |
| if (!sourceFrame) { |
| return; |
| } |
| |
| this._searchView = sourceFrame; |
| this._searchConfig = searchConfig; |
| |
| this._searchView.performSearch(this._searchConfig, shouldJump, jumpBackwards); |
| } |
| |
| /** |
| * @override |
| */ |
| jumpToNextSearchResult() { |
| if (!this._searchView) { |
| return; |
| } |
| |
| if (this._searchView !== this.currentSourceFrame()) { |
| this.performSearch(this._searchConfig, true); |
| return; |
| } |
| |
| this._searchView.jumpToNextSearchResult(); |
| } |
| |
| /** |
| * @override |
| */ |
| jumpToPreviousSearchResult() { |
| if (!this._searchView) { |
| return; |
| } |
| |
| if (this._searchView !== this.currentSourceFrame()) { |
| this.performSearch(this._searchConfig, true); |
| if (this._searchView) { |
| this._searchView.jumpToLastSearchResult(); |
| } |
| return; |
| } |
| |
| this._searchView.jumpToPreviousSearchResult(); |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| supportsCaseSensitiveSearch() { |
| return true; |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| supportsRegexSearch() { |
| return true; |
| } |
| |
| /** |
| * @override |
| * @param {!UI.SearchableView.SearchConfig} searchConfig |
| * @param {string} replacement |
| */ |
| replaceSelectionWith(searchConfig, replacement) { |
| const sourceFrame = this.currentSourceFrame(); |
| if (!sourceFrame) { |
| console.assert(sourceFrame); |
| return; |
| } |
| sourceFrame.replaceSelectionWith(searchConfig, replacement); |
| } |
| |
| /** |
| * @override |
| * @param {!UI.SearchableView.SearchConfig} searchConfig |
| * @param {string} replacement |
| */ |
| replaceAllWith(searchConfig, replacement) { |
| const sourceFrame = this.currentSourceFrame(); |
| if (!sourceFrame) { |
| console.assert(sourceFrame); |
| return; |
| } |
| sourceFrame.replaceAllWith(searchConfig, replacement); |
| } |
| |
| _showOutlineQuickOpen() { |
| QuickOpen.QuickOpen.show('@'); |
| } |
| |
| _showGoToLineQuickOpen() { |
| if (this._editorContainer.currentFile()) { |
| QuickOpen.QuickOpen.show(':'); |
| } |
| } |
| |
| _save() { |
| this._saveSourceFrame(this.currentSourceFrame()); |
| } |
| |
| _saveAll() { |
| const sourceFrames = this._editorContainer.fileViews(); |
| sourceFrames.forEach(this._saveSourceFrame.bind(this)); |
| } |
| |
| /** |
| * @param {?UI.Widget} sourceFrame |
| */ |
| _saveSourceFrame(sourceFrame) { |
| if (!(sourceFrame instanceof Sources.UISourceCodeFrame)) { |
| return; |
| } |
| const uiSourceCodeFrame = /** @type {!Sources.UISourceCodeFrame} */ (sourceFrame); |
| uiSourceCodeFrame.commitEditing(); |
| } |
| |
| /** |
| * @param {boolean} active |
| */ |
| toggleBreakpointsActiveState(active) { |
| this._editorContainer.view.element.classList.toggle('breakpoints-deactivated', !active); |
| } |
| }; |
| |
| /** @enum {symbol} */ |
| Sources.SourcesView.Events = { |
| EditorClosed: Symbol('EditorClosed'), |
| EditorSelected: Symbol('EditorSelected'), |
| }; |
| |
| /** |
| * @interface |
| */ |
| Sources.SourcesView.EditorAction = function() {}; |
| |
| Sources.SourcesView.EditorAction.prototype = { |
| /** |
| * @param {!Sources.SourcesView} sourcesView |
| * @return {!UI.ToolbarButton} |
| */ |
| button(sourcesView) {} |
| }; |
| |
| /** |
| * @implements {UI.ActionDelegate} |
| * @unrestricted |
| */ |
| Sources.SourcesView.SwitchFileActionDelegate = class { |
| /** |
| * @param {!Workspace.UISourceCode} currentUISourceCode |
| * @return {?Workspace.UISourceCode} |
| */ |
| static _nextFile(currentUISourceCode) { |
| /** |
| * @param {string} name |
| * @return {string} |
| */ |
| function fileNamePrefix(name) { |
| const lastDotIndex = name.lastIndexOf('.'); |
| const namePrefix = name.substr(0, lastDotIndex !== -1 ? lastDotIndex : name.length); |
| return namePrefix.toLowerCase(); |
| } |
| |
| const uiSourceCodes = currentUISourceCode.project().uiSourceCodes(); |
| const candidates = []; |
| const url = currentUISourceCode.parentURL(); |
| const name = currentUISourceCode.name(); |
| const namePrefix = fileNamePrefix(name); |
| for (let i = 0; i < uiSourceCodes.length; ++i) { |
| const uiSourceCode = uiSourceCodes[i]; |
| if (url !== uiSourceCode.parentURL()) { |
| continue; |
| } |
| if (fileNamePrefix(uiSourceCode.name()) === namePrefix) { |
| candidates.push(uiSourceCode.name()); |
| } |
| } |
| candidates.sort(String.naturalOrderComparator); |
| const index = mod(candidates.indexOf(name) + 1, candidates.length); |
| const fullURL = (url ? url + '/' : '') + candidates[index]; |
| const nextUISourceCode = currentUISourceCode.project().uiSourceCodeForURL(fullURL); |
| return nextUISourceCode !== currentUISourceCode ? nextUISourceCode : null; |
| } |
| |
| /** |
| * @override |
| * @param {!UI.Context} context |
| * @param {string} actionId |
| * @return {boolean} |
| */ |
| handleAction(context, actionId) { |
| const sourcesView = UI.context.flavor(Sources.SourcesView); |
| const currentUISourceCode = sourcesView.currentUISourceCode(); |
| if (!currentUISourceCode) { |
| return false; |
| } |
| const nextUISourceCode = Sources.SourcesView.SwitchFileActionDelegate._nextFile(currentUISourceCode); |
| if (!nextUISourceCode) { |
| return false; |
| } |
| sourcesView.showSourceLocation(nextUISourceCode); |
| return true; |
| } |
| }; |
| |
| |
| /** |
| * @implements {UI.ActionDelegate} |
| * @unrestricted |
| */ |
| Sources.SourcesView.ActionDelegate = class { |
| /** |
| * @override |
| * @param {!UI.Context} context |
| * @param {string} actionId |
| * @return {boolean} |
| */ |
| handleAction(context, actionId) { |
| const sourcesView = UI.context.flavor(Sources.SourcesView); |
| if (!sourcesView) { |
| return false; |
| } |
| |
| switch (actionId) { |
| case 'sources.close-all': |
| sourcesView._editorContainer.closeAllFiles(); |
| return true; |
| case 'sources.jump-to-previous-location': |
| sourcesView._onJumpToPreviousLocation(); |
| return true; |
| case 'sources.jump-to-next-location': |
| sourcesView._onJumpToNextLocation(); |
| return true; |
| case 'sources.close-editor-tab': |
| return sourcesView._onCloseEditorTab(); |
| case 'sources.go-to-line': |
| sourcesView._showGoToLineQuickOpen(); |
| return true; |
| case 'sources.go-to-member': |
| sourcesView._showOutlineQuickOpen(); |
| return true; |
| case 'sources.save': |
| sourcesView._save(); |
| return true; |
| case 'sources.save-all': |
| sourcesView._saveAll(); |
| return true; |
| } |
| |
| return false; |
| } |
| }; |