Notepad Style System Notes - wnewbery/npp-languages GitHub Wiki

Notes on how Notepad++ styles get from the XML config files, to the Scintilla control

Limitations for plugin lexers

Based on observations from the code explain below:

  • An external lexer has no control over how Notepad++ sets up the editor control.
  • Notepad++ enforces a limit on the number of extra languages / style sets
  • Each language can only have 30 of its own styles, but the builtin languages that combine several sets can exceed this.

Notepad++ Implementation Details

Most of the relevant files have an almost complete lack of comments, so this is some basic info for the relevant ones starting with where Notepad++ sets the editor controls language/lexer.

LexerStylerArray

Stores all the language styles for Notepad++. There is an upper limit of MAX_LEXER_STYLE (80), after which loading styles will fail.

Styles are added to the array by LexerStylerArray::addLexerStyler, which is only called by NppParameters::feedStylerArray(TiXmlNode *node), which loads the styles from XML.

LexerStyler

LexerStyler : public StyleArray

Contains the list of styles for a single language/lexer. This is a fixed size array of SCE_STYLE_ARRAY_SIZE (30), limiting any single language to at most that many styles of its own.

ScintillaEditView

This Notepad++ class provides a lot of the functionality on stop of the Scintilla editor control, such as determining the final styles. It then eventually sends window messages to the control to configure it via _pScintillaFunc which stores a window procedure function and bypass the normal Win32 PostMessage, SendMessage, etc. API layers. This caching happens immediately after the creation of the control in ScintillaEditView::init.

void ScintillaEditView::defineDocType(LangType typeDoc)

This sets up the Scintilla control for a language, defined using an enumeration. External plugin languages get given ID's above LC_EXTERNAL, but all builtin languages have their own ID.

  1. If there is a default style set (STYLE_DEFAULT, 32), passes it to setStyle after forcing COLORSTYLE_ALL.
  2. Sends the SCI_STYLECLEARALL message.
    Misleadingly, especially given the order, this actually clears every style except 32 by setting them to the current style 32.
  3. Sets some other global styles such as indicators and highlights (the user can configure most of these in the "Global Styles" language, rather than per language).
  4. Does a special, non-english document SCI_SETCODEPAGE if isCJK (codepage left untouched otherwise), with either _codepage, or for css, caml, asm or matlab (hardcoded list), CP_ACP.
  5. Shows the margin for all languages except Ascii, batch, text, makefile, asm, haskell, propers, smalltalk, kix and ada (hard coded list).
  6. Sets the lexer, styles and keywords for each builtin language using a hard coded switch statement and functions. For example HTML, XML, PHP, ASP, and JSP all use setXmlLexer. The user defined language uses setUserLexer, and plugin languages must use setExternalLexer (hardcoded, no plugin API).
For `L_TEXT` or unknown languages there is a strange and undocumented `execute(SCI_SETLEXER, (_codepage == CP_CHINESE_TRADITIONAL)?SCLEX_MAKEFILE:SCLEX_NULL);`.

void ScintillaEditView::setXmlLexer(LangType type)

Sets the styles for L_XML via a normal makeStyle(L_XML), and equally for L_HTML, L_PHP, L_ASP and LJSP by makeStyle(L_HTML), setEmbeddedJSLexer(), setEmbeddedPhpLexer() and setEmbeddedAspLexer(). Then sets from properties:

execute(SCI_SETPROPERTY, reinterpret_cast<WPARAM>("fold"), reinterpret_cast<LPARAM>("1"));
execute(SCI_SETPROPERTY, reinterpret_cast<WPARAM>("fold.compact"), reinterpret_cast<LPARAM>("0"));
execute(SCI_SETPROPERTY, reinterpret_cast<WPARAM>("fold.html"), reinterpret_cast<LPARAM>("1"));
// This allow to fold comment strem in php/javascript code
execute(SCI_SETPROPERTY, reinterpret_cast<WPARAM>("fold.hypertext.comment"), reinterpret_cast<LPARAM>("1"));

setEmbeddedJSLexer

Adds L_JS styles and keywords in addition to the others and sends some special messages:

execute(SCI_STYLESETEOLFILLED, SCE_HJ_DEFAULT, true);
execute(SCI_STYLESETEOLFILLED, SCE_HJ_COMMENT, true);
execute(SCI_STYLESETEOLFILLED, SCE_HJ_COMMENTDOC, true);

setEmbeddedPhpLexer

Adds L_PHP styles, keywords, and sends some special messages:

execute(SCI_STYLESETEOLFILLED, SCE_HPHP_DEFAULT, true);
execute(SCI_STYLESETEOLFILLED, SCE_HPHP_COMMENT, true);

setEmbeddedAspLexer

Adds L_ASP styles, keywords, and sends a special message:

execute(SCI_STYLESETEOLFILLED, SCE_HBA_DEFAULT, true);

void ScintillaEditView::setExternalLexer(LangType typeDoc)

This is the setting for any plugin-defined languages.

It just sets the styles and keywords defined directly by that language. Unlike the custom built in languages, there is no functionality to set the additional styles, or to set any of the additional configurations.

This function directly invokes SCI_SETLEXERLANGUAGE before setting any styles though (as a directly invoked message, it is likely not practical to intercept this). Scintilla will create the instance of a custom lexer at this point if its not the current lexer, which does give external Notepad++ lexers some opportunity to make customisations, as long as any following setStyle or SCI_SEETKEYWORDS would not undo them.

void ScintillaEditView::makeStyle(LangType language, const TCHAR **keywordArray)

Sets all the styles for built in languages.

The language is converted from the enum to its name by static LanguageName ScintillaEditView::langNames[L_EXTERNAL+1].

This name then gets the LexerStyler from LexerStylerArray, and sets each of its styles using setStyle.

struct LanguageName {
	const TCHAR * lexerName; //Lowercase name such as "objc" or "javascript" used in the XML etc.
	const TCHAR * shortName; //English full name displayed in menus etc. e.g. "Objective-C", "JavaScript"
	const TCHAR * longName;  //Long name, etc. "Hyper Text Markup Language file"
	LangType LangID;         //Unique enumeration value for builtin languages
	int lexerID;             //The Lexer defined by Scintilla to use. Note that many Notepad++ languages use some lexers. E.g. many of the C-style ones such a C++, C# and Java, use `SCLEX_CPP`. The differences are in the keywords.
};

ScintillaEditView::setStyle(Style styleToSet)

Takes a Notepad++ Style object (note, defined in Parameters.h) and modifies it with any of the global override styles (the one named "Global override" in the "Global Styles" language).

It then calls setSpecialStyle with the modified style object.

ScintillaEditView::setSpecialStyle(const Style & styleToSet)

Takes a Notepad++ Style object (note, defined in Parameters.h) and adds it to the control via a series of direct messages.

The Scintilla editor control

The array of styles for the exit control is stored by std::vector<Style> ViewStyle::styles. This is a mostly public struct, where void ViewStyle::EnsureStyle(size_t index) grows it as needed.

Editor::StyleSetMessage actually sets the styles uisng a window message, with wParam being the styleId and lParam the style value.

⚠️ **GitHub.com Fallback** ⚠️