import*as Bindings from'../bindings/bindings.js';import*as Common from'../common/common.js';import*as Components from'../components/components.js';import*as SDK from'../sdk/sdk.js';import*as Workspace from'../workspace/workspace.js';import{Automapping,AutomappingStatus}from'./Automapping.js';import{LinkDecorator}from'./PersistenceUtils.js';export class PersistenceImpl extends Common.ObjectWrapper.ObjectWrapper{constructor(workspace,breakpointManager){super();this._workspace=workspace;this._breakpointManager=breakpointManager;this._filePathPrefixesToBindingCount=new Map();this._subscribedBindingEventListeners=new Platform.Multimap();const linkDecorator=new LinkDecorator(this);Components.Linkifier.Linkifier.setLinkDecorator(linkDecorator);this._mapping=new Automapping(this._workspace,this._onStatusAdded.bind(this),this._onStatusRemoved.bind(this));}
addNetworkInterceptor(interceptor){this._mapping.addNetworkInterceptor(interceptor);}
refreshAutomapping(){this._mapping.scheduleRemap();}
async addBinding(binding){await this._innerAddBinding(binding);}
async addBindingForTest(binding){await this._innerAddBinding(binding);}
async removeBinding(binding){await this._innerRemoveBinding(binding);}
async removeBindingForTest(binding){await this._innerRemoveBinding(binding);}
async _innerAddBinding(binding){binding.network[_binding]=binding;binding.fileSystem[_binding]=binding;binding.fileSystem.forceLoadOnCheckContent();binding.network.addEventListener(Workspace.UISourceCode.Events.WorkingCopyCommitted,this._onWorkingCopyCommitted,this);binding.fileSystem.addEventListener(Workspace.UISourceCode.Events.WorkingCopyCommitted,this._onWorkingCopyCommitted,this);binding.network.addEventListener(Workspace.UISourceCode.Events.WorkingCopyChanged,this._onWorkingCopyChanged,this);binding.fileSystem.addEventListener(Workspace.UISourceCode.Events.WorkingCopyChanged,this._onWorkingCopyChanged,this);this._addFilePathBindingPrefixes(binding.fileSystem.url());await this._moveBreakpoints(binding.fileSystem,binding.network);console.assert(!binding.fileSystem.isDirty()||!binding.network.isDirty());if(binding.fileSystem.isDirty()){this._syncWorkingCopy(binding.fileSystem);}else if(binding.network.isDirty()){this._syncWorkingCopy(binding.network);}else if(binding.network.hasCommits()&&binding.network.content()!==binding.fileSystem.content()){binding.network.setWorkingCopy(binding.network.content());this._syncWorkingCopy(binding.network);}
this._notifyBindingEvent(binding.network);this._notifyBindingEvent(binding.fileSystem);this.dispatchEventToListeners(Events.BindingCreated,binding);}
async _innerRemoveBinding(binding){if(binding.network[_binding]!==binding){return;}
console.assert(binding.network[_binding]===binding.fileSystem[_binding],'ERROR: inconsistent binding for networkURL '+binding.network.url());binding.network[_binding]=null;binding.fileSystem[_binding]=null;binding.network.removeEventListener(Workspace.UISourceCode.Events.WorkingCopyCommitted,this._onWorkingCopyCommitted,this);binding.fileSystem.removeEventListener(Workspace.UISourceCode.Events.WorkingCopyCommitted,this._onWorkingCopyCommitted,this);binding.network.removeEventListener(Workspace.UISourceCode.Events.WorkingCopyChanged,this._onWorkingCopyChanged,this);binding.fileSystem.removeEventListener(Workspace.UISourceCode.Events.WorkingCopyChanged,this._onWorkingCopyChanged,this);this._removeFilePathBindingPrefixes(binding.fileSystem.url());await this._breakpointManager.copyBreakpoints(binding.network.url(),binding.fileSystem);this._notifyBindingEvent(binding.network);this._notifyBindingEvent(binding.fileSystem);this.dispatchEventToListeners(Events.BindingRemoved,binding);}
async _onStatusAdded(status){const binding=new PersistenceBinding(status.network,status.fileSystem);status[_binding]=binding;await this._innerAddBinding(binding);}
async _onStatusRemoved(status){const binding=(status[_binding]);await this._innerRemoveBinding(binding);}
_onWorkingCopyChanged(event){const uiSourceCode=(event.data);this._syncWorkingCopy(uiSourceCode);}
_syncWorkingCopy(uiSourceCode){const binding=uiSourceCode[_binding];if(!binding||binding[_muteWorkingCopy]){return;}
const other=binding.network===uiSourceCode?binding.fileSystem:binding.network;if(!uiSourceCode.isDirty()){binding[_muteWorkingCopy]=true;other.resetWorkingCopy();binding[_muteWorkingCopy]=false;this._contentSyncedForTest();return;}
const target=Bindings.NetworkProject.NetworkProject.targetForUISourceCode(binding.network);if(target.type()===SDK.SDKModel.Type.Node){const newContent=uiSourceCode.workingCopy();other.requestContent().then(()=>{const nodeJSContent=PersistenceImpl.rewrapNodeJSContent(other,other.workingCopy(),newContent);setWorkingCopy.call(this,()=>nodeJSContent);});return;}
setWorkingCopy.call(this,()=>uiSourceCode.workingCopy());function setWorkingCopy(workingCopyGetter){binding[_muteWorkingCopy]=true;other.setWorkingCopyGetter(workingCopyGetter);binding[_muteWorkingCopy]=false;this._contentSyncedForTest();}}
_onWorkingCopyCommitted(event){const uiSourceCode=(event.data.uiSourceCode);const newContent=(event.data.content);this.syncContent(uiSourceCode,newContent,event.data.encoded);}
syncContent(uiSourceCode,newContent,encoded){const binding=uiSourceCode[_binding];if(!binding||binding[_muteCommit]){return;}
const other=binding.network===uiSourceCode?binding.fileSystem:binding.network;const target=Bindings.NetworkProject.NetworkProject.targetForUISourceCode(binding.network);if(target.type()===SDK.SDKModel.Type.Node){other.requestContent().then(currentContent=>{const nodeJSContent=PersistenceImpl.rewrapNodeJSContent(other,currentContent.content,newContent);setContent.call(this,nodeJSContent);});return;}
setContent.call(this,newContent);function setContent(newContent){binding[_muteCommit]=true;other.setContent(newContent,encoded);binding[_muteCommit]=false;this._contentSyncedForTest();}}
static rewrapNodeJSContent(uiSourceCode,currentContent,newContent){if(uiSourceCode.project().type()===Workspace.Workspace.projectTypes.FileSystem){if(newContent.startsWith(NodePrefix)&&newContent.endsWith(NodeSuffix)){newContent=newContent.substring(NodePrefix.length,newContent.length-NodeSuffix.length);}
if(currentContent.startsWith(NodeShebang)){newContent=NodeShebang+newContent;}}else{if(newContent.startsWith(NodeShebang)){newContent=newContent.substring(NodeShebang.length);}
if(currentContent.startsWith(NodePrefix)&&currentContent.endsWith(NodeSuffix)){newContent=NodePrefix+newContent+NodeSuffix;}}
return newContent;}
_contentSyncedForTest(){}
async _moveBreakpoints(from,to){const breakpoints=this._breakpointManager.breakpointLocationsForUISourceCode(from).map(breakpointLocation=>breakpointLocation.breakpoint);await Promise.all(breakpoints.map(breakpoint=>{breakpoint.remove(false);return this._breakpointManager.setBreakpoint(to,breakpoint.lineNumber(),breakpoint.columnNumber(),breakpoint.condition(),breakpoint.enabled());}));}
hasUnsavedCommittedChanges(uiSourceCode){if(this._workspace.hasResourceContentTrackingExtensions()){return false;}
if(uiSourceCode.project().canSetFileContent()){return false;}
if(uiSourceCode[_binding]){return false;}
return!!uiSourceCode.hasCommits();}
binding(uiSourceCode){return uiSourceCode[_binding]||null;}
subscribeForBindingEvent(uiSourceCode,listener){this._subscribedBindingEventListeners.set(uiSourceCode,listener);}
unsubscribeFromBindingEvent(uiSourceCode,listener){this._subscribedBindingEventListeners.delete(uiSourceCode,listener);}
_notifyBindingEvent(uiSourceCode){if(!this._subscribedBindingEventListeners.has(uiSourceCode)){return;}
const listeners=Array.from(this._subscribedBindingEventListeners.get(uiSourceCode));for(const listener of listeners){listener.call(null);}}
fileSystem(uiSourceCode){const binding=this.binding(uiSourceCode);return binding?binding.fileSystem:null;}
network(uiSourceCode){const binding=this.binding(uiSourceCode);return binding?binding.network:null;}
_addFilePathBindingPrefixes(filePath){let relative='';for(const token of filePath.split('/')){relative+=token+'/';const count=this._filePathPrefixesToBindingCount.get(relative)||0;this._filePathPrefixesToBindingCount.set(relative,count+1);}}
_removeFilePathBindingPrefixes(filePath){let relative='';for(const token of filePath.split('/')){relative+=token+'/';const count=this._filePathPrefixesToBindingCount.get(relative);if(count===1){this._filePathPrefixesToBindingCount.delete(relative);}else{this._filePathPrefixesToBindingCount.set(relative,count-1);}}}
filePathHasBindings(filePath){if(!filePath.endsWith('/')){filePath+='/';}
return this._filePathPrefixesToBindingCount.has(filePath);}}
const _binding=Symbol('Persistence.Binding');const _muteCommit=Symbol('Persistence.MuteCommit');const _muteWorkingCopy=Symbol('Persistence.MuteWorkingCopy');export const NodePrefix='(function (exports, require, module, __filename, __dirname) { ';export const NodeSuffix='\n});';export const NodeShebang='#!/usr/bin/env node';export const Events={BindingCreated:Symbol('BindingCreated'),BindingRemoved:Symbol('BindingRemoved')};export class PathEncoder{constructor(){this._encoder=new Common.CharacterIdMap.CharacterIdMap();}
encode(path){return path.split('/').map(token=>this._encoder.toChar(token)).join('');}
decode(path){return path.split('').map(token=>this._encoder.fromChar(token)).join('/');}}
export class PersistenceBinding{constructor(network,fileSystem){this.network=network;this.fileSystem=fileSystem;}}