Restyle whole buffer on event to fix style breaks
TODO: Make this more precise... some day
This commit is contained in:
parent
9d70f98ed0
commit
dea44561ee
198
src/:
Normal file
198
src/:
Normal file
@ -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]).
|
@ -94,7 +94,6 @@ new(Parent) ->
|
|||||||
?wxFONTSTYLE_NORMAL,
|
?wxFONTSTYLE_NORMAL,
|
||||||
?wxFONTWEIGHT_NORMAL,
|
?wxFONTWEIGHT_NORMAL,
|
||||||
[{face, "Monospace"}]),
|
[{face, "Monospace"}]),
|
||||||
|
|
||||||
SetMonospace = fun(Style) -> wxStyledTextCtrl:styleSetFont(STC, Style, Mono) end,
|
SetMonospace = fun(Style) -> wxStyledTextCtrl:styleSetFont(STC, Style, Mono) end,
|
||||||
ok = lists:foreach(SetMonospace, styles()),
|
ok = lists:foreach(SetMonospace, styles()),
|
||||||
ok = wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono),
|
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
|
%% 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) ->
|
||||||
ok = wxStyledTextCtrl:styleClearAll(STC),
|
ok = wxStyledTextCtrl:styleClearAll(STC),
|
||||||
@ -125,35 +123,34 @@ set_colors(STC) ->
|
|||||||
|
|
||||||
|
|
||||||
update(_Event, STC) ->
|
update(_Event, STC) ->
|
||||||
try
|
Text = wxStyledTextCtrl:getText(STC),
|
||||||
StartPos = wxStyledTextCtrl:getEndStyled(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);
|
||||||
@ -162,6 +159,7 @@ 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 ->
|
||||||
@ -180,20 +178,16 @@ classify_style(Type, Value) ->
|
|||||||
hex -> ?NUMBER;
|
hex -> ?NUMBER;
|
||||||
bytes -> ?NUMBER;
|
bytes -> ?NUMBER;
|
||||||
skip -> ?COMMENT;
|
skip -> ?COMMENT;
|
||||||
_Other -> ?DEFAULT
|
_ -> ?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]).
|
|
||||||
|
@ -296,10 +296,9 @@ terminate(Reason, State) ->
|
|||||||
|
|
||||||
%%% Doers
|
%%% Doers
|
||||||
|
|
||||||
style(#s{code = {Codebook, Pages}}, Win, Event) ->
|
style(#s{code = {_, Pages}}, Win, Event) ->
|
||||||
case lists:keyfind(Win, #p.win, Pages) of
|
case lists:keyfind(Win, #p.win, Pages) of
|
||||||
#p{code = STC} ->
|
#p{code = STC} ->
|
||||||
tell("Received style event.~nWin: ~p~nEvent: ~p", [Win, Event]),
|
|
||||||
gd_sophia_editor:update(Event, STC);
|
gd_sophia_editor:update(Event, STC);
|
||||||
false ->
|
false ->
|
||||||
tell("Received bogus style event.~nWin: ~p~nEvent: ~p", [Win, Event])
|
tell("Received bogus style event.~nWin: ~p~nEvent: ~p", [Win, Event])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user