⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 spellyer.cpp

📁 语法检查程序
💻 CPP
字号:
/***************************************************************************/
/* NOTE:                                                                   */
/* This document is copyright (c) by Oz Solomon and Yonat Sharon, and is   */
/* bound by the MIT open source license.                                   */ 
/* See License.txt or visit www.opensource.org/licenses/mit-license.html   */
/***************************************************************************/

#include "stdafx.h"
#include "Spellyer.h"
#include "spell.h"
#include "SuggestDlg.h"
#include "SpellyParserContext.h"
#include "FilePath.h"
#include "CodeParser/CodeParser.h"
#include "CodeParser/CodeStateMaps.h"
#include <stdexcept>
#include <comdef.h>

#ifdef DOT_NET_ADDIN
const VARIANT_BOOL cMoveSel = VARIANT_FALSE;
const VARIANT_BOOL cExtendSel = VARIANT_TRUE;
#define MOVE_TO MoveToLineAndOffset
inline int CharMoveNum(int i) { return i; }
#else
const CComVariant cMoveSel(dsMove);
const CComVariant cExtendSel(dsExtend);
CComVariant CharMoveNum(long l) { return CComVariant(l); }
#define MOVE_TO MoveTo
#endif

Spellyer::Spellyer() : itsCodeParser(0), itsParserContext(0)
{
    itsDlgOffset.cx = itsDlgOffset.cy = 0;
    itsParserContext = new SpellyParserContext;
    try {
        CString mainDict( GetFilePath("Spelly.lex") );
        CString userDict( GetFilePath("Spelly.dic") );
        int ret = spell_init(mainDict, userDict);
        if (!ret)
            throw "Error loading Spelly.lex";
        if (ret == 2 && !userDict.IsEmpty())
            ReportError("Error loading user dictionary (Spelly.dic)");
    }
    catch (const char *msg) {
        ReportError(msg);
        throw;
    }
    catch (...) {
        ReportError("Exception thrown from spell_init()");
        throw;
    }
}


Spellyer::~Spellyer()
{
    delete itsParserContext;
    spell_done();
}


void Spellyer::Spell(
    I_TEXT_DOC* inDoc,
    bool inCheckAll) // not only comments and strings
{
    CString fatal_error = "fatal error - ";

    try { // main function body
        if (!inCheckAll) InitCodeParser(inDoc);
        InitSelection(inDoc);
        while (SpellCurrLine()) {
            itsCurrPoint.x = 1;
            ++itsCurrPoint.y;
        }
        Cleanup();

    }
    catch (const char* x) {
        ReportError(fatal_error + x);
    }
    catch (const exception& x) {
        ReportError(fatal_error + x.what());
    }
    catch (CException& x) {
        char error[256];
        BOOL ret = x.GetErrorMessage(error, 256);
        if (ret == FALSE)
            strcpy(error, "Unknown MFC error.");
        ReportError(fatal_error + error);
    }
    catch (const _com_error& x) {
        ReportError(fatal_error + (LPCTSTR)x.Description());
    }
    catch (...) {
        ReportError(fatal_error + "Unknown error.");
    }
}


void Spellyer::InitCodeParser(I_TEXT_DOC* inDoc)
{
    const CodeState* c = 0;
    CComBSTR text;
    inDoc->get_Language(&text);
    
    const wchar_t ch = (text.Length() > 0)? text[0] : L'C';  // C++ by default

    switch (text[0])
    {
        case L'F': c = FortranCodeState(); break;
        case L'H': c = HTMLCodeState(); break;
        case L'V': c = BasicCodeState(); break;
        case L'C':
        case L'O':
        case L'J': 
        default:
            c = CxxCodeState(); break;
    }

    if (c) itsCodeParser = new CodeParser(c, itsParserContext);
}

void GetCurrentPos(I_TEXT_SELECTION *pSel, LONG *X, LONG *Y)
{
#if DOT_NET_ADDIN
    CComPtr<EnvDTE::VirtualPoint> pvp;
    pSel->get_ActivePoint(&pvp);
    if (X) pvp->get_LineCharOffset(X);
    if (Y) pvp->get_Line(Y);
#else // VC6
    if (X) pSel->get_CurrentColumn(X);
    if (Y) pSel->get_CurrentLine(Y);
#endif
}

