Click here to Skip to main content
15,892,737 members
Articles / Desktop Programming / Windows Forms

C# and VB.NET Code Searcher - Using Roslyn

Rate me:
Please Sign up or sign in to vote.
4.84/5 (51 votes)
7 Mar 2013LGPL314 min read 201.6K   5.6K   130  
A fast C# and VB.NET code searcher using Roslyn.
//#define debug

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace FastColoredTextBoxNS
{
    /// <summary>
    /// This class contains the source text (chars and styles).
    /// It stores a text lines, the manager of commands, undo/redo stack, styles.
    /// </summary>
    public class FileTextSource : TextSource, IDisposable
    {
        List<int> sourceFileLinePositions = new List<int>();
        FileStream fs;
        Encoding fileEncoding;
        System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

        /// <summary>
        /// Occurs when need to display line in the textbox
        /// </summary>
        public event EventHandler<LineNeededEventArgs> LineNeeded;

        /// <summary>
        /// Occurs when need to save line in the file
        /// </summary>
        public event EventHandler<LinePushedEventArgs> LinePushed;

        public FileTextSource(FastColoredTextBox currentTB)
            : base(currentTB)
        {
            timer.Interval = 10000;
            timer.Tick += new EventHandler(timer_Tick);
            timer.Enabled = true;
        }

        void timer_Tick(object sender, EventArgs e)
        {
            timer.Enabled = false;
            try
            {
                UnloadUnusedLines();
            }
            finally
            {
                timer.Enabled = true;
            }
        }

        private void UnloadUnusedLines()
        {
            const int margin = 2000;
            var iStartVisibleLine = CurrentTB.VisibleRange.Start.iLine;
            var iFinishVisibleLine = CurrentTB.VisibleRange.End.iLine;

            int count = 0;
            for (int i = 0; i < Count; i++)
                if (base.lines[i] != null && !base.lines[i].IsChanged && Math.Abs(i - iFinishVisibleLine) > margin)
                {
                    base.lines[i] = null;
                    count++;
                }
            #if debug
            Console.WriteLine("UnloadUnusedLines: " + count);
            #endif
        }

        public void OpenFile(string fileName, Encoding enc)
        {
            Clear();

            if (fs != null)
                fs.Dispose();

            //read lines of file
            fs = new FileStream(fileName, FileMode.Open);
            var length = fs.Length;
            //read signature
            enc = DefineEncoding(enc, fs);
            int shift = DefineShift(enc);
            //first line
            sourceFileLinePositions.Add((int)fs.Position);
            base.lines.Add(null);
            //other lines
            while(fs.Position < length)
            {
                var b = fs.ReadByte();
                if (b == 10)// char \n
                {
                    sourceFileLinePositions.Add((int)(fs.Position) + shift);
                    base.lines.Add(null);
                }
            }

            Line[] temp = new Line[100];
            var c = base.lines.Count;
            base.lines.AddRange(temp);
            base.lines.TrimExcess();
            base.lines.RemoveRange(c, temp.Length);


            int[] temp2 = new int[100];
            c = base.lines.Count;
            sourceFileLinePositions.AddRange(temp2);
            sourceFileLinePositions.TrimExcess();
            sourceFileLinePositions.RemoveRange(c, temp.Length);
            

            fileEncoding = enc;

            OnLineInserted(0, Count);
            //load first lines for calc width of the text
            var linesCount = Math.Min(lines.Count, CurrentTB.Height/CurrentTB.CharHeight);
            for (int i = 0; i < linesCount; i++)
                LoadLineFromSourceFile(i);
            //
            NeedRecalc(new TextChangedEventArgs(0, 1));
        }

        private int DefineShift(Encoding enc)
        {
            if (enc.IsSingleByte)
                return 0;

            if (enc.HeaderName == "unicodeFFFE")
                return 0;//UTF16 BE

            if (enc.HeaderName == "utf-16")
                return 1;//UTF16 LE

            if (enc.HeaderName == "utf-32BE")
                return 0;//UTF32 BE

            if (enc.HeaderName == "utf-32")
                return 3;//UTF32 LE

            return 0;
        }

        private static Encoding DefineEncoding(Encoding enc, FileStream fs)
        {
            int bytesPerSignature = 0;
            byte[] signature = new byte[4];
            int c = fs.Read(signature, 0, 4);
            if (signature[0] == 0xFF && signature[1] == 0xFE && signature[2] == 0x00 && signature[3] == 0x00 && c >= 4)
            {
                enc = Encoding.UTF32;//UTF32 LE
                bytesPerSignature = 4;
            }
            else
            if (signature[0] == 0x00 && signature[1] == 0x00 && signature[2] == 0xFE && signature[3] == 0xFF)
            {
                enc = new UTF32Encoding(true, true);//UTF32 BE
                bytesPerSignature = 4;
            }
            else
            if (signature[0] == 0xEF && signature[1] == 0xBB && signature[2] == 0xBF)
            {
                enc = Encoding.UTF8;//UTF8
                bytesPerSignature = 3;
            }
            else
            if (signature[0] == 0xFE && signature[1] == 0xFF)
            {
                enc = Encoding.BigEndianUnicode;//UTF16 BE
                bytesPerSignature = 2;
            }
            else
            if (signature[0] == 0xFF && signature[1] == 0xFE)
            {
                enc = Encoding.Unicode;//UTF16 LE
                bytesPerSignature = 2;
            }

            fs.Seek(bytesPerSignature, SeekOrigin.Begin);

            return enc;
        }

        public void CloseFile()
        {
            if(fs!=null)
                fs.Dispose();
            fs = null;
        }

        public override void SaveToFile(string fileName, Encoding enc)
        {
            //
            var newLinePos = new List<int>(Count);
            //create temp file
            var dir = Path.GetDirectoryName(fileName);
            var tempFileName = Path.Combine(dir, Path.GetFileNameWithoutExtension(fileName) + ".tmp");

            StreamReader sr = new StreamReader(fs, fileEncoding);
            using (FileStream tempFs = new FileStream(tempFileName, FileMode.Create))
            using (StreamWriter sw = new StreamWriter(tempFs, enc))
            {
                sw.Flush();

                for (int i = 0; i < Count; i++)
                {
                    newLinePos.Add((int)tempFs.Length);

                    var sourceLine = ReadLine(sr, i);//read line from source file
                    string line;

                    bool lineIsChanged = lines[i] != null && lines[i].IsChanged;

                    if (lineIsChanged)
                        line = lines[i].Text;
                    else
                        line = sourceLine;

                    //call event handler
                    if (LinePushed != null)
                    {
                        var args = new LinePushedEventArgs(sourceLine, i, lineIsChanged ? line : null);
                        LinePushed(this, args);

                        if(args.SavedText != null)
                            line = args.SavedText;
                    }

                    //save line to file
                    if (i == Count - 1)
                        sw.Write(line);
                    else
                        sw.WriteLine(line);

                    sw.Flush();
                }
            }

            //clear lines buffer
            for (int i = 0; i < Count; i++)
                lines[i] = null;
            //deattach from source file
            sr.Dispose();
            fs.Dispose();
            //delete target file
            if (File.Exists(fileName))
                File.Delete(fileName);
            //rename temp file
            File.Move(tempFileName, fileName);

            //binding to new file
            sourceFileLinePositions = newLinePos;
            fs = new FileStream(fileName, FileMode.Open);
            this.fileEncoding = enc;
        }

        private string ReadLine(StreamReader sr, int i)
        {
            string line;
            var filePos = sourceFileLinePositions[i];
            if (filePos < 0)
                return "";
            fs.Seek(filePos, SeekOrigin.Begin);
            sr.DiscardBufferedData();
            line = sr.ReadLine();
            return line;
        }

        public override void ClearIsChanged()
        {
            foreach (var line in lines)
                if(line!=null)
                    line.IsChanged = false;
        }

        public override Line this[int i]
        {
            get 
            {
                if (base.lines[i] != null)
                    return lines[i];
                else
                    LoadLineFromSourceFile(i);

                return lines[i];
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        private void LoadLineFromSourceFile(int i)
        {
            var line = CreateLine();
            fs.Seek(sourceFileLinePositions[i], SeekOrigin.Begin);
            StreamReader sr = new StreamReader(fs, fileEncoding);

            var s = sr.ReadLine();
            if (s == null)
                s = "";

            //call event handler
            if(LineNeeded!=null)
            {
                var args = new LineNeededEventArgs(s, i);
                LineNeeded(this, args);
                s = args.DisplayedLineText;
                if (s == null)
                    return;
            }

            foreach (var c in s)
                line.Add(new Char(c));
            base.lines[i] = line;
        }

        public override void InsertLine(int index, Line line)
        {
            sourceFileLinePositions.Insert(index, -1);
            base.InsertLine(index, line);
        }

        public override void RemoveLine(int index, int count)
        {
            sourceFileLinePositions.RemoveRange(index, count);
            base.RemoveLine(index, count);
        }

        public override void Clear()
        {
            base.Clear();
        }

        public override int GetLineLength(int i)
        {
            if (base.lines[i] == null)
                return 0;
            else
                return base.lines[i].Count;
        }

        public override bool LineHasFoldingStartMarker(int iLine)
        {
            if (lines[iLine] == null)
                return false;
            else
                return !string.IsNullOrEmpty(lines[iLine].FoldingStartMarker);
        }

        public override bool LineHasFoldingEndMarker(int iLine)
        {
            if (lines[iLine] == null)
                return false;
            else
                return !string.IsNullOrEmpty(lines[iLine].FoldingEndMarker);
        }

        public void Dispose()
        {
            if (fs != null)
                fs.Dispose();

            timer.Dispose();
        }

        internal void UnloadLine(int iLine)
        {
            if (lines[iLine] != null && !lines[iLine].IsChanged)
                lines[iLine] = null;
        }
    }

    public class LineNeededEventArgs : EventArgs
    {
        public string SourceLineText { get; private set; }
        public int DisplayedLineIndex { get; private set; }
        /// <summary>
        /// This text will be displayed in textbox
        /// </summary>
        public string DisplayedLineText { get; set; }

        public LineNeededEventArgs(string sourceLineText, int displayedLineIndex)
        {
            this.SourceLineText = sourceLineText;
            this.DisplayedLineIndex = displayedLineIndex;
            this.DisplayedLineText = sourceLineText;
        }
    }

    public class LinePushedEventArgs : EventArgs
    {
        public string SourceLineText { get; private set; }
        public int DisplayedLineIndex { get; private set; }
        /// <summary>
        /// This property contains only changed text.
        /// If text of line is not changed, this property contains null.
        /// </summary>
        public string DisplayedLineText { get; private set; }
        /// <summary>
        /// This text will be saved in the file
        /// </summary>
        public string SavedText { get; set; }

        public LinePushedEventArgs(string sourceLineText, int displayedLineIndex, string displayedLineText)
        {
            this.SourceLineText = sourceLineText;
            this.DisplayedLineIndex = displayedLineIndex;
            this.DisplayedLineText = displayedLineText;
            this.SavedText = displayedLineText;
        }
    }
}

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 GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior)
Netherlands Netherlands
Languages: C#, ASP.NET, HTML, VB, Javascript.
Tools: Visual Studio 2010, 2012.
Databases: MS SQL Server, Oracle, SQLite.
Skills: MCPD Web Developer 4.0, MCSD 2.0
Likes: Solving problems at projecteuler.net. at #66 now.
Technologies:C#, Azure, Xamarin.iOS, Web API, T/SQL, PL/SQL, MSBuild, WIX, XSLT, WPF, WCF, JavaScript

I have been a programmer since 1995. At first at school where we had a computer club with nice Aster CT-80 (TRS-80 clone) computers and 1 MSX computer where I learned to program in BASIC. Later I had my own 8086 XT computer.
My first experience with programming was way before that when I was about 8. A friend of mine had a Commodore 64. He typed over a BASIC game from a computing magazine. The program worked but it contained an error. The thing he did was go to the source line that caused the error, deleted the line, and restarted the program. It worked every time he got an error...

Comments and Discussions