4 #include "../swell/swell.h"
8 #ifndef CURSES_INSTANCE
9 #define CURSES_INSTANCE ((win32CursesCtx *)m_cursesCtx)
13 #include "../wdlutf8.h"
14 #include "../win32_utf8.h"
15 #include "../wdlcstring.h"
16 #include "../eel2/ns-eel-int.h"
19 EEL_Editor::EEL_Editor(void *cursesCtx
) : WDL_CursesEditor(cursesCtx
)
21 m_added_funclist
=NULL
;
22 m_suggestion_x
=m_suggestion_y
=-1;
23 m_case_sensitive
=false;
24 m_comment_str
="//"; // todo IsWithinComment() or something?
25 m_function_prefix
= "function ";
28 EEL_Editor::~EEL_Editor()
32 #define sh_func_ontoken(x,y)
34 int EEL_Editor::namedTokenHighlight(const char *tokStart
, int len
, int state
)
36 if (len
== 4 && !strnicmp(tokStart
,"this",4)) return SYNTAX_KEYWORD
;
37 if (len
== 7 && !strnicmp(tokStart
,"_global",7)) return SYNTAX_KEYWORD
;
38 if (len
== 5 && !strnicmp(tokStart
,"local",5)) return SYNTAX_KEYWORD
;
39 if (len
== 8 && !strnicmp(tokStart
,"function",8)) return SYNTAX_KEYWORD
;
40 if (len
== 6 && !strnicmp(tokStart
,"static",6)) return SYNTAX_KEYWORD
;
41 if (len
== 8 && !strnicmp(tokStart
,"instance",8)) return SYNTAX_KEYWORD
;
42 if (len
== 6 && !strnicmp(tokStart
,"global",6)) return SYNTAX_KEYWORD
;
43 if (len
== 7 && !strnicmp(tokStart
,"globals",7)) return SYNTAX_KEYWORD
;
45 if (len
== 5 && !strnicmp(tokStart
,"while",5)) return SYNTAX_KEYWORD
;
46 if (len
== 4 && !strnicmp(tokStart
,"loop",4)) return SYNTAX_KEYWORD
;
48 if (len
== 17 && !strnicmp(tokStart
,"__denormal_likely",17)) return SYNTAX_FUNC
;
49 if (len
== 19 && !strnicmp(tokStart
,"__denormal_unlikely",19)) return SYNTAX_FUNC
;
54 lstrcpyn_safe(buf
,tokStart
,wdl_min(sizeof(buf
),len
+1));
55 char **r
=m_added_funclist
->GetPtr(buf
);
56 if (r
) return *r
? SYNTAX_FUNC
: SYNTAX_REGVAR
;
59 NSEEL_VMCTX vm
= peek_want_VM_funcs() ? peek_get_VM() : NULL
;
63 functionType
*f
= nseel_getFunctionFromTableEx((compileContext
*)vm
,x
);
65 if (f
&& !strnicmp(tokStart
,f
->name
,len
) && (int)strlen(f
->name
) == len
)
73 int EEL_Editor::parse_format_specifier(const char *fmt_in
, int *var_offs
, int *var_len
)
75 const char *fmt
= fmt_in
+1;
78 if (fmt_in
[0] != '%') return 0; // passed a non-specifier
82 const char c
= *fmt
++;
84 if (c
>0 && isalpha(c
))
86 return (int) (fmt
- fmt_in
);
89 if (c
== '.' || c
== '+' || c
== '-' || c
== ' ' || (c
>='0' && c
<='9'))
94 if (*var_offs
!=0) return 0; // already specified
95 *var_offs
= (int)(fmt
-fmt_in
);
96 if (*fmt
== '.' || (*fmt
>= '0' && *fmt
<= '9')) return 0; // symbol name can't start with 0-9 or .
100 if ((*fmt
>= 'a' && *fmt
<= 'z') ||
101 (*fmt
>= 'A' && *fmt
<= 'Z') ||
102 (*fmt
>= '0' && *fmt
<= '9') ||
103 *fmt
== '_' || *fmt
== '.' || *fmt
== '#')
109 return 0; // bad character in variable name
112 *var_len
= (int)((fmt
-fmt_in
) - *var_offs
);
124 void EEL_Editor::draw_string(int *skipcnt
, const char *str
, int amt
, int *attr
, int newAttr
, int comment_string_state
)
126 if (amt
> 0 && comment_string_state
=='"')
128 while (amt
> 0 && *str
)
130 const char *str_scan
= str
;
131 int varpos
,varlen
,l
=0;
133 while (!l
&& *str_scan
)
135 while (*str_scan
&& *str_scan
!= '%' && str_scan
< str
+amt
) str_scan
++;
136 if (str_scan
>= str
+amt
) break;
138 l
= parse_format_specifier(str_scan
,&varpos
,&varlen
);
139 if (!l
&& *str_scan
) if (*++str_scan
== '%') str_scan
++;
141 if (!*str_scan
|| str_scan
>= str
+amt
) break; // allow default processing to happen if we reached the end of the string
147 const int sz
=wdl_min((int)(str_scan
-str
),amt
);
148 draw_string_urlchk(skipcnt
,str
,sz
,attr
,newAttr
);
154 const int sz
=(varlen
>0) ? wdl_min(varpos
,amt
) : wdl_min(l
,amt
);
157 draw_string_internal(skipcnt
,str
,sz
,attr
,SYNTAX_HIGHLIGHT2
);
165 int sz
= wdl_min(varlen
,amt
);
168 draw_string_internal(skipcnt
,str
,sz
,attr
,*str
== '#' ? SYNTAX_STRINGVAR
: SYNTAX_HIGHLIGHT1
);
173 sz
= wdl_min(l
- varpos
- varlen
, amt
);
176 draw_string_internal(skipcnt
,str
,sz
,attr
,SYNTAX_HIGHLIGHT2
);
183 draw_string_urlchk(skipcnt
,str
,amt
,attr
,newAttr
);
186 void EEL_Editor::draw_string_urlchk(int *skipcnt
, const char *str
, int amt
, int *attr
, int newAttr
)
188 if (amt
> 0 && (newAttr
== SYNTAX_COMMENT
|| newAttr
== SYNTAX_STRING
))
190 const char *sstr
=str
;
191 while (amt
> 0 && *str
)
193 const char *str_scan
= str
;
196 while (l
< 10 && *str_scan
)
201 (strncmp(str_scan
,"http://",7) || (sstr
!= str_scan
&& str_scan
[-1] > 0 && isalnum(str_scan
[-1]))) &&
202 str_scan
< str
+amt
) str_scan
++;
203 if (!*str_scan
|| str_scan
>= str
+amt
) break;
204 while (str_scan
[l
] && str_scan
[l
] != ')' && str_scan
[l
] != '\"' && str_scan
[l
] != ')' && str_scan
[l
] != ' ' && str_scan
[l
] != '\t') l
++;
206 if (!*str_scan
|| str_scan
>= str
+amt
) break; // allow default processing to happen if we reached the end of the string
212 const int sz
=wdl_min((int)(str_scan
-str
),amt
);
213 draw_string_internal(skipcnt
,str
,sz
,attr
,newAttr
);
218 const int sz
=wdl_min(l
,amt
);
221 draw_string_internal(skipcnt
,str
,sz
,attr
,SYNTAX_HIGHLIGHT1
);
227 draw_string_internal(skipcnt
,str
,amt
,attr
,newAttr
);
230 void EEL_Editor::draw_string_internal(int *skipcnt
, const char *str
, int amt
, int *attr
, int newAttr
)
234 const int sk
= *skipcnt
;
249 if (*attr
!= newAttr
)
261 WDL_TypedBuf
<char> EEL_Editor::s_draw_parentokenstack
;
262 bool EEL_Editor::sh_draw_parenttokenstack_pop(char c
)
264 int sz
= s_draw_parentokenstack
.GetSize();
267 char tc
= s_draw_parentokenstack
.Get()[sz
];
270 s_draw_parentokenstack
.Resize(sz
,false);
277 // any open paren or semicolon is enough to cause error for ?:
281 if (tc
== '[') return true;
284 if (tc
== '(') return true;
291 bool EEL_Editor::sh_draw_parentokenstack_update(const char *tok
, int toklen
)
301 s_draw_parentokenstack
.Add(*tok
);
303 case ':': return sh_draw_parenttokenstack_pop('?');
304 case ')': return sh_draw_parenttokenstack_pop('(');
305 case ']': return sh_draw_parenttokenstack_pop('[');
312 void EEL_Editor::draw_line_highlight(int y
, const char *p
, int *c_comment_state
)
314 int last_attr
= A_NORMAL
;
317 int rv
= do_draw_line(p
, c_comment_state
, last_attr
);
318 attrset(rv
< 0 ? SYNTAX_ERROR
: A_NORMAL
);
320 if (rv
< 0) attrset(A_NORMAL
);
323 int EEL_Editor::do_draw_line(const char *p
, int *c_comment_state
, int last_attr
)
326 if (is_code_start_line(p
))
329 s_draw_parentokenstack
.Resize(0,false);
332 int skipcnt
= m_offs_x
;
333 int ignoreSyntaxState
= overrideSyntaxDrawingForLine(&skipcnt
, &p
, c_comment_state
, &last_attr
);
335 if (ignoreSyntaxState
>0)
337 int len
= (int)strlen(p
);
338 draw_string(&skipcnt
,p
,len
,&last_attr
,ignoreSyntaxState
==100 ? SYNTAX_ERROR
:
339 ignoreSyntaxState
==2 ? SYNTAX_COMMENT
: A_NORMAL
);
340 return len
-m_offs_x
< COLS
;
344 // syntax highlighting
345 const char *endptr
= p
+strlen(p
);
349 int last_comment_state
=*c_comment_state
;
350 while (NULL
!= (tok
= sh_tokenize(&p
,endptr
,&toklen
,c_comment_state
)) || lp
< endptr
)
352 if (tok
&& *tok
< 0 && toklen
== 1)
354 while (tok
[toklen
] < 0) {p
++; toklen
++; } // utf-8 skip
356 if (last_comment_state
>0) // if in a multi-line string or comment
358 // draw empty space between lp and p as a string. in this case, tok/toklen includes our string, so we quickly finish after
359 draw_string(&skipcnt
,lp
,(int)(p
-lp
),&last_attr
, last_comment_state
==1 ? SYNTAX_COMMENT
:SYNTAX_STRING
, last_comment_state
);
360 last_comment_state
=0;
364 sh_func_ontoken(tok
,toklen
);
366 // draw empty space between lp and tok/endptr as normal
367 const char *adv_to
= tok
? tok
: endptr
;
368 if (adv_to
> lp
) draw_string(&skipcnt
,lp
,(int)(adv_to
-lp
),&last_attr
, A_NORMAL
);
370 if (adv_to
>= endptr
) break;
372 last_comment_state
=0;
374 if (adv_to
== p
) continue;
380 int start_of_tok
= 0;
382 if (tok
[0] == '/' && toklen
> 1 && (tok
[1] == '*' || tok
[1] == '/'))
384 attr
= SYNTAX_COMMENT
;
386 else if (tok
[0] > 0 && (isalpha(tok
[0]) || tok
[0] == '_' || tok
[0] == '#'))
388 int def_attr
= A_NORMAL
;
392 def_attr
= SYNTAX_STRINGVAR
;
393 draw_string(&skipcnt
,tok
,1,&last_attr
,def_attr
);
399 // divide up by .s, if any
401 while (this_len
< toklen
&& tok
[this_len
] != '.') this_len
++;
404 int attr
=isf
?namedTokenHighlight(tok
,this_len
,*c_comment_state
):def_attr
;
405 if (isf
&& attr
== A_NORMAL
)
407 int ntok_len
=0, cc
= *c_comment_state
;
408 const char *pp
=lp
,*ntok
= sh_tokenize(&pp
,endptr
,&ntok_len
,&cc
);
409 if (ntok
&& ntok_len
>0 && *ntok
== '(') def_attr
= attr
= SYNTAX_FUNC2
;
412 draw_string(&skipcnt
,tok
,this_len
,&last_attr
,attr
==A_NORMAL
?def_attr
:attr
);
418 draw_string(&skipcnt
,tok
,1,&last_attr
,SYNTAX_HIGHLIGHT1
);
426 else if (tok
[0] == '.' ||
427 (tok
[0] >= '0' && tok
[0] <= '9') ||
428 (toklen
> 1 && tok
[0] == '$' && (tok
[1] == 'x' || tok
[1]=='X')))
430 attr
= SYNTAX_HIGHLIGHT2
;
433 if (tok
[0] == '.') mode
=1;
434 else if (toklen
> 1 && (tok
[0] == '$' || tok
[0] == '0') && (tok
[1] == 'x' || tok
[1] == 'X')) { mode
=2; x
++; }
437 if (tok
[x
] == '.' && !mode
) mode
=1;
438 else if (tok
[x
] < '0' || tok
[x
] > '9')
440 if (mode
!= 2 || ((tok
[x
] < 'a' || tok
[x
] > 'f') && (tok
[x
] < 'A' || tok
[x
] > 'F')))
444 if (x
<toklen
) err_right
=toklen
-x
;
446 else if (tok
[0] == '\'' || tok
[0] == '\"')
448 start_of_tok
= tok
[0];
449 attr
= SYNTAX_STRING
;
451 else if (tok
[0] == '$')
453 attr
= SYNTAX_HIGHLIGHT2
;
455 if (toklen
>= 3 && !strnicmp(tok
,"$pi",3)) err_right
= toklen
- 3;
456 else if (toklen
>= 2 && !strnicmp(tok
,"$e",2)) err_right
= toklen
- 2;
457 else if (toklen
>= 4 && !strnicmp(tok
,"$phi",4)) err_right
= toklen
- 4;
458 else if (toklen
== 4 && tok
[1] == '\'' && tok
[3] == '\'') { }
459 else if (toklen
> 1 && tok
[1] == '~')
462 for(x
=2;x
<toklen
;x
++) if (tok
[x
] < '0' || tok
[x
] > '9') break;
463 if (x
<toklen
) err_right
=toklen
-x
;
465 else err_right
= toklen
;
467 else if (ignoreSyntaxState
==-1 && (tok
[0] == '{' || tok
[0] == '}'))
469 attr
= SYNTAX_HIGHLIGHT1
;
473 const char *h
="()+*-=/,|&%;!<>?:^!~[]";
474 while (*h
&& *h
!= tok
[0]) h
++;
477 if (*c_comment_state
!= STATE_BEFORE_CODE
&& sh_draw_parentokenstack_update(tok
,toklen
))
480 attr
= SYNTAX_HIGHLIGHT1
;
485 if (tok
[0] < 0) while (err_left
< toklen
&& tok
[err_left
]<0) err_left
++; // utf-8 skip
489 if (ignoreSyntaxState
) err_left
= err_right
= 0;
493 if (err_left
> toklen
) err_left
=toklen
;
494 draw_string(&skipcnt
,tok
,err_left
,&last_attr
,SYNTAX_ERROR
);
498 if (err_right
> toklen
) err_right
=toklen
;
500 draw_string(&skipcnt
, tok
, toklen
- err_right
, &last_attr
, attr
, start_of_tok
);
503 draw_string(&skipcnt
,tok
+toklen
-err_right
,err_right
,&last_attr
,SYNTAX_ERROR
);
505 if (ignoreSyntaxState
== -1 && tok
[0] == '>')
507 draw_string(&skipcnt
,p
,strlen(p
),&last_attr
,ignoreSyntaxState
==2 ? SYNTAX_COMMENT
: A_NORMAL
);
514 int EEL_Editor::GetCommentStateForLineStart(int line
)
516 if (m_write_leading_tabs
<=0) m_indent_size
=2;
517 const bool uses_code_start_lines
= !!is_code_start_line(NULL
);
522 if (uses_code_start_lines
)
524 state
=STATE_BEFORE_CODE
;
527 WDL_FastString
*t
= m_text
.Get(x
);
528 if (!t
|| is_code_start_line(t
->Get())) break;
530 const char *p
=t
->Get();
532 if (!strnicmp(p
,"tabsize:",8))
535 if (a
>0 && a
< 32) m_indent_size
= a
;
540 // scan backwards to find line starting with @
541 for (x
=line
;x
>=0;x
--)
543 WDL_FastString
*t
= m_text
.Get(x
);
545 if (is_code_start_line(t
->Get()))
554 s_draw_parentokenstack
.Resize(0,false);
558 WDL_FastString
*t
= m_text
.Get(x
);
559 const char *p
= t
?t
->Get():"";
560 if (is_code_start_line(p
))
562 s_draw_parentokenstack
.Resize(0,false);
565 else if (state
!= STATE_BEFORE_CODE
)
567 const int ll
=t
?t
->GetLength():0;
568 const char *endp
= p
+ll
;
571 while (NULL
!= (tok
=sh_tokenize(&p
,endp
,&toklen
,&state
))) // eat all tokens, updating state
573 sh_func_ontoken(tok
,toklen
);
574 sh_draw_parentokenstack_update(tok
,toklen
);
581 const char *EEL_Editor::sh_tokenize(const char **ptr
, const char *endptr
, int *lenOut
, int *state
)
583 return nseel_simple_tokenizer(ptr
, endptr
, lenOut
, state
);
587 bool EEL_Editor::LineCanAffectOtherLines(const char *txt
, int spos
, int slen
) // if multiline comment etc
589 const char *special_start
= txt
+ spos
;
590 const char *special_end
= txt
+ spos
+ slen
;
593 if (txt
>= special_start
-1 && txt
< special_end
)
595 const char c
= txt
[0];
596 if (c
== '*' && txt
[1] == '/') return true;
597 if (c
== '/' && (txt
[1] == '/' || txt
[1] == '*')) return true;
598 if (c
== '\\' && (txt
[1] == '\"' || txt
[1] == '\'')) return true;
600 if (txt
>= special_start
)
602 if (c
== '\"' || c
== '\'') return true;
603 if (c
== '(' || c
== '[' || c
== ')' || c
== ']' || c
== ':' || c
== ';' || c
== '?') return true;
614 int line
, col
, end_col
;
615 unsigned int data
; // packed char for token type, plus 24 bits for linecnt (0=single line, 1=extra line, etc)
617 eel_sh_token(int _line
, int _col
, int toklen
, unsigned char c
)
621 end_col
= col
+ toklen
;
626 void add_linecnt(int endcol
) { data
+= 256; end_col
= endcol
; }
627 int get_linecnt() const { return (data
>> 8); }
628 char get_c() const { return (char) (data
& 255); }
630 bool is_comment() const {
631 return get_c() == '/' && (get_linecnt() || end_col
>col
+1);
635 static int eel_sh_get_token_for_pos(const WDL_TypedBuf
<eel_sh_token
> *toklist
, int line
, int col
, bool *is_after
)
637 const int sz
= toklist
->GetSize();
639 for (x
=0; x
< sz
; x
++)
641 const eel_sh_token
*tok
= toklist
->Get()+x
;
642 const int first_line
= tok
->line
;
643 const int last_line
= first_line
+tok
->get_linecnt(); // last affected line (usually same as first)
645 if (last_line
>= line
) // if this token doesn't end before the line we care about
647 // check to see if the token starts after our position
648 if (first_line
> line
|| (first_line
== line
&& tok
->col
> col
)) break;
650 // token started before line/col, see if it ends after line/col
651 if (last_line
> line
|| tok
->end_col
> col
)
663 static void eel_sh_generate_token_list(const WDL_PtrList
<WDL_FastString
> *lines
, WDL_TypedBuf
<eel_sh_token
> *toklist
, int start_line
, EEL_Editor
*editor
)
665 toklist
->Resize(0,false);
668 int end_line
= lines
->GetSize();
669 if (editor
->is_code_start_line(NULL
))
671 for (l
= start_line
; l
< end_line
; l
++)
673 WDL_FastString
*s
= lines
->Get(l
);
674 if (s
&& editor
->is_code_start_line(s
->Get()))
680 for (; start_line
>= 0; start_line
--)
682 WDL_FastString
*s
= lines
->Get(start_line
);
683 if (s
&& editor
->is_code_start_line(s
->Get())) break;
685 if (start_line
< 0) return; // before any code
695 for (l
=start_line
;l
<end_line
;l
++)
697 WDL_FastString
*t
= lines
->Get(l
);
698 const int ll
= t
?t
->GetLength():0;
699 const char *start_p
= t
?t
->Get():"";
700 const char *p
= start_p
;
701 const char *endp
= start_p
+ll
;
704 int last_state
=state
;
706 while (NULL
!= (tok
=editor
->sh_tokenize(&p
,endp
,&toklen
,&state
))||last_state
)
708 if (last_state
== '\'' || last_state
== '"' || last_state
==1)
710 const int sz
=toklist
->GetSize();
711 // update last token to include this data
712 if (sz
) toklist
->Get()[sz
-1].add_linecnt((int) ((tok
? p
:endp
) - start_p
));
716 if (tok
) switch (tok
[0])
731 eel_sh_token
t(l
,(int)(tok
-start_p
),toklen
,tok
[0]);
742 static bool eel_sh_get_matching_pos_for_pos(WDL_PtrList
<WDL_FastString
> *text
, int curx
, int cury
, int *newx
, int *newy
, const char **errmsg
, EEL_Editor
*editor
)
744 static WDL_TypedBuf
<eel_sh_token
> toklist
;
745 eel_sh_generate_token_list(text
,&toklist
, cury
,editor
);
747 const int hit_tokidx
= eel_sh_get_token_for_pos(&toklist
, cury
, curx
, &is_after
);
748 const eel_sh_token
*hit_tok
= hit_tokidx
>= 0 ? toklist
.Get() + hit_tokidx
: NULL
;
750 if (!is_after
&& hit_tok
&& (hit_tok
->get_c() == '"' || hit_tok
->get_c() == '\'' || hit_tok
->is_comment()))
752 // if within a string or comment, move to start, unless already at start, move to end
753 if (cury
== hit_tok
->line
&& curx
== hit_tok
->col
)
755 *newx
=hit_tok
->end_col
-1;
756 *newy
=hit_tok
->line
+ hit_tok
->get_linecnt();
766 if (!hit_tok
) return false;
768 const int toksz
=toklist
.GetSize();
769 int tokpos
= hit_tokidx
;
770 int pc1
=0,pc2
=0; // (, [ count
771 int pc3
=0; // : or ? count depending on mode
772 int dir
=-1, mode
=0; // default to scan to previous [(
775 switch (hit_tok
->get_c())
777 case '(': mode
=1; dir
=1; break;
778 case '[': mode
=2; dir
=1; break;
779 case ')': mode
=3; dir
=-1; break;
780 case ']': mode
=4; dir
=-1; break;
781 case '?': mode
=5; dir
=1; break;
782 case ':': mode
=6; break;
783 case ';': mode
=7; break;
785 // if hit a token, exclude this token from scanning
789 while (tokpos
>=0 && tokpos
<toksz
)
791 const eel_sh_token
*tok
= toklist
.Get() + tokpos
;
792 const char this_c
= tok
->get_c();
795 bool match
=false, want_abort
=false;
798 case 0: match
= this_c
== '(' || this_c
== '['; break;
799 case 1: match
= this_c
== ')'; break;
800 case 2: match
= this_c
== ']'; break;
801 case 3: match
= this_c
== '('; break;
802 case 4: match
= this_c
== '['; break;
804 // scan forward to nearest : or ;
805 if (this_c
== '?') pc3
++;
806 else if (this_c
== ':')
811 else if (this_c
== ';') match
=true;
812 else if (this_c
== ')' || this_c
== ']')
814 want_abort
=true; // if you have "(x<y?z)", don't match for the ?
817 case 6: // scanning back from : to ?, if any
818 case 7: // semicolon searches same as colon, effectively
819 if (this_c
== ':') pc3
++;
820 else if (this_c
== '?')
825 else if (this_c
== ';' || this_c
== '(' || this_c
== '[')
832 if (want_abort
) break;
842 case '[': pc2
++; break;
843 case ']': pc2
--; break;
844 case '(': pc1
++; break;
845 case ')': pc1
--; break;
852 if (!mode
) *errmsg
= "Could not find previous [ or (";
853 else if (mode
== 1) *errmsg
= "Could not find matching )";
854 else if (mode
== 2) *errmsg
= "Could not find matching ]";
855 else if (mode
== 3) *errmsg
= "Could not find matching (";
856 else if (mode
== 4) *errmsg
= "Could not find matching [";
857 else if (mode
== 5) *errmsg
= "Could not find matching : or ; for ?";
858 else if (mode
== 6) *errmsg
= "Could not find matching ? for :";
859 else if (mode
== 7) *errmsg
= "Could not find matching ? for ;";
865 void EEL_Editor::doParenMatching()
867 WDL_FastString
*curstr
;
868 const char *errmsg
= "";
869 if (NULL
!= (curstr
=m_text
.Get(m_curs_y
)))
871 int bytex
= WDL_utf8_charpos_to_bytepos(curstr
->Get(),m_curs_x
);
872 if (bytex
>= curstr
->GetLength()) bytex
=curstr
->GetLength()-1;
873 if (bytex
<0) bytex
= 0;
876 if (eel_sh_get_matching_pos_for_pos(&m_text
, bytex
,m_curs_y
,&new_x
,&new_y
,&errmsg
,this))
878 curstr
= m_text
.Get(new_y
);
879 if (curstr
) new_x
= WDL_utf8_bytepos_to_charpos(curstr
->Get(),new_x
);
889 draw_message(errmsg
);
895 int EEL_Editor::peek_get_function_info(const char *name
, char *sstr
, size_t sstr_sz
, int chkmask
, int ignoreline
)
897 if ((chkmask
&4) && m_function_prefix
&& *m_function_prefix
)
899 const size_t nlen
= strlen(name
);
900 const char *prefix
= m_function_prefix
;
901 const int prefix_len
= (int) strlen(m_function_prefix
);
902 for (int i
=0; i
< m_text
.GetSize(); ++i
)
904 WDL_FastString
* s
=m_text
.Get(i
);
905 if (s
&& i
!= ignoreline
)
907 const char* p
= s
->Get();
910 if (m_case_sensitive
? !strncmp(p
,prefix
,prefix_len
) : !strnicmp(p
,prefix
,prefix_len
))
913 while (*p
== ' ') p
++;
914 if (m_case_sensitive
? !strncmp(p
,name
,nlen
) : !strnicmp(p
,name
,nlen
))
916 const char *np
= p
+nlen
;
917 while (*np
== ' ') np
++;
921 lstrcpyn_safe(sstr
,p
,sstr_sz
);
932 if ((chkmask
&2) && m_added_funclist
)
934 char **p
=m_added_funclist
->GetPtr(name
);
937 lstrcpyn_safe(sstr
,*p
,sstr_sz
);
945 NSEEL_VMCTX vm
= peek_want_VM_funcs() ? peek_get_VM() : NULL
;
948 functionType
*f
= nseel_getFunctionFromTableEx((compileContext
*)vm
,x
);
950 if (f
&& !stricmp(name
,f
->name
))
952 snprintf(sstr
,sstr_sz
,"'%s' is a function that requires %d parameters", f
->name
,f
->nParams
&0xff);
962 bool EEL_Editor::peek_get_variable_info(const char *name
, char *sstr
, size_t sstr_sz
)
965 NSEEL_VMCTX vm
= peek_get_VM();
966 EEL_F
*vptr
=NSEEL_VM_getvar(vm
,name
);
971 if (!vptr
) return false;
974 snprintf(sstr
,sstr_sz
,"%s=%.14f",name
,v
);
976 if (vm
&& v
> -1.0 && v
< NSEEL_RAM_ITEMSPERBLOCK
*NSEEL_RAM_BLOCKS
)
978 const unsigned int w
= (unsigned int) (v
+NSEEL_CLOSEFACTOR
);
979 EEL_F
*dv
= NSEEL_VM_getramptr_noalloc(vm
,w
,NULL
);
982 snprintf_append(sstr
,sstr_sz
," [0x%06x]=%.14f",w
,*dv
);
987 good_len
= strlen(sstr
);
988 snprintf_append(sstr
,sstr_sz
," [0x%06x]=<uninit>",w
);
994 if (peek_get_numbered_string_value(v
,buf
,sizeof(buf
)))
997 snprintf_append(sstr
,sstr_sz
," %.0f(str)=%s",v
,buf
);
1000 if (good_len
>=0) sstr
[good_len
]=0; // remove [addr]=<uninit> if a string and no ram
1001 snprintf_append(sstr
,sstr_sz
," (str)=%s",buf
);
1007 void EEL_Editor::doWatchInfo(int c
)
1009 // determine the word we are on, check its value in the effect
1010 char sstr
[512], buf
[512];
1011 lstrcpyn_safe(sstr
,"Use this on a valid symbol name", sizeof(sstr
));
1012 WDL_FastString
*t
=m_text
.Get(m_curs_y
);
1016 const char *p
=t
->Get();
1017 const int bytex
= WDL_utf8_charpos_to_bytepos(p
,m_curs_x
);
1018 if (bytex
>= 0 && bytex
< t
->GetLength()) curChar
= p
[bytex
];
1019 if (c
!= KEY_F1
&& (m_selecting
||
1026 WDL_FastString code
;
1027 int miny
,maxy
,minx
,maxx
;
1031 if (eel_sh_get_matching_pos_for_pos(&m_text
,minx
=m_curs_x
,miny
=m_curs_y
,&maxx
, &maxy
,NULL
,this))
1042 else if (maxy
< miny
)
1052 minx
++; // skip leading (
1058 getselectregion(minx
,miny
,maxx
,maxy
);
1060 s
= m_text
.Get(miny
);
1061 if (s
) minx
= WDL_utf8_charpos_to_bytepos(s
->Get(),minx
);
1062 s
= m_text
.Get(maxy
);
1063 if (s
) maxx
= WDL_utf8_charpos_to_bytepos(s
->Get(),maxx
);
1069 for (x
= miny
; x
<= maxy
; x
++)
1071 WDL_FastString
*s
=m_text
.Get(x
);
1074 const char *str
=s
->Get();
1076 if (x
== miny
) sx
=wdl_max(minx
,0);
1078 int tmp
=s
->GetLength();
1079 if (sx
> tmp
) sx
=tmp
;
1081 if (x
== maxy
) ex
=wdl_min(maxx
,tmp
);
1084 if (code
.GetLength()) code
.Append("\r\n");
1085 code
.Append(ex
-sx
?str
+sx
:"",ex
-sx
);
1091 if (m_selecting
&& (GetAsyncKeyState(VK_SHIFT
)&0x8000))
1094 NSEEL_CODEHANDLE ch
;
1095 NSEEL_VMCTX vm
= peek_get_VM();
1097 if (vm
&& (ch
= NSEEL_code_compile_ex(vm
,code
.Get(),1,0)))
1099 codeHandleType
*p
= (codeHandleType
*)ch
;
1100 code
.Ellipsize(3,20);
1101 const char *errstr
= "failed writing to";
1105 GetTempPath(sizeof(buf
)-64,buf
);
1106 lstrcatn(buf
,"jsfx-out",sizeof(buf
));
1107 FILE *fp
= fopen(buf
,"wb");
1111 fwrite(p
->code
,1,p
->code_size
,fp
);
1115 snprintf(sstr
,sizeof(sstr
),"Expression '%s' compiled to %d bytes, %s temp/jsfx-out",code
.Get(),p
->code_size
, errstr
);
1116 NSEEL_code_free(ch
);
1120 code
.Ellipsize(3,20);
1121 snprintf(sstr
,sizeof(sstr
),"Expression '%s' could not compile",code
.Get());
1127 WDL_FastString code2
;
1128 code2
.Set("__debug_watch_value = (((((");
1129 code2
.Append(code
.Get());
1130 code2
.Append(")))));");
1134 NSEEL_VMCTX vm
= peek_get_VM();
1138 const char *err
="Invalid context";
1141 NSEEL_CODEHANDLE ch
= NSEEL_code_compile_ex(vm
,code2
.Get(),1,0);
1142 if (!ch
) err
= "Error parsing";
1145 NSEEL_code_execute(ch
);
1146 NSEEL_code_free(ch
);
1147 vptr
= NSEEL_VM_getvar(vm
,"__debug_watch_value");
1148 if (vptr
) v
= *vptr
;
1155 // remove whitespace from code for display
1158 for (x
=0;x
<code
.GetLength();x
++)
1160 if (isspace(code
.Get()[x
]))
1162 if (lb
) code
.DeleteSub(x
--,1);
1170 if (lb
&& code
.GetLength()>0) code
.SetLen(code
.GetLength()-1);
1173 code
.Ellipsize(3,20);
1176 snprintf(sstr
,sizeof(sstr
),"Expression '%s' evaluates to %.14f",code
.Get(),v
);
1180 snprintf(sstr
,sizeof(sstr
),"Error evaluating '%s': %s",code
.Get(),err
?err
:"Unknown error");
1184 // compile+execute code within () as debug_watch_value = ( code )
1185 // show value (or err msg)
1187 else if (curChar
>0 && (isalnum(curChar
) || curChar
== '_' || curChar
== '.' || curChar
== '#'))
1189 const int bytex
= WDL_utf8_charpos_to_bytepos(p
,m_curs_x
);
1190 const char *lp
=p
+bytex
;
1191 const char *rp
=lp
+ WDL_utf8_charpos_to_bytepos(lp
,1);
1192 while (lp
>= p
&& *lp
> 0 && (isalnum(*lp
) || *lp
== '_' || (*lp
== '.' && (lp
==p
|| lp
[-1]!='.')))) lp
--;
1193 if (lp
< p
|| *lp
!= '#') lp
++;
1194 while (*rp
&& *rp
> 0 && (isalnum(*rp
) || *rp
== '_' || (*rp
== '.' && rp
[1] != '.'))) rp
++;
1196 if (*lp
== '#' && rp
> lp
+1)
1200 n
.Set(lp
,(int)(rp
-lp
));
1202 if ((idx
=peek_get_named_string_value(n
.Get(),buf
,sizeof(buf
)))>=0) snprintf(sstr
,sizeof(sstr
),"#%s(%d)=%s",n
.Get(),idx
,buf
);
1203 else snprintf(sstr
,sizeof(sstr
),"#%s not found",n
.Get());
1205 else if (*lp
> 0 && (isalpha(*lp
) || *lp
== '_') && rp
> lp
)
1208 n
.Set(lp
,(int)(rp
-lp
));
1216 int f
= peek_get_function_info(n
.Get(),sstr
,sizeof(sstr
),~0,-1);
1218 if (!f
) f
= peek_get_variable_info(n
.Get(),sstr
,sizeof(sstr
))?1:0;
1219 if (!f
) snprintf(sstr
,sizeof(sstr
),"'%s' NOT FOUND",n
.Get());
1225 on_help(NULL
,(int)curChar
);
1234 void EEL_Editor::draw_bottom_line()
1236 #define BOLD(x) { attrset(COLOR_BOTTOMLINE|A_BOLD); addstr(x); attrset(COLOR_BOTTOMLINE&~A_BOLD); }
1237 addstr("ma"); BOLD("T"); addstr("ch");
1238 BOLD(" S"); addstr("ave");
1241 addstr(" pee"); BOLD("K");
1243 if (GetTabCount()>1)
1246 BOLD("[], F?"); addstr("=switch ");
1247 BOLD("W"); addstr("=close");
1252 #define CTRL_KEY_DOWN (GetAsyncKeyState(VK_CONTROL)&0x8000)
1253 #define SHIFT_KEY_DOWN (GetAsyncKeyState(VK_SHIFT)&0x8000)
1254 #define ALT_KEY_DOWN (GetAsyncKeyState(VK_MENU)&0x8000)
1256 int EEL_Editor::onChar(int c
)
1258 if ((m_ui_state
== UI_STATE_NORMAL
|| m_ui_state
== UI_STATE_MESSAGE
) &&
1259 (c
== 'K'-'A'+1 || c
== 'S'-'A'+1 || !SHIFT_KEY_DOWN
) && !ALT_KEY_DOWN
) switch (c
)
1262 if (CTRL_KEY_DOWN
) break;
1269 draw_message("Error writing file, changes not saved!");
1277 WDL_FastString
*txtstr
=m_text
.Get(m_curs_y
);
1278 const char *txt
=txtstr
?txtstr
->Get():NULL
;
1280 if (txt
&& line_has_openable_file(txt
,WDL_utf8_charpos_to_bytepos(txt
,m_curs_x
),fnp
,sizeof(fnp
)))
1282 WDL_CursesEditor::OpenFileInTab(fnp
);
1292 return WDL_CursesEditor::onChar(c
);
1295 void EEL_Editor::draw_top_line()
1297 if (m_curs_x
>= m_suggestion_x
&& m_curs_y
== m_suggestion_y
&& m_suggestion
.GetLength())
1299 const char *p
=m_suggestion
.Get();
1301 if (WDL_utf8_get_charlen(m_suggestion
.Get()) > COLS
)
1303 int l
= WDL_utf8_charpos_to_bytepos(m_suggestion
.Get(),COLS
-4);
1304 if (l
> sizeof(str
)-6) l
= sizeof(str
)-6;
1305 lstrcpyn(str
, m_suggestion
.Get(), l
+1);
1310 attrset(COLOR_TOPLINE
|A_BOLD
);
1311 bkgdset(COLOR_TOPLINE
);
1320 m_suggestion_x
=m_suggestion_y
=-1;
1321 if (m_suggestion
.GetLength()) m_suggestion
.Set("");
1322 WDL_CursesEditor::draw_top_line();
1327 void EEL_Editor::onRightClick(HWND hwnd
)
1329 WDL_LogicalSortStringKeyedArray
<int> flist(m_case_sensitive
);
1331 if (!(GetAsyncKeyState(VK_CONTROL
)&0x8000) && m_function_prefix
&& *m_function_prefix
)
1333 const char *prefix
= m_function_prefix
;
1334 const int prefix_len
= (int) strlen(m_function_prefix
);
1335 const int comment_len
=(int)strlen(m_comment_str
);
1336 for (i
=0; i
< m_text
.GetSize(); ++i
)
1338 WDL_FastString
* s
=m_text
.Get(i
);
1339 const char* p
=s
? s
->Get() : NULL
;
1342 if (!strncmp(p
, m_comment_str
, comment_len
)) break;
1344 if (m_case_sensitive
? !strncmp(p
,prefix
,prefix_len
) : !strnicmp(p
,prefix
,prefix_len
))
1347 while (*p
== ' ') p
++;
1348 if ((*p
>= 'a' && *p
<= 'z') || (*p
>= 'A' && *p
<= 'Z') || *p
== '_')
1350 const char *q
= p
+1;
1351 while ((*q
>= '0' && *q
<= '9') ||
1352 (*q
>= 'a' && *q
<= 'z') ||
1353 (*q
>= 'A' && *q
<= 'Z') ||
1354 *q
== '_' || *q
== '.') q
++;
1356 while (*q
== ' ') q
++;
1359 while (*q
&& *q
!= ')') q
++;
1363 lstrcpyn(buf
, p
, wdl_min(q
-p
+1, sizeof(buf
)));
1364 if (strlen(buf
) > sizeof(buf
)-2) lstrcpyn(buf
+sizeof(buf
)-5, "...", 4);
1365 flist
.AddUnsorted(buf
, i
);
1373 if (flist
.GetSize())
1376 HMENU hm
=CreatePopupMenu();
1378 for (i
=0; i
< flist
.GetSize(); ++i
)
1380 const char* fname
=NULL
;
1381 int line
=flist
.Enumerate(i
, &fname
);
1382 InsertMenu(hm
, pos
++, MF_STRING
|MF_BYPOSITION
, line
+1, fname
);
1386 int ret
=TrackPopupMenu(hm
, TPM_NONOTIFY
|TPM_RETURNCMD
, p
.x
, p
.y
, 0, hwnd
, NULL
);
1392 m_select_x2
=strlen(m_text
.Get(ret
)->Get());
1393 m_select_y1
=m_select_y2
=ret
;
1405 #ifdef WDL_IS_FAKE_CURSES
1407 LRESULT
EEL_Editor::onMouseMessage(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1411 case WM_LBUTTONDBLCLK
:
1412 if (CURSES_INSTANCE
&& CURSES_INSTANCE
->m_font_w
&& CURSES_INSTANCE
->m_font_h
)
1414 const int y
= ((short)HIWORD(lParam
)) / CURSES_INSTANCE
->m_font_h
- m_top_margin
;
1415 //const int x = ((short)LOWORD(lParam)) / CURSES_INSTANCE->m_font_w + m_offs_x;
1416 WDL_FastString
*fs
=m_text
.Get(y
+ m_paneoffs_y
[m_curpane
]);
1419 if (!strncmp(fs
->Get(),"import",6) && isspace(fs
->Get()[6]))
1421 onChar('R'-'A'+1); // open imported file
1430 return WDL_CursesEditor::onMouseMessage(hwnd
,uMsg
,wParam
,lParam
);