Merge pull request 'style_massage' (#9) from style_massage into uw-syntax-highlighting
Reviewed-on: #9 Reviewed-by: Ulf Wiger <ulfwiger@qpq.swiss>
This commit is contained in:
commit
1f17d36c42
@ -652,7 +652,7 @@ do_sign_mess(Request = #{"public_id" := ID, "payload" := Message},
|
|||||||
#s{wallet = #wallet{keys = Keys}}) ->
|
#s{wallet = #wallet{keys = Keys}}) ->
|
||||||
case lists:keyfind(ID, #key.id, Keys) of
|
case lists:keyfind(ID, #key.id, Keys) of
|
||||||
#key{pair = #{secret := SecKey}} ->
|
#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});
|
do_sign_mess2(Request#{"signature" => Sig});
|
||||||
false ->
|
false ->
|
||||||
gd_gui:trouble({bad_key, ID})
|
gd_gui:trouble({bad_key, ID})
|
||||||
@ -675,37 +675,6 @@ do_sign_mess2(Request = #{"url" := URL}) ->
|
|||||||
end.
|
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, <<N>>};
|
|
||||||
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) >>,
|
|
||||||
<<Bytes/binary, ExtraZeros/binary>>.
|
|
||||||
|
|
||||||
|
|
||||||
do_sign_tx(Request = #{"public_id" := ID, "payload" := CallData, "network_id" := NID},
|
do_sign_tx(Request = #{"public_id" := ID, "payload" := CallData, "network_id" := NID},
|
||||||
#s{wallet = #wallet{keys = Keys}}) ->
|
#s{wallet = #wallet{keys = Keys}}) ->
|
||||||
BinNID = list_to_binary(NID),
|
BinNID = list_to_binary(NID),
|
||||||
@ -862,7 +831,9 @@ do_make_key2(Name, Bin, Transform,
|
|||||||
#wallet{name = WalletName, poas = POAs, keys = Keys} = Current,
|
#wallet{name = WalletName, poas = POAs, keys = Keys} = Current,
|
||||||
T = transform(Transform),
|
T = transform(Transform),
|
||||||
Seed = T(Bin),
|
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},
|
POA = #poa{name = KeyName, id = ID},
|
||||||
NewKeys = [Key | Keys],
|
NewKeys = [Key | Keys],
|
||||||
NewPOAs = [POA | POAs],
|
NewPOAs = [POA | POAs],
|
||||||
@ -901,7 +872,7 @@ t_xor(B, A) ->
|
|||||||
|
|
||||||
|
|
||||||
do_recover_key(Mnemonic, State) ->
|
do_recover_key(Mnemonic, State) ->
|
||||||
case gd_key_master:decode(Mnemonic) of
|
case hz_key_master:decode(Mnemonic) of
|
||||||
{ok, Seed} ->
|
{ok, Seed} ->
|
||||||
do_recover_key2(Seed, State);
|
do_recover_key2(Seed, State);
|
||||||
Error ->
|
Error ->
|
||||||
@ -911,7 +882,7 @@ do_recover_key(Mnemonic, State) ->
|
|||||||
|
|
||||||
do_recover_key2(Seed, State = #s{wallet = Current, wallets = Wallets, pass = Pass}) ->
|
do_recover_key2(Seed, State = #s{wallet = Current, wallets = Wallets, pass = Pass}) ->
|
||||||
#wallet{name = WalletName, keys = Keys, poas = POAs} = Current,
|
#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
|
case lists:keymember(ID, #key.id, Keys) of
|
||||||
false ->
|
false ->
|
||||||
NewKeys = [Recovered | Keys],
|
NewKeys = [Recovered | Keys],
|
||||||
@ -930,7 +901,7 @@ do_recover_key2(Seed, State = #s{wallet = Current, wallets = Wallets, pass = Pas
|
|||||||
do_mnemonic(ID, #s{wallet = #wallet{keys = Keys}}) ->
|
do_mnemonic(ID, #s{wallet = #wallet{keys = Keys}}) ->
|
||||||
case lists:keyfind(ID, #key.id, Keys) of
|
case lists:keyfind(ID, #key.id, Keys) of
|
||||||
#key{pair = #{secret := <<K:32/binary, _/binary>>}} ->
|
#key{pair = #{secret := <<K:32/binary, _/binary>>}} ->
|
||||||
Mnemonic = gd_key_master:encode(K),
|
Mnemonic = hz_key_master:encode(K),
|
||||||
{ok, Mnemonic};
|
{ok, Mnemonic};
|
||||||
false ->
|
false ->
|
||||||
{error, bad_key}
|
{error, bad_key}
|
||||||
|
@ -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) ->
|
|
||||||
<<Number:(32 * 8)>> = Bin,
|
|
||||||
DictSize = 4096,
|
|
||||||
Words = read_words(),
|
|
||||||
% Width = chunksize(DictSize - 1, 2),
|
|
||||||
Width = 12,
|
|
||||||
Chunks = chunksize(Number, DictSize),
|
|
||||||
Binary = <<Number:(Chunks * Width)>>,
|
|
||||||
encode(Width, Binary, Words).
|
|
||||||
|
|
||||||
encode(Width, Bits, Words) ->
|
|
||||||
CheckSum = checksum(Width, Bits),
|
|
||||||
encode(Width, <<CheckSum:Width, Bits/bitstring>>, Words, []).
|
|
||||||
|
|
||||||
encode(_, <<>>, _, Acc) ->
|
|
||||||
unicode:characters_to_list(lists:join(" ", lists:reverse(Acc)));
|
|
||||||
encode(Width, Bits, Words, Acc) ->
|
|
||||||
<<I:Width, Rest/bitstring>> = 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, <<Acc/bitstring, N:Width>>);
|
|
||||||
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) ->
|
|
||||||
<<N:Width, Rest/bitstring>> = Bits,
|
|
||||||
checksum(Width, Rest, N bxor Sum).
|
|
||||||
|
|
||||||
|
|
||||||
sumcheck(Width, Bits) ->
|
|
||||||
<<CheckSum:Width, Binary/bitstring>> = Bits,
|
|
||||||
case checksum(Width, Binary) =:= CheckSum of
|
|
||||||
true ->
|
|
||||||
<<N:(bit_size(Binary))>> = Binary,
|
|
||||||
{ok, <<N:(32 * 8)>>};
|
|
||||||
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.
|
|
@ -1,84 +1,156 @@
|
|||||||
-module(gd_sophia_editor).
|
-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").
|
-include_lib("wx/include/wx.hrl").
|
||||||
|
|
||||||
|
|
||||||
new(Parent, Id, Opts) ->
|
%%% Formatting Constants
|
||||||
STC = wxStyledTextCtrl:new(Parent, [{id, Id} | Opts]),
|
|
||||||
|
|
||||||
%% Set up the container lexer
|
%% Style labels
|
||||||
wxStyledTextCtrl:setLexer(STC, ?wxSTC_LEX_CONTAINER),
|
-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,
|
FontSize = 13,
|
||||||
Mono = wxFont:new(FontSize, ?wxFONTFAMILY_TELETYPE, ?wxFONTSTYLE_NORMAL,
|
Mono = wxFont:new(FontSize,
|
||||||
?wxFONTWEIGHT_NORMAL, [{face, "Monospace"}]),
|
?wxFONTFAMILY_TELETYPE,
|
||||||
lists:foreach(
|
?wxFONTSTYLE_NORMAL,
|
||||||
fun(Style) ->
|
?wxFONTWEIGHT_NORMAL,
|
||||||
wxStyledTextCtrl:styleSetFont(STC, Style, Mono)
|
[{face, "Monospace"}]),
|
||||||
end,
|
SetMonospace = fun(Style) -> wxStyledTextCtrl:styleSetFont(STC, Style, Mono) end,
|
||||||
lists:seq(0, 6)
|
ok = lists:foreach(SetMonospace, styles()),
|
||||||
),
|
ok = wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono),
|
||||||
%%
|
ok = set_colors(STC),
|
||||||
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}]),
|
|
||||||
STC.
|
STC.
|
||||||
|
|
||||||
get_text(STC) ->
|
get_text(STC) ->
|
||||||
wxStyledTextCtrl:getText(STC).
|
wxStyledTextCtrl:getText(STC).
|
||||||
|
|
||||||
set_text(STC, Text) ->
|
set_text(STC, Text) ->
|
||||||
wxStyledTextCtrl:setText(STC, Text),
|
ok = wxStyledTextCtrl:setText(STC, Text),
|
||||||
%% Force Scintilla to request styling for the entire text
|
%% Force Scintilla to request styling for the entire text
|
||||||
wxStyledTextCtrl:colourise(STC, 0, -1).
|
wxStyledTextCtrl:colourise(STC, 0, -1).
|
||||||
|
|
||||||
%% ======================================================================
|
|
||||||
|
|
||||||
set_colors(STC) ->
|
set_colors(STC) ->
|
||||||
wxStyledTextCtrl:styleClearAll(STC),
|
ok = wxStyledTextCtrl:styleClearAll(STC),
|
||||||
wxStyledTextCtrl:styleSetForeground(STC, 0, {0, 0, 0}), % Default: Black
|
Palette = #{bg := BGC} = palette(color_mode()),
|
||||||
wxStyledTextCtrl:styleSetForeground(STC, 1, {0, 0, 192}), % Keywords: Deep Blue
|
Colorize =
|
||||||
wxStyledTextCtrl:styleSetForeground(STC, 2, {0, 0, 0}), % Identifiers: Black
|
fun(Style) ->
|
||||||
wxStyledTextCtrl:styleSetForeground(STC, 3, {128, 128, 128}), % Comments: Gray
|
Color = maps:get(Style, Palette),
|
||||||
wxStyledTextCtrl:styleSetForeground(STC, 4, {163, 21, 21}), % Strings/Chars: Reddish
|
ok = wxStyledTextCtrl:styleSetForeground(STC, Style, Color),
|
||||||
wxStyledTextCtrl:styleSetForeground(STC, 5, {128, 0, 128}), % Numbers: Purple
|
ok = wxStyledTextCtrl:styleSetBackground(STC, Style, BGC)
|
||||||
wxStyledTextCtrl:styleSetForeground(STC, 6, {0, 0, 0}). % Operators: Black
|
end,
|
||||||
|
ok = wxStyledTextCtrl:styleSetBackground(STC, ?wxSTC_STYLE_DEFAULT, BGC),
|
||||||
|
lists:foreach(Colorize, styles()).
|
||||||
|
|
||||||
style_needed(_Event, STC) ->
|
|
||||||
try
|
update(_Event, STC) ->
|
||||||
StartPos = wxStyledTextCtrl:getEndStyled(STC),
|
Text = wxStyledTextCtrl:getText(STC),
|
||||||
EndPos = wxStyledTextCtrl:getLength(STC),
|
|
||||||
Text = wxStyledTextCtrl:getTextRange(STC, StartPos, EndPos),
|
|
||||||
case so_scan:scan(Text) of
|
case so_scan:scan(Text) of
|
||||||
{ok, Tokens} ->
|
{ok, Tokens} ->
|
||||||
wxStyledTextCtrl:startStyling(STC, StartPos),
|
ok = wxStyledTextCtrl:startStyling(STC, 0),
|
||||||
apply_styles(STC, Tokens);
|
apply_styles(STC, Tokens);
|
||||||
{error, _Reason} ->
|
{error, _Reason} ->
|
||||||
wxStyledTextCtrl:startStyling(STC, StartPos),
|
ok
|
||||||
wxStyledTextCtrl:setStyling(STC, EndPos - StartPos, 0)
|
|
||||||
end
|
|
||||||
catch
|
|
||||||
error:E:T ->
|
|
||||||
dbg("CAUGHT error:~p / ~p~n", [E, T])
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
apply_styles(STC, Tokens) ->
|
apply_styles(STC, Tokens) ->
|
||||||
lists:foreach(fun(Token) -> style_token(STC, Token) end, 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) ->
|
style_token(STC, Token) ->
|
||||||
{Type, Value} = type_and_value(Token),
|
{Type, Value} = type_and_value(Token),
|
||||||
|
{StartOffset, LengthOffset} = offset(Type),
|
||||||
{Line, Col} = element(2, Token),
|
{Line, Col} = element(2, Token),
|
||||||
Len = byte_size(to_binary(Value)),
|
Length = byte_size(to_binary(Value)) + LengthOffset,
|
||||||
Style = classify_style(Type, Value),
|
Style = classify_style(Type, Value),
|
||||||
% Get exact position from Line and Column
|
Start = wxStyledTextCtrl:positionFromLine(STC, Line - 1) + Col + StartOffset,
|
||||||
Start = wxStyledTextCtrl:positionFromLine(STC, Line - 1) + (Col - 1),
|
|
||||||
wxStyledTextCtrl:startStyling(STC, Start),
|
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) ->
|
to_binary(S) when is_list(S) ->
|
||||||
unicode:characters_to_binary(S);
|
unicode:characters_to_binary(S);
|
||||||
@ -87,38 +159,35 @@ to_binary(S) when is_binary(S) ->
|
|||||||
to_binary(I) when is_integer(I) ->
|
to_binary(I) when is_integer(I) ->
|
||||||
integer_to_binary(I).
|
integer_to_binary(I).
|
||||||
|
|
||||||
|
|
||||||
classify_style(Type, Value) ->
|
classify_style(Type, Value) ->
|
||||||
case Type of
|
case Type of
|
||||||
symbol ->
|
symbol ->
|
||||||
case lists:member(Value, keywords()) of
|
case lists:member(Value, keywords()) of
|
||||||
true -> 1;
|
true -> ?KEYWORD;
|
||||||
false -> 6
|
false -> ?OPERATOR
|
||||||
end;
|
end;
|
||||||
id -> 2;
|
id -> ?IDENTIFIER;
|
||||||
qid -> 2;
|
qid -> ?IDENTIFIER;
|
||||||
con -> 2;
|
con -> ?IDENTIFIER;
|
||||||
qcon -> 2;
|
qcon -> ?IDENTIFIER;
|
||||||
tvar -> 2;
|
tvar -> ?IDENTIFIER;
|
||||||
string -> 4;
|
string -> ?STRING;
|
||||||
char -> 4;
|
char -> ?STRING;
|
||||||
int -> 5;
|
int -> ?NUMBER;
|
||||||
hex -> 5;
|
hex -> ?NUMBER;
|
||||||
bytes -> 5;
|
bytes -> ?NUMBER;
|
||||||
skip -> 3;
|
skip -> ?COMMENT;
|
||||||
_Other -> 1
|
_ -> ?DEFAULT
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
type_and_value({Type, _Line, Value}) -> {Type, Value};
|
type_and_value({Type, _Line, Value}) -> {Type, Value};
|
||||||
type_and_value({Type, _}) -> {Type, atom_to_list(Type)}.
|
type_and_value({Type, _}) -> {Type, atom_to_list(Type)}.
|
||||||
|
|
||||||
|
|
||||||
keywords() ->
|
keywords() ->
|
||||||
["contract", "include", "let", "switch", "type", "record", "datatype", "if",
|
["contract", "include", "let", "switch", "type", "record", "datatype", "if",
|
||||||
"elif", "else", "function", "stateful", "payable", "true", "false", "mod",
|
"elif", "else", "function", "stateful", "payable", "true", "false", "mod",
|
||||||
"public", "entrypoint", "private", "indexed", "namespace", "interface",
|
"public", "entrypoint", "private", "indexed", "namespace", "interface",
|
||||||
"main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"].
|
"main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"].
|
||||||
|
|
||||||
%% dbg(Fmt) ->
|
|
||||||
%% dbg(Fmt, []).
|
|
||||||
|
|
||||||
dbg(Fmt, Args) ->
|
|
||||||
io:format("~p: " ++ Fmt, [self()|Args]).
|
|
||||||
|
@ -258,6 +258,9 @@ handle_event(E = #wx{event = #wxCommand{type = command_button_clicked},
|
|||||||
State
|
State
|
||||||
end,
|
end,
|
||||||
{noreply, NewState};
|
{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}) ->
|
handle_event(#wx{event = #wxClose{}}, State = #s{frame = Frame, prefs = Prefs}) ->
|
||||||
Geometry =
|
Geometry =
|
||||||
case wxTopLevelWindow:isMaximized(Frame) of
|
case wxTopLevelWindow:isMaximized(Frame) of
|
||||||
@ -293,6 +296,14 @@ terminate(Reason, State) ->
|
|||||||
|
|
||||||
%%% Doers
|
%%% Doers
|
||||||
|
|
||||||
|
style(#s{code = {_, Pages}}, Win, Event) ->
|
||||||
|
case lists:keyfind(Win, #p.win, Pages) of
|
||||||
|
#p{code = STC} ->
|
||||||
|
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) ->
|
clicked(State = #s{cons = {Consbook, Contracts}}, Name) ->
|
||||||
case wxNotebook:getSelection(Consbook) of
|
case wxNotebook:getSelection(Consbook) of
|
||||||
?wxNOT_FOUND ->
|
?wxNOT_FOUND ->
|
||||||
@ -506,30 +517,13 @@ add_code_page2(State, {hash, Address}) ->
|
|||||||
open_hash2(State, Address).
|
open_hash2(State, Address).
|
||||||
|
|
||||||
add_code_page(State = #s{tabs = TopBook, code = {Codebook, Pages}}, Location, Code) ->
|
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
|
Color = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW),
|
||||||
% have to contend with system theme issues (light/dark themese, namely)
|
tell("Color: ~p", [Color]),
|
||||||
% 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]),
|
|
||||||
Window = wxWindow:new(Codebook, ?wxID_ANY),
|
Window = wxWindow:new(Codebook, ?wxID_ANY),
|
||||||
PageSz = wxBoxSizer:new(?wxHORIZONTAL),
|
PageSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||||
|
|
||||||
CodeTxStyle = {style, ?wxTE_MULTILINE bor ?wxTE_PROCESS_TAB bor ?wxTE_DONTWRAP},
|
CodeTx = gd_sophia_editor:new(Window),
|
||||||
CodeTx = case ?editorMode of
|
ok = gd_sophia_editor:set_text(CodeTx, Code),
|
||||||
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,
|
|
||||||
|
|
||||||
_ = wxSizer:add(PageSz, CodeTx, zxw:flags(wide)),
|
_ = wxSizer:add(PageSz, CodeTx, zxw:flags(wide)),
|
||||||
|
|
||||||
@ -541,6 +535,7 @@ add_code_page(State = #s{tabs = TopBook, code = {Codebook, Pages}}, Location, Co
|
|||||||
{file, Path} -> filename:basename(Path);
|
{file, Path} -> filename:basename(Path);
|
||||||
{hash, Addr} -> Addr
|
{hash, Addr} -> Addr
|
||||||
end,
|
end,
|
||||||
|
ok = wxStyledTextCtrl:connect(Window, stc_styleneeded),
|
||||||
true = wxNotebook:addPage(Codebook, Window, FileName, [{bSelect, true}]),
|
true = wxNotebook:addPage(Codebook, Window, FileName, [{bSelect, true}]),
|
||||||
Page = #p{path = Location, win = Window, code = CodeTx},
|
Page = #p{path = Location, win = Window, code = CodeTx},
|
||||||
NewPages = Pages ++ [Page],
|
NewPages = Pages ++ [Page],
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{author,"Craig Everett"}.
|
{author,"Craig Everett"}.
|
||||||
{desc,"A desktop client for the Gajumaru network of blockchain networks"}.
|
{desc,"A desktop client for the Gajumaru network of blockchain networks"}.
|
||||||
{package_id,{"otpr","gajudesk",{0,5,4}}}.
|
{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","gmserialization",{0,1,3}},
|
||||||
{"otpr","sophia",{9,0,0}},
|
{"otpr","sophia",{9,0,0}},
|
||||||
{"otpr","gmbytecode",{3,4,1}},
|
{"otpr","gmbytecode",{3,4,1}},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user