Browse code

Implement left/right movement. Add test_ui with utf8 tests.

Dario Rodriguez authored on 30/08/2020 21:47:55
Showing 6 changed files
... ...
@@ -2,7 +2,7 @@ CC=gcc
2 2
 CFLAGS=-g -Wall -Wformat-truncation=0 -Ideps -I/usr/include/SDL2
3 3
 LDFLAGS=-lSDL2 -lSDL2_image -lSDL2_ttf
4 4
 
5
-all: re tests
5
+all: re tests tests_ui
6 6
 
7 7
 recenteditor.o: recenteditor.c re_data.h ext/socklib.h
8 8
 
... ...
@@ -14,6 +14,8 @@ re_ui.o: re_ui.c re_ui.h hack_regular.h
14 14
 
15 15
 re_tests.o: re_tests.c re_data.h
16 16
 
17
+re_tests_ui.o: re_tests_ui.c re_ui.h
18
+
17 19
 sha3.o: sha3/sha3.c sha3/sha3.h
18 20
 	$(CC) $(CFLAGS) -Isha3 -c -o sha3.o sha3/sha3.c
19 21
 
... ...
@@ -35,5 +37,8 @@ re: recenteditor.o re_data.o re_plugin_unsaved.o sha3.o re_ui.o hack_regular.o s
35 37
 tests: re_tests.o re_data.o sha3.o
36 38
 	$(CC) $(LDFLAGS) -o tests re_tests.o re_data.o sha3.o
37 39
 
40
+tests_ui: re_tests_ui.o re_ui.o hack_regular.o
41
+	$(CC) $(LDFLAGS) -o tests_ui re_tests_ui.o re_ui.o hack_regular.o
42
+
38 43
 clean:
39 44
 	rm -f recenteditor.o re_data.o re_tests.o re_plugin_unsaved.o sha3.o re_ui.o hack_regular.c hack_regular.h hack_regular.o re tests 
