Click here to Skip to main content
15,885,757 members
Articles / Desktop Programming / MFC
Article

Syntax colorizing with Speller engine using TOM and CSAPI

Rate me:
Please Sign up or sign in to vote.
4.89/5 (17 votes)
12 Nov 20044 min read 101.3K   3.1K   46   22
Syntax colorizing with Speller engine using TOM and CSAPI.

Screen shot

Introduction

The demo enhances a CRichEditCtrl that provides basic syntax highlighting for C/C++ or any other languages. The CRichEditCtrl also provides an interface that checks spelling using the CSAPI and gives a screen feedback by a coloring underline. This demo uses intensively the TOM interface. The check of spelling occurs only in the comments part of a source code or in strings. The control provides highlighting keywords not only in the source code but also for Doxygen in the comments.

Implementation

For a better implementation, the project has been organized into three classes. CSyntaxcolor manages comments, strings and keyword detection. CSpellingChecker manages the spelling of keywords and manages dictionaries. CSyntaxColorSpellChecker inherits from CSyntaxcolor and overloads the highlighting to provide a coloring underline feedback for the CRichEditCtrl.

Strategy

In order to avoid problems with the redraw of the control, I used Text Object Model (TOM) interface. This interface allows to manipulate runs of characters without sad effects on the screen, and gives more functions than the Win32 API. You can, for example, disable the undo during the modifications of colors. For the spelling, I used CSAPI. There is only some documentation about this API, but it eases considerably the implementation of a spell engine. It allows you to use many standardized dictionaries in your application. I am not sure if it is an official API from Microsoft.

Points of Interest

The main problem to handle syntax highlighting correctly is to manage the multi-line comments without re-computing each time colors of the complete file. The change of only one character could affect the entire file. Re-computing the complete file takes time and slows down the process of typing. To detect the modification of the multi-line comment structure, there is no clean solution. My solution has been to overload the notification EN_MSGFILTER of the CRichEditCtrl. When the user types a character able to change the state of the multi-line comments, I set a flag. And I do a fast analysis of the current line in order to know if the comment is opened or closed.

void CDemoEditorView::OnMsgfilterEdit(NMHDR* pNMHDR, LRESULT* pResult) 
{
  MSGFILTER *pMsgFilter = reinterpret_cast<MSGFILTER *>(pNMHDR);
  if(pMsgFilter->msg == WM_CHAR)
  {
    switch(pMsgFilter->wParam)
    {
      case '*':
      case '/':
        {
          m_bChangeAll = true;
          break;// '*' comment could affect all the file
        }
    }
    CHARRANGE cr;
    GetRichEditCtrl().GetSel(cr);
    if(cr.cpMax -cr.cpMin)// when we remove, it could remove '*/' or '/*'
    {
      m_bChangeAll = true;
    }
  }
  if(pMsgFilter->msg == WM_KEYDOWN)
  {
    switch(pMsgFilter->wParam)
    {
      case VK_BACK:
      case VK_DELETE:
      case VK_RETURN:
        m_bChangeAll = true;
        break;
    }
  }
  if(m_bChangeAll)
  {
    CHARRANGE cr;
    GetRichEditCtrl().GetSel(cr);
    m_nextedComments  = 
     m_colorizer.AnalyseMultiCommentLine(GetRichEditCtrl().LineFromChar(cr.cpMin));
  }
  *pResult = 0;
}

Using Colorizer

I consider the colorization of a text as a set of styles in a run of characters. A style includes all properties for a string (color, font, size, …). The ITextRange interface handles the concept of style through the functions GetSyle and SetStyle. Most of the time, this property is not used in a default implementation, but this LONG is stored in the run. I have stored in this value, the index of the highlight class. It is now very easy to quickly continue a multi line comment style when I evaluate only a part of the file. The style is stored directly in the run of characters. The main job of coloring is done in the function ColorizeRange(…). You can customize the keywords with the function AddKeyList(…). It will be easy to customize this class for HTML, XML, or other languages. You can customize the comments and string detection by changing CreateCommentsAndStringsTags(…). Also, you can change and add more highlight styles by changing CreateDefaultStyle().

void CSyntaxColor::CreateDefaultStyle()
{
    m_tStyle.SetSize(styleMax);
    // black for normal
    m_tStyle[normal]    = CKeyStyle(RGB(0,0,0));
    // blue for syntax
    m_tStyle[syntax]    = CKeyStyle(RGB(0,0,255));
    // Gray for directives
    m_tStyle[directive] = CKeyStyle(RGB(192,192,192));
    // blue for pragma
    m_tStyle[pragma]    = CKeyStyle(RGB(0,0,255));
    // green  multi comment green
    m_tStyle[mcomments] = CKeyStyle(RGB(0,200,0));
    // green  single comment green
    m_tStyle[scomments] = CKeyStyle(RGB(0,200,0));
    // blue string
    m_tStyle[sstring]   = CKeyStyle(RGB(0,0,255));
    // blue string
    m_tStyle[dstring]   = CKeyStyle(RGB(0,0,255));
    // red keyword comments and bold
    m_tStyle[highlightComments]= CKeyStyle(RGB(255,0,0),bold);
}

For each word in the NormalSlot or the CommentSlot, ColorizeRange calls OnColorKeyWord(int istart ,int iend ,LPCSTR pword, HighLightStyle style) to colorize a keyword.

Using the Speller

To implement the spelling checker in syntax colorizing, I have created a new class derived from CSyntaxcolor. The class includes an object CSpellingChecker and overload OnColorKeyWord(…). The new class continues the job of OnColorKeyWord done in CSyntaxcolor, and checks where the colorization occurs, checks the spelling, and sets the underline to signal a misspelled word.

