Scintilla icon Scintilla の使い方に関する覚え書きScintilla Usage Notes

自動字下げの実装 Implementing Auto-Indent

改行の後ろで字下げを追加するポイントとなる考え方は SCN_CHARADDED 通知の利用です。

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

通知メッセージの lParam は SCNotification 構造体へのポインタで、そのメンバ ch は追加された文字を表します。改行が追加されたら、前の行を獲得することができますので同じだけの字下げを新しい行に加えればよいのです。

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.

SciTE のコードから適切な形のコードを見てみましょう。(SciTE.cxx 内の SciTEWindow::CharAdded)

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

文法解析によるスタイルは SCN_STYLENEEDED 通知メッセージにより処理されます。Scintilla はスタイルをつけられた文字列の終端を保持しており、これは SCI_GETENDSTYLED で取得することができます。SCN_STYLENEEDED への応答処理の中で ENDSTYLED から通知メッセージに示された位置までのスタイルをテキストに与えることになります。

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.

再び SciTE のコードから関連するものをとりあげます。(SciTE.cxx)

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( 開始位置, 終了位置 ) は与えられた区間のテキストを取得し keywords.cxx にある ColourizeDoc を呼び出します。次の呼び出しによって処理が開始されます。

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);

style は 0 〜 31 で、 SCI_STYLESET ... メッセージで定義された外見設定番号をさします。

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

コールチップの実装 Implementing Calltips

こちらも SCN_CHARADDED 通知メッセージを使って開き括弧が追加されたことを検出します。その括弧の前の単語は現在行から取得できます。

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;

これでコールチップか使えるときはそれを表示することができます。コールチップは指定位置のすぐ下に出てきます。\n で改行を指定するとコールチップを複数行にすることもできます。

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 の実装はもうすこし複雑で、引数間のコンマを数えてコールチップの関連部分を強調表示します。ContinueCallTip の中にそのコードがあります。

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

このページは Andrew McKinlay さんにより寄贈されました。

Page contributed by Andrew McKinlay.