40 45
new file mode 100644
... ...
@@ -0,0 +1,150 @@
1
+/*
2
+ * re_tests_ui.c
3
+ *
4
+ * A programmers editor
5
+ *
6
+ * Tests (ensure correct functionality of modules)
7
+ *
8
+ * Author: Dario Rodriguez dario@softhome.net
9
+ * This program is licensed under the terms of GNU GPL v2.1+
10
+ */
11
+
12
+#include <stdio.h>
13
+#include <stdlib.h>
14
+#include <unistd.h>
15
+#include <string.h>
16
+#include <limits.h>
17
+#include <fcntl.h>
18
+#include <errno.h>
19
+
20
+#include "re_ui.h"
21
+
22
+#define TEST_OK "OK"
23
+
24
+typedef struct test_ui_t  {
25
+        int needs_realui;
26
+        char *name;
27
+        char *(*fn)(reui_t *,char *,int , int, int, int);
28
+} test_ui_t;
29
+
30
+char *test_utf8len(reui_t *ui, char *teststring, int expectednchars, int dummy1, int dummy2,int dummy3);
31
+char *test_utf8col(reui_t *ui, char *teststring, int dummy0, int col, int expectedoffset,int dummy3);
32
+char *test_utf8charlen(reui_t *ui, char *teststring, int dummy0, int dummy1, int offset,int charsizeatoffset);
33
+
34
+int
35
+main(int argc, char *argv[])
36
+{
37
+        struct reui_t dummyui;
38
+        struct {
39
+                char string[1024];
40
+                int nchars;
41
+                int col;
42
+                int offsetcol;
43
+                int charsizeatoffsetcol;
44
+        } teststrings[]={
45
+                {{"This is a latin1 string"},51-28,1,1,1},
46
+                {{"lowercase acute vowels:\xc3\xa1\xc3\xa9\xc3\xad\xc3\xb3\xc3\xba"},23+5,23+1,23+2,2},
47
+        };
48
+        test_ui_t tests[]={
49
+                {0,"utf8len",test_utf8len},
50
+                {0,"utf8col",test_utf8col},
51
+                {0,"utf8charlen",test_utf8charlen},
52
+        };
53
+        int flag_exit=0,flag_all=0;
54
+        reui_t *ui;
55
+        int i,s;
56
+        int nerrors,total;
57
+        char *res;
58
+        for(i=1;i<argc;i++) {
59
+                if(strcmp(argv[i],"--help")==0) {
60
+                        fprintf(stderr,"Syntax: %s [--all] [--exit] [--help]\nExplanation:\n\t--all: do even the slow tests\n\t--exit: exit program at first unsuccessful test\n\t--help: this text\n",argv[0]);
61
+                        return(1);
62
+                } else if(strcmp(argv[i],"--all")==0) {
63
+                        flag_all=1;
64
+                } else if(strcmp(argv[i],"--exit")==0) {
65
+                        flag_exit=1;
66
+                }
67
+        }
68
+        nerrors=0;
69
+        total=0;
70
+        /* flag_all is not used right now, next line is to silence the compiler */
71
+        total+=(flag_all&0);
72
+        /* end of hack */
73
+        memset(&dummyui,0,sizeof(reui_t));
74
+        for(s=0;s<(sizeof(teststrings)/sizeof(teststrings[0]));s++) {
75
+                fprintf(stderr,"\"%s\"\n",teststrings[s].string);
76
+                for(i=0;i<(sizeof(tests)/sizeof(tests[0]));i++,total++) {
77
+                        ui=&dummyui;
78
+                        if(tests[i].needs_realui && (ui=reui_init(NULL))==NULL) {
79
+                                fprintf(stderr,"ERROR: problem initializing ui module\n");
80
+                                return(1);
81
+                        }
82
+                        fprintf(stderr,"%i:%s...",total+1,tests[i].name);
83
+                        res=tests[i].fn(ui,teststrings[s].string,teststrings[s].nchars,teststrings[s].col,teststrings[s].offsetcol,teststrings[s].charsizeatoffsetcol);
84
+                                if(strcmp(res,TEST_OK)==0) {
85
+                                fprintf(stderr," ok.\n");
86
+                        } else {
87
+                                fprintf(stderr," ERROR: %s <= %s(\"%s\",%i,%i,%i,%i)\n",res,tests[i].name,teststrings[s].string,teststrings[s].nchars,teststrings[s].col,teststrings[s].offsetcol,teststrings[s].charsizeatoffsetcol);
88
+                                nerrors++;
89
+                                if(flag_exit) {
90
+                                        /* exit on first error */
91
+                                        s=sizeof(teststrings)/sizeof(teststrings[0]);
92
+                                        break;
93
+                                }
94
+                        }
95
+                        if(tests[i].needs_realui)
96
+                                reui_free(ui),ui=NULL;
97
+                }
98
+        }
99
+        fprintf(stderr,"\n");
100
+        if(nerrors==0)
101
+                fprintf(stderr,"All %i tests passed OK\n",total);
102
+        else
103
+                fprintf(stderr,"%i test(s) failed of %i tests run.\n",nerrors,total);
104
+        if(ui!=NULL && ui!=&dummyui)
105
+                reui_free(ui),ui=NULL;
106
+        return((nerrors==0)?0:1);
107
+}
108
+
109
+char *
110
+test_utf8len(reui_t *ui, char *teststring, int expectednchars, int dummy1, int dummy2, int dummy3)
111
+{
112
+        int res;
113
+        static char errorstr[1024];
114
+        res=reui_utf8len(ui,teststring,strlen(teststring));
115
+        if(res!=expectednchars) {
116
+                snprintf(errorstr,sizeof(errorstr),"expected %i chars, got %i chars",expectednchars,res);
117
+                errorstr[sizeof(errorstr)-1]='\0';
118
+                return(errorstr);
119
+        }
120
+        return(TEST_OK);
121
+}
122
+
123
+char *test_utf8col(reui_t *ui, char *teststring, int dummy0, int col, int expectedoffset, int dummy3)
124
+{
125
+        char *ptr;
126
+        static char errorstr[1024];
127
+        ptr=reui_utf8col(ui,teststring,strlen(teststring),col);
128
+        if(ptr!=(teststring+expectedoffset)) {
129
+                snprintf(errorstr,sizeof(errorstr),"expected offset %i, got offset %i (\"%s\")",expectedoffset,(int) ((ptr==NULL)?-1:ptr-teststring),ptr);
130
+                errorstr[sizeof(errorstr)-1]='\0';
131
+                return(errorstr);
132
+        }
133
+        return(TEST_OK);
134
+}
135
+
136
+char *
137
+test_utf8charlen(reui_t *ui, char *teststring, int dummy0, int dummy1, int offset,int charsizeatoffset)
138
+{
139
+        int res;
140
+        static char errorstr[1024];
141
+        res=reui_utf8charlen(ui,teststring+offset,strlen(teststring+offset));
142
+        if(res!=(charsizeatoffset)) {
143
+                snprintf(errorstr,sizeof(errorstr),"expected char size %i, got char size %i (\"%s\")",charsizeatoffset,res,teststring+offset);
144
+                errorstr[sizeof(errorstr)-1]='\0';
145
+                return(errorstr);
146
+        }
147
+        return(TEST_OK);
148
+}
149
+
150
+
... ...
@@ -198,21 +198,58 @@ reui_printf(reui_t *ui, int x, int y, char *rgba, char *format, ...)
198 198
 int
