Browse code

Improve implementation so it can edit lines longer than a chunk

Dario Rodriguez authored on 24/09/2020 21:40:49
Showing 3 changed files
... ...
@@ -24,7 +24,8 @@
24 24
 #include "sha3/sha3.h"
25 25
 
26 26
 //#define CHUNKSIZE 32768
27
-#define CHUNKSIZE 160
27
+//#define CHUNKSIZE 160
28
+#define CHUNKSIZE 16
28 29
 #define UNDOBLOCK 1024
29 30
 #define ADDNBLOCK 1024
30 31
 #define UNDOGROWSIZE (256*1024)
... ...
@@ -35,6 +36,7 @@ static int redata_hash_gen(redata_t *redata, char *filename, char *buf, long buf
35 36
 static char *securesave_genname(char *filename, char *buf, int bufsize);
36 37
 static void *mymemrchr(const void *s, int c, size_t n);
37 38
 static void meminvert(void *start, void *end);
39
+static size_t memrchroffset(char *ptr, int c, size_t n);
38 40
 
39 41
 #if 0
40 42
 static void
... ...
@@ -45,15 +47,22 @@ redata_debug_chunkdump(redata_t *redata, char *title)
45 47
         title=(title==NULL)?"":title;
46 48
         fprintf(stderr,"%s:CHUNKDUMP (sizechunks:%i)\n",title,redata->sizechunks);
47 49
         for(m=0;m<redata->sizechunks;m++) {
48
-                fprintf(stderr,"%s:chunk[%i]:\"",title,m);
50
+                fprintf(stderr,"%s:chunk[%i] len:%-5i data:\"",title,m,redata->chunks[m]->useddata);
49 51
                 for(k=0;k<redata->chunks[m]->useddata;k++) {
50 52
                         c=redata->chunks[m]->data[k];
51
-                        c=(c>=' ' && c<='~')?c:'.';
52
-                        fprintf(stderr,"%c",c);
53
+                        if(c=='\n' || c=='\0')
54
+                                fprintf(stderr,"\\%c",(c=='\n')?'n':'0');
55
+                        else if(c<' ' || c>'~' || c=='\\')
56
+                                fprintf(stderr,"\\x%02X",((unsigned char *)redata->chunks[m]->data)[k]);
57
+                        else
58
+                                fprintf(stderr,"%c",c);
53 59
                 }
54 60
                 fprintf(stderr,"\"\n");
55 61
         }
56 62
 }
63
+#define CHUNKDEBUG(a) redata_debug_chunkdump a
64
+#else
65
+#define CHUNKDEBUG(a)
57 66
 #endif
58 67
 
59 68
 redata_t *
... ...
@@ -247,6 +256,7 @@ redata_chunk_deletechunk(redata_t *redata, int chunkno)
247 256
                 redata->available+=chunk->useddata;
248 257
                 chunk->useddata=0;
249 258
                 chunk->whatin_fresh=0;
259
+                redata_chunk_unfreshnext(redata, chunkno);
250 260
         }
251 261
         for(i=chunkno;(i+1)<redata->sizechunks;i++)
252 262
                 redata->chunks[i]=redata->chunks[i+1];
... ...
@@ -320,8 +330,9 @@ redata_chunk_movedata(redata_t *redata, int chunkfrom, long posfrom, int chunkto
320 330
                 memmove(from->data+posfrom,from->data+posfrom+size,from->useddata-posfrom-size);
321 331
                 from->useddata-=size;
322 332
                 to->useddata+=size;
323
-                from->whatin_fresh=0;
324
-                to->whatin_fresh=0;
333
+                redata_chunk_unfreshrange(redata,chunkfrom,chunkto);
334
+                redata_chunk_unfreshnext(redata,chunkfrom);
335
+                redata_chunk_unfreshnext(redata,chunkto);
325 336
         } else {
326 337
                 /* from==to */
327 338
                 rechunk_t *chunk=redata->chunks[chunkfrom];
... ...
@@ -334,6 +345,7 @@ redata_chunk_movedata(redata_t *redata, int chunkfrom, long posfrom, int chunkto
334 345
                         memcpy(chunk->data+posto-size,redata->tmpchunk->data,size);
335 346
                 }
336 347
                 chunk->whatin_fresh=0;
348
+                redata_chunk_unfreshnext(redata,chunkfrom);
337 349
         }
338 350
         return(0);
339 351
 }
... ...
@@ -351,8 +363,9 @@ redata_chunk_insertdata(redata_t *redata, int chunkto, long posto, char *buf, lo
351 363
         memmove(chunk->data+posto+buflen,chunk->data+posto,chunk->useddata-posto);
352 364
         memcpy(chunk->data+posto,buf,buflen);
353 365
         chunk->useddata+=buflen;
354
-        chunk->whatin_fresh=0;
355 366
         redata->available-=buflen;
367
+        chunk->whatin_fresh=0;
368
+        redata_chunk_unfreshnext(redata,chunkto);
356 369
         return(0);
357 370
 }
358 371
 
... ...
@@ -370,8 +383,9 @@ redata_chunk_deletedata(redata_t *redata, int chunkno, long pos, long n)
370 383
         chunk=redata->chunks[chunkno];
371 384
         memmove(chunk->data+pos,chunk->data+pos+n,chunk->useddata-pos-n);
372 385
         chunk->useddata-=n;
373
-        chunk->whatin_fresh=0;
374 386
         redata->available+=n;
387
+        chunk->whatin_fresh=0;
388
+        redata_chunk_unfreshnext(redata,chunkno);
375 389
         return(0);
376 390
 }
377 391
 
... ...
@@ -409,6 +423,38 @@ redata_chunk_fillfromnext(redata_t *redata, int chunkno, int n)
409 423
         return(redata_chunk_movedata(redata,chunkno+1,0,chunkno,redata->chunks[chunkno]->useddata,n));
410 424
 }
411 425
 
426
+int
427
+redata_chunk_unfreshnext(redata_t *redata, int chunkno)
428
+{
429
+        int next;
430
+        if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks)
431
+                return(-1); /* sanity check failed */
432
+        /* invalidate whatin for next chunks until one not empty */
433
+        for(next=chunkno+1;next<redata->sizechunks;next++) {
434
+                redata->chunks[next]->whatin_fresh=0;
435
+                if(redata->chunks[next]->useddata!=0)
436
+                        break;
437
+        }
438
+        return(0);
439
+}
440
+
441
+int
442
+redata_chunk_unfreshrange(redata_t *redata, int fromchunkno, int tochunkno)
443
+{
444
+        int chunkno;
445
+        if(redata==NULL || fromchunkno<0 || fromchunkno>=redata->sizechunks || tochunkno<0 || tochunkno>=redata->sizechunks)
446
+                return(-1); /* sanity check failed */
447
+        if(fromchunkno>tochunkno) {
448
+                chunkno=fromchunkno;
449
+                fromchunkno=tochunkno;
450
+                tochunkno=chunkno;
451
+        }
452
+        /* invalidate whatin in range */
453
+        for(chunkno=fromchunkno;chunkno<=tochunkno;chunkno++)
454
+                redata->chunks[chunkno]->whatin_fresh=0;
455
+        return(0);
456
+}
457
+
412 458
 
413 459
 int
414 460
 redata_whatin_refresh(redata_t *redata, int chunkno)
... ...
@@ -423,10 +469,16 @@ redata_whatin_refresh(redata_t *redata, int chunkno)
423 469
                 return(0);
424 470
         memset(&(chunk->whatin),0,sizeof(whatin_t));
425 471
         nlcount=0;
426
-        for(i=0,lim=chunk->useddata;i<lim;i++) {
472
+        for(i=0,lim=chunk->useddata;i<lim;i++)
427 473
                 nlcount+=(chunk->data[i]=='\n')?1:0;
428
-        }
429 474
         chunk->whatin.nlcount=nlcount;
475
+        if(chunkno>0) {
476
+                int prev;
477
+                for(prev=chunkno-1;prev>0 && redata->chunks[prev]->useddata==0;prev--)
478
+                        ;
479
+                if(redata->chunks[prev]->useddata>0 && redata->chunks[prev]->data[redata->chunks[prev]->useddata-1]!='\n')
480
+                        chunk->whatin.iscontinuation=1;
481
+        }
430 482
         chunk->whatin_fresh=1;
431 483
         return(0);
432 484
 }
... ...
@@ -435,14 +487,14 @@ redata_whatin_refresh(redata_t *redata, int chunkno)
435 487
 int
