internal string GetNewTitle(Solution solution, string pattern, SettingsSet cfg)
        {
            Document activeDocument = null;
            Window   activeWindow   = null;

            try {
                activeDocument = Globals.DTE.ActiveDocument;
            }
            catch {
                // Do nothing
            }
            try {
                activeWindow = Globals.DTE.ActiveWindow;
            }
            catch {
                // Do nothing
            }
            var solutionFp = solution?.FullName;

            if (activeDocument == null && string.IsNullOrEmpty(solutionFp))
            {
                if (activeWindow == null || activeWindow.Caption == Globals.DTE.MainWindow.Caption)
                {
                    return(this.IDEName);
                }
            }
            string path;
            var    documentName = Globals.GetActiveDocumentNameOrEmpty(activeDocument);
            var    documentPath = Globals.GetActiveDocumentPathOrEmpty(activeDocument);
            var    windowName   = Globals.GetActiveWindowNameOrEmpty(activeWindow);

            if (!string.IsNullOrEmpty(solutionFp))
            {
                path = solutionFp;
            }
            else
            {
                path = documentPath;
            }

            var pathParts = this.SplitPath(path);

            if (!string.IsNullOrEmpty(path))
            {
                pathParts[0] = Path.GetPathRoot(path).Replace("\\", "");
            }

            var documentPathParts = this.SplitPath(documentPath);

            if (!string.IsNullOrEmpty(documentPath))
            {
                documentPathParts[0] = Path.GetPathRoot(documentPath).Replace("\\", "");
            }

            pattern = this.TagRegex.Replace(pattern, match => {
                try {
                    var tag = match.Groups[1].Value;
                    try {
                        switch (tag)
                        {
                        case "configurationName":
                            return(Globals.GetActiveConfigurationNameOrEmpty(solution));

                        case "platformName":
                            return(Globals.GetPlatformNameOrEmpty(solution));

                        case "projectName":
                            return(Globals.GetActiveProjectNameOrEmpty());

                        case "solutionName":
                            return(cfg.SolutionName ?? string.Empty);

                        case "gitBranchName":
                            Globals.UpdateGitExecFp(this.GlobalSettings.GitDirectory);     // there is likely a better way to adjust the git path
                            return(Globals.GetGitBranchNameOrEmpty(solution));

                        case "workspaceName":
                            return(Globals.GetWorkspaceNameOrEmpty(solution));

                        case "workspaceOwnerName":
                            return(Globals.GetWorkspaceOwnerNameOrEmpty(solution));

                        case "documentName":
                            return(string.IsNullOrEmpty(documentName) ? windowName : documentName);

                        case "documentProjectName":
                            return(Globals.GetActiveDocumentProjectNameOrEmpty(activeDocument: activeDocument));

                        case "documentProjectFileName":
                            return(Globals.GetActiveDocumentProjectFileNameOrEmpty(activeDocument: activeDocument));

                        case "documentPath":
                            return(string.IsNullOrEmpty(documentName) ? windowName : documentPath);

                        case "vsMajorVersion":
                            return(Globals.VsMajorVersion.ToString(CultureInfo.InvariantCulture));

                        case "vsMajorVersionYear":
                            return(Globals.VsMajorVersionYear.ToString(CultureInfo.InvariantCulture));

                        case "ideName":
                            return(this.IDEName ?? string.Empty);

                        case "path":
                            return(string.IsNullOrEmpty(path) ? windowName : path);

                        case "parentPath":
                            return(GetParentPath(pathParts, cfg?.ClosestParentDepth ?? this.GlobalSettings.ClosestParentDepth, cfg?.FarthestParentDepth ?? this.GlobalSettings.FarthestParentDepth) ?? string.Empty);

                        default:
                            if (tag.StartsWith("parent"))
                            {
                                var m = RangeRegex.Match(tag.Substring("parent".Length));
                                if (m.Success)
                                {
                                    if (!pathParts.Any())
                                    {
                                        return(string.Empty);
                                    }
                                    var startIndex = Math.Min(pathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["startIndex"].Value, CultureInfo.InvariantCulture)));
                                    var endIndex   = Math.Min(pathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["endIndex"].Value, CultureInfo.InvariantCulture)));
                                    var pathRange  = pathParts.GetRange(startIndex: pathParts.Length - 1 - startIndex, endIndex: pathParts.Length - 1 - endIndex).ToArray();
                                    return(GetPathForTitle(pathRange));
                                }
                                m = IndexRegex.Match(tag.Substring("parent".Length));
                                if (m.Success)
                                {
                                    if (!pathParts.Any())
                                    {
                                        return(string.Empty);
                                    }
                                    var index = Math.Min(pathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["index"].Value, CultureInfo.InvariantCulture)));
                                    return(pathParts[pathParts.Length - 1 - index]);
                                }
                            }
                            if (tag.StartsWith("path"))
                            {
                                var m = RangeRegex.Match(tag.Substring("path".Length));
                                if (m.Success)
                                {
                                    if (!pathParts.Any())
                                    {
                                        return(string.Empty);
                                    }
                                    var startIndex = Math.Min(pathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["startIndex"].Value, CultureInfo.InvariantCulture)));
                                    var endIndex   = Math.Min(pathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["endIndex"].Value, CultureInfo.InvariantCulture)));
                                    var pathRange  = pathParts.GetRange(startIndex: startIndex, endIndex: endIndex).ToArray();
                                    return(GetPathForTitle(pathRange));
                                }
                                m = IndexRegex.Match(tag.Substring("path".Length));
                                if (m.Success)
                                {
                                    if (!pathParts.Any())
                                    {
                                        return(string.Empty);
                                    }
                                    var index = Math.Min(pathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["index"].Value, CultureInfo.InvariantCulture)));
                                    return(pathParts[index]);
                                }
                            }
                            if (tag.StartsWith("documentPath"))
                            {
                                var m = RangeRegex.Match(tag.Substring("documentPath".Length));
                                if (m.Success)
                                {
                                    if (!documentPathParts.Any())
                                    {
                                        return(string.Empty);
                                    }
                                    var startIndex = Math.Min(documentPathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["startIndex"].Value, CultureInfo.InvariantCulture)));
                                    var endIndex   = Math.Min(documentPathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["endIndex"].Value, CultureInfo.InvariantCulture)));
                                    var pathRange  = documentPathParts.GetRange(startIndex: startIndex, endIndex: endIndex).ToArray();
                                    return(GetPathForTitle(pathRange));
                                }
                                m = IndexRegex.Match(tag.Substring("documentPath".Length));
                                if (m.Success)
                                {
                                    if (!documentPathParts.Any())
                                    {
                                        return(string.Empty);
                                    }
                                    var index = Math.Min(documentPathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["index"].Value, CultureInfo.InvariantCulture)));
                                    return(documentPathParts[index]);
                                }
                            }
                            if (tag.StartsWith("documentParentPath"))
                            {
                                var m = RangeRegex.Match(tag.Substring("documentParentPath".Length));
                                if (m.Success)
                                {
                                    if (!documentPathParts.Any())
                                    {
                                        return(string.Empty);
                                    }
                                    var startIndex = Math.Min(documentPathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["startIndex"].Value, CultureInfo.InvariantCulture)));
                                    var endIndex   = Math.Min(documentPathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["endIndex"].Value, CultureInfo.InvariantCulture)));
                                    var pathRange  = documentPathParts.GetRange(startIndex: documentPathParts.Length - 1 - startIndex, endIndex: documentPathParts.Length - 1 - endIndex).ToArray();
                                    return(GetPathForTitle(pathRange));
                                }
                                m = IndexRegex.Match(tag.Substring("documentParentPath".Length));
                                if (m.Success)
                                {
                                    if (!documentPathParts.Any())
                                    {
                                        return(string.Empty);
                                    }
                                    var index = Math.Min(documentPathParts.Length - 1, Math.Max(0, int.Parse(m.Groups["index"].Value, CultureInfo.InvariantCulture)));
                                    return(documentPathParts[documentPathParts.Length - 1 - index]);
                                }
                            }
                            break;
                        }
                        return(match.Value);
                    }
                    catch (Exception ex) {
                        if (this.GlobalSettings.EnableDebugMode)
                        {
                            WriteOutput("ReplaceTag (" + tag + ") failed: " + ex);
                        }
                        throw;
                    }
                }
                catch {
                    return("");
                }
            });
            var appendedString = cfg?.AppendedString ?? this.GlobalSettings.AppendedString;

            return(pattern + " " + appendedString);
        }
        private void UpdateWindowTitle()
        {
            if (!Monitor.TryEnter(this.UpdateWindowTitleLock))
            {
                return;
            }
            try {
                var useDefaultPattern = true;
                if (this.GlobalSettings.AlwaysRewriteTitles)
                {
                    useDefaultPattern = false;
                }
                else
                {
                    Globals.VSMultiInstanceInfo info;
                    Globals.GetVSMultiInstanceInfo(out info);
                    if (info.nb_instances_same_solution >= 2)
                    {
                        useDefaultPattern = false;
                    }
                    else
                    {
                        var currentInstance = System.Diagnostics.Process.GetCurrentProcess();
                        var vsInstances     = System.Diagnostics.Process.GetProcessesByName("devenv");
                        if (vsInstances.Length >= 2)
                        {
                            //Check if multiple instances of devenv have identical original names. If so, then rewrite the title of current instance (normally the extension will run on each instance so no need to rewrite them as well). Otherwise do not rewrite the title.
                            //The best would be to get the EnvDTE.DTE object of the other instances, and compare the solution or project names directly instead of relying on window titles (which may be hacked by third party software as well). But using moniker it will only work if they are launched with the same privilege.
                            var currentInstanceName = Path.GetFileNameWithoutExtension(Globals.DTE.Solution.FullName);
                            if (string.IsNullOrEmpty(currentInstanceName) || (from vsInstance in vsInstances
                                                                              where vsInstance.Id != currentInstance.Id
                                                                              select this.GetVSSolutionName(vsInstance.MainWindowTitle)).Any(vsInstanceName => vsInstanceName != null && currentInstanceName == vsInstanceName))
                            {
                                useDefaultPattern = false;
                            }
                        }
                    }
                }
                var solution   = Globals.DTE.Solution;
                var solutionFp = solution?.FullName;

                var settings = this.GetSettings(solutionFp);

                var pattern = this.GetPattern(solutionFp, useDefaultPattern, settings);
                this.ChangeWindowTitle(this.GetNewTitle(solution, pattern, settings));
            }
            catch (Exception ex) {
                try {
                    if (this.GlobalSettings.EnableDebugMode)
                    {
                        WriteOutput("UpdateWindowTitle exception: " + ex);
                    }
                }
                catch {
                    // ignored
                }
            }
            finally {
                Monitor.Exit(this.UpdateWindowTitleLock);
            }
        }