From 27b78f8623b29f90744a7cb3f2b29bad302de676 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Sun, 30 Mar 2025 21:16:21 +0200 Subject: [PATCH 1/8] First stab at supporting syntax highlighting via wxStyledTextCtrl --- src/gd_sophia_editor.erl | 113 +++++++++++++++++++++++++++++++++++++++ src/gd_v_devman.erl | 35 +++++++++--- 2 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 src/gd_sophia_editor.erl diff --git a/src/gd_sophia_editor.erl b/src/gd_sophia_editor.erl new file mode 100644 index 0000000..dff5e3e --- /dev/null +++ b/src/gd_sophia_editor.erl @@ -0,0 +1,113 @@ +-module(gd_sophia_editor). +-export([new/3, get_text/1, set_text/2]). + +-include_lib("wx/include/wx.hrl"). + + +new(Parent, Id, Opts) -> + STC = wxStyledTextCtrl:new(Parent, [{id, Id} | Opts]), + + %% Set up the container lexer + wxStyledTextCtrl:setLexer(STC, ?wxSTC_LEX_CONTAINER), + + %% Define styles + set_colors(STC), + %% Connect the styling event + wxStyledTextCtrl:connect( + STC, 'stc_styleneeded', + [{callback, fun(Event, _) -> style_needed(Event, STC) end}]), + STC. + +get_text(STC) -> + wxStyledTextCtrl:getText(STC). + +set_text(STC, Text) -> + wxStyledTextCtrl:setText(STC, Text), + %% Force Scintilla to request styling for the entire text + wxStyledTextCtrl:colourise(STC, 0, -1). + +%% ====================================================================== + +set_colors(STC) -> + wxStyledTextCtrl:styleClearAll(STC), + wxStyledTextCtrl:styleSetForeground(STC, 0, {0, 0, 0}), % Default: Black + wxStyledTextCtrl:styleSetForeground(STC, 1, {0, 0, 192}), % Keywords: Deep Blue + wxStyledTextCtrl:styleSetForeground(STC, 2, {0, 0, 0}), % Identifiers: Black + wxStyledTextCtrl:styleSetForeground(STC, 3, {128, 128, 128}), % Comments: Gray + wxStyledTextCtrl:styleSetForeground(STC, 4, {163, 21, 21}), % Strings/Chars: Reddish + wxStyledTextCtrl:styleSetForeground(STC, 5, {128, 0, 128}), % Numbers: Purple + wxStyledTextCtrl:styleSetForeground(STC, 6, {0, 0, 0}). % Operators: Black + +style_needed(_Event, STC) -> + try + StartPos = wxStyledTextCtrl:getEndStyled(STC), + EndPos = wxStyledTextCtrl:getLength(STC), + Text = wxStyledTextCtrl:getTextRange(STC, StartPos, EndPos), + case so_scan:scan(Text) of + {ok, Tokens} -> + wxStyledTextCtrl:startStyling(STC, StartPos), + apply_styles(STC, Tokens); + {error, _Reason} -> + wxStyledTextCtrl:startStyling(STC, StartPos), + wxStyledTextCtrl:setStyling(STC, EndPos - StartPos, 0) + end + catch + error:E:T -> + dbg("CAUGHT error:~p / ~p~n", [E, T]) + end. + +apply_styles(STC, Tokens) -> + lists:foreach(fun(Token) -> style_token(STC, Token) end, Tokens). + +style_token(STC, Token) -> + {Type, Value} = type_and_value(Token), + {Line, Col} = element(2, Token), + Len = byte_size(to_binary(Value)), + Style = classify_style(Type, Value), + % Get exact position from Line and Column + Start = wxStyledTextCtrl:positionFromLine(STC, Line - 1) + (Col - 1), + wxStyledTextCtrl:startStyling(STC, Start), + wxStyledTextCtrl:setStyling(STC, Len, Style). + +to_binary(S) when is_list(S) -> + unicode:characters_to_binary(S); +to_binary(S) when is_binary(S) -> + S; +to_binary(I) when is_integer(I) -> + integer_to_binary(I). + +classify_style(Type, Value) -> + case Type of + symbol -> + case lists:member(Value, keywords()) of + true -> 1; + false -> 6 + end; + id -> 2; + qid -> 2; + con -> 2; + qcon -> 2; + tvar -> 2; + string -> 4; + char -> 4; + int -> 5; + hex -> 5; + bytes -> 5; + skip -> 3; + _Other -> 1 + end. + +type_and_value({Type,_Line,Value}) -> {Type, Value}; +type_and_value({Type, _}) -> {Type, atom_to_list(Type)}. + +keywords() -> + ["contract", "include", "let", "switch", "type", "record", "datatype", "if", + "elif", "else", "function", "stateful", "payable", "true", "false", "mod", + "public", "entrypoint", "private", "indexed", "namespace", "interface", + "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"]. + +%% dbg(Fmt) -> +%% dbg(Fmt, []). + +dbg(Fmt, Args) -> + io:format("~p: " ++ Fmt, [self()|Args]). diff --git a/src/gd_v_devman.erl b/src/gd_v_devman.erl index 4184ff7..434abcf 100644 --- a/src/gd_v_devman.erl +++ b/src/gd_v_devman.erl @@ -15,6 +15,8 @@ -include("$zx_include/zx_logger.hrl"). -include("gd.hrl"). +-define(editorMode, sophia). + % Widgets -record(w, {name = none :: atom() | {FunName :: binary(), call | dryr}, @@ -514,12 +516,20 @@ add_code_page(State = #s{tabs = TopBook, code = {Codebook, Pages}}, Location, Co PageSz = wxBoxSizer:new(?wxHORIZONTAL), CodeTxStyle = {style, ?wxTE_MULTILINE bor ?wxTE_PROCESS_TAB bor ?wxTE_DONTWRAP}, - CodeTx = wxTextCtrl:new(Window, ?wxID_ANY, [CodeTxStyle]), + CodeTx = case ?editorMode of + plain -> wxTextCtrl:new(Window, ?wxID_ANY, [CodeTxStyle]); + sophia -> gd_sophia_editor:new(Window, ?wxID_ANY, [CodeTxStyle]) + end, TextAt = wxTextAttr:new(), Mono = wxFont:new(10, ?wxMODERN, ?wxNORMAL, ?wxNORMAL, [{face, "Monospace"}]), ok = wxTextAttr:setFont(TextAt, Mono), - true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt), - ok = wxTextCtrl:setValue(CodeTx, Code), + case ?editorMode of + plain -> + true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt), + ok = wxTextCtrl:setValue(CodeTx, Code); + sophia -> + gd_sophia_editor:set_text(CodeTx, Code) + end, _ = wxSizer:add(PageSz, CodeTx, zxw:flags(wide)), @@ -910,7 +920,7 @@ save(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}}) _ -> Name ++ ".aes" end, Path = filename:join(Dir, File), - Source = wxTextCtrl:getValue(Widget), + Source = get_source(Widget), case filelib:ensure_dir(Path) of ok -> case file:write_file(Path, Source) of @@ -938,6 +948,19 @@ save(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}}) end end. +get_source(Widget) -> + case ?editorMode of + plain -> wxTextCtrl:getValue(Widget); + sophia -> gd_sophia_editor:get_text(Widget) + end. + +set_source(Widget, Src) -> + case ?editorMode of + plain -> wxTextCtrl:setValue(Widget, Src); + sophia -> gd_sophia_editor:set_text(Widget, Src) + end. + + % TODO: Break this down -- tons of things in here recur. rename(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}}) -> case wxNotebook:getSelection(Codebook) of @@ -968,7 +991,7 @@ rename(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}} _ -> Name ++ ".aes" end, NewPath = filename:join(Dir, File), - Source = wxTextCtrl:getValue(Widget), + Source = get_source(Widget), case filelib:ensure_dir(NewPath) of ok -> case file:write_file(NewPath, Source) of @@ -1084,7 +1107,7 @@ load3(State = #s{tabs = TopBook, cons = {Consbook, Pages}, buttons = Buttons, j TextAt = wxTextAttr:new(), ok = wxTextAttr:setFont(TextAt, Mono), true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt), - ok = wxTextCtrl:setValue(CodeTx, Source), + ok = set_source(CodeTx, Source), _ = wxSizer:add(CodeSz, CodeTx, zxw:flags(wide)), ScrollWin = wxScrolledWindow:new(Window), FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Function Interfaces")}]), From d4f2b3f2b00209a4fbdda9d26ac862159b265bc1 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Sun, 30 Mar 2025 22:15:02 +0200 Subject: [PATCH 2/8] Set monospace font --- src/gd_sophia_editor.erl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/gd_sophia_editor.erl b/src/gd_sophia_editor.erl index dff5e3e..99f73a7 100644 --- a/src/gd_sophia_editor.erl +++ b/src/gd_sophia_editor.erl @@ -9,7 +9,18 @@ new(Parent, Id, Opts) -> %% Set up the container lexer wxStyledTextCtrl:setLexer(STC, ?wxSTC_LEX_CONTAINER), - + + FontSize = 13, + Mono = wxFont:new(FontSize, ?wxFONTFAMILY_TELETYPE, ?wxFONTSTYLE_NORMAL, + ?wxFONTWEIGHT_NORMAL, [{face, "Monospace"}]), + lists:foreach( + fun(Style) -> + wxStyledTextCtrl:styleSetFont(STC, Style, Mono) + end, + lists:seq(0, 6) + ), + %% + wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono), %% Define styles set_colors(STC), %% Connect the styling event From 9aa2c2f79d2c802a9f0ab10a3a7ee737daf571d1 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 31 Mar 2025 13:58:48 +0900 Subject: [PATCH 3/8] Give the main frame visibility on events --- src/gd_con.erl | 33 +-------- src/gd_sophia_editor.erl | 155 +++++++++++++++++++++++++++------------ src/gd_v_devman.erl | 38 +++++----- 3 files changed, 128 insertions(+), 98 deletions(-) diff --git a/src/gd_con.erl b/src/gd_con.erl index 172f705..5cc4295 100644 --- a/src/gd_con.erl +++ b/src/gd_con.erl @@ -652,7 +652,7 @@ do_sign_mess(Request = #{"public_id" := ID, "payload" := Message}, #s{wallet = #wallet{keys = Keys}}) -> case lists:keyfind(ID, #key.id, Keys) of #key{pair = #{secret := SecKey}} -> - Sig = base64:encode(sign_message(list_to_binary(Message), SecKey)), + Sig = base64:encode(hz:sign_message(list_to_binary(Message), SecKey)), do_sign_mess2(Request#{"signature" => Sig}); false -> gd_gui:trouble({bad_key, ID}) @@ -675,37 +675,6 @@ do_sign_mess2(Request = #{"url" := URL}) -> end. -% TODO: Should probably be part of Hakuzaru -sign_message(Message, SecKey) -> - Prefix = <<"Gajumaru Signed Message:\n">>, - {ok, PSize} = vencode(byte_size(Prefix)), - {ok, MSize} = vencode(byte_size(Message)), - Smashed = iolist_to_binary([PSize, Prefix, MSize, Message]), - {ok, Hashed} = eblake2:blake2b(32, Smashed), - ecu_eddsa:sign_detached(Hashed, SecKey). - - -vencode(N) when N < 0 -> - {error, {negative_N, N}}; -vencode(N) when N < 16#FD -> - {ok, <>}; -vencode(N) when N =< 16#FFFF -> - NBytes = eu(N, 2), - {ok, <<16#FD, NBytes/binary>>}; -vencode(N) when N =< 16#FFFF_FFFF -> - NBytes = eu(N, 4), - {ok, <<16#FE, NBytes/binary>>}; -vencode(N) when N < (2 bsl 64) -> - NBytes = eu(N, 8), - {ok, <<16#FF, NBytes/binary>>}. - -eu(N, Size) -> - Bytes = binary:encode_unsigned(N, little), - NExtraZeros = Size - byte_size(Bytes), - ExtraZeros = << <<0>> || _ <- lists:seq(1, NExtraZeros) >>, - <>. - - do_sign_tx(Request = #{"public_id" := ID, "payload" := CallData, "network_id" := NID}, #s{wallet = #wallet{keys = Keys}}) -> BinNID = list_to_binary(NID), diff --git a/src/gd_sophia_editor.erl b/src/gd_sophia_editor.erl index 99f73a7..6319610 100644 --- a/src/gd_sophia_editor.erl +++ b/src/gd_sophia_editor.erl @@ -1,39 +1,103 @@ -module(gd_sophia_editor). --export([new/3, get_text/1, set_text/2]). +-export([new/1, update/2, + get_text/1, set_text/2]). +-include("$zx_include/zx_logger.hrl"). -include_lib("wx/include/wx.hrl"). -new(Parent, Id, Opts) -> - STC = wxStyledTextCtrl:new(Parent, [{id, Id} | Opts]), - - %% Set up the container lexer - wxStyledTextCtrl:setLexer(STC, ?wxSTC_LEX_CONTAINER), +%%% Formatting Constants +%% Style labels +-define(DEFAULT, 0). +-define(KEYWORD, 1). +-define(IDENTIFIER, 2). +-define(COMMENT, 3). +-define(STRING, 4). +-define(NUMBER, 5). +-define(OPERATOR, 6). + + + +%% Color palette + +% Intensities: +-define(H, 255). % High +-define(M, 192). % Medium +-define(L, 128). % Low +-define(Z, 0). % Zilch + + +% RGB values +% R G B +-define(black, {?Z, ?Z, ?Z}). +-define(light_red, {?H, ?Z, ?Z}). +-define(light_green, {?Z, ?H, ?Z}). +-define(light_blue, {?Z, ?Z, ?H}). +-define(yellow, {?H, ?H, ?Z}). +-define(light_magenta, {?H, ?Z, ?H}). +-define(light_cyan, {?Z, ?H, ?H}). +-define(high_white, {?H, ?H, ?H}). +-define(red, {?L, ?Z, ?Z}). +-define(green, {?Z, ?L, ?Z}). +-define(blue, {?Z, ?Z, ?L}). +-define(brown, {?L, ?L, ?Z}). +-define(magenta, {?L, ?Z, ?L}). +-define(cyan, {?Z, ?L, ?L}). +-define(grey, {?L, ?L, ?L}). +-define(white, {?M, ?M, ?M}). + +styles() -> + [?DEFAULT, + ?KEYWORD, + ?IDENTIFIER, + ?COMMENT, + ?STRING, + ?NUMBER, + ?OPERATOR]. + +palette(light) -> + #{?DEFAULT => ?black, + ?KEYWORD => ?blue, + ?IDENTIFIER => ?cyan, + ?COMMENT => ?grey, + ?STRING => ?red, + ?NUMBER => ?magenta, + ?OPERATOR => ?brown}; +palette(dark) -> + #{?DEFAULT => ?white, + ?KEYWORD => ?light_cyan, + ?IDENTIFIER => ?green, + ?COMMENT => ?grey, + ?STRING => ?light_red, + ?NUMBER => ?light_magenta, + ?OPERATOR => ?yellow}. + +color_mode() -> + light. + + +new(Parent) -> + STC = wxStyledTextCtrl:new(Parent), + ok = wxStyledTextCtrl:setLexer(STC, ?wxSTC_LEX_CONTAINER), FontSize = 13, - Mono = wxFont:new(FontSize, ?wxFONTFAMILY_TELETYPE, ?wxFONTSTYLE_NORMAL, - ?wxFONTWEIGHT_NORMAL, [{face, "Monospace"}]), - lists:foreach( - fun(Style) -> - wxStyledTextCtrl:styleSetFont(STC, Style, Mono) - end, - lists:seq(0, 6) - ), - %% - wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono), - %% Define styles - set_colors(STC), - %% Connect the styling event - wxStyledTextCtrl:connect( - STC, 'stc_styleneeded', - [{callback, fun(Event, _) -> style_needed(Event, STC) end}]), + Mono = wxFont:new(FontSize, + ?wxFONTFAMILY_TELETYPE, + ?wxFONTSTYLE_NORMAL, + ?wxFONTWEIGHT_NORMAL, + [{face, "Monospace"}]), + + SetMonospace = fun(Style) -> wxStyledTextCtrl:styleSetFont(STC, Style, Mono) end, + ok = lists:foreach(SetMonospace, styles()), + ok = wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono), + ok = set_colors(STC), STC. get_text(STC) -> wxStyledTextCtrl:getText(STC). set_text(STC, Text) -> - wxStyledTextCtrl:setText(STC, Text), + ok = wxStyledTextCtrl:setText(STC, Text), %% Force Scintilla to request styling for the entire text wxStyledTextCtrl:colourise(STC, 0, -1). @@ -41,15 +105,16 @@ set_text(STC, Text) -> set_colors(STC) -> wxStyledTextCtrl:styleClearAll(STC), - wxStyledTextCtrl:styleSetForeground(STC, 0, {0, 0, 0}), % Default: Black - wxStyledTextCtrl:styleSetForeground(STC, 1, {0, 0, 192}), % Keywords: Deep Blue - wxStyledTextCtrl:styleSetForeground(STC, 2, {0, 0, 0}), % Identifiers: Black - wxStyledTextCtrl:styleSetForeground(STC, 3, {128, 128, 128}), % Comments: Gray - wxStyledTextCtrl:styleSetForeground(STC, 4, {163, 21, 21}), % Strings/Chars: Reddish - wxStyledTextCtrl:styleSetForeground(STC, 5, {128, 0, 128}), % Numbers: Purple - wxStyledTextCtrl:styleSetForeground(STC, 6, {0, 0, 0}). % Operators: Black + Palette = palette(color_mode()), + Colorize = + fun(Style) -> + Color = maps:get(Style, Palette), + wxStyledTextCtrl:styleSetForeground(STC, Style, Color) + end, + lists:foreach(Colorize, styles()). -style_needed(_Event, STC) -> + +update(_Event, STC) -> try StartPos = wxStyledTextCtrl:getEndStyled(STC), EndPos = wxStyledTextCtrl:getLength(STC), @@ -91,21 +156,21 @@ classify_style(Type, Value) -> case Type of symbol -> case lists:member(Value, keywords()) of - true -> 1; - false -> 6 + true -> ?KEYWORD; + false -> ?OPERATOR end; - id -> 2; - qid -> 2; - con -> 2; - qcon -> 2; - tvar -> 2; - string -> 4; - char -> 4; - int -> 5; - hex -> 5; - bytes -> 5; - skip -> 3; - _Other -> 1 + id -> ?IDENTIFIER; + qid -> ?IDENTIFIER; + con -> ?IDENTIFIER; + qcon -> ?IDENTIFIER; + tvar -> ?IDENTIFIER; + string -> ?STRING; + char -> ?STRING; + int -> ?NUMBER; + hex -> ?NUMBER; + bytes -> ?NUMBER; + skip -> ?COMMENT; + _Other -> ?DEFAULT end. type_and_value({Type,_Line,Value}) -> {Type, Value}; diff --git a/src/gd_v_devman.erl b/src/gd_v_devman.erl index 434abcf..675eaca 100644 --- a/src/gd_v_devman.erl +++ b/src/gd_v_devman.erl @@ -258,6 +258,9 @@ handle_event(E = #wx{event = #wxCommand{type = command_button_clicked}, State end, {noreply, NewState}; +handle_event(#wx{event = Event = #wxStyledText{type = stc_styleneeded}, obj = Win}, State) -> + ok = style(State, Win, Event), + {noreply, State}; handle_event(#wx{event = #wxClose{}}, State = #s{frame = Frame, prefs = Prefs}) -> Geometry = case wxTopLevelWindow:isMaximized(Frame) of @@ -293,6 +296,15 @@ terminate(Reason, State) -> %%% Doers +style(#s{code = {Codebook, Pages}}, Win, Event) -> + case lists:keyfind(Win, #p.win, Pages) of + #p{code = STC} -> + tell("Received style event.~nWin: ~p~nEvent: ~p", [Win, Event]), + gd_sophia_editor:update(Event, STC); + false -> + tell("Received bogus style event.~nWin: ~p~nEvent: ~p", [Win, Event]) + end. + clicked(State = #s{cons = {Consbook, Contracts}}, Name) -> case wxNotebook:getSelection(Consbook) of ?wxNOT_FOUND -> @@ -506,30 +518,13 @@ add_code_page2(State, {hash, Address}) -> open_hash2(State, Address). add_code_page(State = #s{tabs = TopBook, code = {Codebook, Pages}}, Location, Code) -> -% FIXME: One of these days we need to define the text area as a wxStyledTextCtrl and will -% have to contend with system theme issues (light/dark themese, namely) -% Leaving this little thing here to remind myself how any of that works later. -% The call below returns a wx_color4() type (not that we need alpha...). -% Color = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW), -% tell("Color: ~p", [Color]), + Color = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW), + tell("Color: ~p", [Color]), Window = wxWindow:new(Codebook, ?wxID_ANY), PageSz = wxBoxSizer:new(?wxHORIZONTAL), - CodeTxStyle = {style, ?wxTE_MULTILINE bor ?wxTE_PROCESS_TAB bor ?wxTE_DONTWRAP}, - CodeTx = case ?editorMode of - plain -> wxTextCtrl:new(Window, ?wxID_ANY, [CodeTxStyle]); - sophia -> gd_sophia_editor:new(Window, ?wxID_ANY, [CodeTxStyle]) - end, - TextAt = wxTextAttr:new(), - Mono = wxFont:new(10, ?wxMODERN, ?wxNORMAL, ?wxNORMAL, [{face, "Monospace"}]), - ok = wxTextAttr:setFont(TextAt, Mono), - case ?editorMode of - plain -> - true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt), - ok = wxTextCtrl:setValue(CodeTx, Code); - sophia -> - gd_sophia_editor:set_text(CodeTx, Code) - end, + CodeTx = gd_sophia_editor:new(Window), + ok = gd_sophia_editor:set_text(CodeTx, Code), _ = wxSizer:add(PageSz, CodeTx, zxw:flags(wide)), @@ -541,6 +536,7 @@ add_code_page(State = #s{tabs = TopBook, code = {Codebook, Pages}}, Location, Co {file, Path} -> filename:basename(Path); {hash, Addr} -> Addr end, + ok = wxStyledTextCtrl:connect(Window, stc_styleneeded), true = wxNotebook:addPage(Codebook, Window, FileName, [{bSelect, true}]), Page = #p{path = Location, win = Window, code = CodeTx}, NewPages = Pages ++ [Page], From 9d70f98ed0f12ccf3bb73238b918bc069bb6f272 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 31 Mar 2025 14:41:03 +0900 Subject: [PATCH 4/8] Make light/dark mode work --- src/gd_sophia_editor.erl | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/gd_sophia_editor.erl b/src/gd_sophia_editor.erl index 6319610..30217c9 100644 --- a/src/gd_sophia_editor.erl +++ b/src/gd_sophia_editor.erl @@ -25,6 +25,7 @@ -define(H, 255). % High -define(M, 192). % Medium -define(L, 128). % Low +-define(X, 32). % X-Low -define(Z, 0). % Zilch @@ -44,6 +45,7 @@ -define(brown, {?L, ?L, ?Z}). -define(magenta, {?L, ?Z, ?L}). -define(cyan, {?Z, ?L, ?L}). +-define(not_black, {?X, ?X, ?X}). -define(grey, {?L, ?L, ?L}). -define(white, {?M, ?M, ?M}). @@ -63,7 +65,8 @@ palette(light) -> ?COMMENT => ?grey, ?STRING => ?red, ?NUMBER => ?magenta, - ?OPERATOR => ?brown}; + ?OPERATOR => ?brown, + bg => ?high_white}; palette(dark) -> #{?DEFAULT => ?white, ?KEYWORD => ?light_cyan, @@ -71,10 +74,15 @@ palette(dark) -> ?COMMENT => ?grey, ?STRING => ?light_red, ?NUMBER => ?light_magenta, - ?OPERATOR => ?yellow}. + ?OPERATOR => ?yellow, + bg => ?not_black}. color_mode() -> - light. + {R, G, B, _} = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW), + case (lists:sum([R, G, B]) div 3) > 128 of + true -> light; + false -> dark + end. new(Parent) -> @@ -104,13 +112,15 @@ set_text(STC, Text) -> %% ====================================================================== set_colors(STC) -> - wxStyledTextCtrl:styleClearAll(STC), - Palette = palette(color_mode()), + ok = wxStyledTextCtrl:styleClearAll(STC), + Palette = #{bg := BGC} = palette(color_mode()), Colorize = fun(Style) -> Color = maps:get(Style, Palette), - wxStyledTextCtrl:styleSetForeground(STC, Style, Color) + ok = wxStyledTextCtrl:styleSetForeground(STC, Style, Color), + ok = wxStyledTextCtrl:styleSetBackground(STC, Style, BGC) end, + ok = wxStyledTextCtrl:styleSetBackground(STC, ?wxSTC_STYLE_DEFAULT, BGC), lists:foreach(Colorize, styles()). From dea44561eec40c161cee3aae4d3d70a7ab72c38e Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 31 Mar 2025 15:36:31 +0900 Subject: [PATCH 5/8] Restyle whole buffer on event to fix style breaks TODO: Make this more precise... some day --- src/: | 198 +++++++++++++++++++++++++++++++++++++++ src/gd_sophia_editor.erl | 46 ++++----- src/gd_v_devman.erl | 3 +- 3 files changed, 219 insertions(+), 28 deletions(-) create mode 100644 src/: diff --git a/src/: b/src/: new file mode 100644 index 0000000..bbd59d2 --- /dev/null +++ b/src/: @@ -0,0 +1,198 @@ +-module(gd_sophia_editor). +-export([new/1, update/2, + get_text/1, set_text/2]). + +-include("$zx_include/zx_logger.hrl"). +-include_lib("wx/include/wx.hrl"). + + +%%% Formatting Constants + +%% Style labels +-define(DEFAULT, 0). +-define(KEYWORD, 1). +-define(IDENTIFIER, 2). +-define(COMMENT, 3). +-define(STRING, 4). +-define(NUMBER, 5). +-define(OPERATOR, 6). + + + +%% Color palette + +% Intensities: +-define(H, 255). % High +-define(M, 192). % Medium +-define(L, 128). % Low +-define(X, 32). % X-Low +-define(Z, 0). % Zilch + + +% RGB values +% R G B +-define(black, {?Z, ?Z, ?Z}). +-define(light_red, {?H, ?Z, ?Z}). +-define(light_green, {?Z, ?H, ?Z}). +-define(light_blue, {?Z, ?Z, ?H}). +-define(yellow, {?H, ?H, ?Z}). +-define(light_magenta, {?H, ?Z, ?H}). +-define(light_cyan, {?Z, ?H, ?H}). +-define(high_white, {?H, ?H, ?H}). +-define(red, {?L, ?Z, ?Z}). +-define(green, {?Z, ?L, ?Z}). +-define(blue, {?Z, ?Z, ?L}). +-define(brown, {?L, ?L, ?Z}). +-define(magenta, {?L, ?Z, ?L}). +-define(cyan, {?Z, ?L, ?L}). +-define(not_black, {?X, ?X, ?X}). +-define(grey, {?L, ?L, ?L}). +-define(white, {?M, ?M, ?M}). + +styles() -> + [?DEFAULT, + ?KEYWORD, + ?IDENTIFIER, + ?COMMENT, + ?STRING, + ?NUMBER, + ?OPERATOR]. + +palette(light) -> + #{?DEFAULT => ?black, + ?KEYWORD => ?blue, + ?IDENTIFIER => ?cyan, + ?COMMENT => ?grey, + ?STRING => ?red, + ?NUMBER => ?magenta, + ?OPERATOR => ?brown, + bg => ?high_white}; +palette(dark) -> + #{?DEFAULT => ?white, + ?KEYWORD => ?light_cyan, + ?IDENTIFIER => ?green, + ?COMMENT => ?grey, + ?STRING => ?light_red, + ?NUMBER => ?light_magenta, + ?OPERATOR => ?yellow, + bg => ?not_black}. + +color_mode() -> + {R, G, B, _} = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW), + case (lists:sum([R, G, B]) div 3) > 128 of + true -> light; + false -> dark + end. + + +new(Parent) -> + STC = wxStyledTextCtrl:new(Parent), + ok = wxStyledTextCtrl:setLexer(STC, ?wxSTC_LEX_CONTAINER), + FontSize = 13, + Mono = wxFont:new(FontSize, + ?wxFONTFAMILY_TELETYPE, + ?wxFONTSTYLE_NORMAL, + ?wxFONTWEIGHT_NORMAL, + [{face, "Monospace"}]), + SetMonospace = fun(Style) -> wxStyledTextCtrl:styleSetFont(STC, Style, Mono) end, + ok = lists:foreach(SetMonospace, styles()), + ok = wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono), + ok = set_colors(STC), + STC. + +get_text(STC) -> + wxStyledTextCtrl:getText(STC). + +set_text(STC, Text) -> + ok = wxStyledTextCtrl:setText(STC, Text), + %% Force Scintilla to request styling for the entire text + wxStyledTextCtrl:colourise(STC, 0, -1). + +%% ====================================================================== + +set_colors(STC) -> + ok = wxStyledTextCtrl:styleClearAll(STC), + Palette = #{bg := BGC} = palette(color_mode()), + Colorize = + fun(Style) -> + Color = maps:get(Style, Palette), + ok = wxStyledTextCtrl:styleSetForeground(STC, Style, Color), + ok = wxStyledTextCtrl:styleSetBackground(STC, Style, BGC) + end, + ok = wxStyledTextCtrl:styleSetBackground(STC, ?wxSTC_STYLE_DEFAULT, BGC), + lists:foreach(Colorize, styles()). + + +update(_Event, STC) -> + try + StartPos = wxStyledTextCtrl:getEndStyled(STC), + EndPos = wxStyledTextCtrl:getLength(STC), + Text = wxStyledTextCtrl:getTextRange(STC, StartPos, EndPos), + case so_scan:scan(Text) of + {ok, Tokens} -> + wxStyledTextCtrl:startStyling(STC, StartPos), + apply_styles(STC, Tokens); + {error, _Reason} -> + wxStyledTextCtrl:startStyling(STC, StartPos), + wxStyledTextCtrl:setStyling(STC, EndPos - StartPos, 0) + end + catch + error:E:T -> + dbg("CAUGHT error:~p / ~p~n", [E, T]) + end. + +apply_styles(STC, Tokens) -> + lists:foreach(fun(Token) -> style_token(STC, Token) end, Tokens). + +style_token(STC, Token) -> + {Type, Value} = type_and_value(Token), + {Line, Col} = element(2, Token), + Len = byte_size(to_binary(Value)), + Style = classify_style(Type, Value), + % Get exact position from Line and Column + Start = wxStyledTextCtrl:positionFromLine(STC, Line - 1) + (Col - 1), + wxStyledTextCtrl:startStyling(STC, Start), + wxStyledTextCtrl:setStyling(STC, Len, Style). + +to_binary(S) when is_list(S) -> + unicode:characters_to_binary(S); +to_binary(S) when is_binary(S) -> + S; +to_binary(I) when is_integer(I) -> + integer_to_binary(I). + +classify_style(Type, Value) -> + case Type of + symbol -> + case lists:member(Value, keywords()) of + true -> ?KEYWORD; + false -> ?OPERATOR + end; + id -> ?IDENTIFIER; + qid -> ?IDENTIFIER; + con -> ?IDENTIFIER; + qcon -> ?IDENTIFIER; + tvar -> ?IDENTIFIER; + string -> ?STRING; + char -> ?STRING; + int -> ?NUMBER; + hex -> ?NUMBER; + bytes -> ?NUMBER; + skip -> ?COMMENT; + _Other -> ?DEFAULT + end. + +type_and_value({Type,_Line,Value}) -> {Type, Value}; +type_and_value({Type, _}) -> {Type, atom_to_list(Type)}. + +keywords() -> + ["contract", "include", "let", "switch", "type", "record", "datatype", "if", + "elif", "else", "function", "stateful", "payable", "true", "false", "mod", + "public", "entrypoint", "private", "indexed", "namespace", "interface", + "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"]. + +%% dbg(Fmt) -> +%% dbg(Fmt, []). + +dbg(Fmt, Args) -> + io:format("~p: " ++ Fmt, [self()|Args]). diff --git a/src/gd_sophia_editor.erl b/src/gd_sophia_editor.erl index 30217c9..33e8987 100644 --- a/src/gd_sophia_editor.erl +++ b/src/gd_sophia_editor.erl @@ -94,7 +94,6 @@ new(Parent) -> ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL, [{face, "Monospace"}]), - SetMonospace = fun(Style) -> wxStyledTextCtrl:styleSetFont(STC, Style, Mono) end, ok = lists:foreach(SetMonospace, styles()), ok = wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono), @@ -109,7 +108,6 @@ set_text(STC, Text) -> %% Force Scintilla to request styling for the entire text wxStyledTextCtrl:colourise(STC, 0, -1). -%% ====================================================================== set_colors(STC) -> ok = wxStyledTextCtrl:styleClearAll(STC), @@ -125,35 +123,34 @@ set_colors(STC) -> update(_Event, STC) -> - try - StartPos = wxStyledTextCtrl:getEndStyled(STC), - EndPos = wxStyledTextCtrl:getLength(STC), - Text = wxStyledTextCtrl:getTextRange(STC, StartPos, EndPos), + Text = wxStyledTextCtrl:getText(STC), case so_scan:scan(Text) of {ok, Tokens} -> - wxStyledTextCtrl:startStyling(STC, StartPos), + ok = wxStyledTextCtrl:startStyling(STC, 0), apply_styles(STC, Tokens); {error, _Reason} -> - wxStyledTextCtrl:startStyling(STC, StartPos), - wxStyledTextCtrl:setStyling(STC, EndPos - StartPos, 0) - end - catch - error:E:T -> - dbg("CAUGHT error:~p / ~p~n", [E, T]) + ok end. + apply_styles(STC, Tokens) -> lists:foreach(fun(Token) -> style_token(STC, Token) end, Tokens). + +% FIXME: 'qid' is not properly handled. If there are multi-dot qids, they will break style_token(STC, Token) -> {Type, Value} = type_and_value(Token), + {StartOffset, LengthOffset} = offset(Type), {Line, Col} = element(2, Token), - Len = byte_size(to_binary(Value)), + Length = byte_size(to_binary(Value)) + LengthOffset, Style = classify_style(Type, Value), - % Get exact position from Line and Column - Start = wxStyledTextCtrl:positionFromLine(STC, Line - 1) + (Col - 1), + Start = wxStyledTextCtrl:positionFromLine(STC, Line - 1) + Col + StartOffset, wxStyledTextCtrl:startStyling(STC, Start), - wxStyledTextCtrl:setStyling(STC, Len, Style). + wxStyledTextCtrl:setStyling(STC, Length, Style). + +offset(string) -> { 0, 0}; +offset(qid) -> {-1, 1}; +offset(_) -> {-1, 0}. to_binary(S) when is_list(S) -> unicode:characters_to_binary(S); @@ -162,6 +159,7 @@ to_binary(S) when is_binary(S) -> to_binary(I) when is_integer(I) -> integer_to_binary(I). + classify_style(Type, Value) -> case Type of symbol -> @@ -180,20 +178,16 @@ classify_style(Type, Value) -> hex -> ?NUMBER; bytes -> ?NUMBER; skip -> ?COMMENT; - _Other -> ?DEFAULT + _ -> ?DEFAULT end. -type_and_value({Type,_Line,Value}) -> {Type, Value}; -type_and_value({Type, _}) -> {Type, atom_to_list(Type)}. + +type_and_value({Type, _Line, Value}) -> {Type, Value}; +type_and_value({Type, _}) -> {Type, atom_to_list(Type)}. + keywords() -> ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", "stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace", "interface", "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"]. - -%% dbg(Fmt) -> -%% dbg(Fmt, []). - -dbg(Fmt, Args) -> - io:format("~p: " ++ Fmt, [self()|Args]). diff --git a/src/gd_v_devman.erl b/src/gd_v_devman.erl index 675eaca..def3093 100644 --- a/src/gd_v_devman.erl +++ b/src/gd_v_devman.erl @@ -296,10 +296,9 @@ terminate(Reason, State) -> %%% Doers -style(#s{code = {Codebook, Pages}}, Win, Event) -> +style(#s{code = {_, Pages}}, Win, Event) -> case lists:keyfind(Win, #p.win, Pages) of #p{code = STC} -> - tell("Received style event.~nWin: ~p~nEvent: ~p", [Win, Event]), gd_sophia_editor:update(Event, STC); false -> tell("Received bogus style event.~nWin: ~p~nEvent: ~p", [Win, Event]) From 020d3f554b52b8d9d80e0648da3062e7e3d0da18 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 31 Mar 2025 15:39:25 +0900 Subject: [PATCH 6/8] Remove dead file --- src/: | 198 ---------------------------------------------------------- 1 file changed, 198 deletions(-) delete mode 100644 src/: diff --git a/src/: b/src/: deleted file mode 100644 index bbd59d2..0000000 --- a/src/: +++ /dev/null @@ -1,198 +0,0 @@ --module(gd_sophia_editor). --export([new/1, update/2, - get_text/1, set_text/2]). - --include("$zx_include/zx_logger.hrl"). --include_lib("wx/include/wx.hrl"). - - -%%% Formatting Constants - -%% Style labels --define(DEFAULT, 0). --define(KEYWORD, 1). --define(IDENTIFIER, 2). --define(COMMENT, 3). --define(STRING, 4). --define(NUMBER, 5). --define(OPERATOR, 6). - - - -%% Color palette - -% Intensities: --define(H, 255). % High --define(M, 192). % Medium --define(L, 128). % Low --define(X, 32). % X-Low --define(Z, 0). % Zilch - - -% RGB values -% R G B --define(black, {?Z, ?Z, ?Z}). --define(light_red, {?H, ?Z, ?Z}). --define(light_green, {?Z, ?H, ?Z}). --define(light_blue, {?Z, ?Z, ?H}). --define(yellow, {?H, ?H, ?Z}). --define(light_magenta, {?H, ?Z, ?H}). --define(light_cyan, {?Z, ?H, ?H}). --define(high_white, {?H, ?H, ?H}). --define(red, {?L, ?Z, ?Z}). --define(green, {?Z, ?L, ?Z}). --define(blue, {?Z, ?Z, ?L}). --define(brown, {?L, ?L, ?Z}). --define(magenta, {?L, ?Z, ?L}). --define(cyan, {?Z, ?L, ?L}). --define(not_black, {?X, ?X, ?X}). --define(grey, {?L, ?L, ?L}). --define(white, {?M, ?M, ?M}). - -styles() -> - [?DEFAULT, - ?KEYWORD, - ?IDENTIFIER, - ?COMMENT, - ?STRING, - ?NUMBER, - ?OPERATOR]. - -palette(light) -> - #{?DEFAULT => ?black, - ?KEYWORD => ?blue, - ?IDENTIFIER => ?cyan, - ?COMMENT => ?grey, - ?STRING => ?red, - ?NUMBER => ?magenta, - ?OPERATOR => ?brown, - bg => ?high_white}; -palette(dark) -> - #{?DEFAULT => ?white, - ?KEYWORD => ?light_cyan, - ?IDENTIFIER => ?green, - ?COMMENT => ?grey, - ?STRING => ?light_red, - ?NUMBER => ?light_magenta, - ?OPERATOR => ?yellow, - bg => ?not_black}. - -color_mode() -> - {R, G, B, _} = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW), - case (lists:sum([R, G, B]) div 3) > 128 of - true -> light; - false -> dark - end. - - -new(Parent) -> - STC = wxStyledTextCtrl:new(Parent), - ok = wxStyledTextCtrl:setLexer(STC, ?wxSTC_LEX_CONTAINER), - FontSize = 13, - Mono = wxFont:new(FontSize, - ?wxFONTFAMILY_TELETYPE, - ?wxFONTSTYLE_NORMAL, - ?wxFONTWEIGHT_NORMAL, - [{face, "Monospace"}]), - SetMonospace = fun(Style) -> wxStyledTextCtrl:styleSetFont(STC, Style, Mono) end, - ok = lists:foreach(SetMonospace, styles()), - ok = wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono), - ok = set_colors(STC), - STC. - -get_text(STC) -> - wxStyledTextCtrl:getText(STC). - -set_text(STC, Text) -> - ok = wxStyledTextCtrl:setText(STC, Text), - %% Force Scintilla to request styling for the entire text - wxStyledTextCtrl:colourise(STC, 0, -1). - -%% ====================================================================== - -set_colors(STC) -> - ok = wxStyledTextCtrl:styleClearAll(STC), - Palette = #{bg := BGC} = palette(color_mode()), - Colorize = - fun(Style) -> - Color = maps:get(Style, Palette), - ok = wxStyledTextCtrl:styleSetForeground(STC, Style, Color), - ok = wxStyledTextCtrl:styleSetBackground(STC, Style, BGC) - end, - ok = wxStyledTextCtrl:styleSetBackground(STC, ?wxSTC_STYLE_DEFAULT, BGC), - lists:foreach(Colorize, styles()). - - -update(_Event, STC) -> - try - StartPos = wxStyledTextCtrl:getEndStyled(STC), - EndPos = wxStyledTextCtrl:getLength(STC), - Text = wxStyledTextCtrl:getTextRange(STC, StartPos, EndPos), - case so_scan:scan(Text) of - {ok, Tokens} -> - wxStyledTextCtrl:startStyling(STC, StartPos), - apply_styles(STC, Tokens); - {error, _Reason} -> - wxStyledTextCtrl:startStyling(STC, StartPos), - wxStyledTextCtrl:setStyling(STC, EndPos - StartPos, 0) - end - catch - error:E:T -> - dbg("CAUGHT error:~p / ~p~n", [E, T]) - end. - -apply_styles(STC, Tokens) -> - lists:foreach(fun(Token) -> style_token(STC, Token) end, Tokens). - -style_token(STC, Token) -> - {Type, Value} = type_and_value(Token), - {Line, Col} = element(2, Token), - Len = byte_size(to_binary(Value)), - Style = classify_style(Type, Value), - % Get exact position from Line and Column - Start = wxStyledTextCtrl:positionFromLine(STC, Line - 1) + (Col - 1), - wxStyledTextCtrl:startStyling(STC, Start), - wxStyledTextCtrl:setStyling(STC, Len, Style). - -to_binary(S) when is_list(S) -> - unicode:characters_to_binary(S); -to_binary(S) when is_binary(S) -> - S; -to_binary(I) when is_integer(I) -> - integer_to_binary(I). - -classify_style(Type, Value) -> - case Type of - symbol -> - case lists:member(Value, keywords()) of - true -> ?KEYWORD; - false -> ?OPERATOR - end; - id -> ?IDENTIFIER; - qid -> ?IDENTIFIER; - con -> ?IDENTIFIER; - qcon -> ?IDENTIFIER; - tvar -> ?IDENTIFIER; - string -> ?STRING; - char -> ?STRING; - int -> ?NUMBER; - hex -> ?NUMBER; - bytes -> ?NUMBER; - skip -> ?COMMENT; - _Other -> ?DEFAULT - end. - -type_and_value({Type,_Line,Value}) -> {Type, Value}; -type_and_value({Type, _}) -> {Type, atom_to_list(Type)}. - -keywords() -> - ["contract", "include", "let", "switch", "type", "record", "datatype", "if", - "elif", "else", "function", "stateful", "payable", "true", "false", "mod", - "public", "entrypoint", "private", "indexed", "namespace", "interface", - "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"]. - -%% dbg(Fmt) -> -%% dbg(Fmt, []). - -dbg(Fmt, Args) -> - io:format("~p: " ++ Fmt, [self()|Args]). From ea6667c05f029e75301b961dd7b33f3d89b9c47c Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 31 Mar 2025 15:58:56 +0900 Subject: [PATCH 7/8] Remove keymaster; update dep --- src/gd_con.erl | 10 +-- src/gd_key_master.erl | 164 ------------------------------------------ zomp.meta | 2 +- 3 files changed, 7 insertions(+), 169 deletions(-) delete mode 100644 src/gd_key_master.erl diff --git a/src/gd_con.erl b/src/gd_con.erl index 5cc4295..57ade09 100644 --- a/src/gd_con.erl +++ b/src/gd_con.erl @@ -831,7 +831,9 @@ do_make_key2(Name, Bin, Transform, #wallet{name = WalletName, poas = POAs, keys = Keys} = Current, T = transform(Transform), Seed = T(Bin), - Key = #key{name = KeyName, id = ID} = gd_key_master:make_key(Name, Seed), + {ID, Pair} = hz_key_master:make_key(Seed), + KeyName = case Name =:= "" of true -> ID; false -> Name end, + Key = #key{name = KeyName, id = ID, pair = Pair}, POA = #poa{name = KeyName, id = ID}, NewKeys = [Key | Keys], NewPOAs = [POA | POAs], @@ -870,7 +872,7 @@ t_xor(B, A) -> do_recover_key(Mnemonic, State) -> - case gd_key_master:decode(Mnemonic) of + case hz_key_master:decode(Mnemonic) of {ok, Seed} -> do_recover_key2(Seed, State); Error -> @@ -880,7 +882,7 @@ do_recover_key(Mnemonic, State) -> do_recover_key2(Seed, State = #s{wallet = Current, wallets = Wallets, pass = Pass}) -> #wallet{name = WalletName, keys = Keys, poas = POAs} = Current, - Recovered = #key{id = ID, name = AccName} = gd_key_master:make_key("", Seed), + Recovered = #key{id = ID, name = AccName} = hz_key_master:make_key("", Seed), case lists:keymember(ID, #key.id, Keys) of false -> NewKeys = [Recovered | Keys], @@ -899,7 +901,7 @@ do_recover_key2(Seed, State = #s{wallet = Current, wallets = Wallets, pass = Pas do_mnemonic(ID, #s{wallet = #wallet{keys = Keys}}) -> case lists:keyfind(ID, #key.id, Keys) of #key{pair = #{secret := <>}} -> - Mnemonic = gd_key_master:encode(K), + Mnemonic = hz_key_master:encode(K), {ok, Mnemonic}; false -> {error, bad_key} diff --git a/src/gd_key_master.erl b/src/gd_key_master.erl deleted file mode 100644 index ff89b07..0000000 --- a/src/gd_key_master.erl +++ /dev/null @@ -1,164 +0,0 @@ -%%% @doc -%%% Key functions go here. -%%% -%%% The main reason this is a module of its own is that in the original architecture -%%% it was a process rather than just a library of functions. Now that it exists, though, -%%% there is little motivation to cram everything here into the controller process's -%%% code. -%%% @end - --module(gd_key_master). --vsn("0.5.4"). - - --export([make_key/2, encode/1, decode/1]). --export([lcg/1]). --include("gd.hrl"). - - -make_key("", <<>>) -> - Pair = #{public := Public} = ecu_eddsa:sign_keypair(), - ID = gmser_api_encoder:encode(account_pubkey, Public), - Name = binary_to_list(ID), - #key{name = Name, id = ID, pair = Pair}; -make_key("", Seed) -> - Pair = #{public := Public} = ecu_eddsa:sign_seed_keypair(Seed), - ID = gmser_api_encoder:encode(account_pubkey, Public), - Name = binary_to_list(ID), - #key{name = Name, id = ID, pair = Pair}; -make_key(Name, <<>>) -> - Pair = #{public := Public} = ecu_eddsa:sign_keypair(), - ID = gmser_api_encoder:encode(account_pubkey, Public), - #key{name = Name, id = ID, pair = Pair}; -make_key(Name, Seed) -> - Pair = #{public := Public} = ecu_eddsa:sign_seed_keypair(Seed), - ID = gmser_api_encoder:encode(account_pubkey, Public), - #key{name = Name, id = ID, pair = Pair}. - - --spec encode(Secret) -> Phrase - when Secret :: binary(), - Phrase :: string(). -%% @doc -%% The encoding and decoding procesures are written to be able to handle any -%% width of bitstring or binary and a variable size dictionary. The magic numbers -%% 32, 4096 and 12 have been dropped in because currently these are known, but that -%% will change in the future if the key size or type changes. - -encode(Bin) -> - <> = Bin, - DictSize = 4096, - Words = read_words(), -% Width = chunksize(DictSize - 1, 2), - Width = 12, - Chunks = chunksize(Number, DictSize), - Binary = <>, - encode(Width, Binary, Words). - -encode(Width, Bits, Words) -> - CheckSum = checksum(Width, Bits), - encode(Width, <>, Words, []). - -encode(_, <<>>, _, Acc) -> - unicode:characters_to_list(lists:join(" ", lists:reverse(Acc))); -encode(Width, Bits, Words, Acc) -> - <> = Bits, - Word = lists:nth(I + 1, Words), - encode(Width, Rest, Words, [Word | Acc]). - - --spec decode(Phrase) -> {ok, Secret} | {error, Reason} - when Phrase :: string(), - Secret :: binary(), - Reason :: bad_phrase | bad_word. -%% @doc -%% Reverses the encoded secret string back into its binary representation. - -decode(Encoded) -> - DictSize = 4096, - Words = read_words(), - Width = chunksize(DictSize - 1, 2), - decode(Width, Words, Encoded). - -decode(Width, Words, Encoded) when is_list(Encoded) -> - decode(Width, Words, list_to_binary(Encoded)); -decode(Width, Words, Encoded) -> - Split = string:lexemes(Encoded, " "), - decode(Width, Words, Split, <<>>). - -decode(Width, Words, [Word | Rest], Acc) -> - case find(Word, Words) of - {ok, N} -> decode(Width, Words, Rest, <>); - Error -> Error - end; -decode(Width, _, [], Acc) -> - sumcheck(Width, Acc). - - -chunksize(N, C) -> - chunksize(N, C, 0). - -chunksize(0, _, A) -> A; -chunksize(N, C, A) -> chunksize(N div C, C, A + 1). - - -read_words() -> - Path = filename:join([zx:get_home(), "priv", "words4096.txt"]), - {ok, Bin} = file:read_file(Path), - string:lexemes(Bin, "\n"). - - -find(Word, Words) -> - find(Word, Words, 0). - -find(Word, [Word | _], N) -> {ok, N}; -find(Word, [_ | Rest], N) -> find(Word, Rest, N + 1); -find(Word, [], _) -> {error, {bad_word, Word}}. - - -checksum(Width, Bits) -> - checksum(Width, Bits, 0). - -checksum(_, <<>>, Sum) -> - Sum; -checksum(Width, Bits, Sum) -> - <> = Bits, - checksum(Width, Rest, N bxor Sum). - - -sumcheck(Width, Bits) -> - <> = Bits, - case checksum(Width, Binary) =:= CheckSum of - true -> - <> = Binary, - {ok, <>}; - false -> - {error, bad_phrase} - end. - - - --spec lcg(integer()) -> integer(). -%% A simple PRNG that fits into 32 bits and is easy to implement anywhere (Kotlin). -%% Specifically, it is a "linear congruential generator" of the Lehmer variety. -%% The constants used are based on recommendations from Park, Miller and Stockmeyer: -%% https://www.firstpr.com.au/dsp/rand31/p105-crawford.pdf#page=4 -%% -%% The input value should be between 1 and 2^31-1. -%% -%% The purpose of this PRNG is for password-based dictionary shuffling. - -lcg(N) -> - M = 16#7FFFFFFF, - A = 48271, - Q = 44488, % M div A - R = 3399, % M rem A - Div = N div Q, - Rem = N rem Q, - S = Rem * A, - T = Div * R, - Result = S - T, - case Result < 0 of - false -> Result; - true -> Result + M - end. diff --git a/zomp.meta b/zomp.meta index 924eea2..afbf135 100644 --- a/zomp.meta +++ b/zomp.meta @@ -5,7 +5,7 @@ {author,"Craig Everett"}. {desc,"A desktop client for the Gajumaru network of blockchain networks"}. {package_id,{"otpr","gajudesk",{0,5,4}}}. -{deps,[{"otpr","hakuzaru",{0,5,1}}, +{deps,[{"otpr","hakuzaru",{0,6,0}}, {"otpr","gmserialization",{0,1,3}}, {"otpr","sophia",{9,0,0}}, {"otpr","gmbytecode",{3,4,1}}, From 2bef1fd32301973dce213ffc27a12bda2d0af3c6 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Thu, 3 Apr 2025 15:01:16 +0900 Subject: [PATCH 8/8] Fix app name --- zomp.meta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zomp.meta b/zomp.meta index afbf135..c253603 100644 --- a/zomp.meta +++ b/zomp.meta @@ -1,4 +1,4 @@ -{name,"Clutch"}. +{name,"GajuDesk"}. {type,gui}. {modules,[]}. {prefix,"gd"}.