blob: 62d5ffcf11c73bd234680caa4d6106e58cb4cb86 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @implements {Workspace.Project}
* @unrestricted
*/
export default class ContentProviderBasedProject extends Workspace.ProjectStore {
/**
* @param {!Workspace.Workspace} workspace
* @param {string} id
* @param {!Workspace.projectTypes} type
* @param {string} displayName
* @param {boolean} isServiceProject
*/
constructor(workspace, id, type, displayName, isServiceProject) {
super(workspace, id, type, displayName);
/** @type {!Object.<string, !Common.ContentProvider>} */
this._contentProviders = {};
this._isServiceProject = isServiceProject;
workspace.addProject(this);
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
* @returns {!Promise<!Common.DeferredContent>}
*/
async requestFileContent(uiSourceCode) {
const contentProvider = this._contentProviders[uiSourceCode.url()];
try {
const [content, isEncoded] =
await Promise.all([contentProvider.requestContent(), contentProvider.contentEncoded()]);
return {content: content.content, isEncoded, error: content.error};
} catch (err) {
// TODO(rob.paveza): CRBug 1013683 - Consider propagating exceptions full-stack
return {isEncoded: false, error: err ? String(err) : ls`Unknown error loading file`};
}
}
/**
* @override
* @return {boolean}
*/
isServiceProject() {
return this._isServiceProject;
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
* @return {!Promise<?Workspace.UISourceCodeMetadata>}
*/
requestMetadata(uiSourceCode) {
return Promise.resolve(uiSourceCode[_metadata]);
}
/**
* @override
* @return {boolean}
*/
canSetFileContent() {
return false;
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {string} newContent
* @param {boolean} isBase64
* @return {!Promise}
*/
async setFileContent(uiSourceCode, newContent, isBase64) {
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
* @return {string}
*/
fullDisplayName(uiSourceCode) {
let parentPath = uiSourceCode.parentURL().replace(/^(?:https?|file)\:\/\//, '');
try {
parentPath = decodeURI(parentPath);
} catch (e) {
}
return parentPath + '/' + uiSourceCode.displayName(true);
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
* @return {string}
*/
mimeType(uiSourceCode) {
return /** @type {string} */ (uiSourceCode[_mimeType]);
}
/**
* @override
* @return {boolean}
*/
canRename() {
return false;
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {string} newName
* @param {function(boolean, string=, string=, !Common.ResourceType=)} callback
*/
rename(uiSourceCode, newName, callback) {
const path = uiSourceCode.url();
this.performRename(path, newName, innerCallback.bind(this));
/**
* @param {boolean} success
* @param {string=} newName
* @this {ContentProviderBasedProject}
*/
function innerCallback(success, newName) {
if (success && newName) {
const copyOfPath = path.split('/');
copyOfPath[copyOfPath.length - 1] = newName;
const newPath = copyOfPath.join('/');
this._contentProviders[newPath] = this._contentProviders[path];
delete this._contentProviders[path];
this.renameUISourceCode(uiSourceCode, newName);
}
callback(success, newName);
}
}
/**
* @override
* @param {string} path
*/
excludeFolder(path) {
}
/**
* @override
* @param {string} path
* @return {boolean}
*/
canExcludeFolder(path) {
return false;
}
/**
* @override
* @param {string} path
* @param {?string} name
* @param {string} content
* @param {boolean=} isBase64
* @return {!Promise<?Workspace.UISourceCode>}
*/
createFile(path, name, content, isBase64) {
}
/**
* @override
* @return {boolean}
*/
canCreateFile() {
return false;
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
*/
deleteFile(uiSourceCode) {
}
/**
* @override
*/
remove() {
}
/**
* @param {string} path
* @param {string} newName
* @param {function(boolean, string=)} callback
*/
performRename(path, newName, callback) {
callback(false);
}
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {string} query
* @param {boolean} caseSensitive
* @param {boolean} isRegex
* @return {!Promise<!Array<!Common.ContentProvider.SearchMatch>>}
*/
searchInFileContent(uiSourceCode, query, caseSensitive, isRegex) {
const contentProvider = this._contentProviders[uiSourceCode.url()];
return contentProvider.searchInContent(query, caseSensitive, isRegex);
}
/**
* @override
* @param {!Workspace.ProjectSearchConfig} searchConfig
* @param {!Array<string>} filesMathingFileQuery
* @param {!Common.Progress} progress
* @return {!Promise<!Array<string>>}
*/
async findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, progress) {
const result = [];
progress.setTotalWork(filesMathingFileQuery.length);
await Promise.all(filesMathingFileQuery.map(searchInContent.bind(this)));
progress.done();
return result;
/**
* @param {string} path
* @this {ContentProviderBasedProject}
*/
async function searchInContent(path) {
const provider = this._contentProviders[path];
let allMatchesFound = true;
for (const query of searchConfig.queries().slice()) {
const searchMatches = await provider.searchInContent(query, !searchConfig.ignoreCase(), searchConfig.isRegex());
if (!searchMatches.length) {
allMatchesFound = false;
break;
}
}
if (allMatchesFound) {
result.push(path);
}
progress.worked(1);
}
}
/**
* @override
* @param {!Common.Progress} progress
*/
indexContent(progress) {
setImmediate(progress.done.bind(progress));
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {!Common.ContentProvider} contentProvider
* @param {?Workspace.UISourceCodeMetadata} metadata
* @param {string} mimeType
*/
addUISourceCodeWithProvider(uiSourceCode, contentProvider, metadata, mimeType) {
uiSourceCode[_mimeType] = mimeType;
this._contentProviders[uiSourceCode.url()] = contentProvider;
uiSourceCode[_metadata] = metadata;
this.addUISourceCode(uiSourceCode);
}
/**
* @param {string} url
* @param {!Common.ContentProvider} contentProvider
* @param {string} mimeType
* @return {!Workspace.UISourceCode}
*/
addContentProvider(url, contentProvider, mimeType) {
const uiSourceCode = this.createUISourceCode(url, contentProvider.contentType());
this.addUISourceCodeWithProvider(uiSourceCode, contentProvider, null, mimeType);
return uiSourceCode;
}
/**
* @param {string} path
*/
removeFile(path) {
delete this._contentProviders[path];
this.removeUISourceCode(path);
}
reset() {
this._contentProviders = {};
this.removeProject();
this.workspace().addProject(this);
}
dispose() {
this._contentProviders = {};
this.removeProject();
}
}
const _metadata = Symbol('ContentProviderBasedProject.Metadata');
const _mimeType = Symbol('ContentProviderBasedProject.MimeType');
/* Legacy exported object */
self.Bindings = self.Bindings || {};
/* Legacy exported object */
Bindings = Bindings || {};
/** @constructor */
Bindings.ContentProviderBasedProject = ContentProviderBasedProject;