bool CSyntaxColorSpellChecker::OnColorKeyWord(int istart, int iend, 
                          LPCSTR pword, HightLightStyle style)
{
    if(CSyntaxColor::OnColorKeyWord(istart,iend,pword,style)) return true;
    long newunderline = 0;
    if(style == scomments || style == mcomments || 
                             style == sstring || style == dstring )
    {
        newunderline = m_cSpeller.CheckWord(pword) ? 0: SPELL_UNDERLINE;
    }
    m_pRange->SetRange(istart,iend);
    ITextFont *pFont;
    m_pRange->GetFont(&pFont);
    pFont->SetUnderline(newunderline);
    pFont->Release();
    return false;
}

You can change the language of the speller by passing another LID to the function Initialize(lidFrench, lidDutch,…). Just make sure before that the dictionary and the spell engine is rightly installed in the registry. CSpellingChecker also provides some tools to manage dictionaries and words suggestion through a set of functions. Suggestion word comes not only from the main dictionary (.LEX) but also from your user dictionary.

bool CheckWord(LPCSTR pWord);
bool SuggestWords(LPCSTR pWord,int max, CStringArray & tList );
bool IgnoreAlways(LPCSTR pWord);
bool AddToUserDic(LPCSTR pWord);

It is possible to add more user dictionaries (read only) in the function InitEngine, if you modify the SIB structure for each call of SuggestWords and CheckWord. These extra dictionaries can be constructed automatically from your source code, in order to check the spelling of function names, variables, etc…

The Demo

The demo just implements the three classes in a text editor. I have added syntax highlighting and spell checking in comments to a basic application created by Visual C++. The editor is configured for C++ keywords highlight and Doxygen keywords in the comments.

History

  • 11/11/2004 - first version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionMy method start programm without spelling Pin
Konstantin T14-Nov-13 21:37
Konstantin T14-Nov-13 21:37 
1) Change method CSyntaxColorSpellChecker::Initialize
C++
<pre lang="cs">bool CSyntaxColorSpellChecker::Initialize(CRichEditCtrl *pCtrl)
{
    if( !m_cSpeller.Initialize(lidAmerican) )
    {
        MessageBox(NULL, "Speller not initialized!", "Error", MB_OK|MB_ICONERROR);
        //return false;
    }
    return CSyntaxColor::Initialize(pCtrl);
}

2) Add initialisation in class constructor CSpellingChecker.
C++
m_Handle = 0; 

GeneralDidnt get it running Pin
auge__22-Mar-10 5:17
auge__22-Mar-10 5:17 
GeneralThis Colorizer works very well Pin
rob_toutant24-Nov-09 21:32
rob_toutant24-Nov-09 21:32 
GeneraltomWave at Win98 problem Pin
Member 202956929-Sep-05 5:36
Member 202956929-Sep-05 5:36 
GeneralCan't find TOM.h Pin
solikang14-Jun-05 15:50
solikang14-Jun-05 15:50 
GeneralRe: Can't find TOM.h Pin
William Hennebois14-Jun-05 20:16
William Hennebois14-Jun-05 20:16 
GeneralRe: Can't find TOM.h Pin
solikang15-Jun-05 0:55
solikang15-Jun-05 0:55 
GeneralRe: Can't find TOM.h Pin
William Hennebois22-Jun-05 9:10
William Hennebois22-Jun-05 9:10 
GeneralRe: Can't find TOM.h Pin
William Hennebois22-Jun-05 9:11
William Hennebois22-Jun-05 9:11 
QuestionTOM interface is great but is incomplete? Pin
Alexandru Matei27-Jan-05 2:12
Alexandru Matei27-Jan-05 2:12 
QuestionHtml support? Pin
are_all_nicks_taken_or_what6-Dec-04 15:20
are_all_nicks_taken_or_what6-Dec-04 15:20 
GeneralUnhandled exception in DemoEditor.exe Pin
MayaRafi17-Nov-04 20:14
MayaRafi17-Nov-04 20:14 
GeneralRe: Unhandled exception in DemoEditor.exe Pin
Anonymous21-Nov-04 20:56
Anonymous21-Nov-04 20:56 
GeneralRe: Unhandled exception in DemoEditor.exe Pin
ak9937227-Feb-05 22:51
ak9937227-Feb-05 22:51 
GeneralA small problem: You're not allowed to use Microsoft's dictionaries! Pin
Mike Eriksson15-Nov-04 22:08
Mike Eriksson15-Nov-04 22:08 
GeneralRe: A small problem: You're not allowed to use Microsoft's dictionaries! Pin
danmorin16-Nov-04 3:53
danmorin16-Nov-04 3:53 
GeneralRe: A small problem: You're not allowed to use Microsoft's dictionaries! Pin
Gerhold16-Nov-04 5:33
Gerhold16-Nov-04 5:33 
GeneralRe: A small problem: You're not allowed to use Microsoft's dictionaries! Pin
danmorin9-Jun-05 4:17
danmorin9-Jun-05 4:17 
GeneralC# Code Pin
skjagini13-Nov-04 13:58
skjagini13-Nov-04 13:58 
GeneralSample binary crashes Pin
YoSilver13-Nov-04 10:48
YoSilver13-Nov-04 10:48 
GeneralRe: Sample binary crashes Pin
William Hennebois13-Nov-04 22:21
William Hennebois13-Nov-04 22:21 
GeneralRe: Sample binary crashes Pin
YoSilver14-Nov-04 13:01
YoSilver14-Nov-04 13:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.