1 #include "isocline/src/isocline.c"
7 #if !defined LUA_VERSION_NUM || (LUA_VERSION_NUM <= 501)
8 #define lua_rawlen lua_objlen
12 #endif /* LUA_VERSION_NUM */
13 void ic_history_add(char const *entry
);
14 char *ic_readline(char const *prompt_text
);
15 #define lua_initreadline(L) lua_initline(L)
16 #define lua_readline(L, b, p) (((b) = ic_readline(p)) != NULL)
17 #if defined(LUA_VERSION_NUM) && (LUA_VERSION_NUM > 502)
18 #define lua_saveline(L, line) ic_history_add(line)
19 #else /* !LUA_VERSION_NUM */
20 #define lua_saveline(L, idx) \
21 if (lua_rawlen(L, idx) > 0) \
22 ic_history_add(lua_tostring(L, idx));
23 #endif /* LUA_VERSION_NUM */
24 #define lua_freeline(L, b) free(b)
25 static char const *keywords
[] = {
26 "and", "break", "do", "else", "elseif", "end",
27 "false", "for", "function", "goto", "if", "in",
28 "local", "nil", "not", "or", "repeat", "return",
29 "then", "true", "until", "while", NULL
};
30 static int haschr(char const *str
, int chr
)
34 if (*str
== chr
) { return 1; }
38 static int is_id(char const *str
)
41 if ((c
< 'A' || c
> 'Z') && (c
< 'a' || c
> 'z') && c
!= '_') { return 0; }
42 for (++str
, c
= (int)*str
; c
; ++str
, c
= (int)*str
)
44 if ((c
< '0' || c
> '9') && (c
< 'A' || c
> 'Z') && (c
< 'a' || c
> 'z') && c
!= '_') { return 0; }
48 static int match(char const *str
, char const *sub
, size_t len
)
50 char const *const suf
= sub
+ len
;
51 if (str
[0] == '_' && str
[1] == '_')
53 if (sub
[0] != '_') { return 0; }
54 if (sub
[1] == '_' && str
[2] && sub
[2] && str
[2] != sub
[2]) { return 0; }
56 if (str
[0] && sub
[0] && str
[0] != sub
[0]) { return 0; }
57 for (; *str
&& sub
< suf
; ++sub
)
63 if (*++str
== 0) { return 0; }
75 static void completion_exec(ic_completion_env_t
*cenv
, char const *buffer
, char const *suffix
, char const *sep
)
77 char const *subfix
= NULL
;
78 lua_State
*L
= ic_completion_arg(cenv
);
79 if (suffix
== NULL
) { suffix
= buffer
; }
80 for (char const *s
= suffix
; *s
; ++s
)
82 if (*s
== '.' || *s
== ':' || *s
== '[')
91 lua_Integer integer
= 0;
92 if (subfix
[-1] == ']')
95 if (*suffix
== '\'' || *suffix
== '\"')
100 else if (isdigit(*suffix
))
105 size_t key_len
= (size_t)(subfix
- suffix
);
106 if (key_len
>= fix
) { key_len
-= fix
; }
107 lua_pushlstring(L
, suffix
, key_len
);
110 integer
= lua_tointeger(L
, -1);
113 lua_pushinteger(L
, integer
);
114 lua_remove(L
, -2); // table, string, integer
119 lua_gettable(L
, -2); // table, key
120 if (*suffix
!= '.' && *suffix
!= ':' && *suffix
!= '[')
122 if (lua_type(L
, -1) == LUA_TTABLE
)
124 completion_exec(cenv
, buffer
, suffix
, sep
);
126 if (lua_getmetatable(L
, -1))
128 lua_getfield(L
, -1, "__index");
129 lua_remove(L
, -2); // table, value, meta, __index
130 if (lua_type(L
, -1) == LUA_TFUNCTION
)
132 if (strncmp(suffix
, "__index", sizeof("__index") - 1) == 0)
134 int c
= (int)suffix
[sizeof("__index") - 1];
135 if (c
== '.' || c
== ':' || c
== '[')
137 suffix
+= sizeof("__index");
138 sep
+= sizeof("__index");
141 lua_pushvalue(L
, -2);
142 lua_pushstring(L
, "__index");
143 lua_pcall(L
, 2, 1, 0);
145 lua_remove(L
, -2); // table, value, __index
146 if (lua_type(L
, -1) == LUA_TTABLE
)
148 completion_exec(cenv
, buffer
, suffix
, sep
);
157 lua_Alloc alloc
= lua_getallocf(L
, &ud
);
158 size_t prefix_len
= (size_t)((sep
? sep
: suffix
) - buffer
);
159 char *prefix
= (char *)alloc(ud
, NULL
, LUA_TSTRING
, prefix_len
+ 1);
160 memcpy(prefix
, buffer
, prefix_len
);
161 prefix
[prefix_len
] = 0;
163 if (*suffix
== '\'' || *suffix
== '\"')
167 size_t suffix_len
= strlen(suffix
);
170 if (suffix
[suffix_len
- 1] == ']')
174 if (suffix
[suffix_len
- 1] == '\'' || suffix
[suffix_len
- 1] == '\"')
176 --suffix_len
; // 1' 1"
180 for (lua_pushnil(L
); lua_next(L
, -2); lua_pop(L
, 1))
182 if (lua_type(L
, -2) == LUA_TSTRING
)
184 int type
= lua_type(L
, -1);
185 if (sep
&& *sep
== ':' && type
!= LUA_TFUNCTION
) { continue; }
186 char const *key
= lua_tostring(L
, -2);
187 if (match(key
, suffix
, suffix_len
))
194 if ((sep
&& *sep
== '[') || (!is_id(key
) && sep
&& *sep
))
196 lua_pushfstring(L
, "%s%s", key
, key
);
197 char const *k2
= lua_tostring(L
, -1);
198 char const *k1
= key
;
201 if (*k1
== '\"') { *s
.o
++ = '\\'; }
205 lua_pushfstring(L
, "%s[\"%s\"", prefix
, k2
);
206 lua_remove(L
, -2); // key, value, k2, replacement
208 else if (sep
&& *sep
)
210 lua_pushlstring(L
, sep
, (size_t)(suffix
- sep
));
211 if (type
== LUA_TFUNCTION
)
213 lua_pushfstring(L
, "%s%s%s(", prefix
, lua_tostring(L
, -1), key
);
217 lua_pushfstring(L
, "%s%s%s", prefix
, lua_tostring(L
, -1), key
);
219 lua_remove(L
, -2); // key, value, sep, replacement
223 if (type
== LUA_TFUNCTION
)
225 lua_pushfstring(L
, "%s%s(", prefix
, key
);
229 lua_pushfstring(L
, "%s%s", prefix
, key
);
232 ic_add_completion_ex(cenv
, lua_tostring(L
, -1), key
, NULL
);
236 else if (lua_type(L
, -2) == LUA_TNUMBER
)
238 #if defined(LUA_VERSION_NUM) && (LUA_VERSION_NUM > 502)
239 lua_pushfstring(L
, "%I", lua_tointeger(L
, -2));
240 #else /* !LUA_VERSION_NUM */
241 lua_pushfstring(L
, "%d", lua_tointeger(L
, -2));
242 #endif /* LUA_VERSION_NUM */
243 char const *key
= lua_tostring(L
, -1);
244 if (match(key
, suffix
, suffix_len
))
246 lua_pushfstring(L
, "%s[%s", prefix
, key
);
247 ic_add_completion_ex(cenv
, lua_tostring(L
, -1), key
, NULL
);
256 for (char const **s
= keywords
; *s
; ++s
)
258 if (match(*s
, suffix
, suffix_len
))
260 ic_add_completion(cenv
, *s
);
265 alloc(ud
, prefix
, prefix_len
+ 1, 0);
268 static void completion_func(ic_completion_env_t
*cenv
, char const *buffer
)
270 lua_State
*L
= ic_completion_arg(cenv
);
271 #if defined(LUA_RIDX_GLOBALS)
272 lua_rawgeti(L
, LUA_REGISTRYINDEX
, LUA_RIDX_GLOBALS
);
273 #else /* !LUA_RIDX_GLOBALS */
274 lua_pushvalue(L
, LUA_GLOBALSINDEX
);
275 #endif /* LUA_RIDX_GLOBALS */
276 completion_exec(cenv
, buffer
, buffer
, 0);
280 static bool is_block(char const *s
, long len
)
282 return len
> 0 && (isalnum(*s
) || haschr("_.:[\'\"\\]", *s
));
285 static void completer(ic_completion_env_t
*cenv
, char const *buffer
)
287 ic_complete_word(cenv
, buffer
, completion_func
, is_block
);
288 if (strchr(buffer
, '\'') || strchr(buffer
, '\"'))
290 ic_complete_filename(cenv
, buffer
, 0, NULL
, NULL
);
294 static long match_string(void const *s_
, long i
)
297 char const *s
= (char const *)s_
+ i
;
298 if (*s
== '\'') { quote
= '\''; }
299 if (*s
== '\"') { quote
= '\"'; }
302 for (++s
; *s
&& *s
!= quote
; ++s
)
304 if (s
[0] == '\\' && s
[1] == quote
) { ++s
; }
306 if (*s
== quote
) { ++s
; }
308 return (long)(s
- (char const *)s_
- i
);
311 static long match_number(void const *s_
, long i
)
313 char const *s
= (char const *)s_
+ i
;
314 if (i
&& (isalnum(s
[-1]) || s
[-1] == '_')) { return 0; }
315 if (s
[0] == '0' && (s
[1] == 'X' || s
[1] == 'x'))
318 for (; isxdigit(*s
); ++s
) {}
319 if (*s
== '.') { ++s
; }
320 for (; isxdigit(*s
); ++s
) {}
321 if (*s
== 'P' || *s
== 'p')
324 if (*s
== '+' || *s
== '-') { ++s
; }
325 if (!isdigit(*s
)) { return 0; }
327 for (; isdigit(*s
); ++s
) {}
331 for (; isdigit(*s
); ++s
) {}
332 if (*s
== '.') { ++s
; }
333 for (; isdigit(*s
); ++s
) {}
334 if (*s
== 'E' || *s
== 'e')
337 if (*s
== '+' || *s
== '-') { ++s
; }
338 if (!isdigit(*s
)) { return 0; }
340 for (; isdigit(*s
); ++s
) {}
342 return (long)(s
- (char const *)s_
- i
);
345 static void highlighter(ic_highlight_env_t
*henv
, char const *input
, void *arg
)
347 long len
= (long)strlen(input
);
348 for (long i
= 0; i
< len
;)
350 long tlen
; // token length
351 if (ic_starts_with(input
+ i
, "--")) // line comment
353 for (tlen
= 2; i
+ tlen
< len
&& input
[i
+ tlen
] != '\n'; ++tlen
) {}
354 ic_highlight(henv
, i
, tlen
, "comment");
357 else if ((tlen
= ic_match_any_token(input
, i
, ic_char_is_idletter
, keywords
)) > 0)
359 ic_highlight(henv
, i
, tlen
, "keyword");
362 else if ((tlen
= match_string(input
, i
)) > 0)
364 ic_highlight(henv
, i
, tlen
, "string");
367 else if ((tlen
= match_number(input
, i
)) > 0)
369 ic_highlight(henv
, i
, tlen
, "number");
377 static void joinpath(void *buff
, char const *path
, char const *name
)
379 char *p
= (char *)buff
;
380 for (; *path
; ++path
)
382 *p
++ = (char)(*path
!= '\\' ? *path
: '/');
384 if (p
> (char *)buff
&& p
[-1] != '/') { *p
++ = '/'; }
385 for (; *name
; ++name
)
387 *p
++ = (char)(*name
!= '\\' ? *name
: '/');
392 static void loadhistory(char const *name
)
399 char const *home
= getenv("HOME");
402 home
= getenv("USERPROFILE");
406 joinpath(path
, home
, name
);
407 ic_set_history(path
, -1);
411 static void lua_initline(lua_State
*L
)
413 ic_style_def("ic-bracematch", "teal");
414 ic_set_default_completer(completer
, L
);
415 ic_set_default_highlighter(highlighter
, L
);
416 ic_set_prompt_marker("", "");
417 ic_enable_brace_insertion(0);
418 loadhistory(".lua_history");