Click here to Skip to main content
15,892,298 members
Articles / Programming Languages / C#

Code Spelling Checker Extension for Visual Studio 2010 (VSX)

Rate me:
Please Sign up or sign in to vote.
4.98/5 (43 votes)
7 Apr 2010CPOL6 min read 111.5K   2.4K   74  
Building a spelling checker for source code as an extension for Visual Studio 2010
namespace DigitalSamurai.SpellSharp.InputProcessing.Processors
{
    public partial class CSharpProcessor
    {
        public partial class CSharpScanner : Scanner<ScanMethod>
        {
            public const char hashChar = '#';
            public const char singleQuoteChar = '\'';
            public const char doubleQuoteChar = '\"';
            public const char verbatimStringChar = '@';
            public const char backSlashChar = '\\';
            public const char forwardSlashChar = '/';
            public const char asterixChar = '*';


            protected override ScanningActions DetermineScanningAction (ScanningState state)
            {
                var scanMethod = state.CurrentScannerState;

                try
                {
                    switch (state.CurrentScannerState)
                    {
                        case ScanMethod.Preprocessor:
                            return DetermineScanningActionTillNotLetter (state, ref scanMethod);

                        case ScanMethod.PreprocessorDefine:
                        case ScanMethod.PreprocessorUndefine:
                        case ScanMethod.PreprocessorRegion:
                        case ScanMethod.PreprocessorEndRegion:
                            return DetermineScanningActionTillEndOfLine (state, ref scanMethod);


                        case ScanMethod.EscapedChar:
                            return DetermineScanningActionInEscapedChar (state, ref scanMethod);

                        case ScanMethod.EscapedString:
                            return DetermineScanningActionInEscapedString (state, ref scanMethod);

                        case ScanMethod.VerbatimString:
                            return DetermineScanningActionInVerbatimString (state, ref scanMethod);


                        case ScanMethod.SingleLineComment:
                            return DetermineScanningActionTillEndOfLine (state, ref scanMethod);

                        case ScanMethod.MultiLineComment:
                            return DetermineScanningActionInMultiLineComment (state, ref scanMethod);

                        case ScanMethod.XmlComment:
                            return DetermineScanningActionInXmlComment (state, ref scanMethod);


                        case ScanMethod.Number:
                        case ScanMethod.NumberReal:
                            return NumberRecognizer.DetermineScanningAction (state, ref scanMethod);


                        case ScanMethod.SymbolOperator:
                            return DetermineScanningActionInSymbolOperator (state, ref scanMethod);


                        default:
                            return DetermineScanningActionDefault (state, ref scanMethod);
                    }
                }
                finally
                {
                    state.CurrentScannerState = scanMethod;
                }
            }

            private ScanningActions DetermineScanningActionTillNotLetter (ScanningState state, ref ScanMethod scanMethod)
            {
                if (!char.IsLetter (state.CurrentInputChar))
                {
                    scanMethod = ScanMethod.Default;
                    return ScanningActions.YieldCurrentToken;
                }

                return ScanningActions.AppendCurrentChar;
            }

            public ScanningActions DetermineScanningActionTillEndOfLine (ScanningState state, ref ScanMethod scanMethod)
            {
                if (ScanningRules.IsEndOfLine (state))
                {
                    scanMethod = ScanMethod.Default;
                    return ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                }

                return ScanningActions.AppendCurrentChar;
            }


            public static ScanningActions DetermineScanningActionDefault (ScanningState state, ref ScanMethod scanMethod)
            {
                ScanningActions onScanMethodChange;
                if (RequiresScanMethodChange (state, ref scanMethod, out onScanMethodChange))
                {
                    return onScanMethodChange;
                }

                return
                    ScanningRules.HasStartedWith_UnderScoreOrLetter_ContinuesWith_UnderScoreOrLetterOrDigit (state)
                    ||
                    ScanningRules.IsContinuesCharsOfNumberOrPunctuationOrSymbolOrWhiteSpace (state)
                    ?
                    ScanningActions.AppendCurrentChar
                    :
                    ScanningActions.YieldCurrentToken;
            }


