private void showAutoCompleteList()
        {
            hideAutoCompleteList();

            TextUtils.WordInfo currentWordInfo = getCurrentWord(textBoxAutoComplete);
            if (!currentWordInfo.IsValid || currentWordInfo.Word.Length < 2)
            {
                return;
            }

            bool doesWordContainWord(string container, string containee) =>
            container.ToLower().Contains(containee.ToLower());

            object[] objects = _users?
                               .Where(user => doesWordContainWord(user.Name, currentWordInfo.Word) ||
                                      doesWordContainWord(user.Username, currentWordInfo.Word))
                               .Cast <object>()
                               .ToArray() ?? Array.Empty <object>();
            if (objects.Length == 0)
            {
                return;
            }

            ListBox listBox = createListBox();

            fillListBox(listBox, objects);
            resizeListBox(listBox, objects);
            createPopupWindow(listBox);
            showPopupWindow();
            _listBoxAutoComplete = listBox;

            Focus();
        }
        private void applyAutoCompleteListSelection()
        {
            TextUtils.WordInfo currentWordInfo = getCurrentWord(textBoxAutoComplete);
            if (_listBoxAutoComplete.SelectedItem == null || !currentWordInfo.IsValid)
            {
                return;
            }

            string substitutionWord = ((User)(_listBoxAutoComplete.SelectedItem)).Username;

            textBoxAutoComplete.Text           = TextUtils.ReplaceWord(textBoxAutoComplete.Text, currentWordInfo, substitutionWord);
            textBoxAutoComplete.SelectionStart = currentWordInfo.Start + substitutionWord.Length;
        }
        private void showPopupWindow()
        {
            TextUtils.WordInfo currentWordInfo = getCurrentWord(textBoxAutoComplete);
            if (!currentWordInfo.IsValid)
            {
                return;
            }

            Point position = textBoxAutoComplete.GetPositionFromCharIndex(currentWordInfo.Start);
            Point pt       = PointToScreen(new Point(position.X, position.Y + textBoxAutoComplete.Height));

            _popupWindow.Show(pt);
        }
        /// <summary>
        /// Extends functionality.
        /// Trims result of TextUtils.GetCurrentWord():
        /// - removes all non-letter characters before '@' character.
        /// - removes all non-letter characters after a series of letter characters (after '@')
        ///   e.g. "[@abcd]" converted to "abcd"
        /// The following strings are considered incorrect:
        /// - strings without '@'
        /// - strings with letter-characters prior to '@'
        /// - strings where there is a non-letter character next to '@'
        /// </summary>
        private TextUtils.WordInfo getCurrentWord(RichTextBox txt)
        {
            int selectionStartPosition = txt.SelectionStart - 1;

            TextUtils.WordInfo word = TextUtils.GetCurrentWord(txt.Text, selectionStartPosition);
            if (!word.IsValid)
            {
                return(word);
            }

            int?firstLabelPrefixPosition = null;
            int?firstLetterPosition      = null;
            int?firstNonLetterAfterLabelPrefixPosition = null;

            for (int iPosition = 0; iPosition < word.Word.Length; ++iPosition)
            {
                char currentChar = word.Word[iPosition];
                if (Char.IsLetter(currentChar))
                {
                    if (!firstLetterPosition.HasValue)
                    {
                        firstLetterPosition = iPosition;
                    }
                }
                else if (currentChar == GitLabLabelPrefixChar)
                {
                    if (!firstLabelPrefixPosition.HasValue)
                    {
                        firstLabelPrefixPosition = iPosition;
                    }
                }
                else if (!firstNonLetterAfterLabelPrefixPosition.HasValue && firstLabelPrefixPosition.HasValue)
                {
                    firstNonLetterAfterLabelPrefixPosition = iPosition;
                }
            }

            if (!firstLetterPosition.HasValue ||
                !firstLabelPrefixPosition.HasValue ||
                firstLetterPosition.Value < firstLabelPrefixPosition.Value)
            {
                return(TextUtils.WordInfo.Invalid);
            }

            int firstCharAfterLabelPrefixPosition = firstLabelPrefixPosition.Value + 1;
            int textLength = firstNonLetterAfterLabelPrefixPosition.HasValue
            ? firstNonLetterAfterLabelPrefixPosition.Value - firstCharAfterLabelPrefixPosition
            : word.Word.Length - firstCharAfterLabelPrefixPosition;

            if (textLength == 0)
            {
                return(TextUtils.WordInfo.Invalid);
            }

            int    startPosition = word.Start + firstCharAfterLabelPrefixPosition;
            string trimmedWord   = word.Word.Substring(firstCharAfterLabelPrefixPosition, textLength);

            if (selectionStartPosition < startPosition || selectionStartPosition >= startPosition + trimmedWord.Length)
            {
                return(TextUtils.WordInfo.Invalid);
            }

            return(new TextUtils.WordInfo(startPosition, trimmedWord));
        }