5 #include "../swell/swell.h"
9 #ifndef CURSES_INSTANCE
10 #define CURSES_INSTANCE ((win32CursesCtx *)m_cursesCtx)
14 #include "../wdlutf8.h"
15 #include "../win32_utf8.h"
16 #include "../wdlcstring.h"
17 #include "../eel2/ns-eel-int.h"
19 int g_eel_editor_max_vis_suggestions
= 50;
20 int g_eel_editor_flags
;
22 EEL_Editor::EEL_Editor(void *cursesCtx
) : WDL_CursesEditor(cursesCtx
)
24 m_suggestion_curline_comment_state
=0;
25 m_suggestion_x
=m_suggestion_y
=-1;
26 m_case_sensitive
=false;
27 m_comment_str
="//"; // todo IsWithinComment() or something?
28 m_function_prefix
= "function ";
30 m_suggestion_hwnd
=NULL
;
31 m_suggestion_hwnd_scroll
=0;
32 m_suggestion_hwnd_sel
=-1;
33 m_code_func_cache_lines
=0;
34 m_code_func_cache_time
=0;
37 EEL_Editor::~EEL_Editor()
39 if (m_suggestion_hwnd
) DestroyWindow(m_suggestion_hwnd
);
40 m_code_func_cache
.Empty(true,free
);
43 #define sh_func_ontoken(x,y)
45 int EEL_Editor::namedTokenHighlight(const char *tokStart
, int len
, int state
)
47 if (len
== 4 && !strnicmp(tokStart
,"this",4)) return SYNTAX_KEYWORD
;
48 if (len
== 7 && !strnicmp(tokStart
,"_global",7)) return SYNTAX_KEYWORD
;
49 if (len
== 5 && !strnicmp(tokStart
,"local",5)) return SYNTAX_KEYWORD
;
50 if (len
== 8 && !strnicmp(tokStart
,"function",8)) return SYNTAX_KEYWORD
;
51 if (len
== 6 && !strnicmp(tokStart
,"static",6)) return SYNTAX_KEYWORD
;
52 if (len
== 8 && !strnicmp(tokStart
,"instance",8)) return SYNTAX_KEYWORD
;
53 if (len
== 6 && !strnicmp(tokStart
,"global",6)) return SYNTAX_KEYWORD
;
54 if (len
== 7 && !strnicmp(tokStart
,"globals",7)) return SYNTAX_KEYWORD
;
56 if (len
== 5 && !strnicmp(tokStart
,"while",5)) return SYNTAX_KEYWORD
;
57 if (len
== 4 && !strnicmp(tokStart
,"loop",4)) return SYNTAX_KEYWORD
;
59 if (len
== 17 && !strnicmp(tokStart
,"__denormal_likely",17)) return SYNTAX_FUNC
;
60 if (len
== 19 && !strnicmp(tokStart
,"__denormal_unlikely",19)) return SYNTAX_FUNC
;
63 lstrcpyn_safe(buf
,tokStart
,wdl_min(sizeof(buf
),len
+1));
64 NSEEL_VMCTX vm
= peek_want_VM_funcs() ? peek_get_VM() : NULL
;
65 if (nseel_getFunctionByName((compileContext
*)vm
,buf
,NULL
)) return SYNTAX_FUNC
;
70 int EEL_Editor::parse_format_specifier(const char *fmt_in
, int *var_offs
, int *var_len
)
72 const char *fmt
= fmt_in
+1;
75 if (fmt_in
[0] != '%') return 0; // passed a non-specifier
79 const char c
= *fmt
++;
81 if (c
>0 && isalpha(c
))
83 return (int) (fmt
- fmt_in
);
86 if (c
== '.' || c
== '+' || c
== '-' || c
== ' ' || (c
>='0' && c
<='9'))
91 if (*var_offs
!=0) return 0; // already specified
92 *var_offs
= (int)(fmt
-fmt_in
);
93 if (*fmt
== '.' || (*fmt
>= '0' && *fmt
<= '9')) return 0; // symbol name can't start with 0-9 or .
97 if ((*fmt
>= 'a' && *fmt
<= 'z') ||
98 (*fmt
>= 'A' && *fmt
<= 'Z') ||
99 (*fmt
>= '0' && *fmt
<= '9') ||
100 *fmt
== '_' || *fmt
== '.' || *fmt
== '#')
106 return 0; // bad character in variable name
109 *var_len
= (int)((fmt
-fmt_in
) - *var_offs
);
121 void EEL_Editor::draw_string(int *skipcnt
, const char *str
, int amt
, int *attr
, int newAttr
, int comment_string_state
)
123 if (amt
> 0 && comment_string_state
=='"')
125 while (amt
> 0 && *str
)
127 const char *str_scan
= str
;
128 int varpos
,varlen
,l
=0;
130 while (!l
&& *str_scan
)
132 while (*str_scan
&& *str_scan
!= '%' && str_scan
< str
+amt
) str_scan
++;
133 if (str_scan
>= str
+amt
) break;
135 l
= parse_format_specifier(str_scan
,&varpos
,&varlen
);
136 if (!l
&& *str_scan
) if (*++str_scan
== '%') str_scan
++;
138 if (!*str_scan
|| str_scan
>= str
+amt
) break; // allow default processing to happen if we reached the end of the string
144 const int sz
=wdl_min((int)(str_scan
-str
),amt
);
145 draw_string_urlchk(skipcnt
,str
,sz
,attr
,newAttr
);
151 const int sz
=(varlen
>0) ? wdl_min(varpos
,amt
) : wdl_min(l
,amt
);
154 draw_string_internal(skipcnt
,str
,sz
,attr
,SYNTAX_HIGHLIGHT2
);
162 int sz
= wdl_min(varlen
,amt
);
165 draw_string_internal(skipcnt
,str
,sz
,attr
,*str
== '#' ? SYNTAX_STRINGVAR
: SYNTAX_HIGHLIGHT1
);
170 sz
= wdl_min(l
- varpos
- varlen
, amt
);
173 draw_string_internal(skipcnt
,str
,sz
,attr
,SYNTAX_HIGHLIGHT2
);
180 draw_string_urlchk(skipcnt
,str
,amt
,attr
,newAttr
);
183 void EEL_Editor::draw_string_urlchk(int *skipcnt
, const char *str
, int amt
, int *attr
, int newAttr
)
185 if (amt
> 0 && (newAttr
== SYNTAX_COMMENT
|| newAttr
== SYNTAX_STRING
))
187 const char *sstr
=str
;
188 while (amt
> 0 && *str
)
190 const char *str_scan
= str
;
193 while (l
< 10 && *str_scan
)
198 (strncmp(str_scan
,"http://",7) || (sstr
!= str_scan
&& str_scan
[-1] > 0 && isalnum(str_scan
[-1]))) &&
199 str_scan
< str
+amt
) str_scan
++;
200 if (!*str_scan
|| str_scan
>= str
+amt
) break;
201 while (str_scan
[l
] && str_scan
[l
] != ')' && str_scan
[l
] != '\"' && str_scan
[l
] != ')' && str_scan
[l
] != ' ' && str_scan
[l
] != '\t') l
++;
203 if (!*str_scan
|| str_scan
>= str
+amt
) break; // allow default processing to happen if we reached the end of the string
209 const int sz
=wdl_min((int)(str_scan
-str
),amt
);
210 draw_string_internal(skipcnt
,str
,sz
,attr
,newAttr
);
215 const int sz
=wdl_min(l
,amt
);
218 draw_string_internal(skipcnt
,str
,sz
,attr
,SYNTAX_HIGHLIGHT1
);
224 draw_string_internal(skipcnt
,str
,amt
,attr
,newAttr
);
227 void EEL_Editor::draw_string_internal(int *skipcnt
, const char *str
, int amt
, int *attr
, int newAttr
)
229 // *skipcnt is in characters, amt is in bytes
230 while (*skipcnt
> 0 && amt
> 0)
232 const int clen
= wdl_utf8_parsechar(str
,NULL
);
240 if (*attr
!= newAttr
)
250 WDL_TypedBuf
<char> EEL_Editor::s_draw_parentokenstack
;
251 bool EEL_Editor::sh_draw_parenttokenstack_pop(char c
)
253 int sz
= s_draw_parentokenstack
.GetSize();
256 char tc
= s_draw_parentokenstack
.Get()[sz
];
259 s_draw_parentokenstack
.Resize(sz
,false);
266 // any open paren or semicolon is enough to cause error for ?:
270 if (tc
== 'P') return true;
271 if (tc
== '[') return true;
274 if (tc
== 'P') return true;
275 if (tc
== '(') return true;
278 if (tc
!= '?' && tc
!= ';') return true;
286 static int is_preproc_token(const char *tok
)
288 if (tok
[0] == '<') return tok
[1] == '?' ? 1 : 0;
289 if (tok
[0] == '?') return tok
[1] == '>' ? -1 : 0;
293 bool EEL_Editor::sh_draw_parentokenstack_update(const char *tok
, int toklen
)
303 s_draw_parentokenstack
.Add(*tok
);
305 case ':': return sh_draw_parenttokenstack_pop('?');
306 case ')': return sh_draw_parenttokenstack_pop('(');
307 case ']': return sh_draw_parenttokenstack_pop('[');
310 else if (toklen
== 2)
312 switch (is_preproc_token(tok
))
314 case 1: s_draw_parentokenstack
.Add('P'); break;
315 case -1: return sh_draw_parenttokenstack_pop('P');
322 void EEL_Editor::draw_line_highlight(int y
, const char *p
, int *c_comment_state
, int line_n
)
324 if (line_n
== m_curs_y
)
325 m_suggestion_curline_comment_state
= *c_comment_state
;
326 int last_attr
= A_NORMAL
;
329 int rv
= do_draw_line(p
, c_comment_state
, last_attr
);
330 attrset(rv
< 0 ? SYNTAX_ERROR
: A_NORMAL
);
332 if (rv
< 0) attrset(A_NORMAL
);
335 #define STATE_PREPROC (0x20000000)
336 #define STATE_PREPROC_SINGLELINEMODE (0x10000000)
337 #define STATE_PREPROC_MASK (0x3ff00000)
338 #define STATE_PREPROC_ENCODE_OLD(x) (((x)<<20)&0x0ff00000)
339 #define STATE_PREPROC_DECODE_OLD(x) (((x)&0x0ff00000)>>20)
342 int EEL_Editor::do_draw_line(const char *p
, int *c_comment_state
, int last_attr
)
344 if (!(*c_comment_state
& STATE_PREPROC
) && is_code_start_line(p
))
347 s_draw_parentokenstack
.Resize(0,false);
350 int skipcnt
= m_offs_x
;
351 int ignoreSyntaxState
= 0;
353 const int initial_state
= *c_comment_state
;
354 if (!(initial_state
& STATE_PREPROC
))
355 ignoreSyntaxState
= overrideSyntaxDrawingForLine(&skipcnt
, &p
, c_comment_state
, &last_attr
);
357 if (ignoreSyntaxState
>0)
359 int len
= (int)strlen(p
);
360 draw_string(&skipcnt
,p
,len
,&last_attr
,ignoreSyntaxState
==100 ? SYNTAX_ERROR
:
361 ignoreSyntaxState
==2 ? SYNTAX_COMMENT
: A_NORMAL
);
362 return len
-m_offs_x
< COLS
;
364 if (ignoreSyntaxState
<0 && initial_state
== STATE_BEFORE_CODE
) *c_comment_state
=0;
366 if ((initial_state
& STATE_PREPROC
) && (initial_state
& STATE_PREPROC_SINGLELINEMODE
))
367 ignoreSyntaxState
= -1;
369 // syntax highlighting
370 const char *endptr
= p
+strlen(p
);
374 int last_comment_state
=*c_comment_state
& ~STATE_PREPROC_MASK
;
375 while (NULL
!= (tok
= sh_tokenize(&p
,endptr
,&toklen
,c_comment_state
)) || lp
< endptr
)
377 if (initial_state
== STATE_BEFORE_CODE
&& !(*c_comment_state
& STATE_PREPROC
) && !ignoreSyntaxState
)
378 *c_comment_state
= initial_state
;
379 if (tok
&& *tok
< 0 && toklen
== 1)
381 while (tok
[toklen
] < 0) {p
++; toklen
++; } // utf-8 skip
383 bool is_pp
= toklen
== 2 && is_preproc_token(tok
);
384 if (!is_pp
&& (last_comment_state
>0 || *c_comment_state
== STATE_BEFORE_CODE
)) // if in a multi-line string or comment
386 // draw empty space between lp and p as a string. in this case, tok/toklen includes our string, so we quickly finish after
387 draw_string(&skipcnt
,lp
,(int)(p
-lp
),&last_attr
,
388 last_comment_state
== 1 ||
389 last_comment_state
== STATE_BEFORE_CODE
||
390 *c_comment_state
== STATE_BEFORE_CODE
? SYNTAX_COMMENT
: SYNTAX_STRING
,
392 last_comment_state
=0;
396 sh_func_ontoken(tok
,toklen
);
398 // draw empty space between lp and tok/endptr as normal
399 const char *adv_to
= tok
? tok
: endptr
;
400 if (adv_to
> lp
) draw_string(&skipcnt
,lp
,(int)(adv_to
-lp
),&last_attr
, A_NORMAL
);
402 if (adv_to
>= endptr
) break;
404 last_comment_state
=0;
406 if (adv_to
== p
) continue;
412 int start_of_tok
= 0;
414 if (tok
[0] == '/' && toklen
> 1 && (tok
[1] == '*' || tok
[1] == '/'))
416 attr
= SYNTAX_COMMENT
;
418 else if (tok
[0] > 0 && (isalpha(tok
[0]) || tok
[0] == '_' || tok
[0] == '#'))
420 int def_attr
= A_NORMAL
;
423 def_attr
= SYNTAX_STRINGVAR
;
426 // divide up by .s, if any, unless we're a string
427 int this_len
=def_attr
== SYNTAX_STRINGVAR
? toklen
: 0;
428 while (this_len
< toklen
&& tok
[this_len
] != '.') this_len
++;
431 int attr
=isf
?namedTokenHighlight(tok
,this_len
,*c_comment_state
):def_attr
;
432 if (isf
&& attr
== A_NORMAL
)
434 int ntok_len
=0, cc
= *c_comment_state
;
435 const char *pp
=lp
,*ntok
= sh_tokenize(&pp
,endptr
,&ntok_len
,&cc
);
436 if (ntok
&& ntok_len
>0 && *ntok
== '(') def_attr
= attr
= SYNTAX_FUNC2
;
439 draw_string(&skipcnt
,tok
,this_len
,&last_attr
,attr
==A_NORMAL
?def_attr
:attr
);
445 draw_string(&skipcnt
,tok
,1,&last_attr
,SYNTAX_HIGHLIGHT1
);
453 else if (tok
[0] == '.' ||
454 (tok
[0] >= '0' && tok
[0] <= '9') ||
455 (toklen
> 1 && tok
[0] == '$' && (tok
[1] == 'x' || tok
[1]=='X')))
457 attr
= SYNTAX_HIGHLIGHT2
;
460 if (tok
[0] == '.') mode
=1;
461 else if (toklen
> 1 && (tok
[0] == '$' || tok
[0] == '0') && (tok
[1] == 'x' || tok
[1] == 'X')) { mode
=2; x
++; }
464 if (tok
[x
] == '.' && !mode
) mode
=1;
465 else if (tok
[x
] < '0' || tok
[x
] > '9')
467 if (mode
!= 2 || ((tok
[x
] < 'a' || tok
[x
] > 'f') && (tok
[x
] < 'A' || tok
[x
] > 'F')))
471 if (x
<toklen
) err_right
=toklen
-x
;
473 else if (tok
[0] == '\'' || tok
[0] == '\"')
475 start_of_tok
= tok
[0];
476 attr
= SYNTAX_STRING
;
478 else if (tok
[0] == '$')
480 attr
= SYNTAX_HIGHLIGHT2
;
482 if (toklen
>= 3 && !strnicmp(tok
,"$pi",3)) err_right
= toklen
- 3;
483 else if (toklen
>= 2 && !strnicmp(tok
,"$e",2)) err_right
= toklen
- 2;
484 else if (toklen
>= 4 && !strnicmp(tok
,"$phi",4)) err_right
= toklen
- 4;
485 else if (toklen
== 4 && tok
[1] == '\'' && tok
[3] == '\'') { }
486 else if (toklen
> 1 && tok
[1] == '~')
489 for(x
=2;x
<toklen
;x
++) if (tok
[x
] < '0' || tok
[x
] > '9') break;
490 if (x
<toklen
) err_right
=toklen
-x
;
492 else err_right
= toklen
;
494 else if (ignoreSyntaxState
==-1 && (tok
[0] == '{' || tok
[0] == '}'))
496 attr
= SYNTAX_HIGHLIGHT1
;
501 last_comment_state
= *c_comment_state
;
502 if (sh_draw_parentokenstack_update(tok
,toklen
)) attr
= SYNTAX_ERROR
;
503 else attr
= SYNTAX_KEYWORD
;
507 const char *h
="()+*-=/,|&%;!<>?:^!~[]";
508 while (*h
&& *h
!= tok
[0]) h
++;
511 if (*c_comment_state
!= STATE_BEFORE_CODE
&& sh_draw_parentokenstack_update(tok
,toklen
))
514 attr
= SYNTAX_HIGHLIGHT1
;
519 if (tok
[0] < 0) while (err_left
< toklen
&& tok
[err_left
]<0) err_left
++; // utf-8 skip
523 if (ignoreSyntaxState
) err_left
= err_right
= 0;
527 if (err_left
> toklen
) err_left
=toklen
;
528 draw_string(&skipcnt
,tok
,err_left
,&last_attr
,SYNTAX_ERROR
);
532 if (err_right
> toklen
) err_right
=toklen
;
534 draw_string(&skipcnt
, tok
, toklen
- err_right
, &last_attr
, attr
, start_of_tok
);
537 draw_string(&skipcnt
,tok
+toklen
-err_right
,err_right
,&last_attr
,SYNTAX_ERROR
);
539 if (ignoreSyntaxState
== -1 && tok
[0] == '>')
541 draw_string(&skipcnt
,p
,strlen(p
),&last_attr
,ignoreSyntaxState
==2 ? SYNTAX_COMMENT
: A_NORMAL
);
545 if (ignoreSyntaxState
<0)
547 if (initial_state
== STATE_BEFORE_CODE
||
548 ((initial_state
& STATE_PREPROC
) &&
549 (initial_state
& STATE_PREPROC_SINGLELINEMODE
))
552 if (*c_comment_state
& STATE_PREPROC
)
553 *c_comment_state
|= STATE_PREPROC_SINGLELINEMODE
;
555 *c_comment_state
=STATE_BEFORE_CODE
;
561 int EEL_Editor::GetCommentStateForLineStart(int line
)
563 if (m_write_leading_tabs
<=0) m_indent_size
=2;
565 const bool uses_code_start_lines
= !!is_code_start_line(NULL
);
566 int state
=uses_code_start_lines
? STATE_BEFORE_CODE
: 0;
568 s_draw_parentokenstack
.Resize(0,false);
570 int state_ret
= state
;
571 bool need_code
= uses_code_start_lines
; // we're also searching for tabsize:
572 for (int x
=0;x
<line
|| need_code
;x
++)
574 WDL_FastString
*t
= m_text
.Get(x
);
577 const char *p
= t
->Get();
578 if (state
== STATE_BEFORE_CODE
&& !strnicmp(p
,"tabsize:",8))
581 if (a
>0 && a
< 32) m_indent_size
= a
;
583 if (!(state
& STATE_PREPROC
) && uses_code_start_lines
&& is_code_start_line(p
))
587 s_draw_parentokenstack
.Resize(0,false);
592 const int ll
=t
?t
->GetLength():0;
593 const char *endp
= p
+ll
;
596 const int initial_state
= state
;
597 // to be maximally correct here with the preprocessor here, we need to call overrideSyntaxDrawingForLine()
598 // without drawing to detect special lines, but meh corner cases
599 while (NULL
!= (tok
=sh_tokenize(&p
,endp
,&toklen
,&state
))) // eat all tokens, updating state
601 if (initial_state
== STATE_BEFORE_CODE
&& !(state
& STATE_PREPROC
))
602 state
= STATE_BEFORE_CODE
;
603 if (x
<line
&& (state
!= STATE_BEFORE_CODE
|| (toklen
== 2 && is_preproc_token(tok
))))
605 sh_func_ontoken(tok
,toklen
);
606 sh_draw_parentokenstack_update(tok
,toklen
);
610 if (x
<line
) state_ret
= state
;
615 const char *EEL_Editor::sh_tokenize(const char **ptr
, const char *endptr
, int *lenOut
, int *state
)
617 int in_preproc
= 0, prev_state
= state
? *state
: 0;
618 if (prev_state
& STATE_PREPROC
)
620 in_preproc
= prev_state
& STATE_PREPROC_MASK
;
621 *state
&= ~STATE_PREPROC_MASK
;
623 const char *p
= nseel_simple_tokenizer(ptr
, endptr
, lenOut
, state
);
624 if (in_preproc
) *state
|= in_preproc
;
626 if (!p
|| !state
) return p
;
630 if (*lenOut
>= 1 && p
+1 < endptr
&& is_preproc_token(p
) < 0)
634 *state
= STATE_PREPROC_DECODE_OLD(prev_state
); // caller will have to be aware and highlight this token correctly
638 for (int x
= 0; x
< *lenOut
- 1; x
++)
639 if (is_preproc_token(p
+x
) < 0)
641 int adj
= x
- *lenOut
;
650 if (*state
== STATE_BEFORE_CODE
&& *lenOut
== 4 && p
+ 7 < endptr
&& !strnicmp(p
,"http://",7))
653 while (p
+nl
< endptr
&& p
[nl
] && p
[nl
] != ' ' && p
[nl
] != '\t') nl
++;
658 if (*lenOut
>= 1 && p
+1 < endptr
&& is_preproc_token(p
) > 0)
662 *state
= STATE_PREPROC_ENCODE_OLD(prev_state
) | STATE_PREPROC
;
666 for (int x
= 0; x
< *lenOut
- 1; x
++)
667 if (is_preproc_token(p
+x
) > 0)
669 int adj
= x
- *lenOut
;
672 if (p
[0] == '"' || p
[0] == '\'') *state
= p
[0];
681 bool EEL_Editor::LineCanAffectOtherLines(const char *txt
, int spos
, int slen
) // if multiline comment etc
683 const char *special_start
= txt
+ spos
;
684 const char *special_end
= txt
+ spos
+ slen
;
687 if (txt
>= special_start
-1 && txt
< special_end
)
689 const char c
= txt
[0];
690 if (c
== '*' && txt
[1] == '/') return true;
691 if (c
== '/' && (txt
[1] == '/' || txt
[1] == '*')) return true;
692 if (c
== '\\' && (txt
[1] == '\"' || txt
[1] == '\'')) return true;
693 if (is_preproc_token(txt
)) return true;
695 if (txt
>= special_start
)
697 if (c
== '\"' || c
== '\'') return true;
698 if (c
== '(' || c
== '[' || c
== ')' || c
== ']' || c
== ':' || c
== ';' || c
== '?') return true;
709 int line
, col
, end_col
;
710 unsigned int data
; // packed char for token type, plus 24 bits for linecnt (0=single line, 1=extra line, etc)
712 eel_sh_token(int _line
, int _col
, int toklen
, unsigned char c
)
716 end_col
= col
+ toklen
;
721 void add_linecnt(int endcol
) { data
+= 256; end_col
= endcol
; }
722 int get_linecnt() const { return (data
>> 8); }
723 char get_c() const { return (char) (data
& 255); }
725 bool is_comment() const {
726 return get_c() == '/' && (get_linecnt() || end_col
>col
+1);
730 static int eel_sh_get_token_for_pos(const WDL_TypedBuf
<eel_sh_token
> *toklist
, int line
, int col
, bool *is_after
)
732 const int sz
= toklist
->GetSize();
734 for (x
=0; x
< sz
; x
++)
736 const eel_sh_token
*tok
= toklist
->Get()+x
;
737 const int first_line
= tok
->line
;
738 const int last_line
= first_line
+tok
->get_linecnt(); // last affected line (usually same as first)
740 if (last_line
>= line
) // if this token doesn't end before the line we care about
742 // check to see if the token starts after our position
743 if (first_line
> line
|| (first_line
== line
&& tok
->col
> col
)) break;
745 // token started before line/col, see if it ends after line/col
746 if (last_line
> line
|| tok
->end_col
> col
)
758 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
)
760 toklist
->Resize(0,false);
763 int end_line
= lines
->GetSize();
764 if (editor
->is_code_start_line(NULL
))
766 for (l
= start_line
; l
< end_line
; l
++)
768 WDL_FastString
*s
= lines
->Get(l
);
769 if (s
&& editor
->is_code_start_line(s
->Get()))
775 for (; start_line
>= 0; start_line
--)
777 WDL_FastString
*s
= lines
->Get(start_line
);
778 if (s
&& editor
->is_code_start_line(s
->Get())) break;
780 if (start_line
< 0) return; // before any code
790 int last_state_save
= 0;
791 for (l
=start_line
;l
<end_line
;l
++)
793 WDL_FastString
*t
= lines
->Get(l
);
794 const int ll
= t
?t
->GetLength():0;
795 const char *start_p
= t
?t
->Get():"";
796 const char *p
= start_p
;
797 const char *endp
= start_p
+ll
;
800 int last_state
=state
& ~STATE_PREPROC
;
802 while (NULL
!= (tok
=editor
->sh_tokenize(&p
,endp
,&toklen
,&state
))||last_state
)
804 if (tok
&& toklen
== 2 && is_preproc_token(tok
) > 0)
807 for (int x
= toklist
->GetSize()-1; x
>=0; x
--)
809 char c
= toklist
->Get()[x
].get_c();
810 if (c
== 'p') break; // ?>
811 if (c
== 'P') { ok
= false; break; } // <?
813 if (WDL_NORMALLY(ok
))
815 eel_sh_token
t(l
,(int)(tok
-start_p
),2,'P');
817 last_state_save
= STATE_PREPROC_DECODE_OLD(state
);
822 if (tok
&& toklen
== 2 && is_preproc_token(tok
) < 0)
825 for (int x
= toklist
->GetSize()-1; x
>=0; x
--)
827 char c
= toklist
->Get()[x
].get_c();
828 if (c
== 'p') break; // ?>
829 if (c
== 'P') { ok
= true; break; } // <?
831 if (WDL_NORMALLY(ok
))
833 eel_sh_token
t(l
,(int)(tok
-start_p
),2,'p');
835 last_state
= last_state_save
;
840 if (last_state
== '\'' || last_state
== '"' || last_state
==1)
842 const int sz
=toklist
->GetSize();
843 // update last token to include this data
846 char c
= last_state
== 1 ? '/' : last_state
;
847 if (toklist
->Get()[sz
-1].get_c() == c
)
848 toklist
->Get()[sz
-1].add_linecnt((int) ((tok
? p
:endp
) - start_p
));
851 // usually indicates continuation after a preprocessor block
852 eel_sh_token
t(l
,(int)(tok
-start_p
),toklen
,c
);
859 if (tok
) switch (tok
[0])
874 eel_sh_token
t(l
,(int)(tok
-start_p
),toklen
,tok
[0]);
885 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
)
887 static WDL_TypedBuf
<eel_sh_token
> toklist
;
888 eel_sh_generate_token_list(text
,&toklist
, cury
,editor
);
890 const int hit_tokidx
= eel_sh_get_token_for_pos(&toklist
, cury
, curx
, &is_after
);
891 const eel_sh_token
*hit_tok
= hit_tokidx
>= 0 ? toklist
.Get() + hit_tokidx
: NULL
;
893 if (!is_after
&& hit_tok
&& (hit_tok
->get_c() == '"' || hit_tok
->get_c() == '\'' || hit_tok
->is_comment()))
895 eel_sh_token tok
= *hit_tok
; // save a copy, toklist might get destroyed recursively here
898 //if (tok.get_c() == '"')
900 // the user could be editing code in code, tokenize it and see if we can make sense of it
901 WDL_FastString start
, end
;
902 WDL_PtrList
<WDL_FastString
> tmplist
;
903 WDL_FastString
*s
= text
->Get(tok
.line
);
904 if (s
&& s
->GetLength() > tok
.col
+1)
906 int maxl
= tok
.get_linecnt()>0 ? 0 : tok
.end_col
- tok
.col
- 2;
907 start
.Set(s
->Get() + tok
.col
+1, maxl
);
910 const int linecnt
= tok
.get_linecnt();
913 for (int a
=1; a
< linecnt
; a
++)
915 s
= text
->Get(tok
.line
+ a
);
916 if (s
) tmplist
.Add(s
);
918 s
= text
->Get(tok
.line
+ linecnt
);
921 if (tok
.end_col
>1) end
.Set(s
->Get(), tok
.end_col
-1);
926 int lx
= curx
, ly
= cury
- tok
.line
;
927 if (cury
== tok
.line
) lx
-= (tok
.col
+1);
929 // this will destroy the token
930 if (eel_sh_get_matching_pos_for_pos(&tmplist
, lx
, ly
, newx
, newy
, errmsg
, editor
))
933 if (cury
== tok
.line
) *newx
+= tok
.col
+ 1;
938 // if within a string or comment, move to start, unless already at start, move to end
939 if (cury
== hit_tok
->line
&& curx
== hit_tok
->col
)
941 *newx
=hit_tok
->end_col
-1;
942 *newy
=hit_tok
->line
+ hit_tok
->get_linecnt();
952 if (!hit_tok
) return false;
954 const int toksz
=toklist
.GetSize();
955 int tokpos
= hit_tokidx
;
956 int pc1
=0,pc2
=0; // (, [ count
957 int pc3
=0; // : or ? count depending on mode
958 int dir
=-1, mode
=0; // default to scan to previous [( or <?
961 switch (hit_tok
->get_c())
963 case '(': mode
=1; dir
=1; break;
964 case '[': mode
=2; dir
=1; break;
965 case ')': mode
=3; dir
=-1; break;
966 case ']': mode
=4; dir
=-1; break;
967 case '?': mode
=5; dir
=1; break;
968 case ':': mode
=6; break;
969 case ';': mode
=7; break;
970 case 'P': mode
=8; dir
=1; break;
971 case 'p': mode
=9; dir
=-1; break;
973 // if hit a token, exclude this token from scanning
977 while (tokpos
>=0 && tokpos
<toksz
)
979 const eel_sh_token
*tok
= toklist
.Get() + tokpos
;
980 const char this_c
= tok
->get_c();
981 if (mode
!= 8 && mode
!= 9)
985 if (this_c
== 'P') // <?
997 while (--tokpos
>= 0 && toklist
.Get()[tokpos
].get_c() != 'P');
1004 if (this_c
== 'p') break; // ?>
1007 while (++tokpos
< toksz
&& toklist
.Get()[tokpos
].get_c() != 'p');
1013 if ((!pc1
&& !pc2
) || mode
== 8 || mode
== 9)
1015 bool match
=false, want_abort
=false;
1018 case 0: match
= this_c
== '(' || this_c
== '['; break;
1019 case 1: match
= this_c
== ')'; break;
1020 case 2: match
= this_c
== ']'; break;
1021 case 3: match
= this_c
== '('; break;
1022 case 4: match
= this_c
== '['; break;
1024 // scan forward to nearest : or ;
1025 if (this_c
== '?') pc3
++;
1026 else if (this_c
== ':')
1031 else if (this_c
== ';') match
=true;
1032 else if (this_c
== ')' || this_c
== ']')
1034 want_abort
=true; // if you have "(x<y?z)", don't match for the ?
1037 case 6: // scanning back from : to ?, if any
1038 case 7: // semicolon searches same as colon, effectively
1039 if (this_c
== ':') pc3
++;
1040 else if (this_c
== '?')
1045 else if (this_c
== ';' || this_c
== '(' || this_c
== '[')
1050 case 8: match
= this_c
== 'p'; break;
1051 case 9: match
= this_c
== 'P'; break;
1054 if (want_abort
) break;
1064 case '[': pc2
++; break;
1065 case ']': pc2
--; break;
1066 case '(': pc1
++; break;
1067 case ')': pc1
--; break;
1074 if (!mode
) *errmsg
= "Could not find previous [ or (";
1075 else if (mode
== 1) *errmsg
= "Could not find matching )";
1076 else if (mode
== 2) *errmsg
= "Could not find matching ]";
1077 else if (mode
== 3) *errmsg
= "Could not find matching (";
1078 else if (mode
== 4) *errmsg
= "Could not find matching [";
1079 else if (mode
== 5) *errmsg
= "Could not find matching : or ; for ?";
1080 else if (mode
== 6) *errmsg
= "Could not find matching ? for :";
1081 else if (mode
== 7) *errmsg
= "Could not find matching ? for ;";
1087 void EEL_Editor::doParenMatching()
1089 WDL_FastString
*curstr
;
1090 const char *errmsg
= "";
1091 if (NULL
!= (curstr
=m_text
.Get(m_curs_y
)))
1093 int bytex
= WDL_utf8_charpos_to_bytepos(curstr
->Get(),m_curs_x
);
1094 if (bytex
>= curstr
->GetLength()) bytex
=curstr
->GetLength()-1;
1095 if (bytex
<0) bytex
= 0;
1098 if (eel_sh_get_matching_pos_for_pos(&m_text
, bytex
,m_curs_y
,&new_x
,&new_y
,&errmsg
,this))
1100 curstr
= m_text
.Get(new_y
);
1101 if (curstr
) new_x
= WDL_utf8_bytepos_to_charpos(curstr
->Get(),new_x
);
1111 draw_message(errmsg
);
1117 static int word_len(const char *p
)
1120 if (*p
>= 'a' && *p
<='z')
1124 while (p
[l
] && p
[l
] != '_' && p
[l
] != '.' && !(p
[l
] >= 'A' && p
[l
] <='Z')) l
++;
1126 else if (*p
>= 'A' && *p
<= 'Z')
1128 if (!strcmp(p
,"MIDI")) return 4;
1130 if (p
[l
] >= 'A' && p
[l
] <='Z') // UPPERCASE word
1131 while (p
[l
] && p
[l
] != '_' && p
[l
] != '.' && !(p
[l
] >= 'a' && p
[l
] <='z')) l
++;
1132 else // Titlecase word
1133 while (p
[l
] && p
[l
] != '_' && p
[l
] != '.' && !(p
[l
] >= 'A' && p
[l
] <='Z')) l
++;
1138 static int search_str_partial(const char *str
, int reflen
, const char *word
, int wordlen
)
1140 // find the longest leading segment of word in str
1142 for (int y
= 0; y
< reflen
; y
++)
1144 while (y
< reflen
&& !strnicmp(str
+y
,word
,best_len
+1))
1146 if (++best_len
>= wordlen
) return best_len
;
1153 static int fuzzy_match2(const char *codestr
, int codelen
, const char *refstr
, int reflen
)
1155 // codestr is user-typed, refstr is the reference function name
1156 // our APIs are gfx_blah_blah or TrackBlahBlah so breaking the API up by words
1157 // and searching the user-entered code should be effective
1158 int lendiff
= reflen
- codelen
;
1159 if (lendiff
< 0) lendiff
= -lendiff
;
1161 const char *word
= refstr
;
1165 while (*word
== '_' || *word
== '.') word
++;
1166 const int wordlen
= word_len(word
);
1167 if (!wordlen
) break;
1169 lstrcpyn_safe(word_buf
,word
,wordlen
+1);
1171 if (WDL_stristr(refstr
,word_buf
)==word
)
1173 int max_match_len
= search_str_partial(codestr
,codelen
,word
,wordlen
);
1174 if (max_match_len
< 2 && wordlen
== 5 && !strnicmp(word
,"Count",5))
1176 max_match_len
= search_str_partial(codestr
,codelen
,"Num",3);
1178 if (max_match_len
> (wordlen
<= 2 ? 1 : 2))
1179 score
+= max_match_len
;
1184 if (!score
) return 0;
1186 return score
* 1000 + 1000 - wdl_clamp(lendiff
*2,0,200);
1189 int EEL_Editor::fuzzy_match(const char *codestr
, const char *refstr
)
1191 const int codestr_len
= (int)strlen(codestr
), refstr_len
= (int)strlen(refstr
);
1192 if (refstr_len
>= codestr_len
&& !strnicmp(codestr
,refstr
,codestr_len
)) return 1000000000;
1193 int score1
= fuzzy_match2(refstr
,refstr_len
,codestr
,codestr_len
);
1194 int score2
= fuzzy_match2(codestr
,codestr_len
,refstr
,refstr_len
);
1195 if (score2
> score1
) return score2
| 1;
1199 static int eeledit_varenumfunc(const char *name
, EEL_F
*val
, void *ctx
)
1201 void **parms
= (void **)ctx
;
1202 int score
= ((EEL_Editor
*)parms
[2])->fuzzy_match((const char *)parms
[1], name
);
1203 if (score
> 0) ((suggested_matchlist
*)parms
[0])->add(name
,score
,suggested_matchlist::MODE_VAR
);
1207 void EEL_Editor::get_suggested_token_names(const char *fname
, int chkmask
, suggested_matchlist
*list
)
1210 if (chkmask
& (KEYWORD_MASK_BUILTIN_FUNC
|KEYWORD_MASK_USER_VAR
))
1213 NSEEL_VMCTX vm
= peek_get_VM();
1214 compileContext
*fvm
= vm
&& peek_want_VM_funcs() ? (compileContext
*)vm
: NULL
;
1215 if (chkmask
&KEYWORD_MASK_BUILTIN_FUNC
) for (x
=0;;x
++)
1217 functionType
*p
= nseel_enumFunctions(fvm
,x
);
1219 int score
= fuzzy_match(fname
,p
->name
);
1220 if (score
>0) list
->add(p
->name
,score
);
1222 if (vm
&& (chkmask
&KEYWORD_MASK_USER_VAR
))
1224 const void *parms
[3] = { list
, fname
, this };
1225 NSEEL_VM_enumallvars(vm
, eeledit_varenumfunc
, parms
);
1230 if (chkmask
& KEYWORD_MASK_USER_FUNC
)
1232 ensure_code_func_cache_valid();
1233 for (int x
=0;x
< m_code_func_cache
.GetSize();x
++)
1235 const char *k
= m_code_func_cache
.Get(x
) + 4;
1236 if (WDL_NORMALLY(k
))
1238 int score
= fuzzy_match(fname
,k
);
1239 if (score
> 0) list
->add(k
,score
,suggested_matchlist::MODE_USERFUNC
);
1245 int EEL_Editor::peek_get_token_info(const char *name
, char *sstr
, size_t sstr_sz
, int chkmask
, int ignoreline
)
1247 if (chkmask
&KEYWORD_MASK_USER_FUNC
)
1249 ensure_code_func_cache_valid();
1250 for (int i
= 0; i
< m_code_func_cache
.GetSize(); i
++)
1252 const char *cacheptr
= m_code_func_cache
.Get(i
);
1253 const char *nameptr
= cacheptr
+ sizeof(int);
1254 if (!(m_case_sensitive
? strcmp(nameptr
, name
):stricmp(nameptr
,name
)) &&
1255 *(int *)cacheptr
!= ignoreline
)
1257 const char *parms
= nameptr
+strlen(nameptr
)+1;
1258 const char *trail
= parms
+strlen(parms
)+1;
1259 snprintf(sstr
,sstr_sz
,"%s%s%s%s",nameptr
,parms
,*trail
?" " :"",trail
);
1265 if ((chkmask
& KEYWORD_MASK_USER_VAR
) && name
[0] == '#')
1268 int v
= peek_get_named_string_value(name
+1,tmp
,sizeof(tmp
));
1271 snprintf(sstr
,sstr_sz
,"%s(%d)=\"%s\"",name
,v
,tmp
);
1272 return KEYWORD_MASK_USER_VAR
;
1276 if (chkmask
& (KEYWORD_MASK_BUILTIN_FUNC
|KEYWORD_MASK_USER_VAR
))
1280 NSEEL_VMCTX vm
= peek_want_VM_funcs() ? peek_get_VM() : NULL
;
1281 functionType
*f
= (chkmask
&KEYWORD_MASK_BUILTIN_FUNC
) ? nseel_getFunctionByName((compileContext
*)vm
,name
,NULL
) : NULL
;
1285 snprintf(sstr
,sstr_sz
,"'%s' is a function that requires %d parameters", f
->name
,f
->nParams
&0xff);
1286 rv
= KEYWORD_MASK_BUILTIN_FUNC
;
1288 else if (chkmask
& KEYWORD_MASK_USER_VAR
)
1290 if (!vm
) vm
= peek_get_VM();
1291 EEL_F
*vptr
=NSEEL_VM_getvar(vm
,name
);
1295 rv
= KEYWORD_MASK_USER_VAR
;
1300 if (rv
== KEYWORD_MASK_USER_VAR
)
1303 snprintf(sstr
,sstr_sz
,"%s=%.14f",name
,v
);
1304 WDL_remove_trailing_decimal_zeros(sstr
,2);
1306 if (v
> -1.0 && v
< NSEEL_RAM_ITEMSPERBLOCK
*NSEEL_RAM_BLOCKS
)
1308 const unsigned int w
= (unsigned int) (v
+NSEEL_CLOSEFACTOR
);
1309 EEL_F
*dv
= NSEEL_VM_getramptr_noalloc(vm
,w
,NULL
);
1312 snprintf_append(sstr
,sstr_sz
," [%d]=%.14f",w
,*dv
);
1313 WDL_remove_trailing_decimal_zeros(sstr
,2);
1317 good_len
= strlen(sstr
);
1318 snprintf_append(sstr
,sstr_sz
," [%d]=<0>",w
);
1324 if (peek_get_numbered_string_value(v
,buf
,sizeof(buf
)))
1326 if (good_len
>=0) sstr
[good_len
]=0; // remove [addr]=<uninit> if a string and no ram
1327 snprintf_append(sstr
,sstr_sz
," #=\"%s\"",buf
);
1338 void EEL_Editor::doWatchInfo(int c
)
1340 // determine the word we are on, check its value in the effect
1341 char sstr
[512], buf
[512];
1342 lstrcpyn_safe(sstr
,"Use this on a valid symbol name", sizeof(sstr
));
1343 WDL_FastString
*t
=m_text
.Get(m_curs_y
);
1347 const char *p
=t
->Get();
1348 const int bytex
= WDL_utf8_charpos_to_bytepos(p
,m_curs_x
);
1349 if (bytex
>= 0 && bytex
< t
->GetLength()) curChar
= p
[bytex
];
1350 if (c
!= KEY_F1
&& (m_selecting
||
1357 WDL_FastString code
;
1358 int miny
,maxy
,minx
,maxx
;
1362 if (eel_sh_get_matching_pos_for_pos(&m_text
,minx
=m_curs_x
,miny
=m_curs_y
,&maxx
, &maxy
,NULL
,this))
1373 else if (maxy
< miny
)
1383 minx
++; // skip leading (
1389 getselectregion(minx
,miny
,maxx
,maxy
);
1391 s
= m_text
.Get(miny
);
1392 if (s
) minx
= WDL_utf8_charpos_to_bytepos(s
->Get(),minx
);
1393 s
= m_text
.Get(maxy
);
1394 if (s
) maxx
= WDL_utf8_charpos_to_bytepos(s
->Get(),maxx
);
1400 for (x
= miny
; x
<= maxy
; x
++)
1402 WDL_FastString
*s
=m_text
.Get(x
);
1405 const char *str
=s
->Get();
1407 if (x
== miny
) sx
=wdl_max(minx
,0);
1409 int tmp
=s
->GetLength();
1410 if (sx
> tmp
) sx
=tmp
;
1412 if (x
== maxy
) ex
=wdl_min(maxx
,tmp
);
1415 if (code
.GetLength()) code
.Append("\r\n");
1416 code
.Append(ex
-sx
?str
+sx
:"",ex
-sx
);
1422 if (m_selecting
&& (GetAsyncKeyState(VK_SHIFT
)&0x8000))
1425 NSEEL_CODEHANDLE ch
;
1426 NSEEL_VMCTX vm
= peek_get_VM();
1428 if (vm
&& (ch
= NSEEL_code_compile_ex(vm
,code
.Get(),1,0)))
1430 codeHandleType
*p
= (codeHandleType
*)ch
;
1431 code
.Ellipsize(3,20);
1432 const char *errstr
= "failed writing to";
1436 GetTempPath(sizeof(buf
)-64,buf
);
1437 lstrcatn(buf
,"jsfx-out",sizeof(buf
));
1438 FILE *fp
= fopen(buf
,"wb");
1442 fwrite(p
->code
,1,p
->code_size
,fp
);
1446 snprintf(sstr
,sizeof(sstr
),"Expression '%s' compiled to %d bytes, %s temp/jsfx-out",code
.Get(),p
->code_size
, errstr
);
1447 NSEEL_code_free(ch
);
1451 code
.Ellipsize(3,20);
1452 snprintf(sstr
,sizeof(sstr
),"Expression '%s' could not compile",code
.Get());
1458 WDL_FastString code2
;
1459 code2
.Set("__debug_watch_value = (((((");
1460 code2
.Append(code
.Get());
1461 code2
.Append(")))));");
1465 NSEEL_VMCTX vm
= peek_get_VM();
1469 const char *err
="Invalid context";
1472 NSEEL_CODEHANDLE ch
= NSEEL_code_compile_ex(vm
,code2
.Get(),1,0);
1473 if (!ch
) err
= "Error parsing";
1476 NSEEL_code_execute(ch
);
1477 NSEEL_code_free(ch
);
1478 vptr
= NSEEL_VM_getvar(vm
,"__debug_watch_value");
1479 if (vptr
) v
= *vptr
;
1486 // remove whitespace from code for display
1489 for (x
=0;x
<code
.GetLength();x
++)
1491 if (code
.Get()[x
]>0 && isspace(code
.Get()[x
]))
1493 if (lb
) code
.DeleteSub(x
--,1);
1501 if (lb
&& code
.GetLength()>0) code
.SetLen(code
.GetLength()-1);
1504 code
.Ellipsize(3,20);
1507 snprintf(sstr
,sizeof(sstr
),"Expression '%s' evaluates to %.14f",code
.Get(),v
);
1511 snprintf(sstr
,sizeof(sstr
),"Error evaluating '%s': %s",code
.Get(),err
?err
:"Unknown error");
1515 // compile+execute code within () as debug_watch_value = ( code )
1516 // show value (or err msg)
1518 else if (curChar
>0 && (isalnum(curChar
) || curChar
== '_' || curChar
== '.' || curChar
== '#'))
1520 const int bytex
= WDL_utf8_charpos_to_bytepos(p
,m_curs_x
);
1521 const char *lp
=p
+bytex
;
1522 const char *rp
=lp
+ WDL_utf8_charpos_to_bytepos(lp
,1);
1523 while (lp
>= p
&& *lp
> 0 && (isalnum(*lp
) || *lp
== '_' || (*lp
== '.' && (lp
==p
|| lp
[-1]!='.')))) lp
--;
1524 if (lp
< p
|| *lp
!= '#') lp
++;
1525 while (*rp
&& *rp
> 0 && (isalnum(*rp
) || *rp
== '_' || (*rp
== '.' && rp
[1] != '.'))) rp
++;
1527 if (*lp
== '#' && rp
> lp
+1)
1531 n
.Set(lp
,(int)(rp
-lp
));
1532 int idx
=peek_get_named_string_value(n
.Get(),buf
,sizeof(buf
));
1535 snprintf(sstr
,sizeof(sstr
),"#%s(%d)=%s",n
.Get(),idx
,buf
);
1539 if (*lp
> 0 && (isalpha(*lp
) || *lp
== '_') && rp
> lp
)
1542 n
.Set(lp
,(int)(rp
-lp
));
1546 if (m_suggestion
.GetLength())
1552 int f
= peek_get_token_info(n
.Get(),sstr
,sizeof(sstr
),~0,-1);
1553 if (!f
) snprintf(sstr
,sizeof(sstr
),"'%s' NOT FOUND",n
.Get());
1561 if (m_suggestion
.GetLength())
1563 const char *p
= m_suggestion
.Get();
1565 for (l
= 0; isalnum(p
[l
]) || p
[l
] == '_' || p
[l
] == '.'; l
++);
1566 if (l
>0) t
.Set(m_suggestion
.Get(),l
);
1568 on_help(t
.GetLength() > 2 ? t
.Get() : NULL
,(int)curChar
);
1577 void EEL_Editor::draw_bottom_line()
1579 #define BOLD(x) { attrset(COLOR_BOTTOMLINE|A_BOLD); addstr(x); attrset(COLOR_BOTTOMLINE&~A_BOLD); }
1580 addstr("ma"); BOLD("T"); addstr("ch");
1581 BOLD(" S"); addstr("ave");
1584 addstr(" pee"); BOLD("K");
1586 if (GetTabCount()>1)
1589 BOLD("[], F?"); addstr("=switch ");
1590 BOLD("W"); addstr("=close");
1595 #define CTRL_KEY_DOWN (GetAsyncKeyState(VK_CONTROL)&0x8000)
1596 #define SHIFT_KEY_DOWN (GetAsyncKeyState(VK_SHIFT)&0x8000)
1597 #define ALT_KEY_DOWN (GetAsyncKeyState(VK_MENU)&0x8000)
1599 #ifndef WM_MOUSEWHEEL
1600 #define WM_MOUSEWHEEL 0x20A
1603 static const char *suggestion_help_text
= "(up/down to select, tab to insert)";
1604 static const char *suggestion_help_text2
= "(tab or return to insert)";
1605 static LRESULT WINAPI
suggestionProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1609 static int Scroll_Message
;
1610 if (!Scroll_Message
)
1612 Scroll_Message
= (int)RegisterWindowMessage("MSWHEEL_ROLLMSG");
1613 if (!Scroll_Message
) Scroll_Message
=-1;
1615 if (Scroll_Message
> 0 && uMsg
== (UINT
)Scroll_Message
)
1626 editor
= (EEL_Editor
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
1627 if (editor
) editor
->m_suggestion_hwnd
= NULL
;
1631 editor
= (EEL_Editor
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
1632 if (editor
&& editor
->m_cursesCtx
)
1634 win32CursesCtx
*ctx
= (win32CursesCtx
*)editor
->m_cursesCtx
;
1635 int a
= (int)(short)HIWORD(wParam
);
1636 if (a
< 0) { a
/= 120; if (a
==0) a
--; }
1637 else if (a
>0) { a
/= 120; if (a
==0) a
++; }
1641 GetClientRect(hwnd
,&r
);
1642 const int fonth
= wdl_max(ctx
->m_font_h
,1);
1643 const int maxv
= wdl_max(r
.bottom
/ fonth
- 1,1);
1644 const int ni
= editor
->m_suggestion_list
.get_size();
1647 const int nv
= wdl_clamp(editor
->m_suggestion_hwnd_scroll
- a
, 0, ni
- maxv
);
1648 if (nv
!= editor
->m_suggestion_hwnd_scroll
)
1650 editor
->m_suggestion_hwnd_scroll
= nv
;
1651 editor
->m_suggestion_hwnd_sel
= wdl_clamp(editor
->m_suggestion_hwnd_sel
,nv
,nv
+maxv
-1);
1652 InvalidateRect(hwnd
,NULL
,FALSE
);
1658 case WM_LBUTTONDOWN
:
1660 editor
= (EEL_Editor
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
1663 win32CursesCtx
*ctx
= (win32CursesCtx
*)editor
->m_cursesCtx
;
1664 if (ctx
&& ctx
->m_font_h
)
1666 HWND par
= GetParent(hwnd
);
1667 if (uMsg
== WM_MOUSEMOVE
)
1669 HWND foc
= GetFocus();
1670 if (!foc
|| (foc
!= hwnd
&& foc
!= par
)) return 0;
1674 GetClientRect(hwnd
,&r
);
1675 SetForegroundWindow(par
);
1678 const int max_vis
= r
.bottom
/ ctx
->m_font_h
- 1, sel
= editor
->m_suggestion_hwnd_sel
;
1679 int hit
= GET_Y_LPARAM(lParam
) / ctx
->m_font_h
;
1680 if (hit
>= max_vis
) return 0;
1681 hit
+= editor
->m_suggestion_hwnd_scroll
;
1683 if (uMsg
== WM_LBUTTONDOWN
&& !SHIFT_KEY_DOWN
&& !ALT_KEY_DOWN
&& !CTRL_KEY_DOWN
)
1685 editor
->m_suggestion_hwnd_sel
= hit
;
1686 editor
->onChar('\t');
1688 else if (sel
!= hit
)
1692 if (wdl_abs(pt
.x
- editor
->m_suggestion_hwnd_initmousepos
.x
) +
1693 wdl_abs(pt
.y
- editor
->m_suggestion_hwnd_initmousepos
.y
) < ctx
->m_font_h
*3/4)
1697 editor
->m_suggestion_hwnd_initmousepos
.x
= pt
.x
+ 0x10000000;
1699 editor
->m_suggestion_hwnd_sel
= hit
;
1700 InvalidateRect(hwnd
,NULL
,FALSE
);
1704 const char *p
= editor
->m_suggestion_list
.get(hit
);
1705 if (p
&& editor
->peek_get_token_info(p
,sug
,sizeof(sug
),~0,-1))
1707 editor
->m_suggestion
.Set(sug
);
1708 editor
->draw_top_line();
1709 editor
->setCursor();
1716 editor
= (EEL_Editor
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
1720 if (BeginPaint(hwnd
,&ps
))
1723 GetClientRect(hwnd
,&r
);
1725 win32CursesCtx
*ctx
= (win32CursesCtx
*)editor
->m_cursesCtx
;
1726 HBRUSH br
= CreateSolidBrush(ctx
->colortab
[WDL_CursesEditor::COLOR_TOPLINE
][1]);
1727 FillRect(ps
.hdc
,&r
,br
);
1730 suggested_matchlist
*ml
= &editor
->m_suggestion_list
;
1732 HGDIOBJ oldObj
= SelectObject(ps
.hdc
,ctx
->mOurFont
);
1733 SetBkMode(ps
.hdc
,TRANSPARENT
);
1735 const int fonth
= wdl_max(ctx
->m_font_h
,1);
1736 const int maxv
= wdl_max(r
.bottom
/ fonth
- 1,1);
1737 const int selpos
= wdl_max(editor
->m_suggestion_hwnd_sel
,0);
1740 editor
->m_suggestion_hwnd_scroll
= wdl_clamp(editor
->m_suggestion_hwnd_scroll
, selpos
-maxv
+ 1,selpos
);
1741 if (editor
->m_suggestion_hwnd_scroll
< 0)
1742 editor
->m_suggestion_hwnd_scroll
= 0;
1744 const bool show_scores
= (GetAsyncKeyState(VK_SHIFT
)&0x8000) && (GetAsyncKeyState(VK_CONTROL
)&0x8000) && (GetAsyncKeyState(VK_MENU
)&0x8000);
1746 for (int x
= editor
->m_suggestion_hwnd_scroll
; x
< ml
->get_size() && ypos
<= r
.bottom
-fonth
*2; x
++)
1749 const char *p
= ml
->get(x
,&mode
,&score
);
1750 if (WDL_NORMALLY(p
))
1752 const bool sel
= x
== selpos
;
1753 SetTextColor(ps
.hdc
,ctx
->colortab
[
1754 (mode
== suggested_matchlist::MODE_VAR
? WDL_CursesEditor::COLOR_TOPLINE
:
1755 mode
== suggested_matchlist::MODE_USERFUNC
? WDL_CursesEditor::SYNTAX_FUNC2
:
1756 mode
== suggested_matchlist::MODE_REGVAR
? WDL_CursesEditor::SYNTAX_REGVAR
:
1757 WDL_CursesEditor::SYNTAX_KEYWORD
)
1758 | (sel
? A_BOLD
:0)][0]);
1759 RECT tr
= {4, ypos
, r
.right
-4, ypos
+fonth
};
1760 DrawTextUTF8(ps
.hdc
,p
,-1,&tr
,DT_SINGLELINE
|DT_NOPREFIX
|DT_TOP
|DT_LEFT
);
1764 snprintf(tmp
,sizeof(tmp
),"(%d)",score
);
1765 DrawTextUTF8(ps
.hdc
,tmp
,-1,&tr
,DT_SINGLELINE
|DT_NOPREFIX
|DT_TOP
|DT_RIGHT
);
1771 const COLORREF mix
= ((ctx
->colortab
[WDL_CursesEditor::COLOR_TOPLINE
][1]&0xfefefe)>>1) +
1772 ((ctx
->colortab
[WDL_CursesEditor::COLOR_TOPLINE
][0]&0xfefefe)>>1);
1773 SetTextColor(ps
.hdc
,mix
);
1774 RECT tr
= {4, r
.bottom
-fonth
, r
.right
-4, r
.bottom
};
1775 DrawTextUTF8(ps
.hdc
,
1776 editor
->m_suggestion_hwnd_sel
>= 0 ? suggestion_help_text2
: suggestion_help_text
,
1777 -1,&tr
,DT_SINGLELINE
|DT_NOPREFIX
|DT_TOP
|DT_CENTER
);
1779 SelectObject(ps
.hdc
,oldObj
);
1786 return DefWindowProc(hwnd
,uMsg
,wParam
,lParam
);
1789 void EEL_Editor::open_import_line()
1791 WDL_FastString
*txtstr
=m_text
.Get(m_curs_y
);
1792 const char *txt
=txtstr
?txtstr
->Get():NULL
;
1794 if (txt
&& line_has_openable_file(txt
,WDL_utf8_charpos_to_bytepos(txt
,m_curs_x
),fnp
,sizeof(fnp
)))
1796 WDL_CursesEditor::OpenFileInTab(fnp
);
1800 int EEL_Editor::onChar(int c
)
1802 if ((m_ui_state
== UI_STATE_NORMAL
|| m_ui_state
== UI_STATE_MESSAGE
) &&
1803 (c
== 'K'-'A'+1 || c
== 'S'-'A'+1 || c
== 'R'-'A'+1 || !SHIFT_KEY_DOWN
) && !ALT_KEY_DOWN
) switch (c
)
1806 if (CTRL_KEY_DOWN
) break;
1812 WDL_DestroyCheck
chk(&destroy_check
);
1816 draw_message("Error writing file, changes not saved!");
1819 setCursorIfVisible();
1823 // case 'I': note stupidly we map Ctrl+I to VK_TAB, bleh
1825 if (!SHIFT_KEY_DOWN
) break;
1839 int do_sug
= (g_eel_editor_max_vis_suggestions
> 0 &&
1840 m_ui_state
== UI_STATE_NORMAL
&&
1841 !m_selecting
&& m_top_margin
> 0 &&
1842 (c
== 'L'-'A'+1 || (!CTRL_KEY_DOWN
&& !ALT_KEY_DOWN
))) ? 1 : 0;
1843 bool did_fuzzy
= false;
1849 if (m_suggestion_hwnd
)
1851 // insert if tab or shift+enter or (enter if arrow-navigated)
1852 if ((c
== '\t' && !SHIFT_KEY_DOWN
) ||
1853 (c
== '\r' && (m_suggestion_hwnd_sel
>=0 || SHIFT_KEY_DOWN
)))
1857 const char *ptr
= m_suggestion_list
.get(wdl_max(m_suggestion_hwnd_sel
,0), &sug_mode
);
1858 lstrcpyn_safe(buf
,ptr
?ptr
:"",sizeof(buf
));
1860 DestroyWindow(m_suggestion_hwnd
);
1862 WDL_FastString
*l
=m_text
.Get(m_curs_y
);
1864 WDL_NORMALLY(m_suggestion_tokpos
>=0 && m_suggestion_tokpos
< l
->GetLength()) &&
1865 WDL_NORMALLY(m_suggestion_toklen
>0) &&
1866 WDL_NORMALLY(m_suggestion_tokpos
+m_suggestion_toklen
<= l
->GetLength()))
1869 if (sug_mode
== suggested_matchlist::MODE_USERFUNC
)
1871 const char *tok
= l
->Get() + m_suggestion_tokpos
;
1872 for (int j
= m_suggestion_toklen
- 1; j
>= 0; j
--)
1876 const char *be
= buf
;
1878 while (be
> buf
&& *be
!= '.') be
--;
1881 // buf is foo.bar, lead_sz=3, if j>=3 check for common prefix
1882 const int lead_sz
= (int) (be
-buf
);
1883 if (j
== lead_sz
|| (j
> lead_sz
&& tok
[j
-lead_sz
-1] == '.'))
1885 // prefixes match, ignore
1886 if (!strnicmp(buf
, tok
+ j
- lead_sz
, lead_sz
+1)) continue;
1890 m_suggestion_tokpos
+= j
+1;
1891 m_suggestion_toklen
-= j
+1;
1896 l
->DeleteSub(m_suggestion_tokpos
,m_suggestion_toklen
);
1897 l
->Insert(buf
,m_suggestion_tokpos
);
1898 int pos
= m_suggestion_tokpos
+ strlen(buf
);
1899 if (sug_mode
== suggested_matchlist::MODE_FUNC
|| sug_mode
== suggested_matchlist::MODE_USERFUNC
)
1901 if (pos
>= l
->GetLength() || l
->Get()[pos
] != '(')
1902 l
->Insert("(",pos
++);
1904 m_curs_x
= WDL_utf8_bytepos_to_charpos(l
->Get(),pos
);
1912 if ((c
== KEY_UP
|| c
==KEY_DOWN
|| c
== KEY_NPAGE
|| c
== KEY_PPAGE
) && !SHIFT_KEY_DOWN
)
1914 m_suggestion_hwnd_sel
= wdl_max(m_suggestion_hwnd_sel
,0) +
1915 (c
==KEY_UP
? -1 : c
==KEY_DOWN
? 1 : c
==KEY_NPAGE
? 4 : -4);
1917 if (m_suggestion_hwnd_sel
>= m_suggestion_list
.get_size())
1918 m_suggestion_hwnd_sel
= m_suggestion_list
.get_size()-1;
1919 if (m_suggestion_hwnd_sel
< 0)
1920 m_suggestion_hwnd_sel
=0;
1922 InvalidateRect(m_suggestion_hwnd
,NULL
,FALSE
);
1924 const char *p
= m_suggestion_list
.get(m_suggestion_hwnd_sel
);
1925 if (p
) peek_get_token_info(p
,sug
,sizeof(sug
),~0,m_curs_y
);
1934 (c
>=KEY_DOWN
&& c
<= KEY_F12
&& c
!=KEY_DC
)) do_sug
= 2; // no fuzzy window
1935 else if (c
=='\b' && !m_suggestion_hwnd
) do_sug
=2; // backspace will update but won't show suggestions
1938 rv
= WDL_CursesEditor::onChar(c
);
1940 if (!CURSES_INSTANCE
)
1946 WDL_FastString
*l
=m_text
.Get(m_curs_y
);
1949 const int MAX_TOK
=512;
1953 } token_list
[MAX_TOK
];
1955 const char *cursor
= l
->Get() + WDL_utf8_charpos_to_bytepos(l
->Get(),m_curs_x
);
1958 const char *p
= l
->Get(), *endp
= p
+ l
->GetLength();
1959 int state
= m_suggestion_curline_comment_state
, toklen
= 0, bcnt
= 0, pcnt
= 0;
1961 // if no parens/brackets are open, use a peekable token that starts at cursor
1962 while ((tok
=sh_tokenize(&p
,endp
,&toklen
,&state
)) && cursor
> tok
-(!pcnt
&& !bcnt
&& (tok
[0] < 0 || tok
[0] == '_' || isalpha(tok
[0]) || tok
[0] == '#')))
1966 if (WDL_NOT_NORMALLY(ntok
>= MAX_TOK
))
1968 memmove(token_list
,token_list
+1,sizeof(token_list
) - sizeof(token_list
[0]));
1973 case '[': bcnt
++; break;
1974 case ']': bcnt
--; break;
1975 case '(': pcnt
++; break;
1976 case ')': pcnt
--; break;
1978 token_list
[ntok
].tok
= tok
;
1979 token_list
[ntok
].toklen
= toklen
;
1989 const char *tok
= token_list
[t
].tok
;
1990 int toklen
= token_list
[t
].toklen
;
1991 const int lc
= tok
[0], ac
= lc
==')' ? '(' : lc
==']' ? '[' : 0;
1997 const int c
= token_list
[t
].tok
[0];
1999 else if (c
== ac
&& !--cnt
) break;
2003 const int c
= token_list
[t
-1].tok
[0];
2004 if (c
!= ',' && c
!= ')' && c
!= ']') t
--;
2010 if (tok
[0] == ',') comma_cnt
++;
2011 else if ((tok
[0] < 0 || tok
[0] == '_' || isalpha(tok
[0]) || tok
[0] == '#') &&
2012 (cursor
<= tok
+ toklen
|| (t
< ntok
-1 && token_list
[t
+1].tok
[0] == '(')))
2014 // if cursor is within or at end of token, or is a function (followed by open-paren)
2016 lstrcpyn_safe(buf
,tok
,wdl_min(toklen
+1, sizeof(buf
)));
2017 if (peek_get_token_info(buf
,sug
,sizeof(sug
),~0,m_curs_y
))
2021 // hide previous parameters from sug's parameter fields so we know which parameters
2022 // we are (hopefully on)
2024 while (*pstart
&& *pstart
!= '(') pstart
++;
2025 if (*pstart
== '(') pstart
++;
2028 while (comma_pos
< comma_cnt
)
2030 while (*p
== ' ') p
++;
2031 while (*p
&& *p
!= ',' && *p
!= ')') p
++;
2032 if (*p
== ')') break;
2037 if (*p
&& *p
!= ')')
2040 lstrcpyn_safe(buf
,p
,sizeof(buf
));
2041 snprintf_append(sug
,sizeof(sug
), "... %s",buf
);
2047 // only use token up to cursor for suggestions
2048 if (cursor
>= tok
&& cursor
<= tok
+toklen
)
2050 toklen
= (int) (cursor
-tok
);
2051 lstrcpyn_safe(buf
,tok
,wdl_min(toklen
+1, sizeof(buf
)));
2054 if (c
== '\b' && cursor
== tok
)
2057 else if (do_sug
!= 2 && t
== ntok
-1 && toklen
>= 3 && cursor
<= tok
+ toklen
)
2059 m_suggestion_list
.clear();
2060 get_suggested_token_names(buf
,~0,&m_suggestion_list
);
2062 win32CursesCtx
*ctx
= (win32CursesCtx
*)m_cursesCtx
;
2063 if (m_suggestion_list
.get_size()>0 &&
2064 WDL_NORMALLY(ctx
->m_hwnd
) && WDL_NORMALLY(ctx
->m_font_w
) && WDL_NORMALLY(ctx
->m_font_h
))
2066 m_suggestion_toklen
= toklen
;
2067 m_suggestion_tokpos
= (int)(tok
-l
->Get());
2068 m_suggestion_hwnd_sel
= -1;
2069 if (!m_suggestion_hwnd
)
2072 static HINSTANCE last_inst
;
2073 const char *classname
= "eel_edit_predicto";
2074 HINSTANCE inst
= (HINSTANCE
)GetWindowLongPtr(ctx
->m_hwnd
,GWLP_HINSTANCE
);
2075 if (inst
!= last_inst
)
2078 WNDCLASS wc
={CS_DBLCLKS
,suggestionProc
,};
2079 wc
.lpszClassName
=classname
;
2081 wc
.hCursor
=LoadCursor(NULL
,IDC_ARROW
);
2084 m_suggestion_hwnd
= CreateWindowEx(0,classname
,"", WS_CHILD
, 0,0,0,0, ctx
->m_hwnd
, NULL
, inst
, NULL
);
2086 m_suggestion_hwnd
= CreateDialogParam(NULL
,NULL
,ctx
->m_hwnd
, suggestionProc
, 0);
2088 if (m_suggestion_hwnd
) SetWindowLongPtr(m_suggestion_hwnd
,GWLP_USERDATA
,(LPARAM
)this);
2090 if (m_suggestion_hwnd
)
2092 const int fontw
= ctx
->m_font_w
, fonth
= ctx
->m_font_h
;
2093 int xpos
= (WDL_utf8_bytepos_to_charpos(l
->Get(),m_suggestion_tokpos
) - m_offs_x
) * fontw
;
2095 GetClientRect(ctx
->m_hwnd
,&par_cr
);
2097 int use_w
= (int)strlen(suggestion_help_text
);
2098 int use_h
= (wdl_min(g_eel_editor_max_vis_suggestions
,m_suggestion_list
.get_size()) + 1)*fonth
;
2099 for (int x
= 0; x
< m_suggestion_list
.get_size(); x
++)
2101 const char *p
= m_suggestion_list
.get(x
);
2102 if (WDL_NORMALLY(p
!=NULL
))
2104 const int l
= (int) strlen(p
);
2105 if (l
> use_w
) use_w
=l
;
2109 use_w
= 8 + use_w
* fontw
;
2110 if (use_w
> par_cr
.right
- xpos
)
2112 xpos
= wdl_max(par_cr
.right
- use_w
,0);
2113 use_w
= par_cr
.right
- xpos
;
2116 const int cursor_line_y
= ctx
->m_cursor_y
* fonth
;
2117 int ypos
= cursor_line_y
+ fonth
;
2118 if (ypos
+ use_h
> par_cr
.bottom
)
2120 if (cursor_line_y
-fonth
> par_cr
.bottom
- ypos
)
2122 // more room at the top, but enough?
2123 ypos
= cursor_line_y
- use_h
;
2127 use_h
= cursor_line_y
-fonth
;
2131 use_h
= par_cr
.bottom
- ypos
;
2134 SetWindowPos(m_suggestion_hwnd
,NULL
,xpos
,ypos
,use_w
,use_h
, SWP_NOZORDER
|SWP_NOACTIVATE
);
2135 InvalidateRect(m_suggestion_hwnd
,NULL
,FALSE
);
2136 ShowWindow(m_suggestion_hwnd
,SW_SHOWNA
);
2138 GetCursorPos(&m_suggestion_hwnd_initmousepos
);
2141 const char *p
= m_suggestion_list
.get(wdl_max(m_suggestion_hwnd_sel
,0));
2142 if (p
&& peek_get_token_info(p
,sug
,sizeof(sug
),~0,m_curs_y
)) break;
2149 if (!did_fuzzy
&& m_suggestion_hwnd
) DestroyWindow(m_suggestion_hwnd
);
2152 if (strcmp(sug
,m_suggestion
.Get()) && m_ui_state
== UI_STATE_NORMAL
)
2154 m_suggestion
.Set(sug
);
2157 m_suggestion_x
=m_curs_x
;
2158 m_suggestion_y
=m_curs_y
;
2163 if (!sug
[0] && m_suggestion_y
>=0 && m_ui_state
== UI_STATE_NORMAL
)
2165 m_suggestion_x
=m_suggestion_y
=-1;
2166 m_suggestion
.Set("");
2167 if (m_top_margin
>0) draw_top_line();
2175 void EEL_Editor::draw_top_line()
2177 if (m_curs_x
>= m_suggestion_x
&& m_curs_y
== m_suggestion_y
&& m_suggestion
.GetLength() && m_ui_state
== UI_STATE_NORMAL
)
2179 const char *p
=m_suggestion
.Get();
2181 if (WDL_utf8_get_charlen(m_suggestion
.Get()) > COLS
)
2183 int l
= WDL_utf8_charpos_to_bytepos(m_suggestion
.Get(),COLS
-4);
2184 if (l
> sizeof(str
)-6) l
= sizeof(str
)-6;
2185 lstrcpyn(str
, m_suggestion
.Get(), l
+1);
2190 attrset(COLOR_TOPLINE
|A_BOLD
);
2191 bkgdset(COLOR_TOPLINE
);
2200 m_suggestion_x
=m_suggestion_y
=-1;
2201 if (m_suggestion
.GetLength()) m_suggestion
.Set("");
2202 WDL_CursesEditor::draw_top_line();
2207 void EEL_Editor::onRightClick(HWND hwnd
)
2209 WDL_LogicalSortStringKeyedArray
<int> flist(m_case_sensitive
);
2211 if (!(GetAsyncKeyState(VK_CONTROL
)&0x8000))
2213 m_code_func_cache_lines
= -1; // invalidate cache
2214 ensure_code_func_cache_valid();
2215 for (i
= 0; i
< m_code_func_cache
.GetSize(); i
++)
2217 const char *p
= m_code_func_cache
.Get(i
);
2218 const int line
= *(int *)p
;
2221 const char *q
= p
+strlen(p
)+1;
2223 snprintf(buf
,sizeof(buf
),"%s%s",p
,q
);
2224 flist
.AddUnsorted(buf
,line
);
2229 get_extra_filepos_names(&flist
,0);
2231 if (flist
.GetSize()>1)
2234 if (m_case_sensitive
) flist
.Resort(WDL_LogicalSortStringKeyedArray
<int>::cmpistr
);
2237 get_extra_filepos_names(&flist
,1);
2239 if (flist
.GetSize())
2241 HMENU hm
=CreatePopupMenu();
2243 for (i
=0; i
< flist
.GetSize(); ++i
)
2245 const char* fname
=NULL
;
2246 int line
=flist
.Enumerate(i
, &fname
);
2247 InsertMenu(hm
, pos
++, MF_STRING
|MF_BYPOSITION
, line
+1, fname
);
2251 int ret
=TrackPopupMenu(hm
, TPM_NONOTIFY
|TPM_RETURNCMD
, p
.x
, p
.y
, 0, hwnd
, NULL
);
2255 GoToLine(ret
-1,true);
2264 void EEL_Editor::ensure_code_func_cache_valid()
2266 const char *prefix
= m_function_prefix
;
2267 if (!prefix
|| !*prefix
) return;
2269 const DWORD now
= GetTickCount();
2270 if (m_text
.GetSize()==m_code_func_cache_lines
&& (now
-m_code_func_cache_time
)<5000) return;
2272 m_code_func_cache_lines
= m_text
.GetSize();
2273 m_code_func_cache_time
= now
;
2275 m_code_func_cache
.Empty(true,free
);
2277 const int prefix_len
= (int) strlen(m_function_prefix
);
2278 for (int i
=0; i
< m_text
.GetSize(); ++i
)
2280 WDL_FastString
* s
=m_text
.Get(i
);
2281 if (WDL_NORMALLY(s
))
2283 const char *p
= s
->Get();
2286 if (m_case_sensitive
? !strncmp(p
,prefix
,prefix_len
) : !strnicmp(p
,prefix
,prefix_len
))
2289 while (*p
== ' ') p
++;
2290 if ((*p
>= 'a' && *p
<= 'z') || (*p
>= 'A' && *p
<= 'Z') || *p
== '_')
2292 const char *q
= p
+1;
2293 while ((*q
>= '0' && *q
<= '9') ||
2294 (*q
>= 'a' && *q
<= 'z') ||
2295 (*q
>= 'A' && *q
<= 'Z') ||
2297 *q
== '_' || *q
== '.') q
++;
2299 const char *endp
= q
;
2300 while (*q
== ' ') q
++;
2303 const char *endq
= q
;
2304 while (*endq
&& *endq
!= ')') endq
++;
2306 const char *r
= endq
;
2307 while (*r
== ' ') r
++;
2309 const int p_len
= (int) (endp
- p
);
2310 const int q_len
= (int) (endq
- q
);
2311 const int r_len
= (int) strlen(r
);
2314 char *v
= (char *)malloc(sizeof(int) + p_len
+ q_len
+ r_len
+ 3), *wr
= v
;
2315 if (WDL_NORMALLY(v
))
2317 *(int *)wr
= i
; wr
+= sizeof(int);
2318 lstrcpyn_safe(wr
,p
,p_len
+1); wr
+= p_len
+1;
2319 lstrcpyn_safe(wr
,q
,q_len
+1); wr
+= q_len
+1;
2320 lstrcpyn_safe(wr
,r
,r_len
+1); wr
+= r_len
+1;
2322 m_code_func_cache
.Add(v
);
2324 p
= r
; // continue parsing after parentheses
2335 #ifdef WDL_IS_FAKE_CURSES
2337 LRESULT
EEL_Editor::onMouseMessage(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2341 case WM_LBUTTONDBLCLK
:
2342 if (m_suggestion_hwnd
) DestroyWindow(m_suggestion_hwnd
);
2343 if (CURSES_INSTANCE
&& CURSES_INSTANCE
->m_font_w
&& CURSES_INSTANCE
->m_font_h
)
2345 const int y
= ((short)HIWORD(lParam
)) / CURSES_INSTANCE
->m_font_h
- m_top_margin
;
2346 //const int x = ((short)LOWORD(lParam)) / CURSES_INSTANCE->m_font_w + m_offs_x;
2347 WDL_FastString
*fs
=m_text
.Get(y
+ m_paneoffs_y
[m_curpane
]);
2350 if (!strncmp(fs
->Get(),"import",6) && fs
->Get()[6]>0 && isspace(fs
->Get()[6]))
2357 // ctrl+doubleclicking a function goes to it
2358 if (!(g_eel_editor_flags
&1) != !CTRL_KEY_DOWN
)
2360 WDL_FastString
*l
=m_text
.Get(m_curs_y
);
2363 const char *p
= l
->Get(), *endp
= p
+ l
->GetLength(), *cursor
= p
+ WDL_utf8_charpos_to_bytepos(p
,m_curs_x
);
2364 int state
= 0, toklen
= 0;
2366 while ((tok
=sh_tokenize(&p
,endp
,&toklen
,&state
)) && cursor
> tok
+toklen
);
2368 if (tok
&& cursor
<= tok
+toklen
)
2370 ensure_code_func_cache_valid();
2374 for (int i
= 0; i
< m_code_func_cache
.GetSize(); i
++)
2376 const char *p
= m_code_func_cache
.Get(i
);
2377 int line
= *(int *)p
;
2379 if (line
!= m_curs_y
&& strlen(p
) == toklen
&& (m_case_sensitive
? !strncmp(p
,tok
,toklen
) : !strnicmp(p
,tok
,toklen
)))
2381 GoToLine(line
,true);
2386 // try removing any foo. prefixes
2387 while (toklen
> 0 && *tok
!= '.') { tok
++; toklen
--; }
2396 case WM_LBUTTONDOWN
:
2397 case WM_RBUTTONDOWN
:
2398 if (m_suggestion_hwnd
) DestroyWindow(m_suggestion_hwnd
);
2402 return WDL_CursesEditor::onMouseMessage(hwnd
,uMsg
,wParam
,lParam
);