splink/source/definition.c

1170 lines
35 KiB
C
Raw Normal View History

2023-09-30 01:09:40 +00:00
////////////////////////////////
// NOTE(allen): Shhh compiler be quite
internal STR_Index E_DefinitionNameIndex(E_Definition *definition);
internal String8 E_DefinitionName(E_Definition *definition);
internal E_Definition* _E_CreateNewDefinitionWithContents(E_Space *space, E_DefinitionFlags flags,
STR_Index name, C_TokenArray body, C_Id id);
internal void _E_DeleteDefinition(E_Definition *definition);
////////////////////////////////
// NOTE(allen): Space
internal String8
E_SpaceName(E_Space *space){
STR_Hash *string_hash = APP_GetStringHash();
String8 path = STR_Read(string_hash, space->save_path);
String8 file_name = STR_NameFromPath(path);
String8 name = STR_NameWithoutExtension(file_name);
return(name);
}
internal STR_Index
E_SpaceNameIndex(E_Space *space){
STR_Hash *string_hash = APP_GetStringHash();
String8 path = STR_Read(string_hash, space->save_path);
String8 name = STR_NameFromPath(path);
STR_Index name_index = STR_Save(string_hash, name);
return(name_index);
}
internal E_Space*
E_NewSpace(void){
E_Space *result = vars->free_space;
if (result != 0){
SLLStackPop(vars->free_space);
}
else{
result = PushArray(vars->arena, E_Space, 1);
}
MemoryZeroStruct(result);
DLLPushBack_NP(vars->first_space_ordered, vars->last_space_ordered, result, ordered_next, ordered_prev);
DLLPushBack(vars->first_invalid_space, vars->last_invalid_space, result);
result->invalid = 1;
result->id_counter = 1;
return(result);
}
internal void
_E_SpaceFreeAllDefinitions(E_Space *space){
for (E_Definition *node = space->first_definition, *next = 0;
node != 0;
node = next){
next = node->next;
_E_DeleteDefinition(node);
}
}
internal void
E_DeleteSpace(E_Space *space){
_E_SpaceFreeAllDefinitions(space);
DLLRemove_NP(vars->first_space_ordered, vars->last_space_ordered, space, ordered_next, ordered_prev);
if (space->invalid){
DLLRemove(vars->first_invalid_space, vars->last_invalid_space, space);
}
else{
STR_Index name = E_SpaceNameIndex(space);
APP_SignalSpaceNameAvailable(name);
DLLRemove(vars->first_space, vars->last_space, space);
}
SLLStackPush(vars->free_space, space);
}
// TODO(allen): vvvv names
internal C_Cell*
E_GetGlobalEnvironment(E_Space *space){
return(space->defines_env);
}
internal C_Cell*
E_GetGlobalIdEnvironment(E_Space *space){
return(space->id_env);
}
internal C_Id
E_GetUniqueCompId(E_Space *space){
C_Id result = space->id_counter;
space->id_counter += 1;
return(result);
}
internal void
E_SignalIdentifierAvailable(E_Space *space, STR_Index index){
if (space->identifier_available_count < ArrayCount(space->identifier_available)){
space->identifier_available[space->identifier_available_count] = index;
}
space->identifier_available_count += 1;
}
internal E_Space*
_E_GetSpace(STR_Index name, E_Space *skip, E_Space *first){
E_Space *result = 0;
for (E_Space *space = first;
space != 0;
space = space->next){
if (space == skip){
continue;
}
STR_Index space_name = E_SpaceNameIndex(space);
if (space_name == name){
result = space;
break;
}
}
return(result);
}
internal E_Space*
E_GetSpace(STR_Index name, E_Space *skip){
E_Space *result = _E_GetSpace(name, skip, vars->first_space);
return(result);
}
internal E_Space*
E_GetInvalidSpace(STR_Index name, E_Space *skip){
E_Space *result = _E_GetSpace(name, skip, vars->first_invalid_space);
return(result);
}
internal E_Space*
E_GetSpaceByPath(STR_Index path, E_Space *skip){
E_Space *result = 0;
for (E_Space *space = vars->first_space_ordered;
space != 0;
space = space->ordered_next){
if (space == skip){
continue;
}
if (space->save_path == path){
result = space;
break;
}
}
return(result);
}
internal E_Definition*
_E_GetDefinition(STR_Index name, E_Definition *skip, E_Definition *first){
E_Definition *result = 0;
for (E_Definition *node = first;
node != 0;
node = node->next){
if (node == skip){
continue;
}
STR_Index node_name = E_DefinitionNameIndex(node);
if (node_name == name){
result = node;
break;
}
}
return(result);
}
internal E_Definition*
E_GetDefinition(E_Space *space, STR_Index name, E_Definition *skip){
E_Definition *result = _E_GetDefinition(name, skip, space->first_definition);
return(result);
}
internal E_Definition*
E_GetInvalidDefinition(E_Space *space, STR_Index name, E_Definition *skip){
E_Definition *result = _E_GetDefinition(name, skip, space->first_invalid_definition);
return(result);
}
internal E_Definition*
E_GetDefinitionByID(E_Space *space, C_Id id, E_Definition *skip){
E_Definition *result = 0;
for (E_Definition *node = space->first_definition_ordered;
node != 0;
node = node->ordered_next){
if (node == skip){
continue;
}
if (id == node->id){
result = node;
break;
}
}
return(result);
}
internal void
_E_SpaceSetValid(E_Space *space){
if (space->invalid){
DLLRemove(vars->first_invalid_space, vars->last_invalid_space, space);
DLLPushBack(vars->first_space, vars->last_space, space);
space->invalid = 0;
}
}
internal void
_E_SpaceSetInvalid(E_Space *space){
if (!space->invalid){
DLLRemove(vars->first_space, vars->last_space, space);
DLLPushBack(vars->first_invalid_space, vars->last_invalid_space, space);
space->invalid = 1;
}
}
internal void
E_SpaceUpdateValidation(E_Space *space){
b32 should_be_valid = 0;
STR_Index name = E_SpaceNameIndex(space);
if (name != 0){
E_Space *existing_conflict = E_GetSpace(name, space);
if (existing_conflict == 0){
should_be_valid = 1;
}
}
if (!space->invalid){
if (name != space->last_name){
APP_SignalSpaceNameAvailable(space->last_name);
}
}
space->last_name = name;
if (should_be_valid){
_E_SpaceSetValid(space);
}
else{
_E_SpaceSetInvalid(space);
}
}
internal String8
E_SerializeSpace(M_Arena *arena, E_Space *space){
String8_List out = {0};
u8 null_byte = 0;
String8 null = S8(&null_byte, 1);
for (E_Definition *node = space->first_definition_ordered;
node != 0;
node = node->ordered_next){
// NOTE(allen): id
StringListPushF(arena, &out, "%llu", node->id);
StringListPush(arena, &out, null);
// NOTE(allen): flags
if (node->flags & E_DefinitionFlag_Invalid){
StringListPush(arena, &out, S8Lit("I"));
}
if (node->flags & E_DefinitionFlag_Page){
StringListPush(arena, &out, S8Lit("P"));
}
if (node->flags & E_DefinitionFlag_Test){
StringListPush(arena, &out, S8Lit("T"));
}
StringListPush(arena, &out, null);
// NOTE(allen): name
StringListPush(arena, &out, E_DefinitionName(node));
StringListPush(arena, &out, null);
// NOTE(allen): body
C_StringizeTokensStream(arena, node->body.tokens, node->body.count, &out);
StringListPush(arena, &out, null);
}
return(StringListJoin(arena, &out, 0));
}
internal E_Space*
E_DeserializeSpace(String8 data){
STR_Hash *string_hash = APP_GetStringHash();
M_Arena *scratch = OS_GetScratch();
E_Space *space = E_NewSpace();
u8 null_byte = 0;
String8_List parts = StringSplit(scratch, data, &null_byte, 1);
u64 max_id = 0;
for (String8_Node *node = parts.first;
node != 0;){
String8_Node *id_node = node;
String8_Node *flags_node = ((id_node != 0)?id_node->next:0);
String8_Node *name_node = ((flags_node != 0)?flags_node->next:0);
String8_Node *body_node = ((name_node != 0)?name_node->next:0);
node = ((body_node != 0)?body_node->next:0);
if (body_node != 0){
u64 id = GetFirstIntegerFromString(id_node->string);
if (id == 0){
space->init_error = 1;
continue;
}
E_Definition *duplicate = E_GetDefinitionByID(space, id, 0);
if (duplicate != 0){
space->init_error = 1;
continue;
}
String8 name_string = name_node->string;
if (name_string.size > 0 && !C_ValidLabel(name_string)){
name_string.size = 0;
}
E_DefinitionFlags flags = 0;
{
u64 size = flags_node->string.size;
u8 *ptr = flags_node->string.str;
for (u64 i = 0; i < size; i += 1, ptr += 1){
if (*ptr == 'I'){
flags |= E_DefinitionFlag_Invalid;
}
if (*ptr == 'P'){
flags |= E_DefinitionFlag_Page;
}
if (*ptr == 'T'){
flags |= E_DefinitionFlag_Test;
}
}
}
STR_Index name = STR_Save(string_hash, name_node->string);
C_TokenArray tokens = C_Lex(scratch, body_node->string);
_E_CreateNewDefinitionWithContents(space, flags, name, tokens, id);
max_id = Max(max_id, id);
}
}
if (max_id < (~(u64)0) - (~(u32)0)){
space->id_counter = max_id + 1;
}
else{
space->init_error = 1;
}
space->dirty = 0;
OS_ReleaseScratch(scratch);
return(space);
}
////////////////////////////////
// NOTE(allen): Definitions
internal E_Definition*
_E_AllocateDefinition(void){
E_Definition *result = vars->free_definition;
if (result != 0){
SLLStackPop(vars->free_definition);
}
else{
result = PushArray(vars->arena, E_Definition, 1);
}
MemoryZeroStruct(result);
return(result);
}
internal void
_E_FreeDefinition(E_Definition *definition){
SLLStackPush(vars->free_definition, definition);
}
internal STR_Index
E_DefinitionNameIndex(E_Definition *definition){
STR_Index result = 0;
if (definition->name.count > 0){
result = definition->name.tokens[0].string;
}
return(result);
}
internal String8
E_DefinitionName(E_Definition *definition){
String8 result = STR_Read(APP_GetStringHash(), E_DefinitionNameIndex(definition));
return(result);
}
internal b32
E_DefinitionCanEval(E_Definition *definition){
return(!definition->invalid_name_field &&
definition->name_cell != 0 &&
definition->body_cell != 0);
}
internal E_Definition*
_E_CreateNewDefinitionWithContents(E_Space *space, E_DefinitionFlags flags,
STR_Index name, C_TokenArray body, C_Id id){
E_Definition *definition = _E_AllocateDefinition();
DLLPushBack_NP(space->first_definition_ordered, space->last_definition_ordered, definition, ordered_next, ordered_prev);
if (flags & E_DefinitionFlag_Invalid){
DLLPushBack(space->first_invalid_definition, space->last_invalid_definition, definition);
}
else{
DLLPushBack(space->first_definition, space->last_definition, definition);
}
definition->flags = flags;
definition->space = space;
definition->name = E_InitTextFieldTokenBuffer(&definition->name_token_buffer);
if (name != 0){
C_Token new_token = { C_TokenKind_Label, name };
E_TokenBufferReplaceRange(&definition->name, MakeRangeu(0, 0), &new_token, 1);
}
definition->body = E_InitTokenBuffer();
if (body.count > 0){
E_TokenBufferReplaceRange(&definition->body, MakeRangeu(0, 0), body.vals, body.count);
}
definition->bucket = C_InitCellBucket(APP_GetCellMemory());
definition->id = id;
space->dirty = 1;
return(definition);
}
internal E_Definition*
_E_CreateNewDefinition(E_Space *space){
C_TokenArray empty = {0};
E_Definition *definition = _E_CreateNewDefinitionWithContents(space, 1, 0, empty, E_GetUniqueCompId(space));
return(definition);
}
internal void
_E_DeleteDefinition(E_Definition *definition){
E_Space *space = definition->space;
E_ReleaseTokenBuffer(&definition->body);
if (definition->flags & E_DefinitionFlag_Invalid){
DLLRemove(space->first_invalid_definition, space->last_invalid_definition, definition);
}
else{
STR_Index name = E_DefinitionNameIndex(definition);
E_SignalIdentifierAvailable(definition->space, name);
DLLRemove(space->first_definition, space->last_definition, definition);
}
DLLRemove_NP(space->first_definition_ordered, space->last_definition_ordered, definition, ordered_next, ordered_prev);
C_ClearBucket(&definition->bucket);
_E_FreeDefinition(definition);
}
internal void
_E_DefinitionSetValid(E_Definition *definition){
if (definition->flags & E_DefinitionFlag_Invalid){
E_Space *space = definition->space;
DLLRemove(space->first_invalid_definition, space->last_invalid_definition, definition);
DLLPushBack(space->first_definition, space->last_definition, definition);
definition->flags ^= E_DefinitionFlag_Invalid;
}
}
internal void
_E_DefinitionSetInvalid(E_Definition *definition){
if (!(definition->flags & E_DefinitionFlag_Invalid)){
E_Space *space = definition->space;
DLLRemove(space->first_definition, space->last_definition, definition);
DLLPushBack(space->first_invalid_definition, space->last_invalid_definition, definition);
definition->flags ^= E_DefinitionFlag_Invalid;
}
}
internal void
E_DefinitionUpdateValidation(E_Definition *definition){
b32 should_be_valid = 0;
STR_Index name = E_DefinitionNameIndex(definition);
if (name != 0){
E_Definition *existing_conflict = E_GetDefinition(definition->space, name, definition);
if (existing_conflict == 0){
should_be_valid = 1;
}
}
if (!(definition->flags & E_DefinitionFlag_Invalid)){
if (name != definition->last_name){
E_SignalIdentifierAvailable(definition->space, definition->last_name);
}
}
definition->last_name = name;
if (should_be_valid){
_E_DefinitionSetValid(definition);
}
else{
_E_DefinitionSetInvalid(definition);
}
}
internal void
E_DefinitionUpdateCompute(E_Definition *definition){
C_Statics *statics = APP_GetStatics();
C_CellBucket *bucket = &definition->bucket;
C_ClearBucket(bucket);
u64 error_count = 0;
u64 error_space_count = ArrayCount(definition->errors);
C_ParseError *error_space = definition->errors;
for (u64 i = 0; i < ArrayCount(definition->buffers); i += 1){
u64 new_error_count = 0;
definition->cells[i] = C_Parse(statics, bucket, definition->buffers[i].tokens, definition->buffers[i].count,
error_space, ArrayCount(definition->errors) - error_count, &new_error_count);
error_count += new_error_count;
}
definition->error_count = error_count;
STR_Index name_indx = C_GetIdentifierFromCell(definition->name_cell);
definition->invalid_name_field = (name_indx == 0);
}
internal C_Cell*
E_DefinitionEvaluate(E_Definition *definition){
C_EvalCtx eval_ctx = C_InitEvalCtx(APP_GetStatics(), APP_GetEvalBucket(), E_GetGlobalIdEnvironment(definition->space));
C_Cell *cell = 0;
if (definition->body_cell != 0){
C_Cell *env = E_GetGlobalEnvironment(definition->space);
cell = C_EvaluateDeferred(&eval_ctx, &definition->deferred, &env);
}
return(cell);
}
#if 1
internal void
_E_AssertSpaceInvariants(E_Space *space){
u64 id = 0;
for (E_Definition *node = space->first_definition_ordered;
node != 0;
node = node->ordered_next){
Assert(id < node->id);
id = node->id;
E_Definition *scan = space->first_definition;
if (node->flags & E_DefinitionFlag_Invalid){
scan = space->first_invalid_definition;
}
b32 found = 0;
for (;scan != 0;
scan = scan->next){
if (scan == node){
found = 1;
break;
}
}
Assert(found);
}
}
internal void
_E_AssertGlobalEngineInvariants(void){
for (E_Space *space = vars->first_space_ordered;
space != 0;
space = space->ordered_next){
_E_AssertSpaceInvariants(space);
}
}
#else
#define _E_AssertSpaceInvariants(S)
#define _E_AssertGlobalEngineInvariants()
#endif
////////////////////////////////
// NOTE(allen): Tiles
internal E_Tile*
E_AllocateTile(void){
E_Tile *result = vars->free_tile;
if (result != 0){
SLLStackPop(vars->free_tile);
}
else{
result = PushArray(vars->arena, E_Tile, 1);
}
MemoryZeroStruct(result);
return(result);
}
internal void
E_FreeTile(E_Tile *tile){
SLLStackPush(vars->free_tile, tile);
}
internal void
E_TileInit(E_Tile *tile, E_Definition *definition, R_Font *font){
tile->definition = definition;
for (u64 i = 0; i < ArrayCount(tile->editors); i += 1){
tile->editors[i] = E_InitEditorState(&definition->buffers[i], font);
E_EditorUpdateLayout(&tile->editors[i], font);
}
E_EditorSetFieldMode(&tile->name, E_EditorFieldMode_CodeIdentifier);
tile->font = font;
}
internal void
E_TileUpdateLayout(E_Tile *tile, R_Font *font){
tile->font = font;
for (u64 i = 0; i < ArrayCount(tile->editors); i += 1){
E_EditorUpdateLayout(&tile->editors[i], font);
}
}
typedef enum{
E_TileUIPass_Height,
E_TileUIPass_Action,
} E_TileUIPass;
internal f32
_E_TileUI(E_View *view, E_Tile *tile, f32 y, Range x, E_TileUIPass pass){
E_Definition *definition = tile->definition;
E_EditorState *active = APP_GetActiveEditor();
b32 show_parse = 0;
// NOTE(allen): Layout
String8 empty_field_label[2];
empty_field_label[0] = S8Lit("name");
empty_field_label[1] = S8Lit("body");
R_SelectFont(tile->font);
v2 space_dim = R_StringDim(1.f, S8Lit(" "));
f32 outline_t = 2.f;
v2 btn_dim = V2Add(space_dim, v2(outline_t*4.f, outline_t*4.f));
Range inner_x = RangeShrink(x, outline_t);
Range edit_x = inner_x;
Range button_x = RangeSplit(&edit_x, Side_Min, btn_dim.x);
f32 top = y;
y += outline_t;
Range code_y[ArrayCount(tile->editors)];
{
E_EditorState *editor = tile->editors;
for (u64 i = 0; i < ArrayCount(tile->editors); i += 1, editor += 1){
code_y[i] = MakeRange(y, y + editor->runes.dim.y);
y = code_y[i].max + outline_t;
}
}
show_parse = (show_parse && (definition->body_cell != 0));
Range parse_y = {0};
if (show_parse){
parse_y = MakeRange(y, y + space_dim.y);
y = parse_y.max + outline_t;
}
Range eval_y = {0};
eval_y = MakeRange(y, y + space_dim.y);
y = eval_y.max + outline_t;
Range inner_y = MakeRange(top + outline_t, y - outline_t);
Rect rect = MakeRectRanges(x, MakeRange(top, y));
if (pass == E_TileUIPass_Action){
// NOTE(allen): Eval Update
C_Cell *evaluated = E_DefinitionEvaluate(definition);
// NOTE(allen): Color Profile
UI_ColorProfile *cl = UI_ColorsDefault();
if (definition->flags & E_DefinitionFlag_Page){
cl = UI_ColorsPage();
}
else if (definition->flags & E_DefinitionFlag_Test){
cl = UI_ColorsTest();
}
else if (definition->flags & E_DefinitionFlag_Invalid){
cl = UI_ColorsProblem();
}
for (u64 i = 0; i < ArrayCount(tile->editors); i += 1){
if (active == &tile->editors[i]){
APP_SetActiveTile(tile);
break;
}
}
UI_ActionLevel actlvl = UI_ActionLevel_None;
if (UI_MouseInRect(rect)){
actlvl = UI_ActionLevel_Hover;
}
else{
if (APP_GetActiveTile() == tile){
actlvl = UI_ActionLevel_Active;
}
}
// NOTE(allen): Buttons
{
Rect box = MakeRectRanges(button_x, inner_y);
R_Rect(box, cl->outline[UI_ActionLevel_None], 1.f);
UI_ButtonCtx btn_ctx = UI_InitButtonCtx(box, btn_dim, tile->font, UI_IdP(tile));
btn_ctx.outline_t = outline_t;
btn_ctx.enable_drop_down = 1;
btn_ctx.enable_hot_keys = (actlvl == UI_ActionLevel_Active);
UI_SetColorProfile(&btn_ctx, cl);
if (E_ViewWillCloseDefinition(view)){
UI_NextHotkey(&btn_ctx, Key_X, KeyModifier_Alt);
UI_NextTooltip(&btn_ctx, S8Lit("close"));
UI_SetRedText(&btn_ctx.cl);
if (UI_Button(&btn_ctx, 'x', ' ')){
tile->free_me = 1;
view->free_children = 1;
}
UI_SetColorProfile(&btn_ctx, cl);
}
STR_Index name = E_DefinitionNameIndex(definition);
UI_NextHotkey(&btn_ctx, Key_V, KeyModifier_Alt);
UI_NextTooltip(&btn_ctx, S8Lit("force validate"));
UI_NextCondition(&btn_ctx, ((definition->flags & E_DefinitionFlag_Invalid) && name != 0));
if (UI_Button(&btn_ctx, '!', 'v')){
E_Definition *existing_conflict = E_GetDefinition(definition->space, name, definition);
if (existing_conflict != 0){
_E_DefinitionSetInvalid(existing_conflict);
}
_E_DefinitionSetValid(definition);
}
UI_NextHotkey(&btn_ctx, Key_I, KeyModifier_Alt);
UI_NextTooltip(&btn_ctx, S8Lit("view page"));
if (UI_Button(&btn_ctx, 'I', 'p')){
definition->flags |= E_DefinitionFlag_Page;
OP_ViewPageFromDefinition(definition);
}
UI_NextTooltip(&btn_ctx, S8Lit("toggle test mode"));
if (UI_Button(&btn_ctx, 'T', 't')){
definition->flags ^= E_DefinitionFlag_Test;
}
UI_NextTooltip(&btn_ctx, S8Lit("toggle page mode"));
if (UI_Button(&btn_ctx, 'P', 't')){
definition->flags ^= E_DefinitionFlag_Page;
}
{
UI_SetRedText(&btn_ctx.cl);
UI_NextTooltip(&btn_ctx, S8Lit("delete definition"));
if (UI_Button(&btn_ctx, '-', ' ')){
definition->delete_me = 1;
definition->space->delete_signal = 1;
}
UI_SetColorProfile(&btn_ctx, cl);
}
}
// NOTE(allen): Code
E_EditorState *editor = tile->editors;
for (u64 i = 0; i < ArrayCount(tile->editors); i += 1, editor += 1){
Rect box = MakeRectRanges(edit_x, code_y[i]);
Rect clip_restore = R_PushClip(box);
if (active == editor){
R_Rect(box, cl->back[UI_ActionLevel_Hover], 0.3);
}
if (UI_MouseInRect(box)){
R_Rect(box, cl->back[UI_ActionLevel_Hover], 1.f);
OS_Event *event = 0;
if (UI_TryGetLeftClick(&event)){
APP_SetActiveEditor(editor);
E_EditorHandleMousePressEvent(editor, event, box.p0);
}
}
E_EditorRender(editor, tile->font, box.p0);
if (active != editor && editor->buffer->count == 0){
R_String(box.p0, 1.f, empty_field_label[i], cl->front[0], 0.15f);
}
R_SetClip(clip_restore);
}
// NOTE(allen): Fallback Clicks
if (UI_MouseInRect(rect)){
if (UI_TryEatLeftClick()){
APP_SetActiveTile(tile);
}
}
// NOTE(allen): Parse/Eval Render
M_Arena *scratch = OS_GetScratch();
if (show_parse){
String8 string = C_StringizeCell(scratch, definition->body_cell);
if (string.size > 0){
R_String(v2(edit_x.min, parse_y.min), 1.f, string, cl->front[0], 0.5f);
}
else{
R_String(v2(edit_x.min, parse_y.min), 1.f, S8Lit("parse"), cl->front[0], 0.15f);
}
}
b32 eval_fallback = 1;
if (evaluated != 0){
String8 string = C_StringizeCell(scratch, evaluated);
if (string.size > 0){
R_String(v2(edit_x.min, eval_y.min), 1.f, string, cl->front[0], 0.5f);
eval_fallback = 0;
}
}
if (eval_fallback){
R_String(v2(edit_x.min, eval_y.min), 1.f, S8Lit("eval"), cl->front[0], 0.15f);
}
OS_ReleaseScratch(scratch);
// NOTE(allen): Outline
R_RectOutline(rect, outline_t, cl->outline[actlvl], 1.f);
}
return(rect.y1 - rect.y0);
}
internal f32
E_TileUIGetHeight(E_View *view, E_Tile *tile, f32 y, Range x){
return(_E_TileUI(view, tile, y, x, E_TileUIPass_Height));
}
internal f32
E_TileUIUpdateAndRender(E_View *view, E_Tile *tile, f32 y, Range x){
return(_E_TileUI(view, tile, y, x, E_TileUIPass_Action));
}
internal E_Tile*
E_GetTileForDefinition(E_Tile *first, E_Definition *definition){
E_Tile *result = 0;
for (E_Tile *tile = first;
tile != 0;
tile = tile->next){
if (tile->definition == definition){
result = tile;
break;
}
}
return(result);
}
////////////////////////////////
// NOTE(allen): Views
internal E_View*
E_NewView(void){
E_View *result = vars->free_view;
if (result != 0){
SLLStackPop(vars->free_view);
}
else{
result = PushArray(vars->arena, E_View, 1);
}
MemoryZeroStruct(result);
DLLPushBack(vars->first_view, vars->last_view, result);
return(result);
}
internal void
E_DeleteView(E_View *view){
DLLRemove(vars->first_view, vars->last_view, view);
SLLStackPush(vars->free_view, view);
}
internal void
_E_ViewFreeAllTiles(E_View *view){
for (E_Tile *tile = view->first_tile, *next = 0;
tile != 0;
tile = next){
next = tile->next;
if (tile != 0){
E_FreeTile(tile);
}
}
view->first_tile = view->last_tile = 0;
}
internal void
E_ViewDeriveFromSource(E_View *view){
if (view->derive_from_source != 0){
_E_ViewFreeAllTiles(view);
view->derive_from_source(view, 0, 0);
}
}
internal E_Definition*
E_ViewCreateNewDefinition(E_View *view){
E_Definition *definition = 0;
if (view->create_new_definition != 0){
view->create_new_definition(view, 0, (E_ViewCallbackOut*)&definition);
}
return(definition);
}
internal void
E_ViewLookAtDefinition(E_View *view, E_Definition *definition){
if (view->look_at_definition != 0){
E_ViewCallbackIn in = {0};
in.definition = definition;
view->look_at_definition(view, &in, 0);
}
}
internal b32
E_ViewWillLookAtDefinition(E_View *view){
return(view->look_at_definition != 0);
}
internal void
E_ViewCleanup(E_View *view){
if (view->cleanup != 0){
view->cleanup(view, 0, 0);
}
}
internal b32
E_ViewWillCloseDefinition(E_View *view){
return(view->cleanup != 0);
}
internal void
E_ViewShutdown(E_View *view){
if (view->shutdown != 0){
view->shutdown(view, 0, 0);
}
_E_ViewFreeAllTiles(view);
MemoryZeroStruct(view);
}
internal String8
E_ViewGetName(E_View *view){
String8 result = view->name;
if (view->get_name != 0){
view->get_name(view, 0, (E_ViewCallbackOut*)&result);
}
return(result);
}
internal E_Tile*
E_ViewGetTileForDefinition(E_View *view, E_Definition *definition){
E_Tile *result = E_GetTileForDefinition(view->first_tile, definition);
return(result);
}
internal E_Tile*
_E_ViewInsertDefinition(E_View *view, E_Definition *definition){
E_Tile *tile = E_AllocateTile();
DLLPushBack(view->first_tile, view->last_tile, tile);
E_TileInit(tile, definition, view->font);
return(tile);
}
internal void
_E_ScrollToTile(E_View *view, E_Tile *tile){
// TODO(allen): Scroll to it
}
internal E_Tile*
_E_ViewInsertOrScrollTo(E_View *view, E_Definition *definition, b32 *is_new_out){
E_Tile *tile = E_ViewGetTileForDefinition(view, definition);
if (tile != 0){
_E_ScrollToTile(view, tile);
*is_new_out = 0;
}
else{
tile = _E_ViewInsertDefinition(view, definition);
*is_new_out = 1;
}
return(tile);
}
internal E_Definition*
_E_ViewNewDefinitionAndActivate(E_View *view){
E_Definition *definition = _E_CreateNewDefinition(view->space);
b32 ignore;
E_Tile *tile = _E_ViewInsertDefinition(view, definition);
APP_SetActiveEditor(&tile->name);
return(definition);
}
internal void
_E_ViewCleanup(E_View *view){
if (view->free_children){
view->free_children = 0;
for (E_Tile *tile = view->first_tile, *next = 0;
tile != 0;
tile = next){
Assert(!tile->definition->delete_me);
next = tile->next;
if (tile->free_me){
DLLRemove(view->first_tile, view->last_tile, tile);
E_FreeTile(tile);
view->state_ahead_of_source = 1;
}
}
}
}
////////////////////////////////
// NOTE(allen): Global view
internal
VIEW_CALLBACK_SIG(E_GlobalView_DeriveFromSource){
Assert(view->first_tile == 0);
for (E_Definition *node = view->space->first_definition_ordered;
node != 0;
node = node->ordered_next){
_E_ViewInsertDefinition(view, node);
}
}
internal
VIEW_CALLBACK_SIG(E_GlobalView_CreateNewDefinition){
out->definition = _E_ViewNewDefinitionAndActivate(view);
}
internal
VIEW_CALLBACK_SIG(E_GlobalView_LookAtDefinition){
E_Tile *tile = E_ViewGetTileForDefinition(view, in->definition);
Assert(tile != 0);
_E_ScrollToTile(view, tile);
APP_SetActiveEditor(&tile->name);
}
internal
VIEW_CALLBACK_SIG(E_GlobalView_GetName){
String8 path = STR_Read(APP_GetStringHash(), view->space->save_path);
out->name = STR_NameFromPath(path);
if (out->name.size == 0){
out->name = S8Lit("no path!");
}
}
internal void
E_InitGlobalView(E_View *view, R_Font *font, E_Space *space){
view->kind = E_ViewKind_Global;
view->derive_from_source = E_GlobalView_DeriveFromSource;
view->create_new_definition = E_GlobalView_CreateNewDefinition;
view->look_at_definition = E_GlobalView_LookAtDefinition;
view->get_name = E_GlobalView_GetName;
view->font = font;
view->space = space;
E_GlobalView_DeriveFromSource(view, 0, 0);
}
////////////////////////////////
// NOTE(allen): Page view
internal
VIEW_CALLBACK_SIG(E_PageView_DeriveFromSource){
Assert(view->first_tile == 0);
E_Definition *definition = view->definition;
C_Cell *cell = E_DefinitionEvaluate(definition);
C_Statics *statics = APP_GetStatics();
for (C_Cell *node = cell;
!C_IsNil(node) && node->kind == C_CellKind_Constructed;
node = node->next){
C_Cell *first = node->first_child;
if (first->kind == C_CellKind_Id){
C_Cell *env = E_GetGlobalIdEnvironment(definition->space);
C_Cell *resolved = C_EnvironmentResolve(statics, env, first->id);
if (resolved != 0 && resolved->kind == C_CellKind_Deferred){
E_Definition *resolved_definition = (E_Definition*)resolved->deferred->user_ptr;
_E_ViewInsertDefinition(view, resolved_definition);
}
}
}
view->state_ahead_of_source = 0;
}
internal
VIEW_CALLBACK_SIG(E_PageView_CreateNewDefinition){
out->definition = _E_ViewNewDefinitionAndActivate(view);
view->state_ahead_of_source = 1;
}
internal
VIEW_CALLBACK_SIG(E_PageView_LookAtDefinition){
b32 is_new = 0;
E_Tile *tile = _E_ViewInsertOrScrollTo(view, in->definition, &is_new);
APP_SetActiveEditor(&tile->name);
if (is_new){
view->state_ahead_of_source = 1;
}
}
internal
VIEW_CALLBACK_SIG(E_PageView_Cleanup){
_E_ViewCleanup(view);
if (view->state_ahead_of_source){
view->state_ahead_of_source = 0;
STR_Hash *string_hash = APP_GetStringHash();
M_Arena *scratch = OS_GetScratch();
C_Token *tokens = PushArray(scratch, C_Token, 0);
{
C_Token *token = PushArray(scratch, C_Token, 1);
token->kind = C_TokenKind_Quote;
}
{
C_Token *token = PushArray(scratch, C_Token, 1);
token->kind = C_TokenKind_OpenParen;
}
u64 counter = 0;
for (E_Tile *tile = view->first_tile;
tile != 0;
tile = tile->next, counter += 1){
{
C_Token *token = PushArray(scratch, C_Token, 1);
token->kind = C_TokenKind_Label;
M_Temp temp = M_BeginTemp(scratch);
String8 string = PushStringF(scratch, "#%llu", tile->definition->id);
token->string = STR_Save(string_hash, string);
M_EndTemp(temp);
}
if (tile->next != 0){
C_Token *token = PushArray(scratch, C_Token, 1);
token->kind = ((counter%8 < 7)?C_TokenKind_Space:C_TokenKind_Newline);
}
}
{
C_Token *token = PushArray(scratch, C_Token, 1);
token->kind = C_TokenKind_CloseParen;
}
C_Token *opl = PushArray(scratch, C_Token, 0);
E_TokenBufferReplaceRange(&view->definition->body, MakeRangeu(0, view->definition->body.count), tokens, (opl - tokens));
OS_ReleaseScratch(scratch);
}
}
internal
VIEW_CALLBACK_SIG(E_PageView_GetName){
E_Definition *definition = view->definition;
String8 name = E_DefinitionName(definition);
out->name = PushStringF(APP_GetFrameArena(), "%.*s", StringExpand(name));
}
internal void
E_InitPageView(E_View *view, R_Font *font, E_Definition *definition){
view->kind = E_ViewKind_Page;
view->derive_from_source = E_PageView_DeriveFromSource;
view->create_new_definition = E_PageView_CreateNewDefinition;
view->look_at_definition = E_PageView_LookAtDefinition;
view->cleanup = E_PageView_Cleanup;
view->get_name = E_PageView_GetName;
view->font = font;
view->space = definition->space;
view->definition = definition;
E_PageView_DeriveFromSource(view, 0, 0);
}