Compare commits

..

3 Commits

Author SHA1 Message Date
Krzosa Karol
3ab6639afb Fix underline drawing over line numbers 2026-02-04 08:26:32 +01:00
Krzosa Karol
7a73a982d2 Pause on lexing stuff 2026-02-03 23:23:49 +01:00
Krzosa Karol
51f1fd1b4f ProjectDirectory to ProjectFolder 2026-02-03 21:11:02 +01:00
17 changed files with 141 additions and 143 deletions

View File

@@ -7,7 +7,7 @@
- Window position: vscode opens in fullscreen and then remembers what position it was close in (could be a config option)
- On Linux: Try to implement is_debugger_present()
- Maybe IPC for te.exe when it's already open and file arguments are passed it should perhaps open a buffer in current window??
- Add <<File>> <<ProjectDirectory>> template strings to Open (Then remove SEtWorkdirhere)
- Add <<File>> <<ProjectFolder>> template strings to Open (Then remove SEtWorkdirhere)
- :Set Filename to name current buffer ??? :O and others like that!!
- Make a fuzzy command !> grep and fuzzy over it??? (doesn't seem very useful for grep)
- Make the equivalent of SearchOpenBuffers but for cmds like !@git grep -n "@>"

View File

@@ -1,12 +1,3 @@
struct Lexer {
Allocator allocator;
char *at;
char *start;
char *end;
char *name;
Int line, column;
};
enum TriggerKind {
TriggerKind_Error,
TriggerKind_Key,
@@ -34,40 +25,7 @@ struct CachedTrigger {
};
Array<CachedTrigger> CachedTriggers;
Lexer MakeLexer(Allocator allocator, String string, char *file, Int line, Int column) {
Lexer lexer = {allocator, string.data, string.data, string.data + string.len, file, line, column};
return lexer;
}
void Advance(Lexer *lex) {
if (lex->at < lex->end) {
if (lex->at[0] == '\n') {
lex->line += 1;
lex->column = 0;
} else {
lex->column += 1;
}
lex->at += 1;
}
}
void Advance(Lexer *lex, int n) {
for (int i = 0; i < n; i += 1) Advance(lex);
}
char At(Lexer *lex) {
if (lex->at < lex->end) {
return lex->at[0];
}
return 0;
}
String AsString(Lexer *lex) {
String result = {lex->at, lex->end - lex->at};
return result;
}
void EatWhitespace(Lexer *lex) {
void EatWhitespaceEx(Lexer *lex) {
while (At(lex) != '\n' && IsWhitespace(At(lex))) {
Advance(lex);
}
@@ -85,7 +43,7 @@ Trigger *TriggerBinary(Lexer *lex, Trigger *left, Trigger *right, int key) {
Trigger *ParseKeyAtom(Lexer *lex) {
Trigger *result = AllocType(lex->allocator, Trigger);
result->kind = TriggerKind_Key;
EatWhitespace(lex);
EatWhitespaceEx(lex);
for (;;) {
String lex_string = AsString(lex);
if (StartsWith(lex_string, "ctrl")) {
@@ -123,12 +81,12 @@ Trigger *ParseKeyAtom(Lexer *lex) {
if (!found) {
result->kind = TriggerKind_Error;
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected identifier: '%d'", lex->name, lex->line, lex->column, result->key);
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected identifier: '%d'", lex->file, lex->line, lex->column, result->key);
return result;
}
} else {
result->kind = TriggerKind_Error;
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected character: '%c'", lex->name, lex->line, lex->column, At(lex));
ReportErrorf("%s:%d:%d: Failed to parse key trigger, unexpected character: '%c'", lex->file, lex->line, lex->column, At(lex));
return result;
}
@@ -143,21 +101,21 @@ Trigger *ParseKeyAtom(Lexer *lex) {
Trigger *ParseKeyChord(Lexer *lex) {
Trigger *left = ParseKeyAtom(lex);
EatWhitespace(lex);
EatWhitespaceEx(lex);
while (IsAlphanumeric(At(lex))) {
left = TriggerBinary(lex, left, ParseKeyChord(lex), ' ');
EatWhitespace(lex);
EatWhitespaceEx(lex);
}
return left;
}
Trigger *ParseKeyOr(Lexer *lex) {
Trigger *left = ParseKeyChord(lex);
EatWhitespace(lex);
EatWhitespaceEx(lex);
while (At(lex) == '|') {
Advance(lex);
left = TriggerBinary(lex, left, ParseKeyOr(lex), '|');
EatWhitespace(lex);
EatWhitespaceEx(lex);
}
return left;
}

View File

@@ -1,22 +1,25 @@
namespace DataDesc {
struct Lexer {
Allocator allocator;
char *at;
char *start;
char *end;
typedef U32 TokenKind;
enum {
TokenKind_EOF = 0,
TokenKind_Ident = (1<<0),
TokenKind_String = (1<<1),
TokenKind_Numeric = (1<<2),
TokenKind_Comment = (1<<3),
TokenKind_Error = (1<<4),
char *file;
int line, column;
TokenKind_OpenBrace = (1<<5),
TokenKind_CloseBrace = (1<<6),
TokenKind_Colon = (1<<7),
TokenKind_Comma = (1<<8),
TokenKind_Minus = (1<<9),
TokenKind_Tag = (1<<10),
TokenKind_Or = (1<<11),
};
enum TokenKind {
TokenKind_EOF,
TokenKind_String,
TokenKind_Colon,
TokenKind_Comma,
TokenKind_OpenBrace,
TokenKind_CloseBrace,
TokenKind_Error,
typedef U32 TokenGroup;
enum {
TokenGroup_String = (TokenKind_Numeric|TokenKind_String|TokenKind_Ident),
TokenGroup_Symbol = (TokenKind_OpenBrace|TokenKind_CloseBrace|TokenKind_Colon|TokenKind_Comma|TokenKind_Minus|TokenKind_Tag|TokenKind_Or),
};
struct Token {
@@ -25,14 +28,42 @@ struct Token {
struct {char *data; Int len;};
String string;
};
int line, column;
Float f;
Int line, column;
char *file;
};
Lexer MakeLexer(Allocator allocator, String string, char *file) {
Assert(string.data != NULL && string.data != 0);
Lexer result = {allocator, string.data, string.data, string.data + string.len};
result.file = file;
struct Lexer {
Allocator allocator;
char *at;
char *start;
char *end;
char *file;
Int line, column;
};
Lexer MakeLexer(Allocator allocator, String string, char *file, Int line, Int column) {
Lexer lexer = {allocator, string.data, string.data, string.data + string.len, file, line, column};
return lexer;
}
void Advance(Lexer *lex) {
if (lex->at < lex->end) {
if (lex->at[0] == '\n') {
lex->line += 1;
lex->column = 0;
}
lex->column += 1;
lex->at += 1;
}
}
void Advance(Lexer *lex, int n) {
for (int i = 0; i < n; i += 1) Advance(lex);
}
String AsString(Lexer *lex) {
String result = {lex->at, lex->end - lex->at};
return result;
}
@@ -43,18 +74,6 @@ char At(Lexer *lex) {
return 0;
}
void Advance(Lexer *lex) {
if (lex->at >= lex->end) {
return;
}
if (lex->at[0] == '\n') {
lex->line += 1;
lex->column = 0;
}
lex->column += 1;
lex->at += 1;
}
void EatWhitespace(Lexer *lex) {
while (IsWhitespace(At(lex))) {
Advance(lex);
@@ -82,8 +101,24 @@ void LexString(Lexer *lex, Token *t) {
t->len = (Int)(lex->at - t->data) - 1;
}
bool IsOkForIdent(char cc) {
bool result = IsAlphanumeric(cc) || cc == '_' || cc == '/' || cc == '-' || cc == '.' || cc == '@';
void LexDigit(Lexer *lex, Token *t) {
t->kind = TokenKind_Numeric;
for (;;) {
char c = At(lex);
if (c == 0) {
break;
}
if (!IsDigit(c)) {
break;
}
Advance(lex);
}
t->len = (Int)(lex->at - t->data);
t->f = (Float)strtoll(t->data, NULL, 10);
}
bool IsOkForIdent(Lexer *lex, char c) {
bool result = IsAlphanumeric(c) || c == '_' || c == '/';
return result;
}
@@ -99,7 +134,7 @@ Token Next(Lexer *lex) {
Advance(lex);
if (c == 0) {
t.kind = TokenKind_EOF;
return t;
} else if (c == '{') {
t.kind = TokenKind_OpenBrace;
} else if (c == '}') {
@@ -108,15 +143,17 @@ Token Next(Lexer *lex) {
t.kind = TokenKind_Colon;
} else if (c == ',') {
t.kind = TokenKind_Comma;
} else if (IsDigit(c)) {
LexDigit(lex, &t);
} else if (c == '"') {
LexString(lex, &t);
} else if (c == '`') {
LexString(lex, &t);
} else if (IsOkForIdent(c)) {
} else if (IsOkForIdent(lex, c)) {
t.kind = TokenKind_String;
for (;;) {
char cc = At(lex);
bool ok = IsOkForIdent(cc);
bool ok = IsOkForIdent(lex, cc);
if (!ok) {
break;
}
@@ -132,10 +169,10 @@ Token Next(Lexer *lex) {
return t;
}
void RunDataDescriptionTest() {
void TestDataDesc() {
Scratch scratch;
String s = "{/usr/bin/python3, `Hidden`, Input:Clipboard}";
Lexer lexer = MakeLexer(scratch, s, "test");
Lexer lexer = MakeLexer(scratch, s, "test", 0, 0);
{
Token tok = {};
tok = Next(&lexer);
@@ -179,6 +216,4 @@ void RunDataDescriptionTest() {
Assert(tok.kind == TokenKind_EOF);
}
} RegisterFunction(&TestFunctions, RunDataDescriptionTest);
}
} RegisterFunction(&TestFunctions, TestDataDesc);

View File

@@ -103,6 +103,7 @@ void DrawUnderline(Window *window, View *view, Buffer *buffer, Range range, Colo
Vec2I max = {xy_max.col * window->font->char_spacing, (xy_max.line + 1) * window->font->line_spacing};
Rect2I rect = {min, max};
Rect2I scrolled_rect = rect - view->scroll + window->document_rect.min;
if (scrolled_rect.min.x < window->document_rect.min.x) scrolled_rect.min.x = window->document_rect.min.x;
DrawRectOutline(scrolled_rect, color);
}

View File

@@ -12,7 +12,6 @@ bool BreakOnError = false;
Int ErrorCount;
Allocator SysAllocator = {SystemAllocatorProc};
String ConfigDir;
float DPIScale = 1.0f;
// @WARNING: be careful about using this, should only be used for debugging
@@ -175,11 +174,12 @@ RegisterVariable(String, InternetBrowser, "firefox");
RegisterVariable(String, OpenCodePatterns, ".c .h .cpp .hpp .cc .cxx .rs .go .zig .py .lua .js .ts .jsx .tsx .java .kt .swift .cs .rb .php .html .css .scss .bat .sh .bash .zsh .sql .asm .s .cmake .make .json .yaml .toml .ini .txt .md .rst .Makefile .Dockerfile .gitignore .bashrc .zshrc");
RegisterVariable(String, OpenCodeExcludePatterns, "");
RegisterVariable(Int, TrimTrailingWhitespace, 1);
RegisterVariable(String, HomeFolder, "");
RegisterVariable(String, ConfigFolder, "");
// PROJECT_MANAGEMENT
// Set at the beginning of the program to current directory
RegisterVariable(String, ProjectDirectory, "");
RegisterVariable(String, ProjectFolder, "");
// PLUGIN_BUILD_WINDOW
RegisterVariable(String, Build1OnWindows, "build.bat slow");

View File

@@ -5,7 +5,7 @@ BufferID BuildBufferID;
void InitBuildWindow() {
Window *window = CreateWind();
BuildWindowID = window->id;
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "build"));
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "build"));
buffer->special = true;
buffer->no_history = true;
BuildBufferID = buffer->id;
@@ -32,7 +32,7 @@ void LayoutBuildWindow(Rect2I *rect, int16_t wx, int16_t wy) {
window->document_rect = window->total_rect = CutBottom(rect, barsize);
}
BSet ExecBuild(String windows_cmd, String unix_cmd, String working_dir = ProjectDirectory) {
BSet ExecBuild(String windows_cmd, String unix_cmd, String working_dir = ProjectFolder) {
SaveAll();
Scratch scratch;
BSet build = GetBSet(BuildWindowID);

View File

@@ -67,7 +67,7 @@ void LayoutCommandWindow(Rect2I *rect, int16_t wx, int16_t wy) {
void InitCommandWindow() {
Window *window = CreateWind();
CommandWindowID = window->id;
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "command_bar"));
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "command_bar"));
buffer->special = true;
buffer->no_history = true;
View *view = CreateView(buffer->id);

View File

@@ -117,8 +117,8 @@ void Set(String string) {
}
#if PLUGIN_PROJECT_MANAGEMENT
if (name == "ProjectDirectory") {
SetProjectDirectory(*var->string);
if (name == "ProjectFolder") {
SetProjectFolder(*var->string);
}
#endif
return;
@@ -153,4 +153,4 @@ void Set(String string) {
ReportErrorf("Failed to :Set, no such variable found: %S", name);
}
#endif
#endif

View File

@@ -12,7 +12,7 @@ void InitDebugWindow() {
window->primary = false;
window->jump_history = false;
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "debug"));
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "debug"));
DebugBufferID = buffer->id;
buffer->no_history = true;
buffer->special = true;

View File

@@ -2,7 +2,7 @@ void Windows_SetupVCVarsall(mco_coro *co) {
View *view = NULL;
{
Scratch scratch;
String working_dir = ProjectDirectory;
String working_dir = ProjectFolder;
String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-");
String cmd = Format(scratch, "\"%S\" && set", VCVarsPath);
view = OpenBufferView(buffer_name);

View File

@@ -1,5 +1,5 @@
void SetProjectDirectory(String dir) {
ProjectDirectory = Intern(&GlobalInternTable, dir);
void SetProjectFolder(String dir) {
ProjectFolder = Intern(&GlobalInternTable, dir);
Scratch scratch;
For (Buffers) {
if (it->special) {
@@ -14,7 +14,7 @@ void CO_OpenCode(mco_coro *co) {
Array<String> patterns = SplitWhitespace(ctx->arena, OpenCodePatterns);
Array<String> exclude_patterns = SplitWhitespace(ctx->arena, OpenCodeExcludePatterns);
Array<String> dirs = {ctx->arena};
Add(&dirs, Copy(ctx->arena, ProjectDirectory));
Add(&dirs, Copy(ctx->arena, ProjectFolder));
for (int diri = 0; diri < dirs.len; diri += 1) {
for (FileIter it = IterateFiles(ctx->arena, dirs[diri]); IsValid(it); Advance(&it)) {
bool should_open = true;
@@ -52,12 +52,12 @@ void CO_OpenCode(mco_coro *co) {
} RegisterCoroutineCommand(
CO_OpenCode,
"",
"Open all code files in current ProjectDirectory, the code files are determined through NonCodePatterns_EndsWith config variable list",
"Open all code files in current ProjectFolder, the code files are determined through NonCodePatterns_EndsWith config variable list",
data->dont_wait_until_resolved = true;
);
void OpenProject(String directory) {
SetProjectDirectory(directory);
SetProjectFolder(directory);
#if PLUGIN_CONFIG
Scratch scratch;
for (FileIter it = IterateFiles(scratch, directory); IsValid(it); Advance(&it)) {
@@ -111,7 +111,7 @@ void CO_CreateProject(mco_coro *co) {
String src_dir = Format(scratch, "%S/src", project_dir);
MakeDir(project_dir);
MakeDir(src_dir);
SetProjectDirectory(project_dir);
SetProjectFolder(project_dir);
Buffer *project = BufferOpenFile(Format(scratch, "%S/project.te", project_dir));
RawAppendf(project, ":OpenCode\n:Set BinaryUnderDebug '%S/build/main.exe'\n", project_dir);

View File

@@ -1 +1 @@
void SetProjectDirectory(String name);
void SetProjectFolder(String name);

View File

@@ -2124,7 +2124,7 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
if (file.len == 0) {
Scratch scratch;
for (FileIter it = IterateFiles(scratch, ProjectDirectory); IsValid(it); Advance(&it)) {
for (FileIter it = IterateFiles(scratch, ProjectFolder); IsValid(it); Advance(&it)) {
if (EndsWith(it.filename, ".rdbg")) {
file = Intern(&GlobalInternTable, it.absolute_path);
break;
@@ -2178,7 +2178,7 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
rdbg_Id cfg_id;
char *exe = file.data;
char *args = NULL;
char *work_dir = ProjectDirectory.data;
char *work_dir = ProjectFolder.data;
char *env = NULL;
AddSessionConfig(&RDBG_Ctx, exe, args, work_dir, env, true, true, &res, &cfg_id);
if (ContextHadError(&RDBG_Ctx)) {

View File

@@ -63,7 +63,7 @@ void CMD_ToggleSearchWordBoundary() {
void InitSearchWindow() {
Window *window = CreateWind();
SearchWindowID = window->id;
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "search"));
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "search"));
buffer->special = true;
SearchBufferID = buffer->id;
View *view = CreateView(buffer->id);

View File

@@ -3,7 +3,7 @@ WindowID StatusWindowID;
void InitStatusWindow() {
Window *window = CreateWind();
StatusWindowID = window->id;
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectDirectory, "status_bar"));
Buffer *buffer = CreateBuffer(SysAllocator, GetUniqueBufferName(ProjectFolder, "status_bar"));
buffer->special = true;
View *view = CreateView(buffer->id);
view->special = true;

View File

@@ -861,7 +861,21 @@ int main(int argc, char **argv)
#endif
InitScratch();
InitOS(ReportErrorf);
ProjectDirectory = GetWorkingDir(Perm);
ProjectFolder = GetWorkingDir(Perm);
HomeFolder = SDL_GetUserFolder(SDL_FOLDER_HOME);
{
String sdl_config_path = SDL_GetPrefPath("krzosa", "text_editor");
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '\\') {
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
}
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '/') {
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
}
ConfigFolder = NormalizePath(Perm, sdl_config_path);
SDL_free(sdl_config_path.data);
}
#if OS_WINDOWS
{
wchar_t *p = GetEnvironmentStringsW();
@@ -889,18 +903,6 @@ int main(int argc, char **argv)
// return 0;
}
{
String sdl_config_path = SDL_GetPrefPath("krzosa", "text_editor");
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '\\') {
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
}
if (sdl_config_path.len && sdl_config_path.data[sdl_config_path.len - 1] == '/') {
sdl_config_path = Chop(sdl_config_path, 1); // chop '/'
}
ConfigDir = NormalizePath(Perm, sdl_config_path);
SDL_free(sdl_config_path.data);
}
if (!SDL_Init(SDL_INIT_VIDEO)) {
ReportErrorf("Couldn't initialize SDL! %s", SDL_GetError());
return 1;
@@ -978,13 +980,13 @@ int main(int argc, char **argv)
{
Allocator sys_allocator = GetSystemAllocator();
Scratch scratch;
Buffer *null_buffer = CreateBuffer(sys_allocator, Format(scratch, "%S/scratch", ProjectDirectory));
Buffer *null_buffer = CreateBuffer(sys_allocator, Format(scratch, "%S/scratch", ProjectFolder));
null_buffer->special = true;
View *null_view = CreateView(null_buffer->id);
null_view->special = true;
Assert(null_buffer->id == NullBufferID && null_view->id == NullViewID);
Buffer *logs_buffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectDirectory, "logs", ""));
Buffer *logs_buffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectFolder, "logs", ""));
logs_buffer->special = true;
View *logs_view = CreateView(logs_buffer->id);
logs_view->special = true;
@@ -992,13 +994,13 @@ int main(int argc, char **argv)
LogView = logs_view;
#if PLUGIN_RECORD_GC
GCInfoBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectDirectory, "gc"));
GCInfoBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectFolder, "gc"));
GCInfoBuffer->special = true;
GCInfoBuffer->no_history = true;
#endif
#if PLUGIN_RECORD_EVENTS
EventBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectDirectory, "events"));
EventBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(ProjectFolder, "events"));
EventBuffer->no_history = true;
EventBuffer->special = true;
#endif
@@ -1027,7 +1029,7 @@ int main(int argc, char **argv)
#if PLUGIN_CONFIG
{
Scratch scratch;
GlobalConfigBufferID = LoadConfig(Format(scratch, "%S/config.te", ConfigDir));
GlobalConfigBufferID = LoadConfig(Format(scratch, "%S/config.te", ConfigFolder));
}
#endif
for (int i = 1; i < argc; i += 1) {

View File

@@ -291,8 +291,8 @@ String InsertVariables(Allocator allocator, String string) {
void TestInsertVariable() {
Scratch scratch;
String a = "Thing/@(ProjectDirectory)/Another";
String b = "Thing/@ProjectDirectory/Another";
String a = "Thing/@(ProjectFolder)/Another";
String b = "Thing/@ProjectFolder/Another";
Assert(InsertVariables(scratch, a) == InsertVariables(scratch, b));
int c = 10;
@@ -301,8 +301,8 @@ void TestInsertVariable() {
/*
Variables:
@ProjectDirectory/build/te
@(ProjectDirectory)/build/te
@ProjectFolder/build/te
@(ProjectFolder)/build/te
Start of string matchers:
! Execute with default shell
@@ -315,7 +315,7 @@ Start of string matchers:
py: @todo, execute in python shell, this will use the python3 shell and pass the rest to it's stdin or maybe as a file
More comprehensive syntax for commands:
!{/usr/bin/python,Hidden,Input:Clipboard,Output:Clipboard,WorkingDirectory:@ProjectDirectory,Env:{Thing:asd}}
!{/usr/bin/python,Hidden,Input:Clipboard,Output:Clipboard,WorkingDirectory:@ProjectFolder,Env:{Thing:asd}}
Otherwise it does filepath parsing:
C:/windows/path
@@ -325,15 +325,17 @@ Otherwise it does filepath parsing:
./thing(10)
TODO: need to add '~', but where?
USECASE: Wouldn't it be cool to just select a part of codebase pipe that into a script
and get a result in a clipboard or capture the output and change the selection.
TODO: Data desc language
and get a result in a clipboard or capture the output and change the selection?
PREV IDEA:
!{bash,Out:Sel} SCRIPT
!{bash,Out:Clip} SCRIPT
Use variables for injecting selection: @Sel
TODO: I would pause the data desc language, seems a bit cumbersome... think of more concrete, constrained ideas
TODO: Unify lexers (Set and Trigger)
*/
ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpenMeta meta) {
ResolvedOpen result = {};