. 주석 처리

이 기능은 코드의 일부분을 잠시 주석으로 묶어 두고 싶을 때 코드의 선두에 한 줄 주석 문자열을 삽입한다. 일반 텍스트에는 의미가 없으며 오로지 프로그래밍 소스를 작성할 때만 사용할 수 있다. 다음 함수를 작성하자.

 

void CApiEdit::InsertComment(BOOL bUnComment)

{

     int SelFirst, SelSecond;

     int st,ed;

     int pr1,pr2,pc;

     int Line;

     TCHAR *src,*dest;

     TCHAR *s,*d;

     int i;

     int nDiff;

     BOOL bFirstPara=TRUE;

     TCHAR szComment[32];

     int clen;

 

     lstrcpy(szComment,Parser->GetInfo(2));

     clen=lstrlen(szComment);

     if (clen==0) {

          return;

     }

 

     if (SelStart==SelEnd) {

          SelFirst=SelSecond=off;

     } else {

          SelFirst=min(SelStart,SelEnd);

          SelSecond=max(SelStart,SelEnd);

     }

 

     GetParaFromOff(SelFirst,pr1,pc);

     Line=GetParaFirstLine(pr1);

     st=pLine[Line].Start;

 

     GetParaFromOff(SelSecond,pr2,pc);

     Line=GetParaFirstLine(pr2);

     if (SelSecond==pLine[Line].Start && SelStart!=SelEnd) {

          pr2--;

     }

     Line=GetParaLastLine(pr2);

     ed=pLine[Line].End;

 

     src=(TCHAR *)malloc(ed-st+1);

     dest=(TCHAR *)malloc(ed-st+1+(pr2-pr1+1)*clen);

     lstrcpyn(src,buf+st,ed-st+1);

 

     s=src;

     d=dest;

     nDiff=0;

     if (!bUnComment) {

          for (;;) {

              for (i=0;i<clen;i++) {

                   *d++=szComment[i];

                   nDiff++;

              }

 

              while (!AeIsLineEnd(*s)) {

                   *d++=*s++;

              }

              if (*s==0) {

                   *d=*s;

                   break;

              } else {

                   *d++=*s++;

                   *d++=*s++;

              }

          }

 

          if (SelFirst!=st) {

              SelFirst+=clen;

          }

          SelSecond+=nDiff;

     } else {

          for (;;) {

              if (_strnicmp(s,szComment,clen)==0) {

                   s+=clen;

                   nDiff-=clen;

 

                   if (bFirstPara) {

                        SelFirst+=nDiff;

                        SelFirst=max(SelFirst,st);

                        bFirstPara=FALSE;

                   }

              }

 

              while (*s!=‘\r’ && *s != 0) {

                   *d++=*s++;

              }

              if (*s==0) {

                   *d=*s;

                   break;

              } else {

                   *d++=*s++;

                   *d++=*s++;

              }

          }

          SelSecond+=nDiff;

     }

 

     StartUndoGroup();

     Delete(st,lstrlen(src));

     Insert(st,dest);

     EndUndoGroup();

 

     if (SelStart == SelEnd) {

          off=SelStart=SelEnd=SelSecond;

     } else if (SelStart < SelEnd) {

          off=SelEnd=SelSecond;

          SelStart=SelFirst;

     } else {

          off=SelEnd=SelFirst;

          SelStart=SelSecond;

     }

 

     Invalidate(st);

     SetCaret();

     free(src);

     free(dest);

}

 

코드의 전체적인 구조는 블록 들여쓰기 함수와 거의 유사하다. 변환 대상이 동일하며 탭을 삽입하는 대신 주석 문자열을 삽입하는 점만 다르다. 함수를 좀 더 치밀하게 설계하면 두 함수를 하나로 합칠 수도 있을 정도로 유사하다. 코드가 유사하므로 이 함수에 대한 상세한 분석은 할 필요가 없을 것 같고 다만 다음 두 가지 특이 사항에 대해서만 논해보자.

첫 번째로 문단 앞에 주석 문자열만 삽입함으로써 주석 기호와 문장이 너무 밀착해서 보기에 좋지 않은데 주석 문자열 다음에 공백을 하나 더 넣는 것이 좋을 것 같다. break;에 주석을 달 경우 // break; //break보다 더 여유 있어 보이고 좋아 보이지 않는가? 하지만 사용자가 직접 넣은 공백과 덤으로 들어간 공백이 구분되지 않기 때문에 그럴 수가 없다. 주석을 넣었다 빼면 그대로 복구되어야 하는데 주석 문자열 다음에 있는 공백이 원래 있던 것인지 이 기능에 의해 추가된 것인지 구분이 안된다.

두 번째로 이미 주석이 있는 줄에 대해서도 중복 주석 처리하는데 주석인 줄은 이중 주석 처리하지 않도록 하는 것이 어떨까? 멋진 생각인 것 같지만 이것도 안된다. 왜냐하면 이미 있는 주석은 사용자가 직접 넣은 것이고 이 기능에 의해 삽입된 주석은 어디까지나 임시로 넣은 것이다. 다음 예문을 보자.

 

value=func();

// value++;

printout(value);

 

이 문장을 주석화했다가 풀면 그대로 복원되어야 한다. 두 번째 줄에 주석이 있다고 하여 주석 처리를 중복하지 않으면 그대로 복원할 수가 없다. 그래서 좀 보기 싫지만 무조건 문단 앞에 주석 문자열을 넣도록 했다. 호스트의 OnCommand에서 이 함수를 호출한다.

 

void OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     ....

     case IDM_EDIT_COMMENT:

          pSi->Ae.InsertComment(FALSE);

          break;

     case IDM_EDIT_UNCOMMENT:

          pSi->Ae.InsertComment(TRUE);

          break;

 

두 명령은 편집 메뉴에 있지만 단축키 <Ctrl+M>, <Ctrl+Shift+M>이 작성되어 있으므로 단축키를 사용하는 것이 더 편리하다. OnInitMenu에서는 이 항목들을 관리한다.

 

void OnInitMenu(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     ....

     if (hActive) {

          ....

          if (lstrlen(pSi->Ae.GetParser()->GetInfo(2)) == 0) {

              EnableMenuItem(hMenu,IDM_EDIT_COMMENT, MF_BYCOMMAND | MF_GRAYED);

              EnableMenuItem(hMenu,IDM_EDIT_UNCOMMENT, MF_BYCOMMAND | MF_GRAYED);

          } else {

              EnableMenuItem(hMenu,IDM_EDIT_COMMENT, MF_BYCOMMAND | MF_ENABLED);

              EnableMenuItem(hMenu,IDM_EDIT_UNCOMMENT, MF_BYCOMMAND | MF_ENABLED);

          }

 

한 줄 주석이 없는 문법이면 이 기능을 쓸 수 없다. 블록 주석은 원상 복구를 하기가 어렵기 때문에 지원하지 않았다.