diff -r 928661c9cdce include/ILexer.h
--- a/include/ILexer.h	Sun Jul 29 17:05:26 2012 +1000
+++ b/include/ILexer.h	Thu Aug 02 23:20:47 2012 +1000
@@ -45,7 +45,7 @@
 	virtual int SCI_METHOD GetLineIndentation(int line) = 0;
 };
 
-enum { lvOriginal=0 };
+enum { lvOriginal=0, lvSubStyles=1 };
 
 class ILexer {
 public:
@@ -62,6 +62,17 @@
 	virtual void * SCI_METHOD PrivateCall(int operation, void *pointer) = 0;
 };
 
+class ILexerWithSubStyles : public ILexer {
+public:
+	virtual int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) = 0;
+	virtual int SCI_METHOD SubStylesStart(int styleBase) = 0;
+	virtual int SCI_METHOD SubStylesLength(int styleBase) = 0;
+	virtual void SCI_METHOD FreeSubStyles() = 0;
+	virtual void SCI_METHOD SetIdentifiers(int style, const char *identifiers) = 0;
+	virtual int SCI_METHOD DistanceToSecondaryStyles() = 0;
+	virtual const char * SCI_METHOD GetSubStyleBases() = 0;
+};
+
 class ILoader {
 public:
 	virtual int SCI_METHOD Release() = 0;
diff -r 928661c9cdce include/Scintilla.h
--- a/include/Scintilla.h	Sun Jul 29 17:05:26 2012 +1000
+++ b/include/Scintilla.h	Thu Aug 02 23:20:47 2012 +1000
@@ -873,6 +873,13 @@
 #define SCI_PROPERTYTYPE 4015
 #define SCI_DESCRIBEPROPERTY 4016
 #define SCI_DESCRIBEKEYWORDSETS 4017
+#define SCI_ALLOCATESUBSTYLES 4020
+#define SCI_GETSUBSTYLESSTART 4021
+#define SCI_GETSUBSTYLESLENGTH 4022
+#define SCI_FREESUBSTYLES 4023
+#define SCI_SETIDENTIFIERS 4024
+#define SCI_DISTANCETOSECONDARYSTYLES 4025
+#define SCI_GETSUBSTYLEBASES 4026
 #define SC_MOD_INSERTTEXT 0x1
 #define SC_MOD_DELETETEXT 0x2
 #define SC_MOD_CHANGESTYLE 0x4
diff -r 928661c9cdce include/Scintilla.iface
--- a/include/Scintilla.iface	Sun Jul 29 17:05:26 2012 +1000
+++ b/include/Scintilla.iface	Thu Aug 02 23:20:47 2012 +1000
@@ -2327,6 +2327,28 @@
 # Retrieve a '\n' separated list of descriptions of the keyword sets understood by the current lexer.
 fun int DescribeKeyWordSets=4017(, stringresult descriptions)
 
+# Allocate a set of sub styles for a particular base style, returning start of range
+fun int AllocateSubStyles=4020(int styleBase, int numberStyles)
+
+# The starting style number for the sub styles associated with a base style
+get int GetSubStylesStart=4021(int styleBase,)
+
+# The number of sub styles associated with a base style
+get int GetSubStylesLength=4022(int styleBase,)
+
+# Free allocated sub styles
+fun void FreeSubStyles=4023(,)
+
+# Set the identifiers that are shown in a particular style
+set void SetIdentifiers=4024(int style, string identifiers)
+
+# Where styles are duplicated by a feature such as active/inactive code
+# return the distance between the two types.
+get int DistanceToSecondaryStyles=4025(,)
+
+# Get the set of base styles that can be extended with sub styles
+get int GetSubStyleBases=4026(, stringresult styles)
+
 # Notifications
 # Type of modification and the action which caused the modification.
 # These are defined as a bit mask to make it easy to specify which notifications are wanted.
diff -r 928661c9cdce lexers/LexCPP.cxx
--- a/lexers/LexCPP.cxx	Sun Jul 29 17:05:26 2012 +1000
+++ b/lexers/LexCPP.cxx	Thu Aug 02 23:20:47 2012 +1000
@@ -30,6 +30,7 @@
 #include "LexerModule.h"
 #include "OptionSet.h"
 #include "SparseState.h"
+#include "SubStyles.h"
 
 #ifdef SCI_NAMESPACE
 using namespace Scintilla;
@@ -307,7 +308,9 @@
 	}
 };
 
