How do I apply style information to text algorithmicly - like MSVC does for C++ mode - rather than via user-applied style run setting calls?
The basic idea is that we will be - to some extent - mimicking the implementation of StandardStyledTextImager. Recall that the class StyledTextImager is what provides the basic infrastructure for displaying styled text. It introduces the special marker class StyledTextImager::StyleMarker - which we can subclass todo our own kind of special display of text. And StandardStyledTextImager simply leverages off this generic implementation, and maintains a database of non-overlapping StyleMarkers according to the usual editing, and style application conventions most standard text editing packages support (eg. apply style to region of text etc).
Where we will diverge, is that we won't generate our style markers from external function calls or UI commands.
Instead, we will programmaticly generate the style markers ourselves based on a simple
lexical analysis of the text (SyntaxAnalyzer).
The sample implementation contained herein is very basic, but works enuf to show you how to accomplish this sort of thing in Led.
There are two main classes to consider:
SyntaxColoringMarkerOwner and
SyntaxAnalyzer: both abstract classes.
The SyntaxColoringMarkerOwner class manages a set of Marker objects which mark regions of text with particular
styles, and manages detecting changes in the text and reanalyzing the text to apply styles to it.
Typically - the actual function will be achieved with one of the concrete implementation subclasses
SimpleSyntaxColoringMarkerOwner or
WindowedSyntaxColoringMarkerOwner.
It probably matters little which of these you choose. SimpleSyntaxColoringMarkerOwner maybe a little simpler to use.
For larger documents WindowedSyntaxColoringMarkerOwner will perform better. But this choice is totally separated from the actual
syntactic analysis you will be doing.
That function of syntactically analyzing the text is managed by your subclass of
SyntaxAnalyzer.
Most likely, and most simply, you will be using a subclass (or instance of)
TableDrivenKeywordSyntaxAnalyzer
to handle that.
The syntax coloring functionality has been integrated into the LedLineItMFC sample application
under conditional compilation.
You can turn on or off the conditional compilation flag qSupportSyntaxColoring defined
in LedLineItConfig.h to see the functionality enabled or disabled.
You can also search for the use of this compilation flag throughout the LedLineItMFC source code
to see exactly what (little) it does, and what code you need to lift into your application
to make this work for you. A brief tour of that code may make it easier to understand, and easier to modify/adopt
for your uses.
#include "SyntaxColoring.h"LedLineItMFCBaseClass to incorporate a mixin of StyledTextImager
typedef Led_MFC_X<SimpleTextInteractor> LedLineItMFCBaseClass;
to:
struct LedLineItMFCBaseClass : public Led_MFC_X<SimpleTextInteractor>, public StyledTextImager {
protected:
override Led_Distance MeasureSegmentHeight (size_t from, size_t to) const
{
return SimpleTextInteractor::MeasureSegmentHeight (from, to);
}
override Led_Distance MeasureSegmentBaseLine (size_t from, size_t to) const
{
return SimpleTextInteractor::MeasureSegmentBaseLine (from, to);
}
};
This is done so that the StyledTextImager functionality is mixed into your TextImager subclass.
protected:
SyntaxColoringMarkerOwner* fSyntaxColoringMarkerOwner;
fSyntaxColoringMarkerOwner to NULL in the CTORfSyntaxColoringMarkerOwner is NULL in the DTOR at the end of your view class (after SetTextStore (NULL) call)
void LedLineItView::HookLosingTextStore ()
{
delete fSyntaxColoringMarkerOwner;
fSyntaxColoringMarkerOwner = NULL;
inherited::HookLosingTextStore ();
}
void LedLineItView::HookGainedNewTextStore ()
{
Led_Assert (fSyntaxColoringMarkerOwner == NULL);
inherited::HookGainedNewTextStore ();
ResetSyntaxColoringTable ();
}
void LedLineItView::ResetSyntaxColoringTable ()
{
if (PeekAtTextStore () != NULL) {
// static const TrivialRGBSyntaxAnalyzer kAnalyzer;
static const TableDrivenKeywordSyntaxAnalyzer kCPlusPlusAnalyzer (TableDrivenKeywordSyntaxAnalyzer::kCPlusPlusKeywords);
static const TableDrivenKeywordSyntaxAnalyzer kVisualBasicAnalyzer (TableDrivenKeywordSyntaxAnalyzer::kVisualBasicKeywords);
const SyntaxAnalyzer* analyzer = NULL;
switch (Options ().GetSyntaxColoringOption ()) {
case Options::eSyntaxColoringNone: /* nothing - analyzer already NULL*/ break;
case Options::eSyntaxColoringCPlusPlus: analyzer = &kCPlusPlusAnalyzer; break;
case Options::eSyntaxColoringVB: analyzer = &kVisualBasicAnalyzer; break;
}
delete fSyntaxColoringMarkerOwner;
fSyntaxColoringMarkerOwner = NULL;
if (analyzer != NULL) {
#if qSupportOnlyMarkersWhichOverlapVisibleRegion
fSyntaxColoringMarkerOwner = new WindowedSyntaxColoringMarkerOwner (*this, GetTextStore (), *analyzer);
#else
fSyntaxColoringMarkerOwner = new SimpleSyntaxColoringMarkerOwner (*this, GetTextStore (), *analyzer);
#endif
fSyntaxColoringMarkerOwner->RecheckAll ();
}
}
}
SyntaxColoringMarkerOwner you will use
(WindowedSyntaxColoringMarkerOwner or SimpleSyntaxColoringMarkerOwner), but
also what SyntaxAnalyzer you will use.
TabletChangedMetrics
void LedLineItView::TabletChangedMetrics ()
{
...
--KEEP WHAT WAS THERE BFORE AND ADD THIS--
if (fSyntaxColoringMarkerOwner != NULL) {
fSyntaxColoringMarkerOwner->RecheckAll ();
}
}
to make sure any markers whose font metrics may somehow depend on the tablet font metrics get updated.
qSupportOnlyMarkersWhichOverlapVisibleRegion - you must override
UpdateScrollBars.
void LedLineItView::UpdateScrollBars ()
{
...
--KEEP WHAT WAS THERE BFORE AND ADD THIS--
#if qSupportOnlyMarkersWhichOverlapVisibleRegion
Led_AssertNotNil (fSyntaxColoringMarkerOwner);
fSyntaxColoringMarkerOwner->RecheckScrolling ();
#endif
}
To assure the newly scrolled-to region has its text scanned (analyzed) for syntax coloring markup.
It is very likely anyone using this functionality will want to provide support for a different set
of keywords, and perhaps more complex lexical analysis. The way to go about this is to
provide your own SyntaxAnalyzer instance. You might be able to do this by simply
defining a new table of keywords, and using the existing TableDrivenKeywordSyntaxAnalyzer
class. You may find you need to do this, and to subclass that class to slightly refine the behavior
for some lexical elements. Or you may find it most useful to write your own SyntaxAnalyzer
subclass from scratch. Either way - to use it - all you need todo is to plug
it into the fSyntaxColoringMarkerOwner instance you create in LedLineItView::ResetSyntaxColoringTable ()
by passing it as an argument to either SimpleSyntaxColoringMarkerOwner or WindowedSyntaxColoringMarkerOwner
(depending on which you've chosen to use).