├── .gitattributes ├── .github ├── CONTRIBUTING.md └── ISSUE_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── IgnoreFiles.sln ├── LICENSE ├── README.md ├── appveyor.yml ├── art ├── button-sync.png ├── context-menu.png ├── icons.png ├── lightbulb-match.png ├── lightbulb-non-match.png ├── non-match.png ├── settings.png ├── syntax-highlighting.png ├── tooltip-all-files.png ├── tooltip-search.png ├── tooltip.png └── validation.png └── src ├── Classify ├── IgnoreClassificationTypes.cs ├── IgnoreClassifier.cs └── IgnoreClassifierProvider.cs ├── Commands ├── RemoveNonMatchesCommand.cs └── __dummy__ ├── ContentType ├── FileIcons.imagemanifest ├── FileIcons │ ├── babel.16.16.png │ ├── babel.32.32.png │ ├── bazaar.16.16.png │ ├── bazaar.32.32.png │ ├── cf.16.16.png │ ├── cf.32.32.png │ ├── chef.16.16.png │ ├── chef.32.32.png │ ├── cvs.16.16.png │ ├── cvs.32.32.png │ ├── darcs.16.16.png │ ├── darcs.32.32.png │ ├── docker.16.16.png │ ├── docker.32.32.png │ ├── eslint.16.16.png │ ├── eslint.32.32.png │ ├── floobits.16.16.png │ ├── floobits.32.32.png │ ├── fossil.16.16.png │ ├── fossil.32.32.png │ ├── git.16.16.png │ ├── git.32.32.png │ ├── jetpack.16.16.png │ ├── jetpack.32.32.png │ ├── jshint.16.16.png │ ├── jshint.32.32.png │ ├── mercurial.16.16.png │ ├── mercurial.32.32.png │ ├── monotone.16.16.png │ ├── monotone.32.32.png │ ├── nodemon.16.16.png │ ├── nodemon.32.32.png │ ├── npm.16.16.png │ ├── npm.32.32.png │ ├── perforce.16.16.png │ ├── perforce.32.32.png │ ├── svn.16.16.png │ ├── svn.32.32.png │ ├── tf.16.16.png │ ├── tf.32.32.png │ ├── yarn.16.16.png │ └── yarn.32.32.png ├── IgnoreContentTypeDefinition.cs ├── IgnoreCreationListener.cs └── icons.pkgdef ├── Controls ├── IgnoreTree.xaml ├── IgnoreTree.xaml.cs ├── SearchHighlight.cs └── Shared.xaml ├── Converters ├── IconConverter.cs ├── ItemOpacityConverter.cs ├── ItemVisibilityConverter.cs └── PathMatchConverter.cs ├── DragDrop ├── IgnoreDropHandler.cs ├── IgnoreDropHandlerProvider.cs └── __dummy__ ├── Helpers ├── IEventsFilter.cs ├── Logger.cs └── ProjectHelpers.cs ├── IgnoreCommandTable.cs ├── IgnoreCommandTable.vsct ├── IgnoreFiles.csproj ├── IgnorePackage.cs ├── Models ├── ActionCommand.cs ├── BindableBase.cs ├── FileTree.cs ├── FileTreeModel.cs └── IgnoreTreeModel.cs ├── Options.cs ├── Properties └── AssemblyInfo.cs ├── QuickInfo ├── IgnoreQuickInfo.cs ├── IgnoreQuickInfoController.cs ├── IgnoreQuickInfoControllerProvider.cs └── IgnoreQuickInfoSourceProvider.cs ├── Resources ├── Icon.png └── Preview.png ├── SuggestedActions ├── Actions │ ├── BaseSuggestedAction.cs │ ├── DeleteMatchesSuggestedAction.cs │ ├── ExcludeMatchesFromProjectSuggestedAction.cs │ ├── RemoveAllNonMatchSuggestedAction.cs │ └── RemoveNonMatchSuggestedAction.cs ├── SuggestedActionsSource.cs └── SuggestedActionsSourceProvider.cs ├── Theme.cs ├── Validation ├── IgnoreErrorTagger.cs └── IgnoreErrorTaggerProvider.cs ├── WpfUtil.cs ├── packages.config ├── source.extension.cs ├── source.extension.ico ├── source.extension.resx └── source.extension.vsixmanifest /.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 | -------------------------------------------------------------------------------- /.github/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 | -------------------------------------------------------------------------------- /.github/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. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | packages 3 | 4 | # User files 5 | *.user 6 | .vs/ 7 | 8 | # Build results 9 | [Dd]ebug/ 10 | [Rr]elease/ 11 | [Bb]in/ 12 | [Oo]bj/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Road map 2 | 3 | - [ ] Intellisense for file paths 4 | - [ ] Item templates for most popular .ignore files 5 | - [ ] Glyph in margin to indicate folders 6 | 7 | Features that have a checkmark are complete and available for 8 | download in the 9 | [CI build](http://vsixgallery.com/extension/7ac24965-ea21-4108-9cac-6e46394aaaef/). 10 | 11 | # Change log 12 | 13 | These are the changes to each version that has been released 14 | on the official Visual Studio extension gallery. 15 | 16 | ## 1.2 17 | 18 | **2016-05-26** 19 | 20 | - [x] Support for paths starting with / or \ 21 | - [x] Persist "Sync selected files" in Tools -> Options 22 | 23 | ## 1.1 24 | 25 | **2016-05-17** 26 | 27 | - [x] Write result of removing non-matches to status bar 28 | - [x] Light bulbs support 29 | - [x] Remove non-matching entry 30 | - [x] Remove non-matching entry in document 31 | - [x] Delete files that match 32 | - [x] Remove matching files from project 33 | - [x] Full tree view in tooltip 34 | - [x] Add support for .babelignore and .svnignore 35 | - [x] Make files in tooltip clickable (selects file, opens file) 36 | - [x] Button to toggle the Solution Explorer selection sync 37 | 38 | ## 1.0 39 | 40 | **2016-05-13** 41 | 42 | - [x] Syntax highlighting 43 | - [x] File icons for Solution Explorer 44 | - [x] Support all known .ignore files 45 | - [x] Mark paths that doesn't hit any files 46 | - [x] Hover QuickInfo for file match details 47 | - [x] Support globbing/minimatch 48 | - [x] Command to remove all lines that matches nothing 49 | - [x] Package load on .ignore file open 50 | - [x] Options page 51 | - [x] Hover tooltip to show matched files 52 | - [x] Add error logging 53 | - [x] Drag 'n drop files onto .ignore file 54 | - [x] Support for Visual Studio "15" 55 | - [x] Fix issue where not all non-matches are removed -------------------------------------------------------------------------------- /IgnoreFiles.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IgnoreFiles", "src\IgnoreFiles.csproj", "{60419598-D990-4B43-9832-50BAE09271EC}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{62806505-053A-468E-A626-2A39196ED825}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitignore = .gitignore 11 | appveyor.yml = appveyor.yml 12 | CHANGELOG.md = CHANGELOG.md 13 | README.md = README.md 14 | EndProjectSection 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {60419598-D990-4B43-9832-50BAE09271EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {60419598-D990-4B43-9832-50BAE09271EC}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {60419598-D990-4B43-9832-50BAE09271EC}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {60419598-D990-4B43-9832-50BAE09271EC}.Release|Any CPU.Build.0 = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(SolutionProperties) = preSolution 28 | HideSolutionNode = FALSE 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Mads Kristensen 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .ignore 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/28ov3nlmta59snrw?svg=true)](https://ci.appveyor.com/project/madskristensen/ignorefiles) 4 | 5 | Download this extension from the [VS Gallery](https://visualstudiogallery.msdn.microsoft.com/d0eba56d-603b-45ab-a680-edfda585f7f3) 6 | or get the [CI build](http://vsixgallery.com/extension/7ac24965-ea21-4108-9cac-6e46394aaaef/). 7 | 8 | --------------------------------------- 9 | 10 | This extension supports: 11 | 12 | - `.babelignore` (Babel) 13 | - `.boringignore` (Darcs) 14 | - `.bzrignore` (Bazaar) 15 | - `.cfignore` (Cloud Foundry) 16 | - `.chefignore` (Chef) 17 | - `.cvsignore` (CVS) 18 | - `.dockerignore` (Docker) 19 | - `.eslintignore` (ESLint) 20 | - `.flooignore` (Floobits) 21 | - `.gitignore` (Git) 22 | - `.hgignore` (Mercurial) 23 | - `.jpmignore` (Jetpack) 24 | - `.jshintignore` (JSHint) 25 | - `.mtn-ignore` (Monotone) 26 | - `.npmignore` (npm) 27 | - `.p4ignore` (Perforce) 28 | - `.svnignore` (SVN) 29 | - `.tfignore` (Team Foundation) 30 | - `.vscodeignore` (VSCode) 31 | - `.yarnignore` (Yarn) 32 | 33 | See the [change log](CHANGELOG.md) for changes and road map. 34 | 35 | ## Features 36 | 37 | - Syntax highlighting 38 | - File icons in Solution Explorer 39 | - Non-matching file paths are grayed out 40 | - Hover tooltips show path details 41 | - Button to remove all non-matching paths 42 | - Drag 'n drop support for files and folders 43 | - Syntax validation 44 | - Light Bulbs for quick actions 45 | 46 | ### Syntax highlighting 47 | Syntax highlighting for all types of .ignore file. 48 | 49 | ![Syntax Highlighting](art/syntax-highlighting.png) 50 | 51 | ### File icons 52 | Correct file icons will be shown for all the .ignore files. 53 | 54 | ![Icons](art/icons.png) 55 | 56 | ### Tooltip 57 | Hovering over any path entry will give details about what 58 | files the path points to. 59 | 60 | ![Tooltip Short](art/tooltip.png) 61 | 62 | To filter the results directly in the tooltip, simply place 63 | the cursor in the bottom search field and start typing. 64 | 65 | ![Tooltip Search](art/tooltip-search.png) 66 | 67 | To synchronize the selected file in the tooltip with the 68 | selected file in Solution Explorer, enable the 69 | *Sync with Selected Document* button: 70 | 71 | ![Button Sync](art/button-sync.png) 72 | 73 | Sometimes it can be helpful to see the matched files in context 74 | of all the non-matched files. You can do that by toggling the 75 | *Show All Files* button: 76 | 77 | ![Tooltip All Files](art/tooltip-all-files.png) 78 | 79 | Double-clicking any file in the tooltip will open it up 80 | in Visual Studio. 81 | 82 | ### Non-matches 83 | If a file path doesn't point to a file or folder, then it 84 | is being grayed out. 85 | 86 | ![Non Match](art/non-match.png) 87 | 88 | ### Remove non-matches 89 | Right-click in any .ignore file and select 90 | *Remove non-matching entries*. 91 | 92 | ![Context menu](art/context-menu.png) 93 | 94 | This action can be undone by invoking the *Undo* command 95 | (ctrl-z). 96 | 97 | ### Drag 'n drop 98 | You can drag any file or folder from Solution Explorer or 99 | from the file system using Windows Explorer. This will add 100 | the correct relative path to the .ignore file. 101 | 102 | ### Validation 103 | Validation helps you avoid typos and other mistakes. For 104 | instance, it will catch the user of **../** which is not 105 | allowed in .ignore files. 106 | 107 | ![Validation](art/validation.png) 108 | 109 | ### Light Bulbs 110 | Light bulbs appear in the margin to give you quick access 111 | to perform helpful actions. 112 | 113 | #### For matches: 114 | ![Light bulb matches](art/lightbulb-match.png) 115 | 116 | **Delete matching file entries...** will delete the matching 117 | files and folders from disk. If any of the files are part 118 | of a project in the solution, the project will be updated 119 | to reflect the deletion of the files. 120 | 121 | **Exclude matching entries from project...** will not delete 122 | the files and folder from disk, it will only remove any 123 | reference to them from the project. 124 | 125 | #### For non-matches: 126 | ![Light bulb non-matches](art/lightbulb-non-match.png) 127 | 128 | **Remove non-matching entry** will delete the current 129 | line from the .ignore file. This action can be undone by 130 | invoking the *Undo* command (ctrl-z). 131 | 132 | **Remove all non-matching entry** will delete all non-matching 133 | entries in the .ignore file. It does the same as the right-click 134 | command does. This action can be undone by invoking the 135 | *Undo* command (ctrl-z). 136 | 137 | ## Settings 138 | Various settings are available in the **Tools -> Options** 139 | dialog. You can find them under **Text Editor/.ignore** 140 | in the list. 141 | 142 | ![Settings](art/settings.png) 143 | 144 | ## Contribute 145 | Check out the [contribution guidelines](.github/CONTRIBUTING.md) 146 | if you want to contribute to this project. 147 | 148 | For cloning and building this project yourself, make sure 149 | to install the 150 | [Extensibility Tools 2015](https://visualstudiogallery.msdn.microsoft.com/ab39a092-1343-46e2-b0f1-6a3f91155aa6) 151 | extension for Visual Studio which enables some features 152 | used by this project. 153 | 154 | ## License 155 | [Apache 2.0](LICENSE) 156 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | 3 | install: 4 | - ps: (new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iex 5 | 6 | before_build: 7 | - ps: Vsix-IncrementVsixVersion | Vsix-UpdateBuildVersion 8 | - ps: Vsix-TokenReplacement src\source.extension.cs 'Version = "([0-9\\.]+)"' 'Version = "{version}"' 9 | 10 | build_script: 11 | - nuget restore -Verbosity quiet 12 | - msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m 13 | 14 | after_test: 15 | - ps: Vsix-PushArtifacts | Vsix-PublishToGallery 16 | 17 | before_deploy: 18 | - ps: Vsix-CreateChocolatyPackage -packageId ignorefiles 19 | 20 | deploy: 21 | - provider: Environment 22 | name: Chocolatey 23 | on: 24 | branch: master 25 | appveyor_repo_commit_message_extended: /\[release\]/ -------------------------------------------------------------------------------- /art/button-sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/button-sync.png -------------------------------------------------------------------------------- /art/context-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/context-menu.png -------------------------------------------------------------------------------- /art/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/icons.png -------------------------------------------------------------------------------- /art/lightbulb-match.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/lightbulb-match.png -------------------------------------------------------------------------------- /art/lightbulb-non-match.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/lightbulb-non-match.png -------------------------------------------------------------------------------- /art/non-match.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/non-match.png -------------------------------------------------------------------------------- /art/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/settings.png -------------------------------------------------------------------------------- /art/syntax-highlighting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/syntax-highlighting.png -------------------------------------------------------------------------------- /art/tooltip-all-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/tooltip-all-files.png -------------------------------------------------------------------------------- /art/tooltip-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/tooltip-search.png -------------------------------------------------------------------------------- /art/tooltip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/tooltip.png -------------------------------------------------------------------------------- /art/validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/art/validation.png -------------------------------------------------------------------------------- /src/Classify/IgnoreClassificationTypes.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text.Classification; 4 | using Microsoft.VisualStudio.Utilities; 5 | 6 | namespace IgnoreFiles 7 | { 8 | public static class IgnoreClassificationTypes 9 | { 10 | public const string Keyword = "Ignore_keyword"; 11 | public const string Path = "Ignore_path"; 12 | public const string PathNoMatch = "Ignore_path_no_match"; 13 | 14 | [Export, Name(Keyword)] 15 | public static ClassificationTypeDefinition IgnoreClassificationBold { get; set; } 16 | 17 | [Export, Name(Path)] 18 | public static ClassificationTypeDefinition IgnoreClassificationPath { get; set; } 19 | 20 | [Export, Name(PathNoMatch)] 21 | public static ClassificationTypeDefinition IgnoreClassificationPathNoMatch { get; set; } 22 | } 23 | 24 | [Export(typeof(EditorFormatDefinition))] 25 | [ClassificationType(ClassificationTypeNames = IgnoreClassificationTypes.Keyword)] 26 | [Name(IgnoreClassificationTypes.Keyword)] 27 | [Order(After = Priority.Default)] 28 | [UserVisible(true)] 29 | internal sealed class IgnoreBoldFormatDefinition : ClassificationFormatDefinition 30 | { 31 | public IgnoreBoldFormatDefinition() 32 | { 33 | ForegroundBrush = Brushes.OrangeRed; 34 | IsBold = true; 35 | DisplayName = "Ignore Keyword"; 36 | } 37 | } 38 | 39 | [Export(typeof(EditorFormatDefinition))] 40 | [ClassificationType(ClassificationTypeNames = IgnoreClassificationTypes.Path)] 41 | [Name(IgnoreClassificationTypes.Path)] 42 | [Order(After = Priority.Default)] 43 | [UserVisible(true)] 44 | internal sealed class IgnorePathFormatDefinition : ClassificationFormatDefinition 45 | { 46 | public IgnorePathFormatDefinition() 47 | { 48 | ForegroundBrush = Brushes.SteelBlue; 49 | DisplayName = "Ignore Path"; 50 | } 51 | } 52 | 53 | [Export(typeof(EditorFormatDefinition))] 54 | [ClassificationType(ClassificationTypeNames = IgnoreClassificationTypes.PathNoMatch)] 55 | [Name(IgnoreClassificationTypes.PathNoMatch)] 56 | [Order(After = Priority.Default)] 57 | [UserVisible(true)] 58 | internal sealed class IgnorePathNoMatchFormatDefinition : ClassificationFormatDefinition 59 | { 60 | public IgnorePathNoMatchFormatDefinition() 61 | { 62 | ForegroundOpacity = 0.4; 63 | DisplayName = "Ignore Path No Match"; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Classify/IgnoreClassifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | using System.Timers; 9 | using Microsoft.VisualStudio.Language.StandardClassification; 10 | using Microsoft.VisualStudio.Text; 11 | using Microsoft.VisualStudio.Text.Classification; 12 | 13 | namespace IgnoreFiles 14 | { 15 | public class IgnoreClassifier : IClassifier 16 | { 17 | private IClassificationType _symbol, _comment, _path, _pathNoMatch; 18 | private static Regex _commentRegex = new Regex(@"(?^[^:#\r\n]+)", RegexOptions.Compiled); 20 | private static Regex _symbolRegex = new Regex(@"^(?syntax)(?::[^#:]+)", RegexOptions.Compiled); 21 | private ConcurrentDictionary _cache = new ConcurrentDictionary(); 22 | private Queue> _queue = new Queue>(); 23 | private string _root; 24 | private ITextBuffer _buffer; 25 | private bool _isResetting, _isAsynchronous = true; 26 | private Timer _timer; 27 | 28 | public IgnoreClassifier(IClassificationTypeRegistryService registry, ITextBuffer buffer, string fileName) 29 | { 30 | _buffer = buffer; 31 | _root = Path.GetDirectoryName(fileName); 32 | _comment = registry.GetClassificationType(PredefinedClassificationTypeNames.Comment); 33 | _path = registry.GetClassificationType(IgnoreClassificationTypes.Path); 34 | _pathNoMatch = registry.GetClassificationType(IgnoreClassificationTypes.PathNoMatch); 35 | _symbol = registry.GetClassificationType(IgnoreClassificationTypes.Keyword); 36 | 37 | _timer = new Timer(250); 38 | _timer.Elapsed += TimerElapsed; 39 | } 40 | 41 | public bool HasMatches(SnapshotSpan span) 42 | { 43 | try 44 | { 45 | _isAsynchronous = false; 46 | return GetClassificationSpans(span).Any(t => t.ClassificationType.IsOfType(IgnoreClassificationTypes.PathNoMatch)); 47 | } 48 | finally { _isAsynchronous = true; } 49 | } 50 | 51 | public IList GetClassificationSpans(SnapshotSpan span) 52 | { 53 | IList list = new List(); 54 | 55 | string text = span.GetText(); 56 | 57 | if (string.IsNullOrWhiteSpace(text)) 58 | return list; 59 | 60 | var comment = _commentRegex.Match(text); 61 | 62 | if (comment.Success) 63 | { 64 | var result = new SnapshotSpan(span.Snapshot, span.Start + comment.Index, comment.Length); 65 | list.Add(new ClassificationSpan(result, _comment)); 66 | 67 | // Whole line is a comment, so just return here 68 | if (comment.Index == 0) 69 | return list; 70 | } 71 | 72 | var symbolMatch = _symbolRegex.Match(text); 73 | if (symbolMatch.Success) 74 | { 75 | var keyword = GetSpan(span, symbolMatch.Groups["name"], _symbol); 76 | list.Add(keyword); 77 | 78 | // Whole line is a symbol, so just return here 79 | return list; 80 | } 81 | 82 | var pathMatch = _pathRegex.Match(text); 83 | 84 | if (!pathMatch.Success) 85 | return list; 86 | 87 | var pathType = GetPathClassificationType(pathMatch.Groups["path"].Value.Trim(), span); 88 | 89 | var path = GetSpan(span, pathMatch.Groups["path"], pathType); 90 | if (path != null) 91 | list.Add(path); 92 | 93 | return list; 94 | } 95 | 96 | public void Reset() 97 | { 98 | if (!_isResetting) 99 | { 100 | _isResetting = true; 101 | _cache.Clear(); 102 | _queue.Clear(); 103 | var span = new SnapshotSpan(_buffer.CurrentSnapshot, 0, _buffer.CurrentSnapshot.Length); 104 | OnClassificationChanged(span); 105 | _isResetting = false; 106 | } 107 | } 108 | 109 | private IClassificationType GetPathClassificationType(string pattern, SnapshotSpan span) 110 | { 111 | if (pattern.StartsWith("../")) 112 | return _pathNoMatch; 113 | 114 | if (!_cache.ContainsKey(pattern)) 115 | { 116 | if (_isAsynchronous) 117 | { 118 | _queue.Enqueue(Tuple.Create(pattern, span)); 119 | _timer.Start(); 120 | } 121 | else 122 | { 123 | ProcessPath(pattern, span); 124 | return GetPathClassificationType(pattern, span); 125 | } 126 | 127 | return _path; 128 | } 129 | 130 | return _cache[pattern] ? _path : _pathNoMatch; 131 | } 132 | 133 | private void TimerElapsed(object sender, ElapsedEventArgs e) 134 | { 135 | if (_queue.Count == 0) 136 | return; 137 | 138 | _timer.Stop(); 139 | 140 | Task.Run(() => 141 | { 142 | try 143 | { 144 | do 145 | { 146 | var t = _queue.Dequeue(); 147 | 148 | if (_buffer.CurrentSnapshot.Version == t.Item2.Snapshot.Version) 149 | ProcessPath(t.Item1, t.Item2); 150 | 151 | } while (_queue.Count > 0); 152 | } 153 | catch (Exception ex) 154 | { 155 | Logger.Log(ex); 156 | } 157 | }); 158 | } 159 | 160 | private void ProcessPath(string pattern, SnapshotSpan span) 161 | { 162 | bool hasFiles = IgnoreQuickInfo.GetFiles(_root, pattern).Any(); 163 | 164 | _cache[pattern] = hasFiles; 165 | 166 | if (!hasFiles) 167 | { 168 | OnClassificationChanged(span); 169 | } 170 | } 171 | 172 | private void OnClassificationChanged(SnapshotSpan span) 173 | { 174 | if (_buffer.CurrentSnapshot.Version == span.Snapshot.Version) 175 | ClassificationChanged?.Invoke(this, new ClassificationChangedEventArgs(span)); 176 | } 177 | 178 | private ClassificationSpan GetSpan(SnapshotSpan span, Group group, IClassificationType type) 179 | { 180 | if (group.Length > 0) 181 | { 182 | var result = new SnapshotSpan(span.Snapshot, span.Start + group.Index, group.Length); 183 | return new ClassificationSpan(result, type); 184 | } 185 | 186 | return null; 187 | } 188 | 189 | public event EventHandler ClassificationChanged; 190 | } 191 | } -------------------------------------------------------------------------------- /src/Classify/IgnoreClassifierProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Classification; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace IgnoreFiles 8 | { 9 | [Export(typeof(IClassifierProvider))] 10 | [ContentType(IgnoreContentTypeDefinition.IgnoreContentType)] 11 | [TextViewRole(PredefinedTextViewRoles.Document)] 12 | public class IgnoreClassifierProvider : IClassifierProvider 13 | { 14 | [Import] 15 | public IClassificationTypeRegistryService Registry { get; set; } 16 | 17 | [Import] 18 | ITextDocumentFactoryService TextDocumentFactoryService { get; set; } 19 | 20 | public IClassifier GetClassifier(ITextBuffer buffer) 21 | { 22 | ITextDocument document; 23 | 24 | if (TextDocumentFactoryService.TryGetTextDocument(buffer, out document)) 25 | { 26 | var classifier = buffer.Properties.GetOrCreateSingletonProperty(() => new IgnoreClassifier(Registry, buffer, document.FilePath)); 27 | 28 | document.FileActionOccurred += (s, e) => { 29 | if (e.FileActionType == FileActionTypes.ContentSavedToDisk) 30 | classifier.Reset(); 31 | }; 32 | 33 | return classifier; 34 | } 35 | 36 | return null; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Commands/RemoveNonMatchesCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | using System.Linq; 4 | using EnvDTE; 5 | using EnvDTE80; 6 | using Microsoft.VisualStudio.Shell; 7 | using Microsoft.VisualStudio.Text; 8 | 9 | namespace IgnoreFiles 10 | { 11 | internal sealed class RemoveNonMatchesCommand 12 | { 13 | private ITextBuffer _buffer; 14 | private DTE2 _dte; 15 | 16 | private RemoveNonMatchesCommand(OleMenuCommandService commandService, DTE2 dte) 17 | { 18 | _dte = dte; 19 | 20 | var cmdID = new CommandID(PackageGuids.guidPackageCmdSet, PackageIds.RemoveNonMatches); 21 | var command = new OleMenuCommand(Execute, cmdID); 22 | command.BeforeQueryStatus += BeforeQueryStatus; 23 | commandService.AddCommand(command); 24 | } 25 | 26 | public static RemoveNonMatchesCommand Instance 27 | { 28 | get; 29 | private set; 30 | } 31 | 32 | public static async System.Threading.Tasks.Task Initialize(AsyncPackage package) 33 | { 34 | var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 35 | var dte = await package.GetServiceAsync(typeof(DTE)) as DTE2; 36 | Instance = new RemoveNonMatchesCommand(commandService, dte); 37 | } 38 | 39 | private void BeforeQueryStatus(object sender, EventArgs e) 40 | { 41 | var button = (OleMenuCommand)sender; 42 | button.Enabled = button.Visible = false; 43 | 44 | _buffer = Helpers.GetCurentTextBuffer(); 45 | 46 | if (_buffer != null && _buffer.ContentType.IsOfType(IgnoreContentTypeDefinition.IgnoreContentType)) 47 | { 48 | button.Enabled = button.Visible = true; 49 | } 50 | } 51 | 52 | private void Execute(object sender, EventArgs e) 53 | { 54 | IgnoreClassifier classifier; 55 | 56 | if (!_buffer.Properties.TryGetProperty(typeof(IgnoreClassifier), out classifier)) 57 | return; 58 | 59 | int linesRemoved = 0; 60 | 61 | try 62 | { 63 | _dte.StatusBar.Text = "Analyzing file and removing non-matches..."; 64 | _dte.UndoContext.Open("Removed non-matches"); 65 | 66 | using (var edit = _buffer.CreateEdit()) 67 | { 68 | var lines = _buffer.CurrentSnapshot.Lines.Reverse(); 69 | 70 | foreach (var line in lines) 71 | { 72 | var span = new SnapshotSpan(line.Start, line.LengthIncludingLineBreak); 73 | 74 | if (classifier.HasMatches(span)) 75 | { 76 | edit.Delete(span.Span); 77 | linesRemoved += 1; 78 | } 79 | } 80 | 81 | edit.Apply(); 82 | } 83 | } 84 | catch (Exception ex) 85 | { 86 | Logger.Log(ex); 87 | } 88 | finally 89 | { 90 | _dte.StatusBar.Text = $"{linesRemoved} non-matching entries removed"; 91 | _dte.UndoContext.Close(); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Commands/__dummy__: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/Commands/__dummy__ -------------------------------------------------------------------------------- /src/ContentType/FileIcons.imagemanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 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 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /src/ContentType/FileIcons/babel.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/babel.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/babel.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/babel.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/bazaar.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/bazaar.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/bazaar.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/bazaar.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/cf.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/cf.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/cf.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/cf.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/chef.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/chef.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/chef.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/chef.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/cvs.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/cvs.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/cvs.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/cvs.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/darcs.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/darcs.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/darcs.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/darcs.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/docker.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/docker.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/docker.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/docker.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/eslint.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/eslint.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/eslint.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/eslint.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/floobits.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/floobits.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/floobits.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/floobits.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/fossil.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/fossil.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/fossil.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/fossil.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/git.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/git.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/git.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/git.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/jetpack.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/jetpack.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/jetpack.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/jetpack.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/jshint.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/jshint.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/jshint.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/jshint.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/mercurial.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/mercurial.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/mercurial.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/mercurial.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/monotone.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/monotone.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/monotone.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/monotone.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/nodemon.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/nodemon.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/nodemon.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/nodemon.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/npm.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/npm.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/npm.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/npm.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/perforce.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/perforce.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/perforce.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/perforce.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/svn.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/svn.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/svn.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/svn.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/tf.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/tf.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/tf.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/tf.32.32.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/yarn.16.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/yarn.16.16.png -------------------------------------------------------------------------------- /src/ContentType/FileIcons/yarn.32.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/ContentType/FileIcons/yarn.32.32.png -------------------------------------------------------------------------------- /src/ContentType/IgnoreContentTypeDefinition.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Utilities; 3 | 4 | namespace IgnoreFiles 5 | { 6 | public class IgnoreContentTypeDefinition 7 | { 8 | public const string IgnoreContentType = "Ignore"; 9 | 10 | [Export(typeof(ContentTypeDefinition))] 11 | [Name(IgnoreContentType)] 12 | [BaseDefinition("plaintext")] 13 | public ContentTypeDefinition IIgnoreContentType { get; set; } 14 | 15 | [Export(typeof(FileExtensionToContentTypeDefinition))] 16 | [ContentType(IgnoreContentType)] 17 | [FileExtension(".gitignore")] 18 | public FileExtensionToContentTypeDefinition GitFileExtension { get; set; } 19 | 20 | [Export(typeof(FileExtensionToContentTypeDefinition))] 21 | [ContentType(IgnoreContentType)] 22 | [FileExtension(".tfignore")] 23 | public FileExtensionToContentTypeDefinition TfFileExtension { get; set; } 24 | 25 | [Export(typeof(FileExtensionToContentTypeDefinition))] 26 | [ContentType(IgnoreContentType)] 27 | [FileExtension(".hgignore")] 28 | public FileExtensionToContentTypeDefinition HgFileExtension { get; set; } 29 | 30 | [Export(typeof(FileExtensionToContentTypeDefinition))] 31 | [ContentType(IgnoreContentType)] 32 | [FileExtension(".nodemonignore")] 33 | public FileExtensionToContentTypeDefinition NodemonFileExtension { get; set; } 34 | 35 | [Export(typeof(FileExtensionToContentTypeDefinition))] 36 | [ContentType(IgnoreContentType)] 37 | [FileExtension(".npmignore")] 38 | public FileExtensionToContentTypeDefinition NpmFileExtension { get; set; } 39 | 40 | [Export(typeof(FileExtensionToContentTypeDefinition))] 41 | [ContentType(IgnoreContentType)] 42 | [FileExtension(".dockerignore")] 43 | public FileExtensionToContentTypeDefinition DockerFileExtension { get; set; } 44 | 45 | [Export(typeof(FileExtensionToContentTypeDefinition))] 46 | [ContentType(IgnoreContentType)] 47 | [FileExtension(".chefignore")] 48 | public FileExtensionToContentTypeDefinition ChefFileExtension { get; set; } 49 | 50 | [Export(typeof(FileExtensionToContentTypeDefinition))] 51 | [ContentType(IgnoreContentType)] 52 | [FileExtension(".cvsignore")] 53 | public FileExtensionToContentTypeDefinition CvsFileExtension { get; set; } 54 | 55 | [Export(typeof(FileExtensionToContentTypeDefinition))] 56 | [ContentType(IgnoreContentType)] 57 | [FileExtension(".boringignore")] 58 | public FileExtensionToContentTypeDefinition DarcsFileExtension { get; set; } 59 | 60 | [Export(typeof(FileExtensionToContentTypeDefinition))] 61 | [ContentType(IgnoreContentType)] 62 | [FileExtension(".mtn-ignore")] 63 | public FileExtensionToContentTypeDefinition MonotoneFileExtension { get; set; } 64 | 65 | [Export(typeof(FileExtensionToContentTypeDefinition))] 66 | [ContentType(IgnoreContentType)] 67 | [FileExtension(".p4ignore")] 68 | public FileExtensionToContentTypeDefinition PerforceFileExtension { get; set; } 69 | 70 | [Export(typeof(FileExtensionToContentTypeDefinition))] 71 | [ContentType(IgnoreContentType)] 72 | [FileExtension(".flooignore")] 73 | public FileExtensionToContentTypeDefinition FloobitsFileExtension { get; set; } 74 | 75 | [Export(typeof(FileExtensionToContentTypeDefinition))] 76 | [ContentType(IgnoreContentType)] 77 | [FileExtension(".jpmignore")] 78 | public FileExtensionToContentTypeDefinition JetpackFileExtension { get; set; } 79 | 80 | [Export(typeof(FileExtensionToContentTypeDefinition))] 81 | [ContentType(IgnoreContentType)] 82 | [FileExtension(".bzrignore")] 83 | public FileExtensionToContentTypeDefinition BazaarFileExtension { get; set; } 84 | 85 | [Export(typeof(FileExtensionToContentTypeDefinition))] 86 | [ContentType(IgnoreContentType)] 87 | [FileExtension(".jshintignore")] 88 | public FileExtensionToContentTypeDefinition JshintFileExtension { get; set; } 89 | 90 | [Export(typeof(FileExtensionToContentTypeDefinition))] 91 | [ContentType(IgnoreContentType)] 92 | [FileExtension(".eslintignore")] 93 | public FileExtensionToContentTypeDefinition EslintFileExtension { get; set; } 94 | 95 | [Export(typeof(FileExtensionToContentTypeDefinition))] 96 | [ContentType(IgnoreContentType)] 97 | [FileExtension(".cfignore")] 98 | public FileExtensionToContentTypeDefinition CloudFoundryFileExtension { get; set; } 99 | 100 | [Export(typeof(FileExtensionToContentTypeDefinition))] 101 | [ContentType(IgnoreContentType)] 102 | [FileExtension(".svnignore")] 103 | public FileExtensionToContentTypeDefinition SvnFileExtension { get; set; } 104 | 105 | [Export(typeof(FileExtensionToContentTypeDefinition))] 106 | [ContentType(IgnoreContentType)] 107 | [FileExtension(".vscodeignore")] 108 | public FileExtensionToContentTypeDefinition VsCodeFileExtension { get; set; } 109 | 110 | [Export(typeof(FileExtensionToContentTypeDefinition))] 111 | [ContentType(IgnoreContentType)] 112 | [FileExtension(".yarnignore")] 113 | public FileExtensionToContentTypeDefinition YarnFileExtension { get; set; } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/ContentType/IgnoreCreationListener.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Editor; 3 | using Microsoft.VisualStudio.Shell; 4 | using Microsoft.VisualStudio.Shell.Interop; 5 | using Microsoft.VisualStudio.Text.Editor; 6 | using Microsoft.VisualStudio.TextManager.Interop; 7 | using Microsoft.VisualStudio.Utilities; 8 | 9 | namespace IgnoreFiles 10 | { 11 | [Export(typeof(IVsTextViewCreationListener))] 12 | [ContentType(IgnoreContentTypeDefinition.IgnoreContentType)] 13 | [TextViewRole(PredefinedTextViewRoles.PrimaryDocument)] 14 | class IgnoreCreationListener : IVsTextViewCreationListener 15 | { 16 | public void VsTextViewCreated(IVsTextView textViewAdapter) 17 | { 18 | if (!IgnorePackage.IsInitialized) 19 | { 20 | var shell = (IVsShell)Package.GetGlobalService(typeof(SVsShell)); 21 | IVsPackage package; 22 | shell.LoadPackage(ref PackageGuids.guidPackage, out package); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ContentType/icons.pkgdef: -------------------------------------------------------------------------------- 1 | 2 | // Bazaar 3 | [$RootKey$\ShellFileAssociations\.bzrignore] 4 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:0" 5 | 6 | // Cloud foundry 7 | [$RootKey$\ShellFileAssociations\.cfignore] 8 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:1" 9 | 10 | // Chef 11 | [$RootKey$\ShellFileAssociations\.chefignore] 12 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:2" 13 | 14 | // CVS 15 | [$RootKey$\ShellFileAssociations\.cvsignore] 16 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:3" 17 | 18 | // Darcs 19 | [$RootKey$\ShellFileAssociations\.boringignore] 20 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:4" 21 | 22 | // Docker 23 | [$RootKey$\ShellFileAssociations\.dockerignore] 24 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:6" 25 | 26 | // ESLint 27 | [$RootKey$\ShellFileAssociations\.eslintignore] 28 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:7" 29 | 30 | // Floobits 31 | [$RootKey$\ShellFileAssociations\.flooignore] 32 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:8" 33 | 34 | // GIT 35 | [$RootKey$\ShellFileAssociations\.gitignore] 36 | "DefaultIconMoniker"="KnownMonikers.Git" 37 | 38 | // Jetpack 39 | [$RootKey$\ShellFileAssociations\.jpmignore] 40 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:11" 41 | 42 | // JSHint 43 | [$RootKey$\ShellFileAssociations\.jshintignore] 44 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:12" 45 | 46 | // Mercurial 47 | [$RootKey$\ShellFileAssociations\.hgignore] 48 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:13" 49 | 50 | // Monotone 51 | [$RootKey$\ShellFileAssociations\.mtn-ignore] 52 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:14" 53 | 54 | // nodemon 55 | [$RootKey$\ShellFileAssociations\.nodemonignore] 56 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:15" 57 | 58 | // npm 59 | [$RootKey$\ShellFileAssociations\.npmignore] 60 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:16" 61 | 62 | // Perforce 63 | [$RootKey$\ShellFileAssociations\.p4ignore] 64 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:17" 65 | 66 | // Team Foundation 67 | [$RootKey$\ShellFileAssociations\.tfignore] 68 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:18" 69 | 70 | // SVN 71 | [$RootKey$\ShellFileAssociations\.svnignore] 72 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:19" 73 | 74 | // Babel 75 | [$RootKey$\ShellFileAssociations\.babelignore] 76 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:20" 77 | 78 | // VS Code 79 | [$RootKey$\ShellFileAssociations\.vscodeignore] 80 | "DefaultIconMoniker"="KnownMonikers.VisualStudioExpressWindows" 81 | 82 | // Yarn 83 | [$RootKey$\ShellFileAssociations\.yarnignore] 84 | "DefaultIconMoniker"="d9bd2728-008e-4865-9387-67da8074c9fc:21" -------------------------------------------------------------------------------- /src/Controls/IgnoreTree.xaml: -------------------------------------------------------------------------------- 1 |  16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 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 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 111 | 112 | -------------------------------------------------------------------------------- /src/Controls/IgnoreTree.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Input; 6 | using EnvDTE; 7 | using IgnoreFiles.Models; 8 | using Microsoft.VisualStudio.Language.Intellisense; 9 | 10 | namespace IgnoreFiles.Controls 11 | { 12 | public partial class IgnoreTree : IInteractiveQuickInfoContent 13 | { 14 | private readonly Action _closeAction; 15 | 16 | public IgnoreTree() 17 | { 18 | InitializeComponent(); 19 | this.ShouldBeThemed(); 20 | } 21 | 22 | public IgnoreTree(string directory, string pattern, Action closeAction) 23 | : this() 24 | { 25 | _closeAction = closeAction; 26 | ViewModel = new IgnoreTreeModel(directory, pattern); 27 | CloseCommand = ActionCommand.Create(_closeAction); 28 | ToggleShowAllFilesCommand = ActionCommand.Create(() => ViewModel.ShowAllFiles = !ViewModel.ShowAllFiles); 29 | ToggleSyncCommand = ActionCommand.Create(() => ViewModel.SyncToSolutionExplorer = !ViewModel.SyncToSolutionExplorer); 30 | } 31 | 32 | public IgnoreTreeModel ViewModel 33 | { 34 | get { return Dispatcher.Invoke(() => DataContext as IgnoreTreeModel); } 35 | set { Dispatcher.Invoke(() => DataContext = value); } 36 | } 37 | 38 | public bool KeepQuickInfoOpen => IsMouseOverAggregated || IsKeyboardFocusWithin || IsKeyboardFocused || IsFocused; 39 | 40 | public bool IsMouseOverAggregated => IsMouseOver || IsMouseDirectlyOver; 41 | 42 | private void SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) 43 | { 44 | if (!ViewModel.SyncToSolutionExplorer) 45 | { 46 | return; 47 | } 48 | 49 | FileTreeModel selected = e.NewValue as FileTreeModel; 50 | 51 | if (selected != null) 52 | { 53 | UIHierarchy solutionExplorer = (UIHierarchy) IgnorePackage.DTE.Windows.Item(Constants.vsext_wk_SProjectWindow).Object; 54 | UIHierarchyItem rootNode = solutionExplorer.UIHierarchyItems.Item(1); 55 | 56 | Stack> parents = new Stack>(); 57 | ProjectItem targetItem = IgnorePackage.DTE.Solution.FindProjectItem(selected.FullPath); 58 | 59 | if (targetItem == null) 60 | { 61 | return; 62 | } 63 | 64 | UIHierarchyItems collection = rootNode.UIHierarchyItems; 65 | int cursor = 1; 66 | bool oldExpand = collection.Expanded; 67 | 68 | while (cursor <= collection.Count || parents.Count > 0) 69 | { 70 | while (cursor > collection.Count && parents.Count > 0) 71 | { 72 | collection.Expanded = oldExpand; 73 | Tuple parent = parents.Pop(); 74 | collection = parent.Item1; 75 | cursor = parent.Item2; 76 | oldExpand = parent.Item3; 77 | } 78 | 79 | if (cursor > collection.Count) 80 | { 81 | break; 82 | } 83 | 84 | UIHierarchyItem result = collection.Item(cursor); 85 | ProjectItem item = result.Object as ProjectItem; 86 | 87 | if (item == targetItem) 88 | { 89 | result.Select(vsUISelectionType.vsUISelectionTypeSelect); 90 | return; 91 | } 92 | 93 | ++cursor; 94 | 95 | bool oldOldExpand = oldExpand; 96 | oldExpand = result.UIHierarchyItems.Expanded; 97 | result.UIHierarchyItems.Expanded = true; 98 | if (result.UIHierarchyItems.Count > 0) 99 | { 100 | parents.Push(Tuple.Create(collection, cursor, oldOldExpand)); 101 | collection = result.UIHierarchyItems; 102 | cursor = 1; 103 | } 104 | } 105 | } 106 | } 107 | 108 | private void ItemDoubleClicked(object sender, MouseButtonEventArgs e) 109 | { 110 | TreeViewItem item = sender as TreeViewItem; 111 | FileTreeModel model = item?.DataContext as FileTreeModel; 112 | model?.ItemDoubleClicked(sender, e); 113 | _closeAction?.Invoke(); 114 | } 115 | 116 | protected override void OnKeyDown(KeyEventArgs e) 117 | { 118 | if (e.Key == Key.Escape) 119 | { 120 | _closeAction?.Invoke(); 121 | e.Handled = true; 122 | } 123 | } 124 | 125 | public ICommand CloseCommand { get; } 126 | 127 | public ICommand ToggleShowAllFilesCommand { get; } 128 | 129 | public ICommand ToggleSyncCommand { get; } 130 | 131 | private void OnKeyDown(object sender, KeyEventArgs e) 132 | { 133 | OnKeyDown(e); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Controls/SearchHighlight.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Data; 7 | using System.Windows.Documents; 8 | 9 | namespace IgnoreFiles.Controls 10 | { 11 | public static class SearchHighlight 12 | { 13 | public static readonly DependencyProperty SourceTextProperty = DependencyProperty.RegisterAttached( 14 | "SourceText", typeof(string), typeof(SearchHighlight), new PropertyMetadata("", TextChanged)); 15 | 16 | 17 | public static readonly DependencyProperty HighlightStyleProperty = DependencyProperty.RegisterAttached( 18 | "HighlightStyle", typeof(Style), typeof(SearchHighlight), new PropertyMetadata(default(Style), StyleChanged)); 19 | 20 | private static void StyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 21 | { 22 | Label lbl = d as Label; 23 | TextBlock block = d as TextBlock; 24 | 25 | if (lbl != null) 26 | { 27 | block = lbl.Content as TextBlock; 28 | 29 | if (block == null) 30 | { 31 | string lblChild = lbl.Content as string; 32 | 33 | if (lblChild == null) 34 | { 35 | return; 36 | } 37 | 38 | TextBlock newChild = new TextBlock { Text = lblChild }; 39 | lbl.Content = newChild; 40 | block = newChild; 41 | } 42 | } 43 | 44 | if (block == null) 45 | { 46 | return; 47 | } 48 | } 49 | 50 | public static void SetSourceText(DependencyObject element, string value) 51 | { 52 | element.SetValue(SourceTextProperty, value); 53 | } 54 | 55 | public static string GetSourceText(DependencyObject element) 56 | { 57 | return (string)element.GetValue(SourceTextProperty); 58 | } 59 | 60 | public static void SetHighlightStyle(DependencyObject element, Style value) 61 | { 62 | element.SetValue(HighlightStyleProperty, value); 63 | } 64 | 65 | public static Style GetHighlightStyle(DependencyObject element) 66 | { 67 | return (Style)element.GetValue(HighlightStyleProperty); 68 | } 69 | 70 | public static readonly DependencyProperty HighlightTextProperty = DependencyProperty.RegisterAttached( 71 | "HighlightText", typeof(string), typeof(SearchHighlight), new PropertyMetadata(default(string), TextChanged)); 72 | 73 | private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 74 | { 75 | Label lbl = d as Label; 76 | TextBlock block = d as TextBlock; 77 | 78 | if (lbl != null) 79 | { 80 | block = lbl.Content as TextBlock; 81 | 82 | if (block == null) 83 | { 84 | string lblChild = lbl.Content as string; 85 | TextBlock newChild = new TextBlock { Text = lblChild ?? "" }; 86 | lbl.Content = newChild; 87 | block = newChild; 88 | } 89 | } 90 | 91 | if (block == null) 92 | { 93 | return; 94 | } 95 | 96 | string searchText = GetHighlightText(d); 97 | string blockText = GetSourceText(d); 98 | 99 | if (blockText == null) 100 | { 101 | return; 102 | } 103 | 104 | int last = 0; 105 | block.Inlines.Clear(); 106 | 107 | if (!string.IsNullOrEmpty(searchText)) 108 | { 109 | IReadOnlyList matches = GetMatchesInText(searchText, blockText); 110 | 111 | for (int i = 0; i < matches.Count; ++i) 112 | { 113 | if (matches[i].Length == 0) 114 | { 115 | continue; 116 | } 117 | 118 | if (last < matches[i].Start) 119 | { 120 | string inserted = blockText.Substring(last, matches[i].Start - last); 121 | block.Inlines.Add(inserted); 122 | last += inserted.Length; 123 | } 124 | 125 | Run highlight = new Run(matches[i].ToString()); 126 | highlight.SetBinding(FrameworkContentElement.StyleProperty, new Binding 127 | { 128 | Mode = BindingMode.OneWay, 129 | Source = d, 130 | Path = new PropertyPath(HighlightStyleProperty) 131 | }); 132 | block.Inlines.Add(highlight); 133 | last += matches[i].Length; 134 | } 135 | } 136 | 137 | if (last < blockText.Length) 138 | { 139 | block.Inlines.Add(blockText.Substring(last)); 140 | } 141 | } 142 | 143 | public static void SetHighlightText(DependencyObject element, string value) 144 | { 145 | element.SetValue(HighlightTextProperty, value); 146 | } 147 | 148 | public static string GetHighlightText(DependencyObject element) 149 | { 150 | return (string)element.GetValue(HighlightTextProperty); 151 | } 152 | 153 | private static IReadOnlyList GetMatchesInText(string searchTerm, string text) 154 | { 155 | List ranges = new List(); 156 | 157 | string pattern = Regex.Escape(searchTerm); 158 | Regex r = new Regex(pattern, RegexOptions.IgnoreCase); 159 | MatchCollection matches = r.Matches(text); 160 | ProcessMatchCollection(text, matches, ranges); 161 | return ranges; 162 | } 163 | 164 | private static void ProcessMatchCollection(string sourceString, MatchCollection matches, List ranges) 165 | { 166 | foreach (Match match in matches) 167 | { 168 | Range range = new Range(match.Index, match.Length, sourceString); 169 | if (ranges.Count == 0) 170 | { 171 | ranges.Add(range); 172 | } 173 | else 174 | { 175 | bool included = false; 176 | int sortPosition = 0; 177 | for (int i = 0; i < ranges.Count; ++i) 178 | { 179 | if (ranges[i].Start < range.Start) 180 | { 181 | sortPosition = i + 1; 182 | } 183 | 184 | Range tmp; 185 | if (range.TryUnion(ranges[i], out tmp)) 186 | { 187 | included = true; 188 | ranges[i] = tmp; 189 | 190 | if (i < ranges.Count - 1 && tmp.TryUnion(ranges[i + 1], out range)) 191 | { 192 | ranges[i] = range; 193 | ranges.RemoveAt(i + 1); 194 | break; 195 | } 196 | } 197 | } 198 | 199 | if (!included) 200 | { 201 | if (sortPosition == ranges.Count) 202 | { 203 | ranges.Add(range); 204 | } 205 | else 206 | { 207 | ranges.Insert(sortPosition, range); 208 | } 209 | } 210 | } 211 | } 212 | } 213 | 214 | public struct Range 215 | { 216 | public Range(int start, int length, string sourceString) 217 | { 218 | Start = start; 219 | Length = length; 220 | SourceString = sourceString; 221 | } 222 | 223 | public readonly int Length; 224 | 225 | public readonly int Start; 226 | 227 | public readonly string SourceString; 228 | 229 | public bool TryUnion(Range other, out Range composite) 230 | { 231 | if (!string.Equals(other.SourceString, SourceString, StringComparison.Ordinal)) 232 | { 233 | composite = default(Range); 234 | return false; 235 | } 236 | 237 | bool isLow = Start <= other.Start; 238 | Range low = isLow ? this : other; 239 | Range high = isLow ? other : this; 240 | 241 | int lowHigh = low.Start + low.Length; 242 | 243 | if (high.Start > lowHigh) 244 | { 245 | composite = default(Range); 246 | return false; 247 | } 248 | 249 | int highHigh = high.Start + high.Length; 250 | 251 | if (highHigh <= lowHigh) 252 | { 253 | composite = low; 254 | return true; 255 | } 256 | 257 | composite = new Range(low.Start, highHigh - low.Start, SourceString); 258 | return true; 259 | } 260 | 261 | public override string ToString() 262 | { 263 | return SourceString.Substring(Start, Length); 264 | } 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/Controls/Shared.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 14 | 25 | 26 | 27 | 28 | 60 | 61 | -------------------------------------------------------------------------------- /src/Converters/IconConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using System.Windows.Media; 6 | using IgnoreFiles.Models; 7 | using Microsoft.VisualStudio.Imaging; 8 | using Microsoft.VisualStudio.Imaging.Interop; 9 | 10 | namespace IgnoreFiles.Converters 11 | { 12 | public class IconConverter : IMultiValueConverter 13 | { 14 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (values == null || values.Length != 4 || !(values[0] is FileTreeModel) || !(values[1] is double) || !(values[2] is double) || !(values[3] is DependencyObject)) 17 | { 18 | return null; 19 | } 20 | 21 | int x = (int)(double)values[1]; 22 | int y = (int)(double)values[2]; 23 | 24 | if (x < 1) 25 | { 26 | x = 1; 27 | } 28 | 29 | if (y < 1) 30 | { 31 | y = 1; 32 | } 33 | 34 | FileTreeModel model = (FileTreeModel)values[0]; 35 | if (!model.IsFile) 36 | { 37 | bool isExpanded = model.IsExpanded; 38 | ImageMoniker moniker = isExpanded ? KnownMonikers.FolderOpened : KnownMonikers.FolderClosed; 39 | return WpfUtil.ThemeImage((DependencyObject)values[3], WpfUtil.GetIconForImageMoniker(moniker, x, y)); 40 | } 41 | 42 | string name = model.Name; 43 | bool isThemeIcon; 44 | ImageSource source = WpfUtil.GetIconForFile((DependencyObject)values[3], name, out isThemeIcon); 45 | return source; 46 | } 47 | 48 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 49 | { 50 | throw new NotSupportedException(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Converters/ItemOpacityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | using IgnoreFiles.Models; 5 | 6 | namespace IgnoreFiles.Converters 7 | { 8 | public class ItemOpacityConverter : IMultiValueConverter 9 | { 10 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | FileTreeModel model = values[0] as FileTreeModel; 13 | Func predicate = values[1] as Func; 14 | bool result = predicate?.Invoke(model) ?? true; 15 | 16 | for (int i = 2; result && i < values.Length; ++i) 17 | { 18 | predicate = values[i] as Func; 19 | 20 | if (predicate != null) 21 | { 22 | result &= !predicate(model); 23 | } 24 | else if (values[i] is bool) 25 | { 26 | result &= !(bool) values[i]; 27 | } 28 | else 29 | { 30 | break; 31 | } 32 | } 33 | 34 | return result ? System.Convert.ToDouble(parameter) : 1; 35 | } 36 | 37 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 38 | { 39 | throw new NotImplementedException(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Converters/ItemVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using IgnoreFiles.Models; 6 | 7 | namespace IgnoreFiles.Converters 8 | { 9 | public class ItemVisibilityConverter : IMultiValueConverter 10 | { 11 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | FileTreeModel model = values[0] as FileTreeModel; 14 | Func predicate = values[1] as Func; 15 | bool result = predicate?.Invoke(model) ?? true; 16 | 17 | for (int i = 2; !result && i < values.Length; ++i) 18 | { 19 | predicate = values[i] as Func; 20 | 21 | if (predicate != null) 22 | { 23 | result |= predicate(model); 24 | } 25 | else if (values[i] is bool) 26 | { 27 | result |= (bool) values[i]; 28 | } 29 | else 30 | { 31 | break; 32 | } 33 | } 34 | 35 | return result ? Visibility.Visible : Visibility.Collapsed; 36 | } 37 | 38 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 39 | { 40 | throw new NotImplementedException(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Converters/PathMatchConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | using IgnoreFiles.Models; 5 | 6 | namespace IgnoreFiles.Converters 7 | { 8 | public class PathMatchConverter : IMultiValueConverter 9 | { 10 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | FileTreeModel model = values[0] as FileTreeModel; 13 | Func predicate = values[1] as Func; 14 | return predicate?.Invoke(model) ?? true; 15 | } 16 | 17 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/DragDrop/IgnoreDropHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows; 4 | using Microsoft.VisualStudio.Shell; 5 | using Microsoft.VisualStudio.Text.Editor; 6 | using Microsoft.VisualStudio.Text.Editor.DragDrop; 7 | 8 | namespace IgnoreFiles 9 | { 10 | internal class IgnoreDropHandler : IDropHandler 11 | { 12 | private IWpfTextView _view; 13 | private string _draggedFileName; 14 | private string _documentFileName; 15 | 16 | public IgnoreDropHandler(IWpfTextView view, string fileName) 17 | { 18 | _view = view; 19 | _documentFileName = fileName; 20 | } 21 | 22 | public DragDropPointerEffects HandleDataDropped(DragDropInfo dragDropInfo) 23 | { 24 | var position = dragDropInfo.VirtualBufferPosition.Position; 25 | var line = _view.GetTextViewLineContainingBufferPosition(position); 26 | string text = PackageUtilities.MakeRelative(_documentFileName, _draggedFileName) 27 | .Replace("\\", "/"); 28 | 29 | // Insert a new line if dragged after existing text 30 | if (line.Start < position) 31 | text = Environment.NewLine + text; 32 | 33 | using (var edit = _view.TextBuffer.CreateEdit()) 34 | { 35 | edit.Insert(position, text); 36 | edit.Apply(); 37 | } 38 | 39 | return DragDropPointerEffects.Copy; 40 | } 41 | 42 | public void HandleDragCanceled() 43 | { } 44 | 45 | public DragDropPointerEffects HandleDragStarted(DragDropInfo dragDropInfo) 46 | { 47 | return DragDropPointerEffects.All; 48 | } 49 | 50 | public DragDropPointerEffects HandleDraggingOver(DragDropInfo dragDropInfo) 51 | { 52 | return DragDropPointerEffects.All; 53 | } 54 | 55 | public bool IsDropEnabled(DragDropInfo dragDropInfo) 56 | { 57 | _draggedFileName = GetImageFilename(dragDropInfo); 58 | 59 | return File.Exists(_draggedFileName) || Directory.Exists(_draggedFileName); 60 | } 61 | 62 | private static string GetImageFilename(DragDropInfo info) 63 | { 64 | var data = new DataObject(info.Data); 65 | 66 | if (info.Data.GetDataPresent("FileDrop")) 67 | { 68 | // The drag and drop operation came from the file system 69 | var files = data.GetFileDropList(); 70 | 71 | if (files != null && files.Count == 1) 72 | { 73 | return files[0]; 74 | } 75 | } 76 | else if (info.Data.GetDataPresent("CF_VSSTGPROJECTITEMS")) 77 | { 78 | // The drag and drop operation came from the VS solution explorer 79 | return data.GetText(); 80 | } 81 | 82 | return null; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src/DragDrop/IgnoreDropHandlerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Editor; 4 | using Microsoft.VisualStudio.Text.Editor.DragDrop; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace IgnoreFiles 8 | { 9 | [Export(typeof(IDropHandlerProvider))] 10 | [DropFormat("CF_VSSTGPROJECTITEMS")] 11 | [DropFormat("FileDrop")] 12 | [Name("IgnoreDropHandler")] 13 | [ContentType(IgnoreContentTypeDefinition.IgnoreContentType)] 14 | [Order(Before = "DefaultFileDropHandler")] 15 | internal class IgnoreDropHandlerProvider : IDropHandlerProvider 16 | { 17 | [Import] 18 | ITextDocumentFactoryService TextDocumentFactoryService { get; set; } 19 | 20 | public IDropHandler GetAssociatedDropHandler(IWpfTextView view) 21 | { 22 | ITextDocument document; 23 | 24 | if (TextDocumentFactoryService.TryGetTextDocument(view.TextBuffer, out document)) 25 | { 26 | return view.Properties.GetOrCreateSingletonProperty(() => new IgnoreDropHandler(view, document.FilePath)); 27 | } 28 | 29 | return null; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/DragDrop/__dummy__: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/DragDrop/__dummy__ -------------------------------------------------------------------------------- /src/Helpers/IEventsFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.OLE.Interop; 4 | using Microsoft.VisualStudio.TextManager.Interop; 5 | 6 | namespace IgnoreFiles 7 | { 8 | public interface IEventsFilter 9 | { 10 | void Remove(); 11 | } 12 | 13 | internal class EventsFilter : IEventsFilter, IOleCommandTarget 14 | { 15 | private readonly IVsTextView _view; 16 | 17 | public EventsFilter(IVsTextView view) 18 | { 19 | _view = view; 20 | } 21 | 22 | public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) 23 | { 24 | return VSConstants.S_OK; 25 | } 26 | 27 | public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) 28 | { 29 | return VSConstants.S_OK; 30 | } 31 | 32 | public void Remove() 33 | { 34 | _view.RemoveCommandFilter(this); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Helpers/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using task = System.Threading.Tasks.Task; 5 | 6 | internal static class Logger 7 | { 8 | private static string _name; 9 | private static IVsOutputWindowPane _pane; 10 | private static IVsOutputWindow _output; 11 | 12 | public static void Initialize(IServiceProvider provider, string name) 13 | { 14 | _output = (IVsOutputWindow)provider.GetService(typeof(SVsOutputWindow)); 15 | _name = name; 16 | } 17 | 18 | public static async task InitializeAsync(AsyncPackage package, string name) 19 | { 20 | _output = await package.GetServiceAsync(typeof(SVsOutputWindow)) as IVsOutputWindow; 21 | _name = name; 22 | } 23 | 24 | public static void Log(object message) 25 | { 26 | try 27 | { 28 | if (EnsurePane()) 29 | { 30 | _pane.OutputString(DateTime.Now.ToString() + ": " + message + Environment.NewLine); 31 | } 32 | } 33 | catch (Exception ex) 34 | { 35 | System.Diagnostics.Debug.Write(ex); 36 | } 37 | } 38 | 39 | private static bool EnsurePane() 40 | { 41 | if (_pane == null && _output != null) 42 | { 43 | ThreadHelper.Generic.BeginInvoke(() => 44 | { 45 | Guid guid = Guid.NewGuid(); 46 | _output.CreatePane(ref guid, _name, 1, 1); 47 | _output.GetPane(ref guid, out _pane); 48 | }); 49 | } 50 | 51 | return _pane != null; 52 | } 53 | } -------------------------------------------------------------------------------- /src/Helpers/ProjectHelpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio; 2 | using Microsoft.VisualStudio.ComponentModelHost; 3 | using Microsoft.VisualStudio.Editor; 4 | using Microsoft.VisualStudio.OLE.Interop; 5 | using Microsoft.VisualStudio.Shell; 6 | using Microsoft.VisualStudio.Text; 7 | using Microsoft.VisualStudio.Text.Editor; 8 | using Microsoft.VisualStudio.TextManager.Interop; 9 | using Minimatch; 10 | 11 | namespace IgnoreFiles 12 | { 13 | class Helpers 14 | { 15 | public static ITextBuffer GetCurentTextBuffer() 16 | { 17 | return GetCurentTextView().TextBuffer; 18 | } 19 | 20 | public static IWpfTextView GetCurentTextView() 21 | { 22 | var componentModel = GetComponentModel(); 23 | if (componentModel == null) return null; 24 | var editorAdapter = componentModel.GetService(); 25 | 26 | return editorAdapter.GetWpfTextView(GetCurrentNativeTextView()); 27 | } 28 | 29 | private static IVsTextView GetCurrentNativeTextView() 30 | { 31 | var textManager = (IVsTextManager)ServiceProvider.GlobalProvider.GetService(typeof(SVsTextManager)); 32 | 33 | IVsTextView activeView = null; 34 | ErrorHandler.ThrowOnFailure(textManager.GetActiveView(1, null, out activeView)); 35 | return activeView; 36 | } 37 | 38 | public static IEventsFilter DemandEventsFilterForCurrentNativeTextView() 39 | { 40 | IVsTextView view = GetCurrentNativeTextView(); 41 | EventsFilter filter = new EventsFilter(view); 42 | IOleCommandTarget nextTarget; 43 | view.AddCommandFilter(filter, out nextTarget); 44 | return filter; 45 | } 46 | 47 | private static IComponentModel GetComponentModel() 48 | { 49 | return (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); 50 | } 51 | 52 | private static readonly Minimatch.Options _options = new Minimatch.Options { AllowWindowsPaths = true, MatchBase = true }; 53 | 54 | public static bool CheckGlobbing(string path, string pattern) 55 | { 56 | string p = pattern?.TrimEnd('/'); 57 | 58 | if (!string.IsNullOrWhiteSpace(p)) 59 | { 60 | return Minimatcher.Check(path, p, _options); 61 | } 62 | 63 | return false; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/IgnoreCommandTable.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by Extensibility Tools 2015 v1.7.160 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace IgnoreFiles 7 | { 8 | using System; 9 | 10 | /// 11 | /// Helper class that exposes all GUIDs used across VS Package. 12 | /// 13 | internal sealed partial class PackageGuids 14 | { 15 | public const string guidPackageString = "ebf4d448-5b20-423f-b81d-7d330a809e4c"; 16 | public const string guidPackageCmdSetString = "9cf0341b-c711-440c-b75f-739da99a06b8"; 17 | public static Guid guidPackage = new Guid(guidPackageString); 18 | public static Guid guidPackageCmdSet = new Guid(guidPackageCmdSetString); 19 | } 20 | /// 21 | /// Helper class that encapsulates all CommandIDs uses across VS Package. 22 | /// 23 | internal sealed partial class PackageIds 24 | { 25 | public const int MyMenuGroup = 0x1020; 26 | public const int RemoveNonMatches = 0x0100; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/IgnoreCommandTable.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/IgnoreFiles.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | $(VisualStudioVersion) 6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 7 | true 8 | 9 | 10 | 11 | 12 | Program 13 | $(DevEnvDir)\devenv.exe 14 | /rootsuffix Exp 15 | 16 | 17 | 18 | Debug 19 | AnyCPU 20 | 2.0 21 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 22 | {60419598-D990-4B43-9832-50BAE09271EC} 23 | Library 24 | Properties 25 | IgnoreFiles 26 | IgnoreFiles 27 | v4.5.1 28 | true 29 | true 30 | true 31 | false 32 | true 33 | false 34 | 35 | 36 | true 37 | full 38 | false 39 | bin\Debug\ 40 | DEBUG;TRACE 41 | prompt 42 | 4 43 | 44 | 45 | pdbonly 46 | true 47 | bin\Release\ 48 | TRACE 49 | prompt 50 | 4 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | IgnoreTree.xaml 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | True 72 | True 73 | IgnoreCommandTable.vsct 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Component 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | source.extension.vsixmanifest 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Resources\LICENSE 108 | true 109 | 110 | 111 | true 112 | Designer 113 | 114 | 115 | 116 | Designer 117 | VsixManifestGenerator 118 | source.extension.resx 119 | 120 | 121 | 122 | 123 | True 124 | True 125 | source.extension.vsixmanifest 126 | true 127 | VSPackage 128 | 129 | 130 | 131 | 132 | False 133 | False 134 | 135 | 136 | False 137 | False 138 | 139 | 140 | False 141 | False 142 | 143 | 144 | False 145 | False 146 | 147 | 148 | 149 | 150 | False 151 | False 152 | 153 | 154 | False 155 | 156 | 157 | ..\packages\Microsoft.VisualStudio.CoreUtility.14.2.25123\lib\net45\Microsoft.VisualStudio.CoreUtility.dll 158 | True 159 | False 160 | 161 | 162 | ..\packages\Microsoft.VisualStudio.Editor.14.2.25123\lib\net45\Microsoft.VisualStudio.Editor.dll 163 | True 164 | False 165 | 166 | 167 | ..\packages\Microsoft.VisualStudio.ImageCatalog.14.2.25123\lib\net45\Microsoft.VisualStudio.ImageCatalog.dll 168 | False 169 | 170 | 171 | ..\packages\Microsoft.VisualStudio.Imaging.14.2.25123\lib\net45\Microsoft.VisualStudio.Imaging.dll 172 | False 173 | 174 | 175 | True 176 | ..\packages\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.14.2.25123\lib\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll 177 | True 178 | 179 | 180 | ..\packages\Microsoft.VisualStudio.Language.Intellisense.14.2.25123\lib\net45\Microsoft.VisualStudio.Language.Intellisense.dll 181 | False 182 | 183 | 184 | ..\packages\Microsoft.VisualStudio.Language.StandardClassification.14.2.25123\lib\net45\Microsoft.VisualStudio.Language.StandardClassification.dll 185 | True 186 | False 187 | 188 | 189 | ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6070\lib\Microsoft.VisualStudio.OLE.Interop.dll 190 | True 191 | False 192 | 193 | 194 | ..\packages\Microsoft.VisualStudio.Shell.14.0.14.2.25123\lib\Microsoft.VisualStudio.Shell.14.0.dll 195 | False 196 | 197 | 198 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.10.0.10.0.30319\lib\net40\Microsoft.VisualStudio.Shell.Immutable.10.0.dll 199 | False 200 | 201 | 202 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.11.0.11.0.50727\lib\net45\Microsoft.VisualStudio.Shell.Immutable.11.0.dll 203 | False 204 | 205 | 206 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.12.0.12.0.21003\lib\net45\Microsoft.VisualStudio.Shell.Immutable.12.0.dll 207 | False 208 | 209 | 210 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.14.0.14.2.25123\lib\net45\Microsoft.VisualStudio.Shell.Immutable.14.0.dll 211 | False 212 | 213 | 214 | ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6071\lib\Microsoft.VisualStudio.Shell.Interop.dll 215 | True 216 | False 217 | 218 | 219 | True 220 | ..\packages\Microsoft.VisualStudio.Shell.Interop.10.0.10.0.30319\lib\Microsoft.VisualStudio.Shell.Interop.10.0.dll 221 | True 222 | 223 | 224 | True 225 | ..\packages\Microsoft.VisualStudio.Shell.Interop.11.0.11.0.61030\lib\Microsoft.VisualStudio.Shell.Interop.11.0.dll 226 | True 227 | 228 | 229 | True 230 | ..\packages\Microsoft.VisualStudio.Shell.Interop.12.0.12.0.30110\lib\Microsoft.VisualStudio.Shell.Interop.12.0.dll 231 | True 232 | 233 | 234 | True 235 | ..\packages\Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.14.2.25123\lib\Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll 236 | True 237 | 238 | 239 | ..\packages\Microsoft.VisualStudio.Shell.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.Shell.Interop.8.0.dll 240 | False 241 | 242 | 243 | ..\packages\Microsoft.VisualStudio.Shell.Interop.9.0.9.0.30729\lib\Microsoft.VisualStudio.Shell.Interop.9.0.dll 244 | False 245 | 246 | 247 | ..\packages\Microsoft.VisualStudio.Text.Data.14.2.25123\lib\net45\Microsoft.VisualStudio.Text.Data.dll 248 | True 249 | False 250 | 251 | 252 | ..\packages\Microsoft.VisualStudio.Text.Logic.14.2.25123\lib\net45\Microsoft.VisualStudio.Text.Logic.dll 253 | True 254 | False 255 | 256 | 257 | ..\packages\Microsoft.VisualStudio.Text.UI.14.2.25123\lib\net45\Microsoft.VisualStudio.Text.UI.dll 258 | True 259 | False 260 | 261 | 262 | ..\packages\Microsoft.VisualStudio.Text.UI.Wpf.14.2.25123\lib\net45\Microsoft.VisualStudio.Text.UI.Wpf.dll 263 | True 264 | False 265 | 266 | 267 | ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6070\lib\Microsoft.VisualStudio.TextManager.Interop.dll 268 | True 269 | False 270 | 271 | 272 | ..\packages\Microsoft.VisualStudio.TextManager.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.TextManager.Interop.8.0.dll 273 | True 274 | False 275 | 276 | 277 | ..\packages\Microsoft.VisualStudio.Threading.14.1.131\lib\net45\Microsoft.VisualStudio.Threading.dll 278 | False 279 | 280 | 281 | ..\packages\Microsoft.VisualStudio.Utilities.14.2.25123\lib\net45\Microsoft.VisualStudio.Utilities.dll 282 | False 283 | 284 | 285 | ..\packages\Microsoft.VisualStudio.Validation.14.1.111\lib\net45\Microsoft.VisualStudio.Validation.dll 286 | False 287 | 288 | 289 | ..\packages\Minimatch.1.1.0.0\lib\portable-net40+sl50+win+wp80\Minimatch.dll 290 | True 291 | 292 | 293 | 294 | 295 | False 296 | False 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | true 349 | 350 | 351 | Menus.ctmenu 352 | VsctGenerator 353 | IgnoreCommandTable.cs 354 | 355 | 356 | 357 | 358 | 359 | 360 | true 361 | 362 | 363 | true 364 | 365 | 366 | source.extension.vsixmanifest 367 | 368 | 369 | 370 | 371 | Designer 372 | MSBuild:Compile 373 | 374 | 375 | Designer 376 | MSBuild:Compile 377 | 378 | 379 | 380 | 381 | 382 | 383 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 384 | 385 | 386 | 387 | 388 | 389 | 396 | -------------------------------------------------------------------------------- /src/IgnorePackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Threading; 4 | using EnvDTE80; 5 | using Microsoft.VisualStudio; 6 | using Microsoft.VisualStudio.Shell; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | 9 | namespace IgnoreFiles 10 | { 11 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 12 | [InstalledProductRegistration("#110", "#112", Vsix.Version, IconResourceID = 400)] 13 | [ProvideMenuResource("Menus.ctmenu", 1)] 14 | [ProvideOptionPage(typeof(Options), "Text Editor\\.ignore", "General", 101, 111, true, new[] { ".gitignore", ".tfignore" }, ProvidesLocalizedCategoryName = false)] 15 | [Guid(PackageGuids.guidPackageString)] 16 | public sealed class IgnorePackage : AsyncPackage 17 | { 18 | private static Options _options; 19 | private static DTE2 _dte; 20 | 21 | public static DTE2 DTE 22 | { 23 | get 24 | { 25 | if (_dte == null) 26 | { 27 | _dte = (DTE2)ServiceProvider.GlobalProvider.GetService(typeof(SDTE)); 28 | } 29 | 30 | return _dte; 31 | } 32 | } 33 | 34 | public static Options Options 35 | { 36 | get 37 | { 38 | if (_options == null) 39 | { 40 | // Load the package when options are needed 41 | var shell = (IVsShell)GetGlobalService(typeof(SVsShell)); 42 | IVsPackage package; 43 | ErrorHandler.ThrowOnFailure(shell.LoadPackage(ref PackageGuids.guidPackage, out package)); 44 | } 45 | 46 | return _options; 47 | } 48 | } 49 | 50 | protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 51 | { 52 | await Logger.InitializeAsync(this, Vsix.Name); 53 | await RemoveNonMatchesCommand.Initialize(this); 54 | 55 | _options = (Options)GetDialogPage(typeof(Options)); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Models/ActionCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace IgnoreFiles.Models 5 | { 6 | public class ActionCommand : ICommand 7 | { 8 | private readonly Func _canExecute; 9 | private readonly Action _execute; 10 | private bool _canExecuteValue; 11 | 12 | private ActionCommand(Action execute, Func canExecute, bool initialCanExecute) 13 | { 14 | _canExecuteValue = initialCanExecute; 15 | _canExecute = canExecute; 16 | _execute = execute; 17 | } 18 | 19 | public event EventHandler CanExecuteChanged; 20 | 21 | public static ICommand Create(Action execute, Func canExecute = null, bool initialCanExecute = true) 22 | { 23 | return new ActionCommand(execute, canExecute, initialCanExecute); 24 | } 25 | 26 | public static ICommand Create(Action execute, Func canExecute = null, bool initialCanExecute = true) 27 | { 28 | return new ActionCommand(execute, canExecute, initialCanExecute); 29 | } 30 | 31 | public bool CanExecute(object parameter) 32 | { 33 | if (_canExecute == null) 34 | { 35 | return true; 36 | } 37 | 38 | bool oldCanExecute = _canExecuteValue; 39 | _canExecuteValue = _canExecute(); 40 | 41 | if (oldCanExecute ^ _canExecuteValue) 42 | { 43 | OnCanExecuteChanged(); 44 | } 45 | 46 | return _canExecuteValue; 47 | } 48 | 49 | public void Execute(object parameter) 50 | { 51 | _execute(); 52 | } 53 | 54 | private void OnCanExecuteChanged() 55 | { 56 | EventHandler handler = CanExecuteChanged; 57 | 58 | if (handler != null) 59 | { 60 | handler(this, EventArgs.Empty); 61 | } 62 | } 63 | } 64 | 65 | internal class ActionCommand : ICommand 66 | { 67 | private readonly Func _canExecute; 68 | private readonly Action _execute; 69 | private bool _canExecuteValue; 70 | 71 | public ActionCommand(Action execute, Func canExecute, bool initialCanExecute) 72 | { 73 | _execute = execute; 74 | _canExecute = canExecute; 75 | _canExecuteValue = initialCanExecute; 76 | } 77 | 78 | public event EventHandler CanExecuteChanged; 79 | 80 | public bool CanExecute(object parameter) 81 | { 82 | if (_canExecute == null) 83 | { 84 | return true; 85 | } 86 | 87 | bool oldCanExecute = _canExecuteValue; 88 | _canExecuteValue = _canExecute((T)parameter); 89 | 90 | if (oldCanExecute ^ _canExecuteValue) 91 | { 92 | OnCanExecuteChanged(); 93 | } 94 | 95 | return _canExecuteValue; 96 | } 97 | 98 | public void Execute(object parameter) 99 | { 100 | _execute((T)parameter); 101 | } 102 | 103 | private void OnCanExecuteChanged() 104 | { 105 | EventHandler handler = CanExecuteChanged; 106 | 107 | if (handler != null) 108 | { 109 | handler(this, EventArgs.Empty); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Models/BindableBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | using System.Diagnostics; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace IgnoreFiles.Models 7 | { 8 | public class BindableBase : INotifyPropertyChanged 9 | { 10 | public event PropertyChangedEventHandler PropertyChanged; 11 | 12 | public bool Set(ref T value, T newValue, IEqualityComparer comparer = null, [CallerMemberName] string propertyName = null) 13 | { 14 | IEqualityComparer comparerToUse = comparer ?? EqualityComparer.Default; 15 | 16 | if (!comparerToUse.Equals(value, newValue)) 17 | { 18 | value = newValue; 19 | OnPropertyChangedCore(propertyName); 20 | return true; 21 | } 22 | 23 | return false; 24 | } 25 | 26 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 27 | { 28 | OnPropertyChangedCore(propertyName); 29 | } 30 | 31 | private void OnPropertyChangedCore(string propertyName) 32 | { 33 | Debug.Assert(propertyName != null, "Property name cannot be null"); 34 | PropertyChangedEventHandler handler = PropertyChanged; 35 | 36 | if (handler != null) 37 | { 38 | handler(this, new PropertyChangedEventArgs(propertyName)); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Models/FileTree.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace IgnoreFiles.Models 7 | { 8 | public class FileTree 9 | { 10 | private readonly string _rootDir; 11 | private List _children = new List(); 12 | private readonly Dictionary _lookup = new Dictionary(); 13 | 14 | public static IReadOnlyList DirectoryIgnoreList { get; } = new List 15 | { 16 | ".git" 17 | }; 18 | 19 | public FileTree(string rootDirectory) 20 | { 21 | _rootDir = rootDirectory; 22 | } 23 | 24 | public IEnumerable AllFiles => _lookup.Values; 25 | 26 | public IReadOnlyList Children => _children; 27 | 28 | public static FileTree ForDirectory(string rootDirectory) 29 | { 30 | FileTree root = new FileTree(rootDirectory); 31 | 32 | foreach (string file in Directory.EnumerateFileSystemEntries(rootDirectory, "*", SearchOption.AllDirectories)) 33 | { 34 | string[] parts = file.Split('/', '\\'); 35 | bool skip = false; 36 | bool isFile = File.Exists(file); 37 | int fileNameParts = isFile ? 1 : 0; 38 | 39 | for (int i = 1; !skip && i < parts.Length - fileNameParts; ++i) 40 | { 41 | if (DirectoryIgnoreList.Contains(parts[i], StringComparer.OrdinalIgnoreCase)) 42 | { 43 | skip = true; 44 | } 45 | } 46 | 47 | if (!skip) 48 | { 49 | root.GetModelFor(file, isFile); 50 | } 51 | } 52 | 53 | root.SortChildren(); 54 | return root; 55 | } 56 | 57 | private void SortChildren() 58 | { 59 | _children = _children.OrderBy(x => x.IsFile).ThenBy(x => x.Name, StringComparer.OrdinalIgnoreCase).ToList(); 60 | 61 | foreach (FileTreeModel child in _children.Where(x => !x.IsFile)) 62 | { 63 | child.SortChildren(); 64 | } 65 | } 66 | 67 | public FileTreeModel GetModelFor(string fullPath, bool isFile) 68 | { 69 | if (fullPath.Length <= _rootDir.Length) 70 | { 71 | return null; 72 | } 73 | 74 | FileTreeModel existingModel; 75 | if (!_lookup.TryGetValue(fullPath, out existingModel)) 76 | { 77 | existingModel = new FileTreeModel(fullPath, this, isFile); 78 | _lookup[fullPath] = existingModel; 79 | 80 | if (existingModel.Parent == null) 81 | { 82 | _children.Add(existingModel); 83 | } 84 | } 85 | 86 | return existingModel; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/Models/FileTreeModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Windows.Input; 6 | using System.Windows.Media; 7 | 8 | namespace IgnoreFiles.Models 9 | { 10 | public class FileTreeModel : BindableBase 11 | { 12 | private bool _isExpanded; 13 | private string _name; 14 | private List _children; 15 | 16 | public FileTreeModel(string fullPath, FileTree root, bool isFile) 17 | { 18 | IsExpanded = true; 19 | _children = new List(); 20 | Root = root; 21 | Name = Path.GetFileName(fullPath); 22 | IsFile = isFile; 23 | FullPath = fullPath; 24 | int lastSlash = fullPath.LastIndexOfAny(new[] { '/', '\\' }); 25 | 26 | //Normally this would be -1, but if the path starts with / or \, we don't want to make an empty entry 27 | if(lastSlash > 0) 28 | { 29 | string parentFullPath = fullPath.Substring(0, lastSlash).Trim('/', '\\'); 30 | 31 | if(!string.IsNullOrEmpty(parentFullPath)) 32 | { 33 | Parent = root.GetModelFor(parentFullPath, false); 34 | Parent?.Children?.Add(this); 35 | } 36 | } 37 | } 38 | 39 | public void ItemDoubleClicked(object sender, MouseButtonEventArgs e) 40 | { 41 | if (IsFile) 42 | { 43 | IgnorePackage.DTE.ItemOperations.OpenFile(FullPath); 44 | } 45 | } 46 | 47 | public ImageSource CachedIcon { get; set; } 48 | 49 | public List Children => _children; 50 | 51 | public bool IsFile { get; } 52 | 53 | public string Name 54 | { 55 | get { return _name; } 56 | set { Set(ref _name, value, StringComparer.Ordinal); } 57 | } 58 | 59 | public FileTreeModel Parent { get; } 60 | 61 | public FileTree Root { get; } 62 | 63 | public string FullPath { get; } 64 | 65 | public bool IsExpanded 66 | { 67 | get { return _isExpanded; } 68 | set { Set(ref _isExpanded, value); } 69 | } 70 | 71 | public void SortChildren() 72 | { 73 | _children = _children.OrderBy(x => x.IsFile).ThenBy(x => x.Name, StringComparer.OrdinalIgnoreCase).ToList(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Models/IgnoreTreeModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Imaging; 5 | using System.Collections.Generic; 6 | using System.Windows.Input; 7 | using System.Windows.Media.Imaging; 8 | 9 | namespace IgnoreFiles.Models 10 | { 11 | public class IgnoreTreeModel : BindableBase 12 | { 13 | private bool _showAllFiles; 14 | private string _searchText; 15 | private readonly string _pattern; 16 | private HashSet _visibleNodes; 17 | private HashSet _filterMatches; 18 | private string _lastSearchText; 19 | private bool _lastShowAllFiles; 20 | 21 | public IgnoreTreeModel(string rootDirectory, string pattern) 22 | { 23 | _pattern = pattern.TrimStart('/', '\\', '!'); 24 | TreeRoot = FileTree.ForDirectory(rootDirectory); 25 | ShouldBeVisible = CheckVisibility; 26 | IsSearchMatch = f => true; 27 | 28 | NodeFilter = FilterNodes; 29 | FileCount = TreeRoot.AllFiles.Count(x => x.IsFile && ShouldBeVisible(x)); 30 | SearchIcon = WpfUtil.GetIconForImageMoniker(KnownMonikers.Search, 16, 16); 31 | ShowAllFilesIcon = WpfUtil.GetIconForImageMoniker(KnownMonikers.ShowAllFiles, 16, 16); 32 | SyncToSolutionExplorerIcon = WpfUtil.GetIconForImageMoniker(KnownMonikers.Sync, 16, 16); 33 | } 34 | 35 | public ImageSource SyncToSolutionExplorerIcon { get; } 36 | 37 | private bool CheckVisibility(FileTreeModel arg) 38 | { 39 | EnsureFilterRun(); 40 | return _visibleNodes.Contains(arg); 41 | } 42 | 43 | private bool FilterNodes(FileTreeModel arg) 44 | { 45 | EnsureFilterRun(); 46 | return _filterMatches.Contains(arg); 47 | } 48 | 49 | private void EnsureFilterRun() 50 | { 51 | if (string.Equals(_lastSearchText, _searchText) && _lastShowAllFiles == _showAllFiles && _visibleNodes != null) 52 | { 53 | return; 54 | } 55 | 56 | string searchText = _searchText; 57 | bool showAllFiles = _showAllFiles; 58 | HashSet visibleNodes = new HashSet(); 59 | HashSet filterMatches = new HashSet(); 60 | HashSet directGlobMatches = new HashSet(); 61 | 62 | foreach (FileTreeModel model in TreeRoot.AllFiles) 63 | { 64 | if (visibleNodes.Contains(model)) 65 | { 66 | continue; 67 | } 68 | 69 | bool globMatch = Helpers.CheckGlobbing(model.FullPath, _pattern); 70 | 71 | if (globMatch) 72 | { 73 | directGlobMatches.Add(model); 74 | filterMatches.Add(model); 75 | FileTreeModel parent = model.Parent; 76 | 77 | while (parent != null) 78 | { 79 | //If something else has already added our parent, it's already added the whole parent tree, bail. 80 | if (!filterMatches.Add(parent)) 81 | { 82 | break; 83 | } 84 | 85 | parent = parent.Parent; 86 | } 87 | } 88 | else 89 | { 90 | FileTreeModel parent = model.Parent; 91 | while (parent != null) 92 | { 93 | //If we're included by a direct glob match (even though this model isn't one itself)... 94 | if (directGlobMatches.Contains(parent)) 95 | { 96 | filterMatches.Add(model); 97 | globMatch = true; 98 | parent = model.Parent; 99 | 100 | while (parent != null) 101 | { 102 | //If something else has already added our parent, it's already added the whole parent tree, bail. 103 | if (!filterMatches.Add(parent)) 104 | { 105 | break; 106 | } 107 | 108 | parent = parent.Parent; 109 | } 110 | 111 | break; 112 | } 113 | 114 | parent = parent.Parent; 115 | } 116 | } 117 | 118 | 119 | bool isVisible = showAllFiles || globMatch; 120 | bool explicitMatch = globMatch; 121 | 122 | if (!string.IsNullOrEmpty(searchText)) 123 | { 124 | bool searchMatch = model.Name.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) > -1; 125 | explicitMatch |= searchMatch; 126 | isVisible &= searchMatch; 127 | } 128 | 129 | model.IsExpanded = globMatch; 130 | 131 | if (isVisible) 132 | { 133 | visibleNodes.Add(model); 134 | 135 | FileTreeModel parent = model.Parent; 136 | 137 | while (parent != null) 138 | { 139 | //If something else has already added our parent, it's already added the whole parent tree, bail. 140 | if (!visibleNodes.Add(parent) && (!explicitMatch || parent.IsExpanded)) 141 | { 142 | break; 143 | } 144 | 145 | if (explicitMatch) 146 | { 147 | parent.IsExpanded = true; 148 | } 149 | 150 | parent = parent.Parent; 151 | } 152 | } 153 | } 154 | 155 | if (string.Equals(searchText, _searchText) && showAllFiles == _showAllFiles) 156 | { 157 | _filterMatches = filterMatches; 158 | _visibleNodes = visibleNodes; 159 | _lastShowAllFiles = showAllFiles; 160 | _lastSearchText = searchText; 161 | } 162 | } 163 | 164 | public Func NodeFilter { get; } 165 | 166 | public FileTree TreeRoot { get; } 167 | 168 | public Func ShouldBeVisible { get; } 169 | 170 | public bool ShowAllFiles 171 | { 172 | get { return _showAllFiles; } 173 | set { Set(ref _showAllFiles, value); } 174 | } 175 | 176 | public int FileCount { get; } 177 | 178 | public string SearchText 179 | { 180 | get { return _searchText; } 181 | set { Set(ref _searchText, value, StringComparer.Ordinal); } 182 | } 183 | 184 | public ImageSource SearchIcon { get; } 185 | 186 | public ImageSource ShowAllFilesIcon { get; } 187 | 188 | public Func IsSearchMatch { get; } 189 | 190 | public bool SyncToSolutionExplorer 191 | { 192 | get 193 | { 194 | return IgnorePackage.Options.SyncToSolutionExplorer; 195 | } 196 | set 197 | { 198 | IgnorePackage.Options.SyncToSolutionExplorer = value; 199 | IgnorePackage.Options.SaveSettingsToStorage(); 200 | } 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/Options.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace IgnoreFiles 7 | { 8 | public class Options : DialogPage 9 | { 10 | [Category("General settings")] 11 | [DisplayName("Ignore patterns")] 12 | [Description("A semicolon-separated list of strings. Any file containing one of the strings in the path will be ignored.")] 13 | [DefaultValue(@"\node_modules;\.git;\packages")] 14 | public string IgnorePatterns { get; set; } = @"\node_modules;\.git;\packages"; 15 | 16 | [Category("Tooltip")] 17 | [DisplayName("Show hover tooltip")] 18 | [Description("Shows tooltip when hovering the mouse over entries in .ignore files.")] 19 | [DefaultValue(true)] 20 | public bool ShowTooltip { get; set; } = true; 21 | 22 | [Category("Tooltip")] 23 | [DisplayName("Sync to Solution Explorer")] 24 | [Description("Automatically sync the selection from the tooltip to Solution Explorer.")] 25 | [DefaultValue(true)] 26 | public bool SyncToSolutionExplorer { get; set; } = true; 27 | 28 | public IEnumerable GetIgnorePatterns() 29 | { 30 | var raw = IgnorePatterns.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries); 31 | 32 | foreach (string pattern in raw) 33 | { 34 | yield return pattern; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using IgnoreFiles; 4 | 5 | [assembly: AssemblyTitle(Vsix.Name)] 6 | [assembly: AssemblyDescription(Vsix.Description)] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany(Vsix.Author)] 9 | [assembly: AssemblyProduct(Vsix.Name)] 10 | [assembly: AssemblyCopyright(Vsix.Author)] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: AssemblyVersion(Vsix.Version)] 17 | [assembly: AssemblyFileVersion(Vsix.Version)] 18 | -------------------------------------------------------------------------------- /src/QuickInfo/IgnoreQuickInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using IgnoreFiles.Controls; 6 | using Microsoft.VisualStudio.Language.Intellisense; 7 | using Microsoft.VisualStudio.Text; 8 | using Microsoft.VisualStudio.Text.Classification; 9 | 10 | namespace IgnoreFiles 11 | { 12 | internal class IgnoreQuickInfo : IQuickInfoSource 13 | { 14 | private IClassifier _classifier; 15 | private string _root; 16 | private IEventsFilter _eventsFilter; 17 | private const int _maxFiles = 20; 18 | 19 | public IgnoreQuickInfo(ITextBuffer buffer, IClassifierAggregatorService classifier, ITextDocument document) 20 | { 21 | _classifier = classifier.GetClassifier(buffer); 22 | _root = Path.GetDirectoryName(document.FilePath); 23 | } 24 | 25 | public void AugmentQuickInfoSession(IQuickInfoSession session, IList qiContent, out ITrackingSpan applicableToSpan) 26 | { 27 | _eventsFilter?.Remove(); 28 | _eventsFilter = Helpers.DemandEventsFilterForCurrentNativeTextView(); 29 | session.Dismissed += RemoveFilter; 30 | applicableToSpan = null; 31 | 32 | var buffer = session.TextView.TextBuffer; 33 | SnapshotPoint? point = session.GetTriggerPoint(buffer.CurrentSnapshot); 34 | 35 | if (!point.HasValue) 36 | return; 37 | 38 | int position = point.Value.Position; 39 | var line = point.Value.GetContainingLine(); 40 | var span = new SnapshotSpan(line.Start, line.Length); 41 | var tags = _classifier.GetClassificationSpans(span); 42 | 43 | foreach (var tag in tags.Where(t => t.Span.Contains(position))) 44 | { 45 | if (tag.ClassificationType.IsOfType(IgnoreClassificationTypes.PathNoMatch)) 46 | { 47 | string text = tag.Span.GetText(); 48 | 49 | // Only show error tooltips 50 | if (!text.Contains("../") && !IgnorePackage.Options.ShowTooltip) 51 | continue; 52 | 53 | string tooltip = $"The path \"{text}\" does not point to any existing file"; 54 | 55 | if (text.StartsWith("../")) 56 | tooltip = "The entry contains a relative path segment which is not allowed"; 57 | 58 | applicableToSpan = buffer.CurrentSnapshot.CreateTrackingSpan(tag.Span.Span, SpanTrackingMode.EdgeNegative); 59 | qiContent.Add(tooltip); 60 | break; 61 | } 62 | else if (tag.ClassificationType.IsOfType(IgnoreClassificationTypes.Path)) 63 | { 64 | if (!IgnorePackage.Options.ShowTooltip) 65 | { 66 | continue; 67 | } 68 | 69 | string pattern = tag.Span.GetText().Trim(); 70 | IgnoreTree tree = new IgnoreTree(_root, pattern, () => 71 | { 72 | try 73 | { 74 | session.Dismiss(); 75 | } 76 | catch 77 | { 78 | } 79 | }); 80 | 81 | //Resizing in quick info causes the position to change relative to the mouse's position relative to 82 | // the original launch point. Fix the size of the control when launching from QI 83 | tree.Width = tree.MaxWidth; 84 | tree.Height = (tree.MaxHeight + tree.MinHeight)/2; 85 | qiContent.Add(tree); 86 | applicableToSpan = buffer.CurrentSnapshot.CreateTrackingSpan(tag.Span.Span, SpanTrackingMode.EdgeNegative); 87 | 88 | //var files = GetFiles(_root, pattern); 89 | //qiContent.Add(string.Join(Environment.NewLine, files.Take(_maxFiles))); 90 | 91 | //if (files.Count() > _maxFiles) 92 | //{ 93 | // qiContent.Add($"...and {files.Count() - _maxFiles} more"); 94 | //} 95 | break; 96 | } 97 | } 98 | } 99 | 100 | private void RemoveFilter(object sender, EventArgs e) 101 | { 102 | _eventsFilter.Remove(); 103 | _eventsFilter = null; 104 | } 105 | 106 | public static IEnumerable GetFiles(string folder, string pattern, string root = null) 107 | { 108 | var ignorePaths = IgnorePackage.Options.GetIgnorePatterns(); 109 | var files = new List(); 110 | root = root ?? folder; 111 | pattern = pattern.TrimStart('/', '\\'); 112 | 113 | try 114 | { 115 | foreach (var file in Directory.EnumerateFileSystemEntries(folder).Where(f => !ignorePaths.Any(p => folder.Contains(p)))) 116 | { 117 | if (pattern.EndsWith("/") && !File.GetAttributes(file).HasFlag(FileAttributes.Directory)) 118 | continue; 119 | 120 | string relative = file.Replace(root, "").Replace("\\", "/").Trim('/'); 121 | 122 | if (Helpers.CheckGlobbing(relative, pattern.TrimEnd('/'))) 123 | files.Add(relative); 124 | } 125 | 126 | foreach (var directory in Directory.EnumerateDirectories(folder).Where(d => !ignorePaths.Any(i => d.Contains(i)))) 127 | files.AddRange(GetFiles(directory, pattern, root)); 128 | } 129 | catch (Exception ex) 130 | { 131 | System.Diagnostics.Debug.Write(ex); 132 | } 133 | 134 | return files; 135 | } 136 | 137 | public void Dispose() 138 | { 139 | // nothing to dispose 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/QuickInfo/IgnoreQuickInfoController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.VisualStudio.Language.Intellisense; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | 6 | namespace IgnoreFiles 7 | { 8 | internal class IgnoreQuickInfoController : IIntellisenseController 9 | { 10 | private ITextView m_textView; 11 | private IList m_subjectBuffers; 12 | private IgnoreQuickInfoControllerProvider m_provider; 13 | 14 | internal IgnoreQuickInfoController(ITextView textView, IList subjectBuffers, IgnoreQuickInfoControllerProvider provider) 15 | { 16 | m_textView = textView; 17 | m_subjectBuffers = subjectBuffers; 18 | m_provider = provider; 19 | 20 | m_textView.MouseHover += this.OnTextViewMouseHover; 21 | } 22 | 23 | private void OnTextViewMouseHover(object sender, MouseHoverEventArgs e) 24 | { 25 | //find the mouse position by mapping down to the subject buffer 26 | SnapshotPoint? point = m_textView.BufferGraph.MapDownToFirstMatch 27 | (new SnapshotPoint(m_textView.TextSnapshot, e.Position), 28 | PointTrackingMode.Positive, 29 | snapshot => m_subjectBuffers.Contains(snapshot.TextBuffer), 30 | PositionAffinity.Predecessor); 31 | 32 | if (point != null) 33 | { 34 | ITrackingPoint triggerPoint = point.Value.Snapshot.CreateTrackingPoint(point.Value.Position, 35 | PointTrackingMode.Positive); 36 | 37 | if (!m_provider.QuickInfoBroker.IsQuickInfoActive(m_textView)) 38 | { 39 | m_provider.QuickInfoBroker.TriggerQuickInfo(m_textView, triggerPoint, true); 40 | } 41 | } 42 | } 43 | 44 | public void Detach(ITextView textView) 45 | { 46 | if (m_textView == textView) 47 | { 48 | m_textView.MouseHover -= this.OnTextViewMouseHover; 49 | m_textView = null; 50 | } 51 | } 52 | 53 | 54 | public void ConnectSubjectBuffer(ITextBuffer subjectBuffer) 55 | { 56 | } 57 | 58 | public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer) 59 | { 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/QuickInfo/IgnoreQuickInfoControllerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Language.Intellisense; 4 | using Microsoft.VisualStudio.Text; 5 | using Microsoft.VisualStudio.Text.Editor; 6 | using Microsoft.VisualStudio.Utilities; 7 | 8 | namespace IgnoreFiles 9 | { 10 | [Export(typeof(IIntellisenseControllerProvider))] 11 | [Name("Ignore QuickInfo Controller")] 12 | [ContentType(IgnoreContentTypeDefinition.IgnoreContentType)] 13 | public class IgnoreQuickInfoControllerProvider : IIntellisenseControllerProvider 14 | { 15 | [Import] 16 | public IQuickInfoBroker QuickInfoBroker { get; set; } 17 | 18 | public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList subjectBuffers) 19 | { 20 | return new IgnoreQuickInfoController(textView, subjectBuffers, this); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/QuickInfo/IgnoreQuickInfoSourceProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Language.Intellisense; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Classification; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace IgnoreFiles 8 | { 9 | [Export(typeof(IQuickInfoSourceProvider))] 10 | [Name("Ignore QuickInfo Source")] 11 | [Order(Before = "Default Quick Info Presenter")] 12 | [ContentType(IgnoreContentTypeDefinition.IgnoreContentType)] 13 | internal class ImageHtmlQuickInfoSourceProvider : IQuickInfoSourceProvider 14 | { 15 | [Import] 16 | IClassifierAggregatorService _classifierService = null; 17 | 18 | [Import] 19 | ITextDocumentFactoryService TextDocumentFactoryService = null; 20 | 21 | public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer buffer) 22 | { 23 | ITextDocument document; 24 | 25 | if (TextDocumentFactoryService.TryGetTextDocument(buffer, out document)) 26 | { 27 | return buffer.Properties.GetOrCreateSingletonProperty(() => new IgnoreQuickInfo(buffer, _classifierService, document)); 28 | } 29 | 30 | return null; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/Resources/Icon.png -------------------------------------------------------------------------------- /src/Resources/Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/Resources/Preview.png -------------------------------------------------------------------------------- /src/SuggestedActions/Actions/BaseSuggestedAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.VisualStudio.Imaging.Interop; 6 | using Microsoft.VisualStudio.Language.Intellisense; 7 | 8 | namespace IgnoreFiles 9 | { 10 | abstract class BaseSuggestedAction : ISuggestedAction 11 | { 12 | public abstract string DisplayText { get; } 13 | 14 | public virtual bool IsEnabled { get; } = true; 15 | 16 | public virtual bool HasActionSets 17 | { 18 | get { return false; } 19 | } 20 | 21 | public virtual bool HasPreview 22 | { 23 | get { return false; } 24 | } 25 | 26 | public string IconAutomationText 27 | { 28 | get { return null; } 29 | } 30 | 31 | public virtual ImageMoniker IconMoniker 32 | { 33 | get { return default(ImageMoniker); } 34 | } 35 | 36 | public string InputGestureText 37 | { 38 | get { return null; } 39 | } 40 | 41 | public virtual void Dispose() 42 | { 43 | // nothing to dispose 44 | } 45 | 46 | public Task> GetActionSetsAsync(CancellationToken cancellationToken) 47 | { 48 | return Task.FromResult>(null); 49 | } 50 | 51 | public Task GetPreviewAsync(CancellationToken cancellationToken) 52 | { 53 | return null; 54 | } 55 | 56 | public abstract void Invoke(CancellationToken cancellationToken); 57 | 58 | public bool TryGetTelemetryId(out Guid telemetryId) 59 | { 60 | telemetryId = Guid.Empty; 61 | return false; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/SuggestedActions/Actions/DeleteMatchesSuggestedAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Windows; 6 | using EnvDTE; 7 | using EnvDTE80; 8 | using Microsoft.VisualStudio.Imaging; 9 | using Microsoft.VisualStudio.Imaging.Interop; 10 | using Microsoft.VisualStudio.Shell; 11 | using Microsoft.VisualStudio.Text; 12 | 13 | namespace IgnoreFiles 14 | { 15 | class DeleteMatchesSuggestedAction : BaseSuggestedAction 16 | { 17 | private SnapshotSpan _span; 18 | 19 | public DeleteMatchesSuggestedAction(SnapshotSpan span) 20 | { 21 | _span = span; 22 | } 23 | 24 | public override string DisplayText 25 | { 26 | get { return "Delete matching file entries..."; } 27 | } 28 | 29 | public override ImageMoniker IconMoniker 30 | { 31 | get { return KnownMonikers.DeleteDocument; } 32 | } 33 | 34 | public override void Invoke(CancellationToken cancellationToken) 35 | { 36 | var okToProceed = MessageBox.Show("Are you sure you want to delete the matching file system entries?", Vsix.Name, MessageBoxButton.OKCancel, MessageBoxImage.Question); 37 | if (okToProceed != MessageBoxResult.OK) 38 | return; 39 | 40 | var dte = (DTE2)Package.GetGlobalService(typeof(DTE)); 41 | 42 | string folder = Path.GetDirectoryName(dte.ActiveDocument.FullName); 43 | string pattern = _span.GetText().Trim(); 44 | var entries = IgnoreQuickInfo.GetFiles(folder, pattern); 45 | 46 | foreach (var entry in entries.Select(e => Path.Combine(folder, e).Replace("/", "\\"))) 47 | { 48 | try 49 | { 50 | var item = dte.Solution?.FindProjectItem(entry); 51 | 52 | if (item != null) 53 | { 54 | item.Delete(); 55 | } 56 | else 57 | { 58 | if (Directory.Exists(entry)) 59 | Directory.Delete(entry, true); 60 | else if (File.Exists(entry)) 61 | File.Delete(entry); 62 | } 63 | } 64 | catch (Exception ex) 65 | { 66 | Logger.Log(ex); 67 | } 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/SuggestedActions/Actions/ExcludeMatchesFromProjectSuggestedAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Windows; 6 | using EnvDTE; 7 | using EnvDTE80; 8 | using Microsoft.VisualStudio.Imaging; 9 | using Microsoft.VisualStudio.Imaging.Interop; 10 | using Microsoft.VisualStudio.Shell; 11 | using Microsoft.VisualStudio.Text; 12 | 13 | namespace IgnoreFiles 14 | { 15 | class ExcludeMatchesFromProjectSuggestedAction : BaseSuggestedAction 16 | { 17 | private SnapshotSpan _span; 18 | 19 | public ExcludeMatchesFromProjectSuggestedAction(SnapshotSpan span) 20 | { 21 | _span = span; 22 | } 23 | 24 | public override string DisplayText 25 | { 26 | get { return "Exclude matching entries from project..."; } 27 | } 28 | 29 | public override ImageMoniker IconMoniker 30 | { 31 | get { return KnownMonikers.HiddenFile; } 32 | } 33 | 34 | public override bool IsEnabled 35 | { 36 | get 37 | { 38 | var dte = (DTE2)Package.GetGlobalService(typeof(DTE)); 39 | return !string.IsNullOrEmpty(dte.Solution?.FullName); 40 | } 41 | } 42 | 43 | public override void Invoke(CancellationToken cancellationToken) 44 | { 45 | var okToProceed = MessageBox.Show("Are you sure you want remove the entries from the project?\r\rNo files or folders will be deleted from disk.", Vsix.Name, MessageBoxButton.OKCancel, MessageBoxImage.Question); 46 | if (okToProceed != MessageBoxResult.OK) 47 | return; 48 | 49 | var dte = (DTE2)Package.GetGlobalService(typeof(DTE)); 50 | 51 | string folder = Path.GetDirectoryName(dte.ActiveDocument.FullName); 52 | string pattern = _span.GetText().Trim(); 53 | var entries = IgnoreQuickInfo.GetFiles(folder, pattern); 54 | 55 | foreach (var entry in entries.Select(e => Path.Combine(folder, e).Replace("/", "\\"))) 56 | { 57 | try 58 | { 59 | var item = dte.Solution?.FindProjectItem(entry); 60 | 61 | if (item != null) 62 | { 63 | item.Remove(); 64 | } 65 | } 66 | catch (Exception ex) 67 | { 68 | Logger.Log(ex); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/SuggestedActions/Actions/RemoveAllNonMatchSuggestedAction.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using EnvDTE; 3 | using EnvDTE80; 4 | using Microsoft.VisualStudio.Imaging; 5 | using Microsoft.VisualStudio.Imaging.Interop; 6 | using Microsoft.VisualStudio.Shell; 7 | using Microsoft.VisualStudio.Text; 8 | 9 | namespace IgnoreFiles 10 | { 11 | class RemoveAllNonMatchSuggestedAction : BaseSuggestedAction 12 | { 13 | private SnapshotSpan _span; 14 | 15 | public RemoveAllNonMatchSuggestedAction(SnapshotSpan span) 16 | { 17 | _span = span; 18 | } 19 | 20 | public override string DisplayText 21 | { 22 | get { return "Remove all non-matching entries"; } 23 | } 24 | 25 | public override ImageMoniker IconMoniker 26 | { 27 | get { return KnownMonikers.RecursivelyUncheckAll; } 28 | } 29 | 30 | public override void Invoke(CancellationToken cancellationToken) 31 | { 32 | var dte = (DTE2)Package.GetGlobalService(typeof(DTE)); 33 | dte.Commands.Raise(PackageGuids.guidPackageCmdSetString, PackageIds.RemoveNonMatches, null, null); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/SuggestedActions/Actions/RemoveNonMatchSuggestedAction.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using Microsoft.VisualStudio.Imaging; 3 | using Microsoft.VisualStudio.Imaging.Interop; 4 | using Microsoft.VisualStudio.Text; 5 | 6 | namespace IgnoreFiles 7 | { 8 | class RemoveNonMatchSuggestedAction : BaseSuggestedAction 9 | { 10 | private SnapshotSpan _span; 11 | 12 | public RemoveNonMatchSuggestedAction(SnapshotSpan span) 13 | { 14 | _span = span; 15 | } 16 | 17 | public override string DisplayText 18 | { 19 | get { return "Remove non-matching entry"; } 20 | } 21 | 22 | public override void Invoke(CancellationToken cancellationToken) 23 | { 24 | var line = _span.Snapshot.GetLineFromPosition(_span.Start.Position); 25 | 26 | using (var edit = _span.Snapshot.TextBuffer.CreateEdit()) 27 | { 28 | edit.Delete(line.Start.Position, line.LengthIncludingLineBreak); 29 | edit.Apply(); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/SuggestedActions/SuggestedActionsSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.VisualStudio.Language.Intellisense; 7 | using Microsoft.VisualStudio.Text; 8 | 9 | namespace IgnoreFiles 10 | { 11 | class SuggestedActionsSource : ISuggestedActionsSource 12 | { 13 | private readonly SuggestedActionsSourceProvider m_factory; 14 | private readonly IgnoreClassifier _classifier; 15 | 16 | public SuggestedActionsSource(SuggestedActionsSourceProvider testSuggestedActionsSourceProvider, IgnoreClassifier classifier) 17 | { 18 | m_factory = testSuggestedActionsSourceProvider; 19 | _classifier = classifier; 20 | } 21 | 22 | public Task HasSuggestedActionsAsync(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken) 23 | { 24 | return Task.Factory.StartNew(() => 25 | { 26 | var spans = _classifier.GetClassificationSpans(range); 27 | 28 | if (spans.Any(s => s.ClassificationType.IsOfType(IgnoreClassificationTypes.Path) || s.ClassificationType.IsOfType(IgnoreClassificationTypes.PathNoMatch))) 29 | { 30 | return true; 31 | } 32 | 33 | return false; 34 | }); 35 | } 36 | 37 | public IEnumerable GetSuggestedActions(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken) 38 | { 39 | var classifications = _classifier.GetClassificationSpans(range); 40 | 41 | foreach (var classification in classifications) 42 | { 43 | if (classification.ClassificationType.IsOfType(IgnoreClassificationTypes.Path)) 44 | { 45 | var deleteMatches = new DeleteMatchesSuggestedAction(classification.Span); 46 | var excludeFromProj = new ExcludeMatchesFromProjectSuggestedAction(classification.Span); 47 | 48 | return CreateActionSet(deleteMatches, excludeFromProj); 49 | } 50 | else if (classification.ClassificationType.IsOfType(IgnoreClassificationTypes.PathNoMatch)) 51 | { 52 | var removeNonMatch = new RemoveNonMatchSuggestedAction(classification.Span); 53 | var removeAllNonMatch = new RemoveAllNonMatchSuggestedAction(classification.Span); 54 | 55 | return CreateActionSet(removeNonMatch, removeAllNonMatch); 56 | } 57 | } 58 | 59 | return Enumerable.Empty(); 60 | } 61 | 62 | public IEnumerable CreateActionSet(params BaseSuggestedAction[] actions) 63 | { 64 | var enabledActions = actions.Where(action => action.IsEnabled); 65 | return new[] { new SuggestedActionSet(enabledActions) }; 66 | } 67 | 68 | public void Dispose() 69 | { 70 | } 71 | 72 | public bool TryGetTelemetryId(out Guid telemetryId) 73 | { 74 | // This is a sample provider and doesn't participate in LightBulb telemetry 75 | telemetryId = Guid.Empty; 76 | return false; 77 | } 78 | 79 | 80 | public event EventHandler SuggestedActionsChanged 81 | { 82 | add { } 83 | remove { } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/SuggestedActions/SuggestedActionsSourceProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Language.Intellisense; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.Text.Operations; 6 | using Microsoft.VisualStudio.Utilities; 7 | 8 | namespace IgnoreFiles 9 | { 10 | [Export(typeof(ISuggestedActionsSourceProvider))] 11 | [Name("Test Suggested Actions")] 12 | [ContentType(IgnoreContentTypeDefinition.IgnoreContentType)] 13 | class SuggestedActionsSourceProvider : ISuggestedActionsSourceProvider 14 | { 15 | [Import(typeof(ITextStructureNavigatorSelectorService))] 16 | internal ITextStructureNavigatorSelectorService NavigatorService { get; set; } 17 | 18 | public ISuggestedActionsSource CreateSuggestedActionsSource(ITextView textView, ITextBuffer textBuffer) 19 | { 20 | IgnoreClassifier classifier; 21 | 22 | if (!textBuffer.Properties.TryGetProperty(typeof(IgnoreClassifier), out classifier)) 23 | return null; 24 | 25 | return textView.Properties.GetOrCreateSingletonProperty(() => new SuggestedActionsSource(this, classifier)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Theme.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace IgnoreFiles 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 | ResourceDictionary localThemingContainer = (ResourceDictionary)Application.LoadComponent(new Uri("IgnoreFiles;component/Controls/Shared.xaml", UriKind.Relative)); 16 | allResources.MergedDictionaries.Add(shellResources); 17 | allResources.MergedDictionaries.Add(scrollStyleContainer); 18 | allResources.MergedDictionaries.Add(localThemingContainer); 19 | allResources[typeof(ScrollViewer)] = new Style 20 | { 21 | TargetType = typeof(ScrollViewer), 22 | BasedOn = (Style)scrollStyleContainer[VsResourceKeys.ScrollViewerStyleKey] 23 | }; 24 | return allResources; 25 | } 26 | 27 | private static ResourceDictionary ThemeResources { get; } = BuildThemeResources(); 28 | 29 | public static void ShouldBeThemed(this FrameworkElement control) 30 | { 31 | if (control.Resources == null) 32 | { 33 | control.Resources = ThemeResources; 34 | } 35 | else if (control.Resources != ThemeResources) 36 | { 37 | ResourceDictionary d = new ResourceDictionary(); 38 | d.MergedDictionaries.Add(ThemeResources); 39 | d.MergedDictionaries.Add(control.Resources); 40 | control.Resources = null; 41 | control.Resources = d; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Validation/IgnoreErrorTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Adornments; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | 7 | namespace IgnoreFiles 8 | { 9 | class IgnoreErrorTagger : ITagger 10 | { 11 | private const string _relativePattern = "../"; 12 | 13 | public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 14 | { 15 | var list = new List>(); 16 | 17 | foreach (SnapshotSpan currSpan in spans) 18 | { 19 | var text = currSpan.GetText(); 20 | 21 | if (text.Contains(_relativePattern)) 22 | { 23 | var length = text.Contains(" ") ? text.IndexOf(" ") : text.Length; 24 | var span = new SnapshotSpan(currSpan.Snapshot, currSpan.Start, length); 25 | var tag = new ErrorTag(PredefinedErrorTypeNames.SyntaxError, "foo bar"); 26 | list.Add(new TagSpan(span, tag)); 27 | } 28 | } 29 | 30 | return list; 31 | } 32 | 33 | public event EventHandler TagsChanged 34 | { 35 | add { } 36 | remove { } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Validation/IgnoreErrorTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Tagging; 4 | using Microsoft.VisualStudio.Utilities; 5 | 6 | namespace IgnoreFiles 7 | { 8 | [Export(typeof(ITaggerProvider))] 9 | [ContentType(IgnoreContentTypeDefinition.IgnoreContentType)] 10 | [TagType(typeof(ErrorTag))] 11 | class IgnoreErrorTaggerProvider : ITaggerProvider 12 | { 13 | public ITagger CreateTagger(ITextBuffer buffer) where T : ITag 14 | { 15 | return buffer.Properties.GetOrCreateSingletonProperty(() => new IgnoreErrorTagger() as ITagger); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/WpfUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | using System.Windows.Media; 5 | using System.Windows.Media.Imaging; 6 | using Microsoft.Internal.VisualStudio.Shell; 7 | using Microsoft.VisualStudio; 8 | using Microsoft.VisualStudio.Imaging.Interop; 9 | using Microsoft.VisualStudio.PlatformUI; 10 | using Microsoft.VisualStudio.Shell; 11 | using Microsoft.VisualStudio.Shell.Interop; 12 | using Color = System.Windows.Media.Color; 13 | 14 | namespace IgnoreFiles 15 | { 16 | public static class WpfUtil 17 | { 18 | public static BitmapSource GetIconForImageMoniker(ImageMoniker? imageMoniker, int sizeX, int sizeY) 19 | { 20 | if (imageMoniker == null) 21 | { 22 | return null; 23 | } 24 | 25 | IVsImageService2 vsIconService = ServiceProvider.GlobalProvider.GetService(typeof(SVsImageService)) as IVsImageService2; 26 | 27 | if (vsIconService == null) 28 | { 29 | return null; 30 | } 31 | 32 | ImageAttributes imageAttributes = new ImageAttributes 33 | { 34 | Flags = (uint)_ImageAttributesFlags.IAF_RequiredFlags, 35 | ImageType = (uint)_UIImageType.IT_Bitmap, 36 | Format = (uint)_UIDataFormat.DF_WPF, 37 | LogicalHeight = sizeY, 38 | LogicalWidth = sizeX, 39 | StructSize = Marshal.SizeOf(typeof(ImageAttributes)) 40 | }; 41 | 42 | IVsUIObject result = vsIconService.GetImage(imageMoniker.Value, imageAttributes); 43 | 44 | object data; 45 | result.get_Data(out data); 46 | BitmapSource glyph = data as BitmapSource; 47 | 48 | if (glyph != null) 49 | { 50 | glyph.Freeze(); 51 | } 52 | 53 | return glyph; 54 | } 55 | 56 | public static ImageSource GetIconForFile(DependencyObject owner, string file, out bool themeIcon) 57 | { 58 | return GetImage(owner, file, __VSUIDATAFORMAT.VSDF_WPF, out themeIcon); 59 | } 60 | 61 | private static ImageSource GetImage(DependencyObject owner, string file, __VSUIDATAFORMAT format, out bool themeIcon) 62 | { 63 | IVsImageService imageService = ServiceProvider.GlobalProvider.GetService(typeof(SVsImageService)) as IVsImageService; 64 | BitmapSource result = null; 65 | uint iconSource = (uint)__VSIconSource.IS_Unknown; 66 | 67 | if (imageService != null && !string.IsNullOrWhiteSpace(file)) 68 | { 69 | IVsUIObject image = imageService.GetIconForFileEx(file, format, out iconSource); 70 | if (image != null) 71 | { 72 | object imageData = GetObjectData(image); 73 | result = imageData as BitmapSource; 74 | } 75 | } 76 | 77 | themeIcon = (iconSource == (uint)__VSIconSource.IS_VisualStudio); 78 | 79 | if (themeIcon && result != null && owner != null) 80 | { 81 | return ThemeImage(owner, result); 82 | } 83 | 84 | return result; 85 | } 86 | 87 | public static ImageSource ThemeImage(DependencyObject owner, BitmapSource source) 88 | { 89 | Color background = ImageThemingUtilities.GetImageBackgroundColor(owner); 90 | return ImageThemingUtilities.GetOrCreateThemedBitmapSource(source, background, true, Colors.Black, false); 91 | } 92 | 93 | private static object GetObjectData(IVsUIObject obj) 94 | { 95 | Validate.IsNotNull(obj, nameof(obj)); 96 | 97 | object value; 98 | int result = obj.get_Data(out value); 99 | 100 | if (result != VSConstants.S_OK) 101 | { 102 | throw new COMException("Could not get object data", result); 103 | } 104 | 105 | return (value); 106 | } 107 | 108 | [DllImport("gdi32.dll", SetLastError = true)] 109 | private static extern bool DeleteObject(IntPtr hObject); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/source.extension.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by Extensibility Tools v1.10.211 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace IgnoreFiles 7 | { 8 | static class Vsix 9 | { 10 | public const string Id = "7ac24965-ea21-4108-9cac-6e46394aaaef"; 11 | public const string Name = ".ignore"; 12 | public const string Description = @"A language service that makes it painless to handle all types of .ignore files such as .gitignore, .tfignore, etc."; 13 | public const string Language = "en-US"; 14 | public const string Version = "1.2"; 15 | public const string Author = "Mads Kristensen"; 16 | public const string Tags = "git, tfs, svn, mercurial, ignore, npm, scc"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/source.extension.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/IgnoreFiles/8a037c941f2e65b279d2f62f84942b7c802e3f7f/src/source.extension.ico -------------------------------------------------------------------------------- /src/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 | .ignore 122 | 123 | 124 | A language service that makes it painless to handle all types of .ignore files such as .gitignore, .tfignore, etc. 125 | 126 | 127 | 128 | source.extension.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | -------------------------------------------------------------------------------- /src/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | .ignore 6 | A language service that makes it painless to handle all types of .ignore files such as .gitignore, .tfignore, etc. 7 | https://github.com/madskristensen/IgnoreFiles 8 | Resources\LICENSE 9 | https://github.com/madskristensen/IgnoreFiles/blob/master/CHANGELOG.md 10 | Resources\Icon.png 11 | Resources\Preview.png 12 | git, tfs, svn, mercurial, ignore, npm, scc 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | --------------------------------------------------------------------------------