From 2f3df96a4dd5a77bf29b0609f678b04ad32c83ee Mon Sep 17 00:00:00 2001 From: cxl Date: Thu, 15 May 2014 06:51:02 +0000 Subject: [PATCH] CodeEditor: Syntax support refactored git-svn-id: svn://ultimatepp.org/upp/trunk@7375 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppdev/ceditor/ceditor.upp | 9 + uppdev/ceditor/init | 4 + uppdev/ceditor/main.cpp | 14 ++ uppsrc/CodeEditor/CHighlight.cpp | 328 +++++++++++++++++++++++++++ uppsrc/CodeEditor/CInit.cpp | 337 ++++++++++++++++++++++++++++ uppsrc/CodeEditor/CLogic.cpp | 185 +++++++++++++++ uppsrc/CodeEditor/CRegister.icpp | 33 +++ uppsrc/CodeEditor/CSyntax.cpp | 348 +++++++++++++++++++++++++++++ uppsrc/CodeEditor/HighlightOut.cpp | 126 +++++++++++ uppsrc/CodeEditor/Register.cpp | 40 ++++ uppsrc/CodeEditor/Style.cpp | 157 +++++++++++++ uppsrc/CodeEditor/Syntax.h | 197 ++++++++++++++++ 12 files changed, 1778 insertions(+) create mode 100644 uppdev/ceditor/ceditor.upp create mode 100644 uppdev/ceditor/init create mode 100644 uppdev/ceditor/main.cpp create mode 100644 uppsrc/CodeEditor/CHighlight.cpp create mode 100644 uppsrc/CodeEditor/CInit.cpp create mode 100644 uppsrc/CodeEditor/CLogic.cpp create mode 100644 uppsrc/CodeEditor/CRegister.icpp create mode 100644 uppsrc/CodeEditor/CSyntax.cpp create mode 100644 uppsrc/CodeEditor/HighlightOut.cpp create mode 100644 uppsrc/CodeEditor/Register.cpp create mode 100644 uppsrc/CodeEditor/Style.cpp create mode 100644 uppsrc/CodeEditor/Syntax.h diff --git a/uppdev/ceditor/ceditor.upp b/uppdev/ceditor/ceditor.upp new file mode 100644 index 000000000..59c78cd45 --- /dev/null +++ b/uppdev/ceditor/ceditor.upp @@ -0,0 +1,9 @@ +uses + CodeEditor; + +file + main.cpp; + +mainconfig + "" = "GUI"; + diff --git a/uppdev/ceditor/init b/uppdev/ceditor/init new file mode 100644 index 000000000..0c7d3915c --- /dev/null +++ b/uppdev/ceditor/init @@ -0,0 +1,4 @@ +#ifndef _ceditor_icpp_init_stub +#define _ceditor_icpp_init_stub +#include "CodeEditor/init" +#endif diff --git a/uppdev/ceditor/main.cpp b/uppdev/ceditor/main.cpp new file mode 100644 index 000000000..3148ba14d --- /dev/null +++ b/uppdev/ceditor/main.cpp @@ -0,0 +1,14 @@ +#include + +using namespace Upp; + +GUI_APP_MAIN { + TopWindow win; + CodeEditor editor; + + editor <<= LoadFile("C:/u/highlight/uppsrc/CodeEditor/CodeEditor.cpp"); + editor.Highlight(CodeEditor::HIGHLIGHT_CPP); + + win.Add(editor.SizePos()); + win.Run(); +} \ No newline at end of file diff --git a/uppsrc/CodeEditor/CHighlight.cpp b/uppsrc/CodeEditor/CHighlight.cpp new file mode 100644 index 000000000..102d9fc28 --- /dev/null +++ b/uppsrc/CodeEditor/CHighlight.cpp @@ -0,0 +1,328 @@ +#include "CodeEditor.h" + +NAMESPACE_UPP + +#define LTIMING(x) // RTIMING(x) + +bool cmps(const wchar *q, const char *s, int& n) { + const wchar *t = q; + while(*q) + if(*q++ != *s++) + return false; + n += int(q - t); + return *q == *s; +} + +bool IsUpperString(const char *q) +{ + while(*q) + { + if(*q != '_' && (*q < '0' || *q > '9') && !IsUpper(*q)) + return false; + q++; + } + return true; +} + +Color CSyntax::BlockColor(int level) +{ + if(hilite_scope == 1) + return GetHlStyle(level & 1 ? PAPER_BLOCK1 : PAPER_NORMAL).color; + if(hilite_scope == 2) { + int q = level % 5; + return GetHlStyle(q ? PAPER_BLOCK1 + q - 1 : PAPER_NORMAL).color; + } + return GetHlStyle(PAPER_NORMAL).color; +} + +void CSyntax::Bracket(int pos, HighlightOutput& hls, CodeEditor& editor) // TODO:SYNTAX: Cleanup passing bracket info +{ + if(editor.IsCursorBracket(pos)) { + HlStyle& h = hl_style[PAPER_BRACKET0]; + hls.SetPaper(hls.pos, 1, h.color); + hls.SetFont(hls.pos, 1, h); + } + if(editor.IsMatchingBracket(pos)) { + HlStyle& h = hl_style[PAPER_BRACKET]; + hls.SetPaper(hls.pos, 1, h.color); + hls.SetFont(hls.pos, 1, h); + } +} + +void CSyntax::Highlight(CodeEditor& editor, int line, Vector& hl, int pos) +{ + ONCELOCK { + InitKeywords(); + } + + LTIMING("HighlightLine"); + if(highlight < 0 || highlight >= keyword.GetCount()) + return; + WString text = editor.GetWLine(line); + One ess = editor.GetSyntax(line); + CSyntax *ss = dynamic_cast(~ess); + + CSyntax next; + next.Set(ss->Get()); + next.ScanSyntax(~text, text.End(), line + 1, editor.GetTabSize()); + bool macro = next.macro != MACRO_OFF; + + ss->Grounding(text.Begin(), text.End()); + HighlightOutput hls(hl); + const wchar *p = text; + const wchar *e = text.End(); + if(highlight == HIGHLIGHT_CALC) { + if(line == editor.GetLineCount() - 1 || *p == '$') + hls.SetPaper(0, text.GetLength() + 1, hl_style[PAPER_BLOCK1].color); + } + else + hls.SetPaper(0, text.GetLength() + 1, + macro ? hl_style[PAPER_MACRO].color : hl_style[PAPER_NORMAL].color); + int block_level = ss->bid.GetCount() - 1; + String cppid; + if(!ss->comment && highlight != HIGHLIGHT_CALC) { + if(!macro) { + int i = 0, bid = 0, pos = 0, tabsz = editor.GetTabSize(); + while(bid < ss->bid.GetCount() - 1 + && (i >= text.GetLength() || text[i] == ' ' || text[i] == '\t')) { + hls.SetPaper(i, 1, BlockColor(bid)); + if(i < text.GetLength() && text[i] == '\t' || ++pos >= tabsz) { + pos = 0; + bid++; + } + i++; + } + hls.SetPaper(i, 1 + max(0, text.GetLength() - i), BlockColor(ss->bid.GetCount() - 1)); + } + while(*p == ' ' || *p == '\t') { + p++; + hls.Put(hl_style[INK_NORMAL]); + } + if(*p == '#' && findarg(highlight, HIGHLIGHT_JAVASCRIPT, HIGHLIGHT_CSS, HIGHLIGHT_JSON) < 0) { + static const char *pd[] = { + "define", "error", "if", "elif", "else", "endif", + "ifdef", "ifndef", "include", "line", "undef", "pragma", + // CLR + "using" + }; + static Index macro; + if(macro.GetCount() == 0) + for(int i = 0; i < __countof(pd); i++) + macro.Add(pd[i]); + const wchar *q = p + 1; + while(*q == ' ' || *q == '\t') + q++; + StringBuffer id; + while(IsAlpha(*q)) + id.Cat(*q++); + cppid = id; + hls.Put(macro.Find(cppid) < 0 ? 1 : int(q - p), hl_style[INK_MACRO]); + p = q; + } + } + int lindent = int(p - ~text); + int lbrace = -1; + int lbclose = -1; + Color lbcolor = Null; + bool is_comment = false; + while(p < e) { + int pair = MAKELONG(p[0], p[1]); + if(ss->linecomment && ss->linecont || pair == MAKELONG('/', '/') && highlight != HIGHLIGHT_CSS && highlight != HIGHLIGHT_JSON) { + hls.Put(text.GetLength() + 1 - hls.pos, hl_style[INK_COMMENT]); + is_comment = true; + break; + } + else + if(ss->comment && highlight != HIGHLIGHT_JSON) + if(pair == MAKELONG('*', '/')) { + hls.Put(2, hl_style[INK_COMMENT]); + p += 2; + ss->comment = false; + } + else { + hls.Put(hl_style[INK_COMMENT]); + p++; + } + else + if((*p == '\"' || *p == '\'') || ss->linecont && ss->string) + p = hls.CString(p); + else + if(pair == MAKELONG('/', '*') && highlight != HIGHLIGHT_JSON) { + hls.Put(2, hl_style[INK_COMMENT]); + p += 2; + ss->comment = true; + is_comment = true; + } + else + if(*p == '(') { + ss->brk.Add(')'); + Bracket(int(p - text) + pos, hls, editor); + hls.Put(hl_style[INK_PAR0 + max(ss->pl++, 0) % 4]); + p++; + } + else + if(*p == '{') { + ss->brk.Add(ss->was_namespace ? 0 : '}'); + Bracket(int(p - text) + pos, hls, editor); + hls.Put(hl_style[INK_PAR0 + max(ss->cl, 0) % 4]); + if(ss->was_namespace) + ss->was_namespace = false; + else { + ++block_level; + ++ss->cl; + } + if(hls.pos < text.GetCount()) + hls.SetPaper(hls.pos, text.GetCount() - hls.pos + 1, BlockColor(block_level)); + p++; + } + else + if(*p == '[') { + ss->brk.Add(']'); + Bracket(int(p - text) + pos, hls, editor); + hls.Put(hl_style[INK_PAR0 + max(ss->bl++, 0) % 4]); + p++; + } + else + if(*p == ')' || *p == '}' || *p == ']') { + int bl = ss->brk.GetCount(); + int bc = bl ? ss->brk.Pop() : 0; + if(*p == '}' && hilite_scope && block_level > 0 && bc) + hls.SetPaper(hls.pos, text.GetLength() + 1 - hls.pos, BlockColor(--block_level)); + Bracket(int(p - text) + pos, hls, editor); + int& l = *p == ')' ? ss->pl : *p == '}' ? ss->cl : ss->bl; + if(bc && (bc != *p || l <= 0) || bc == 0 && *p != '}') { + hls.Put(p == ~text ? hl_style[INK_PAR0] : hl_style[INK_ERROR]); + ss->brk.Clear(); + ss->cl = ss->bl = ss->pl = 0; + } + else { + if(bc) --l; + hls.Put(1, hl_style[INK_PAR0 + l % 4]); + } + p++; + } + else + if(highlight == HIGHLIGHT_CSS ? *p == '#' : pair == MAKELONG('0', 'x') || pair == MAKELONG('0', 'X')) { + hls.Put(2, hl_style[INK_CONST_HEX]); + p += 2; + const wchar *t = p; + while(IsXDigit(*p)) + p++; + hls.Put(int(p - t), hl_style[INK_CONST_HEX]); + } + else + if(IsDigit(*p)) { + const wchar *t = p; + int c = INK_CONST_INT; + bool isDot, isFloat = false; + if(*p == '0') c = INK_CONST_OCT; + while(IsDigit(*p)) p++; + int fixdigits = p - t; + if(*p == '.' || (*p == 'e' || *p == 'E') && highlight != HIGHLIGHT_CSS) { + if(*p == '.') + isDot = true; + c = INK_CONST_FLOAT; + p++; + if(*p == '-') + p++; + } + while((IsDigit(*p) || (isDot && *p == 'f')) && (isFloat == false)) { + if(*p == 'f') + isFloat = true; + p++; + } + if(c == INK_CONST_OCT && p - t == 1) + c = INK_CONST_INT; + int n = int(p - t); + for(int i = 0; i < n; i++) + hls.Put(hl_style[c], + c == INK_CONST_OCT || (fixdigits < 4 && n - fixdigits < 5) || i == fixdigits || !thousands_separator ? 0 : + i < fixdigits ? decode((fixdigits - i) % 3, 1, LineEdit::SHIFT_L, 0, LineEdit::SHIFT_R, 0) : + decode((i - fixdigits) % 3, 1, LineEdit::SHIFT_R, 0, LineEdit::SHIFT_L, 0)); + +// hls.Put(q > 0 && ((q / 3) & 1) == 1 && c != INK_CONST_OCT ? +// hl_style[c == INK_CONST_INT ? INK_CONST_INT_3 : INK_CONST_FLOAT_3] +// : hl_style[c]); + } + else + if(pair == MAKELONG('t', '_') && p[2] == '(' && p[3] == '\"') { + int pos0 = hls.pos; + hls.Put(3, hl_style[INK_UPP]); + p = hls.CString(p + 3); + if(*p == ')') { + hls.Put(hl_style[INK_UPP]); + p++; + } + hls.SetPaper(pos0, hls.pos - pos0, hl_style[PAPER_LNG].color); + } + else + if(pair == MAKELONG('t', 't') && p[3] == '(' && p[4] == '\"') { + int pos0 = hls.pos; + hls.Put(4, hl_style[INK_UPP]); + p = hls.CString(p + 4); + if(*p == ')') { + hls.Put(hl_style[INK_UPP]); + p++; + } + hls.SetPaper(pos0, hls.pos - pos0, hl_style[PAPER_LNG].color); + } + else + if(iscib(*p)) { + const wchar *q = p; + StringBuffer id; + while((iscidl(*q) || highlight == HIGHLIGHT_CSS && *q == '-') && q < e) + id.Cat(*q++); + String iid = id; + if(highlight == HIGHLIGHT_SQL) + iid = ToUpper(iid); + int uq = kw_upp.Find(iid); + int nq; + hls.Put(int(q - p), (nq = keyword[highlight].Find(iid)) >= 0 ? hl_style[INK_KEYWORD] : + name[highlight].Find(iid) >= 0 ? hl_style[INK_UPP] : + uq >= 0 ? uq < kw_macros ? hl_style[INK_UPPMACROS] : + uq < kw_logs ? hl_style[INK_UPPLOGS] : + uq < kw_sql_base ? hl_style[INK_SQLBASE] : + uq < kw_sql_func ? hl_style[INK_SQLFUNC] : + hl_style[INK_SQLBOOL] : + IsUpperString(iid) && !macro ? hl_style[INK_UPPER] : + hl_style[INK_NORMAL]); + p = q; + if(nq == 0) + ss->was_namespace = true; + } + else { + if(*p == ';') + ss->was_namespace = false; + hls.Put(strchr("!+-*^/%~&|=[]:?<>.", *p) ? hl_style[INK_OPERATOR] : hl_style[INK_NORMAL]); + p++; + } + } + if(hilite_ifdef && !IsNull(cppid) && !is_comment) { + if((cppid == "else" || cppid == "elif" || cppid == "endif") && !ss->ifstack.IsEmpty()) { + WStringBuffer ifln; + ifln.Cat(" // "); + ifln.Cat(ss->ifstack.Top().iftext); + if(ss->ifstack.Top().ifline && hilite_ifdef == 2) { + ifln.Cat(", line "); + ifln.Cat(FormatInt(ss->ifstack.Top().ifline)); + } + ifln.Cat('\t'); + int start = text.GetLength(); + WString ifs(ifln); + hls.Set(start, ifs.GetLength(), hl_style[INK_IFDEF]); + for(int i = 0; i < ifs.GetCount(); i++) + hl[start + i].chr = ifs[i]; + } + } + if(hilite_scope) { + if(lbrace >= 0 && lbclose < 0 && lbrace > lindent) + hls.SetPaper(lindent, lbrace - lindent, lbcolor); + if(lbrace < 0 && lbclose >= 0) + hls.SetPaper(lbclose, text.GetLength() + 1 - lbclose, lbcolor); + } + if(!IsNull(cppid) && (cppid == "else" || cppid == "elif" || cppid == "endif" || cppid == "if" + || cppid == "ifdef" || cppid == "ifndef")) + hls.SetPaper(0, hls.v.GetCount(), hl_style[PAPER_IFDEF].color); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/CodeEditor/CInit.cpp b/uppsrc/CodeEditor/CInit.cpp new file mode 100644 index 000000000..0acf24292 --- /dev/null +++ b/uppsrc/CodeEditor/CInit.cpp @@ -0,0 +1,337 @@ +#include "CodeEditor.h" + +NAMESPACE_UPP + +Vector > CSyntax::keyword; +Vector > CSyntax::name; +Index CSyntax::kw_upp; +int CSyntax::kw_macros; +int CSyntax::kw_logs; +int CSyntax::kw_sql_base; +int CSyntax::kw_sql_func; + +int CSyntax::InitUpp(const char **q) +{ + while(*q) + kw_upp.Add(*q++); + return kw_upp.GetCount(); +} + +void CSyntax::InitKeywords() +{ + static const char *cpp[] = { + "namespace", "asm", "__asm", "else", "struct", + "enum", "switch", "auto", "__except", "template", + "explicit", "this", + "bool", "extern", "mutable", "thread", + "break", "false", "throw", + "case", "__fastcall", "true", + "catch", "__finally", "new", "try", + "__cdecl", "float", "__try", + "char", "wchar_t", "for", "operator", "typedef", + "class", "friend", "private", "typeid", + "const", "goto", "protected", "typename", + "const_cast", "if", "public", "union", + "continue", "inline", "register", "unsigned", + "__declspec", "__inline", "reinterpret_cast", "using", + "using", "default", "int", "return", + "delete", "__int8", "short", "__uuidof", + "dllexport", "__int16", "signed", "virtual", + "dllimport", "__int32", "sizeof", "void", + "do", "__int64", "static", "volatile", + "double", "__leave", "static_cast", + "dynamic_cast", "long", "__stdcall", "while", + "force_inline", + "and", "bitor", "or", "xor", + "compl", "bitand", "and_eq", "or_eq", + "xor_eq", "not", "not_eq", + "char16_t", "char32_t", "constexpr", "decltype", + "noexcept", "nullptr", "static_assert", + "override", "final", + NULL + }; + static const char *cs[] = { + "abstract", "event", "new", "struct", + "as", "explicit", "null", "switch", + "base", "extern", "object", "this", + "bool", "false", "operator", "throw", + "break", "finally", "out", "true", + "byte", "fixed", "override", "try", + "case", "float", "params", "typeof", + "catch", "for", "private", "uint", + "char", "foreach", "protected", "ulong", + "checked", "goto", "public", "unchecked", + "class", "if", "readonly", "unsafe", + "const", "implicit", "ref", "ushort", + "continue", "in", "return", "using", + "decimal", "int", "sbyte", "virtual", + "default", "interface", "sealed", "volatile", + "delegate", "internal", "short", "void", + "do", "is", "sizeof", "while", + "double", "lock", "stackalloc", + "else", "long", "static", + "enum", "namespace", "string", + "await", "async", "throws", "awaits", + NULL + }; + static const char *upp[] = { + "byte", "word", "dword", "__countof", "pick_", "wchar", "NULL", "Null", + "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "qword", + "INTERLOCKED_", "INTERLOCKED", "ONCELOCK", "ONCELOCK_", "INITBLOCK", "EXITBLOCK", + "rval_", "rval_default", + NULL + }; + static const char *usc[] = { + "void", "self", "if", "else", "while", "do", "case", + "default", "break", "return", "switch", "operator", "for", + "fn", "group", "ctrl", "subctrl", "template", "enum_property", + "raw", "int", "double", "String", "bool", + "Text", "Qtf", "Doc", "Font", "Color", "macro", + NULL + }; + static const char *usclib[] = { + "Color", "Point", "Size", "Rect", "RectC", + "StdFont", "Arial", "Roman", "Courier", "GetImageSize", + "GetTextSize", "print", "Black", "Gray", "LtGray", + "WhiteGray", "White", "Red", "Green", "Brown", "Blue", + "Magenta", "Cyan", "Yellow", "LtRed", "LtGreen", "LtYellow", + "LtBlue", "LtMagenta", "LtCyan", "SBlack", "SGray", "SLtGray", + "SWhiteGray", "SWhite", "SRed", "SGreen", "SBrown", "SBlue", + "SMagenta", "SCyan", "SYellow", "SLtRed", "SLtGreen", "SLtYellow", + "SLtBlue", "SLtMagenta", "SLtCyan", "IntNull", "DblNullLim", + "DrawRect", "DrawText", "DrawSmartText", "DrawImage", "is_number", + "GetSmartTextSize", "GetQtfHeight", "DrawQtf", + "is_array", "is_map", "is_void", "int", "to_string", "count", + "len", "remove", "insert", "mid", "keys", "values", "exists", + "erase", "rand", "OpenFileOut", "OpenFileAppend", "OpenFileIn", + "GetMinSize", "GetStdSize", + NULL + }; + static const char *java[] = { + "abstract", "assert", "boolean", "break", "byte", "case", + "catch", "char", "class", "const", "continue", + "default", "do", "double", "else", "enum", "extends", + "false", "final", "finally", "float", "for", + "goto", "if", "implements", "import", "instanceof", + "int", "interface", "long", "native", "new", + "null", "package", "private", "protected", "public", + "return", "short", "static", "strictfp", "super", "switch", + "synchronized", "this", "throw", "throws", "transient", + "true", "try", "void", "volatile", "while", + NULL + }; + static const char *javascript[] = { + "break", "continue", "do", "for", "import", "new", "this", "void", + "case", "default", "else", "function", "in", "return", "typeof", "while", + "comment", "delete", "export", "if", "label", "switch", "var", "with", + "catch", "enum", "throw", "class", "extends", "try", "const", "finally", + "debugger", "super", "true", "false", "undefined", + NULL + }; + static const char *css[] = { + "azimuth", "background-attachment", "background-color", "background-image", "background-position", + "background-repeat", "background", "border-collapse", "border-color", "border-spacing", "border-style", + "border-top", "border-right", "border-bottom", "border-left", "border-top-color", "border-right-color", + "border-bottom-color", "border-left-color", "border-top-style", "border-right-style", "border-bottom-style", + "border-left-style", "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", + "border-width", "border", "bottom", "caption-side", "clear", "clip", "color", "content", "counter-increment", + "counter-reset", "cue-after", "cue-before", "cue", "cursor", "direction", "display", "elevation", "empty-cells", + "float", "font-family", "font-size", "font-style", "font-variant", "font-weight", "font", "height", "left", + "letter-spacing", "line-height", "list-style-image", "list-style-position", "list-style-type", "list-style", + "margin-right", "margin-left", "margin-top", "margin-bottom", "margin", "max-height", "max-width", "min-height", + "min-width", "orphans", "outline-color", "outline-style", "outline-width", "outline", "overflow", "padding-top", + "padding-right", "padding-bottom", "padding-left", "padding", "page-break-after", "page-break-before", + "page-break-inside", "pause-after", "pause-before", "pause", "pitch-range", "pitch", "play-during", "position", + "quotes", "richness", "right", "speak-header", "speak-numeral", "speak-punctuation", "speak", "speech-rate", + "stress", "table-layout", "text-align", "text-decoration", "text-indent", "text-transform", "top", + "unicode-bidi", "vertical-align", "visibility", "voice-family", "volume", "white-space", "widows", "width", + "word-spacing", "z-index", + // CSS3 + "opacity", + "background-clip", "background-origin", "background-size", "border-bottom-left-radius", "border-bottom-right-radius", + "border-image", "border-image-outset", "border-image-repeat", "border-image-slice", "border-image-source", + "border-image-width", "border-radius", "border-top-left-radius", "border-top-right-radius", "box-decoration-break", + "box-shadow", + "overflow-x", "overflow-y", + "align-content", "align-items", "align-self", "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow", + "flex-shrink", "flex-wrap", "justify-content", "order", + "hanging-punctuation", "hyphens", "line-break", "overflow-wrap", "tab-size", "text-align-last", "text-justify", + "word-break", "word-wrap", + "text-decoration-color", "text-decoration-line", "text-decoration-style", "text-shadow", "text-underline-position", + "animation", "animation-delay", "animation-direction", "animation-duration", "animation-fill-mode", + "animation-iteration-count", "animation-name", "animation-timing-function", "animation-play-state", + "backface-visibility", "perspective", "perspective-origin", "transform", "transform-origin", "transform-style", + "transition", "transition-property", "transition-duration", "transition-timing-function", "transition-delay", + "box-sizing", "nav-down", "nav-index", "nav-left", "nav-right", "nav-up", "outline-offset", "resize", "text-overflow", + "break-after", "break-before", "break-inside", "column-count", "column-fill", "column-gap", "column-rule", + "column-rule-color", "column-rule-style", "column-rule-width", "column-span", "column-width", "columns", + "marks", + "filter", + "image-orientation", "image-rendering", "image-resolution", "object-fit", "object-position", + "mask", "mask-type", + "mark", "mark-after", "mark-before", "phonemes", "rest", "rest-after", "rest-before", "voice-balance", + "voice-duration", "voice-pitch", "voice-pitch-range", "voice-rate", "voice-stress", "voice-volume", + "marquee-direction", "marquee-play-count", "marquee-speed", "marquee-style", + NULL + }; + static const char *cssn[] = { + "em", "px", "pt", + "left-side", "far-left", "left", "center-left", "center", "center-right", "right", "far-right", "right-side", + "behind", "leftwards", "rightwards", "inherit", "scroll", "fixed", "transparent", "none", "top", "bottom", + "repeat", "repeat-x", "repeat-y", "no-repeat", "background-color", "background-image", "background-repeat", + "background-attachment", "background-position", "collapse", "separate", "border-top-color", "auto", "both", + "normal", "attr", "open-quote", "close-quote", "no-open-quote", "no-close-quote", "cue-before", "cue-after", + "crosshair", "default", "pointer", "move", "e-resize", "ne-resize", "nw-resize", "n-resize", "se-resize", + "sw-resize", "s-resize", "w-resize", "text", "wait", "help", "progress", "ltr", "rtl", "inline", "block", + "list-item", "inline-block", "table", "inline-table", "table-row-group", "table-header-group", + "table-footer-group", "table-row", "table-column-group", "table-column", "table-cell", "table-caption", + "below", "level", "above", "higher", "lower", "show", "hide", "italic", "oblique", "small-caps", "bold", + "bolder", "lighter", "font-style", + "font-variant", "font-weight", "font-size", "line-height", "font-family", "caption", "icon", "menu", + "message-box", "small-caption", "status-bar", "inside", "outside", "disc", "circle", "square", "decimal", + "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-latin", "upper-latin", "armenian", + "georgian", "lower-alpha", "upper-alpha", "list-style-type", "list-style-position", "list-style-image", + "invert", "outline-color", "outline-style", "outline-width", "visible", "hidden", "always", "avoid", + "x-low", "low", "medium", "high", "x-high", "mix", "static", "relative", "absolute", "once", "digits", + "continuous", "code", "spell-out", "x-slow", "slow", "fast", "x-fast", "faster", "slower", "justify", + "underline", "overline", "line-through", "blink", "capitalize", "uppercase", "lowercase", "embed", + "bidi-override", "baseline", "sub", "super", "text-top", "middle", "text-bottom", "silent", "x-soft", "soft", + "loud", "x-loud", "pre", "nowrap", "pre-wrap", "pre-line", "solid", + NULL + }; + static const char *upp_macros[] = { + "CLASSNAME", "THISBACK", "THISBACK1", "THISBACK2", "THISBACK3", "THISBACK4", + "PTEBACK", "PTEBACK1", "PTEBACK2", "PTEBACK3", "PTEBACK4", + "QUOTE", "XASSERT", "NEVER", "XNEVER", "CHECK", "XCHECK", "ASSERT", "ASSERT_", + "NAMESPACE_UPP", "END_UPP_NAMESPACE", "NEVER_", "SKYLARK", "RPC_METHOD", "RPC_GMETHOD", + NULL + }; + static const char *upp_logs[] = { + "LOG", "LOGF", "DUMP", "DUMPC", "DUMPCC", "DUMPCCC", "DUMPM", + "LLOG", "LLOGF", "LDUMP", "LDUMPC", "LDUMPCC", "LDUMPCCC", "LDUMPM", + "DLOG", "DLOGF", "DDUMP", "DDUMPC", "DDUMPCC", "DDUMPCCC", "DDUMPM", + "RLOG", "RLOGF", "RDUMP", "RDUMPC", "RDUMPCC", "RDUMPCCC", "RDUMPM", + "LOGBEGIN", "LOGEND", "LOGBLOCK", "LOGHEXDUMP", "LOGSRCPOS", + "RLOGBEGIN", "RLOGEND", "RLOGBLOCK", "RLOGHEXDUMP", "RLOGSRCPOS", "RQUOTE", + "RTIMING", "TIMING", "LTIMING", "DTIMING", + "LOGHEX", "DUMPHEX", "DLOGHEX", "DDUMPHEX", "RLOGHEX", "RDUMPHEX", "LLOGHEX", "LDUMPHEX", + "DEBUGCODE", + NULL + }; + static const char *sql_base[] = { + "Select", "Update", "Insert", "Delete", "From", + "Join", "InnerJoin", "LeftJoin", "RightJoin", "FullJoin", "OuterJoin", + "Where", "On", "OrderBy", "GroupBy", + "Of", "As", "StartWith", "ConnectBy", "Having", "ForUpdate", "NoWait", "Limit", + "Offset", "Hint", "SQL", + NULL + }; + static const char *sql_func[] = { + "Decode", "Distinct", "All", "SqlAll", "Count", "Descending", + "SqlMax", "SqlMin", "SqlSum", "Avg", "Stddev", "Variance", + "Greatest", "Least", "ConvertCharset", "ConvertAscii", + "Upper", "Lower", "Substr", "Instr", "Wild", "SqlDate", "AddMonths", "LastDay", + "MonthsBetween", "NextDay", "SqlNvl", "Prior", "NextVal", "CurrVal", "SqlArg", + NULL + }; + static const char *sql_bool[] = { + "SqlIsNull", "NotNull", "Like", "LikeUpperAscii", "NotLike", "Between", + "NotBetween", "In", "NotIn", "Exists", "NotExists", + NULL + }; + static const char *tfile[] = { + "T_", + NULL, + }; + static const char *tlng[] = { + "enUS", "enGB", "enAU", "enCA", "enNZ", "enIE", "enZA", "enJM", "enCB", "enBZ", + "enTT", "bgBG", "csCZ", "daDK", "deDE", "deCH", "deAT", "deLU", "deLI", "elGR", + "esES", "esMX", "esES", "esGT", "esCR", "esPA", "esDO", "esVE", "esCO", "esPE", + "esAR", "esEC", "esCL", "esUY", "esPY", "esBO", "esSV", "esHN", "esNI", "esPR", + "fiFI", "frFR", "frBE", "frCA", "frCH", "frLU", "huHU", "isIS", "itIT", "itCH", + "nlNL", "nlBE", "noNO", "noNO", "plPL", "ptBR", "ptPT", "roRO", "ruRU", "hrHR", + "srSP", "srSP", "skSK", "svSE", "svFI", "trTR", "slSI", "afZA", "sqAL", "euES", + "beBY", "caES", "etEE", "foFO", "idID", "lvLV", "ltLT", "ukUA", "zhCN", "zhTW", + "koKR", "jaJP", + NULL + }; + static const char *lay[] = { + "LAYOUT", "ITEM", "UNTYPED", "END_LAYOUT", + NULL + }; + static const char *sch[] = { + "BIT", "BIT_ARRAY", "BIT_", "BIT_ARRAY_", + "BOOL", "BOOL_ARRAY", "BOOL_", "BOOL_ARRAY_", + "INT", "INT_ARRAY", "INT_", "INT_ARRAY_", + "DOUBLE", "DOUBLE_ARRAY", "DOUBLE_", "DOUBLE_ARRAY_", + "DATE", "DATE_ARRAY", "DATE_", "DATE_ARRAY_", + "DATETIME", "DATETIME_ARRAY", "DATETIME_", "DATETIME_ARRAY_", + "TIME", "TIME_ARRAY", "TIME_", "TIME_ARRAY_", + "STRING", "STRING_ARRAY", "STRING_", "STRING_ARRAY_", + "LONG", "LONG_", "LONGRAW", "LONGRAW_", "BLOB", "BLOB_", "CLOB", "CLOB_", + "AUTO_INCREMENT", "KEY", "NOT_NULL", "TIMESTAMP", "COMMENT", "SEQUENCE", "SEQUENCE_", + "PRIMARY_KEY", "INDEX", "PARTIAL_INDEX", "UNIQUE", "SQLDEFAULT", "REFERENCES", "REFERENCES_", + "REFERENCES_CASCADE", "REFERENCES_CASCADE_", "DUAL_PRIMARY_KEY", "DUAL_UNIQUE", + "UNIQUE_LIST", "SQLCHECK", + "TABLE", "TABLE_", "END_TABLE", "TABLE_I", "TABLE_I_", "TABLE_II", + "TABLE_II_", "TABLE_III", "TABLE_III_", "VAR", "VAR_", + "COLUMN", "COLUMN_ARRAY", "ATTRIBUTE", "INLINE_ATTRIBUTE", + "TYPE", "TYPE_I", "TYPE_II", "TYPE_III", "END_TYPE", + "TYPE_", "TYPE_I_", "TYPE_II_", "TYPE_III_", "SERIAL", "ISERIAL", + "INDEX_LIST", + NULL + }; + static const char *sql[] = { + "ABORT", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ATTACH", + "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CHECK", + "COLLATE", "COLUMN", "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", + "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", + "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH", "END", "EXCEPT", + "EXCLUSIVE", "EXISTS", "FOREIGN", "FROM", "FULL", "GROUP", "HAVING", + "IN", "INDEX", "INITIALLY", "INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", + "IS", "ISNULL", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NOT", + "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PRIMARY", "RAISE", + "REFERENCES", "REPLACE", "RESTRICT", "UPDATE", "SET", "WHERE", "NEW", "OLD", "TRIGGER", + "SELECT", "TABLE", + NULL + }; + static const char *empty[] = { + NULL + }; + static const char *javascriptn[] = { + "alert", "eval", "toString", "valueOf", "length", + NULL + }; + + LoadSyntax(cpp, upp); // Order here is important, must be the same as enum + LoadSyntax(usc, usclib); + LoadSyntax(java, empty); + LoadSyntax(tfile, tlng); + LoadSyntax(usc, usclib); + LoadSyntax(lay, empty); + LoadSyntax(sch, empty); + LoadSyntax(sql, empty); + LoadSyntax(cs, empty); + LoadSyntax(javascript, javascriptn); + LoadSyntax(css, cssn); + LoadSyntax(empty, empty); + + kw_macros = InitUpp(upp_macros); + kw_logs = InitUpp(upp_logs); + kw_sql_base = InitUpp(sql_base); + kw_sql_func = InitUpp(sql_func); + InitUpp(sql_bool); +} + +int CSyntax::LoadSyntax(const char *keywords[], const char *names[]) // Changed +{ + Index &key = keyword.Add() ; + while(*keywords) + key.Add(*keywords++); + Index &nam = name.Add(); + while(*names) + nam.Add(*names++); + return keyword.GetCount() - 1; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/CodeEditor/CLogic.cpp b/uppsrc/CodeEditor/CLogic.cpp new file mode 100644 index 000000000..c45a28c58 --- /dev/null +++ b/uppsrc/CodeEditor/CLogic.cpp @@ -0,0 +1,185 @@ +#include "CodeEditor.h" + +NAMESPACE_UPP + +void CSyntax::IndentInsert(CodeEditor& e, int chr, int count) +{ + if(chr == '\n') { + while(count--) { + int pp = e.GetCursor(); + int cl = e.GetLinePos(pp); + WString pl = e.GetWLine(cl); + e.InsertChar('\n', 1); + int p = e.GetCursor(); + int l = e.GetLinePos(p); + One esyntax; + CSyntax *syntax = NULL; + if(e.GetLineLength(l) == p) { // At the last char of line + esyntax = e.GetSyntax(e.GetCursorLine()); + syntax = dynamic_cast(~esyntax); + } + if(syntax && syntax->par.GetCount() && syntax->par.Top().line == cl && no_parenthesis_indent) { + for(int i = 0; i < syntax->par.Top().pos && i < pl.GetLength(); i++) // Indent e.g. next line of if( + e.InsertChar(pl[i] == '\t' ? '\t' : ' ', 1); + return; + } + if(syntax && syntax->endstmtline == cl && syntax->seline >= 0 && syntax->seline < e.GetLineCount()) + pl = e.GetWLine(syntax->seline); + else { + int i; + for(i = 0; i < pl.GetLength(); i++) { + if(pl[i] != ' ' && pl[i] != '\t') + break; + } + if(pp < i) + return; + } + const wchar *s = pl; + while(*s == '\t' || *s == ' ') + e.InsertChar(*s++, 1); + if(!syntax) + return; + if(syntax->stmtline == cl || syntax->blk.GetCount() && syntax->blk.Top() == cl) { + if(indent_spaces || (s > pl && s[-1] == ' ')) + e.InsertChar(' ', indent_amount); + else + e.InsertChar('\t', 1); + } + } + return; + } + // {, } inserted on line alone should be moved left sometimes: + int cl = e.GetCursorLine(); + WString l = e.GetWLine(cl); + if(chr != '{' && chr != '}' || count > 1) { + e.InsertChar(chr, 1, true); + return; + } + const wchar *s; + for(s = l; s < l.End(); s++) + if(*s != ' ' && *s != '\t') { + e.InsertChar(chr, 1); + return; + } + int len = l.GetLength(); + WString tl; + int pos = e.GetPos(cl); + if(chr == '{' && cl > 0 && stmtline == cl - 1 && e.GetHighlight() >= 0) // TODO: Remove GetHighight + tl = e.GetWLine(cl - 1); + else + if(chr == '}' && blk.GetCount() && e.GetHighlight() >= 0) // TODO: Remove HetHighlight + tl = e.GetWLine(blk.Top()); + else { + e.InsertChar(chr, 1); + return; + } + e.SetCursor(pos); + e.Remove(pos, len); + s = tl; + while(*s == '\t' || *s == ' ') + e.InsertChar(*s++, 1); + e.InsertChar(chr, 1); +} + +bool NotEscape(int pos, const WString& s) +{ + return pos == 0 || s[pos - 1] != '\\' ? true : !NotEscape(pos - 1, s); +} + +bool CSyntax::CheckBracket(CodeEditor& e, int li, int pos, int ppos, int pos0, WString ln, int d, int limit, + int& bpos0, int& bpos) +{ + int li0 = li; + int lvl = 1; + pos += d; + ppos += d; + for(;;) { + int c; + for(;;) { + while(pos < 0 || pos >= ln.GetLength()) { + li += d; + if(d * li >= d * limit) + return false; + ln = e.GetWLine(li); + int q = ln.Find("//"); + if(q >= 0) + ln = ln.Mid(0, q) + WString(' ', ln.GetCount() - q); + pos = d < 0 ? ln.GetLength() - 1 : 0; + ppos += d; + } + c = ln[pos]; + if((c == '\"' || c == '\'') && (pos > 0 && NotEscape(pos, ln) && ln[pos - 1] != '\'')) { + pos += d; + ppos += d; + int lc = c; + while(pos < ln.GetLength() && pos > 0) { + if(ln[pos] == lc && NotEscape(pos, ln)) { + pos += d; + ppos += d; + break; + } + pos += d; + ppos += d; + } + } + else + break; + } + if(islbrkt(c)) + lvl += d; + if(isrbrkt(c)) + lvl -= d; + if(lvl <= 0) { + bpos0 = pos0; + bpos = ppos; + return true; + } + pos += d; + ppos += d; + } + return false; +} + +bool CSyntax::CheckLeftBracket(CodeEditor& e, int pos, int& bpos0, int& bpos) +{ + if(pos < 0 || pos >= e.GetLength()) + return false; + int ppos = pos; + int li = e.GetLinePos(pos); + WString ln = e.GetWLine(li); + return islbrkt(ln[pos]) && + CheckBracket(e, li, pos, ppos, ppos, ln, 1, min(li + 3000, e.GetLineCount()), bpos0, bpos); +} + +bool CSyntax::CheckRightBracket(CodeEditor& e, int pos, int& bpos0, int& bpos) +{ + if(pos < 0 || pos >= e.GetLength()) + return false; + int ppos = pos; + int li = e.GetLinePos(pos); + WString ln = e.GetWLine(li); + int c = ln[pos]; + return isrbrkt(ln[pos]) && + CheckBracket(e, li, pos, ppos, ppos, ln, -1, max(li - 3000, 0), bpos0, bpos); +} + +bool CSyntax::CheckBrackets(CodeEditor& e, int& bpos0, int& bpos) +{ + int c = e.GetCursor(); + return CheckLeftBracket(e, c, bpos0, bpos) || + CheckRightBracket(e, c, bpos0, bpos) || + CheckLeftBracket(e, c - 1, bpos0, bpos) || + CheckRightBracket(e, c - 1, bpos0, bpos); +} + +bool CSyntax::CanAssist() const +{ + return !comment && !string && !linecomment; +} + +Vector CSyntax::PickIfStack() +{ + return pick(ifstack); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/CodeEditor/CRegister.icpp b/uppsrc/CodeEditor/CRegister.icpp new file mode 100644 index 000000000..3383b65e9 --- /dev/null +++ b/uppsrc/CodeEditor/CRegister.icpp @@ -0,0 +1,33 @@ +#include "CodeEditor.h" + +NAMESPACE_UPP + +void CreateCSyntax(One& e, int kind) +{ + CSyntax& s = e.Create(); + s.SetHighlight(kind); +} + +void RegisterCSyntax(const char *id, int kind, + const char *exts, const char *description) { + EditorSyntax::Register(id, callback1(CreateCSyntax, kind), exts, description); +} + +INITBLOCK { + RegisterCSyntax("cpp", CSyntax::HIGHLIGHT_CPP, + "*.c *.cpp *.cc *.cxx *.h *.hpp *.hh *.hxx *.m *.mm *.icpp *.conf", + "C/C++"); + RegisterCSyntax("java", CSyntax::HIGHLIGHT_JAVA, "*.java", "Java"); + RegisterCSyntax("js", CSyntax::HIGHLIGHT_JAVASCRIPT, "*.js", "JavaScript"); + RegisterCSyntax("cs", CSyntax::HIGHLIGHT_CS, "*.cs", "C#"); + RegisterCSyntax("json", CSyntax::HIGHLIGHT_JSON, "*.json", "JSON"); + RegisterCSyntax("css", CSyntax::HIGHLIGHT_CSS, "*.css", "Cascading Style Sheet"); + RegisterCSyntax("sql", CSyntax::HIGHLIGHT_SQL, "*.sql", "SQL script"); + RegisterCSyntax("lay", CSyntax::HIGHLIGHT_LAY, "*.lay", "U++ layout (.lay)"); + RegisterCSyntax("sch", CSyntax::HIGHLIGHT_SCH, "*.sch", "U++ SQL schema (.sch)"); + RegisterCSyntax("t", CSyntax::HIGHLIGHT_T, "*.t *.jt", "U++ translation (.t)"); + RegisterCSyntax("usc", CSyntax::HIGHLIGHT_USC, "*.usc", "U++ widget definitions (.usc)"); + RegisterCSyntax("calc", CSyntax::HIGHLIGHT_CALC, "", ""); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/CodeEditor/CSyntax.cpp b/uppsrc/CodeEditor/CSyntax.cpp new file mode 100644 index 000000000..2a8956623 --- /dev/null +++ b/uppsrc/CodeEditor/CSyntax.cpp @@ -0,0 +1,348 @@ +#include "CodeEditor.h" + +NAMESPACE_UPP + +const wchar *eatstring(const wchar *s) { + int delim = *s++; + while(*s) + if(*s == '\\' && s[1]) + s += 2; + else + if(*s == delim) + return s + 1; + else + s++; + return s; +} + +inline const wchar *strnext(const wchar *p, const wchar *end, int ch) { + while(p < end) { + if(*p == ch) return p; + p++; + } + return NULL; +} + +void CSyntax::ClearBraces() { + cl = bl = pl = 0; + spar = 0; + brk.Clear(); + blk.Clear(); + bid.Clear(); + bid.Add(0); + par.Clear(); +} + +void CSyntax::Clear() { + ClearBraces(); + linecont = linecomment = comment = string = false; + macro = MACRO_OFF; + stmtline = endstmtline = seline = -1; + was_namespace = false; + ifstack.Clear(); +} + +const wchar *isstmt(const wchar *p) { + static const char *stmt[] = { + "if", "else", "while", "do", "for" + }; + for(const char **q = stmt; q < stmt + __countof(stmt); q++) { + const char *k = *q; + const wchar *s = p; + for(;;) { + if(*k== '\0') { + if(!iscidl(*s)) return s; + break; + } + if(*s != *k) + break; + s++; + k++; + } + } + return NULL; +} + +static WString sReadLn(const wchar *p) +{ + WStringBuffer wbuf; + while(*p && *p <= ' ') + p++; + while(*p && !(*p == '/' && (p[1] == '/' || p[1] == '*'))) { + if(*p <= ' ') { + while(*++p && *p <= ' ') + ; + if(*p && !(*p == '/' && (p[1] == '/' || p[1] == '*'))) + wbuf.Cat(' '); + } + else + wbuf.Cat(*p++); + } + return wbuf; +} + +int LastC(const wchar *b, const wchar *e) +{ + if(e == b) + return 0; + return *e; +} + +void CSyntax::Grounding(const wchar *b, const wchar *e) +{ + if(b >= e || comment || !iscib(*b)) + return; + e--; + while(e > b && (*e == '\t' || *e == ' ' )) + e--; + if(*e != ':') + ClearBraces(); +} + +void CSyntax::ScanSyntax(const wchar *ln, const wchar *e, int line, int tab_size) +{ + CTIMING("ScanSyntax"); + Grounding(ln, e); + if(!linecont) { + linecomment = false; + string = false; + } + if(macro != CSyntax::MACRO_CONT) + macro = CSyntax::MACRO_OFF; + linecont = e > ln && e[-1] == '\\'; + const wchar *p = ln; + int lindent = 0, pos = 0; + while(p < e && (*p == '\t' || *p == ' ')) { + if(*p++ == '\t' || ++pos >= tab_size) { + pos = 0; + lindent++; + } + } + if(!comment && *p == '#') { + while(++p < e && (*p == ' ' || *p == '\t')) + p++; + const wchar *id = p; + while(p < e && iscidl(*p)) + p++; + int idlen = int(p - id); + if(id[0] == 'i' && id[1] == 'f' + && (idlen == 2 || idlen == 5 && id[2] == 'd' && id[3] == 'e' && id[4] == 'f' + || idlen == 6 && id[2] == 'n' && id[3] == 'd' && id[4] == 'e' && id[5] == 'f')) { + IfState& ifstate = ifstack.Add(); + ifstate.state = IfState::IF; + ifstate.iftext = sReadLn(ln); + ifstate.ifline = line + 1; + } + switch(idlen) + { + case 6: + if(id[0] == 'd' && id[1] == 'e' && id[2] == 'f' && id[3] == 'i' && id[4] == 'n' && id[5] == 'e') + macro = CSyntax::MACRO_CONT; + break; + + case 4: + if(id[0] == 'e' && id[1] == 'l') { + if(id[2] == 'i' && id[3] == 'f') { + if(ifstack.GetCount() == 0) { + IfState& ifstate = ifstack.Add(); + ifstate.ifline = 0; + ifstate.state = IfState::ELSE_ERROR; + } + else { + IfState& ifstate = ifstack.Top(); + if(ifstate.state == IfState::IF || ifstate.state == IfState::ELIF) { + ifstate.state = IfState::ELIF; + ifstate.iftext = WString().Cat() << sReadLn(ln) << ", " << ifstate.iftext; + } + else + ifstate.state = IfState::ELSE_ERROR; + } + } + else + if(id[2] == 's' && id[3] == 'e') { + if(ifstack.GetCount() == 0) { + IfState& ifstate = ifstack.Add(); + ifstate.ifline = 0; + ifstate.state = IfState::ELSE_ERROR; + } + else { + IfState& ifstate = ifstack.Top(); + if(ifstate.state == IfState::IF || ifstate.state == IfState::ELIF) { + ifstate.state = IfState::ELSE; + ifstate.iftext = "#else, " + ifstate.iftext; + } + else + ifstate.state = IfState::ELSE_ERROR; + } + } + } + break; + + case 5: + if(id[0] == 'e' && id[1] == 'n' && id[2] == 'd' && id[3] == 'i' && id[4] == 'f') + { + int itop = ifstack.GetCount() - 1; + if(itop < 0) { + IfState& ifstate = ifstack.Add(); + ifstate.ifline = 0; + ifstate.state = IfState::ENDIF_ERROR; + } + else if(ifstack[itop].state != IfState::ENDIF_ERROR) + ifstack.Trim(itop); + } + break; + } + } + if(macro == CSyntax::MACRO_CONT && !(p < e && e[-1] == '\\')) + macro = CSyntax::MACRO_END; + for(;;) { + if(comment) { + p = strnext(p, e, '*'); + if(!p) break; + if(*++p == '/') { + comment = false; + p++; + } + } + else { + int pc = 0; + for(;;) { + if(p >= e) return; + const wchar *pp; + if(!iscidl(pc) && (pp = isstmt(p)) != NULL) { + stmtline = line; + spar = 0; + pc = 0; + p = pp; + } + else + if(!iscidl(pc) && p[0] == 'n' && p[1] == 'a' && p[2] == 'm' && p[3] == 'e' && + p[4] == 's' && p[5] == 'p' && p[6] == 'a' && p[7] == 'c' && p[8] == 'e' && + !iscidl(p[9])) { + was_namespace = true; + p += 9; + } + else { + int c = *p++; + if(c == '/') break; + if(c == '\'' || c == '\"') { + p = eatstring(p - 1); + if(p >= e) { + string = true; + return; + } + } + else + if(c == ';' && spar == 0) { + seline = stmtline; + endstmtline = line; + stmtline = -1; + was_namespace = false; + } + else + if(c == '{') { + if(was_namespace) { + brk.Add(0); + was_namespace = false; + } + else { + cl++; + brk.Add('}'); + bid.Add(lindent + 1); + } + blk.Add() = line; + stmtline = -1; + par.Clear(); + } + else + if(c == '}') { + if(brk.GetCount()) { + if(brk.Top()) { + cl--; + if(bid.GetCount() > 1) + bid.Drop(); + } + brk.Drop(); + } + if(blk.GetCount()) + blk.Drop(); + stmtline = -1; + par.Clear(); + } + else + if(c == '(') { + pl++; + brk.Add(')'); + Isx& m = par.Add(); + m.line = line; + m.pos = int(p - ln); + spar++; + } + else + if(c == '[') { + bl++; + brk.Add(']'); + Isx& m = par.Add(); + m.line = line; + m.pos = int(p - ln); + spar++; + } + else + if(c == ')') { + if(brk.GetCount()) { + pl--; + brk.Drop(); + } + if(par.GetCount()) + par.Drop(); + spar--; + } + else + if(c == ']') { + if(brk.GetCount()) { + bl--; + brk.Drop(); + } + if(par.GetCount()) + par.Drop(); + spar--; + } + pc = c; + } + } + if(*p == '/') { + linecomment = true; + return; + } + if(*p == '*') { + comment = true; + p++; + } + } + } +} + +void CSyntax::Serialize(Stream& s) +{ + s % comment; + s % linecomment; + s % string; + s % linecont; + s % was_namespace; + s % macro; + + s % cl % bl % pl; + + s % brk; + s % blk; + s % bid; + s % par; + s % ifstack; + + s % stmtline; + s % endstmtline; + s % seline; + s % spar; +}; + +END_UPP_NAMESPACE diff --git a/uppsrc/CodeEditor/HighlightOut.cpp b/uppsrc/CodeEditor/HighlightOut.cpp new file mode 100644 index 000000000..e6ea8d226 --- /dev/null +++ b/uppsrc/CodeEditor/HighlightOut.cpp @@ -0,0 +1,126 @@ +#include "CodeEditor.h" + +NAMESPACE_UPP + +HighlightOutput::HighlightOutput(Vector& v) +: v(v) +{ + pos = 0; + def = v[0]; + def.chr = ' '; + v.Top().chr = ' '; +} + +HighlightOutput::~HighlightOutput() +{ + for(int i = 0; i < v.GetCount(); i++) + if(v[i].chr == '_') { + v[i].font.NoBold(); + v[i].font.NoItalic(); + } +} + +void HighlightOutput::Set(int pos, int count, const HlStyle& ink) +{ + if(pos + count > v.GetCount()) + v.At(pos + count - 1, def); + while(count-- > 0) { + LineEdit::Highlight& x = v[pos++]; + x.ink = ink.color; + if(ink.bold) + x.font.Bold(); + if(ink.italic) + x.font.Italic(); + if(ink.underline) + x.font.Underline(); + } +} + +void HighlightOutput::SetFont(int pos, int count, const HlStyle& f) +{ + if(pos + count > v.GetCount()) + v.At(pos + count - 1, def); + while(count-- > 0) { + LineEdit::Highlight& x = v[pos++]; + if(f.bold) + x.font.Bold(); + if(f.italic) + x.font.Italic(); + if(f.underline) + x.font.Underline(); + } +} + +void HighlightOutput::SetPaper(int pos, int count, Color paper) +{ + if(pos + count > v.GetCount()) + v.At(pos + count - 1, def); + while(count-- > 0) + v[pos++].paper = paper; +} + +void HighlightOutput::SetInk(int pos, int count, Color ink) +{ + if(pos + count > v.GetCount()) + v.At(pos + count - 1, def); + while(count-- > 0) + v[pos++].ink = ink; +} + +const wchar *HighlightOutput::CString(const wchar *p) +{ + Put(hl_style[INK_CONST_STRING]); + const wchar delim = *p; + p++; + while(*p && *p != delim) + if(*p == '\\') { + const wchar *t = p++; + if(*p == 'x' || *p == 'X') { + p++; + if(IsXDigit(*p)) + p++; + if(IsXDigit(*p)) + p++; + Put(int(p - t), hl_style[INK_CONST_STRINGOP]); + } + else + if(*p >= '0' && *p <= '7') { + p++; + if(*p >= '0' && *p <= '7') p++; + if(*p >= '0' && *p <= '7') p++; + Put(int(p - t), hl_style[INK_CONST_STRINGOP]); + } + else { + Put(hl_style[INK_CONST_STRINGOP]); + if(*p) { + Put(hl_style[INK_CONST_STRINGOP]); + p++; + } + } + } + else + if(*p == '%') + if(p[1] == '%') { + Put(2, hl_style[INK_CONST_STRING]); + p += 2; + } + else { + const wchar *t = p++; + while(!IsAlpha(*p) && *p && *p != '`' && *p != '\"' && *p != '\'' && *p != '\\') + p++; + while(IsAlpha(*p) && *p) + p++; + Put(int(p - t), hl_style[INK_CONST_STRINGOP]); + } + else { + Put(hl_style[INK_CONST_STRING]); + p++; + } + if(*p == delim) { + Put(hl_style[INK_CONST_STRING]); + p++; + } + return p; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/CodeEditor/Register.cpp b/uppsrc/CodeEditor/Register.cpp new file mode 100644 index 000000000..ba2af675a --- /dev/null +++ b/uppsrc/CodeEditor/Register.cpp @@ -0,0 +1,40 @@ +#include "CodeEditor.h" + +NAMESPACE_UPP + +ArrayMap& EditorSyntax::defs() +{ + static ArrayMap d; + return d; +} + +void EditorSyntax::Register(const char *id, Callback1&> factory, + const char *exts, const char *description) +{ + SyntaxDef& f = defs().GetAdd(id); + f.factory = factory; + f.patterns = exts; + f.description = description; +} + +One EditorSyntax::Create(const char *id) +{ + One s; + SyntaxDef *f = defs().FindPtr(id); + if(f) + f->factory(s); + if(!s) + s.Create(); + return s; +} + +String EditorSyntax::GetSyntaxForFilename(const char *fn) +{ + ArrayMap& d = defs(); + for(int i = 0; i < d.GetCount(); i++) + if(PatternMatchMulti(d[i].patterns, fn)) + return d.GetKey(i); + return Null; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/CodeEditor/Style.cpp b/uppsrc/CodeEditor/Style.cpp new file mode 100644 index 000000000..abd59419d --- /dev/null +++ b/uppsrc/CodeEditor/Style.cpp @@ -0,0 +1,157 @@ +#include "CodeEditor.h" + +NAMESPACE_UPP + +HlStyle HighlightSetup::hl_style[HL_COUNT]; +byte HighlightSetup::hilite_scope = 0; +byte HighlightSetup::hilite_ifdef = 1; +byte HighlightSetup::hilite_bracket = 1; +bool HighlightSetup::thousands_separator = true; + +bool HighlightSetup::indent_spaces = false; +bool HighlightSetup::no_parenthesis_indent = false; +int HighlightSetup::indent_amount = 4; + +#define HL_COLOR(x, a, b) #x, +static const char *s_hl_color[] = { +#include "hl_color.i" +}; +#undef HL_COLOR + +#define HL_COLOR(a, x, b) x, +static const char *s_hl_name[] = { +#include "hl_color.i" +}; +#undef HL_COLOR + +#define HL_COLOR(a, b, x) x, +static bool s_hl_font[] = { +#include "hl_color.i" +}; +#undef HL_COLOR + +const HlStyle& HighlightSetup::GetHlStyle(int i) +{ + return hl_style[i]; +} + +const char *HighlightSetup::GetHlName(int i) +{ + return s_hl_name[i]; +} + +bool HighlightSetup::HasHlFont(int i) +{ + return s_hl_font[i]; +} + +void HighlightSetup::SetHlStyle(int i, Color c, bool bold, bool italic, bool underline) +{ + HlStyle& st = hl_style[i]; + st.color = c; + st.bold = bold; + st.italic = italic; + st.underline = underline; +} + +void HighlightSetup::LoadHlStyles(const char *s) +{ + CParser p(s); + try { + while(!p.IsEof()) { + String id = p.ReadId(); + Color c = ReadColor(p); + bool bold = false; + bool italic = false; + bool underline = false; + for(;;) + if(p.Id("bold")) + bold = true; + else + if(p.Id("italic")) + italic = true; + else + if(p.Id("underline")) + underline = true; + else + break; + for(int i = 0; i < HL_COUNT; i++) + if(id == s_hl_color[i]) { + SetHlStyle(i, c, bold, italic, underline); + break; + } + p.PassChar(';'); + } + } + catch(CParser::Error) { + DefaultHlStyles(); + } +} + +String HighlightSetup::StoreHlStyles() +{ + String r; + for(int i = 0; i < HL_COUNT; i++) { + const HlStyle& s = GetHlStyle(i); + r << Format("%-25s", s_hl_color[i]) << ' ' << FormatColor(s.color); + if(s.bold) + r << " bold"; + if(s.italic) + r << " italic"; + if(s.underline) + r << " underline"; + r << ";\r\n"; + } + return r; +} + +void HighlightSetup::DefaultHlStyles() +{ + SetHlStyle(INK_COMMENT, Green, false, true); + SetHlStyle(INK_CONST_STRING, Red); + + SetHlStyle(INK_CONST_STRINGOP, LtBlue); + SetHlStyle(INK_CONST_INT, Red); + SetHlStyle(INK_CONST_FLOAT, Magenta); + SetHlStyle(INK_CONST_HEX, Blue); + SetHlStyle(INK_CONST_OCT, Blue); + + SetHlStyle(INK_OPERATOR, LtBlue); + SetHlStyle(INK_KEYWORD, LtBlue, true); + SetHlStyle(INK_UPP, Cyan); + SetHlStyle(PAPER_LNG, Color(255, 255, 224)); + SetHlStyle(INK_ERROR, LtRed); + SetHlStyle(INK_PAR0, Black); + SetHlStyle(INK_PAR1, Green); + SetHlStyle(INK_PAR2, Magenta); + SetHlStyle(INK_PAR3, Brown); + + SetHlStyle(INK_UPPER, Black); + SetHlStyle(INK_SQLBASE, Black); + SetHlStyle(INK_SQLFUNC, Black); + SetHlStyle(INK_SQLBOOL, Black); + SetHlStyle(INK_UPPMACROS, Cyan); + SetHlStyle(INK_UPPLOGS, Green); + + SetHlStyle(PAPER_BLOCK1, Blend(LtBlue, White, 240)); + SetHlStyle(PAPER_BLOCK2, Blend(LtGreen, White, 240)); + SetHlStyle(PAPER_BLOCK3, Blend(LtYellow, White, 240)); + SetHlStyle(PAPER_BLOCK4, Blend(LtMagenta, White, 240)); + + SetHlStyle(INK_MACRO, Magenta); + SetHlStyle(PAPER_MACRO, Color(255, 255, 230)); + SetHlStyle(PAPER_IFDEF, Color(230, 255, 255)); + SetHlStyle(INK_IFDEF, Color(170, 170, 170)); + + SetHlStyle(PAPER_BRACKET0, LtYellow); + SetHlStyle(PAPER_BRACKET, Yellow, true); + + SetHlStyle(INK_NORMAL, SColorText); + SetHlStyle(INK_DISABLED, SColorDisabled); + SetHlStyle(INK_SELECTED, SColorHighlightText); + SetHlStyle(PAPER_NORMAL, SColorPaper); + SetHlStyle(PAPER_READONLY, SColorFace); + SetHlStyle(PAPER_SELECTED, SColorHighlight); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/CodeEditor/Syntax.h b/uppsrc/CodeEditor/Syntax.h new file mode 100644 index 000000000..25a42497e --- /dev/null +++ b/uppsrc/CodeEditor/Syntax.h @@ -0,0 +1,197 @@ +#ifndef _CodeEditor_Highlight_h_ +#define _CodeEditor_Highlight_h_ + +#define CTIMING(x) RTIMING(x) + +struct HlStyle { + Color color; + bool bold; + bool italic; + bool underline; +}; + +struct Isx : Moveable { // '(', '[' position + int line; + int pos; + + void Serialize(Stream& s) { s % line % pos; } + + friend bool operator==(Isx a, Isx b) { return a.line == b.line && a.pos == b.pos; } + friend bool operator!=(Isx a, Isx b) { return !(a == b); } +}; + +struct IfState : Moveable { + enum { IF = '0', ELIF, ELSE, ELSE_ERROR, ENDIF_ERROR }; + + WString iftext; + short ifline; + char state; + + void Serialize(Stream& s) { s % iftext % ifline % state; } + + bool operator==(const IfState& b) const { + return iftext == b.iftext && state == b.state && ifline == b.ifline; + } + + IfState() { ifline = state = 0; } +}; + +struct HighlightSetup { // Global highlighting settings +public: +#define HL_COLOR(x, a, b) x, + enum { +#include "hl_color.i" + HL_COUNT + }; +#undef HL_COLOR + + static HlStyle hl_style[HL_COUNT]; + static byte hilite_scope; + static byte hilite_ifdef; + static byte hilite_bracket; + static bool thousands_separator; + static bool indent_spaces; + static int indent_amount; + static bool no_parenthesis_indent; + + static const HlStyle& GetHlStyle(int i); + static void SetHlStyle(int i, Color c, bool bold = false, bool italic = false, bool underline = false); + static void LoadHlStyles(const char *s); + static String StoreHlStyles(); + static void DefaultHlStyles(); + + static const char *GetHlName(int i); + static bool HasHlFont(int i); +}; + +struct HighlightOutput : HighlightSetup { + Vector& v; + LineEdit::Highlight def; + int pos; + +public: + void Set(int pos, int count, const HlStyle& ink); + void SetFont(int pos, int count, const HlStyle& f); + void SetPaper(int pos, int count, Color paper); + void SetInk(int pos, int count, Color ink); + void Put(int count, const HlStyle& ink) { Set(pos, count, ink); pos += count; } + void Put(const HlStyle& ink) { Put(1, ink); } + void Put(const HlStyle& ink, word flags) { Put(1, ink); v[pos - 1].flags = flags; } + + const wchar *CString(const wchar *p); + + HighlightOutput(Vector& v); + ~HighlightOutput(); +}; + +class EditorSyntax : public HighlightSetup { // Inheriting to make static members available + struct SyntaxDef { + Callback1&> factory; + String patterns; + String description; + }; + + static ArrayMap& defs(); + +public: + virtual void Clear(); + virtual void ScanSyntax(const wchar *ln, const wchar *e, int line, int tab_size); + virtual void Serialize(Stream& s); + virtual void IndentInsert(CodeEditor& editor, int chr, int count); + virtual bool CheckBrackets(CodeEditor& e, int& bpos0, int& bpos); + virtual bool CanAssist() const; + virtual void Highlight(CodeEditor& editor, int line, Vector& hl, int pos); + virtual Vector PickIfStack(); + virtual ~EditorSyntax(); + + static Color IfColor(char ifstate); + + void Set(const String& s) { CTIMING("Set"); if(s.GetCount() == 0) Clear(); else LoadFromString(*this, s); } + String Get() { CTIMING("Get"); return StoreAsString(*this); } + + EditorSyntax() { Clear(); } + + static void Register(const char *id, Callback1&> factory, + const char *exts, const char *description); + static One Create(const char *id); + static String GetSyntaxForFilename(const char *fn); + static int GetSyntaxCount() { return defs().GetCount(); } + static String GetSyntax(int i) { return defs().GetKey(i); } + static String GetSyntaxDescription(int i) { return defs()[i].description; } +}; + +class CSyntax : public EditorSyntax { // Curly braces languages (C++, Java, C#, Javascript...) common support +public: + virtual void Clear(); + virtual void ScanSyntax(const wchar *ln, const wchar *e, int line, int tab_size); + virtual void Serialize(Stream& s); + virtual void IndentInsert(CodeEditor& editor, int chr, int count); + virtual bool CheckBrackets(CodeEditor& e, int& bpos0, int& bpos); + virtual bool CanAssist() const; + virtual void Highlight(CodeEditor& editor, int line, Vector& hl, int pos); + virtual Vector PickIfStack(); // TODO: Refactor? + +private: + bool comment; // we are in /* */ block comment + bool linecomment; // we are in // line comment (because it can be continued by '\') + bool string; // we are in string (becase it can be continued by '\') + bool linecont; // line ended with '\' + bool was_namespace; // true if there was 'namespace', until '{' or ';' (not in ( [ brackets) + char macro; // can be one of: + enum { + MACRO_OFF = 0, // last line was not #define + MACRO_CONT, // last line was #define and ended with '\' + MACRO_END // last line was a macro, but ended + }; + + int cl, bl, pl; // levels of { [ ( + + Vector brk; // { ( [ stack (contains '{', ')', ']') + Vector blk; // { line stack //TODO:SYNTAX: Join blk and bid + Vector bid; // { indentation stack + Vector par; // ( [ position stack + Vector ifstack; + + int stmtline; // line of latest "if", "else", "while", "do", "for" or -1 + int endstmtline; // line of latest ';' (not in ( [ brackets) + int seline; // stmtline stored here on ';' (not in ( [ brackets) + int spar; // ( [ level, reset on "if", "else", "while", "do", "for" + + int highlight; // subtype (temporary) TODO + + static int LoadSyntax(const char *keywords[], const char *names[]); + static int InitUpp(const char **q); + static void InitKeywords(); + + static Vector< Index > keyword; + static Vector< Index > name; + static Index kw_upp; + static int kw_macros, kw_logs, kw_sql_base, kw_sql_func; + + + static Color BlockColor(int level); + + void Bracket(int pos, HighlightOutput& hls, CodeEditor& editor); + + void ClearBraces(); + + void Grounding(const wchar *ln, const wchar *e); + + bool CheckBracket(CodeEditor& e, int li, int pos, int ppos, int pos0, WString ln, int d, int limit, int& bpos0, int& bpos); + bool CheckLeftBracket(CodeEditor& e, int pos, int& bpos0, int& bpos); + bool CheckRightBracket(CodeEditor& e, int pos, int& bpos0, int& bpos); + +public: + enum HighlightType { + HIGHLIGHT_NONE = -1, HIGHLIGHT_CPP = 0, HIGHLIGHT_USC, HIGHLIGHT_JAVA, HIGHLIGHT_T, + HIGHLIGHT_CALC, HIGHLIGHT_LAY, HIGHLIGHT_SCH, HIGHLIGHT_SQL, HIGHLIGHT_CS, + HIGHLIGHT_JAVASCRIPT, HIGHLIGHT_CSS, HIGHLIGHT_JSON, + HIGHLIGHT_COUNT + }; + + void SetHighlight(int h) { highlight = h; } + + CSyntax() { Clear(); } +}; + +#endif