            public static bool RequiresScanMethodChange (ScanningState state, ref ScanMethod scanMethod, out ScanningActions onScanMethodChange)
            {
                switch (state.CurrentInputChar)
                {
                    case hashChar:
                        if (IsPreprocessing (state, ref scanMethod, out onScanMethodChange))
                        {
                            return true;
                        }
                        break;

                    case singleQuoteChar:
                        scanMethod = ScanMethod.EscapedChar;
                        onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                        return true;

                    case verbatimStringChar:
                        if (IsVerbatimString (state, ref scanMethod, out onScanMethodChange))
                        {
                            return true;
                        }
                        break;

                    case doubleQuoteChar:
                        scanMethod = ScanMethod.EscapedString;
                        onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                        return true;

                    case forwardSlashChar:
                        if (IsComment (state, ref scanMethod, out onScanMethodChange))
                        {
                            return true;
                        }
                        break;
                }

                if (NumberRecognizer.ShouldScanAsNumber (state))
                {
                    scanMethod = ScanMethod.Number;
                    onScanMethodChange = ScanningActions.YieldCurrentToken;
                    return true;
                }

                if (SymbolRecognizer.ShouldScanAsSymbolOperator (state))
                {
                    scanMethod = ScanMethod.SymbolOperator;
                    onScanMethodChange = ScanningActions.YieldCurrentToken;
                    return true;
                }

                onScanMethodChange = 0;
                return false;
            }

            private static bool IsPreprocessing (ScanningState state, ref ScanMethod scanMethod, out ScanningActions onScanMethodChange)
            {
                if (state.CanPeekString (1, "define") && char.IsWhiteSpace (state.PeekInputChar (7) ?? 'x'))
                {
                    state.InputIncrement = 7;
                    scanMethod = ScanMethod.PreprocessorDefine;
                    onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                    return true;
                }

                if (state.CanPeekString (1, "undef") && char.IsWhiteSpace (state.PeekInputChar (6) ?? 'x'))
                {
                    state.InputIncrement = 6;
                    scanMethod = ScanMethod.PreprocessorUndefine;
                    onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                    return true;
                }

                if (state.CanPeekString (1, "region") && char.IsWhiteSpace (state.PeekInputChar (7) ?? 'x'))
                {
                    state.InputIncrement = 7;
                    scanMethod = ScanMethod.PreprocessorRegion;
                    onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                    return true;
                }

                if (state.CanPeekString (1, "endregion") && char.IsWhiteSpace (state.PeekInputChar (10) ?? 'x'))
                {
                    state.InputIncrement = 10;
                    scanMethod = ScanMethod.PreprocessorEndRegion;
                    onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                    return true;
                }

                scanMethod = ScanMethod.Preprocessor;
                onScanMethodChange = ScanningActions.YieldCurrentToken;
                return true;
            }

            private static bool IsVerbatimString (ScanningState state, ref ScanMethod scanMethod, out ScanningActions onScanMethodChange)
            {
                if (state.NextInputChar != doubleQuoteChar)
                {
                    onScanMethodChange = 0;
                    return false;
                }

                state.InputIncrement = 2;
                scanMethod = ScanMethod.VerbatimString;
                onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                return true;
            }

            private static bool IsComment (ScanningState state, ref ScanMethod scanMethod, out ScanningActions onScanMethodChange)
            {
                switch (state.NextInputChar)
                {
                    case asterixChar:
                        state.InputIncrement = 2;
                        scanMethod = ScanMethod.MultiLineComment;
                        onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                        return true;

                    case forwardSlashChar:
                        if (state.PeekInputChar (2) == forwardSlashChar)
                        {
                            state.InputIncrement = 3;
                            scanMethod = ScanMethod.XmlComment;
                            onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                            return true;
                        }

                        state.InputIncrement = 2;
                        scanMethod = ScanMethod.SingleLineComment;
                        onScanMethodChange = ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                        return true;

                    default:
                        onScanMethodChange = 0;
                        return false;
                }
            }


            public static ScanningActions DetermineScanningActionInEscapedChar (ScanningState state, ref ScanMethod scanMethod)
            {
                var currentChar = state.CurrentInputChar;

                if (currentChar == backSlashChar)
                {
                    return EscapedCharRecognizer.DetermineScanningAction (state);
                }

                if (currentChar == singleQuoteChar)
                {
                    scanMethod = ScanMethod.Default;
                    return ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                }

                return ScanningActions.AppendCurrentChar;
            }

