Scintilla Usage Notes

| Scintilla Usage Notes

## Implementing Auto-Indent

The key idea is to use the SCN_CHARADDED notification to add indentation after a newline.

The lParam on the notification is a pointer to a SCNotification structure whose ch member specifies the character added. If a newline was added, the previous line can be retrieved and the same indentation can be added to the new line.

Here is the relevant portion of code from SciTE: (SciTE.cxx SciTEWindow::CharAdded)

if (ch == '\r' || ch == '\n') {
char linebuf[1000];
int curLine = GetCurrentLineNumber();
int lineLength = SendEditor(SCI_LINELENGTH, curLine);
//Platform::DebugPrintf("[CR] %d len = %d\n", curLine, lineLength);
if (curLine > 0 && lineLength <= 2) {
int prevLineLength = SendEditor(SCI_LINELENGTH, curLine - 1);
if (prevLineLength < sizeof(linebuf)) {
WORD buflen = sizeof(linebuf);
memcpy(linebuf, &buflen, sizeof(buflen));
SendEditor(EM_GETLINE, curLine - 1,
reinterpret_cast<LPARAM>(static_cast<char *>(linebuf)));
linebuf[prevLineLength] = '\0';
for (int pos = 0; linebuf[pos]; pos++) {
if (linebuf[pos] != ' ' && linebuf[pos] != '\t')
linebuf[pos] = '\0';
}
SendEditor(EM_REPLACESEL, 0, reinterpret_cast<LPARAM>(static_cast<char *>(linebuf)));
}
}

Of course, fancier handling could be implemented. For example, if the previous line was the start of a control construct, the next line could be automatically indented one tab further. (Assuming that is your indenting style.)

## Implementing Syntax Styling

Syntax styling is handled by the SCN_STYLENEEDED notification. Scintilla keeps track of the end of the styled text - this is retrieved with SCI_GETENDSTYLED. In response to the SCN_STYLENEEDED notification, you should apply styles to the text from ENDSTYLED to the position specified by the notification.

Here is the relevant portion of code from SciTE: (SciTE.cxx)

void SciTEWindow::Notify(SCNotification *notification) {
switch (notification->nmhdr.code) {
case SCN_STYLENEEDED: {
if (notification->nmhdr.idFrom == IDM_SRCWIN) {
int endStyled = SendEditor(SCI_GETENDSTYLED);
int lineEndStyled = SendEditor(EM_LINEFROMCHAR, endStyled);
endStyled = SendEditor(EM_LINEINDEX, lineEndStyled);
Colourise(endStyled, notification->position);

Colourize(start, end) retrieves the specified range of text and then calls ColourizeDoc in keywords.cxx. It starts the process by calling:

SendMessage(hwnd, SCI_STARTSTYLING, startPos, 31);

and then for each token of the text, calling:

SendMessage(hwnd, SCI_SETSTYLING, length, style);

where style is a number from 0 to 31 whose appearance has been defined using the SCI_STYLESET... messages.

## Implementing Calltips

Again, the SCN_CHARADDED notification is used to catch when an opening parenthesis is added. The preceding word can then be retrieved from the current line:

char linebuf[1000];
int current = SendEditor(SCI_GETCURLINE, sizeof(linebuf),
reinterpret_cast<LPARAM>(static_cast<char *>(linebuf)));
int pos = SendEditor(SCI_GETCURRENTPOS);
int startword = current - 1;
while (startword > 0 && isalpha(linebuf[startword - 1]))
startword--;
linebuf[current - 1] = '\0';
char* word = linebuf + startword;

Then if a calltip is available it can be displayed. The calltip appears immediately below the position specified. The calltip can be multiple lines separated by newlines (\n).

pos = SendMessage(hwnd, SCI_GETCURRENTPOS, 0, 0);
SendMessageText(hwnd, SCI_CALLTIPSHOW, pos - wordLen - 1, calltip);

The calltip can be removed when a closing parenthesis is entered:

if (SendMessage(hwnd, SCI_CALLTIPACTIVE, 0, 0))
SendMessage(hwnd, SCI_CALLTIPCANCEL, 0, 0);

Obviously, it is up the application to look after supplying the appropriate calltip text.

SciTE goes one step further, counting the commas between arguments and highlighting the corresponding part of the calltip. This code is in ContinueCallTip.

Page contributed by Andrew McKinlay.