First stab at supporting syntax highlighting via wxStyledTextCtrl #8
113
src/gd_sophia_editor.erl
Normal file
113
src/gd_sophia_editor.erl
Normal file
@ -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]).
|
@ -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")}]),
|
||||
|
Loading…
x
Reference in New Issue
Block a user