{"action":"created","comment":{"url":"https://api.github.com/repos/dotnet/roslyn/pulls/comments/49887832","id":49887832,"diff_hunk":"@@ -111,53 +116,119 @@ public virtual bool ShouldFormatSnippet(SnippetInfo snippetInfo)\n \n         private void PopulateSnippetCaches()\n         {\n-            var updatedSnippets = GetSnippetInfoList();\n-            var updatedSnippetShortcuts = GetShortcutsHashFromSnippets(updatedSnippets);\n+            Debug.Assert(_expansionManager != null);\n \n-            lock (cacheGuard)\n-            {\n-                snippets = updatedSnippets;\n-                snippetShortcuts = updatedSnippetShortcuts;\n-            }\n-        }\n+            // Ideally we'd fork execution here based on whether the expansion manager is an\n+            // IExpansionManager or not. Unfortunately, we cannot mention that type by name until\n+            // the Roslyn build machines are upgraded to Visual Studio 2015 Update 1. We therefore\n+            // need to try using IExpansionManager dynamically, from a background thread. If that\n+            // fails, then we come back to the UI thread for using IVsExpansionManager instead.\n \n-        protected static HashSet<string> GetShortcutsHashFromSnippets(IList<SnippetInfo> updatedSnippets)\n-        {\n-            return new HashSet<string>(updatedSnippets.Select(s => s.Shortcut), StringComparer.OrdinalIgnoreCase);\n+            var token = _waiter.BeginAsyncOperation(GetType().Name + \".Start\");\n+\n+            Task.Factory.StartNew(async () => await PopulateSnippetCacheOnBackgroundWithForegroundFallback().ConfigureAwait(false),\n+                            CancellationToken.None,\n+                            TaskCreationOptions.None,\n+                            TaskScheduler.Default).CompletesAsyncOperation(token);\n         }\n \n-        private IList<SnippetInfo> GetSnippetInfoList()\n+        private async Task PopulateSnippetCacheOnBackgroundWithForegroundFallback()\n         {\n-            IVsExpansionEnumeration expansionEnumerator;\n-            if (TryGetVsSnippets(out expansionEnumerator))\n+            AssertIsBackground();\n+\n+            try\n             {\n-                return ExtractSnippetInfo(expansionEnumerator);\n+                IVsExpansionEnumeration expansionEnumerator = await ((dynamic)_expansionManager).EnumerateExpansionsAsync(\n+                    _languageGuidForSnippets,\n+                    0, // shortCutOnly\n+                    Array.Empty<string>(), // types\n+                    0, // countTypes\n+                    1, // includeNULLTypes\n+                    1 // includeDulicates: Allows snippets with the same title but different shortcuts\n+                    ).ConfigureAwait(false);\n+\n+                // The rest of the process requires being on the UI thread, see the explanation on \n+                // PopulateSnippetCacheFromExpansionEnumeration for details\n+                await Task.Factory.StartNew(() => PopulateSnippetCacheFromExpansionEnumeration(expansionEnumerator),\n+                    CancellationToken.None,\n+                    TaskCreationOptions.None,\n+                    ForegroundTaskScheduler).ConfigureAwait(false);\n+            }\n+            catch (RuntimeBinderException)\n+            {\n+                // The IExpansionManager.EnumerateExpansionsAsync could not be found. Use \n+                // IVsExpansionManager.EnumerateExpansions instead, but from the UI thread.\n+                await Task.Factory.StartNew(() => PopulateSnippetCacheOnForeground(),\n+                            CancellationToken.None,\n+                            TaskCreationOptions.None,\n+                            ForegroundTaskScheduler).ConfigureAwait(false);\n             }\n+        }\n \n-            return SpecializedCollections.EmptyList<SnippetInfo>();\n+        /// <remarks>\n+        /// Changes to the <see cref=\"IVsExpansionManager.EnumerateExpansions\"/> invocation\n+        /// should also be made to the IExpansionManager.EnumerateExpansionsAsync\n+        /// invocation in <see cref=\"PopulateSnippetCacheOnBackgroundWithForegroundFallback\"/>.\n+        /// </remarks>\n+        private void PopulateSnippetCacheOnForeground()\n+        {\n+            AssertIsForeground();\n+\n+            IVsExpansionEnumeration expansionEnumerator = null;\n+            _expansionManager.EnumerateExpansions(\n+                _languageGuidForSnippets,\n+                fShortCutOnly: 0,\n+                bstrTypes: null,\n+                iCountTypes: 0,\n+                fIncludeNULLType: 1,\n+                fIncludeDuplicates: 1, // Allows snippets with the same title but different shortcuts\n+                pEnum: out expansionEnumerator);\n+\n+            PopulateSnippetCacheFromExpansionEnumeration(expansionEnumerator);\n         }\n \n-        private bool TryGetVsSnippets(out IVsExpansionEnumeration expansionEnumerator)\n+        /// <remarks>\n+        /// This method must be called on the UI thread because it eventually calls into\n+        /// IVsExpansionEnumeration.Next, which must be called on the UI thread due to an issue\n+        /// with how the call is marshalled.\n+        /// \n+        /// The second parameter for IVsExpansionEnumeration.Next is defined like this:","path":"src/VisualStudio/Core/Def/Implementation/Snippets/AbstractSnippetInfoService.cs","position":240,"original_position":240,"commit_id":"0a4d136a8b17e2f31e9249bce6a57ec620d070db","original_commit_id":"0a4d136a8b17e2f31e9249bce6a57ec620d070db","user":{"login":"tinaschrepfer","id":11824926,"avatar_url":"https://avatars.githubusercontent.com/u/11824926?v=3","gravatar_id":"","url":"https://api.github.com/users/tinaschrepfer","html_url":"https://github.com/tinaschrepfer","followers_url":"https://api.github.com/users/tinaschrepfer/followers","following_url":"https://api.github.com/users/tinaschrepfer/following{/other_user}","gists_url":"https://api.github.com/users/tinaschrepfer/gists{/gist_id}","starred_url":"https://api.github.com/users/tinaschrepfer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tinaschrepfer/subscriptions","organizations_url":"https://api.github.com/users/tinaschrepfer/orgs","repos_url":"https://api.github.com/users/tinaschrepfer/repos","events_url":"https://api.github.com/users/tinaschrepfer/events{/privacy}","received_events_url":"https://api.github.com/users/tinaschrepfer/received_events","type":"User","site_admin":false},"body":"@dpoeschl @Pilchie , yup!","created_at":"2016-01-15T18:36:17Z","updated_at":"2016-01-15T18:36:17Z","html_url":"https://github.com/dotnet/roslyn/pull/7935#discussion_r49887832","pull_request_url":"https://api.github.com/repos/dotnet/roslyn/pulls/7935","_links":{"self":{"href":"https://api.github.com/repos/dotnet/roslyn/pulls/comments/49887832"},"html":{"href":"https://github.com/dotnet/roslyn/pull/7935#discussion_r49887832"},"pull_request":{"href":"https://api.github.com/repos/dotnet/roslyn/pulls/7935"}}},"pull_request":{"url":"https://api.github.com/repos/dotnet/roslyn/pulls/7935","id":55951529,"html_url":"https://github.com/dotnet/roslyn/pull/7935","diff_url":"https://github.com/dotnet/roslyn/pull/7935.diff","patch_url":"https://github.com/dotnet/roslyn/pull/7935.patch","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/7935","number":7935,"state":"open","locked":false,"title":"Consume asynchronous snippet enumeration API","user":{"login":"dpoeschl","id":235241,"avatar_url":"https://avatars.githubusercontent.com/u/235241?v=3","gravatar_id":"","url":"https://api.github.com/users/dpoeschl","html_url":"https://github.com/dpoeschl","followers_url":"https://api.github.com/users/dpoeschl/followers","following_url":"https://api.github.com/users/dpoeschl/following{/other_user}","gists_url":"https://api.github.com/users/dpoeschl/gists{/gist_id}","starred_url":"https://api.github.com/users/dpoeschl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dpoeschl/subscriptions","organizations_url":"https://api.github.com/users/dpoeschl/orgs","repos_url":"https://api.github.com/users/dpoeschl/repos","events_url":"https://api.github.com/users/dpoeschl/events{/privacy}","received_events_url":"https://api.github.com/users/dpoeschl/received_events","type":"User","site_admin":false},"body":"Fixes #6685\r\n\r\nCalling IVsTextManager2.GetExpansionManager can return either an IVsExpansionManager or an IExpansionManager, and we now support reading snippet info from both.\r\n\r\nIn the IVsExpansionManager case, we behave as before this change. During package load, we kick off a task to execute later on the UI thread to synchronously enumerate the snippets.\r\n\r\nIn the IExpansionManager case, we instead enumerate the snippets asynchronously on a background thread.\r\n\r\nFor Visual Studio 2015 Update 1 RTM, we will only receive an IVsExpansionManager. Starting with Update 2 (and with some intermediate builds) we will receive IExpansionManagers by default, but the platform reserves the right to return an IVsExpansionManager as a fallback, so we must continue to support both. Because IExpansionManager already existed in Update 1, we do not need to do adaptive light-up to keep this code working on Update 1.","created_at":"2016-01-13T20:32:54Z","updated_at":"2016-01-15T18:36:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9cb8d11f5b10f93a7717448a800117704362ef91","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/dotnet/roslyn/pulls/7935/commits","review_comments_url":"https://api.github.com/repos/dotnet/roslyn/pulls/7935/comments","review_comment_url":"https://api.github.com/repos/dotnet/roslyn/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/7935/comments","statuses_url":"https://api.github.com/repos/dotnet/roslyn/statuses/0a4d136a8b17e2f31e9249bce6a57ec620d070db","head":{"label":"dpoeschl:AsyncSnippets","ref":"AsyncSnippets","sha":"0a4d136a8b17e2f31e9249bce6a57ec620d070db","user":{"login":"dpoeschl","id":235241,"avatar_url":"https://avatars.githubusercontent.com/u/235241?v=3","gravatar_id":"","url":"https://api.github.com/users/dpoeschl","html_url":"https://github.com/dpoeschl","followers_url":"https://api.github.com/users/dpoeschl/followers","following_url":"https://api.github.com/users/dpoeschl/following{/other_user}","gists_url":"https://api.github.com/users/dpoeschl/gists{/gist_id}","starred_url":"https://api.github.com/users/dpoeschl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dpoeschl/subscriptions","organizations_url":"https://api.github.com/users/dpoeschl/orgs","repos_url":"https://api.github.com/users/dpoeschl/repos","events_url":"https://api.github.com/users/dpoeschl/events{/privacy}","received_events_url":"https://api.github.com/users/dpoeschl/received_events","type":"User","site_admin":false},"repo":{"id":30046829,"name":"roslyn","full_name":"dpoeschl/roslyn","owner":{"login":"dpoeschl","id":235241,"avatar_url":"https://avatars.githubusercontent.com/u/235241?v=3","gravatar_id":"","url":"https://api.github.com/users/dpoeschl","html_url":"https://github.com/dpoeschl","followers_url":"https://api.github.com/users/dpoeschl/followers","following_url":"https://api.github.com/users/dpoeschl/following{/other_user}","gists_url":"https://api.github.com/users/dpoeschl/gists{/gist_id}","starred_url":"https://api.github.com/users/dpoeschl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dpoeschl/subscriptions","organizations_url":"https://api.github.com/users/dpoeschl/orgs","repos_url":"https://api.github.com/users/dpoeschl/repos","events_url":"https://api.github.com/users/dpoeschl/events{/privacy}","received_events_url":"https://api.github.com/users/dpoeschl/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dpoeschl/roslyn","description":"The .NET Compiler Platform (\"Roslyn\") provides open-source C# and Visual Basic compilers with rich code analysis APIs.","fork":true,"url":"https://api.github.com/repos/dpoeschl/roslyn","forks_url":"https://api.github.com/repos/dpoeschl/roslyn/forks","keys_url":"https://api.github.com/repos/dpoeschl/roslyn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dpoeschl/roslyn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dpoeschl/roslyn/teams","hooks_url":"https://api.github.com/repos/dpoeschl/roslyn/hooks","issue_events_url":"https://api.github.com/repos/dpoeschl/roslyn/issues/events{/number}","events_url":"https://api.github.com/repos/dpoeschl/roslyn/events","assignees_url":"https://api.github.com/repos/dpoeschl/roslyn/assignees{/user}","branches_url":"https://api.github.com/repos/dpoeschl/roslyn/branches{/branch}","tags_url":"https://api.github.com/repos/dpoeschl/roslyn/tags","blobs_url":"https://api.github.com/repos/dpoeschl/roslyn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dpoeschl/roslyn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dpoeschl/roslyn/git/refs{/sha}","trees_url":"https://api.github.com/repos/dpoeschl/roslyn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dpoeschl/roslyn/statuses/{sha}","languages_url":"https://api.github.com/repos/dpoeschl/roslyn/languages","stargazers_url":"https://api.github.com/repos/dpoeschl/roslyn/stargazers","contributors_url":"https://api.github.com/repos/dpoeschl/roslyn/contributors","subscribers_url":"https://api.github.com/repos/dpoeschl/roslyn/subscribers","subscription_url":"https://api.github.com/repos/dpoeschl/roslyn/subscription","commits_url":"https://api.github.com/repos/dpoeschl/roslyn/commits{/sha}","git_commits_url":"https://api.github.com/repos/dpoeschl/roslyn/git/commits{/sha}","comments_url":"https://api.github.com/repos/dpoeschl/roslyn/comments{/number}","issue_comment_url":"https://api.github.com/repos/dpoeschl/roslyn/issues/comments{/number}","contents_url":"https://api.github.com/repos/dpoeschl/roslyn/contents/{+path}","compare_url":"https://api.github.com/repos/dpoeschl/roslyn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dpoeschl/roslyn/merges","archive_url":"https://api.github.com/repos/dpoeschl/roslyn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dpoeschl/roslyn/downloads","issues_url":"https://api.github.com/repos/dpoeschl/roslyn/issues{/number}","pulls_url":"https://api.github.com/repos/dpoeschl/roslyn/pulls{/number}","milestones_url":"https://api.github.com/repos/dpoeschl/roslyn/milestones{/number}","notifications_url":"https://api.github.com/repos/dpoeschl/roslyn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dpoeschl/roslyn/labels{/name}","releases_url":"https://api.github.com/repos/dpoeschl/roslyn/releases{/id}","created_at":"2015-01-29T23:37:29Z","updated_at":"2015-03-18T18:28:52Z","pushed_at":"2016-01-15T00:03:52Z","git_url":"git://github.com/dpoeschl/roslyn.git","ssh_url":"git@github.com:dpoeschl/roslyn.git","clone_url":"https://github.com/dpoeschl/roslyn.git","svn_url":"https://github.com/dpoeschl/roslyn","homepage":"","size":121268,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"dotnet:master","ref":"master","sha":"eae40a7aa05c9afac4f7e0abfbc740da069b385b","user":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"repo":{"id":29078997,"name":"roslyn","full_name":"dotnet/roslyn","owner":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dotnet/roslyn","description":"The .NET Compiler Platform (\"Roslyn\") provides open-source C# and Visual Basic compilers with rich code analysis APIs.","fork":false,"url":"https://api.github.com/repos/dotnet/roslyn","forks_url":"https://api.github.com/repos/dotnet/roslyn/forks","keys_url":"https://api.github.com/repos/dotnet/roslyn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dotnet/roslyn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dotnet/roslyn/teams","hooks_url":"https://api.github.com/repos/dotnet/roslyn/hooks","issue_events_url":"https://api.github.com/repos/dotnet/roslyn/issues/events{/number}","events_url":"https://api.github.com/repos/dotnet/roslyn/events","assignees_url":"https://api.github.com/repos/dotnet/roslyn/assignees{/user}","branches_url":"https://api.github.com/repos/dotnet/roslyn/branches{/branch}","tags_url":"https://api.github.com/repos/dotnet/roslyn/tags","blobs_url":"https://api.github.com/repos/dotnet/roslyn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dotnet/roslyn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dotnet/roslyn/git/refs{/sha}","trees_url":"https://api.github.com/repos/dotnet/roslyn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dotnet/roslyn/statuses/{sha}","languages_url":"https://api.github.com/repos/dotnet/roslyn/languages","stargazers_url":"https://api.github.com/repos/dotnet/roslyn/stargazers","contributors_url":"https://api.github.com/repos/dotnet/roslyn/contributors","subscribers_url":"https://api.github.com/repos/dotnet/roslyn/subscribers","subscription_url":"https://api.github.com/repos/dotnet/roslyn/subscription","commits_url":"https://api.github.com/repos/dotnet/roslyn/commits{/sha}","git_commits_url":"https://api.github.com/repos/dotnet/roslyn/git/commits{/sha}","comments_url":"https://api.github.com/repos/dotnet/roslyn/comments{/number}","issue_comment_url":"https://api.github.com/repos/dotnet/roslyn/issues/comments{/number}","contents_url":"https://api.github.com/repos/dotnet/roslyn/contents/{+path}","compare_url":"https://api.github.com/repos/dotnet/roslyn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dotnet/roslyn/merges","archive_url":"https://api.github.com/repos/dotnet/roslyn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dotnet/roslyn/downloads","issues_url":"https://api.github.com/repos/dotnet/roslyn/issues{/number}","pulls_url":"https://api.github.com/repos/dotnet/roslyn/pulls{/number}","milestones_url":"https://api.github.com/repos/dotnet/roslyn/milestones{/number}","notifications_url":"https://api.github.com/repos/dotnet/roslyn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dotnet/roslyn/labels{/name}","releases_url":"https://api.github.com/repos/dotnet/roslyn/releases{/id}","created_at":"2015-01-11T02:39:03Z","updated_at":"2016-01-15T16:34:08Z","pushed_at":"2016-01-15T18:35:12Z","git_url":"git://github.com/dotnet/roslyn.git","ssh_url":"git@github.com:dotnet/roslyn.git","clone_url":"https://github.com/dotnet/roslyn.git","svn_url":"https://github.com/dotnet/roslyn","homepage":"","size":149644,"stargazers_count":4227,"watchers_count":4227,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1035,"mirror_url":null,"open_issues_count":1892,"forks":1035,"open_issues":1892,"watchers":4227,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dotnet/roslyn/pulls/7935"},"html":{"href":"https://github.com/dotnet/roslyn/pull/7935"},"issue":{"href":"https://api.github.com/repos/dotnet/roslyn/issues/7935"},"comments":{"href":"https://api.github.com/repos/dotnet/roslyn/issues/7935/comments"},"review_comments":{"href":"https://api.github.com/repos/dotnet/roslyn/pulls/7935/comments"},"review_comment":{"href":"https://api.github.com/repos/dotnet/roslyn/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dotnet/roslyn/pulls/7935/commits"},"statuses":{"href":"https://api.github.com/repos/dotnet/roslyn/statuses/0a4d136a8b17e2f31e9249bce6a57ec620d070db"}}}}