            public static ScanningActions DetermineScanningActionInEscapedString (ScanningState state, ref ScanMethod scanMethod)
            {
                var currentChar = state.CurrentInputChar;

                if (currentChar == backSlashChar)
                {
                    return EscapedCharRecognizer.DetermineScanningAction (state);
                }

                if (currentChar == doubleQuoteChar)
                {
                    scanMethod = ScanMethod.Default;
                    return ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                }

                return ScanningActions.AppendCurrentChar;
            }

            public static ScanningActions DetermineScanningActionInVerbatimString (ScanningState state, ref ScanMethod scanMethod)
            {
                var currentChar = state.CurrentInputChar;

                if (currentChar == doubleQuoteChar)
                {
                    if (state.NextInputChar == doubleQuoteChar)
                    {
                        state.InputIncrement = 2;
                        return ScanningActions.AppendCurrentChar;
                    }
                    else
                    {
                        scanMethod = ScanMethod.Default;
                        return ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                    }
                }

                return ScanningActions.AppendCurrentChar;
            }


            public static ScanningActions DetermineScanningActionInMultiLineComment (ScanningState state, ref ScanMethod scanMethod)
            {
                if ((state.CurrentInputChar == asterixChar) && (state.NextInputChar == forwardSlashChar))
                {
                    state.InputIncrement = 2;
                    scanMethod = ScanMethod.Default;
                    return ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                }

                return ScanningActions.AppendCurrentChar;
            }

            public static ScanningActions DetermineScanningActionInXmlComment (ScanningState state, ref ScanMethod scanMethod)
            {
                if (ScanningRules.IsEndOfLine (state))
                {
                    scanMethod = ScanMethod.Default;
                    return ScanningActions.YieldCurrentTokenAndSuppressCurrentChar;
                }

                return ScanningActions.AppendCurrentChar;
            }


            public static ScanningActions DetermineScanningActionInSymbolOperator (ScanningState state, ref ScanMethod scanMethod)
            {
                if (!SymbolRecognizer.ShouldYieldAsToken (state))
                {
                    return ScanningActions.AppendCurrentChar;
                }

                scanMethod = ScanMethod.Default;

                ScanningActions onScanMethodChange;
                if (RequiresScanMethodChange (state, ref scanMethod, out onScanMethodChange))
                {
                    return onScanMethodChange;
                }

                return ScanningActions.YieldCurrentToken;
            }


            protected override LexicalElements DetermineLexicalElement (string text, ScanMethod scanMethod)
            {
                if (string.IsNullOrWhiteSpace (text))
                {
                    return LexicalElements.WhiteSpace;
                }

                switch (scanMethod)
                {
                    case ScanMethod.PreprocessorDefine:
                        return LexicalElements.PreprocessorDefine;

                    case ScanMethod.PreprocessorUndefine:
                        return LexicalElements.PreprocessorUndefine;

                    case ScanMethod.PreprocessorRegion:
                        return LexicalElements.PreprocessorRegion;

                    case ScanMethod.PreprocessorEndRegion:
                        return LexicalElements.PreprocessorEndRegion;

                    case ScanMethod.EscapedChar:
                        return LexicalElements.CharValue;

                    case ScanMethod.EscapedString:
                    case ScanMethod.VerbatimString:
                        return LexicalElements.StringValue;

                    case ScanMethod.SingleLineComment:
                    case ScanMethod.MultiLineComment:
                        return LexicalElements.Comment;

                    case ScanMethod.XmlComment:
                        return LexicalElements.XmlComment;

                    case ScanMethod.Number:
                        if (text == ".")
                        {
                            break;
                        }
                        return LexicalElements.IntegerValue;


                    case ScanMethod.NumberReal:
                        return LexicalElements.RealValue;
                }

                LexicalElements recognized;
                if (LiteralToElementRecognizer.TryDetermine (text, out recognized))
                {
                    return recognized;
                }

                return LexicalElements.Unknown;
            }

            protected override bool IsTokenIgnored (Token token)
            {
                return token.LexicalElement == LexicalElements.WhiteSpace;
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


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

Comments and Discussions