-class LexerCPP : public ILexer {
+static const char styleSubable[] = {SCE_C_IDENTIFIER, SCE_C_COMMENTDOCKEYWORD, 0};
+
+class LexerCPP : public ILexerWithSubStyles {
 	bool caseSensitive;
 	CharacterSet setWord;
 	CharacterSet setNegationOp;
@@ -326,6 +329,8 @@
 	OptionSetCPP osCPP;
 	SparseState<std::string> rawStringTerminators;
 	enum { activeFlag = 0x40 };
+	enum { ssIdentifier, ssDocKeyword };
+	SubStyles subStyles;
 public:
 	LexerCPP(bool caseSensitive_) :
 		caseSensitive(caseSensitive_),
@@ -333,7 +338,8 @@
 		setNegationOp(CharacterSet::setNone, "!"),
 		setArithmethicOp(CharacterSet::setNone, "+-/*%"),
 		setRelOp(CharacterSet::setNone, "=!<>"),
-		setLogicalOp(CharacterSet::setNone, "|&") {
+		setLogicalOp(CharacterSet::setNone, "|&"),
+		subStyles(styleSubable, 0x80, 0x40, activeFlag) {
 	}
 	virtual ~LexerCPP() {
 	}
@@ -341,7 +347,7 @@
 		delete this;
 	}
 	int SCI_METHOD Version() const {
-		return lvOriginal;
+		return lvSubStyles;
 	}
 	const char * SCI_METHOD PropertyNames() {
 		return osCPP.PropertyNames();
@@ -364,6 +370,28 @@
 		return 0;
 	}
 
+	int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) {
+		return subStyles.Allocate(styleBase, numberStyles);
+	}
+	int SCI_METHOD SubStylesStart(int styleBase) {
+		return subStyles.Start(styleBase);
+	}
+	int SCI_METHOD SubStylesLength(int styleBase) {
+		return subStyles.Length(styleBase);
+	}
+	void SCI_METHOD FreeSubStyles() {
+		subStyles.Free();
+	}
+	void SCI_METHOD SetIdentifiers(int style, const char *identifiers) {
+		subStyles.SetIdentifiers(style, identifiers);
+	}
+	int SCI_METHOD DistanceToSecondaryStyles() {
+		return activeFlag;
+	}
+	const char * SCI_METHOD GetSubStyleBases() {
+		return styleSubable;
+	}
+
 	static ILexer *LexerFactoryCPP() {
 		return new LexerCPP(true);
 	}
@@ -496,7 +524,7 @@
 		}
 	}
 
-	StyleContext sc(startPos, length, initStyle, styler, 0x7f);
+	StyleContext sc(startPos, length, initStyle, styler, static_cast<char>(0xff));
 	LinePPState preproc = vlls.ForLine(lineCurrent);
 
 	bool definitionsChanged = false;
@@ -522,6 +550,9 @@
 
 	int activitySet = preproc.IsInactive() ? activeFlag : 0;
 
+	const WordClassifier &classifierIdentifiers = subStyles.Classifier(SCE_C_IDENTIFIER);
+	const WordClassifier &classifierDocKeyWords = subStyles.Classifier(SCE_C_COMMENTDOCKEYWORD);
+
 	for (; sc.More();) {
 
 		if (sc.atLineStart) {
@@ -597,6 +628,11 @@
 						sc.ChangeState(SCE_C_WORD2|activitySet);
 					} else if (keywords4.InList(s)) {
 						sc.ChangeState(SCE_C_GLOBALCLASS|activitySet);
+					} else {
+						int subStyle = classifierIdentifiers.ValueFor(s);
+						if (subStyle >= 0) {
+							sc.ChangeState(subStyle|activitySet);
+						}
 					}
 					const bool literalString = sc.ch == '\"';
 					if (literalString || sc.ch == '\'') {
@@ -685,8 +721,15 @@
 					} else {
 						sc.GetCurrentLowered(s, sizeof(s));
 					}
-					if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {
+					if (!IsASpace(sc.ch)) {
 						sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR|activitySet);
+					} else if (!keywords3.InList(s + 1)) {
+						int subStyleCDKW = classifierDocKeyWords.ValueFor(s+1);
+						if (subStyleCDKW >= 0) {
+							sc.ChangeState(subStyleCDKW|activitySet);
+						} else {
+							sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR|activitySet);
+						}
 					}
 					sc.SetState(styleBeforeDCKeyword|activitySet);
 				}
