... | ... |
@@ -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 |
} |