// SyntaxColorizer.cpp: implementation of the CSyntaxColorizer class. // // Version: 1.0.0 // Author: Jeff Schering jeffschering@hotmail.com // Date: Jan 2001 // Copyright 2001 by Jeff Schering // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "SyntaxColorizer.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CSyntaxColorizer::CSyntaxColorizer() { createDefaultCharFormat(); SetCommentColor(CLR_COMMENT); SetStringColor(CLR_STRING); createTables(); m_pskKeyword = NULL; createDefaultKeywordList(); } CSyntaxColorizer::~CSyntaxColorizer() { ClearKeywordList(); deleteTables(); } ////////////////////////////////////////////////////////////////////// // Member Functions ////////////////////////////////////////////////////////////////////// void CSyntaxColorizer::createDefaultCharFormat() { ZeroStruct( m_cfDefault ); m_cfDefault.dwMask = CFM_CHARSET | CFM_FACE | CFM_SIZE | CFM_OFFSET | CFM_COLOR; m_cfDefault.dwMask ^= CFM_ITALIC ^ CFM_BOLD ^ CFM_STRIKEOUT ^ CFM_UNDERLINE; m_cfDefault.dwEffects = 0; //m_cfDefault.yHeight = 200; //10pts * 20 twips/point = 200 twips m_cfDefault.yHeight = 180; //10pts * 20 twips/point = 200 twips m_cfDefault.bCharSet = ANSI_CHARSET; m_cfDefault.bPitchAndFamily = FIXED_PITCH | FF_MODERN; m_cfDefault.yOffset = 0; m_cfDefault.crTextColor = CLR_PLAIN; strcpy(m_cfDefault.szFaceName,"Courier New"); m_cfDefault.cbSize = sizeof(m_cfDefault); m_cfComment = m_cfDefault; m_cfString = m_cfDefault; } void CSyntaxColorizer::createDefaultKeywordList() { LPTSTR sKeywords = "__asm,else,main,struct,__assume,enum," "__multiple_inheritance,switch,auto,__except,__single_inheritance," "template,__based,explicit,__virtual_inheritance,this,bool,extern," "mutable,thread,break,false,naked,throw,case,__fastcall,namespace," "true,catch,__finally,new,try,__cdecl,float,noreturn,__try,char,for," "operator,typedef,class,friend,private,typeid,const,goto,protected," "typename,const_cast,if,public,union,continue,inline,register," "unsigned,__declspec,__inline,reinterpret_cast,using,declaration," "directive,default,int,return,uuid,delete,__int8,short," "__uuidof,dllexport,__int16,signed,virtual,dllimport,__int32,sizeof," "void,do,__int64,static,volatile,double,__leave,static_cast,wmain," "dynamic_cast,long,__stdcall,while"; LPTSTR sDirectives = "#define,#elif,#else,#endif,#error,#ifdef," "#ifndef,#import,#include,#line,#pragma,#undef"; LPTSTR sPragmas = "alloc_text,comment,init_seg1,optimize,auto_inline," "component,inline_depth,pack,bss_seg,data_seg," "inline_recursion,pointers_to_members1,check_stack," "function,intrinsic,setlocale,code_seg,hdrstop,message," "vtordisp1,const_seg,include_alias,once,warning"; AddKeyword(sKeywords,CLR_KEYWORD,GRP_KEYWORD); AddKeyword(sDirectives,CLR_KEYWORD,GRP_KEYWORD); AddKeyword(sPragmas,CLR_KEYWORD,GRP_KEYWORD); } void CSyntaxColorizer::createTables() { m_pTableZero = new unsigned char[256]; m_pTableOne = new unsigned char[256]; m_pTableTwo = new unsigned char[256]; m_pTableThree = new unsigned char[256]; m_pTableFour = new unsigned char[256]; m_pAllowable = new unsigned char[256]; memset(m_pTableZero,SKIP,256); memset(m_pTableOne,SKIP,256); memset(m_pTableTwo,SKIP,256); memset(m_pTableThree,SKIP,256); memset(m_pTableFour,SKIP,256); memset(m_pAllowable,false,256); *(m_pTableZero + '"') = DQSTART; *(m_pTableZero + '\'') = SQSTART; *(m_pTableZero + '/') = CMSTART; *(m_pTableOne + '"') = DQEND; *(m_pTableTwo + '\'') = SQEND; *(m_pTableThree + '\n') = SLEND; *(m_pTableThree + '\r') = SLEND; *(m_pTableFour + '*') = MLEND; *(m_pAllowable + '\n') = true; *(m_pAllowable + '\r') = true; *(m_pAllowable + '\t') = true; *(m_pAllowable + '\0') = true; *(m_pAllowable + ' ') = true; *(m_pAllowable + ';') = true; *(m_pAllowable + '(') = true; *(m_pAllowable + ')') = true; *(m_pAllowable + '{') = true; *(m_pAllowable + '}') = true; *(m_pAllowable + '[') = true; *(m_pAllowable + ']') = true; *(m_pAllowable + '*') = true; } void CSyntaxColorizer::deleteTables() { delete m_pTableZero; delete m_pTableOne; delete m_pTableTwo; delete m_pTableThree; delete m_pTableFour; delete m_pAllowable; } void CSyntaxColorizer::AddKeyword(LPCTSTR Keyword, COLORREF cr, int grp) { LPTSTR token; //make a copy of Keyword so that strtok will operate correctly LPTSTR keyword = new TCHAR[strlen(Keyword) + 1]; strcpy(keyword,Keyword); CHARFORMAT cf = m_cfDefault; cf.crTextColor = cr; token = strtok(keyword,","); while(token != NULL) { if(_stricmp(token,"rem") == 0) *(m_pTableTwo + '\n') = SLEND; //set single quote as comment start addKey(token,cf,grp); token = strtok(NULL,","); } delete keyword; } void CSyntaxColorizer::AddKeyword(LPCTSTR Keyword, CHARFORMAT cf, int grp) { LPTSTR token; //make a copy of Keyword so that strtok will operate correctly LPTSTR keyword = new TCHAR[strlen(Keyword) + 1]; strcpy(keyword,Keyword); token = strtok(keyword,","); while(token != NULL) { if(_stricmp(token,"rem") == 0) *(m_pTableTwo + '\n') = SLEND; //set single quote as comment start addKey(token,cf,grp); token = strtok(NULL,","); } delete keyword; } void CSyntaxColorizer::addKey(LPCTSTR Keyword, CHARFORMAT cf, int grp) //add in ascending order { SKeyword* pskNewKey = new SKeyword; SKeyword* prev,*curr; //the string pointed to by Keyword is only temporary, so make a copy // of it for our list pskNewKey->keyword = new TCHAR[strlen(Keyword)+1]; strcpy(pskNewKey->keyword,Keyword); m_keywords[pskNewKey->keyword] = pskNewKey; pskNewKey->keylen = strlen(Keyword); pskNewKey->cf = cf; pskNewKey->group = grp; pskNewKey->pNext = NULL; *(m_pTableZero + pskNewKey->keyword[0]) = KEYWORD; //if list is empty, add first node if(m_pskKeyword == NULL) m_pskKeyword = pskNewKey; else { //check to see if new node goes before first node if(strcmp(Keyword,m_pskKeyword->keyword) < 0) { pskNewKey->pNext = m_pskKeyword; m_pskKeyword = pskNewKey; } //check to see if new keyword already exists at the first node else if(strcmp(Keyword,m_pskKeyword->keyword) == 0) { //the keyword exists, so replace the existing with the new pskNewKey->pNext = m_pskKeyword->pNext; delete []m_pskKeyword->keyword; delete m_pskKeyword; m_pskKeyword = pskNewKey; } else { prev = m_pskKeyword; curr = m_pskKeyword->pNext; while(curr != NULL && strcmp(curr->keyword,Keyword) < 0) { prev = curr; curr = curr->pNext; } if(curr != NULL && strcmp(curr->keyword,Keyword) == 0) { //the keyword exists, so replace the existing with the new prev->pNext = pskNewKey; pskNewKey->pNext = curr->pNext; delete []curr->keyword; delete curr; } else { pskNewKey->pNext = curr; prev->pNext = pskNewKey; } } } } void CSyntaxColorizer::ClearKeywordList() { SKeyword* pTemp = m_pskKeyword; m_keywords.clear(); while(m_pskKeyword != NULL) { *(m_pTableZero + m_pskKeyword->keyword[0]) = SKIP; if(_stricmp(m_pskKeyword->keyword,"rem") == 0) *(m_pTableTwo + '\n') = SKIP; pTemp = m_pskKeyword->pNext; delete []m_pskKeyword->keyword; delete m_pskKeyword; m_pskKeyword = pTemp; } } CString CSyntaxColorizer::GetKeywordList() { CString sList; SKeyword* pTemp = m_pskKeyword; while(pTemp != NULL) { sList += pTemp->keyword; sList += ","; pTemp = pTemp->pNext; } sList.TrimRight(','); return sList; } CString CSyntaxColorizer::GetKeywordList(int grp) { CString sList; SKeyword* pTemp = m_pskKeyword; while(pTemp != NULL) { if(pTemp->group == grp) { sList += pTemp->keyword; sList += ","; } pTemp = pTemp->pNext; } sList.TrimRight(','); return sList; } void CSyntaxColorizer::SetCommentColor(COLORREF cr) { CHARFORMAT cf = m_cfComment; cf.dwMask = CFM_COLOR; cf.crTextColor = cr; SetCommentStyle(cf); } void CSyntaxColorizer::SetStringColor(COLORREF cr) { CHARFORMAT cf = m_cfString; cf.dwMask = CFM_COLOR; cf.crTextColor = cr; SetStringStyle(cf); } void CSyntaxColorizer::SetGroupStyle(int grp, CHARFORMAT cf) { SKeyword* pTemp = m_pskKeyword; while(pTemp != NULL) { if(pTemp->group == grp) { pTemp->cf = cf; } pTemp = pTemp->pNext; } } void CSyntaxColorizer::GetGroupStyle(int grp, CHARFORMAT &cf) { SKeyword* pTemp = m_pskKeyword; while(pTemp != NULL) { if(pTemp->group == grp) { cf = pTemp->cf; pTemp = NULL; } else { pTemp = pTemp->pNext; //if grp is not found, return default style if(pTemp == NULL) cf = m_cfDefault; } } } void CSyntaxColorizer::SetGroupColor(int grp, COLORREF cr) { CHARFORMAT cf; GetGroupStyle(grp,cf); cf.dwMask = CFM_COLOR; cf.crTextColor = cr; SetGroupStyle(grp,cf); } void CSyntaxColorizer::Colorize(CHARRANGE cr, CRichEditCtrl *pCtrl) { pCtrl->LockWindowUpdate(); long nTextLength = 0; if(cr.cpMin == 0 && cr.cpMax == -1) //send entire text of rich edit box { //set up the buffer to hold the text from the rich edit box nTextLength = pCtrl->GetTextLength(); //if there is alot of text in the Rich Edit (>64K) then GetWindowText doesn't //work. We have to select all of the text, and then use GetSelText pCtrl->SetSel(0,-1); } else { //set up the buffer to hold the text nTextLength = cr.cpMax - cr.cpMin + 1;//add 1 because zero-based array //get the text pCtrl->SetSel(cr.cpMin,cr.cpMax); } LPTSTR lpszBuf = new TCHAR[nTextLength+1]; pCtrl->GetSelText(lpszBuf); pCtrl->SetSelectionCharFormat(m_cfDefault); colorize(lpszBuf,pCtrl,cr.cpMin); delete lpszBuf; pCtrl->UnlockWindowUpdate(); } void CSyntaxColorizer::Colorize(long nStartChar, long nEndChar, CRichEditCtrl *pCtrl) { long nTextLength = 0; if(nStartChar == 0 && nEndChar == -1) //send entire text of rich edit box { nTextLength = pCtrl->GetTextLength(); //if there is alot of text in the Rich Edit (>64K) then GetWindowText doesn't //work. We have to select all of the text, and then use GetSelText pCtrl->SetSel(0,-1); } else { //set up the text buffer; add 1 because zero-based array nTextLength = nEndChar - nStartChar + 1; pCtrl->SetSel(nStartChar,nEndChar); } LPTSTR lpszBuf = new TCHAR[nTextLength+1]; pCtrl->GetSelText(lpszBuf); pCtrl->SetSelectionCharFormat(m_cfDefault); colorize(lpszBuf,pCtrl,nStartChar); delete lpszBuf; } void CSyntaxColorizer::colorize(LPTSTR lpszBuf, CRichEditCtrl *pCtrl, long iOffset /*=0*/) { //setup some vars char sWord[4096]; CHARFORMAT cf; LPTSTR lpszTemp; long iStart; long x = 0; SKeyword* pskTemp = m_pskKeyword; unsigned char* pTable = m_pTableZero; //do the work while(lpszBuf[x]) { switch(pTable[lpszBuf[x]]) { case DQSTART: pTable = m_pTableOne; iStart = iOffset + x; break; case SQSTART: pTable = m_pTableTwo; iStart = iOffset + x; break; case CMSTART: if(lpszBuf[x+1] == '/') { pTable = m_pTableThree; iStart = iOffset + x; x++; } else if(lpszBuf[x+1] == '*') { pTable = m_pTableFour; iStart = iOffset + x; x++; } else if(lpszBuf[x] == '\'') { pTable = m_pTableThree; iStart = iOffset + x; x++; } break; case MLEND: if(lpszBuf[x+1] == '/') { x++; pTable = m_pTableZero; pCtrl->SetSel(iStart,iOffset + x+1); pCtrl->SetSelectionCharFormat(m_cfComment); } break; case SLEND: if(lpszBuf[x-2] != '\\') // line continuation character { pTable = m_pTableZero; pCtrl->SetSel(iStart,iOffset + x+1); pCtrl->SetSelectionCharFormat(m_cfComment); } break; case DQEND: pTable = m_pTableZero; pCtrl->SetSel(iStart,iOffset + x+1); pCtrl->SetSelectionCharFormat(m_cfString); break; case SQEND: if(lpszBuf[x-1] == '\\' && lpszBuf[x+1] == '\'') break; pTable = m_pTableZero; pCtrl->SetSel(iStart,iOffset + x+1); pCtrl->SetSelectionCharFormat(m_cfString); break; case KEYWORD: // Extract whole word. lpszTemp = lpszBuf+x; { if (x > 0 && !m_pAllowable[lpszBuf[x-1]]) break; int i = 0; while (!m_pAllowable[lpszTemp[i]]) { sWord[i] = lpszTemp[i]; i++; } if (i > 0) { sWord[i] = 0; // Word extracted. // Find keyword. Keywords::iterator it = m_keywords.find( sWord ); if (it != m_keywords.end()) { int iStart = iOffset + x; pskTemp = it->second; x += pskTemp->keylen; pCtrl->SetSel(iStart,iOffset + x); pCtrl->SetSelectionCharFormat(pskTemp->cf); } } } /* lpszTemp = lpszBuf+x; while(pskTemp != NULL) { if(pskTemp->keyword[0] == lpszTemp[0]) { int x1=0,y1=0;iStart = iOffset + x; while(pskTemp->keyword[x1]) { y1 += lpszTemp[x1] ^ pskTemp->keyword[x1]; x1++; } if(y1 == 0 && (*(m_pAllowable + (lpszBuf[x-1])) && *(m_pAllowable + (lpszBuf[x+pskTemp->keylen])))) { if(_stricmp(pskTemp->keyword,"rem") == 0) { pTable = m_pTableThree; } else { x += pskTemp->keylen; pCtrl->SetSel(iStart,iOffset + x); pCtrl->SetSelectionCharFormat(pskTemp->cf); } } } pskTemp = pskTemp->pNext; } */ pskTemp = m_pskKeyword; break; case SKIP:; } x++; } //sometimes we get to the end of the file before the end of the string //or comment, so we deal with that situation here if(pTable == m_pTableOne) { cf = m_cfString; pCtrl->SetSel(iStart,iOffset + x+1); pCtrl->SetSelectionCharFormat(cf); } else if(pTable == m_pTableTwo) { cf = m_cfString; pCtrl->SetSel(iStart,iOffset + x+1); pCtrl->SetSelectionCharFormat(cf); } else if(pTable == m_pTableThree) { cf = m_cfComment; pCtrl->SetSel(iStart,iOffset + x+1); pCtrl->SetSelectionCharFormat(cf); } else if(pTable == m_pTableFour) { cf = m_cfComment; pCtrl->SetSel(iStart,iOffset + x+1); pCtrl->SetSelectionCharFormat(cf); } }