11import { ContainerSink } from "./caretsink.js" ;
2- import { insertAt , deleteAt , distMouseEventToEl , vertDistPointToLineEl , makeid , } from "./helpers.js" ;
2+ import { insertAt , deleteAt , makeid , } from "./helpers.js" ;
33export const editor = ( id = Math . random ( ) + "" , parentContainerSink , eContext ) => {
4- const { elFromFocusId, calcAndRenderSelection, renderCaret , renderAnchor , getCaretId , setCaretId, getCaretPos , setCaretPos, setAnchorId, setAnchorPos, pushHistory , } = eContext ;
4+ const { elFromFocusId, calcAndRenderSelection, setCaretId, setCaretPos, setAnchorId, setAnchorPos, getCursors , } = eContext ;
55 const wrapEl = new DOMParser ( ) . parseFromString ( `<div style="
66 padding: 4px 4px 4px 0;
77 border: 1px solid black;
@@ -11,32 +11,7 @@ export const editor = (id = Math.random() + "", parentContainerSink, eContext) =
1111 display: inline-block;
1212 " class="editor" tabIndex="0"></div>` , "text/html" ) . body . firstChild ;
1313 elFromFocusId [ id ] = wrapEl ;
14- const mousePick = ( shouldMoveAnchor ) => ( e ) => {
15- e . stopPropagation ( ) ;
16- const closestLineEl = wrapEl . lineEls . sort ( ( el1 , el2 ) => vertDistPointToLineEl ( e , el1 ) - vertDistPointToLineEl ( e , el2 ) ) [ 0 ] ;
17- const picked = [ ...closestLineEl . children ] . sort ( ( el1 , el2 ) => distMouseEventToEl ( e , el1 ) - distMouseEventToEl ( e , el2 ) ) [ 0 ] ;
18- setCaretId ( id ) ;
19- setCaretPos ( picked . id ) ;
20- if ( shouldMoveAnchor ) {
21- setAnchorId ( id ) ;
22- setAnchorPos ( picked . id ) ;
23- renderAnchor ( ) ;
24- wrapEl . focus ( ) ;
25- }
26- renderCaret ( ) ;
27- calcAndRenderSelection ( ) ;
28- } ;
29- wrapEl . addEventListener ( "mousedown" , ( e ) => {
30- requestAnimationFrame ( ( ) => mousePick ( true ) ( e ) ) ;
31- e . stopPropagation ( ) ;
32- } ) ;
33- wrapEl . addEventListener ( "mousemove" , ( e ) => {
34- if ( e . buttons === 1 ) {
35- requestAnimationFrame ( ( ) => mousePick ( false ) ( e ) ) ;
36- e . stopPropagation ( ) ;
37- }
38- } ) ;
39- wrapEl . addEventListener ( "keydown" , ( e ) => {
14+ wrapEl . onKey = ( e ) => {
4015 if ( e . key === "Tab" )
4116 e . preventDefault ( ) ;
4217 if ( e . key === "Backspace" )
@@ -45,38 +20,33 @@ export const editor = (id = Math.random() + "", parentContainerSink, eContext) =
4520 e . preventDefault ( ) ;
4621 const discrim = e . key . length === 1 || e . key === "Enter" || e . key === "Backspace" ;
4722 if ( discrim && ! e . metaKey ) {
48- pushHistory ( {
23+ return {
4924 key : e . key ,
50- keyId : makeid ( 7 ) , // MULTICURSOR BUG! same id for both cursors. uh oh.
51- } ) ;
52- e . stopPropagation ( ) ;
25+ keyId : makeid ( 7 ) ,
26+ } ;
5327 }
5428 else if ( e . key === "b" && e . metaKey ) {
55- pushHistory ( {
29+ return {
5630 key : e . key ,
57- newId : makeid ( 7 ) , // MULTICURSOR BUG! same id for both cursors. uh oh.
58- } ) ;
59- e . stopPropagation ( ) ;
31+ newId : makeid ( 7 ) ,
32+ } ;
6033 }
6134 else if ( e . key === "Tab" && e . shiftKey ) {
62- pushHistory ( {
35+ return {
6336 despacify : true ,
64- } ) ;
65- e . stopPropagation ( ) ;
37+ } ;
6638 }
6739 else if ( e . key === "Tab" ) {
68- pushHistory ( {
40+ return {
6941 spacify : true ,
70- } ) ;
71- e . stopPropagation ( ) ;
42+ } ;
7243 }
7344 else if ( e . key === "/" && e . metaKey ) {
74- pushHistory ( {
45+ return {
7546 commentify : true ,
76- } ) ;
77- e . stopPropagation ( ) ;
47+ } ;
7848 }
79- } ) ;
49+ } ;
8050 wrapEl . sink = new ContainerSink ( ( ) => wrapEl . getBoundingClientRect ( ) ) ;
8151 wrapEl . sink . parent = parentContainerSink ?? null ;
8252 let str = [ ] ;
@@ -85,78 +55,92 @@ export const editor = (id = Math.random() + "", parentContainerSink, eContext) =
8555 str = [ ] ;
8656 lines = [ [ ] ] ;
8757 wrapEl . str = str . map ( ( s ) => s . char ) ;
58+ wrapEl . rawStr = str ;
8859 wrapEl . lines = lines ;
8960 wrapEl . innerHTML = "" ;
9061 }
9162 function myInsertAt ( pos , char ) {
9263 str = insertAt ( str , pos , char ) ;
93- wrapEl . str = str . map ( ( s ) => s . char ) ;
9464 }
95- // char id to pos
96- // insertAfter(id, newThing)
97- // delete(id)
9865 const insertAfter = ( id , char ) => {
9966 const i = str . findIndex ( ( v ) => v . id === id ) ;
10067 if ( i === - 1 )
10168 myInsertAt ( 0 , char ) ;
10269 else
10370 myInsertAt ( i + 1 , char ) ;
71+ wrapEl . str = str . map ( ( s ) => s . char ) ;
72+ wrapEl . rawStr = str ;
10473 } ;
10574 const deleteAtId = ( id ) => {
10675 const i = str . findIndex ( ( v ) => v . id === id ) ;
10776 if ( i === - 1 )
10877 throw "couldn't find id to delete" ;
10978 str = deleteAt ( str , i ) ;
79+ wrapEl . str = str . map ( ( s ) => s . char ) ;
80+ wrapEl . rawStr = str ;
11081 return i ;
11182 } ;
11283 function act ( e ) {
113- if ( e . paste ) {
114- if ( e . paste . id ) {
115- act ( { ...e , paste : undefined , newId : e . paste . id } ) ;
116- // BUG: I think his causes issues, it moves the caret to the wrong spot mid-paste?
117- elFromFocusId [ getCaretId ( ) ] ?. act ( {
118- ...e ,
119- paste : e . paste . data ,
120- } ) ;
121- }
122- else if ( Array . isArray ( e . paste ) ) {
123- for ( const entry of e . paste ) {
124- if ( entry . id )
125- act ( { ...e , paste : entry } ) ;
126- else
127- act ( { ...e , paste : undefined , key : entry } ) ;
128- }
129- }
130- }
131- else if ( e . newId ) {
84+ // if (e.paste) {
85+ // if (e.paste.id) {
86+ // act({ ...e, paste: undefined, newId: e.paste.id });
87+ // // BUG: I think his causes issues, it moves the caret to the wrong spot mid-paste?
88+ // elFromFocusId[getCaretId()]?.act({
89+ // ...e,
90+ // paste: e.paste.data,
91+ // });
92+ // } else if (Array.isArray(e.paste)) {
93+ // for (const entry of e.paste) {
94+ // if (entry.id) act({ ...e, paste: entry });
95+ // else act({ ...e, paste: undefined, key: entry });
96+ // }
97+ // }
98+ // } else
99+ if ( e . newId ) {
132100 const newE = editor ( e . newId , wrapEl . sink , eContext ) ;
133101 newE . render ( ) ;
134- insertAfter ( getCaretPos ( ) , { char : newE , id : e . newId } ) ;
135- setCaretPos ( newE . id ) ;
136- setCaretId ( newE . id ) ;
102+ insertAfter ( e . adr . caret [ 1 ] , { char : newE , id : e . newId } ) ;
103+ e . setAdr ( { ...e . adr , caret : [ newE . id , newE . id ] } ) ;
137104 }
138105 else if ( e . key . length === 1 ) {
139- //const iid = getCaretPos() === 0 ? id : str[getCaretPos() - 1].id;
140- insertAfter ( getCaretPos ( ) , { char : e . key , id : e . keyId } ) ;
141- setCaretId ( id ) ;
142- setCaretPos ( e . keyId ) ;
106+ insertAfter ( e . adr . caret [ 1 ] , { char : e . key , id : e . keyId } ) ;
107+ e . setAdr ( { ...e . adr , caret : [ id , e . keyId ] } ) ;
143108 }
144109 if ( e . key === "Enter" ) {
145- insertAfter ( getCaretPos ( ) , { char : "\n" , id : e . keyId } ) ;
146- setCaretId ( id ) ;
147- setCaretPos ( e . keyId ) ;
110+ insertAfter ( e . adr . caret [ 1 ] , { char : "\n" , id : e . keyId } ) ;
111+ e . setAdr ( { ...e . adr , caret : [ id , e . keyId ] } ) ;
148112 }
149113 if ( e . key === "Backspace" ) {
150- if ( getCaretPos ( ) !== id ) {
151- const i = deleteAtId ( getCaretPos ( ) ) ;
152- setCaretId ( id ) ;
153- setCaretPos ( str [ i - 1 ] ?. id ?? id ) ;
114+ const isFirstSink = e . getAdr ( ) . caret [ 1 ] === id ;
115+ if ( ! isFirstSink ) {
116+ const i = deleteAtId ( e . getAdr ( ) . caret [ 1 ] ) ;
117+ // gross, but it works
118+ for ( const cursor of getCursors ( ) ) {
119+ if ( cursor . getAdr ( ) . caret [ 1 ] === e . adr . caret [ 1 ] ) {
120+ cursor . setAdr ( {
121+ ...cursor . getAdr ( ) ,
122+ caret : [ id , str [ i - 1 ] ?. id ?? id ] ,
123+ } ) ;
124+ }
125+ if ( cursor . getAdr ( ) . anchor [ 1 ] === e . adr . caret [ 1 ] ) {
126+ cursor . setAdr ( {
127+ ...cursor . getAdr ( ) ,
128+ anchor : [ id , str [ i - 1 ] ?. id ?? id ] ,
129+ } ) ;
130+ }
131+ if ( cursor . getAdr ( ) . carry [ 1 ] === e . adr . caret [ 1 ] ) {
132+ cursor . setAdr ( {
133+ ...cursor . getAdr ( ) ,
134+ carry : [ id , str [ i - 1 ] ?. id ?? id ] ,
135+ } ) ;
136+ }
137+ }
138+ //e.setAdr({ ...e.adr, caret: [id, str[i - 1]?.id ?? id] });
154139 }
155140 else {
156141 // TODO?: delete at start of editor
157142 }
158143 }
159- wrapEl . str = str . map ( ( s ) => s . char ) ;
160144 }
161145 function calcLines ( ) {
162146 let curLine = [ ] ;
0 commit comments