diff -r 928661c9cdce lexlib/SubStyles.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lexlib/SubStyles.h	Thu Aug 02 23:20:47 2012 +1000
@@ -0,0 +1,158 @@
+// Scintilla source code edit control
+/** @file SubStyles.h
+ ** Manage substyles for a lexer.
+ **/
+// Copyright 2012 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef SUBSTYLES_H
+#define SUBSTYLES_H
+
+#ifdef SCI_NAMESPACE
+namespace Scintilla {
+#endif
+
+class WordClassifier {
+	int firstStyle;
+	int lenStyles;
+	std::map<std::string, int> wordToStyle;
+
+public:
+
+	WordClassifier() : firstStyle(0), lenStyles(0) {
+	}
+
+	void Allocate(int firstStyle_, int lenStyles_) {
+		firstStyle = firstStyle_;
+		lenStyles = lenStyles_;
+		wordToStyle.clear();
+	}
+
+	int Start() const {
+		return firstStyle;
+	}
+
+	int Length() const {
+		return lenStyles;
+	}
+
+	void Clear() {
+		firstStyle = 0;
+		lenStyles = 0;
+		wordToStyle.clear();
+	}
+
+	int ValueFor(const std::string &s) const {
+		std::map<std::string, int>::const_iterator it = wordToStyle.find(s);
+		if (it != wordToStyle.end())
+			return it->second;
+		else
+			return -1;
+	}
+
+	bool IncludesStyle(int style) const {
+		return (style >= firstStyle) && (style < (firstStyle + lenStyles));
+	}
+
+	void SetIdentifiers(int style, const char *identifiers) {
+		while (*identifiers) {
+			const char *cpSpace = identifiers;
+			while (*cpSpace && *cpSpace != ' ')
+				cpSpace++;
+			std::string word(identifiers, cpSpace - identifiers);
+			wordToStyle[word] = style;
+			identifiers = cpSpace;
+			if (*identifiers)
+				identifiers++;
+		}
+	}
+};
+
+class SubStyles {
+	int classifications;
+	const char *baseStyles;
+	int styleFirst;
+	int stylesAvailable;
+	int secondaryDistance;
+	int allocated;
+	std::vector<WordClassifier> classifiers;
+
+	int BlockFromBaseStyle(int baseStyle) const {
+		for (int b=0; b < classifications; b++) {
+			if (baseStyle == baseStyles[b])
+				return b;
+		}
+		return -1;
+	}
+
+	int BlockFromStyle(int style) const {
+		int b = 0;
+		for (std::vector<WordClassifier>::const_iterator it=classifiers.begin(); it != classifiers.end(); ++it) {
+			if (it->IncludesStyle(style))
+				return b;
+			b++;
+		}
+		return -1;
+	}
+
+public:
+
+	SubStyles(const char *baseStyles_, int styleFirst_, int stylesAvailable_, int secondaryDistance_) :
+		classifications(0),
+		baseStyles(baseStyles_),
+		styleFirst(styleFirst_),
+		stylesAvailable(stylesAvailable_),
+		secondaryDistance(secondaryDistance_),
+		allocated(0) {
+		while (baseStyles[classifications]) {
+			classifications++;
+			classifiers.push_back(WordClassifier());
+		}
+	}
+
+	int Allocate(int styleBase, int numberStyles) {
+		int block = BlockFromBaseStyle(styleBase);
+		if (block >= 0) {
+			if ((allocated + numberStyles) > stylesAvailable)
+				return -1;
+			int startBlock = styleFirst + allocated;
+			allocated += numberStyles;
+			classifiers[block].Allocate(startBlock, numberStyles);
+			return startBlock;
+		} else {
+			return -1;
+		}
+	}
+
+	int Start(int styleBase) {
+		int block = BlockFromBaseStyle(styleBase);
+		return (block >= 0) ? classifiers[block].Start() : -1;
+	}
+
+	int Length(int styleBase) {
+		int block = BlockFromBaseStyle(styleBase);
+		return (block >= 0) ? classifiers[block].Length() : 0;
+	}
+
+	void SetIdentifiers(int style, const char *identifiers) {
+		int block = BlockFromStyle(style);
+		if (block >= 0)
+			classifiers[block].SetIdentifiers(style, identifiers);
+	}
+
+	void Free() {
+		allocated = 0;
+		for (std::vector<WordClassifier>::iterator it=classifiers.begin(); it != classifiers.end(); ++it)
+			it->Clear();
+	}
+
+	const WordClassifier &Classifier(int baseStyle) const {
+		return classifiers[BlockFromBaseStyle(baseStyle)];
+	}
+};
+
+#ifdef SCI_NAMESPACE
+}
+#endif
+
+#endif
diff -r 928661c9cdce src/ScintillaBase.cxx
--- a/src/ScintillaBase.cxx	Sun Jul 29 17:05:26 2012 +1000
+++ b/src/ScintillaBase.cxx	Thu Aug 02 23:20:47 2012 +1000
@@ -472,6 +472,7 @@
 	const LexerModule *lexCurrent;
 	void SetLexerModule(const LexerModule *lex);
 	PropSetSimple props;