199 199
 reui_utf8len(reui_t *ui, char *ptr, int size)
200 200
 {
201
+        int len,i;
201 202
         /* calculate the number of utf8-charaters in buffer */
202 203
         if(ui==NULL || size<0 || (ptr==NULL && size!=0))
203 204
                 return(-1);
204
-#warning TODO
205
-        return(size);
206
-#warning Also consider tabs
205
+        /* for now we only count the number of code points */
206
+        /* in UTF8: 0x00-0x7f single byte chars
207
+         *          0xc0-0xff leading bytes
208
+         *          0x80-0xbf continuation bytes (ignore these for len)*/
209
+/*#warning TODO: XXX support combining code points (at least U+0300 - U+036F ( https://en.wikipedia.org/wiki/Combining_character ) */
210
+        for(len=0,i=0;i<size;i++)
211
+                len+=((ptr[i]&0xc0)!=0x80)?1:0;
212
+        return(len);
213
+/*#warning TODO: XXX Also consider tabs*/
207 214
 }
208 215
 
209 216
 char *
210 217
 reui_utf8col(reui_t *ui, char *ptr, int size, int col)
211 218
 {
219
+        int len,i;
212 220
         /* return a pointer to the "n"th ("col"th) utf8-character in buffer */
213
-#warning TODO
214
-        if(col<size)
215
-                return(ptr+col);
216
-        return(NULL);
217
-#warning Also consider tabs
221
+        if(ui==NULL || size<0 || (ptr==NULL && size!=0))
222
+                return(NULL); /* sanity check failed */
223
+        /* see reui_utf8len() for explanation of algorithm */
224
+/*#warning TODO: support combining code points (at least U+0300 - U+036F ( https://en.wikipedia.org/wiki/Combining_character ) */
225
+        if(col>=size)
226
+                return(NULL);/* col greater than maximum possible char. count */
227
+        /* skip "col" amount of single byte chars and leading bytes */
228
+        for(len=0,i=0;len<col && i<size;i++)
229
+                len+=((ptr[i]&0xc0)!=0x80)?1:0;
230
+        /* if we landed in a continuation byte, advance until next single byte chars or leading byte */
231
+        while(i<size && (ptr[i]&0xc0)==0x80)
232
+                i++;
233
+        if(i>=size)
234
+                return(NULL); /* col is beyond end of string */
235
+        return(ptr+i);
236
+/*#warning TODO: XXX Also consider tabs*/
237
+}
238
+
239
+int
240
+reui_utf8charlen(reui_t *ui, char *ptr, int maxsize)
241
+{
242
+        int i;
243
+        /* returns the len in bytes of the character starting at ptr (zero on error)*/
244
+        if(ui==NULL || ptr==NULL || maxsize<1)
245
+                return(0); /* sanity check failed */
246
+/*#warning TODO: support combining code points (at least U+0300 - U+036F ( https://en.wikipedia.org/wiki/Combining_character ) */
247
+        if(((unsigned char *)ptr)[0]<0x80)
248
+                return(1); /* single byte char */
249
+        if((ptr[0]&0xc0)==0x80)
250
+                return(0); /* error: this is continuation, not leading byte */
251
+        for(i=1;i<maxsize && (ptr[i]&0xc0)==0x80;i++)
252
+                ;
253
+        return(i);
218 254
 }
255
+
... ...
@@ -51,3 +51,6 @@ int reui_utf8len(reui_t *ui, char *ptr, int size);
51 51
 /* return a pointer to the "n"th ("col"th) utf8-character in buffer */
52 52
 char *reui_utf8col(reui_t *ui, char *ptr, int size, int col);
53 53
 
54
+/* returns the len in bytes of the character starting at ptr (zero on error)*/
55
+int reui_utf8charlen(reui_t *ui, char *ptr, int maxsize);
56
+
... ...
@@ -27,6 +27,7 @@ typedef struct re_t {
27 27
         long cursorpos;
28 28
         int lastcol,lastrow;
29 29
         int maxrow,maxcol;
30
+        int contentsdirty;
30 31
 } re_t;
31 32
 
32 33
 volatile int flag_sigint;
... ...
@@ -43,6 +44,7 @@ void re_free(re_t *re);
43 44
 
44 45
 int re_setfilename(re_t *re, char *filename);
45 46
 int re_processkey(re_t *re, SDL_Event *event);
47
+int re_drawheader(re_t *re);
46 48
 int re_drawcontents(re_t *re);
47 49
 
48 50
 
... ...
@@ -98,11 +100,12 @@ main(int argc, char *argv[])
98 100
         re->x=0,re->y=re->ui->fontheight,re->w=re->ui->w,re->h=re->ui->h-re->y;
99 101
         re->maxrow=re->h/re->ui->fontheight-1;
100 102
         re->maxcol=re->w/re->ui->fontwidth-1;
101
-        reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,"\x00\x00\xff\xff");
102
-        reui_printf(re->ui,0,0,"\xff\xff\x00\xff","Fichero: %s",re->filename);
103
+        re_drawheader(re);
103 104
         re_drawcontents(re);
104 105
         flag_had_events=0;
105 106
         while(do_exit==0 && flag_sigint==0) {
107
+                if(re->contentsdirty)
108
+                        re_drawcontents(re);
106 109
                 if(re->ui->rendererdirty)
107 110
                         reui_present(re->ui);
108 111
                 sselect_wait(ssel,(flag_had_events)?10:100);
... ...
@@ -207,8 +210,10 @@ re_processkey(re_t *re, SDL_Event *event)
207 210
         int len;
208 211
         int has_nl;
209 212
         int oldcol;
213
+        int linecols;
214
+        int inc;
210 215
         if(re==NULL || event==NULL || event->type!=SDL_KEYDOWN)
211
-                return(-1);
216
+                return(-1); /* sanity check failed */
212 217
         if(event->key.keysym.sym==SDLK_DOWN || event->key.keysym.sym==SDLK_UP) {
213 218
                 if(redata_line_info(re->data,re->cursorpos,&newpos,&ptr,&len)==-1)
214 219
                         return(-1); /* couldn't get current line data */
... ...
@@ -227,12 +232,44 @@ re_processkey(re_t *re, SDL_Event *event)
227 232
                         re->lastrow++;
228 233
                 else if(event->key.keysym.sym==SDLK_UP && re->lastrow>0)
229 234
                         re->lastrow--;
230
-                re_drawcontents(re);
235
+                re_drawheader(re);
236
+                re->contentsdirty=1;
237
+        } else if(event->key.keysym.sym==SDLK_LEFT || event->key.keysym.sym==SDLK_RIGHT) {
238
+                if(redata_line_info(re->data,re->cursorpos,&newpos,&ptr,&len)==-1)
239
+                        return(-1); /* couldn't get current line data */
240
+                if(event->key.keysym.sym==SDLK_LEFT && re->cursorpos==0)
241
+                        return(-1); /* going left but already at leftmost char */
242
+                linecols=reui_utf8len(re->ui,ptr,len);
243
+                oldcol=reui_utf8len(re->ui,ptr,re->cursorpos-newpos);
244
+                inc=(event->key.keysym.sym==SDLK_LEFT)?-1:1;
245
+                has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
246
+                if(re->lastcol<=linecols) {
247
+                        ptr2=reui_utf8col(re->ui,ptr,len,re->lastcol+inc);
248
+                } else
249
+                        ptr2=NULL;
250
+                if(ptr2!=NULL)
251
+                        re->cursorpos=newpos+(ptr2-ptr);
252
+                else
253
+                        re->cursorpos=newpos+(len-has_nl); /* we're past the last col, set cursor to last pos in line */
254
+                if((re->lastcol+inc)>=0)
255
+                        re->lastcol+=inc;
256
+                re_drawheader(re);
257
+                re->contentsdirty=1;
231 258
         }
232 259
         return(-1);
233 260
 }
