mirror of https://gitlab.com/nakst/essence
script console: command history, log groups
This commit is contained in:
parent
13affeb977
commit
f6a54e1093
|
@ -2,11 +2,6 @@
|
|||
#include <essence.h>
|
||||
#include <shared/array.cpp>
|
||||
|
||||
// TODO:
|
||||
// LogOpenGroup()
|
||||
// LogOpenList()
|
||||
// LogClose()
|
||||
|
||||
struct Instance : EsInstance {
|
||||
EsCommand commandClearOutput;
|
||||
|
||||
|
@ -18,6 +13,8 @@ struct Instance : EsInstance {
|
|||
EsElement *defaultPrefixDisplay;
|
||||
EsPanel *inputRow;
|
||||
EsPanel *outputPanel;
|
||||
EsElement *logOutputGroup;
|
||||
uintptr_t logGroupNestLevel;
|
||||
EsSpacer *outputDecoration;
|
||||
EsTextbox *inputTextbox;
|
||||
|
||||
|
@ -30,9 +27,13 @@ struct Instance : EsInstance {
|
|||
Array<EsElement *> outputElements;
|
||||
};
|
||||
|
||||
EsFileInformation globalCommandHistoryFile;
|
||||
|
||||
void AddPrompt(Instance *instance);
|
||||
void AddOutput(Instance *instance, const char *text, size_t textBytes);
|
||||
void AddLogOutput(Instance *instance, const char *text, size_t textBytes);
|
||||
void AddLogOpenGroup(Instance *instance, const char *title, size_t titleBytes);
|
||||
void AddLogClose(Instance *instance);
|
||||
|
||||
// --------------------------------- Script engine interface.
|
||||
|
||||
|
@ -172,6 +173,20 @@ int ExternalLog(ExecutionContext *context, Value *returnValue) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int ExternalLogOpenGroup(ExecutionContext *context, Value *returnValue) {
|
||||
(void) returnValue;
|
||||
STACK_POP_STRING(entryText, entryBytes);
|
||||
AddLogOpenGroup(scriptInstance, entryText, entryBytes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ExternalLogClose(ExecutionContext *context, Value *returnValue) {
|
||||
(void) context;
|
||||
(void) returnValue;
|
||||
AddLogClose(scriptInstance);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ExternalTextFormat(ExecutionContext *context, Value *returnValue, const char *mode) {
|
||||
char *buffer = (char *) EsHeapAllocate(16, false);
|
||||
uintptr_t index = HeapAllocate(context);
|
||||
|
@ -582,6 +597,27 @@ const EsStyle styleOutputPanel = {
|
|||
},
|
||||
};
|
||||
|
||||
const EsStyle styleLogOutputGroup = {
|
||||
.metrics = {
|
||||
.mask = ES_THEME_METRICS_INSETS | ES_THEME_METRICS_GAP_MAJOR,
|
||||
.insets = ES_RECT_4(26, 0, 4, 4),
|
||||
.gapMajor = 0,
|
||||
},
|
||||
};
|
||||
|
||||
const EsStyle styleLogOutputGroupTitle = {
|
||||
.metrics = {
|
||||
.mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT | ES_THEME_METRICS_INSETS
|
||||
| ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_TEXT_ALIGN | ES_THEME_METRICS_TEXT_COLOR,
|
||||
.insets = ES_RECT_4(6, 0, 0, 0),
|
||||
.textColor = COLOR_TEXT_MAIN,
|
||||
.textAlign = ES_TEXT_H_LEFT | ES_TEXT_V_CENTER,
|
||||
.textSize = TEXT_SIZE_SMALL,
|
||||
.fontFamily = ES_FONT_SANS,
|
||||
.fontWeight = 6,
|
||||
},
|
||||
};
|
||||
|
||||
const EsStyle styleOutputDecorationInProgress = {
|
||||
.metrics = {
|
||||
.mask = ES_THEME_METRICS_PREFERRED_WIDTH,
|
||||
|
@ -685,6 +721,7 @@ const EsStyle styleImageViewerPane = {
|
|||
|
||||
void AddREPLResult(ExecutionContext *context, EsElement *parent, Node *type, Value value) {
|
||||
// TODO Truncating/scrolling/collapsing/saving/copying output.
|
||||
// TODO Pagination.
|
||||
// TODO Letting scripts register custom views for structs.
|
||||
|
||||
size_t bytes;
|
||||
|
@ -884,6 +921,11 @@ void EnterCommand(Instance *instance) {
|
|||
instance->inputRow = nullptr;
|
||||
instance->defaultPrefixDisplay = nullptr;
|
||||
|
||||
uint8_t newline = '\n';
|
||||
EsFileWriteSync(globalCommandHistoryFile.handle, EsFileGetSize(globalCommandHistoryFile.handle), dataBytes, data);
|
||||
EsFileWriteSync(globalCommandHistoryFile.handle, EsFileGetSize(globalCommandHistoryFile.handle), 1, &newline);
|
||||
EsFileControl(globalCommandHistoryFile.handle, ES_FILE_CONTROL_FLUSH);
|
||||
|
||||
EsPanel *commandLogRow = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, EsStyleIntern(&styleInputRow));
|
||||
EsTextDisplayCreate(commandLogRow, ES_FLAGS_DEFAULT, EsStyleIntern(&stylePromptText), "\u2661");
|
||||
EsTextDisplay *inputCommand = EsTextDisplayCreate(commandLogRow, ES_CELL_H_FILL, EsStyleIntern(&styleCommandLogText), data, dataBytes);
|
||||
|
@ -895,6 +937,7 @@ void EnterCommand(Instance *instance) {
|
|||
EsPanel *outputPanelWrapper = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, EsStyleIntern(&styleOutputPanelWrapper));
|
||||
instance->outputDecoration = EsSpacerCreate(outputPanelWrapper, ES_CELL_V_FILL, EsStyleIntern(&styleOutputDecorationInProgress));
|
||||
instance->outputPanel = EsPanelCreate(outputPanelWrapper, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_VERTICAL, EsStyleIntern(&styleOutputPanel));
|
||||
instance->logOutputGroup = instance->outputPanel;
|
||||
|
||||
instance->inputText = data;
|
||||
instance->inputBytes = dataBytes;
|
||||
|
@ -949,17 +992,59 @@ void AddLogOutput(Instance *instance, const char *text, size_t textBytes) {
|
|||
|
||||
if (EsUTF8IsValid(text, textBytes)) {
|
||||
EsMessageMutexAcquire();
|
||||
EsTextDisplayCreate(instance->outputPanel, ES_CELL_H_FILL | ES_TEXT_DISPLAY_RICH_TEXT,
|
||||
EsTextDisplayCreate(instance->logOutputGroup, ES_CELL_H_FILL | ES_TEXT_DISPLAY_RICH_TEXT,
|
||||
EsStyleIntern(&styleOutputParagraph), text, textBytes);
|
||||
EsMessageMutexRelease();
|
||||
} else {
|
||||
EsMessageMutexAcquire();
|
||||
EsTextDisplayCreate(instance->outputPanel, ES_CELL_H_FILL,
|
||||
EsTextDisplayCreate(instance->logOutputGroup, ES_CELL_H_FILL,
|
||||
EsStyleIntern(&styleOutputParagraphItalic), EsLiteral("Encoding error.\n"));
|
||||
EsMessageMutexRelease();
|
||||
}
|
||||
}
|
||||
|
||||
void DisclosureButtonCommand(Instance *, EsElement *element, EsCommand *) {
|
||||
EsElement *panel = (EsElement *) element->userData.p;
|
||||
EsButtonSetIcon((EsButton *) element, EsElementIsHidden(panel) ? ES_ICON_PANE_HIDE_SYMBOLIC : ES_ICON_PANE_SHOW_SYMBOLIC);
|
||||
EsElementSetHidden(panel, !EsElementIsHidden(panel));
|
||||
}
|
||||
|
||||
void AddLogOpenGroup(Instance *instance, const char *title, size_t titleBytes) {
|
||||
if (instance->logGroupNestLevel >= 1000000000) {
|
||||
return;
|
||||
}
|
||||
|
||||
instance->anyOutput = true;
|
||||
instance->logGroupNestLevel++;
|
||||
|
||||
if (instance->logGroupNestLevel < 20) {
|
||||
bool startCollapsed = instance->logGroupNestLevel > 2;
|
||||
EsMessageMutexAcquire();
|
||||
EsPanel *row = EsPanelCreate(instance->logOutputGroup, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL);
|
||||
// TODO Better icons, or make a dedicated disclosure control in the API?
|
||||
EsButton *disclosureButton = EsButtonCreate(row, ES_FLAGS_DEFAULT, ES_STYLE_PUSH_BUTTON_STATUS_BAR);
|
||||
EsButtonSetIcon(disclosureButton, startCollapsed ? ES_ICON_PANE_SHOW_SYMBOLIC : ES_ICON_PANE_HIDE_SYMBOLIC);
|
||||
EsTextDisplayCreate(row, ES_CELL_H_FILL, EsStyleIntern(&styleLogOutputGroupTitle), title, titleBytes);
|
||||
instance->logOutputGroup = EsPanelCreate(instance->logOutputGroup,
|
||||
ES_CELL_H_FILL | (startCollapsed ? ES_ELEMENT_HIDDEN : 0), EsStyleIntern(&styleLogOutputGroup));
|
||||
disclosureButton->userData = instance->logOutputGroup;
|
||||
EsButtonOnCommand(disclosureButton, DisclosureButtonCommand);
|
||||
EsMessageMutexRelease();
|
||||
}
|
||||
}
|
||||
|
||||
void AddLogClose(Instance *instance) {
|
||||
if (instance->logGroupNestLevel) {
|
||||
if (instance->logGroupNestLevel < 20) {
|
||||
EsMessageMutexAcquire();
|
||||
instance->logOutputGroup = EsElementGetLayoutParent(instance->logOutputGroup);
|
||||
EsMessageMutexRelease();
|
||||
}
|
||||
|
||||
instance->logGroupNestLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
void AddPrompt(Instance *instance) {
|
||||
if (instance->outputPanel) {
|
||||
instance->outputElements.Add(EsElementGetLayoutParent(instance->outputPanel));
|
||||
|
@ -974,7 +1059,7 @@ void AddPrompt(Instance *instance) {
|
|||
instance->defaultPrefixDisplay = EsTextDisplayCreate(instance->root, ES_CELL_H_FILL, EsStyleIntern(&stylePathDefaultPrefixDisplay), "Essence HD (0:)");
|
||||
instance->inputRow = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, EsStyleIntern(&styleInputRow));
|
||||
EsTextDisplayCreate(instance->inputRow, ES_FLAGS_DEFAULT, EsStyleIntern(&stylePromptText), "\u2665");
|
||||
instance->inputTextbox = EsTextboxCreate(instance->inputRow, ES_CELL_H_FILL, EsStyleIntern(&styleInputTextbox));
|
||||
instance->inputTextbox = EsTextboxCreate(instance->inputRow, ES_CELL_H_FILL, EsStyleIntern(&styleInputTextbox)); // TODO Command history.
|
||||
EsTextboxSetupSyntaxHighlighting(instance->inputTextbox, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_SCRIPT);
|
||||
EsTextboxEnableSmartReplacement(instance->inputTextbox, false);
|
||||
instance->inputTextbox->messageUser = InputTextboxMessage;
|
||||
|
@ -992,6 +1077,8 @@ void CommandClearOutput(Instance *instance, EsElement *, EsCommand *) {
|
|||
int InstanceCallback(Instance *instance, EsMessage *message) {
|
||||
if (message->type == ES_MSG_INSTANCE_DESTROY) {
|
||||
// TODO Stopping the script thread.
|
||||
// Probably the script thread will need to open a reference to the instance,
|
||||
// and check the instance is not closed every time it attempts to acquire the message mutex.
|
||||
EsHeapFree(instance->outputLineBuffer);
|
||||
instance->outputElements.Free();
|
||||
}
|
||||
|
@ -1002,6 +1089,8 @@ int InstanceCallback(Instance *instance, EsMessage *message) {
|
|||
void _start() {
|
||||
_init();
|
||||
|
||||
globalCommandHistoryFile = EsFileOpen(EsLiteral("|Settings:/Command History.txt"), ES_FILE_WRITE);
|
||||
|
||||
// TODO Proper drive mounting.
|
||||
|
||||
size_t deviceCount;
|
||||
|
|
|
@ -441,6 +441,10 @@ char baseModuleSource[] = {
|
|||
"void LogInfo(str x) { Log(TextColorHighlight() + x); }"
|
||||
"void LogError(str x) { Log(TextColorError() + \"Error: \" + x); }"
|
||||
|
||||
"void LogOpenGroup(str title) #extcall;"
|
||||
// TODO LogOpenList, LogOpenTable, etc.
|
||||
"void LogClose() #extcall;"
|
||||
|
||||
"str TextColorError() #extcall;"
|
||||
"str TextColorHighlight() #extcall;"
|
||||
"str TextWeight(int i) #extcall;"
|
||||
|
@ -699,6 +703,8 @@ char baseModuleSource[] = {
|
|||
// --------------------------------- External function calls.
|
||||
|
||||
int ExternalLog(ExecutionContext *context, Value *returnValue);
|
||||
int ExternalLogOpenGroup(ExecutionContext *context, Value *returnValue);
|
||||
int ExternalLogClose(ExecutionContext *context, Value *returnValue);
|
||||
int ExternalTextColorError(ExecutionContext *context, Value *returnValue);
|
||||
int ExternalTextColorHighlight(ExecutionContext *context, Value *returnValue);
|
||||
int ExternalTextWeight(ExecutionContext *context, Value *returnValue);
|
||||
|
@ -741,6 +747,8 @@ int ExternalOpenDocumentEnumerate(ExecutionContext *context, Value *returnValue)
|
|||
|
||||
ExternalFunction externalFunctions[] = {
|
||||
{ .cName = "Log", .callback = ExternalLog },
|
||||
{ .cName = "LogOpenGroup", .callback = ExternalLogOpenGroup },
|
||||
{ .cName = "LogClose", .callback = ExternalLogClose },
|
||||
{ .cName = "TextColorError", .callback = ExternalTextColorError },
|
||||
{ .cName = "TextColorHighlight", .callback = ExternalTextColorHighlight },
|
||||
{ .cName = "TextWeight", .callback = ExternalTextWeight },
|
||||
|
@ -5567,12 +5575,26 @@ int ExternalSystemShellEnableLogging(ExecutionContext *context, Value *returnVal
|
|||
}
|
||||
|
||||
int ExternalLog(ExecutionContext *context, Value *returnValue) {
|
||||
(void) returnValue;
|
||||
STACK_POP_STRING(entryText, entryBytes);
|
||||
fprintf(stderr, "%.*s", (int) entryBytes, (char *) entryText);
|
||||
fprintf(stderr, coloredOutput ? "\033[0;m\n" : "\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ExternalLogOpenGroup(ExecutionContext *context, Value *returnValue) {
|
||||
(void) returnValue;
|
||||
if (context->c->stackPointer < 1) return -1;
|
||||
context->c->stackPointer--;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ExternalLogClose(ExecutionContext *context, Value *returnValue) {
|
||||
(void) context;
|
||||
(void) returnValue;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ExternalTextFormat(ExecutionContext *context, Value *returnValue, const char *mode) {
|
||||
if (coloredOutput) {
|
||||
char *buffer = malloc(32);
|
||||
|
|
Loading…
Reference in New Issue