UNPKG

chrome-devtools-frontend

Version:
188 lines (160 loc) • 6.76 kB
// Copyright 2013 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import type * as Common from '../../core/common/common.js'; import * as i18n from '../../core/i18n/i18n.js'; import * as Platform from '../../core/platform/platform.js'; import * as TextUtils from '../text_utils/text_utils.js'; import * as Workspace from '../workspace/workspace.js'; const UIStrings = { /** * @description Error message that is displayed in the Sources panel when can't be loaded. */ unknownErrorLoadingFile: 'Unknown error loading file', } as const; const str_ = i18n.i18n.registerUIStrings('models/bindings/ContentProviderBasedProject.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); interface UISourceCodeData { mimeType: string; metadata: Workspace.UISourceCode.UISourceCodeMetadata|null; contentProvider: TextUtils.ContentProvider.ContentProvider; } export class ContentProviderBasedProject extends Workspace.Workspace.ProjectStore { readonly #isServiceProject: boolean; readonly #uiSourceCodeToData = new WeakMap<Workspace.UISourceCode.UISourceCode, UISourceCodeData>(); constructor( workspace: Workspace.Workspace.WorkspaceImpl, id: string, type: Workspace.Workspace.projectTypes, displayName: string, isServiceProject: boolean) { super(workspace, id, type, displayName); this.#isServiceProject = isServiceProject; workspace.addProject(this); } async requestFileContent(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<TextUtils.ContentData.ContentDataOrError> { const {contentProvider} = this.#uiSourceCodeToData.get(uiSourceCode) as UISourceCodeData; try { return await contentProvider.requestContentData(); } catch (err) { // TODO(rob.paveza): CRBug 1013683 - Consider propagating exceptions full-stack return { error: err ? String(err) : i18nString(UIStrings.unknownErrorLoadingFile), }; } } isServiceProject(): boolean { return this.#isServiceProject; } async requestMetadata(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<Workspace.UISourceCode.UISourceCodeMetadata|null> { const {metadata} = this.#uiSourceCodeToData.get(uiSourceCode) as UISourceCodeData; return metadata; } canSetFileContent(): boolean { return false; } async setFileContent(_uiSourceCode: Workspace.UISourceCode.UISourceCode, _newContent: string, _isBase64: boolean): Promise<void> { } fullDisplayName(uiSourceCode: Workspace.UISourceCode.UISourceCode): string { let parentPath = uiSourceCode.parentURL().replace(/^(?:https?|file)\:\/\//, ''); try { parentPath = decodeURI(parentPath); } catch { } return parentPath + '/' + uiSourceCode.displayName(true); } mimeType(uiSourceCode: Workspace.UISourceCode.UISourceCode): string { const {mimeType} = (this.#uiSourceCodeToData.get(uiSourceCode) as UISourceCodeData); return mimeType; } canRename(): boolean { return false; } override rename( _uiSourceCode: Workspace.UISourceCode.UISourceCode, _newName: Platform.DevToolsPath.RawPathString, callback: (arg0: boolean, arg1?: string|undefined, arg2?: Platform.DevToolsPath.UrlString|undefined, arg3?: Common.ResourceType.ResourceType|undefined) => void): void { callback(false); } override excludeFolder(_path: Platform.DevToolsPath.UrlString): void { } canExcludeFolder(_path: Platform.DevToolsPath.EncodedPathString): boolean { return false; } async createFile( _path: Platform.DevToolsPath.EncodedPathString, _name: string|null, _content: string, _isBase64?: boolean): Promise<Workspace.UISourceCode.UISourceCode|null> { return null; } canCreateFile(): boolean { return false; } override deleteFile(_uiSourceCode: Workspace.UISourceCode.UISourceCode): void { } override remove(): void { } searchInFileContent( uiSourceCode: Workspace.UISourceCode.UISourceCode, query: string, caseSensitive: boolean, isRegex: boolean): Promise<TextUtils.ContentProvider.SearchMatch[]> { const {contentProvider} = this.#uiSourceCodeToData.get(uiSourceCode) as UISourceCodeData; return contentProvider.searchInContent(query, caseSensitive, isRegex); } async findFilesMatchingSearchRequest( searchConfig: Workspace.SearchConfig.SearchConfig, filesMatchingFileQuery: Workspace.UISourceCode.UISourceCode[], progress: Common.Progress.Progress): Promise<Map<Workspace.UISourceCode.UISourceCode, TextUtils.ContentProvider.SearchMatch[]|null>> { const result = new Map(); progress.totalWork = filesMatchingFileQuery.length; await Promise.all(filesMatchingFileQuery.map(searchInContent.bind(this))); progress.done = true; return result; async function searchInContent( this: ContentProviderBasedProject, uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> { let allMatchesFound = true; let matches: TextUtils.ContentProvider.SearchMatch[] = []; for (const query of searchConfig.queries().slice()) { const searchMatches = await this.searchInFileContent(uiSourceCode, query, !searchConfig.ignoreCase(), searchConfig.isRegex()); if (!searchMatches.length) { allMatchesFound = false; break; } matches = Platform.ArrayUtilities.mergeOrdered( matches, searchMatches, TextUtils.ContentProvider.SearchMatch.comparator); } if (allMatchesFound) { result.set(uiSourceCode, matches); } ++progress.worked; } } override indexContent(progress: Common.Progress.Progress): void { queueMicrotask(() => { progress.done = true; }); } addUISourceCodeWithProvider( uiSourceCode: Workspace.UISourceCode.UISourceCode, contentProvider: TextUtils.ContentProvider.ContentProvider, metadata: Workspace.UISourceCode.UISourceCodeMetadata|null, mimeType: string, ): void { this.#uiSourceCodeToData.set(uiSourceCode, {mimeType, metadata, contentProvider}); this.addUISourceCode(uiSourceCode); } addContentProvider( url: Platform.DevToolsPath.UrlString, contentProvider: TextUtils.ContentProvider.ContentProvider, mimeType: string): Workspace.UISourceCode.UISourceCode { const uiSourceCode = this.createUISourceCode(url, contentProvider.contentType()); this.addUISourceCodeWithProvider(uiSourceCode, contentProvider, null, mimeType); return uiSourceCode; } reset(): void { this.removeProject(); this.workspace().addProject(this); } dispose(): void { this.removeProject(); } }