fix emscripten builds on windows
[liba.git] / lua / isocline.c
blob148788060eb253726685689185f4dd926204ed6c
1 #include "isocline/src/isocline.c"
2 #include <ctype.h>
3 #if !defined lua_h
4 #include "lua.h"
5 #endif /* lua_h */
7 #if !defined LUA_VERSION_NUM || (LUA_VERSION_NUM <= 501)
8 #define lua_rawlen lua_objlen
9 #undef lua_readline
10 #undef lua_saveline
11 #undef lua_freeline
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)
32 for (; *str; ++str)
34 if (*str == chr) { return 1; }
36 return 0;
38 static int is_id(char const *str)
40 int c = (int)*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; }
46 return 1;
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)
59 if (*str != *sub)
61 for (;;)
63 if (*++str == 0) { return 0; }
64 if (*str == *sub)
66 ++str;
67 break;
71 else { ++str; }
73 return sub == suf;
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 == '[')
84 subfix = s;
85 break;
88 if (subfix > suffix)
90 unsigned int fix = 0;
91 lua_Integer integer = 0;
92 if (subfix[-1] == ']')
94 ++fix; // 1]
95 if (*suffix == '\'' || *suffix == '\"')
97 ++fix; // '1' "1"
98 ++suffix;
100 else if (isdigit(*suffix))
102 integer = 1;
105 size_t key_len = (size_t)(subfix - suffix);
106 if (key_len >= fix) { key_len -= fix; }
107 lua_pushlstring(L, suffix, key_len);
108 if (integer)
110 integer = lua_tointeger(L, -1);
111 if (integer)
113 lua_pushinteger(L, integer);
114 lua_remove(L, -2); // table, string, integer
117 sep = subfix;
118 suffix = subfix + 1;
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);
152 lua_pop(L, 1);
153 return;
156 void *ud;
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 == '\"')
165 ++suffix; // '1 "1
167 size_t suffix_len = strlen(suffix);
168 if (suffix_len)
170 if (suffix[suffix_len - 1] == ']')
172 --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))
189 union
191 char const *p;
192 char *o;
193 } s;
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;
199 for (s.p = k2; *k1;)
201 if (*k1 == '\"') { *s.o++ = '\\'; }
202 *s.o++ = *k1++;
204 *s.o = 0;
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);
215 else
217 lua_pushfstring(L, "%s%s%s", prefix, lua_tostring(L, -1), key);
219 lua_remove(L, -2); // key, value, sep, replacement
221 else
223 if (type == LUA_TFUNCTION)
225 lua_pushfstring(L, "%s%s(", prefix, key);
227 else
229 lua_pushfstring(L, "%s%s", prefix, key);
232 ic_add_completion_ex(cenv, lua_tostring(L, -1), key, NULL);
233 lua_pop(L, 1);
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);
248 lua_pop(L, 1);
250 lua_pop(L, 1);
254 if (!sep)
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);
277 lua_pop(L, 1);
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)
296 int quote = 0;
297 char const *s = (char const *)s_ + i;
298 if (*s == '\'') { quote = '\''; }
299 if (*s == '\"') { quote = '\"'; }
300 if (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'))
317 s += 2;
318 for (; isxdigit(*s); ++s) {}
319 if (*s == '.') { ++s; }
320 for (; isxdigit(*s); ++s) {}
321 if (*s == 'P' || *s == 'p')
323 ++s;
324 if (*s == '+' || *s == '-') { ++s; }
325 if (!isdigit(*s)) { return 0; }
327 for (; isdigit(*s); ++s) {}
329 else
331 for (; isdigit(*s); ++s) {}
332 if (*s == '.') { ++s; }
333 for (; isdigit(*s); ++s) {}
334 if (*s == 'E' || *s == 'e')
336 ++s;
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");
355 i += tlen;
357 else if ((tlen = ic_match_any_token(input, i, ic_char_is_idletter, keywords)) > 0)
359 ic_highlight(henv, i, tlen, "keyword");
360 i += tlen;
362 else if ((tlen = match_string(input, i)) > 0)
364 ic_highlight(henv, i, tlen, "string");
365 i += tlen;
367 else if ((tlen = match_number(input, i)) > 0)
369 ic_highlight(henv, i, tlen, "number");
370 i += tlen;
372 else { ++i; }
374 (void)arg;
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 : '/');
389 *p = 0;
392 static void loadhistory(char const *name)
394 #if defined(_WIN32)
395 char path[260];
396 #else /* _WIN32 */
397 char path[4096];
398 #endif /* _WIN32 */
399 char const *home = getenv("HOME");
400 if (!home)
402 home = getenv("USERPROFILE");
404 if (home)
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");