436 488
 redata_fix_nl(redata_t *redata, int chunkno)
437 489
 {
490
+        rechunk_t *chunk,*nextchunk;
491
+        int linesize, nextlinesize, avail, nextavail;
492
+        unsigned char *ptr,*nextptr;
438 493
         /* make sure a line of len<chunksize is entirely inside one chunk (to simplify the syntax highlighting code) */
439 494
         if(redata==NULL || chunkno<0 || chunkno>=redata->sizechunks || redata->chunks[chunkno]==NULL)
440 495
                 return(-1); /* sanity check failed */
441 496
         if(chunkno==(redata->sizechunks-1) || redata->chunks[chunkno]->useddata==0 || redata->chunks[chunkno]->data[redata->chunks[chunkno]->useddata-1]=='\n')
442 497
                 return(0); /* Nothing to do (last chunk, chunk empty or already fixed) */
443
-        rechunk_t *chunk,*nextchunk;
444
-        int linesize, nextlinesize, avail, nextavail;
445
-        unsigned char *ptr,*nextptr;
446 498
         chunk=redata->chunks[chunkno];
447 499
         nextchunk=redata->chunks[chunkno+1];
448 500
         ptr=mymemrchr(chunk->data,'\n',chunk->useddata);
... ...
@@ -763,6 +815,20 @@ redata_undo_reactivatelast(redata_t *redata, undostack_t *stack)
763 815
         return(0);
764 816
 }
765 817
 
818
+int
819
+redata_undo_groupinit(redata_t *redata, undostack_t *stack)
820
+{
821
+        /* stores the current position in (undostack).groupinit */
822
+#warning TODO
823
+        return(-1);
824
+}
825
+
826
+int redata_undo_groupcommit(redata_t *redata, undostack_t *stack)
827
+{
828
+        /* marks as hint_groupedwithnext from stored groupinit position in undostack to last-1 position (if groupinit pos is the last pos, do nothing)*/
829
+#warning TODO
830
+        return(-1);
831
+}
766 832
 
767 833
 int
768 834
 redata_op_add(redata_t *redata, long insertpos, char *buf, long buflen, undostack_t *fromhere)
... ...
@@ -1333,6 +1399,14 @@ redata_generic_utf8charlen(char *ptr, int maxsize)
1333 1399
         return(i);
1334 1400
 }
1335 1401
 
1402
+inline int
1403
+redata_generic_utf8isstartbyte(int candidate)
1404
+{
1405
+        if((candidate&0xc0)!=0x80)
1406
+                return(1);
1407
+        return(0);
1408
+}
1409
+
1336 1410
 
1337 1411
 static int
1338 1412
 redata_hash_gen(redata_t *redata, char *filename, char *buf, long buflen, char *resbuf129bytes)
... ...
@@ -1389,94 +1463,225 @@ redata_hash_gen(redata_t *redata, char *filename, char *buf, long buflen, char *
1389 1463
         return(0);
1390 1464
 }
1391 1465
 
1392
-/*#define DEBUG_LINE_INFO*/
1393
-
1394
-#ifdef DEBUG_LINE_INFO
1395
-#define LINEINFODEBUG(a) fprintf a
1396
-#else
1397
-#define LINEINFODEBUG(a)
1398
-#endif
1399
-
1400 1466
 int
