@@ -63,6 +63,7 @@ type StateProps = {
63
63
lineLength : T . LineLength ;
64
64
noteId : T . EntityId ;
65
65
note : T . Note ;
66
+ notes : Map < T . EntityId , T . Note > ;
66
67
searchQuery : string ;
67
68
spellCheckEnabled : boolean ;
68
69
theme : T . Theme ;
@@ -72,6 +73,7 @@ type DispatchProps = {
72
73
clearSearch : ( ) => any ;
73
74
editNote : ( noteId : T . EntityId , changes : Partial < T . Note > ) => any ;
74
75
insertTask : ( ) => any ;
76
+ openNote : ( noteId : T . EntityId ) => any ;
75
77
storeEditorSelection : (
76
78
noteId : T . EntityId ,
77
79
start : number ,
@@ -430,6 +432,41 @@ class NoteContentEditor extends Component<Props> {
430
432
this . editor = editor ;
431
433
this . monaco = monaco ;
432
434
435
+ monaco . languages . registerLinkProvider ( 'plaintext' , {
436
+ provideLinks : ( model ) => {
437
+ const matches = model . findMatches (
438
+ 'simplenote://note/[a-zA-Z0-9-]+' ,
439
+ true , // searchOnlyEditableRange
440
+ true , // isRegex
441
+ false , // matchCase
442
+ null , // wordSeparators
443
+ false // captureMatches
444
+ ) ;
445
+ return {
446
+ // don't set a URL on these links, because then Monaco skips resolveLink
447
+ // @cite : https://github.com/Microsoft/vscode/blob/8f89095aa6097f6e0014f2d459ef37820983ae55/src/vs/editor/contrib/links/getLinks.ts#L43:L65
448
+ links : matches . map ( ( { range } ) => ( { range } ) ) ,
449
+ } ;
450
+ } ,
451
+ resolveLink : ( link ) => {
452
+ const href = editor . getModel ( ) ?. getValueInRange ( link . range ) ?? '' ;
453
+ const match = / ^ s i m p l e n o t e : \/ \/ n o t e \/ ( .+ ) $ / . exec ( href ) ;
454
+ if ( ! match ) {
455
+ return ;
456
+ }
457
+
458
+ const [ fullMatch , linkedNoteId ] = match as [ string , T . EntityId ] ;
459
+
460
+ // if we try to open a note that doesn't exist in local state,
461
+ // then we annoyingly close the open note without opening anything else
462
+ // implicit else: links that aren't openable will just do nothing
463
+ if ( this . props . notes . has ( linkedNoteId ) ) {
464
+ this . props . openNote ( linkedNoteId ) ;
465
+ }
466
+ return { ...link , url : '#' } ; // tell Monaco to do nothing and not complain about it
467
+ } ,
468
+ } ) ;
469
+
433
470
// remove keybindings; see https://github.com/microsoft/monaco-editor/issues/287
434
471
const shortcutsToDisable = [
435
472
'cancelSelection' , // escape; we need to allow this to bubble up to clear search
@@ -975,6 +1012,7 @@ const mapStateToProps: S.MapState<StateProps> = (state) => ({
975
1012
lineLength : state . settings . lineLength ,
976
1013
noteId : state . ui . openedNote ,
977
1014
note : state . data . notes . get ( state . ui . openedNote ) ,
1015
+ notes : state . data . notes ,
978
1016
searchQuery : state . ui . searchQuery ,
979
1017
spellCheckEnabled : state . settings . spellCheckEnabled ,
980
1018
theme : selectors . getTheme ( state ) ,
@@ -984,6 +1022,7 @@ const mapDispatchToProps: S.MapDispatch<DispatchProps> = {
984
1022
clearSearch : ( ) => dispatch ( search ( '' ) ) ,
985
1023
editNote : actions . data . editNote ,
986
1024
insertTask : ( ) => ( { type : 'INSERT_TASK' } ) ,
1025
+ openNote : actions . ui . selectNote ,
987
1026
storeEditorSelection : ( noteId , start , end , direction ) => ( {
988
1027
type : 'STORE_EDITOR_SELECTION' ,
989
1028
noteId,
0 commit comments