2023-07-19 15:09:41 +00:00
|
|
|
////////////////////////////////
|
|
|
|
//~ sixten: Mutable String Functions
|
|
|
|
|
|
|
|
static mutable_string MutableStringAllocate(u64 Size)
|
|
|
|
{
|
|
|
|
mutable_string Result = {};
|
|
|
|
Result.Arena = ArenaAllocate(Size);
|
|
|
|
ArenaSetAlign(Result.Arena, 1);
|
|
|
|
Result.String = MakeString(PushArray(Result.Arena, u8, 1), 0);
|
|
|
|
return(Result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MutableStringRelease(mutable_string *String)
|
|
|
|
{
|
|
|
|
ArenaRelease(String->Arena);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MutableStringReplaceRange(mutable_string *MutString, string ReplaceString, range1_s64 Range)
|
|
|
|
{
|
|
|
|
Range = Intersection(Range, Range1S64(0, MutString->String.Count));
|
|
|
|
|
|
|
|
s64 NewCount = MutString->String.Count+ReplaceString.Count-DimOfRange(Range);
|
|
|
|
if(NewCount > MutString->String.Count)
|
|
|
|
{
|
|
|
|
s64 ToAllocate = NewCount-MutString->String.Count;
|
2023-07-24 13:50:57 +00:00
|
|
|
PushArrayNoClear(MutString->Arena, u8, ToAllocate);
|
|
|
|
}
|
|
|
|
else if(NewCount < MutString->String.Count)
|
|
|
|
{
|
|
|
|
ArenaPopTo(MutString->Arena, sizeof(memory_arena)+NewCount+1);
|
2023-07-19 15:09:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-DimOfRange(Range));
|
|
|
|
Copy(MutString->String.Data+Range.Min, ReplaceString.Data, ReplaceString.Count);
|
|
|
|
|
|
|
|
MutString->String.Count = NewCount;
|
2023-07-24 13:50:57 +00:00
|
|
|
MutString->String.Data[NewCount] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
//~ sixten: History & Undo Functions
|
|
|
|
|
|
|
|
static history_entry HistoryEntry(memory_arena *Arena, string ReplaceString, range1_s64 Range)
|
|
|
|
{
|
|
|
|
// sixten(TODO): proper memory management, right now we just keep appending to the arena, which works but never frees any memory
|
|
|
|
history_entry Entry = {};
|
|
|
|
Entry.ReplaceString = PushString(Arena, ReplaceString);
|
|
|
|
Entry.Range = Range;
|
|
|
|
return(Entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AppendToHistory(memory_arena *Arena, history_list *List, history_entry Forward, history_entry Backward)
|
|
|
|
{
|
|
|
|
// sixten(TODO): proper memory management, right now we just keep appending to the arena, which works but never frees any memory
|
|
|
|
history_node *Node = PushStructNoClear(Arena, history_node);
|
|
|
|
Node->Forward = Forward;
|
|
|
|
Node->Backward = Backward;
|
|
|
|
|
|
|
|
SenDLLInsertLast(&List->Sentinel, Node);
|
2023-07-19 15:09:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
//~ sixten: Workspace Text Editor Functions
|
|
|
|
|
|
|
|
static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *Arena, string Text)
|
|
|
|
{
|
|
|
|
temporary_memory Scratch = GetScratch(&Arena, 1);
|
|
|
|
|
2023-07-24 13:50:57 +00:00
|
|
|
//- sixten: clear the target arena
|
|
|
|
ArenaClear(Arena);
|
|
|
|
|
2023-07-19 15:09:41 +00:00
|
|
|
//- sixten: tokenize the text
|
|
|
|
tokenize_result TokenizeResult = T_TokenizeFromText(Arena, StrLit("*scratch*"), Text);
|
|
|
|
token_array Tokens = TokenizeResult.Tokens;
|
|
|
|
|
|
|
|
//- sixten: gather all line ranges
|
|
|
|
range1_s64_list Lines = {};
|
|
|
|
{
|
|
|
|
u8 *TextBegin = Text.Data;
|
|
|
|
u8 *TextEnd = TextBegin + Text.Count;
|
|
|
|
range1_s64 Range = Range1S64(0, 0);
|
|
|
|
for(u8 *Char = TextBegin; Char <= TextEnd; Char += 1)
|
|
|
|
{
|
|
|
|
Range.Max += 1;
|
|
|
|
|
|
|
|
//- sixten: push line range on newline and EOF
|
|
|
|
if(Char == TextEnd || *Char == '\n')
|
|
|
|
{
|
|
|
|
Range1S64ListPush(Scratch.Arena, &Lines, Range);
|
|
|
|
Range = Range1S64(Range.Max, Range.Max);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: fill & return
|
|
|
|
workspace_text_data Result = {};
|
|
|
|
{
|
|
|
|
Result.Tokens = Tokens;
|
|
|
|
Result.Lines = Range1S64ArrayFromList(Arena, &Lines);;
|
|
|
|
}
|
|
|
|
ReleaseScratch(Scratch);
|
|
|
|
return(Result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback)
|
|
|
|
{
|
|
|
|
temporary_memory Scratch = GetScratch();
|
|
|
|
workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data;
|
|
|
|
|
|
|
|
//- sixten: get dimensions & scroll offset from container
|
|
|
|
ui_box *ContainerBox = Editor->ContainerBox;
|
|
|
|
range2_r32 ParentRect = ContainerBox->Rect;
|
|
|
|
v2 ParentDim = DimOfRange(ParentRect);
|
|
|
|
v2 Offset = Box->Parent->Offset;
|
|
|
|
|
|
|
|
//- sixten: rendering properties
|
|
|
|
r32 FontSize = 16.0f;
|
|
|
|
r32 LineHeight = FontSize + 4.0f;
|
|
|
|
|
|
|
|
//- sixten: calculate the dimensions of the glyphs
|
|
|
|
glyph *Glyph = GetGlyph(Atlas, Font_Monospace, 'A', FontSize, 0);
|
|
|
|
r32 GlyphAdvance = Glyph->Advance;
|
|
|
|
|
|
|
|
//- sixten: find the text point
|
|
|
|
text_point CursorTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor);
|
|
|
|
text_point MarkTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Mark);
|
|
|
|
|
|
|
|
//- sixten: get the line count
|
|
|
|
range1_s64_array *Lines = &Editor->Lines;
|
|
|
|
s64 LineCount = Lines->Count;
|
|
|
|
|
|
|
|
//- sixten: calculate the text dim
|
|
|
|
Editor->TextDim = V2(1900, LineCount*LineHeight);
|
|
|
|
|
|
|
|
//- sixten: calculate the line margin dim
|
|
|
|
s32 LineMarginDigitsRequired = 6;
|
|
|
|
v2_r32 LineMarginDim = V2((LineMarginDigitsRequired)*GlyphAdvance, ParentRect.Max.y - ParentRect.Min.y);
|
|
|
|
|
|
|
|
//- sixten: tokenize text
|
|
|
|
tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, StrLit("nobody cares"), Editor->Text.String);
|
|
|
|
token_array Tokens = TokenizeResult.Tokens;
|
|
|
|
token *TokensBegin = Tokens.Tokens;
|
|
|
|
token *TokensEnd = TokensBegin + Tokens.Count;
|
|
|
|
|
|
|
|
//- sixten: find the first visible token
|
|
|
|
token *VisibleTokensBegin = TokensBegin;
|
|
|
|
s64 TopMostLine = Min((s64)Floor(-Offset.y / LineHeight), LineCount);
|
|
|
|
for(s64 LinesFound = 0; LinesFound < TopMostLine && VisibleTokensBegin < TokensEnd; VisibleTokensBegin += 1)
|
|
|
|
{
|
|
|
|
if(VisibleTokensBegin->Flags & TokenFlag_Newline)
|
|
|
|
{
|
|
|
|
LinesFound += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: find the last visible token
|
|
|
|
token *VisibleTokensEnd = VisibleTokensBegin;
|
|
|
|
s64 LinesOnScreen = Min((s64)Floor(ParentDim.y / LineHeight)+1, LineCount-TopMostLine);
|
|
|
|
for(s64 LinesFound = 0; LinesFound < LinesOnScreen && VisibleTokensEnd < TokensEnd; VisibleTokensEnd += 1)
|
|
|
|
{
|
|
|
|
if(VisibleTokensEnd->Flags & TokenFlag_Newline)
|
|
|
|
{
|
|
|
|
LinesFound += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: draw line numbers & line highlights
|
|
|
|
{
|
|
|
|
//- sixten: draw the background
|
|
|
|
v4 LineMarginColor = ColorFromHex(0x10203080);
|
|
|
|
range2_r32 LineMarginBox = Range2R32(ParentRect.Min, ParentRect.Min+LineMarginDim);
|
|
|
|
PushQuad(Group, LineMarginBox, LineMarginColor, LineMarginColor, LineMarginColor, LineMarginColor, 0, 0, 0);
|
|
|
|
|
|
|
|
//- sixten: draw the numbers
|
|
|
|
v2_r32 LineOffset = Box->Rect.Min;
|
|
|
|
for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1)
|
|
|
|
{
|
|
|
|
r32 LineY = LineOffset.y + LineIndex*LineHeight;
|
|
|
|
PushTextF(Group, Atlas, Font_Monospace, V2(0, LineY), FontSize, Color_Grey, "%*.i", LineMarginDigitsRequired, LineIndex+1);
|
|
|
|
|
|
|
|
if(LineIndex + 1 == CursorTextP.Line)
|
|
|
|
{
|
|
|
|
v4_r32 LineHighlightColor = ColorFromHex(0x10204080);
|
|
|
|
range2_r32 LineHighlightBox = Range2R32(V2(LineMarginBox.Max.x, LineY), V2(Box->Rect.Max.x, LineY+LineHeight));
|
|
|
|
PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 0, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: render tokens
|
|
|
|
v2 BaseTokenP = Box->Rect.Min+V2(LineMarginDim.x, TopMostLine*LineHeight);
|
|
|
|
v2 TokenP = BaseTokenP;
|
|
|
|
for(token *Token = VisibleTokensBegin; Token < VisibleTokensEnd; Token += 1)
|
|
|
|
{
|
|
|
|
string TokenString = T_StringFromToken(Editor->Text.String, *Token);
|
|
|
|
|
2023-07-24 13:50:57 +00:00
|
|
|
//- sixten: get color & font from token
|
|
|
|
font_id Font = Font_Monospace;
|
2023-07-19 15:09:41 +00:00
|
|
|
v4 Color = Color_Magenta;
|
2023-07-24 13:50:57 +00:00
|
|
|
if(Token->Flags & TokenGroup_Comment) { Color = Color_Grey; Font = Font_MonospaceOblique; }
|
2023-07-19 15:09:41 +00:00
|
|
|
else if(Token->Flags & TokenFlag_Reserved) { Color = Color_Grey; }
|
|
|
|
else if(Token->Flags & TokenFlag_Symbol) { Color = ColorFromHex(0xbd2d2dff); }
|
|
|
|
else if(Token->Flags & TokenFlag_StringLiteral) { Color = ColorFromHex(0xffa900ff); }
|
|
|
|
else if(Token->Flags & TokenFlag_Numeric) { Color = ColorFromHex(0xffa900ff); }
|
|
|
|
else if(Token->Flags & TokenFlag_Identifier)
|
|
|
|
{
|
|
|
|
//- sixten: check for keywords
|
2023-07-24 13:50:57 +00:00
|
|
|
if(AreEqual(TokenString, StrLit("true")) ||
|
2023-07-19 15:09:41 +00:00
|
|
|
AreEqual(TokenString, StrLit("false")))
|
2023-07-24 13:50:57 +00:00
|
|
|
{
|
|
|
|
Color = ColorFromHex(0xffa900ff);
|
|
|
|
}
|
|
|
|
else if(AreEqual(TokenString, StrLit("var")) ||
|
|
|
|
AreEqual(TokenString, StrLit("proc")) ||
|
|
|
|
AreEqual(TokenString, StrLit("branch")) ||
|
|
|
|
AreEqual(TokenString, StrLit("jump")) ||
|
|
|
|
AreEqual(TokenString, StrLit("if")))
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
|
|
|
Color = ColorFromHex(0xf0c674ff);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Color = Theme_TextColor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//- sixten: render & advance by token
|
|
|
|
if(!(Token->Flags & TokenGroup_Whitespace))
|
|
|
|
{
|
2023-07-24 13:50:57 +00:00
|
|
|
TokenP.x += PushText(Group, Atlas, Font, TokenP, FontSize, Color, TokenString);
|
2023-07-19 15:09:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(Token->Flags & TokenFlag_Newline)
|
|
|
|
{
|
|
|
|
TokenP.x = BaseTokenP.x;
|
|
|
|
TokenP.y += LineHeight;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
u8 *StringBegin = TokenString.Data;
|
|
|
|
u8 *StringEnd = StringBegin + TokenString.Count;
|
|
|
|
for(u8 *Char = StringBegin; Char < StringEnd; Char += 1)
|
|
|
|
{
|
|
|
|
if(*Char == ' ' || *Char == '\t')
|
|
|
|
{
|
|
|
|
TokenP.x += GlyphAdvance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2023-07-24 13:50:57 +00:00
|
|
|
//- sixten: render cursor
|
|
|
|
s64 LineIndex = CursorTextP.Line-1;
|
|
|
|
string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]);
|
|
|
|
s64 ColumnIndex = CursorTextP.Column-1;
|
|
|
|
s64 ColumnOffset = UTF8OffsetFromIndex(Line, ColumnIndex);
|
|
|
|
|
|
|
|
v2 TargetCursorP = Box->Rect.Min+V2(LineMarginDim.x+ColumnOffset*GlyphAdvance, LineIndex*LineHeight);
|
2023-07-19 15:09:41 +00:00
|
|
|
v2 CursorP = V2(AnimationCurve_AnimateValueF(TargetCursorP.x, TargetCursorP.x, 0.1, "Workspace Text Editor Cursor X %p", Editor),
|
|
|
|
AnimationCurve_AnimateValueF(TargetCursorP.y, TargetCursorP.y, 0.1, "Workspace Text Editor Cursor Y %p", Editor));
|
|
|
|
v2 CursorDim = V2(2, LineHeight);
|
|
|
|
range2_r32 CursorRect = Range2R32(CursorP, CursorP+CursorDim);
|
|
|
|
v4 CursorColor = ColorFromHex(0x10FF20FF);
|
|
|
|
PushQuad(Group, CursorRect, CursorColor, CursorColor, CursorColor, CursorColor, 2, 0.4, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: render the selection
|
|
|
|
{
|
|
|
|
text_range Selection = TextRange(CursorTextP, MarkTextP);
|
|
|
|
range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line);
|
2023-07-24 13:50:57 +00:00
|
|
|
for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1)
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
2023-07-24 13:50:57 +00:00
|
|
|
r32 LineY = Box->Rect.Min.y + LineIndex*LineHeight;
|
|
|
|
if(Contains(LineRange, LineIndex + 1))
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
2023-07-24 13:50:57 +00:00
|
|
|
range1_s64 ColumnRange = Lines->Ranges[LineIndex];
|
2023-07-19 15:09:41 +00:00
|
|
|
range1_s64 NormalizedColumnRange = Range1S64(0, DimOfRange(ColumnRange));
|
2023-07-24 13:50:57 +00:00
|
|
|
if(LineIndex+1 == LineRange.Min && LineIndex+1 == LineRange.Max)
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
|
|
|
NormalizedColumnRange = Range1S64(Editor->EditState.Cursor - ColumnRange.Min, Editor->EditState.Mark - ColumnRange.Min);
|
|
|
|
}
|
2023-07-24 13:50:57 +00:00
|
|
|
else if(LineIndex+1 == LineRange.Min)
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
|
|
|
NormalizedColumnRange = Range1S64(Min(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min, DimOfRange(ColumnRange));
|
|
|
|
}
|
2023-07-24 13:50:57 +00:00
|
|
|
else if(LineIndex+1 == LineRange.Max)
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
|
|
|
NormalizedColumnRange = Range1S64(0, Max(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min);
|
|
|
|
}
|
2023-07-24 13:50:57 +00:00
|
|
|
|
|
|
|
string Line = Substring(Editor->Text.String, ColumnRange);
|
|
|
|
range1_s64 ColumnOffsetRange = Range1S64(UTF8OffsetFromIndex(Line, NormalizedColumnRange.Min),
|
|
|
|
UTF8OffsetFromIndex(Line, NormalizedColumnRange.Max));
|
|
|
|
|
2023-07-19 15:09:41 +00:00
|
|
|
v4_r32 LineHighlightColor = ColorFromHex(0x66B3CC4C);
|
2023-07-24 13:50:57 +00:00
|
|
|
range2_r32 LineHighlightBox = Range2R32(V2(LineMarginDim.x+ColumnOffsetRange.Min*GlyphAdvance, LineY),
|
|
|
|
V2(LineMarginDim.x+ColumnOffsetRange.Max*GlyphAdvance, LineY+LineHeight));
|
2023-07-19 15:09:41 +00:00
|
|
|
PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ReleaseScratch(Scratch);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Workspace_BuildTextEditor(workspace_view *View)
|
|
|
|
{
|
|
|
|
workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data;
|
|
|
|
|
|
|
|
temporary_memory Scratch = GetScratch();
|
|
|
|
|
|
|
|
//- sixten: rendering properties
|
|
|
|
r32 FontSize = 16.0f;
|
|
|
|
r32 LineHeight = FontSize + 4.0f;
|
|
|
|
|
|
|
|
//- sixten: calculate the dimensions of the glyphs
|
|
|
|
glyph *Glyph = GetGlyph(UI_GlyphAtlas(), Font_Monospace, 'A', FontSize, 0);
|
|
|
|
r32 GlyphAdvance = Glyph->Advance;
|
|
|
|
|
|
|
|
//- sixten: calculate the line margin dim
|
|
|
|
s32 LineMarginDigitsRequired = 6;
|
|
|
|
r32 LineMarginWidth = (LineMarginDigitsRequired)*GlyphAdvance;
|
|
|
|
|
|
|
|
ui_box *EditorBox = 0;
|
|
|
|
UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1));
|
|
|
|
UI_Scroll(0, &Editor->Offset.y)
|
|
|
|
{
|
|
|
|
//- sixten: find the container box for the scrollable region
|
|
|
|
Editor->ContainerBox = UI_TopParent()->Parent->Parent;
|
|
|
|
|
|
|
|
UI_SetNextSize(UI_Pixels(Editor->TextDim.x, 1), UI_Pixels(Editor->TextDim.y, 1));
|
|
|
|
EditorBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, "Workspace Text Editor %p", View);
|
|
|
|
EditorBox->DrawCallback = Workspace_TextEditorDrawCallback;
|
|
|
|
EditorBox->DrawCallbackData = Editor;
|
|
|
|
}
|
|
|
|
|
|
|
|
b32 CursorHasBeenModified = false;
|
|
|
|
|
|
|
|
if(Workspace_ViewIsCurrent(View))
|
|
|
|
{
|
2023-07-24 13:50:57 +00:00
|
|
|
//- sixten: handle history
|
|
|
|
{
|
|
|
|
history_list *List = &Editor->History;
|
|
|
|
|
|
|
|
//- sixten: undo
|
|
|
|
if(Platform_KeyPress(UI_EventList(), Key_Z, PlatformModifier_Ctrl))
|
|
|
|
{
|
|
|
|
history_node *Node = List->At;
|
|
|
|
if(Node != &List->Sentinel)
|
|
|
|
{
|
|
|
|
//- sixten: get entry & apply
|
|
|
|
history_entry Entry = Node->Backward;
|
|
|
|
MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range);
|
|
|
|
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
|
|
|
Editor->Tokens = TextData.Tokens;
|
|
|
|
Editor->Lines = TextData.Lines;
|
|
|
|
Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count;
|
|
|
|
CursorHasBeenModified = true;
|
|
|
|
|
|
|
|
List->At = Node->Prev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: redo
|
|
|
|
if(Platform_KeyPress(UI_EventList(), Key_Y, PlatformModifier_Ctrl))
|
|
|
|
{
|
|
|
|
history_node *Node = List->At->Next;
|
|
|
|
if(Node != &List->Sentinel)
|
|
|
|
{
|
|
|
|
//- sixten: get entry & apply
|
|
|
|
history_entry Entry = Node->Forward;
|
|
|
|
MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range);
|
|
|
|
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
|
|
|
Editor->Tokens = TextData.Tokens;
|
|
|
|
Editor->Lines = TextData.Lines;
|
|
|
|
Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count;
|
|
|
|
CursorHasBeenModified = true;
|
|
|
|
|
|
|
|
List->At = Node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: select all
|
|
|
|
if(Platform_KeyPress(UI_EventList(), Key_A, PlatformModifier_Ctrl))
|
|
|
|
{
|
|
|
|
Editor->EditState.Mark = 0;
|
|
|
|
Editor->EditState.Cursor = Editor->Text.String.Count;
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: keyboard input -> text op
|
2023-07-19 15:09:41 +00:00
|
|
|
for(platform_event *Event = UI_EventList()->First;
|
|
|
|
Event != 0;
|
|
|
|
Event = Event->Next)
|
|
|
|
{
|
|
|
|
if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text)
|
|
|
|
{
|
|
|
|
text_action Action = MultiLineTextActionFromEvent(Event);
|
|
|
|
if(IsValid(&Action))
|
|
|
|
{
|
|
|
|
text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1);
|
|
|
|
|
2023-07-24 13:50:57 +00:00
|
|
|
if(DimOfRange(Op.Range) != 0 || !AreEqual(StrLit(""), Op.ReplaceString))
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
2023-07-24 13:50:57 +00:00
|
|
|
//- sixten: append to the history
|
|
|
|
{
|
|
|
|
history_list *List = &Editor->History;
|
|
|
|
|
|
|
|
//- sixten: remove the pre-existing history if needed
|
|
|
|
if(List->Sentinel.Prev != List->At)
|
|
|
|
{
|
|
|
|
// sixten(TODO): instead of just removing the links to the old memory, find some way to manage it.
|
|
|
|
List->Sentinel.Prev->Next = List->At;
|
|
|
|
List->Sentinel.Prev = List->At;
|
|
|
|
}
|
|
|
|
|
|
|
|
range1_s64 Selection = Range1S64(Editor->EditState.Cursor, Editor->EditState.Mark);
|
|
|
|
AppendToHistory(Editor->HistoryArena, List,
|
|
|
|
HistoryEntry(Editor->HistoryArena, Op.ReplaceString, Op.Range),
|
|
|
|
HistoryEntry(Editor->HistoryArena, Substring(Editor->Text.String, Op.Range), Range1S64(Op.Range.Min, Op.Range.Min+Op.ReplaceString.Count)));
|
|
|
|
|
|
|
|
List->At = List->Sentinel.Prev;
|
|
|
|
}
|
|
|
|
//- sixten: apply the text action
|
2023-07-19 15:09:41 +00:00
|
|
|
MutableStringReplaceRange(&Editor->Text, Op.ReplaceString, Op.Range);
|
|
|
|
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
|
|
|
Editor->Tokens = TextData.Tokens;
|
|
|
|
Editor->Lines = TextData.Lines;
|
|
|
|
}
|
|
|
|
|
2023-07-24 13:50:57 +00:00
|
|
|
CursorHasBeenModified = true;
|
2023-07-19 15:09:41 +00:00
|
|
|
Editor->EditState.Cursor = Op.NewCursor;
|
|
|
|
Editor->EditState.Mark = Op.NewMark;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ui_signal Signal = UI_SignalFromBox(EditorBox);
|
|
|
|
if(Signal.Dragging)
|
|
|
|
{
|
|
|
|
if(Signal.Pressed)
|
|
|
|
{
|
|
|
|
//- sixten: translate mouse position to text point
|
|
|
|
v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0);
|
2023-07-24 13:50:57 +00:00
|
|
|
s64 LineIndex = (s64)(MouseOffset.y / LineHeight);
|
|
|
|
string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]);
|
|
|
|
s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance);
|
|
|
|
s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset);
|
|
|
|
|
|
|
|
text_point Point = {LineIndex + 1, ColumnIndex + 1};
|
2023-07-19 15:09:41 +00:00
|
|
|
Editor->EditState.Cursor = Editor->EditState.Mark = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: translate mouse position to text point
|
|
|
|
v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0);
|
2023-07-24 13:50:57 +00:00
|
|
|
s64 LineIndex = (s64)(MouseOffset.y / LineHeight);
|
|
|
|
string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]);
|
|
|
|
s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance);
|
|
|
|
s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset);
|
|
|
|
|
|
|
|
text_point Point = {LineIndex + 1, ColumnIndex + 1};
|
2023-07-19 15:09:41 +00:00
|
|
|
Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
|
|
|
|
|
|
|
|
CursorHasBeenModified = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: update eventual text point extents
|
|
|
|
if(CursorHasBeenModified)
|
|
|
|
{
|
|
|
|
text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor);
|
|
|
|
if(Editor->LastTextPoint.Line == Point.Line)
|
|
|
|
{
|
|
|
|
Editor->LastTextPoint = Point;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Editor->LastTextPoint.Line = Point.Line;
|
|
|
|
Editor->LastTextPoint.Column = Max(Editor->LastTextPoint.Column, Point.Column);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseScratch(Scratch);
|
|
|
|
}
|