void Spellyer::InitSelection(I_TEXT_DOC* inDoc)
{
    IDispatch* pDispatch;

    // get the TextSelection object
    inDoc->get_Selection(&pDispatch);
    itsSelection = pDispatch;
    pDispatch->Release();


    //
    // get the selection coordinates, and move to its start
    //

#ifdef DOT_NET_ADDIN
    CComPtr<EnvDTE::VirtualPoint> pvpTop, pvpBottom;
    itsSelection->get_TopPoint(&pvpTop);
    itsSelection->get_BottomPoint(&pvpBottom);

    pvpBottom->get_Line(&itsEndPoint.y);
    pvpTop->get_Line(&itsStartPoint.y);
#else // VC6
    itsSelection->get_BottomLine(&itsEndPoint.y);
    itsSelection->get_TopLine(&itsStartPoint.y);
#endif 

    itsOrgPoint.x = itsOrgPoint.y = 0;
    itsRestoreSelection = true;
    itsParserContext->itsInSpellZone = false;
    
    if (itsEndPoint.y == itsStartPoint.y) { // short selection (1 line)
        CComBSTR text;
        itsSelection->get_Text(&text);
        unsigned len = text.Length();
        if (len == 0) { // nothing selected - check entire document
            itsOrgPoint.y = itsStartPoint.y;
            GetCurrentPos(itsSelection, &itsOrgPoint.x, NULL);
            itsStartPoint.x = itsStartPoint.y = 1;
            itsSelection->EndOfDocument(cMoveSel);
            GetCurrentPos(itsSelection, &itsEndPoint.x, &itsEndPoint.y);
        }
        else {
            itsSelection->CharRight(cMoveSel, CharMoveNum(1));
            GetCurrentPos(itsSelection, &itsEndPoint.x, &itsEndPoint.y);
            len -= (itsEndPoint.y - itsStartPoint.y);
            itsSelection->CharLeft(cMoveSel, CharMoveNum(len));
            GetCurrentPos(itsSelection, &itsStartPoint.x, NULL);
            itsParserContext->itsInSpellZone = true;
        }
    } // end if selection is all in 1 line

    // long selection
    else {
        long currLine, currCol;
        GetCurrentPos(itsSelection, &currCol, &currLine);
        if (currLine == itsStartPoint.y) {
            itsStartPoint.x = currCol;
            itsSelection->CharRight(cMoveSel, CharMoveNum(1));
            GetCurrentPos(itsSelection, &itsEndPoint.x, NULL);
        }
        else { // currLine == itsEndPoint.y
            itsEndPoint.x = currCol;
#ifndef DOT_NET_ADDIN
            if (currCol == 1) // workaround for MSDev bug
                ++itsEndPoint.y;
#endif
            itsSelection->CharLeft(cMoveSel, CharMoveNum(1));
            GetCurrentPos(itsSelection, &itsStartPoint.x, NULL);
        }
    } // end if the selection is longer than 1 line

    itsCurrPoint = itsStartPoint;
}


bool Spellyer::SpellCurrLine()
{
    // select current line
    itsSelection->MOVE_TO(itsCurrPoint.y, itsCurrPoint.x, cMoveSel);

    if (itsCurrPoint.y < itsEndPoint.y)
    {
        itsSelection->EndOfLine(cExtendSel);
#ifdef DOT_NET_ADDIN
        // When an outlined area is hidden, its text is included at
		// the end of the line which represents the outline.  We can detect
		// this here by seeing if the EndOfLine command actually moved us
		// to a different line.  If so, we simply backtrack one character,
		// which deselects the entire hidden text.
        CPoint ptVisible;
        GetCurrentPos(itsSelection, &ptVisible.x, &ptVisible.y);
        if (ptVisible.y != itsCurrPoint.y)
        {
            itsSelection->CharLeft(cExtendSel, 1);
        }
#endif
    }
    else if ( (itsCurrPoint.y == itsEndPoint.y) &&
              (itsCurrPoint.x < itsEndPoint.x))
        itsSelection->MOVE_TO(itsEndPoint.y, itsEndPoint.x, cExtendSel);
    else // passed itsEndPoint
___ ___ return false;

    // get current text to check
    CComBSTR w_text;
    itsSelection->get_Text(&w_text);
    CString text(w_text);

    // separate the text to words and spell each word
    CString replacement;
    unsigned wordStart, wordLen, currPos = 0;
    int delta = 0; // between length of original line and line with replacements
    while (FindNextWord(LPCSTR(text)+currPos, wordStart, wordLen)) {
        currPos += wordStart;
        CString word(LPCSTR(text)+currPos, wordLen);

        if (Misspelled(word)) {
            // select current word
            itsSelection->MOVE_TO(itsCurrPoint.y, itsCurrPoint.x, cMoveSel);
            itsSelection->CharRight(cMoveSel, CharMoveNum(currPos+delta));
            itsSelection->CharRight(cExtendSel, CharMoveNum(wordLen));

            // spell current word
            replacement = SpellWord(word);
            if (!replacement.IsEmpty()) {
                if (replacement[0] == '!') { // stop checking
                    itsCurrPoint.x += currPos+delta;
                    itsRestoreSelection = false;
___ ___ ___ ___ ___ return false;
                }
                itsSelection->put_Text(CComBSTR(replacement));
                delta += replacement.GetLength() - wordLen;
            }
        }
        currPos += wordLen;
    }

    if (itsCurrPoint.y == itsOrgPoint.y) {
        if ((LONG)(itsCurrPoint.x+currPos) <= itsOrgPoint.x)
            itsOrgPoint.x += delta;
    }
    else if (itsCurrPoint.y == itsEndPoint.y)
        itsEndPoint.x += delta;

    Parse('\n');

___ return true;
}