+	int interfaceVersion;
 public:
 	int lexLanguage;
 
@@ -491,6 +492,14 @@
 	const char *PropGet(const char *key) const;
 	int PropGetInt(const char *key, int defaultValue=0) const;
 	int PropGetExpanded(const char *key, char *result) const;
+
+	int AllocateSubStyles(int styleBase, int numberStyles);
+	int SubStylesStart(int styleBase);
+	int SubStylesLength(int styleBase);
+	void FreeSubStyles();
+	void SetIdentifiers(int style, const char *identifiers);
+	int DistanceToSecondaryStyles();
+	const char *GetSubStyleBases();
 };
 
 #ifdef SCI_NAMESPACE
@@ -500,6 +509,7 @@
 LexState::LexState(Document *pdoc_) : LexInterface(pdoc_) {
 	lexCurrent = 0;
 	performingStyle = false;
+	interfaceVersion = lvOriginal;
 	lexLanguage = SCLEX_CONTAINER;
 }
 
@@ -523,9 +533,12 @@
 			instance->Release();
 			instance = 0;
 		}
+		interfaceVersion = lvOriginal;
 		lexCurrent = lex;
-		if (lexCurrent)
+		if (lexCurrent) {
 			instance = lexCurrent->Create();
+			interfaceVersion = instance->Version();
+		}
 		pdoc->LexerChanged();
 	}
 }
@@ -630,6 +643,53 @@
 	return props.GetExpanded(key, result);
 }
 
+int LexState::AllocateSubStyles(int styleBase, int numberStyles) {
+	if (instance && (interfaceVersion >= lvSubStyles)) {
+		return static_cast<ILexerWithSubStyles *>(instance)->AllocateSubStyles(styleBase, numberStyles);
+	}
+	return -1;
+}
+
+int LexState::SubStylesStart(int styleBase) {
+	if (instance && (interfaceVersion >= lvSubStyles)) {
+		return static_cast<ILexerWithSubStyles *>(instance)->SubStylesStart(styleBase);
+	}
+	return -1;
+}
+
+int LexState::SubStylesLength(int styleBase) {
+	if (instance && (interfaceVersion >= lvSubStyles)) {
+		return static_cast<ILexerWithSubStyles *>(instance)->SubStylesLength(styleBase);
+	}
+	return 0;
+}
+
+void LexState::FreeSubStyles() {
+	if (instance && (interfaceVersion >= lvSubStyles)) {
+		static_cast<ILexerWithSubStyles *>(instance)->FreeSubStyles();
+	}
+}
+
+void LexState::SetIdentifiers(int style, const char *identifiers) {
+	if (instance && (interfaceVersion >= lvSubStyles)) {
+		static_cast<ILexerWithSubStyles *>(instance)->SetIdentifiers(style, identifiers);
+	}
+}
+
+int LexState::DistanceToSecondaryStyles() {
+	if (instance && (interfaceVersion >= lvSubStyles)) {
+		return static_cast<ILexerWithSubStyles *>(instance)->DistanceToSecondaryStyles();
+	}
+	return 0;
+}
+
+const char *LexState::GetSubStyleBases() {
+	if (instance && (interfaceVersion >= lvSubStyles)) {
+		return static_cast<ILexerWithSubStyles *>(instance)->GetSubStyleBases();
+	}
+	return "";
+}
+
 #endif
 
 void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded) {
@@ -890,6 +950,28 @@
 	case SCI_DESCRIBEKEYWORDSETS:
 		return StringResult(lParam, DocumentLexState()->DescribeWordListSets());
 
+	case SCI_ALLOCATESUBSTYLES:
+		return DocumentLexState()->AllocateSubStyles(wParam, lParam);
+
+	case SCI_GETSUBSTYLESSTART:
+		return DocumentLexState()->SubStylesStart(wParam);
+
+	case SCI_GETSUBSTYLESLENGTH:
+		return DocumentLexState()->SubStylesLength(wParam);
+
+	case SCI_FREESUBSTYLES:
+		DocumentLexState()->FreeSubStyles();
+		break;
+
+	case SCI_SETIDENTIFIERS:
+		DocumentLexState()->SetIdentifiers(wParam, reinterpret_cast<const char *>(lParam));
+		break;
+
+	case SCI_DISTANCETOSECONDARYSTYLES:
+		return DocumentLexState()->DistanceToSecondaryStyles();
+
+	case SCI_GETSUBSTYLEBASES:
+		return StringResult(lParam, DocumentLexState()->GetSubStyleBases());
 #endif
 
 	default:
