diff --git a/port/linux/.vscode/launch.json b/port/linux/.vscode/launch.json index 9aa4aed9d..57e7b4c51 100644 --- a/port/linux/.vscode/launch.json +++ b/port/linux/.vscode/launch.json @@ -11,40 +11,9 @@ "program": "${workspaceFolder}/build/test/pikascript_test", // "program": "${workspaceFolder}/build/boot/demo06-pikamain/pikascript_demo06-pikamain", "args": [ - // "--gtest_filter=vm.keyword_2" - // "--gtest_filter=compiler.find_break_point" - // "--gtest_filter=pikaMain.REPL_pdb_set_break" - // "--gtest_filter=vm.subsrc_import", - // "--gtest_filter=vm.run_file_subsrc" - // "--gtest_filter=vm.run_file" - // "--gtest_filter=stddata.encode_decode" - // "--gtest_filter=packtool.packfiles_txt" - // "--gtest_filter=cmodule.class_attr_obj" - // "--gtest_filter=except.try_import_except" - // "--gtest_filter=vm.test_cmodule_import_as" - // "--gtest_filter=vm.subsrc_import" - // "--gtest_filter=event.event_thread3" - // "--gtest_filter=parser.semicolon*" - // "--gtest_filter=time*" - // "--gtest_filter=flashdb.tsdb1" - // "--gtest_filter=flashdb.base" - // "--gtest_filter=jrpc.server" - // "--gtest_filter=jrpc.client" - // "--gtest_filter=jrpc.BlockingRequestBetweenTwoJRPC" - // "--gtest_filter=jrpc.cmd" - // "--gtest_filter=jrpc.exec_get_val" - // "--gtest_filter=jrpc.exec_get_val" - // "--gtest_filter=thread.issue1" - // "--gtest_filter=except.isinstance" - // "--gtest_filter=builtin.isinstance" - // "--gtest_filter=bytes.bytes_split" - // "--gtest_filter=except.dict" - // "--gtest_filter=except.*" - // "--gtest_filter=except.try1" - // "--gtest_filter=except.for_loop" - // "--gtest_filter=builtin.init_raise" - // "--gtest_filter=builtin.strformat" - // "--gtest_filter=except.typeerr" + // "--gtest_filter=module.REPL_big_script" + // "--gtest_filter=parser.input_issue1" + "--gtest_filter=except.raise_type" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/port/linux/test/module-test.cpp b/port/linux/test/module-test.cpp index be5b95d9e..513d5ba1f 100644 --- a/port/linux/test/module-test.cpp +++ b/port/linux/test/module-test.cpp @@ -504,9 +504,9 @@ char f_getchar(void) { if (n > 0) { return c; } - pika_platform_printf("f_getchar error\r\n"); - pika_assert(0); - return -1; + // pika_platform_printf("f_getchar error\r\n"); + // pika_assert(0); + return EOF; } void pikaScriptShell_withGetchar(PikaObj* self, sh_getchar getchar_fn); } @@ -603,7 +603,7 @@ TEST(module, REPL_big_script) { fclose((FILE*)f_getchar_fp); /* collect */ /* assert */ - EXPECT_STREQ(log_buff[0], + EXPECT_STREQ(log_buff[3], "\r\nError: line buff overflow, please use bigger " "'PIKA_LINE_BUFF_SIZE'\r\n"); /* deinit */ diff --git a/port/linux/test/pikaMain-test.cpp b/port/linux/test/pikaMain-test.cpp index f158e5db8..0b485b3e3 100644 --- a/port/linux/test/pikaMain-test.cpp +++ b/port/linux/test/pikaMain-test.cpp @@ -2753,6 +2753,27 @@ TEST(pikaMain, REPL_backspace) { EXPECT_EQ(pikaMemNow(), 0); } +#if PIKA_TAB_ENABLE +TEST(pikaMain, REPL_tab_completion) { + char lines[] = {'p', 'r', 0x09, '(', '\"', + 't', 'e', 's', 't', '\"', ')', '\r', '\n', 0x00}; + /* init */ + g_PikaMemInfo.heapUsedMax = 0; + PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain); + /* run */ + __platform_printf("BEGIN\r\n"); + for (size_t i = 0; i < strGetSize(lines); i++) { + obj_runChar(pikaMain, lines[i]); + } + /* collect */ + /* assert */ + EXPECT_STREQ(log_buff[1], "test\r\n"); + /* deinit */ + obj_deinit(pikaMain); + EXPECT_EQ(pikaMemNow(), 0); +} +#endif + TEST(pikaMain, REPL_backspace_issue_1) { char* lines = "print('test'\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b'a'\r\n"; /* init */ diff --git a/src/PikaObj.c b/src/PikaObj.c index 2255ab6e3..f56f6b8f1 100644 --- a/src/PikaObj.c +++ b/src/PikaObj.c @@ -1395,6 +1395,121 @@ typedef enum { __FILTER_SUCCESS_DROP_ALL_PEEKED } FilterReturn; +typedef struct { + int count; + char **completions; +} CompletionList; + +typedef struct { + char lineBuff[PIKA_LINE_BUFF_SIZE]; + size_t line_position; + size_t line_curpos; + char prefix[32]; +} Shell; + +const char *dictionary[] = { + "import", "PikaStdLib", "from", "high", "low", "Pin", "value", "def", + "PikaStdDevice", "setPin", "enable", "print", "sleep_ms", "read", + "setMode", "setCallBack", "setPull", "as", "MemChecker", "max", "min", + "float", "int", "str", "list", "dict", "tuple", "if", "else", + "elif", "for", "while", "break", "continue", "return", "try", "except", + "finally", "with", "open", "write", "append", "close", "True", "False", + "None", "self", "class", "init", "len", "range", "input", "output", + "config", "setup", "loop", "GPIO", "UART", "I2C", "SPI", "ADC", "PWM", + "digitalRead", "digitalWrite", "analogRead", "analogWrite", "time", "datetime", + "random", "OS", "sys", "math", "json", "readFile", "writeFile", + "" +}; + +int dictSize = sizeof(dictionary) / sizeof(dictionary[0]); + +static CompletionList filtered_complete = {0, NULL}; + +void shCompletePrint(CompletionList *completeList, const char *prefix) { + for (int i = 0; i < completeList->count; i++) { + printf("%s ", completeList->completions[i]); + } +} + +void getFilteredCompletions(const char* prefix, const char** dictionary, int dictSize, CompletionList *result) { + printf("\n"); + if (result->completions != NULL) { + for (int i = 0; i < result->count; i++) { + free(result->completions[i]); + } + free(result->completions); + result->completions = NULL; + } + result->count = 0; + result->completions = (char**)malloc(dictSize * sizeof(char*)); + if (result->completions == NULL) { + printf("Memory allocation failed\n"); + return; + } + + for (int i = 0; i < dictSize; i++) { + if (strncmp(dictionary[i], prefix, strlen(prefix)) == 0) { + result->completions[result->count] = strdup(dictionary[i]); + if (result->completions[result->count] == NULL) { + printf("Memory allocation failed for completion\n"); + continue; + } + result->count++; + } + } + + if (result->count == 0) { + printf("Warning: No matches found for '%s'\n", prefix); + } +} + +/*free CompletionList*/ +void freeCompletionList(CompletionList *list) { + for (int i = 0; i < list->count; ++i) { + free(list->completions[i]); + } + free(list->completions); + list->completions = NULL; + list->count = 0; +} + +void handleTabCompletion(ShellConfig* shell, char* prefix) { +#if PIKA_TAB_ENABLE + if (shell->line_position > 0) { + if (prefix == NULL) { + printf("Memory allocation failed for prefix\n"); + return; + } + + // printf("\n================[fetch : %s ]=====================\n", prefix); + getFilteredCompletions(prefix, dictionary, dictSize, &filtered_complete); + + if (filtered_complete.count == 1) { + char* last_space = strrchr(shell->lineBuff, ' '); + size_t start_pos = 0; + + if (last_space != NULL) { + /*保留空格以前的内容*/ + start_pos = last_space - shell->lineBuff + 1; + } + + memset(shell->lineBuff + start_pos, 0, sizeof(shell->lineBuff) - start_pos); + strncpy(shell->lineBuff + start_pos, filtered_complete.completions[0], sizeof(shell->lineBuff) - start_pos - 1); + shell->lineBuff[sizeof(shell->lineBuff) - 1] = '\0'; + shell->line_position = strlen(shell->lineBuff); + shell->line_curpos = shell->line_position; + + printf(">>> %s", shell->lineBuff); + } else { + shCompletePrint(&filtered_complete, prefix); + printf("\n>>> %s", shell->lineBuff); + } + free(prefix); + } +#endif + freeCompletionList(&filtered_complete); +} + pika_bool _filter_msg_hi_pika_handler(FilterItem* msg, PikaObj* self, ShellConfig* shell) { @@ -1668,6 +1783,14 @@ enum shellCTRL _inner_do_obj_runChar(PikaObj* self, ShellConfig* shell) { char* input_line = NULL; enum shellCTRL ctrl = SHELL_CTRL_CONTINUE; + static uint64_t tick_start_block_input = 0; + if (tick_start_block_input != 0) { + if (pika_platform_get_tick() - tick_start_block_input < 5000) { + return SHELL_CTRL_CONTINUE; + } else { + tick_start_block_input = 0; + } + } if (inputChar == 0x7F) { inputChar = '\b'; } @@ -1682,6 +1805,33 @@ enum shellCTRL _inner_do_obj_runChar(PikaObj* self, ctrl = SHELL_CTRL_CONTINUE; goto __exit; } + if (inputChar == 0x09) { +#if PIKA_TAB_ENABLE + if (shell->line_position > 0) { + // printf("Current cursor position: %zu, Line position: %zu\n", shell->line_curpos, shell->line_position); + char* shell_content = NULL; + char* last_space = strrchr(shell->lineBuff, ' '); + + if (last_space == NULL) { + shell_content = strndup(shell->lineBuff, shell->line_position); + } else { + shell_content = strdup(last_space + 1); + } + + if (shell_content == NULL) { + printf("Memory allocation failed for shell_content\n"); + // return; + } + + handleTabCompletion(shell, shell_content); + ctrl = SHELL_CTRL_CONTINUE; + // __clearBuff(shell); + goto __exit; + } +#endif + ctrl = SHELL_CTRL_CONTINUE; + goto __exit; + } if (inputChar == 0x1b) { shell->stat = PIKA_SHELL_STATE_WAIT_SPEC_KEY; ctrl = SHELL_CTRL_CONTINUE; @@ -1762,7 +1912,12 @@ enum shellCTRL _inner_do_obj_runChar(PikaObj* self, pika_platform_printf( "\r\nError: line buff overflow, please use bigger " "'PIKA_LINE_BUFF_SIZE'\r\n"); - ctrl = SHELL_CTRL_EXIT; + ctrl = SHELL_CTRL_CONTINUE; + pika_platform_printf( + "Input is blocked for 5 seconds to protect the " + "kernel...\r\n"); + tick_start_block_input = pika_platform_get_tick(); + pika_platform_printf(">>> "); __clearBuff(shell); goto __exit; } @@ -1981,6 +2136,12 @@ void _do_pikaScriptShell(PikaObj* self, ShellConfig* cfg) { while (1) { inputChar[1] = inputChar[0]; inputChar[0] = _await_getchar(cfg->fn_getchar); +#ifdef __linux + if (inputChar[0] == EOF) { + pika_platform_printf("\r\n"); + return; + } +#endif #if !PIKA_NANO_ENABLE /* run python script */ if (inputChar[0] == '!' && inputChar[1] == '#') { diff --git a/src/PikaObj.h b/src/PikaObj.h index 61ae2f8a6..e30eb5208 100644 --- a/src/PikaObj.h +++ b/src/PikaObj.h @@ -498,6 +498,10 @@ char* shHistory_getPrev(ShellHistory* self); char* shHistory_getNext(ShellHistory* self); #endif +#if PIKA_TAB_ENABLE +void handleTabCompletion(ShellConfig* shell, char* prefix); +#endif + void _do_pikaScriptShell(PikaObj* self, ShellConfig* cfg); void _temp__do_pikaScriptShell(PikaObj* self, ShellConfig* cfg); diff --git a/src/PikaParser.c b/src/PikaParser.c index 7f4c2c072..90c1e4dd4 100644 --- a/src/PikaParser.c +++ b/src/PikaParser.c @@ -2720,7 +2720,9 @@ static char* Suger_semicolon(Args* outbuffs, char* sLine) { sStmtItem = strsAppend(&buffs, sStmtItem, "\n"); sStmtAfter = strsAppend(&buffs, sStmtAfter, sStmtItem); } - sStmtAfter[strGetSize(sStmtAfter) - 1] = '\0'; + if (sStmtAfter[0] != '\0') { + sStmtAfter[strGetSize(sStmtAfter) - 1] = '\0'; + } sStmtAfter = strsCopy(outbuffs, sStmtAfter); strsDeinit(&buffs); return sStmtAfter; diff --git a/src/pika_config_valid.h b/src/pika_config_valid.h index c18f3e3bd..5282b0cff 100644 --- a/src/pika_config_valid.h +++ b/src/pika_config_valid.h @@ -93,6 +93,10 @@ extern "C" { #define PIKA_SHELL_HISTORY_ENABLE 0 #endif + #ifndef PIKA_TAB_ENABLE + #define PIKA_TAB_ENABLE 0 + #endif + #endif /* default optimize */ @@ -495,6 +499,10 @@ extern "C" { #define PIKA_SHELL_HISTORY_ENABLE 1 #endif + #ifndef PIKA_TAB_ENABLE + #define PIKA_TAB_ENABLE 1 + #endif + #ifndef PIKA_SHELL_HISTORY_NUM #define PIKA_SHELL_HISTORY_NUM 5 #endif