#include "wx_pch.h"
#include "TMyCodingAssistance.h"
#include "TMyScintilla.h"
#include <Scintilla.h>
#include <ILexer.h>
#include <Lexilla.h>
#include <SciLexer.h>
namespace
{
struct TMyProgrammingLanguageBase
{
enum class ID{NONE,CPP,PYTHON};
template<size_t sz> static bool FindId(const char* const (&extList)[sz],const char* ext)
{
return std::find_if(extList,extList+sz
,[ext](const char* aExt) {return std::strcmp(aExt,ext)==0;})
!=extList+sz;
}
static ID GetId(const wxString& fileName)
{
static const char* const cppExt[]={"c","cc","cp","cxx","cpp","c++","h","hh","hp","hxx","hpp","h++","tcc"};
static const char* const pythonExt[]={"py","pyw"};
auto ext=wxFileName{fileName}.GetExt().Lower();
if (FindId(cppExt,ext.mb_str())) {return ID::CPP;}
if (FindId(pythonExt,ext.mb_str())) {return ID::PYTHON;}
return ID::NONE;
}
static TMyProgrammingLanguageBase* Create(TMyScintilla* ctrl,const wxString& fileName);
virtual ~TMyProgrammingLanguageBase() {}
virtual void SetStyles()=0;
virtual void OnScintillaNotify(SCNotification* notification)=0;
};
template<TMyProgrammingLanguageBase::ID>
class TMyProgrammingLanguage:public TMyProgrammingLanguageBase
{
private:
TMyScintilla* const ctrl_;
void Init();
public:
TMyProgrammingLanguage(TMyScintilla* ctrl):ctrl_{ctrl} {Init();}
void SetStyles() override;
void OnScintillaNotify(SCNotification* notification) override;
};
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::NONE>::SetStyles();
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::NONE>::Init()
{
ctrl_->SendMsg(SCI_SETILEXER,0,0);
ctrl_->SendMsg(SCI_CLEARDOCUMENTSTYLE,0,0);
ctrl_->SendMsg(SCI_SETWRAPMODE,SC_WRAP_WORD,0);
ctrl_->SendMsg(SCI_SETKEYWORDS,0,(LPARAM)"");
SetStyles();
}
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::NONE>::SetStyles()
{
ctrl_->SendMsg(SCI_STYLECLEARALL,0,0);
}
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::NONE>::OnScintillaNotify
(SCNotification* notification)
{
}
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::CPP>::SetStyles();
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::CPP>::Init()
{
ctrl_->SendMsg(SCI_SETILEXER,0,(LPARAM)CreateLexer("cpp"));
ctrl_->SendMsg(SCI_CLEARDOCUMENTSTYLE,0,0);
ctrl_->SendMsg(SCI_SETWRAPMODE,SC_WRAP_NONE,0);
ctrl_->SendMsg(SCI_SETKEYWORDS,0,(LPARAM)
"alignas alignof asm auto bool break case catch char char8_t "
"char16_t char32_t class concept const consteval constexpr constinit "
"const_cast continue co_await co_return co_yield decltype default "
"delete do double dynamic_cast else enum explicit export extern false "
"float for friend goto if inline int long mutable namespace new "
"noexcept nullptr operator private protected public register "
"reinterpret_cast requires return short signed sizeof static "
"static_assert static_cast struct switch template this thread_local "
"throw true try typedef typeid typename union unsigned using virtual "
"void volatile wchar_t while");
SetStyles();
}
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::CPP>::SetStyles()
{
ctrl_->SendMsg(SCI_STYLECLEARALL,0,0);
ctrl_->SendMsg(SCI_STYLESETFORE,SCE_C_COMMENT,0x000080);
ctrl_->SendMsg(SCI_STYLESETFORE,SCE_C_COMMENTLINE,0x000080);
ctrl_->SendMsg(SCI_STYLESETFORE,SCE_C_COMMENTDOC,0x000080);
ctrl_->SendMsg(SCI_STYLESETFORE,SCE_C_COMMENTLINEDOC,0x000080);
ctrl_->SendMsg(SCI_STYLESETFORE,SCE_C_PREPROCESSOR,0x008000);
ctrl_->SendMsg(SCI_STYLESETBOLD,SCE_C_WORD,true);
ctrl_->SendMsg(SCI_STYLESETBOLD,STYLE_BRACELIGHT,true);
ctrl_->SendMsg(SCI_STYLESETBOLD,STYLE_BRACEBAD,true);
ctrl_->SendMsg(SCI_STYLESETBACK,STYLE_BRACEBAD,0x8080FF);
}
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::CPP>::OnScintillaNotify
(SCNotification* notification)
{
switch(notification->nmhdr.code)
{
case SCN_CHARADDED:
if (notification->ch=='\n')
{
auto curLine=ctrl_->SendMsg(SCI_LINEFROMPOSITION,ctrl_->SendMsg(SCI_GETCURRENTPOS,0,0),0);
auto buf=std::vector<char>(ctrl_->SendMsg(SCI_LINELENGTH,curLine-1,0));
ctrl_->SendMsg(SCI_GETLINE,curLine-1,(LPARAM)buf.data());
for (auto p=buf.begin();p!=buf.end();++p) {if (*p!=' '&&*p!='\t') {*p=0;break;}}
ctrl_->SendMsg(SCI_REPLACESEL,0,(LPARAM)buf.data());
}
break;
case SCN_UPDATEUI:
{
auto PosIsBraced=[this](auto p)
{
static const char braces[]="()[]{}";
static const char* const bracesEnd=braces+std::extent<decltype(braces)>::value-1;
return std::find(braces,bracesEnd,ctrl_->SendMsg(SCI_GETCHARAT,p,0))!=bracesEnd;
};
auto p1=ctrl_->SendMsg(SCI_GETCURRENTPOS,0,0)-1;
if (PosIsBraced(p1)||PosIsBraced(++p1))
{
auto p2=ctrl_->SendMsg(SCI_BRACEMATCH,p1,0);
if (p2!=-1) {ctrl_->SendMsg(SCI_BRACEHIGHLIGHT,p1,p2);}
else {ctrl_->SendMsg(SCI_BRACEBADLIGHT,p1,0);}
}
else
{
ctrl_->SendMsg(SCI_BRACEHIGHLIGHT,-1,-1);
}
}
break;
}
}
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::PYTHON>::SetStyles();
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::PYTHON>::Init()
{
ctrl_->SendMsg(SCI_SETILEXER,0,(LPARAM)CreateLexer("python"));
ctrl_->SendMsg(SCI_CLEARDOCUMENTSTYLE,0,0);
ctrl_->SendMsg(SCI_SETWRAPMODE,SC_WRAP_NONE,0);
ctrl_->SendMsg(SCI_SETKEYWORDS,0,(LPARAM)
"False await else import pass None break except in raise True class "
"finally is return and continue for lambda try as def from nonlocal "
"while assert del global not with async elif if or yield");
SetStyles();
}
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::PYTHON>::SetStyles()
{
ctrl_->SendMsg(SCI_STYLECLEARALL,0,0);
ctrl_->SendMsg(SCI_STYLESETFORE,SCE_P_COMMENTLINE,0x000080);
ctrl_->SendMsg(SCI_STYLESETFORE,SCE_P_COMMENTBLOCK,0x000080);
ctrl_->SendMsg(SCI_STYLESETBOLD,SCE_P_WORD,true);
ctrl_->SendMsg(SCI_STYLESETBOLD,STYLE_BRACELIGHT,true);
ctrl_->SendMsg(SCI_STYLESETBOLD,STYLE_BRACEBAD,true);
ctrl_->SendMsg(SCI_STYLESETBACK,STYLE_BRACEBAD,0x8080FF);
}
template<> void TMyProgrammingLanguage<TMyProgrammingLanguageBase::ID::PYTHON>::OnScintillaNotify
(SCNotification* notification)
{
switch(notification->nmhdr.code)
{
case SCN_CHARADDED:
if (notification->ch=='\n')
{
auto curLine=ctrl_->SendMsg(SCI_LINEFROMPOSITION,ctrl_->SendMsg(SCI_GETCURRENTPOS,0,0),0);
auto buf=std::vector<char>(ctrl_->SendMsg(SCI_LINELENGTH,curLine-1,0));
ctrl_->SendMsg(SCI_GETLINE,curLine-1,(LPARAM)buf.data());
for (auto p=buf.begin();p!=buf.end();++p) {if (*p!=' '&&*p!='\t') {*p=0;break;}}
ctrl_->SendMsg(SCI_REPLACESEL,0,(LPARAM)buf.data());
}
break;
case SCN_UPDATEUI:
{
auto PosIsBraced=[this](auto p)
{
static const char braces[]="()[]{}";
static const char* const bracesEnd=braces+std::extent<decltype(braces)>::value-1;
return std::find(braces,bracesEnd,ctrl_->SendMsg(SCI_GETCHARAT,p,0))!=bracesEnd;
};
auto p1=ctrl_->SendMsg(SCI_GETCURRENTPOS,0,0)-1;
if (PosIsBraced(p1)||PosIsBraced(++p1))
{
auto p2=ctrl_->SendMsg(SCI_BRACEMATCH,p1,0);
if (p2!=-1) {ctrl_->SendMsg(SCI_BRACEHIGHLIGHT,p1,p2);}
else {ctrl_->SendMsg(SCI_BRACEBADLIGHT,p1,0);}
}
else
{
ctrl_->SendMsg(SCI_BRACEHIGHLIGHT,-1,-1);
}
}
break;
}
}
TMyProgrammingLanguageBase* TMyProgrammingLanguageBase::Create(TMyScintilla* ctrl,const wxString& fileName)
{
switch (GetId(fileName))
{
case ID::CPP:
return new TMyProgrammingLanguage<ID::CPP>{ctrl};
case ID::PYTHON:
return new TMyProgrammingLanguage<ID::PYTHON>{ctrl};
default:
return new TMyProgrammingLanguage<ID::NONE>{ctrl};
}
}
}
class TMyCodingAssistance::Impl
{
private:
TMyScintilla* const ctrl_;
std::unique_ptr<TMyProgrammingLanguageBase> programmingLanguage_;
public:
Impl(TMyScintilla* ctrl):ctrl_{ctrl}
,programmingLanguage_{TMyProgrammingLanguageBase::Create(ctrl_,wxString{})}
{}
void SetProgrammingLanguage(const wxString& fileName)
{
programmingLanguage_.reset(TMyProgrammingLanguageBase::Create(ctrl_,fileName));
}
void SetStyles() {programmingLanguage_->SetStyles();}
void OnScintillaNotify(SCNotification* notification)
{programmingLanguage_->OnScintillaNotify(notification);}
};
TMyCodingAssistance::TMyCodingAssistance(TMyScintilla* ctrl):pimpl_{new Impl{ctrl}} {}
TMyCodingAssistance::~TMyCodingAssistance() {}
void TMyCodingAssistance::SetProgrammingLanguage(const wxString& fileName)
{pimpl_->SetProgrammingLanguage(fileName);}
void TMyCodingAssistance::SetStyles() {pimpl_->SetStyles();}
void TMyCodingAssistance::OnScintillaNotify(SCNotification* notification)
{pimpl_->OnScintillaNotify(notification);}
...
#include "version_macro.h"
#include "TMyOptionDialog.h"
#include "TMyFileOpenSave.h"
#include "TMyScintilla.h"
#include <Scintilla.h>
#include "TMyCodingAssistance.h"
#include "MyUtility.h"
class TMyCoreImpl::Impl:public TMyOptionDialog::Observer
{
private:
TMyScintilla* const textCtrl_;
TMyFileOpenSave fileOpenSave_;
TMyCodingAssistance codingAssistance_;
TMyEventHandlerManager evtHandlerManager_;
bool ConfirmIfModified() const
{
return !textCtrl_->SendMsg(SCI_GETMODIFY,0,0)
||wxMessageBox(_("The current file is modified. Do you want to discard?")
,MYAPPINFO_NAME,wxICON_QUESTION|wxYES_NO|wxNO_DEFAULT|wxCENTER)==wxYES;
}
void OnScintillaNotify(wxCommandEvent& evt)
{
if (auto* notification=static_cast<SCNotification*>(evt.GetClientData()))
{codingAssistance_.OnScintillaNotify(notification);}
}
void OnFileChange(wxCommandEvent& evt)
{
codingAssistance_.SetProgrammingLanguage(evt.GetString());
}
public:
Impl(wxWindow* parent)
:textCtrl_{new TMyScintilla{parent,wxID_ANY}}
,fileOpenSave_{textCtrl_}
,codingAssistance_{textCtrl_}
,evtHandlerManager_{textCtrl_}
{
Update(TMyOptionDialog::GetInstance());
evtHandlerManager_.Add(wxEVT_MY_SCHINTILLA_NOTIFY,&Impl::OnScintillaNotify,this);
evtHandlerManager_.Add(wxEVT_MY_FILE_CHANGE,&Impl::OnFileChange,this);
}
...
void Update(const TMyOptionDialog& subject) override
{
const auto& font=subject.GetTextCtrlFont();
textCtrl_->SendMsg(SCI_STYLESETFORE,STYLE_DEFAULT,subject.GetTextCtrlForegroundColor().GetRGB());
textCtrl_->SendMsg(SCI_STYLESETBACK,STYLE_DEFAULT,subject.GetTextCtrlBackgroundColor().GetRGB());
textCtrl_->SendMsg(SCI_STYLESETFONT,STYLE_DEFAULT,(LPARAM)(const char*)font.GetFaceName().utf8_str());
textCtrl_->SendMsg(SCI_STYLESETWEIGHT,STYLE_DEFAULT
,font.GetWeight()==wxFONTWEIGHT_NORMAL?SC_WEIGHT_NORMAL
:font.GetWeight()==wxFONTWEIGHT_LIGHT?SC_WEIGHT_SEMIBOLD:SC_WEIGHT_BOLD);
textCtrl_->SendMsg(SCI_STYLESETITALIC,STYLE_DEFAULT,font.GetStyle()==wxFONTSTYLE_ITALIC);
textCtrl_->SendMsg(SCI_STYLESETSIZE,STYLE_DEFAULT,font.GetPointSize());
codingAssistance_.SetStyles();
textCtrl_->SendMsg(SCI_SETMARGINWIDTHN,0
,textCtrl_->SendMsg(SCI_TEXTWIDTH,STYLE_LINENUMBER,(LPARAM)"_999999"));
}
...
};
...