First stab at supporting syntax highlighting via wxStyledTextCtrl #8

Merged
zxq9 merged 8 commits from uw-syntax-highlighting into master 2025-04-02 16:34:58 +09:00
2 changed files with 142 additions and 6 deletions
Showing only changes of commit 27b78f8623 - Show all commits

113
src/gd_sophia_editor.erl Normal file
View 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]).

View File

@ -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")}]),