diff --git a/src/editor/buffer.d b/src/editor/buffer.d index 5e536ec..cf2387e 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -30,6 +30,7 @@ struct LineBuffers struct LineBuffer { u8[] text; + TS[] style; LineBuffer* next; } @@ -45,7 +46,7 @@ CreateFlatBuffer(u8[] data) { u64 capacity = data.length > 0 ? RoundUp(cast(u64)(data.length) * 2, KB(4)) : KB(4); - u8[] buf = MAllocArray!(u8)(capacity); + u8[] buf = Alloc!(u8)(capacity); buf[0 .. data.length] = data[0 .. data.length]; FlatBuffer fb = { @@ -66,16 +67,16 @@ CreateFlatBuffer(u8[] data) void Change(FlatBuffer* fb, u8[] data) { - MFreeArray(fb.data); + FreeArray(fb.data); u64 cap = data.length > 0 ? RoundUp(cast(u64)(data.length) * 2, KB(4)) : KB(4); - fb.data = MAllocArray!(u8)(cap); + fb.data = Alloc!(u8)(cap); fb.data[0 .. data.length] = data[0 .. $]; fb.length = data.length; fb.lf_count = CountLF(data); - Change(fb); + ResetTokenizer(fb); Fix(fb); } @@ -116,8 +117,7 @@ Fix(FlatBuffer* buffer) dirty = false; - // TODO: fix - //Tokenize(buffer); + TokenizeD(buffer); } } @@ -259,6 +259,13 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) linebufs.first.text = AllocArray!(u8)(&linebufs.arena, len); linebufs.first.text = data[start_of_line .. start_of_line+len]; + + if(!Nil(buffer.tk.first)) + { + linebufs.first.style = Alloc!(TS)(&linebufs.arena, len); + linebufs.first.style = buffer.tk.buffer[start_of_line .. start_of_line+len]; + } + linebufs.count += 1; } else with(linebufs) @@ -271,8 +278,15 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) if(len > 0) { - current.text = AllocArray!(u8)(&arena, len); + current.text = Alloc!(u8)(&arena, len); current.text[0 .. len] = buffer.data[start .. start+len]; + + if(!Nil(buffer.tk.first)) + { + current.style = Alloc!(TS)(&arena, len); + current.style[0 .. len] = buffer.tk.buffer[start .. start+len]; + } + count += 1; current.next = Alloc!(LineBuffer)(&arena); @@ -473,421 +487,6 @@ Replace(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos, u64 delete_length) Insert(buffer, insert, insert.length, pos); } -enum TokenStyle : u8 -{ - None = 0, - Keyword, - Import, - ImportTarget, - Type, - Identifier, - Op, - Bracket, - Function, - Macro, - Comment, - String, - Number, - Char, - Exclamation, -} - -alias TS = TokenStyle; - -enum TokenType : u32 -{ - None = 0, - Keyword, - Type, - Identifier, - String, - Char, - Function, - LiteralStr, - Macro, - Exclamation, - At, - Dollar, - Percent, - Caret, - Ampersand, - Asterisk, - LeftBracket, - RightBracket, - VertBar, - LeftBrace, - RightBrace, - LeftSquareBrace, - RightSquareBrace, - Tilde, - BackTick, - Semicolon, - Hash, - Colon, - Dot, - Minus, - Plus, - Equals, - Underscore, - BackSlash, - Slash, - LessThan, - GreaterThan, - Question, - Comma, - Comment, - Number, - SingleQuote, - DoubleQuote, -} - -alias TT = TokenType; - -struct Token -{ - TokenType type; - u64 start; - u64 end; -} - -struct Tokenizer -{ - Arena arena; - TokenStyle[] buffer; - Token[] tokens; - u64 tk_count; - u64 pos; -} - -Tokenizer -CreateTokenizer(FlatBuffer* fb) -{ - Tokenizer tokenizer = { - arena: CreateArena(MB(8)), - buffer: MAllocArray!(TS)(fb.data.length), - }; - - return tokenizer; -} - -void -Change(FlatBuffer* fb) -{ - MFreeArray(fb.tk.buffer); - fb.tk.buffer = MAllocArray!(TS)(fb.data.length); -} - -u8 -PeekNextChar(FlatBuffer* fb) -{ - return fb.tk.pos+1 < fb.length ? fb.data[fb.tk.pos+1] : 0; -} - -/* -bool -Advance(FlatBuffer* fb) -{ - Tokenizer* tk = &fb.tk; - tk.pos = tk.pos_end; - tk.next_type = TS.None; - u8[] buf = fb.data; - - tk.pos_next = tk.pos; - tk.pos_end_next = tk.pos_end; - - tk.pos = tk.pos == -1 ? 0 : tk.pos; - tk.pos_end = tk.pos_end == -1 ? 0 : tk.pos_end; - - bool result = false; - bool started = false; - bool str_started = false; - for(i64 i = tk.pos; i < buf.length; i += 1) - { - u8 ch = buf.ptr[i]; - bool space = CheckWhiteSpace(ch); - bool delim = CheckDelimiter(ch); - bool str = CheckString(ch); - - if(!started && delim) - { - tk.pos = i; - tk.pos_end = i+1; - result = true; - break; - } - - if(!started && !space) - { - tk.pos = i; - started = true; - str_started = str; - continue; - } - - if(started && (delim || space) && !str_started) - { - result = true; - tk.pos_end = i; - - if(ch == EOF) - { - Logf("final token"); - tk.final_token = true; - } - - switch(ch) - { - case '(': tk.next_type = TS.Bracket; break; - case '!': tk.next_type = TS.Exclamation; break; - default: break; - } - - break; - } - - if(started && str_started && str) - { - result = true; - tk.pos_end = i+1; - break; - } - } - - return result; -} - -static string -StrToU8(string str) -{ - string res = "["; - foreach(ch; str) - { - res ~= "'" ~ ch ~ "',"; - } - res ~= "]"; - - return res; -} - -void -Highlight(FlatBuffer* fb) -{ - const const(u8[])[]['z'-95] keywords = [ - '_'-95: [ - cast(u8[])r"__FILE__", - cast(u8[])r"__FILE_FULL_PATH__", - cast(u8[])r"__FUNCTION__", - cast(u8[])r"__LINE__", - cast(u8[])r"__MODULE__", - cast(u8[])r"__PRETTY_FUNCTION__", - cast(u8[])r"__gshared", - cast(u8[])r"__parameters", - cast(u8[])r"__rvalue", - cast(u8[])r"__traits", - cast(u8[])r"__vector", - ], - 'a'-95: [ - cast(u8[])r"abstract", - cast(u8[])r"alias", - cast(u8[])r"align", - cast(u8[])r"asm", - cast(u8[])r"assert", - cast(u8[])r"auto", - ], - 'b'-95: [ - cast(u8[])r"bool", - cast(u8[])r"break", - cast(u8[])r"byte", - ], - 'c'-95: [ - cast(u8[])r"case", - cast(u8[])r"cast", - cast(u8[])r"catch", - cast(u8[])r"char", - cast(u8[])r"class", - cast(u8[])r"const", - cast(u8[])r"continue", - ], - 'd'-95: [ - cast(u8[])r"dchar", - cast(u8[])r"debug", - cast(u8[])r"default", - cast(u8[])r"delegate", - cast(u8[])r"deprecated", - cast(u8[])r"do", - cast(u8[])r"double", - ], - 'e'-95: [ - cast(u8[])r"else", - cast(u8[])r"enum", - cast(u8[])r"export", - cast(u8[])r"extern", - ], - 'f'-95: [ - cast(u8[])r"false", - cast(u8[])r"final", - cast(u8[])r"finally", - cast(u8[])r"float", - cast(u8[])r"for", - cast(u8[])r"foreach", - cast(u8[])r"foreach_reverse", - cast(u8[])r"function", - ], - 'g'-95: [ - cast(u8[])r"goto", - ], - 'i'-95: [ - cast(u8[])r"if", - cast(u8[])r"immutable", - cast(u8[])r"import", - cast(u8[])r"in", - cast(u8[])r"inout", - cast(u8[])r"int", - cast(u8[])r"interface", - cast(u8[])r"invariant", - cast(u8[])r"is", - ], - 'l'-95: [ - cast(u8[])r"lazy", - cast(u8[])r"long", - ], - 'm'-95: [ - cast(u8[])r"mixin", - cast(u8[])r"module", - ], - 'n'-95: [ - cast(u8[])r"new", - cast(u8[])r"nothrow", - cast(u8[])r"null", - ], - 'o'-95: [ - cast(u8[])r"out", - cast(u8[])r"override", - ], - 'p'-95: [ - cast(u8[])r"package", - cast(u8[])r"pragma", - cast(u8[])r"private", - cast(u8[])r"protected", - cast(u8[])r"public", - cast(u8[])r"pure", - ], - 'r'-95: [ - cast(u8[])r"real", - cast(u8[])r"ref", - cast(u8[])r"return", - ], - 's'-95: [ - cast(u8[])r"scope", - cast(u8[])r"shared", - cast(u8[])r"short", - cast(u8[])r"static", - cast(u8[])r"struct", - cast(u8[])r"super", - cast(u8[])r"switch", - cast(u8[])r"synchronized", - ], - 't'-95: [ - cast(u8[])r"template", - cast(u8[])r"this", - cast(u8[])r"throw", - cast(u8[])r"true", - cast(u8[])r"try", - cast(u8[])r"typeid", - cast(u8[])r"typeof", - ], - 'u'-95: [ - cast(u8[])r"ubyte", - cast(u8[])r"uint", - cast(u8[])r"ulong", - cast(u8[])r"union", - cast(u8[])r"unittest", - cast(u8[])r"ushort", - ], - 'v'-95: [ - cast(u8[])r"version", - cast(u8[])r"void", - ], - 'w'-95: [ - cast(u8[])r"wchar", - cast(u8[])r"while", - cast(u8[])r"with", - ], - ]; - - Tokenizer* tk = &fb.tk; - u8[] token; - u8[] prev_token; - - if(tk.final_token) - { - goto InnerTokenStream; - } - -TokenStream: - while (Advance(fb)) - { -InnerTokenStream: - token = fb.data[tk.pos .. tk.pos_end]; - scope(exit) prev_token = token; - - if(token.length == 0) break; - - if(token.length == 1 && CheckDelimiter(token.ptr[0])) - { - tk.buffer[tk.pos] = TS.Bracket; - continue TokenStream; - } - - if(CheckString(token[0]) && CheckString(token[token.length-1]) && token[0] == token[token.length-1]) - { - tk.buffer[tk.pos .. tk.pos_end] = TS.String; - continue TokenStream; - } - - if(token[0] >= '0' && token[0] <= '9') - { - tk.buffer[tk.pos .. tk.pos_end] = TS.Number; - continue TokenStream; - } - - if((token[0] >= 'a' && token[0] <= 'z') || (token[0] >= 'A' && token[0] <= 'Z') || token[0] == '_') - { - if(prev_token == r"import") - { - tk.buffer[tk.pos .. tk.pos_end] = TS.ImportTarget; - continue TokenStream; - } - - if(tk.next_type == TS.Exclamation) - { - tk.buffer[tk.pos .. tk.pos_end] = TS.Macro; - continue TokenStream; - } - - u8 index = cast(u8)(token[0]-cast(u8)95); - if(index > 0 && index < keywords.length) - { - // TODO: look at simd string comparison - const(u8[])[] key_table = keywords[index]; - foreach(key; key_table) - { - if(token == key) - { - tk.buffer[tk.pos .. tk.pos_end] = TS.Keyword; - continue TokenStream; - } - } - } - - tk.buffer[tk.pos .. tk.pos_end] = (tk.next_type == TS.Bracket) ? TS.Function : TS.Identifier; - continue TokenStream; - } - } -} - -*/ - unittest { import std.stdio; diff --git a/src/editor/editor.d b/src/editor/editor.d index d1f54f9..ae6c80e 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -1,15 +1,12 @@ -import dlib.aliases; -import dlib.util; -import dlib.platform; -import dlib.fonts; +import dlib; + import vulkan; -import dlib.math; import std.format : sformat; -import dlib.alloc; import buffer; import ui; import widgets : Nil; import widgets; +import parsing; import std.format; import std.stdio; @@ -155,7 +152,7 @@ InitEditorCtx(PlatformWindow* window) cmd: { arena: CreateArena(MB(1)), cmd_arena: CreateArena(MB(1)), - buffer: MAllocArray!(u8)(1024), + buffer: Alloc!(u8)(1024), }, }; @@ -227,23 +224,19 @@ OpenFile(Editor* ed, u8[] file_name) auto f = fopen(cast(char*)file_name.ptr, "rb"); if(f != null) { - Logf("opened"); - scope(exit) fclose(f); - fseek(f, 0, SEEK_END); i64 len = ftell(f); fseek(f, 0, SEEK_SET); if(len > 0) { - Logf("b"); u8[] buf = ScratchAlloc!(u8)(len); - Logf("a"); fread(buf.ptr, u8.sizeof, len, f); + Change(&ed.buf, buf); - - Logf("read"); } + + fclose(f); } else { diff --git a/src/editor/main.d b/src/editor/main.d index 80e3c9d..d9a3a43 100644 --- a/src/editor/main.d +++ b/src/editor/main.d @@ -13,7 +13,7 @@ void main(string[] argv) { buffer_name = (cast(u8*)argv[1].ptr)[0 .. argv[1].length]; File f = File(argv[1], "rb"); - buffer = MAllocArray!(u8)(f.size()); + buffer = Alloc!(u8)(f.size()); f.rawRead(buffer); f.close(); } diff --git a/src/editor/parsing.d b/src/editor/parsing.d index b983e8a..a593e61 100644 --- a/src/editor/parsing.d +++ b/src/editor/parsing.d @@ -1,10 +1,210 @@ -import dlib.aliases; -import dlib.alloc; +import dlib; import buffer; -const const(u8[])[]['z'-95] D_KEYWORDS = [ - '_'-95: [ +enum TokenStyle : u8 +{ + None = 0, + Keyword, + Import, + ImportTarget, + Type, + Identifier, + Op, + Bracket, + Function, + Macro, + Comment, + String, + Number, + Char, + Exclamation, + Max, +} + +alias TS = TokenStyle; + +enum TokenType : u8 +{ + None = 0, + Keyword, + Type, + Identifier, + String, + Char, + Function, + LiteralStr, + Macro, + Exclamation, + At, + Dollar, + Percent, + Caret, + Ampersand, + Asterisk, + LeftParen, + RightParen, + VertBar, + LeftBrace, + RightBrace, + LeftSquareBrace, + RightSquareBrace, + Tilde, + BackTick, + Semicolon, + Hash, + Colon, + Dot, + Minus, + Plus, + Equals, + Underscore, + BackSlash, + Slash, + LessThan, + GreaterThan, + Question, + Comma, + Comment, + Number, + SingleQuote, + DoubleQuote, + Whitespace, + NewLine, + Import, + ImportTarget, + TypeKeyword, + EnumKeyword, + Max, +} + +alias TT = TokenType; + +const Vec4 GREY = Vec4(0.45, 0.45, 0.45, 1.0); +const Vec4 PURPLE = Vec4(0.67, 0.63, 0.81, 1.0); +const Vec4 YELLOW = Vec4(1.0, 1.0, 0.4, 1.0); +const Vec4 BLUE = Vec4(0.24, 0.51, 0.72, 1.0); +const Vec4 RED = Vec4(1.0, 0.0, 0.0, 1.0); +const Vec4 GREEN = Vec4(0.0, 1.0, 0.0, 1.0); +const Vec4 TURQUOISE = Vec4(0.15, 0.59, 0.76, 1.0); +const Vec4 PINK = Vec4(0.85, 0.48, 0.60, 1.0); + +const Vec4[TS.max] SYNTAX_COLORS = SyntaxCols(); + +static Vec4[TS.max] +SyntaxCols() +{ + Vec4[TS.max] colors = [ + TS.Op: YELLOW, + TS.Bracket: GREY, + TS.Function: PURPLE, + TS.Macro: PURPLE, + TS.Keyword: BLUE, + TS.String: GREEN, + TS.Char: GREEN, + TS.Import: RED, + TS.ImportTarget: RED, + TS.Type: TURQUOISE, + TS.Number: PINK, + TS.Comment: GREY, + ]; + + Vec4[TS.max] result = Vec4(0.8, 0.8, 0.8, 1.0); + + import std.traits; + + static foreach(i; TS.min .. TS.max) + { + if(colors[i].v[3] != 0.0) + { + result[i] = colors[i]; + } + } + + return result; +} + +const TokenStyle[TT.max] TOKEN_STYLES = [ + TT.Dollar: TS.Op, + TT.Hash: TS.Op, + TT.Tilde: TS.Op, + TT.Percent: TS.Op, + TT.Caret: TS.Op, + TT.Ampersand: TS.Op, + TT.Asterisk: TS.Op, + TT.Minus: TS.Op, + TT.Equals: TS.Op, + TT.Plus: TS.Op, + TT.LessThan: TS.Op, + TT.GreaterThan: TS.Op, + TT.Exclamation: TS.Op, + + TT.LeftParen: TS.Bracket, + TT.RightParen: TS.Bracket, + TT.LeftSquareBrace: TS.Bracket, + TT.RightSquareBrace: TS.Bracket, + TT.LeftBrace: TS.Bracket, + TT.RightBrace: TS.Bracket, + TT.VertBar: TS.Bracket, + TT.Semicolon: TS.Bracket, + TT.Colon: TS.Bracket, + TT.Question: TS.Bracket, + TT.Comma: TS.Bracket, + + TT.SingleQuote: TS.Char, + TT.DoubleQuote: TS.String, + TT.BackTick: TS.String, + + TT.Keyword: TS.Keyword, + TT.TypeKeyword: TS.Keyword, + TT.EnumKeyword: TS.Keyword, + TT.Function: TS.Function, + TT.Macro: TS.Macro, + TT.Type: TS.Type, + TT.Number: TS.Number, + TT.Import: TS.Import, + TT.ImportTarget: TS.Import, + TT.Comment: TS.Comment, +]; + +const TT[128] D_STD_TOKEN = [ + '$': TT.Dollar, + '(': TT.LeftParen, + ')': TT.RightParen, + '[': TT.LeftSquareBrace, + ']': TT.RightSquareBrace, + '{': TT.LeftBrace, + '}': TT.RightBrace, + '|': TT.VertBar, + ';': TT.Semicolon, + ':': TT.Colon, + '?': TT.Question, + ',': TT.Comma, + '#': TT.Hash, +]; + +const TT[128] D_OP_TOKEN = [ + '~': TT.Tilde, + '%': TT.Percent, + '^': TT.Caret, + '&': TT.Ampersand, + '*': TT.Asterisk, + '-': TT.Minus, + '=': TT.Equals, + '+': TT.Plus, + '<': TT.LessThan, + '>': TT.GreaterThan, + '!': TT.Exclamation, +]; + +const TT[128] D_STR_TOKEN = [ + '`' : TT.BackTick, + '\'': TT.SingleQuote, + '"' : TT.DoubleQuote, +]; + +u8[][][128] D_KEYWORDS = [ + '_': [ cast(u8[])r"__FILE__", cast(u8[])r"__FILE_FULL_PATH__", cast(u8[])r"__FUNCTION__", @@ -17,7 +217,7 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"__traits", cast(u8[])r"__vector", ], - 'a'-95: [ + 'a': [ cast(u8[])r"abstract", cast(u8[])r"alias", cast(u8[])r"align", @@ -25,12 +225,12 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"assert", cast(u8[])r"auto", ], - 'b'-95: [ + 'b': [ cast(u8[])r"bool", cast(u8[])r"break", cast(u8[])r"byte", ], - 'c'-95: [ + 'c': [ cast(u8[])r"case", cast(u8[])r"cast", cast(u8[])r"catch", @@ -39,7 +239,7 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"const", cast(u8[])r"continue", ], - 'd'-95: [ + 'd': [ cast(u8[])r"dchar", cast(u8[])r"debug", cast(u8[])r"default", @@ -48,13 +248,13 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"do", cast(u8[])r"double", ], - 'e'-95: [ + 'e': [ cast(u8[])r"else", cast(u8[])r"enum", cast(u8[])r"export", cast(u8[])r"extern", ], - 'f'-95: [ + 'f': [ cast(u8[])r"false", cast(u8[])r"final", cast(u8[])r"finally", @@ -64,10 +264,10 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"foreach_reverse", cast(u8[])r"function", ], - 'g'-95: [ + 'g': [ cast(u8[])r"goto", ], - 'i'-95: [ + 'i': [ cast(u8[])r"if", cast(u8[])r"immutable", cast(u8[])r"import", @@ -78,24 +278,24 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"invariant", cast(u8[])r"is", ], - 'l'-95: [ + 'l': [ cast(u8[])r"lazy", cast(u8[])r"long", ], - 'm'-95: [ + 'm': [ cast(u8[])r"mixin", cast(u8[])r"module", ], - 'n'-95: [ - cast(u8[])r"new", + 'n': [ +cast(u8[])r"new", cast(u8[])r"nothrow", cast(u8[])r"null", ], - 'o'-95: [ + 'o': [ cast(u8[])r"out", cast(u8[])r"override", ], - 'p'-95: [ + 'p': [ cast(u8[])r"package", cast(u8[])r"pragma", cast(u8[])r"private", @@ -103,12 +303,12 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"public", cast(u8[])r"pure", ], - 'r'-95: [ + 'r': [ cast(u8[])r"real", cast(u8[])r"ref", cast(u8[])r"return", ], - 's'-95: [ + 's': [ cast(u8[])r"scope", cast(u8[])r"shared", cast(u8[])r"short", @@ -118,7 +318,7 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"switch", cast(u8[])r"synchronized", ], - 't'-95: [ + 't': [ cast(u8[])r"template", cast(u8[])r"this", cast(u8[])r"throw", @@ -127,7 +327,7 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"typeid", cast(u8[])r"typeof", ], - 'u'-95: [ + 'u': [ cast(u8[])r"ubyte", cast(u8[])r"uint", cast(u8[])r"ulong", @@ -135,247 +335,152 @@ const const(u8[])[]['z'-95] D_KEYWORDS = [ cast(u8[])r"unittest", cast(u8[])r"ushort", ], - 'v'-95: [ + 'v': [ cast(u8[])r"version", cast(u8[])r"void", ], - 'w'-95: [ + 'w': [ cast(u8[])r"wchar", cast(u8[])r"while", cast(u8[])r"with", ], ]; -void -Tokenize(FlatBuffer* fb) +const Token g_nil_tk; +Token* g_NIL_TOKEN; + +struct Token { - if(fb.data.length == 0) return; + Token* prev; + Token* next; + TokenType type; + u64 start; + u64 end; +} - Tokenizer* tk = &fb.tk; +struct Tokenizer +{ + Arena arena; + TokenStyle[] buffer; + u64 tk_count; + u64 pos; + Token* first; + Token* last; +} - u64 semi_count; - foreach(i; 0 .. fb.length) +bool +Nil(Token* tk) +{ + return tk == g_NIL_TOKEN || tk == null; +} + +Tokenizer +CreateTokenizer(FlatBuffer* fb) +{ + Tokenizer tk = { + arena: CreateArena(MB(4)), + buffer: Alloc!(TS)(fb.data.length), + }; + + g_NIL_TOKEN = cast(Token*)&g_nil_tk; + tk.first = tk.last = g_NIL_TOKEN; + + for(u64 i = 0; i < D_KEYWORDS.length; i += 1) { - if(fb.data.ptr[i] == ';') + if(D_KEYWORDS[i] == null) { - semi_count += 1; + D_KEYWORDS[i] = []; } } + return tk; +} + +void +ResetTokenizer(FlatBuffer* fb) +{ + Reset(&fb.tk.arena); + FreeArray(fb.tk.buffer); + + fb.tk.buffer = Alloc!(TS)(fb.data.length); + fb.tk.pos = 0; + fb.tk.first = fb.tk.last = g_NIL_TOKEN; +} + +Token* +MakeToken(Tokenizer* tk, TokenType type, u64 start, u64 end) +{ + Token* t = Alloc!(Token)(&tk.arena); + + t.type = type; + t.start = start; + t.end = end; + + DLLPush(tk, t, g_NIL_TOKEN); + + return t; +} + +u8 +Peek(FlatBuffer* fb) +{ + return fb.tk.pos+1 < fb.length ? fb.data[fb.tk.pos+1] : 0; +} + +void +TokenizeD(FlatBuffer* fb) +{ + Tokenizer* tk = &fb.tk; + Reset(&tk.arena); + tk.pos = 0; - tk.tokens = AllocArray!(Token)(&tk.arena, fb.length); - tk.tk_count = 1; - - bool _macro = false; - bool id_of_line = false; - Token* prev = tk.tokens.ptr; - while (true) + for(; tk.pos < fb.length;) { - scope(exit) tk.tk_count += 1; - Token* t = NextToken(fb); - if(t == null) break; + u8 ch = fb.data[tk.pos]; + bool alpha = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); - switch(t.type) + if(ch == ' ' || ch == '\t') { - case TT.Identifier: - case TT.Keyword: + Token* t = MakeToken(tk, TT.Whitespace, tk.pos, tk.pos+1); + + for(tk.pos += 1; tk.pos < fb.length; tk.pos += 1) + { + if(fb.data[tk.pos] != ' ' && fb.data[tk.pos] != '\t') { - TokenStyle ts = t.type == TT.Keyword ? TS.Keyword : TS.Identifier; - tk.buffer[t.start .. t.end] = ts; - } break; - case TT.Number: - { - tk.buffer[t.start .. t.end] = TS.Number; - } break; - case TT.Comment: - { - tk.buffer[t.start .. t.end] = TS.Comment; - } break; - case TT.DoubleQuote: - case TT.BackTick: - { - tk.buffer[t.start .. t.end] = TS.String; - } break; - case TT.SingleQuote: - { - tk.buffer[t.start .. t.end] = TS.Char; - } break; - case TT.LeftBracket: - { - if(prev != null && _macro && prev.type != TT.Exclamation) - { - _macro = false; - } - } goto case TT.Comma; - case TT.LeftBrace: - { - if(prev) - { - if(prev.type == TT.RightBracket) - { - i64 i = tk.tk_count - 2; - Token* pt = null; - bool is_macro = false; - i64 brackets = 1; - while (i > 0) - { - pt = tk.tokens.ptr + i; - i -= 1; + break; + } + } - if(pt.type == TT.Keyword && brackets == 0) break; - - if(pt.type == TT.LeftBracket) - { - brackets -= 1; - continue; - } - - if(pt.type == TT.RightBracket) - { - brackets += 1; - is_macro = true; - continue; - } - - if(pt.type == TT.Identifier && brackets == 0) - { - if(i >= 0) - { - Token* tmp = tk.tokens.ptr + i; - tmp.type = TT.Type; - tk.buffer[tmp.start .. tmp.end] = TS.Type; - } - - pt.type = TT.Function; - tk.buffer[pt.start .. pt.end] = TS.Function; - - brackets = 0; - Token* ppt = null; - while (i < tk.tk_count - 1) - { - ppt = pt; - i += 1; - pt = tk.tokens.ptr + i; - - bool id_keyword = pt.type == TT.Identifier || pt.type == TT.Keyword; - - if( is_macro && brackets == 4) break; - if(!is_macro && brackets == 2) break; - - if(is_macro && brackets < 2 && id_keyword) - { - pt.type = TT.Type; - tk.buffer[pt.start .. pt.end] = TS.Type; - continue; - } - - if(((!is_macro && brackets < 2) || (is_macro && brackets == 3)) && id_keyword && (ppt.type == TT.LeftBracket || ppt.type == TT.Comma)) - { - pt.type = TT.Type; - tk.buffer[pt.start .. pt.end] = TS.Type; - continue; - } - - if(pt.type == TT.LeftBracket || pt.type == TT.RightBracket) - { - brackets += 1; - continue; - } - } - - break; - } - } - } - } - } goto case TT.Comma; - case TT.Semicolon: - { - id_of_line = false; - } goto case TT.Comma; - case TT.RightBracket: - case TT.RightBrace: - case TT.LeftSquareBrace: - case TT.RightSquareBrace: - case TT.Colon: - case TT.Equals: - case TT.Comma: - { - tk.buffer[t.start .. t.end] = TS.Bracket; - } break; - case TT.Exclamation: - { - _macro = true; - } goto case TT.Equals; - case TT.Tilde: - case TT.Question: - case TT.LessThan: - case TT.GreaterThan: - case TT.Dollar: - case TT.Percent: - case TT.Caret: - case TT.Ampersand: - case TT.Asterisk: - case TT.VertBar: - case TT.Plus: - case TT.Minus: - case TT.Slash: - { - tk.buffer[t.start .. t.end] = TS.Op; - } break; - default: - tk.buffer[t.start .. t.end] = TS.Keyword; + t.end = tk.pos; } - - prev = t; - } -} - -Token* -PrevToken(FlatBuffer* fb, i64 prev) -{ - i64 c = cast(i64)(fb.tk.tk_count); - return (c-prev) > 0 ? fb.tk.tokens.ptr + (c-prev) : fb.tk.tokens.ptr + 0; -} - -Token* -NextToken(FlatBuffer* fb) -{ - Tokenizer* tk = &fb.tk; - - SkipWhiteSpace(fb); - - u8 ch = fb.data[tk.pos]; - - Token* t = null; - if(tk.pos < fb.length) - { - t = tk.tokens.ptr + tk.tk_count; - t.start = tk.pos; - - bool ascii = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); - - if(ch == 'r') + else if(ch == '\n') { - u8 next = PeekNextChar(fb); + Token* t = MakeToken(tk, TT.NewLine, tk.pos, tk.pos+1); + tk.pos += 1; + } + else if(ch == 'r') + { + u8 next = Peek(fb); if(next == '"') { tk.pos += 1; - ParseStr(fb); + ParseStr!('"')(fb); } else { ParseId(fb); } } + else if(ch >= '0' && ch <= '9') { ParseNum(fb); } + else if(ch == 'P' || ch == 'E' || ch == 'e' || ch == 'p') { - u8 next = PeekNextChar(fb); + u8 next = Peek(fb); if(next == '-' || next == '+' || (next >= '0' && next <= '9')) { ParseNum(fb); @@ -385,71 +490,44 @@ NextToken(FlatBuffer* fb) ParseId(fb); } } - else if(ascii || ch == '_') + else if(alpha || ch == '_') { ParseId(fb); } + else if(ch < 128 && D_STD_TOKEN[ch] != TT.None) + { + Token* t = MakeToken(tk, D_STD_TOKEN[ch], tk.pos, tk.pos+1); + tk.pos += 1; + } + else if(ch < 128 && D_OP_TOKEN[ch] != TT.None) + { + Token* t = MakeToken(tk, D_OP_TOKEN[ch], tk.pos, tk.pos+1); + tk.pos += 1; + FixOpAssign(fb, t); + } + else if(ch < 128 && D_STR_TOKEN[ch] != TT.None) + { + if(ch == '"') + { + ParseStr!('"')(fb); + } + else if(ch == '`') + { + ParseStr!('`')(fb); + } + else + { + ParseStr!('\'')(fb); + } + } else { switch (ch) { - case '$': t.type = TT.Dollar; goto case u8.max; - case '(': t.type = TT.LeftBracket; goto case u8.max; - case ')': t.type = TT.RightBracket; goto case u8.max; - case '[': t.type = TT.LeftSquareBrace; goto case u8.max; - case ']': t.type = TT.RightSquareBrace; goto case u8.max; - case '{': t.type = TT.LeftBrace; goto case u8.max; - case '}': t.type = TT.RightBrace; goto case u8.max; - case '|': t.type = TT.VertBar; goto case u8.max; - case ';': t.type = TT.Semicolon; goto case u8.max; - case ':': t.type = TT.Colon; goto case u8.max; - case '?': t.type = TT.Question; goto case u8.max; - case ',': t.type = TT.Comma; goto case u8.max; - case '#': t.type = TT.Hash; goto case u8.max; - case '~': t.type = TT.Tilde; goto case u8.max; - case '%': t.type = TT.Percent; goto case 251; - case '^': t.type = TT.Caret; goto case 251; - case '&': t.type = TT.Ampersand; goto case 251; - case '*': t.type = TT.Asterisk; goto case 251; - case '-': t.type = TT.Minus; goto case 251; - case '=': t.type = TT.Equals; goto case 251; - case '+': t.type = TT.Plus; goto case 251; - case '<': t.type = TT.LessThan; goto case 251; - case '>': t.type = TT.GreaterThan; goto case 251; - - case '`': - { - t.type = TT.BackTick; - tk.pos += 1; - while (fb.data[tk.pos] != '`' && tk.pos < fb.length) - { - tk.pos += 1; - } - t.end = tk.pos+1; - } break; - case '\'': - { - t.type = TT.SingleQuote; - - bool ignore = true; - while ((fb.data[tk.pos] != '\'' || ignore) && tk.pos < fb.length) - { - tk.pos += 1; - ignore = fb.data[tk.pos] == '\\'; - } - - tk.pos += 1; - t.end = tk.pos; - } break; - case '"': - ParseStr(fb); - break; - case '!': - t.type = TT.Exclamation; - goto case 251; case '@': { - t.type = TT.At; + Token* t = MakeToken(tk, TT.At, tk.pos, tk.pos+1); + while (tk.pos < fb.length) { tk.pos += 1; @@ -463,18 +541,21 @@ NextToken(FlatBuffer* fb) } break; case '.': { - t.type = TT.Dot; + Token* t = MakeToken(tk, TT.Dot, tk.pos, tk.pos+1); + while (tk.pos < fb.length) { tk.pos += 1; if(tk.pos != '.') break; } + t.end = tk.pos; } break; case '/': { - t.type = TT.Slash; - u8 next = PeekNextChar(fb); + Token* t = MakeToken(tk, TT.Slash, tk.pos, tk.pos+1); + + u8 next = Peek(fb); if(next == '/') { tk.pos += 1; @@ -493,45 +574,267 @@ NextToken(FlatBuffer* fb) t.end = tk.pos+1; } else - { - goto case u8.max; - } - } break; - case 251: - { - u8 next = PeekNextChar(fb); - if(next == '=') { tk.pos += 1; + FixOpAssign(fb, t); } - - t.end = tk.pos+1; - tk.pos += 1; - } break; - case u8.max: - { - t.end = tk.pos+1; - tk.pos += 1; } break; default: - t.type = TT.None; - t.end = tk.pos+1; - tk.pos += 1; + { + Token* t = MakeToken(tk, TT.None, tk.pos, tk.pos+1); + + for(; tk.pos < fb.length; tk.pos += 1) + { + if(CheckWhiteSpace(fb.data[tk.pos])) + { + break; + } + } + + t.end = tk.pos; + } break; } } } - return t; + for(auto n = tk.first; !Nil(n); n = Next(n)) + { + Token* prev = Prev(n); + Token* next = Next(n); + + if(CheckFuncOrTemplateSig(fb, n)) + { + continue; + } + + if(!Nil(prev)) + { + if(prev.type == TT.Import) + { + n.type = TT.ImportTarget; + continue; + } + else if(prev.type == TT.TypeKeyword && n.type == TT.Identifier) + { + n.type = TT.Type; + CheckTypeDeclaration(fb, n); + continue; + } + else if(prev.type == TT.EnumKeyword && n.type == TT.Identifier) + { + n.type = TT.Type; + continue; + } + } + + if(!Nil(next)) + { + if(n.type == TT.Identifier && next.type == TT.LeftParen) + { + n.type = TT.Function; + continue; + } + else if(n.type == TT.Identifier && next.type == TT.Exclamation) + { + n.type = TT.Macro; + continue; + } + } + + CheckVarDeclaration(n); + } + + for(auto token = tk.first; !Nil(token); token = token.next) + { + tk.buffer[token.start .. token.end] = TOKEN_STYLES[token.type]; + } } +bool +CheckVarDeclaration(Token* token) +{ + bool found, ignore; + if(token.type == TT.Identifier && Next(token).type != TT.Equals) + { + for(Token* ahead = Next(token); !Nil(ahead); ahead = Next(ahead)) + { + if(ahead.type == TT.Semicolon || ahead.next.type == TT.NewLine) + { + break; + } + + if(ahead.type == TT.Asterisk) + { + continue; + } + else if(ahead.type == TT.LeftSquareBrace) + { + ignore = true; + } + else if(ahead.type == TT.RightSquareBrace) + { + ignore = false; + } + else if(!ignore && ahead.type == TT.Identifier) + { + Token* eq = Next(ahead); + if(eq.type == TT.Equals || eq.type == TT.Semicolon) + { + token.type = TT.Type; + found = true; + break; + } + } + else if(!ignore) + { + break; + } + } + } + + return found; +} + +void +CheckTypeDeclaration(FlatBuffer* fb, Token* token) +{ + for(Token* tk = Next(token); !Nil(tk) && tk.type != TT.RightBrace; tk = Next(tk)) + { + Token* prev = Prev(tk); + if(prev.type == TT.LeftBrace || prev.type == TT.Semicolon) + { + tk.type = TT.Type; + } + } +} + +bool +CheckFuncOrTemplateSig(FlatBuffer* fb, Token* token) +{ + bool found = false; + + if(token.type == TT.Identifier || token.type == TT.Type || token.type == TT.Keyword) + { + Token* n = Next(token); + if(n.type == TT.Asterisk) + { + n = Next(n); + } + + if(n.type == TT.Identifier && Next(n).type == TT.LeftParen) + { + token.type = TT.Type; + n.type = TT.Function; + found = true; + } + } + + if(found) + { + bool ignore = false; + bool macro_decl = false; + for(Token* next = Next(token); !Nil(next); next = Next(next)) + { + if(next.type == TT.RightParen && Next(next).type != TT.LeftParen && !macro_decl) + { + macro_decl = true; + } + else if(next.type == TT.RightParen || next.next.type == TT.NewLine) + { + break; + } + else if(next.type == TT.LeftParen || next.type == TT.Comma) + { + Next(next).type = TT.Type; + } + } + } + + return found; +} + +u8[] +TokenStr(FlatBuffer* fb, Token* t) +{ + return fb.data[t.start .. t.end]; +} + +Token* +Prev(types...)(Token* t) +{ + Token* res = g_NIL_TOKEN; + +PrevMain: + for(auto p = t.prev; !Nil(p); p = p.prev) + { + if(p.type == TT.NewLine || p.type == TT.Whitespace || p.type == TT.Comment) + { + continue; + } + + static if(types.length == 0) + { + res = p; + break PrevMain; + } + + static foreach(type; types) + { + if(p.type == type) + { + res = p; + break PrevMain; + } + } + } + + return res; +} + +alias PrevFnToken = Prev!(TT.Identifier, TT.LeftParen, TT.RightParen, TT.Comma, TT.Keyword, TT.Dot, TT.Exclamation, TT.Type); + +Token* +Next(types...)(Token* t) +{ + Token* res = g_NIL_TOKEN; + +NextMain: + for(auto n = t.next; !Nil(n); n = n.next) + { + if(n.type == TT.NewLine || n.type == TT.Whitespace || n.type == TT.Comment) + { + continue; + } + + static if(types.length == 0) + { + res = n; + break NextMain; + } + + static foreach(type; types) + { + if(n.type == type) + { + res = n; + break NextMain; + } + } + } + + return res; +} + +alias NextFnToken = Next!(TT.Identifier, TT.LeftParen, TT.RightParen, TT.Comma, TT.Keyword, TT.Dot, TT.Exclamation, TT.Type); + void ParseNum(FlatBuffer* fb) { Tokenizer* tk = &fb.tk; - Token* t = tk.tokens.ptr + tk.tk_count; - t.type = TT.Number; - t.start = tk.pos; + Token* t = Alloc!(Token)(&tk.arena); + t.type = TT.Number; + t.start = tk.pos; bool first = true; while (tk.pos < fb.length) @@ -560,58 +863,78 @@ ParseNum(FlatBuffer* fb) } t.end = tk.pos; + + DLLPush(tk, t, g_NIL_TOKEN); } void -ParseStr(FlatBuffer* fb) +ParseStr(u8 str_ch)(FlatBuffer* fb) { + static assert(str_ch == '`' || str_ch == '\'' || str_ch == '"'); + Tokenizer* tk = &fb.tk; - tk.tokens[tk.tk_count].type = TT.DoubleQuote; + Token* t = Alloc!(Token)(&tk.arena); - bool ignore = true; - while ((fb.data[tk.pos] != '"' || ignore) && tk.pos < fb.length) + t.type = D_STR_TOKEN[str_ch]; + t.start = tk.pos; + + static if(str_ch == '`') tk.pos += 1; + + bool ignore = str_ch != '`'; + for(; (fb.data[tk.pos] != str_ch || ignore) && tk.pos < fb.length; tk.pos += 1) { - tk.pos += 1; - ignore = fb.data[tk.pos] == '\\'; + static if(str_ch != '`') ignore = fb.data[tk.pos] == '\\'; } tk.pos += 1; - tk.tokens[tk.tk_count].end = tk.pos; + t.end = tk.pos; + + DLLPush(tk, t, g_NIL_TOKEN); } void ParseId(FlatBuffer* fb) { Tokenizer* tk = &fb.tk; - Token* t = tk.tokens.ptr + tk.tk_count; + Token* t = MakeToken(tk, TT.Identifier, tk.pos, tk.pos+1); - t.type = TT.Identifier; - t.start = tk.pos; - - while (tk.pos < fb.length) + for(; tk.pos < fb.length; tk.pos += 1) { - tk.pos += 1; - u8 ch = fb.data[tk.pos]; if(!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') && ch != '.' && ch != '_' && !(ch >= '0' && ch <= '9')) { + t.end = tk.pos; break; } } - t.end = tk.pos; - - u8 ch = cast(u8)(fb.data[t.start] - cast(u8)(95)); // Index into keywords lookup - if(ch < D_KEYWORDS.length && D_KEYWORDS[ch].length > 0) + u8 ch = fb.data[t.start]; + if(ch < D_KEYWORDS.length && D_KEYWORDS[ch] != null && D_KEYWORDS[ch].length > 0) { u8[] id = fb.data[t.start .. t.end]; foreach(k; D_KEYWORDS[ch]) { if(id == k) { - t.type = TT.Keyword; + if(k == r"import") + { + t.type = TT.Import; + } + else if(k == r"class" || k == r"struct") + { + t.type = TT.TypeKeyword; + } + else if(k == r"enum") + { + t.type = TT.EnumKeyword; + } + else + { + t.type = TT.Keyword; + } + break; } } @@ -620,6 +943,19 @@ ParseId(FlatBuffer* fb) assert(t.start < t.end, "ParseId failure: start is lower or equal to end"); } +void +FixOpAssign(FlatBuffer* fb, Token* t) +{ + if(fb.tk.pos < fb.length) + { + if(fb.data[fb.tk.pos] == '=') + { + fb.tk.pos += 1; + t.end += 1; + } + } +} + pragma(inline) bool CheckEOL(u8 ch) { diff --git a/src/editor/ui.d b/src/editor/ui.d index a3e0ca2..91c1d78 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -10,6 +10,7 @@ import std.format : sformat; import core.stdc.string : memset; import core.stdc.math : fabsf; +import parsing; import editor; const u8[] FONT_BYTES = import("pc-9800.ttf"); @@ -179,6 +180,7 @@ struct UIItem // Calculated Parameters Vec4[4] color; Vec4[4] border_color; + TokenStyle[] token_styles; Rect rect; Vec2 size; Vec2 last_click_pos; @@ -1227,14 +1229,20 @@ GlyphWidth(Glyph* g) return width; } -Node!(u8[])* -MakeMultiline(u8[] text, f32 width, u8[] parent_id, u64 line_no) +struct TextBuffer +{ + u8[] text; + TS[] style; +} + +Node!(TextBuffer)* +MakeMultiline(u8[] text, f32 width, u8[] parent_id, u64 line_no, TS[] style = []) { f32 scaled_width = width * (g_ui_ctx.atlas_buf.atlas.size/g_ui_ctx.text_size); f32 text_width = CalcTextWidth(text); u64 line_count = cast(u64)(ceil(text_width/scaled_width)); - Node!(u8[])* node = null; + Node!(TextBuffer)* node = null; if(line_count > 0) { f32 w = 0.0; @@ -1252,25 +1260,39 @@ MakeMultiline(u8[] text, f32 width, u8[] parent_id, u64 line_no) (cast(char[])buf).sformat("%s%05s%05s", cast(char[])parent_id, line_no, line); u8[] str = ScratchAlloc!(u8)(len+extra_buf); + str[0 .. len] = text[start .. start+len]; str[len .. len+extra_buf] = buf[0 .. $]; - Node!(u8[])* n = node; + TS[] stl = []; + if(style.length > 0) + { + stl = ScratchAlloc!(TS)(len); + stl[0 .. len] = style[start .. start+len]; + } + + Node!(TextBuffer)* n = node; for(;;) { if(node == null) { - node = ScratchAlloc!(Node!(u8[]))(); - node.value = str; - node.next = null; + node = ScratchAlloc!(Node!(TextBuffer))(); + + node.value.text = str; + node.value.style = stl; + node.next = null; + break; } if(n.next == null) { - n.next = ScratchAlloc!(Node!(u8[]))(); - n.next.value = str; - n.next.next = null; + n.next = ScratchAlloc!(Node!(TextBuffer))(); + + n.next.value.text = str; + n.next.value.style = stl; + n.next.next = null; + break; } @@ -1338,9 +1360,10 @@ DrawLine(UIItem* item) for(u64 i = 0; i < item.key.text.length && item.key.text[i] != '\0'; i += 1) { u8 ch = item.key.text.ptr[i]; + TS ts = i < item.token_styles.length ? item.token_styles[i] : TS.None; if(ch < 128) { - DrawGlyph(item, &atlas.glyphs[ch], atlas.size/ctx.text_size, &x, y, !edit_mode && i == item.highlighted_char); + DrawGlyph(item, &atlas.glyphs[ch], atlas.size/ctx.text_size, &x, y, !edit_mode && i == item.highlighted_char, ts); } } @@ -1430,7 +1453,7 @@ CullText(UIItem* item, GlyphBounds* gb) } pragma(inline) void -DrawGlyph(UIItem* item, Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool highlight = false, Vec4 col = Vec4(1.0)) +DrawGlyph(UIItem* item, Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool highlight = false, TokenStyle ts = TS.None, Vec4 col = Vec4(1.0)) { UICtx* ctx = GetCtx(); Vertex* bg_v = null, v = null; @@ -1484,6 +1507,10 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool highlig { col = Vec4(Vec3(1.0)-col.xyz, 1.0); } + else if(ts != TS.None) + { + col = SYNTAX_COLORS[ts]; + } v.cols = col; diff --git a/src/editor/widgets.d b/src/editor/widgets.d index 008cf72..49ae274 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -3,6 +3,7 @@ import dlib; import buffer; import ui : Nil; import ui; +import parsing; import editor; import std.format : sformat; import std.math.rounding : ceil, floor; @@ -221,14 +222,19 @@ LineCounter(u8[] parent_id, FlatBuffer* buf, f32 offset, i64 start_row, i64 end_ { UIItem* inner = Container(ScratchName(parent_id, "lcinner"), s_x, s_y, A2D.Y, UIF.None); { - Push!("offset")(Vec2(0.0, offset)); - for(u64 i = 0; i < line_counts.length; i += 1) { - UIItem* line = Text(line_counts[i]); + if(i == 0) + { + Push!("offset")(Vec2(0.0, offset)); + UIItem* line = Text(line_counts[i]); + Pop!("offset"); + } + else + { + UIItem* line = Text(line_counts[i]); + } } - - Pop!("offset")(); } EndContainer(); } @@ -370,12 +376,15 @@ EditorView(UIPanel* panel) Container(ScratchName(panel.id, "lines"), UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0), A2D.Y); { - Push!("offset")(Vec2(0.0, offset)); - U64Vec2 pos = VecPos(&ed.buf); u64 i = 0; for(LineBuffer* buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1) { + if(buf == ed.linebufs.first) + { + Push!("offset")(Vec2(0.0, offset)); + } + if(buf.text.length > 0) { i64 line_no = i+line_offset; @@ -384,15 +393,19 @@ EditorView(UIPanel* panel) Push!("highlighted_char")(pos.x); } - TextPart* tp = WrappedTextLine(buf.text, panel.id, text_size, line_no); + TextPart* tp = WrappedTextLine(buf.text, panel.id, text_size, line_no, buf.style); height += (text_size * tp.count); if(TextClicked(tp)) { SetFocusedPanel(panel); } } - Push!("highlighted_char")(-1); + + if(buf == ed.linebufs.first) + { + Pop!("offset"); + } } } EndContainer(); @@ -405,8 +418,6 @@ EditorView(UIPanel* panel) { SetFocusedPanel(panel); } - - Pop!("offset"); } bool @@ -429,7 +440,7 @@ TextClicked(TextPart* text_part) } TextPart* -WrappedTextLine(u8[] text, u8[] parent_id, f32 text_size, u64 line_no) +WrappedTextLine(u8[] text, u8[] parent_id, f32 text_size, u64 line_no, TS[] style = []) { Push!("color")(Vec4(1.0)); Push!("text_size")(text_size); @@ -439,15 +450,17 @@ WrappedTextLine(u8[] text, u8[] parent_id, f32 text_size, u64 line_no) part.item = g_UI_NIL; TextPart* tp = part; - Node!(u8[])* lines = MakeMultiline(text, parent.size.x, parent_id, line_no); - for(Node!(u8[])* line = lines; line != null; line = line.next, tp = tp.next) + Node!(TextBuffer)* lines = MakeMultiline(text, parent.size.x, parent_id, line_no, style); + for(Node!(TextBuffer)* line = lines; line != null; line = line.next, tp = tp.next) { part.count += 1; - tp.item = Get(line.value); + tp.item = Get(line.value.text); tp.next = ScratchAlloc!(TextPart)(); tp.next.item = g_UI_NIL; + tp.item.token_styles = line.value.style; + Signal(tp.item); if(tp.item.signal & UIS.Clicked)