234 261
 
235 262
 
263
+int
264
+re_drawheader(re_t *re)
265
+{
266
+        if(re==NULL)
267
+                return(-1);
268
+        reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,"\x00\x00\xff\xff");
269
+        reui_printf(re->ui,0,0,"\xff\xff\x00\xff","Fichero: %s Col:%i Row:%i Pos:%li",re->filename,re->lastcol,re->lastrow,re->cursorpos);
270
+        return(0);
271
+}
272
+
236 273
 int
237 274
 re_drawcontents(re_t *re)
238 275
 {
... ...
@@ -240,8 +277,11 @@ re_drawcontents(re_t *re)
240 277
         char *ptr;
241 278
         int len;
242 279
         int y,row;
243
-        char c;
280
+        char *curptr;
281
+        int curptrlen;
244 282
         int has_nl;
283
+        if(re==NULL)
284
+                return(-1);
245 285
         reui_fill(re->ui,re->x,re->y,re->w,re->h,"\xdf\xdf\xdf\xff");
246 286
         row=re->lastrow;
247 287
         pos=re->cursorpos;
... ...
@@ -260,14 +300,17 @@ re_drawcontents(re_t *re)
260 300
                 has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
261 301
                 reui_write(re->ui,re->x,y,"\x00\x00\x00\xff",(char *)ptr,len-has_nl);
262 302
                 if(row==re->lastrow) {
303
+     #warning DEBUG write of current char
263 304
                         reui_fill(re->ui,re->x+re->ui->fontwidth*re->lastcol,y,re->ui->fontwidth,re->ui->fontheight+1,"\x00\x00\x00\xff");
264
-#warning TODO: consider multibytes characters and tabs to position on the corect character and to pass the correct number of bytes to reui_write
265
-                        c=(re->lastcol>=(len-has_nl))?' ':ptr[re->lastcol];
266
-                        reui_write(re->ui,re->x+re->ui->fontwidth*re->lastcol,y,"\xff\xff\xff\xff",&c,1);
305
+#warning TODO: consider tabs
306
+                        curptr=reui_utf8col(re->ui,ptr,len-has_nl,re->lastcol);
307
+                        curptrlen=(curptr==NULL)?0:(len-has_nl)-(curptr-ptr);
308
+                        reui_write(re->ui,re->x+re->ui->fontwidth*re->lastcol,y,"\xff\xff\xff\xff",curptr,reui_utf8charlen(re->ui,ptr,curptrlen));
267 309
 #warning TODO: if it is one of  '[','{','<','>','}',']', highlight the matching bracket/parens/anglebracket.
268 310
                 }
269 311
                 pos=newpos+len;
270 312
         }
313
+        re->contentsdirty=0;
271 314
         re->ui->rendererdirty=1;
272 315
         return(0);
273 316
 }
274 317
new file mode 100755
275 318
Binary files /dev/null and b/tests_ui differ