├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── Common.props ├── ISSUE_TEMPLATE.md ├── LICENSE ├── NuGet.config ├── ProjectFileTools.sln ├── README.md ├── art ├── FooDefinitions.png ├── SdkImports.png ├── completion-name.png ├── completion-version.png └── tooltip.png ├── build.cmd ├── build ├── Signing │ └── Signing.props └── Strong Name Keys │ └── 35MSSharedLib1024.snk ├── netci.groovy ├── src ├── ProjectFileTools.MSBuild │ ├── Definition.cs │ ├── IWorkspace.cs │ ├── IWorkspaceManager.cs │ ├── ProjectFileTools.MSBuild.csproj │ ├── SyntaxNames.cs │ ├── Utilities.cs │ ├── Workspace.cs │ └── WorkspaceManager.cs ├── ProjectFileTools.NuGetSearch │ ├── Contracts │ │ ├── IDependencyManager.cs │ │ ├── IPackageFeed.cs │ │ ├── IPackageFeedFactory.cs │ │ ├── IPackageFeedFactorySelector.cs │ │ ├── IPackageFeedRegistryProvider.cs │ │ ├── IPackageFeedSearchJob.cs │ │ ├── IPackageFeedSearcher.cs │ │ ├── IPackageInfo.cs │ │ ├── IPackageNameSearchResult.cs │ │ ├── IPackageQueryConfiguration.cs │ │ ├── IPackageSearchManager.cs │ │ └── IPackageVersionSearchResult.cs │ ├── Feeds │ │ ├── Disk │ │ │ ├── NuGetDiskFeedFactory.cs │ │ │ ├── NuGetPackageMatcher.cs │ │ │ ├── NuGetV2DiskFeed.cs │ │ │ └── NuGetV3DiskFeed.cs │ │ ├── FeedKind.cs │ │ ├── NuSpecReader.cs │ │ ├── PackageFeedFactoryBase.cs │ │ ├── PackageFeedFactorySelector.cs │ │ ├── PackageInfo.cs │ │ ├── PackageQueryConfiguration.cs │ │ └── Web │ │ │ ├── NuGetV2ServiceFeed.cs │ │ │ └── NuGetV3ServiceFeed.cs │ ├── IO │ │ ├── FileSystem.cs │ │ ├── IFileSystem.cs │ │ ├── IWebRequestFactory.cs │ │ ├── WebRequestFactory.cs │ │ └── WebRequestFactoryExtensions.cs │ ├── ProjectFileTools.NuGetSearch.csproj │ ├── Properties │ │ └── PublishProfiles │ │ │ └── FolderProfile.pubxml │ ├── Search │ │ ├── PackageFeedSearchJob.cs │ │ ├── PackageNameSearchResult.cs │ │ ├── PackageSearchManager.cs │ │ └── PackageVersionSearchResult.cs │ └── SemanticVersion.cs └── ProjectFileTools │ ├── Adornments │ ├── HighlightWordFormatDefinition.cs │ ├── HighlightWordTag.cs │ ├── HighlightWordTagger.cs │ └── HighlightWordTaggerProvider.cs │ ├── Completion │ ├── CompletionController.cs │ ├── CompletionTooltipElementProvider.cs │ ├── PackageCompletion.cs │ ├── PackageCompletionSet.cs │ ├── PackageCompletionSource.cs │ ├── PackageCompletionSourceProvider.cs │ ├── PackageIntellisenseController.cs │ ├── PackageIntellisenseControllerProvider.cs │ └── VersionCompletion.cs │ ├── Exports │ ├── ExportedFileSystem.cs │ ├── ExportedNuGetDiskFeedFactory.cs │ ├── ExportedNuGetV2ServiceFeedFactory.cs │ ├── ExportedNuGetV3ServiceFeedFactory.cs │ ├── ExportedPackageFeedFactorySelector.cs │ ├── ExportedPackageSearchManager.cs │ ├── ExportedWebRequestFactory.cs │ └── ExportedWorkspaceManager.cs │ ├── FindAllReferences │ ├── FarDataSnapshot.cs │ ├── FarDataSource.cs │ ├── FarDataSubscription.cs │ └── FarDefinitionBucket.cs │ ├── GoToDefinition │ └── GoToDefinitionController.cs │ ├── Helpers │ ├── XmlInfo.cs │ └── XmlTools.cs │ ├── PackageFeedRegistryProvider.cs │ ├── PackageInfoControl.xaml │ ├── PackageInfoControl.xaml.cs │ ├── ProjectFileTools.csproj │ ├── ProjectFileToolsPackage.cs │ ├── Properties │ └── launchSettings.json │ ├── QuickInfo │ └── QuickInfoProvider.cs │ ├── Resources │ └── Icon.png │ ├── ServiceUtil.cs │ ├── TextViewCreationListener.cs │ ├── Theme.cs │ ├── WpfUtil.cs │ ├── app.config │ ├── extension.vsixmanifest │ ├── project.json │ ├── source.extension.cs │ ├── source.extension.ico │ ├── source.extension.resx │ └── source.extension.vsixmanifest └── test └── ProjectFileTools.NuGetSearch.Tests ├── Mocks ├── MockFileSystem.cs └── MockWebRequestFactory.cs ├── NuGetV2ServiceFeedTests.cs ├── ProjectFileTools.NuGetSearch.Tests.csproj ├── TestFiles ├── GetPackageInfo.CommonLogging.xml ├── GetPackageNames.CommonLogging.xml └── GetPackageVersions.CommonLogging.xml └── UnitTest1.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | packages 2 | 3 | # User files 4 | *.suo 5 | *.user 6 | *.sln.docstates 7 | .vs/ 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Rr]elease/ 12 | x64/ 13 | [Bb]in/ 14 | [Oo]bj/ 15 | *.binlog 16 | 17 | # MSTest test Results 18 | [Tt]est[Rr]esult*/ 19 | [Bb]uild[Ll]og.* 20 | 21 | # NuGet restore 22 | *.nuget.props 23 | *.nuget.targets 24 | project.lock.json 25 | 26 | # NCrunch 27 | *.ncrunchsolution 28 | *.ncrunchproject 29 | _NCrunch_WebCompiler -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Road map 2 | 3 | - [x] A feature that has been completed 4 | - [ ] A feature that has NOT yet been completed 5 | 6 | Features that have a checkmark are complete and available for 7 | download in the 8 | [CI build](http://vsixgallery.com/extension/ProjPackageIntellisense.Mike Lorbetske.e22f3d7e-6b01-4ef8-b3cb-ea293a87b00f/). 9 | 10 | # Change log 11 | 12 | These are the changes to each version that has been released 13 | on the official Visual Studio extension gallery. 14 | 15 | ## 1.4 16 | - [x] Implement NuGet V2 Feed support 17 | - [x] Change WebRequestFactory to use a static HttpClient to avoid connection socket exhaustion problem 18 | 19 | ## 1.1 20 | 21 | - [x] Go To Definition (F12) for MSBuild imports 22 | - [x] Go To Definition (F12) for MSBuild properties 23 | 24 | ## 1.0 25 | 26 | - [x] Initial release 27 | - [x] Intellisense for NuGet package name and version 28 | - [x] Hover tooltips for NuGet packages 29 | 30 | ## Template 31 | - [x] Initial release 32 | - [x] Feature 1 33 | - [x] Feature 2 34 | - [x] Sub feature -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the code of conduct defined by the Contributor Covenant 4 | to clarify expected behavior in our community. 5 | 6 | For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Looking to contribute something? **Here's how you can help.** 4 | 5 | Please take a moment to review this document in order to make the contribution 6 | process easy and effective for everyone involved. 7 | 8 | Following these guidelines helps to communicate that you respect the time of 9 | the developers managing and developing this open source project. In return, 10 | they should reciprocate that respect in addressing your issue or assessing 11 | patches and features. 12 | 13 | 14 | ## Using the issue tracker 15 | 16 | The issue tracker is the preferred channel for [bug reports](#bug-reports), 17 | [features requests](#feature-requests) and 18 | [submitting pull requests](#pull-requests), but please respect the 19 | following restrictions: 20 | 21 | * Please **do not** use the issue tracker for personal support requests. Stack 22 | Overflow is a better place to get help. 23 | 24 | * Please **do not** derail or troll issues. Keep the discussion on topic and 25 | respect the opinions of others. 26 | 27 | * Please **do not** open issues or pull requests which *belongs to* third party 28 | components. 29 | 30 | 31 | ## Bug reports 32 | 33 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 34 | Good bug reports are extremely helpful, so thanks! 35 | 36 | Guidelines for bug reports: 37 | 38 | 1. **Use the GitHub issue search** — check if the issue has already been 39 | reported. 40 | 41 | 2. **Check if the issue has been fixed** — try to reproduce it using the 42 | latest `master` or development branch in the repository. 43 | 44 | 3. **Isolate the problem** — ideally create an 45 | [SSCCE](http://www.sscce.org/) and a live example. 46 | Uploading the project on cloud storage (OneDrive, DropBox, et el.) 47 | or creating a sample GitHub repository is also helpful. 48 | 49 | 50 | A good bug report shouldn't leave others needing to chase you up for more 51 | information. Please try to be as detailed as possible in your report. What is 52 | your environment? What steps will reproduce the issue? What browser(s) and OS 53 | experience the problem? Do other browsers show the bug differently? What 54 | would you expect to be the outcome? All these details will help people to fix 55 | any potential bugs. 56 | 57 | Example: 58 | 59 | > Short and descriptive example bug report title 60 | > 61 | > A summary of the issue and the Visual Studio, browser, OS environments 62 | > in which it occurs. If suitable, include the steps required to reproduce the bug. 63 | > 64 | > 1. This is the first step 65 | > 2. This is the second step 66 | > 3. Further steps, etc. 67 | > 68 | > `` - a link to the project/file uploaded on cloud storage or other publicly accessible medium. 69 | > 70 | > Any other information you want to share that is relevant to the issue being 71 | > reported. This might include the lines of code that you have identified as 72 | > causing the bug, and potential solutions (and your opinions on their 73 | > merits). 74 | 75 | 76 | ## Feature requests 77 | 78 | Feature requests are welcome. But take a moment to find out whether your idea 79 | fits with the scope and aims of the project. It's up to *you* to make a strong 80 | case to convince the project's developers of the merits of this feature. Please 81 | provide as much detail and context as possible. 82 | 83 | 84 | ## Pull requests 85 | 86 | Good pull requests, patches, improvements and new features are a fantastic 87 | help. They should remain focused in scope and avoid containing unrelated 88 | commits. 89 | 90 | **Please ask first** before embarking on any significant pull request (e.g. 91 | implementing features, refactoring code, porting to a different language), 92 | otherwise you risk spending a lot of time working on something that the 93 | project's developers might not want to merge into the project. 94 | 95 | Please adhere to the [coding guidelines](#code-guidelines) used throughout the 96 | project (indentation, accurate comments, etc.) and any other requirements 97 | (such as test coverage). 98 | 99 | Adhering to the following process is the best way to get your work 100 | included in the project: 101 | 102 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 103 | and configure the remotes: 104 | 105 | ```bash 106 | # Clone your fork of the repo into the current directory 107 | git clone https://github.com//.git 108 | # Navigate to the newly cloned directory 109 | cd 110 | # Assign the original repo to a remote called "upstream" 111 | git remote add upstream https://github.com/madskristensen/.git 112 | ``` 113 | 114 | 2. If you cloned a while ago, get the latest changes from upstream: 115 | 116 | ```bash 117 | git checkout master 118 | git pull upstream master 119 | ``` 120 | 121 | 3. Create a new topic branch (off the main project development branch) to 122 | contain your feature, change, or fix: 123 | 124 | ```bash 125 | git checkout -b 126 | ``` 127 | 128 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 129 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 130 | or your code is unlikely be merged into the main project. Use Git's 131 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 132 | feature to tidy up your commits before making them public. Also, prepend name of the feature 133 | to the commit message. For instance: "SCSS: Fixes compiler results for IFileListener.\nFixes `#123`" 134 | 135 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 136 | 137 | ```bash 138 | git pull [--rebase] upstream master 139 | ``` 140 | 141 | 6. Push your topic branch up to your fork: 142 | 143 | ```bash 144 | git push origin 145 | ``` 146 | 147 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 148 | with a clear title and description against the `master` branch. 149 | 150 | 151 | ## Code guidelines 152 | 153 | - Always use proper indentation. 154 | - In Visual Studio under `Tools > Options > Text Editor > C# > Advanced`, make sure 155 | `Place 'System' directives first when sorting usings` option is enabled (checked). 156 | - Before committing, organize usings for each updated C# source file. Either you can 157 | right-click editor and select `Organize Usings > Remove and sort` OR use extension 158 | like [BatchFormat](http://visualstudiogallery.msdn.microsoft.com/a7f75c34-82b4-4357-9c66-c18e32b9393e). 159 | - Before committing, run Code Analysis in `Debug` configuration and follow the guidelines 160 | to fix CA issues. Code Analysis commits can be made separately. 161 | -------------------------------------------------------------------------------- /Common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildThisFileDirectory) 5 | Debug 6 | 7 | $([System.IO.Path]::GetFullPath('$(RepositoryRootDirectory)bin\$(Configuration)'))\ 8 | $(OutputPath) 9 | 10 | 11 | 12 | $(OutputPath) 13 | 14 | 15 | 16 | $(OutputPath)\net46 17 | 18 | 19 | 20 | $(OutputPath)\netstandard1.4 21 | 22 | 23 | 24 | 25 | true 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Installed product versions 2 | - Visual Studio: [example 2015 Professional] 3 | - This extension: [example 1.1.21] 4 | 5 | ### Description 6 | Replace this text with a short description 7 | 8 | ### Steps to recreate 9 | 1. Replace this 10 | 2. text with 11 | 3. the steps 12 | 4. to recreate 13 | 14 | ### Current behavior 15 | Explain what it's doing and why it's wrong 16 | 17 | ### Expected behavior 18 | Explain what it should be doing after it's fixed. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 .NET Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /ProjectFileTools.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31803.25 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectFileTools", "src\ProjectFileTools\ProjectFileTools.csproj", "{4FE11F73-0836-4630-9B92-712D0482CFCB}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectFileTools.NuGetSearch", "src\ProjectFileTools.NuGetSearch\ProjectFileTools.NuGetSearch.csproj", "{7129446A-7769-4753-9BF1-5E79CE1CB4C4}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectFileTools.NuGetSearch.Tests", "test\ProjectFileTools.NuGetSearch.Tests\ProjectFileTools.NuGetSearch.Tests.csproj", "{BE6FCD51-F6DC-4E85-97E1-DEC914744398}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2EDA15B3-7D5D-4ACA-A053-8D281CE42B20}" 13 | ProjectSection(SolutionItems) = preProject 14 | .gitattributes = .gitattributes 15 | .gitignore = .gitignore 16 | build.cmd = build.cmd 17 | CHANGELOG.md = CHANGELOG.md 18 | Common.props = Common.props 19 | CONTRIBUTING.md = CONTRIBUTING.md 20 | ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md 21 | LICENSE = LICENSE 22 | NuGet.config = NuGet.config 23 | README.md = README.md 24 | EndProjectSection 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1479D323-9D5A-4D0C-84BE-8A7F9FC8BD8E}" 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{55FC45A3-11F4-49F0-AEB4-F43145397E43}" 29 | EndProject 30 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9AB88CF8-3AED-42E1-845B-97978A9A7A5C}" 31 | EndProject 32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Signing", "Signing", "{7A3C8AE5-1C1C-4087-B5E2-B61BC1970FDE}" 33 | ProjectSection(SolutionItems) = preProject 34 | build\Signing\Signing.props = build\Signing\Signing.props 35 | EndProjectSection 36 | EndProject 37 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StrongNameKeys", "StrongNameKeys", "{01F0EC18-761B-4FEC-B9A0-39AB17167B58}" 38 | ProjectSection(SolutionItems) = preProject 39 | build\Strong Name Keys\35MSSharedLib1024.snk = build\Strong Name Keys\35MSSharedLib1024.snk 40 | EndProjectSection 41 | EndProject 42 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectFileTools.MSBuild", "src\ProjectFileTools.MSBuild\ProjectFileTools.MSBuild.csproj", "{C2C10E1F-A307-49FC-9D9A-106FCBE88B91}" 43 | EndProject 44 | Global 45 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 46 | Debug|Any CPU = Debug|Any CPU 47 | Release|Any CPU = Release|Any CPU 48 | EndGlobalSection 49 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 50 | {4FE11F73-0836-4630-9B92-712D0482CFCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {4FE11F73-0836-4630-9B92-712D0482CFCB}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {4FE11F73-0836-4630-9B92-712D0482CFCB}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {4FE11F73-0836-4630-9B92-712D0482CFCB}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {7129446A-7769-4753-9BF1-5E79CE1CB4C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {7129446A-7769-4753-9BF1-5E79CE1CB4C4}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {7129446A-7769-4753-9BF1-5E79CE1CB4C4}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {7129446A-7769-4753-9BF1-5E79CE1CB4C4}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {BE6FCD51-F6DC-4E85-97E1-DEC914744398}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {BE6FCD51-F6DC-4E85-97E1-DEC914744398}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {BE6FCD51-F6DC-4E85-97E1-DEC914744398}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {BE6FCD51-F6DC-4E85-97E1-DEC914744398}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {C2C10E1F-A307-49FC-9D9A-106FCBE88B91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {C2C10E1F-A307-49FC-9D9A-106FCBE88B91}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {C2C10E1F-A307-49FC-9D9A-106FCBE88B91}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {C2C10E1F-A307-49FC-9D9A-106FCBE88B91}.Release|Any CPU.Build.0 = Release|Any CPU 66 | EndGlobalSection 67 | GlobalSection(SolutionProperties) = preSolution 68 | HideSolutionNode = FALSE 69 | EndGlobalSection 70 | GlobalSection(NestedProjects) = preSolution 71 | {4FE11F73-0836-4630-9B92-712D0482CFCB} = {1479D323-9D5A-4D0C-84BE-8A7F9FC8BD8E} 72 | {7129446A-7769-4753-9BF1-5E79CE1CB4C4} = {1479D323-9D5A-4D0C-84BE-8A7F9FC8BD8E} 73 | {BE6FCD51-F6DC-4E85-97E1-DEC914744398} = {55FC45A3-11F4-49F0-AEB4-F43145397E43} 74 | {7A3C8AE5-1C1C-4087-B5E2-B61BC1970FDE} = {9AB88CF8-3AED-42E1-845B-97978A9A7A5C} 75 | {01F0EC18-761B-4FEC-B9A0-39AB17167B58} = {9AB88CF8-3AED-42E1-845B-97978A9A7A5C} 76 | {C2C10E1F-A307-49FC-9D9A-106FCBE88B91} = {1479D323-9D5A-4D0C-84BE-8A7F9FC8BD8E} 77 | EndGlobalSection 78 | GlobalSection(ExtensibilityGlobals) = postSolution 79 | SolutionGuid = {2FE037C9-333A-496F-B7ED-746049B8B2D0} 80 | EndGlobalSection 81 | EndGlobal 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProjFileTools Archived 2 | 3 | This project was an experiment for older versions of Visual Studio that we have not carried forward in the same manner. 4 | 5 | As part of creating transparency of the state of the repo, we are archiving this repo in current state. All issues will remain unresolved with regard to this source code. 6 | 7 | For alternative extensions that are more recent we recommend looking at [MSBuild Editor](https://marketplace.visualstudio.com/items?itemName=mhutch.MSBuildEditor). 8 | 9 | In addition, you may log feature suggestions on [Developer Community](https://developercommunity.visualstudio.com/VisualStudio) that can be upvoted by others to help us in our prioritization of different features that go into Visual Studio. 10 | 11 | 12 | 13 | --------------------------------------- 14 | 15 | # Project File Tools 16 | 17 | 18 | [![Build status](https://ci.appveyor.com/api/projects/status/hv6uyc059rqbc6fj?svg=true)](https://ci.appveyor.com/project/madskristensen/extensibilitytools) 19 | 20 | 21 | Download this extension from the [VS Gallery](https://aka.ms/projfiletools). 22 | 23 | --------------------------------------- 24 | 25 | Provides Intellisense and other tooling for XML based project files such as .csproj and .vbproj files. 26 | 27 | See the [change log](CHANGELOG.md) for changes and road map. 28 | 29 | ## Features 30 | 31 | - Intellisense for NuGet package name and version 32 | - Hover tooltips for NuGet packages 33 | - Go To Definition (F12) for MSBuild imports 34 | - Go To Definition (F12) for MSBuild properties 35 | 36 | ### Intellisense 37 | Full Intellisense for NuGet package references is provided for both packages that are locally cached as well as packages defined in any feed - local and online. 38 | 39 | ![Tooltip](art/completion-name.png) 40 | 41 | ![Tooltip](art/completion-version.png) 42 | 43 | ### Hover tooltips 44 | Hovering over a package reference shows details about that package. 45 | 46 | ![Tooltip](art/tooltip.png) 47 | 48 | ### Go To Definition (F12) for MSBuild imports 49 | Set the caret on any import statement and press F12, Visual Studio will automatically open the imported file. 50 | 51 | It supports both the traditional `Import` `Project` construct 52 | ```XML 53 | 54 | ``` 55 | as well as the new `SDK` attribute used by .Net Core projects: 56 | ```XML 57 | 58 | ... 59 | 60 | ``` 61 | 62 | It supports evaluation of MSBuild properties and globs. 63 | 64 | When the import statement covers multiple files (which is typically the case with the `SDK` attribute or when using globs), it opens the a window that displays the set of files included. In that list you can: 65 | - click on a file to open it in Visual Studio 66 | - hover the file name and the tooltip will display the full path 67 | 68 | ![Tooltip](art/SdkImports.png) 69 | 70 | ### Go To Definition (F12) for MSBuild properties 71 | 72 | Pressing F12 when caret is on the usage of a MSBuild property will open the location where that property is defined. If multiple definitions are found, it opens a window that lists all the definitions and their evaluated values. 73 | 74 | For example: 75 | ```XML 76 | 77 | abc 78 | def 79 | $(Foo) 80 | 81 | ``` 82 | Pressing F12 on `$(Foo)` in this case will lists both places where `Foo` is defined, including the evaluated values that get set (`abc`, `def`). 83 | 84 | ![Tooltip](art/FooDefinitions.png) 85 | 86 | Selecting an item from the list, will open the corresponding location in the editor. 87 | 88 | ## Contribute 89 | Check out the [contribution guidelines](CONTRIBUTING.md) 90 | if you want to contribute to this project. 91 | 92 | For cloning and building this project yourself, make sure 93 | to install the 94 | [Extensibility Tools 2015](https://visualstudiogallery.msdn.microsoft.com/ab39a092-1343-46e2-b0f1-6a3f91155aa6) 95 | extension for Visual Studio which enables some features 96 | used by this project. 97 | 98 | ## License 99 | [MIT](LICENSE) 100 | -------------------------------------------------------------------------------- /art/FooDefinitions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/ProjFileTools/04a8c6224339ea941fab56c71a9402bd667a36b4/art/FooDefinitions.png -------------------------------------------------------------------------------- /art/SdkImports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/ProjFileTools/04a8c6224339ea941fab56c71a9402bd667a36b4/art/SdkImports.png -------------------------------------------------------------------------------- /art/completion-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/ProjFileTools/04a8c6224339ea941fab56c71a9402bd667a36b4/art/completion-name.png -------------------------------------------------------------------------------- /art/completion-version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/ProjFileTools/04a8c6224339ea941fab56c71a9402bd667a36b4/art/completion-version.png -------------------------------------------------------------------------------- /art/tooltip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/ProjFileTools/04a8c6224339ea941fab56c71a9402bd667a36b4/art/tooltip.png -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | set BatchFile=%0 5 | set Root=%~dp0 6 | set BuildConfiguration=Debug 7 | set MSBuildTarget=Build 8 | set NodeReuse=true 9 | set DeveloperCommandPrompt=%VS160COMNTOOLS%\VsDevCmd.bat 10 | set MSBuildAdditionalArguments=/m 11 | set RunTests=true 12 | set DeployVsixExtension=true 13 | set Solution=%Root%\ProjectFileTools.sln 14 | set OnlyRestore=false 15 | set SignType=public 16 | set OfficialBuild=false 17 | set VSTestAdditionalArguments= 18 | 19 | :ParseArguments 20 | if "%1" == "" goto :DoneParsing 21 | if /I "%1" == "/?" call :Usage && exit /b 1 22 | if /I "%1" == "/debug" set BuildConfiguration=Debug&&shift&& goto :ParseArguments 23 | if /I "%1" == "/release" set BuildConfiguration=Release&&shift&& goto :ParseArguments 24 | if /I "%1" == "/rebuild" set MSBuildTarget=Rebuild&&shift&& goto :ParseArguments 25 | if /I "%1" == "/restore" set OnlyRestore=true&&shift&& goto :ParseArguments 26 | if /I "%1" == "/skiptests" set RunTests=false&&shift&& goto :ParseArguments 27 | if /I "%1" == "/no-deploy-extension" set DeployVsixExtension=false&&shift&& goto :ParseArguments 28 | if /I "%1" == "/no-node-reuse" set NodeReuse=false&&shift&& goto :ParseArguments 29 | if /I "%1" == "/no-multi-proc" set MSBuildAdditionalArguments=&&shift&& goto :ParseArguments 30 | if /I "%1" == "/official-build" set SignType=real&&set OfficialBuild=true&&shift&& goto :ParseArguments 31 | if /I "%1" == "/trx-test-results" set VSTestAdditionalArguments=/logger:trx&&shift&&goto :ParseArguments 32 | call :Usage && exit /b 1 33 | :DoneParsing 34 | 35 | if not exist "%VS160COMNTOOLS%" ( 36 | echo To build this repository, this script needs to be run from a Visual Studio 2019 developer command prompt. 37 | echo. 38 | echo If Visual Studio is not installed, visit this page to download: 39 | echo. 40 | echo https://www.visualstudio.com/ 41 | exit /b 1 42 | ) 43 | 44 | if not exist "%VSSDKInstall%" ( 45 | echo To build this repository, you need to modify your Visual Studio installation to include the "Visual Studio extension development" workload. 46 | exit /b 1 47 | ) 48 | 49 | if "%VisualStudioVersion%" == "" ( 50 | REM In Jenkins and MicroBuild, we set VS160COMNTOOLS and VSSDKInstall to point to where VS is installed but don't launch in a developer prompt 51 | call "%DeveloperCommandPrompt%" || goto :BuildFailed 52 | ) 53 | 54 | set BinariesDirectory=%Root%bin\%BuildConfiguration%\ 55 | set RestoreLogFile=%BinariesDirectory%Restore.log 56 | set LogFile=%BinariesDirectory%Build.log 57 | if not exist "%BinariesDirectory%" mkdir "%BinariesDirectory%" || goto :BuildFailed 58 | 59 | echo. 60 | echo Restoring packages 61 | msbuild /nologo /nodeReuse:%NodeReuse% /consoleloggerparameters:Verbosity=minimal /fileLogger /fileloggerparameters:LogFile="%RestoreLogFile%";verbosity=diagnostic /t:Restore /p:Configuration="%BuildConfiguration%" %Solution% %MSBuildAdditionalArguments% 62 | if ERRORLEVEL 1 ( 63 | echo. 64 | call :PrintColor Red "Restore failed, for full log see %RestoreLogFile%." 65 | exit /b 1 66 | ) 67 | 68 | echo. 69 | call :PrintColor Green "Restore completed successfully, for full log see %RestoreLogFile%" 70 | 71 | if "%OnlyRestore%" == "true" ( 72 | exit /b 0 73 | ) 74 | 75 | echo. 76 | echo Building solution 77 | msbuild /nologo /nodeReuse:%NodeReuse% /consoleloggerparameters:Verbosity=minimal /fileLogger /fileloggerparameters:LogFile="%LogFile%";verbosity=diagnostic /t:"%MSBuildTarget%" /p:Configuration="%BuildConfiguration%" /p:DeployVsixExtension="%DeployVsixExtension%" /p:SignType="%SignType%" %Solution% %MSBuildAdditionalArguments% 78 | if ERRORLEVEL 1 ( 79 | echo. 80 | call :PrintColor Red "Build failed, for full log see %LogFile%." 81 | exit /b 1 82 | ) 83 | 84 | echo. 85 | call :PrintColor Green "Build completed successfully, for full log see %LogFile%" 86 | 87 | if "%RunTests%" == "false" ( 88 | exit /b 0 89 | ) 90 | 91 | echo. 92 | echo Running tests 93 | VSTest.Console.exe "bin\%BuildConfiguration%\ProjectFileTools.NuGetSearch.Tests.dll" %VSTestAdditionalArguments% 94 | if ERRORLEVEL 1 ( 95 | echo. 96 | call :PrintColor Red "Tests failed, see console output for failed tests" 97 | exit /b 1 98 | ) 99 | 100 | exit /b 0 101 | 102 | :Usage 103 | echo Usage: %BatchFile% [/rebuild^|/restore^|/modernvsixonly] [/debug^|/release] [/no-node-reuse] [/no-multi-proc] [/skiptests] [/no-deploy-extension] 104 | echo. 105 | echo Build targets: 106 | echo /rebuild Perform a clean, then build 107 | echo /restore Only restore NuGet packages 108 | echo /skiptests Don't run unit tests 109 | echo. 110 | echo Build options: 111 | echo /debug Perform debug build (default) 112 | echo /release Perform release build 113 | echo /no-node-reuse Prevents MSBuild from reusing existing MSBuild instances, 114 | echo useful for avoiding unexpected behavior on build machines 115 | echo /no-multi-proc No multi-proc build, useful for diagnosing build logs 116 | echo /no-deploy-extension Does not deploy the VSIX extension when building the solution 117 | echo /official-build Run the full signed build. 118 | echo /trx-test-results Outputs MSTest logs in TRX format for test result archival 119 | goto :eof 120 | 121 | :BuildFailed 122 | call :PrintColor Red "Build failed with ERRORLEVEL %ERRORLEVEL%" 123 | exit /b 1 124 | 125 | :PrintColor 126 | "%Windir%\System32\WindowsPowerShell\v1.0\Powershell.exe" write-host -foregroundcolor %1 "'%2'" -------------------------------------------------------------------------------- /build/Signing/Signing.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | MicrosoftShared 6 | $(MSBuildThisFileDirectory)\..\ 7 | 002400000480000094000000060200000024000052534131000400000100010055e0217eb635f69281051f9a823e0c7edd90f28063eb6c7a742a19b4f6139778ee0af438f47aed3b6e9f99838aa8dba689c7a71ddb860c96d923830b57bbd5cd6119406ddb9b002cf1c723bf272d6acbb7129e9d6dd5a5309c94e0ff4b2c884d45a55f475cd7dba59198086f61f5a8c8b5e601c0edbf269733f6f578fc8579c2 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | true 19 | false 20 | 21 | 22 | 23 | 24 | false 25 | true 26 | 27 | 28 | 29 | 30 | 31 | $(BuildDirPath)Strong Name Keys\35MSSharedLib1024.snk 32 | 0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9 33 | 31BF3856AD364E35 34 | 72 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | false 48 | true 49 | 50 | 51 | 52 | 56 | 57 | 58 | <_DelaySignMainAssembly>$(DelaySign) 59 | true 60 | 61 | 62 | 63 | 64 | $(_DelaySignMainAssembly) 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /build/Strong Name Keys/35MSSharedLib1024.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/ProjFileTools/04a8c6224339ea941fab56c71a9402bd667a36b4/build/Strong Name Keys/35MSSharedLib1024.snk -------------------------------------------------------------------------------- /netci.groovy: -------------------------------------------------------------------------------- 1 | // Groovy Script: http://www.groovy-lang.org/syntax.html 2 | // Jenkins DSL: https://github.com/jenkinsci/job-dsl-plugin/wiki 3 | 4 | import jobs.generation.ArchivalSettings 5 | import jobs.generation.Utilities 6 | 7 | def project = GithubProject 8 | def branchName = GithubBranchName 9 | 10 | // Generate jobs to build the latest head, and PRs 11 | [true, false].each { isPr -> 12 | // Build both debug and release 13 | ['Debug', 'Release'].each { configuration -> 14 | def newJobName = Utilities.getFullJobName(project, "windows_${configuration.toLowerCase()}", isPr) 15 | def newJob = job(newJobName) { 16 | steps { 17 | batchFile(""" 18 | SET VS150COMNTOOLS=%ProgramFiles(x86)%\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\ 19 | SET VSSDK150Install=%ProgramFiles(x86)%\\Microsoft Visual Studio\\2017\\Enterprise\\VSSDK\\ 20 | SET VSSDKInstall=%ProgramFiles(x86)%\\Microsoft Visual Studio\\2017\\Enterprise\\VSSDK\\ 21 | 22 | build.cmd /no-deploy-extension /${configuration.toLowerCase()} /no-node-reuse /trx-test-results 23 | """) 24 | } 25 | } 26 | 27 | def archiveSettings = new ArchivalSettings() 28 | archiveSettings.addFiles("bin/**/*") 29 | archiveSettings.excludeFiles("bin/obj/*") 30 | archiveSettings.setFailIfNothingArchived() 31 | archiveSettings.setArchiveOnFailure() 32 | Utilities.addArchival(newJob, archiveSettings) 33 | Utilities.setMachineAffinity(newJob, 'Windows_NT', 'latest-or-auto-dev15-0') 34 | Utilities.addMSTestResults(newJob, "TestResults/*.trx") 35 | Utilities.standardJobSetup(newJob, project, isPr, "*/$branchName") 36 | if (isPr) { 37 | Utilities.addGithubPRTriggerForBranch(newJob, branchName, "Windows ${configuration}") 38 | } else { 39 | Utilities.addGithubPushTrigger(newJob) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ProjectFileTools.MSBuild/Definition.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectFileTools.MSBuild 2 | { 3 | /// 4 | /// Contains the information for the FindAllReferences table and GoToDefinition command for a single MSBuild item. 5 | /// 6 | public class Definition 7 | { 8 | /// 9 | /// File that the definition is associated with 10 | /// 11 | public string File { get; } 12 | 13 | /// 14 | /// Project containing the definition 15 | /// 16 | public string Project { get; } 17 | 18 | /// 19 | /// Type of definition 20 | /// 21 | public string Type { get; } 22 | 23 | /// 24 | /// Text displayed for this definition in the find all references window 25 | /// 26 | public string Text { get; } 27 | 28 | /// 29 | /// Line associated with this definition 30 | /// 31 | public int? Line { get; } 32 | 33 | /// 34 | /// Column associated with this definition 35 | /// 36 | public int? Col { get; } 37 | 38 | internal Definition(string file, string project, string type, string text) 39 | { 40 | File = file; 41 | Project = project; 42 | Type = type; 43 | Text = text; 44 | Line = null; 45 | Col = null; 46 | } 47 | 48 | internal Definition(string file, string project, string type, string text, int line, int col) 49 | { 50 | File = file; 51 | Project = project; 52 | Type = type; 53 | Text = text; 54 | Line = line; 55 | Col = col; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/ProjectFileTools.MSBuild/IWorkspace.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ProjectFileTools.MSBuild 4 | { 5 | public interface IWorkspace 6 | { 7 | bool EvaluateCondition(string text); 8 | 9 | string GetEvaluatedPropertyValue(string text); 10 | 11 | List GetItemProvenance(string fileSpec); 12 | 13 | List GetItems(string fileSpec); 14 | 15 | List ResolveDefinition(string filePath, string sourceText, int position); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/ProjectFileTools.MSBuild/IWorkspaceManager.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectFileTools.MSBuild 2 | { 3 | public interface IWorkspaceManager 4 | { 5 | IWorkspace GetWorkspace(string filePath); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ProjectFileTools.MSBuild/ProjectFileTools.MSBuild.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net46 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | MicrosoftSHA2 20 | MsSharedLib72 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/ProjectFileTools.MSBuild/SyntaxNames.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectFileTools.MSBuild 2 | { 3 | internal static class SyntaxNames 4 | { 5 | internal const string Import = "Import"; 6 | internal const string Project = "Project"; 7 | internal const string Sdk = "Sdk"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/ProjectFileTools.MSBuild/Utilities.cs: -------------------------------------------------------------------------------- 1 | using Microsoft; 2 | 3 | namespace ProjectFileTools.MSBuild 4 | { 5 | internal static class Utilities 6 | { 7 | internal static int GetLine(string text, int position) 8 | { 9 | Requires.NotNullOrEmpty(text, nameof(text)); 10 | Requires.Range(position > -1 && position < text.Length, nameof(position), "Position must be positive and less than text.Length"); 11 | 12 | int line = 0; 13 | for (int ind = 0; ind < position; ind++) 14 | { 15 | if (text[ind] == '\n') 16 | { 17 | line++; 18 | } 19 | } 20 | 21 | return line; 22 | } 23 | 24 | internal static int GetStartOfLine(string text, int position) 25 | { 26 | Requires.NotNullOrEmpty(text, nameof(text)); 27 | Requires.Range(position > -1 && position <= text.Length, nameof(position), "Position must be positive and less than or equal to text.Length"); 28 | 29 | while (position > 0 && text[position - 1] != '\n') 30 | { 31 | position--; 32 | } 33 | 34 | return position; 35 | } 36 | 37 | internal static bool IsProperty(string text, int position, out string propertyName) 38 | { 39 | Requires.NotNull(text, nameof(text)); 40 | Requires.Range(position > -1 && position < text.Length, nameof(position), "Position must be positive and less than text.Length"); 41 | 42 | propertyName = null; 43 | if (text[position] == ')' && position > 1) 44 | { 45 | position--; 46 | } 47 | 48 | int propStart = position; 49 | int propEnd = position + 1; 50 | 51 | while (text[propStart] != '(' && propStart > 1) 52 | { 53 | if (!char.IsLetterOrDigit(text[propStart])) 54 | { 55 | return false; 56 | } 57 | 58 | propStart--; 59 | } 60 | 61 | if (!(text[propStart] == '(' && text[propStart - 1] == '$')) 62 | { 63 | return false; 64 | } 65 | 66 | while (propEnd < text.Length - 1 && text[propEnd] != '.' && text[propEnd] != ')') 67 | { 68 | if (!char.IsLetterOrDigit(text[propEnd])) 69 | { 70 | return false; 71 | } 72 | 73 | propEnd++; 74 | } 75 | if (!(text[propEnd] == '.' || text[propEnd] == ')')) 76 | { 77 | return false; 78 | } 79 | 80 | propertyName = text.Substring(propStart + 1, propEnd - propStart - 1); 81 | return true; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/ProjectFileTools.MSBuild/WorkspaceManager.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectFileTools.MSBuild 2 | { 3 | /// 4 | /// Provides the correct workspace 5 | /// 6 | public class WorkspaceManager : IWorkspaceManager 7 | { 8 | // TODO: Support multiple workspaces simultaneously 9 | private Workspace _workspace; 10 | 11 | /// 12 | /// Returns a Workspace that contains the filePath, or creates a new one using the filePath 13 | /// 14 | public IWorkspace GetWorkspace(string filePath) 15 | { 16 | if (_workspace != null && !_workspace.IsDisposed && _workspace.ContainsProject(filePath)) 17 | { 18 | return _workspace; 19 | } 20 | 21 | _workspace?.Dispose(); 22 | 23 | _workspace = new Workspace(filePath); 24 | return _workspace; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IDependencyManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ProjectFileTools.NuGetSearch.Contracts 4 | { 5 | 6 | public interface IDependencyManager 7 | { 8 | T GetComponent(); 9 | 10 | IReadOnlyList GetComponents(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageFeed.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace ProjectFileTools.NuGetSearch.Contracts 5 | { 6 | 7 | public interface IPackageFeed 8 | { 9 | string DisplayName { get; } 10 | 11 | Task GetPackageNamesAsync(string prefix, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken); 12 | 13 | Task GetPackageVersionsAsync(string id, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken); 14 | 15 | Task GetPackageInfoAsync(string id, string version, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageFeedFactory.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectFileTools.NuGetSearch.Contracts 2 | { 3 | 4 | public interface IPackageFeedFactory 5 | { 6 | bool TryHandle(string feed, out IPackageFeed instance); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageFeedFactorySelector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ProjectFileTools.NuGetSearch.Contracts 4 | { 5 | public interface IPackageFeedFactorySelector 6 | { 7 | IEnumerable FeedFactories { get; } 8 | 9 | IPackageFeed GetFeed(string source); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageFeedRegistryProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ProjectFileTools.NuGetSearch.Contracts 4 | { 5 | 6 | public interface IPackageFeedRegistryProvider 7 | { 8 | IReadOnlyList ConfiguredFeeds { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageFeedSearchJob.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ProjectFileTools.NuGetSearch.Contracts 5 | { 6 | public interface IPackageFeedSearchJob 7 | { 8 | IReadOnlyList RemainingFeeds { get; } 9 | 10 | IReadOnlyList SearchingIn { get; } 11 | 12 | IReadOnlyList Results { get; } 13 | 14 | bool IsCancelled { get; } 15 | 16 | event EventHandler Updated; 17 | 18 | void Cancel(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageFeedSearcher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace ProjectFileTools.NuGetSearch.Contracts 4 | { 5 | 6 | public interface IPackageFeedSearcher 7 | { 8 | Task SearchPackagesAsync(string prefix, params string[] feeds); 9 | 10 | Task SearchVersionsAsync(string prefix, params string[] feeds); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageInfo.cs: -------------------------------------------------------------------------------- 1 | using ProjectFileTools.NuGetSearch.Feeds; 2 | 3 | namespace ProjectFileTools.NuGetSearch.Contracts 4 | { 5 | public interface IPackageInfo 6 | { 7 | string Id { get; } 8 | 9 | string Version { get; } 10 | 11 | string Title { get; } 12 | 13 | string Authors { get; } 14 | 15 | string Summary { get; } 16 | 17 | string Description { get; } 18 | 19 | string LicenseUrl { get; } 20 | 21 | string ProjectUrl { get; } 22 | 23 | string IconUrl { get; } 24 | 25 | string Tags { get; } 26 | 27 | FeedKind SourceKind { get; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageNameSearchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ProjectFileTools.NuGetSearch.Feeds; 3 | 4 | namespace ProjectFileTools.NuGetSearch.Contracts 5 | { 6 | 7 | public interface IPackageNameSearchResult 8 | { 9 | bool Success { get; } 10 | 11 | FeedKind SourceKind { get; } 12 | 13 | IReadOnlyList Names { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageQueryConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectFileTools.NuGetSearch.Contracts 2 | { 3 | public interface IPackageQueryConfiguration 4 | { 5 | string CompatibiltyTarget { get; } 6 | 7 | bool IncludePreRelease { get; } 8 | 9 | int MaxResults { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageSearchManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ProjectFileTools.NuGetSearch.Feeds; 3 | 4 | namespace ProjectFileTools.NuGetSearch.Contracts 5 | { 6 | 7 | public interface IPackageSearchManager 8 | { 9 | IPackageFeedSearchJob> SearchPackageNames(string prefix, string tfm); 10 | 11 | IPackageFeedSearchJob> SearchPackageVersions(string packageName, string tfm); 12 | 13 | IPackageFeedSearchJob SearchPackageInfo(string packageId, string version, string tfm); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Contracts/IPackageVersionSearchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ProjectFileTools.NuGetSearch.Feeds; 3 | 4 | namespace ProjectFileTools.NuGetSearch.Contracts 5 | { 6 | 7 | public interface IPackageVersionSearchResult 8 | { 9 | bool Success { get; } 10 | 11 | IReadOnlyList Versions { get; } 12 | 13 | FeedKind SourceKind { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/Disk/NuGetDiskFeedFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.IO; 3 | using System.Linq; 4 | using ProjectFileTools.NuGetSearch.Contracts; 5 | using ProjectFileTools.NuGetSearch.IO; 6 | 7 | namespace ProjectFileTools.NuGetSearch.Feeds.Disk 8 | { 9 | public class NuGetDiskFeedFactory : IPackageFeedFactory 10 | { 11 | private static readonly ConcurrentDictionary Instances = new ConcurrentDictionary(); 12 | private readonly IFileSystem _fileSystem; 13 | 14 | public NuGetDiskFeedFactory(IFileSystem fileSystem) 15 | { 16 | _fileSystem = fileSystem; 17 | } 18 | 19 | public bool TryHandle(string feed, out IPackageFeed instance) 20 | { 21 | if (!_fileSystem.DirectoryExists(feed)) 22 | { 23 | instance = null; 24 | return false; 25 | } 26 | 27 | instance = Instances.GetOrAdd(feed, x => CreateInstance(x)); 28 | return instance != null; 29 | } 30 | 31 | private IPackageFeed CreateInstance(string feed) 32 | { 33 | if (IsNuGetV3Feed(feed)) 34 | { 35 | return Instances.GetOrAdd(feed, x => new NuGetV3DiskFeed(x, _fileSystem)); 36 | } 37 | 38 | if (IsNuGetV2Feed(feed)) 39 | { 40 | return Instances.GetOrAdd(feed, x => new NuGetV2DiskFeed(x, _fileSystem)); 41 | } 42 | 43 | return null; 44 | } 45 | 46 | private bool IsNuGetV2Feed(string feed) 47 | { 48 | foreach (string dir in _fileSystem.EnumerateDirectories(feed, "*", SearchOption.TopDirectoryOnly)) 49 | { 50 | if (_fileSystem.EnumerateFiles(dir, "*.nupkg", SearchOption.TopDirectoryOnly).Any()) 51 | { 52 | return true; 53 | } 54 | } 55 | 56 | return false; 57 | } 58 | 59 | private bool IsNuGetV3Feed(string feed) 60 | { 61 | foreach (string dir in _fileSystem.EnumerateDirectories(feed, "*", SearchOption.TopDirectoryOnly)) 62 | { 63 | if (_fileSystem.EnumerateFiles(dir, "*.nuspec", SearchOption.TopDirectoryOnly).Any()) 64 | { 65 | return false; 66 | } 67 | 68 | if(_fileSystem.GetDirectoryName(dir).IndexOf('.') == 0) 69 | { 70 | continue; 71 | } 72 | 73 | foreach (string sub in _fileSystem.EnumerateDirectories(dir, "*", SearchOption.TopDirectoryOnly)) 74 | { 75 | if (SemanticVersion.Parse(_fileSystem.GetDirectoryName(sub)) == null) 76 | { 77 | return false; 78 | } 79 | 80 | if (!_fileSystem.EnumerateFiles(sub, "*.nuspec", SearchOption.TopDirectoryOnly).Any()) 81 | { 82 | return false; 83 | } 84 | } 85 | } 86 | 87 | return true; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/Disk/NuGetPackageMatcher.cs: -------------------------------------------------------------------------------- 1 | using ProjectFileTools.NuGetSearch.Contracts; 2 | using ProjectFileTools.NuGetSearch.IO; 3 | 4 | namespace ProjectFileTools.NuGetSearch.Feeds.Disk 5 | { 6 | internal static class NuGetPackageMatcher 7 | { 8 | public static bool IsMatch(string dir, IPackageInfo info, IPackageQueryConfiguration queryConfiguration, IFileSystem fileSystem) 9 | { 10 | if (!queryConfiguration.IncludePreRelease) 11 | { 12 | SemanticVersion ver = SemanticVersion.Parse(info.Version); 13 | 14 | if(!string.IsNullOrEmpty(ver?.PrereleaseVersion)) 15 | { 16 | return false; 17 | } 18 | } 19 | 20 | return true; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/Disk/NuGetV2DiskFeed.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using ProjectFileTools.NuGetSearch.Contracts; 8 | using ProjectFileTools.NuGetSearch.IO; 9 | using ProjectFileTools.NuGetSearch.Search; 10 | 11 | namespace ProjectFileTools.NuGetSearch.Feeds.Disk 12 | { 13 | internal class NuGetV2DiskFeed : IPackageFeed 14 | { 15 | private readonly string _feed; 16 | private readonly bool _isRemote; 17 | private readonly IFileSystem _fileSystem; 18 | 19 | public NuGetV2DiskFeed(string feed, IFileSystem fileSystem) 20 | { 21 | _fileSystem = fileSystem; 22 | _feed = feed; 23 | _isRemote = Uri.TryCreate(feed, UriKind.Absolute, out Uri uri) && uri.IsUnc; 24 | } 25 | 26 | public string DisplayName 27 | { 28 | get 29 | { 30 | if (_isRemote) 31 | { 32 | return $"NuGet v2 (Remote: {_feed})"; 33 | } 34 | 35 | return $"NuGet v2 (Local: {_feed})"; 36 | } 37 | } 38 | 39 | public Task GetPackageInfoAsync(string id, string version, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 40 | { 41 | return Task.Run(() => 42 | { 43 | if (version != null) 44 | { 45 | string nuspec = Path.Combine(_feed, $"{id}.{version}", $"{id}.nuspec"); 46 | 47 | if (_fileSystem.FileExists(nuspec)) 48 | { 49 | return Task.FromResult(NuSpecReader.Read(nuspec, FeedKind.Local)); 50 | } 51 | else 52 | { 53 | return Task.FromResult(null); 54 | } 55 | } 56 | else 57 | { 58 | string dir = _fileSystem.EnumerateDirectories(_feed).OrderByDescending(x => SemanticVersion.Parse(_fileSystem.GetDirectoryNameOnly(x).Substring(id.Length + 1))).FirstOrDefault(); 59 | 60 | if (dir == null) 61 | { 62 | return Task.FromResult(null); 63 | } 64 | 65 | string nuspec = Path.Combine(dir, $"{id}.nuspec"); 66 | 67 | if (_fileSystem.FileExists(nuspec)) 68 | { 69 | return Task.FromResult(NuSpecReader.Read(nuspec, FeedKind.Local)); 70 | } 71 | else 72 | { 73 | return Task.FromResult(null); 74 | } 75 | } 76 | }, cancellationToken); 77 | } 78 | 79 | public Task GetPackageNamesAsync(string prefix, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 80 | { 81 | return Task.Run(() => 82 | { 83 | try 84 | { 85 | List infos = new List(); 86 | foreach (string path in _fileSystem.EnumerateDirectories(_feed).Where(x => x.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) > -1)) 87 | { 88 | if (cancellationToken.IsCancellationRequested) 89 | { 90 | return PackageNameSearchResult.Cancelled; 91 | } 92 | 93 | string nuspec = _fileSystem.EnumerateFiles(path, "*.nuspec", SearchOption.TopDirectoryOnly).FirstOrDefault(); 94 | 95 | if (nuspec != null) 96 | { 97 | IPackageInfo info = NuSpecReader.Read(nuspec, FeedKind.Local); 98 | 99 | if (info != null && NuGetPackageMatcher.IsMatch(path, info, queryConfiguration, _fileSystem)) 100 | { 101 | infos.Add(info); 102 | 103 | if (infos.Count >= queryConfiguration.MaxResults) 104 | { 105 | break; 106 | } 107 | } 108 | } 109 | } 110 | 111 | return new PackageNameSearchResult(infos.Select(x => x.Id).ToList(), FeedKind.Local); 112 | } 113 | catch 114 | { 115 | return PackageNameSearchResult.Failure; 116 | } 117 | }, cancellationToken); 118 | } 119 | 120 | public Task GetPackageVersionsAsync(string prefix, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 121 | { 122 | return Task.Run(() => 123 | { 124 | try 125 | { 126 | List versions = new List(); 127 | bool anyFound = false; 128 | foreach (string path in _fileSystem.EnumerateDirectories(_feed).Where(x => x.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) > -1)) 129 | { 130 | if (cancellationToken.IsCancellationRequested) 131 | { 132 | return PackageVersionSearchResult.Cancelled; 133 | } 134 | 135 | string nuspec = _fileSystem.EnumerateFiles(path, "*.nuspec", SearchOption.TopDirectoryOnly).FirstOrDefault(); 136 | 137 | if (nuspec != null) 138 | { 139 | anyFound = true; 140 | IPackageInfo info = NuSpecReader.Read(nuspec, FeedKind.Local); 141 | 142 | if (info != null && string.Equals(info.Id, prefix, StringComparison.OrdinalIgnoreCase)) 143 | { 144 | versions.Add(info.Version); 145 | } 146 | } 147 | } 148 | 149 | if (anyFound) 150 | { 151 | return new PackageVersionSearchResult(versions, FeedKind.Local); 152 | } 153 | 154 | return PackageVersionSearchResult.Failure; 155 | } 156 | catch 157 | { 158 | return PackageVersionSearchResult.Failure; 159 | } 160 | }, cancellationToken); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/Disk/NuGetV3DiskFeed.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using ProjectFileTools.NuGetSearch.Contracts; 8 | using ProjectFileTools.NuGetSearch.IO; 9 | using ProjectFileTools.NuGetSearch.Search; 10 | 11 | namespace ProjectFileTools.NuGetSearch.Feeds.Disk 12 | { 13 | internal class NuGetV3DiskFeed : IPackageFeed 14 | { 15 | private readonly string _feed; 16 | private readonly IFileSystem _fileSystem; 17 | private readonly bool _isRemote; 18 | 19 | public NuGetV3DiskFeed(string feed, IFileSystem fileSystem) 20 | { 21 | _fileSystem = fileSystem; 22 | _feed = feed; 23 | _isRemote = Uri.TryCreate(feed, UriKind.Absolute, out Uri uri) && uri.IsUnc; 24 | } 25 | 26 | public string DisplayName 27 | { 28 | get 29 | { 30 | if (_isRemote) 31 | { 32 | return $"NuGet v3 (Remote: {_feed})"; 33 | } 34 | 35 | return $"NuGet v3 (Local: {_feed})"; 36 | } 37 | } 38 | 39 | public Task GetPackageInfoAsync(string id, string version, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 40 | { 41 | return Task.Run(() => 42 | { 43 | if (version != null) 44 | { 45 | string nuspec = Path.Combine(_feed, id, version, $"{id}.nuspec"); 46 | 47 | if (_fileSystem.FileExists(nuspec)) 48 | { 49 | return Task.FromResult(NuSpecReader.Read(nuspec, FeedKind.Local)); 50 | } 51 | else 52 | { 53 | return Task.FromResult(null); 54 | } 55 | } 56 | else 57 | { 58 | string package = Path.Combine(_feed, id); 59 | string dir = _fileSystem.EnumerateDirectories(package).OrderByDescending(x => SemanticVersion.Parse(_fileSystem.GetDirectoryName(x))).FirstOrDefault(); 60 | 61 | if (dir == null) 62 | { 63 | return Task.FromResult(null); 64 | } 65 | 66 | string nuspec = Path.Combine(dir, $"{id}.nuspec"); 67 | 68 | if (_fileSystem.FileExists(nuspec)) 69 | { 70 | return Task.FromResult(NuSpecReader.Read(nuspec, FeedKind.Local)); 71 | } 72 | else 73 | { 74 | return Task.FromResult(null); 75 | } 76 | } 77 | }, cancellationToken); 78 | } 79 | 80 | public Task GetPackageNamesAsync(string prefix, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 81 | { 82 | return Task.Run(() => 83 | { 84 | try 85 | { 86 | List infos = new List(); 87 | foreach (string path in _fileSystem.EnumerateDirectories(_feed, $"*{prefix}*", SearchOption.TopDirectoryOnly).Where(x => x.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) > -1)) 88 | { 89 | if (cancellationToken.IsCancellationRequested) 90 | { 91 | return null; 92 | } 93 | 94 | if (infos.Count >= queryConfiguration.MaxResults) 95 | { 96 | break; 97 | } 98 | 99 | foreach (string verDir in _fileSystem.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)) 100 | { 101 | if (cancellationToken.IsCancellationRequested) 102 | { 103 | return null; 104 | } 105 | 106 | if (verDir == null || SemanticVersion.Parse(verDir) == null) 107 | { 108 | continue; 109 | } 110 | 111 | string nuspec = _fileSystem.EnumerateFiles(verDir, "*.nuspec", SearchOption.TopDirectoryOnly).FirstOrDefault(); 112 | 113 | if (nuspec != null) 114 | { 115 | IPackageInfo info = NuSpecReader.Read(nuspec, FeedKind.Local); 116 | if (info != null && NuGetPackageMatcher.IsMatch(verDir, info, queryConfiguration, _fileSystem)) 117 | { 118 | infos.Add(info); 119 | 120 | if (infos.Count >= queryConfiguration.MaxResults) 121 | { 122 | break; 123 | } 124 | } 125 | } 126 | } 127 | } 128 | 129 | return new PackageNameSearchResult(infos.Select(x => x.Id).ToList(), FeedKind.Local); 130 | } 131 | catch 132 | { 133 | return PackageNameSearchResult.Failure; 134 | } 135 | }, cancellationToken); 136 | } 137 | 138 | public Task GetPackageVersionsAsync(string id, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 139 | { 140 | return Task.Run(() => 141 | { 142 | string packagePath = Path.Combine(_feed, id.ToLowerInvariant()); 143 | if (!_fileSystem.DirectoryExists(packagePath)) 144 | { 145 | return PackageVersionSearchResult.Failure; 146 | } 147 | 148 | try 149 | { 150 | List versions = new List(); 151 | 152 | foreach (string directory in _fileSystem.EnumerateDirectories(packagePath, "*", SearchOption.TopDirectoryOnly)) 153 | { 154 | string version = SemanticVersion.Parse(_fileSystem.GetDirectoryNameOnly(directory))?.ToString(); 155 | 156 | if (version != null) 157 | { 158 | versions.Add(version); 159 | } 160 | } 161 | 162 | return new PackageVersionSearchResult(versions, FeedKind.Local); 163 | } 164 | catch 165 | { 166 | return PackageVersionSearchResult.Failure; 167 | } 168 | }, cancellationToken); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/FeedKind.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectFileTools.NuGetSearch.Feeds 2 | { 3 | public enum FeedKind 4 | { 5 | NuGet, 6 | MyGet, 7 | Local 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/NuSpecReader.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | using ProjectFileTools.NuGetSearch.Contracts; 3 | 4 | namespace ProjectFileTools.NuGetSearch.Feeds 5 | { 6 | 7 | internal class NuSpecReader 8 | { 9 | internal static IPackageInfo Read(string nuspec, FeedKind kind) 10 | { 11 | XDocument doc = XDocument.Load(nuspec); 12 | XNamespace ns = doc.Root.GetDefaultNamespace(); 13 | XElement package = doc.Root; 14 | XElement metadata = package?.Element(XName.Get("metadata", ns.NamespaceName)); 15 | XElement id = metadata?.Element(XName.Get("id", ns.NamespaceName)); 16 | XElement version = metadata?.Element(XName.Get("version", ns.NamespaceName)); 17 | XElement title = metadata?.Element(XName.Get("title", ns.NamespaceName)); 18 | XElement authors = metadata?.Element(XName.Get("authors", ns.NamespaceName)); 19 | XElement summary = metadata?.Element (XName.Get ("summary", ns.NamespaceName)); 20 | XElement description = metadata?.Element(XName.Get("description", ns.NamespaceName)); 21 | XElement licenseUrl = metadata?.Element(XName.Get("licenseUrl", ns.NamespaceName)); 22 | XElement projectUrl = metadata?.Element (XName.Get ("projectUrl", ns.NamespaceName)); 23 | XElement iconUrl = metadata?.Element (XName.Get ("iconUrl", ns.NamespaceName)); 24 | XElement tags = metadata?.Element(XName.Get("tags", ns.NamespaceName)); 25 | 26 | if (id != null) 27 | { 28 | return new PackageInfo(id.Value, version?.Value, title?.Value, authors?.Value, summary?.Value, description?.Value, licenseUrl?.Value, projectUrl?.Value, iconUrl?.Value, tags?.Value, kind); 29 | } 30 | 31 | return null; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/PackageFeedFactoryBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using ProjectFileTools.NuGetSearch.Contracts; 3 | 4 | namespace ProjectFileTools.NuGetSearch.Feeds 5 | { 6 | 7 | internal abstract class PackageFeedFactoryBase : IPackageFeedFactory 8 | { 9 | private static readonly ConcurrentDictionary Instances = new ConcurrentDictionary(); 10 | 11 | protected abstract bool CanHandle(string feed); 12 | 13 | protected abstract IPackageFeed Create(string feed); 14 | 15 | public virtual bool TryHandle(string feed, out IPackageFeed instance) 16 | { 17 | if (!CanHandle(feed)) 18 | { 19 | instance = null; 20 | return false; 21 | } 22 | 23 | instance = Instances.GetOrAdd(feed, Create); 24 | return true; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/PackageFeedFactorySelector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using ProjectFileTools.NuGetSearch.Contracts; 5 | 6 | namespace ProjectFileTools.NuGetSearch.Feeds 7 | { 8 | public class PackageFeedFactorySelector : IPackageFeedFactorySelector 9 | { 10 | private readonly ConcurrentDictionary _feedCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); 11 | 12 | public PackageFeedFactorySelector(IEnumerable feedFactories) 13 | { 14 | FeedFactories = feedFactories; 15 | } 16 | 17 | public IEnumerable FeedFactories { get; } 18 | 19 | public IPackageFeed GetFeed(string source) 20 | { 21 | if (_feedCache.TryGetValue(source, out IPackageFeed match)) 22 | { 23 | return match; 24 | } 25 | 26 | foreach(IPackageFeedFactory feed in FeedFactories) 27 | { 28 | if (feed.TryHandle(source, out IPackageFeed instance)) 29 | { 30 | _feedCache[source] = instance; 31 | return instance; 32 | } 33 | } 34 | 35 | _feedCache[source] = null; 36 | return null; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/PackageInfo.cs: -------------------------------------------------------------------------------- 1 | using ProjectFileTools.NuGetSearch.Contracts; 2 | 3 | namespace ProjectFileTools.NuGetSearch.Feeds 4 | { 5 | 6 | public class PackageInfo : IPackageInfo 7 | { 8 | public PackageInfo(string id, string version, string title, string authors, string summary, string description, string licenseUrl, string projectUrl, string iconUrl, string tags, FeedKind sourceKind) 9 | { 10 | Id = id; 11 | Version = version; 12 | Title = title; 13 | Authors = authors; 14 | Description = description; 15 | LicenseUrl = licenseUrl; 16 | ProjectUrl = projectUrl; 17 | SourceKind = sourceKind; 18 | IconUrl = iconUrl; 19 | Tags = tags; 20 | } 21 | 22 | public string Id { get; } 23 | 24 | public string Version { get; } 25 | 26 | public string Title { get; } 27 | 28 | public string Authors { get; } 29 | 30 | public string Summary { get; } 31 | 32 | public string Description { get; } 33 | 34 | public string LicenseUrl { get; } 35 | 36 | public string ProjectUrl { get; } 37 | 38 | public string IconUrl { get; } 39 | 40 | public string Tags { get; } 41 | 42 | public FeedKind SourceKind { get; } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/PackageQueryConfiguration.cs: -------------------------------------------------------------------------------- 1 | using ProjectFileTools.NuGetSearch.Contracts; 2 | 3 | namespace ProjectFileTools.NuGetSearch.Feeds 4 | { 5 | public class PackageQueryConfiguration : IPackageQueryConfiguration 6 | { 7 | public PackageQueryConfiguration(string targetFrameworkMoniker, bool includePreRelease = true, int maxResults = 100) 8 | { 9 | CompatibiltyTarget = targetFrameworkMoniker; 10 | IncludePreRelease = includePreRelease; 11 | MaxResults = maxResults; 12 | } 13 | 14 | public string CompatibiltyTarget { get; } 15 | 16 | public bool IncludePreRelease { get; } 17 | 18 | public int MaxResults { get; } 19 | 20 | public override int GetHashCode() 21 | { 22 | return (CompatibiltyTarget?.GetHashCode() ?? 0) ^ IncludePreRelease.GetHashCode() ^ MaxResults.GetHashCode(); 23 | } 24 | 25 | public override bool Equals(object obj) 26 | { 27 | PackageQueryConfiguration cfg = obj as PackageQueryConfiguration; 28 | return cfg != null 29 | && string.Equals(CompatibiltyTarget, cfg.CompatibiltyTarget, System.StringComparison.Ordinal) 30 | && IncludePreRelease == cfg.IncludePreRelease 31 | && MaxResults == cfg.MaxResults; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Feeds/Web/NuGetV2ServiceFeed.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Xml.Linq; 7 | using Newtonsoft.Json.Linq; 8 | using ProjectFileTools.NuGetSearch.Contracts; 9 | using ProjectFileTools.NuGetSearch.IO; 10 | using ProjectFileTools.NuGetSearch.Search; 11 | 12 | namespace ProjectFileTools.NuGetSearch.Feeds.Web 13 | { 14 | public class NuGetV2ServiceFeedFactory : IPackageFeedFactory 15 | { 16 | private readonly IWebRequestFactory _webRequestFactory; 17 | 18 | public NuGetV2ServiceFeedFactory(IWebRequestFactory webRequestFactory) 19 | { 20 | _webRequestFactory = webRequestFactory; 21 | } 22 | 23 | public bool TryHandle(string feed, out IPackageFeed instance) 24 | { 25 | if (Uri.TryCreate(feed, UriKind.Absolute, out Uri location) && feed.EndsWith("nuget", StringComparison.OrdinalIgnoreCase)) 26 | { 27 | instance = new NuGetV2ServiceFeed(feed, _webRequestFactory); 28 | return true; 29 | } 30 | 31 | instance = null; 32 | return false; 33 | } 34 | } 35 | 36 | public class NuGetV2ServiceFeed : IPackageFeed 37 | { 38 | private readonly string _feed; 39 | private readonly FeedKind _kind; 40 | private readonly IWebRequestFactory _webRequestFactory; 41 | 42 | public NuGetV2ServiceFeed(string feed, IWebRequestFactory webRequestFactory) 43 | { 44 | _webRequestFactory = webRequestFactory; 45 | _feed = feed; 46 | _kind = feed.IndexOf("nuget.org", StringComparison.OrdinalIgnoreCase) > -1 ? FeedKind.NuGet : FeedKind.MyGet; 47 | } 48 | 49 | public string DisplayName => $"{_feed} (NuGet v2)"; 50 | 51 | 52 | public async Task GetPackageNamesAsync(string prefix, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 53 | { 54 | if (cancellationToken.IsCancellationRequested) 55 | { 56 | return PackageNameSearchResult.Failure; 57 | } 58 | 59 | IReadOnlyList results = new List(); 60 | string frameworkQuery = !string.IsNullOrEmpty(queryConfiguration.CompatibiltyTarget) ? $"&targetFramework={queryConfiguration.CompatibiltyTarget}" : ""; 61 | var serviceEndpoint = $"{_feed}/Search()"; 62 | Func queryFunc = x => $"{x}?searchTerm='{prefix}'{frameworkQuery}&includePrerelease={queryConfiguration.IncludePreRelease}&semVerLevel=2.0.0"; 63 | XDocument document = await ExecuteAutocompleteServiceQueryAsync(serviceEndpoint, queryFunc, cancellationToken).ConfigureAwait(false); 64 | 65 | if (document != null) 66 | { 67 | try 68 | { 69 | results = GetPackageNamesFromNuGetV2CompatibleQueryResult(document); 70 | return new PackageNameSearchResult(results, _kind); 71 | } 72 | catch 73 | { 74 | } 75 | } 76 | 77 | return PackageNameSearchResult.Failure; 78 | } 79 | 80 | public async Task GetPackageInfoAsync(string packageId, string version, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 81 | { 82 | if (packageId == null) 83 | { 84 | return null; 85 | } 86 | 87 | var serviceEndpoint = $"{_feed}/Packages(Id='{packageId}',Version='{version}')"; 88 | Func queryFunc = x => $"{x}"; 89 | XDocument document = await ExecuteAutocompleteServiceQueryAsync(serviceEndpoint, queryFunc, cancellationToken).ConfigureAwait(false); 90 | 91 | if (document != null) 92 | { 93 | var el = document.Root; 94 | 95 | var id = GetPropertyValue(document, el, "Id"); 96 | var title = GetPropertyValue(document, el, "Title"); 97 | var authors = GetPropertyValue(document, el, "Authors"); 98 | var summary = GetPropertyValue(document, el, "Summary"); 99 | var description = GetPropertyValue(document, el, "Description"); 100 | var projectUrl = GetPropertyValue(document, el, "ProjectUrl"); 101 | var licenseUrl = GetPropertyValue(document, el, "LicenseUrl"); 102 | var iconUrl = GetPropertyValue(document, el, "IconUrl"); 103 | var tags = GetPropertyValue(document, el, "Tags"); 104 | var packageInfo = new PackageInfo(id, version, title, authors, summary, description, licenseUrl, projectUrl, iconUrl, tags, _kind); 105 | return packageInfo; 106 | } 107 | 108 | 109 | return null; 110 | } 111 | 112 | public async Task GetPackageVersionsAsync(string id, IPackageQueryConfiguration queryConfiguration, CancellationToken cancellationToken) 113 | { 114 | IReadOnlyList results = new List(); 115 | var serviceEndpoint = $"{_feed}/FindPackagesById()"; 116 | Func queryFunc = x => $"{x}?id='{id}'"; 117 | XDocument document = await ExecuteAutocompleteServiceQueryAsync(serviceEndpoint, queryFunc, cancellationToken).ConfigureAwait(false); 118 | 119 | try 120 | { 121 | results = GetPackageVersionsFromNuGetV2CompatibleQueryResult(id, document); 122 | } 123 | catch 124 | { 125 | return PackageVersionSearchResult.Failure; 126 | } 127 | 128 | return new PackageVersionSearchResult(results, _kind); 129 | } 130 | 131 | private async Task ExecuteAutocompleteServiceQueryAsync(string endpoint, Func query, CancellationToken cancellationToken) 132 | { 133 | if (endpoint == null) 134 | { 135 | return null; 136 | } 137 | 138 | try 139 | { 140 | string location = query(endpoint); 141 | var xml = await _webRequestFactory.GetStringAsync(location, cancellationToken).ConfigureAwait(false); 142 | return XDocument.Parse(xml); 143 | } 144 | catch (Exception) 145 | { 146 | } 147 | 148 | return null; 149 | } 150 | 151 | 152 | private static string GetPropertyValue(XDocument document, XElement el, string propertyKey) 153 | { 154 | return el 155 | .Element(document.Root.GetNamespaceOfPrefix("m") + "properties") 156 | .Element(document.Root.GetNamespaceOfPrefix("d") + propertyKey) 157 | ?.Value; 158 | } 159 | 160 | 161 | 162 | private static IReadOnlyList GetPackageVersionsFromNuGetV2CompatibleQueryResult(string id, XDocument document) 163 | { 164 | List results = new List(); 165 | 166 | if (document != null) 167 | { 168 | foreach (var el in document.Root.Elements(document.Root.GetDefaultNamespace() + "entry")) 169 | { 170 | var pkgId = GetPropertyValue(document, el, "Id"); 171 | 172 | var pkgVersion = GetPropertyValue(document, el, "Version"); 173 | 174 | if (pkgId.Equals(pkgId, StringComparison.OrdinalIgnoreCase)) 175 | { 176 | results.Add(pkgVersion); 177 | } 178 | } 179 | } 180 | 181 | return results; 182 | } 183 | 184 | private static IReadOnlyList GetPackageNamesFromNuGetV2CompatibleQueryResult(XDocument document) 185 | { 186 | List results = new List(); 187 | 188 | if (document != null) 189 | { 190 | foreach (var el in document.Root.Elements(document.Root.GetDefaultNamespace() + "entry")) 191 | { 192 | var id = GetPropertyValue(document, el, "Id"); 193 | 194 | results.Add(id); 195 | } 196 | } 197 | 198 | return results.Distinct().ToList(); 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/IO/FileSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace ProjectFileTools.NuGetSearch.IO 7 | { 8 | public class FileSystem : IFileSystem 9 | { 10 | public bool DirectoryExists(string path) 11 | { 12 | if (string.IsNullOrWhiteSpace(path)) 13 | { 14 | return false; 15 | } 16 | 17 | // avoid a first-chance exception in Directory.Exists if we know it's a URL anyway 18 | if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) 19 | { 20 | return false; 21 | } 22 | 23 | return path.IndexOfAny(Path.GetInvalidPathChars()) < 0 && Directory.Exists(path); 24 | } 25 | 26 | public IEnumerable EnumerateDirectories(string path, string pattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) 27 | { 28 | if (!DirectoryExists(path)) 29 | { 30 | return Enumerable.Empty(); 31 | } 32 | 33 | return Directory.EnumerateDirectories(path, pattern, searchOption); 34 | } 35 | 36 | public IEnumerable EnumerateFiles(string path, string pattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) 37 | { 38 | if (!DirectoryExists(path)) 39 | { 40 | return Enumerable.Empty(); 41 | } 42 | 43 | return Directory.EnumerateFiles(path, pattern, searchOption); 44 | } 45 | 46 | public bool FileExists(string path) 47 | { 48 | return path.IndexOfAny(Path.GetInvalidPathChars()) < 0 && File.Exists(path); 49 | } 50 | 51 | public string GetDirectoryName(string path) 52 | { 53 | return Path.GetDirectoryName(path); 54 | } 55 | 56 | public string GetDirectoryNameOnly(string path) 57 | { 58 | return new DirectoryInfo(path).Name; 59 | } 60 | 61 | public string ReadAllText(string path) 62 | { 63 | return File.ReadAllText(path); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/IO/IFileSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | 4 | namespace ProjectFileTools.NuGetSearch.IO 5 | { 6 | public interface IFileSystem 7 | { 8 | IEnumerable EnumerateFiles(string path, string pattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); 9 | 10 | IEnumerable EnumerateDirectories(string path, string pattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); 11 | 12 | string ReadAllText(string path); 13 | 14 | bool DirectoryExists(string path); 15 | 16 | bool FileExists(string path); 17 | 18 | string GetDirectoryName(string path); 19 | 20 | string GetDirectoryNameOnly(string path); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/IO/IWebRequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace ProjectFileTools.NuGetSearch.IO 5 | { 6 | public interface IWebRequestFactory 7 | { 8 | Task GetStringAsync(string endpoint, CancellationToken cancellationToken); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/IO/WebRequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace ProjectFileTools.NuGetSearch.IO 6 | { 7 | public class WebRequestFactory : IWebRequestFactory 8 | { 9 | private static readonly HttpClient _httpClient = new HttpClient(); 10 | public async Task GetStringAsync(string endpoint, CancellationToken cancellationToken) 11 | { 12 | try 13 | { 14 | HttpResponseMessage responseMessage = await _httpClient.GetAsync(endpoint, cancellationToken).ConfigureAwait(false); 15 | if (responseMessage.StatusCode == System.Net.HttpStatusCode.NotFound) 16 | { 17 | // avoid a first-chance exception on 404 18 | return null; 19 | } 20 | 21 | responseMessage.EnsureSuccessStatusCode(); 22 | return await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); 23 | } 24 | catch 25 | { 26 | return null; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/IO/WebRequestFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace ProjectFileTools.NuGetSearch.IO 6 | { 7 | 8 | public static class WebRequestFactoryExtensions 9 | { 10 | public static async Task GetJsonAsync(this IWebRequestFactory factory, string endpoint, CancellationToken cancellationToken) 11 | { 12 | string result = await factory.GetStringAsync(endpoint, cancellationToken).ConfigureAwait(false); 13 | 14 | if(result == null) 15 | { 16 | return null; 17 | } 18 | 19 | try 20 | { 21 | return JToken.Parse(result); 22 | } 23 | catch 24 | { 25 | return null; 26 | } 27 | } 28 | 29 | public static async Task GetDeserializedJsonAsync(this IWebRequestFactory factory, string endpoint, CancellationToken cancellationToken) 30 | { 31 | JToken token = await factory.GetJsonAsync(endpoint, cancellationToken).ConfigureAwait(false); 32 | 33 | if(token == null) 34 | { 35 | return default(T); 36 | } 37 | 38 | return token.ToObject(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/ProjectFileTools.NuGetSearch.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net46;netstandard1.6 5 | Helper package for querying NuGet feeds for intellisense in project files 6 | Microsoft 7 | https://github.com/dotnet/projfiletools 8 | https://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm 9 | http://go.microsoft.com/fwlink/?LinkID=288859 10 | Microsoft 11 | Copyright © Microsoft Corporation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | MicrosoftSHA2 28 | MsSharedLib72 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | FileSystem 9 | Release 10 | net46 11 | bin\Release\PublishOutput 12 | 13 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Search/PackageFeedSearchJob.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using ProjectFileTools.NuGetSearch.Contracts; 8 | 9 | namespace ProjectFileTools.NuGetSearch.Search 10 | { 11 | internal class PackageFeedSearchJob : IPackageFeedSearchJob 12 | { 13 | private readonly CancellationTokenSource _cancellationTokenSource; 14 | private readonly ConcurrentDictionary>, string> _taskMap = new ConcurrentDictionary>, string>(); 15 | private readonly object _updateSync = new object(); 16 | private readonly object _cancellationSync = new object(); 17 | private bool _isInitializing; 18 | 19 | public event EventHandler Updated; 20 | 21 | public PackageFeedSearchJob(IReadOnlyList>>> searchTasks, CancellationTokenSource cancellationTokenSource) 22 | { 23 | _cancellationTokenSource = cancellationTokenSource; 24 | List searchingIn = new List(searchTasks.Count); 25 | _isInitializing = true; 26 | Results = new List(); 27 | 28 | foreach (Tuple>> taskInfo in searchTasks) 29 | { 30 | _taskMap[taskInfo.Item2] = taskInfo.Item1; 31 | taskInfo.Item2.ContinueWith(HandleCompletion); 32 | searchingIn.Add(taskInfo.Item1); 33 | } 34 | 35 | RemainingFeeds = searchingIn; 36 | SearchingIn = searchingIn; 37 | 38 | _isInitializing = false; 39 | foreach (Tuple>> taskInfo in searchTasks) 40 | { 41 | if (taskInfo.Item2.IsCompleted) 42 | { 43 | HandleCompletion(taskInfo.Item2); 44 | } 45 | } 46 | } 47 | 48 | private void HandleCompletion(Task> task) 49 | { 50 | if (_isInitializing) 51 | { 52 | return; 53 | } 54 | 55 | if (_taskMap.TryRemove(task, out string name)) 56 | { 57 | lock (_updateSync) 58 | { 59 | List remaining = RemainingFeeds.ToList(); 60 | remaining.Remove(name); 61 | RemainingFeeds = remaining; 62 | 63 | if (task.Result != null) 64 | { 65 | List results = Results.ToList(); 66 | results.AddRange(task.Result.Where(x => !Equals(x, default(T)))); 67 | Results = results; 68 | } 69 | } 70 | 71 | Updated?.Invoke(this, EventArgs.Empty); 72 | } 73 | } 74 | 75 | public void Cancel() 76 | { 77 | if(RemainingFeeds.Count == 0) 78 | { 79 | return; 80 | } 81 | 82 | if (!_cancellationTokenSource.IsCancellationRequested) 83 | { 84 | lock (_cancellationSync) 85 | { 86 | if (!_cancellationTokenSource.IsCancellationRequested) 87 | { 88 | _cancellationTokenSource.Cancel(); 89 | IsCancelled = true; 90 | } 91 | } 92 | } 93 | } 94 | 95 | public IReadOnlyList RemainingFeeds { get; private set; } 96 | 97 | public IReadOnlyList Results { get; private set; } 98 | 99 | public IReadOnlyList SearchingIn { get; } 100 | 101 | public bool IsCancelled { get; private set; } 102 | } 103 | } -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Search/PackageNameSearchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using ProjectFileTools.NuGetSearch.Contracts; 4 | using ProjectFileTools.NuGetSearch.Feeds; 5 | 6 | namespace ProjectFileTools.NuGetSearch.Search 7 | { 8 | 9 | internal class PackageNameSearchResult : IPackageNameSearchResult 10 | { 11 | public static IPackageNameSearchResult Cancelled { get; } = new PackageNameSearchResult(); 12 | 13 | public static IPackageNameSearchResult Failure { get; } = new PackageNameSearchResult(); 14 | 15 | public static Task CancelledTask { get; } = Task.FromResult(Cancelled); 16 | 17 | public static Task FailureTask { get; } = Task.FromResult(Failure); 18 | 19 | public bool Success { get; } 20 | 21 | public IReadOnlyList Names { get; } 22 | 23 | public FeedKind SourceKind { get; } 24 | 25 | private PackageNameSearchResult() 26 | { 27 | } 28 | 29 | public PackageNameSearchResult(IReadOnlyList names, FeedKind sourceKind) 30 | { 31 | Success = true; 32 | Names = names; 33 | SourceKind = sourceKind; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/Search/PackageVersionSearchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using ProjectFileTools.NuGetSearch.Contracts; 4 | using ProjectFileTools.NuGetSearch.Feeds; 5 | 6 | namespace ProjectFileTools.NuGetSearch.Search 7 | { 8 | 9 | internal class PackageVersionSearchResult : IPackageVersionSearchResult 10 | { 11 | public static IPackageVersionSearchResult Cancelled { get; } = new PackageVersionSearchResult(); 12 | 13 | public static Task CancelledTask { get; } = Task.FromResult(Cancelled); 14 | 15 | public static IPackageVersionSearchResult Failure { get; } = new PackageVersionSearchResult(); 16 | 17 | public static Task FailureTask { get; } = Task.FromResult(Failure); 18 | 19 | public bool Success { get; } 20 | 21 | public IReadOnlyList Versions { get; } 22 | 23 | public FeedKind SourceKind { get; } 24 | 25 | private PackageVersionSearchResult() 26 | { 27 | } 28 | 29 | public PackageVersionSearchResult(IReadOnlyList versions, FeedKind kind) 30 | { 31 | Versions = versions; 32 | Success = true; 33 | SourceKind = kind; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ProjectFileTools.NuGetSearch/SemanticVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ProjectFileTools.NuGetSearch 4 | { 5 | 6 | /// 7 | /// Semantic version 2.0.0 parser per http://semver.org/spec/v2.0.0.html 8 | /// 9 | public class SemanticVersion : IComparable, IEquatable 10 | { 11 | private readonly int _hashCode; 12 | 13 | public int Major { get; private set; } 14 | 15 | public int Minor { get; private set; } 16 | 17 | public int Patch { get; private set; } 18 | 19 | public string BuildMetadata { get; private set; } 20 | 21 | public string PrereleaseVersion { get; private set; } 22 | 23 | public string OriginalText { get; private set; } 24 | 25 | private SemanticVersion(string originalText) 26 | { 27 | _hashCode = originalText?.GetHashCode() ?? 0; 28 | OriginalText = originalText; 29 | } 30 | 31 | public static SemanticVersion Parse(string value) 32 | { 33 | SemanticVersion ver = new SemanticVersion(value); 34 | 35 | if (value == null) 36 | { 37 | return ver; 38 | } 39 | 40 | int prereleaseStart = value.IndexOf('-'); 41 | int buildMetadataStart = value.IndexOf('+'); 42 | 43 | //If the index of the build metadata marker (+) is greater than the index of the prerelease marker (-) 44 | // then it is necessarily found in the string because if both were not found they'd be equal 45 | if (buildMetadataStart > prereleaseStart) 46 | { 47 | //If the build metadata marker is not the last character in the string, take off everything after it 48 | // and use it for the build metadata field 49 | if (buildMetadataStart < value.Length - 1) 50 | { 51 | ver.BuildMetadata = value.Substring(buildMetadataStart + 1); 52 | } 53 | 54 | value = value.Substring(0, buildMetadataStart); 55 | 56 | //If the prerelease section is found, extract it 57 | if (prereleaseStart > -1) 58 | { 59 | //If the prerelease section marker is not the last character in the string, take off everything after it 60 | // and use it for the prerelease field 61 | if (prereleaseStart < value.Length - 1) 62 | { 63 | ver.PrereleaseVersion = value.Substring(prereleaseStart + 1); 64 | } 65 | 66 | value = value.Substring(0, prereleaseStart); 67 | } 68 | } 69 | //If the build metadata wasn't the last metadata section found, check to see if a prerelease section exists. 70 | // If it doesn't, then neither section exists 71 | else if (prereleaseStart > -1) 72 | { 73 | //If the prerelease version marker is not the last character in the string, take off everything after it 74 | // and use it for the prerelease version field 75 | if (prereleaseStart < value.Length - 1) 76 | { 77 | ver.PrereleaseVersion = value.Substring(prereleaseStart + 1); 78 | } 79 | 80 | value = value.Substring(0, prereleaseStart); 81 | 82 | //If the build metadata section is found, extract it 83 | if (buildMetadataStart > -1) 84 | { 85 | //If the build metadata marker is not the last character in the string, take off everything after it 86 | // and use it for the build metadata field 87 | if (buildMetadataStart < value.Length - 1) 88 | { 89 | ver.BuildMetadata = value.Substring(buildMetadataStart + 1); 90 | } 91 | 92 | value = value.Substring(0, buildMetadataStart); 93 | } 94 | } 95 | 96 | string[] versionParts = value.Split('.'); 97 | 98 | if (versionParts.Length > 0) 99 | { 100 | int major; 101 | int.TryParse(versionParts[0], out major); 102 | ver.Major = major; 103 | } 104 | 105 | if (versionParts.Length > 1) 106 | { 107 | int minor; 108 | int.TryParse(versionParts[1], out minor); 109 | ver.Minor = minor; 110 | } 111 | 112 | if (versionParts.Length > 2) 113 | { 114 | int patch; 115 | int.TryParse(versionParts[2], out patch); 116 | ver.Patch = patch; 117 | } 118 | 119 | return ver; 120 | } 121 | 122 | public int CompareTo(SemanticVersion other) 123 | { 124 | if (other == null) 125 | { 126 | return 1; 127 | } 128 | 129 | int result = Major.CompareTo(other.Major); 130 | 131 | if (result != 0) 132 | { 133 | return result; 134 | } 135 | 136 | result = Minor.CompareTo(other.Minor); 137 | 138 | if (result != 0) 139 | { 140 | return result; 141 | } 142 | 143 | result = Patch.CompareTo(other.Patch); 144 | 145 | if (result != 0) 146 | { 147 | return result; 148 | } 149 | 150 | //A version not marked with prerelease is later than one with a prerelease designation 151 | if (PrereleaseVersion == null && other.PrereleaseVersion != null) 152 | { 153 | return 1; 154 | } 155 | 156 | //A version not marked with prerelease is later than one with a prerelease designation 157 | if (PrereleaseVersion != null && other.PrereleaseVersion == null) 158 | { 159 | return -1; 160 | } 161 | 162 | result = StringComparer.OrdinalIgnoreCase.Compare(PrereleaseVersion, other.PrereleaseVersion); 163 | 164 | if (result != 0) 165 | { 166 | return result; 167 | } 168 | 169 | return StringComparer.OrdinalIgnoreCase.Compare(OriginalText, other.OriginalText); 170 | } 171 | 172 | public bool Equals(SemanticVersion other) 173 | { 174 | return other != null && string.Equals(OriginalText, other.OriginalText, StringComparison.OrdinalIgnoreCase); 175 | } 176 | 177 | public override bool Equals(object obj) 178 | { 179 | return Equals(obj as SemanticVersion); 180 | } 181 | 182 | public override int GetHashCode() 183 | { 184 | return _hashCode; 185 | } 186 | 187 | public override string ToString() 188 | { 189 | return OriginalText; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Adornments/HighlightWordFormatDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio; 5 | using Microsoft.VisualStudio.Shell; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | using Microsoft.VisualStudio.Text.Classification; 8 | using Microsoft.VisualStudio.Utilities; 9 | 10 | namespace ProjectFileTools.Adornments 11 | { 12 | /// 13 | /// Used by HighLightWordTag to get the acquired tags' background color. 14 | /// 15 | [Export(typeof(EditorFormatDefinition))] 16 | [Name("MarkerFormatDefinition/HighlightWordFormatDefinition")] 17 | internal class HighlightWordFormatDefinition : MarkerFormatDefinition 18 | { 19 | /// 20 | /// Refers to MEF Items category (where the Highlighted Reference item is located) 21 | /// 22 | private readonly Guid _textEditorCategory = new Guid("{75A05685-00A8-4DED-BAE5-E7A50BFA929A}"); 23 | 24 | /// 25 | /// Refers to the Highlighted Reference item 26 | /// 27 | private readonly string _itemName = "MarkerFormatDefinition/HighlightedReference"; 28 | 29 | public HighlightWordFormatDefinition() 30 | { 31 | ThreadHelper.ThrowIfNotOnUIThread(); 32 | // Default the BackgroundColor to a transparent grey 33 | BackgroundColor = Color.FromArgb(127, 170, 170, 170); 34 | DisplayName = "Highlight Word"; 35 | ZOrder = 5; 36 | 37 | // If possible, set the Background color to match the Highlighted Reference color 38 | IVsFontAndColorStorage colorStorage = ServiceUtil.GetService(); 39 | ColorableItemInfo[] itemInfoOut = new ColorableItemInfo[1]; 40 | if(colorStorage.OpenCategory(ref _textEditorCategory, (uint)(__FCSTORAGEFLAGS.FCSF_READONLY | __FCSTORAGEFLAGS.FCSF_LOADDEFAULTS) ) == VSConstants.S_OK) 41 | { 42 | if (colorStorage.GetItem(_itemName, itemInfoOut) == VSConstants.S_OK) 43 | { 44 | uint hexColor = itemInfoOut[0].crBackground; 45 | BackgroundColor = Color.FromArgb(255, (byte)hexColor, (byte)(hexColor >> 8), (byte)(hexColor >> 16)); 46 | } 47 | colorStorage.CloseCategory(); 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/ProjectFileTools/Adornments/HighlightWordTag.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text.Tagging; 2 | 3 | namespace ProjectFileTools.Adornments 4 | { 5 | /// 6 | /// Each matched string from HighlightWordTagger is contained in a HighlightWordTag. 7 | /// 8 | internal class HighlightWordTag : TextMarkerTag 9 | { 10 | public HighlightWordTag() : base("MarkerFormatDefinition/HighlightWordFormatDefinition") { } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ProjectFileTools/Adornments/HighlightWordTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.VisualStudio.Text; 5 | using Microsoft.VisualStudio.Text.Editor; 6 | using Microsoft.VisualStudio.Text.Operations; 7 | using Microsoft.VisualStudio.Text.Tagging; 8 | 9 | namespace ProjectFileTools.Adornments 10 | { 11 | /// 12 | /// Finds and updates the correct tags for the highlighted text. 13 | /// 14 | internal class HighlightWordTagger : ITagger 15 | { 16 | private readonly ITextView _view; 17 | 18 | private readonly ITextBuffer _sourceBuffer; 19 | 20 | private readonly ITextSearchService _textSearchService; 21 | 22 | /// 23 | /// Contains Snapshots for each string that matches the highlighted text 24 | /// 25 | private NormalizedSnapshotSpanCollection _wordSpans; 26 | 27 | /// 28 | /// Last highlighted text 29 | /// 30 | private string _currentWord; 31 | 32 | private readonly HighlightWordTag _highlightWordTag; 33 | 34 | public event EventHandler TagsChanged; 35 | 36 | public HighlightWordTagger(ITextView view, ITextBuffer sourceBuffer, ITextSearchService textSearchService) 37 | { 38 | _view = view; 39 | _sourceBuffer = sourceBuffer; 40 | _textSearchService = textSearchService; 41 | _wordSpans = new NormalizedSnapshotSpanCollection(); 42 | _currentWord = ""; 43 | _highlightWordTag = new HighlightWordTag(); 44 | _view.Selection.SelectionChanged += ViewSelectionChanged; 45 | } 46 | 47 | private void ViewSelectionChanged(object sender, EventArgs e) 48 | { 49 | UpdateWordAdornnents(_view.Selection.StreamSelectionSpan.GetText()); 50 | } 51 | 52 | private void UpdateWordAdornnents(string newSelection) 53 | { 54 | // If the new string is equal to the old one, we do not need to update the tags. 55 | if (_currentWord.Equals(newSelection)) 56 | { 57 | return; 58 | } 59 | 60 | List wordSpans = new List(); 61 | 62 | // If the user only selected whitespace, do not create any snapshots. 63 | if (!newSelection.All(char.IsWhiteSpace)) 64 | { 65 | // Finds exact matches (does not match with substrings of words). 66 | FindData findData = new FindData(newSelection, _view.TextSnapshot) 67 | { 68 | FindOptions = FindOptions.WholeWord | FindOptions.MatchCase 69 | }; 70 | wordSpans.AddRange(_textSearchService.FindAll(findData)); 71 | } 72 | 73 | _wordSpans = new NormalizedSnapshotSpanCollection(wordSpans); 74 | Update(newSelection); 75 | } 76 | 77 | private void Update(string currentWord) 78 | { 79 | _currentWord = currentWord; 80 | TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(_sourceBuffer.CurrentSnapshot, 0, _sourceBuffer.CurrentSnapshot.Length))); 81 | } 82 | 83 | public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 84 | { 85 | foreach (SnapshotSpan span in _wordSpans) 86 | { 87 | yield return new TagSpan(span, _highlightWordTag); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Adornments/HighlightWordTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Editor; 4 | using Microsoft.VisualStudio.Text.Operations; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | using Microsoft.VisualStudio.Utilities; 7 | 8 | namespace ProjectFileTools.Adornments 9 | { 10 | /// 11 | /// Creates an instance of HighlightWordTagger and provides the tags it finds to the editor. 12 | /// 13 | [Export(typeof(IViewTaggerProvider))] 14 | [ContentType("XML")] 15 | [TagType(typeof(TextMarkerTag))] 16 | internal class HighlightWordTaggerProvider : IViewTaggerProvider 17 | { 18 | [ImportingConstructor] 19 | public HighlightWordTaggerProvider(ITextSearchService textSearchService) 20 | { 21 | TextSearchService = textSearchService; 22 | } 23 | 24 | internal ITextSearchService TextSearchService { get; } 25 | 26 | public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) 27 | where T : ITag 28 | { 29 | if (textView.TextBuffer != buffer) 30 | { 31 | return null; 32 | } 33 | return new HighlightWordTagger(textView, buffer, TextSearchService) as ITagger; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/ProjectFileTools/Completion/CompletionController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using Microsoft.VisualStudio; 6 | using Microsoft.VisualStudio.Language.Intellisense; 7 | using Microsoft.VisualStudio.OLE.Interop; 8 | using Microsoft.VisualStudio.Shell; 9 | using Microsoft.VisualStudio.Text; 10 | using Microsoft.VisualStudio.Text.Editor; 11 | 12 | namespace ProjectFileTools.Completion 13 | { 14 | class CompletionController : IOleCommandTarget 15 | { 16 | private ICompletionSession _currentSession; 17 | 18 | public CompletionController(IWpfTextView textView, ICompletionBroker broker) 19 | { 20 | _currentSession = null; 21 | 22 | TextView = textView; 23 | Broker = broker; 24 | } 25 | 26 | public IWpfTextView TextView { get; private set; } 27 | 28 | public ICompletionBroker Broker { get; private set; } 29 | 30 | public IOleCommandTarget Next { get; set; } 31 | 32 | private static char GetTypeChar(IntPtr pvaIn) 33 | { 34 | return (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); 35 | } 36 | 37 | public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) 38 | { 39 | ThreadHelper.ThrowIfNotOnUIThread(); 40 | bool handled = false; 41 | int hresult = VSConstants.S_OK; 42 | 43 | // 1. Pre-process 44 | if (pguidCmdGroup == VSConstants.VSStd2K) 45 | { 46 | switch ((VSConstants.VSStd2KCmdID)nCmdID) 47 | { 48 | case VSConstants.VSStd2KCmdID.AUTOCOMPLETE: 49 | case VSConstants.VSStd2KCmdID.COMPLETEWORD: 50 | case VSConstants.VSStd2KCmdID.SHOWMEMBERLIST: 51 | StartSession(); 52 | break; 53 | case VSConstants.VSStd2KCmdID.RETURN: 54 | handled = Complete(false); 55 | break; 56 | case VSConstants.VSStd2KCmdID.TAB: 57 | handled = Complete(true); 58 | break; 59 | case VSConstants.VSStd2KCmdID.CANCEL: 60 | handled = Cancel(); 61 | break; 62 | } 63 | } 64 | 65 | if (!handled) 66 | { 67 | hresult = Next.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); 68 | } 69 | 70 | if (ErrorHandler.Succeeded(hresult)) 71 | { 72 | if (pguidCmdGroup == VSConstants.VSStd2K) 73 | { 74 | switch ((VSConstants.VSStd2KCmdID)nCmdID) 75 | { 76 | case VSConstants.VSStd2KCmdID.TYPECHAR: 77 | char ch = GetTypeChar(pvaIn); 78 | 79 | if (!char.IsControl(ch)) 80 | { 81 | StartSession(); 82 | Filter(); 83 | } 84 | else if (_currentSession != null) 85 | { 86 | Filter(); 87 | } 88 | break; 89 | case VSConstants.VSStd2KCmdID.BACKSPACE: 90 | if (_currentSession == null) 91 | { 92 | StartSession(); 93 | } 94 | 95 | Filter(); 96 | break; 97 | } 98 | } 99 | } 100 | 101 | return hresult; 102 | } 103 | 104 | private void Filter() 105 | { 106 | if (_currentSession == null || _currentSession.SelectedCompletionSet == null) 107 | { 108 | if (Broker.IsCompletionActive(TextView)) 109 | { 110 | _currentSession = Broker.GetSessions(TextView).FirstOrDefault(); 111 | } 112 | 113 | if (_currentSession == null || _currentSession.SelectedCompletionSet == null) 114 | { 115 | return; 116 | } 117 | 118 | _currentSession.Dismissed += (sender, args) => 119 | { 120 | if (sender is ICompletionSession session) 121 | session.Committed -= HandleCompletionSessionCommit; 122 | _currentSession = null; 123 | }; 124 | _currentSession.Committed += HandleCompletionSessionCommit; 125 | } 126 | 127 | if (_currentSession != null) 128 | { 129 | if (_currentSession.TextView.TextBuffer.Properties.TryGetProperty(typeof(PackageCompletionSource), out ICompletionSource src)) 130 | { 131 | src.AugmentCompletionSession(_currentSession, new List()); 132 | } 133 | 134 | _currentSession.Filter(); 135 | } 136 | } 137 | 138 | bool Cancel() 139 | { 140 | if (_currentSession == null) 141 | { 142 | if (Broker.IsCompletionActive(TextView)) 143 | { 144 | _currentSession = Broker.GetSessions(TextView).FirstOrDefault(); 145 | } 146 | 147 | if (_currentSession == null) 148 | { 149 | return false; 150 | } 151 | } 152 | 153 | _currentSession.Dismiss(); 154 | 155 | return true; 156 | } 157 | 158 | bool Complete(bool force) 159 | { 160 | var currentSession = _currentSession; 161 | 162 | if (currentSession == null) 163 | { 164 | if (Broker.IsCompletionActive(TextView)) 165 | { 166 | currentSession = Broker.GetSessions(TextView).FirstOrDefault(); 167 | } 168 | 169 | _currentSession = currentSession; 170 | 171 | if (currentSession == null) 172 | { 173 | return false; 174 | } 175 | 176 | currentSession.Dismissed += (sender, args) => 177 | { 178 | if (sender is ICompletionSession session) 179 | session.Committed -= HandleCompletionSessionCommit; 180 | _currentSession = null; 181 | }; 182 | currentSession.Committed += HandleCompletionSessionCommit; 183 | } 184 | 185 | if (currentSession.SelectedCompletionSet != null && currentSession.SelectedCompletionSet.SelectionStatus != null && !currentSession.SelectedCompletionSet.SelectionStatus.IsSelected && !force) 186 | { 187 | currentSession.Dismiss(); 188 | _currentSession = null; 189 | return false; 190 | } 191 | else if (currentSession.SelectedCompletionSet != null && currentSession.SelectedCompletionSet.SelectionStatus != null) 192 | { 193 | currentSession.Commit(); 194 | _currentSession = null; 195 | return true; 196 | } 197 | else 198 | { 199 | return false; 200 | } 201 | } 202 | 203 | bool StartSession() 204 | { 205 | if (Broker.IsCompletionActive(TextView)) 206 | { 207 | return false; 208 | } 209 | else 210 | { 211 | _currentSession = null; 212 | } 213 | 214 | SnapshotPoint caret = TextView.Caret.Position.BufferPosition; 215 | 216 | if (caret.Position == 0) 217 | { 218 | return false; 219 | } 220 | 221 | ITextSnapshot snapshot = caret.Snapshot; 222 | ICompletionSession currentSession = _currentSession; 223 | 224 | if (!Broker.IsCompletionActive(TextView)) 225 | { 226 | currentSession = Broker.CreateCompletionSession(TextView, snapshot.CreateTrackingPoint(caret, PointTrackingMode.Positive), true); 227 | } 228 | else 229 | { 230 | currentSession = Broker.GetSessions(TextView)[0]; 231 | } 232 | 233 | _currentSession = currentSession; 234 | 235 | if (currentSession != null) 236 | { 237 | currentSession.Dismissed += (sender, args) => 238 | { 239 | if (sender is ICompletionSession session) 240 | session.Committed -= HandleCompletionSessionCommit; 241 | _currentSession = null; 242 | }; 243 | 244 | currentSession.Committed += HandleCompletionSessionCommit; 245 | 246 | if (!currentSession.IsStarted) 247 | { 248 | currentSession.Start(); 249 | } 250 | } 251 | 252 | return true; 253 | } 254 | 255 | private void HandleCompletionSessionCommit(object sender, EventArgs e) 256 | { 257 | CompletionSet completionSet = ((ICompletionSession)sender).SelectedCompletionSet; 258 | 259 | if (completionSet == null) 260 | { 261 | return; 262 | } 263 | 264 | SnapshotSpan span = completionSet.ApplicableTo.GetSpan(completionSet.ApplicableTo.TextBuffer.CurrentSnapshot); 265 | int caret = span.Span.Start; 266 | 267 | if (PackageCompletionSource.TryHealOrAdvanceAttributeSelection(span.Snapshot, ref caret, out Span targetSpan, out string replacementText, out bool isHealingRequired)) 268 | { 269 | if (isHealingRequired) 270 | { 271 | ITextSnapshot newSnapshot = span.Snapshot.TextBuffer.Replace(targetSpan, replacementText); 272 | TextView.Caret.MoveTo(new SnapshotPoint(newSnapshot, caret)); 273 | } 274 | else 275 | { 276 | TextView.Caret.MoveTo(new SnapshotPoint(span.Snapshot, caret)); 277 | } 278 | 279 | Broker.TriggerCompletion(TextView); 280 | } 281 | } 282 | 283 | public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) 284 | { 285 | ThreadHelper.ThrowIfNotOnUIThread(); 286 | 287 | if (pguidCmdGroup == VSConstants.VSStd2K) 288 | { 289 | switch ((VSConstants.VSStd2KCmdID)prgCmds[0].cmdID) 290 | { 291 | case VSConstants.VSStd2KCmdID.AUTOCOMPLETE: 292 | case VSConstants.VSStd2KCmdID.COMPLETEWORD: 293 | case VSConstants.VSStd2KCmdID.SHOWMEMBERLIST: 294 | prgCmds[0].cmdf = (uint)OLECMDF.OLECMDF_ENABLED | (uint)OLECMDF.OLECMDF_SUPPORTED; 295 | return VSConstants.S_OK; 296 | } 297 | } 298 | return Next.QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText); 299 | } 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Completion/CompletionTooltipElementProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using System.Windows; 3 | using Microsoft.VisualStudio.Language.Intellisense; 4 | using Microsoft.VisualStudio.Utilities; 5 | using ProjectFileTools.NuGetSearch.Contracts; 6 | 7 | namespace ProjectFileTools.Completion 8 | { 9 | [Export(typeof(IUIElementProvider))] 10 | [Name("Package Information Completion Tooltip")] 11 | [ContentType("XML")] 12 | internal class CompletionTooltipElementProvider : IUIElementProvider 13 | { 14 | private readonly IPackageSearchManager _searcher; 15 | 16 | [ImportingConstructor] 17 | public CompletionTooltipElementProvider(IPackageSearchManager searcher) 18 | { 19 | _searcher = searcher; 20 | } 21 | 22 | public UIElement GetUIElement(Microsoft.VisualStudio.Language.Intellisense.Completion itemToRender, ICompletionSession context, UIElementType elementType) 23 | { 24 | if (itemToRender is PackageCompletion && elementType == UIElementType.Tooltip) 25 | { 26 | return new PackageInfoControl(itemToRender.DisplayText, null, null, _searcher); 27 | } 28 | else 29 | { 30 | return null; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Completion/PackageCompletion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.VisualStudio.Imaging.Interop; 3 | using Microsoft.VisualStudio.Language.Intellisense; 4 | 5 | namespace ProjectFileTools.Completion 6 | { 7 | public class PackageCompletion : Completion4 8 | { 9 | public PackageCompletion(string displayText, string insertionText, string description, ImageMoniker iconMoniker, string iconAutomationText = null, IEnumerable attributeIcons = null, string suffix = null) 10 | : base(displayText, insertionText, description, iconMoniker, iconAutomationText, attributeIcons, suffix) 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Completion/PackageCompletionSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.VisualStudio.Language.Intellisense; 3 | using Microsoft.VisualStudio.Text; 4 | 5 | namespace ProjectFileTools.Completion 6 | { 7 | 8 | internal class PackageCompletionSet : CompletionSet2 9 | { 10 | public PackageCompletionSet(string moniker, string displayName, ITrackingSpan applicableTo) 11 | : base(moniker, displayName, applicableTo, new Microsoft.VisualStudio.Language.Intellisense.Completion[0], new Microsoft.VisualStudio.Language.Intellisense.Completion[0], new IIntellisenseFilter[0]) 12 | { 13 | AccessibleCompletions = new BulkObservableCollection(); 14 | } 15 | 16 | public override IList Completions => AccessibleCompletions; 17 | 18 | public BulkObservableCollection AccessibleCompletions { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Completion/PackageCompletionSourceProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Language.Intellisense; 4 | using Microsoft.VisualStudio.Text; 5 | using Microsoft.VisualStudio.Text.Classification; 6 | using Microsoft.VisualStudio.Utilities; 7 | using ProjectFileTools.NuGetSearch.Contracts; 8 | 9 | namespace ProjectFileTools.Completion 10 | { 11 | [Export(typeof(ICompletionSourceProvider))] 12 | [Name("Xml Package Intellisense Controller")] 13 | [ContentType("XML")] 14 | internal class PackageCompletionSourceProvider : ICompletionSourceProvider 15 | { 16 | private readonly IClassifierAggregatorService _classifier; 17 | private readonly ICompletionBroker _completionBroker; 18 | private readonly IPackageSearchManager _searchManager; 19 | 20 | [ImportingConstructor] 21 | public PackageCompletionSourceProvider(ICompletionBroker completionBroker, IPackageSearchManager searchManager, IClassifierAggregatorService classifier) 22 | { 23 | _classifier = classifier; 24 | _completionBroker = completionBroker; 25 | _searchManager = searchManager; 26 | } 27 | 28 | public ICompletionSource TryCreateCompletionSource(ITextBuffer textBuffer) 29 | { 30 | string text = textBuffer.CurrentSnapshot.GetText(); 31 | bool isCore = text.IndexOf("Microsoft.Net.Sdk", StringComparison.OrdinalIgnoreCase) > -1; 32 | 33 | if (isCore) 34 | { 35 | return PackageCompletionSource.GetOrAddCompletionSource(textBuffer, _completionBroker, _classifier, _searchManager); 36 | } 37 | 38 | return null; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Completion/PackageIntellisenseController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.VisualStudio.Language.Intellisense; 4 | using Microsoft.VisualStudio.Text; 5 | using Microsoft.VisualStudio.Text.Editor; 6 | 7 | namespace ProjectFileTools.Completion 8 | { 9 | 10 | internal class PackageIntellisenseController : IIntellisenseController 11 | { 12 | private ICompletionBroker _completionBroker; 13 | private IList _subjectBuffers; 14 | private ITextView _textView; 15 | 16 | public PackageIntellisenseController(ITextView textView, IList subjectBuffers, ICompletionBroker completionBroker) 17 | { 18 | _textView = textView; 19 | _subjectBuffers = subjectBuffers.ToList(); 20 | _completionBroker = completionBroker; 21 | 22 | foreach(ITextBuffer buffer in subjectBuffers) 23 | { 24 | ConnectSubjectBuffer(buffer); 25 | } 26 | } 27 | 28 | public void ConnectSubjectBuffer(ITextBuffer subjectBuffer) 29 | { 30 | _subjectBuffers.Add(subjectBuffer); 31 | } 32 | 33 | public void Detach(ITextView textView) 34 | { 35 | if (_textView == textView) 36 | { 37 | _textView = null; 38 | } 39 | } 40 | 41 | public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer) 42 | { 43 | _subjectBuffers.Remove(subjectBuffer); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Completion/PackageIntellisenseControllerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using Microsoft.VisualStudio.Language.Intellisense; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.VisualStudio.Text.Editor; 7 | using Microsoft.VisualStudio.Utilities; 8 | using ProjectFileTools.NuGetSearch.Contracts; 9 | 10 | namespace ProjectFileTools.Completion 11 | { 12 | [Export(typeof(IIntellisenseControllerProvider))] 13 | [Name("Xml Package Intellisense Controller")] 14 | [ContentType("XML")] 15 | internal class PackageIntellisenseControllerProvider : IIntellisenseControllerProvider 16 | { 17 | private readonly IPackageSearchManager _searchManager; 18 | 19 | [ImportingConstructor] 20 | public PackageIntellisenseControllerProvider(ICompletionBroker completionBroker, IPackageSearchManager searchManager) 21 | { 22 | CompletionBroker = completionBroker; 23 | _searchManager = searchManager; 24 | } 25 | 26 | internal ICompletionBroker CompletionBroker { get; } 27 | 28 | public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList subjectBuffers) 29 | { 30 | return new PackageIntellisenseController(textView, subjectBuffers, CompletionBroker); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Completion/VersionCompletion.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Imaging.Interop; 2 | using Microsoft.VisualStudio.Language.Intellisense; 3 | 4 | namespace ProjectFileTools.Completion 5 | { 6 | public class VersionCompletion : Completion3 7 | { 8 | public VersionCompletion(string displayText, string insertionText, string description, ImageMoniker iconMoniker, string iconAutomationText) 9 | : base(displayText, insertionText, description, iconMoniker, iconAutomationText) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Exports/ExportedFileSystem.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Utilities; 3 | using ProjectFileTools.NuGetSearch.IO; 4 | 5 | namespace ProjectFileTools.Exports 6 | { 7 | [Export(typeof(IFileSystem))] 8 | [Name("Default File System Implementation")] 9 | internal class ExportedFileSystem : FileSystem 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Exports/ExportedNuGetDiskFeedFactory.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Utilities; 3 | using ProjectFileTools.NuGetSearch.Contracts; 4 | using ProjectFileTools.NuGetSearch.Feeds.Disk; 5 | using ProjectFileTools.NuGetSearch.IO; 6 | 7 | namespace ProjectFileTools.Exports 8 | { 9 | [Export(typeof(IPackageFeedFactory))] 10 | [Name("Default Package Feed Factory")] 11 | internal class ExportedNuGetDiskFeedFactory : NuGetDiskFeedFactory 12 | { 13 | [ImportingConstructor] 14 | public ExportedNuGetDiskFeedFactory(IFileSystem fileSystem) 15 | : base(fileSystem) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Exports/ExportedNuGetV2ServiceFeedFactory.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Utilities; 3 | using ProjectFileTools.NuGetSearch.Contracts; 4 | using ProjectFileTools.NuGetSearch.Feeds.Web; 5 | using ProjectFileTools.NuGetSearch.IO; 6 | 7 | namespace ProjectFileTools.Exports 8 | { 9 | [Export(typeof(IPackageFeedFactory))] 10 | [Name("Default NuGet v2 Service Feed Factory")] 11 | internal class ExportedNuGetV2ServiceFeedFactory : NuGetV2ServiceFeedFactory 12 | { 13 | [ImportingConstructor] 14 | public ExportedNuGetV2ServiceFeedFactory(IWebRequestFactory webRequestFactory) 15 | : base(webRequestFactory) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Exports/ExportedNuGetV3ServiceFeedFactory.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Utilities; 3 | using ProjectFileTools.NuGetSearch.Contracts; 4 | using ProjectFileTools.NuGetSearch.Feeds.Web; 5 | using ProjectFileTools.NuGetSearch.IO; 6 | 7 | namespace ProjectFileTools.Exports 8 | { 9 | [Export(typeof(IPackageFeedFactory))] 10 | [Name("Default NuGet v3 Service Feed Factory")] 11 | internal class ExportedNuGetV3ServiceFeedFactory : NuGetV3ServiceFeedFactory 12 | { 13 | [ImportingConstructor] 14 | public ExportedNuGetV3ServiceFeedFactory(IWebRequestFactory webRequestFactory) 15 | : base(webRequestFactory) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Exports/ExportedPackageFeedFactorySelector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Utilities; 4 | using ProjectFileTools.NuGetSearch.Contracts; 5 | using ProjectFileTools.NuGetSearch.Feeds; 6 | 7 | namespace ProjectFileTools.Exports 8 | { 9 | [Export(typeof(IPackageFeedFactorySelector))] 10 | [Name("Default Package Feed Factory Selector")] 11 | internal class ExportedPackageFeedFactorySelector : PackageFeedFactorySelector 12 | { 13 | [ImportingConstructor] 14 | public ExportedPackageFeedFactorySelector([ImportMany] IEnumerable feedFactories) 15 | : base(feedFactories) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/ProjectFileTools/Exports/ExportedPackageSearchManager.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Utilities; 3 | using ProjectFileTools.NuGetSearch.Contracts; 4 | using ProjectFileTools.NuGetSearch.Search; 5 | 6 | namespace ProjectFileTools.Exports 7 | { 8 | [Export(typeof(IPackageSearchManager))] 9 | [Name("Default Package Search Manager")] 10 | internal class ExportedPackageSearchManager : PackageSearchManager 11 | { 12 | [ImportingConstructor] 13 | public ExportedPackageSearchManager(IPackageFeedRegistryProvider feedRegistry, IPackageFeedFactorySelector factorySelector) 14 | : base(feedRegistry, factorySelector) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Exports/ExportedWebRequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Utilities; 3 | using ProjectFileTools.NuGetSearch.IO; 4 | 5 | namespace ProjectFileTools.Exports 6 | { 7 | [Export(typeof(IWebRequestFactory))] 8 | [Name("Default Web Request Factory")] 9 | internal class ExportedWebRequestFactory : WebRequestFactory 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Exports/ExportedWorkspaceManager.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Utilities; 3 | using ProjectFileTools.MSBuild; 4 | 5 | namespace ProjectFileTools.Exports 6 | { 7 | [Export(typeof(IWorkspaceManager))] 8 | [Name("Default WorkspaceManager")] 9 | public class ExportedWorkspaceManager : WorkspaceManager 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ProjectFileTools/FindAllReferences/FarDataSnapshot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using System.Windows; 5 | using System.Windows.Documents; 6 | using Microsoft.VisualStudio.Shell.TableControl; 7 | using Microsoft.VisualStudio.Shell.TableManager; 8 | using ProjectFileTools.MSBuild; 9 | 10 | namespace FarTestProvider 11 | { 12 | public class FarDataSnapshot : WpfTableEntriesSnapshotBase 13 | { 14 | private static int versionGenerator = -1; 15 | private readonly int _versionNumber; 16 | private readonly List _definitions; 17 | private IList _buckets; 18 | 19 | public FarDataSnapshot(List definitions) 20 | { 21 | _definitions = definitions; 22 | 23 | _versionNumber = Interlocked.Increment(ref versionGenerator); 24 | 25 | _buckets = new FarDefinitionBucket[definitions.Count]; 26 | for (int i = 0; i < definitions.Count; i++) 27 | { 28 | _buckets[i] = new FarDefinitionBucket(definitions[i]); 29 | } 30 | } 31 | 32 | public override int Count { get { return _buckets.Count; } } 33 | 34 | public override int VersionNumber { get { return _versionNumber; } } 35 | 36 | /// 37 | /// Find All References table calls this method for information 38 | /// 39 | public override bool TryGetValue(int index, string keyName, out object content) 40 | { 41 | Debug.Assert(index >= 0 && index < _buckets.Count); 42 | 43 | FarDefinitionBucket bucket = _buckets[index % _buckets.Count]; 44 | 45 | switch (keyName) 46 | { 47 | case StandardTableKeyNames.ProjectName: 48 | content = bucket.Location.Project; 49 | return true; 50 | case StandardTableKeyNames.DocumentName: 51 | content = bucket.Location.File; 52 | return true; 53 | case StandardTableKeyNames.Line: 54 | // line # 55 | content = bucket.Location.Line.GetValueOrDefault() - 1; 56 | return true; 57 | case StandardTableKeyNames.Column: 58 | // col # 59 | content = bucket.Location.Col.GetValueOrDefault() - 1; 60 | return true; 61 | case StandardTableKeyNames.Text: 62 | content = bucket.Location.Type; 63 | return true; 64 | case StandardTableKeyNames.FullText: 65 | case StandardTableKeyNames2.TextInlines: 66 | { 67 | List inlines = new List 68 | { 69 | new Run(bucket.Location.Text) { FontWeight = FontWeights.Bold } 70 | }; 71 | 72 | content = inlines; 73 | return true; 74 | } 75 | case StandardTableKeyNames2.Definition: 76 | { 77 | // queries which definition bucket this entry belongs to 78 | content = bucket; 79 | return true; 80 | } 81 | case StandardTableKeyNames.HelpKeyword: 82 | content = "FindAllReferences"; 83 | return true; 84 | case StandardTableKeyNames.HelpLink: 85 | content = "https://www.visualstudio.com/"; 86 | return true; 87 | case StandardTableKeyNames.HasVerticalContent: 88 | case StandardTableKeyNames.DetailsExpander: 89 | case StandardTableColumnDefinitions2.LineText: 90 | case StandardTableKeyNames2.ProjectNames: 91 | case "IPersistentSpan": 92 | break; 93 | default: 94 | Debug.Fail($"Unknown column key: {keyName}"); 95 | break; 96 | } 97 | 98 | content = null; 99 | return false; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/ProjectFileTools/FindAllReferences/FarDataSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Shell.TableControl; 3 | using Microsoft.VisualStudio.Shell.TableManager; 4 | using ProjectFileTools.FindAllReferences; 5 | 6 | namespace FarTestProvider 7 | { 8 | public class FarDataSource : ITableDataSource 9 | { 10 | public const string Name = "FAR_TestDataSource"; 11 | 12 | public ITableDataSink Sink { get; private set; } 13 | public ITableEntriesSnapshot[] Snapshots; 14 | 15 | public FarDataSource(int snapshotCapacity) 16 | { 17 | this.Snapshots = new FarDataSnapshot[snapshotCapacity]; 18 | } 19 | 20 | public string DisplayName 21 | { 22 | get 23 | { 24 | return Name; 25 | } 26 | } 27 | 28 | public string Identifier 29 | { 30 | get 31 | { 32 | return Name; 33 | } 34 | } 35 | 36 | public string SourceTypeIdentifier 37 | { 38 | get 39 | { 40 | return StandardTableDataSources2.FindAllReferencesTableDataSource; 41 | } 42 | } 43 | 44 | /// 45 | /// Called when a subscribes to the . 46 | /// 47 | /// A for and to share data. 48 | /// A disposable object representing this subscription. Can be disposed of by the when unsubscribed. 49 | public IDisposable Subscribe(ITableDataSink sink) 50 | { 51 | this.Sink = sink; 52 | return new FarDataSubscription(sink); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/ProjectFileTools/FindAllReferences/FarDataSubscription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Shell.TableManager; 3 | 4 | namespace ProjectFileTools.FindAllReferences 5 | { 6 | /// 7 | /// A dummy class for data source subscription 8 | /// 9 | public class FarDataSubscription : IDisposable 10 | { 11 | // reserve for future use 12 | private ITableDataSink _sink; 13 | 14 | public FarDataSubscription(ITableDataSink sink) 15 | { 16 | _sink = sink; 17 | } 18 | 19 | public void Dispose() 20 | { 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ProjectFileTools/FindAllReferences/FarDefinitionBucket.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Windows; 4 | using System.Windows.Documents; 5 | using Microsoft.VisualStudio.Imaging; 6 | using Microsoft.VisualStudio.Shell.FindAllReferences; 7 | using Microsoft.VisualStudio.Shell.TableControl; 8 | using Microsoft.VisualStudio.Shell.TableManager; 9 | using ProjectFileTools.MSBuild; 10 | 11 | /// 12 | /// An displaying a symbol definition bucket. 13 | /// A bucket displays in the table as a grouping row, under which entries in the same bucket are displaied. 14 | /// 15 | public class FarDefinitionBucket : DefinitionBucket 16 | { 17 | public const string BucketIdentifier = "Far_DefinitionBucket"; 18 | 19 | internal readonly Definition Location; 20 | 21 | public FarDefinitionBucket(Definition location) 22 | : base(location.Type, BucketIdentifier, BucketIdentifier) 23 | { 24 | Location = location; 25 | } 26 | 27 | public override bool TryGetValue(string key, out object content) 28 | { 29 | 30 | switch (key) 31 | { 32 | case StandardTableKeyNames.Text: 33 | content = this.Name; 34 | return true; 35 | case StandardTableKeyNames.DocumentName: 36 | content = Location.File; 37 | return true; 38 | case StandardTableKeyNames.Line: 39 | content = 0; 40 | return true; 41 | case StandardTableKeyNames.Column: 42 | content = 0; 43 | return true; 44 | case StandardTableKeyNames2.DefinitionIcon: 45 | // icon image of this bucket (displayed in front of the bucket content) 46 | content = KnownMonikers.TypeDefinition; 47 | return true; 48 | case StandardTableKeyNames2.TextInlines: 49 | // content of the bucket displayed as a rich text 50 | List inlines = new List(); 51 | inlines.Add(new Run(this.Name) { FontWeight = FontWeights.Bold }); 52 | content = inlines; 53 | return true; 54 | case StandardTableKeyNames.HelpKeyword: 55 | content = "FindAllReferences"; 56 | return true; 57 | case StandardTableKeyNames.HelpLink: 58 | content = "https://www.visualstudio.com/"; 59 | return true; 60 | case "IPersistentSpan": 61 | break; 62 | default: 63 | Debug.Fail($"Unknown bucket key: {key}"); 64 | break; 65 | } 66 | 67 | content = null; 68 | return false; 69 | } 70 | } -------------------------------------------------------------------------------- /src/ProjectFileTools/Helpers/XmlInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Linq; 2 | 3 | namespace ProjectFileTools.Helpers 4 | { 5 | public class XmlInfo 6 | { 7 | public XmlInfo(string originalText, string elementText, int start, int end, int startQuote, int endQuote, bool isModified, int realEnd, string elementName, string attributeName) 8 | { 9 | OriginalText = originalText; 10 | ElementText = elementText; 11 | TagStart = start; 12 | TagEnd = end; 13 | AttributeQuoteStart = startQuote; 14 | AttributeQuoteEnd = endQuote; 15 | IsModified = isModified; 16 | EndInActualDocument = realEnd; 17 | TagName = elementName; 18 | AttributeName = attributeName; 19 | } 20 | 21 | public bool TryGetElement(out XElement element) 22 | { 23 | try 24 | { 25 | element = XElement.Parse(ElementText); 26 | } 27 | catch 28 | { 29 | element = null; 30 | return false; 31 | } 32 | 33 | return true; 34 | } 35 | 36 | public int TagStart { get; } 37 | 38 | public int TagEnd { get; } 39 | 40 | public int AttributeQuoteStart { get; } 41 | 42 | public int AttributeQuoteEnd { get; } 43 | 44 | public int EndInActualDocument { get; } 45 | 46 | public bool IsModified { get; } 47 | 48 | public string OriginalText { get; } 49 | 50 | public string ElementText { get; } 51 | 52 | public string TagName { get; } 53 | 54 | public string AttributeName { get; } 55 | 56 | public int AttributeValueStart => AttributeQuoteStart + 1; 57 | 58 | public int AttributeValueLength => AttributeQuoteEnd - AttributeQuoteStart - 1; 59 | 60 | public string AttributeValue => ElementText.Substring(AttributeValueStart - TagStart, AttributeValueLength); 61 | 62 | public int RealDocumentLength => EndInActualDocument - TagStart + 1; 63 | 64 | public void Flatten(out string documentText, out int start, out int end, out int startQuote, out int endQuote, out int realEnd, out bool isHealingRequired, out string healedXml) 65 | { 66 | documentText = OriginalText; 67 | start = TagStart; 68 | end = TagEnd; 69 | startQuote = AttributeQuoteStart; 70 | endQuote = AttributeQuoteEnd; 71 | realEnd = EndInActualDocument; 72 | isHealingRequired = IsModified; 73 | healedXml = ElementText; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Helpers/XmlTools.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text; 2 | 3 | namespace ProjectFileTools.Helpers 4 | { 5 | internal static class XmlTools 6 | { 7 | public static XmlInfo GetXmlInfo(ITextSnapshot snapshot, int pos) 8 | { 9 | if (pos < 1) 10 | { 11 | return null; 12 | } 13 | 14 | string documentText = snapshot.GetText(); 15 | return GetXmlInfo(documentText, pos); 16 | } 17 | 18 | public static XmlInfo GetXmlInfo(string documentText, int pos) 19 | { 20 | if (pos < 1) 21 | { 22 | return null; 23 | } 24 | 25 | int start = pos < documentText.Length ? documentText.LastIndexOf('<', pos) : documentText.LastIndexOf('<'); 26 | int end = pos < documentText.Length ? documentText.IndexOf('>', pos) : -1; 27 | int startQuote = pos <= documentText.Length ? documentText.LastIndexOf('"', pos - 1) : -1; 28 | int endQuote = pos < documentText.Length ? documentText.IndexOf('"', pos) : -1; 29 | int realEnd = end; 30 | bool isHealed = false; 31 | 32 | if (startQuote > -1 && startQuote < start || end > -1 && endQuote > end) 33 | { 34 | //We could be inside of an element here 35 | int closeTag = (pos < documentText.Length ? documentText.LastIndexOf('>', pos) : documentText.LastIndexOf('>')); 36 | bool isInsideTag = closeTag > start; 37 | 38 | if (isInsideTag && closeTag > 1) 39 | { 40 | if (documentText[closeTag - 1] == '/') 41 | { 42 | return null; 43 | } 44 | 45 | int nextOpen = documentText.IndexOf('<', closeTag); 46 | 47 | if (nextOpen < 0) 48 | { 49 | nextOpen = documentText.Length; 50 | } 51 | 52 | int elementNameEnd = documentText.IndexOf(' ', start); 53 | 54 | if (elementNameEnd < 0 || elementNameEnd > closeTag) 55 | { 56 | elementNameEnd = closeTag; 57 | } 58 | 59 | string elementName = documentText.Substring(start + 1, elementNameEnd - start - 1); 60 | string text = documentText.Substring(closeTag + 1, nextOpen - closeTag - 1); 61 | return new XmlInfo(text, text, closeTag + 1, nextOpen - 1, closeTag, nextOpen, false, nextOpen - 1, elementName, null); 62 | } 63 | 64 | return null; 65 | } 66 | 67 | string fragmentText = null; 68 | //If we managed to find a close... 69 | if (end > start) 70 | { 71 | int nextStart = start < documentText.Length - 1 ? documentText.IndexOf('<', start + 1) : -1; 72 | 73 | //If we found another start before the close... 74 | // nextStart || endQuote < 0) 86 | { 87 | endQuote = fragmentText.Length + start; 88 | fragmentText += "\""; 89 | } 90 | break; 91 | case '>': 92 | return null; 93 | default: 94 | //If there's a start quote in play, we're just looking at an unclosed attribute value 95 | if (startQuote > start) 96 | { 97 | endQuote = fragmentText.Length + start; 98 | fragmentText += "\""; 99 | } 100 | else 101 | { 102 | return null; 103 | } 104 | break; 105 | } 106 | 107 | end = fragmentText.Length + 2 + start; 108 | fragmentText += " />"; 109 | isHealed = true; 110 | } 111 | //We didn't find that, so we're "good"... we might have a non-self closed tag though 112 | else 113 | { 114 | isHealed = false; 115 | 116 | //Unless of course the closing quote was after the end... 117 | if (endQuote < 0 || endQuote > end) 118 | { 119 | fragmentText = documentText.Substring(start, end - start); 120 | 121 | if (fragmentText.EndsWith("/")) 122 | { 123 | fragmentText = fragmentText.Substring(0, fragmentText.Length - 1); 124 | } 125 | 126 | fragmentText = fragmentText.TrimEnd() + "\" />"; 127 | 128 | endQuote = fragmentText.Length - 4 + start; 129 | end = fragmentText.Length - 1 + start; 130 | } 131 | else 132 | { 133 | fragmentText = documentText.Substring(start, end - start + 1); 134 | } 135 | } 136 | } 137 | //If we didn't find a close, if we found an end quote, that's good enough 138 | else if (endQuote > start) 139 | { 140 | realEnd = endQuote; 141 | fragmentText = documentText.Substring(start, endQuote - start + 1) + " />"; 142 | end = fragmentText.Length - 1 + start; 143 | isHealed = true; 144 | } 145 | //If we didn't find an end quote even, if we found an open quote, that might be good enough 146 | else if (startQuote > start) 147 | { 148 | //If we find a close before the cursor that's after the start quote, we're outside of the element 149 | if (pos > documentText.Length || documentText.LastIndexOf('>', pos - 1) > startQuote) 150 | { 151 | return null; 152 | } 153 | 154 | //Otherwise, we're presumably just after the start quote (and maybe some text) at the end of the document 155 | // we already know there's no closing quote or end, see if there's another start we can run up to 156 | int nextStart = pos < documentText.Length ? documentText.IndexOf('<', pos) : -1; 157 | 158 | //If there isn't, run off to the end of the document 159 | if (nextStart < 0) 160 | { 161 | fragmentText = documentText.Substring(start, documentText.Length - start).TrimEnd(); 162 | } 163 | else 164 | { 165 | fragmentText = documentText.Substring(start, nextStart - start + 1); 166 | fragmentText = fragmentText.Trim(); 167 | } 168 | 169 | realEnd = start + fragmentText.Length - 1; 170 | fragmentText += "\" />"; 171 | endQuote = fragmentText.Length - 4 + start; 172 | end = fragmentText.Length - 1 + start; 173 | isHealed = true; 174 | } 175 | //All we've got to go on is the start, that's not good enough 176 | else 177 | { 178 | return null; 179 | } 180 | 181 | string healedXml = fragmentText; 182 | 183 | int indexAfterTagNameEnd = healedXml.FindFirstWhitespaceAtOrAfter(1); 184 | string tagName = healedXml.Substring(1, indexAfterTagNameEnd - 1); 185 | int equalsIndex = startQuote >= start && (startQuote - start) < healedXml.Length ? healedXml.LastIndexOf('=', startQuote - start) - 1 : -1; 186 | int afterAttributeIndex = healedXml.FindFirstNonWhitespaceAtOrBefore(equalsIndex, false); 187 | int beforeAttributeIndex = healedXml.FindFirstNonWhitespaceAtOrBefore(afterAttributeIndex, true); 188 | string attributeName = healedXml.Substring(beforeAttributeIndex + 1, afterAttributeIndex - beforeAttributeIndex); 189 | string orignalText = documentText.Substring(start, realEnd - start + 1); 190 | 191 | return new XmlInfo(orignalText, healedXml, start, end, startQuote, endQuote, isHealed, realEnd, tagName, attributeName); 192 | } 193 | 194 | private static int FindFirstWhitespaceAtOrAfter(this string text, int startAt) 195 | { 196 | for (int i = startAt; i < text.Length; ++i) 197 | { 198 | if (char.IsWhiteSpace(text[i])) 199 | { 200 | return i; 201 | } 202 | } 203 | 204 | return -1; 205 | } 206 | 207 | private static int FindFirstNonWhitespaceAtOrBefore(this string text, int startAt, bool invert) 208 | { 209 | for (int i = startAt; i > -1; --i) 210 | { 211 | if (invert ^ !char.IsWhiteSpace(text[i])) 212 | { 213 | return i; 214 | } 215 | } 216 | 217 | return -1; 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/ProjectFileTools/PackageFeedRegistryProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.Utilities; 6 | using ProjectFileTools.NuGetSearch.Contracts; 7 | using ProjectFileTools.NuGetSearch.IO; 8 | 9 | namespace ProjectFileTools 10 | { 11 | [Export(typeof(IPackageFeedRegistryProvider))] 12 | [Name("Default Package Feed Registry Provider")] 13 | internal class PackageFeedRegistryProvider : IPackageFeedRegistryProvider 14 | { 15 | private readonly object _provider; 16 | private readonly IWebRequestFactory _webRequestFactory; 17 | 18 | [ImportingConstructor] 19 | public PackageFeedRegistryProvider([Import("NuGet.VisualStudio.IVsPackageSourceProvider")]object provider, IWebRequestFactory webRequestFactory) 20 | { 21 | _webRequestFactory = webRequestFactory; 22 | _provider = provider; 23 | } 24 | 25 | public IReadOnlyList ConfiguredFeeds 26 | { 27 | get 28 | { 29 | List sources = new List(); 30 | IEnumerable> enabledSources = (IEnumerable>)_provider.GetType().GetMethod("GetSources").Invoke(_provider, new object[] { true, false }); 31 | 32 | foreach (KeyValuePair curEnabledSource in enabledSources) 33 | { 34 | string source = curEnabledSource.Value; 35 | sources.Add(source); 36 | } 37 | 38 | if(!sources.Any(x => x.IndexOf("\\.nuget", StringComparison.OrdinalIgnoreCase) > -1)) 39 | { 40 | sources.Add(Environment.ExpandEnvironmentVariables("%USERPROFILE%\\.nuget\\packages")); 41 | } 42 | 43 | return sources; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ProjectFileTools/PackageInfoControl.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | -------------------------------------------------------------------------------- /src/ProjectFileTools/PackageInfoControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Media; 7 | using System.Windows.Media.Imaging; 8 | using Microsoft.VisualStudio.Imaging; 9 | using Microsoft.VisualStudio.Shell; 10 | using ProjectFileTools.NuGetSearch; 11 | using ProjectFileTools.NuGetSearch.Contracts; 12 | 13 | namespace ProjectFileTools 14 | { 15 | /// 16 | /// Interaction logic for PackageInfoControl.xaml 17 | /// 18 | public partial class PackageInfoControl : UserControl 19 | { 20 | private readonly IPackageFeedSearchJob _job; 21 | private static readonly Dictionary SourceLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); 22 | private bool _firstRun; 23 | 24 | public PackageInfoControl(string packageId, string version, string tfm, IPackageSearchManager searcher) 25 | { 26 | InitializeComponent(); 27 | this.ShouldBeThemed(); 28 | PackageId.Content = packageId; 29 | Glyph.Source = WpfUtil.MonikerToBitmap(KnownMonikers.NuGet, 32); 30 | Glyph.ImageFailed += OnImageFailed; 31 | _job = searcher.SearchPackageInfo(packageId, version, tfm); 32 | _job.Updated += JobUpdated; 33 | _firstRun = true; 34 | JobUpdated(null, EventArgs.Empty); 35 | } 36 | 37 | private void OnImageFailed(object sender, ExceptionRoutedEventArgs e) 38 | { 39 | Glyph.ImageFailed -= OnImageFailed; 40 | Glyph.Source = WpfUtil.MonikerToBitmap(KnownMonikers.NuGet, 32); 41 | } 42 | 43 | private void JobUpdated(object sender, EventArgs e) 44 | { 45 | IPackageInfo package = _job.Results.OrderByDescending(x => SemanticVersion.Parse(x.Version)).FirstOrDefault(); 46 | 47 | if (package != null) 48 | { 49 | ThreadHelper.JoinableTaskFactory.Run(async () => 50 | { 51 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 52 | if (!_firstRun && !IsVisible) 53 | { 54 | return; 55 | } 56 | 57 | _firstRun = false; 58 | if (!string.IsNullOrEmpty(package.IconUrl) && Uri.TryCreate(package.IconUrl, UriKind.Absolute, out Uri iconUri)) 59 | { 60 | try 61 | { 62 | if (!SourceLookup.TryGetValue(package.IconUrl, out ImageSource source)) 63 | { 64 | source = SourceLookup[package.IconUrl] = new BitmapImage(iconUri); 65 | } 66 | 67 | Glyph.Source = source; 68 | } 69 | catch 70 | { 71 | } 72 | } 73 | 74 | Description.Text = package.Description; 75 | }); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/ProjectFileTools/ProjectFileTools.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 15.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | Program 7 | $(DevEnvDir)\devenv.exe 8 | /rootsuffix Exp 9 | 10 | 11 | true 12 | 13 | 14 | 15 | 16 | Debug 17 | AnyCPU 18 | 2.0 19 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 20 | {4FE11F73-0836-4630-9B92-712D0482CFCB} 21 | Library 22 | Properties 23 | ProjectFileTools 24 | ProjectFileTools 25 | v4.6 26 | true 27 | false 28 | true 29 | false 30 | false 31 | true 32 | true 33 | 34 | 35 | true 36 | full 37 | false 38 | bin\Debug\ 39 | DEBUG;TRACE 40 | prompt 41 | 4 42 | 43 | 44 | pdbonly 45 | true 46 | bin\Release\ 47 | TRACE 48 | prompt 49 | 4 50 | 51 | 52 | 53 | 54 | 55 | 56 | Designer 57 | MSBuild:Compile 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | PackageInfoControl.xaml 87 | 88 | 89 | 90 | 91 | 92 | source.extension.vsixmanifest 93 | 94 | 95 | 96 | 97 | 98 | PackageInfoControl.xaml 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | Designer 107 | VsixManifestGenerator 108 | source.extension.resx 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | {C2C10E1F-A307-49FC-9D9A-106FCBE88B91} 129 | ProjectFileTools.MSBuild 130 | BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b 131 | DebugSymbolsProjectOutputGroup%3b 132 | 133 | 134 | {7129446a-7769-4753-9bf1-5e79ce1cb4c4} 135 | ProjectFileTools.NuGetSearch 136 | BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b 137 | DebugSymbolsProjectOutputGroup%3b 138 | 139 | 140 | CopyIfNewer 141 | true 142 | Microsoft.Language.Xml.dll 143 | false 144 | 145 | 146 | 147 | 148 | true 149 | 150 | 151 | source.extension.vsixmanifest 152 | 153 | 154 | 155 | 156 | True 157 | True 158 | source.extension.vsixmanifest 159 | true 160 | VSPackage 161 | 162 | 163 | 164 | 165 | MicrosoftSHA2 166 | MsSharedLib72 167 | 168 | 169 | VsixSHA2 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | true 178 | 179 | 180 | false 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /src/ProjectFileTools/ProjectFileToolsPackage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace ProjectFileTools 6 | { 7 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 8 | [InstalledProductRegistration("#110", "#112", Vsix.Version, IconResourceID = 400)] 9 | [Guid(PackageGuidString)] 10 | public sealed class ProjectFileToolsPackage : AsyncPackage 11 | { 12 | public const string PackageGuidString = "60347b36-f766-4480-8038-ff1b70212235"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ProjectFileTools": { 4 | "commandName": "Executable", 5 | "executablePath": "$(DevEnvDir)\\devenv.exe", 6 | "commandLineArgs": "/rootsuffix Exp" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/ProjectFileTools/QuickInfo/QuickInfoProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using System.Diagnostics; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.VisualStudio.Language.Intellisense; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Text; 9 | using Microsoft.VisualStudio.Utilities; 10 | using ProjectFileTools.Completion; 11 | using ProjectFileTools.Helpers; 12 | using ProjectFileTools.MSBuild; 13 | using ProjectFileTools.NuGetSearch.Contracts; 14 | 15 | namespace ProjectFileTools.QuickInfo 16 | { 17 | [Export(typeof(IAsyncQuickInfoSourceProvider))] 18 | [Name("Project file tools Quick Info Controller")] 19 | [ContentType("XML")] 20 | internal class QuickInfoProvider : IAsyncQuickInfoSourceProvider 21 | { 22 | private readonly IPackageSearchManager _searchManager; 23 | private readonly IWorkspaceManager _workspaceManager; 24 | 25 | [ImportingConstructor] 26 | public QuickInfoProvider(IWorkspaceManager workspaceManager, IPackageSearchManager searchManager) 27 | { 28 | _searchManager = searchManager; 29 | _workspaceManager = workspaceManager; 30 | } 31 | 32 | public IAsyncQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) 33 | { 34 | return new MsBuildPropertyQuickInfoSource(_workspaceManager, _searchManager); 35 | } 36 | } 37 | 38 | internal class MsBuildPropertyQuickInfoSource : IAsyncQuickInfoSource 39 | { 40 | private readonly IPackageSearchManager _searchManager; 41 | private readonly IWorkspaceManager _workspaceManager; 42 | 43 | public MsBuildPropertyQuickInfoSource(IWorkspaceManager workspaceManager, IPackageSearchManager searchManager) 44 | { 45 | _searchManager = searchManager; 46 | _workspaceManager = workspaceManager; 47 | } 48 | 49 | public void Dispose() 50 | { 51 | } 52 | 53 | public async Task GetQuickInfoItemAsync(IAsyncQuickInfoSession session, CancellationToken cancellationToken) 54 | { 55 | if (!session.TextView.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out ITextDocument textDoc)) 56 | { 57 | return null; 58 | } 59 | 60 | SnapshotPoint? triggerPoint = session.GetTriggerPoint(session.TextView.TextSnapshot); 61 | 62 | if (triggerPoint == null) 63 | { 64 | return null; 65 | } 66 | 67 | int pos = triggerPoint.Value.Position; 68 | 69 | if (!PackageCompletionSource.IsInRangeForPackageCompletion(session.TextView.TextSnapshot, pos, out Span s, out string packageId, out string packageVersion, out string type)) 70 | { 71 | XmlInfo info = XmlTools.GetXmlInfo(session.TextView.TextSnapshot, pos); 72 | 73 | if (info != null) 74 | { 75 | IWorkspace workspace = workspace = _workspaceManager.GetWorkspace(textDoc.FilePath); 76 | string evaluatedValue = workspace.GetEvaluatedPropertyValue(info.AttributeValue); 77 | ITrackingSpan target = session.TextView.TextSnapshot.CreateTrackingSpan(new Span(info.AttributeValueStart, info.AttributeValueLength), SpanTrackingMode.EdgeNegative); 78 | 79 | if (info.AttributeName == "Condition") 80 | { 81 | try 82 | { 83 | bool isTrue = workspace.EvaluateCondition(info.AttributeValue); 84 | evaluatedValue = $"Expanded value: {evaluatedValue}\nEvaluation result: {isTrue}"; 85 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 86 | return new QuickInfoItem(target, evaluatedValue); 87 | } 88 | catch (Exception ex) 89 | { 90 | Debug.Fail(ex.ToString()); 91 | } 92 | } 93 | else 94 | { 95 | evaluatedValue = $"Value(s):\n {string.Join("\n ", evaluatedValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))}"; 96 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 97 | return new QuickInfoItem(target, evaluatedValue); 98 | } 99 | } 100 | } 101 | else 102 | { 103 | string text = session.TextView.TextBuffer.CurrentSnapshot.GetText(); 104 | int targetFrameworkElementStartIndex = text.IndexOf("", StringComparison.OrdinalIgnoreCase); 105 | int targetFrameworksElementStartIndex = text.IndexOf("", StringComparison.OrdinalIgnoreCase); 106 | string tfm = "netcoreapp1.0"; 107 | 108 | if (targetFrameworksElementStartIndex > -1) 109 | { 110 | int closeTfms = text.IndexOf("", targetFrameworksElementStartIndex); 111 | int realStart = targetFrameworksElementStartIndex + "".Length; 112 | string allTfms = text.Substring(realStart, closeTfms - realStart); 113 | tfm = allTfms.Split(';')[0]; 114 | } 115 | else if (targetFrameworkElementStartIndex > -1) 116 | { 117 | int closeTfm = text.IndexOf("", targetFrameworkElementStartIndex); 118 | int realStart = targetFrameworkElementStartIndex + "".Length; 119 | tfm = text.Substring(realStart, closeTfm - realStart); 120 | } 121 | 122 | ITrackingSpan applicableToSpan = session.TextView.TextBuffer.CurrentSnapshot.CreateTrackingSpan(s, SpanTrackingMode.EdgeInclusive); 123 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 124 | return new QuickInfoItem(applicableToSpan, new PackageInfoControl(packageId, packageVersion, tfm, _searchManager)); 125 | } 126 | 127 | return null; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/ProjFileTools/04a8c6224339ea941fab56c71a9402bd667a36b4/src/ProjectFileTools/Resources/Icon.png -------------------------------------------------------------------------------- /src/ProjectFileTools/ServiceUtil.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | 5 | namespace ProjectFileTools 6 | { 7 | internal static class ServiceUtil 8 | { 9 | private static DTE _dte; 10 | 11 | public static DTE DTE => _dte ?? (_dte = GetService()); 12 | 13 | public static TService GetService() => GetService(); 14 | 15 | public static TInterface GetService() 16 | { 17 | ThreadHelper.ThrowIfNotOnUIThread(); 18 | return (TInterface)ServiceProvider.GlobalProvider.GetService(typeof(TService)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ProjectFileTools/TextViewCreationListener.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Editor; 3 | using Microsoft.VisualStudio.Language.Intellisense; 4 | using Microsoft.VisualStudio.OLE.Interop; 5 | using Microsoft.VisualStudio.Text.Editor; 6 | using Microsoft.VisualStudio.TextManager.Interop; 7 | using Microsoft.VisualStudio.Utilities; 8 | using ProjectFileTools.Completion; 9 | using ProjectFileTools.MSBuild; 10 | 11 | namespace ProjectFileTools 12 | { 13 | [Export(typeof(IVsTextViewCreationListener))] 14 | [Name("Xml Package Intellisense Controller")] 15 | [ContentType("XML")] 16 | [TextViewRole(PredefinedTextViewRoles.Editable)] 17 | internal sealed class TextViewCreationListener : IVsTextViewCreationListener 18 | { 19 | private readonly IVsEditorAdaptersFactoryService _adaptersFactory; 20 | private readonly ICompletionBroker _completionBroker; 21 | private readonly IWorkspaceManager _workspaceManager; 22 | 23 | //[Export(typeof(AdornmentLayerDefinition))] 24 | //[Name("TextAdornment")] 25 | //[Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)] 26 | //private AdornmentLayerDefinition editorAdornmentLayer; 27 | 28 | [ImportingConstructor] 29 | public TextViewCreationListener(ICompletionBroker completionBroker, IVsEditorAdaptersFactoryService adaptersFactory, IWorkspaceManager workspaceManager) 30 | { 31 | _completionBroker = completionBroker; 32 | _adaptersFactory = adaptersFactory; 33 | _workspaceManager = workspaceManager; 34 | } 35 | 36 | public void VsTextViewCreated(IVsTextView textViewAdapter) 37 | { 38 | IWpfTextView view = _adaptersFactory.GetWpfTextView(textViewAdapter); 39 | 40 | CompletionController completion = new CompletionController(view, _completionBroker); 41 | textViewAdapter.AddCommandFilter(completion, out IOleCommandTarget completionNext); 42 | completion.Next = completionNext; 43 | 44 | // Command Filter for GoToDefinition in .csproj files 45 | GotoDefinitionController gotoDefinition = GotoDefinitionController.CreateAndRegister(view, _workspaceManager, textViewAdapter); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ProjectFileTools/Theme.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace ProjectFileTools 7 | { 8 | public static class Theme 9 | { 10 | private static ResourceDictionary BuildThemeResources() 11 | { 12 | ResourceDictionary allResources = new ResourceDictionary(); 13 | ResourceDictionary shellResources = (ResourceDictionary) Application.LoadComponent(new Uri("Microsoft.VisualStudio.Platform.WindowManagement;component/Themes/ThemedDialogDefaultStyles.xaml", UriKind.Relative)); 14 | ResourceDictionary scrollStyleContainer = (ResourceDictionary) Application.LoadComponent(new Uri("Microsoft.VisualStudio.Shell.UI.Internal;component/Styles/ScrollBarStyle.xaml", UriKind.Relative)); 15 | allResources.MergedDictionaries.Add(shellResources); 16 | allResources.MergedDictionaries.Add(scrollStyleContainer); 17 | allResources[typeof (ScrollViewer)] = new Style 18 | { 19 | TargetType = typeof (ScrollViewer), 20 | BasedOn = (Style) scrollStyleContainer[VsResourceKeys.ScrollViewerStyleKey] 21 | }; 22 | return allResources; 23 | } 24 | 25 | private static ResourceDictionary ThemeResources { get; } = BuildThemeResources(); 26 | 27 | public static void ShouldBeThemed(this FrameworkElement control) 28 | { 29 | if (control.Resources == null) 30 | { 31 | control.Resources = ThemeResources; 32 | } 33 | else if(control.Resources != ThemeResources) 34 | { 35 | ResourceDictionary d = new ResourceDictionary(); 36 | d.MergedDictionaries.Add(ThemeResources); 37 | d.MergedDictionaries.Add(control.Resources); 38 | control.Resources = null; 39 | control.Resources = d; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ProjectFileTools/WpfUtil.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Windows.Media.Imaging; 3 | using Microsoft.VisualStudio.Imaging.Interop; 4 | using Microsoft.VisualStudio.PlatformUI; 5 | using Microsoft.VisualStudio.Shell; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | 8 | namespace ProjectFileTools 9 | { 10 | internal static class WpfUtil 11 | { 12 | public static BitmapSource MonikerToBitmap(ImageMoniker moniker, int size) 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | var shell = ServiceUtil.GetService(); 16 | var backgroundColor = shell.GetThemedColorRgba(EnvironmentColors.MainWindowButtonActiveBorderBrushKey); 17 | 18 | var imageAttributes = new ImageAttributes 19 | { 20 | Flags = (uint)_ImageAttributesFlags.IAF_RequiredFlags | unchecked((uint)_ImageAttributesFlags.IAF_Background), 21 | //Flags = (uint)_ImageAttributesFlags.IAF_RequiredFlags, 22 | ImageType = (uint)_UIImageType.IT_Bitmap, 23 | Format = (uint)_UIDataFormat.DF_WPF, 24 | Dpi = 96, 25 | LogicalHeight = size, 26 | LogicalWidth = size, 27 | Background = backgroundColor, 28 | StructSize = Marshal.SizeOf(typeof(ImageAttributes)) 29 | }; 30 | 31 | var service = ServiceUtil.GetService(); 32 | IVsUIObject result = service.GetImage(moniker, imageAttributes); 33 | result.get_Data(out object data); 34 | 35 | return data as BitmapSource; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/ProjectFileTools/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/ProjectFileTools/extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ProjectFileTools 5 | Empty VSIX Project. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/ProjectFileTools/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "frameworks": { 3 | ".NETFramework,Version=v4.6": {} 4 | }, 5 | "dependencies": { 6 | "EnvDTE": "8.0.2", 7 | "EnvDTE100": "10.0.3", 8 | "EnvDTE80": "8.0.3", 9 | "EnvDTE90": "9.0.3", 10 | "Microsoft.VisualStudio.ComponentModelHost": "15.7.27703", 11 | "Microsoft.VisualStudio.CoreUtility": "15.6.27740", 12 | "Microsoft.VisualStudio.Editor": "15.6.27740", 13 | "Microsoft.VisualStudio.ImageCatalog": "15.7.27703", 14 | "Microsoft.VisualStudio.Imaging": "15.7.27703", 15 | "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "15.0.25726-Preview5", 16 | "Microsoft.VisualStudio.Language.Intellisense": "15.6.27740", 17 | "Microsoft.VisualStudio.OLE.Interop": "7.10.6071", 18 | "Microsoft.VisualStudio.Shell.15.0": "15.7.27703", 19 | "Microsoft.VisualStudio.Shell.Framework": "15.7.27703", 20 | "Microsoft.VisualStudio.Shell.Interop": "7.10.6072", 21 | "Microsoft.VisualStudio.Shell.Interop.10.0": "10.0.30320", 22 | "Microsoft.VisualStudio.Shell.Interop.11.0": "11.0.61031", 23 | "Microsoft.VisualStudio.Shell.Interop.12.0": "12.0.30111", 24 | "Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime": "15.0.25726-Preview5", 25 | "Microsoft.VisualStudio.Shell.Interop.8.0": "8.0.50728", 26 | "Microsoft.VisualStudio.Shell.Interop.9.0": "9.0.30730", 27 | "Microsoft.VisualStudio.Text.Data": "15.6.27740", 28 | "Microsoft.VisualStudio.Text.Logic": "15.6.27740", 29 | "Microsoft.VisualStudio.Text.UI": "15.6.27740", 30 | "Microsoft.VisualStudio.Text.UI.Wpf": "15.6.27740", 31 | "Microsoft.VisualStudio.TextManager.Interop": "7.10.6071", 32 | "Microsoft.VisualStudio.TextManager.Interop.11.0": "11.0.61032", 33 | "Microsoft.VisualStudio.TextManager.Interop.8.0": "8.0.50728", 34 | "Microsoft.VisualStudio.Threading": "15.8.132", 35 | "Microsoft.VisualStudio.Utilities": "15.7.27703", 36 | "Microsoft.VisualStudio.Validation": "15.3.58", 37 | "Microsoft.VisualStudioEng.MicroBuild.Core": "0.4.1", 38 | "Microsoft.VSSDK.BuildTools": "15.7.109", 39 | "Microsoft.Win32.Primitives": "4.3.0", 40 | "Newtonsoft.Json": "11.0.2", 41 | "RoslynTools.SignTool": "1.1.0-beta3.21260.1", 42 | "stdole": "7.0.3303", 43 | "System.Diagnostics.DiagnosticSource": "4.5.0", 44 | "System.Net.Http": "4.3.4" 45 | }, 46 | "runtimes": { 47 | "win": {} 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ProjectFileTools/source.extension.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by Extensibility Tools v1.10.188 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace ProjectFileTools 7 | { 8 | static class Vsix 9 | { 10 | public const string Id = "e22f3d7e-6b01-4ef8-b3cb-ea293a87b00e"; 11 | public const string Name = "Project File Tools"; 12 | public const string Description = @"Provides Intellisense and other tooling for XML based project files such as .csproj and .vbproj files."; 13 | public const string Language = "en-US"; 14 | public const string Version = "1.0.1"; 15 | public const string Author = "Mike Lorbetske"; 16 | public const string Tags = "project, csproj, vbproj"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ProjectFileTools/source.extension.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/ProjFileTools/04a8c6224339ea941fab56c71a9402bd667a36b4/src/ProjectFileTools/source.extension.ico -------------------------------------------------------------------------------- /src/ProjectFileTools/source.extension.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Project File Tools 122 | 123 | 124 | Provides Intellisense and other tooling for XML based project files such as .csproj and .vbproj files. 125 | 126 | 127 | 128 | source.extension.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | -------------------------------------------------------------------------------- /src/ProjectFileTools/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Project File Tools 6 | Provides Intellisense and other tooling for XML based project files such as .csproj and .vbproj files. 7 | https://marketplace.visualstudio.com/items?itemName=ms-madsk.ProjectFileTools 8 | Resources\Icon.png 9 | Resources\Icon.png 10 | project, csproj, vbproj 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/ProjectFileTools.NuGetSearch.Tests/Mocks/MockFileSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using ProjectFileTools.NuGetSearch.IO; 5 | 6 | namespace PackageFeedManagerTests 7 | { 8 | 9 | public class MockFileSystem : IFileSystem 10 | { 11 | public bool DirectoryExists(string path) 12 | { 13 | throw new NotImplementedException(); 14 | } 15 | 16 | public IEnumerable EnumerateDirectories(string path, string pattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) 17 | { 18 | throw new NotImplementedException(); 19 | } 20 | 21 | public IEnumerable EnumerateFiles(string path, string pattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | 26 | public bool FileExists(string path) 27 | { 28 | throw new NotImplementedException(); 29 | } 30 | 31 | public string GetDirectoryName(string path) 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | 36 | public string GetDirectoryNameOnly(string path) 37 | { 38 | throw new NotImplementedException(); 39 | } 40 | 41 | public string ReadAllText(string path) 42 | { 43 | throw new NotImplementedException(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/ProjectFileTools.NuGetSearch.Tests/Mocks/MockWebRequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ProjectFileTools.NuGetSearch.IO; 5 | 6 | namespace PackageFeedManagerTests 7 | { 8 | public class MockWebRequestFactory : IWebRequestFactory 9 | { 10 | public Task GetStringAsync(string endpoint, CancellationToken cancellationToken) 11 | { 12 | throw new NotImplementedException(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/ProjectFileTools.NuGetSearch.Tests/NuGetV2ServiceFeedTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Moq; 3 | using ProjectFileTools.NuGetSearch.Feeds; 4 | using ProjectFileTools.NuGetSearch.Feeds.Web; 5 | using ProjectFileTools.NuGetSearch.IO; 6 | using System.IO; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | 11 | namespace ProjectFileTools.NuGetSearch.Tests 12 | { 13 | /// 14 | /// The tests below only work when signing is disabled. 15 | /// When signing enabled, no test will be found as a result of `ProjectFileTools.NuGetSearch` failing to load with signing key validation error 16 | /// 17 | public class NuGetV2ServiceFeedTests 18 | { 19 | 20 | public class TheDisplayNameProperty 21 | { 22 | 23 | [Theory] 24 | [InlineData("http://localhost/nuget")] 25 | public void GivenFeed_ReturnDisplayName(string feed) 26 | { 27 | var webRequestFactory = Mock.Of(); 28 | var sut = new NuGetV2ServiceFeed(feed, webRequestFactory); 29 | sut.DisplayName.Should().Be($"{feed} (NuGet v2)"); 30 | } 31 | } 32 | 33 | public class TheGetPackageNamesAsyncMethod 34 | { 35 | [Theory] 36 | [InlineData("http://localhost/nuget", "GetPackageNames.CommonLogging.xml")] 37 | public async Task GivenPackagesFound_ReturnListOfIds(string feed, string testFile) 38 | { 39 | var webRequestFactory = Mock.Of(); 40 | 41 | Mock.Get(webRequestFactory) 42 | .Setup(f => f.GetStringAsync("http://localhost/nuget/Search()?searchTerm='Common.Logging'&targetFramework=netcoreapp2.0&includePrerelease=False&semVerLevel=2.0.0", It.IsAny())) 43 | .Returns(Task.FromResult(GetXmlFromTestFile(testFile))); 44 | 45 | var sut = new NuGetV2ServiceFeed(feed, webRequestFactory); 46 | 47 | var packageNameResults = await sut.GetPackageNamesAsync("Common.Logging", new PackageQueryConfiguration("netcoreapp2.0", false), new CancellationToken()); 48 | packageNameResults.Names.Count.Should().Be(5); 49 | } 50 | } 51 | 52 | public class TheGetPackageVersionsAsyncMethod 53 | { 54 | [Theory] 55 | [InlineData("http://localhost/nuget", "GetPackageVersions.CommonLogging.xml")] 56 | public async Task GivenPackagesFound_ReturnListOfVersions(string feed, string testFile) 57 | { 58 | var webRequestFactory = Mock.Of(); 59 | 60 | Mock.Get(webRequestFactory) 61 | .Setup(f => f.GetStringAsync("http://localhost/nuget/FindPackagesById()?id='Acme.Common.Logging.AspNetCore'", It.IsAny())) 62 | .Returns(Task.FromResult(GetXmlFromTestFile(testFile))); 63 | 64 | var sut = new NuGetV2ServiceFeed(feed, webRequestFactory); 65 | 66 | var packageNameResults = await sut.GetPackageVersionsAsync("Acme.Common.Logging.AspNetCore", new PackageQueryConfiguration("netcoreapp2.0", false), new CancellationToken()); 67 | packageNameResults.Versions.Count.Should().Be(8); 68 | Assert.Collection(packageNameResults.Versions, 69 | v => v.Should().Be("1.6.0.5"), 70 | v => v.Should().Be("1.6.1"), 71 | v => v.Should().Be("1.6.2"), 72 | v => v.Should().Be("1.7.0"), 73 | v => v.Should().Be("1.7.1"), 74 | v => v.Should().Be("1.8.0"), 75 | v => v.Should().Be("1.9.0"), 76 | v => v.Should().Be("1.9.1")); 77 | } 78 | } 79 | 80 | public class TheGetPackageInfoAsyncMethod 81 | { 82 | [Theory] 83 | [InlineData("http://localhost/nuget", "GetPackageInfo.CommonLogging.xml")] 84 | public async Task GivenPackageFound_ReturnPackageInfo(string feed, string testFile) 85 | { 86 | var webRequestFactory = Mock.Of(); 87 | 88 | Mock.Get(webRequestFactory) 89 | .Setup(f => f.GetStringAsync("http://localhost/nuget/Packages(Id='Acme.Common.Logging.AspNetCore',Version='1.8.0')", It.IsAny())) 90 | .Returns(Task.FromResult(GetXmlFromTestFile(testFile))); 91 | 92 | var sut = new NuGetV2ServiceFeed(feed, webRequestFactory); 93 | 94 | var pkgInfo = await sut.GetPackageInfoAsync("Acme.Common.Logging.AspNetCore", "1.8.0", new PackageQueryConfiguration("netcoreapp2.0", false), new CancellationToken()); 95 | 96 | pkgInfo.Id.Should().Be("Acme.Common.Logging.AspNetCore"); 97 | pkgInfo.Title.Should().Be("Common Logging AspNetCore"); 98 | pkgInfo.Summary.Should().BeNullOrEmpty(); 99 | pkgInfo.Description.Should().Be("Common Logging integration within Aspnet core services"); 100 | pkgInfo.Authors.Should().Be("Patrick Assuied"); 101 | pkgInfo.Version.Should().Be("1.8.0"); 102 | pkgInfo.ProjectUrl.Should().Be("https://bitbucket.acme.com/projects/Acme/repos/Acme-common-logging"); 103 | pkgInfo.LicenseUrl.Should().BeNullOrEmpty(); 104 | pkgInfo.Tags.Should().Be(" common logging aspnetcore "); 105 | 106 | 107 | } 108 | } 109 | 110 | public static string GetXmlFromTestFile(string filename) 111 | { 112 | string path = Path.Combine(Directory.GetCurrentDirectory(), $"../../test/ProjectFileTools.NuGetSearch.Tests/TestFiles/{filename}"); 113 | return File.ReadAllText(path); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /test/ProjectFileTools.NuGetSearch.Tests/ProjectFileTools.NuGetSearch.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net46 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/ProjectFileTools.NuGetSearch.Tests/TestFiles/GetPackageInfo.CommonLogging.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | http://localhost/Nuget/nuget/Packages(Id='Acme.Common.Logging.AspNetCore',Version='1.8.0') 4 | 5 | 6 | 7 | Acme.Common.Logging.AspNetCore 8 | 2018-11-21T17:31:07Z 9 | 2018-11-21T17:31:07Z 10 | 11 | Patrick Assuied 12 | 13 | 14 | 15 | 16 | Acme.Common.Logging.AspNetCore 17 | 1.8.0 18 | 1.8.0 19 | false 20 | Common Logging AspNetCore 21 | Patrick Assuied 22 | Patrick Assuied 23 | 24 | 25 | https://bitbucket.acme.com/projects/Acme/repos/Acme-common-logging 26 | -1 27 | false 28 | false 29 | Common Logging integration within Aspnet core services 30 | 31 | ## Available in CHANGELOG.md 32 | 2018-11-21T17:31:07.1944763Z 33 | 2018-11-21T17:31:07.1944763Z 34 | Acme.Common.Logging.Configuration:1.8.0:netcoreapp2.0|Microsoft.AspNetCore.Diagnostics:2.0.3:netcoreapp2.0|NLog.Web.AspNetCore:4.5.4:netcoreapp2.0|SourceLink.Copy.PdbFiles:2.8.3:netcoreapp2.0|SourceLink.Create.CommandLine:2.8.3:netcoreapp2.0|Acme.Common.Logging.Configuration:1.8.0:netcoreapp2.1|Microsoft.AspNetCore.Diagnostics:2.1.1:netcoreapp2.1|NLog.Web.AspNetCore:4.5.4:netcoreapp2.1|SourceLink.Copy.PdbFiles:2.8.3:netcoreapp2.1|SourceLink.Create.CommandLine:2.8.3:netcoreapp2.1 35 | KP5xJayLLtWJx59wz6rSHW8tvxNY4xp/3eCsqG2QF7wINphpXs+0k2rKLigMWq/6geWptbdXXjJdZXYJUG0EUQ== 36 | SHA512 37 | 22002 38 | Copyright 2016-2018 39 | common logging aspnetcore 40 | false 41 | false 42 | true 43 | -1 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /test/ProjectFileTools.NuGetSearch.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | // using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using NuGet.Frameworks; 5 | using ProjectFileTools.NuGetSearch.Contracts; 6 | using ProjectFileTools.NuGetSearch.Feeds; 7 | using ProjectFileTools.NuGetSearch.Feeds.Disk; 8 | using ProjectFileTools.NuGetSearch.IO; 9 | using Xunit; 10 | 11 | namespace PackageFeedManagerTests 12 | { 13 | public class UnitTest1 14 | { 15 | [Fact(Skip="Just for testing locally")] 16 | public void TestMethod1() 17 | { 18 | //IFileSystem fileSystem = new MockFileSystem(); 19 | //IPackageFeedFactory diskFeed = new NuGetDiskFeedFactory(fileSystem); 20 | //PackageFeedFactorySelector factory = new PackageFeedFactorySelector(new[] { diskFeed }); 21 | //IPackageFeed feed = factory.GetFeed(@"C:\Users\mlorbe\.nuget"); 22 | //var config = new PackageQueryConfiguration(new NuGetFramework(".NETFramework", new Version(4, 5, 2, 0)).ToString(), includePreRelease: true); 23 | //var ids = feed.GetPackageNamesAsync("cli.ut", config, CancellationToken.None).Result; 24 | //var vers = feed.GetPackageVersionsAsync(ids.Names[0], config, CancellationToken.None).Result; 25 | } 26 | } 27 | } 28 | --------------------------------------------------------------------------------