📄 spellyer.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 + -