1401
-redata_line_info(redata_t *redata, long pos, long *startpos, char **startptr, int *len)
1467
+redata_line_rawinfo(redata_t *redata, long pos, long *startpos, char **startptr, int *len, int *is_continuation)
1402 1468
 {
1403
-        int chunkno;
1404
-        long start;
1405
-        unsigned char *ptr,*end;
1469
+        long chunkpos,newpos,endpos;
1470
+        int nchunk;
1406 1471
         rechunk_t *chunk;
1407
-        if(redata==NULL || pos<0)
1472
+        if(redata==NULL || pos<0 || pos>=redata_getused(redata))
1408 1473
                 return(-1);
1409
-        /* search chunk of pos */
1410
-        for(chunkno=0,start=0;chunkno<redata->sizechunks;start+=(redata->chunks[chunkno]==NULL)?0:redata->chunks[chunkno]->useddata,chunkno++) {
1411
-        LINEINFODEBUG((stderr,"redata_line_info: searchchunk: pos:%li chunkno:%i start:%li\n",pos,chunkno,start));
1412
-                if(redata->chunks[chunkno]==NULL)
1474
+        for(nchunk=0,chunkpos=0
1475
+          ;nchunk<redata->sizechunks
1476
+          ;chunkpos+=(chunk!=NULL)?chunk->useddata:0,nchunk++) {
1477
+                if((chunk=redata->chunks[nchunk])==NULL)
1413 1478
                         continue;
1414
-                if((start+redata->chunks[chunkno]->useddata)>pos)
1479
+                if(pos>=chunkpos && pos<(chunkpos+chunk->useddata))
1415 1480
                         break;
1416 1481
         }
1417
-        if(chunkno>=redata->sizechunks)
1418
-                return(-1);
1419
-        chunk=redata->chunks[chunkno];
1420
-        /* search line start */
1421
-        LINEINFODEBUG((stderr,"redata_line_info: searchstart: pos:%li start:%li offset:%li\n",pos,start,(pos-start)));
1422
-        for(ptr=chunk->data+(pos-start);ptr>chunk->data && ptr[-1]!='\n';ptr--)
1482
+        if(nchunk>=redata->sizechunks)
1483
+                return(-1); /* pos not found */
1484
+        for(newpos=pos;newpos>chunkpos && chunk->data[newpos-chunkpos-1]!='\n';newpos--)
1423 1485
                 ;
1424
-        /* search line end */
1425
-        LINEINFODEBUG((stderr,"redata_line_info: searchend: pos:%li start:%li offset:%li\n",pos,start,(pos-start)));
1426
-        for(end=chunk->data+(pos-start)+1;end<=(chunk->data+chunk->useddata) && end[-1]!='\n';end++)
1486
+        for(endpos=pos;endpos<(chunkpos+chunk->useddata) && chunk->data[endpos-chunkpos]!='\n';endpos++)
1427 1487
                 ;
1428
-        /* fill results */
1429
-        LINEINFODEBUG((stderr,"redata_line_info: filling results: len:%li\n",end-ptr));
1488
+        if(endpos==(chunkpos+chunk->useddata))
1489
+                endpos--;
1430 1490
         if(startpos!=NULL)
1431
-                *startpos=(pos-((chunk->data+(pos-start))-ptr));
1491
+                *startpos=newpos;
1432 1492
         if(startptr!=NULL)
1433
-                *startptr=(char *) ptr;
1493
+                *startptr=(char *) (chunk->data+(newpos-chunkpos));
1434 1494
         if(len!=NULL)
1435
-                *len=end-ptr;
1495
+                *len=endpos-newpos+1;
1496
+        if(is_continuation!=NULL) {
1497
+                if(!(chunk->whatin_fresh))
1498
+                        redata_whatin_refresh(redata,nchunk);
1499
+                *is_continuation=(newpos==chunkpos && chunk->whatin.iscontinuation)?1:0;
1500
+        }
1436 1501
         return(0);
1437 1502
 }
1438 1503
 
1439 1504
 int
1440
-redata_pos2linecol(redata_t *redata, long pos, int *resline, int *rescol)
1505
+redata_line_realstart(redata_t *redata, long pos, long *startpos)
1441 1506
 {
1442
-        int line,col;
1443
-        int nchunk;
1444
-        int chunkpos;
1445
-        rechunk_t *chunk;
1446
-        int i,o;
1447
-        if(redata==NULL)
1507
+        int is_continuation;
1508
+        long nextpos,newpos;
1509
+        if(redata==NULL || pos<0)
1510
+                return(-1); /* sanity check failed */
1511
+        nextpos=pos;
1512
+        do {
1513
+                if(redata_line_rawinfo(redata,nextpos,&newpos,NULL,NULL,&is_continuation)==-1)
1514
+                        return(-1);
1515
+                nextpos=newpos-1;
1516
+        } while(is_continuation && newpos>0);
1517
+        if(startpos!=NULL)
1518
+                *startpos=newpos;
1519
+        return(0);
1520
+}
1521
+
1522
+int
1523
+redata_line_realend(redata_t *redata, long pos, long *endpos)
1524
+{
1525
+        long nextpos,newpos;
1526
+        long datasize;
1527
+        char *ptr;
1528
+        int len;
1529
+        int has_nl;
1530
+        if(redata==NULL || pos<0)
1531
+                return(-1); /* sanity check failed */
1532
+        nextpos=pos;
1533
+        if((datasize=redata_getused(redata))<=0)
1534
+                return(-1); /* couldn't get last pos or there is no data */
1535
+        do {
1536
+                if(redata_line_rawinfo(redata,nextpos,&newpos,&ptr,&len,NULL)==-1 || len==0)
1537
+                        return(-1);
1538
+                nextpos=newpos+len;
1539
+                has_nl=(len>0 && ptr[len-1]=='\n')?1:0;
1540
+        } while(!has_nl && nextpos<datasize);
1541
+        if(endpos!=NULL)
1542
+                *endpos=newpos+len-1;
1543
+        return(0);
1544
+}
1545
+
1546
+int
1547
+redata_line_prevrealstart(redata_t *redata, long pos, long *startpos)
1548
+{
1549
+        long newpos,prevpos;
1550
+        if(redata==NULL || pos<0)
1448 1551
                 return(-1);
1449
-        /* search chunk of pos while calculating first lineno of chunk */
1450
-        for(nchunk=0,line=0,chunkpos=0
1451
-          ;nchunk<redata->sizechunks
1452
-          ;line+=chunk->whatin.nlcount,chunkpos+=chunk->useddata,nchunk++) {
1453
-                chunk=redata->chunks[nchunk];
1454
-                if(!(chunk->whatin_fresh))
1455
-                        redata_whatin_refresh(redata,nchunk);
1456
-                if(pos<(chunkpos+chunk->useddata))
1552
+        if(redata_line_realstart(redata,pos,&newpos)==-1)
1553
+                return(-1); /* couldn't get start of line */
1554
+        if(redata_line_realstart(redata,newpos-1,&prevpos)==-1)
1555
+                return(-1); /* couldn't get start of line */
1556
+        if(startpos!=NULL)
1557
+                *startpos=prevpos;
1558
+        return(0);
1559
+}
1560
+
1561
+int
1562
+redata_line_nextrealstart(redata_t *redata, long pos, long *startpos)
1563
+{
1564
+        long newpos,nextpos;
1565
+        if(redata==NULL || pos<0)
1566
+                return(-1);
1567
+        if(redata_line_realend(redata,pos,&newpos)==-1)
1568
+                return(-1); /* couldn't get end of line */
1569
+        if(redata_line_realstart(redata,newpos+1,&nextpos)==-1)
1570
+                return(-1); /* couldn't get start of line */
1571
+        if(startpos!=NULL)
1572
+                *startpos=nextpos;
1573
+        return(0);
1574
+}
1575
+
1576
+int
1577
+redata_line_inccol(redata_t *redata, long pos, int ncolrequest, long *newpos, int *ncoldone)
1578
+{
1579
+        long curpos,startpos;
1580
+        char *ptr;
1581
+        int len;
1582
+        int n,i;
1583
+        int done;
1584
+        if(redata==NULL || pos<0 || ncolrequest<0)
1585
+                return(-1); /* sanity check failed */
1586
+        if(ncolrequest==0) {
1587
+                if(newpos!=NULL)
1588
+                        *newpos=pos;
1589
+                if(ncoldone!=NULL)
1590
+                        *ncoldone=0;
1591
+                return(0); /* nothing to do */
1592
+        }
1593
+        for(n=0,curpos=pos;n<=ncolrequest;) {
1594
+                if(redata_line_rawinfo(redata,curpos,&startpos,&ptr,&len,NULL)==-1 || len==0)
1595
+                        return(-1); /* couldn't get current line data */
1596
+                done=0;
1597
+                /* advance until we are positioned on the next char to the requested one */
1598
+                for(i=startpos-curpos;i<len;i++) {
1599
+                        if(redata_generic_utf8isstartbyte(ptr[i]))
1600
+                                n++;
1601
+                        if(ptr[i]=='\n' || n>ncolrequest) {
1602
+                                done=1;
1603
+                                break;
1604
+                        }
1605
+                }
1606
+                curpos=startpos+i;
1607
+                if(done)
1457 1608
                         break;
1458 1609
         }
1459
-        if(nchunk>=redata->sizechunks)
1460
-                return(-1); /* pos not found */
1461
-        /* search chunk line/col */
1462
-        for(i=0,o=chunkpos,col=0;o<pos && i<chunk->useddata;i++,o++) {
1463
-                if(chunk->data[i]=='\n')
1464
-                        line++,col=0;
1465
-                else
1466
-                        col++;
1610
+        if(newpos!=NULL)
1611
+                *newpos=curpos;
1612
+        if(ncoldone!=NULL)
1613
+                *ncoldone=n-1;
1614
+        return(0);
1615
+}
1616
+
1617
+
1618
+int
1619
+redata_pos2linecol(redata_t *redata, long pos, int *resline, int *rescol)
1620
+{
1621
+        if(redata==NULL || pos<0 || pos>=redata_getused(redata))
1622
+                return(-1);
1623
+        /* calculate line */
1624
+        if(resline!=NULL) {
1625
+                long chunkpos,newpos;
1626
+                int startline;
1627
+                int nchunk;
1628
+                rechunk_t *chunk;
1629
+                int i;
1630
+                for(nchunk=0,chunkpos=0,startline=0
1631
+                  ;nchunk<redata->sizechunks
1632
+                  ;chunkpos+=(chunk!=NULL)?chunk->useddata:0
1633
+                    ,startline+=(chunk!=NULL)?chunk->whatin.nlcount:0,nchunk++) {
1634
+                        if((chunk=redata->chunks[nchunk])==NULL)
1635
+                                continue;
1636
+                        if(!(chunk->whatin_fresh))
1637
+                                redata_whatin_refresh(redata,nchunk);
1638
+                        if(pos>=chunkpos && pos<(chunkpos+chunk->useddata))
1639
+                                break;
1640
+                }
1641
+                if(nchunk>=redata->sizechunks)
1642
+                        return(-1); /* pos not found */
1643
+                for(newpos=chunkpos,i=0;newpos<pos && i<chunk->useddata;newpos++,i++) {
1644
+                        if(chunk->data[i]=='\n')
1645
+                                startline++;
1646
+                }
1647
+                *resline=startline;
1648
+        }
1649
+        /* calculate col */
1650
+        if(rescol!=NULL) {
1651
+                long realstart,curpos,startpos;
1652
+                char *ptr;
1653
+                int len;
1654
+                int n,i;
1655
+                int done;
1656
+                if(redata_line_realstart(redata,pos,&realstart)==-1)
1657
+                        return(-1); /* startpos for pos not found */
1658
+                for(n=0,curpos=realstart;curpos<pos;) {
1659
+                        if(redata_line_rawinfo(redata,curpos,&startpos,&ptr,&len,NULL)==-1 || len==0)
1660
+                                return(-1); /* couldn't get current line data */
1661
+                        done=0;
1662
+                        for(i=0;i<len && (startpos+i)<pos;i++) {
1663
+                                if(redata_generic_utf8isstartbyte(ptr[i]))
1664
+                                        n++;
1665
+                                if(ptr[i]=='\n') {
1666
+                                        done=1;
1667
+                                        break;
1668
+                                }
1669
+                        }
1670
+                        curpos=startpos+i;
1671
+                        if(done)
1672
+                                break;
1673
+                }
1674
+                *rescol=n;
1467 1675
         }
1468
-        if(resline!=NULL)
1469
-                *resline=line;
1470
-        if(rescol!=NULL)
1471
-                *rescol=col;
1472 1676
         return(0);
1473
-#warning DEBUG THIS
1474 1677
 }
1475 1678
 
1476 1679
 int
1477
-redata_linecol2pos(redata_t *redata, int line, int col, long *pos)
1680
+redata_linecol2pos(redata_t *redata, int line, int colrequest, long *pos, int *coldone)
1478 1681
 {
1479
-#warning TODO
1682
+#warning TODO: XXX Implement this function
1683
+#warning TODO: XXX: Add a parameter so this is like reata_line_inccol (colrequest, coldone) --  maybe also linetrequest linedone? (zed doesn''t let you go to the last line doing a Ctrl+q+l to an arbitrarily large number...)
1684
+#warning TODO: XXX: The extra parameters are super-handy because the main user of this function is Control+Q+L "Go To Line" and we want to go to nearest pos
1480 1685
         return(-1);
1481 1686
 }
1482 1687
 
... ...
@@ -1514,3 +1719,14 @@ meminvert(void *start, void *end)
1514 1719
         }
1515 1720
 }
1516 1721
 
1722
+static size_t
1723
+memrchroffset(char *ptr, int c, size_t n)
1724
+{
1725
+        size_t i;
1726
+        for(i=n-1;i>=0;i--) {
1727
+                if(((unsigned char *)ptr)[i]==c)
1728
+                        return(i);
1729
+        }
1730
+        return(-1);
1731
+}
1732
+
... ...
@@ -17,6 +17,7 @@
17 17
 
18 18
 typedef struct whatin_t {
19 19
         int nlcount;
20
+        int iscontinuation;
20 21
 } whatin_t;
21 22
 
22 23
 typedef struct rechunk_t {
... ...
@@ -34,12 +35,14 @@ typedef struct undo_t {
34 35
         long posdest;
35 36
         char prehash[129];
36 37
         char posthash[129];
38
+        int hint_groupedwithnext;
37 39
 } undo_t;
38 40
 
39 41
 typedef struct undostack_t {
40 42
         int sizeundo;
41 43
         int usedundo;
42 44
         undo_t *undo;
45
+        int groupinit;
43 46
         long sizebuf;
44 47
         long usedbuf;
45 48
         char *buf;
... ...
@@ -98,6 +101,9 @@ int redata_chunk_insertdata(redata_t *redata, int chunkto, long posto, char *buf
98 101
 int redata_chunk_deletedata(redata_t *redata, int chunkno, long pos, long n);
99 102
 int redata_chunk_splithere(redata_t *redata, int chunkno, int pos);
100 103
 int redata_chunk_fillfromnext(redata_t *redata, int chunkno, int n);
104
+int redata_chunk_unfreshnext(redata_t *redata, int chunkno);
105
+int redata_chunk_unfreshrange(redata_t *redata, int fromchunkno, int tochunkno);
106
+
101 107
 int redata_whatin_refresh(redata_t *redata, int chunkno);
102 108
 int redata_fix_nl(redata_t *redata, int chunkno);
103 109
 int redata_undobuf_reserve(redata_t *redata, undostack_t *stack, int minavail);
... ...
@@ -109,6 +115,8 @@ int redata_undo_removelast(redata_t *redata, undostack_t *stack);
109 115
 int redata_undo_wipe(redata_t *redata, undostack_t *stack);
110 116
 int redata_undo_inactivatelast(redata_t *redata, undostack_t *stack);
111 117
 int redata_undo_reactivatelast(redata_t *redata, undostack_t *stack);
118
+int redata_undo_groupinit(redata_t *redata, undostack_t *stack);
119
+int redata_undo_groupcommit(redata_t *redata, undostack_t *stack);
112 120
 
113 121
 /* high level stuff */
114 122
 int redata_load(redata_t *redata, char *filename, int (*callback_question)(/*char *title, char *body, int nopts, char *opts[],void *userptr*/), void *userptr);
... ...
@@ -140,10 +148,25 @@ int redata_generic_utf8len(char *ptr, int size);
140 148
 char *redata_generic_utf8col(char *ptr, int size, int col);
141 149
 /* returns the len in bytes of the character starting at ptr (zero on error)*/
142 150
 int redata_generic_utf8charlen(char *ptr, int maxsize);
151
+/* returns true if it's the forst byte of an UTF char */
152
+int redata_generic_utf8isstartbyte(int candidate);
143 153
 
144 154
 
145 155
 /* line convenience funtions */
146
-int redata_line_info(redata_t *redata, long pos, long *startpos, char **startptr, int *len);
156
+/* get pointer to full line (if it doesn't fit in a chunk, is_continuation is 1 and/or doesn't have an ending nl */
157
+int redata_line_rawinfo(redata_t *redata, long pos, long *startpos, char **startptr, int *len, int *is_continuation);
158
+/* get the startpos of line, even if it doesn't fit in one chunk */
159
+int redata_line_realstart(redata_t *redata, long pos, long *startpos);
160
+/* get the endpos of line, even if it doesn't fit in one chunk */
161
+int redata_line_realend(redata_t *redata, long pos, long *endpos);
162
+/* get pos of previous line real start */
163
+int redata_line_prevrealstart(redata_t *redata, long pos, long *startpos);
164
+/* get pos of next line real start */
165
+int redata_line_nextrealstart(redata_t *redata, long pos, long *startpos);
166
+/* advance "n" cols */
167
+int redata_line_inccol(redata_t *redata, long pos, int ncolrequest, long *newpos, int *ncoldone);
168
+/* get the line/col of a pos (col is calculated using the utf8 convenience functions) */
147 169
 int redata_pos2linecol(redata_t *redata, long pos, int *line, int *col);
148
-int redata_linecol2pos(redata_t *redata, int line, int col, long *pos);
170
+/* get the pos of that line/col (calculated using the utf8 convenience functions) */
171
+int redata_linecol2pos(redata_t *redata, int line, int colrequest, long *pos, int *coldone);
149 172
 
... ...
@@ -21,6 +21,21 @@
21 21
 #define LINEFORCESCROLL 1
22 22
 #define COLFORCESCROLL 5
23 23
 
24
+#define COMMANDBUFSIZE 1024
25
+
26
+#define COMMAND_WARNING "(!)"
27
+#define COMMAND_GOTOLINE "Go to line: "
28
+
29
+#define COLOR_STATUSBG "\x00\x00\xff\xff"
30
+#define COLOR_STATUSFG "\xff\xff\x00\xff"
31
+
32
+#define COLOR_QUERYBG "\xc0\xff\x00\xff"
33
+#define COLOR_QUERYFG "\x3f\x00\xff\xff"
34
+
35
+#define COLOR_WARNINGBG "\xff\x00\x00\xff"
36
+#define COLOR_WARNINGFG "\xff\xff\x00\xff"
37
+
38
+
24 39
 typedef struct re_t {
25 40
         redata_t *data;
26 41
         reui_t *ui;
... ...
@@ -31,7 +46,11 @@ typedef struct re_t {
31 46
         int originline,origincol;
32 47
         int curline,curcol;
33 48
         int maxrow,maxcol;
49
+        int headerdirty;
34 50
         int contentsdirty;
51
+        char *command;
52
+        char commandbuf[COMMANDBUFSIZE];
53
+        int ignorenkeys;
35 54
 } re_t;
36 55
 
37 56
 volatile int flag_sigint;
... ...
@@ -47,10 +66,15 @@ re_t *re_init(void);
47 66
 void re_free(re_t *re);
48 67
 
49 68
 int re_setfilename(re_t *re, char *filename);
50
-int re_processkey(re_t *re, SDL_Event *event);
69
+int re_processkey_editing(re_t *re, SDL_Event *event);
70
+int re_processkey_commandwait(re_t *re, SDL_Event *event);
71
+int re_processkey_commanddata(re_t *re, SDL_Event *event);
72
+int re_processcommand(re_t *re);
51 73
 int re_moveupdown(re_t *re, int totalinc);
52 74
 int re_moveleftright(re_t *re, int totalinc);
53
-int re_drawheader(re_t *re);
75
+int re_rtrim(re_t *re, long curpos, int *trimmed);
76
+int re_drawheader_editing(re_t *re);
77
+int re_drawheader_command(re_t *re);
54 78
 int re_drawcontents(re_t *re);
55 79
 
56 80
 
... ...
@@ -106,10 +130,25 @@ main(int argc, char *argv[])
106 130
         re->x=0,re->y=re->ui->fontheight,re->w=re->ui->w,re->h=re->ui->h-re->y;
107 131
         re->maxrow=re->h/re->ui->fontheight-1;
108 132
         re->maxcol=re->w/re->ui->fontwidth-1;
109
-        re_drawheader(re);
110
-        re_drawcontents(re);
133
+        re->headerdirty=1;
134
+        re->contentsdirty=1;
111 135
         flag_had_events=0;
136
+        SDL_StartTextInput();
112 137
         while(do_exit==0 && flag_sigint==0) {
138
+                if(re->headerdirty) {
139
+                        if(re->command==NULL) {
140
+fprintf(stderr,"REDRAW Header (editing)\n");
141
+                                re_drawheader_editing(re);
142
+                        } else {
143
+fprintf(stderr,"REDRAW Header (command)\n");
144
+                                re_drawheader_command(re);
145
+                                if(strcmp(re->command,COMMAND_WARNING)==0) {
146
+                                        /* the warnings only get shown once, remove it */
147
+                                        re->command=NULL;
148
+                                        re->commandbuf[0]='\0';
149
+                                }
150
+                        }
151
+                }
113 152
                 if(re->contentsdirty)
114 153
                         re_drawcontents(re);
115 154
                 if(re->ui->rendererdirty)
... ...
@@ -124,7 +163,14 @@ main(int argc, char *argv[])
124 163
                                         do_exit=1;
125 164
                                         break;
126 165
                                 case SDL_KEYDOWN:
127
-                                        re_processkey(re,&event);
166
+                                case SDL_TEXTINPUT:
167
+                                case SDL_TEXTEDITING:
168
+                                        if(re->command==NULL || strcmp(re->command,COMMAND_WARNING)==0)
169
+                                                re_processkey_editing(re,&event);
170
+                                        else if(re->command[0]=='\0')
171
+                                                re_processkey_commandwait(re,&event);
172
+                                        else
173
+                                                re_processkey_commanddata(re,&event);
128 174
                                         break;
129 175
                                 case SDL_WINDOWEVENT:
130 176
                                         if(event.window.event==SDL_WINDOWEVENT_SHOWN
... ...
@@ -137,6 +183,7 @@ main(int argc, char *argv[])
137 183
                         }
138 184
                 }
139 185
         }
186
+        SDL_StopTextInput();
140 187
         sselect_free(ssel),ssel=NULL;
141 188
         re_free(re),re=NULL;
142 189
         return(0);
... ...
@@ -209,14 +256,79 @@ re_setfilename(re_t *re, char *filename)
209 256
 }
210 257
 
211 258
 int
212
-re_processkey(re_t *re, SDL_Event *event)
259
+re_processkey_editing(re_t *re, SDL_Event *event)
213 260
 {
214
-        long newpos;
215
-        char *ptr;
216
-        int len;
217
-        int has_nl;
218
-        if(re==NULL || event==NULL || event->type!=SDL_KEYDOWN)
261
+        SDL_Event returnevent;
262
+        if(re==NULL || event==NULL)
219 263
                 return(-1); /* sanity check failed */
264
+        /* convert RETURN KEYDOWN to TEXTINPUT */
265
+        if(event->type==SDL_KEYDOWN && event->key.keysym.sym==SDLK_RETURN) {
266
+                memset(&returnevent,0,sizeof(SDL_Event));
267
+                event=&returnevent;
268
+                event->type=SDL_TEXTINPUT;
269
+                strncpy(event->text.text,"\n",sizeof(event->text.text));
270
+                event->text.text[sizeof(event->text.text)-1]='\0';
271
+        }
272
+        /* special case: text editing event */
273
+        if(event->type==SDL_TEXTINPUT) {
274
+                int len;
275
+                long realend;
276
+                int at_end;
277
+                if(re->ignorenkeys>0) {
278
+                        re->ignorenkeys--;
279
+                        return(0); /* this is an already processed key, ignore it */
280
+                }
281
+#if 1
282
+fprintf(stderr,"SDL_TEXTINPUT:\"%s\"\n",event->text.text);
283
+#endif
284
+                if(redata_line_realend(re->data,re->cursorpos,&realend)==-1)
285
+                        return(-1); /* couldn't get current line info */
286
+                at_end=(re->cursorpos==realend)?1:0;
287
+                if(event->text.text[0]==' ' && event->text.text[1]=='\0' && at_end) {
288
+                        /* instead of adding a space at the end of the line, we move the cursor to the right */
289
+                        re_moveleftright(re,1);
290
+                        return(0); /* no need to add spaces at the end of the line */
291
+                }
292
+                redata_undo_groupinit(re->data,NULL);
293
+                if(event!=&returnevent && at_end) {
294
+                        int col;
295
+                        if(redata_pos2linecol(re->data,re->cursorpos,NULL,&col)==-1)
296
+                                return(-1); /* couldn't get line info */
297
+                        if(redata_op_addn(re->data,re->cursorpos,' ',re->curcol-col,NULL)!=0)
298
+                                return(-1); /* couldn't add spaces up to current displayed pos */
299
+                        /* increment cursorpos; spaces are 1 byte, so the number of columns advanced is the number of bytes advanced */
300
+                        re->cursorpos+=re->curcol-col;
301
+                }
302
+                len=strlen(event->text.text);
303
+                if(redata_op_add(re->data,re->cursorpos,event->text.text,len,NULL)!=0)
304
+                        return(-1); /* couldn't add requested text */
305
+                re->cursorpos+=len;
306
+                if(event==&returnevent) {
307
+                        int trimmed;
308
+                        if(re_rtrim(re,re->cursorpos-len,&trimmed))
309
+                                re->cursorpos-=trimmed;
310
+                        re->curline++;
311
+                        re->curcol=0;
312
+                } else {
313
+                        re->curcol+=redata_generic_utf8len(event->text.text,len);
314
+                }
315
+                redata_undo_groupcommit(re->data,NULL);
316
+                re->headerdirty=1;
317
+                re->contentsdirty=1;
318
+        }
319
+#if 0
320
+        else if(event->type==SDL_TEXTEDITING) {
321
+                /* NOTE: on window enter events we cam also receive this event (!) */
322
+#warning TODO: CJK support
323
+fprintf(stderr,"SDL_TEXTEDITING: composition:\"%s\" start:%i len:%i\n",event->edit.text,event->edit.start,event->edit.length);
324
+        }
325
+#endif
326
+        /* default case: keydown event */
327
+        if(event->type!=SDL_KEYDOWN)
328
+                return(0); /* Ignore other possible events */
329
+#if 1
330
+fprintf(stderr,"SDL_KEYDOWN: sym:%i\n",event->key.keysym.sym);
331
+#endif
220 332
         if(event->key.keysym.sym==SDLK_DOWN || event->key.keysym.sym==SDLK_UP) {
221 333
                 re_moveupdown(re,(event->key.keysym.sym==SDLK_UP)?-1:1);
222 334
         } else if(event->key.keysym.sym==SDLK_LEFT || event->key.keysym.sym==SDLK_RIGHT) {
... ...
@@ -224,11 +336,14 @@ re_processkey(re_t *re, SDL_Event *event)
224 336
         } else if(event->key.keysym.sym==SDLK_PAGEDOWN || event->key.keysym.sym==SDLK_PAGEUP) {
225 337
                 re_moveupdown(re,(event->key.keysym.sym==SDLK_PAGEUP)?-(re->maxrow):re->maxrow);
226 338
         } else if(event->key.keysym.sym==SDLK_HOME || event->key.keysym.sym==SDLK_END) {
227
-                if(redata_line_info(re->data,re->cursorpos,&newpos,&ptr,&len)==-1)
228
-                        return(-1); /* couldn't get current line data */
229
-                has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
230
-                re->cursorpos=(event->key.keysym.sym==SDLK_HOME)?newpos:newpos+len-has_nl;
231
-                redata_pos2linecol(re->data,re->cursorpos,&(re->curline),&(re->curcol));
339
+                long newpos;
340
+                if((event->key.keysym.sym==SDLK_HOME && redata_line_realstart(re->data,re->cursorpos,&newpos)==-1)
341
+                  || (event->key.keysym.sym==SDLK_END && redata_line_realend(re->data,re->cursorpos,&newpos)==-1)) {
342
+                        return(-1); /* couldn't get destination pos data */
343
+                }
344
+                re->cursorpos=newpos;
345
+                if(redata_pos2linecol(re->data,re->cursorpos,NULL,&(re->curcol))==-1)
346
+                        return(-1); /* couldn't get col of current pos */;
232 347
                 if(re->curcol<re->origincol) {
233 348
                         re->origincol=re->curcol;
234 349
                         re->origincol=(re->origincol<0)?0:re->origincol;
... ...
@@ -237,42 +352,184 @@ re_processkey(re_t *re, SDL_Event *event)
237 352
                         re->origincol=re->curcol+COLFORCESCROLL-re->maxcol;
238 353
                         re->origincol=(re->origincol<0)?0:re->origincol;
239 354
                 }
355
+                re->headerdirty=1;
356
+                re->contentsdirty=1;
357
+        } else if(event->key.keysym.sym==SDLK_BACKSPACE && re->cursorpos>0) {
358
+                int line,col;
359
+                long startpos,newpos,realstart,start;
360
+                char *ptr;
361
+                int len;
362
+                int trimmed;
363
+                int doneinc;
364
+                if(redata_pos2linecol(re->data,re->cursorpos,&line,&col)==-1)
365
+                        return(-1); /* couldn't get current line data */
366
+                if(col<re->curcol) {
367
+                        re_moveleftright(re,-1);
368
+                        return(0); /* were hovering to the right of last char, only had to move left */
369
+                }
370
+                redata_undo_groupinit(re->data,NULL);
371
+                /* delete the last character */
372
+                if(col>0) {
373
+                        if(redata_line_realstart(re->data,re->cursorpos,&realstart)==-1)
374
+                                return(-1); /* couldn't get line info */
375
+                        /* get the start of the part to delete */
376
+                        doneinc=0;
377
+                        for(ptr=NULL,len=0,start=0,newpos=re->cursorpos;doneinc!=1;) {
378
+                                if((newpos-1)<realstart)
379
+                                        break;
380
+                                newpos--;
381
+                                if(ptr==NULL || (start+len)<=newpos || newpos<start) {
382
+                                        if(redata_line_rawinfo(re->data,newpos,&start,&ptr,&len,NULL)==-1)
383
+                                                return(-1); /* couldn't get line data */
384
+                                }
385
+                                if(redata_generic_utf8isstartbyte(ptr[newpos-start]))
386
+                                        doneinc++;
387
+                        }
388
+                        /* delete */
389
+                        redata_op_del(re->data,newpos,re->cursorpos-newpos,NULL);
390
+                        re->cursorpos=newpos;
391
+                } else {
392
+                        long delpos;
393
+                        /* to make the code trivial, we're being lazy on '\n' deletion: we call ...rawinfo() for each byte in the multibyte utf-8 sequence */
394
+                        for(delpos=re->cursorpos-1
395
+                          ;delpos>0
396
+                          && redata_line_rawinfo(re->data,delpos,&startpos,&ptr,&len,NULL)==0;) {
397
+                                if(redata_generic_utf8isstartbyte(ptr[delpos-startpos]))
398
+                                        break;
399
+                        }
400
+                        redata_op_del(re->data,delpos,re->cursorpos-delpos,NULL);
401
+                        re->cursorpos=delpos;
402
+                }
403
+                redata_pos2linecol(re->data,re->cursorpos,&(re->curline),&(re->curcol));
404
+                /* if cursor at end of line and there are trailing spaces at the end, delete them too */
405
+                if(re_rtrim(re,re->cursorpos,&trimmed))
406
+                        re->cursorpos-=trimmed;
407
+                /* end of deletion */
408
+                redata_undo_groupcommit(re->data,NULL);
409
+                re->headerdirty=1;
240 410
                 re->contentsdirty=1;
241
-                re_drawheader(re);
242
-        } else if(event->key.keysym.sym==SDLK_a) {
243
-                ;
411
+        } else if(event->key.keysym.sym==SDLK_q && (SDL_GetModState()&KMOD_CTRL)!=0) {
412
+fprintf(stderr,"re_processkey(): received Control+q\n");
413
+                re->command="";
414
+                re->headerdirty=1;
244 415
         }
245
-        return(-1);
416
+        return(0);
417
+}
418
+
419
+int
420
+re_processkey_commandwait(re_t *re, SDL_Event *event)
421
+{
422
+        if(re==NULL || event==NULL)
423
+                return(-1); /* sanity check failed */
424
+        /* default case: keydown event */
425
+        if(event->type!=SDL_KEYDOWN)
426
+                return(0); /* Ignore other possible events */
427
+        if(event->key.keysym.sym==SDLK_l) {
428
+                re->command=COMMAND_GOTOLINE;
429
+                re->commandbuf[0]='\0';
430
+                re->headerdirty=1;
431
+        } else {
432
+                re->command=NULL;
433
+                re->headerdirty=1;
434
+        }
435
+        if(!(SDL_GetModState()&KMOD_CTRL))
436
+                re->ignorenkeys=1; /* control was not pressed, we will receive again this key as SDL_TEXTINPUT */
437
+        return(0);
438
+}
439
+
440
+int
441
+re_processkey_commanddata(re_t *re, SDL_Event *event)
442
+{
443
+        if(re==NULL || event==NULL)
444
+                return(-1); /* sanity check failed */
445
+        /* special case: text editing event */
446
+        if(event->type==SDL_TEXTINPUT) {
447
+                int len;
448
+                if(re->ignorenkeys>0) {
449
+                        re->ignorenkeys--;
450
+                        return(0); /* this is an already processed key, ignore it */
451
+                }
452
+                len=strlen(event->text.text);
453
+                if((strlen(re->commandbuf)+len+1)>=sizeof(re->commandbuf))
454
+                        return(-1); /* No space for text */
455
+                memcpy(re->commandbuf+strlen(re->commandbuf),event->text.text,len+1);
456
+                re->commandbuf[sizeof(re->commandbuf)-1]='\0';
457
+                re->headerdirty=1;
458
+        }
459
+        /* default case: keydown event */
460
+        if(event->type!=SDL_KEYDOWN)
461
+                return(0); /* Ignore other possible events */
462
+        if(event->key.keysym.sym==SDLK_ESCAPE) {
463
+                re->command=NULL;
464
+                re->commandbuf[0]='\0';
465
+                re->headerdirty=1;
466
+        } else if(event->key.keysym.sym==SDLK_BACKSPACE && re->commandbuf[0]!='\0') {
467
+                int nchar;
468
+                char *ptr;
469
+                if((nchar=redata_generic_utf8len(re->commandbuf,strlen(re->commandbuf)))<=0)
470
+                        return(-1); /* error parsing commandbuf */
471
+                if((ptr=redata_generic_utf8col(re->commandbuf,strlen(re->commandbuf),nchar-1))==NULL)
472
+                        return(-1); /* error positioning in commandbuf */
473
+                *ptr='\0';
474
+                re->headerdirty=1;
475
+        } else if(event->key.keysym.sym==SDLK_RETURN) {
476
+                re_processcommand(re);
477
+        }
478
+        return(0);
479
+}
480
+
481
+int
482
+re_processcommand(re_t *re)
483
+{
484
+        if(re==NULL || re->command==NULL)
485
+                return(-1);
486
+        re->commandbuf[sizeof(re->commandbuf)-1]='\0';
487
+        if(strcmp(re->command,COMMAND_GOTOLINE)==0) {
488
+                int line;
489
+                long pos;
490
+                line=atoi(re->commandbuf);
491
+                if(redata_linecol2pos(re->data,line,0,&pos,NULL)==-1) {
492
+                        re->command=COMMAND_WARNING;
493
+                        snprintf(re->commandbuf,sizeof(re->commandbuf),"Unknown line");
494
+                        re->commandbuf[sizeof(re->commandbuf)-1]='\0';
495
+                        re->headerdirty=1;
496
+                        return(-1);
497
+                }
498
+                re->cursorpos=pos;
499
+                re->curcol=0;
500
+                re->curline=line;
501
+                re->origincol=0;
502
+                if((re->originline+re->maxrow)<line)
503
+                        re->originline=line-re->maxrow;
504
+                if(line<re->originline)
505
+                        re->originline=line;
506
+                re->headerdirty=1;
507
+                re->contentsdirty=1;
508
+        }
509
+        re->command=NULL;
510
+        re->commandbuf[0]='\0';
511
+        return(0);
246 512
 }
247 513
 
248 514
 int
249 515
 re_moveupdown(re_t *re, int totalinc)
250 516
 {
251 517
         long newpos,newpos2;
252
-        char *ptr;
253
-        int len;
254
-        int has_nl;
255
-        int maxcol;
256 518
         int inc,doneinc;
257
-
519
+        long realstart;
258 520
         if(re==NULL)
259 521
                 return(-1); /* sanity check failed */
260 522
         if(totalinc==0)
261 523
                 return(0); /* nothing to do */
262 524
         inc=(totalinc<0)?-1:1;
525
+        newpos=re->cursorpos; /* get rid of compiler warning (will be overwitten in the loop as totalinc never is 0) */
263 526
         for(doneinc=0;doneinc!=totalinc;doneinc+=inc) {
264
-                if(redata_line_info(re->data,re->cursorpos,&newpos,&ptr,&len)==-1)
265
-                        return(-1); /* couldn't get current line data */
266
-                if(inc==-1 && newpos==0)
267
-                        return(0); /* going up but already at top; nothing to do */
268
-                if(redata_line_info(re->data,(inc==1)?(newpos+len):(newpos-1),&newpos2,&ptr,&len)==-1)
269
-                        return(-1); /* couldn't get next line data */
270
-                has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
271
-                maxcol=redata_generic_utf8len(ptr,len)-has_nl;
272
-                if(maxcol<re->curcol)
273
-                        re->cursorpos=newpos2+len-has_nl;
274
-                else
275
-                        re->cursorpos=newpos2+(redata_generic_utf8col(ptr,len,re->curcol)-ptr);
527
+fprintf(stderr,"MOVING from cursorpos:%li line:%i col:%i\n",re->cursorpos,re->curline,re->curcol);
528
+                if((inc==-1 && redata_line_prevrealstart(re->data,re->cursorpos,&realstart)==-1)
529
+                   || (inc==1 && redata_line_nextrealstart(re->data,re->cursorpos,&realstart)==-1)) {
530
+                        break; /* couldn't get current line data, we are at start/end */
531
+                }
532
+                re->cursorpos=realstart;
276 533
                 re->curline+=inc;
277 534
                 if(re->curline<(re->originline+LINEFORCESCROLL)) {
278 535
                         re->originline-=LINEFORCESCROLL;
... ...
@@ -280,110 +537,230 @@ re_moveupdown(re_t *re, int totalinc)
280 537
                 }
281 538
                 if(re->curline>(re->originline+re->maxrow-LINEFORCESCROLL))
282 539
                         re->originline+=LINEFORCESCROLL;
283
-                re->contentsdirty=1;
540
+fprintf(stderr,"MOVING   to cursorpos:%li line:%i col:%i\n",re->cursorpos,re->curline,re->curcol);
541
+        }
542
+        if(redata_line_inccol(re->data,re->cursorpos,re->curcol,&newpos2,NULL)==-1) {
543
+                /* error advancing cursor, "emergency" repositioning */
544
+fprintf(stderr,"COLUMN ERROR\n");
545
+                re->curcol=0;
546
+                newpos2=newpos;
284 547
         }
285
-        re_drawheader(re);
548
+fprintf(stderr,"COLUMN from cursorpos:%li line:%i col:%i\n",re->cursorpos,re->curline,re->curcol);
549
+        re->cursorpos=newpos2;
550
+fprintf(stderr,"COLUMN   to cursorpos:%li line:%i col:%i\n",re->cursorpos,re->curline,re->curcol);
551
+        re->contentsdirty=1;
552
+        re->headerdirty=1;
286 553
         return(0);
287 554
 }
288 555
 
289 556
 int
290 557
 re_moveleftright(re_t *re, int totalinc)
291 558
 {
292
-        long newpos;
293
-        char *ptr,*ptr2;
294
-        int len;
295
-        int has_nl;
296
-        int maxcol;
559
+        long newpos,realstart;
297 560
         int inc,doneinc;
298
-
561
+        char *ptr;
562
+        int len;
563
+        long start;
564
+        int tmpcol,oldcol;
299 565
         if(re==NULL)
300 566
                 return(-1); /* sanity check failed */
301 567
         if(totalinc==0)
302 568
                 return(0); /* nothing to do */
303
-        inc=(totalinc<0)?-1:1;
304
-        for(doneinc=0;doneinc!=totalinc;doneinc+=inc) {
305
-                if(redata_line_info(re->data,re->cursorpos,&newpos,&ptr,&len)==-1)
306
-                        return(-1); /* couldn't get current line data */
307
-                if(inc==-1 && re->cursorpos==0)
308
-                        return(-1); /* going left but already at leftmost char */
309
-                maxcol=redata_generic_utf8len(ptr,len);
310
-                has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
311
-                if(re->curcol<=maxcol)
312
-                        ptr2=redata_generic_utf8col(ptr,len,re->curcol+inc);
313
-                else
314
-                        ptr2=NULL;
315
-                if(ptr2!=NULL)
316
-                        re->cursorpos=newpos+(ptr2-ptr);
317
-                else
318
-                        re->cursorpos=newpos+(len-has_nl); /* we're past the last col, set cursor to last pos in line */
319
-                if((re->curcol+inc)>=0)
320
-                        re->curcol+=inc;
321
-                if(re->curcol<(re->origincol+COLFORCESCROLL)) {
322
-                        re->origincol-=COLFORCESCROLL;
323
-                        re->origincol=(re->origincol<0)?0:re->origincol;
569
+        oldcol=re->curcol;
570
+        if(totalinc<0 && (re->curcol+totalinc)<=0) {
571
+                /* we'll land on the start of the line -- do it trivially */
572
+                if(redata_line_realstart(re->data,re->cursorpos,&realstart)==-1)
573
+                        return(-1); /* couldn't get current pos */
574
+                re->curcol=0;
575
+                re->cursorpos=realstart;
576
+        } else {
577
+                /* move a char at a time */
578
+                if(redata_line_realstart(re->data,re->cursorpos,&realstart)==-1)
579
+                        return(-1); /* couldn't get current pos */
580
+                inc=(totalinc<0)?-1:1;
581
+                doneinc=0;
582
+                if(redata_pos2linecol(re->data,re->cursorpos,NULL,&tmpcol)==-1)
583
+                        return(-1); /* couldn't get current pos */
584
+                /* special case: we're just over the '\n' going right, we have to move 1 to enable the generic case to work */
585
+                if(tmpcol==re->curcol && inc>0) {
586
+                        if(redata_line_rawinfo(re->data,re->cursorpos,&start,&ptr,&len,NULL)==-1)
587
+                                return(-1); /* couldn't get line data */
588
+                        if(ptr[re->cursorpos-start]=='\n') {
589
+                                tmpcol++;
590
+                                totalinc--;
591
+                                re->curcol++;
592
+                        }
324 593
                 }
325
-                if(re->curcol>(re->origincol+re->maxcol-COLFORCESCROLL))
326
-                        re->origincol+=COLFORCESCROLL;
594
+                /* generic case: cursor after the \n ("floating") */
595
+                if(tmpcol<re->curcol) {
596
+                        int avail;
597
+                        if(inc>0) {
598
+                                doneinc=totalinc;
599
+                        } else {
600
+                                avail=re->curcol-tmpcol;
601
+                                doneinc=(avail>=totalinc)?totalinc:avail;
602
+                        }
603
+                }
604
+                /* generic case: move one char at a time */
605
+                for(ptr=NULL,len=0,start=0,newpos=re->cursorpos;doneinc!=totalinc;) {
606
+                        if((newpos+inc)<realstart)
607
+                                break;
608
+                        newpos+=inc;
609
+                        if(ptr==NULL || (start+len)<=newpos || newpos<start) {
610
+                                if(redata_line_rawinfo(re->data,newpos,&start,&ptr,&len,NULL)==-1)
611
+                                        return(-1); /* couldn't get line data */
612
+                        }
613
+                        if(redata_generic_utf8isstartbyte(ptr[newpos-start]))
614
+                                doneinc+=inc;
615
+                        if(ptr[newpos-start]=='\n')
616
+                                break;
617
+                }
618
+                re->cursorpos=newpos;
619
+                re->curcol+=doneinc;
620
+                if(inc>0 && doneinc<totalinc && ptr!=NULL && ptr[newpos-start]=='\n')
621
+                        re->curcol+=(totalinc-doneinc);
622
+        }
623
+        if(re->curcol!=oldcol) {
327 624
                 re->contentsdirty=1;
625
+                re->headerdirty=1;
626
+                if(re->curcol<(re->origincol+COLFORCESCROLL) && re->origincol>0) {
627
+                        re->origincol=(re->curcol-COLFORCESCROLL)/COLFORCESCROLL;
628
+                        re->origincol=re->origincol*COLFORCESCROLL;
629
+                        re->origincol=(re->origincol<0)?0:re->origincol;
630
+                }
631
+                if(re->curcol>(re->origincol+re->maxrow-COLFORCESCROLL)) {
632
+                        re->origincol=re->curcol+COLFORCESCROLL-re->maxrow-(re->curcol%COLFORCESCROLL);
633
+                        re->origincol=(re->origincol<0)?0:re->origincol;
634
+                }
635
+        }
636
+        return(0);
637
+}
638
+
639
+int
640
+re_rtrim(re_t *re, long curpos, int *trimmed)
641
+{
642
+        long startpos;
643
+        char *start;
644
+        int len;
645
+        int n;
646
+        if(re==NULL || curpos<0 || curpos>redata_getused(re->data))
647
+                return(-1);
648
+        if(trimmed!=NULL)
649
+                *trimmed=0;
650
+        if(redata_line_rawinfo(re->data,curpos,&startpos,&start,&len,NULL)==0 &&
651
+           len>1 && start[len-1]=='\n' && start[len-2]==' ' && curpos==(startpos+len-1)) {
652
+                for(n=1;(n+1)<(len-1) && start[len-1-n-1]==' ';n++)
653
+                        ;
654
+fprintf(stderr,"Trying to DELETE SPACES del %i bytes\n",n);
655
+                redata_op_del(re->data,curpos-n,n,NULL);
656
+                re->cursorpos-=n;
657
+                if(trimmed!=NULL)
658
+                        *trimmed=n;
328 659
         }
329
-        re_drawheader(re);
330 660
         return(0);
331 661
 }
332 662
 
333 663
 int
334
-re_drawheader(re_t *re)
664
+re_drawheader_editing(re_t *re)
335 665
 {
336 666
         int line,col;
337 667
         if(re==NULL)
338 668
                 return(-1);
339 669
         if(redata_pos2linecol(re->data,re->cursorpos,&line,&col)==-1)
340 670
                 line=col=-1;
341
-        reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,"\x00\x00\xff\xff");
342
-        reui_printf(re->ui,0,0,"\xff\xff\x00\xff","File: %s Line:%i (%i) Col:%i (%i) Pos:%li",re->filename,re->curline,line,re->curcol,col,re->cursorpos);
671
+        reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_STATUSBG);
672
+        reui_printf(re->ui,0,0,COLOR_STATUSFG,"File: %s Line:%i (%i) Col:%i (%i) Pos:%li",re->filename,re->curline,line,re->curcol,col,re->cursorpos);
673
+        re->headerdirty=0;
674
+        re->ui->rendererdirty=1;
675
+        return(0);
676
+}
677
+
678
+int
679
+re_drawheader_command(re_t *re)
680
+{
681
+        if(re==NULL)
682
+                return(-1);
683
+        if(re->command==NULL)
684
+                return(-1);
685
+        if(re->command[0]=='\0') {
686
+                reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG);
687
+                reui_printf(re->ui,0,0,COLOR_QUERYFG,"Command:");
688
+        } else if(strcmp(re->command,COMMAND_WARNING)==0) {
689
+                reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_WARNINGBG);
690
+                reui_printf(re->ui,0,0,COLOR_WARNINGFG,"%s %s",re->command,re->commandbuf);
691
+        } else {
692
+                reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG);
693
+                re->commandbuf[sizeof(re->commandbuf)-1]='\0';
694
+                reui_printf(re->ui,0,0,COLOR_QUERYFG,"%s %s",re->command,re->commandbuf);
695
+        }
696
+        re->headerdirty=0;
697
+        re->ui->rendererdirty=1;
343 698
         return(0);
344 699
 }
