diff --git a/documentation/build_kwp.cpp b/documentation/build_kwp.cpp index 3bf62d22..e921e3a5 100644 --- a/documentation/build_kwp.cpp +++ b/documentation/build_kwp.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../src/ui/strlib.h" #define code_t int @@ -45,6 +46,7 @@ struct spopr_keyword_s { struct HelpItem { char package[20]; char keyword[20]; + char type[20]; char id[20]; char signature[128]; char help[1024]; @@ -177,6 +179,8 @@ bool readHelpReference(strlib::List *helpItems) { break; case 1: // type + strncpy(item->type, lineBuffer + start, fieldLen); + item->type[fieldLen] = '\0'; break; case 2: // keyword @@ -215,6 +219,16 @@ bool readHelpReference(strlib::List *helpItems) { return true; } +uint32_t getHash(const char *key) { + uint32_t hash, i; + for (hash = i = 0; key[i] != '\0'; i++) { + hash += tolower(key[i]); + hash += (hash << 3); + hash ^= (hash >> 1); + } + return hash; +} + int main(int argc, char *argv[]) { strlib::List helpItems; if (!readHelpReference(&helpItems)) { @@ -228,12 +242,44 @@ int main(int argc, char *argv[]) { fprintf(stdout, " const char *signature;\n"); fprintf(stdout, " const char *help;\n"); fprintf(stdout, "} keyword_help[] = {\n"); + + int max_keyword_len = 0; List_each(HelpItem *, it, helpItems) { HelpItem *item = (*it); fprintf(stdout, "{\"%s\",\"%s\",\"%s\",\"%s\"},\n", item->package, item->keyword, item->signature, item->help); + int len = strlen(item->keyword); + if (len > max_keyword_len) { + max_keyword_len = len; + } } fprintf(stdout, "};\n"); fprintf(stdout, "const int keyword_help_len = %d;\n", helpItems.size()); + fprintf(stdout, "const int keyword_max_len = %d;\n", max_keyword_len); + + int count = 0; + fprintf(stdout, "const uint32_t keyword_hash_statement[] = {\n"); + List_each(HelpItem *, it, helpItems) { + HelpItem *item = (*it); + if (strcasecmp(item->package, "Language") == 0) { + count++; + fprintf(stdout, " %uu,\n", getHash(item->keyword)); + } + } + fprintf(stdout, "};\n"); + fprintf(stdout, "const int keyword_hash_statement_len = %d;\n", count); + + count = 0; + fprintf(stdout, "const uint32_t keyword_hash_command[] = {\n"); + List_each(HelpItem *, it, helpItems) { + HelpItem *item = (*it); + if (strcasecmp(item->package, "Language") != 0) { + count++; + fprintf(stdout, " %uu,\n", getHash(item->keyword)); + } + } + fprintf(stdout, "};\n"); + fprintf(stdout, "const int keyword_hash_command_len = %d;\n", count); + return 0; } diff --git a/src/platform/sdl/keymap.h b/src/platform/sdl/keymap.h index 3118bae3..a354176c 100644 --- a/src/platform/sdl/keymap.h +++ b/src/platform/sdl/keymap.h @@ -63,7 +63,7 @@ const int shiftmap[][2] = { {'8', '*'}, {'9', '('}, {'0', ')'}, - {'-', '-'}, + {'-', '_'}, {'=', '+'}, {'[', '{'}, {']', '}'}, diff --git a/src/platform/sdl/settings.cpp b/src/platform/sdl/settings.cpp index 9336743b..7a80e379 100644 --- a/src/platform/sdl/settings.cpp +++ b/src/platform/sdl/settings.cpp @@ -17,6 +17,8 @@ #include "ui/utils.h" #include "common/smbas.h" +extern int g_themeId; + static const char *ENV_VARS[] = { "APPDATA", "HOME", "TMP", "TEMP", "TMPDIR" }; @@ -88,6 +90,7 @@ void restoreSettings(const char *configName, SDL_Rect &rect, int &fontScale) { fontScale = nextInteger(fp, DEFAULT_SCALE); opt_mute_audio = nextInteger(fp, 0); opt_ide = nextInteger(fp, 0); + g_themeId = nextInteger(fp, 0); fclose(fp); } else { rect.x = SDL_WINDOWPOS_UNDEFINED; @@ -97,6 +100,7 @@ void restoreSettings(const char *configName, SDL_Rect &rect, int &fontScale) { fontScale = DEFAULT_SCALE; opt_mute_audio = 0; opt_ide = IDE_NONE; + g_themeId = 0; } } @@ -109,7 +113,8 @@ void saveSettings(const char *configName, SDL_Window *window, int fontScale) { int x, y, w, h; SDL_GetWindowPosition(window, &x, &y); SDL_GetWindowSize(window, &w, &h); - fprintf(fp, "%d,%d,%d,%d,%d,%d,%d\n", x, y, w, h, fontScale, opt_mute_audio, opt_ide); + fprintf(fp, "%d,%d,%d,%d,%d,%d,%d,%d\n", x, y, w, h, + fontScale, opt_mute_audio, opt_ide, g_themeId); fclose(fp); } } diff --git a/src/ui/system.cpp b/src/ui/system.cpp index b04fc0b5..719a8d41 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -95,14 +95,15 @@ void System::editSource(strlib::String &loadPath) { fileName = loadPath; } + const char *help = " Ctrl+h (C-h)=Help"; strlib::String dirtyFile; dirtyFile.append(" * "); dirtyFile.append(fileName); - dirtyFile.append(" C-h=Help"); + dirtyFile.append(help); strlib::String cleanFile; cleanFile.append(" - "); cleanFile.append(fileName); - cleanFile.append(" C-h=Help"); + cleanFile.append(help); int w = _output->getWidth(); int h = _output->getHeight(); diff --git a/src/ui/textedit.cpp b/src/ui/textedit.cpp index febb7034..8fcd52ee 100644 --- a/src/ui/textedit.cpp +++ b/src/ui/textedit.cpp @@ -22,18 +22,38 @@ #define GROW_SIZE 128 #define LINE_BUFFER_SIZE 200 #define INDENT_LEVEL 2 -#define HELP_WIDTH 22 -#define THEME_FOREGROUND 0xa7aebc -#define THEME_BACKGROUND 0x272b33 -#define THEME_SELECTION_BACKGROUND 0x3d4350 -#define THEME_NUMBER_SELECTION_BACKGROUND 0x2b3039 -#define THEME_NUMBER_SELECTION 0xabb2c0 -#define THEME_NUMBER 0x484f5f -#define THEME_CURSOR 0xa7aebc -#define THEME_CURSOR_BACKGROUND 0x3875ed -#define THEME_MATCH_BACKGROUND 0x373b88 -#define THEME_ROW_CURSOR 0x2b313a -#define THEME_SYNTAX_COMMENTS 0x00bb00 +#define HELP_WIDTH 22 +#define NUM_THEMES 4 + +int g_themeId = 0; + +const int theme1[] = { + 0xc8cedb, 0xa7aebc, 0x484f5f, 0xa7aebc, 0xa7aebc, 0x00bb00, + 0x272b33, 0x3d4350, 0x2b3039, 0x3875ed, 0x373b88, 0x2b313a, + 0x0083f8, 0xff9d00, 0x31ccac, 0xc679dd +}; + +const int theme2[] = { + 0xc8cedb, 0x002b36, 0x3d4350, 0xa7aebc, 0xa7aebc, 0x00bb00, + 0x002b36, 0x657b83, 0x073642, 0x9f7d18, 0x2b313a, 0x073642, + 0x0083f8, 0xff9d00, 0x31ccac, 0xc679dd +}; + +const int theme3[] = { + 0xc8cedb, 0xd7decc, 0x484f5f, 0xa7aebc, 0xa7aebc, 0x00bb00, + 0x001b33, 0x0088ff, 0x000d1a, 0x0051b1, 0x373b88, 0x022444, + 0x0083f8, 0xff9d00, 0x31ccac, 0xc679dd +}; + +const int theme4[] = { + 0xc8cedb, 0xa7aebc, 0x484f5f, 0xa7aebc, 0xa7aebc, 0x00bb00, + 0x2e3436, 0x888a85, 0x000000, 0x4d483b, 0x000000, 0x2b313a, + 0x0083f8, 0xff9d00, 0x31ccac, 0xc679dd +}; + +const int* themes[] = { + theme1, theme2, theme3, theme4 +}; const char *helpText = "C-a select-all\n" @@ -56,27 +76,32 @@ const char *helpText = "A-c change case\n" "A-g goto line\n" "A-n trim line-endings\n" + "A-t select theme\n" "SHIFT- select\n" "TAB indent line\n" "F1,A-h keyword help\n" "F9, C-r run\n"; +inline bool match(const char *str, const char *pattern , int len) { + int i, j; + for (i = 0, j = 0; i < len; i++, j += 2) { + if (str[i] != pattern[j] && str[i] != pattern[j + 1]) { + break; + } + } + return i == len; +} + +inline bool is_comment(const char *str, int offs) { + return (str[offs] == '\'' || (str[offs] == '#' && !isdigit(str[offs + 1])) + || match(str + offs, "RrEeMm ", 3)); +} + // // EditTheme // -EditTheme::EditTheme() : - _color(THEME_FOREGROUND), - _background(THEME_BACKGROUND), - _selection_color(THEME_FOREGROUND), - _selection_background(THEME_SELECTION_BACKGROUND), - _number_color(THEME_NUMBER), - _number_selection_color(THEME_FOREGROUND), - _number_selection_background(THEME_NUMBER_SELECTION_BACKGROUND), - _cursor_color(THEME_CURSOR), - _cursor_background(THEME_CURSOR_BACKGROUND), - _match_background(THEME_MATCH_BACKGROUND), - _row_cursor(THEME_ROW_CURSOR), - _syntax_comments(THEME_SYNTAX_COMMENTS) { +EditTheme::EditTheme() { + selectTheme(themes[g_themeId]); } EditTheme::EditTheme(int fg, int bg) : @@ -88,7 +113,30 @@ EditTheme::EditTheme(int fg, int bg) : _cursor_background(fg), _match_background(fg), _row_cursor(bg), - _syntax_comments(bg) { + _syntax_comments(bg), + _syntax_text(fg), + _syntax_command(fg), + _syntax_statement(fg), + _syntax_digit(fg) { +} + +void EditTheme::selectTheme(const int theme[]) { + _color = theme[0]; + _selection_color = theme[1]; + _number_color = theme[2]; + _number_selection_color = theme[3]; + _cursor_color = theme[4]; + _syntax_comments = theme[5]; + _background = theme[6]; + _selection_background = theme[7]; + _number_selection_background = theme[8]; + _cursor_background = theme[9]; + _match_background = theme[10]; + _row_cursor = theme[11]; + _syntax_text = theme[12]; + _syntax_command = theme[13]; + _syntax_statement = theme[14]; + _syntax_digit = theme[15]; } // @@ -158,12 +206,6 @@ char *EditBuffer::textRange(int start, int end) { return result; } -void EditBuffer::replaceChars(const char *replace, int start, int end) { - for (int i = start, j = 0; i < end && i < _len && replace[j] != '\0'; i++, j++) { - _buffer[i] = replace[j]; - } -} - void EditBuffer::removeTrailingSpaces(STB_TexteditState *state) { int lineEnd = _len - 1; int lastChar = lineEnd; @@ -236,6 +278,7 @@ void TextEditInput::completeWord(const char *word) { } void TextEditInput::draw(int x, int y, int w, int h, int chw) { + SyntaxState syntax = kReset; StbTexteditRow r; int len = _buf._len; int i = 0; @@ -245,6 +288,7 @@ void TextEditInput::draw(int x, int y, int w, int h, int chw) { int cursorMatchX = x; int cursorMatchY = y; int row = 0; + int line = 0; int selectStart = MIN(_state.select_start, _state.select_end); int selectEnd = MAX(_state.select_start, _state.select_end); @@ -258,8 +302,15 @@ void TextEditInput::draw(int x, int y, int w, int h, int chw) { break; } + if (i == 0 || + _buf._buffer[i - 1] == '\r' || + _buf._buffer[i - 1] == '\n') { + line++; + } + if (row++ >= _scroll) { - if (_matchingBrace != -1 && _matchingBrace >= i && _matchingBrace < i + r.num_chars) { + if (_matchingBrace != -1 && _matchingBrace >= i && + _matchingBrace < i + r.num_chars) { cursorMatchX = x + ((_matchingBrace - i) * chw); cursorMatchY = y + baseY; } @@ -325,11 +376,16 @@ void TextEditInput::draw(int x, int y, int w, int h, int chw) { maSetColor(_theme->_selection_background); maFillRect(x + _marginWidth, y + baseY, _charWidth / 2, _charHeight); } - drawLineNumber(x, y + baseY, row, true); + drawLineNumber(x, y + baseY, line, true); } else { - drawLineNumber(x, y + baseY, row, false); + drawLineNumber(x, y + baseY, line, false); if (numChars) { - drawText(x + _marginWidth, y + baseY, _buf._buffer + i, numChars); + if (_marginWidth > 0) { + drawText(x + _marginWidth, y + baseY, _buf._buffer + i, numChars, syntax); + } else { + maSetColor(_theme->_color); + maDrawText(x + _marginWidth, y + baseY, _buf._buffer + i, numChars); + } } } baseY += _charHeight; @@ -337,7 +393,7 @@ void TextEditInput::draw(int x, int y, int w, int h, int chw) { i += r.num_chars; } - drawLineNumber(x, y + baseY, row + 1, false); + drawLineNumber(x, y + baseY, line + 1, false); // draw cursor maSetColor(_theme->_cursor_background); @@ -359,23 +415,92 @@ void TextEditInput::draw(int x, int y, int w, int h, int chw) { } } -void TextEditInput::drawText(int x, int y, const char *str, int length) { - maSetColor(_theme->_color); - int i_next = 0; - - if (_marginWidth > 0) { - for (int i = 0; i < length; i++) { - if (str[i] == '\'' || strncasecmp(str + i, "rem ", 4) == 0) { - maDrawText(x, y, str + i_next, i - i_next); - maSetColor(_theme->_syntax_comments); - length -= i; - x += (i * _charWidth); - i_next = i; +void TextEditInput::drawText(int x, int y, const char *str, + int length, SyntaxState &state) { + int i = 0; + int offs = 0; + SyntaxState nextState = state; + + while (offs < length && i < length) { + int count = 0; + int next = 0; + nextState = state; + + // find the end of the current segment + while (i < length) { + if (state == kComment || is_comment(str, i)) { + next = length - i; + nextState = kComment; break; + } else if (state == kText || str[i] == '\"') { + next = 1; + while (i + next < length && str[i + next] != '\"') { + next++; + } + if (str[i + next] == '\"') { + next++; + } + nextState = kText; + break; + } else if (state == kReset && isdigit(str[i]) && + (i == 0 || !isalpha(str[i - 1]))) { + next = 1; + while (i + next < length && isdigit(str[i + next])) { + next++; + } + if (i > 0 && str[i - 1] == '.') { + count--; + next++; + } + nextState = kDigit; + break; + } else if (state == kReset) { + int size = 0; + uint32_t hash = getHash(str, i, size); + if (hash > 0) { + if (matchCommand(hash)) { + nextState = kCommand; + next = size; + break; + } else if (matchStatement(hash)) { + nextState = kStatement; + next = size; + break; + } else if (size > 0) { + i += size - 1; + count += size - 1; + } + } } + i++; + count++; + } + + // draw the current segment + if (count > 0) { + setColor(state); + maDrawText(x, y, str + offs, count); + offs += count; + x += (count * _charWidth); + } + + // draw the next segment + if (next > 0) { + setColor(nextState); + maDrawText(x, y, str + offs, next); + state = kReset; + offs += next; + x += (next * _charWidth); + i += next; } } - maDrawText(x, y, str + i_next, length); + + char cend = str[length]; + if (cend == '\r' || cend == '\n') { + state = kReset; + } else { + state = nextState; + } } bool TextEditInput::edit(int key, int screenWidth, int charWidth) { @@ -395,6 +520,9 @@ bool TextEditInput::edit(int key, int screenWidth, int charWidth) { case SB_KEY_ALT('n'): removeTrailingSpaces(); break; + case SB_KEY_ALT('t'): + cycleTheme(); + break; case SB_KEY_TAB: editTab(); break; @@ -460,6 +588,8 @@ void TextEditInput::gotoLine(const char *buffer) { } void TextEditInput::reload(const char *text) { + _scroll = 0; + _cursorRow = 0; _buf.clear(); _buf.insertChars(0, text, strlen(text)); stb_textedit_initialize_state(&_state, false); @@ -533,7 +663,8 @@ bool TextEditInput::updateUI(var_p_t form, var_p_t field) { } bool TextEditInput::selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw) { - stb_textedit_drag(&_buf, &_state, pt.x - _marginWidth, pt.y + scrollY + (_scroll * _charHeight)); + stb_textedit_drag(&_buf, &_state, pt.x - _marginWidth, + pt.y + scrollY + (_scroll * _charHeight)); redraw = true; return 1; } @@ -563,20 +694,23 @@ void TextEditInput::paste(const char *text) { void TextEditInput::layout(StbTexteditRow *row, int start) const { int i = start; int len = _buf._len; + int x1 = 0; int x2 = _width - _charWidth - _marginWidth; - row->x1 = 0; - row->num_chars = 0; + int numChars = 0; // advance to newline or rectangle edge while (i < len - && (int)row->x1 < x2 + && x1 < x2 && _buf._buffer[i] != '\r' && _buf._buffer[i] != '\n') { - row->x1 += _charWidth; - row->num_chars++; + x1 += _charWidth; + numChars++; i++; } + row->num_chars = numChars; + row->x1 = x1; + if (_buf._buffer[i] == '\r') { // advance over DOS newline row->num_chars++; @@ -629,13 +763,18 @@ void TextEditInput::changeCase() { } } if (selection[0]) { - _buf.replaceChars(selection, start, end); _state.select_start = start; _state.select_end = end; + stb_textedit_paste(&_buf, &_state, selection, strlen(selection)); } free(selection); } +void TextEditInput::cycleTheme() { + g_themeId = (g_themeId + 1) % NUM_THEMES; + _theme->selectTheme(themes[g_themeId]); +} + void TextEditInput::drawLineNumber(int x, int y, int row, bool selected) { if (_marginWidth > 0) { if (selected) { @@ -833,6 +972,23 @@ int TextEditInput::getCursorRow() const { return row + 1; } +uint32_t TextEditInput::getHash(const char *str, int offs, int &count) { + uint32_t result = 0; + if ((offs == 0 || IS_WHITE(str[offs - 1])) + && !IS_WHITE(str[offs]) && str[offs] != '\0') { + for (count = 0; count < keyword_max_len; count++) { + char ch = str[offs + count]; + if (!isalpha(ch) && ch != '_') { + break; + } + result += tolower(str[offs + count]); + result += (result << 3); + result ^= (result >> 1); + } + } + return result; +} + int TextEditInput::getIndent(char *spaces, int len, int pos) { // count the indent level and find the start of text char *buf = lineText(pos); @@ -860,7 +1016,7 @@ int TextEditInput::getIndent(char *spaces, int len, int pos) { int j = i + 4; while (buf[j] != 0 && buf[j] != '\n') { // line also 'ends' at start of comments - if (strncasecmp(buf + j, "rem", 3) == 0 || buf[j] == '\'') { + if (is_comment(buf, j)) { break; } j++; @@ -1000,6 +1156,26 @@ int TextEditInput::linePos(int pos, bool end, bool excludeBreak) { return start; } +bool TextEditInput::matchCommand(uint32_t hash) { + bool result = false; + for (int i = 0; i < keyword_hash_command_len && !result; i++) { + if (keyword_hash_command[i] == hash) { + result = true; + } + } + return result; +} + +bool TextEditInput::matchStatement(uint32_t hash) { + bool result = false; + for (int i = 0; i < keyword_hash_statement_len && !result; i++) { + if (keyword_hash_statement[i] == hash) { + result = true; + } + } + return result; +} + void TextEditInput::pageNavigate(bool pageDown, bool shift) { int pageRows = (_height / _charHeight) + 1; int nextRow = _cursorRow + (pageDown ? pageRows : -pageRows); @@ -1024,6 +1200,7 @@ void TextEditInput::pageNavigate(bool pageDown, bool shift) { } else { _state.select_start = _state.select_end; } + _state.cursor = i; _cursorRow = row; updateScroll(); @@ -1035,11 +1212,34 @@ void TextEditInput::removeTrailingSpaces() { setCursorRow(row - 1); } +void TextEditInput::setColor(SyntaxState &state) { + switch (state) { + case kComment: + maSetColor(_theme->_syntax_comments); + break; + case kText: + maSetColor(_theme->_syntax_text); + break; + case kCommand: + maSetColor(_theme->_syntax_command); + break; + case kStatement: + maSetColor(_theme->_syntax_statement); + break; + case kDigit: + maSetColor(_theme->_syntax_digit); + break; + case kReset: + maSetColor(_theme->_color); + break; + } +} + void TextEditInput::updateScroll() { int pageRows = _height / _charHeight; if (_cursorRow + 1 < pageRows) { _scroll = 0; - } else if (_cursorRow > _scroll + pageRows || _cursorRow <= _scroll) { + } else if (_cursorRow >= _scroll + pageRows || _cursorRow <= _scroll) { // cursor outside current view _scroll = _cursorRow - (pageRows / 2); } @@ -1142,7 +1342,7 @@ bool TextEditHelpWidget::edit(int key, int screenWidth, int charWidth) { break; case kKeyword: _mode = kHelp; - completeWord(0); + completeLine(0); break; default: break; @@ -1156,6 +1356,18 @@ bool TextEditHelpWidget::edit(int key, int screenWidth, int charWidth) { return result; } +void TextEditHelpWidget::completeLine(int pos) { + int end = pos; + while (end < _buf._len && _buf._buffer[end] != '\n') { + end++; + } + char *text = _buf.textRange(pos, end); + if (text[0] != '\0' && text[0] != '[') { + _editor->completeWord(text); + } + free(text); +} + void TextEditHelpWidget::completeWord(int pos) { char *text = lineText(pos); if (text[0] != '\0' && text[0] != '[') { diff --git a/src/ui/textedit.h b/src/ui/textedit.h index 82cb0403..15a6700e 100644 --- a/src/ui/textedit.h +++ b/src/ui/textedit.h @@ -27,6 +27,7 @@ struct TextEditInput; struct EditTheme { EditTheme(); EditTheme(int fg, int bg); + void selectTheme(const int theme[]); int _color; int _background; @@ -40,6 +41,10 @@ struct EditTheme { int _match_background; int _row_cursor; int _syntax_comments; + int _syntax_text; + int _syntax_command; + int _syntax_statement; + int _syntax_digit; }; struct EditBuffer { @@ -57,7 +62,6 @@ struct EditBuffer { int deleteChars(int pos, int num); int insertChars(int pos, const char *text, int num); void removeTrailingSpaces(STB_TexteditState *state); - void replaceChars(const char *replace, int start, int end); char *textRange(int start, int end); }; @@ -67,7 +71,6 @@ struct TextEditInput : public FormEditInput { void completeWord(const char *word); void draw(int x, int y, int w, int h, int chw); - void drawText(int x, int y, const char *str, int length); bool edit(int key, int screenWidth, int charWidth); bool find(const char *word, bool next); int getCursorPos() const { return _state.cursor; } @@ -98,13 +101,25 @@ struct TextEditInput : public FormEditInput { bool replaceNext(const char *text); protected: + enum SyntaxState { + kReset = 0, + kComment, + kText, + kCommand, + kStatement, + kDigit, + }; + + void drawText(int x, int y, const char *str, int length, SyntaxState &state); void changeCase(); + void cycleTheme(); void drawLineNumber(int x, int y, int row, bool selected); void editDeleteLine(); void editEnter(); void editTab(); void findMatchingBrace(); int getCursorRow() const; + uint32_t getHash(const char *str, int offs, int &count); int getIndent(char *spaces, int len, int pos); int getLineChars(StbTexteditRow *row, int pos); char *getSelection(int *start, int *end); @@ -113,8 +128,11 @@ struct TextEditInput : public FormEditInput { int lineEnd(int pos) { return linePos(pos, true); } int linePos(int pos, bool end, bool excludeBreak=true); int lineStart(int pos) { return linePos(pos, false); } + bool matchCommand(uint32_t hash); + bool matchStatement(uint32_t hash); void pageNavigate(bool pageDown, bool shift); void removeTrailingSpaces(); + void setColor(SyntaxState &state); void updateScroll(); int wordStart(); @@ -167,6 +185,7 @@ struct TextEditHelpWidget : public TextEditInput { bool replaceDoneMode() const { return _mode == kReplaceDone; } private: + void completeLine(int pos); void completeWord(int pos); void createPackageIndex(); bool createKeywordHelp(const char *keyword);