@@ -655,25 +655,18 @@ function execOperation(type, action, url, $target, name) {
655655 }
656656
657657 newName = Url . basename ( newUrl ) ;
658- $target . querySelector ( ":scope>.text" ) . textContent = newName ;
659- $target . dataset . url = newUrl ;
660- $target . dataset . name = newName ;
661658 if ( helpers . isFile ( type ) ) {
662- $target . querySelector ( ":scope>span" ) . className =
663- helpers . getIconForFile ( newName ) ;
664659 let file = editorManager . getFile ( url , "uri" ) ;
665660 if ( file ) {
666661 file . uri = newUrl ;
667662 file . filename = newName ;
668663 }
669664 } else {
670665 helpers . updateUriOfAllActiveFiles ( url , newUrl ) ;
671- //Reloading the folder by collapsing and expanding the folder
672- $target . click ( ) ; //collapse
673- $target . click ( ) ; //expand
674666 }
675- toast ( strings . success ) ;
676667 FileList . rename ( url , newUrl ) ;
668+ await refreshRenamedEntryInOpenFolders ( url , newUrl ) ;
669+ toast ( strings . success ) ;
677670 }
678671
679672 async function createNew ( ) {
@@ -1000,6 +993,115 @@ function getLoadedFileTree($el) {
1000993 ) ;
1001994}
1002995
996+ function normalizeUrlPathKey ( url ) {
997+ if ( ! url ) return url ;
998+ const { url : parsedUrl } = Url . parse ( url ) ;
999+
1000+ if ( Url . getProtocol ( parsedUrl ) === "content:" ) {
1001+ try {
1002+ const { rootUri, docId } = Uri . parse ( parsedUrl ) ;
1003+ const normalizedDocId = docId . endsWith ( "/" ) ? docId . slice ( 0 , - 1 ) : docId ;
1004+ return `${ rootUri } ::${ normalizedDocId } ` ;
1005+ } catch ( error ) {
1006+ return parsedUrl ;
1007+ }
1008+ }
1009+
1010+ if ( parsedUrl . endsWith ( "/" ) && Url . pathname ( parsedUrl ) !== "/" ) {
1011+ return parsedUrl . slice ( 0 , - 1 ) ;
1012+ }
1013+
1014+ return parsedUrl ;
1015+ }
1016+
1017+ function areSameOpenFolderUrl ( leftUrl , rightUrl ) {
1018+ return normalizeUrlPathKey ( leftUrl ) === normalizeUrlPathKey ( rightUrl ) ;
1019+ }
1020+
1021+ function isInsideOpenFolder ( url , folderUrl ) {
1022+ const urlKey = normalizeUrlPathKey ( url ) ;
1023+ const folderKey = normalizeUrlPathKey ( folderUrl ) ;
1024+ if ( ! urlKey || ! folderKey ) return false ;
1025+
1026+ return urlKey === folderKey || urlKey . startsWith ( `${ folderKey } /` ) ;
1027+ }
1028+
1029+ function appendUrlPathSuffix ( url , suffix ) {
1030+ if ( ! suffix ) return url ;
1031+ const { url : parsedUrl , query } = Url . parse ( url ) ;
1032+ if ( parsedUrl . endsWith ( "/" ) && suffix . startsWith ( "/" ) ) {
1033+ return parsedUrl . slice ( 0 , - 1 ) + suffix + query ;
1034+ }
1035+ return parsedUrl + suffix + query ;
1036+ }
1037+
1038+ function preserveTrailingSlashShape ( url , sourceUrl ) {
1039+ const { url : sourcePath } = Url . parse ( sourceUrl ) ;
1040+ if ( ! sourcePath . endsWith ( "/" ) ) return url ;
1041+
1042+ const { url : targetPath , query } = Url . parse ( url ) ;
1043+ if ( targetPath . endsWith ( "/" ) ) return url ;
1044+
1045+ return `${ targetPath } /${ query } ` ;
1046+ }
1047+
1048+ function getListStateEntries ( listState ) {
1049+ if ( ! listState ) return [ ] ;
1050+ if ( listState instanceof Map ) return Array . from ( listState . entries ( ) ) ;
1051+ return Object . entries ( listState ) ;
1052+ }
1053+
1054+ function setListStateEntry ( listState , key , value ) {
1055+ if ( listState instanceof Map ) {
1056+ listState . set ( key , value ) ;
1057+ return ;
1058+ }
1059+
1060+ listState [ key ] = value ;
1061+ }
1062+
1063+ function deleteListStateEntry ( listState , key ) {
1064+ if ( listState instanceof Map ) {
1065+ listState . delete ( key ) ;
1066+ return ;
1067+ }
1068+
1069+ delete listState [ key ] ;
1070+ }
1071+
1072+ /**
1073+ * Move saved expanded-state keys after a folder rename.
1074+ * @param {string } oldUrl
1075+ * @param {string } newUrl
1076+ */
1077+ function migrateOpenFolderStateUrls ( oldUrl , newUrl ) {
1078+ if ( ! oldUrl || ! newUrl || areSameOpenFolderUrl ( oldUrl , newUrl ) ) return ;
1079+
1080+ const oldKey = normalizeUrlPathKey ( oldUrl ) ;
1081+
1082+ addedFolder . forEach ( ( { listState } ) => {
1083+ const matchingEntries = getListStateEntries ( listState ) . filter (
1084+ ( [ folderUrl ] ) => {
1085+ return isInsideOpenFolder ( folderUrl , oldUrl ) ;
1086+ } ,
1087+ ) ;
1088+
1089+ matchingEntries . forEach ( ( [ folderUrl , isExpanded ] ) => {
1090+ const suffix = normalizeUrlPathKey ( folderUrl ) . slice ( oldKey . length ) ;
1091+ const migratedUrl = preserveTrailingSlashShape (
1092+ appendUrlPathSuffix ( newUrl , suffix ) ,
1093+ folderUrl ,
1094+ ) ;
1095+ deleteListStateEntry ( listState , folderUrl ) ;
1096+ setListStateEntry ( listState , migratedUrl , isExpanded ) ;
1097+ } ) ;
1098+ } ) ;
1099+ }
1100+
1101+ function getParentUrl ( url ) {
1102+ return Url . dirname ( url ) ;
1103+ }
1104+
10031105/**
10041106 * Remove matching rendered entries from expanded folder views.
10051107 * This keeps FileTree's in-memory state aligned with the rendered tree.
@@ -1068,21 +1170,38 @@ function appendEntryToOpenFolder(parentUrl, entryUrl, type) {
10681170 * @param {string } folderUrl
10691171 */
10701172async function refreshOpenFolder ( folderUrl ) {
1071- const filesApp = sidebarApps . get ( "files" ) ;
1072- const $els = filesApp . getAll ( `[data-url=" ${ folderUrl } "]` ) ;
1173+ const folder = openFolder . find ( folderUrl ) ;
1174+ if ( ! folder ) return ;
10731175
1074- await Promise . all (
1075- Array . from ( $els ) . map ( async ( $el ) => {
1076- if ( ! ( helpers . isDir ( $el . dataset . type ) || $el . dataset . type === "root" ) ) {
1077- return ;
1078- }
1176+ const fileTree = getLoadedFileTree ( folder . $node . $title ) ;
1177+ if ( ! fileTree ) return ;
10791178
1080- const fileTree = getLoadedFileTree ( $el ) ;
1081- if ( fileTree ) {
1082- await fileTree . refresh ( ) ;
1083- }
1084- } ) ,
1085- ) ;
1179+ await fileTree . refreshFolder ( folderUrl , areSameOpenFolderUrl ) ;
1180+ }
1181+
1182+ /**
1183+ * Refresh affected folder trees after a rename/move.
1184+ * @param {string } oldUrl
1185+ * @param {string } newUrl
1186+ */
1187+ async function refreshRenamedEntryInOpenFolders (
1188+ oldUrl ,
1189+ newUrl ,
1190+ oldParentUrl = getParentUrl ( oldUrl ) ,
1191+ newParentUrl = getParentUrl ( newUrl ) ,
1192+ ) {
1193+ if ( ! oldUrl || ! newUrl || areSameOpenFolderUrl ( oldUrl , newUrl ) ) return ;
1194+
1195+ migrateOpenFolderStateUrls ( oldUrl , newUrl ) ;
1196+
1197+ const parentUrls = [ oldParentUrl , newParentUrl ] . filter ( Boolean ) ;
1198+ const refreshUrls = parentUrls . filter ( ( parentUrl , index ) => {
1199+ return ! parentUrls . some ( ( otherUrl , otherIndex ) => {
1200+ return otherIndex < index && areSameOpenFolderUrl ( otherUrl , parentUrl ) ;
1201+ } ) ;
1202+ } ) ;
1203+
1204+ await Promise . all ( refreshUrls . map ( refreshOpenFolder ) ) ;
10861205}
10871206
10881207/**
@@ -1132,29 +1251,11 @@ openFolder.add = async (url, type) => {
11321251 appendEntryToOpenFolder ( parent , url , type ) ;
11331252} ;
11341253
1135- openFolder . renameItem = ( oldFile , newFile , newFilename ) => {
1254+ openFolder . renameItem = ( oldFile , newFile ) => {
11361255 FileList . rename ( oldFile , newFile ) ;
11371256
11381257 helpers . updateUriOfAllActiveFiles ( oldFile , newFile ) ;
1139-
1140- const filesApp = sidebarApps . get ( "files" ) ;
1141- const $els = filesApp . getAll ( `[data-url="${ oldFile } "]` ) ;
1142- Array . from ( $els ) . forEach ( ( $el ) => {
1143- if ( $el . dataset . type === "dir" ) {
1144- $el = $el . $title ;
1145- setTimeout ( ( ) => {
1146- $el . collapse ( ) ;
1147- $el . expand ( ) ;
1148- } , 0 ) ;
1149- } else {
1150- $el . querySelector ( ":scope>span" ) . className =
1151- helpers . getIconForFile ( newFilename ) ;
1152- }
1153-
1154- $el . dataset . url = newFile ;
1155- $el . dataset . name = newFilename ;
1156- $el . querySelector ( ":scope>.text" ) . textContent = newFilename ;
1157- } ) ;
1258+ refreshRenamedEntryInOpenFolders ( oldFile , newFile ) . catch ( helpers . error ) ;
11581259} ;
11591260
11601261openFolder . removeItem = ( url ) => {
@@ -1185,13 +1286,11 @@ openFolder.removeFolders = (url) => {
11851286 * @returns {Folder }
11861287 */
11871288openFolder . find = ( url ) => {
1188- const found = addedFolder . find ( ( folder ) => folder . url === url ) ;
1289+ const found = addedFolder . find ( ( folder ) =>
1290+ areSameOpenFolderUrl ( folder . url , url ) ,
1291+ ) ;
11891292 if ( found ) return found ;
1190- return addedFolder . find ( ( folder ) => {
1191- const { url : furl } = Url . parse ( folder . url ) ;
1192- const regex = new RegExp ( "^" + escapeStringRegexp ( furl ) ) ;
1193- return regex . test ( url ) ;
1194- } ) ;
1293+ return addedFolder . find ( ( folder ) => isInsideOpenFolder ( url , folder . url ) ) ;
11951294} ;
11961295
11971296export default openFolder ;
0 commit comments