void Spellyer::Cleanup()
{
    // restore original selection
    if (itsRestoreSelection) {
        if (itsOrgPoint.x != 0)
            itsSelection->MOVE_TO(itsOrgPoint.y, itsOrgPoint.x, cMoveSel);
        else {
            itsSelection->MOVE_TO(itsStartPoint.y, itsStartPoint.x, cMoveSel);
            itsSelection->MOVE_TO(itsEndPoint.y, itsEndPoint.x, cExtendSel);
        }
    }

    if (itsCodeParser) {
        delete itsCodeParser;
        itsCodeParser = 0;
    }
}


inline void Spellyer::Parse(char c)
{
    if (itsCodeParser)
        itsCodeParser->Process(c);
}


inline bool Spellyer::InSpellZone(char c)
{
    if (itsCodeParser) {
___ ___ return itsParserContext->itsInSpellZone;
    }
___ return true;
}


bool Spellyer::FindNextWord(
    const char* inText,
    unsigned&   outWordStartPos,
    unsigned&   outWordLen)
{
    const unsigned char * const inTextUnsigned = 
        reinterpret_cast<const unsigned char *>(inText);
    const unsigned char* beg = inTextUnsigned;
    const unsigned char* end;

    // find beginning of zone to spell
    while (*beg != '\0' && !InSpellZone(*beg))
        Parse(*beg++);

    // find word beginning
    while (*beg != '\0' && !isalpha(*beg) && InSpellZone(*beg))
        Parse(*beg++);

    // find word end
    end = beg;
    while (isupper(*end) && InSpellZone(*end))
        Parse(*end++);
    if ( (end-beg > 1)      // several consecutive capital letters
         && islower(*end) ) // last capital letter is a beginning of a new word
        --end;
    else {
        while (islower(*end) && InSpellZone(*end))
            Parse(*end++);
    }

    // handle apostrophes - like in "doesn't"
    if (*end == '\'') {
        const unsigned char* a_end = end;
        while (isalpha(*++a_end) && InSpellZone(*a_end))
            Parse(*a_end);
        if (a_end-end > 1)
            end = a_end;
    }

    outWordStartPos = (unsigned)(beg - inTextUnsigned);
    outWordLen = (unsigned)(end - beg);

    return (outWordLen != 0);
}


bool Spellyer::Misspelled(const CString& inWord)
{
    if (inWord.GetLength() <= 1)
___ ___ return false;
    if (itsReplacements.end() != itsReplacements.find(inWord))
___ ___ return true;
    if (spell_check(inWord))
___ ___ return false;
___ return true;
}


CString Spellyer::SpellWord( // returns: ""=no replacement, "!"=stop, else=replace
    const CString& inWord)
{
    CString retval;

    // check "Replace All" list
    if (itsReplacements.end() != itsReplacements.find(inWord)) {
___ ___ return itsReplacements[inWord];
    }

    char** suggestions = spell_suggest(inWord);
    CSuggestDlg dlg(inWord, suggestions, itsDlgOffset);
    INT_PTR ret = dlg.DoModal();
    switch (ret) {
    case IDCANCEL: // stop checking
        retval = "!";
        break;
    case IDIGNORE:
        break;
    case IDIGNOREALL:
        if (0 != spell_ignore(inWord))
            ReportError("Error trying to ignore \"" + inWord + '\"');
        break;
    case IDLEARN:
        if (0 != spell_learn(inWord))
            ReportError("Error trying to learn \"" + inWord + '\"');
        break;
    case IDREPALL: // replace always
        itsReplacements[inWord] = dlg.GetReplacement();
        // fallthrough
    case IDOK: // replace this time
        retval = dlg.GetReplacement();
        break;
    default:
        throw "CSuggestDlg.DoModal returned unknown value";
    } // end switch

___ return retval;
}


void Spellyer::ReportError(const CString& error)
{
    AfxMessageBox("Spelly: " + error);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -