Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
ParagraphBuilder.cs358 linesDownload Raw Back to HelpWindow
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.ComponentModel;7using System.Text;8using System.Windows.Documents;9using System.Windows.Media;10 11namespace Microsoft.Management.UI.Internal12{13    /// <summary>14    /// Builds a paragraph based on Text + Bold + Highlight information.15    /// Bold are the segments of the text that should be bold, and Highlight are16    /// the segments of the text that should be highlighted (like search results).17    /// </summary>18    internal class ParagraphBuilder : INotifyPropertyChanged19    {20        /// <summary>21        /// The text spans that should be bold.22        /// </summary>23        private readonly List<TextSpan> boldSpans;24 25        /// <summary>26        /// The text spans that should be highlighted.27        /// </summary>28        private readonly List<TextSpan> highlightedSpans;29 30        /// <summary>31        /// The text displayed.32        /// </summary>33        private readonly StringBuilder textBuilder;34 35        /// <summary>36        /// Paragraph built in BuildParagraph.37        /// </summary>38        private readonly Paragraph paragraph;39 40        /// <summary>41        /// Initializes a new instance of the ParagraphBuilder class.42        /// </summary>43        /// <param name="paragraph">Paragraph we will be adding lines to in BuildParagraph.</param>44        internal ParagraphBuilder(Paragraph paragraph)45        {46            ArgumentNullException.ThrowIfNull(paragraph);47 48            this.paragraph = paragraph;49            this.boldSpans = new List<TextSpan>();50            this.highlightedSpans = new List<TextSpan>();51            this.textBuilder = new StringBuilder();52        }53 54        #region INotifyPropertyChanged Members55        /// <summary>56        /// Used to notify of property changes.57        /// </summary>58        public event PropertyChangedEventHandler PropertyChanged;59        #endregion60 61        /// <summary>62        /// Gets the number of highlights.63        /// </summary>64        internal int HighlightCount65        {66            get { return this.highlightedSpans.Count; }67        }68 69        /// <summary>70        /// Gets the paragraph built in BuildParagraph.71        /// </summary>72        internal Paragraph Paragraph73        {74            get { return this.paragraph; }75        }76 77        /// <summary>78        /// Called after all the AddText calls have been made to build the paragraph79        /// based on the current text.80        /// This method goes over 3 collections simultaneously:81        ///    1) characters in this.textBuilder82        ///    2) spans in this.boldSpans83        ///    3) spans in this.highlightedSpans84        /// And adds the minimal number of Inlines to the paragraph so that all85        /// characters that should be bold and/or highlighted are.86        /// </summary>87        internal void BuildParagraph()88        {89            this.paragraph.Inlines.Clear();90 91            int currentBoldIndex = 0;92            TextSpan? currentBoldSpan = this.boldSpans.Count == 0 ? (TextSpan?)null : this.boldSpans[0];93            int currentHighlightedIndex = 0;94            TextSpan? currentHighlightedSpan = this.highlightedSpans.Count == 0 ? (TextSpan?)null : this.highlightedSpans[0];95 96            bool currentBold = false;97            bool currentHighlighted = false;98 99            StringBuilder sequence = new StringBuilder();100            int i = 0;101            foreach (char c in this.textBuilder.ToString())102            {103                bool newBold = false;104                bool newHighlighted = false;105 106                ParagraphBuilder.MoveSpanToPosition(ref currentBoldIndex, ref currentBoldSpan, i, this.boldSpans);107                newBold = currentBoldSpan == null ? false : currentBoldSpan.Value.Contains(i);108 109                ParagraphBuilder.MoveSpanToPosition(ref currentHighlightedIndex, ref currentHighlightedSpan, i, this.highlightedSpans);110                newHighlighted = currentHighlightedSpan == null ? false : currentHighlightedSpan.Value.Contains(i);111 112                if (newBold != currentBold || newHighlighted != currentHighlighted)113                {114                    ParagraphBuilder.AddInline(this.paragraph, currentBold, currentHighlighted, sequence);115                }116 117                sequence.Append(c);118 119                currentHighlighted = newHighlighted;120                currentBold = newBold;121                i++;122            }123 124            ParagraphBuilder.AddInline(this.paragraph, currentBold, currentHighlighted, sequence);125        }126 127        /// <summary>128        /// Highlights all occurrences of <paramref name="search"/>.129        /// This is called after all calls to AddText have been made.130        /// </summary>131        /// <param name="search">Search string.</param>132        /// <param name="caseSensitive">True if search should be case sensitive.</param>133        /// <param name="wholeWord">True if we should search whole word only.</param>134        internal void HighlightAllInstancesOf(string search, bool caseSensitive, bool wholeWord)135        {136            this.highlightedSpans.Clear();137 138            if (search == null || search.Trim().Length == 0)139            {140                this.BuildParagraph();141                this.OnNotifyPropertyChanged("HighlightCount");142                return;143            }144 145            string text = this.textBuilder.ToString();146            StringComparison comparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;147            int start = 0;148            int match;149            while ((match = text.IndexOf(search, start, comparison)) != -1)150            {151                // false loop152                do153                {154                    if (wholeWord)155                    {156                        if (match > 0 && char.IsLetterOrDigit(text[match - 1]))157                        {158                            break;159                        }160 161                        if ((match + search.Length <= text.Length - 1) && char.IsLetterOrDigit(text[match + search.Length]))162                        {163                            break;164                        }165                    }166 167                    this.AddHighlight(match, search.Length);168                }169                while (false);170 171                start = match + search.Length;172            }173 174            this.BuildParagraph();175            this.OnNotifyPropertyChanged("HighlightCount");176        }177 178        /// <summary>179        /// Adds text to the paragraph later build with BuildParagraph.180        /// </summary>181        /// <param name="str">Text to be added.</param>182        /// <param name="bold">True if the text should be bold.</param>183        internal void AddText(string str, bool bold)184        {185            ArgumentNullException.ThrowIfNull(str);186 187            if (str.Length == 0)188            {189                return;190            }191 192            if (bold)193            {194                this.boldSpans.Add(new TextSpan(this.textBuilder.Length, str.Length));195            }196 197            this.textBuilder.Append(str);198        }199 200        /// <summary>201        /// Called before a derived class starts adding text202        /// to reset the current content.203        /// </summary>204        internal void ResetAllText()205        {206            this.boldSpans.Clear();207            this.highlightedSpans.Clear();208            this.textBuilder.Clear();209        }210 211        /// <summary>212        /// Adds an inline to <paramref name="currentParagraph"/> based on the remaining parameters.213        /// </summary>214        /// <param name="currentParagraph">Paragraph to add Inline to.</param>215        /// <param name="currentBold">True if text should be added in bold.</param>216        /// <param name="currentHighlighted">True if the text should be added with highlight.</param>217        /// <param name="sequence">The text to add and clear.</param>218        private static void AddInline(Paragraph currentParagraph, bool currentBold, bool currentHighlighted, StringBuilder sequence)219        {220            if (sequence.Length == 0)221            {222                return;223            }224 225            Run run = new Run(sequence.ToString());226            if (currentHighlighted)227            {228                run.Background = ParagraphSearcher.HighlightBrush;229            }230 231            Inline inline = currentBold ? (Inline)new Bold(run) : run;232            currentParagraph.Inlines.Add(inline);233            sequence.Clear();234        }235 236        /// <summary>237        /// This is an auxiliar method in BuildParagraph to move the current bold or highlighted spans238        /// according to the <paramref name="caracterPosition"/>239        /// The current bold and highlighted span should be ending ahead of the current position.240        /// Moves <paramref name="currentSpanIndex"/> and <paramref name="currentSpan"/> to the241        /// proper span in <paramref name="allSpans"/> according to the <paramref name="caracterPosition"/>242        /// This is an auxiliar method in BuildParagraph.243        /// </summary>244        /// <param name="currentSpanIndex">Current index within <paramref name="allSpans"/>.</param>245        /// <param name="currentSpan">Current span within <paramref name="allSpans"/>.</param>246        /// <param name="caracterPosition">Character position. This comes from a position within this.textBuilder.</param>247        /// <param name="allSpans">The collection of spans. This is either this.boldSpans or this.highlightedSpans.</param>248        private static void MoveSpanToPosition(ref int currentSpanIndex, ref TextSpan? currentSpan, int caracterPosition, List<TextSpan> allSpans)249        {250            if (currentSpan == null || caracterPosition <= currentSpan.Value.End)251            {252                return;253            }254 255            for (int newBoldIndex = currentSpanIndex + 1; newBoldIndex < allSpans.Count; newBoldIndex++)256            {257                TextSpan newBoldSpan = allSpans[newBoldIndex];258                if (caracterPosition <= newBoldSpan.End)259                {260                    currentSpanIndex = newBoldIndex;261                    currentSpan = newBoldSpan;262                    return;263                }264            }265 266            // there is no span ending ahead of current position, so267            // we set the current span to null to prevent unnecessary comparisons against the currentSpan268            currentSpan = null;269        }270 271        /// <summary>272        /// Adds one individual text highlight273        /// This is called after all calls to AddText have been made.274        /// </summary>275        /// <param name="start">Highlight start.</param>276        /// <param name="length">Highlight length.</param>277        private void AddHighlight(int start, int length)278        {279            ArgumentOutOfRangeException.ThrowIfNegative(start);280            ArgumentOutOfRangeException.ThrowIfGreaterThan(start + length, this.textBuilder.Length, nameof(length));281 282            this.highlightedSpans.Add(new TextSpan(start, length));283        }284 285        /// <summary>286        /// Called internally to notify when a property changed.287        /// </summary>288        /// <param name="propertyName">Property name.</param>289        private void OnNotifyPropertyChanged(string propertyName)290        {291            PropertyChangedEventHandler handler = this.PropertyChanged;292            if (handler != null)293            {294                handler(this, new PropertyChangedEventArgs(propertyName));295            }296        }297 298        /// <summary>299        /// A text span used to mark bold and highlighted segments.300        /// </summary>301        internal struct TextSpan302        {303            /// <summary>304            /// Index of the first character in the span.305            /// </summary>306            private readonly int start;307 308            /// <summary>309            /// Index of the last character in the span.310            /// </summary>311            private readonly int end;312 313            /// <summary>314            /// Initializes a new instance of the TextSpan struct.315            /// </summary>316            /// <param name="start">Index of the first character in the span.</param>317            /// <param name="length">Index of the last character in the span.</param>318            internal TextSpan(int start, int length)319            {320                ArgumentOutOfRangeException.ThrowIfNegative(start);321                ArgumentOutOfRangeException.ThrowIfLessThan(length, 1);322 323                this.start = start;324                this.end = start + length - 1;325            }326 327            /// <summary>328            /// Gets the index of the first character in the span.329            /// </summary>330            internal int Start331            {332                get { return this.start; }333            }334 335            /// <summary>336            /// Gets the index of the first character in the span.337            /// </summary>338            internal int End339            {340                get341                {342                    return this.end;343                }344            }345 346            /// <summary>347            /// Returns true if the <paramref name="position"/> is between start and end (inclusive).348            /// </summary>349            /// <param name="position">Position to verify if is in the span.</param>350            /// <returns>True if the <paramref name="position"/> is between start and end (inclusive).</returns>351            internal bool Contains(int position)352            {353                return (position >= this.start) && (position <= this.end);354            }355        }356    }357}358