|
|
|
@ -46,7 +46,7 @@ class FileManager extends Plugin { |
|
|
|
|
getFolder: (path: any) => Promise<unknown> |
|
|
|
|
setFile: (path: any, data: any) => Promise<unknown> |
|
|
|
|
switchFile: (path: any) => Promise<void> |
|
|
|
|
constructor (editor, appManager) { |
|
|
|
|
constructor(editor, appManager) { |
|
|
|
|
super(profile) |
|
|
|
|
this.mode = 'browser' |
|
|
|
|
this.openedFiles = {} // list all opened files
|
|
|
|
@ -58,19 +58,19 @@ class FileManager extends Plugin { |
|
|
|
|
this.init() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getOpenedFiles () { |
|
|
|
|
getOpenedFiles() { |
|
|
|
|
return this.openedFiles |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setMode (mode) { |
|
|
|
|
setMode(mode) { |
|
|
|
|
this.mode = mode |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
limitPluginScope (path) { |
|
|
|
|
limitPluginScope(path) { |
|
|
|
|
return path.replace(/^\/browser\//, '').replace(/^browser\//, '') // forbids plugin to access the root filesystem
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
normalize (path) { |
|
|
|
|
normalize(path) { |
|
|
|
|
return path.replace(/^\/+/, '') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -79,7 +79,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the file/directory |
|
|
|
|
* @param {string} message message to display if path doesn't exist. |
|
|
|
|
*/ |
|
|
|
|
async _handleExists (path: string, message?:string) { |
|
|
|
|
async _handleExists(path: string, message?: string) { |
|
|
|
|
const exists = await this.exists(path) |
|
|
|
|
|
|
|
|
|
if (!exists) { |
|
|
|
@ -92,7 +92,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the file/directory |
|
|
|
|
* @param {string} message message to display if path is not a file. |
|
|
|
|
*/ |
|
|
|
|
async _handleIsFile (path, message) { |
|
|
|
|
async _handleIsFile(path, message) { |
|
|
|
|
const isFile = await this.isFile(path) |
|
|
|
|
|
|
|
|
|
if (!isFile) { |
|
|
|
@ -105,7 +105,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the file/directory |
|
|
|
|
* @param {string} message message to display if path is not a directory. |
|
|
|
|
*/ |
|
|
|
|
async _handleIsDir (path: string, message?: string) { |
|
|
|
|
async _handleIsDir(path: string, message?: string) { |
|
|
|
|
const isDir = await this.isDirectory(path) |
|
|
|
|
|
|
|
|
|
if (!isDir) { |
|
|
|
@ -114,7 +114,7 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** The current opened file */ |
|
|
|
|
file () { |
|
|
|
|
file() { |
|
|
|
|
try { |
|
|
|
|
const file = this.currentFile() |
|
|
|
|
|
|
|
|
@ -130,7 +130,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the directory or file |
|
|
|
|
* @returns {boolean} true if the path exists |
|
|
|
|
*/ |
|
|
|
|
async exists (path) { |
|
|
|
|
async exists(path) { |
|
|
|
|
try { |
|
|
|
|
path = this.normalize(path) |
|
|
|
|
path = this.limitPluginScope(path) |
|
|
|
@ -146,7 +146,7 @@ class FileManager extends Plugin { |
|
|
|
|
/* |
|
|
|
|
* refresh the file explorer |
|
|
|
|
*/ |
|
|
|
|
refresh () { |
|
|
|
|
refresh() { |
|
|
|
|
const provider = this.fileProviderOf('/') |
|
|
|
|
// emit rootFolderChanged so that File Explorer reloads the file tree
|
|
|
|
|
provider.event.emit('rootFolderChanged', provider.workspace || '/') |
|
|
|
@ -158,7 +158,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the directory or file |
|
|
|
|
* @returns {boolean} true if path is a file. |
|
|
|
|
*/ |
|
|
|
|
async isFile (path) { |
|
|
|
|
async isFile(path) { |
|
|
|
|
const provider = this.fileProviderOf(path) |
|
|
|
|
const result = await provider.isFile(path) |
|
|
|
|
return result |
|
|
|
@ -169,7 +169,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the directory |
|
|
|
|
* @returns {boolean} true if path is a directory. |
|
|
|
|
*/ |
|
|
|
|
async isDirectory (path) { |
|
|
|
|
async isDirectory(path) { |
|
|
|
|
const provider = this.fileProviderOf(path) |
|
|
|
|
const result = await provider.isDirectory(path) |
|
|
|
|
|
|
|
|
@ -181,7 +181,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the file |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
async open (path) { |
|
|
|
|
async open(path) { |
|
|
|
|
path = this.normalize(path) |
|
|
|
|
path = this.limitPluginScope(path) |
|
|
|
|
path = this.getPathFromUrl(path).file |
|
|
|
@ -196,7 +196,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} data content to write on the file |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
async writeFile (path, data) { |
|
|
|
|
async writeFile(path, data) { |
|
|
|
|
try { |
|
|
|
|
path = this.normalize(path) |
|
|
|
|
path = this.limitPluginScope(path) |
|
|
|
@ -218,7 +218,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the file |
|
|
|
|
* @returns {string} content of the file |
|
|
|
|
*/ |
|
|
|
|
async readFile (path) { |
|
|
|
|
async readFile(path) { |
|
|
|
|
try { |
|
|
|
|
path = this.normalize(path) |
|
|
|
|
path = this.limitPluginScope(path) |
|
|
|
@ -236,7 +236,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} dest path of the destrination file |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
async copyFile (src, dest, customName) { |
|
|
|
|
async copyFile(src, dest, customName) { |
|
|
|
|
try { |
|
|
|
|
src = this.normalize(src) |
|
|
|
|
dest = this.normalize(dest) |
|
|
|
@ -262,7 +262,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} dest path of the destination dir |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
async copyDir (src, dest) { |
|
|
|
|
async copyDir(src, dest) { |
|
|
|
|
try { |
|
|
|
|
src = this.normalize(src) |
|
|
|
|
dest = this.normalize(dest) |
|
|
|
@ -278,7 +278,7 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async inDepthCopy (src, dest, count = 0) { |
|
|
|
|
async inDepthCopy(src, dest, count = 0) { |
|
|
|
|
const content = await this.readdir(src) |
|
|
|
|
let copiedFolderPath = count === 0 ? dest + '/' + `Copy_${helper.extractNameFromKey(src)}` : dest + '/' + helper.extractNameFromKey(src) |
|
|
|
|
copiedFolderPath = await helper.createNonClashingDirNameAsync(copiedFolderPath, this) |
|
|
|
@ -300,7 +300,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} newPath new path of the file/directory |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
async rename (oldPath, newPath) { |
|
|
|
|
async rename(oldPath, newPath) { |
|
|
|
|
try { |
|
|
|
|
oldPath = this.normalize(oldPath) |
|
|
|
|
newPath = this.normalize(newPath) |
|
|
|
@ -340,7 +340,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the new directory |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
async mkdir (path) { |
|
|
|
|
async mkdir(path) { |
|
|
|
|
try { |
|
|
|
|
path = this.normalize(path) |
|
|
|
|
path = this.limitPluginScope(path) |
|
|
|
@ -359,7 +359,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the directory |
|
|
|
|
* @returns {string[]} list of the file/directory name in this directory |
|
|
|
|
*/ |
|
|
|
|
async readdir (path) { |
|
|
|
|
async readdir(path) { |
|
|
|
|
try { |
|
|
|
|
path = this.normalize(path) |
|
|
|
|
path = this.limitPluginScope(path) |
|
|
|
@ -384,7 +384,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} path path of the directory/file to remove |
|
|
|
|
* @returns {void} |
|
|
|
|
*/ |
|
|
|
|
async remove (path) { |
|
|
|
|
async remove(path) { |
|
|
|
|
try { |
|
|
|
|
path = this.normalize(path) |
|
|
|
|
path = this.limitPluginScope(path) |
|
|
|
@ -396,7 +396,7 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
init () { |
|
|
|
|
init() { |
|
|
|
|
this._deps = { |
|
|
|
|
config: this._components.registry.get('config').api, |
|
|
|
|
browserExplorer: this._components.registry.get('fileproviders/browser').api, |
|
|
|
@ -424,15 +424,15 @@ class FileManager extends Plugin { |
|
|
|
|
this.switchFile = this.open |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileAddedEvent (path) { |
|
|
|
|
fileAddedEvent(path) { |
|
|
|
|
this.emit('fileAdded', path) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileChangedEvent (path) { |
|
|
|
|
fileChangedEvent(path) { |
|
|
|
|
this.emit('fileChanged', path) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileRenamedEvent (oldName, newName, isFolder) { |
|
|
|
|
fileRenamedEvent(oldName, newName, isFolder) { |
|
|
|
|
if (!isFolder) { |
|
|
|
|
this._deps.config.set('currentFile', '') |
|
|
|
|
this.editor.discard(oldName) |
|
|
|
@ -458,7 +458,7 @@ class FileManager extends Plugin { |
|
|
|
|
this.events.emit('fileRenamed', oldName, newName, isFolder) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
currentFileProvider () { |
|
|
|
|
currentFileProvider() { |
|
|
|
|
var path = this.currentPath() |
|
|
|
|
if (path) { |
|
|
|
|
return this.fileProviderOf(path) |
|
|
|
@ -466,11 +466,11 @@ class FileManager extends Plugin { |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
currentFile () { |
|
|
|
|
currentFile() { |
|
|
|
|
return this.editor.current() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async closeAllFiles () { |
|
|
|
|
async closeAllFiles() { |
|
|
|
|
// TODO: Only keep `this.emit` (issue#2210)
|
|
|
|
|
this.emit('filesAllClosed') |
|
|
|
|
this.events.emit('filesAllClosed') |
|
|
|
@ -479,7 +479,7 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async closeFile (name) { |
|
|
|
|
async closeFile(name) { |
|
|
|
|
delete this.openedFiles[name] |
|
|
|
|
if (!Object.keys(this.openedFiles).length) { |
|
|
|
|
this._deps.config.set('currentFile', '') |
|
|
|
@ -492,18 +492,18 @@ class FileManager extends Plugin { |
|
|
|
|
this.events.emit('fileClosed', name) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
currentPath () { |
|
|
|
|
currentPath() { |
|
|
|
|
var currentFile = this._deps.config.get('currentFile') |
|
|
|
|
return this.extractPathOf(currentFile) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
extractPathOf (file) { |
|
|
|
|
extractPathOf(file) { |
|
|
|
|
var reg = /(.*)(\/).*/ |
|
|
|
|
var path = reg.exec(file) |
|
|
|
|
return path ? path[1] : '/' |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getFileContent (path) { |
|
|
|
|
getFileContent(path) { |
|
|
|
|
const provider = this.fileProviderOf(path) |
|
|
|
|
|
|
|
|
|
if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` }) |
|
|
|
@ -517,19 +517,19 @@ class FileManager extends Plugin { |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async setFileContent (path, content) { |
|
|
|
|
async setFileContent(path, content) { |
|
|
|
|
if (this.currentRequest) { |
|
|
|
|
const canCall = await this.askUserPermission('writeFile', '') |
|
|
|
|
const required = this.appManager.isRequired(this.currentRequest.from) |
|
|
|
|
if (canCall && !required) { |
|
|
|
|
// inform the user about modification after permission is granted and even if permission was saved before
|
|
|
|
|
this.call('notification','toast', fileChangedToastMsg(this.currentRequest.from, path)) |
|
|
|
|
this.call('notification', 'toast', fileChangedToastMsg(this.currentRequest.from, path)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return await this._setFileInternal(path, content) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_setFileInternal (path, content) { |
|
|
|
|
_setFileInternal(path, content) { |
|
|
|
|
const provider = this.fileProviderOf(path) |
|
|
|
|
if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` }) |
|
|
|
|
// TODO : Add permission
|
|
|
|
@ -544,7 +544,7 @@ class FileManager extends Plugin { |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_saveAsCopy (path, content) { |
|
|
|
|
_saveAsCopy(path, content) { |
|
|
|
|
const fileProvider = this.fileProviderOf(path) |
|
|
|
|
if (fileProvider) { |
|
|
|
|
helper.createNonClashingNameAsync(path, this, '', (error, copyName) => { |
|
|
|
@ -564,7 +564,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} file url we are trying to resolve |
|
|
|
|
* @returns {{ string, provider }} file path resolved and its provider. |
|
|
|
|
*/ |
|
|
|
|
getPathFromUrl (file) { |
|
|
|
|
getPathFromUrl(file) { |
|
|
|
|
const provider = this.fileProviderOf(file) |
|
|
|
|
if (!provider) throw new Error(`no provider for ${file}`) |
|
|
|
|
return { |
|
|
|
@ -578,7 +578,7 @@ class FileManager extends Plugin { |
|
|
|
|
* @param {string} file path we are trying to resolve |
|
|
|
|
* @returns {{ string, provider }} file url resolved and its provider. |
|
|
|
|
*/ |
|
|
|
|
getUrlFromPath (file) { |
|
|
|
|
getUrlFromPath(file) { |
|
|
|
|
const provider = this.fileProviderOf(file) |
|
|
|
|
if (!provider) throw new Error(`no provider for ${file}`) |
|
|
|
|
return { |
|
|
|
@ -587,7 +587,7 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
removeTabsOf (provider) { |
|
|
|
|
removeTabsOf(provider) { |
|
|
|
|
for (var tab in this.openedFiles) { |
|
|
|
|
if (this.fileProviderOf(tab).type === provider.type) { |
|
|
|
|
this.fileRemovedEvent(tab) |
|
|
|
@ -595,7 +595,7 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileRemovedEvent (path) { |
|
|
|
|
fileRemovedEvent(path) { |
|
|
|
|
if (path === this._deps.config.get('currentFile')) { |
|
|
|
|
this._deps.config.set('currentFile', '') |
|
|
|
|
} |
|
|
|
@ -607,7 +607,7 @@ class FileManager extends Plugin { |
|
|
|
|
this.openFile() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async unselectCurrentFile () { |
|
|
|
|
async unselectCurrentFile() { |
|
|
|
|
await this.saveCurrentFile() |
|
|
|
|
this._deps.config.set('currentFile', '') |
|
|
|
|
// TODO: Only keep `this.emit` (issue#2210)
|
|
|
|
@ -615,7 +615,7 @@ class FileManager extends Plugin { |
|
|
|
|
this.events.emit('noFileSelected') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async openFile (file?: string) { |
|
|
|
|
async openFile(file?: string) { |
|
|
|
|
if (!file) { |
|
|
|
|
this.emit('noFileSelected') |
|
|
|
|
this.events.emit('noFileSelected') |
|
|
|
@ -663,7 +663,7 @@ class FileManager extends Plugin { |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
async getProviderOf (file) { |
|
|
|
|
async getProviderOf(file) { |
|
|
|
|
const cancall = await this.askUserPermission('getProviderByName') |
|
|
|
|
if (cancall) { |
|
|
|
|
return file ? this.fileProviderOf(file) : this.currentFileProvider() |
|
|
|
@ -676,18 +676,18 @@ class FileManager extends Plugin { |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
async getProviderByName (name) { |
|
|
|
|
async getProviderByName(name) { |
|
|
|
|
const cancall = await this.askUserPermission('getProviderByName') |
|
|
|
|
if (cancall) { |
|
|
|
|
return this.getProvider(name) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getProvider (name) { |
|
|
|
|
getProvider(name) { |
|
|
|
|
return this._deps.filesProviders[name] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileProviderOf (file) { |
|
|
|
|
fileProviderOf(file) { |
|
|
|
|
if (file.startsWith('localhost') || this.mode === 'localhost') { |
|
|
|
|
return this._deps.filesProviders.localhost |
|
|
|
|
} |
|
|
|
@ -698,7 +698,7 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// returns the list of directories inside path
|
|
|
|
|
dirList (path) { |
|
|
|
|
dirList(path) { |
|
|
|
|
const dirPaths = [] |
|
|
|
|
const collectList = (path) => { |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
@ -717,11 +717,11 @@ class FileManager extends Plugin { |
|
|
|
|
return collectList(path) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
isRemixDActive () { |
|
|
|
|
isRemixDActive() { |
|
|
|
|
return this.appManager.isActive('remixd') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async saveCurrentFile () { |
|
|
|
|
async saveCurrentFile() { |
|
|
|
|
var currentFile = this._deps.config.get('currentFile') |
|
|
|
|
if (currentFile && this.editor.current()) { |
|
|
|
|
var input = this.editor.get(currentFile) |
|
|
|
@ -737,7 +737,7 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async syncEditor (path) { |
|
|
|
|
async syncEditor(path) { |
|
|
|
|
var currentFile = this._deps.config.get('currentFile') |
|
|
|
|
if (path !== currentFile) return |
|
|
|
|
var provider = this.fileProviderOf(currentFile) |
|
|
|
@ -751,18 +751,19 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async setBatchFiles (filesSet, fileProvider, override, callback) { |
|
|
|
|
async setBatchFiles(filesSet, fileProvider, override, callback) { |
|
|
|
|
const self = this |
|
|
|
|
if (!fileProvider) fileProvider = 'workspace' |
|
|
|
|
if (override === undefined) override = false |
|
|
|
|
|
|
|
|
|
console.log('setBatchFiles', filesSet, fileProvider, override) |
|
|
|
|
for (const file of Object.keys(filesSet)) { |
|
|
|
|
if (override) { |
|
|
|
|
await self._deps.filesProviders[fileProvider].set(file, filesSet[file].content, (e) => { |
|
|
|
|
if (e) callback(e.message || e) |
|
|
|
|
}) |
|
|
|
|
try { |
|
|
|
|
await self._deps.filesProviders[fileProvider].set(file, filesSet[file].content) |
|
|
|
|
} catch (e) { |
|
|
|
|
callback(e.message || e) |
|
|
|
|
} |
|
|
|
|
await self.syncEditor(fileProvider + file) |
|
|
|
|
return callback() |
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
|
helper.createNonClashingName(file, self._deps.filesProviders[fileProvider], |
|
|
|
@ -773,10 +774,10 @@ class FileManager extends Plugin { |
|
|
|
|
message: 'Unexpected error loading file ' + file + ': ' + error |
|
|
|
|
}) |
|
|
|
|
} else if (helper.checkSpecialChars(name)) { |
|
|
|
|
this.call('notification', 'alert', { |
|
|
|
|
id: 'fileManagerAlert', |
|
|
|
|
message: 'Special characters are not allowed in file names.' |
|
|
|
|
}) |
|
|
|
|
this.call('notification', 'alert', { |
|
|
|
|
id: 'fileManagerAlert', |
|
|
|
|
message: 'Special characters are not allowed in file names.' |
|
|
|
|
}) |
|
|
|
|
} else { |
|
|
|
|
try { |
|
|
|
|
await self._deps.filesProviders[fileProvider].set(name, filesSet[file].content) |
|
|
|
@ -785,13 +786,14 @@ class FileManager extends Plugin { |
|
|
|
|
} |
|
|
|
|
self.syncEditor(fileProvider + name) |
|
|
|
|
} |
|
|
|
|
callback() } |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
callback() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
currentWorkspace () { |
|
|
|
|
currentWorkspace() { |
|
|
|
|
if (this.mode !== 'localhost') { |
|
|
|
|
const file = this.currentFile() || '' |
|
|
|
|
const provider = this.fileProviderOf(file) |
|
|
|
|