345 700
 
701
+
346 702
 int
347 703
 re_drawcontents(re_t *re)
348 704
 {
349 705
         long pos,newpos;
350
-        char *ptr,*visibleptr;
706
+        char *ptr;
351 707
         int len;
352 708
         int y,row;
353 709
         char *curptr;
354 710
         int curptrlen;
355 711
         int has_nl;
712
+        int is_continuation;
713
+        int tmpcol,availcol;
714
+        int in_error;
715
+        long realstart,realend;
716
+        int drawn_cursor;
356 717
         if(re==NULL)
357 718
                 return(-1);
358 719
         reui_fill(re->ui,re->x,re->y,re->w,re->h,"\xdf\xdf\xdf\xff");
359 720
         row=re->curline-re->originline;
360 721
         pos=re->cursorpos;
361
-        while(row>0) {
362
-                if(redata_line_info(re->data,pos,&newpos,NULL,NULL)==-1)
722
+        while(row>0 && pos>0) {
723
+                if(redata_line_rawinfo(re->data,pos,&newpos,NULL,NULL,&is_continuation)==-1)
363 724
                         return(-1);
364 725
                 pos=(newpos>0)?newpos-1:0;
365
-                row--;
726
+                if(!is_continuation)
727
+                        row--;
366 728
         }
367 729
         /* highlight current line */
368 730
         reui_fill(re->ui,re->x,re->y+(re->curline-re->originline)*re->ui->fontheight,re->w,re->ui->fontheight+1,"\xef\xef\xef\xff");
369 731
         /* draw the lines */
732
+        drawn_cursor=0;
370 733
         for(y=re->y;y<(re->y+re->h);y+=re->ui->fontheight,row++) {
371
-                if(redata_line_info(re->data,pos,&newpos,&ptr,&len)==-1)
372
-                        break; /* couldn't get line start pos */
373
-                has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
374
-                visibleptr=redata_generic_utf8col((char *)ptr,len-has_nl,re->origincol);
375
-                if(visibleptr!=NULL)
376
-                        reui_write(re->ui,re->x,y,"\x00\x00\x00\xff",visibleptr,len-has_nl-(visibleptr-ptr));
377
-                if(row==(re->curline-re->originline)) {
378
-#warning DEBUG write of current char
379
-                        reui_fill(re->ui,re->x+re->ui->fontwidth*(re->curcol-re->origincol),y,re->ui->fontwidth,re->ui->fontheight+1,"\x00\x00\x00\xff");
734
+                if(redata_line_realstart(re->data,pos,&realstart)==-1 || redata_line_realend(re->data,pos,&realend)==-1) {
735
+                        break; /* couldn't get real start/end */
736
+                }
737
+                in_error=0;
738
+                for(tmpcol=0,pos=realstart,availcol=0;tmpcol<(re->origincol+re->maxcol) && pos<=realend;pos=newpos+len,tmpcol+=availcol) {
739
+                        if(redata_line_rawinfo(re->data,pos,&newpos,&ptr,&len,&is_continuation)==-1) {
740
+                                in_error=1;
741
+                                break; /* couldn't get this line part info */
742
+                        }
743
+                        has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
744
+                        availcol=redata_generic_utf8len(ptr,len-has_nl);
380 745
 #warning TODO: consider tabs
381
-                        curptr=redata_generic_utf8col(ptr,len-has_nl,re->curcol);
382
-                        curptrlen=(curptr==NULL)?0:(len-has_nl)-(curptr-ptr);
383
-                        reui_write(re->ui,re->x+re->ui->fontwidth*(re->curcol-re->origincol),y,"\xff\xff\xff\xff",curptr,redata_generic_utf8charlen(ptr,curptrlen));
746
+                        reui_write(re->ui,re->x+(tmpcol-re->origincol)*re->ui->fontwidth,y,"\x00\x00\x00\xff",ptr,len-has_nl);
747
+                        if(row==(re->curline-re->originline)) {
748
+                                if(re->curcol>=tmpcol && re->curcol<(tmpcol+availcol)) {
749
+                                        reui_fill(re->ui,re->x+re->ui->fontwidth*(re->curcol-re->origincol),y,re->ui->fontwidth,re->ui->fontheight+1,"\x00\x00\x00\xff");
750
+                                        drawn_cursor=1;
751
+                                        curptr=redata_generic_utf8col(ptr,len-has_nl,re->curcol-tmpcol);
752
+                                        curptrlen=(curptr==NULL)?0:(len-has_nl)-(curptr-ptr);
753
+                                        reui_write(re->ui,re->x+re->ui->fontwidth*(re->curcol-re->origincol),y,"\xff\xff\xff\xff",curptr,redata_generic_utf8charlen(ptr,curptrlen));
754
+                                }
384 755
 #warning TODO: if it is one of  '[','{','<','>','}',']', highlight the matching bracket/parens/anglebracket.
756
+                        }
385 757
                 }
386
-                pos=newpos+len;
758
+                if(row==(re->curline-re->originline) && !drawn_cursor)
759
+                        reui_fill(re->ui,re->x+re->ui->fontwidth*(re->curcol-re->origincol),y,re->ui->fontwidth,re->ui->fontheight+1,"\x00\x00\x00\xff");
760
+                if(in_error)
761
+                        break;
762
+                pos=realend+1;
763
+/*LONG LINE LEFT FOR DEBUGGING PURPOSES: reui_write(re->ui,re->x+re->ui->fontwidth*(re->curcol-re->origincol),y,"\xff\xff\xff\xff",curptr,redata_generic_utf8charlen(ptr,curptrlen));*/
387 764
         }
388 765
         re->contentsdirty=0;
389 766
         re->ui->rendererdirty=1;