@@ -16,6 +16,13 @@ enum FolderNoteType {
1616 OutsideFolder = "OUTSIDE_FOLDER" ,
1717}
1818
19+ enum SortType {
20+ Priority = "PRIORITY" ,
21+ Natural = "NATURAL" ,
22+ Lexicographic = "LEXICOGRAPHIC" ,
23+ FoldersFirst = "FOLDERS_FIRST" ,
24+ }
25+
1926interface WaypointSettings {
2027 waypointFlag : string ;
2128 stopScanAtFolderNotes : boolean ;
@@ -25,6 +32,8 @@ interface WaypointSettings {
2532 useWikiLinks : boolean ;
2633 showEnclosingNote : boolean ;
2734 folderNoteType : string ;
35+ waypointPriorityKey : string ;
36+ sortType : string ;
2837 useSpaces : boolean ;
2938 numSpaces : number ;
3039 ignoredFolders : string [ ] ;
@@ -40,6 +49,8 @@ const DEFAULT_SETTINGS: WaypointSettings = {
4049 useWikiLinks : true ,
4150 showEnclosingNote : false ,
4251 folderNoteType : FolderNoteType . InsideFolder ,
52+ waypointPriorityKey : "waypointPriority" ,
53+ sortType : SortType . Natural ,
4354 useSpaces : false ,
4455 numSpaces : 2 ,
4556 ignoredFolders : [ "Templates" ] ,
@@ -85,7 +96,14 @@ export default class Waypoint extends Plugin {
8596 this . scheduleUpdate ( ) ;
8697 } )
8798 ) ;
88- this . registerEvent ( this . app . vault . on ( "modify" , this . detectWaypointFlag ) ) ;
99+ this . registerEvent (
100+ this . app . vault . on ( "modify" , ( file ) => {
101+ this . log ( "modify " + file . name ) ;
102+ this . foldersWithChanges . add ( file . parent ) ;
103+ this . scheduleUpdate ( ) ;
104+ this . detectWaypointFlag ( file as TFile ) ;
105+ } )
106+ ) ;
89107 } ) ;
90108
91109 // This adds a settings tab so the user can configure various aspects of the plugin
@@ -113,7 +131,7 @@ export default class Waypoint extends Plugin {
113131 * Scan the given file for the waypoint flag. If found, update the waypoint.
114132 * @param file The file to scan
115133 */
116- detectWaypointFlag = async ( file : TFile ) = > {
134+ async detectWaypointFlag ( file : TFile ) : Promise < void > {
117135 this . log ( "Modification on " + file . name ) ;
118136 this . log ( "Scanning for Waypoint flags..." ) ;
119137 const text = await this . app . vault . cachedRead ( file ) ;
@@ -123,7 +141,7 @@ export default class Waypoint extends Plugin {
123141 if ( this . isFolderNote ( file ) ) {
124142 this . log ( "Found waypoint flag in folder note!" ) ;
125143 await this . updateWaypoint ( file ) ;
126- await this . updateParentWaypoint ( file . parent , this . settings . folderNoteType === FolderNoteType . OutsideFolder ) ;
144+ await this . updateAncestorWaypoint ( file . parent , this . settings . folderNoteType === FolderNoteType . OutsideFolder ) ;
127145 return ;
128146 } else if ( file . parent . isRoot ( ) ) {
129147 this . log ( "Found waypoint flag in root folder." ) ;
@@ -142,7 +160,7 @@ export default class Waypoint extends Plugin {
142160 }
143161 }
144162 this . log ( "No waypoint flags found." ) ;
145- } ;
163+ }
146164
147165 isFolderNote ( file : TFile ) : boolean {
148166 if ( this . settings . folderNoteType === FolderNoteType . InsideFolder ) {
@@ -186,7 +204,7 @@ export default class Waypoint extends Plugin {
186204 * Given a file with a waypoint flag, generate a file tree representation and update the waypoint text.
187205 * @param file The file to update
188206 */
189- async updateWaypoint ( file : TFile ) {
207+ async updateWaypoint ( file : TFile ) : Promise < void > {
190208 this . log ( "Updating waypoint in " + file . path ) ;
191209 let fileTree ;
192210 if ( this . settings . folderNoteType === FolderNoteType . InsideFolder ) {
@@ -220,8 +238,17 @@ export default class Waypoint extends Plugin {
220238 return ;
221239 }
222240 this . log ( "Waypoint found at " + waypointStart + " to " + waypointEnd ) ;
223- lines . splice ( waypointStart , waypointEnd !== - 1 ? waypointEnd - waypointStart + 1 : 1 , waypoint ) ;
224- await this . app . vault . modify ( file , lines . join ( "\n" ) ) ;
241+
242+ // Get the current waypoint block from lines and join it to form a string
243+ const currentWaypoint =
244+ waypointEnd !== - 1 ? lines . slice ( waypointStart , waypointEnd + 1 ) . join ( "\n" ) : lines [ waypointStart ] ;
245+
246+ // Only splice and modify if waypoint differs from the current block
247+ if ( currentWaypoint !== waypoint ) {
248+ this . log ( "Waypoint content changed, updating" ) ;
249+ lines . splice ( waypointStart , waypointEnd !== - 1 ? waypointEnd - waypointStart + 1 : 1 , waypoint ) ;
250+ await this . app . vault . modify ( file , lines . join ( "\n" ) ) ;
251+ }
225252 }
226253
227254 /**
@@ -241,7 +268,7 @@ export default class Waypoint extends Plugin {
241268 const indent = this . settings . useSpaces ? " " . repeat ( this . settings . numSpaces ) : " " ;
242269 const bullet = indent . repeat ( indentLevel ) + "-" ;
243270 if ( node instanceof TFile ) {
244- console . log ( node ) ;
271+ this . log ( node ) ;
245272 // Print the file name
246273 // Check for the parent being the root because otherwise the "root note" would be included in the tree
247274 if ( node . extension == "md" && ! node . parent . isRoot ( ) ) {
@@ -295,12 +322,21 @@ export default class Waypoint extends Plugin {
295322 if ( node . children && node . children . length > 0 ) {
296323 // Print the files and nested folders within the folder
297324 let children = node . children ;
298- children = children . sort ( ( a , b ) => {
299- return a . name . localeCompare ( b . name , undefined , {
300- numeric : true ,
301- sensitivity : "base" ,
302- } ) ;
303- } ) ;
325+
326+ switch ( this . settings . sortType ) {
327+ case SortType . Lexicographic :
328+ children = children . sort ( ) ;
329+ break ;
330+ case SortType . Priority :
331+ children = children . sort ( this . sortWithPriority ) ;
332+ break ;
333+ case SortType . FoldersFirst :
334+ children = children . sort ( this . sortFoldersFirst ) ;
335+ break ;
336+ default :
337+ children = children . sort ( this . sortWithNaturalOrder ) ;
338+ break ;
339+ }
304340 if ( ! this . settings . showFolderNotes ) {
305341 if ( this . settings . folderNoteType === FolderNoteType . InsideFolder ) {
306342 children = children . filter ( ( child ) => this . settings . showFolderNotes || child . name !== node . name + ".md" ) ;
@@ -350,31 +386,31 @@ export default class Waypoint extends Plugin {
350386 /**
351387 * Scan the changed folders and their ancestors for waypoints and update them if found.
352388 */
353- updateChangedFolders = async ( ) => {
389+ async updateChangedFolders ( ) {
354390 this . log ( "Updating changed folders..." ) ;
355391 this . foldersWithChanges . forEach ( ( folder ) => {
356392 this . log ( "Updating " + folder . path ) ;
357- this . updateParentWaypoint ( folder , true ) ;
393+ this . updateAncestorWaypoint ( folder , true ) ;
358394 } ) ;
359395 this . foldersWithChanges . clear ( ) ;
360- } ;
396+ }
361397
362398 /**
363399 * Schedule an update for the changed folders after debouncing to prevent excessive updates.
364400 */
365401 scheduleUpdate = debounce ( this . updateChangedFolders . bind ( this ) , 500 , true ) ;
366402
367403 /**
368- * Update the ancestor waypoint (if any) of the given file/folder.
404+ * Update all ancestor waypoints (if any) of the given file/folder.
369405 * @param node The node to start the search from
370406 * @param includeCurrentNode Whether to include the given folder in the search
371407 */
372- updateParentWaypoint = async ( node : TAbstractFile , includeCurrentNode : boolean ) = > {
408+ async updateAncestorWaypoint ( node : TAbstractFile , includeCurrentNode : boolean ) : Promise < void > {
373409 const parentWaypoint = await this . locateParentWaypoint ( node , includeCurrentNode ) ;
374410 if ( parentWaypoint !== null ) {
375411 this . updateWaypoint ( parentWaypoint ) ;
376412 }
377- } ;
413+ }
378414
379415 /**
380416 * Locate the ancestor waypoint (if any) of the given file/folder.
@@ -383,10 +419,10 @@ export default class Waypoint extends Plugin {
383419 * @returns The ancestor waypoint, or null if none was found
384420 */
385421 async locateParentWaypoint ( node : TAbstractFile , includeCurrentNode : boolean ) : Promise < TFile > {
386- this . log ( "Locating parent waypoint of " + node . name ) ;
422+ this . log ( "Locating all ancestor waypoints of " + node . name ) ;
387423 let folder = includeCurrentNode ? node : node . parent ;
388424 // When there's a root-level folder note
389- if ( node . parent . isRoot ( ) && this . settings . root !== null ) {
425+ if ( node . parent ? .isRoot ( ) && this . settings . root !== null ) {
390426 const file = this . app . vault . getAbstractFileByPath ( this . settings . root ) ;
391427 if ( file instanceof TFile ) {
392428 this . log ( "Found folder note: " + file . path ) ;
@@ -435,7 +471,7 @@ export default class Waypoint extends Plugin {
435471 }
436472 }
437473
438- log ( message : string ) {
474+ log ( message ? : string | TFile ) {
439475 if ( this . settings . debugLogging ) {
440476 console . log ( message ) ;
441477 }
@@ -448,6 +484,71 @@ export default class Waypoint extends Plugin {
448484 async saveSettings ( ) {
449485 await this . saveData ( this . settings ) ;
450486 }
487+
488+ getWaypointPriority ( file : TAbstractFile ) : number | null {
489+ if ( file instanceof TFile ) {
490+ const { waypointPriorityKey } = this . settings ;
491+ const fileCache = this . app . metadataCache . getFileCache ( file as TFile ) ;
492+ if ( typeof fileCache ?. frontmatter ?. [ waypointPriorityKey ] === "number" ) {
493+ return fileCache . frontmatter [ waypointPriorityKey ] ;
494+ } else {
495+ return null ;
496+ }
497+ } else if ( file instanceof TFolder ) {
498+ if ( this . settings . folderNoteType === FolderNoteType . InsideFolder ) {
499+ // If file is a folder and folder note is an inside note, attempt to find a child note with the same name.
500+ const foldernote : TAbstractFile | null = file . children . find (
501+ ( child ) => child instanceof TFile && child . basename === file . name
502+ ) ;
503+ return foldernote ? this . getWaypointPriority ( foldernote ) : null ;
504+ } else if ( this . settings . folderNoteType === FolderNoteType . OutsideFolder ) {
505+ // If file is a folder and folder note is an outside note, attempt to find a sibling note with the same name.
506+ if ( ! file . isRoot ( ) ) {
507+ const foldernote : TAbstractFile | null = file . parent . children . find (
508+ ( child ) => child instanceof TFile && child . basename === file . name
509+ ) ;
510+ return foldernote ? this . getWaypointPriority ( foldernote ) : null ;
511+ } else {
512+ return null ; // Handle case when the file is the root folder.
513+ }
514+ }
515+ return null ;
516+ }
517+ }
518+
519+ sortWithNaturalOrder = ( a : TAbstractFile , b : TAbstractFile ) : number => {
520+ return a . name . localeCompare ( b . name , undefined , {
521+ numeric : true ,
522+ sensitivity : "base" ,
523+ } ) ;
524+ } ;
525+
526+ sortFoldersFirst = ( a : TAbstractFile , b : TAbstractFile ) : number => {
527+ if ( a instanceof TFolder ) {
528+ // if b is also a folder, sort normally, otherwise a (the folder) comes first
529+ return b instanceof TFolder ? this . sortWithNaturalOrder ( a , b ) : - 1 ;
530+ }
531+ // a is a file. if b is a folder, it comes first, otherwise sort both files normally
532+ return b instanceof TFolder ? 1 : this . sortWithNaturalOrder ( a , b ) ;
533+ } ;
534+
535+ sortWithPriority = ( a : TAbstractFile , b : TAbstractFile ) : number => {
536+ const aPriority = this . getWaypointPriority ( a ) ;
537+ const bPriority = this . getWaypointPriority ( b ) ;
538+ if ( aPriority !== null && bPriority !== null ) {
539+ // If both have waypointPriority, the one with a lower priority number should come first.
540+ return aPriority - bPriority ;
541+ } else if ( aPriority !== null ) {
542+ // If only `a` has waypointPriority, `a` should come first.
543+ return - 1 ;
544+ } else if ( bPriority !== null ) {
545+ // If only `b` has waypointPriority, `b` should come first.
546+ return 1 ;
547+ } else {
548+ // If neither has priority, sort alphabetically.
549+ return this . sortWithNaturalOrder ( a , b ) ;
550+ }
551+ } ;
451552}
452553
453554class WaypointSettingsTab extends PluginSettingTab {
@@ -473,6 +574,7 @@ class WaypointSettingsTab extends PluginSettingTab {
473574 . onChange ( async ( value ) => {
474575 this . plugin . settings . folderNoteType = value ;
475576 await this . plugin . saveSettings ( ) ;
577+ this . display ( ) ;
476578 } )
477579 ) ;
478580 new Setting ( containerEl )
@@ -573,6 +675,35 @@ class WaypointSettingsTab extends PluginSettingTab {
573675 await this . plugin . saveSettings ( ) ;
574676 } )
575677 ) ;
678+ new Setting ( this . containerEl )
679+ . setName ( "Sorting Method" )
680+ . setDesc ( "Select how you would like to have your waypoint lists sorted." )
681+ . addDropdown ( ( dropdown ) =>
682+ dropdown
683+ . addOption ( SortType . Natural , "Natural" )
684+ . addOption ( SortType . Priority , "Prioritized" )
685+ . addOption ( SortType . Lexicographic , "Lexicographic" )
686+ . addOption ( SortType . FoldersFirst , "Folders First" )
687+ . setValue ( this . plugin . settings . sortType )
688+ . onChange ( async ( value ) => {
689+ this . plugin . settings . sortType = value ;
690+ await this . plugin . saveSettings ( ) ;
691+ this . display ( ) ;
692+ } )
693+ ) ;
694+ new Setting ( containerEl )
695+ . setName ( "Frontmatter key for note priority" )
696+ . setDesc ( "The frontmatter key to set the note order piority when listed in a Waypoint." )
697+ . addText ( ( text ) =>
698+ text
699+ . setPlaceholder ( DEFAULT_SETTINGS . waypointPriorityKey )
700+ . setValue ( this . plugin . settings . waypointPriorityKey )
701+ . onChange ( async ( value ) => {
702+ this . plugin . settings . waypointPriorityKey = value ;
703+ await this . plugin . saveSettings ( ) ;
704+ } )
705+ )
706+ . setDisabled ( this . plugin . settings . sortType !== SortType . Priority ) ;
576707 new Setting ( containerEl )
577708 . setName ( "Ignored folders" )
578709 . setDesc ( "Folders that Waypoint should ignore" )
0 commit comments